diff --git a/README.md b/README.md index 55a38834..449da86d 100644 --- a/README.md +++ b/README.md @@ -1,24 +1,16 @@ # QMK Userspace — Keebart Corne Choc Pro -## Setup +QMK userspace overlay for the Keebart Corne Choc Pro. Builds against +[Keebart/vial-qmk-corne-choc-pro](https://github.com/Keebart/vial-qmk-corne-choc-pro) (vial branch). -1. Use [qmk/qmk_userspace](https://github.com/qmk/qmk_userspace) as a template to create your repo -2. Clone your new repo -3. Unzip this into it -4. Add the Keebart fork as a submodule: +GitHub Actions builds the `.uf2` automatically on push — download from the **Releases** tab. + +Flash: hold **Q** while plugging in the left half, drag `.uf2` onto `RPI-RP2`. Repeat with **P** for the right half. + +## Local builds ```bash -git submodule add https://github.com/Keebart/vial-qmk-corne-choc-pro.git qmk_firmware -git submodule update --init --recursive +pip install qmk +qmk setup -H . --home $(pwd)/qmk_firmware +qmk compile -kb keebart/corne_choc_pro/standard -km timfee ``` - -5. Commit and push: - -```bash -git add . -git commit -m "Initial keymap" -git push -``` - -6. GitHub Actions builds the .uf2 — download from the Releases tab -7. Flash: hold Q while plugging in left half, drag .uf2 onto RPI-RP2. Repeat with P for right. diff --git a/keyboards/keebart/corne_choc_pro/keymaps/timfee/keymap.c b/keyboards/keebart/corne_choc_pro/keymaps/timfee/keymap.c index 564f31bd..2e4afbba 100644 --- a/keyboards/keebart/corne_choc_pro/keymaps/timfee/keymap.c +++ b/keyboards/keebart/corne_choc_pro/keymaps/timfee/keymap.c @@ -6,7 +6,7 @@ const uint16_t PROGMEM keymaps[][MATRIX_ROWS][MATRIX_COLS] = { [0] = LAYOUT_split_3x6_3( ESC_L2, KC_Q, KC_W, KC_E, KC_R, KC_T, KC_Y, KC_U, KC_I, KC_O, KC_P, MIN_L1, KC_TAB, KC_A, KC_S, KC_D, KC_F, KC_G, KC_H, KC_J, KC_K, KC_L, KC_SCLN, KC_EQL, - KC_LSFT, Z_L1, KC_X, KC_C, KC_V, KC_B, KC_N, KC_M, KC_COMM, KC_DOT, SL_L2, KC_QUOT, + KC_LSFT, KC_Z, KC_X, KC_C, KC_V, KC_B, KC_N, KC_M, KC_COMM, KC_DOT, KC_SLSH, KC_QUOT, CT_GRV, AL_DEL, GU_BSP, GU_SPC, AL_ENT, CT_BSL ), diff --git a/users/timfee/bitmaps.h b/users/timfee/bitmaps.h index 27c3bd1b..58c2c603 100644 --- a/users/timfee/bitmaps.h +++ b/users/timfee/bitmaps.h @@ -19,3 +19,82 @@ static const char SCROLL_LOCK_BITMAP [] PROGMEM = { 0x00, 0xc0, 0x20, 0x38, 0x24, 0x32, 0xaa, 0xaa, 0xaa, 0xaa, 0xb2, 0x24, 0x38, 0x20, 0xc0, 0x00, 0x00, 0x3f, 0x40, 0x40, 0x40, 0x53, 0x52, 0x52, 0x54, 0x54, 0x4c, 0x40, 0x40, 0x40, 0x3f, 0x00 }; + +// ── Large digit bitmaps (32×32, 4 OLED pages) for layer display ── +#define LARGE_DIGIT_WIDTH 32 +#define LARGE_DIGIT_PAGES 4 + +static const char PROGMEM DIGIT_0[] = { + 0xff, 0xff, 0xff, 0xff, 0xff, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xf8, 0xf8, 0xf8, 0xf8, 0xf8, 0xf8, 0xf8, 0xf8, 0xf8, 0xf8, 0xf8, 0xf8, 0xf8, 0xf8, 0xf8, 0xf8, 0xf8, 0xf8, 0xf8, 0xf8, 0xf8, 0xf8, 0xff, 0xff, 0xff, 0xff, 0xff, +}; + +static const char PROGMEM DIGIT_1[] = { + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x03, 0x07, 0x0f, 0x1f, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xf8, 0xf8, 0xf8, 0xf8, 0xf8, 0xf8, 0xff, 0xff, 0xff, 0xff, 0xff, 0xf8, 0xf8, 0xf8, 0xf8, 0xf8, 0xf8, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +}; + +static const char PROGMEM DIGIT_2[] = { + 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xf8, 0xf8, 0xf8, 0xf8, 0xf8, 0xf8, 0xf8, 0xf8, 0xf8, 0xf8, 0xf8, 0xf8, 0xf8, 0xf8, 0xf8, 0xf8, 0xf8, 0xf8, 0xf8, 0xf8, 0xf8, 0xf8, 0xf8, 0xf8, 0xf8, 0xf8, 0xf8, +}; + +static const char PROGMEM DIGIT_3[] = { + 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0xff, 0xff, 0xff, 0xff, 0xff, + 0x00, 0x00, 0x00, 0x00, 0x00, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xff, 0xff, 0xff, 0xff, 0xff, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xf8, 0xf8, 0xf8, 0xf8, 0xf8, 0xf8, 0xf8, 0xf8, 0xf8, 0xf8, 0xf8, 0xf8, 0xf8, 0xf8, 0xf8, 0xf8, 0xf8, 0xf8, 0xf8, 0xf8, 0xf8, 0xf8, 0xf8, 0xf8, 0xf8, 0xf8, 0xf8, 0xff, 0xff, 0xff, 0xff, 0xff, +}; + +static const char PROGMEM DIGIT_4[] = { + 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xff, 0xff, 0xff, 0xff, 0xff, + 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0xff, 0xff, 0xff, 0xff, 0xff, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, +}; + +static const char PROGMEM DIGIT_5[] = { + 0xff, 0xff, 0xff, 0xff, 0xff, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, + 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xf8, 0xf8, 0xf8, 0xf8, 0xf8, 0xf8, 0xf8, 0xf8, 0xf8, 0xf8, 0xf8, 0xf8, 0xf8, 0xf8, 0xf8, 0xf8, 0xf8, 0xf8, 0xf8, 0xf8, 0xf8, 0xf8, 0xf8, 0xf8, 0xf8, 0xf8, 0xf8, 0xff, 0xff, 0xff, 0xff, 0xff, +}; + +static const char PROGMEM DIGIT_6[] = { + 0xff, 0xff, 0xff, 0xff, 0xff, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, + 0xff, 0xff, 0xff, 0xff, 0xff, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xf8, 0xf8, 0xf8, 0xf8, 0xf8, 0xf8, 0xf8, 0xf8, 0xf8, 0xf8, 0xf8, 0xf8, 0xf8, 0xf8, 0xf8, 0xf8, 0xf8, 0xf8, 0xf8, 0xf8, 0xf8, 0xf8, 0xff, 0xff, 0xff, 0xff, 0xff, +}; + +static const char PROGMEM DIGIT_7[] = { + 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0xff, 0xff, 0xff, 0xff, 0xff, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, +}; + +static const char PROGMEM DIGIT_8[] = { + 0xff, 0xff, 0xff, 0xff, 0xff, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xf8, 0xf8, 0xf8, 0xf8, 0xf8, 0xf8, 0xf8, 0xf8, 0xf8, 0xf8, 0xf8, 0xf8, 0xf8, 0xf8, 0xf8, 0xf8, 0xf8, 0xf8, 0xf8, 0xf8, 0xf8, 0xf8, 0xff, 0xff, 0xff, 0xff, 0xff, +}; + +static const char PROGMEM DIGIT_9[] = { + 0xff, 0xff, 0xff, 0xff, 0xff, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xff, 0xff, 0xff, 0xff, 0xff, + 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xf8, 0xf8, 0xf8, 0xf8, 0xf8, 0xf8, 0xf8, 0xf8, 0xf8, 0xf8, 0xf8, 0xf8, 0xf8, 0xf8, 0xf8, 0xf8, 0xf8, 0xf8, 0xf8, 0xf8, 0xf8, 0xf8, 0xf8, 0xf8, 0xf8, 0xf8, 0xf8, 0xff, 0xff, 0xff, 0xff, 0xff, +}; + +static const char * const PROGMEM LARGE_DIGITS[] = { + DIGIT_0, DIGIT_1, DIGIT_2, DIGIT_3, DIGIT_4, + DIGIT_5, DIGIT_6, DIGIT_7, DIGIT_8, DIGIT_9, +}; diff --git a/users/timfee/config.h b/users/timfee/config.h index 77c211ee..fdb85827 100644 --- a/users/timfee/config.h +++ b/users/timfee/config.h @@ -5,14 +5,14 @@ #define TAPPING_TERM_PER_KEY #define QUICK_TAP_TERM 90 #define QUICK_TAP_TERM_PER_KEY -#define PERMISSIVE_HOLD_PER_KEY +#define CHORDAL_HOLD #define HOLD_ON_OTHER_KEY_PRESS_PER_KEY #define RETRO_TAPPING #define RETRO_TAPPING_PER_KEY // ── Combos ── #define COMBO_COUNT 8 -#define COMBO_TERM 40 +#define COMBO_TERM 50 #define COMBO_ONLY_FROM_LAYER 0 // ── OLED (SSD1312 on Keebart Corne Choc Pro) ── diff --git a/users/timfee/rules.mk b/users/timfee/rules.mk index 9bccca09..a0e4ba52 100644 --- a/users/timfee/rules.mk +++ b/users/timfee/rules.mk @@ -4,3 +4,6 @@ OLED_DRIVER = ssd1306 OLED_TRANSPORT = i2c WPM_ENABLE = yes KEYCODE_STRING_ENABLE = yes +RGB_MATRIX_ENABLE = yes +NKRO_ENABLE = no +EXTRAKEY_ENABLE = no diff --git a/users/timfee/timfee.c b/users/timfee/timfee.c index cbfe7a2e..3a476b63 100644 --- a/users/timfee/timfee.c +++ b/users/timfee/timfee.c @@ -5,8 +5,57 @@ #include "transactions.h" #endif -// ── State for require-prior-idle ── -static uint16_t last_key_time = 0; +// ── Chordal Hold layout ── +// Defines hand assignments for the "opposite hands" tap-hold rule. +// 'L' = left hand, 'R' = right hand. +// Thumbs are assigned to their physical hand so that same-hand fast +// typing (e.g. backspace → A) resolves as tap, while cross-hand +// shortcuts (e.g. left-thumb CMD + right-hand key) resolve as hold. +// Matrix: 8 rows × 7 cols (4 rows per half, split keyboard). +// Rows 0-2: left finger rows Rows 4-6: right finger rows +// Row 3: left thumb Row 7: right thumb +const char chordal_hold_layout[MATRIX_ROWS][MATRIX_COLS] PROGMEM = { + {'L', 'L', 'L', 'L', 'L', 'L', 'L'}, // row 0: left top + {'L', 'L', 'L', 'L', 'L', 'L', 'L'}, // row 1: left mid + {'L', 'L', 'L', 'L', 'L', 'L', 0 }, // row 2: left bot + { 0, 0, 0, 'L', 'L', 'L', 0 }, // row 3: left thumb + {'R', 'R', 'R', 'R', 'R', 'R', 'R'}, // row 4: right top + {'R', 'R', 'R', 'R', 'R', 'R', 'R'}, // row 5: right mid + {'R', 'R', 'R', 'R', 'R', 'R', 0 }, // row 6: right bot + { 0, 0, 0, 'R', 'R', 'R', 0 }, // row 7: right thumb +}; + +// ── Chordal Hold per-key override ── +// Layer-tap keys always resolve as hold so the layer activates for +// same-hand keys. Cross-hand mod-tap chords always resolve as hold. +// Same-hand mod-tap chords use a timing heuristic: if the other key +// arrives within 150 ms of the tap-hold key press it is a fast typing +// roll (tap); otherwise the user is deliberately holding a modifier +// for a shortcut like Cmd+V or Cmd+Shift+V (hold). +#define CHORDAL_SAME_HAND_MS 150 + +bool get_chordal_hold(uint16_t tap_hold_keycode, keyrecord_t *tap_hold_record, + uint16_t other_keycode, keyrecord_t *other_record) { + // Layer-tap: always hold (layers need same-hand access). + if (IS_QK_LAYER_TAP(tap_hold_keycode)) { + return true; + } + + // Cross-hand: always hold (standard chordal behaviour). + if (get_chordal_hold_default(tap_hold_record, other_record)) { + return true; + } + + // Same-hand mod-tap: timing decides tap vs hold. + if (IS_QK_MOD_TAP(tap_hold_keycode)) { + uint16_t elapsed = + other_record->event.time - tap_hold_record->event.time; + return elapsed >= CHORDAL_SAME_HAND_MS; + } + + // Any other same-hand combo: tap. + return false; +} // ── Combos (matching Vial config) ── const uint16_t PROGMEM lparen_combo[] = {KC_R, KC_T, COMBO_END}; @@ -15,8 +64,8 @@ const uint16_t PROGMEM lbkt_combo[] = {KC_F, KC_G, COMBO_END}; const uint16_t PROGMEM rbkt_combo[] = {KC_H, KC_J, COMBO_END}; const uint16_t PROGMEM lbrace_combo[] = {KC_V, KC_B, COMBO_END}; const uint16_t PROGMEM rbrace_combo[] = {KC_N, KC_M, COMBO_END}; -const uint16_t PROGMEM pipe_combo[] = {Z_L1, KC_X, COMBO_END}; -const uint16_t PROGMEM bslh_combo[] = {SL_L2, KC_QUOT, COMBO_END}; +const uint16_t PROGMEM pipe_combo[] = {KC_Z, KC_X, COMBO_END}; +const uint16_t PROGMEM bslh_combo[] = {KC_SLSH, KC_QUOT, COMBO_END}; combo_t key_combos[COMBO_COUNT] = { COMBO(lparen_combo, KC_LPRN), @@ -36,10 +85,6 @@ combo_t key_combos[COMBO_COUNT] = { static const uint8_t OLED_WIDTH = OLED_DISPLAY_HEIGHT; -static const char PROGMEM QMK_LOGO_1[] = { 0x81, 0x82, 0x83, 0x84, 0x00 }; -static const char PROGMEM QMK_LOGO_2[] = { 0xA1, 0xA2, 0xA3, 0xA4, 0x00 }; -static const char PROGMEM QMK_LOGO_3[] = { 0xC1, 0xC2, 0xC3, 0xC4, 0x00 }; - typedef struct { bool oled_on; } oled_state_m2s_t; typedef struct { uint16_t keycode; } lastkey_m2s_t; typedef struct { uint32_t left; uint32_t right; } presses_m2s_t; @@ -89,6 +134,21 @@ static void oled_print_right_aligned(const char *text, uint8_t width) { oled_write(text, false); } +// ── Large digit renderer (32×32 bitmap, centered) ── +static void render_large_layer_digit(uint8_t start_page) { + uint8_t layer = get_highest_layer(layer_state); + if (layer > 9) layer = 9; + const char *bitmap = (const char *)pgm_read_ptr(&LARGE_DIGITS[layer]); + uint8_t x_offset = (OLED_WIDTH - LARGE_DIGIT_WIDTH) / 2; + for (uint8_t page = 0; page < LARGE_DIGIT_PAGES; page++) { + for (uint8_t col = 0; col < LARGE_DIGIT_WIDTH; col++) { + oled_write_raw_byte( + pgm_read_byte(&bitmap[page * LARGE_DIGIT_WIDTH + col]), + (start_page + page) * OLED_WIDTH + x_offset + col); + } + } +} + // ── Info renderers ── static void print_current_layer(uint8_t row) { char buf[8]; @@ -179,7 +239,7 @@ void housekeeping_task_user(void) { } #endif // OLED_ENABLE -// ── Require-prior-idle + OLED keypress tracking ── +// ── OLED keypress tracking ── bool process_record_user(uint16_t keycode, keyrecord_t *record) { #ifdef OLED_ENABLE g_user_ontime = timer_read32(); @@ -187,7 +247,6 @@ bool process_record_user(uint16_t keycode, keyrecord_t *record) { if (record->event.pressed) { #ifdef OLED_ENABLE - // Track every keypress for OLED (before RPI may intercept) g_last_keycode = keycode; if (record->event.key.row < MATRIX_ROWS / 2) { g_press_left++; @@ -201,50 +260,6 @@ bool process_record_user(uint16_t keycode, keyrecord_t *record) { (void)transaction_rpc_send(USER_SYNC_PRESSES, sizeof(ppkt), &ppkt); } #endif - - // Require-prior-idle handling - uint16_t elapsed = timer_elapsed(last_key_time); - - switch (keycode) { - case GU_SPC: - if (elapsed < RPI_SPACE) { - tap_code(KC_SPC); - return false; - } - break; - case GU_BSP: - if (elapsed < RPI_BKSP) { - tap_code(KC_BSPC); - return false; - } - break; - case Z_L1: - if (elapsed < RPI_Z) { - tap_code(KC_Z); - return false; - } - break; - case SL_L2: - if (elapsed < RPI_SLASH) { - tap_code(KC_SLSH); - return false; - } - break; - case ESC_L2: - if (elapsed < RPI_ESC) { - tap_code(KC_ESC); - return false; - } - break; - case MIN_L1: - if (elapsed < RPI_MINUS) { - tap_code(KC_MINS); - return false; - } - break; - } - - last_key_time = timer_read(); } return true; } @@ -254,49 +269,28 @@ uint16_t get_tapping_term(uint16_t keycode, keyrecord_t *record) { switch (keycode) { case GU_BSP: return 100; case GU_SPC: return 150; - case Z_L1: - case SL_L2: return 120; case AL_DEL: case AL_ENT: return 130; default: return TAPPING_TERM; } } -// ── Per-key permissive hold (pinky layer-taps only) ── -bool get_permissive_hold(uint16_t keycode, keyrecord_t *record) { - switch (keycode) { - case ESC_L2: - case Z_L1: - case MIN_L1: - case SL_L2: - return true; - default: - return false; - } -} - -// ── Per-key hold on other key press (yeet only) ── +// ── Per-key hold on other key press ── +// Disabled; chordal hold handles the opposite-hands rule. bool get_hold_on_other_key_press(uint16_t keycode, keyrecord_t *record) { - switch (keycode) { - case GU_BSP: return true; - default: return false; - } + return false; } -// ── Per-key retro tapping (yeet only) ── +// ── Per-key retro tapping — disabled to prevent unintended tap action +// firing after modifier-only holds (e.g. Cmd+click in apps like Figma) ── bool get_retro_tapping(uint16_t keycode, keyrecord_t *record) { - switch (keycode) { - case GU_BSP: return true; - default: return false; - } + return false; } // ── Per-key quick tap term ── uint16_t get_quick_tap_term(uint16_t keycode, keyrecord_t *record) { switch (keycode) { case GU_SPC: return 90; - case Z_L1: - case SL_L2: return 80; default: return QUICK_TAP_TERM; } } @@ -402,11 +396,8 @@ bool oled_task_user(void) { oled_print_right_aligned(get_keycode_string(unwrap_keycode(g_last_keycode)), g_oled_max_char); - // QMK logo - oled_set_cursor(0, 13); oled_write_P(QMK_LOGO_1, false); - oled_set_cursor(0, 14); oled_write_P(QMK_LOGO_2, false); - oled_set_cursor(0, 15); oled_write_P(QMK_LOGO_3, false); - oled_set_cursor(7, 15); oled_write_P(PSTR("QMK"), false); + // Large layer digit (centered, pages 12–15) + render_large_layer_digit(12); } // ── Right half ── else { @@ -424,14 +415,80 @@ bool oled_task_user(void) { oled_write_P(PSTR("Right:"), false); print_balance(8, pct_right); - // QMK logo - oled_set_cursor(0, 13); oled_write_P(QMK_LOGO_1, false); - oled_set_cursor(0, 14); oled_write_P(QMK_LOGO_2, false); - oled_set_cursor(0, 15); oled_write_P(QMK_LOGO_3, false); - oled_set_cursor(7, 15); oled_write_P(PSTR("QMK"), false); + // Large layer digit (centered, pages 12–15) + render_large_layer_digit(12); } return false; } #endif // OLED_ENABLE + +// ═══════════════════════════════════════════════════════════════════ +// RGB Matrix – per-layer LED colours +// ═══════════════════════════════════════════════════════════════════ +#ifdef RGB_MATRIX_ENABLE + +// LED indices derived from keyboard.json rgb_matrix.layout +// Left half (0-22) – matrix position → LED index: +// [3,5]=0 [2,5]=1 [1,5]=2 [0,5]=3 [0,4]=4 [1,4]=5 [2,4]=6 +// [3,4]=7 [3,3]=8 [2,3]=9 [1,3]=10 [0,3]=11 [0,2]=12 [1,2]=13 +// [2,2]=14 [2,1]=15 [1,1]=16 [0,1]=17 [0,0]=18 [1,0]=19 [2,0]=20 +// [0,6]=21 [1,6]=22 +// Right half (23-45): +// [7,5]=23 [6,5]=24 [5,5]=25 [4,5]=26 [4,4]=27 [5,4]=28 [6,4]=29 +// [7,4]=30 [7,3]=31 [6,3]=32 [5,3]=33 [4,3]=34 [4,2]=35 [5,2]=36 +// [6,2]=37 [6,1]=38 [5,1]=39 [4,1]=40 [4,0]=41 [5,0]=42 [6,0]=43 +// [4,6]=44 [5,6]=45 + +// Layer 1: left-side symbol keys (!, @, #, $, %, ^, &, *, (, )) +static const uint8_t L1_SYMBOL_LEDS[] = { 17, 12, 11, 4, 3, 16, 13, 10, 5, 2 }; +// Layer 1: right-side number keys (7-9, 4-6, 1-3, comma/0/dot on thumbs) +static const uint8_t L1_NUMBER_LEDS[] = { 26, 27, 34, 25, 28, 33, 24, 29, 32, 23, 30, 31 }; +// Layer 2: left-side F-keys (F1-F10) +static const uint8_t L2_FKEY_LEDS[] = { 17, 12, 11, 4, 3, 16, 13, 10, 5, 2 }; +// Layer 2: right-side arrow keys (all 3 rows: alt-arrows, arrows, gui-arrows) +static const uint8_t L2_ARROW_LEDS[] = { 26, 27, 34, 35, 25, 28, 33, 36, 24, 29, 32, 37 }; + +static inline bool led_in_set(uint8_t led, const uint8_t *set, uint8_t count) { + for (uint8_t j = 0; j < count; j++) { + if (set[j] == led) return true; + } + return false; +} + +bool rgb_matrix_indicators_advanced_user(uint8_t led_min, uint8_t led_max) { + uint8_t layer = get_highest_layer(layer_state); + + for (uint8_t i = led_min; i < led_max; i++) { + switch (layer) { + case 1: + if (led_in_set(i, L1_SYMBOL_LEDS, sizeof(L1_SYMBOL_LEDS))) { + rgb_matrix_set_color(i, 0, 255, 0); // bright green (symbols) + } else if (led_in_set(i, L1_NUMBER_LEDS, sizeof(L1_NUMBER_LEDS))) { + rgb_matrix_set_color(i, 255, 0, 0); // bright red (numbers) + } else { + rgb_matrix_set_color(i, 200, 0, 0); // dim red (inactive) + } + break; + + case 2: + if (led_in_set(i, L2_FKEY_LEDS, sizeof(L2_FKEY_LEDS))) { + rgb_matrix_set_color(i, 148, 0, 211); // purple (F-keys) + } else if (led_in_set(i, L2_ARROW_LEDS, sizeof(L2_ARROW_LEDS))) { + rgb_matrix_set_color(i, 0, 0, 255); // bright blue (arrows) + } else { + rgb_matrix_set_color(i, 200, 0, 0); // dim red (inactive) + } + break; + + default: + rgb_matrix_set_color(i, 200, 0, 0); // solid red (base layer) + break; + } + } + + return false; +} + +#endif // RGB_MATRIX_ENABLE diff --git a/users/timfee/timfee.h b/users/timfee/timfee.h index 90eba2cf..4c9d62ab 100644 --- a/users/timfee/timfee.h +++ b/users/timfee/timfee.h @@ -3,20 +3,10 @@ // ── Key aliases (layer numbers match Vial: L1=symbols, L2=nav) ── #define ESC_L2 LT(2, KC_ESC) -#define Z_L1 LT(1, KC_Z) #define MIN_L1 LT(1, KC_MINS) -#define SL_L2 LT(2, KC_SLSH) #define CT_GRV LCTL_T(KC_GRV) #define AL_DEL LALT_T(KC_DEL) #define GU_BSP LGUI_T(KC_BSPC) #define GU_SPC RGUI_T(KC_SPC) #define AL_ENT RALT_T(KC_ENT) #define CT_BSL RCTL_T(KC_BSLS) - -// ── Require-prior-idle thresholds (ms) ── -#define RPI_SPACE 150 -#define RPI_Z 125 -#define RPI_SLASH 150 -#define RPI_ESC 125 -#define RPI_MINUS 150 -#define RPI_BKSP 150