471 lines
		
	
	
		
			12 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
			
		
		
	
	
			471 lines
		
	
	
		
			12 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
/*  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; }
 |