0fd99096577a57453f070f2dd94baefa09e369a4
- Author
- Filios92 <filios92@gmail.com>
- Committer
- GitHub <noreply@github.com>
- Date
Message
Diff
This diff is truncated to protect this page.
1diff --git a/docs/features/combo.md b/docs/features/combo.md
2index bdb8c4b15fcb32ae6072084044fa82e118aa46a1..10e33b72a6389e4c933e67a246645856682c41eb 100644
3--- a/docs/features/combo.md
4+++ b/docs/features/combo.md
5@@ -307,6 +307,50 @@ bool process_combo_key_release(uint16_t combo_index, combo_t *combo, uint8_t key
6 return false;
7 }
8 ```
9+
10+### Customizable key repress
11diff --git a/quantum/process_keycode/process_combo.c b/quantum/process_keycode/process_combo.c
12index b0034d136a8f233229c5b1f9c85139f02192bf4e..38171c2f54b6d36a989daf55193fe2bb545e5fc7 100644
13--- a/quantum/process_keycode/process_combo.c
14+++ b/quantum/process_keycode/process_combo.c
15@@ -65,12 +65,20 @@ __attribute__((weak)) bool process_combo_key_release(uint16_t combo_index, combo
16 }
17 #endif
18
19+#ifdef COMBO_PROCESS_KEY_REPRESS
20+__attribute__((weak)) bool process_combo_key_repress(uint16_t combo_index, combo_t *combo, uint8_t key_index, uint16_t keycode) {
21+ return false;
22+}
23+#endif
24+
25 #ifdef COMBO_SHOULD_TRIGGER
26 __attribute__((weak)) bool combo_should_trigger(uint16_t combo_index, combo_t *combo, uint16_t keycode, keyrecord_t *record) {
27 return true;
28 }
29 #endif
30
31+typedef enum { COMBO_KEY_NOT_PRESSED, COMBO_KEY_PRESSED, COMBO_KEY_REPRESSED } combo_key_action_t;
32+
33 #ifndef COMBO_NO_TIMER
34 static uint16_t timer = 0;
35 #endif
36@@ -414,14 +422,14 @@ static bool keys_pressed_in_order(uint16_t combo_index, combo_t *combo, uint16_t
37 }
38 #endif
39
40-static bool process_single_combo(combo_t *combo, uint16_t keycode, keyrecord_t *record, uint16_t combo_index) {
41+static combo_key_action_t process_single_combo(combo_t *combo, uint16_t keycode, keyrecord_t *record, uint16_t combo_index) {
42 uint8_t key_count = 0;
43 uint16_t key_index = -1;
44 _find_key_index_and_count(combo->keys, keycode, &key_index, &key_count);
45
46 /* Continue processing if key isn't part of current combo. */
47 if (-1 == (int16_t)key_index) {
48- return false;
49+ return COMBO_KEY_NOT_PRESSED;
50 }
51
52 bool key_is_part_of_combo = (!COMBO_DISABLED(combo) && is_combo_enabled()
53@@ -449,7 +457,7 @@ static bool process_single_combo(combo_t *combo, uint16_t keycode, keyrecord_t *
54 /* Don't buffer this combo if its combo term has passed. */
55 if (timer && timer_elapsed(timer) > time) {
56 DISABLE_COMBO(combo);
57- return true;
58+ return COMBO_KEY_PRESSED;
59 } else
60 #endif
61 {
62@@ -485,6 +493,15 @@ static bool process_single_combo(combo_t *combo, uint16_t keycode, keyrecord_t *
63 }
64 } // if timer elapsed end
65 }
66+#ifdef COMBO_PROCESS_KEY_REPRESS
67+ } else if (record->event.pressed) {
68+ if (COMBO_ACTIVE(combo)) {
69+ if (process_combo_key_repress(combo_index, combo, key_index, keycode)) {
70+ KEY_STATE_DOWN(combo->state, key_index);
71+ return COMBO_KEY_REPRESSED;
72+ }
73+ }
74+#endif
75 } else {
76 // chord releases
77 if (!COMBO_ACTIVE(combo) && ALL_COMBO_KEYS_ARE_DOWN(COMBO_STATE(combo), key_count)) {
78@@ -531,12 +548,12 @@ static bool process_single_combo(combo_t *combo, uint16_t keycode, keyrecord_t *
79 KEY_STATE_UP(combo->state, key_index);
80 }
81
82- return key_is_part_of_combo;
83+ return key_is_part_of_combo ? COMBO_KEY_PRESSED : COMBO_KEY_NOT_PRESSED;
84 }
85
86 bool process_combo(uint16_t keycode, keyrecord_t *record) {
87- bool is_combo_key = false;
88- bool no_combo_keys_pressed = true;
89+ uint8_t is_combo_key = COMBO_KEY_NOT_PRESSED;
90+ bool no_combo_keys_pressed = true;
91
92 if (keycode == QK_COMBO_ON && record->event.pressed) {
93 combo_enable();
94@@ -582,12 +599,17 @@ bool process_combo(uint16_t keycode, keyrecord_t *record) {
95 # endif
96 #endif
97
98- if (key_buffer_size < COMBO_KEY_BUFFER_LENGTH) {
99- key_buffer[key_buffer_size++] = (queued_record_t){
100- .record = *record,
101- .keycode = keycode,
102- .combo_index = -1, // this will be set when applying combos
103- };
104+#ifdef COMBO_PROCESS_KEY_REPRESS
105+ if (is_combo_key == COMBO_KEY_PRESSED)
106+#endif
107+ {
108+ if (key_buffer_size < COMBO_KEY_BUFFER_LENGTH) {
109+ key_buffer[key_buffer_size++] = (queued_record_t){
110+ .record = *record,
111+ .keycode = keycode,
112+ .combo_index = -1, // this will be set when applying combos
113+ };
114+ }
115diff --git a/tests/combo/combo_repress/config.h b/tests/combo/combo_repress/config.h
116new file mode 100644
117index 0000000000000000000000000000000000000000..61ba58177eb57a6b6a3e6a4db1771cb1824aca50
118--- /dev/null
119+++ b/tests/combo/combo_repress/config.h
120@@ -0,0 +1,10 @@
121+// Copyright 2024 @Filios92
122+// SPDX-License-Identifier: GPL-2.0-or-later
123+
124+#pragma once
125+
126+#include "test_common.h"
127+
128+#define TAPPING_TERM 200
129+
130+#define COMBO_PROCESS_KEY_REPRESS
131diff --git a/tests/combo/combo_repress/test.mk b/tests/combo/combo_repress/test.mk
132new file mode 100644
133index 0000000000000000000000000000000000000000..cc3f482f4bbc99b9e6a0463727e3e2f9a7675d34
134--- /dev/null
135+++ b/tests/combo/combo_repress/test.mk
136@@ -0,0 +1,6 @@
137+# Copyright 2024 @Filios92
138+# SPDX-License-Identifier: GPL-2.0-or-later
139+
140+COMBO_ENABLE = yes
141+
142+INTROSPECTION_KEYMAP_C = test_combos_repress.c
143diff --git a/tests/combo/combo_repress/test_combo.cpp b/tests/combo/combo_repress/test_combo.cpp
144new file mode 100644
145index 0000000000000000000000000000000000000000..1488d5c1bdc0e6a2e10a28b3840d679a82138cca
146--- /dev/null
147+++ b/tests/combo/combo_repress/test_combo.cpp
148@@ -0,0 +1,158 @@
149+// Copyright 2024 @Filios92
150+// SPDX-License-Identifier: GPL-2.0-or-later
151+
152+#include "keyboard_report_util.hpp"
153+#include "quantum.h"
154+#include "keycode.h"
155+#include "test_common.h"
156+#include "test_driver.hpp"
157+#include "test_fixture.hpp"
158+#include "test_keymap_key.hpp"
159+
160+using testing::_;
161+using testing::InSequence;
162+
163+class ComboRepress : public TestFixture {};
164+
165+TEST_F(ComboRepress, combo_repress_tapped) {
166+ TestDriver driver;
167+ KeymapKey key_f(0, 0, 0, KC_F);
168+ KeymapKey key_g(0, 0, 1, KC_G);
169+ set_keymap({key_f, key_g});
170+
171+ EXPECT_REPORT(driver, (KC_LEFT_ALT)).Times(2);
172+ EXPECT_REPORT(driver, (KC_TAB, KC_LEFT_ALT));
173+ EXPECT_EMPTY_REPORT(driver);
174+ tap_combo({key_f, key_g}, 20);
175+ VERIFY_AND_CLEAR(driver);
176+}
177+
178+TEST_F(ComboRepress, combo_repress_held_released_one_key_and_repressed) {
179+ TestDriver driver;
180+ KeymapKey key_f(0, 0, 0, KC_F);
181+ KeymapKey key_g(0, 0, 1, KC_G);
182+ KeymapKey key_h(0, 0, 2, KC_H);
183+ KeymapKey key_j(0, 0, 3, KC_J);
184+ set_keymap({key_f, key_g, key_h, key_j});
185+
186+ /* Press combo F+G */
187+ EXPECT_REPORT(driver, (KC_LEFT_ALT)).Times(2);
188+ EXPECT_REPORT(driver, (KC_TAB, KC_LEFT_ALT));
189+ key_f.press();
190+ run_one_scan_loop();
191+ key_g.press();
192+ run_one_scan_loop();
193+ idle_for(COMBO_TERM + 1);
194+ VERIFY_AND_CLEAR(driver);
195+
196+ /* Release G */
197+ EXPECT_NO_REPORT(driver);
198+ key_g.release();
199+ idle_for(80);
200+ VERIFY_AND_CLEAR(driver);
201+
202+ /* Tap G */
203+ EXPECT_REPORT(driver, (KC_TAB, KC_LEFT_ALT));
204+ EXPECT_REPORT(driver, (KC_LEFT_ALT));
205+ tap_key(key_g, TAPPING_TERM + 1);
206+ VERIFY_AND_CLEAR(driver);
207+
208+ /* Tap G, but hold for longer */
209+ EXPECT_REPORT(driver, (KC_TAB, KC_LEFT_ALT));
210+ EXPECT_REPORT(driver, (KC_LEFT_ALT));
211+ tap_key(key_g, TAPPING_TERM * 2);
212+ VERIFY_AND_CLEAR(driver);
213+
214+ idle_for(500);
215+
216+ /* Tap other combo while holding F */
217+ EXPECT_REPORT(driver, (KC_ESCAPE, KC_LEFT_ALT));
218+ EXPECT_REPORT(driver, (KC_LEFT_ALT));
219+ tap_combo({key_h, key_j}, TAPPING_TERM + 1);
220+ VERIFY_AND_CLEAR(driver);
221+
222+ /* G press and hold */
223+ EXPECT_REPORT(driver, (KC_TAB, KC_LEFT_ALT));
224+ EXPECT_REPORT(driver, (KC_LEFT_ALT));
225+ key_g.press();
226+ run_one_scan_loop();
227+ VERIFY_AND_CLEAR(driver);
228+
229+ /* F release and tap */
230+ EXPECT_REPORT(driver, (KC_LEFT_ALT, KC_LEFT_SHIFT)).Times(2);
231+ EXPECT_REPORT(driver, (KC_TAB, KC_LEFT_ALT, KC_LEFT_SHIFT));
232+ EXPECT_REPORT(driver, (KC_LEFT_ALT));
233+ key_f.release();
234+ run_one_scan_loop();
235+ tap_key(key_f);
236+ VERIFY_AND_CLEAR(driver);
237+
238+ /* Release G */
239+ EXPECT_EMPTY_REPORT(driver);
240+ key_g.release();
241+ run_one_scan_loop();
242+ VERIFY_AND_CLEAR(driver);
243+}
244+
245+TEST_F(ComboRepress, combo_repress_normal_combo) {
246+ TestDriver driver;
247+ KeymapKey key_f(0, 0, 0, KC_F);
248diff --git a/tests/combo/combo_repress/test_combos_repress.c b/tests/combo/combo_repress/test_combos_repress.c
249new file mode 100644
250index 0000000000000000000000000000000000000000..73fa77b0aabe2c6121ef367ef6b0e9c179d6b43d
251--- /dev/null
252+++ b/tests/combo/combo_repress/test_combos_repress.c
253@@ -0,0 +1,43 @@
254+// Copyright 2024 @Filios92
255+// SPDX-License-Identifier: GPL-2.0-or-later
256+#include "quantum.h"
257+
258+enum combos { alttab, esc };
259+
260+uint16_t const alttab_combo[] = {KC_F, KC_G, COMBO_END};
261+uint16_t const esc_combo[] = {KC_H, KC_J, COMBO_END};
262+
263+// clang-format off
264+combo_t key_combos[] = {
265+ [alttab] = COMBO(alttab_combo, KC_NO),
266+ [esc] = COMBO(esc_combo, KC_ESC)
267+};
268+// clang-format on
269+
270+void process_combo_event(uint16_t combo_index, bool pressed) {
271+ switch (combo_index) {
272+ case alttab:
273+ if (pressed) {
274+ register_mods(MOD_LALT);
275+ tap_code(KC_TAB);
276+ } else {
277+ unregister_mods(MOD_LALT);
278+ }
279+ break;
280+ }
281+}
282+
283+bool process_combo_key_repress(uint16_t combo_index, combo_t *combo, uint8_t key_index, uint16_t keycode) {
284+ switch (combo_index) {
285+ case alttab:
286+ switch (keycode) {
287+ case KC_F:
288+ tap_code16(S(KC_TAB));
289+ return true;
290+ case KC_G:
291+ tap_code(KC_TAB);
292+ return true;
293+ }
294+ }
295+ return false;
296+}