70e34e491c297231a3f987fd69760d38e79dbfa4
- Author
- Ryan <fauxpark@gmail.com>
- Committer
- GitHub <noreply@github.com>
- Date
Message
Diff
This diff is truncated to protect this page.
1diff --git a/Makefile b/Makefile
2index c77eadf97d5d683cbcfd2cb0bd1fec0530f12aba..9f2e4636a4b478cbe6752b9d9e749e2915abf766 100644
3--- a/Makefile
4+++ b/Makefile
5@@ -328,7 +328,7 @@ define PARSE_TEST
6 ifeq ($$(TEST_NAME),all)
7 MATCHED_TESTS := $$(TEST_LIST)
8 else
9- MATCHED_TESTS := $$(foreach TEST, $$(TEST_LIST),$$(if $$(findstring $$(TEST_NAME), $$(notdir $$(TEST))), $$(TEST),))
10+ MATCHED_TESTS := $$(foreach TEST, $$(TEST_LIST),$$(if $$(findstring x$$(TEST_NAME)x, x$$(notdir $$(TEST))x), $$(TEST),))
11 endif
12 $$(foreach TEST,$$(MATCHED_TESTS),$$(eval $$(call BUILD_TEST,$$(TEST),$$(TEST_TARGET))))
13 endef
14diff --git a/builddefs/common_features.mk b/builddefs/common_features.mk
15index 60fcdc6b93d8eca2c05fb7d1145846cdf803aaed..094eda6fef642a7ad0405a20a8137bf5dd4a1ddf 100644
16--- a/builddefs/common_features.mk
17+++ b/builddefs/common_features.mk
18@@ -784,13 +784,15 @@ endif
19 ifeq ($(strip $(UCIS_ENABLE)), yes)
20 OPT_DEFS += -DUCIS_ENABLE
21 UNICODE_COMMON := yes
22- SRC += $(QUANTUM_DIR)/process_keycode/process_ucis.c
23+ SRC += $(QUANTUM_DIR)/process_keycode/process_ucis.c \
24+ $(QUANTUM_DIR)/unicode/ucis.c
25 endif
26
27 ifeq ($(strip $(UNICODEMAP_ENABLE)), yes)
28 OPT_DEFS += -DUNICODEMAP_ENABLE
29 UNICODE_COMMON := yes
30- SRC += $(QUANTUM_DIR)/process_keycode/process_unicodemap.c
31+ SRC += $(QUANTUM_DIR)/process_keycode/process_unicodemap.c \
32+ $(QUANTUM_DIR)/unicode/unicodemap.c
33 endif
34
35 ifeq ($(strip $(UNICODE_ENABLE)), yes)
36diff --git a/docs/feature_unicode.md b/docs/feature_unicode.md
37index 312a0332570e5901b9e30bae356215a241a12596..341084f9260a7f9196289b86371f27c3006fd033 100644
38--- a/docs/feature_unicode.md
39+++ b/docs/feature_unicode.md
40@@ -1,48 +1,77 @@
41-# Unicode Support
42+# Unicode :id=unicode
43
44-Unicode characters can be input straight from your keyboard! There are some limitations, however.
45+With a little help from your OS, practically any Unicode character can be input using your keyboard.
46
47-In order to enable Unicode support on your keyboard, you will need to do the following:
48+## Caveats :id=caveats
49
50-1. Choose one of three supported Unicode implementations: [Basic Unicode](#basic-unicode), [Unicode Map](#unicode-map), [UCIS](#ucis).
51-2. Find which [input mode](#input-modes) is the best match for your operating system and setup.
52-3. [Set](#setting-the-input-mode) the appropriate input mode (or modes) in your configuration.
53-4. Add Unicode keycodes to your keymap.
54diff --git a/keyboards/spaceman/2_milk/keymaps/emoji/keymap.c b/keyboards/spaceman/2_milk/keymaps/emoji/keymap.c
55index 8859573ce5d9fe926cdf53295ae9ebe9e68881f5..26ac20ad5cefa71deee544cfcefb7628f78b2750 100644
56--- a/keyboards/spaceman/2_milk/keymaps/emoji/keymap.c
57+++ b/keyboards/spaceman/2_milk/keymaps/emoji/keymap.c
58@@ -11,7 +11,7 @@ void dance_key_one (tap_dance_state_t *state, void *user_data) {
59 tap_code(KC_ENTER);
60 reset_tap_dance (state);
61 } else if (state->count == 2) {
62- cycle_unicode_input_mode(+1);
63+ unicode_input_mode_step();
64 reset_tap_dance (state);
65 }
66 }
67diff --git a/quantum/process_keycode/process_ucis.c b/quantum/process_keycode/process_ucis.c
68index 3aa09d5948f2196aa2a479dae24693b11755d99b..91a038bab1cdf5c36a5abde563161599ed4872df 100644
69--- a/quantum/process_keycode/process_ucis.c
70+++ b/quantum/process_keycode/process_ucis.c
71@@ -15,110 +15,30 @@
72 */
73
74 #include "process_ucis.h"
75-#include "unicode.h"
76-#include "keycode.h"
77-#include "wait.h"
78+#include "ucis.h"
79+#include "keycodes.h"
80
81-ucis_state_t ucis_state;
82-
83-void ucis_start(void) {
84- ucis_state.count = 0;
85- ucis_state.in_progress = true;
86-
87- ucis_start_user();
88-}
89-
90-__attribute__((weak)) void ucis_start_user(void) {
91- register_unicode(0x2328); // ⌨
92-}
93-
94-__attribute__((weak)) void ucis_success(uint8_t symbol_index) {}
95-
96-static bool is_uni_seq(char *seq) {
97- uint8_t i;
98- for (i = 0; seq[i]; i++) {
99- uint16_t keycode;
100- if ('1' <= seq[i] && seq[i] <= '0') {
101- keycode = seq[i] - '1' + KC_1;
102- } else {
103- keycode = seq[i] - 'a' + KC_A;
104- }
105- if (i > ucis_state.count || ucis_state.codes[i] != keycode) {
106+bool process_ucis(uint16_t keycode, keyrecord_t *record) {
107+ if (ucis_active() && record->event.pressed) {
108+ bool special = keycode == KC_SPACE || keycode == KC_ENTER || keycode == KC_ESCAPE || keycode == KC_BACKSPACE;
109+ if (ucis_count() >= UCIS_MAX_INPUT_LENGTH && !special) {
110 return false;
111 }
112- }
113- return ucis_state.codes[i] == KC_ENTER || ucis_state.codes[i] == KC_SPACE;
114-}
115-
116-__attribute__((weak)) void ucis_symbol_fallback(void) {
117- for (uint8_t i = 0; i < ucis_state.count - 1; i++) {
118- tap_code(ucis_state.codes[i]);
119- }
120-}
121-
122-__attribute__((weak)) void ucis_cancel(void) {}
123-
124-void register_ucis(const uint32_t *code_points) {
125- for (int i = 0; i < UCIS_MAX_CODE_POINTS && code_points[i]; i++) {
126- register_unicode(code_points[i]);
127- }
128-}
129
130-bool process_ucis(uint16_t keycode, keyrecord_t *record) {
131- if (!ucis_state.in_progress || !record->event.pressed) {
132- return true;
133- }
134-
135- bool special = keycode == KC_SPACE || keycode == KC_ENTER || keycode == KC_ESCAPE || keycode == KC_BACKSPACE;
136- if (ucis_state.count >= UCIS_MAX_SYMBOL_LENGTH && !special) {
137- return false;
138- }
139-
140- ucis_state.codes[ucis_state.count] = keycode;
141- ucis_state.count++;
142-
143- switch (keycode) {
144- case KC_BACKSPACE:
145- if (ucis_state.count >= 2) {
146- ucis_state.count -= 2;
147- return true;
148- } else {
149- ucis_state.count--;
150- return false;
151- }
152-
153- case KC_SPACE:
154- case KC_ENTER:
155- case KC_ESCAPE:
156- for (uint8_t i = 0; i < ucis_state.count; i++) {
157- tap_code(KC_BACKSPACE);
158- }
159-
160- if (keycode == KC_ESCAPE) {
161- ucis_state.in_progress = false;
162- ucis_cancel();
163- return false;
164- }
165-
166- uint8_t i;
167- bool symbol_found = false;
168- for (i = 0; ucis_symbol_table[i].symbol; i++) {
169- if (is_uni_seq(ucis_symbol_table[i].symbol)) {
170- symbol_found = true;
171diff --git a/quantum/process_keycode/process_ucis.h b/quantum/process_keycode/process_ucis.h
172index 54eb9413d41bac7ccb5634ce5957294a90cbac02..6282df78932d8cc12908f3d5329d4ab0d46e5689 100644
173--- a/quantum/process_keycode/process_ucis.h
174+++ b/quantum/process_keycode/process_ucis.h
175@@ -18,48 +18,6 @@
176
177 #include <stdbool.h>
178 #include <stdint.h>
179-
180 #include "action.h"
181
182-#ifndef UCIS_MAX_SYMBOL_LENGTH
183-# define UCIS_MAX_SYMBOL_LENGTH 32
184-#endif
185-#ifndef UCIS_MAX_CODE_POINTS
186-# define UCIS_MAX_CODE_POINTS 3
187-#endif
188-
189-typedef struct {
190- char * symbol;
191- uint32_t code_points[UCIS_MAX_CODE_POINTS];
192-} ucis_symbol_t;
193-
194-typedef struct {
195- uint8_t count;
196- uint16_t codes[UCIS_MAX_SYMBOL_LENGTH];
197- bool in_progress : 1;
198-} ucis_state_t;
199-
200-extern ucis_state_t ucis_state;
201-
202-// clang-format off
203-
204-#define UCIS_TABLE(...) \
205- { \
206- __VA_ARGS__, \
207- { NULL, {} } \
208- }
209-#define UCIS_SYM(name, ...) \
210- { name, {__VA_ARGS__} }
211-
212-// clang-format on
213-
214-extern const ucis_symbol_t ucis_symbol_table[];
215-
216-void ucis_start(void);
217-void ucis_start_user(void);
218-void ucis_symbol_fallback(void);
219-void ucis_success(uint8_t symbol_index);
220-
221-void register_ucis(const uint32_t *code_points);
222-
223 bool process_ucis(uint16_t keycode, keyrecord_t *record);
224diff --git a/quantum/process_keycode/process_unicode.c b/quantum/process_keycode/process_unicode.c
225index 1ec76245a342aab5e4dddc28de580f11840bb2c5..8ee6fcd7fca70e7d18ec19133097bdbb9e9ab760 100644
226--- a/quantum/process_keycode/process_unicode.c
227+++ b/quantum/process_keycode/process_unicode.c
228@@ -16,6 +16,7 @@
229
230 #include "process_unicode.h"
231 #include "unicode.h"
232+#include "keycodes.h"
233 #include "quantum_keycodes.h"
234
235 bool process_unicode(uint16_t keycode, keyrecord_t *record) {
236diff --git a/quantum/process_keycode/process_unicode.h b/quantum/process_keycode/process_unicode.h
237index 341bc8d861dcd5968f1d5d2b51f60521365096ab..9a51ffaf7c9c44428c0e510b5ba38e1c6e05e3dc 100644
238--- a/quantum/process_keycode/process_unicode.h
239+++ b/quantum/process_keycode/process_unicode.h
240@@ -18,7 +18,6 @@
241
242 #include <stdbool.h>
243 #include <stdint.h>
244-
245 #include "action.h"
246
247 bool process_unicode(uint16_t keycode, keyrecord_t *record);
248diff --git a/quantum/process_keycode/process_unicode_common.c b/quantum/process_keycode/process_unicode_common.c
249index a0b9010027637856fecf6a20792b88dba264052e..f43770977aa2836dcdba810258e9e76b8614dbd4 100644
250--- a/quantum/process_keycode/process_unicode_common.c
251+++ b/quantum/process_keycode/process_unicode_common.c
252@@ -17,7 +17,8 @@
253 #include "process_unicode_common.h"
254 #include "unicode.h"
255 #include "action_util.h"
256-#include "keycode.h"
257+#include "keycodes.h"
258+#include "modifiers.h"
259
260 #if defined(UNICODE_ENABLE)
261 # include "process_unicode.h"
262@@ -32,10 +33,18 @@ bool process_unicode_common(uint16_t keycode, keyrecord_t *record) {
263 bool shifted = get_mods() & MOD_MASK_SHIFT;
264 switch (keycode) {
265 case QK_UNICODE_MODE_NEXT:
266- cycle_unicode_input_mode(shifted ? -1 : +1);
267+ if (shifted) {
268+ unicode_input_mode_step_reverse();
269+ } else {
270+ unicode_input_mode_step();
271+ }
272 break;
273 case QK_UNICODE_MODE_PREVIOUS:
274- cycle_unicode_input_mode(shifted ? +1 : -1);
275+ if (shifted) {
276+ unicode_input_mode_step();
277+ } else {
278+ unicode_input_mode_step_reverse();
279+ }
280 break;
281 case QK_UNICODE_MODE_MACOS:
282 set_unicode_input_mode(UNICODE_MODE_MACOS);
283diff --git a/quantum/process_keycode/process_unicode_common.h b/quantum/process_keycode/process_unicode_common.h
284index fd09a41818f93a895af2ce77798feed9f12d0b78..046583007930122ca4b929ee43abb260abc393e5 100644
285--- a/quantum/process_keycode/process_unicode_common.h
286+++ b/quantum/process_keycode/process_unicode_common.h
287@@ -18,7 +18,6 @@
288
289 #include <stdbool.h>
290 #include <stdint.h>
291-
292 #include "action.h"
293
294 bool process_unicode_common(uint16_t keycode, keyrecord_t *record);
295diff --git a/quantum/process_keycode/process_unicodemap.c b/quantum/process_keycode/process_unicodemap.c
296index 195c093e6e5d2cbec923f550af6bd359c57f3131..a85568521c5d5ecaeea0e82c93c9bab9d7ea3e15 100644
297--- a/quantum/process_keycode/process_unicodemap.c
298+++ b/quantum/process_keycode/process_unicodemap.c
299@@ -15,41 +15,12 @@
300 */
301
302 #include "process_unicodemap.h"
303-#include "unicode.h"
304-#include "quantum_keycodes.h"
305-#include "keycode.h"
306-#include "action_util.h"
307-#include "host.h"
308-
309-__attribute__((weak)) uint16_t unicodemap_index(uint16_t keycode) {
310- if (keycode >= QK_UNICODEMAP_PAIR) {
311- // Keycode is a pair: extract index based on Shift / Caps Lock state
312- uint16_t index;
313-
314- uint8_t mods = get_mods() | get_weak_mods();
315-#ifndef NO_ACTION_ONESHOT
316- mods |= get_oneshot_mods();
317-#endif
318-
319- bool shift = mods & MOD_MASK_SHIFT;
320- bool caps = host_keyboard_led_state().caps_lock;
321- if (shift ^ caps) {
322- index = QK_UNICODEMAP_PAIR_GET_SHIFTED_INDEX(keycode);
323- } else {
324- index = QK_UNICODEMAP_PAIR_GET_UNSHIFTED_INDEX(keycode);
325- }
326-
327- return index;
328- } else {
329- // Keycode is a regular index
330- return QK_UNICODEMAP_GET_INDEX(keycode);
331- }
332-}
333+#include "unicodemap.h"
334+#include "keycodes.h"
335
336 bool process_unicodemap(uint16_t keycode, keyrecord_t *record) {
337 if (keycode >= QK_UNICODEMAP && keycode <= QK_UNICODEMAP_PAIR_MAX && record->event.pressed) {
338- uint32_t code_point = pgm_read_dword(unicode_map + unicodemap_index(keycode));
339- register_unicode(code_point);
340+ register_unicodemap(unicodemap_index(keycode));
341 }
342 return true;
343 }
344diff --git a/quantum/process_keycode/process_unicodemap.h b/quantum/process_keycode/process_unicodemap.h
345index 5a3aeb000062b72e1699bea7aaed9f5bc7798dad..f07082e9efa952cdadadc39f2104090e0edbfd85 100644
346--- a/quantum/process_keycode/process_unicodemap.h
347+++ b/quantum/process_keycode/process_unicodemap.h
348@@ -18,11 +18,6 @@
349
350 #include <stdbool.h>
351 #include <stdint.h>
352-
353 #include "action.h"
354-#include "progmem.h"
355-
356-extern const uint32_t unicode_map[] PROGMEM;
357
358-uint16_t unicodemap_index(uint16_t keycode);
359-bool process_unicodemap(uint16_t keycode, keyrecord_t *record);
360+bool process_unicodemap(uint16_t keycode, keyrecord_t *record);
361diff --git a/quantum/quantum.c b/quantum/quantum.c
362index 3d440e0de8981c1298f0d74eb582a9e7a13ce1f2..3323a5adb62ef515e656b20a864cdd074ea91a85 100644
363--- a/quantum/quantum.c
364+++ b/quantum/quantum.c
365@@ -68,10 +68,6 @@
366 # include "process_unicode_common.h"
367 #endif
368
369-#ifdef UNICODE_ENABLE
370-# include "process_unicode.h"
371-#endif
372-
373 #ifdef VELOCIKEY_ENABLE
374 # include "velocikey.h"
375 #endif
376diff --git a/quantum/quantum.h b/quantum/quantum.h
377index 017844c558dacd50e28127bd39f9583795a52edb..4d183e755f28fdaf70ef4939099da38176703a71 100644
378--- a/quantum/quantum.h
379+++ b/quantum/quantum.h
380@@ -97,16 +97,16 @@ extern layer_state_t layer_state;
381 # include "leader.h"
382 #endif
383
384-#ifdef UCIS_ENABLE
385-# include "process_ucis.h"
386+#ifdef UNICODE_COMMON_ENABLE
387+# include "unicode.h"
388 #endif
389
390-#ifdef UNICODEMAP_ENABLE
391-# include "process_unicodemap.h"
392+#ifdef UCIS_ENABLE
393+# include "ucis.h"
394 #endif
395
396-#ifdef UNICODE_COMMON_ENABLE
397-# include "unicode.h"
398+#ifdef UNICODEMAP_ENABLE
399+# include "unicodemap.h"
400 #endif
401
402 #ifdef KEY_OVERRIDE_ENABLE
403diff --git a/quantum/quantum_keycodes_legacy.h b/quantum/quantum_keycodes_legacy.h
404index ad078cdad5ce8eb5517c736a118b57100bb10b8e..260ac1c8a443ffcf75fb36a56a2651d3624300ff 100644
405--- a/quantum/quantum_keycodes_legacy.h
406+++ b/quantum/quantum_keycodes_legacy.h
407@@ -55,4 +55,4 @@
408 #define GUI_TOG QK_MAGIC_TOGGLE_GUI
409
410 #define X(i) UM(i)
411-#define XP(i, j) UM(i, j)
412+#define XP(i, j) UP(i, j)
413diff --git a/quantum/unicode/ucis.c b/quantum/unicode/ucis.c
414new file mode 100644
415index 0000000000000000000000000000000000000000..32be78520654d8f8b0e58f68dea75ca86917f186
416--- /dev/null
417+++ b/quantum/unicode/ucis.c
418@@ -0,0 +1,96 @@
419+// Copyright 2023 QMK
420+// SPDX-License-Identifier: GPL-2.0-or-later
421+
422+#include "ucis.h"
423+#include "unicode.h"
424+#include "action.h"
425+
426+uint8_t count = 0;
427+bool active = false;
428+char input[UCIS_MAX_INPUT_LENGTH] = {0};
429+
430+void ucis_start(void) {
431+ count = 0;
432+ active = true;
433+
434+ register_unicode(0x2328); // ⌨
435+}
436+
437+bool ucis_active(void) {
438+ return active;
439+}
440+
441+uint8_t ucis_count(void) {
442+ return count;
443+}
444+
445+static char keycode_to_char(uint16_t keycode) {
446+ if (keycode >= KC_A && keycode <= KC_Z) {
447+ return 'a' + (keycode - KC_A);
448+ } else if (keycode >= KC_1 && keycode <= KC_9) {
449+ return '1' + (keycode - KC_1);
450+ } else if (keycode == KC_0) {
451+ return '0';
452+ }
453+ return 0;
454+}
455+
456+bool ucis_add(uint16_t keycode) {
457+ char c = keycode_to_char(keycode);
458+ if (c) {
459+ input[count++] = c;
460+ return true;
461+ }
462+ return false;
463+}
464+
465+bool ucis_remove_last(void) {
466+ if (count) {
467+ count--;
468+ return true;
469+ }
470+
471+ return false;
472+}
473+
474+static bool match_mnemonic(char *mnemonic) {
475+ for (uint8_t i = 0; input[i]; i++) {
476+ if (i > count || input[i] != mnemonic[i]) {
477+ return false;
478+ }
479+ }
480+ return true;
481+}
482+
483+void ucis_finish(void) {
484+ uint8_t i = 0;
485+ bool found = false;
486+ for (; ucis_symbol_table[i].mnemonic; i++) {
487+ if (match_mnemonic(ucis_symbol_table[i].mnemonic)) {
488+ found = true;
489+ break;
490+ }
491+ }
492+
493+ if (found) {
494+ for (uint8_t j = 0; j <= count; j++) {
495+ tap_code(KC_BACKSPACE);
496+ }
497+ register_ucis(i);
498+ }
499+
500+ active = false;
501+}
502+
503+void ucis_cancel(void) {
504+ count = 0;
505+ active = false;
506+}
507+
508+void register_ucis(uint8_t index) {
509+ const uint32_t *code_points = ucis_symbol_table[index].code_points;
510+
511+ for (int i = 0; i < UCIS_MAX_CODE_POINTS && code_points[i]; i++) {
512+ register_unicode(code_points[i]);
513+ }
514+}
515diff --git a/quantum/unicode/ucis.h b/quantum/unicode/ucis.h
516new file mode 100644
517index 0000000000000000000000000000000000000000..5a4fa267841cf53c992c32888a4028f7cf67b36a
518--- /dev/null
519+++ b/quantum/unicode/ucis.h
520@@ -0,0 +1,97 @@
521+// Copyright 2023 QMK
522+// SPDX-License-Identifier: GPL-2.0-or-later
523+
524+#pragma once
525+
526+#include <stdbool.h>
527+#include <stdint.h>
528+
529+/**
530+ * \file
531+ *
532+ * \defgroup ucis UCIS
533+ * \{
534+ */
535+
536+#ifndef UCIS_MAX_INPUT_LENGTH
537+# define UCIS_MAX_INPUT_LENGTH 32
538+#endif
539+
540+#ifndef UCIS_MAX_CODE_POINTS
541+# define UCIS_MAX_CODE_POINTS 3
542+#endif
543+
544+typedef struct {
545+ char* mnemonic;
546+ uint32_t code_points[UCIS_MAX_CODE_POINTS];
547+} ucis_symbol_t;
548+
549+// clang-format off
550+
551+#define UCIS_TABLE(...) { \
552+ __VA_ARGS__, \
553+ { NULL, {} } \
554+}
555+
556+#define UCIS_SYM(name, ...) { \
557+ .mnemonic = name, \
558+ .code_points = {__VA_ARGS__} \
559+}
560+
561+// clang-format on
562+
563+extern const ucis_symbol_t ucis_symbol_table[];
564+
565+/**
566+ * \brief Begin the input sequence.
567+ */
568+void ucis_start(void);
569+
570+/**
571+ * \brief Whether UCIS is currently active.
572+ *
573+ * \return `true` if UCIS is active.
574+ */
575+bool ucis_active(void);
576+
577+/**
578+ * \brief Get the number of characters in the input sequence buffer.
579+ *
580+ * \return The current input sequence buffer length.
581+ */
582+uint8_t ucis_count(void);
583+
584+/**
585+ * \brief Add the given keycode to the input sequence buffer.
586+ *
587+ * \param keycode The keycode to add. Must be between `KC_A` and `KC_Z`, or `KC_1` and `KC_0`.
588+ *
589+ * \return `true` if the keycode was added.
590+ */
591+bool ucis_add(uint16_t keycode);
592+
593+/**
594+ * \brief Remove the last character from the input sequence.
595+ *
596+ * \return `true` if the sequence was not empty.
597+ */
598+bool ucis_remove_last(void);
599+
600+/**
601+ * Mark the input sequence as complete, and attempt to match.
602+ */
603+void ucis_finish(void);
604+
605+/**
606+ * \brief Cancel the input sequence.
607+ */
608+void ucis_cancel(void);
609+
610+/**
611+ * Send the code point(s) for the given UCIS index.
612+ *
613+ * \param index The index into the UCIS symbol table.
614+ */
615+void register_ucis(uint8_t index);
616+
617+/** \} */
618diff --git a/quantum/unicode/unicode.c b/quantum/unicode/unicode.c
619index f92ba51984d0fdcedf0cb8f16b11dee9cf5a4af6..78a4cad585949cc63644bb0a295162383c1c5ce5 100644
620--- a/quantum/unicode/unicode.c
621+++ b/quantum/unicode/unicode.c
622@@ -73,16 +73,8 @@ static int8_t selected_count = ARRAY_SIZE(selected);
623 static int8_t selected_index;
624 #endif
625
626-/** \brief unicode input mode set at user level
627- *
628- * Run user code on unicode input mode change
629- */
630 __attribute__((weak)) void unicode_input_mode_set_user(uint8_t input_mode) {}
631
632-/** \brief unicode input mode set at keyboard level
633- *
634- * Run keyboard code on unicode input mode change
635- */
636 __attribute__((weak)) void unicode_input_mode_set_kb(uint8_t input_mode) {
637 unicode_input_mode_set_user(input_mode);
638 }
639@@ -172,6 +164,10 @@ uint8_t get_unicode_input_mode(void) {
640 return unicode_config.input_mode;
641 }
642
643+static void persist_unicode_input_mode(void) {
644+ eeprom_update_byte(EECONFIG_UNICODEMODE, unicode_config.input_mode);
645+}
646+
647 void set_unicode_input_mode(uint8_t mode) {
648 unicode_config.input_mode = mode;
649 persist_unicode_input_mode();
650@@ -182,26 +178,34 @@ void set_unicode_input_mode(uint8_t mode) {
651 dprintf("Unicode input mode set to: %u\n", unicode_config.input_mode);
652 }
653
654-void cycle_unicode_input_mode(int8_t offset) {
655+static void cycle_unicode_input_mode(int8_t offset) {
656 #if UNICODE_SELECTED_MODES != -1
657 selected_index = (selected_index + offset) % selected_count;
658 if (selected_index < 0) {
659 selected_index += selected_count;
660 }
661+
662 unicode_config.input_mode = selected[selected_index];
663+
664 # if UNICODE_CYCLE_PERSIST
665 persist_unicode_input_mode();
666 # endif
667+
668 # ifdef AUDIO_ENABLE
669 unicode_play_song(unicode_config.input_mode);
670 # endif
671+
672 unicode_input_mode_set_kb(unicode_config.input_mode);
673 dprintf("Unicode input mode cycle to: %u\n", unicode_config.input_mode);
674 #endif
675 }
676
677-void persist_unicode_input_mode(void) {
678- eeprom_update_byte(EECONFIG_UNICODEMODE, unicode_config.input_mode);
679+void unicode_input_mode_step(void) {
680+ cycle_unicode_input_mode(1);
681+}
682+
683+void unicode_input_mode_step_reverse(void) {
684+ cycle_unicode_input_mode(-1);
685 }
686
687 __attribute__((weak)) void unicode_input_start(void) {
688diff --git a/quantum/unicode/unicode.h b/quantum/unicode/unicode.h
689index fc0e9d3d2b7cee226837633efa6dc318b1304c58..90a54c8b18b5587b9a83465d41a4be84a56c2ab4 100644
690--- a/quantum/unicode/unicode.h
691+++ b/quantum/unicode/unicode.h
692@@ -19,6 +19,13 @@
693 #include <stdint.h>
694 #include "unicode_keycodes.h"
695
696+/**
697+ * \file
698+ *
699+ * \defgroup unicode Unicode
700+ * \{
701+ */
702+
703 typedef union {
704 uint8_t raw;
705 struct {
706@@ -41,21 +48,87 @@ enum unicode_input_modes {
707 UNICODE_MODE_COUNT // Number of available input modes (always leave at the end)
708 };
709
710-void unicode_input_mode_init(void);
711+void unicode_input_mode_init(void);
712+
713+/**
714+ * \brief Get the current Unicode input mode.
715+ *
716+ * \return The currently active Unicode input mode.
717+ */
718 uint8_t get_unicode_input_mode(void);
719-void set_unicode_input_mode(uint8_t mode);
720-void cycle_unicode_input_mode(int8_t offset);
721-void persist_unicode_input_mode(void);
722
723+/**
724+ * \brief Set the Unicode input mode.
725+ *
726+ * \param mode The input mode to set.
727+ */
728+void set_unicode_input_mode(uint8_t mode);
729+
730+/**
731+ * \brief Change to the next Unicode input mode.
732+ */
733+void unicode_input_mode_step(void);
734+
735+/**
736+ * \brief Change to the previous Unicode input mode.
737+ */
738+void unicode_input_mode_step_reverse(void);
739+
740+/**
741+ * \brief User-level callback, invoked when the input mode is changed.
742+ *
743+ * \param input_mode The new input mode.
744+ */
745 void unicode_input_mode_set_user(uint8_t input_mode);
746+
747+/**
748+ * \brief Keyboard-level callback, invoked when the input mode is changed.
749+ *
750+ * \param input_mode The new input mode.
751+ */
752 void unicode_input_mode_set_kb(uint8_t input_mode);
753
754+/**
755+ * \brief Begin the Unicode input sequence. The exact behavior depends on the currently selected input mode.
756+ */
757 void unicode_input_start(void);
758+
759+/**
760+ * \brief Complete the Unicode input sequence. The exact behavior depends on the currently selected input mode.
761+ */
762 void unicode_input_finish(void);
763+
764+/**
765+ * \brief Cancel the Unicode input sequence. The exact behavior depends on the currently selected input mode.
766+ */
767 void unicode_input_cancel(void);
768
769+/**
770+ * \brief Send a 16-bit hex number.
771+ *
772+ * \param hex The number to send.
773+ */
774 void register_hex(uint16_t hex);
775+
776+/**
777+ * \brief Send a 32-bit hex number.
778+ *
779+ * \param hex The number to send.
780+ */
781 void register_hex32(uint32_t hex);
782+
783+/**
784+ * \brief Input a single Unicode character. A surrogate pair will be sent if required by the input mode.
785+ *
786+ * \param code_point The code point of the character to send.
787+ */
788 void register_unicode(uint32_t code_point);
789
790+/**
791+ * \brief Send a string containing Unicode characters.
792diff --git a/quantum/unicode/unicodemap.c b/quantum/unicode/unicodemap.c
793new file mode 100644
794index 0000000000000000000000000000000000000000..2f618056a2e719018f0e08e7de1adffb6481be40
795--- /dev/null
796+++ b/quantum/unicode/unicodemap.c
797@@ -0,0 +1,43 @@
798+// Copyright 2023 QMK
799+// SPDX-License-Identifier: GPL-2.0-or-later
800+
801+#include "unicodemap.h"
802+#include "unicode.h"
803+#include "keycodes.h"
804+#include "quantum_keycodes.h"
805+#include "modifiers.h"
806+#include "host.h"
807+#include "action_util.h"
808+
809+uint8_t unicodemap_index(uint16_t keycode) {
810+ if (keycode >= QK_UNICODEMAP_PAIR) {
811+ // Keycode is a pair: extract index based on Shift / Caps Lock state
812+ uint16_t index;
813+
814+ uint8_t mods = get_mods() | get_weak_mods();
815+#ifndef NO_ACTION_ONESHOT
816+ mods |= get_oneshot_mods();
817+#endif
818+
819+ bool shift = mods & MOD_MASK_SHIFT;
820+ bool caps = host_keyboard_led_state().caps_lock;
821+ if (shift ^ caps) {
822+ index = QK_UNICODEMAP_PAIR_GET_SHIFTED_INDEX(keycode);
823+ } else {
824+ index = QK_UNICODEMAP_PAIR_GET_UNSHIFTED_INDEX(keycode);
825+ }
826+
827+ return index;
828+ } else {
829+ // Keycode is a regular index
830+ return QK_UNICODEMAP_GET_INDEX(keycode);
831+ }
832+}
833+
834+uint32_t unicodemap_get_code_point(uint8_t index) {
835+ return pgm_read_dword(unicode_map + index);
836+}
837+
838+void register_unicodemap(uint8_t index) {
839+ register_unicode(unicodemap_get_code_point(index));
840+}
841diff --git a/quantum/unicode/unicodemap.h b/quantum/unicode/unicodemap.h
842new file mode 100644
843index 0000000000000000000000000000000000000000..00d2aec8fad7988063d264673bda5815cabfd854
844--- /dev/null
845+++ b/quantum/unicode/unicodemap.h
846@@ -0,0 +1,43 @@
847+// Copyright 2023 QMK
848+// SPDX-License-Identifier: GPL-2.0-or-later
849+
850+#pragma once
851+
852+#include <stdint.h>
853+#include "progmem.h"
854+
855+/**
856+ * \file
857+ *
858+ * \defgroup unicodemap Unicode Map
859+ * \{
860+ */
861+
862+extern const uint32_t unicode_map[] PROGMEM;
863+
864+/**
865+ * \brief Get the index into the `unicode_map` array for the given keycode, respecting shift state for pair keycodes.
866+ *
867+ * \param keycode The Unicode Map keycode to get the index of.
868+ *
869+ * \return An index into the `unicode_map` array.
870+ */
871+uint8_t unicodemap_index(uint16_t keycode);
872+
873+/**
874+ * \brief Get the code point for the given index in the `unicode_map` array.
875+ *
876+ * \param index The index into the `unicode_map` array.
877+ *
878+ * \return A Unicode code point value.
879+ */
880+uint32_t unicodemap_get_code_point(uint8_t index);
881+
882+/**
883+ * \brief Send the code point for the given index in the `unicode_map` array.
884+ *
885+ * \param index The index into the `unicode_map` array.
886+ */
887+void register_unicodemap(uint8_t index);
888+
889+/** \} */
890diff --git a/tests/test_common/test_driver.cpp b/tests/test_common/test_driver.cpp
891index f1c52cb7b62bd89ac73c2fa49390bff7e2c4a0a7..0495da8205ac001eb080dadcbe24f34bc325cb3e 100644
892--- a/tests/test_common/test_driver.cpp
893+++ b/tests/test_common/test_driver.cpp
894@@ -60,7 +60,10 @@ void TestDriver::send_extra(report_extra_t* report) {
895 namespace internal {
896 void expect_unicode_code_point(TestDriver& driver, uint32_t code_point) {
897 testing::InSequence seq;
898- EXPECT_REPORT(driver, (KC_LCTL, KC_LSFT, KC_U));
899+ EXPECT_REPORT(driver, (KC_LEFT_CTRL, KC_LEFT_SHIFT));
900+ EXPECT_REPORT(driver, (KC_LEFT_CTRL, KC_LEFT_SHIFT, KC_U));
901+ EXPECT_REPORT(driver, (KC_LEFT_CTRL, KC_LEFT_SHIFT));
902+ EXPECT_EMPTY_REPORT(driver);
903
904 bool print_zero = false;
905 for (int i = 7; i >= 0; --i) {
906@@ -71,10 +74,12 @@ void expect_unicode_code_point(TestDriver& driver, uint32_t code_point) {
907 const uint8_t digit = (code_point >> (i * 4)) & 0xf;
908 if (digit || print_zero) {
909 EXPECT_REPORT(driver, (hex_digit_to_keycode(digit)));
910+ EXPECT_EMPTY_REPORT(driver);
911 print_zero = true;
912 }
913 }
914
915- EXPECT_REPORT(driver, (KC_SPC));
916+ EXPECT_REPORT(driver, (KC_SPACE));
917+ EXPECT_EMPTY_REPORT(driver);
918 }
919 } // namespace internal
920diff --git a/tests/unicode/config.h b/tests/unicode/config.h
921new file mode 100644
922index 0000000000000000000000000000000000000000..16f95f7f8db77aa7f57d895eb9b9696e5e53376f
923--- /dev/null
924+++ b/tests/unicode/config.h
925@@ -0,0 +1,8 @@
926+// Copyright 2023 QMK
927+// SPDX-License-Identifier: GPL-2.0-or-later
928+
929+#pragma once
930+
931+#include "test_common.h"
932+
933+#define UNICODE_SELECTED_MODES UNICODE_MODE_LINUX, UNICODE_MODE_MACOS
934diff --git a/tests/unicode/test.mk b/tests/unicode/test.mk
935new file mode 100644
936index 0000000000000000000000000000000000000000..1afc79be559c68931cd916da9e816ed62384dd35
937--- /dev/null
938+++ b/tests/unicode/test.mk
939@@ -0,0 +1,5 @@
940+# --------------------------------------------------------------------------------
941+# Keep this file, even if it is empty, as a marker that this folder contains tests
942+# --------------------------------------------------------------------------------
943+
944+UNICODE_COMMON = yes
945diff --git a/tests/unicode/test_unicode.cpp b/tests/unicode/test_unicode.cpp
946new file mode 100644
947index 0000000000000000000000000000000000000000..a8500e3ba1dc67fb8dac8133259949ad7f95d483
948--- /dev/null
949+++ b/tests/unicode/test_unicode.cpp
950@@ -0,0 +1,86 @@
951+// Copyright 2023 QMK
952+// SPDX-License-Identifier: GPL-2.0-or-later
953+
954+#include "keyboard_report_util.hpp"
955+#include "keycode.h"
956+#include "test_common.hpp"
957+#include "test_keymap_key.hpp"
958+
959+using testing::_;
960+
961+class Unicode : public TestFixture {};
962+
963+TEST_F(Unicode, sends_bmp_unicode_sequence) {
964+ TestDriver driver;
965+
966+ set_unicode_input_mode(UNICODE_MODE_LINUX);
967+
968+ EXPECT_UNICODE(driver, 0x03A8); // Ψ
969+ register_unicode(0x03A8);
970+
971+ VERIFY_AND_CLEAR(driver);
972+}
973+
974+TEST_F(Unicode, sends_smp_unicode_sequence) {
975+ TestDriver driver;
976+
977+ set_unicode_input_mode(UNICODE_MODE_LINUX);
978+
979+ EXPECT_UNICODE(driver, 0x1F9D9); // 🧙
980+ register_unicode(0x1F9D9);
981+
982+ VERIFY_AND_CLEAR(driver);
983+}
984+
985+TEST_F(Unicode, sends_surrogate_pair_for_macos) {
986+ TestDriver driver;
987+
988+ set_unicode_input_mode(UNICODE_MODE_MACOS);
989+
990+ // EXPECT_UNICODE() assumes Linux input mode
991+ {
992+ testing::InSequence s;
993+
994+ // Alt+D83EDDD9 🧙
995+ EXPECT_REPORT(driver, (KC_LEFT_ALT));
996+ EXPECT_REPORT(driver, (KC_D, KC_LEFT_ALT));
997+ EXPECT_REPORT(driver, (KC_LEFT_ALT));
998+ EXPECT_REPORT(driver, (KC_8, KC_LEFT_ALT));
999+ EXPECT_REPORT(driver, (KC_LEFT_ALT));
1000+ EXPECT_REPORT(driver, (KC_3, KC_LEFT_ALT));
1001+ EXPECT_REPORT(driver, (KC_LEFT_ALT));
1002+ EXPECT_REPORT(driver, (KC_E, KC_LEFT_ALT));
1003+ EXPECT_REPORT(driver, (KC_LEFT_ALT));
1004+ EXPECT_REPORT(driver, (KC_D, KC_LEFT_ALT));
1005+ EXPECT_REPORT(driver, (KC_LEFT_ALT));
1006+ EXPECT_REPORT(driver, (KC_D, KC_LEFT_ALT));
1007+ EXPECT_REPORT(driver, (KC_LEFT_ALT));
1008+ EXPECT_REPORT(driver, (KC_D, KC_LEFT_ALT));
1009+ EXPECT_REPORT(driver, (KC_LEFT_ALT));
1010+ EXPECT_REPORT(driver, (KC_9, KC_LEFT_ALT));
1011+ EXPECT_REPORT(driver, (KC_LEFT_ALT));
1012+ EXPECT_EMPTY_REPORT(driver);
1013+ }
1014+
1015+ register_unicode(0x1F9D9);
1016+
1017+ VERIFY_AND_CLEAR(driver);
1018+}
1019+
1020+TEST_F(Unicode, sends_unicode_string) {
1021+ TestDriver driver;
1022+
1023+ set_unicode_input_mode(UNICODE_MODE_LINUX);
1024+
1025+ {
1026+ testing::InSequence s;
1027+
1028+ EXPECT_UNICODE(driver, 0xFF31);
1029+ EXPECT_UNICODE(driver, 0xFF2D);
1030+ EXPECT_UNICODE(driver, 0xFF2B);
1031+ EXPECT_UNICODE(driver, 0xFF01);
1032+ }
1033+ send_unicode_string("QMK!");
1034+
1035+ VERIFY_AND_CLEAR(driver);
1036+}
1037diff --git a/tests/unicode/unicode_basic/config.h b/tests/unicode/unicode_basic/config.h
1038new file mode 100644
1039index 0000000000000000000000000000000000000000..16f95f7f8db77aa7f57d895eb9b9696e5e53376f
1040--- /dev/null
1041+++ b/tests/unicode/unicode_basic/config.h
1042@@ -0,0 +1,8 @@
1043+// Copyright 2023 QMK
1044+// SPDX-License-Identifier: GPL-2.0-or-later
1045+
1046+#pragma once
1047+
1048+#include "test_common.h"
1049+
1050+#define UNICODE_SELECTED_MODES UNICODE_MODE_LINUX, UNICODE_MODE_MACOS
1051diff --git a/tests/unicode/unicode_basic/test.mk b/tests/unicode/unicode_basic/test.mk
1052new file mode 100644
1053index 0000000000000000000000000000000000000000..f53051dce67990b6d354ba0fefa68899495147ea
1054--- /dev/null
1055+++ b/tests/unicode/unicode_basic/test.mk
1056@@ -0,0 +1,5 @@
1057+# --------------------------------------------------------------------------------
1058+# Keep this file, even if it is empty, as a marker that this folder contains tests
1059+# --------------------------------------------------------------------------------
1060+
1061+UNICODE_ENABLE = yes
1062diff --git a/tests/unicode/unicode_basic/test_unicode_basic.cpp b/tests/unicode/unicode_basic/test_unicode_basic.cpp
1063new file mode 100644
1064index 0000000000000000000000000000000000000000..598b57277f3d839e66f17a12139bf9e4274db01a
1065--- /dev/null
1066+++ b/tests/unicode/unicode_basic/test_unicode_basic.cpp
1067@@ -0,0 +1,26 @@
1068+// Copyright 2023 QMK
1069+// SPDX-License-Identifier: GPL-2.0-or-later
1070+
1071+#include "keyboard_report_util.hpp"
1072+#include "keycode.h"
1073+#include "test_common.hpp"
1074+#include "test_keymap_key.hpp"
1075+
1076+using testing::_;
1077+
1078+class UnicodeBasic : public TestFixture {};
1079+
1080+TEST_F(UnicodeBasic, sends_unicode_sequence) {
1081+ TestDriver driver;
1082+
1083+ set_unicode_input_mode(UNICODE_MODE_LINUX);
1084+
1085+ auto key_uc = KeymapKey(0, 0, 0, UC(0x03A8)); // Ψ
1086+
1087+ set_keymap({key_uc});
1088+
1089+ EXPECT_UNICODE(driver, 0x03A8);
1090+ tap_key(key_uc);
1091+
1092+ VERIFY_AND_CLEAR(driver);
1093+}
1094diff --git a/tests/unicode/unicode_map/config.h b/tests/unicode/unicode_map/config.h
1095new file mode 100644
1096index 0000000000000000000000000000000000000000..0d86922f0da786340d2895551df28188cb79b27a
1097--- /dev/null
1098+++ b/tests/unicode/unicode_map/config.h
1099@@ -0,0 +1,8 @@
1100+// Copyright 2023 QMK
1101+// SPDX-License-Identifier: GPL-2.0-or-later
1102+
1103+#pragma once
1104+
1105+#include "test_common.h"
1106+
1107+#define UNICODE_SELECTED_MODES UNICODE_MODE_LINUX
1108diff --git a/tests/unicode/unicode_map/test.mk b/tests/unicode/unicode_map/test.mk
1109new file mode 100644
1110index 0000000000000000000000000000000000000000..e6d003479988c10140216608f90245144b5db250
1111--- /dev/null
1112+++ b/tests/unicode/unicode_map/test.mk
1113@@ -0,0 +1,5 @@
1114+# --------------------------------------------------------------------------------
1115+# Keep this file, even if it is empty, as a marker that this folder contains tests
1116+# --------------------------------------------------------------------------------
1117+
1118+UNICODEMAP_ENABLE = yes
1119diff --git a/tests/unicode/unicode_map/test_unicode_map.cpp b/tests/unicode/unicode_map/test_unicode_map.cpp
1120new file mode 100644
1121index 0000000000000000000000000000000000000000..cacd8f3c757c69fbe2c8c4e23775ea2ccd0d3747
1122--- /dev/null
1123+++ b/tests/unicode/unicode_map/test_unicode_map.cpp
1124@@ -0,0 +1,54 @@
1125+// Copyright 2023 QMK
1126+// SPDX-License-Identifier: GPL-2.0-or-later
1127+
1128+#include "keyboard_report_util.hpp"
1129+#include "keycode.h"
1130+#include "test_common.hpp"
1131+#include "test_keymap_key.hpp"
1132+
1133+using testing::_;
1134+
1135+const uint32_t PROGMEM unicode_map[] = {
1136+ 0x03A8, // Ψ
1137+ 0x2318 // ⌘
1138+};
1139+
1140+class UnicodeMap : public TestFixture {};
1141+
1142+TEST_F(UnicodeMap, sends_unicodemap_code_point_from_keycode) {
1143+ TestDriver driver;
1144+
1145+ auto key_um = KeymapKey(0, 0, 0, UM(0));
1146+
1147+ set_keymap({key_um});
1148+
1149+ EXPECT_UNICODE(driver, 0x03A8);
1150+ tap_key(key_um);
1151+
1152+ VERIFY_AND_CLEAR(driver);
1153+}
1154+
1155+TEST_F(UnicodeMap, sends_unicodemap_pair_from_keycode) {
1156+ TestDriver driver;
1157+
1158+ auto key_shift = KeymapKey(0, 0, 0, KC_LEFT_SHIFT);
1159+ auto key_up = KeymapKey(0, 1, 0, UP(0, 1));
1160+
1161+ set_keymap({key_shift, key_up});
1162+
1163+ EXPECT_UNICODE(driver, 0x03A8);
1164+ tap_key(key_up);
1165+
1166+ EXPECT_REPORT(driver, (KC_LEFT_SHIFT));
1167+ key_shift.press();
1168+ run_one_scan_loop();
1169+
1170+ EXPECT_UNICODE(driver, 0x2318);
1171+ tap_key(key_up);
1172+
1173+ EXPECT_NO_REPORT(driver);
1174+ key_shift.release();
1175+ run_one_scan_loop();
1176+
1177+ VERIFY_AND_CLEAR(driver);
1178+}
1179diff --git a/tests/unicode/unicode_ucis/config.h b/tests/unicode/unicode_ucis/config.h
1180new file mode 100644
1181index 0000000000000000000000000000000000000000..0d86922f0da786340d2895551df28188cb79b27a
1182--- /dev/null
1183+++ b/tests/unicode/unicode_ucis/config.h
1184@@ -0,0 +1,8 @@
1185+// Copyright 2023 QMK
1186+// SPDX-License-Identifier: GPL-2.0-or-later
1187+
1188+#pragma once
1189+
1190+#include "test_common.h"
1191+
1192+#define UNICODE_SELECTED_MODES UNICODE_MODE_LINUX
1193diff --git a/tests/unicode/unicode_ucis/test.mk b/tests/unicode/unicode_ucis/test.mk
1194new file mode 100644
1195index 0000000000000000000000000000000000000000..d7ac6517583add9305e2b60bd188663325d77fc5
1196--- /dev/null
1197+++ b/tests/unicode/unicode_ucis/test.mk
1198@@ -0,0 +1,5 @@
1199+# --------------------------------------------------------------------------------
1200+# Keep this file, even if it is empty, as a marker that this folder contains tests
1201+# --------------------------------------------------------------------------------
1202+
1203+UCIS_ENABLE = yes
1204diff --git a/tests/unicode/unicode_ucis/test_unicode_ucis.cpp b/tests/unicode/unicode_ucis/test_unicode_ucis.cpp
1205new file mode 100644
1206index 0000000000000000000000000000000000000000..acc6329f857b4272bc982fd58297b5a7b66a1fc2
1207--- /dev/null
1208+++ b/tests/unicode/unicode_ucis/test_unicode_ucis.cpp
1209@@ -0,0 +1,221 @@
1210+// Copyright 2023 QMK
1211+// SPDX-License-Identifier: GPL-2.0-or-later
1212+
1213+#include "keyboard_report_util.hpp"
1214+#include "keycode.h"
1215+#include "test_common.hpp"
1216+#include "test_keymap_key.hpp"
1217+
1218+using testing::_;
1219+
1220+// clang-format off
1221+const ucis_symbol_t ucis_symbol_table[] = UCIS_TABLE(
1222+ UCIS_SYM("qmk", 0x03A8) // Ψ
1223+);
1224+// clang-format on
1225+
1226+class UnicodeUCIS : public TestFixture {};
1227+
1228+TEST_F(UnicodeUCIS, matches_sequence) {
1229+ TestDriver driver;
1230+
1231+ auto key_q = KeymapKey(0, 0, 0, KC_Q);
1232+ auto key_m = KeymapKey(0, 1, 0, KC_M);
1233+ auto key_k = KeymapKey(0, 2, 0, KC_K);
1234+ auto key_enter = KeymapKey(0, 3, 0, KC_ENTER);
1235+
1236+ set_keymap({key_q, key_m, key_k, key_enter});
1237+
1238+ EXPECT_UNICODE(driver, 0x2328); // ⌨
1239+ ucis_start();
1240+
1241+ EXPECT_EQ(ucis_active(), true);
1242+ EXPECT_EQ(ucis_count(), 0);
1243+
1244+ EXPECT_REPORT(driver, (KC_Q));
1245+ EXPECT_EMPTY_REPORT(driver);
1246+ tap_key(key_q);
1247+ EXPECT_EQ(ucis_count(), 1);
1248+
1249+ EXPECT_REPORT(driver, (KC_M));
1250+ EXPECT_EMPTY_REPORT(driver);
1251+ tap_key(key_m);
1252+ EXPECT_EQ(ucis_count(), 2);
1253+
1254+ EXPECT_REPORT(driver, (KC_K));
1255+ EXPECT_EMPTY_REPORT(driver);
1256+ tap_key(key_k);
1257+ EXPECT_EQ(ucis_count(), 3);
1258+
1259+ EXPECT_REPORT(driver, (KC_BACKSPACE)).Times(4);
1260+ EXPECT_EMPTY_REPORT(driver).Times(4);
1261+ EXPECT_UNICODE(driver, 0x03A8);
1262+ tap_key(key_enter);
1263+
1264+ EXPECT_EQ(ucis_active(), false);
1265+
1266+ VERIFY_AND_CLEAR(driver);
1267+}
1268+
1269+TEST_F(UnicodeUCIS, cancels_sequence) {
1270+ TestDriver driver;
1271+
1272+ auto key_q = KeymapKey(0, 0, 0, KC_Q);
1273+ auto key_m = KeymapKey(0, 1, 0, KC_M);
1274+ auto key_k = KeymapKey(0, 2, 0, KC_K);
1275+ auto key_escape = KeymapKey(0, 3, 0, KC_ESCAPE);
1276+
1277+ set_keymap({key_q, key_m, key_k, key_escape});
1278+
1279+ EXPECT_UNICODE(driver, 0x2328); // ⌨
1280+ ucis_start();
1281+
1282+ EXPECT_EQ(ucis_active(), true);
1283+ EXPECT_EQ(ucis_count(), 0);
1284+
1285+ EXPECT_REPORT(driver, (KC_Q));
1286+ EXPECT_EMPTY_REPORT(driver);
1287+ tap_key(key_q);
1288+ EXPECT_EQ(ucis_count(), 1);
1289+
1290+ EXPECT_REPORT(driver, (KC_M));
1291+ EXPECT_EMPTY_REPORT(driver);
1292+ tap_key(key_m);
1293+ EXPECT_EQ(ucis_count(), 2);
1294+
1295+ EXPECT_REPORT(driver, (KC_K));
1296+ EXPECT_EMPTY_REPORT(driver);
1297+ tap_key(key_k);
1298+ EXPECT_EQ(ucis_count(), 3);
1299+
1300+ EXPECT_NO_REPORT(driver);
1301+ tap_key(key_escape);
1302+
1303+ EXPECT_EQ(ucis_active(), false);
1304+
1305+ VERIFY_AND_CLEAR(driver);
1306+}
1307+
1308+TEST_F(UnicodeUCIS, matches_sequence_with_corrected_typo) {