diff options
Diffstat (limited to 'jack/tap_code.c')
-rw-r--r-- | jack/tap_code.c | 159 |
1 files changed, 159 insertions, 0 deletions
diff --git a/jack/tap_code.c b/jack/tap_code.c new file mode 100644 index 0000000..a430200 --- /dev/null +++ b/jack/tap_code.c @@ -0,0 +1,159 @@ +// Copyright 2025 [email protected] +// SPDX-License-Identifier: GPL-2.0-or-later +#include "jack.h" + +#define TAP_CODE_BUFFER_SIZE 32 +#define NEXT_TAP_CODE_POS(p) p = ((p + 1) & (TAP_CODE_BUFFER_SIZE - 1)) + +#define CHECK_REGISTERED_MT(keycode) \ + (keycode >= QK_MOD_TAP && keycode <= QK_MOD_TAP_MAX && \ + (get_mods() & ((keycode & 0x0F00) >> 8))) + +#define PGM_LOADBIT(mem, pos) \ + ((pgm_read_byte(&((mem)[(pos) / 8])) >> ((pos) % 8)) & 1) + +typedef struct { + uint8_t code; + uint8_t mods; + uint16_t delay; + bool register_tap; +} tap_code_buffer; + +tap_code_buffer tap_codes[TAP_CODE_BUFFER_SIZE] = {0}; +static uint16_t process_pos = 0, queue_pos = 0; +static fast_timer_t process_timer = 0; + +void tap_code_buffer_init(void) { memset(&tap_codes, 0, sizeof(tap_codes)); } + +void tap_code_register(uint8_t code, uint8_t mods, uint16_t delay, + bool register_tap) { + if (code && !delay) { + if (mods) + register_mods(mods); + register_code(code); + unregister_code(code); + if (mods) + unregister_mods(mods); + return; + } + if (tap_codes[queue_pos].code) { + if (!process_timer) + process_tap_code_buffer(); + while (timer_elapsed_fast(process_timer) < tap_codes[process_pos].delay) + wait_ms(1); + process_tap_code_buffer(); + } + tap_codes[queue_pos].code = code; + tap_codes[queue_pos].mods = mods; + tap_codes[queue_pos].delay = delay; + tap_codes[queue_pos].register_tap = register_tap; + NEXT_TAP_CODE_POS(queue_pos); +} + +void process_tap_code_buffer(void) { + if (tap_codes[process_pos].code) { + if (!process_timer) { + if (tap_codes[process_pos].register_tap) { + if (tap_codes[process_pos].mods) { + add_weak_mods(tap_codes[process_pos].mods); + } + register_code(tap_codes[process_pos].code); + } + process_timer = timer_read_fast(); + } else if (timer_elapsed_fast(process_timer) >= + tap_codes[process_pos].delay) { + del_weak_mods(tap_codes[process_pos].mods); + unregister_code(tap_codes[process_pos].code); + tap_codes[process_pos].code = 0; + tap_codes[process_pos].mods = 0; + tap_codes[process_pos].delay = 0; + tap_codes[process_pos].register_tap = false; + process_timer = 0; + NEXT_TAP_CODE_POS(process_pos); + } + } +} + +void tap_code(uint8_t code) { + tap_code_register(code, 0, TAP_CODE_DELAY, true); +} + +void tap_code_chord(uint8_t code) { + register_code(code); + tap_code_register(code, 0, TAP_CODE_DELAY, false); +} + +void tap_code_delay(uint8_t code, uint16_t delay) { + tap_code_register(code, 0, delay, true); +} + +void tap_code16(uint16_t code) { + tap_code_register((uint8_t)code, extract_mod_bits(code), TAP_CODE_DELAY, + true); +} + +bool process_record_taps(uint16_t keycode, keyrecord_t *record) { + static fast_timer_t prev_interrupt = 0, prev_key_down = 0; + switch (keycode) { + case QK_LAYER_TAP ... QK_LAYER_TAP_MAX: + case QK_MOD_TAP ... QK_MOD_TAP_MAX: + if (record->event.pressed && + ((timer_elapsed_fast(prev_interrupt) < TAPPING_TERM) || + (timer_elapsed_fast(prev_key_down) < 200) || + CHECK_REGISTERED_MT(keycode))) { + tap_code_chord(keycode & 0xFF); + record->tap.count = 0xF; + prev_interrupt = timer_read_fast(); + return false; + } + if (record->tap.count == 0xF) + return false; + if (record->tap.count > 1 && (keycode == ESCLWR || keycode == RSEBSP)) { + if (record->event.pressed) { + register_code(keycode & 0xFF); + return false; + } else { + unregister_code(keycode & 0xFF); + return false; + } + } + if (record->tap.count && record->event.pressed) { + if (keycode >= QK_MOD_TAP || keycode == ESCLWR || keycode == RSEBSP) + tap_code_chord(keycode & 0xFF); + else + tap_code(keycode & 0xFF); + return false; + } else if (record->tap.interrupted && + (keycode >= QK_MOD_TAP || keycode == ESCLWR || + keycode == RSEBSP)) { + tap_code_chord(keycode & 0xFF); + prev_interrupt = timer_read_fast(); + return false; + } + default: + if (record->event.pressed) + prev_key_down = timer_read_fast(); + break; + } + return true; +} + +extern const uint8_t ascii_to_shift_lut[16]; +extern const uint8_t ascii_to_keycode_lut[128]; + +void tap_char(char ascii_code) { + uint8_t keycode = pgm_read_byte(&ascii_to_keycode_lut[(uint8_t)ascii_code]); + bool shifted = PGM_LOADBIT(ascii_to_shift_lut, (uint8_t)ascii_code); + tap_code_register(keycode, shifted ? MOD_BIT(KC_LSFT) : 0, TAP_CODE_DELAY, + true); +} + +void tap_string(const char *str) { + while (1) { + char ascii_code = pgm_read_byte(str); + if (!ascii_code) + break; + tap_char(ascii_code); + ++str; + } +} |