summaryrefslogtreecommitdiff
path: root/users/ericgebhart/extensions/oneshot.c
diff options
context:
space:
mode:
Diffstat (limited to 'users/ericgebhart/extensions/oneshot.c')
-rw-r--r--users/ericgebhart/extensions/oneshot.c217
1 files changed, 217 insertions, 0 deletions
diff --git a/users/ericgebhart/extensions/oneshot.c b/users/ericgebhart/extensions/oneshot.c
new file mode 100644
index 0000000000..83c8a04363
--- /dev/null
+++ b/users/ericgebhart/extensions/oneshot.c
@@ -0,0 +1,217 @@
+#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_LCTRL + osmod);
+ } else {
+ if (current_state == ONESHOT_STATE_OFF) {
+ unregister_code(KC_LCTRL + 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