Commit | Line | Data |
---|---|---|
a074364c | 1 | #include <stdint.h> |
2 | #include <stdbool.h> | |
3 | #include "action.h" | |
4 | #include "action_layer.h" | |
5 | #include "action_tapping.h" | |
6 | #include "keycode.h" | |
7 | #include "timer.h" | |
8 | ||
2bbf3d58 | 9 | #ifdef DEBUG_ACTION |
a074364c | 10 | #include "debug.h" |
2bbf3d58 JH |
11 | #else |
12 | #include "nodebug.h" | |
13 | #endif | |
a074364c | 14 | |
15 | #ifndef NO_ACTION_TAPPING | |
16 | ||
17 | #define IS_TAPPING() !IS_NOEVENT(tapping_key.event) | |
18 | #define IS_TAPPING_PRESSED() (IS_TAPPING() && tapping_key.event.pressed) | |
19 | #define IS_TAPPING_RELEASED() (IS_TAPPING() && !tapping_key.event.pressed) | |
20 | #define IS_TAPPING_KEY(k) (IS_TAPPING() && KEYEQ(tapping_key.event.key, (k))) | |
a074364c | 21 | |
5701b75e DJ |
22 | __attribute__ ((weak)) |
23 | uint16_t get_tapping_term(uint16_t keycode) { | |
24 | return TAPPING_TERM; | |
25 | } | |
26 | ||
27 | #ifdef TAPPING_TERM_PER_KEY | |
28 | #define WITHIN_TAPPING_TERM(e) (TIMER_DIFF_16(e.time, tapping_key.event.time) < get_tapping_term(get_event_keycode(tapping_key.event))) | |
29 | #else | |
30 | #define WITHIN_TAPPING_TERM(e) (TIMER_DIFF_16(e.time, tapping_key.event.time) < TAPPING_TERM) | |
31 | #endif | |
a074364c | 32 | |
33 | static keyrecord_t tapping_key = {}; | |
34 | static keyrecord_t waiting_buffer[WAITING_BUFFER_SIZE] = {}; | |
35 | static uint8_t waiting_buffer_head = 0; | |
36 | static uint8_t waiting_buffer_tail = 0; | |
37 | ||
38 | static bool process_tapping(keyrecord_t *record); | |
39 | static bool waiting_buffer_enq(keyrecord_t record); | |
40 | static void waiting_buffer_clear(void); | |
41 | static bool waiting_buffer_typed(keyevent_t event); | |
42 | static bool waiting_buffer_has_anykey_pressed(void); | |
43 | static void waiting_buffer_scan_tap(void); | |
44 | static void debug_tapping_key(void); | |
45 | static void debug_waiting_buffer(void); | |
46 | ||
47 | ||
7c9d5ace | 48 | /** \brief Action Tapping Process |
49 | * | |
50 | * FIXME: Needs doc | |
51 | */ | |
a074364c | 52 | void action_tapping_process(keyrecord_t record) |
53 | { | |
54 | if (process_tapping(&record)) { | |
55 | if (!IS_NOEVENT(record.event)) { | |
56 | debug("processed: "); debug_record(record); debug("\n"); | |
57 | } | |
58 | } else { | |
59 | if (!waiting_buffer_enq(record)) { | |
60 | // clear all in case of overflow. | |
61 | debug("OVERFLOW: CLEAR ALL STATES\n"); | |
62 | clear_keyboard(); | |
63 | waiting_buffer_clear(); | |
64 | tapping_key = (keyrecord_t){}; | |
65 | } | |
66 | } | |
67 | ||
68 | // process waiting_buffer | |
69 | if (!IS_NOEVENT(record.event) && waiting_buffer_head != waiting_buffer_tail) { | |
70 | debug("---- action_exec: process waiting_buffer -----\n"); | |
71 | } | |
72 | for (; waiting_buffer_tail != waiting_buffer_head; waiting_buffer_tail = (waiting_buffer_tail + 1) % WAITING_BUFFER_SIZE) { | |
73 | if (process_tapping(&waiting_buffer[waiting_buffer_tail])) { | |
74 | debug("processed: waiting_buffer["); debug_dec(waiting_buffer_tail); debug("] = "); | |
75 | debug_record(waiting_buffer[waiting_buffer_tail]); debug("\n\n"); | |
76 | } else { | |
77 | break; | |
78 | } | |
79 | } | |
80 | if (!IS_NOEVENT(record.event)) { | |
81 | debug("\n"); | |
82 | } | |
83 | } | |
84 | ||
85 | ||
7c9d5ace | 86 | /** \brief Tapping |
a074364c | 87 | * |
88 | * Rule: Tap key is typed(pressed and released) within TAPPING_TERM. | |
89 | * (without interfering by typing other key) | |
90 | */ | |
91 | /* return true when key event is processed or consumed. */ | |
92 | bool process_tapping(keyrecord_t *keyp) | |
93 | { | |
94 | keyevent_t event = keyp->event; | |
95 | ||
96 | // if tapping | |
97 | if (IS_TAPPING_PRESSED()) { | |
98 | if (WITHIN_TAPPING_TERM(event)) { | |
99 | if (tapping_key.tap.count == 0) { | |
100 | if (IS_TAPPING_KEY(event.key) && !event.pressed) { | |
101 | // first tap! | |
102 | debug("Tapping: First tap(0->1).\n"); | |
103 | tapping_key.tap.count = 1; | |
104 | debug_tapping_key(); | |
bf5c2cce | 105 | process_record(&tapping_key); |
a074364c | 106 | |
107 | // copy tapping state | |
108 | keyp->tap = tapping_key.tap; | |
109 | // enqueue | |
110 | return false; | |
111 | } | |
a074364c | 112 | /* Process a key typed within TAPPING_TERM |
113 | * This can register the key before settlement of tapping, | |
114 | * useful for long TAPPING_TERM but may prevent fast typing. | |
115 | */ | |
5701b75e DJ |
116 | #if defined(TAPPING_TERM_PER_KEY) || (!defined(PER_KEY_TAPPING_TERM) && TAPPING_TERM >= 500) || defined(PERMISSIVE_HOLD) |
117 | #ifdef TAPPING_TERM_PER_KEY | |
118 | else if ( ( get_tapping_term(get_event_keycode(tapping_key.event)) >= 500) && IS_RELEASED(event) && waiting_buffer_typed(event)) | |
119 | #else | |
120 | else if ( IS_RELEASED(event) && waiting_buffer_typed(event)) | |
121 | #endif | |
122 | { | |
a074364c | 123 | debug("Tapping: End. No tap. Interfered by typing key\n"); |
bf5c2cce | 124 | process_record(&tapping_key); |
a074364c | 125 | tapping_key = (keyrecord_t){}; |
126 | debug_tapping_key(); | |
127 | // enqueue | |
128 | return false; | |
129 | } | |
130 | #endif | |
131 | /* Process release event of a key pressed before tapping starts | |
132 | * Without this unexpected repeating will occur with having fast repeating setting | |
133 | * https://github.com/tmk/tmk_keyboard/issues/60 | |
134 | */ | |
135 | else if (IS_RELEASED(event) && !waiting_buffer_typed(event)) { | |
136 | // Modifier should be retained till end of this tapping. | |
137 | action_t action = layer_switch_get_action(event.key); | |
138 | switch (action.kind.id) { | |
139 | case ACT_LMODS: | |
140 | case ACT_RMODS: | |
141 | if (action.key.mods && !action.key.code) return false; | |
142 | if (IS_MOD(action.key.code)) return false; | |
143 | break; | |
144 | case ACT_LMODS_TAP: | |
145 | case ACT_RMODS_TAP: | |
146 | if (action.key.mods && keyp->tap.count == 0) return false; | |
147 | if (IS_MOD(action.key.code)) return false; | |
148 | break; | |
149 | } | |
150 | // Release of key should be process immediately. | |
151 | debug("Tapping: release event of a key pressed before tapping\n"); | |
bf5c2cce | 152 | process_record(keyp); |
a074364c | 153 | return true; |
154 | } | |
155 | else { | |
156 | // set interrupted flag when other key preesed during tapping | |
157 | if (event.pressed) { | |
158 | tapping_key.tap.interrupted = true; | |
159 | } | |
d5b72e7b | 160 | // enqueue |
a074364c | 161 | return false; |
162 | } | |
163 | } | |
164 | // tap_count > 0 | |
165 | else { | |
166 | if (IS_TAPPING_KEY(event.key) && !event.pressed) { | |
167 | debug("Tapping: Tap release("); debug_dec(tapping_key.tap.count); debug(")\n"); | |
168 | keyp->tap = tapping_key.tap; | |
bf5c2cce | 169 | process_record(keyp); |
a074364c | 170 | tapping_key = *keyp; |
171 | debug_tapping_key(); | |
172 | return true; | |
173 | } | |
174 | else if (is_tap_key(event.key) && event.pressed) { | |
175 | if (tapping_key.tap.count > 1) { | |
176 | debug("Tapping: Start new tap with releasing last tap(>1).\n"); | |
177 | // unregister key | |
bf5c2cce | 178 | process_record(&(keyrecord_t){ |
a074364c | 179 | .tap = tapping_key.tap, |
180 | .event.key = tapping_key.event.key, | |
181 | .event.time = event.time, | |
182 | .event.pressed = false | |
183 | }); | |
184 | } else { | |
185 | debug("Tapping: Start while last tap(1).\n"); | |
186 | } | |
187 | tapping_key = *keyp; | |
188 | waiting_buffer_scan_tap(); | |
189 | debug_tapping_key(); | |
190 | return true; | |
191 | } | |
192 | else { | |
193 | if (!IS_NOEVENT(event)) { | |
194 | debug("Tapping: key event while last tap(>0).\n"); | |
195 | } | |
bf5c2cce | 196 | process_record(keyp); |
a074364c | 197 | return true; |
198 | } | |
199 | } | |
200 | } | |
201 | // after TAPPING_TERM | |
202 | else { | |
203 | if (tapping_key.tap.count == 0) { | |
204 | debug("Tapping: End. Timeout. Not tap(0): "); | |
205 | debug_event(event); debug("\n"); | |
bf5c2cce | 206 | process_record(&tapping_key); |
a074364c | 207 | tapping_key = (keyrecord_t){}; |
208 | debug_tapping_key(); | |
209 | return false; | |
210 | } else { | |
211 | if (IS_TAPPING_KEY(event.key) && !event.pressed) { | |
212 | debug("Tapping: End. last timeout tap release(>0)."); | |
213 | keyp->tap = tapping_key.tap; | |
bf5c2cce | 214 | process_record(keyp); |
a074364c | 215 | tapping_key = (keyrecord_t){}; |
216 | return true; | |
217 | } | |
218 | else if (is_tap_key(event.key) && event.pressed) { | |
219 | if (tapping_key.tap.count > 1) { | |
220 | debug("Tapping: Start new tap with releasing last timeout tap(>1).\n"); | |
221 | // unregister key | |
bf5c2cce | 222 | process_record(&(keyrecord_t){ |
a074364c | 223 | .tap = tapping_key.tap, |
224 | .event.key = tapping_key.event.key, | |
225 | .event.time = event.time, | |
226 | .event.pressed = false | |
227 | }); | |
228 | } else { | |
229 | debug("Tapping: Start while last timeout tap(1).\n"); | |
230 | } | |
231 | tapping_key = *keyp; | |
232 | waiting_buffer_scan_tap(); | |
233 | debug_tapping_key(); | |
234 | return true; | |
235 | } | |
236 | else { | |
237 | if (!IS_NOEVENT(event)) { | |
238 | debug("Tapping: key event while last timeout tap(>0).\n"); | |
239 | } | |
bf5c2cce | 240 | process_record(keyp); |
a074364c | 241 | return true; |
242 | } | |
243 | } | |
244 | } | |
245 | } else if (IS_TAPPING_RELEASED()) { | |
246 | if (WITHIN_TAPPING_TERM(event)) { | |
247 | if (event.pressed) { | |
248 | if (IS_TAPPING_KEY(event.key)) { | |
64f9779f | 249 | #ifndef TAPPING_FORCE_HOLD |
a074364c | 250 | if (!tapping_key.tap.interrupted && tapping_key.tap.count > 0) { |
251 | // sequential tap. | |
252 | keyp->tap = tapping_key.tap; | |
253 | if (keyp->tap.count < 15) keyp->tap.count += 1; | |
254 | debug("Tapping: Tap press("); debug_dec(keyp->tap.count); debug(")\n"); | |
bf5c2cce | 255 | process_record(keyp); |
a074364c | 256 | tapping_key = *keyp; |
257 | debug_tapping_key(); | |
258 | return true; | |
a074364c | 259 | } |
64f9779f | 260 | #endif |
261 | // FIX: start new tap again | |
262 | tapping_key = *keyp; | |
263 | return true; | |
a074364c | 264 | } else if (is_tap_key(event.key)) { |
265 | // Sequential tap can be interfered with other tap key. | |
266 | debug("Tapping: Start with interfering other tap.\n"); | |
267 | tapping_key = *keyp; | |
268 | waiting_buffer_scan_tap(); | |
269 | debug_tapping_key(); | |
270 | return true; | |
271 | } else { | |
272 | // should none in buffer | |
273 | // FIX: interrupted when other key is pressed | |
274 | tapping_key.tap.interrupted = true; | |
bf5c2cce | 275 | process_record(keyp); |
a074364c | 276 | return true; |
277 | } | |
278 | } else { | |
d0b4dcc8 | 279 | if (!IS_NOEVENT(event)) debug("Tapping: other key just after tap.\n"); |
bf5c2cce | 280 | process_record(keyp); |
a074364c | 281 | return true; |
282 | } | |
283 | } else { | |
5d771039 | 284 | // FIX: process_action here? |
a074364c | 285 | // timeout. no sequential tap. |
286 | debug("Tapping: End(Timeout after releasing last tap): "); | |
287 | debug_event(event); debug("\n"); | |
288 | tapping_key = (keyrecord_t){}; | |
289 | debug_tapping_key(); | |
290 | return false; | |
291 | } | |
292 | } | |
293 | // not tapping state | |
294 | else { | |
295 | if (event.pressed && is_tap_key(event.key)) { | |
296 | debug("Tapping: Start(Press tap key).\n"); | |
297 | tapping_key = *keyp; | |
5d771039 | 298 | process_record_tap_hint(&tapping_key); |
a074364c | 299 | waiting_buffer_scan_tap(); |
300 | debug_tapping_key(); | |
301 | return true; | |
302 | } else { | |
bf5c2cce | 303 | process_record(keyp); |
a074364c | 304 | return true; |
305 | } | |
306 | } | |
307 | } | |
308 | ||
309 | ||
7c9d5ace | 310 | /** \brief Waiting buffer enq |
311 | * | |
312 | * FIXME: Needs docs | |
a074364c | 313 | */ |
314 | bool waiting_buffer_enq(keyrecord_t record) | |
315 | { | |
316 | if (IS_NOEVENT(record.event)) { | |
317 | return true; | |
318 | } | |
319 | ||
320 | if ((waiting_buffer_head + 1) % WAITING_BUFFER_SIZE == waiting_buffer_tail) { | |
321 | debug("waiting_buffer_enq: Over flow.\n"); | |
322 | return false; | |
323 | } | |
324 | ||
325 | waiting_buffer[waiting_buffer_head] = record; | |
326 | waiting_buffer_head = (waiting_buffer_head + 1) % WAITING_BUFFER_SIZE; | |
327 | ||
328 | debug("waiting_buffer_enq: "); debug_waiting_buffer(); | |
329 | return true; | |
330 | } | |
331 | ||
7c9d5ace | 332 | /** \brief Waiting buffer clear |
333 | * | |
334 | * FIXME: Needs docs | |
335 | */ | |
a074364c | 336 | void waiting_buffer_clear(void) |
337 | { | |
338 | waiting_buffer_head = 0; | |
339 | waiting_buffer_tail = 0; | |
340 | } | |
341 | ||
7c9d5ace | 342 | /** \brief Waiting buffer typed |
343 | * | |
344 | * FIXME: Needs docs | |
345 | */ | |
a074364c | 346 | bool waiting_buffer_typed(keyevent_t event) |
347 | { | |
348 | for (uint8_t i = waiting_buffer_tail; i != waiting_buffer_head; i = (i + 1) % WAITING_BUFFER_SIZE) { | |
349 | if (KEYEQ(event.key, waiting_buffer[i].event.key) && event.pressed != waiting_buffer[i].event.pressed) { | |
350 | return true; | |
351 | } | |
352 | } | |
353 | return false; | |
354 | } | |
355 | ||
7c9d5ace | 356 | /** \brief Waiting buffer has anykey pressed |
357 | * | |
358 | * FIXME: Needs docs | |
359 | */ | |
d5b72e7b | 360 | __attribute__((unused)) |
a074364c | 361 | bool waiting_buffer_has_anykey_pressed(void) |
362 | { | |
363 | for (uint8_t i = waiting_buffer_tail; i != waiting_buffer_head; i = (i + 1) % WAITING_BUFFER_SIZE) { | |
364 | if (waiting_buffer[i].event.pressed) return true; | |
365 | } | |
366 | return false; | |
367 | } | |
368 | ||
7c9d5ace | 369 | /** \brief Scan buffer for tapping |
370 | * | |
371 | * FIXME: Needs docs | |
372 | */ | |
a074364c | 373 | void waiting_buffer_scan_tap(void) |
374 | { | |
375 | // tapping already is settled | |
376 | if (tapping_key.tap.count > 0) return; | |
377 | // invalid state: tapping_key released && tap.count == 0 | |
378 | if (!tapping_key.event.pressed) return; | |
379 | ||
380 | for (uint8_t i = waiting_buffer_tail; i != waiting_buffer_head; i = (i + 1) % WAITING_BUFFER_SIZE) { | |
381 | if (IS_TAPPING_KEY(waiting_buffer[i].event.key) && | |
382 | !waiting_buffer[i].event.pressed && | |
383 | WITHIN_TAPPING_TERM(waiting_buffer[i].event)) { | |
384 | tapping_key.tap.count = 1; | |
385 | waiting_buffer[i].tap.count = 1; | |
bf5c2cce | 386 | process_record(&tapping_key); |
a074364c | 387 | |
388 | debug("waiting_buffer_scan_tap: found at ["); debug_dec(i); debug("]\n"); | |
389 | debug_waiting_buffer(); | |
390 | return; | |
391 | } | |
392 | } | |
393 | } | |
394 | ||
395 | ||
7c9d5ace | 396 | /** \brief Tapping key debug print |
397 | * | |
398 | * FIXME: Needs docs | |
a074364c | 399 | */ |
400 | static void debug_tapping_key(void) | |
401 | { | |
402 | debug("TAPPING_KEY="); debug_record(tapping_key); debug("\n"); | |
403 | } | |
404 | ||
7c9d5ace | 405 | /** \brief Waiting buffer debug print |
406 | * | |
407 | * FIXME: Needs docs | |
408 | */ | |
a074364c | 409 | static void debug_waiting_buffer(void) |
410 | { | |
411 | debug("{ "); | |
412 | for (uint8_t i = waiting_buffer_tail; i != waiting_buffer_head; i = (i + 1) % WAITING_BUFFER_SIZE) { | |
413 | debug("["); debug_dec(i); debug("]="); debug_record(waiting_buffer[i]); debug(" "); | |
414 | } | |
415 | debug("}\n"); | |
416 | } | |
417 | ||
418 | #endif |