/* Copyright 2024 @ Keychron (https://www.keychron.com) * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #include #include "eeconfig_kb.h" #include "snap_click.h" #include "raw_hid.h" #include "eeconfig.h" #include "matrix.h" #include "quantum.h" #include "keychron_raw_hid.h" #if defined(SNAP_CLICK_ENABLE) &&defined(EECONFIG_SIZE_SNAP_CLICK) enum { SNAP_CLICK_TYPE_NONE = 0, SNAP_CLICK_TYPE_REGULAR, SNAP_CLICK_TYPE_LAST_INPUT, SNAP_CLICK_TYPE_FIRST_KEY, SNAP_CLICK_TYPE_SECOND_KEY, SNAP_CLICK_TYPE_NEUTRAL, SNAP_CLICK_TYPE_MAX, }; #define SC_MASK_BOTH_KEYS_PRESSED 3 snap_click_config_t snap_click_pair[SNAP_CLICK_COUNT]; snap_click_state_t snap_click_state[SNAP_CLICK_COUNT]; void snap_click_config_reset(void) { memset(snap_click_pair, 0, sizeof(snap_click_pair)); eeprom_update_block(snap_click_pair, (uint8_t *)(EECONFIG_BASE_SNAP_CLICK), sizeof(snap_click_pair)); } void snap_click_init(void) { eeprom_read_block(snap_click_pair, (uint8_t *)(EECONFIG_BASE_SNAP_CLICK), sizeof(snap_click_pair)); memset(snap_click_state, 0, sizeof(snap_click_state)); } bool process_record_snap_click(uint16_t keycode, keyrecord_t * record) { for (uint8_t i=0; itype && (keycode == p->key[0] || keycode == p->key[1])) { snap_click_state_t *pState = &snap_click_state[i]; uint8_t index = keycode == p->key[1]; // 0 or 1 of key pair if (record->event.pressed) { uint8_t state = 0x01 << index; if (pState->state == 0) { // Single key down pState->state_keys = pState->last_single_key = state; } else if ((state & pState->state_keys) == 0) { // TODO: do we need checking? // Both keys are pressed pState->state_keys = SC_MASK_BOTH_KEYS_PRESSED; switch (p->type) { case SNAP_CLICK_TYPE_REGULAR: case SNAP_CLICK_TYPE_LAST_INPUT: unregister_code(p->key[1-index]); register_code(p->key[index]); break; case SNAP_CLICK_TYPE_FIRST_KEY: unregister_code(p->key[1]); register_code(p->key[0]); break; case SNAP_CLICK_TYPE_SECOND_KEY: unregister_code(p->key[0]); register_code(p->key[1]); break; case SNAP_CLICK_TYPE_NEUTRAL: unregister_code(p->key[1-index]); break; } return false; } } else { if (pState->state_keys == SC_MASK_BOTH_KEYS_PRESSED) { // Snap click active uint8_t state = 0x01 << (1-index); pState->state_keys = pState->last_single_key = state; switch (p->type) { case SNAP_CLICK_TYPE_REGULAR: unregister_code(p->key[index]); break; case SNAP_CLICK_TYPE_LAST_INPUT: case SNAP_CLICK_TYPE_FIRST_KEY: case SNAP_CLICK_TYPE_SECOND_KEY: if (is_key_pressed(p->key[index])) { unregister_code(p->key[index]); } if (!is_key_pressed(p->key[1-index])) { register_code(p->key[1-index]); } break; case SNAP_CLICK_TYPE_NEUTRAL: register_code(p->key[1-index]); break; } return false; } else { pState->state = 0; } } } } return true; } static bool snap_click_get_info(uint8_t *data) { data[1] = SNAP_CLICK_COUNT; return true; } static bool snap_click_get(uint8_t *data) { uint8_t start = data[0]; uint8_t count = data[1]; if (count > 9 || start + count > SNAP_CLICK_COUNT) return false; memcpy(&data[1], &snap_click_pair[start], count * sizeof(snap_click_config_t)); return true; } static bool snap_click_set(uint8_t *data) { uint8_t start = data[0]; uint8_t count = data[1]; if (count > 9 || start + count > SNAP_CLICK_COUNT) return false; for (uint8_t i=0; i= SNAP_CLICK_TYPE_MAX) return false; if (type != 0 && (keycode1 == 0 || keycode2 == 0)) return false; } memcpy(&snap_click_pair[start], &data[2], count * sizeof(snap_click_config_t)); return true; } static bool snap_click_save(uint8_t *data) { eeprom_update_block(snap_click_pair, (uint8_t *)(EECONFIG_BASE_SNAP_CLICK), sizeof(snap_click_pair)); return true; } void snap_click_rx(uint8_t *data, uint8_t length) { uint8_t cmd = data[1]; bool success = true; switch (cmd) { case SNAP_CLICK_GET_INFO: success = snap_click_get_info(&data[2]); break; case SNAP_CLICK_GET: success = snap_click_get(&data[2]); break; case SNAP_CLICK_SET: success = snap_click_set(&data[2]); break; case SNAP_CLICK_SAVE: success = snap_click_save(&data[2]); break; default: data[0] = 0xFF; break; } data[2] = success ? 0 : 1; } #endif