#include QMK_KEYBOARD_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
#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(
______ , ______ , ______ , ______ , ______ , ______ , ______ , ______ ,
______ , ______ , ______ , ______ , ______ , ______ , ______ , ______ , ______ , ______ , ______ , ______ ,
______ , ______ , ______ , ______ , ______ , ______ , ______ , ______ , ______ , ______ ,
______ , ______ , ______ , ______ , ______ , ______ , ______ , ______
)
};
struct _pending_key_t {
uint16_t keycode;
keyrecord_t record;
struct _pending_key_t *next;
};
typedef struct _pending_key_t pending_key_t;
#define RINGSIZE 11
struct _kring_t {
pending_key_t items[RINGSIZE];
int8_t ifirst;
int8_t ilast;
uint8_t count;
pending_key_t *head;
};
typedef struct _kring_t kring_t;
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]);
}
pending_key_t* kring_last(kring_t *ring) {
if (ring->count == 0) {
return NULL;
}
return kring_get(ring, ring->count - 1);
}
void kring_pop(kring_t *ring) {
if (ring->count > 0) {
ring->ifirst += 1;
ring->ifirst %= RINGSIZE;
ring->head = ring->head->next;
ring->count -= 1;
}
}
void kring_append(kring_t *ring, uint16_t keycode, keyrecord_t *record) {
if (ring->count >= RINGSIZE) {
return;
}
if (ring->count == 0) {
ring->count += 1;
ring->ifirst = 0;
ring->ilast = 0;
ring->head = &(ring->items[0]);
}
else {
ring->count += 1;
ring->ilast += 1;
ring->ilast %= RINGSIZE;
}
int8_t i = ring->ilast;
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;
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) {}
bool is_ambiguous_kc(uint16_t kc) {
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;
}
struct _pending_pair_t {
pending_key_t *down;
pending_key_t *up;
};
typedef struct _pending_pair_t pending_pair_t;
bool is_downup_pair(pending_key_t *k, pending_pair_t *p) {
if (!is_down(k)) {
return false;
}
pending_key_t *next = k->next;
while (next != NULL) {
if (keys_match(k, next) && is_up(next)) {
if (p != NULL) {
p->down = k;
p->up = next;
}
return true;
}
next = next->next;
}
return false;
}
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:
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) {
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);
}
bool parse_next(kring_t *pending) {
pending_pair_t p;
pending_key_t *first = kring_get(pending, 0);
if (!is_ambiguous_kc(first->keycode)) {
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)) {
dprintf(" %s: found down-up pair, interpreting as normal key\n", __func__);
interpret_as_normal(&p);
execute_head_and_pop(pending);
return true;
} else {
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;
}
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)) {
dprintf("%s: handled by qmk\n", __func__);
return true;
} else {
dprintf("%s: got dual-role key\n", __func__);
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)) {
dprintf("%s: %d pending keys are ambiguous\n", __func__, g_pending.count);
break;
}
}
return false;
}
}