diff options
Diffstat (limited to 'keyboards/gboards/engine/engine.c')
-rw-r--r-- | keyboards/gboards/engine/engine.c | 458 |
1 files changed, 458 insertions, 0 deletions
diff --git a/keyboards/gboards/engine/engine.c b/keyboards/gboards/engine/engine.c new file mode 100644 index 000000000..9094b7905 --- /dev/null +++ b/keyboards/gboards/engine/engine.c | |||
@@ -0,0 +1,458 @@ | |||
1 | /* This is a stripped down version of the Georgi engine meant for use with | ||
2 | * Ginni. As such serial-Steno features are disabled, chords are 16bits and | ||
3 | * crap is removed where possible | ||
4 | * | ||
5 | * Do not use this on anything other then Ginny if you want to be sane | ||
6 | */ | ||
7 | #include "engine.h" | ||
8 | |||
9 | // Chord state | ||
10 | C_SIZE cChord = 0; // Current Chord | ||
11 | int chordIndex = 0; // Keys in previousachord | ||
12 | C_SIZE pressed = 0; // number of held keys | ||
13 | C_SIZE chordState[32]; // Full Chord history | ||
14 | #define QWERBUF 24 // Size of chords to buffer for output | ||
15 | |||
16 | bool repeatFlag = false; // Should we repeat? | ||
17 | C_SIZE pChord = 0; // Previous Chord | ||
18 | C_SIZE stickyBits = 0; // Or'd with every incoming press | ||
19 | int pChordIndex = 0; // Keys in previousachord | ||
20 | C_SIZE pChordState[32]; // Previous chord sate | ||
21 | |||
22 | // Key Dicts | ||
23 | extern const struct keyEntry keyDict[]; | ||
24 | extern const struct comboEntry cmbDict[]; | ||
25 | extern const struct funcEntry funDict[]; | ||
26 | extern const struct stringEntry strDict[]; | ||
27 | extern const struct specialEntry spcDict[]; | ||
28 | extern size_t specialLen; | ||
29 | extern size_t stringLen; | ||
30 | extern size_t funcsLen; | ||
31 | extern size_t keyLen; | ||
32 | extern size_t comboLen; | ||
33 | |||
34 | // Mode state | ||
35 | enum MODE { STENO = 0, QWERTY, COMMAND }; | ||
36 | enum MODE pMode; | ||
37 | enum MODE cMode = QWERTY; | ||
38 | |||
39 | // Command State | ||
40 | #define MAX_CMD_BUF 20 | ||
41 | uint8_t CMDLEN = 0; | ||
42 | uint8_t CMDBUF[MAX_CMD_BUF]; | ||
43 | |||
44 | // Key Repeat state | ||
45 | bool inChord = false; | ||
46 | bool repEngaged = false; | ||
47 | uint16_t repTimer = 0; | ||
48 | #define REP_INIT_DELAY 750 | ||
49 | #define REP_DELAY 25 | ||
50 | |||
51 | // Mousekeys state | ||
52 | bool inMouse = false; | ||
53 | int8_t mousePress; | ||
54 | |||
55 | // All processing done at chordUp goes through here | ||
56 | void processKeysUp() { | ||
57 | // Check for mousekeys, this is release | ||
58 | #ifdef MOUSEKEY_ENABLE | ||
59 | if (inMouse) { | ||
60 | inMouse = false; | ||
61 | mousekey_off(mousePress); | ||
62 | mousekey_send(); | ||
63 | } | ||
64 | #endif | ||
65 | |||
66 | // handle command mode | ||
67 | if (cChord == COMMAND_MODE) { | ||
68 | #ifndef NO_DEBUG | ||
69 | uprintf("COMMAND Toggle\n"); | ||
70 | #endif | ||
71 | if (cMode != COMMAND) { // Entering Command Mode | ||
72 | CMDLEN = 0; | ||
73 | pMode = cMode; | ||
74 | cMode = COMMAND; | ||
75 | } else { // Exiting Command Mode | ||
76 | cMode = pMode; | ||
77 | |||
78 | // Press all and release all | ||
79 | for (int i = 0; i < CMDLEN; i++) { | ||
80 | register_code(CMDBUF[i]); | ||
81 | } | ||
82 | clear_keyboard(); | ||
83 | } | ||
84 | } | ||
85 | |||
86 | // Process and reset state | ||
87 | processChord(); | ||
88 | cChord = pressed; | ||
89 | inChord = false; | ||
90 | chordIndex = 0; | ||
91 | clear_keyboard(); | ||
92 | repEngaged = false; | ||
93 | for (int i = 0; i < 32; i++) chordState[i] = 0xFFFF; | ||
94 | } | ||
95 | |||
96 | // Update Chord State | ||
97 | bool process_record_kb(uint16_t keycode, keyrecord_t *record) { | ||
98 | // Everything happens in here when steno keys come in. | ||
99 | // Bail on keyup | ||
100 | |||
101 | // Update key repeat timers | ||
102 | repTimer = timer_read(); | ||
103 | bool pr = record->event.pressed; | ||
104 | // Switch on the press adding to chord | ||
105 | switch (keycode) { | ||
106 | ENGINE_CONFIG | ||
107 | default: | ||
108 | return true; | ||
109 | } | ||
110 | |||
111 | // Handle any postprocessing | ||
112 | |||
113 | // All keys up, send it! | ||
114 | if (inChord && !pr && (pressed & IN_CHORD_MASK) == 0) { | ||
115 | processKeysUp(); | ||
116 | return false; | ||
117 | } | ||
118 | if (pressed == 0 && !pr) { | ||
119 | processKeysUp(); | ||
120 | return false; | ||
121 | } | ||
122 | |||
123 | cChord |= pressed; | ||
124 | cChord = process_engine_post(cChord, keycode, record); | ||
125 | inChord = (cChord & IN_CHORD_MASK) != 0; | ||
126 | |||
127 | // Store previous state for fastQWER | ||
128 | if (pr) { | ||
129 | chordState[chordIndex] = cChord; | ||
130 | chordIndex++; | ||
131 | } | ||
132 | |||
133 | #ifndef NO_DEBUG | ||
134 | uprintf("Chord: %u\n", cChord); | ||
135 | #endif | ||
136 | return false; | ||
137 | } | ||
138 | void matrix_scan_user(void) { | ||
139 | // We abuse this for early sending of key | ||
140 | // Key repeat only on QWER/SYMB layers | ||
141 | if (cMode != QWERTY || !inChord) return; | ||
142 | |||
143 | // Check timers | ||
144 | #ifndef NO_HOLD | ||
145 | if (!repEngaged && timer_elapsed(repTimer) > REP_INIT_DELAY) { | ||
146 | // Process Key for report | ||
147 | processChord(); | ||
148 | |||
149 | // Send report to host | ||
150 | send_keyboard_report(); | ||
151 | repEngaged = true; | ||
152 | } | ||
153 | #endif | ||
154 | }; | ||
155 | |||
156 | // Try and match cChord | ||
157 | C_SIZE mapKeys(C_SIZE chord, bool lookup) { | ||
158 | lookup = lookup || repEngaged; | ||
159 | #ifndef NO_DEBUG | ||
160 | if (!lookup) uprint("SENT!\n"); | ||
161 | #endif | ||
162 | // Single key chords | ||
163 | for (int i = 0; i < keyLen; i++) { | ||
164 | if (keyDict[i].chord == chord) { | ||
165 | if (!lookup) SEND(keyDict[i].key); | ||
166 | return chord; | ||
167 | } | ||
168 | } | ||
169 | |||
170 | // strings | ||
171 | for (int i = 0; i < stringLen; i++) { | ||
172 | struct stringEntry fromPgm; | ||
173 | memcpy_P(&fromPgm, &strDict[i], sizeof(stringEntry_t)); | ||
174 | if (fromPgm.chord == chord) { | ||
175 | if (!lookup) { | ||
176 | if (get_mods() & (MOD_LSFT | MOD_RSFT)) { | ||
177 | set_mods(get_mods() & ~(MOD_LSFT | MOD_RSFT)); | ||
178 | set_oneshot_mods(MOD_LSFT); | ||
179 | } | ||
180 | send_string_P((PGM_P)(fromPgm.str)); | ||
181 | } | ||
182 | return chord; | ||
183 | } | ||
184 | } | ||
185 | |||
186 | // combos | ||
187 | for (int i = 0; i < comboLen; i++) { | ||
188 | struct comboEntry fromPgm; | ||
189 | memcpy_P(&fromPgm, &cmbDict[i], sizeof(comboEntry_t)); | ||
190 | if (fromPgm.chord == chord) { | ||
191 | #ifndef NO_DEBUG | ||
192 | uprintf("%d found combo\n", i); | ||
193 | #endif | ||
194 | |||
195 | if (!lookup) { | ||
196 | uint8_t comboKeys[COMBO_MAX]; | ||
197 | memcpy_P(&comboKeys, fromPgm.keys, sizeof(uint8_t) * COMBO_MAX); | ||
198 | for (int j = 0; j < COMBO_MAX; j++) | ||
199 | #ifndef NO_DEBUG | ||
200 | uprintf("Combo [%u]: %u\n", j, comboKeys[j]); | ||
201 | #endif | ||
202 | |||
203 | for (int j = 0; (j < COMBO_MAX) && (comboKeys[j] != COMBO_END); j++) { | ||
204 | #ifndef NO_DEBUG | ||
205 | uprintf("Combo [%u]: %u\n", j, comboKeys[j]); | ||
206 | #endif | ||
207 | SEND(comboKeys[j]); | ||
208 | } | ||
209 | } | ||
210 | return chord; | ||
211 | } | ||
212 | } | ||
213 | |||
214 | // functions | ||
215 | for (int i = 0; i < funcsLen; i++) { | ||
216 | if (funDict[i].chord == chord) { | ||
217 | if (!lookup) funDict[i].act(); | ||
218 | return chord; | ||
219 | } | ||
220 | } | ||
221 | |||
222 | // Special handling | ||
223 | for (int i = 0; i < specialLen; i++) { | ||
224 | if (spcDict[i].chord == chord) { | ||
225 | if (!lookup) { | ||
226 | uint16_t arg = spcDict[i].arg; | ||
227 | switch (spcDict[i].action) { | ||
228 | case SPEC_STICKY: | ||
229 | SET_STICKY(arg); | ||
230 | break; | ||
231 | case SPEC_REPEAT: | ||
232 | REPEAT(); | ||
233 | break; | ||
234 | case SPEC_CLICK: | ||
235 | CLICK_MOUSE((uint8_t)arg); | ||
236 | break; | ||
237 | case SPEC_SWITCH: | ||
238 | SWITCH_LAYER(arg); | ||
239 | break; | ||
240 | default: | ||
241 | SEND_STRING("Invalid Special in Keymap"); | ||
242 | } | ||
243 | } | ||
244 | return chord; | ||
245 | } | ||
246 | } | ||
247 | |||
248 | if ((chord & IN_CHORD_MASK) && (chord & IN_CHORD_MASK) != chord && mapKeys((chord & IN_CHORD_MASK), true) == (chord & IN_CHORD_MASK)) { | ||
249 | #ifndef NO_DEBUG | ||
250 | uprintf("Try with ignore mask:%u\n", (chord & IN_CHORD_MASK)); | ||
251 | #endif | ||
252 | mapKeys((chord & ~IN_CHORD_MASK), lookup); | ||
253 | mapKeys((chord & IN_CHORD_MASK), lookup); | ||
254 | return chord; | ||
255 | } | ||
256 | #ifndef NO_DEBUG | ||
257 | uprintf("Reached end\n"); | ||
258 | #endif | ||
259 | return 0; | ||
260 | } | ||
261 | // Traverse the chord history to a given point | ||
262 | // Returns the mask to use | ||
263 | void processChord(void) { | ||
264 | // Save the clean chord state | ||
265 | C_SIZE savedChord = cChord; | ||
266 | |||
267 | // Apply Stick Bits if needed | ||
268 | if (stickyBits != 0) { | ||
269 | cChord |= stickyBits; | ||
270 | for (int i = 0; i <= chordIndex; i++) chordState[i] |= stickyBits; | ||
271 | } | ||
272 | |||
273 | // First we test if a whole chord was passsed | ||
274 | // If so we just run it handling repeat logic | ||
275 | if (mapKeys(cChord, true) == cChord) { | ||
276 | mapKeys(cChord, false); | ||
277 | // Repeat logic | ||
278 | if (repeatFlag) { | ||
279 | #ifndef NO_DEBUG | ||
280 | uprintf("repeating?\n"); | ||
281 | #endif | ||
282 | restoreState(); | ||
283 | repeatFlag = false; | ||
284 | processChord(); | ||
285 | } else { | ||
286 | saveState(cChord); | ||
287 | } | ||
288 | return; | ||
289 | } | ||
290 | |||
291 | C_SIZE next = process_chord_getnext(cChord); | ||
292 | if (next && next != cChord) { | ||
293 | #ifndef NO_DEBUG | ||
294 | uprintf("Trying next candidate: %u\n", next); | ||
295 | #endif | ||
296 | if (mapKeys(next, true) == next) { | ||
297 | mapKeys(next, false); | ||
298 | // Repeat logic | ||
299 | if (repeatFlag) { | ||
300 | #ifndef NO_DEBUG | ||
301 | uprintf("repeating?\n"); | ||
302 | #endif | ||
303 | restoreState(); | ||
304 | repeatFlag = false; | ||
305 | processChord(); | ||
306 | } else { | ||
307 | saveState(cChord); | ||
308 | } | ||
309 | return; | ||
310 | } | ||
311 | } | ||
312 | |||
313 | #ifndef NO_DEBUG | ||
314 | uprintf("made it past the maw\n"); | ||
315 | #endif | ||
316 | |||
317 | // Iterate through chord picking out the individual | ||
318 | // and longest chords | ||
319 | C_SIZE bufChords[QWERBUF]; | ||
320 | int bufLen = 0; | ||
321 | C_SIZE mask = 0; | ||
322 | |||
323 | // We iterate over it multiple times to catch the longest | ||
324 | // chord. Then that gets addded to the mask and re run. | ||
325 | while (savedChord != mask) { | ||
326 | C_SIZE test = 0; | ||
327 | C_SIZE longestChord = 0; | ||
328 | |||
329 | for (int i = 0; i <= chordIndex; i++) { | ||
330 | cChord = chordState[i] & ~mask; | ||
331 | if (cChord == 0) continue; | ||
332 | |||
333 | test = mapKeys(cChord, true); | ||
334 | if (test != 0) { | ||
335 | longestChord = test; | ||
336 | } | ||
337 | } | ||
338 | |||
339 | mask |= longestChord; | ||
340 | bufChords[bufLen] = longestChord; | ||
341 | bufLen++; | ||
342 | |||
343 | // That's a loop of sorts, halt processing | ||
344 | if (bufLen >= QWERBUF) { | ||
345 | #ifndef NO_DEBUG | ||
346 | uprintf("looped. exiting"); | ||
347 | #endif | ||
348 | return; | ||
349 | } | ||
350 | } | ||
351 | |||
352 | // Now that the buffer is populated, we run it | ||
353 | for (int i = 0; i < bufLen; i++) { | ||
354 | cChord = bufChords[i]; | ||
355 | #ifndef NO_DEBUG | ||
356 | uprintf("sending: %u\n", cChord); | ||
357 | #endif | ||
358 | mapKeys(cChord, false); | ||
359 | } | ||
360 | |||
361 | // Save state in case of repeat | ||
362 | if (!repeatFlag) { | ||
363 | saveState(savedChord); | ||
364 | } | ||
365 | |||
366 | // Restore cChord for held repeat | ||
367 | cChord = savedChord; | ||
368 | return; | ||
369 | } | ||
370 | void saveState(C_SIZE cleanChord) { | ||
371 | pChord = cleanChord; | ||
372 | pChordIndex = chordIndex; | ||
373 | for (int i = 0; i < 32; i++) pChordState[i] = chordState[i]; | ||
374 | } | ||
375 | void restoreState() { | ||
376 | cChord = pChord; | ||
377 | chordIndex = pChordIndex; | ||
378 | for (int i = 0; i < 32; i++) chordState[i] = pChordState[i]; | ||
379 | } | ||
380 | |||
381 | // Macros for calling from keymap.c | ||
382 | void SEND(uint8_t kc) { | ||
383 | // Send Keycode, Does not work for Quantum Codes | ||
384 | if (cMode == COMMAND && CMDLEN < MAX_CMD_BUF) { | ||
385 | #ifndef NO_DEBUG | ||
386 | uprintf("CMD LEN: %d BUF: %d\n", CMDLEN, MAX_CMD_BUF); | ||
387 | #endif | ||
388 | CMDBUF[CMDLEN] = kc; | ||
389 | CMDLEN++; | ||
390 | } | ||
391 | |||
392 | if (cMode != COMMAND) register_code(kc); | ||
393 | return; | ||
394 | } | ||
395 | void REPEAT(void) { | ||
396 | if (cMode != QWERTY) return; | ||
397 | |||
398 | repeatFlag = true; | ||
399 | return; | ||
400 | } | ||
401 | void SET_STICKY(C_SIZE stick) { | ||
402 | stickyBits ^= stick; | ||
403 | return; | ||
404 | } | ||
405 | void CLICK_MOUSE(uint8_t kc) { | ||
406 | #ifdef MOUSEKEY_ENABLE | ||
407 | mousekey_on(kc); | ||
408 | mousekey_send(); | ||
409 | |||
410 | // Store state for later use | ||
411 | inMouse = true; | ||
412 | mousePress = kc; | ||
413 | #endif | ||
414 | } | ||
415 | void SWITCH_LAYER(int layer) { | ||
416 | #ifndef NO_ACTION_LAYER | ||
417 | if (keymapsCount >= layer) layer_on(layer); | ||
418 | #endif | ||
419 | } | ||
420 | uint8_t bitpop_v(C_SIZE val) { | ||
421 | #if C_SIZE == uint8_t | ||
422 | return bitpop(val); | ||
423 | #elif C_SIZE == uint16_t | ||
424 | return bitpop16(val); | ||
425 | #elif C_SIZE == uint32_t | ||
426 | return bitpop32(val); | ||
427 | #elif C_SIZE == uint64_t | ||
428 | uint8_t n = 0; | ||
429 | if (bits >> 32) { | ||
430 | bits >>= 32; | ||
431 | n += 32; | ||
432 | } | ||
433 | if (bits >> 16) { | ||
434 | bits >>= 16; | ||
435 | n += 16; | ||
436 | } | ||
437 | if (bits >> 8) { | ||
438 | bits >>= 8; | ||
439 | n += 8; | ||
440 | } | ||
441 | if (bits >> 4) { | ||
442 | bits >>= 4; | ||
443 | n += 4; | ||
444 | } | ||
445 | if (bits >> 2) { | ||
446 | bits >>= 2; | ||
447 | n += 2; | ||
448 | } | ||
449 | if (bits >> 1) { | ||
450 | bits >>= 1; | ||
451 | n += 1; | ||
452 | } | ||
453 | return n; | ||
454 | #else | ||
455 | # error unsupported C_SIZE | ||
456 | #endif | ||
457 | } | ||
458 | __attribute__((weak)) C_SIZE process_engine_post(C_SIZE cur_chord, uint16_t keycode, keyrecord_t *record) { return cur_chord; } | ||