1 // see https://github.com/pepaslabs/hexon38
32 // Dual-role keys: modifier when held, alpha when tapped.
33 #define A_CTL CTL_T(KC_A)
34 #define S_ALT ALT_T(KC_S)
35 #define D_GUI GUI_T(KC_D)
36 #define F_SFT SFT_T(KC_F)
37 #define J_SFT SFT_T(KC_J)
38 #define K_GUI GUI_T(KC_K)
39 #define L_ALT ALT_T(KC_L)
40 #define COLN_CTL CTL_T(KC_SCLN)
42 #define ______ KC_TRNS
43 #define LSHIFT KC_LSHIFT
44 #define RSHIFT KC_RSHIFT
53 #define BASE_LAYER LAYOUT
54 #define BLANK_LAYER LAYOUT
57 const uint16_t PROGMEM keymaps[][MATRIX_ROWS][MATRIX_COLS] = {
60 // ,--------+--------+--------+--------. ,--------+--------+--------+--------.
61 W_ , E_ , R_ , T_ , Y_ , U_ , I_ , O_ ,
62 //|--------+--------+--------+--------+--------+--------| |--------+--------+--------+--------+--------+--------.
63 Q_ , A_CTL , S_ALT , D_GUI , F_SFT , G_ , H_ , J_SFT , K_GUI , L_ALT ,COLN_CTL, P_ ,
64 //|--------+--------+--------+--------+--------+--------' `--------+--------+--------+--------+--------+--------|
65 B_ , Z_ , X_ , C_ , V_ , M_ , COMMA , PERIOD , SLASH , N_ ,
66 //`--------+--------+--------+--------+--------' `--------+--------+--------+--------+--------'
68 // ,--------+--------+--------+--------. ,--------+--------+--------+--------.
69 LSHIFT , SPACE , TAB , DEBUG , SPACE , BKSPC , ENTER , RSHIFT
70 // `--------+--------+--------+--------' `--------+--------+--------+--------'
74 // ,--------+--------+--------+--------. ,--------+--------+--------+--------.
75 ______ , ______ , ______ , ______ , ______ , ______ , ______ , ______ ,
76 //|--------+--------+--------+--------+--------+--------| |--------+--------+--------+--------+--------+--------.
77 ______ , ______ , ______ , ______ , ______ , ______ , ______ , ______ , ______ , ______ , ______ , ______ ,
78 //|--------+--------+--------+--------+--------+--------' `--------+--------+--------+--------+--------+--------|
79 ______ , ______ , ______ , ______ , ______ , ______ , ______ , ______ , ______ , ______ ,
80 //`--------+--------+--------+--------+--------' `--------+--------+--------+--------+--------'
82 // ,--------+--------+--------+--------. ,--------+--------+--------+--------.
83 ______ , ______ , ______ , ______ , ______ , ______ , ______ , ______
84 // `--------+--------+--------+--------' `--------+--------+--------+--------'
89 // a linked list of pending key events (press or release) which we haven't processed yet.
90 struct _pending_key_t {
93 struct _pending_key_t *next;
95 typedef struct _pending_key_t pending_key_t;
97 // worst case is 10 down strokes and 1 up stroke before we can start disambiguating.
100 // a ring buffer and linked list to store pending key events (presses and releases).
101 // (basically, this is a fixed-allocation linked list.)
103 // the actual key events.
104 pending_key_t items[RINGSIZE];
105 // the index of the oldest item, or -1 if no items.
107 // the index of the most recently added item, or -1 if no items.
109 // the number of items in the ring.
111 // the head of the linked list.
114 typedef struct _kring_t kring_t;
116 // safe accessor to the i-th item of the linked list (returns pointer or NULL).
117 pending_key_t* kring_get(kring_t *ring, uint8_t i) {
118 if (i >= ring->count) {
121 uint8_t j = (ring->ifirst + i) % RINGSIZE;
122 return &(ring->items[j]);
125 // return the last key in the list of buffered keys.
126 pending_key_t* kring_last(kring_t *ring) {
127 if (ring->count == 0) {
130 return kring_get(ring, ring->count - 1);
133 // remove the oldest item from the ring (the head of the list).
134 void kring_pop(kring_t *ring) {
135 if (ring->count > 0) {
137 ring->ifirst %= RINGSIZE;
138 ring->head = ring->head->next;
143 // add an item to the ring (append to the list).
144 void kring_append(kring_t *ring, uint16_t keycode, keyrecord_t *record) {
145 if (ring->count >= RINGSIZE) {
146 // uh oh, we overflowed the capacity of our buffer :(
150 // if the ring is empty, insert at index 0.
151 if (ring->count == 0) {
155 ring->head = &(ring->items[0]);
157 // else, append it onto the end.
161 ring->ilast %= RINGSIZE;
164 // the index at which we should insert this item.
165 int8_t i = ring->ilast;
168 ring->items[i].keycode = keycode;
169 ring->items[i].record.event = record->event;
170 #ifndef NO_ACTION_TAPPING
171 ring->items[i].record.tap = record->tap;
173 ring->items[i].next = NULL;
175 // update the previous item to point to this item.
176 if (ring->count > 1) {
177 kring_get(ring, ring->count - 2)->next = &(ring->items[i]);
183 void matrix_init_user(void) {
184 g_pending.ifirst = -1;
185 g_pending.ilast = -1;
187 g_pending.head = NULL;
190 void matrix_scan_user(void) {}
194 a_ b_ b- a-: emit SHIFT+b
195 a_ b_ a- b-: emit a, b
196 dual1down, dual1up -> norm1down, norm1up
197 dual1down, norm2down, norm2up -> mod1down, norm2down, norm2up
198 dual1down, norm2down, dual1up -> norm1down, norm2down, norm1up
199 dual1down, dual2down, norm3down, norm3up -> mod1down, mod2down, norm3down, norm3up
200 so, a dual key can't be disambiguated until the next keyup of a keydown (not including keyups from keys before it).
203 bool is_ambiguous_kc(uint16_t kc) {
204 // See the MT() define: https://github.com/qmk/qmk_firmware/blob/master/quantum/quantum_keycodes.h#L642
205 // See the QK_MOD_TAP case: https://github.com/qmk/qmk_firmware/blob/master/quantum/keymap_common.c#L134
206 uint8_t mod = mod_config((kc >> 0x8) & 0x1F);
210 bool is_down(pending_key_t *k) {
211 return k->record.event.pressed;
214 bool is_up(pending_key_t *k) {
218 bool keys_match(pending_key_t *a, pending_key_t *b) {
219 return a->record.event.key.col == b->record.event.key.col
220 && a->record.event.key.row == b->record.event.key.row;
223 // both the down and corresponding upstroke of a keypress.
224 struct _pending_pair_t {
228 typedef struct _pending_pair_t pending_pair_t;
230 // returns true if this keydown event has a corresponding keyup event in the
231 // list of buffered keys. also fills out 'p'.
232 bool is_downup_pair(pending_key_t *k, pending_pair_t *p) {
233 // first, make sure this event is keydown.
237 // now find its matching keyup.
238 pending_key_t *next = k->next;
239 while (next != NULL) {
240 if (keys_match(k, next) && is_up(next)) {
254 // given a QK_MOD_TAP keycode, return the KC_* version of the modifier keycode.
255 uint16_t get_mod_kc(uint16_t keycode) {
256 uint8_t mod = mod_config((keycode >> 0x8) & 0x1F);
275 // shrug? this shouldn't happen.
280 bool is_mod_kc(uint16_t keycode) {
282 case QK_MODS ... QK_MODS_MAX:
289 void interpret_as_mod(pending_pair_t *p) {
290 // see https://github.com/qmk/qmk_firmware/issues/1503
294 k->keycode = get_mod_kc(k->keycode);
298 k->keycode = get_mod_kc(k->keycode);
302 void interpret_as_normal(pending_pair_t *p) {
306 k->keycode = k->keycode & 0xFF;
310 k->keycode = k->keycode & 0xFF;
314 void execute_head_and_pop(kring_t *ring) {
315 pending_key_t *head = kring_get(ring, 0);
316 uint16_t kc = head->keycode;
319 dprintf(" %s: mod down 0x%04X\n", __func__, kc);
320 set_mods(get_mods() | MOD_BIT(kc));
322 dprintf(" %s: mod up 0x%04X\n", __func__, kc);
323 set_mods(get_mods() & ~MOD_BIT(kc));
327 dprintf(" %s: key down 0x%04X\n", __func__, kc);
330 dprintf(" %s: key up 0x%04X\n", __func__, kc);
331 unregister_code16(kc);
337 // try to figure out what the next pending keypress means.
338 bool parse_next(kring_t *pending) {
340 pending_key_t *first = kring_get(pending, 0);
341 if (!is_ambiguous_kc(first->keycode)) {
342 // this pending key isn't ambiguous, so execute it.
343 dprintf(" %s: found unambiguous key\n", __func__);
344 execute_head_and_pop(pending);
346 } else if (is_ambiguous_kc(first->keycode) && is_up(first)) {
347 dprintf(" %s: interpreting keyup as mod\n", __func__);
350 interpret_as_mod(&p);
351 execute_head_and_pop(pending);
353 } else if (is_downup_pair(first, &p)) {
354 // 'first' was released before any other pressed key, so treat this as
355 // a rolling series of normal key taps.
356 dprintf(" %s: found down-up pair, interpreting as normal key\n", __func__);
357 interpret_as_normal(&p);
358 execute_head_and_pop(pending);
361 // if another key was pressed and released while 'first' was held, then we
362 // should treat it like a modifier.
363 pending_key_t *next = first->next;
364 while (next != NULL) {
365 if (is_downup_pair(next, NULL)) {
366 dprintf(" %s: found subsequent downup pair, interpreting head as mod\n", __func__);
369 interpret_as_mod(&p);
370 execute_head_and_pop(pending);
376 // we can't disambiguate 'first' yet. wait for another keypress.
377 dprintf(" %s: can't disambiguate (yet)\n", __func__);
382 bool process_record_user(uint16_t keycode, keyrecord_t *record) {
383 if (keycode == DEBUG) {
387 if (g_pending.count == 0 && !is_ambiguous_kc(keycode)) {
388 // we have no pending keys and this key isn't ambiguous, so we should
389 // just let QMK take care of it.
390 dprintf("%s: handled by qmk\n", __func__);
393 dprintf("%s: got dual-role key\n", __func__);
394 // append the keypress and then try parsing all pending keypresses.
395 kring_append(&g_pending, keycode, record);
396 while (g_pending.count > 0) {
397 dprintf("%s: looping through %d keys...\n", __func__, g_pending.count);
398 if (!parse_next(&g_pending)) {
399 // one of our keypresses is ambiguous and we can't proceed until
400 // we get further keypresses to disambiguate it.
401 dprintf("%s: %d pending keys are ambiguous\n", __func__, g_pending.count);