summaryrefslogtreecommitdiffstats
path: root/keyboards/handwired/hexon38/keymaps/default/keymap.c
blob: c3805991f0f0592714ae7ae6813b329488ffcd1c (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
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;
    }
}