aboutsummaryrefslogtreecommitdiff
path: root/keyboards/gboards/engine/engine.c
diff options
context:
space:
mode:
Diffstat (limited to 'keyboards/gboards/engine/engine.c')
-rw-r--r--keyboards/gboards/engine/engine.c458
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
10C_SIZE cChord = 0; // Current Chord
11int chordIndex = 0; // Keys in previousachord
12C_SIZE pressed = 0; // number of held keys
13C_SIZE chordState[32]; // Full Chord history
14#define QWERBUF 24 // Size of chords to buffer for output
15
16bool repeatFlag = false; // Should we repeat?
17C_SIZE pChord = 0; // Previous Chord
18C_SIZE stickyBits = 0; // Or'd with every incoming press
19int pChordIndex = 0; // Keys in previousachord
20C_SIZE pChordState[32]; // Previous chord sate
21
22// Key Dicts
23extern const struct keyEntry keyDict[];
24extern const struct comboEntry cmbDict[];
25extern const struct funcEntry funDict[];
26extern const struct stringEntry strDict[];
27extern const struct specialEntry spcDict[];
28extern size_t specialLen;
29extern size_t stringLen;
30extern size_t funcsLen;
31extern size_t keyLen;
32extern size_t comboLen;
33
34// Mode state
35enum MODE { STENO = 0, QWERTY, COMMAND };
36enum MODE pMode;
37enum MODE cMode = QWERTY;
38
39// Command State
40#define MAX_CMD_BUF 20
41uint8_t CMDLEN = 0;
42uint8_t CMDBUF[MAX_CMD_BUF];
43
44// Key Repeat state
45bool inChord = false;
46bool repEngaged = false;
47uint16_t repTimer = 0;
48#define REP_INIT_DELAY 750
49#define REP_DELAY 25
50
51// Mousekeys state
52bool inMouse = false;
53int8_t mousePress;
54
55// All processing done at chordUp goes through here
56void 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
97bool 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}
138void 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
157C_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
263void 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}
370void saveState(C_SIZE cleanChord) {
371 pChord = cleanChord;
372 pChordIndex = chordIndex;
373 for (int i = 0; i < 32; i++) pChordState[i] = chordState[i];
374}
375void 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
382void 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}
395void REPEAT(void) {
396 if (cMode != QWERTY) return;
397
398 repeatFlag = true;
399 return;
400}
401void SET_STICKY(C_SIZE stick) {
402 stickyBits ^= stick;
403 return;
404}
405void 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}
415void SWITCH_LAYER(int layer) {
416#ifndef NO_ACTION_LAYER
417 if (keymapsCount >= layer) layer_on(layer);
418#endif
419}
420uint8_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; }