From 15e84f79f177d92e0faeb5e424a48506489eb955 Mon Sep 17 00:00:00 2001 From: Jeremy Bernhardt Date: Mon, 4 May 2020 10:49:47 -0600 Subject: gBoards Common (#8921) Co-Authored-By: Drashna Jaelre --- keyboards/gboards/g/engine.c | 470 +++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 470 insertions(+) create mode 100644 keyboards/gboards/g/engine.c (limited to 'keyboards/gboards/g/engine.c') diff --git a/keyboards/gboards/g/engine.c b/keyboards/gboards/g/engine.c new file mode 100644 index 0000000000..015b48d38f --- /dev/null +++ b/keyboards/gboards/g/engine.c @@ -0,0 +1,470 @@ +/* This is a stripped down version of the Georgi engine meant for use with + * Ginni. As such serial-Steno features are disabled, chords are 16bits and + * crap is removed where possible + * + * Do not use this on anything other then Ginny if you want to be sane + */ +#include "engine.h" + +// Chord state +C_SIZE cChord = 0; // Current Chord +int chordIndex = 0; // Keys in previousachord +C_SIZE pressed = 0; // number of held keys +C_SIZE chordState[32]; // Full Chord history +#define QWERBUF 24 // Size of chords to buffer for output + +bool repeatFlag = false; // Should we repeat? +C_SIZE pChord = 0; // Previous Chord +C_SIZE stickyBits = 0; // Or'd with every incoming press +int pChordIndex = 0; // Keys in previousachord +C_SIZE pChordState[32]; // Previous chord sate + +// Key Dicts +extern const struct keyEntry keyDict[]; +extern const struct comboEntry cmbDict[]; +extern const struct funcEntry funDict[]; +extern const struct stringEntry strDict[]; +extern const struct specialEntry spcDict[]; +extern size_t specialLen; +extern size_t stringLen; +extern size_t funcsLen; +extern size_t keyLen; +extern size_t comboLen; + +// Mode state +enum MODE { STENO = 0, QWERTY, COMMAND }; +enum MODE pMode; +enum MODE cMode = QWERTY; + +// 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 +void processKeysUp() { + // Check for mousekeys, this is release +#ifdef MOUSEKEY_ENABLE + if (inMouse) { + inMouse = false; + mousekey_off(mousePress); + mousekey_send(); + } +#endif + + // handle command mode +#ifdef COMMAND_MODE + if (cChord == COMMAND_MODE) { +# ifndef NO_DEBUG + uprintf("COMMAND Toggle\n"); +# endif + 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(); + } + } +#endif + + // Process and reset state + processChord(); + cChord = pressed; + inChord = false; + chordIndex = 0; + clear_keyboard(); + repEngaged = false; + for (int i = 0; i < 32; i++) chordState[i] = 0xFFFF; +} + +// Update Chord State +bool process_record_kb(uint16_t keycode, keyrecord_t *record) { + // Check if we should run at all + if (process_engine_pre(cChord, keycode, record) == false) return true; + + // Everything happens in here when steno keys come in. + // Bail on keyup + + // Update key repeat timers + repTimer = timer_read(); + bool pr = record->event.pressed; + // Switch on the press adding to chord + switch (keycode) { + ENGINE_CONFIG + default: + return true; + } + + // Handle any postprocessing + + // All keys up, send it! + if (inChord && !pr && (pressed & IN_CHORD_MASK) == 0) { + processKeysUp(); + return false; + } + if (pressed == 0 && !pr) { + processKeysUp(); + return false; + } + + cChord |= pressed; + cChord = process_engine_post(cChord, keycode, record); + inChord = (cChord & IN_CHORD_MASK) != 0; + + // Store previous state for fastQWER + if (pr) { + chordState[chordIndex] = cChord; + chordIndex++; + } + +#ifndef NO_DEBUG + uprintf("Chord: %u\n", cChord); +#endif + return false; +} +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_HOLD + if (!repEngaged && timer_elapsed(repTimer) > REP_INIT_DELAY) { + // Process Key for report + processChord(); + + // Send report to host + send_keyboard_report(); + repEngaged = true; + } +#endif +}; + +// Try and match cChord +C_SIZE mapKeys(C_SIZE chord, bool lookup) { + lookup = lookup || repEngaged; +#ifndef NO_DEBUG + if (!lookup) uprint("SENT!\n"); +#endif + // Single key chords + for (int i = 0; i < keyLen; i++) { + if (keyDict[i].chord == chord) { + if (!lookup) SEND(keyDict[i].key); + return chord; + } + } + + // strings + for (int i = 0; i < stringLen; i++) { + struct stringEntry fromPgm; + memcpy_P(&fromPgm, &strDict[i], sizeof(stringEntry_t)); + if (fromPgm.chord == chord) { + if (!lookup) { + if (get_mods() & (MOD_LSFT | MOD_RSFT)) { + set_mods(get_mods() & ~(MOD_LSFT | MOD_RSFT)); + set_oneshot_mods(MOD_LSFT); + } + send_string_P((PGM_P)(fromPgm.str)); + } + return chord; + } + } + + // combos + for (int i = 0; i < comboLen; i++) { + struct comboEntry fromPgm; + memcpy_P(&fromPgm, &cmbDict[i], sizeof(comboEntry_t)); + if (fromPgm.chord == chord) { +#ifndef NO_DEBUG + uprintf("%d found combo\n", i); +#endif + + if (!lookup) { + uint8_t comboKeys[COMBO_MAX]; + memcpy_P(&comboKeys, fromPgm.keys, sizeof(uint8_t) * COMBO_MAX); + for (int j = 0; j < COMBO_MAX; j++) +#ifndef NO_DEBUG + uprintf("Combo [%u]: %u\n", j, comboKeys[j]); +#endif + + for (int j = 0; (j < COMBO_MAX) && (comboKeys[j] != COMBO_END); j++) { +#ifndef NO_DEBUG + uprintf("Combo [%u]: %u\n", j, comboKeys[j]); +#endif + SEND(comboKeys[j]); + } + } + return chord; + } + } + + // functions + for (int i = 0; i < funcsLen; i++) { + if (funDict[i].chord == chord) { + if (!lookup) funDict[i].act(); + return chord; + } + } + + // Special handling + for (int i = 0; i < specialLen; i++) { + if (spcDict[i].chord == chord) { + if (!lookup) { + uint16_t arg = spcDict[i].arg; + switch (spcDict[i].action) { + case SPEC_STICKY: + SET_STICKY(arg); + break; + case SPEC_REPEAT: + REPEAT(); + break; + case SPEC_CLICK: + CLICK_MOUSE((uint8_t)arg); + break; + case SPEC_SWITCH: + SWITCH_LAYER(arg); + break; + default: + SEND_STRING("Invalid Special in Keymap"); + } + } + return chord; + } + } + + if ((chord & IN_CHORD_MASK) && (chord & IN_CHORD_MASK) != chord && mapKeys((chord & IN_CHORD_MASK), true) == (chord & IN_CHORD_MASK)) { +#ifndef NO_DEBUG + uprintf("Try with ignore mask:%u\n", (chord & IN_CHORD_MASK)); +#endif + mapKeys((chord & ~IN_CHORD_MASK), lookup); + mapKeys((chord & IN_CHORD_MASK), lookup); + return chord; + } +#ifndef NO_DEBUG + uprintf("Reached end\n"); +#endif + return 0; +} +// Traverse the chord history to a given point +// Returns the mask to use +void processChord(void) { + // Save the clean chord state + C_SIZE savedChord = cChord; + + // Apply Stick Bits if needed + if (stickyBits != 0) { + cChord |= stickyBits; + for (int i = 0; i <= chordIndex; i++) chordState[i] |= stickyBits; + } + + // First we test if a whole chord was passsed + // If so we just run it handling repeat logic + if (mapKeys(cChord, true) == cChord) { + mapKeys(cChord, false); + // Repeat logic + if (repeatFlag) { +#ifndef NO_DEBUG + uprintf("repeating?\n"); +#endif + restoreState(); + repeatFlag = false; + processChord(); + } else { + saveState(cChord); + } + return; + } + + C_SIZE next = process_chord_getnext(cChord); + if (next && next != cChord) { +#ifndef NO_DEBUG + uprintf("Trying next candidate: %u\n", next); +#endif + if (mapKeys(next, true) == next) { + mapKeys(next, false); + // Repeat logic + if (repeatFlag) { +#ifndef NO_DEBUG + uprintf("repeating?\n"); +#endif + restoreState(); + repeatFlag = false; + processChord(); + } else { + saveState(cChord); + } + return; + } + } + +#ifndef NO_DEBUG + uprintf("made it past the maw\n"); +#endif + + // Iterate through chord picking out the individual + // and longest chords + C_SIZE bufChords[QWERBUF]; + int bufLen = 0; + C_SIZE 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) { + C_SIZE test = 0; + C_SIZE longestChord = 0; + + for (int i = 0; i <= chordIndex; i++) { + cChord = chordState[i] & ~mask; + if (cChord == 0) continue; + + test = mapKeys(cChord, true); + if (test != 0) { + longestChord = test; + } + } + + mask |= longestChord; + bufChords[bufLen] = longestChord; + bufLen++; + + // That's a loop of sorts, halt processing + if (bufLen >= QWERBUF) { +#ifndef NO_DEBUG + uprintf("looped. exiting"); +#endif + return; + } + } + + // Now that the buffer is populated, we run it + for (int i = 0; i < bufLen; i++) { + cChord = bufChords[i]; +#ifndef NO_DEBUG + uprintf("sending: %u\n", cChord); +#endif + mapKeys(cChord, false); + } + + // Save state in case of repeat + if (!repeatFlag) { + saveState(savedChord); + } + + // Restore cChord for held repeat + cChord = savedChord; + return; +} +void saveState(C_SIZE 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(C_SIZE stick) { + stickyBits ^= stick; + return; +} +void CLICK_MOUSE(uint8_t kc) { +#ifdef MOUSEKEY_ENABLE + mousekey_on(kc); + mousekey_send(); + + // Store state for later use + inMouse = true; + mousePress = kc; +#endif +} +void SWITCH_LAYER(int layer) { +#ifndef NO_ACTION_LAYER + if (keymapsCount >= layer) { + layer_clear(); + layer_on(layer); + } +#endif +} +uint8_t bitpop_v(C_SIZE val) { +#if C_SIZE == uint8_t + return bitpop(val); +#elif C_SIZE == uint16_t + return bitpop16(val); +#elif C_SIZE == uint32_t + return bitpop32(val); +#elif C_SIZE == uint64_t + uint8_t n = 0; + if (bits >> 32) { + bits >>= 32; + n += 32; + } + if (bits >> 16) { + bits >>= 16; + n += 16; + } + if (bits >> 8) { + bits >>= 8; + n += 8; + } + if (bits >> 4) { + bits >>= 4; + n += 4; + } + if (bits >> 2) { + bits >>= 2; + n += 2; + } + if (bits >> 1) { + bits >>= 1; + n += 1; + } + return n; +#else +# error unsupported C_SIZE +#endif +} + +// See engine.h for what these hooks do +__attribute__((weak)) C_SIZE process_engine_post(C_SIZE cur_chord, uint16_t keycode, keyrecord_t *record) { return cur_chord; } +__attribute__((weak)) C_SIZE process_engine_pre(C_SIZE cur_chord, uint16_t keycode, keyrecord_t *record) { return true; } +__attribute__((weak)) C_SIZE process_chord_getnext(C_SIZE cur_chord) { return 0; } -- cgit v1.2.3