#include QMK_KEYBOARD_H #include USERSPACE_H #include "oneshot.h" #ifdef ONESHOT_MOD_ENABLE /* -------------------------------------------- */ // Add to process_record_user. /* int8_t keycode_consumed = 0; */ /* #ifdef ONESHOT_ENABLE */ /* keycode_consumed += update_oneshot_modifiers(keycode, record, keycode_consumed); */ /* #endif */ /* -------------------------------------------- */ #define ONESHOT(KEYCODE, MOD) case KEYCODE: return MOD; #define A_KEY(KEYCODE) case KEYCODE: #define BLANK(...) #define CANCEL_KEY BLANK #define IGNORE_KEY BLANK // the basic states a oneshot modifier can be in typedef enum { ONESHOT_STATE_OFF = 0, ONESHOT_STATE_PRESSED = 1, ONESHOT_STATE_QUEUED = 2, ONESHOT_STATE_CAPSWORD = 3, ONESHOT_STATE_LOCK = 4, ONESHOT_STATE_END_PRESSED = 5, } oneshot_state; oneshot_state modifiers_state_transitions_normal[5] = {ONESHOT_STATE_PRESSED, ONESHOT_STATE_QUEUED, ONESHOT_STATE_LOCK, ONESHOT_STATE_END_PRESSED, ONESHOT_STATE_END_PRESSED}; static oneshot_state modifiers_with_state[ONESHOT_MOD_COUNT] = { ONESHOT_STATE_OFF, ONESHOT_STATE_OFF, ONESHOT_STATE_OFF, ONESHOT_STATE_OFF, ONESHOT_STATE_OFF, ONESHOT_STATE_OFF, ONESHOT_STATE_OFF, ONESHOT_STATE_OFF, }; // oneshot mods always get registered immediately to the operating system, but we also // need to keep track if the mod(s) got combined with a normal key (applied) static bool unapplied_mods_present = false; // keycode of the last pressed 'normal' key which haven't been released yet static uint16_t repeating_normal_key = 0; // utility functions (implemented at the bottom of this file) static void set_modifier_state(oneshot_mod osmod, oneshot_state new_state); static int8_t set_modifier_state_all(oneshot_state new_state); static void set_modifier_state_all_from_to(oneshot_state oneshot_state_from, oneshot_state oneshot_state_to); static bool all_modifiers_are_off(void); int8_t turnoff_oneshot_modifiers(void) { return set_modifier_state_all(ONESHOT_STATE_OFF); } // see comment in corresponding headerfile int8_t update_oneshot_modifiers(uint16_t keycode, keyrecord_t *record, int8_t keycode_consumed) { // cancel keys if (is_oneshot_modifier_cancel_key(keycode) && record->event.pressed) { if (keycode_consumed == 0) { unapplied_mods_present = false; keycode_consumed += set_modifier_state_all(ONESHOT_STATE_OFF); } else { keycode_consumed = 0; } return keycode_consumed; } // ignored keys if (is_oneshot_modifier_ignored_key(keycode)) { return keycode_consumed; } oneshot_mod osmod = get_modifier_for_trigger_key(keycode); // trigger keys if (osmod != ONESHOT_NONE) { oneshot_state state = modifiers_with_state[osmod]; if (record->event.pressed) { if (state == ONESHOT_STATE_OFF) { unapplied_mods_present = (repeating_normal_key == 0); } oneshot_state tostate = modifiers_state_transitions_normal[state]; set_modifier_state(osmod, tostate); } else { if (state == ONESHOT_STATE_PRESSED) { if (!unapplied_mods_present) { set_modifier_state(osmod, ONESHOT_STATE_OFF); } else { set_modifier_state(osmod, ONESHOT_STATE_QUEUED); } } else if (state == ONESHOT_STATE_END_PRESSED) { set_modifier_state(osmod, ONESHOT_STATE_OFF); } } } // normal keys else { if (record->event.pressed) { if (!all_modifiers_are_off()) { if (unapplied_mods_present) { unapplied_mods_present = false; } else { unregister_code(repeating_normal_key); set_modifier_state_all_from_to(ONESHOT_STATE_QUEUED, ONESHOT_STATE_OFF); } } repeating_normal_key = keycode; } else { if (!all_modifiers_are_off()) { unregister_code(keycode); set_modifier_state_all_from_to(ONESHOT_STATE_QUEUED, ONESHOT_STATE_OFF); } repeating_normal_key = 0; } } return 0; } // implementation of utility functions // registers/unregisters a mod to the operating system on state change if necessary void update_modifier(oneshot_mod osmod, oneshot_state previous_state, oneshot_state current_state) { if (previous_state == ONESHOT_STATE_OFF) { register_code(KC_LCTL + osmod); } else { if (current_state == ONESHOT_STATE_OFF) { unregister_code(KC_LCTL + osmod); } } } void set_modifier_state(oneshot_mod osmod, oneshot_state new_state) { oneshot_state previous_state = modifiers_with_state[osmod]; if (previous_state != new_state) { modifiers_with_state[osmod] = new_state; update_modifier(osmod, previous_state, new_state); } } int8_t set_modifier_state_all(oneshot_state new_state) { int8_t c = 0; for (int8_t i = 0; i < ONESHOT_MOD_COUNT; i++) { oneshot_state previous_state = modifiers_with_state[i]; if (previous_state != new_state) { modifiers_with_state[i] = new_state; update_modifier(i, previous_state, new_state); c += 1; } } return c; } void set_modifier_state_all_from_to(oneshot_state oneshot_state_from, oneshot_state oneshot_state_to) { for (int8_t i = 0; i < ONESHOT_MOD_COUNT; i++) { if (modifiers_with_state[i] == oneshot_state_from) { modifiers_with_state[i] = oneshot_state_to; update_modifier(i, oneshot_state_from, oneshot_state_to); } } } bool all_modifiers_are_off(void) { for (int8_t i = 0; i < ONESHOT_MOD_COUNT; i++) { if (modifiers_with_state[i] != ONESHOT_STATE_OFF) { return false; } } return true; } oneshot_mod get_modifier_for_trigger_key(uint16_t keycode) { switch (keycode) { #include "oneshot.def" return true; default: return ONESHOT_NONE; } } // turn off the oneshot macros #undef ONESHOT #define ONESHOT BLANK #define NSHOT BLANK #undef CANCEL_KEY #undef IGNORE_KEY #define CANCEL_KEY A_KEY #define IGNORE_KEY BLANK bool is_oneshot_modifier_cancel_key(uint16_t keycode) { switch (keycode) { #include "oneshot.def" return true; default: return false; } } #undef CANCEL_KEY #undef IGNORE_KEY #define CANCEL_KEY BLANK #define IGNORE_KEY A_KEY bool is_oneshot_modifier_ignored_key(uint16_t keycode) { switch (keycode) { #include "oneshot.def" return true; default: return false; } } #endif