qmk-dactyl-manuform-a/keyboards/butterstick/sten.c

368 lines
9.1 KiB
C

#include "sten.h"
// Chord state
uint32_t cChord = 0; // Current Chord
int chordIndex = 0; // Keys in previousachord
int32_t chordState[32]; // Full Chord history
#define QWERBUF 24 // Size of chords to buffer for output
bool repeatFlag = false; // Should we repeat?
uint32_t pChord = 0; // Previous Chord
int pChordIndex = 0; // Keys in previousachord
uint32_t pChordState[32]; // Previous chord sate
uint32_t stickyBits = 0; // Or'd with every incoming press
// Mode state
enum MODE { STENO = 0, QWERTY, COMMAND };
enum MODE pMode;
bool QWERSTENO = false;
#ifdef ONLYQWERTY
enum MODE cMode = QWERTY;
#else
enum MODE cMode = STENO;
#endif
// Command State
#define MAX_CMD_BUF 20
uint8_t CMDLEN = 0;
uint8_t CMDBUF[MAX_CMD_BUF];
// Key Repeat state
bool inChord = false;
bool repEngaged = false;
uint16_t repTimer = 0;
#define REP_INIT_DELAY 750
#define REP_DELAY 25
// Mousekeys state
bool inMouse = false;
int8_t mousePress;
// All processing done at chordUp goes through here
// Note, this is a gutted version of the Georgi sten.h
bool send_steno_chord_user(steno_mode_t mode, uint8_t chord[6]) {
// Check for mousekeys, this is release
#ifdef MOUSEKEY_ENABLE
if (inMouse) {
inMouse = false;
mousekey_off(mousePress);
mousekey_send();
}
#endif
// handle command mode
if (cChord == (LSU | LSD | RD | RZ)) {
if (cMode != COMMAND) { // Entering Command Mode
CMDLEN = 0;
pMode = cMode;
cMode = COMMAND;
} else { // Exiting Command Mode
cMode = pMode;
// Press all and release all
for (int i = 0; i < CMDLEN; i++) {
register_code(CMDBUF[i]);
}
clear_keyboard();
}
goto out;
}
// Handle Gaming Toggle,
if (cChord == (LSU | LSD | LFT | LK | RT | RS | RD | RZ) && keymapsCount > 1) {
#ifndef NO_DEBUG
uprintf("Switching to QMK\n");
#endif
layer_on(1);
goto out;
}
// Do QWERTY and Momentary QWERTY
if (cMode == QWERTY || (cMode == COMMAND)) {
processChord(false);
goto out;
}
out:
cChord = 0;
inChord = false;
chordIndex = 0;
clear_keyboard();
repEngaged = false;
for (int i = 0; i < 32; i++)
chordState[i] = 0xFFFF;
return false;
}
// Update Chord State
bool process_steno_user(uint16_t keycode, keyrecord_t *record) {
// Everything happens in here when steno keys come in.
// Bail on keyup
if (!record->event.pressed) return true;
// Update key repeat timers
repTimer = timer_read();
inChord = true;
// Switch on the press adding to chord
bool pr = record->event.pressed;
switch (keycode) {
// Mods and stuff
case STN_ST1: pr ? (cChord |= (ST1)): (cChord &= ~(ST1)); break;
case STN_ST2: pr ? (cChord |= (ST2)): (cChord &= ~(ST2)); break;
case STN_ST3: pr ? (cChord |= (ST3)): (cChord &= ~(ST3)); break;
case STN_ST4: pr ? (cChord |= (ST4)): (cChord &= ~(ST4)); break;
case STN_FN: pr ? (cChord |= (FN)) : (cChord &= ~(FN)); break;
case STN_PWR: pr ? (cChord |= (PWR)): (cChord &= ~(PWR)); break;
case STN_N1...STN_N6: pr ? (cChord |= (LNO)): (cChord &= ~(LNO)); break;
case STN_N7...STN_NC: pr ? (cChord |= (RNO)): (cChord &= ~(RNO)); break;
// All the letter keys
case STN_S1: pr ? (cChord |= (LSU)) : (cChord &= ~(LSU)); break;
case STN_S2: pr ? (cChord |= (LSD)) : (cChord &= ~(LSD)); break;
case STN_TL: pr ? (cChord |= (LFT)) : (cChord &= ~(LFT)); break;
case STN_KL: pr ? (cChord |= (LK)) : (cChord &= ~(LK)); break;
case STN_PL: pr ? (cChord |= (LP)) : (cChord &= ~(LP)); break;
case STN_WL: pr ? (cChord |= (LW)) : (cChord &= ~(LW)); break;
case STN_HL: pr ? (cChord |= (LH)) : (cChord &= ~(LH)); break;
case STN_RL: pr ? (cChord |= (LR)) : (cChord &= ~(LR)); break;
case STN_A: pr ? (cChord |= (LA)) : (cChord &= ~(LA)); break;
case STN_O: pr ? (cChord |= (LO)) : (cChord &= ~(LO)); break;
case STN_E: pr ? (cChord |= (RE)) : (cChord &= ~(RE)); break;
case STN_U: pr ? (cChord |= (RU)) : (cChord &= ~(RU)); break;
case STN_FR: pr ? (cChord |= (RF)) : (cChord &= ~(RF)); break;
case STN_RR: pr ? (cChord |= (RR)) : (cChord &= ~(RR)); break;
case STN_PR: pr ? (cChord |= (RP)) : (cChord &= ~(RP)); break;
case STN_BR: pr ? (cChord |= (RB)) : (cChord &= ~(RB)); break;
case STN_LR: pr ? (cChord |= (RL)) : (cChord &= ~(RL)); break;
case STN_GR: pr ? (cChord |= (RG)) : (cChord &= ~(RG)); break;
case STN_TR: pr ? (cChord |= (RT)) : (cChord &= ~(RT)); break;
case STN_SR: pr ? (cChord |= (RS)) : (cChord &= ~(RS)); break;
case STN_DR: pr ? (cChord |= (RD)) : (cChord &= ~(RD)); break;
case STN_ZR: pr ? (cChord |= (RZ)) : (cChord &= ~(RZ)); break;
}
// Store previous state for fastQWER
if (pr) {
chordState[chordIndex] = cChord;
chordIndex++;
}
return true;
}
void matrix_scan_user(void) {
// We abuse this for early sending of key
// Key repeat only on QWER/SYMB layers
if (cMode != QWERTY || !inChord) return;
// Check timers
#ifndef NO_REPEAT
if (repEngaged && timer_elapsed(repTimer) > REP_DELAY) {
// Process Key for report
processChord(false);
// Send report to host
send_keyboard_report();
clear_keyboard();
repTimer = timer_read();
}
if (!repEngaged && timer_elapsed(repTimer) > REP_INIT_DELAY) {
repEngaged = true;
}
#endif
};
// For Plover NKRO
uint32_t processFakeSteno(bool lookup) {
P( LSU, SEND(KC_Q););
P( LSD, SEND(KC_A););
P( LFT, SEND(KC_W););
P( LP, SEND(KC_E););
P( LH, SEND(KC_R););
P( LK, SEND(KC_S););
P( LW, SEND(KC_D););
P( LR, SEND(KC_F););
P( ST1, SEND(KC_T););
P( ST2, SEND(KC_G););
P( LA, SEND(KC_C););
P( LO, SEND(KC_V););
P( RE, SEND(KC_N););
P( RU, SEND(KC_M););
P( ST3, SEND(KC_Y););
P( ST4, SEND(KC_H););
P( RF, SEND(KC_U););
P( RP, SEND(KC_I););
P( RL, SEND(KC_O););
P( RT, SEND(KC_P););
P( RD, SEND(KC_LBRC););
P( RR, SEND(KC_J););
P( RB, SEND(KC_K););
P( RG, SEND(KC_L););
P( RS, SEND(KC_SCLN););
P( RZ, SEND(KC_COMM););
P( LNO, SEND(KC_1););
P( RNO, SEND(KC_1););
return 0;
}
// Traverse the chord history to a given point
// Returns the mask to use
void processChord(bool useFakeSteno) {
// Save the clean chord state
uint32_t savedChord = cChord;
// Apply Stick Bits if needed
if (stickyBits != 0) {
cChord |= stickyBits;
for (int i = 0; i <= chordIndex; i++)
chordState[i] |= stickyBits;
}
// Strip FN
if (cChord & FN) cChord ^= FN;
// First we test if a whole chord was passsed
// If so we just run it handling repeat logic
if (useFakeSteno && processFakeSteno(true) == cChord) {
processFakeSteno(false);
return;
} else if (processQwerty(true) == cChord) {
processQwerty(false);
// Repeat logic
if (repeatFlag) {
restoreState();
repeatFlag = false;
processChord(false);
} else {
saveState(cChord);
}
return;
}
// Iterate through chord picking out the individual
// and longest chords
uint32_t bufChords[QWERBUF];
int bufLen = 0;
uint32_t mask = 0;
// We iterate over it multiple times to catch the longest
// chord. Then that gets addded to the mask and re run.
while (savedChord != mask) {
uint32_t test = 0;
uint32_t longestChord = 0;
for (int i = 0; i <= chordIndex; i++) {
cChord = chordState[i] & ~mask;
if (cChord == 0)
continue;
// Assume mid parse Sym is new chord
if (i != 0 && test != 0 && (cChord ^ test) == PWR) {
longestChord = test;
break;
}
// Lock SYM layer in once detected
if (mask & PWR)
cChord |= PWR;
// Testing for keycodes
if (useFakeSteno) {
test = processFakeSteno(true);
} else {
test = processQwerty(true);
}
if (test != 0) {
longestChord = test;
}
}
mask |= longestChord;
bufChords[bufLen] = longestChord;
bufLen++;
// That's a loop of sorts, halt processing
if (bufLen >= QWERBUF) {
return;
}
}
// Now that the buffer is populated, we run it
for (int i = 0; i < bufLen ; i++) {
cChord = bufChords[i];
if (useFakeSteno) {
processFakeSteno(false);
} else {
processQwerty(false);
}
}
// Save state in case of repeat
if (!repeatFlag) {
saveState(savedChord);
}
// Restore cChord for held repeat
cChord = savedChord;
return;
}
void saveState(uint32_t cleanChord) {
pChord = cleanChord;
pChordIndex = chordIndex;
for (int i = 0; i < 32; i++)
pChordState[i] = chordState[i];
}
void restoreState() {
cChord = pChord;
chordIndex = pChordIndex;
for (int i = 0; i < 32; i++)
chordState[i] = pChordState[i];
}
// Macros for calling from keymap.c
void SEND(uint8_t kc) {
// Send Keycode, Does not work for Quantum Codes
if (cMode == COMMAND && CMDLEN < MAX_CMD_BUF) {
#ifndef NO_DEBUG
uprintf("CMD LEN: %d BUF: %d\n", CMDLEN, MAX_CMD_BUF);
#endif
CMDBUF[CMDLEN] = kc;
CMDLEN++;
}
if (cMode != COMMAND) register_code(kc);
return;
}
void REPEAT(void) {
if (cMode != QWERTY)
return;
repeatFlag = true;
return;
}
void SET_STICKY(uint32_t stick) {
stickyBits = stick;
return;
}
void SWITCH_LAYER(int layer) {
if (keymapsCount >= layer)
layer_on(layer);
}
void CLICK_MOUSE(uint8_t kc) {
#ifdef MOUSEKEY_ENABLE
mousekey_on(kc);
mousekey_send();
// Store state for later use
inMouse = true;
mousePress = kc;
#endif
}