218 lines
		
	
	
		
			6.3 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
			
		
		
	
	
			218 lines
		
	
	
		
			6.3 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
| #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() {
 | |
|     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() {
 | |
|     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
 |