summaryrefslogtreecommitdiffstats
path: root/keyboards/handwired/hexon38/keymaps/default/keymap.c
diff options
context:
space:
mode:
authorJason Pepas <jasonpepas@gmail.com>2019-01-12 23:30:19 -0600
committerDrashna Jaelre <drashna@live.com>2019-01-12 21:30:19 -0800
commit9ef46494b2412061a8e0fff6f10d50e90a7152b0 (patch)
treede39159f46ec340285c00b1719b18347c4f732ea /keyboards/handwired/hexon38/keymaps/default/keymap.c
parentbaaa138e90e5512ac58de7b627d537e6ce4c1197 (diff)
[Keyboard] hexon38 and Dual-role key implementation (#4709)
* initial dual-role key implementation for hexon38 * PR feedback, adding README * Moving to handwired subdir * Additional PR feedback
Diffstat (limited to 'keyboards/handwired/hexon38/keymaps/default/keymap.c')
-rw-r--r--keyboards/handwired/hexon38/keymaps/default/keymap.c407
1 files changed, 407 insertions, 0 deletions
diff --git a/keyboards/handwired/hexon38/keymaps/default/keymap.c b/keyboards/handwired/hexon38/keymaps/default/keymap.c
new file mode 100644
index 0000000000..c3805991f0
--- /dev/null
+++ b/keyboards/handwired/hexon38/keymaps/default/keymap.c
@@ -0,0 +1,407 @@
+// see https://github.com/pepaslabs/hexon38
+
+#include "hexon38.h"
+
+#define A_ KC_A
+#define B_ KC_B
+#define C_ KC_C
+#define D_ KC_D
+#define E_ KC_E
+#define F_ KC_F
+#define G_ KC_G
+#define H_ KC_H
+#define I_ KC_I
+#define J_ KC_J
+#define K_ KC_K
+#define L_ KC_L
+#define M_ KC_M
+#define N_ KC_N
+#define O_ KC_O
+#define P_ KC_P
+#define Q_ KC_Q
+#define R_ KC_R
+#define S_ KC_S
+#define T_ KC_T
+#define U_ KC_U
+#define V_ KC_V
+#define W_ KC_W
+#define X_ KC_X
+#define Y_ KC_Y
+#define Z_ KC_Z
+
+// Dual-role keys: modifier when held, alpha when tapped.
+#define A_CTL CTL_T(KC_A)
+#define S_ALT ALT_T(KC_S)
+#define D_GUI GUI_T(KC_D)
+#define F_SFT SFT_T(KC_F)
+#define J_SFT SFT_T(KC_J)
+#define K_GUI GUI_T(KC_K)
+#define L_ALT ALT_T(KC_L)
+#define COLN_CTL CTL_T(KC_SCLN)
+
+#define ______ KC_TRNS
+#define LSHIFT KC_LSHIFT
+#define RSHIFT KC_RSHIFT
+#define COMMA KC_COMM
+#define SLASH KC_SLSH
+#define SPACE KC_SPC
+#define TAB KC_TAB
+#define BKSPC KC_BSPC
+#define ENTER KC_ENT
+#define PERIOD KC_DOT
+
+#define BASE_LAYER LAYOUT
+#define BLANK_LAYER LAYOUT
+
+
+const uint16_t PROGMEM keymaps[][MATRIX_ROWS][MATRIX_COLS] = {
+
+ BASE_LAYER(
+// ,--------+--------+--------+--------. ,--------+--------+--------+--------.
+ W_ , E_ , R_ , T_ , Y_ , U_ , I_ , O_ ,
+//|--------+--------+--------+--------+--------+--------| |--------+--------+--------+--------+--------+--------.
+ Q_ , A_CTL , S_ALT , D_GUI , F_SFT , G_ , H_ , J_SFT , K_GUI , L_ALT ,COLN_CTL, P_ ,
+//|--------+--------+--------+--------+--------+--------' `--------+--------+--------+--------+--------+--------|
+ B_ , Z_ , X_ , C_ , V_ , M_ , COMMA , PERIOD , SLASH , N_ ,
+//`--------+--------+--------+--------+--------' `--------+--------+--------+--------+--------'
+
+// ,--------+--------+--------+--------. ,--------+--------+--------+--------.
+ LSHIFT , SPACE , TAB , DEBUG , SPACE , BKSPC , ENTER , RSHIFT
+// `--------+--------+--------+--------' `--------+--------+--------+--------'
+),
+
+ BLANK_LAYER(
+// ,--------+--------+--------+--------. ,--------+--------+--------+--------.
+ ______ , ______ , ______ , ______ , ______ , ______ , ______ , ______ ,
+//|--------+--------+--------+--------+--------+--------| |--------+--------+--------+--------+--------+--------.
+ ______ , ______ , ______ , ______ , ______ , ______ , ______ , ______ , ______ , ______ , ______ , ______ ,
+//|--------+--------+--------+--------+--------+--------' `--------+--------+--------+--------+--------+--------|
+ ______ , ______ , ______ , ______ , ______ , ______ , ______ , ______ , ______ , ______ ,
+//`--------+--------+--------+--------+--------' `--------+--------+--------+--------+--------'
+
+// ,--------+--------+--------+--------. ,--------+--------+--------+--------.
+ ______ , ______ , ______ , ______ , ______ , ______ , ______ , ______
+// `--------+--------+--------+--------' `--------+--------+--------+--------'
+)
+
+};
+
+// a linked list of pending key events (press or release) which we haven't processed yet.
+struct _pending_key_t {
+ uint16_t keycode;
+ keyrecord_t record;
+ struct _pending_key_t *next;
+};
+typedef struct _pending_key_t pending_key_t;
+
+// worst case is 10 down strokes and 1 up stroke before we can start disambiguating.
+#define RINGSIZE 11
+
+// a ring buffer and linked list to store pending key events (presses and releases).
+// (basically, this is a fixed-allocation linked list.)
+struct _kring_t {
+ // the actual key events.
+ pending_key_t items[RINGSIZE];
+ // the index of the oldest item, or -1 if no items.
+ int8_t ifirst;
+ // the index of the most recently added item, or -1 if no items.
+ int8_t ilast;
+ // the number of items in the ring.
+ uint8_t count;
+ // the head of the linked list.
+ pending_key_t *head;
+};
+typedef struct _kring_t kring_t;
+
+// safe accessor to the i-th item of the linked list (returns pointer or NULL).
+pending_key_t* kring_get(kring_t *ring, uint8_t i) {
+ if (i >= ring->count) {
+ return NULL;
+ }
+ uint8_t j = (ring->ifirst + i) % RINGSIZE;
+ return &(ring->items[j]);
+}
+
+// return the last key in the list of buffered keys.
+pending_key_t* kring_last(kring_t *ring) {
+ if (ring->count == 0) {
+ return NULL;
+ }
+ return kring_get(ring, ring->count - 1);
+}
+
+// remove the oldest item from the ring (the head of the list).
+void kring_pop(kring_t *ring) {
+ if (ring->count > 0) {
+ ring->ifirst += 1;
+ ring->ifirst %= RINGSIZE;
+ ring->head = ring->head->next;
+ ring->count -= 1;
+ }
+}
+
+// add an item to the ring (append to the list).
+void kring_append(kring_t *ring, uint16_t keycode, keyrecord_t *record) {
+ if (ring->count >= RINGSIZE) {
+ // uh oh, we overflowed the capacity of our buffer :(
+ return;
+ }
+
+ // if the ring is empty, insert at index 0.
+ if (ring->count == 0) {
+ ring->count += 1;
+ ring->ifirst = 0;
+ ring->ilast = 0;
+ ring->head = &(ring->items[0]);
+ }
+ // else, append it onto the end.
+ else {
+ ring->count += 1;
+ ring->ilast += 1;
+ ring->ilast %= RINGSIZE;
+ }
+
+ // the index at which we should insert this item.
+ int8_t i = ring->ilast;
+
+ // insert the item.
+ ring->items[i].keycode = keycode;
+ ring->items[i].record.event = record->event;
+#ifndef NO_ACTION_TAPPING
+ ring->items[i].record.tap = record->tap;
+#endif
+ ring->items[i].next = NULL;
+
+ // update the previous item to point to this item.
+ if (ring->count > 1) {
+ kring_get(ring, ring->count - 2)->next = &(ring->items[i]);
+ }
+}
+
+kring_t g_pending;
+
+void matrix_init_user(void) {
+ g_pending.ifirst = -1;
+ g_pending.ilast = -1;
+ g_pending.count = 0;
+ g_pending.head = NULL;
+}
+
+void matrix_scan_user(void) {}
+
+/*
+a_ a-: emit a
+a_ b_ b- a-: emit SHIFT+b
+a_ b_ a- b-: emit a, b
+dual1down, dual1up -> norm1down, norm1up
+dual1down, norm2down, norm2up -> mod1down, norm2down, norm2up
+dual1down, norm2down, dual1up -> norm1down, norm2down, norm1up
+dual1down, dual2down, norm3down, norm3up -> mod1down, mod2down, norm3down, norm3up
+so, a dual key can't be disambiguated until the next keyup of a keydown (not including keyups from keys before it).
+*/
+
+bool is_ambiguous_kc(uint16_t kc) {
+ // See the MT() define: https://github.com/qmk/qmk_firmware/blob/master/quantum/quantum_keycodes.h#L642
+ // See the QK_MOD_TAP case: https://github.com/qmk/qmk_firmware/blob/master/quantum/keymap_common.c#L134
+ uint8_t mod = mod_config((kc >> 0x8) & 0x1F);
+ return mod != 0;
+}
+
+bool is_down(pending_key_t *k) {
+ return k->record.event.pressed;
+}
+
+bool is_up(pending_key_t *k) {
+ return !is_down(k);
+}
+
+bool keys_match(pending_key_t *a, pending_key_t *b) {
+ return a->record.event.key.col == b->record.event.key.col
+ && a->record.event.key.row == b->record.event.key.row;
+}
+
+// both the down and corresponding upstroke of a keypress.
+struct _pending_pair_t {
+ pending_key_t *down;
+ pending_key_t *up;
+};
+typedef struct _pending_pair_t pending_pair_t;
+
+// returns true if this keydown event has a corresponding keyup event in the
+// list of buffered keys. also fills out 'p'.
+bool is_downup_pair(pending_key_t *k, pending_pair_t *p) {
+ // first, make sure this event is keydown.
+ if (!is_down(k)) {
+ return false;
+ }
+ // now find its matching keyup.
+ pending_key_t *next = k->next;
+ while (next != NULL) {
+ if (keys_match(k, next) && is_up(next)) {
+ // found it.
+ if (p != NULL) {
+ p->down = k;
+ p->up = next;
+ }
+ return true;
+ }
+ next = next->next;
+ }
+ // didn't find it.
+ return false;
+}
+
+// given a QK_MOD_TAP keycode, return the KC_* version of the modifier keycode.
+uint16_t get_mod_kc(uint16_t keycode) {
+ uint8_t mod = mod_config((keycode >> 0x8) & 0x1F);
+ switch (mod) {
+ case MOD_LCTL:
+ return KC_LCTL;
+ case MOD_RCTL:
+ return KC_RCTL;
+ case MOD_LSFT:
+ return KC_LSFT;
+ case MOD_RSFT:
+ return KC_RSFT;
+ case MOD_LALT:
+ return KC_LALT;
+ case MOD_RALT:
+ return KC_RALT;
+ case MOD_LGUI:
+ return KC_LGUI;
+ case MOD_RGUI:
+ return KC_RGUI;
+ default:
+ // shrug? this shouldn't happen.
+ return keycode;
+ }
+}
+
+bool is_mod_kc(uint16_t keycode) {
+ switch (keycode) {
+ case QK_MODS ... QK_MODS_MAX:
+ return true;
+ default:
+ return false;
+ }
+}
+
+void interpret_as_mod(pending_pair_t *p) {
+ // see https://github.com/qmk/qmk_firmware/issues/1503
+ pending_key_t *k;
+ k = p->down;
+ if (k != NULL) {
+ k->keycode = get_mod_kc(k->keycode);
+ }
+ k = p->up;
+ if (k != NULL) {
+ k->keycode = get_mod_kc(k->keycode);
+ }
+}
+
+void interpret_as_normal(pending_pair_t *p) {
+ pending_key_t *k;
+ k = p->down;
+ if (k != NULL) {
+ k->keycode = k->keycode & 0xFF;
+ }
+ k = p->up;
+ if (k != NULL) {
+ k->keycode = k->keycode & 0xFF;
+ }
+}
+
+void execute_head_and_pop(kring_t *ring) {
+ pending_key_t *head = kring_get(ring, 0);
+ uint16_t kc = head->keycode;
+ if (is_mod_kc(kc)) {
+ if (is_down(head)) {
+ dprintf(" %s: mod down 0x%04X\n", __func__, kc);
+ set_mods(get_mods() | MOD_BIT(kc));
+ } else {
+ dprintf(" %s: mod up 0x%04X\n", __func__, kc);
+ set_mods(get_mods() & ~MOD_BIT(kc));
+ }
+ } else {
+ if (is_down(head)) {
+ dprintf(" %s: key down 0x%04X\n", __func__, kc);
+ register_code16(kc);
+ } else {
+ dprintf(" %s: key up 0x%04X\n", __func__, kc);
+ unregister_code16(kc);
+ }
+ }
+ kring_pop(ring);
+}
+
+// try to figure out what the next pending keypress means.
+bool parse_next(kring_t *pending) {
+ pending_pair_t p;
+ pending_key_t *first = kring_get(pending, 0);
+ if (!is_ambiguous_kc(first->keycode)) {
+ // this pending key isn't ambiguous, so execute it.
+ dprintf(" %s: found unambiguous key\n", __func__);
+ execute_head_and_pop(pending);
+ return true;
+ } else if (is_ambiguous_kc(first->keycode) && is_up(first)) {
+ dprintf(" %s: interpreting keyup as mod\n", __func__);
+ p.down = NULL;
+ p.up = first;
+ interpret_as_mod(&p);
+ execute_head_and_pop(pending);
+ return true;
+ } else if (is_downup_pair(first, &p)) {
+ // 'first' was released before any other pressed key, so treat this as
+ // a rolling series of normal key taps.
+ dprintf(" %s: found down-up pair, interpreting as normal key\n", __func__);
+ interpret_as_normal(&p);
+ execute_head_and_pop(pending);
+ return true;
+ } else {
+ // if another key was pressed and released while 'first' was held, then we
+ // should treat it like a modifier.
+ pending_key_t *next = first->next;
+ while (next != NULL) {
+ if (is_downup_pair(next, NULL)) {
+ dprintf(" %s: found subsequent downup pair, interpreting head as mod\n", __func__);
+ p.down = first;
+ p.up = NULL;
+ interpret_as_mod(&p);
+ execute_head_and_pop(pending);
+ return true;
+ }
+ next = next->next;
+ }
+
+ // we can't disambiguate 'first' yet. wait for another keypress.
+ dprintf(" %s: can't disambiguate (yet)\n", __func__);
+ return false;
+ }
+}
+
+bool process_record_user(uint16_t keycode, keyrecord_t *record) {
+ if (keycode == DEBUG) {
+ return true;
+ }
+
+ if (g_pending.count == 0 && !is_ambiguous_kc(keycode)) {
+ // we have no pending keys and this key isn't ambiguous, so we should
+ // just let QMK take care of it.
+ dprintf("%s: handled by qmk\n", __func__);
+ return true;
+ } else {
+ dprintf("%s: got dual-role key\n", __func__);
+ // append the keypress and then try parsing all pending keypresses.
+ kring_append(&g_pending, keycode, record);
+ while (g_pending.count > 0) {
+ dprintf("%s: looping through %d keys...\n", __func__, g_pending.count);
+ if (!parse_next(&g_pending)) {
+ // one of our keypresses is ambiguous and we can't proceed until
+ // we get further keypresses to disambiguate it.
+ dprintf("%s: %d pending keys are ambiguous\n", __func__, g_pending.count);
+ break;
+ }
+ }
+ return false;
+ }
+}