diff options
Diffstat (limited to 'keyboards/butterstick/sten.c')
-rw-r--r-- | keyboards/butterstick/sten.c | 418 |
1 files changed, 418 insertions, 0 deletions
diff --git a/keyboards/butterstick/sten.c b/keyboards/butterstick/sten.c new file mode 100644 index 000000000..197abaf92 --- /dev/null +++ b/keyboards/butterstick/sten.c | |||
@@ -0,0 +1,418 @@ | |||
1 | #include "sten.h" | ||
2 | |||
3 | // Chord state | ||
4 | uint32_t cChord = 0; // Current Chord | ||
5 | int chordIndex = 0; // Keys in previousachord | ||
6 | int32_t chordState[32]; // Full Chord history | ||
7 | #define QWERBUF 24 // Size of chords to buffer for output | ||
8 | |||
9 | bool repeatFlag = false; // Should we repeat? | ||
10 | uint32_t pChord = 0; // Previous Chord | ||
11 | int pChordIndex = 0; // Keys in previousachord | ||
12 | uint32_t pChordState[32]; // Previous chord sate | ||
13 | uint32_t stickyBits = 0; // Or'd with every incoming press | ||
14 | #ifndef NO_DEBUG | ||
15 | char debugMsg[32]; | ||
16 | #endif | ||
17 | |||
18 | // StenoLayer | ||
19 | uint32_t releasedChord = 0; // Keys released from current chord | ||
20 | uint32_t tChord = 0; // Protects state of cChord | ||
21 | |||
22 | #ifndef STENOLAYERS | ||
23 | uint32_t stenoLayers[] = { PWR }; | ||
24 | size_t stenoLayerCount = sizeof(stenoLayers)/sizeof(stenoLayers[0]); | ||
25 | #endif | ||
26 | |||
27 | // Mode state | ||
28 | enum MODE { STENO = 0, QWERTY, COMMAND }; | ||
29 | enum MODE pMode; | ||
30 | bool QWERSTENO = false; | ||
31 | #ifdef ONLYQWERTY | ||
32 | enum MODE cMode = QWERTY; | ||
33 | #else | ||
34 | enum MODE cMode = STENO; | ||
35 | #endif | ||
36 | |||
37 | // Command State | ||
38 | #define MAX_CMD_BUF 20 | ||
39 | uint8_t CMDLEN = 0; | ||
40 | uint8_t CMDBUF[MAX_CMD_BUF]; | ||
41 | |||
42 | // Key Repeat state | ||
43 | bool inChord = false; | ||
44 | bool repEngaged = false; | ||
45 | uint16_t repTimer = 0; | ||
46 | #define REP_INIT_DELAY 750 | ||
47 | #define REP_DELAY 25 | ||
48 | |||
49 | // Mousekeys state | ||
50 | bool inMouse = false; | ||
51 | int8_t mousePress; | ||
52 | |||
53 | // All processing done at chordUp goes through here | ||
54 | bool send_steno_chord_user(steno_mode_t mode, uint8_t chord[6]) { | ||
55 | // Check for mousekeys, this is release | ||
56 | #ifdef MOUSEKEY_ENABLE | ||
57 | if (inMouse) { | ||
58 | inMouse = false; | ||
59 | mousekey_off(mousePress); | ||
60 | mousekey_send(); | ||
61 | } | ||
62 | #endif | ||
63 | |||
64 | // Toggle Serial/QWERTY steno | ||
65 | if (cChord == (PWR | FN | ST1 | ST2)) { | ||
66 | #ifndef NO_DEBUG | ||
67 | uprintf("Fallback Toggle\n"); | ||
68 | #endif | ||
69 | QWERSTENO = !QWERSTENO; | ||
70 | |||
71 | goto out; | ||
72 | } | ||
73 | |||
74 | // handle command mode | ||
75 | if (cChord == (PWR | FN | RD | RZ)) { | ||
76 | #ifndef NO_DEBUG | ||
77 | uprintf("COMMAND Toggle\n"); | ||
78 | #endif | ||
79 | if (cMode != COMMAND) { // Entering Command Mode | ||
80 | CMDLEN = 0; | ||
81 | pMode = cMode; | ||
82 | cMode = COMMAND; | ||
83 | } else { // Exiting Command Mode | ||
84 | cMode = pMode; | ||
85 | |||
86 | // Press all and release all | ||
87 | for (int i = 0; i < CMDLEN; i++) { | ||
88 | register_code(CMDBUF[i]); | ||
89 | } | ||
90 | clear_keyboard(); | ||
91 | } | ||
92 | |||
93 | goto out; | ||
94 | } | ||
95 | |||
96 | // Handle Gaming Toggle, | ||
97 | if (cChord == (PWR | FN | ST4 | ST3) && keymapsCount > 1) { | ||
98 | #ifndef NO_DEBUG | ||
99 | uprintf("Switching to QMK\n"); | ||
100 | #endif | ||
101 | layer_on(1); | ||
102 | goto out; | ||
103 | } | ||
104 | |||
105 | // Lone FN press, toggle QWERTY | ||
106 | #ifndef ONLYQWERTY | ||
107 | if (cChord == FN) { | ||
108 | (cMode == STENO) ? (cMode = QWERTY) : (cMode = STENO); | ||
109 | goto out; | ||
110 | } | ||
111 | #endif | ||
112 | |||
113 | // Check for Plover momentary | ||
114 | if (cMode == QWERTY && (cChord & FN)) { | ||
115 | cChord ^= FN; | ||
116 | goto steno; | ||
117 | } | ||
118 | |||
119 | // Do QWERTY and Momentary QWERTY | ||
120 | if (cMode == QWERTY || (cMode == COMMAND) || (cChord & (FN | PWR))) { | ||
121 | processChord(false); | ||
122 | goto out; | ||
123 | } | ||
124 | |||
125 | // Fallback NKRO Steno | ||
126 | if (cMode == STENO && QWERSTENO) { | ||
127 | processChord(true); | ||
128 | goto out; | ||
129 | } | ||
130 | |||
131 | steno: | ||
132 | // Hey that's a steno chord! | ||
133 | inChord = false; | ||
134 | chordIndex = 0; | ||
135 | cChord = 0; | ||
136 | return true; | ||
137 | |||
138 | out: | ||
139 | cChord = 0; | ||
140 | inChord = false; | ||
141 | chordIndex = 0; | ||
142 | clear_keyboard(); | ||
143 | repEngaged = false; | ||
144 | for (int i = 0; i < 32; i++) | ||
145 | chordState[i] = 0xFFFF; | ||
146 | |||
147 | return false; | ||
148 | } | ||
149 | |||
150 | // Update Chord State | ||
151 | bool process_steno_user(uint16_t keycode, keyrecord_t *record) { | ||
152 | // Everything happens in here when steno keys come in. | ||
153 | // Bail on keyup | ||
154 | if (!record->event.pressed) return true; | ||
155 | |||
156 | // Update key repeat timers | ||
157 | repTimer = timer_read(); | ||
158 | inChord = true; | ||
159 | |||
160 | // Switch on the press adding to chord | ||
161 | bool pr = record->event.pressed; | ||
162 | switch (keycode) { | ||
163 | // Mods and stuff | ||
164 | case STN_ST1: pr ? (cChord |= (ST1)): (cChord &= ~(ST1)); break; | ||
165 | case STN_ST2: pr ? (cChord |= (ST2)): (cChord &= ~(ST2)); break; | ||
166 | case STN_ST3: pr ? (cChord |= (ST3)): (cChord &= ~(ST3)); break; | ||
167 | case STN_ST4: pr ? (cChord |= (ST4)): (cChord &= ~(ST4)); break; | ||
168 | case STN_FN: pr ? (cChord |= (FN)) : (cChord &= ~(FN)); break; | ||
169 | case STN_PWR: pr ? (cChord |= (PWR)): (cChord &= ~(PWR)); break; | ||
170 | case STN_N1...STN_N6: pr ? (cChord |= (LNO)): (cChord &= ~(LNO)); break; | ||
171 | case STN_N7...STN_NC: pr ? (cChord |= (RNO)): (cChord &= ~(RNO)); break; | ||
172 | |||
173 | // All the letter keys | ||
174 | case STN_S1: pr ? (cChord |= (LSU)) : (cChord &= ~(LSU)); break; | ||
175 | case STN_S2: pr ? (cChord |= (LSD)) : (cChord &= ~(LSD)); break; | ||
176 | case STN_TL: pr ? (cChord |= (LFT)) : (cChord &= ~(LFT)); break; | ||
177 | case STN_KL: pr ? (cChord |= (LK)) : (cChord &= ~(LK)); break; | ||
178 | case STN_PL: pr ? (cChord |= (LP)) : (cChord &= ~(LP)); break; | ||
179 | case STN_WL: pr ? (cChord |= (LW)) : (cChord &= ~(LW)); break; | ||
180 | case STN_HL: pr ? (cChord |= (LH)) : (cChord &= ~(LH)); break; | ||
181 | case STN_RL: pr ? (cChord |= (LR)) : (cChord &= ~(LR)); break; | ||
182 | case STN_A: pr ? (cChord |= (LA)) : (cChord &= ~(LA)); break; | ||
183 | case STN_O: pr ? (cChord |= (LO)) : (cChord &= ~(LO)); break; | ||
184 | case STN_E: pr ? (cChord |= (RE)) : (cChord &= ~(RE)); break; | ||
185 | case STN_U: pr ? (cChord |= (RU)) : (cChord &= ~(RU)); break; | ||
186 | case STN_FR: pr ? (cChord |= (RF)) : (cChord &= ~(RF)); break; | ||
187 | case STN_RR: pr ? (cChord |= (RR)) : (cChord &= ~(RR)); break; | ||
188 | case STN_PR: pr ? (cChord |= (RP)) : (cChord &= ~(RP)); break; | ||
189 | case STN_BR: pr ? (cChord |= (RB)) : (cChord &= ~(RB)); break; | ||
190 | case STN_LR: pr ? (cChord |= (RL)) : (cChord &= ~(RL)); break; | ||
191 | case STN_GR: pr ? (cChord |= (RG)) : (cChord &= ~(RG)); break; | ||
192 | case STN_TR: pr ? (cChord |= (RT)) : (cChord &= ~(RT)); break; | ||
193 | case STN_SR: pr ? (cChord |= (RS)) : (cChord &= ~(RS)); break; | ||
194 | case STN_DR: pr ? (cChord |= (RD)) : (cChord &= ~(RD)); break; | ||
195 | case STN_ZR: pr ? (cChord |= (RZ)) : (cChord &= ~(RZ)); break; | ||
196 | } | ||
197 | |||
198 | // Store previous state for fastQWER | ||
199 | if (pr) { | ||
200 | chordState[chordIndex] = cChord; | ||
201 | chordIndex++; | ||
202 | } | ||
203 | |||
204 | return true; | ||
205 | } | ||
206 | void matrix_scan_user(void) { | ||
207 | // We abuse this for early sending of key | ||
208 | // Key repeat only on QWER/SYMB layers | ||
209 | if (cMode != QWERTY || !inChord) return; | ||
210 | |||
211 | // Check timers | ||
212 | #ifndef NO_REPEAT | ||
213 | if (repEngaged && timer_elapsed(repTimer) > REP_DELAY) { | ||
214 | // Process Key for report | ||
215 | processChord(false); | ||
216 | |||
217 | // Send report to host | ||
218 | send_keyboard_report(); | ||
219 | clear_keyboard(); | ||
220 | repTimer = timer_read(); | ||
221 | } | ||
222 | |||
223 | if (!repEngaged && timer_elapsed(repTimer) > REP_INIT_DELAY) { | ||
224 | repEngaged = true; | ||
225 | } | ||
226 | #endif | ||
227 | }; | ||
228 | |||
229 | // For Plover NKRO | ||
230 | uint32_t processFakeSteno(bool lookup) { | ||
231 | P( LSU, SEND(KC_Q);); | ||
232 | P( LSD, SEND(KC_A);); | ||
233 | P( LFT, SEND(KC_W);); | ||
234 | P( LP, SEND(KC_E);); | ||
235 | P( LH, SEND(KC_R);); | ||
236 | P( LK, SEND(KC_S);); | ||
237 | P( LW, SEND(KC_D);); | ||
238 | P( LR, SEND(KC_F);); | ||
239 | P( ST1, SEND(KC_T);); | ||
240 | P( ST2, SEND(KC_G);); | ||
241 | P( LA, SEND(KC_C);); | ||
242 | P( LO, SEND(KC_V);); | ||
243 | P( RE, SEND(KC_N);); | ||
244 | P( RU, SEND(KC_M);); | ||
245 | P( ST3, SEND(KC_Y);); | ||
246 | P( ST4, SEND(KC_H);); | ||
247 | P( RF, SEND(KC_U);); | ||
248 | P( RP, SEND(KC_I);); | ||
249 | P( RL, SEND(KC_O);); | ||
250 | P( RT, SEND(KC_P);); | ||
251 | P( RD, SEND(KC_LBRC);); | ||
252 | P( RR, SEND(KC_J);); | ||
253 | P( RB, SEND(KC_K);); | ||
254 | P( RG, SEND(KC_L);); | ||
255 | P( RS, SEND(KC_SCLN);); | ||
256 | P( RZ, SEND(KC_COMM);); | ||
257 | P( LNO, SEND(KC_1);); | ||
258 | P( RNO, SEND(KC_1);); | ||
259 | |||
260 | return 0; | ||
261 | } | ||
262 | |||
263 | // Traverse the chord history to a given point | ||
264 | // Returns the mask to use | ||
265 | void processChord(bool useFakeSteno) { | ||
266 | // Save the clean chord state | ||
267 | uint32_t savedChord = cChord; | ||
268 | |||
269 | // Apply Stick Bits if needed | ||
270 | if (stickyBits != 0) { | ||
271 | cChord |= stickyBits; | ||
272 | for (int i = 0; i <= chordIndex; i++) | ||
273 | chordState[i] |= stickyBits; | ||
274 | } | ||
275 | |||
276 | // Strip FN | ||
277 | if (cChord & FN) cChord ^= FN; | ||
278 | |||
279 | // First we test if a whole chord was passsed | ||
280 | // If so we just run it handling repeat logic | ||
281 | if (useFakeSteno && processFakeSteno(true) == cChord) { | ||
282 | processFakeSteno(false); | ||
283 | return; | ||
284 | } else if (processQwerty(true) == cChord) { | ||
285 | processQwerty(false); | ||
286 | // Repeat logic | ||
287 | if (repeatFlag) { | ||
288 | restoreState(); | ||
289 | repeatFlag = false; | ||
290 | processChord(false); | ||
291 | } else { | ||
292 | saveState(cChord); | ||
293 | } | ||
294 | return; | ||
295 | } | ||
296 | |||
297 | // Iterate through chord picking out the individual | ||
298 | // and longest chords | ||
299 | uint32_t bufChords[QWERBUF]; | ||
300 | int bufLen = 0; | ||
301 | uint32_t mask = 0; | ||
302 | |||
303 | // We iterate over it multiple times to catch the longest | ||
304 | // chord. Then that gets addded to the mask and re run. | ||
305 | while (savedChord != mask) { | ||
306 | uint32_t test = 0; | ||
307 | uint32_t longestChord = 0; | ||
308 | |||
309 | for (int i = 0; i <= chordIndex; i++) { | ||
310 | cChord = chordState[i] & ~mask; | ||
311 | if (cChord == 0) | ||
312 | continue; | ||
313 | |||
314 | // Assume mid parse Sym is new chord | ||
315 | if (i != 0 && test != 0 && (cChord ^ test) == PWR) { | ||
316 | longestChord = test; | ||
317 | break; | ||
318 | } | ||
319 | |||
320 | // Lock SYM layer in once detected | ||
321 | if (mask & PWR) | ||
322 | cChord |= PWR; | ||
323 | |||
324 | |||
325 | // Testing for keycodes | ||
326 | if (useFakeSteno) { | ||
327 | test = processFakeSteno(true); | ||
328 | } else { | ||
329 | test = processQwerty(true); | ||
330 | } | ||
331 | |||
332 | if (test != 0) { | ||
333 | longestChord = test; | ||
334 | } | ||
335 | } | ||
336 | |||
337 | mask |= longestChord; | ||
338 | bufChords[bufLen] = longestChord; | ||
339 | bufLen++; | ||
340 | |||
341 | // That's a loop of sorts, halt processing | ||
342 | if (bufLen >= QWERBUF) { | ||
343 | return; | ||
344 | } | ||
345 | } | ||
346 | |||
347 | // Now that the buffer is populated, we run it | ||
348 | for (int i = 0; i < bufLen ; i++) { | ||
349 | cChord = bufChords[i]; | ||
350 | if (useFakeSteno) { | ||
351 | processFakeSteno(false); | ||
352 | } else { | ||
353 | processQwerty(false); | ||
354 | } | ||
355 | } | ||
356 | |||
357 | // Save state in case of repeat | ||
358 | if (!repeatFlag) { | ||
359 | saveState(savedChord); | ||
360 | } | ||
361 | |||
362 | // Restore cChord for held repeat | ||
363 | cChord = savedChord; | ||
364 | |||
365 | return; | ||
366 | } | ||
367 | void saveState(uint32_t cleanChord) { | ||
368 | pChord = cleanChord; | ||
369 | pChordIndex = chordIndex; | ||
370 | for (int i = 0; i < 32; i++) | ||
371 | pChordState[i] = chordState[i]; | ||
372 | } | ||
373 | void restoreState() { | ||
374 | cChord = pChord; | ||
375 | chordIndex = pChordIndex; | ||
376 | for (int i = 0; i < 32; i++) | ||
377 | chordState[i] = pChordState[i]; | ||
378 | } | ||
379 | |||
380 | // Macros for calling from keymap.c | ||
381 | void SEND(uint8_t kc) { | ||
382 | // Send Keycode, Does not work for Quantum Codes | ||
383 | if (cMode == COMMAND && CMDLEN < MAX_CMD_BUF) { | ||
384 | #ifndef NO_DEBUG | ||
385 | uprintf("CMD LEN: %d BUF: %d\n", CMDLEN, MAX_CMD_BUF); | ||
386 | #endif | ||
387 | CMDBUF[CMDLEN] = kc; | ||
388 | CMDLEN++; | ||
389 | } | ||
390 | |||
391 | if (cMode != COMMAND) register_code(kc); | ||
392 | return; | ||
393 | } | ||
394 | void REPEAT(void) { | ||
395 | if (cMode != QWERTY) | ||
396 | return; | ||
397 | |||
398 | repeatFlag = true; | ||
399 | return; | ||
400 | } | ||
401 | void SET_STICKY(uint32_t stick) { | ||
402 | stickyBits = stick; | ||
403 | return; | ||
404 | } | ||
405 | void SWITCH_LAYER(int layer) { | ||
406 | if (keymapsCount >= layer) | ||
407 | layer_on(layer); | ||
408 | } | ||
409 | void CLICK_MOUSE(uint8_t kc) { | ||
410 | #ifdef MOUSEKEY_ENABLE | ||
411 | mousekey_on(kc); | ||
412 | mousekey_send(); | ||
413 | |||
414 | // Store state for later use | ||
415 | inMouse = true; | ||
416 | mousePress = kc; | ||
417 | #endif | ||
418 | } | ||