diff --git a/src/keyboard.c b/src/keyboard.c index c538861..c32f1b1 100644 --- a/src/keyboard.c +++ b/src/keyboard.c @@ -4,7 +4,9 @@ * © 2019 Raheman Vaiya (see also: LICENSE). */ -#include "keyboard.h" +#include "keyd.h" + +static long process_event(struct keyboard *kbd, uint8_t code, int pressed, long time); /* * Here be tiny dragons. @@ -183,6 +185,15 @@ static void lookup_descriptor(struct keyboard *kbd, uint8_t code, long maxts = 0; + if (code >= KEYD_CHORD_1 && code <= KEYD_CHORD_MAX) { + size_t idx = code - KEYD_CHORD_1; + + *d = kbd->active_chords[idx].chord.d; + *dl = kbd->active_chords[idx].layer; + + return; + } + for (i = 0; i < kbd->config.nr_layers; i++) { struct layer *layer = &kbd->config.layers[i]; @@ -310,10 +321,10 @@ static void enqueue_chord_event(struct keyboard *kbd, uint8_t code, uint8_t pres /* Returns: * 0 in the case of no match * 1 in the case of a partial match - * 2 in the case of an unambiguous match (populating d and dl) - * 3 in the case of an ambiguous match (populating d and dl) + * 2 in the case of an unambiguous match (populating chord and layer) + * 3 in the case of an ambiguous match (populating chord and layer) */ -static int check_chord_match(struct keyboard *kbd, struct descriptor *d, int *dl) +static int check_chord_match(struct keyboard *kbd, const struct chord **chord, int *chord_layer) { size_t idx; int full_match = 0; @@ -334,8 +345,9 @@ static int check_chord_match(struct keyboard *kbd, struct descriptor *d, int *dl if (ret == 2 && maxts <= kbd->layer_state[idx].activation_time) { - *d = layer->chords[i].d; - *dl = idx; + *chord_layer = (int)idx; + *chord = &layer->chords[i]; + full_match = 1; maxts = kbd->layer_state[idx].activation_time; } else if (ret == 1) { @@ -751,42 +763,92 @@ struct keyboard *new_keyboard(struct config *config, static int resolve_chord(struct keyboard *kbd) { - if (kbd->chord.match_sz != 0) { - process_descriptor(kbd, - kbd->chord.start_code, - &kbd->chord.match, - kbd->chord.match_layer, 1, kbd->chord.last_code_time); - cache_set(kbd, - kbd->chord.start_code, - &kbd->chord.match, kbd->chord.match_layer); - } + size_t queue_offset = 0; + const struct chord *chord = kbd->chord.match; kbd->chord.state = CHORD_RESOLVING; + + if (chord) { + size_t i; + uint8_t code = 0; + + for (i = 0; i < ARRAY_SIZE(kbd->active_chords); i++) { + struct active_chord *ac = &kbd->active_chords[i]; + if (!ac->active) { + ac->active = 1; + ac->chord = *chord; + ac->layer = kbd->chord.match_layer; + code = KEYD_CHORD_1 + i; + + break; + } + } + + assert(code); + + queue_offset = chord->sz; + process_event(kbd, code, 1, kbd->chord.last_code_time); + } + + kbd_process_events(kbd, - kbd->chord.queue + kbd->chord.match_sz, - kbd->chord.queue_sz - kbd->chord.match_sz); + kbd->chord.queue + queue_offset, + kbd->chord.queue_sz - queue_offset); kbd->chord.state = CHORD_INACTIVE; return 1; } static int abort_chord(struct keyboard *kbd) { - kbd->chord.match_sz = 0; + kbd->chord.match = NULL; return resolve_chord(kbd); } static int handle_chord(struct keyboard *kbd, uint8_t code, int pressed, long time) { + size_t i; const long interkey_timeout = kbd->config.chord_interkey_timeout; const long hold_timeout = kbd->config.chord_hold_timeout; + if (code && !pressed) { + for (i = 0; i < ARRAY_SIZE(kbd->active_chords); i++) { + struct active_chord *ac = &kbd->active_chords[i]; + uint8_t chord_code = KEYD_CHORD_1 + i; + + if (ac->active) { + size_t i; + int nremaining = 0; + int found = 0; + + for (i = 0; i < ac->chord.sz; i++) { + if (ac->chord.keys[i] == code) { + ac->chord.keys[i] = 0; + found = 1; + } + + if (ac->chord.keys[i]) + nremaining++; + } + + if (found) { + if (nremaining == 0) { + ac->active = 0; + process_event(kbd, chord_code, 0, time); + } + + return 1; + } + } + } + } + switch (kbd->chord.state) { case CHORD_RESOLVING: return 0; case CHORD_INACTIVE: kbd->chord.queue_sz = 0; - kbd->chord.match_sz = 0; + kbd->chord.match = NULL; kbd->chord.start_code = code; enqueue_chord_event(kbd, code, pressed, time); @@ -794,8 +856,6 @@ static int handle_chord(struct keyboard *kbd, case 0: return 0; case 3: - kbd->chord.match_sz = kbd->chord.queue_sz; - /* FALLTHROUGH */ case 1: kbd->chord.state = CHORD_PENDING_DISAMBIGUATION; kbd->chord.last_code_time = time; @@ -803,9 +863,8 @@ static int handle_chord(struct keyboard *kbd, return 1; default: case 2: - kbd->chord.match_sz = kbd->chord.queue_sz; - kbd->chord.last_code_time = time; + if (hold_timeout) { kbd->chord.state = CHORD_PENDING_HOLD_TIMEOUT; schedule_timeout(kbd, time + hold_timeout); @@ -817,7 +876,7 @@ static int handle_chord(struct keyboard *kbd, case CHORD_PENDING_DISAMBIGUATION: if (!code) { if ((time - kbd->chord.last_code_time) >= interkey_timeout) { - if (kbd->chord.match_sz) { + if (kbd->chord.match) { long timeleft = hold_timeout - interkey_timeout; if (timeleft > 0) { schedule_timeout(kbd, time + timeleft); @@ -844,8 +903,6 @@ static int handle_chord(struct keyboard *kbd, case 0: return abort_chord(kbd); case 3: - kbd->chord.match_sz = kbd->chord.queue_sz; - /* FALLTHROUGH */ case 1: kbd->chord.last_code_time = time; @@ -856,7 +913,6 @@ static int handle_chord(struct keyboard *kbd, case 2: kbd->chord.last_code_time = time; - kbd->chord.match_sz = kbd->chord.queue_sz; if (hold_timeout) { kbd->chord.state = CHORD_PENDING_HOLD_TIMEOUT; schedule_timeout(kbd, time + hold_timeout); @@ -878,8 +934,8 @@ static int handle_chord(struct keyboard *kbd, if (!pressed) { size_t i; - for (i = 0; i < kbd->chord.match_sz; i++) - if (kbd->chord.queue[i].code == code) + for (i = 0; i < kbd->chord.match->sz; i++) + if (kbd->chord.match->keys[i] == code) return abort_chord(kbd); } @@ -901,6 +957,9 @@ static long process_event(struct keyboard *kbd, uint8_t code, int pressed, long int dl = -1; struct descriptor d; + if (handle_chord(kbd, code, pressed, time)) + goto exit; + if (kbd->pending_key.code) { struct descriptor action = {0}; @@ -960,9 +1019,6 @@ static long process_event(struct keyboard *kbd, uint8_t code, int pressed, long goto exit; } - if (handle_chord(kbd, code, pressed, time)) - goto exit; - if (kbd->active_macro) { if (code) { kbd->active_macro = NULL; diff --git a/src/keyboard.h b/src/keyboard.h index 4a151fd..934b216 100644 --- a/src/keyboard.h +++ b/src/keyboard.h @@ -7,6 +7,7 @@ #define KEYBOARD_H #include "keyd.h" +#include "keys.h" #include "unicode.h" #include "config.h" #include "device.h" @@ -54,13 +55,18 @@ struct keyboard { long timeouts[64]; size_t nr_timeouts; + struct active_chord { + uint8_t active; + struct chord chord; + int layer; + } active_chords[KEYD_CHORD_MAX-KEYD_CHORD_1+1]; + struct { struct key_event queue[32]; size_t queue_sz; - struct descriptor match; + const struct chord *match; int match_layer; - size_t match_sz; uint8_t start_code; long last_code_time; diff --git a/src/keys.h b/src/keys.h index a1396a0..7ad251d 100644 --- a/src/keys.h +++ b/src/keys.h @@ -278,6 +278,9 @@ struct modifier_table_ent { #define KEYD_NOOP 195 #define KEYD_EXTERNAL_MOUSE_BUTTON 196 +#define KEYD_CHORD_1 197 +#define KEYD_CHORD_2 198 +#define KEYD_CHORD_MAX 199 #define KEYD_LEFT_MOUSE 249 #define KEYD_MIDDLE_MOUSE 250 #define KEYD_RIGHT_MOUSE 251 diff --git a/t/chord3.t b/t/chord3.t new file mode 100644 index 0000000..a96e4b1 --- /dev/null +++ b/t/chord3.t @@ -0,0 +1,15 @@ +a down +b down +200 ms +a up +j down +k down +200 ms +b up +j up +k up + +control down +c down +control up +c up