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(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
 |