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
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
16 enum MODE { STENO = 0, QWERTY, COMMAND };
18 bool QWERSTENO = false;
20 enum MODE cMode = QWERTY;
22 enum MODE cMode = STENO;
26 #define MAX_CMD_BUF 20
28 uint8_t CMDBUF[MAX_CMD_BUF];
32 bool repEngaged = false;
33 uint16_t repTimer = 0;
34 #define REP_INIT_DELAY 750
41 // All processing done at chordUp goes through here
42 bool send_steno_chord_user(steno_mode_t mode, uint8_t chord[6]) {
43 // Check for mousekeys, this is release
44 #ifdef MOUSEKEY_ENABLE
47 mousekey_off(mousePress);
52 // Toggle Serial/QWERTY steno
53 if (cChord == (PWR | FN | ST1 | ST2)) {
55 uprintf("Fallback Toggle\n");
57 QWERSTENO = !QWERSTENO;
62 // handle command mode
63 if (cChord == (PWR | FN | RD | RZ)) {
65 uprintf("COMMAND Toggle\n");
67 if (cMode != COMMAND) { // Entering Command Mode
71 } else { // Exiting Command Mode
74 // Press all and release all
75 for (int i = 0; i < CMDLEN; i++) {
76 register_code(CMDBUF[i]);
84 // Handle Gaming Toggle,
85 if (cChord == (PWR | FN | ST4 | ST3) && keymapsCount > 1) {
87 uprintf("Switching to QMK\n");
93 // Lone FN press, toggle QWERTY
96 (cMode == STENO) ? (cMode = QWERTY) : (cMode = STENO);
101 // Check for Plover momentary
102 if (cMode == QWERTY && (cChord & FN)) {
107 // Do QWERTY and Momentary QWERTY
108 if (cMode == QWERTY || (cMode == COMMAND) || (cChord & (FN | PWR))) {
113 // Fallback NKRO Steno
114 if (cMode == STENO && QWERSTENO) {
120 // Hey that's a steno chord!
132 for (int i = 0; i < 32; i++)
133 chordState[i] = 0xFFFF;
138 // Update Chord State
139 bool process_steno_user(uint16_t keycode, keyrecord_t *record) {
140 // Everything happens in here when steno keys come in.
142 if (!record->event.pressed) return true;
144 // Update key repeat timers
145 repTimer = timer_read();
148 // Switch on the press adding to chord
149 bool pr = record->event.pressed;
152 case STN_ST1: pr ? (cChord |= (ST1)): (cChord &= ~(ST1)); break;
153 case STN_ST2: pr ? (cChord |= (ST2)): (cChord &= ~(ST2)); break;
154 case STN_ST3: pr ? (cChord |= (ST3)): (cChord &= ~(ST3)); break;
155 case STN_ST4: pr ? (cChord |= (ST4)): (cChord &= ~(ST4)); break;
156 case STN_FN: pr ? (cChord |= (FN)) : (cChord &= ~(FN)); break;
157 case STN_PWR: pr ? (cChord |= (PWR)): (cChord &= ~(PWR)); break;
158 case STN_N1...STN_N6: pr ? (cChord |= (LNO)): (cChord &= ~(LNO)); break;
159 case STN_N7...STN_NC: pr ? (cChord |= (RNO)): (cChord &= ~(RNO)); break;
161 // All the letter keys
162 case STN_S1: pr ? (cChord |= (LSU)) : (cChord &= ~(LSU)); break;
163 case STN_S2: pr ? (cChord |= (LSD)) : (cChord &= ~(LSD)); break;
164 case STN_TL: pr ? (cChord |= (LFT)) : (cChord &= ~(LFT)); break;
165 case STN_KL: pr ? (cChord |= (LK)) : (cChord &= ~(LK)); break;
166 case STN_PL: pr ? (cChord |= (LP)) : (cChord &= ~(LP)); break;
167 case STN_WL: pr ? (cChord |= (LW)) : (cChord &= ~(LW)); break;
168 case STN_HL: pr ? (cChord |= (LH)) : (cChord &= ~(LH)); break;
169 case STN_RL: pr ? (cChord |= (LR)) : (cChord &= ~(LR)); break;
170 case STN_A: pr ? (cChord |= (LA)) : (cChord &= ~(LA)); break;
171 case STN_O: pr ? (cChord |= (LO)) : (cChord &= ~(LO)); break;
172 case STN_E: pr ? (cChord |= (RE)) : (cChord &= ~(RE)); break;
173 case STN_U: pr ? (cChord |= (RU)) : (cChord &= ~(RU)); break;
174 case STN_FR: pr ? (cChord |= (RF)) : (cChord &= ~(RF)); break;
175 case STN_RR: pr ? (cChord |= (RR)) : (cChord &= ~(RR)); break;
176 case STN_PR: pr ? (cChord |= (RP)) : (cChord &= ~(RP)); break;
177 case STN_BR: pr ? (cChord |= (RB)) : (cChord &= ~(RB)); break;
178 case STN_LR: pr ? (cChord |= (RL)) : (cChord &= ~(RL)); break;
179 case STN_GR: pr ? (cChord |= (RG)) : (cChord &= ~(RG)); break;
180 case STN_TR: pr ? (cChord |= (RT)) : (cChord &= ~(RT)); break;
181 case STN_SR: pr ? (cChord |= (RS)) : (cChord &= ~(RS)); break;
182 case STN_DR: pr ? (cChord |= (RD)) : (cChord &= ~(RD)); break;
183 case STN_ZR: pr ? (cChord |= (RZ)) : (cChord &= ~(RZ)); break;
186 // Store previous state for fastQWER
188 chordState[chordIndex] = cChord;
194 void matrix_scan_user(void) {
195 // We abuse this for early sending of key
196 // Key repeat only on QWER/SYMB layers
197 if (cMode != QWERTY || !inChord) return;
201 if (repEngaged && timer_elapsed(repTimer) > REP_DELAY) {
202 // Process Key for report
205 // Send report to host
206 send_keyboard_report();
208 repTimer = timer_read();
211 if (!repEngaged && timer_elapsed(repTimer) > REP_INIT_DELAY) {
218 uint32_t processFakeSteno(bool lookup) {
219 P( LSU, SEND(KC_Q););
220 P( LSD, SEND(KC_A););
221 P( LFT, SEND(KC_W););
227 P( ST1, SEND(KC_T););
228 P( ST2, SEND(KC_G););
233 P( ST3, SEND(KC_Y););
234 P( ST4, SEND(KC_H););
239 P( RD, SEND(KC_LBRC););
243 P( RS, SEND(KC_SCLN););
244 P( RZ, SEND(KC_COMM););
245 P( LNO, SEND(KC_1););
246 P( RNO, SEND(KC_1););
251 // Traverse the chord history to a given point
252 // Returns the mask to use
253 void processChord(bool useFakeSteno) {
254 // Save the clean chord state
255 uint32_t savedChord = cChord;
257 // Apply Stick Bits if needed
258 if (stickyBits != 0) {
259 cChord |= stickyBits;
260 for (int i = 0; i <= chordIndex; i++)
261 chordState[i] |= stickyBits;
265 if (cChord & FN) cChord ^= FN;
267 // First we test if a whole chord was passsed
268 // If so we just run it handling repeat logic
269 if (useFakeSteno && processFakeSteno(true) == cChord) {
270 processFakeSteno(false);
272 } else if (processQwerty(true) == cChord) {
273 processQwerty(false);
285 // Iterate through chord picking out the individual
286 // and longest chords
287 uint32_t bufChords[QWERBUF];
291 // We iterate over it multiple times to catch the longest
292 // chord. Then that gets addded to the mask and re run.
293 while (savedChord != mask) {
295 uint32_t longestChord = 0;
297 for (int i = 0; i <= chordIndex; i++) {
298 cChord = chordState[i] & ~mask;
302 // Assume mid parse Sym is new chord
303 if (i != 0 && test != 0 && (cChord ^ test) == PWR) {
308 // Lock SYM layer in once detected
313 // Testing for keycodes
315 test = processFakeSteno(true);
317 test = processQwerty(true);
325 mask |= longestChord;
326 bufChords[bufLen] = longestChord;
329 // That's a loop of sorts, halt processing
330 if (bufLen >= QWERBUF) {
335 // Now that the buffer is populated, we run it
336 for (int i = 0; i < bufLen ; i++) {
337 cChord = bufChords[i];
339 processFakeSteno(false);
341 processQwerty(false);
345 // Save state in case of repeat
347 saveState(savedChord);
350 // Restore cChord for held repeat
355 void saveState(uint32_t cleanChord) {
357 pChordIndex = chordIndex;
358 for (int i = 0; i < 32; i++)
359 pChordState[i] = chordState[i];
361 void restoreState() {
363 chordIndex = pChordIndex;
364 for (int i = 0; i < 32; i++)
365 chordState[i] = pChordState[i];
368 // Macros for calling from keymap.c
369 void SEND(uint8_t kc) {
370 // Send Keycode, Does not work for Quantum Codes
371 if (cMode == COMMAND && CMDLEN < MAX_CMD_BUF) {
373 uprintf("CMD LEN: %d BUF: %d\n", CMDLEN, MAX_CMD_BUF);
379 if (cMode != COMMAND) register_code(kc);
389 void SET_STICKY(uint32_t stick) {
393 void SWITCH_LAYER(int layer) {
394 if (keymapsCount >= layer)
397 void CLICK_MOUSE(uint8_t kc) {
398 #ifdef MOUSEKEY_ENABLE
402 // Store state for later use