* Working on chording * Working on chording * Got layouts in order * Initial Georgi support * forgot to add keymaps * Updated readme * Update keyboards/georgi/keymaps/template/readme.md Co-Authored-By: germ <jeremythegeek@gmail.com> * Update keyboards/georgi/georgi.h Co-Authored-By: germ <jeremythegeek@gmail.com> * Update keyboards/georgi/keymaps/default/keymap.c Co-Authored-By: germ <jeremythegeek@gmail.com> * Update keyboards/georgi/keymaps/default/keymap.c Co-Authored-By: germ <jeremythegeek@gmail.com> * Update keyboards/georgi/rules.mk Co-Authored-By: germ <jeremythegeek@gmail.com> * Update keyboards/georgi/rules.mk Co-Authored-By: germ <jeremythegeek@gmail.com> * Update keyboards/georgi/matrix.c Co-Authored-By: germ <jeremythegeek@gmail.com> * Update keyboards/georgi/georgi.c Co-Authored-By: germ <jeremythegeek@gmail.com> * Update keyboards/georgi/georgi.c Co-Authored-By: germ <jeremythegeek@gmail.com> * Update keyboards/georgi/rules.mk Co-Authored-By: germ <jeremythegeek@gmail.com> * Update keyboards/georgi/keymaps/default/keymap.c Co-Authored-By: germ <jeremythegeek@gmail.com> * Update keyboards/georgi/keymaps/template/keymap.c Co-Authored-By: germ <jeremythegeek@gmail.com> * Update keyboards/georgi/matrix.c Co-Authored-By: germ <jeremythegeek@gmail.com> * Disabled features, updated info * Update keyboards/georgi/config.h Co-Authored-By: germ <jeremythegeek@gmail.com> * Update keyboards/georgi/config.h Co-Authored-By: germ <jeremythegeek@gmail.com> * Fixed info.json * Split the number button and fixed gaming mode. * started work on history feature * Working history/multikeyfuckery * type * inital code reduction refactor * Got multikey patched up, optimizing for size * Forgot to remove stuff * fixed key repeat * Key repeat added. * Symshift locking * Midchord Sym shenanigans. * Added only QWERTY mode * Split out header * Added stickybits, minimal layour * Fixing user layout * Whitespace fixing * Fixing Version name * Starting work on BS * Fixing default layout and rules * Updated Butter fw * Copy-paste rebase * more fixing from merge. Fuck * Forgot to roll version * Added revisions as per @mechmerlinpull/5743/head
@ -0,0 +1,56 @@ | |||
/* Copyright 2019 Jeremy Bernhardt | |||
* | |||
* This program is free software: you can redistribute it and/or modify | |||
* it under the terms of the GNU General Public License as published by | |||
* the Free Software Foundation, either version 2 of the License, or | |||
* (at your option) any later version. | |||
* | |||
* This program is distributed in the hope that it will be useful, | |||
* but WITHOUT ANY WARRANTY; without even the implied warranty of | |||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |||
* GNU General Public License for more details. | |||
* | |||
* You should have received a copy of the GNU General Public License | |||
* along with this program. If not, see <http://www.gnu.org/licenses/>. | |||
*/ | |||
#include "butterstick.h" | |||
// Optional override functions below. | |||
// You can leave any or all of these undefined. | |||
// These are only required if you want to perform custom actions. | |||
/* | |||
void matrix_init_kb(void) { | |||
// put your keyboard start-up code here | |||
// runs once when the firmware starts up | |||
matrix_init_user(); | |||
} | |||
*/ | |||
void matrix_scan_kb(void) { | |||
#ifdef DEBUG_MATRIX | |||
for (uint8_t c = 0; c < MATRIX_COLS; c++) | |||
for (uint8_t r = 0; r < MATRIX_ROWS; r++) | |||
if (matrix_is_on(r, c)) xprintf("r:%d c:%d \n", r, c); | |||
#endif | |||
matrix_scan_user(); | |||
} | |||
/* | |||
bool process_record_kb(uint16_t keycode, keyrecord_t *record) { | |||
// put your per-action keyboard code here | |||
// runs for every action, just before processing by the firmware | |||
return process_record_user(keycode, record); | |||
} | |||
void led_set_kb(uint8_t usb_led) { | |||
// put your keyboard LED indicator (ex: Caps Lock LED) toggling code here | |||
led_set_user(usb_led); | |||
} | |||
*/ |
@ -0,0 +1,11 @@ | |||
#pragma once | |||
#include "quantum.h" | |||
#define LAYOUT_butter( \ | |||
k09, k08, k07, k06, k05, k04, k03, k02, k01, k00, \ | |||
k19, k18, k17, k16, k15, k14, k13, k12, k11, k10 \ | |||
) { \ | |||
{ k00, k01, k02, k03, k04, k05, k06, k07, k08, k09}, \ | |||
{ k10, k11, k12, k13, k14, k15, k16, k17, k18, k19}, \ | |||
} |
@ -0,0 +1,26 @@ | |||
#pragma once | |||
#include "config_common.h" | |||
/* USB Device descriptor parameter */ | |||
#define VENDOR_ID 0xFEED | |||
#define PRODUCT_ID 0x1337 | |||
#define DEVICE_VER 0x0001 | |||
#define MANUFACTURER g Heavy Industries | |||
#define PRODUCT Butter Stick | |||
#define DESCRIPTION Its a stick of butter | |||
#define VERSION "Paula Deen" | |||
#define DEBOUNCING_DELAY 5 | |||
#define FORCE_NKRO | |||
/* key matrix size */ | |||
#define MATRIX_ROWS 2 | |||
#define MATRIX_COLS 10 | |||
#define MATRIX_ROW_PINS { F4, F5 } | |||
#define MATRIX_COL_PINS { B0, B1, B2, B3, B4, B5, B6, B7, C6, C7} | |||
#define UNUSED_PINS | |||
/* COL2ROW, ROW2COL*/ | |||
#define DIODE_DIRECTION ROW2COL | |||
@ -0,0 +1,183 @@ | |||
#include QMK_KEYBOARD_H | |||
#include "sten.h" | |||
/* | |||
* Key names are inherited from steno machines | |||
* .-----------------------------------------------------. | |||
* | LSU | LFT | LP | LH | ST1 | RF | RP | RL | RT | RD | | |||
* |-----------------------------------------------------| | |||
* | LSD | LK | LW | LR | ST2 | RR | RB | RG | RS | RZ | | |||
* '-----------------------------------------------------' | |||
*/ | |||
// Function prefixes | |||
#define MEDIA (LSD | LK | LW | LR) | |||
#define FUNCT (LSD | LK | LP | LH) | |||
#define MOVE (LSU | LFT | LP | LH) | |||
#define SYMB (RD | RZ) | |||
#define NUMA (LW | LR) | |||
#define NUMB (RR | RB) | |||
// QMK Layer Numbers | |||
#define BASE 0 | |||
#define GAME 1 | |||
// Do not change QMK Layer 0! This is your main keyboard. | |||
// Make your QMK modifications to the later layers, to add | |||
// keys/customize on the first layer modify processQwerty(): | |||
const uint16_t PROGMEM keymaps[][MATRIX_ROWS][MATRIX_COLS] = { | |||
[BASE] = LAYOUT_butter( | |||
STN_S1, STN_TL, STN_PL, STN_HL, STN_ST1, STN_FR, STN_PR, STN_LR, STN_TR, STN_DR, | |||
STN_S2, STN_KL, STN_WL, STN_RL, STN_ST2, STN_RR, STN_BR, STN_GR, STN_SR, STN_ZR | |||
), | |||
// I don't game don't roast me thanks | |||
[GAME] = LAYOUT_butter( | |||
KC_Q, KC_W, KC_E, KC_R, KC_T, KC_Y, KC_U, KC_I, KC_O, KC_ENT, | |||
KC_A, KC_S, KC_D, KC_F, KC_G, KC_H, KC_J, KC_K, KC_L, TO(BASE) | |||
) | |||
}; | |||
// Note: You can only use basic keycodes here! | |||
// P() is just a wrapper to make your life easier, any C code can be executed. | |||
// Only the longest matched chord is run! | |||
// | |||
// http://docs.gboards.ca | |||
uint32_t processQwerty(bool lookup) { | |||
// SECRET AGENT CHORDS | |||
P( LSU | LK | RS | RD, SEND_STRING(VERSION); SEND_STRING(__DATE__)); | |||
P( LSD | RZ, SEND(KC_SPC)); | |||
// Dual chords | |||
P( LP | LH, CLICK_MOUSE(KC_MS_BTN2)); | |||
P( ST1 | RF, CLICK_MOUSE(KC_MS_BTN1)); | |||
P( LSU | LFT, SEND(KC_ESC)); | |||
P( LSD | LK, SEND(KC_LSFT)); | |||
P( RZ | RS, SEND(KC_LSFT)); | |||
P( ST2 | RR, SEND(KC_SPC)); | |||
P( RP | RL, SEND(KC_LGUI)); | |||
P( RT | RD, SEND(KC_LCTL)); | |||
P( RL | RT, SEND(KC_LALT)); | |||
P( LSU | LSD | LFT | LK, SEND(KC_LCTL)); | |||
P( RS | RT | RD | RZ, SEND(KC_ENT)); | |||
// Function Layer | |||
P( FUNCT | RF, SEND(KC_F1)); | |||
P( FUNCT | RP, SEND(KC_F2)); | |||
P( FUNCT | RL, SEND(KC_F3)); | |||
P( FUNCT | RT, SEND(KC_F4)); | |||
P( FUNCT | RF | RR, SEND(KC_F5)); | |||
P( FUNCT | RP | RB, SEND(KC_F6)); | |||
P( FUNCT | RL | RG, SEND(KC_F7)); | |||
P( FUNCT | RT | RS, SEND(KC_F8)); | |||
P( FUNCT | RR, SEND(KC_F9)); | |||
P( FUNCT | RB, SEND(KC_F10)); | |||
P( FUNCT | RG, SEND(KC_F11)); | |||
P( FUNCT | RS, SEND(KC_F12)); | |||
// Movement Layer | |||
P( MOVE | RF, SEND(KC_LEFT)); | |||
P( MOVE | RP, SEND(KC_DOWN)); | |||
P( MOVE | RL, SEND(KC_UP)); | |||
P( MOVE | RT, SEND(KC_RIGHT)); | |||
P( MOVE | ST1, SEND(KC_PGUP)); | |||
P( MOVE | ST2, SEND(KC_PGDN)); | |||
// Media Layer | |||
P( MEDIA | RF, SEND(KC_MPRV)); | |||
P( MEDIA | RP, SEND(KC_MPLY)); | |||
P( MEDIA | RL, SEND(KC_MPLY)); | |||
P( MEDIA | RT, SEND(KC_MNXT)); | |||
P( MEDIA | RG, SEND(KC_VOLU)); | |||
P( MEDIA | RB, SEND(KC_VOLD)); | |||
P( MEDIA | RS, SEND(KC_MUTE)); | |||
// Number Row, Right | |||
P( NUMB | LSU, SEND(KC_1)); | |||
P( NUMB | LFT, SEND(KC_2)); | |||
P( NUMB | LP, SEND(KC_3)); | |||
P( NUMB | LH, SEND(KC_4)); | |||
P( NUMB | ST1, SEND(KC_5)); | |||
P( NUMB | RF, SEND(KC_6)); | |||
P( NUMB | RP, SEND(KC_7)); | |||
P( NUMB | RL, SEND(KC_8)); | |||
P( NUMB | RT, SEND(KC_9)); | |||
P( NUMB | RD, SEND(KC_0)); | |||
// Number Row, Left | |||
P( NUMA | LSU, SEND(KC_1)); | |||
P( NUMA | LFT, SEND(KC_2)); | |||
P( NUMA | LP, SEND(KC_3)); | |||
P( NUMA | LH, SEND(KC_4)); | |||
P( NUMA | ST1, SEND(KC_5)); | |||
P( NUMA | RF, SEND(KC_6)); | |||
P( NUMA | RP, SEND(KC_7)); | |||
P( NUMA | RL, SEND(KC_8)); | |||
P( NUMA | RT, SEND(KC_9)); | |||
P( NUMA | RD, SEND(KC_0)); | |||
// Symbols and Numbers | |||
P( SYMB | LP | LW, SEND(KC_LSFT); SEND(KC_9)); // ( | |||
P( SYMB | LH | LR, SEND(KC_LSFT); SEND(KC_0)); // ) | |||
P( SYMB | ST1 | ST2, SEND(KC_GRV)); // ` | |||
P( SYMB | RR | RF, SEND(KC_LSFT); SEND(KC_3)); // # | |||
P( SYMB | LFT | LK, SEND(KC_LSFT); SEND(KC_4)); // $ | |||
P( SYMB | LSU, SEND(KC_LSFT); SEND(KC_1)); // ! | |||
P( SYMB | LSD, SEND(KC_LSFT); SEND(KC_5)); // % | |||
P( SYMB | LFT, SEND(KC_LSFT); SEND(KC_2)); // @ | |||
P( SYMB | LK, SEND(KC_LSFT); SEND(KC_6)); // ^ | |||
P( SYMB | LP, SEND(KC_LSFT); SEND(KC_LBRC)); // { | |||
P( SYMB | LW, SEND(KC_LBRC)); | |||
P( SYMB | LH, SEND(KC_LSFT); SEND(KC_RBRC)); // } | |||
P( SYMB | LR, SEND(KC_RBRC)); | |||
P( SYMB | ST1, SEND(KC_LSFT); SEND(KC_BSLS)); // | | |||
P( SYMB | ST2, SEND(KC_LSFT); SEND(KC_GRV)); // ~ | |||
P( SYMB | RP | RB, SEND(KC_QUOT)); | |||
P( SYMB | RP | RG, SEND(KC_LSFT); SEND(KC_QUOT)); // " | |||
P( SYMB | RF, SEND(KC_KP_PLUS)); | |||
P( SYMB | RR, SEND(KC_LSFT); SEND(KC_7)); // & | |||
P( SYMB | RP, SEND(KC_MINS)); | |||
P( SYMB | RB, SEND(KC_EQL)); | |||
P( SYMB | RL, SEND(KC_SLSH)); | |||
P( SYMB | RG, SEND(KC_COMM)); | |||
P( SYMB | RT, SEND(KC_PAST)); | |||
P( SYMB | RS, SEND(KC_DOT)); | |||
// Letters | |||
P( LSU | LSD, SEND(KC_A)); | |||
P( LFT | LK, SEND(KC_S)); | |||
P( LP | LW, SEND(KC_D)); | |||
P( LH | LR, SEND(KC_F)); | |||
P( ST1 | ST2, SEND(KC_G)); | |||
P( RF | RR, SEND(KC_H)); | |||
P( RT | RS, SEND(KC_L)); | |||
P( RD | RZ, SEND(KC_SCLN)); | |||
P( RG | RL, SEND(KC_K)); | |||
P( RP | RB, SEND(KC_J)); | |||
P( LSU, SEND(KC_Q)); | |||
P( LSD, SEND(KC_Z)); | |||
P( LFT, SEND(KC_W)); | |||
P( LK, SEND(KC_X)); | |||
P( LP, SEND(KC_E)); | |||
P( LW, SEND(KC_C)); | |||
P( LH, SEND(KC_R)); | |||
P( LR, SEND(KC_V)); | |||
P( ST1, SEND(KC_T)); | |||
P( ST2, SEND(KC_B)); | |||
P( RF, SEND(KC_Y)); | |||
P( RR, SEND(KC_N)); | |||
P( RP, SEND(KC_U)); | |||
P( RB, SEND(KC_M)); | |||
P( RL, SEND(KC_I)); | |||
P( RG, SEND(KC_COMM)); | |||
P( RT, SEND(KC_O)); | |||
P( RS, SEND(KC_DOT)); | |||
P( RD, SEND(KC_P)); | |||
P( RZ, SEND(KC_SLSH)); | |||
return 0; | |||
} | |||
// Don't fuck with this, thanks. | |||
size_t keymapsCount = sizeof(keymaps)/sizeof(keymaps[0]); |
@ -0,0 +1,14 @@ | |||
# Butter Stick | |||
![Butter Stick](https://i.redd.it/mvteaomko7s21.jpg) | |||
A chorded 20% keyboard packing full sized useage into your pocket. More info on [gboards.ca](http://docs.gboards.ca/Meet-Butter-Stick)! | |||
Keyboard Maintainer: [Germ](https://github.com/germ) | |||
Hardware Availability: [g Heavy Industries](https://www.gboards.ca/product/butter-stick-limited-edition) | |||
Make example for this keyboard (after setting up your build environment): | |||
make butterstick:default | |||
See the [build environment setup](https://docs.qmk.fm/#/getting_started_build_tools) and the [make instructions](https://docs.qmk.fm/#/getting_started_make_guide) for more information. Brand new to QMK? Start with our [Complete Newbs Guide](https://docs.qmk.fm/#/newbs). |
@ -0,0 +1,19 @@ | |||
# MCU name | |||
MCU = atmega32u4 | |||
F_CPU = 16000000 | |||
ARCH = AVR8 | |||
F_USB = $(F_CPU) | |||
OPT_DEFS += -DINTERRUPT_CONTROL_ENDPOINT -DONLYQWERTY -DDEBUG_MATRIX | |||
SRC += sten.c | |||
EXTRAFLAGS += -flto | |||
BOOTLOADER = atmel-dfu | |||
MOUSEKEY_ENABLE = yes # Mouse keys(+4700) | |||
EXTRAKEY_ENABLE = yes # Audio control and System control(+450) | |||
CONSOLE_ENABLE = yes # Console for debug(+400) | |||
COMMAND_ENABLE = no # Commands for debug and configuration | |||
NKRO_ENABLE = yes # USB Nkey Rollover | |||
STENO_ENABLE = yes # Needed for chording | |||
@ -0,0 +1,367 @@ | |||
#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 | |||
} |
@ -0,0 +1,77 @@ | |||
// 2019, g Heavy Industries | |||
// Blessed mother of Christ, please keep this readable | |||
// and protect us from segfaults. For thine is the clock, | |||
// the slave and the master. Until we return from main. | |||
// | |||
// Amen. | |||
#include QMK_KEYBOARD_H | |||
#include "mousekey.h" | |||
#include "keymap.h" | |||
#include "keymap_steno.h" | |||
#include "wait.h" | |||
extern size_t keymapsCount; // Total keymaps | |||
extern uint32_t cChord; // Current Chord | |||
// Function defs | |||
void processChord(bool useFakeSteno); | |||
uint32_t processQwerty(bool lookup); | |||
uint32_t processFakeSteno(bool lookup); | |||
void saveState(uint32_t cChord); | |||
void restoreState(void); | |||
// Macros for use in keymap.c | |||
void SEND(uint8_t kc); | |||
void REPEAT(void); | |||
void SET_STICKY(uint32_t); | |||
void SWITCH_LAYER(int); | |||
void CLICK_MOUSE(uint8_t); | |||
// Keymap helper | |||
#define P(chord, act) if (cChord == (chord)) { if (!lookup) {act;} return chord;} | |||
// Shift to internal representation | |||
// i.e) S(teno)R(ight)F | |||
#define STN(n) (1L<<n) | |||
enum ORDER { | |||
SFN = 0, SPWR, SST1, SST2, SST3, SST4, SNUML, SNUMR, | |||
SLSU, SLSD, SLT, SLK, SLP, SLW, SLH, SLR, SLA, SLO, | |||
SRE, SRU, SRF, SRR, SRP, SRB, SRL, SRG, SRT, SRS, SRD, SRZ, SRES1, SRES2 | |||
}; | |||
// Break it all out | |||
#define FN STN(SFN) | |||
#define PWR STN(SPWR) | |||
#define ST1 STN(SST1) | |||
#define ST2 STN(SST2) | |||
#define ST3 STN(SST3) | |||
#define ST4 STN(SST4) | |||
#define LNO STN(SNUML) // STN1-6 | |||
#define RNO STN(SNUMR) // STN7-C | |||
#define RES1 STN(SRES1) // Use reserved for sticky state | |||
#define RES2 STN(SRES2) | |||
#define LSU STN(SLSU) | |||
#define LSD STN(SLSD) | |||
#define LFT STN(SLT) // (L)e(F)t (T), preprocessor conflict | |||
#define LK STN(SLK) | |||
#define LP STN(SLP) | |||
#define LW STN(SLW) | |||
#define LH STN(SLH) | |||
#define LR STN(SLR) | |||
#define LA STN(SLA) | |||
#define LO STN(SLO) | |||
#define RE STN(SRE) | |||
#define RU STN(SRU) | |||
#define RF STN(SRF) | |||
#define RR STN(SRR) | |||
#define RP STN(SRP) | |||
#define RB STN(SRB) | |||
#define RL STN(SRL) | |||
#define RG STN(SRG) | |||
#define RT STN(SRT) | |||
#define RS STN(SRS) | |||
#define RD STN(SRD) | |||
#define RZ STN(SRZ) |