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