From 1c39a28ca06cd3a49ba9d98dae5aa2073cb03a0f Mon Sep 17 00:00:00 2001 From: Norbert Csibra Date: Sat, 2 Nov 2024 14:44:58 +0100 Subject: [PATCH] put features to separate files --- .../preonic/rev2/keymaps/ncsibra/keymap.c | 2 + users/ncsibra/ncsibra.c | 74 +--- users/ncsibra/ncsibra.h | 41 +-- users/ncsibra/osl.c | 318 ++++++++++++++++++ users/ncsibra/osl.h | 3 + users/ncsibra/rules.mk | 4 +- users/ncsibra/tap_hold.c | 83 +++++ users/ncsibra/tap_hold.h | 9 + 8 files changed, 440 insertions(+), 94 deletions(-) create mode 100644 users/ncsibra/osl.c create mode 100644 users/ncsibra/osl.h create mode 100644 users/ncsibra/tap_hold.c create mode 100644 users/ncsibra/tap_hold.h diff --git a/keyboards/preonic/rev2/keymaps/ncsibra/keymap.c b/keyboards/preonic/rev2/keymaps/ncsibra/keymap.c index 1f16cbf1..b89926f5 100644 --- a/keyboards/preonic/rev2/keymaps/ncsibra/keymap.c +++ b/keyboards/preonic/rev2/keymaps/ncsibra/keymap.c @@ -15,4 +15,6 @@ const uint16_t PROGMEM keymaps[][MATRIX_ROWS][MATRIX_COLS] = { [_NUM] = LAYOUT_wrapper(NUM_LAYER), [_WOW] = LAYOUT_wrapper(WOW_LAYER), + +[_MOD] = LAYOUT_wrapper(MOD_LAYER), }; diff --git a/users/ncsibra/ncsibra.c b/users/ncsibra/ncsibra.c index 3a5339a2..05755679 100644 --- a/users/ncsibra/ncsibra.c +++ b/users/ncsibra/ncsibra.c @@ -1,20 +1,6 @@ #include "ncsibra.h" - -// Tap hold keys -static taphold_t th_events[] = { - { .is_pressed = false, .is_double = false, .timer = 0, .kc_tap = KC_ESC, .kc_hold = KC_F11 }, - { .is_pressed = false, .is_double = false, .timer = 0, .kc_tap = KC_1, .kc_hold = KC_F1 }, - { .is_pressed = false, .is_double = false, .timer = 0, .kc_tap = KC_2, .kc_hold = KC_F2 }, - { .is_pressed = false, .is_double = false, .timer = 0, .kc_tap = KC_3, .kc_hold = KC_F3 }, - { .is_pressed = false, .is_double = false, .timer = 0, .kc_tap = KC_4, .kc_hold = KC_F4 }, - { .is_pressed = false, .is_double = false, .timer = 0, .kc_tap = KC_5, .kc_hold = KC_F5 }, - { .is_pressed = false, .is_double = false, .timer = 0, .kc_tap = KC_6, .kc_hold = KC_F6 }, - { .is_pressed = false, .is_double = false, .timer = 0, .kc_tap = KC_7, .kc_hold = KC_F7 }, - { .is_pressed = false, .is_double = false, .timer = 0, .kc_tap = KC_8, .kc_hold = KC_F8 }, - { .is_pressed = false, .is_double = false, .timer = 0, .kc_tap = KC_9, .kc_hold = KC_F9 }, - { .is_pressed = false, .is_double = false, .timer = 0, .kc_tap = KC_0, .kc_hold = KC_F10 }, - { .is_pressed = false, .is_double = false, .timer = 0, .kc_tap = KC_BSPC, .kc_hold = KC_F12 }, -}; +#include "osl.h" +#include "tap_hold.h" bool process_record_user(uint16_t keycode, keyrecord_t *record) { // if keycode is ESC or at least contains ESC(in case of Mod Tap etc) and one shot modifier or one shot layer is active, then cancel @@ -25,62 +11,10 @@ bool process_record_user(uint16_t keycode, keyrecord_t *record) { return false; } - return process_record_user_taphold(keycode, record); + return process_record_user_taphold(keycode, record) && + process_record_user_osl(keycode, record); }; -static uint16_t prev_th_key = KC_NO; -static uint16_t prev_th_time = 0; - -bool process_record_user_taphold(uint16_t keycode, keyrecord_t *record) { - if (record->event.pressed) { - matrix_scan_tap_hold(TAPPED); - } - - if (keycode < TH_FIRST || keycode > TH_LAST) { return true; } - - taphold_t *th_event = &th_events[keycode - TH_FIRST]; - - // if pressed twice fast, hold KC_TAP instead of usual logic, Mod Tap functionality works similarly - if (record->event.pressed) { - if (keycode == prev_th_key && timer_elapsed(prev_th_time) < TH_DELAY) { - register_code16(th_event->kc_tap); - th_event->is_double = true; - return false; - } else { - prev_th_key = keycode; - prev_th_time = timer_read(); - } - } - - if (record->event.pressed) { - th_event->timer = timer_read(); - th_event->is_pressed = true; - } else if (th_event->is_pressed) { - register_code16(th_event->kc_tap); - unregister_code16(th_event->kc_tap); - th_event->is_pressed = false; - } else if (th_event->is_double) { - unregister_code16(th_event->kc_tap); - th_event->is_double = false; - } - - return false; -} - -void matrix_scan_tap_hold(taphold_state state) { - for (uint8_t index = 0 ; index < TH_EVENTS_COUNT ; ++index ) { - taphold_t *th_event = &th_events[index]; - if (!th_event->is_pressed) { continue; } - if (state == TAPPED || timer_elapsed(th_event->timer) > TH_DELAY) { - uint16_t code = state == HELD ? th_event->kc_hold : th_event->kc_tap; - register_code16(code); - unregister_code16(code); - th_event->is_pressed = false; - } - } -} - - void matrix_scan_user(void) { matrix_scan_tap_hold(HELD); } diff --git a/users/ncsibra/ncsibra.h b/users/ncsibra/ncsibra.h index 24785b83..cda58e9a 100644 --- a/users/ncsibra/ncsibra.h +++ b/users/ncsibra/ncsibra.h @@ -18,7 +18,8 @@ enum layers { _LOWER, _RAISE, _NUM, - _WOW + _WOW, + _MOD, }; enum keycodes { @@ -41,6 +42,15 @@ enum keycodes { TH_0, TH_BSPC, + // Custom oneshot layer implementation. + OSL_MOD_LAYER, + + // Custom oneshot mods implementation. + OSM_SHFT, + OSM_CTRL, + OSM_ALT, + OSM_GUI, + NEW_SAFE_RANGE }; @@ -81,7 +91,7 @@ enum keycodes { #define RAISE_LAYER \ QK_BOOT, XXXXXXX, XXXXXXX, XXXXXXX, DF(_QWERTY), XXXXXXX, XXXXXXX, DF(_COLEMAK), XXXXXXX, XXXXXXX, XXXXXXX, TO(_WOW), \ DB_TOGG, XXXXXXX, XXXXXXX, KC_LSFT, XXXXXXX, XXXXXXX, XXXXXXX, KC_HOME, KC_UP, KC_END, XXXXXXX, KC_DEL, \ - _______, KC_LGUI, KC_LALT, XXXXXXX, KC_LCTL, XXXXXXX, XXXXXXX, KC_LEFT, KC_DOWN, KC_RIGHT, XXXXXXX, _______, \ + _______, KC_LGUI, KC_LALT, KC_LSFT, KC_LCTL, XXXXXXX, XXXXXXX, KC_LEFT, KC_DOWN, KC_RIGHT, XXXXXXX, _______, \ KC_CAPS, _______, _______, _______, _______, _______, XXXXXXX, KC_PGUP, XXXXXXX, KC_PGDN, XXXXXXX, _______, \ _______, _______, _______, _______, _______, LCTL(KC_SPC), _______, _______, _______, TG(_RAISE), _______, _______ @@ -101,25 +111,10 @@ enum keycodes { KC_LSFT, KC_Z, KC_X, KC_N, KC_C, KC_V, KC_N, KC_M, KC_COMM, KC_DOT, KC_SLSH, KC_RSFT, \ KC_M, KC_Y, KC_LALT, KC_LCTL, KC_SPC, KC_P, KC_BSPC, KC_DEL, KC_DEL, KC_LGUI, KC_PSCR, TO(_COLEMAK) -// tap-hold settings -#define TH_DELAY 300 -#define TH_EVENTS_COUNT 12 -#define TH_FIRST TH_ESC -#define TH_LAST TH_BSPC - -typedef struct { - bool is_pressed; - bool is_double; - uint16_t timer; - uint16_t kc_tap; - uint16_t kc_hold; -} taphold_t; - -typedef enum { - TAPPED, - HELD, -} taphold_state; - -void matrix_scan_tap_hold(taphold_state state); -bool process_record_user_taphold(uint16_t keycode, keyrecord_t *record); +#define MOD_LAYER \ + _______, _______, _______, _______, _______, _______, _______, _______, _______, _______, _______, _______, \ + _______, _______, _______, OSM_SHFT, _______, _______, _______, _______, _______, _______, _______, _______, \ + _______, OSM_GUI, OSM_ALT, OSM_SHFT, OSM_CTRL, _______, _______, _______, _______, _______, _______, _______, \ + _______, _______, _______, _______, _______, _______, _______, _______, _______, _______, _______, _______, \ + _______, _______, _______, _______, _______, _______, _______, _______, _______, _______, _______, OSL_MOD_LAYER diff --git a/users/ncsibra/osl.c b/users/ncsibra/osl.c new file mode 100644 index 00000000..b561224c --- /dev/null +++ b/users/ncsibra/osl.c @@ -0,0 +1,318 @@ +#include QMK_KEYBOARD_H +#include "ncsibra.h" + +// copied from https://github.com/Exidex/qmk_firmware/blob/custom/keyboards/beekeeb/piantor_pro/readme.md + +// ================ mostly callum's oneshot mods + +typedef enum { + osl_up_unqueued, // default, waiting for layer to be pressed + osl_up_queued, // layer pressed and released without pressing mod key, next modifier press will have layer enabled, on all mod release layer will be disabled + osl_up_pending_used, // layer was pressed but released when some mods were still held, on all mod release layer will be disabled + osl_down_unused, // layer pressed and held, all mod presses will have layer enabled, until all mods are released + osl_down_pending_used, // layer pressed and held, some mods are still pressed + osl_down_used, // mods were pressed but layer is still held, on layer release layer will be disabled +} oneshot_layer_state; + +typedef enum { + osm_up_unqueued, // default, waiting for mod to be pressed + osm_down_unused, // mod pressed and held, all other presses will be with this modifier enabled, until mod released + osm_down_used, // other key pressed while mod is held, on mod release modifier will be disabled + osm_up_queued, // mod pressed and released without pressing other key, next press will have modifier enabled + osm_up_queued_with_layer, // other key pressed abd released while layer and mod are active, next presses will have modifier enabled until layer is released +} oneshot_mod_state; + +oneshot_mod_state osm_shift_state = osm_up_unqueued; +oneshot_mod_state osm_ctrl_state = osm_up_unqueued; +oneshot_mod_state osm_alt_state = osm_up_unqueued; +oneshot_mod_state osm_gui_state = osm_up_unqueued; + +bool is_oneshot_cancel_key(uint16_t keycode) { + switch (keycode) { + case MO(_LOWER): + case TT(_RAISE): + return true; + default: + return false; + } +} + +bool is_oneshot_ignored_key(uint16_t keycode) { + switch (keycode) { + case OSL_MOD_LAYER: + case OSM_SHFT: + case OSM_CTRL: + case OSM_ALT: + case OSM_GUI: + return true; + default: + return false; + } +} + +void update_oneshot_mod( + oneshot_layer_state *layer_state, + oneshot_mod_state *mod_state, + uint16_t mod, + uint16_t trigger, + uint16_t keycode, + keyrecord_t *record +) { + if (keycode == trigger) { + if (record->event.pressed) { + // Trigger keydown + if (*mod_state == osm_up_unqueued) { + register_code(mod); + } + *mod_state = osm_down_unused; + } else { + // Trigger keyup + switch (*mod_state) { + case osm_down_unused: + // If we didn't use the mod while trigger was held, queue it. + *mod_state = osm_up_queued; + break; + case osm_down_used: + // If we did use the mod while trigger was held, unregister it. + *mod_state = osm_up_unqueued; + unregister_code(mod); +// uprintf("0x%04X unregister_code up trigger\n", keycode); + break; + default: + break; + } + } + } else { + if (record->event.pressed) { + if (is_oneshot_cancel_key(keycode) && *mod_state != osm_up_unqueued) { + // Cancel oneshot on designated cancel keydown. + *mod_state = osm_up_unqueued; + unregister_code(mod); +// uprintf("0x%04X unregister_code down non-trigger\n", keycode); + } + } else { + if (!is_oneshot_ignored_key(keycode)) { + // On non-ignored keyup, mark the oneshot as used. + switch (*mod_state) { + case osm_down_unused: + *mod_state = osm_down_used; + break; + case osm_up_queued: + switch (*layer_state) { + case osl_up_pending_used: // because some other mod is still pressed + case osl_down_unused: + case osl_down_pending_used: + case osl_down_used: + *mod_state = osm_up_queued_with_layer; + break; + default: + *mod_state = osm_up_unqueued; + unregister_code(mod); + break; + } + break; + default: + break; + } + } + } + } +} + +// ================ my oneshot layers + +oneshot_layer_state osl_mod_state = osl_up_unqueued; + +uint16_t pressed_one_shot_mods = 0; + +#define CUSTOM_ONE_SHOT_MOD_GET_MODS(kc) ((kc)&0x1F) + +bool is_oneshot_mod_key(uint16_t keycode) { + switch (keycode) { + case OSM_SHFT: + case OSM_CTRL: + case OSM_ALT: + case OSM_GUI: + return true; + default: + return false; + } +} + +void update_oneshot_layer( + oneshot_layer_state *layer_state, + oneshot_mod_state *shift_state, + oneshot_mod_state *ctrl_state, + oneshot_mod_state *alt_state, + oneshot_mod_state *gui_state, + uint16_t trigger, + uint16_t layer, + uint16_t keycode, + keyrecord_t *record +) { + if (keycode == trigger) { + if (record->event.pressed) { + if (*layer_state == osl_up_unqueued) { + layer_on(layer); + } + *layer_state = osl_down_unused; + } else { + switch (*layer_state) { + case osl_down_unused: + *layer_state = osl_up_queued; + break; + case osl_down_used: + *layer_state = osl_up_unqueued; + layer_off(layer); + + { + if (*shift_state == osm_up_queued_with_layer) { + *shift_state = osm_up_unqueued; + unregister_code(KC_LSFT); + } + if (*ctrl_state == osm_up_queued_with_layer) { + *ctrl_state = osm_up_unqueued; + unregister_code(KC_LCTL); + } + if (*alt_state == osm_up_queued_with_layer) { + *alt_state = osm_up_unqueued; + unregister_code(KC_LALT); + } + if (*gui_state == osm_up_queued_with_layer) { + *gui_state = osm_up_unqueued; + unregister_code(KC_LGUI); + } + } + + break; + case osl_down_pending_used: + *layer_state = osl_up_pending_used; + break; + default: + break; + } + } + } else { + if (record->event.pressed) { + if (is_oneshot_mod_key(keycode)) { + pressed_one_shot_mods |= CUSTOM_ONE_SHOT_MOD_GET_MODS(keycode); + } + } else { + if (is_oneshot_mod_key(keycode)) { + pressed_one_shot_mods &= CUSTOM_ONE_SHOT_MOD_GET_MODS(~(CUSTOM_ONE_SHOT_MOD_GET_MODS(keycode))); + } + + switch (*layer_state) { + case osl_down_pending_used: + case osl_down_unused: + if (is_oneshot_mod_key(keycode)) { + if (pressed_one_shot_mods) { + *layer_state = osl_down_pending_used; + } else { + *layer_state = osl_down_used; + layer_off(layer); + } + } + break; + case osl_up_queued: + case osl_up_pending_used: + if (is_oneshot_mod_key(keycode)) { + if (pressed_one_shot_mods) { + *layer_state = osl_up_pending_used; + } else { + *layer_state = osl_up_unqueued; + layer_off(layer); + } + } + break; + default: + break; + } + } + } +} + +// ============== + +//const char* oneshot_layer_state_string(oneshot_layer_state value) { +// switch (value) { +// case osl_up_unqueued: return "osl_up_unqueued"; +// case osl_up_queued: return "osl_up_queued"; +// case osl_up_pending_used: return "osl_up_pending_used"; +// case osl_down_unused: return "osl_down_unused"; +// case osl_down_pending_used: return "osl_down_pending_used"; +// case osl_down_used: return "osl_down_used"; +// default: return "-----"; +// } +//} +// +//const char* oneshot_mod_state_string(oneshot_mod_state value) { +// switch (value) { +// case osm_up_unqueued: return "osm_up_unqueued"; +// case osm_down_unused: return "osm_down_unused"; +// case osm_down_used: return "osm_down_used"; +// case osm_up_queued: return "osm_up_queued"; +// case osm_up_queued_with_layer: return "osm_up_queued_with_layer"; +// default: return "-----"; +// } +//} + +bool process_record_user_osl(uint16_t keycode, keyrecord_t *record) { +// uprintf("\n"); +// uprintf("\n"); +// +// uprintf("%s %s %s %s %s \n", oneshot_layer_state_string(osl_mod_state), oneshot_mod_state_string(osm_shift_state), oneshot_mod_state_string(osm_ctrl_state), oneshot_mod_state_string(osm_alt_state), oneshot_mod_state_string(osm_gui_state)); + + update_oneshot_mod( + &osl_mod_state, + &osm_shift_state, + KC_LSFT, + OSM_SHFT, + keycode, + record + ); + + update_oneshot_mod( + &osl_mod_state, + &osm_ctrl_state, + KC_LCTL, + OSM_CTRL, + keycode, + record + ); + + update_oneshot_mod( + &osl_mod_state, + &osm_alt_state, + KC_LALT, + OSM_ALT, + keycode, + record + ); + + update_oneshot_mod( + &osl_mod_state, + &osm_gui_state, + KC_LGUI, + OSM_GUI, + keycode, + record + ); + + + update_oneshot_layer( + &osl_mod_state, + &osm_shift_state, + &osm_ctrl_state, + &osm_alt_state, + &osm_gui_state, + OSL_MOD_LAYER, + _MOD, + keycode, + record + ); + +// uprintf("%s %s %s %s %s \n", oneshot_layer_state_string(osl_mod_state), oneshot_mod_state_string(osm_shift_state), oneshot_mod_state_string(osm_ctrl_state), oneshot_mod_state_string(osm_alt_state), oneshot_mod_state_string(osm_gui_state)); + + return true; +} \ No newline at end of file diff --git a/users/ncsibra/osl.h b/users/ncsibra/osl.h new file mode 100644 index 00000000..df5d93fe --- /dev/null +++ b/users/ncsibra/osl.h @@ -0,0 +1,3 @@ +#pragma once + +bool process_record_user_osl(uint16_t keycode, keyrecord_t *record); \ No newline at end of file diff --git a/users/ncsibra/rules.mk b/users/ncsibra/rules.mk index 0a924986..7344c280 100644 --- a/users/ncsibra/rules.mk +++ b/users/ncsibra/rules.mk @@ -1,4 +1,6 @@ -SRC += ncsibra.c +SRC += ncsibra.c \ + osl.c \ + tap_hold.c CONSOLE_ENABLE = yes NKRO_ENABLE = yes diff --git a/users/ncsibra/tap_hold.c b/users/ncsibra/tap_hold.c new file mode 100644 index 00000000..530f0dc7 --- /dev/null +++ b/users/ncsibra/tap_hold.c @@ -0,0 +1,83 @@ +#include QMK_KEYBOARD_H +#include "ncsibra.h" +#include "tap_hold.h" + +#define TH_DELAY 300 +#define TH_EVENTS_COUNT 12 +#define TH_FIRST TH_ESC +#define TH_LAST TH_BSPC + +typedef struct { + bool is_pressed; + bool is_double; + uint16_t timer; + uint16_t kc_tap; + uint16_t kc_hold; +} taphold_t; + +static uint16_t prev_th_key = KC_NO; +static uint16_t prev_th_time = 0; + +static taphold_t th_events[] = { + { .is_pressed = false, .is_double = false, .timer = 0, .kc_tap = KC_ESC, .kc_hold = KC_F11 }, + { .is_pressed = false, .is_double = false, .timer = 0, .kc_tap = KC_1, .kc_hold = KC_F1 }, + { .is_pressed = false, .is_double = false, .timer = 0, .kc_tap = KC_2, .kc_hold = KC_F2 }, + { .is_pressed = false, .is_double = false, .timer = 0, .kc_tap = KC_3, .kc_hold = KC_F3 }, + { .is_pressed = false, .is_double = false, .timer = 0, .kc_tap = KC_4, .kc_hold = KC_F4 }, + { .is_pressed = false, .is_double = false, .timer = 0, .kc_tap = KC_5, .kc_hold = KC_F5 }, + { .is_pressed = false, .is_double = false, .timer = 0, .kc_tap = KC_6, .kc_hold = KC_F6 }, + { .is_pressed = false, .is_double = false, .timer = 0, .kc_tap = KC_7, .kc_hold = KC_F7 }, + { .is_pressed = false, .is_double = false, .timer = 0, .kc_tap = KC_8, .kc_hold = KC_F8 }, + { .is_pressed = false, .is_double = false, .timer = 0, .kc_tap = KC_9, .kc_hold = KC_F9 }, + { .is_pressed = false, .is_double = false, .timer = 0, .kc_tap = KC_0, .kc_hold = KC_F10 }, + { .is_pressed = false, .is_double = false, .timer = 0, .kc_tap = KC_BSPC, .kc_hold = KC_F12 }, +}; + +bool process_record_user_taphold(uint16_t keycode, keyrecord_t *record) { + if (record->event.pressed) { + matrix_scan_tap_hold(TAPPED); + } + + if (keycode < TH_FIRST || keycode > TH_LAST) { return true; } + + taphold_t *th_event = &th_events[keycode - TH_FIRST]; + + // if pressed twice fast, hold KC_TAP instead of usual logic, Mod Tap functionality works similarly + if (record->event.pressed) { + if (keycode == prev_th_key && timer_elapsed(prev_th_time) < TH_DELAY) { + register_code16(th_event->kc_tap); + th_event->is_double = true; + return false; + } else { + prev_th_key = keycode; + prev_th_time = timer_read(); + } + } + + if (record->event.pressed) { + th_event->timer = timer_read(); + th_event->is_pressed = true; + } else if (th_event->is_pressed) { + register_code16(th_event->kc_tap); + unregister_code16(th_event->kc_tap); + th_event->is_pressed = false; + } else if (th_event->is_double) { + unregister_code16(th_event->kc_tap); + th_event->is_double = false; + } + + return false; +} + +void matrix_scan_tap_hold(taphold_state state) { + for (uint8_t index = 0 ; index < TH_EVENTS_COUNT ; ++index ) { + taphold_t *th_event = &th_events[index]; + if (!th_event->is_pressed) { continue; } + if (state == TAPPED || timer_elapsed(th_event->timer) > TH_DELAY) { + uint16_t code = state == HELD ? th_event->kc_hold : th_event->kc_tap; + register_code16(code); + unregister_code16(code); + th_event->is_pressed = false; + } + } +} \ No newline at end of file diff --git a/users/ncsibra/tap_hold.h b/users/ncsibra/tap_hold.h new file mode 100644 index 00000000..b2eacc2e --- /dev/null +++ b/users/ncsibra/tap_hold.h @@ -0,0 +1,9 @@ +#pragma once + +typedef enum { + TAPPED, + HELD, +} taphold_state; + +void matrix_scan_tap_hold(taphold_state state); +bool process_record_user_taphold(uint16_t keycode, keyrecord_t *record); \ No newline at end of file