202 lines
6.5 KiB
C
202 lines
6.5 KiB
C
/* 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 <http://www.gnu.org/licenses/>.
|
|
*/
|
|
|
|
#include <string.h>
|
|
#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; i<SNAP_CLICK_COUNT; i++)
|
|
{
|
|
snap_click_config_t *p = &snap_click_pair[i];
|
|
|
|
if (p->type && (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<count; i++) {
|
|
uint8_t offset = 2+sizeof(snap_click_config_t)*i;
|
|
uint8_t type = data[offset];
|
|
uint8_t keycode1 = data[offset+1];
|
|
uint8_t keycode2 = data[offset+2];
|
|
|
|
if (type >= 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
|