aboutsummaryrefslogtreecommitdiff
path: root/keyboards/butterstick/sten.c
diff options
context:
space:
mode:
Diffstat (limited to 'keyboards/butterstick/sten.c')
-rw-r--r--keyboards/butterstick/sten.c418
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
4uint32_t cChord = 0; // Current Chord
5int chordIndex = 0; // Keys in previousachord
6int32_t chordState[32]; // Full Chord history
7#define QWERBUF 24 // Size of chords to buffer for output
8
9bool repeatFlag = false; // Should we repeat?
10uint32_t pChord = 0; // Previous Chord
11int pChordIndex = 0; // Keys in previousachord
12uint32_t pChordState[32]; // Previous chord sate
13uint32_t stickyBits = 0; // Or'd with every incoming press
14#ifndef NO_DEBUG
15char debugMsg[32];
16#endif
17
18// StenoLayer
19uint32_t releasedChord = 0; // Keys released from current chord
20uint32_t tChord = 0; // Protects state of cChord
21
22#ifndef STENOLAYERS
23uint32_t stenoLayers[] = { PWR };
24size_t stenoLayerCount = sizeof(stenoLayers)/sizeof(stenoLayers[0]);
25#endif
26
27// Mode state
28enum MODE { STENO = 0, QWERTY, COMMAND };
29enum MODE pMode;
30bool QWERSTENO = false;
31#ifdef ONLYQWERTY
32enum MODE cMode = QWERTY;
33#else
34enum MODE cMode = STENO;
35#endif
36
37// Command State
38#define MAX_CMD_BUF 20
39uint8_t CMDLEN = 0;
40uint8_t CMDBUF[MAX_CMD_BUF];
41
42// Key Repeat state
43bool inChord = false;
44bool repEngaged = false;
45uint16_t repTimer = 0;
46#define REP_INIT_DELAY 750
47#define REP_DELAY 25
48
49// Mousekeys state
50bool inMouse = false;
51int8_t mousePress;
52
53// All processing done at chordUp goes through here
54bool 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
131steno:
132 // Hey that's a steno chord!
133 inChord = false;
134 chordIndex = 0;
135 cChord = 0;
136 return true;
137
138out:
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
151bool 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}
206void 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
230uint32_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
265void 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}
367void saveState(uint32_t cleanChord) {
368 pChord = cleanChord;
369 pChordIndex = chordIndex;
370 for (int i = 0; i < 32; i++)
371 pChordState[i] = chordState[i];
372}
373void 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
381void 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}
394void REPEAT(void) {
395 if (cMode != QWERTY)
396 return;
397
398 repeatFlag = true;
399 return;
400}
401void SET_STICKY(uint32_t stick) {
402 stickyBits = stick;
403 return;
404}
405void SWITCH_LAYER(int layer) {
406 if (keymapsCount >= layer)
407 layer_on(layer);
408}
409void 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}