2022-11-12 00:09:41 +01:00
|
|
|
#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) {
|
2022-11-15 17:52:01 +01:00
|
|
|
register_code(KC_LCTL + osmod);
|
2022-11-12 00:09:41 +01:00
|
|
|
} else {
|
|
|
|
if (current_state == ONESHOT_STATE_OFF) {
|
2022-11-15 17:52:01 +01:00
|
|
|
unregister_code(KC_LCTL + osmod);
|
2022-11-12 00:09:41 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
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
|