From f12902eb1689d6c8269ac7bb1421d4cf4e888d70 Mon Sep 17 00:00:00 2001
From: Jack Sangdahl
Date: Wed, 10 Sep 2025 17:32:48 -0600
Subject: Refactor `1upkeyboards/sweet16v2` (#25610)
---
data/mappings/keyboard_aliases.hjson | 6 ++++++
1 file changed, 6 insertions(+)
(limited to 'data')
diff --git a/data/mappings/keyboard_aliases.hjson b/data/mappings/keyboard_aliases.hjson
index 3978235e20..babbf38940 100644
--- a/data/mappings/keyboard_aliases.hjson
+++ b/data/mappings/keyboard_aliases.hjson
@@ -1632,6 +1632,12 @@
"1upkeyboards/sweet16": {
"target": "1upkeyboards/sweet16/v1"
},
+ "1upkeyboards/sweet16v2/kb2040": {
+ "target": "1upkeyboards/sweet16v2"
+ },
+ "1upkeyboards/sweet16v2/pro_micro": {
+ "target": "1upkeyboards/sweet16v2"
+ },
"25keys/aleth42": {
"target": "25keys/aleth42/rev1"
},
--
cgit v1.2.3
From 286947b6faa3b3afd7862c52e52542c8a6ab3f20 Mon Sep 17 00:00:00 2001
From: Jack Sangdahl
Date: Wed, 10 Sep 2025 17:33:12 -0600
Subject: Refactor `0xcb/splaytoraid` (#25609)
---
data/mappings/keyboard_aliases.hjson | 7 +-
keyboards/0xcb/splaytoraid/32u4/keyboard.json | 27 ---
keyboards/0xcb/splaytoraid/info.json | 144 ----------------
keyboards/0xcb/splaytoraid/keyboard.json | 183 +++++++++++++++++++++
.../0xcb/splaytoraid/keymaps/default/keymap.c | 49 ------
.../0xcb/splaytoraid/keymaps/default/keymap.json | 21 +++
.../0xcb/splaytoraid/keymaps/default/rules.mk | 1 -
keyboards/0xcb/splaytoraid/readme.md | 9 +-
keyboards/0xcb/splaytoraid/rp2040_ce/keyboard.json | 44 -----
keyboards/0xcb/splaytoraid/rp2040_ce/readme.md | 3 -
10 files changed, 210 insertions(+), 278 deletions(-)
delete mode 100644 keyboards/0xcb/splaytoraid/32u4/keyboard.json
delete mode 100644 keyboards/0xcb/splaytoraid/info.json
create mode 100644 keyboards/0xcb/splaytoraid/keyboard.json
delete mode 100644 keyboards/0xcb/splaytoraid/keymaps/default/keymap.c
create mode 100644 keyboards/0xcb/splaytoraid/keymaps/default/keymap.json
delete mode 100644 keyboards/0xcb/splaytoraid/keymaps/default/rules.mk
delete mode 100644 keyboards/0xcb/splaytoraid/rp2040_ce/keyboard.json
delete mode 100644 keyboards/0xcb/splaytoraid/rp2040_ce/readme.md
(limited to 'data')
diff --git a/data/mappings/keyboard_aliases.hjson b/data/mappings/keyboard_aliases.hjson
index babbf38940..4f63c5f15f 100644
--- a/data/mappings/keyboard_aliases.hjson
+++ b/data/mappings/keyboard_aliases.hjson
@@ -1620,8 +1620,11 @@
"0_sixty": {
"target": "0_sixty/base"
},
- "0xcb/splaytoraid": {
- "target": "0xcb/splaytoraid/rp2040_ce"
+ "0xcb/splaytoraid/32u4": {
+ "target": "0xcb/splaytoraid"
+ },
+ "0xcb/splaytoraid/rp2040_ce": {
+ "target": "0xcb/splaytoraid"
},
"1upkeyboards/pi40": {
"target": "1upkeyboards/pi40/mit_v1_0"
diff --git a/keyboards/0xcb/splaytoraid/32u4/keyboard.json b/keyboards/0xcb/splaytoraid/32u4/keyboard.json
deleted file mode 100644
index 7c3f0590a6..0000000000
--- a/keyboards/0xcb/splaytoraid/32u4/keyboard.json
+++ /dev/null
@@ -1,27 +0,0 @@
-{
- "development_board": "promicro",
- "bootloader": "qmk-dfu",
- "matrix_pins": {
- "cols": ["F5", "F6", "F7", "F4", "B3", "B1", "B2"],
- "rows": ["D3", "D2", "D1", "D4", "D7", "E6", "B4", "C6"]
- },
- "diode_direction": "COL2ROW",
- "rgb_matrix": {
- "animations": {
- "band_sat": true,
- "band_spiral_val": true,
- "breathing": true,
- "cycle_all": true,
- "cycle_left_right": true,
- "raindrops": true
- }
- },
- "encoder": {
- "rotary": [
- {"pin_a": "B5", "pin_b": "B6"}
- ]
- },
- "ws2812": {
- "pin": "D0"
- }
-}
diff --git a/keyboards/0xcb/splaytoraid/info.json b/keyboards/0xcb/splaytoraid/info.json
deleted file mode 100644
index 8c32b0dc11..0000000000
--- a/keyboards/0xcb/splaytoraid/info.json
+++ /dev/null
@@ -1,144 +0,0 @@
-{
- "manufacturer": "Freya",
- "keyboard_name": "splaytoraid",
- "maintainer": "freya-irl",
- "url": "https://github.com/freya-irl/splaytoraid40",
- "usb": {
- "device_version": "1.0.0",
- "pid": "0xCB00",
- "vid": "0x2004"
- },
- "features": {
- "extrakey": true,
- "rgb_matrix": true,
- "bootmagic": true,
- "console": true,
- "mousekey": true,
- "nkro": true,
- "encoder": true
- },
- "bootmagic": {
- "matrix": [1, 0]
- },
- "build": {
- "lto": true
- },
- "rgb_matrix": {
- "default": {
- "animation": "breathing",
- "hue": 152,
- "sat": 232,
- "speed": 50
- },
- "driver": "ws2812",
- "layout": [
- {"flags": 4, "matrix": [0, 2], "x": 0, "y": 0},
- {"flags": 4, "matrix": [1, 0], "x": 20, "y": 0},
- {"flags": 4, "matrix": [7, 0], "x": 61, "y": 0},
- {"flags": 4, "matrix": [7, 1], "x": 163, "y": 0},
- {"flags": 4, "matrix": [5, 0], "x": 203, "y": 0},
- {"flags": 4, "matrix": [4, 2], "x": 224, "y": 0},
- {"flags": 4, "matrix": [6, 2], "x": 0, "y": 21},
- {"flags": 4, "matrix": [6, 1], "x": 224, "y": 21},
- {"flags": 4, "matrix": [3, 3], "x": 20, "y": 43},
- {"flags": 4, "matrix": [7, 3], "x": 61, "y": 43},
- {"flags": 4, "matrix": [6, 4], "x": 163, "y": 43},
- {"flags": 4, "matrix": [6, 3], "x": 203, "y": 43},
- {"flags": 4, "matrix": [4, 3], "x": 61, "y": 64},
- {"flags": 4, "matrix": [5, 5], "x": 81, "y": 64},
- {"flags": 4, "matrix": [7, 4], "x": 101, "y": 64},
- {"flags": 4, "matrix": [7, 5], "x": 122, "y": 64},
- {"flags": 4, "matrix": [1, 5], "x": 142, "y": 64},
- {"flags": 4, "matrix": [0, 3], "x": 163, "y": 64}
- ],
- "max_brightness": 200
- },
- "layouts": {
- "LAYOUT_36": {
- "layout": [
- {"label": "K10", "matrix": [1, 0], "x": 0, "y": 0},
- {"label": "K11", "matrix": [1, 1], "x": 1, "y": 0},
- {"label": "K02", "matrix": [0, 2], "x": 2, "y": 0},
- {"label": "K01", "matrix": [0, 1], "x": 3, "y": 0},
- {"label": "K12", "matrix": [1, 2], "x": 4, "y": 0},
- {"label": "K52", "matrix": [5, 2], "x": 6, "y": 0},
- {"label": "K04", "matrix": [0, 4], "x": 7, "y": 0},
- {"label": "K03", "matrix": [0, 3], "x": 8, "y": 0},
- {"label": "K14", "matrix": [1, 4], "x": 9, "y": 0},
- {"label": "K15", "matrix": [1, 5], "x": 10, "y": 0},
- {"label": "K30", "matrix": [3, 0], "x": 0, "y": 1},
- {"label": "K31", "matrix": [3, 1], "x": 1, "y": 1},
- {"label": "K22", "matrix": [2, 2], "x": 2, "y": 1},
- {"label": "K21", "matrix": [2, 1], "x": 3, "y": 1},
- {"label": "K13", "matrix": [1, 3], "x": 4, "y": 1},
- {"label": "K53", "matrix": [5, 3], "x": 6, "y": 1},
- {"label": "K24", "matrix": [2, 4], "x": 7, "y": 1},
- {"label": "K23", "matrix": [2, 3], "x": 8, "y": 1},
- {"label": "K34", "matrix": [3, 4], "x": 9, "y": 1},
- {"label": "K35", "matrix": [3, 5], "x": 10, "y": 1},
- {"label": "K50", "matrix": [5, 0], "x": 0, "y": 2},
- {"label": "K51", "matrix": [5, 1], "x": 1, "y": 2},
- {"label": "K42", "matrix": [4, 2], "x": 2, "y": 2},
- {"label": "K41", "matrix": [4, 1], "x": 3, "y": 2},
- {"label": "K32", "matrix": [3, 2], "x": 4, "y": 2},
- {"label": "K72", "matrix": [7, 2], "x": 6, "y": 2},
- {"label": "K44", "matrix": [4, 4], "x": 7, "y": 2},
- {"label": "K43", "matrix": [4, 3], "x": 8, "y": 2},
- {"label": "K54", "matrix": [5, 4], "x": 9, "y": 2},
- {"label": "K55", "matrix": [5, 5], "x": 10, "y": 2},
- {"label": "K62", "matrix": [6, 2], "x": 2, "y": 3},
- {"label": "K61", "matrix": [6, 1], "x": 3, "y": 3},
- {"label": "K33", "matrix": [3, 3], "x": 4, "y": 3},
- {"label": "K66", "matrix": [6, 6], "x": 5, "y": 3},
- {"label": "K73", "matrix": [7, 3], "x": 6, "y": 3},
- {"label": "K64", "matrix": [6, 4], "x": 7, "y": 3},
- {"label": "K63", "matrix": [6, 3], "x": 8, "y": 3}
- ]
- },
- "LAYOUT_40": {
- "layout": [
- {"label": "K70", "matrix": [7, 0], "x": 0, "y": 0},
- {"label": "K10", "matrix": [1, 0], "x": 1, "y": 0},
- {"label": "K11", "matrix": [1, 1], "x": 2, "y": 0},
- {"label": "K02", "matrix": [0, 2], "x": 3, "y": 0},
- {"label": "K01", "matrix": [0, 1], "x": 4, "y": 0},
- {"label": "K12", "matrix": [1, 2], "x": 5, "y": 0},
- {"label": "K52", "matrix": [5, 2], "x": 7, "y": 0},
- {"label": "K04", "matrix": [0, 4], "x": 8, "y": 0},
- {"label": "K03", "matrix": [0, 3], "x": 9, "y": 0},
- {"label": "K14", "matrix": [1, 4], "x": 10, "y": 0},
- {"label": "K15", "matrix": [1, 5], "x": 11, "y": 0},
- {"label": "K75", "matrix": [7, 5], "x": 12, "y": 0},
- {"label": "K71", "matrix": [7, 1], "x": 0, "y": 1},
- {"label": "K30", "matrix": [3, 0], "x": 1, "y": 1},
- {"label": "K31", "matrix": [3, 1], "x": 2, "y": 1},
- {"label": "K22", "matrix": [2, 2], "x": 3, "y": 1},
- {"label": "K21", "matrix": [2, 1], "x": 4, "y": 1},
- {"label": "K13", "matrix": [1, 3], "x": 5, "y": 1},
- {"label": "K53", "matrix": [5, 3], "x": 7, "y": 1},
- {"label": "K24", "matrix": [2, 4], "x": 8, "y": 1},
- {"label": "K23", "matrix": [2, 3], "x": 9, "y": 1},
- {"label": "K34", "matrix": [3, 4], "x": 10, "y": 1},
- {"label": "K35", "matrix": [3, 5], "x": 11, "y": 1},
- {"label": "K74", "matrix": [7, 4], "x": 12, "y": 1},
- {"label": "K50", "matrix": [5, 0], "x": 1, "y": 2},
- {"label": "K51", "matrix": [5, 1], "x": 2, "y": 2},
- {"label": "K42", "matrix": [4, 2], "x": 3, "y": 2},
- {"label": "K41", "matrix": [4, 1], "x": 4, "y": 2},
- {"label": "K32", "matrix": [3, 2], "x": 5, "y": 2},
- {"label": "K72", "matrix": [7, 2], "x": 7, "y": 2},
- {"label": "K44", "matrix": [4, 4], "x": 8, "y": 2},
- {"label": "K43", "matrix": [4, 3], "x": 9, "y": 2},
- {"label": "K54", "matrix": [5, 4], "x": 10, "y": 2},
- {"label": "K55", "matrix": [5, 5], "x": 11, "y": 2},
- {"label": "K62", "matrix": [6, 2], "x": 3, "y": 3},
- {"label": "K61", "matrix": [6, 1], "x": 4, "y": 3},
- {"label": "K33", "matrix": [3, 3], "x": 5, "y": 3},
- {"label": "K66", "matrix": [6, 6], "x": 6, "y": 3},
- {"label": "K73", "matrix": [7, 3], "x": 7, "y": 3},
- {"label": "K64", "matrix": [6, 4], "x": 8, "y": 3},
- {"label": "K63", "matrix": [6, 3], "x": 9, "y": 3}
- ]
- }
- }
-}
diff --git a/keyboards/0xcb/splaytoraid/keyboard.json b/keyboards/0xcb/splaytoraid/keyboard.json
new file mode 100644
index 0000000000..457cc7a23a
--- /dev/null
+++ b/keyboards/0xcb/splaytoraid/keyboard.json
@@ -0,0 +1,183 @@
+{
+ "manufacturer": "Freya",
+ "keyboard_name": "splaytoraid",
+ "maintainer": "freya-irl",
+ "bootloader": "qmk-dfu",
+ "bootmagic": {
+ "matrix": [1, 0]
+ },
+ "build": {
+ "lto": true
+ },
+ "development_board": "promicro",
+ "diode_direction": "COL2ROW",
+ "encoder": {
+ "rotary": [
+ {"pin_a": "B5", "pin_b": "B6"}
+ ]
+ },
+ "features": {
+ "bootmagic": true,
+ "encoder": true,
+ "extrakey": true,
+ "mousekey": true,
+ "nkro": true,
+ "rgb_matrix": true
+ },
+ "matrix_pins": {
+ "cols": ["F5", "F6", "F7", "F4", "B3", "B1", "B2"],
+ "rows": ["D3", "D2", "D1", "D4", "D7", "E6", "B4", "C6"]
+ },
+ "rgb_matrix": {
+ "animations": {
+ "band_sat": true,
+ "band_spiral_val": true,
+ "breathing": true,
+ "cycle_all": true,
+ "cycle_left_right": true,
+ "cycle_out_in": true,
+ "cycle_out_in_dual": true,
+ "cycle_pinwheel": true,
+ "cycle_up_down": true,
+ "digital_rain": true,
+ "dual_beacon": true,
+ "hue_breathing": true,
+ "jellybean_raindrops": true,
+ "multisplash": true,
+ "pixel_fractal": true,
+ "pixel_rain": true,
+ "rainbow_moving_chevron": true,
+ "raindrops": true,
+ "solid_reactive": true,
+ "solid_reactive_simple": true,
+ "solid_splash": true,
+ "splash": true,
+ "typing_heatmap": true
+ },
+ "default": {
+ "animation": "breathing",
+ "hue": 152,
+ "sat": 232,
+ "speed": 50
+ },
+ "driver": "ws2812",
+ "layout": [
+ {"matrix": [0, 2], "x": 0, "y": 0, "flags": 4},
+ {"matrix": [1, 0], "x": 20, "y": 0, "flags": 4},
+ {"matrix": [7, 0], "x": 61, "y": 0, "flags": 4},
+ {"matrix": [7, 1], "x": 163, "y": 0, "flags": 4},
+ {"matrix": [5, 0], "x": 203, "y": 0, "flags": 4},
+ {"matrix": [4, 2], "x": 224, "y": 0, "flags": 4},
+ {"matrix": [6, 2], "x": 0, "y": 21, "flags": 4},
+ {"matrix": [6, 1], "x": 224, "y": 21, "flags": 4},
+ {"matrix": [3, 3], "x": 20, "y": 43, "flags": 4},
+ {"matrix": [7, 3], "x": 61, "y": 43, "flags": 4},
+ {"matrix": [6, 4], "x": 163, "y": 43, "flags": 4},
+ {"matrix": [6, 3], "x": 203, "y": 43, "flags": 4},
+ {"matrix": [4, 3], "x": 61, "y": 64, "flags": 4},
+ {"matrix": [5, 5], "x": 81, "y": 64, "flags": 4},
+ {"matrix": [7, 4], "x": 101, "y": 64, "flags": 4},
+ {"matrix": [7, 5], "x": 122, "y": 64, "flags": 4},
+ {"matrix": [1, 5], "x": 142, "y": 64, "flags": 4},
+ {"matrix": [0, 3], "x": 163, "y": 64, "flags": 4}
+ ],
+ "max_brightness": 200
+ },
+ "url": "https://github.com/freya-irl/splaytoraid40",
+ "usb": {
+ "device_version": "1.0.0",
+ "pid": "0xCB00",
+ "vid": "0x2004"
+ },
+ "ws2812": {
+ "pin": "D0"
+ },
+ "layouts": {
+ "LAYOUT_36": {
+ "layout": [
+ {"label": "K10", "matrix": [1, 0], "x": 0, "y": 0},
+ {"label": "K11", "matrix": [1, 1], "x": 1, "y": 0},
+ {"label": "K02", "matrix": [0, 2], "x": 2, "y": 0},
+ {"label": "K01", "matrix": [0, 1], "x": 3, "y": 0},
+ {"label": "K12", "matrix": [1, 2], "x": 4, "y": 0},
+ {"label": "K52", "matrix": [5, 2], "x": 6, "y": 0},
+ {"label": "K04", "matrix": [0, 4], "x": 7, "y": 0},
+ {"label": "K03", "matrix": [0, 3], "x": 8, "y": 0},
+ {"label": "K14", "matrix": [1, 4], "x": 9, "y": 0},
+ {"label": "K15", "matrix": [1, 5], "x": 10, "y": 0},
+ {"label": "K30", "matrix": [3, 0], "x": 0, "y": 1},
+ {"label": "K31", "matrix": [3, 1], "x": 1, "y": 1},
+ {"label": "K22", "matrix": [2, 2], "x": 2, "y": 1},
+ {"label": "K21", "matrix": [2, 1], "x": 3, "y": 1},
+ {"label": "K13", "matrix": [1, 3], "x": 4, "y": 1},
+ {"label": "K53", "matrix": [5, 3], "x": 6, "y": 1},
+ {"label": "K24", "matrix": [2, 4], "x": 7, "y": 1},
+ {"label": "K23", "matrix": [2, 3], "x": 8, "y": 1},
+ {"label": "K34", "matrix": [3, 4], "x": 9, "y": 1},
+ {"label": "K35", "matrix": [3, 5], "x": 10, "y": 1},
+ {"label": "K50", "matrix": [5, 0], "x": 0, "y": 2},
+ {"label": "K51", "matrix": [5, 1], "x": 1, "y": 2},
+ {"label": "K42", "matrix": [4, 2], "x": 2, "y": 2},
+ {"label": "K41", "matrix": [4, 1], "x": 3, "y": 2},
+ {"label": "K32", "matrix": [3, 2], "x": 4, "y": 2},
+ {"label": "K72", "matrix": [7, 2], "x": 6, "y": 2},
+ {"label": "K44", "matrix": [4, 4], "x": 7, "y": 2},
+ {"label": "K43", "matrix": [4, 3], "x": 8, "y": 2},
+ {"label": "K54", "matrix": [5, 4], "x": 9, "y": 2},
+ {"label": "K55", "matrix": [5, 5], "x": 10, "y": 2},
+ {"label": "K62", "matrix": [6, 2], "x": 2, "y": 3},
+ {"label": "K61", "matrix": [6, 1], "x": 3, "y": 3},
+ {"label": "K33", "matrix": [3, 3], "x": 4, "y": 3},
+ {"label": "K66", "matrix": [6, 6], "x": 5, "y": 3},
+ {"label": "K73", "matrix": [7, 3], "x": 6, "y": 3},
+ {"label": "K64", "matrix": [6, 4], "x": 7, "y": 3},
+ {"label": "K63", "matrix": [6, 3], "x": 8, "y": 3}
+ ]
+ },
+ "LAYOUT_40": {
+ "layout": [
+ {"label": "K70", "matrix": [7, 0], "x": 0, "y": 0},
+ {"label": "K10", "matrix": [1, 0], "x": 1, "y": 0},
+ {"label": "K11", "matrix": [1, 1], "x": 2, "y": 0},
+ {"label": "K02", "matrix": [0, 2], "x": 3, "y": 0},
+ {"label": "K01", "matrix": [0, 1], "x": 4, "y": 0},
+ {"label": "K12", "matrix": [1, 2], "x": 5, "y": 0},
+ {"label": "K52", "matrix": [5, 2], "x": 7, "y": 0},
+ {"label": "K04", "matrix": [0, 4], "x": 8, "y": 0},
+ {"label": "K03", "matrix": [0, 3], "x": 9, "y": 0},
+ {"label": "K14", "matrix": [1, 4], "x": 10, "y": 0},
+ {"label": "K15", "matrix": [1, 5], "x": 11, "y": 0},
+ {"label": "K75", "matrix": [7, 5], "x": 12, "y": 0},
+ {"label": "K71", "matrix": [7, 1], "x": 0, "y": 1},
+ {"label": "K30", "matrix": [3, 0], "x": 1, "y": 1},
+ {"label": "K31", "matrix": [3, 1], "x": 2, "y": 1},
+ {"label": "K22", "matrix": [2, 2], "x": 3, "y": 1},
+ {"label": "K21", "matrix": [2, 1], "x": 4, "y": 1},
+ {"label": "K13", "matrix": [1, 3], "x": 5, "y": 1},
+ {"label": "K53", "matrix": [5, 3], "x": 7, "y": 1},
+ {"label": "K24", "matrix": [2, 4], "x": 8, "y": 1},
+ {"label": "K23", "matrix": [2, 3], "x": 9, "y": 1},
+ {"label": "K34", "matrix": [3, 4], "x": 10, "y": 1},
+ {"label": "K35", "matrix": [3, 5], "x": 11, "y": 1},
+ {"label": "K74", "matrix": [7, 4], "x": 12, "y": 1},
+ {"label": "K50", "matrix": [5, 0], "x": 1, "y": 2},
+ {"label": "K51", "matrix": [5, 1], "x": 2, "y": 2},
+ {"label": "K42", "matrix": [4, 2], "x": 3, "y": 2},
+ {"label": "K41", "matrix": [4, 1], "x": 4, "y": 2},
+ {"label": "K32", "matrix": [3, 2], "x": 5, "y": 2},
+ {"label": "K72", "matrix": [7, 2], "x": 7, "y": 2},
+ {"label": "K44", "matrix": [4, 4], "x": 8, "y": 2},
+ {"label": "K43", "matrix": [4, 3], "x": 9, "y": 2},
+ {"label": "K54", "matrix": [5, 4], "x": 10, "y": 2},
+ {"label": "K55", "matrix": [5, 5], "x": 11, "y": 2},
+ {"label": "K62", "matrix": [6, 2], "x": 3, "y": 3},
+ {"label": "K61", "matrix": [6, 1], "x": 4, "y": 3},
+ {"label": "K33", "matrix": [3, 3], "x": 5, "y": 3},
+ {"label": "K66", "matrix": [6, 6], "x": 6, "y": 3},
+ {"label": "K73", "matrix": [7, 3], "x": 7, "y": 3},
+ {"label": "K64", "matrix": [6, 4], "x": 8, "y": 3},
+ {"label": "K63", "matrix": [6, 3], "x": 9, "y": 3}
+ ]
+ }
+ }
+}
diff --git a/keyboards/0xcb/splaytoraid/keymaps/default/keymap.c b/keyboards/0xcb/splaytoraid/keymaps/default/keymap.c
deleted file mode 100644
index 6a82f2ca74..0000000000
--- a/keyboards/0xcb/splaytoraid/keymaps/default/keymap.c
+++ /dev/null
@@ -1,49 +0,0 @@
-// Copyright 2023 Conor Burns (@Conor-Burns)
-/* SPDX-License-Identifier: GPL-2.0-or-later */
-
-#include QMK_KEYBOARD_H
-
-enum layer_names {
- _BASE,
- _LOWER,
- _RAISE,
- _ADJUST
-};
-
-const uint16_t PROGMEM keymaps[][MATRIX_ROWS][MATRIX_COLS] = {
- [_BASE] = LAYOUT_40(
- KC_TAB, KC_Q, KC_W, KC_E, KC_R, KC_T, KC_Y, KC_U, KC_I, KC_O, KC_P, KC_BSPC,
- KC_LCTL, KC_A, KC_S, KC_D, KC_F, KC_G, KC_H, KC_J, KC_K, KC_L, KC_SCLN, KC_QUOT,
- KC_Z, KC_X, KC_C, KC_V, KC_B, KC_N, KC_M, KC_COMM, KC_DOT, KC_SLSH,
- KC_LSFT, KC_ESC, KC_ENT, KC_MPLY, KC_SPC, KC_DEL, KC_RSFT
- ),
-
- [_LOWER] = LAYOUT_40(
- _______, _______, _______, _______, _______, _______, _______, _______, _______, _______, _______, _______,
- _______, _______, _______, _______, _______, _______, _______, _______, _______, _______, _______, _______,
- _______, _______, _______, _______, _______, _______, _______, _______, _______, _______,
- _______, _______, _______, _______, _______, _______, _______
- ),
-
- [_RAISE] = LAYOUT_40(
- _______, _______, _______, _______, _______, _______, _______, _______, _______, _______, _______, _______,
- _______, _______, _______, _______, _______, _______, _______, _______, _______, _______, _______, _______,
- _______, _______, _______, _______, _______, _______, _______, _______, _______, _______,
- _______, _______, _______, _______, _______, _______, _______
- ),
-
- [_ADJUST] = LAYOUT_40(
- _______, _______, _______, _______, _______, _______, _______, _______, _______, _______, _______, _______,
- _______, _______, _______, _______, _______, _______, _______, _______, _______, _______, _______, _______,
- _______, _______, _______, _______, _______, _______, _______, _______, _______, _______,
- _______, _______, _______, _______, _______, _______, _______
- )
-};
-
-
-const uint16_t PROGMEM encoder_map[][NUM_ENCODERS][NUM_DIRECTIONS] = {
- [_BASE] = { ENCODER_CCW_CW(KC_VOLD, KC_VOLU) },
- [_LOWER] = { ENCODER_CCW_CW(KC_MPRV, KC_MNXT) },
- [_RAISE] = { ENCODER_CCW_CW(UG_VALD, UG_VALU) },
- [_ADJUST] = { ENCODER_CCW_CW(KC_RGHT, KC_LEFT) }
-};
diff --git a/keyboards/0xcb/splaytoraid/keymaps/default/keymap.json b/keyboards/0xcb/splaytoraid/keymaps/default/keymap.json
new file mode 100644
index 0000000000..42bcaf28a1
--- /dev/null
+++ b/keyboards/0xcb/splaytoraid/keymaps/default/keymap.json
@@ -0,0 +1,21 @@
+{
+ "keyboard": "0xcb/splaytoraid",
+ "keymap": "default",
+ "config": {
+ "features": {
+ "encoder_map": true
+ }
+ },
+ "layout": "LAYOUT_40",
+ "layers": [
+ [
+ "KC_TAB", "KC_Q", "KC_W", "KC_E", "KC_R", "KC_T", "KC_Y", "KC_U", "KC_I", "KC_O", "KC_P", "KC_BSPC",
+ "KC_LCTL", "KC_A", "KC_S", "KC_D", "KC_F", "KC_G", "KC_H", "KC_J", "KC_K", "KC_L", "KC_SCLN", "KC_QUOT",
+ "KC_Z", "KC_X", "KC_C", "KC_V", "KC_B", "KC_N", "KC_M", "KC_COMM", "KC_DOT", "KC_SLSH",
+ "KC_LSFT", "KC_ESC", "KC_ENT", "KC_MPLY", "KC_SPC", "KC_DEL", "KC_RSFT"
+ ]
+ ],
+ "encoders": [
+ [{"ccw": "KC_VOLD", "cw": "KC_VOLU"}]
+ ]
+}
diff --git a/keyboards/0xcb/splaytoraid/keymaps/default/rules.mk b/keyboards/0xcb/splaytoraid/keymaps/default/rules.mk
deleted file mode 100644
index ee32568148..0000000000
--- a/keyboards/0xcb/splaytoraid/keymaps/default/rules.mk
+++ /dev/null
@@ -1 +0,0 @@
-ENCODER_MAP_ENABLE = yes
diff --git a/keyboards/0xcb/splaytoraid/readme.md b/keyboards/0xcb/splaytoraid/readme.md
index ab53696c95..4fe04c8abd 100644
--- a/keyboards/0xcb/splaytoraid/readme.md
+++ b/keyboards/0xcb/splaytoraid/readme.md
@@ -11,13 +11,10 @@ A 40% ergonomic keyboard with a stacked acrylic case and RGB underglow.
Make example for this keyboard (after setting up your build environment):
make 0xcb/splaytoraid:default
- make 0xcb/splaytoraid/32u4:default
Flashing example for this keyboard:
make 0xcb/splaytoraid:default:flash
- make 0xcb/splaytoraid/32u4:default:flash
-
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).
@@ -29,8 +26,4 @@ Enter the bootloader in 3 ways:
* **Physical reset button**: Press the button on the controller (Helios) for more than 500ms or just press it (Pluto)
* **Keycode in layout**: Press the key mapped to `QK_BOOT` if it is available
-Please note that the default bootloader for the `32u4` version is QMK DFU, for compatibility with [0xCB Pluto](https://github.com/0xCB-dev/0xCB-Pluto). Generic Pro Micros often use a different bootloader, such as `caterina`.
-
-If the incorrect bootloader is specified, bootmagic reset and the `QK_BOOT` keycode will not work.
-
-To avoid this problem, set the correct bootloader in your custom keymap's `rules.mk` file before compiling, or flash using an appropriate target (ex: `make 0xcb/splaytoraid/32u4:default:avrdude`). See [flashing instructions and bootloader information](https://docs.qmk.fm/#/flashing) for more details.
+When using an AVR microcontroller with this keyboard, the bootloader is configured to be QMK DFU for compatibility with [0xCB Pluto](https://github.com/0xCB-dev/0xCB-Pluto). If your microcontroller does not use this bootloader, ensure to set this parameter appropriately in your keymap's `rules.mk` file.
diff --git a/keyboards/0xcb/splaytoraid/rp2040_ce/keyboard.json b/keyboards/0xcb/splaytoraid/rp2040_ce/keyboard.json
deleted file mode 100644
index b5a4f95c69..0000000000
--- a/keyboards/0xcb/splaytoraid/rp2040_ce/keyboard.json
+++ /dev/null
@@ -1,44 +0,0 @@
-{
- "development_board": "promicro_rp2040",
- "matrix_pins": {
- "cols": ["GP28", "GP27", "GP26", "GP29", "GP20", "GP22", "GP23"],
- "rows": ["GP0", "GP1", "GP2", "GP4", "GP6", "GP7", "GP8", "GP5"]
- },
- "diode_direction": "COL2ROW",
- "rgb_matrix": {
- "animations": {
- "band_sat": true,
- "band_spiral_val": true,
- "breathing": true,
- "cycle_all": true,
- "cycle_left_right": true,
- "cycle_out_in": true,
- "cycle_out_in_dual": true,
- "cycle_pinwheel": true,
- "cycle_up_down": true,
- "digital_rain": true,
- "dual_beacon": true,
- "hue_breathing": true,
- "jellybean_raindrops": true,
- "multisplash": true,
- "pixel_fractal": true,
- "pixel_rain": true,
- "rainbow_moving_chevron": true,
- "raindrops": true,
- "solid_reactive": true,
- "solid_reactive_simple": true,
- "solid_splash": true,
- "splash": true,
- "typing_heatmap": true
- }
- },
- "encoder": {
- "rotary": [
- {"pin_a": "GP9", "pin_b": "GP21"}
- ]
- },
- "ws2812": {
- "pin": "GP3",
- "driver": "vendor"
- }
-}
diff --git a/keyboards/0xcb/splaytoraid/rp2040_ce/readme.md b/keyboards/0xcb/splaytoraid/rp2040_ce/readme.md
deleted file mode 100644
index f6ff200ac3..0000000000
--- a/keyboards/0xcb/splaytoraid/rp2040_ce/readme.md
+++ /dev/null
@@ -1,3 +0,0 @@
-# rp2040_ce
-
-This folder is set as default in the parent rules.mk - it will build firmware compatible with the RP2040 Community Edition. See [here](https://docs.qmk.fm/#/platformdev_rp2040?id=rp2040_ce) for a list.
--
cgit v1.2.3
From a4d10e9f64da253ffea326ce4dc9fe2c06298f71 Mon Sep 17 00:00:00 2001
From: Jack Sangdahl
Date: Wed, 10 Sep 2025 17:33:38 -0600
Subject: Refactor `40percentclub/gherkin` (#25608)
---
data/mappings/keyboard_aliases.hjson | 6 ++
keyboards/40percentclub/gherkin/info.json | 62 ------------------
.../40percentclub/gherkin/kb2040/keyboard.json | 12 ----
keyboards/40percentclub/gherkin/kb2040/readme.md | 22 -------
keyboards/40percentclub/gherkin/keyboard.json | 74 ++++++++++++++++++++++
.../40percentclub/gherkin/keymaps/default/keymap.c | 52 ---------------
.../gherkin/keymaps/default/keymap.json | 37 +++++++++++
.../40percentclub/gherkin/pro_micro/keyboard.json | 16 -----
keyboards/40percentclub/gherkin/readme.md | 22 ++++---
9 files changed, 131 insertions(+), 172 deletions(-)
delete mode 100644 keyboards/40percentclub/gherkin/info.json
delete mode 100644 keyboards/40percentclub/gherkin/kb2040/keyboard.json
delete mode 100644 keyboards/40percentclub/gherkin/kb2040/readme.md
create mode 100644 keyboards/40percentclub/gherkin/keyboard.json
delete mode 100644 keyboards/40percentclub/gherkin/keymaps/default/keymap.c
create mode 100644 keyboards/40percentclub/gherkin/keymaps/default/keymap.json
delete mode 100644 keyboards/40percentclub/gherkin/pro_micro/keyboard.json
(limited to 'data')
diff --git a/data/mappings/keyboard_aliases.hjson b/data/mappings/keyboard_aliases.hjson
index 4f63c5f15f..c958cecbc9 100644
--- a/data/mappings/keyboard_aliases.hjson
+++ b/data/mappings/keyboard_aliases.hjson
@@ -1647,6 +1647,12 @@
"25keys/zinc": {
"target": "25keys/zinc/rev1"
},
+ "40percentclub/gherkin/kb2040": {
+ "target": "40percentclub/gherkin"
+ },
+ "40percentclub/gherkin/pro_micro": {
+ "target": "40percentclub/gherkin"
+ },
"40percentclub/i75": {
"target": "40percentclub/i75/promicro"
},
diff --git a/keyboards/40percentclub/gherkin/info.json b/keyboards/40percentclub/gherkin/info.json
deleted file mode 100644
index 644001bc05..0000000000
--- a/keyboards/40percentclub/gherkin/info.json
+++ /dev/null
@@ -1,62 +0,0 @@
-{
- "keyboard_name": "Gherkin",
- "manufacturer": "40 Percent Club",
- "maintainer": "qmk",
- "usb": {
- "vid": "0x4025",
- "pid": "0x6060",
- "device_version": "0.0.1"
- },
- "features": {
- "bootmagic": true,
- "mousekey": true,
- "extrakey": true,
- "nkro": true,
- "backlight": true
- },
- "qmk": {
- "locking": {
- "enabled": true,
- "resync": true
- }
- },
- "community_layouts": ["ortho_3x10"],
- "layouts": {
- "LAYOUT_ortho_3x10": {
- "layout": [
- {"matrix": [0, 0], "x": 0, "y": 0},
- {"matrix": [0, 1], "x": 1, "y": 0},
- {"matrix": [0, 2], "x": 2, "y": 0},
- {"matrix": [0, 3], "x": 3, "y": 0},
- {"matrix": [0, 4], "x": 4, "y": 0},
- {"matrix": [0, 5], "x": 5, "y": 0},
- {"matrix": [1, 0], "x": 6, "y": 0},
- {"matrix": [1, 1], "x": 7, "y": 0},
- {"matrix": [1, 2], "x": 8, "y": 0},
- {"matrix": [1, 3], "x": 9, "y": 0},
-
- {"matrix": [1, 4], "x": 0, "y": 1},
- {"matrix": [1, 5], "x": 1, "y": 1},
- {"matrix": [2, 0], "x": 2, "y": 1},
- {"matrix": [2, 1], "x": 3, "y": 1},
- {"matrix": [2, 2], "x": 4, "y": 1},
- {"matrix": [2, 3], "x": 5, "y": 1},
- {"matrix": [2, 4], "x": 6, "y": 1},
- {"matrix": [2, 5], "x": 7, "y": 1},
- {"matrix": [3, 0], "x": 8, "y": 1},
- {"matrix": [3, 1], "x": 9, "y": 1},
-
- {"matrix": [3, 2], "x": 0, "y": 2},
- {"matrix": [3, 3], "x": 1, "y": 2},
- {"matrix": [3, 4], "x": 2, "y": 2},
- {"matrix": [3, 5], "x": 3, "y": 2},
- {"matrix": [4, 0], "x": 4, "y": 2},
- {"matrix": [4, 1], "x": 5, "y": 2},
- {"matrix": [4, 2], "x": 6, "y": 2},
- {"matrix": [4, 3], "x": 7, "y": 2},
- {"matrix": [4, 4], "x": 8, "y": 2},
- {"matrix": [4, 5], "x": 9, "y": 2}
- ]
- }
- }
-}
diff --git a/keyboards/40percentclub/gherkin/kb2040/keyboard.json b/keyboards/40percentclub/gherkin/kb2040/keyboard.json
deleted file mode 100644
index 431ac371ee..0000000000
--- a/keyboards/40percentclub/gherkin/kb2040/keyboard.json
+++ /dev/null
@@ -1,12 +0,0 @@
-{
- "development_board": "kb2040",
- "matrix_pins": {
- "cols": ["GP8", "GP7", "GP6", "GP5", "GP4", "GP3"],
- "rows": ["GP26", "GP18", "GP20", "GP19", "GP10"]
- },
- "diode_direction": "COL2ROW",
- "backlight": {
- "pin": "GP9",
- "driver": "software"
- }
-}
diff --git a/keyboards/40percentclub/gherkin/kb2040/readme.md b/keyboards/40percentclub/gherkin/kb2040/readme.md
deleted file mode 100644
index 1fc9e8b18e..0000000000
--- a/keyboards/40percentclub/gherkin/kb2040/readme.md
+++ /dev/null
@@ -1,22 +0,0 @@
-# Gherkin
-
-
-
-
-===
-
-A 30 key keyboard with Adafruit's KB2040 as microcontroller.
-
-* [The original TMK firmware](https://github.com/di0ib/tmk_keyboard/tree/master/keyboard/gherkin)
-
-Keyboard Maintainer: QMK Community
-Hardware Supported: Gherkin PCB & Adafruit KB2040
-Hardware Availability: [Gherkin project on 40% Keyboards](http://www.40percent.club/2016/11/gherkin.html) and [Adafruit KB2040](https://www.adafruit.com/product/5302)
-
-Make example for this keyboard (after setting up your build environment):
-
- make 40percentclub/gherkin/kb2040: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).
-
-Use the KB2040 microcontroller as a drop-in replacement for the Pro Micro in this cute 30% keyboard.
diff --git a/keyboards/40percentclub/gherkin/keyboard.json b/keyboards/40percentclub/gherkin/keyboard.json
new file mode 100644
index 0000000000..1416997b47
--- /dev/null
+++ b/keyboards/40percentclub/gherkin/keyboard.json
@@ -0,0 +1,74 @@
+{
+ "manufacturer": "40 Percent Club",
+ "keyboard_name": "Gherkin",
+ "maintainer": "qmk",
+ "backlight": {
+ "pin": "B5"
+ },
+ "development_board": "promicro",
+ "diode_direction": "COL2ROW",
+ "features": {
+ "backlight": true,
+ "bootmagic": true,
+ "extrakey": true,
+ "mousekey": true,
+ "nkro": true
+ },
+ "indicators": {
+ "caps_lock": "B0",
+ "num_lock": "D5",
+ "on_state": 0
+ },
+ "matrix_pins": {
+ "cols": ["B4", "E6", "D7", "C6", "D4", "D0"],
+ "rows": ["F7", "B1", "B3", "B2", "B6"]
+ },
+ "qmk": {
+ "locking": {
+ "enabled": true,
+ "resync": true
+ }
+ },
+ "usb": {
+ "device_version": "0.0.1",
+ "pid": "0x6060",
+ "vid": "0x4025"
+ },
+ "community_layouts": ["ortho_3x10"],
+ "layouts": {
+ "LAYOUT_ortho_3x10": {
+ "layout": [
+ {"matrix": [0, 0], "x": 0, "y": 0},
+ {"matrix": [0, 1], "x": 1, "y": 0},
+ {"matrix": [0, 2], "x": 2, "y": 0},
+ {"matrix": [0, 3], "x": 3, "y": 0},
+ {"matrix": [0, 4], "x": 4, "y": 0},
+ {"matrix": [0, 5], "x": 5, "y": 0},
+ {"matrix": [1, 0], "x": 6, "y": 0},
+ {"matrix": [1, 1], "x": 7, "y": 0},
+ {"matrix": [1, 2], "x": 8, "y": 0},
+ {"matrix": [1, 3], "x": 9, "y": 0},
+ {"matrix": [1, 4], "x": 0, "y": 1},
+ {"matrix": [1, 5], "x": 1, "y": 1},
+ {"matrix": [2, 0], "x": 2, "y": 1},
+ {"matrix": [2, 1], "x": 3, "y": 1},
+ {"matrix": [2, 2], "x": 4, "y": 1},
+ {"matrix": [2, 3], "x": 5, "y": 1},
+ {"matrix": [2, 4], "x": 6, "y": 1},
+ {"matrix": [2, 5], "x": 7, "y": 1},
+ {"matrix": [3, 0], "x": 8, "y": 1},
+ {"matrix": [3, 1], "x": 9, "y": 1},
+ {"matrix": [3, 2], "x": 0, "y": 2},
+ {"matrix": [3, 3], "x": 1, "y": 2},
+ {"matrix": [3, 4], "x": 2, "y": 2},
+ {"matrix": [3, 5], "x": 3, "y": 2},
+ {"matrix": [4, 0], "x": 4, "y": 2},
+ {"matrix": [4, 1], "x": 5, "y": 2},
+ {"matrix": [4, 2], "x": 6, "y": 2},
+ {"matrix": [4, 3], "x": 7, "y": 2},
+ {"matrix": [4, 4], "x": 8, "y": 2},
+ {"matrix": [4, 5], "x": 9, "y": 2}
+ ]
+ }
+ }
+}
diff --git a/keyboards/40percentclub/gherkin/keymaps/default/keymap.c b/keyboards/40percentclub/gherkin/keymaps/default/keymap.c
deleted file mode 100644
index 8c28b68afa..0000000000
--- a/keyboards/40percentclub/gherkin/keymaps/default/keymap.c
+++ /dev/null
@@ -1,52 +0,0 @@
-#include QMK_KEYBOARD_H
-
-#define FN1_SPC LT(1, KC_SPC)
-#define FN2_BSPC LT(2, KC_BSPC)
-#define FN3_C LT(3, KC_C)
-#define FN4_V LT(4, KC_V)
-#define FN5_B LT(5, KC_B)
-#define CTL_Z CTL_T(KC_Z)
-#define ALT_X ALT_T(KC_X)
-#define ALT_N ALGR_T(KC_N)
-#define CTL_M RCTL_T(KC_M)
-#define SFT_ENT RSFT_T(KC_ENT)
-
-const uint16_t PROGMEM keymaps[][MATRIX_ROWS][MATRIX_COLS] = {
-
- [0] = LAYOUT_ortho_3x10(
- KC_Q, KC_W, KC_E, KC_R, KC_T, KC_Y, KC_U, KC_I, KC_O, KC_P,
- KC_A, KC_S, KC_D, KC_F, KC_G, KC_H, KC_J, KC_K, KC_L, KC_ESC,
- CTL_Z, ALT_X, FN3_C, FN4_V, FN2_BSPC,FN1_SPC, FN5_B, ALT_N, CTL_M, SFT_ENT
- ),
-
- [1] = LAYOUT_ortho_3x10(
- KC_1, KC_2, KC_3, KC_4, KC_5, KC_6, KC_7, KC_8, KC_9, KC_0,
- KC_F1, KC_F2, KC_F3, KC_F4, KC_F5, KC_F6, KC_F7, KC_F8, KC_F9, KC_F10,
- _______, _______, _______, _______, KC_DEL, _______, _______, _______, _______, _______
- ),
-
- [2] = LAYOUT_ortho_3x10(
- KC_EXLM, KC_AT, KC_HASH, KC_DLR, KC_PERC, KC_CIRC, KC_AMPR, KC_ASTR, KC_LPRN, KC_RPRN,
- KC_F11, KC_F12, _______, _______, _______, _______, _______, _______, _______, KC_GRV,
- _______, _______, _______, _______, _______, _______, _______, _______, _______, _______
- ),
-
- [3] = LAYOUT_ortho_3x10(
- _______, _______, _______, _______, _______, KC_MINS, KC_EQL, KC_LBRC, KC_RBRC, KC_BSLS,
- KC_TAB, _______, _______, _______, _______, KC_COMM, KC_DOT, KC_SLSH, KC_SCLN, KC_QUOT,
- _______, _______, _______, _______, _______, _______, KC_LEFT, KC_DOWN, KC_UP, KC_RGHT
- ),
-
- [4] = LAYOUT_ortho_3x10(
- _______, _______, _______, _______, _______, KC_UNDS, KC_PLUS, KC_LCBR, KC_RCBR, KC_PIPE,
- KC_TAB, _______, _______, _______, _______, KC_LABK, KC_RABK, KC_QUES, KC_COLN, KC_DQUO,
- _______, _______, _______, _______, _______, _______, KC_HOME, KC_PGDN, KC_PGUP, KC_END
- ),
-
- [5] = LAYOUT_ortho_3x10(
- KC_CALC, KC_WHOM, KC_MAIL, KC_MYCM, _______, _______, _______, _______, _______, KC_PSCR,
- _______, _______, _______, _______, _______, _______, _______, _______, BL_DOWN, BL_UP,
- _______, _______, _______, _______, QK_BOOT, _______, _______, _______, _______, _______
- )
-
-};
diff --git a/keyboards/40percentclub/gherkin/keymaps/default/keymap.json b/keyboards/40percentclub/gherkin/keymaps/default/keymap.json
new file mode 100644
index 0000000000..e81a6fd8f5
--- /dev/null
+++ b/keyboards/40percentclub/gherkin/keymaps/default/keymap.json
@@ -0,0 +1,37 @@
+{
+ "keyboard": "40percentclub/gherkin",
+ "keymap": "default",
+ "layout": "LAYOUT_ortho_3x10",
+ "layers": [
+ [
+ "KC_Q", "KC_W", "KC_E", "KC_R", "KC_T", "KC_Y", "KC_U", "KC_I", "KC_O", "KC_P",
+ "KC_A", "KC_S", "KC_D", "KC_F", "KC_G", "KC_H", "KC_J", "KC_K", "KC_L", "KC_ESC",
+ "CTL_T(KC_Z)", "ALT_T(KC_X)", "LT(3, KC_C)", "LT(4, KC_V)", "LT(2, KC_BSPC)", "LT(1, KC_SPC)", "LT(5, KC_B)", "ALGR_T(KC_N)", "RCTL_T(KC_M)", "RSFT_T(KC_ENT)"
+ ],
+ [
+ "KC_1", "KC_2", "KC_3", "KC_4", "KC_5", "KC_6", "KC_7", "KC_8", "KC_9", "KC_0",
+ "KC_F1", "KC_F2", "KC_F3", "KC_F4", "KC_F5", "KC_F6", "KC_F7", "KC_F8", "KC_F9", "KC_F10",
+ "_______", "_______", "_______", "_______", "KC_DEL", "_______", "_______", "_______", "_______", "_______"
+ ],
+ [
+ "KC_EXLM", "KC_AT", "KC_HASH", "KC_DLR", "KC_PERC", "KC_CIRC", "KC_AMPR", "KC_ASTR", "KC_LPRN", "KC_RPRN",
+ "KC_F11", "KC_F12", "_______", "_______", "_______", "_______", "_______", "_______", "_______", "KC_GRV",
+ "_______", "_______", "_______", "_______", "_______", "_______", "_______", "_______", "_______", "_______"
+ ],
+ [
+ "_______", "_______", "_______", "_______", "_______", "KC_MINS", "KC_EQL", "KC_LBRC", "KC_RBRC", "KC_BSLS",
+ "KC_TAB", "_______", "_______", "_______", "_______", "KC_COMM", "KC_DOT", "KC_SLSH", "KC_SCLN", "KC_QUOT",
+ "_______", "_______", "_______", "_______", "_______", "_______", "KC_LEFT", "KC_DOWN", "KC_UP", "KC_RGHT"
+ ],
+ [
+ "_______", "_______", "_______", "_______", "_______", "KC_UNDS", "KC_PLUS", "KC_LCBR", "KC_RCBR", "KC_PIPE",
+ "KC_TAB", "_______", "_______", "_______", "_______", "KC_LABK", "KC_RABK", "KC_QUES", "KC_COLN", "KC_DQUO",
+ "_______", "_______", "_______", "_______", "_______", "_______", "KC_HOME", "KC_PGDN", "KC_PGUP", "KC_END"
+ ],
+ [
+ "KC_CALC", "KC_WHOM", "KC_MAIL", "KC_MYCM", "_______", "_______", "_______", "_______", "_______", "KC_PSCR",
+ "_______", "_______", "_______", "_______", "_______", "_______", "_______", "_______", "BL_DOWN", "BL_UP",
+ "_______", "_______", "_______", "_______", "QK_BOOT", "_______", "_______", "_______", "_______", "_______"
+ ]
+ ]
+}
diff --git a/keyboards/40percentclub/gherkin/pro_micro/keyboard.json b/keyboards/40percentclub/gherkin/pro_micro/keyboard.json
deleted file mode 100644
index 882ea8f72b..0000000000
--- a/keyboards/40percentclub/gherkin/pro_micro/keyboard.json
+++ /dev/null
@@ -1,16 +0,0 @@
-{
- "development_board": "promicro",
- "matrix_pins": {
- "cols": ["B4", "E6", "D7", "C6", "D4", "D0"],
- "rows": ["F7", "B1", "B3", "B2", "B6"]
- },
- "diode_direction": "COL2ROW",
- "indicators": {
- "num_lock": "D5",
- "caps_lock": "B0",
- "on_state": 0
- },
- "backlight": {
- "pin": "B5"
- }
-}
diff --git a/keyboards/40percentclub/gherkin/readme.md b/keyboards/40percentclub/gherkin/readme.md
index 7404f19819..5ce4d173ca 100644
--- a/keyboards/40percentclub/gherkin/readme.md
+++ b/keyboards/40percentclub/gherkin/readme.md
@@ -1,21 +1,27 @@
# Gherkin

-===
A 30 key keyboard.
-* [The original TMK firmware](https://github.com/di0ib/tmk_keyboard/tree/master/keyboard/gherkin)
-
-Keyboard Maintainer: QMK Community
-Hardware Supported: Gherkin PCB
-Hardware Availability: [Gherkin project on 40% Keyboards](http://www.40percent.club/2016/11/gherkin.html)
+* Keyboard Maintainer: QMK Community
+* Hardware Supported: Gherkin PCB w/ Pro Micro compatible development board
+* Hardware Availability: [Gherkin project on 40% Keyboards](http://www.40percent.club/2016/11/gherkin.html)
Make example for this keyboard (after setting up your build environment):
make 40percentclub/gherkin:default
+Flashing example for this keyboard:
+
+ make 40percentclub/gherkin:default:flash
+
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).
-First pass at adding support for the gherkin keyboard. Compiles but completely
-untested. Intended to kick-start development.
+## Bootloader
+
+Enter the bootloader in 3 ways:
+
+* **Bootmagic reset**: Hold down the key at (0,0) in the matrix and plug in the keyboard
+* **Physical reset**: Short the Ground and Reset pins on the microcontroller
+* **Keycode in layout**: Press the key mapped to `QK_BOOT` if it is available
--
cgit v1.2.3
From cf58a8733d35bbfea2f54bd0175747ee63d9b2e3 Mon Sep 17 00:00:00 2001
From: Joel Challis
Date: Fri, 12 Sep 2025 10:23:23 +0100
Subject: Add DIP Switch map support to keymap.json (#25431)
---
data/schemas/keymap.jsonschema | 11 +++++++++++
lib/python/qmk/keymap.py | 20 ++++++++++++++++++++
lib/python/qmk/tests/test_cli_commands.py | 2 ++
lib/python/qmk/tests/test_qmk_keymap.py | 1 +
4 files changed, 34 insertions(+)
(limited to 'data')
diff --git a/data/schemas/keymap.jsonschema b/data/schemas/keymap.jsonschema
index 99aeaa6b6c..71b39fc7c4 100644
--- a/data/schemas/keymap.jsonschema
+++ b/data/schemas/keymap.jsonschema
@@ -35,6 +35,17 @@
}
}
},
+ "dip_switches": {
+ "type": "array",
+ "items": {
+ "type": "object",
+ "required": ["on", "off"],
+ "properties": {
+ "on": {"type": "string"},
+ "off": {"type": "string"}
+ }
+ }
+ },
"macros": {
"type": "array",
"items": {
diff --git a/lib/python/qmk/keymap.py b/lib/python/qmk/keymap.py
index 4cf07f59d6..0ac04f6f73 100644
--- a/lib/python/qmk/keymap.py
+++ b/lib/python/qmk/keymap.py
@@ -32,6 +32,7 @@ __INCLUDES__
__KEYMAP_GOES_HERE__
__ENCODER_MAP_GOES_HERE__
+__DIP_SWITCH_MAP_GOES_HERE__
__MACRO_OUTPUT_GOES_HERE__
#ifdef OTHER_KEYMAP_C
@@ -66,6 +67,19 @@ def _generate_encodermap_table(keymap_json):
return lines
+def _generate_dipswitchmap_table(keymap_json):
+ lines = [
+ '#if defined(DIP_SWITCH_ENABLE) && defined(DIP_SWITCH_MAP_ENABLE)',
+ 'const uint16_t PROGMEM dip_switch_map[NUM_DIP_SWITCHES][NUM_DIP_STATES] = {',
+ ]
+ for index, switch in enumerate(keymap_json['dip_switches']):
+ if index != 0:
+ lines[-1] = lines[-1] + ','
+ lines.append(f' DIP_SWITCH_OFF_ON({_strip_any(switch["off"])}, {_strip_any(switch["on"])})')
+ lines.extend(['};', '#endif // defined(DIP_SWITCH_ENABLE) && defined(DIP_SWITCH_MAP_ENABLE)'])
+ return lines
+
+
def _generate_macros_function(keymap_json):
macro_txt = [
'bool process_record_user(uint16_t keycode, keyrecord_t *record) {',
@@ -286,6 +300,12 @@ def generate_c(keymap_json):
encodermap = '\n'.join(encoder_txt)
new_keymap = new_keymap.replace('__ENCODER_MAP_GOES_HERE__', encodermap)
+ dipswitchmap = ''
+ if 'dip_switches' in keymap_json and keymap_json['dip_switches'] is not None:
+ dip_txt = _generate_dipswitchmap_table(keymap_json)
+ dipswitchmap = '\n'.join(dip_txt)
+ new_keymap = new_keymap.replace('__DIP_SWITCH_MAP_GOES_HERE__', dipswitchmap)
+
macros = ''
if 'macros' in keymap_json and keymap_json['macros'] is not None:
macro_txt = _generate_macros_function(keymap_json)
diff --git a/lib/python/qmk/tests/test_cli_commands.py b/lib/python/qmk/tests/test_cli_commands.py
index dd659fe0f2..2716459989 100644
--- a/lib/python/qmk/tests/test_cli_commands.py
+++ b/lib/python/qmk/tests/test_cli_commands.py
@@ -159,6 +159,7 @@ const uint16_t PROGMEM keymaps[][MATRIX_ROWS][MATRIX_COLS] = {
+
#ifdef OTHER_KEYMAP_C
# include OTHER_KEYMAP_C
#endif // OTHER_KEYMAP_C
@@ -196,6 +197,7 @@ const uint16_t PROGMEM keymaps[][MATRIX_ROWS][MATRIX_COLS] = {
+
#ifdef OTHER_KEYMAP_C
# include OTHER_KEYMAP_C
#endif // OTHER_KEYMAP_C
diff --git a/lib/python/qmk/tests/test_qmk_keymap.py b/lib/python/qmk/tests/test_qmk_keymap.py
index 80cc679b00..34360d3b6d 100644
--- a/lib/python/qmk/tests/test_qmk_keymap.py
+++ b/lib/python/qmk/tests/test_qmk_keymap.py
@@ -27,6 +27,7 @@ const uint16_t PROGMEM keymaps[][MATRIX_ROWS][MATRIX_COLS] = {
+
#ifdef OTHER_KEYMAP_C
# include OTHER_KEYMAP_C
#endif // OTHER_KEYMAP_C
--
cgit v1.2.3
From 883465d9fb29cd793684ee0ac688ff3517cf8bc0 Mon Sep 17 00:00:00 2001
From: Joel Challis
Date: Mon, 22 Sep 2025 00:01:23 +0100
Subject: Add generic handling to cycle LED/RGB Matrix flags (#24649)
---
data/constants/keycodes/keycodes_0.0.8.hjson | 0
.../keycodes/keycodes_0.0.8_lighting.hjson | 37 ++++++++++++++++
data/mappings/info_config.hjson | 2 +
data/schemas/keyboard.jsonschema | 10 +++++
docs/reference_info_json.md | 6 +++
lib/python/qmk/info.py | 8 ++++
quantum/keycodes.h | 22 +++++++---
quantum/led_matrix/led_matrix.c | 51 ++++++++++++++++++++++
quantum/led_matrix/led_matrix.h | 4 ++
quantum/process_keycode/process_led_matrix.c | 6 +++
quantum/process_keycode/process_rgb_matrix.c | 14 ++++++
quantum/rgb_matrix/rgb_matrix.c | 51 ++++++++++++++++++++++
quantum/rgb_matrix/rgb_matrix.h | 4 ++
13 files changed, 208 insertions(+), 7 deletions(-)
create mode 100644 data/constants/keycodes/keycodes_0.0.8.hjson
create mode 100644 data/constants/keycodes/keycodes_0.0.8_lighting.hjson
(limited to 'data')
diff --git a/data/constants/keycodes/keycodes_0.0.8.hjson b/data/constants/keycodes/keycodes_0.0.8.hjson
new file mode 100644
index 0000000000..e69de29bb2
diff --git a/data/constants/keycodes/keycodes_0.0.8_lighting.hjson b/data/constants/keycodes/keycodes_0.0.8_lighting.hjson
new file mode 100644
index 0000000000..fa7227235e
--- /dev/null
+++ b/data/constants/keycodes/keycodes_0.0.8_lighting.hjson
@@ -0,0 +1,37 @@
+{
+ "keycodes": {
+ "0x7819": {
+ "group": "led_matrix",
+ "key": "QK_LED_MATRIX_FLAG_NEXT",
+ "label": "LED Matrix Flag Next",
+ "aliases": [
+ "LM_FLGN"
+ ]
+ },
+ "0x781A": {
+ "group": "led_matrix",
+ "key": "QK_LED_MATRIX_FLAG_PREVIOUS",
+ "label": "LED Matrix Flag Previous",
+ "aliases": [
+ "LM_FLGP"
+ ]
+ },
+
+ "0x784D": {
+ "group": "rgb_matrix",
+ "key": "QK_RGB_MATRIX_FLAG_NEXT",
+ "label": "RGB Matrix Flag Next",
+ "aliases": [
+ "RM_FLGN"
+ ]
+ },
+ "0x784E": {
+ "group": "rgb_matrix",
+ "key": "QK_RGB_MATRIX_FLAG_PREVIOUS",
+ "label": "RGB Matrix Flag Previous",
+ "aliases": [
+ "RM_FLGP"
+ ]
+ }
+ }
+}
diff --git a/data/mappings/info_config.hjson b/data/mappings/info_config.hjson
index 4c53aa4339..c4052c64f6 100644
--- a/data/mappings/info_config.hjson
+++ b/data/mappings/info_config.hjson
@@ -98,6 +98,7 @@
// LED Matrix
"LED_MATRIX_CENTER": {"info_key": "led_matrix.center_point", "value_type": "array.int"},
+ "LED_MATRIX_FLAG_STEPS": {"info_key": "led_matrix.flag_steps", "value_type": "array.int"},
"LED_MATRIX_KEYRELEASES": {"info_key": "led_matrix.react_on_keyup", "value_type": "flag"},
"LED_MATRIX_LED_FLUSH_LIMIT": {"info_key": "led_matrix.led_flush_limit", "value_type": "int"},
"LED_MATRIX_LED_PROCESS_LIMIT": {"info_key": "led_matrix.led_process_limit", "value_type": "int", "to_json": false},
@@ -147,6 +148,7 @@
// RGB Matrix
"RGB_MATRIX_CENTER": {"info_key": "rgb_matrix.center_point", "value_type": "array.int"},
+ "RGB_MATRIX_FLAG_STEPS": {"info_key": "rgb_matrix.flag_steps", "value_type": "array.int"},
"RGB_MATRIX_HUE_STEP": {"info_key": "rgb_matrix.hue_steps", "value_type": "int"},
"RGB_MATRIX_KEYRELEASES": {"info_key": "rgb_matrix.react_on_keyup", "value_type": "flag"},
"RGB_MATRIX_LED_FLUSH_LIMIT": {"info_key": "rgb_matrix.led_flush_limit", "value_type": "int"},
diff --git a/data/schemas/keyboard.jsonschema b/data/schemas/keyboard.jsonschema
index 93b1c82b1c..93fc4ed8a2 100644
--- a/data/schemas/keyboard.jsonschema
+++ b/data/schemas/keyboard.jsonschema
@@ -571,6 +571,11 @@
"maxItems": 2,
"items": {"$ref": "./definitions.jsonschema#/unsigned_int_8"}
},
+ "flag_steps": {
+ "type": "array",
+ "minItems": 1,
+ "items": {"$ref": "./definitions.jsonschema#/unsigned_int_8"}
+ },
"max_brightness": {"$ref": "./definitions.jsonschema#/unsigned_int_8"},
"timeout": {"$ref": "./definitions.jsonschema#/unsigned_int"},
"val_steps": {"$ref": "./definitions.jsonschema#/unsigned_int"},
@@ -656,6 +661,11 @@
"maxItems": 2,
"items": {"$ref": "./definitions.jsonschema#/unsigned_int_8"}
},
+ "flag_steps": {
+ "type": "array",
+ "minItems": 1,
+ "items": {"$ref": "./definitions.jsonschema#/unsigned_int_8"}
+ },
"max_brightness": {"$ref": "./definitions.jsonschema#/unsigned_int_8"},
"timeout": {"$ref": "./definitions.jsonschema#/unsigned_int"},
"hue_steps": {"$ref": "./definitions.jsonschema#/unsigned_int"},
diff --git a/docs/reference_info_json.md b/docs/reference_info_json.md
index 84377ef36c..e7cb8c31e8 100644
--- a/docs/reference_info_json.md
+++ b/docs/reference_info_json.md
@@ -415,6 +415,9 @@ Configures the [LED Matrix](features/led_matrix) feature.
* `center_point` Array: Number
* The centroid (geometric center) of the LEDs. Used for certain effects.
* Default: `[112, 32]`
+ * `flag_steps` Array: Number
+ * A list of flag bitfields that can be cycled through.
+ * Default: `[255, 5, 0]`
* `default`
* `animation` String
* The default effect. Must be one of `led_matrix.animations`
@@ -660,6 +663,9 @@ Configures the [RGB Matrix](features/rgb_matrix) feature.
* `center_point` Array: Number
* The centroid (geometric center) of the LEDs. Used for certain effects.
* Default: `[112, 32]`
+ * `flag_steps` Array: Number
+ * A list of flag bitfields that can be cycled through.
+ * Default: `[255, 5, 2, 0]`
* `default`
* `animation` String
* The default effect. Must be one of `rgb_matrix.animations`
diff --git a/lib/python/qmk/info.py b/lib/python/qmk/info.py
index f63228b2bc..59a64095e7 100644
--- a/lib/python/qmk/info.py
+++ b/lib/python/qmk/info.py
@@ -800,6 +800,14 @@ def _extract_led_config(info_data, keyboard):
if info_data[feature].get('layout', None) and not info_data[feature].get('led_count', None):
info_data[feature]['led_count'] = len(info_data[feature]['layout'])
+ if info_data[feature].get('layout', None) and not info_data[feature].get('flag_steps', None):
+ flags = {0xFF, 0}
+ # if only a single flag is used, assume only all+none flags
+ unique_flags = set(x.get('flags', 0) for x in info_data[feature]['layout'])
+ if len(unique_flags) > 1:
+ flags.update(unique_flags)
+ info_data[feature]['flag_steps'] = sorted(list(flags), reverse=True)
+
return info_data
diff --git a/quantum/keycodes.h b/quantum/keycodes.h
index 6a59aa376d..e5a64d9a71 100644
--- a/quantum/keycodes.h
+++ b/quantum/keycodes.h
@@ -26,11 +26,11 @@
#pragma once
// clang-format off
-#define QMK_KEYCODES_VERSION "0.0.7"
-#define QMK_KEYCODES_VERSION_BCD 0x00000007
+#define QMK_KEYCODES_VERSION "0.0.8"
+#define QMK_KEYCODES_VERSION_BCD 0x00000008
#define QMK_KEYCODES_VERSION_MAJOR 0
#define QMK_KEYCODES_VERSION_MINOR 0
-#define QMK_KEYCODES_VERSION_PATCH 7
+#define QMK_KEYCODES_VERSION_PATCH 8
enum qk_keycode_ranges {
// Ranges
@@ -663,6 +663,8 @@ enum qk_keycode_defines {
QK_LED_MATRIX_BRIGHTNESS_DOWN = 0x7816,
QK_LED_MATRIX_SPEED_UP = 0x7817,
QK_LED_MATRIX_SPEED_DOWN = 0x7818,
+ QK_LED_MATRIX_FLAG_NEXT = 0x7819,
+ QK_LED_MATRIX_FLAG_PREVIOUS = 0x781A,
QK_UNDERGLOW_TOGGLE = 0x7820,
QK_UNDERGLOW_MODE_NEXT = 0x7821,
QK_UNDERGLOW_MODE_PREVIOUS = 0x7822,
@@ -697,6 +699,8 @@ enum qk_keycode_defines {
QK_RGB_MATRIX_VALUE_DOWN = 0x784A,
QK_RGB_MATRIX_SPEED_UP = 0x784B,
QK_RGB_MATRIX_SPEED_DOWN = 0x784C,
+ QK_RGB_MATRIX_FLAG_NEXT = 0x784D,
+ QK_RGB_MATRIX_FLAG_PREVIOUS = 0x784E,
QK_BOOTLOADER = 0x7C00,
QK_REBOOT = 0x7C01,
QK_DEBUG_TOGGLE = 0x7C02,
@@ -1352,6 +1356,8 @@ enum qk_keycode_defines {
LM_BRID = QK_LED_MATRIX_BRIGHTNESS_DOWN,
LM_SPDU = QK_LED_MATRIX_SPEED_UP,
LM_SPDD = QK_LED_MATRIX_SPEED_DOWN,
+ LM_FLGN = QK_LED_MATRIX_FLAG_NEXT,
+ LM_FLGP = QK_LED_MATRIX_FLAG_PREVIOUS,
UG_TOGG = QK_UNDERGLOW_TOGGLE,
UG_NEXT = QK_UNDERGLOW_MODE_NEXT,
UG_PREV = QK_UNDERGLOW_MODE_PREVIOUS,
@@ -1386,6 +1392,8 @@ enum qk_keycode_defines {
RM_VALD = QK_RGB_MATRIX_VALUE_DOWN,
RM_SPDU = QK_RGB_MATRIX_SPEED_UP,
RM_SPDD = QK_RGB_MATRIX_SPEED_DOWN,
+ RM_FLGN = QK_RGB_MATRIX_FLAG_NEXT,
+ RM_FLGP = QK_RGB_MATRIX_FLAG_PREVIOUS,
QK_BOOT = QK_BOOTLOADER,
QK_RBT = QK_REBOOT,
DB_TOGG = QK_DEBUG_TOGGLE,
@@ -1511,10 +1519,10 @@ enum qk_keycode_defines {
#define IS_MACRO_KEYCODE(code) ((code) >= QK_MACRO_0 && (code) <= QK_MACRO_31)
#define IS_CONNECTION_KEYCODE(code) ((code) >= QK_OUTPUT_AUTO && (code) <= QK_BLUETOOTH_PROFILE5)
#define IS_BACKLIGHT_KEYCODE(code) ((code) >= QK_BACKLIGHT_ON && (code) <= QK_BACKLIGHT_TOGGLE_BREATHING)
-#define IS_LED_MATRIX_KEYCODE(code) ((code) >= QK_LED_MATRIX_ON && (code) <= QK_LED_MATRIX_SPEED_DOWN)
+#define IS_LED_MATRIX_KEYCODE(code) ((code) >= QK_LED_MATRIX_ON && (code) <= QK_LED_MATRIX_FLAG_PREVIOUS)
#define IS_UNDERGLOW_KEYCODE(code) ((code) >= QK_UNDERGLOW_TOGGLE && (code) <= QK_UNDERGLOW_SPEED_DOWN)
#define IS_RGB_KEYCODE(code) ((code) >= RGB_MODE_PLAIN && (code) <= RGB_MODE_TWINKLE)
-#define IS_RGB_MATRIX_KEYCODE(code) ((code) >= QK_RGB_MATRIX_ON && (code) <= QK_RGB_MATRIX_SPEED_DOWN)
+#define IS_RGB_MATRIX_KEYCODE(code) ((code) >= QK_RGB_MATRIX_ON && (code) <= QK_RGB_MATRIX_FLAG_PREVIOUS)
#define IS_QUANTUM_KEYCODE(code) ((code) >= QK_BOOTLOADER && (code) <= QK_LAYER_LOCK)
#define IS_KB_KEYCODE(code) ((code) >= QK_KB_0 && (code) <= QK_KB_31)
#define IS_USER_KEYCODE(code) ((code) >= QK_USER_0 && (code) <= QK_USER_31)
@@ -1537,10 +1545,10 @@ enum qk_keycode_defines {
#define MACRO_KEYCODE_RANGE QK_MACRO_0 ... QK_MACRO_31
#define CONNECTION_KEYCODE_RANGE QK_OUTPUT_AUTO ... QK_BLUETOOTH_PROFILE5
#define BACKLIGHT_KEYCODE_RANGE QK_BACKLIGHT_ON ... QK_BACKLIGHT_TOGGLE_BREATHING
-#define LED_MATRIX_KEYCODE_RANGE QK_LED_MATRIX_ON ... QK_LED_MATRIX_SPEED_DOWN
+#define LED_MATRIX_KEYCODE_RANGE QK_LED_MATRIX_ON ... QK_LED_MATRIX_FLAG_PREVIOUS
#define UNDERGLOW_KEYCODE_RANGE QK_UNDERGLOW_TOGGLE ... QK_UNDERGLOW_SPEED_DOWN
#define RGB_KEYCODE_RANGE RGB_MODE_PLAIN ... RGB_MODE_TWINKLE
-#define RGB_MATRIX_KEYCODE_RANGE QK_RGB_MATRIX_ON ... QK_RGB_MATRIX_SPEED_DOWN
+#define RGB_MATRIX_KEYCODE_RANGE QK_RGB_MATRIX_ON ... QK_RGB_MATRIX_FLAG_PREVIOUS
#define QUANTUM_KEYCODE_RANGE QK_BOOTLOADER ... QK_LAYER_LOCK
#define KB_KEYCODE_RANGE QK_KB_0 ... QK_KB_31
#define USER_KEYCODE_RANGE QK_USER_0 ... QK_USER_31
diff --git a/quantum/led_matrix/led_matrix.c b/quantum/led_matrix/led_matrix.c
index 9c8004cc17..b2665597df 100644
--- a/quantum/led_matrix/led_matrix.c
+++ b/quantum/led_matrix/led_matrix.c
@@ -70,6 +70,13 @@ uint8_t g_led_frame_buffer[MATRIX_ROWS][MATRIX_COLS] = {{0}};
last_hit_t g_last_hit_tracker;
#endif // LED_MATRIX_KEYREACTIVE_ENABLED
+#ifndef LED_MATRIX_FLAG_STEPS
+# define LED_MATRIX_FLAG_STEPS \
+ { LED_FLAG_ALL, LED_FLAG_KEYLIGHT | LED_FLAG_MODIFIER, LED_FLAG_NONE }
+#endif
+static const uint8_t led_matrix_flag_steps[] = LED_MATRIX_FLAG_STEPS;
+#define LED_MATRIX_FLAG_STEPS_COUNT ARRAY_SIZE(led_matrix_flag_steps)
+
// internals
static bool suspend_state = false;
static uint8_t led_last_enable = UINT8_MAX;
@@ -661,6 +668,50 @@ void led_matrix_set_flags_noeeprom(led_flags_t flags) {
led_matrix_set_flags_eeprom_helper(flags, false);
}
+void led_matrix_flags_step_helper(bool write_to_eeprom) {
+ led_flags_t flags = led_matrix_get_flags();
+
+ uint8_t next = 0;
+ for (uint8_t i = 0; i < LED_MATRIX_FLAG_STEPS_COUNT; i++) {
+ if (led_matrix_flag_steps[i] == flags) {
+ next = i == LED_MATRIX_FLAG_STEPS_COUNT - 1 ? 0 : i + 1;
+ break;
+ }
+ }
+
+ led_matrix_set_flags_eeprom_helper(led_matrix_flag_steps[next], write_to_eeprom);
+}
+
+void led_matrix_flags_step_noeeprom(void) {
+ led_matrix_flags_step_helper(false);
+}
+
+void led_matrix_flags_step(void) {
+ led_matrix_flags_step_helper(true);
+}
+
+void led_matrix_flags_step_reverse_helper(bool write_to_eeprom) {
+ led_flags_t flags = led_matrix_get_flags();
+
+ uint8_t next = 0;
+ for (uint8_t i = 0; i < LED_MATRIX_FLAG_STEPS_COUNT; i++) {
+ if (led_matrix_flag_steps[i] == flags) {
+ next = i == 0 ? LED_MATRIX_FLAG_STEPS_COUNT - 1 : i - 1;
+ break;
+ }
+ }
+
+ led_matrix_set_flags_eeprom_helper(led_matrix_flag_steps[next], write_to_eeprom);
+}
+
+void led_matrix_flags_step_reverse_noeeprom(void) {
+ led_matrix_flags_step_reverse_helper(false);
+}
+
+void led_matrix_flags_step_reverse(void) {
+ led_matrix_flags_step_reverse_helper(true);
+}
+
// LED Matrix naming
#undef LED_MATRIX_EFFECT
#ifdef LED_MATRIX_MODE_NAME_ENABLE
diff --git a/quantum/led_matrix/led_matrix.h b/quantum/led_matrix/led_matrix.h
index 9a49515ab2..f484c700f4 100644
--- a/quantum/led_matrix/led_matrix.h
+++ b/quantum/led_matrix/led_matrix.h
@@ -183,6 +183,10 @@ void led_matrix_decrease_speed_noeeprom(void);
led_flags_t led_matrix_get_flags(void);
void led_matrix_set_flags(led_flags_t flags);
void led_matrix_set_flags_noeeprom(led_flags_t flags);
+void led_matrix_flags_step_noeeprom(void);
+void led_matrix_flags_step(void);
+void led_matrix_flags_step_reverse_noeeprom(void);
+void led_matrix_flags_step_reverse(void);
#ifdef LED_MATRIX_MODE_NAME_ENABLE
const char *led_matrix_get_mode_name(uint8_t mode);
diff --git a/quantum/process_keycode/process_led_matrix.c b/quantum/process_keycode/process_led_matrix.c
index 7f95bf1011..3342b33b92 100644
--- a/quantum/process_keycode/process_led_matrix.c
+++ b/quantum/process_keycode/process_led_matrix.c
@@ -40,6 +40,12 @@ bool process_led_matrix(uint16_t keycode, keyrecord_t *record) {
case QK_LED_MATRIX_SPEED_DOWN:
led_matrix_decrease_speed();
return false;
+ case QK_LED_MATRIX_FLAG_NEXT:
+ led_matrix_flags_step();
+ return false;
+ case QK_LED_MATRIX_FLAG_PREVIOUS:
+ led_matrix_flags_step_reverse();
+ return false;
}
}
diff --git a/quantum/process_keycode/process_rgb_matrix.c b/quantum/process_keycode/process_rgb_matrix.c
index fd2aa1a0c7..c18212294d 100644
--- a/quantum/process_keycode/process_rgb_matrix.c
+++ b/quantum/process_keycode/process_rgb_matrix.c
@@ -94,6 +94,20 @@ bool process_rgb_matrix(uint16_t keycode, keyrecord_t *record) {
rgb_matrix_decrease_speed();
}
return false;
+ case QK_RGB_MATRIX_FLAG_NEXT:
+ if (shifted) {
+ rgb_matrix_flags_step_reverse();
+ } else {
+ rgb_matrix_flags_step();
+ }
+ return false;
+ case QK_RGB_MATRIX_FLAG_PREVIOUS:
+ if (shifted) {
+ rgb_matrix_flags_step();
+ } else {
+ rgb_matrix_flags_step_reverse();
+ }
+ return false;
}
}
diff --git a/quantum/rgb_matrix/rgb_matrix.c b/quantum/rgb_matrix/rgb_matrix.c
index ab0aa17512..19edfb52b0 100644
--- a/quantum/rgb_matrix/rgb_matrix.c
+++ b/quantum/rgb_matrix/rgb_matrix.c
@@ -72,6 +72,13 @@ uint8_t g_rgb_frame_buffer[MATRIX_ROWS][MATRIX_COLS] = {{0}};
last_hit_t g_last_hit_tracker;
#endif // RGB_MATRIX_KEYREACTIVE_ENABLED
+#ifndef RGB_MATRIX_FLAG_STEPS
+# define RGB_MATRIX_FLAG_STEPS \
+ { LED_FLAG_ALL, LED_FLAG_KEYLIGHT | LED_FLAG_MODIFIER, LED_FLAG_UNDERGLOW, LED_FLAG_NONE }
+#endif
+static const uint8_t rgb_matrix_flag_steps[] = RGB_MATRIX_FLAG_STEPS;
+#define RGB_MATRIX_FLAG_STEPS_COUNT ARRAY_SIZE(rgb_matrix_flag_steps)
+
// internals
static bool suspend_state = false;
static uint8_t rgb_last_enable = UINT8_MAX;
@@ -747,6 +754,50 @@ void rgb_matrix_set_flags_noeeprom(led_flags_t flags) {
rgb_matrix_set_flags_eeprom_helper(flags, false);
}
+void rgb_matrix_flags_step_helper(bool write_to_eeprom) {
+ led_flags_t flags = rgb_matrix_get_flags();
+
+ uint8_t next = 0;
+ for (uint8_t i = 0; i < RGB_MATRIX_FLAG_STEPS_COUNT; i++) {
+ if (rgb_matrix_flag_steps[i] == flags) {
+ next = i == RGB_MATRIX_FLAG_STEPS_COUNT - 1 ? 0 : i + 1;
+ break;
+ }
+ }
+
+ rgb_matrix_set_flags_eeprom_helper(rgb_matrix_flag_steps[next], write_to_eeprom);
+}
+
+void rgb_matrix_flags_step_noeeprom(void) {
+ rgb_matrix_flags_step_helper(false);
+}
+
+void rgb_matrix_flags_step(void) {
+ rgb_matrix_flags_step_helper(true);
+}
+
+void rgb_matrix_flags_step_reverse_helper(bool write_to_eeprom) {
+ led_flags_t flags = rgb_matrix_get_flags();
+
+ uint8_t next = 0;
+ for (uint8_t i = 0; i < RGB_MATRIX_FLAG_STEPS_COUNT; i++) {
+ if (rgb_matrix_flag_steps[i] == flags) {
+ next = i == 0 ? RGB_MATRIX_FLAG_STEPS_COUNT - 1 : i - 1;
+ break;
+ }
+ }
+
+ rgb_matrix_set_flags_eeprom_helper(rgb_matrix_flag_steps[next], write_to_eeprom);
+}
+
+void rgb_matrix_flags_step_reverse_noeeprom(void) {
+ rgb_matrix_flags_step_reverse_helper(false);
+}
+
+void rgb_matrix_flags_step_reverse(void) {
+ rgb_matrix_flags_step_reverse_helper(true);
+}
+
//----------------------------------------------------------
// RGB Matrix naming
#undef RGB_MATRIX_EFFECT
diff --git a/quantum/rgb_matrix/rgb_matrix.h b/quantum/rgb_matrix/rgb_matrix.h
index a91dded4a8..f800679b46 100644
--- a/quantum/rgb_matrix/rgb_matrix.h
+++ b/quantum/rgb_matrix/rgb_matrix.h
@@ -218,6 +218,10 @@ void rgb_matrix_decrease_speed_noeeprom(void);
led_flags_t rgb_matrix_get_flags(void);
void rgb_matrix_set_flags(led_flags_t flags);
void rgb_matrix_set_flags_noeeprom(led_flags_t flags);
+void rgb_matrix_flags_step_noeeprom(void);
+void rgb_matrix_flags_step(void);
+void rgb_matrix_flags_step_reverse_noeeprom(void);
+void rgb_matrix_flags_step_reverse(void);
void rgb_matrix_update_pwm_buffers(void);
#ifdef RGB_MATRIX_MODE_NAME_ENABLE
--
cgit v1.2.3
From 0a4c1caf204bd9750ef8af8296b2e225682b6029 Mon Sep 17 00:00:00 2001
From: Joel Challis
Date: Mon, 29 Sep 2025 20:26:47 +0100
Subject: Add DD {LED,RGB}_MATRIX_DEFAULT_FLAGS support (#25671)
---
data/mappings/info_config.hjson | 2 ++
data/mappings/info_defaults.hjson | 6 ++++--
data/schemas/keyboard.jsonschema | 6 ++++--
docs/reference_info_json.md | 6 ++++++
keyboards/xelus/valor/rev2/keyboard.json | 3 +++
keyboards/xelus/valor/rev2/rev2.c | 5 -----
6 files changed, 19 insertions(+), 9 deletions(-)
(limited to 'data')
diff --git a/data/mappings/info_config.hjson b/data/mappings/info_config.hjson
index c4052c64f6..1c6e86d876 100644
--- a/data/mappings/info_config.hjson
+++ b/data/mappings/info_config.hjson
@@ -112,6 +112,7 @@
"LED_MATRIX_DEFAULT_ON": {"info_key": "led_matrix.default.on", "value_type": "bool"},
"LED_MATRIX_DEFAULT_VAL": {"info_key": "led_matrix.default.val", "value_type": "int"},
"LED_MATRIX_DEFAULT_SPD": {"info_key": "led_matrix.default.speed", "value_type": "int"},
+ "LED_MATRIX_DEFAULT_FLAGS": {"info_key": "led_matrix.default.flags", "value_type": "int"},
// Locking Switch
"LOCKING_SUPPORT_ENABLE": {"info_key": "qmk.locking.enabled", "value_type": "flag"},
@@ -166,6 +167,7 @@
"RGB_MATRIX_DEFAULT_SAT": {"info_key": "rgb_matrix.default.sat", "value_type": "int"},
"RGB_MATRIX_DEFAULT_VAL": {"info_key": "rgb_matrix.default.val", "value_type": "int"},
"RGB_MATRIX_DEFAULT_SPD": {"info_key": "rgb_matrix.default.speed", "value_type": "int"},
+ "RGB_MATRIX_DEFAULT_FLAGS": {"info_key": "rgb_matrix.default.flags", "value_type": "int"},
// RGBLight
"RGBLED_SPLIT": {"info_key": "rgblight.split_count", "value_type": "array.int"},
diff --git a/data/mappings/info_defaults.hjson b/data/mappings/info_defaults.hjson
index b33cb4fa1f..d1f1579c55 100644
--- a/data/mappings/info_defaults.hjson
+++ b/data/mappings/info_defaults.hjson
@@ -23,7 +23,8 @@
"animation": "solid",
"on": true,
"val": 255,
- "speed": 128
+ "speed": 128,
+ "flags": 255
},
"led_flush_limit": 16,
"max_brightness": 255,
@@ -53,7 +54,8 @@
"hue": 0,
"sat": 255,
"val": 255,
- "speed": 128
+ "speed": 128,
+ "flags": 255
},
"hue_steps": 8,
"led_flush_limit": 16,
diff --git a/data/schemas/keyboard.jsonschema b/data/schemas/keyboard.jsonschema
index 93fc4ed8a2..57aeb3de22 100644
--- a/data/schemas/keyboard.jsonschema
+++ b/data/schemas/keyboard.jsonschema
@@ -543,7 +543,8 @@
"on": {"type": "boolean"},
"animation": {"type": "string"},
"val": {"$ref": "./definitions.jsonschema#/unsigned_int_8"},
- "speed": {"$ref": "./definitions.jsonschema#/unsigned_int_8"}
+ "speed": {"$ref": "./definitions.jsonschema#/unsigned_int_8"},
+ "flags": {"$ref": "./definitions.jsonschema#/unsigned_int_8"}
}
},
"driver": {
@@ -631,7 +632,8 @@
"hue": {"$ref": "./definitions.jsonschema#/unsigned_int_8"},
"sat": {"$ref": "./definitions.jsonschema#/unsigned_int_8"},
"val": {"$ref": "./definitions.jsonschema#/unsigned_int_8"},
- "speed": {"$ref": "./definitions.jsonschema#/unsigned_int_8"}
+ "speed": {"$ref": "./definitions.jsonschema#/unsigned_int_8"},
+ "flags": {"$ref": "./definitions.jsonschema#/unsigned_int_8"}
}
},
"driver": {
diff --git a/docs/reference_info_json.md b/docs/reference_info_json.md
index e7cb8c31e8..91ab7f4577 100644
--- a/docs/reference_info_json.md
+++ b/docs/reference_info_json.md
@@ -431,6 +431,9 @@ Configures the [LED Matrix](features/led_matrix) feature.
* `speed` Number
* The default animation speed.
* Default: `128`
+ * `flags` Number
+ * The default LED flags.
+ * Default: `255`
* `driver` String Required
* The driver to use. Must be one of `custom`, `is31fl3218`, `is31fl3731`, `is31fl3733`, `is31fl3736`, `is31fl3737`, `is31fl3741`, `is31fl3742a`, `is31fl3743a`, `is31fl3745`, `is31fl3746a`, `snled27351`.
* `layout` Array: Object Required
@@ -685,6 +688,9 @@ Configures the [RGB Matrix](features/rgb_matrix) feature.
* `speed` Number
* The default animation speed.
* Default: `128`
+ * `flags` Number
+ * The default LED flags.
+ * Default: `255`
* `driver` String Required
* The driver to use. Must be one of `aw20216s`, `custom`, `is31fl3218`, `is31fl3236`, `is31fl3729`, `is31fl3731`, `is31fl3733`, `is31fl3736`, `is31fl3737`, `is31fl3741`, `is31fl3742a`, `is31fl3743a`, `is31fl3745`, `is31fl3746a`, `snled27351`, `ws2812`.
* `hue_steps` Number
diff --git a/keyboards/xelus/valor/rev2/keyboard.json b/keyboards/xelus/valor/rev2/keyboard.json
index 451eeb99ff..6356f69646 100644
--- a/keyboards/xelus/valor/rev2/keyboard.json
+++ b/keyboards/xelus/valor/rev2/keyboard.json
@@ -67,6 +67,9 @@
"solid_splash": true,
"solid_multisplash": true
},
+ "default": {
+ "flags": 7
+ },
"driver": "ws2812",
"max_brightness": 200,
"sleep": true
diff --git a/keyboards/xelus/valor/rev2/rev2.c b/keyboards/xelus/valor/rev2/rev2.c
index 34a32cfc61..99db45e08d 100644
--- a/keyboards/xelus/valor/rev2/rev2.c
+++ b/keyboards/xelus/valor/rev2/rev2.c
@@ -64,9 +64,4 @@ led_config_t g_led_config = { {
8, 8, 8, 8
} };
-
-void keyboard_pre_init_kb(void) {
- rgb_matrix_set_flags(LED_FLAG_MODIFIER|LED_FLAG_UNDERGLOW|LED_FLAG_KEYLIGHT);
- keyboard_pre_init_user();
-}
#endif
--
cgit v1.2.3
From 466e0856f553d5fd1e50d990f97b8bb5de899145 Mon Sep 17 00:00:00 2001
From: Pascal Hansjosten
Date: Mon, 6 Oct 2025 18:26:16 +0200
Subject: add durgod venus iso support (#25526)
---
data/mappings/keyboard_aliases.hjson | 2 +-
keyboards/durgod/dgk6x/readme.md | 3 +-
keyboards/durgod/dgk6x/venus/config.h | 19 ---
keyboards/durgod/dgk6x/venus/keyboard.json | 153 ---------------------
.../durgod/dgk6x/venus/keymaps/default/keymap.json | 38 -----
.../durgod/dgk6x/venus/keymaps/default/readme.md | 11 --
keyboards/durgod/dgk6x/venus/readme.md | 26 ----
keyboards/durgod/dgk6x/venus/venus.c | 95 -------------
keyboards/durgod/dgk6x/venus_ansi/config.h | 19 +++
keyboards/durgod/dgk6x/venus_ansi/keyboard.json | 153 +++++++++++++++++++++
.../dgk6x/venus_ansi/keymaps/default/keymap.json | 38 +++++
.../dgk6x/venus_ansi/keymaps/default/readme.md | 11 ++
keyboards/durgod/dgk6x/venus_ansi/readme.md | 26 ++++
keyboards/durgod/dgk6x/venus_ansi/venus_ansi.c | 95 +++++++++++++
keyboards/durgod/dgk6x/venus_iso/config.h | 19 +++
keyboards/durgod/dgk6x/venus_iso/keyboard.json | 151 ++++++++++++++++++++
.../dgk6x/venus_iso/keymaps/default/keymap.json | 37 +++++
keyboards/durgod/dgk6x/venus_iso/readme.md | 26 ++++
keyboards/durgod/dgk6x/venus_iso/venus_iso.c | 97 +++++++++++++
keyboards/durgod/venus/readme.md | 2 +-
20 files changed, 676 insertions(+), 345 deletions(-)
delete mode 100644 keyboards/durgod/dgk6x/venus/config.h
delete mode 100644 keyboards/durgod/dgk6x/venus/keyboard.json
delete mode 100644 keyboards/durgod/dgk6x/venus/keymaps/default/keymap.json
delete mode 100644 keyboards/durgod/dgk6x/venus/keymaps/default/readme.md
delete mode 100644 keyboards/durgod/dgk6x/venus/readme.md
delete mode 100644 keyboards/durgod/dgk6x/venus/venus.c
create mode 100644 keyboards/durgod/dgk6x/venus_ansi/config.h
create mode 100644 keyboards/durgod/dgk6x/venus_ansi/keyboard.json
create mode 100644 keyboards/durgod/dgk6x/venus_ansi/keymaps/default/keymap.json
create mode 100644 keyboards/durgod/dgk6x/venus_ansi/keymaps/default/readme.md
create mode 100644 keyboards/durgod/dgk6x/venus_ansi/readme.md
create mode 100644 keyboards/durgod/dgk6x/venus_ansi/venus_ansi.c
create mode 100644 keyboards/durgod/dgk6x/venus_iso/config.h
create mode 100644 keyboards/durgod/dgk6x/venus_iso/keyboard.json
create mode 100644 keyboards/durgod/dgk6x/venus_iso/keymaps/default/keymap.json
create mode 100644 keyboards/durgod/dgk6x/venus_iso/readme.md
create mode 100644 keyboards/durgod/dgk6x/venus_iso/venus_iso.c
(limited to 'data')
diff --git a/data/mappings/keyboard_aliases.hjson b/data/mappings/keyboard_aliases.hjson
index c958cecbc9..71a7e880a3 100644
--- a/data/mappings/keyboard_aliases.hjson
+++ b/data/mappings/keyboard_aliases.hjson
@@ -1750,7 +1750,7 @@
"target": "durgod/dgk6x/galaxy"
},
"durgod/venus": {
- "target": "durgod/dgk6x/venus"
+ "target": "durgod/dgk6x/venus_ansi"
},
"dztech/tofu/ii": {
"target": "dztech/tofu/ii/v1"
diff --git a/keyboards/durgod/dgk6x/readme.md b/keyboards/durgod/dgk6x/readme.md
index a14dfcb9f4..58ba599069 100644
--- a/keyboards/durgod/dgk6x/readme.md
+++ b/keyboards/durgod/dgk6x/readme.md
@@ -11,7 +11,8 @@ This DGK6X code is shared between the ANSI variants of Venus, Hades and Galaxy k
### Build
Instructions for building the Venus, Hades and Galaxy firmware can be found here:
-* [Venus](venus/readme.md)
+* [Venus ANSI](venus_ansi/readme.md)
+* [Venus ISO](venus_iso/readme.md)
* [Hades ANSI](hades_ansi/readme.md)
* [Hades ISO](hades_iso/readme.md)
* [Galaxy](galaxy/readme.md)
diff --git a/keyboards/durgod/dgk6x/venus/config.h b/keyboards/durgod/dgk6x/venus/config.h
deleted file mode 100644
index a03084f2e6..0000000000
--- a/keyboards/durgod/dgk6x/venus/config.h
+++ /dev/null
@@ -1,19 +0,0 @@
-/* Copyright 2021 Jessica Sullivan and Don Kjer
- *
- * 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 .
- */
-
-#pragma once
-
-#define CAPS_LED 28
diff --git a/keyboards/durgod/dgk6x/venus/keyboard.json b/keyboards/durgod/dgk6x/venus/keyboard.json
deleted file mode 100644
index a32a5b1065..0000000000
--- a/keyboards/durgod/dgk6x/venus/keyboard.json
+++ /dev/null
@@ -1,153 +0,0 @@
-{
- "keyboard_name": "DURGOD Venus (QMK)",
- "manufacturer": "Hoksi Technology",
- "url": "https://www.amazon.com/dp/B07XFP5MMZ",
- "maintainer": "J-Sully",
- "usb": {
- "vid": "0xD60D",
- "pid": "0x7EC5",
- "device_version": "0.0.1"
- },
- "matrix_pins": {
- "cols": ["B0", "B1", "B2", "B10", "B11", "B12", "B13", "B14", "B15", "C6", "C7", "C8", "C9", "C10"],
- "rows": ["A0", "A1", "A2", "A3", "A4"]
- },
- "diode_direction": "ROW2COL",
- "rgb_matrix": {
- "layout": [
- {"matrix": [0, 0], "x": 0, "y": 0, "flags": 1},
- {"matrix": [0, 1], "x": 16, "y": 0, "flags": 4},
- {"matrix": [0, 2], "x": 32, "y": 0, "flags": 4},
- {"matrix": [0, 3], "x": 48, "y": 0, "flags": 4},
- {"matrix": [0, 4], "x": 64, "y": 0, "flags": 4},
- {"matrix": [0, 5], "x": 80, "y": 0, "flags": 4},
- {"matrix": [0, 6], "x": 96, "y": 0, "flags": 4},
- {"matrix": [0, 7], "x": 112, "y": 0, "flags": 4},
- {"matrix": [0, 8], "x": 128, "y": 0, "flags": 4},
- {"matrix": [0, 9], "x": 144, "y": 0, "flags": 4},
- {"matrix": [0, 10], "x": 160, "y": 0, "flags": 4},
- {"matrix": [0, 11], "x": 176, "y": 0, "flags": 4},
- {"matrix": [0, 12], "x": 192, "y": 0, "flags": 4},
- {"matrix": [0, 13], "x": 216, "y": 0, "flags": 1},
- {"matrix": [1, 0], "x": 4, "y": 16, "flags": 4},
- {"matrix": [1, 1], "x": 24, "y": 16, "flags": 4},
- {"matrix": [1, 2], "x": 40, "y": 16, "flags": 4},
- {"matrix": [1, 3], "x": 56, "y": 16, "flags": 4},
- {"matrix": [1, 4], "x": 72, "y": 16, "flags": 4},
- {"matrix": [1, 5], "x": 88, "y": 16, "flags": 4},
- {"matrix": [1, 6], "x": 104, "y": 16, "flags": 4},
- {"matrix": [1, 7], "x": 120, "y": 16, "flags": 4},
- {"matrix": [1, 8], "x": 136, "y": 16, "flags": 4},
- {"matrix": [1, 9], "x": 152, "y": 16, "flags": 4},
- {"matrix": [1, 10], "x": 168, "y": 16, "flags": 4},
- {"matrix": [1, 11], "x": 184, "y": 16, "flags": 4},
- {"matrix": [1, 12], "x": 200, "y": 16, "flags": 4},
- {"matrix": [1, 13], "x": 220, "y": 16, "flags": 4},
- {"matrix": [2, 0], "x": 6, "y": 32, "flags": 4},
- {"matrix": [2, 1], "x": 28, "y": 32, "flags": 4},
- {"matrix": [2, 2], "x": 44, "y": 32, "flags": 4},
- {"matrix": [2, 3], "x": 60, "y": 32, "flags": 4},
- {"matrix": [2, 4], "x": 76, "y": 32, "flags": 4},
- {"matrix": [2, 5], "x": 92, "y": 32, "flags": 4},
- {"matrix": [2, 6], "x": 108, "y": 32, "flags": 4},
- {"matrix": [2, 7], "x": 124, "y": 32, "flags": 4},
- {"matrix": [2, 8], "x": 140, "y": 32, "flags": 4},
- {"matrix": [2, 9], "x": 156, "y": 32, "flags": 4},
- {"matrix": [2, 10], "x": 172, "y": 32, "flags": 4},
- {"matrix": [2, 11], "x": 188, "y": 32, "flags": 4},
- {"matrix": [2, 13], "x": 214, "y": 32, "flags": 1},
- {"matrix": [3, 0], "x": 10, "y": 48, "flags": 1},
- {"matrix": [3, 1], "x": 36, "y": 48, "flags": 4},
- {"matrix": [3, 2], "x": 52, "y": 48, "flags": 4},
- {"matrix": [3, 3], "x": 68, "y": 48, "flags": 4},
- {"matrix": [3, 4], "x": 84, "y": 48, "flags": 4},
- {"matrix": [3, 5], "x": 100, "y": 48, "flags": 4},
- {"matrix": [3, 6], "x": 116, "y": 48, "flags": 4},
- {"matrix": [3, 7], "x": 132, "y": 48, "flags": 4},
- {"matrix": [3, 8], "x": 148, "y": 48, "flags": 4},
- {"matrix": [3, 9], "x": 164, "y": 48, "flags": 4},
- {"matrix": [3, 10], "x": 180, "y": 48, "flags": 4},
- {"matrix": [3, 11], "x": 210, "y": 48, "flags": 1},
- {"matrix": [4, 0], "x": 2, "y": 64, "flags": 1},
- {"matrix": [4, 1], "x": 22, "y": 64, "flags": 1},
- {"matrix": [4, 2], "x": 42, "y": 64, "flags": 1},
- {"matrix": [4, 5], "x": 102, "y": 64, "flags": 4},
- {"matrix": [4, 9], "x": 162, "y": 64, "flags": 1},
- {"matrix": [4, 10], "x": 182, "y": 64, "flags": 1},
- {"matrix": [4, 11], "x": 202, "y": 64, "flags": 1},
- {"matrix": [4, 12], "x": 222, "y": 64, "flags": 1}
- ]
- },
- "community_layouts": ["60_ansi"],
- "layouts": {
- "LAYOUT_60_ansi": {
- "layout": [
- {"matrix": [0, 0], "x": 0, "y": 0},
- {"matrix": [0, 1], "x": 1, "y": 0},
- {"matrix": [0, 2], "x": 2, "y": 0},
- {"matrix": [0, 3], "x": 3, "y": 0},
- {"matrix": [0, 4], "x": 4, "y": 0},
- {"matrix": [0, 5], "x": 5, "y": 0},
- {"matrix": [0, 6], "x": 6, "y": 0},
- {"matrix": [0, 7], "x": 7, "y": 0},
- {"matrix": [0, 8], "x": 8, "y": 0},
- {"matrix": [0, 9], "x": 9, "y": 0},
- {"matrix": [0, 10], "x": 10, "y": 0},
- {"matrix": [0, 11], "x": 11, "y": 0},
- {"matrix": [0, 12], "x": 12, "y": 0},
- {"matrix": [0, 13], "x": 13, "y": 0, "w": 2},
-
- {"matrix": [1, 0], "x": 0, "y": 1, "w": 1.5},
- {"matrix": [1, 1], "x": 1.5, "y": 1},
- {"matrix": [1, 2], "x": 2.5, "y": 1},
- {"matrix": [1, 3], "x": 3.5, "y": 1},
- {"matrix": [1, 4], "x": 4.5, "y": 1},
- {"matrix": [1, 5], "x": 5.5, "y": 1},
- {"matrix": [1, 6], "x": 6.5, "y": 1},
- {"matrix": [1, 7], "x": 7.5, "y": 1},
- {"matrix": [1, 8], "x": 8.5, "y": 1},
- {"matrix": [1, 9], "x": 9.5, "y": 1},
- {"matrix": [1, 10], "x": 10.5, "y": 1},
- {"matrix": [1, 11], "x": 11.5, "y": 1},
- {"matrix": [1, 12], "x": 12.5, "y": 1},
- {"matrix": [1, 13], "x": 13.5, "y": 1, "w": 1.5},
-
- {"matrix": [2, 0], "x": 0, "y": 2, "w": 1.75},
- {"matrix": [2, 1], "x": 1.75, "y": 2},
- {"matrix": [2, 2], "x": 2.75, "y": 2},
- {"matrix": [2, 3], "x": 3.75, "y": 2},
- {"matrix": [2, 4], "x": 4.75, "y": 2},
- {"matrix": [2, 5], "x": 5.75, "y": 2},
- {"matrix": [2, 6], "x": 6.75, "y": 2},
- {"matrix": [2, 7], "x": 7.75, "y": 2},
- {"matrix": [2, 8], "x": 8.75, "y": 2},
- {"matrix": [2, 9], "x": 9.75, "y": 2},
- {"matrix": [2, 10], "x": 10.75, "y": 2},
- {"matrix": [2, 11], "x": 11.75, "y": 2},
- {"matrix": [2, 13], "x": 12.75, "y": 2, "w": 2.25},
-
- {"matrix": [3, 0], "x": 0, "y": 3, "w": 2.25},
- {"matrix": [3, 1], "x": 2.25, "y": 3},
- {"matrix": [3, 2], "x": 3.25, "y": 3},
- {"matrix": [3, 3], "x": 4.25, "y": 3},
- {"matrix": [3, 4], "x": 5.25, "y": 3},
- {"matrix": [3, 5], "x": 6.25, "y": 3},
- {"matrix": [3, 6], "x": 7.25, "y": 3},
- {"matrix": [3, 7], "x": 8.25, "y": 3},
- {"matrix": [3, 8], "x": 9.25, "y": 3},
- {"matrix": [3, 9], "x": 10.25, "y": 3},
- {"matrix": [3, 10], "x": 11.25, "y": 3},
- {"matrix": [3, 11], "x": 12.25, "y": 3, "w": 2.75},
-
- {"matrix": [4, 0], "x": 0, "y": 4, "w": 1.25},
- {"matrix": [4, 1], "x": 1.25, "y": 4, "w": 1.25},
- {"matrix": [4, 2], "x": 2.5, "y": 4, "w": 1.25},
- {"matrix": [4, 5], "x": 3.75, "y": 4, "w": 6.25},
- {"matrix": [4, 9], "x": 10, "y": 4, "w": 1.25},
- {"matrix": [4, 10], "x": 11.25, "y": 4, "w": 1.25},
- {"matrix": [4, 11], "x": 12.5, "y": 4, "w": 1.25},
- {"matrix": [4, 12], "x": 13.75, "y": 4, "w": 1.25}
- ]
- }
- }
-}
diff --git a/keyboards/durgod/dgk6x/venus/keymaps/default/keymap.json b/keyboards/durgod/dgk6x/venus/keymaps/default/keymap.json
deleted file mode 100644
index 8acd302a05..0000000000
--- a/keyboards/durgod/dgk6x/venus/keymaps/default/keymap.json
+++ /dev/null
@@ -1,38 +0,0 @@
-{
- "keyboard": "durgod/dgk6x/venus",
- "keymap": "default",
- "layout": "LAYOUT_60_ansi",
- "layers": [
- [
- "QK_GESC", "KC_1", "KC_2", "KC_3", "KC_4", "KC_5", "KC_6", "KC_7", "KC_8", "KC_9", "KC_0", "KC_MINS", "KC_EQL", "KC_BSPC",
- "KC_TAB", "KC_Q", "KC_W", "KC_E", "KC_R", "KC_T", "KC_Y", "KC_U", "KC_I", "KC_O", "KC_P", "KC_LBRC", "KC_RBRC", "KC_BSLS",
- "KC_CAPS", "KC_A", "KC_S", "KC_D", "KC_F", "KC_G", "KC_H", "KC_J", "KC_K", "KC_L", "KC_SCLN", "KC_QUOT", "KC_ENT",
- "KC_LSFT", "KC_Z", "KC_X", "KC_C", "KC_V", "KC_B", "KC_N", "KC_M", "KC_COMM", "KC_DOT", "KC_SLSH", "RSFT_T(KC_UP)",
- "KC_LCTL", "KC_LGUI", "KC_LALT", "KC_SPC", "KC_RALT","LT(1,KC_LEFT)","LT(2,KC_DOWN)","RCTL_T(KC_RIGHT)"
- ],
- [
- "KC_GRV", "KC_F1", "KC_F2", "KC_F3", "KC_F4", "KC_F5", "KC_F6", "KC_F7", "KC_F8", "KC_F9", "KC_F10", "KC_F11", "KC_F12", "KC_TRNS",
- "KC_TRNS", "KC_TRNS", "KC_UP", "KC_TRNS", "KC_TRNS", "KC_TRNS", "KC_TRNS", "KC_TRNS", "KC_TRNS", "KC_PSCR", "KC_INS", "KC_HOME", "KC_END", "KC_DEL",
- "KC_TRNS", "KC_LEFT", "KC_DOWN", "KC_RIGHT","KC_TRNS", "KC_TRNS", "KC_TRNS", "KC_TRNS", "KC_TRNS", "KC_TRNS", "KC_PGUP", "KC_PGDN", "KC_TRNS",
- "KC_TRNS", "KC_TRNS", "KC_TRNS", "KC_TRNS", "KC_TRNS", "KC_TRNS", "KC_TRNS", "KC_TRNS", "KC_TRNS", "KC_TRNS", "KC_TRNS", "KC_TRNS",
- "KC_TRNS", "GU_TOGG", "KC_TRNS", "KC_TRNS", "KC_TRNS", "KC_TRNS", "MO(3)", "KC_TRNS"
- ],
- [
- "KC_TRNS", "KC_MPLY", "KC_MSTP", "KC_MPRV", "KC_MNXT", "KC_MUTE", "KC_VOLD", "KC_VOLU", "KC_TRNS", "KC_TRNS", "KC_TRNS", "RM_TOGG", "RM_NEXT", "RM_PREV",
- "KC_TRNS", "KC_TRNS", "RM_VALU", "KC_TRNS", "KC_TRNS", "KC_TRNS", "KC_TRNS", "KC_TRNS", "KC_TRNS", "KC_TRNS", "KC_TRNS", "KC_TRNS", "KC_TRNS", "KC_TRNS",
- "KC_TRNS", "RM_SPDD", "RM_VALD", "RM_SPDU", "KC_TRNS", "KC_TRNS", "KC_TRNS", "KC_TRNS", "KC_TRNS", "KC_TRNS", "KC_TRNS", "KC_TRNS", "KC_TRNS",
- "KC_TRNS", "KC_TRNS", "KC_TRNS", "KC_TRNS", "KC_TRNS", "KC_TRNS", "KC_TRNS", "KC_TRNS", "KC_TRNS", "KC_TRNS", "KC_TRNS", "KC_TRNS",
- "KC_TRNS", "KC_TRNS", "KC_TRNS", "KC_TRNS", "KC_TRNS", "MO(3)", "KC_TRNS", "KC_TRNS"
- ],
- [
- "XXXXXXX", "XXXXXXX", "XXXXXXX", "XXXXXXX", "XXXXXXX", "XXXXXXX", "XXXXXXX", "XXXXXXX", "XXXXXXX", "XXXXXXX", "XXXXXXX", "XXXXXXX", "XXXXXXX", "XXXXXXX",
- "XXXXXXX", "XXXXXXX", "RM_SATU", "XXXXXXX", "XXXXXXX", "XXXXXXX", "XXXXXXX", "XXXXXXX", "XXXXXXX", "XXXXXXX", "XXXXXXX", "XXXXXXX", "XXXXXXX", "XXXXXXX",
- "XXXXXXX", "RM_HUED", "RM_SATD", "RM_HUEU", "XXXXXXX", "XXXXXXX", "XXXXXXX", "XXXXXXX", "XXXXXXX", "XXXXXXX", "XXXXXXX", "XXXXXXX", "XXXXXXX",
- "XXXXXXX", "XXXXXXX", "XXXXXXX", "XXXXXXX", "XXXXXXX", "XXXXXXX", "XXXXXXX", "XXXXXXX", "XXXXXXX", "XXXXXXX", "XXXXXXX", "XXXXXXX",
- "XXXXXXX", "XXXXXXX", "XXXXXXX", "XXXXXXX", "XXXXXXX", "KC_TRNS", "KC_TRNS", "XXXXXXX"
- ]
- ],
- "author":"dkjer",
- "notes":"",
- "version":1
-}
diff --git a/keyboards/durgod/dgk6x/venus/keymaps/default/readme.md b/keyboards/durgod/dgk6x/venus/keymaps/default/readme.md
deleted file mode 100644
index da166eb60a..0000000000
--- a/keyboards/durgod/dgk6x/venus/keymaps/default/readme.md
+++ /dev/null
@@ -1,11 +0,0 @@
-
-
-# The default keymap for Durgod Venus.
-
-Layer 0 : Standard ANSI 60 Keys layout
-
-Layer 1 : F1-12 and additional navigation keys.
-
-Layer 2 : Media and RGB matrix control
-
-Layer 3 : RGB matrix hue and saturation
diff --git a/keyboards/durgod/dgk6x/venus/readme.md b/keyboards/durgod/dgk6x/venus/readme.md
deleted file mode 100644
index 9e37d49633..0000000000
--- a/keyboards/durgod/dgk6x/venus/readme.md
+++ /dev/null
@@ -1,26 +0,0 @@
-# Venus
-
-This is a standard off-the-shelf Durgod Venus 60% ANSI Layout keyboard with RGB matrix.
-
-* Keyboard Maintainer: [J-Sully](https://github.com/J-Sully) and [dkjer](https://github.com/dkjer)
-* Hardware Supported: Durgod Venus board with STM32F070RBT6
-* Hardware Availability: https://www.amazon.com/dp/B07XFP5MMZ
-
-## Instructions
-
-### Build
-
-Make command example for this keyboard (after setting up your build environment):
-
- make durgod/venus:default
-
-Flashing example for this keyboard:
-
- make durgod/venus:default:flash
-
-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).
-
-### Initial Flash
-
-Instructions for Flashing from initial Stock Firmware:
-* [Initial Flashing](../readme.md#initial-flash)
diff --git a/keyboards/durgod/dgk6x/venus/venus.c b/keyboards/durgod/dgk6x/venus/venus.c
deleted file mode 100644
index 30735fc80e..0000000000
--- a/keyboards/durgod/dgk6x/venus/venus.c
+++ /dev/null
@@ -1,95 +0,0 @@
-/* Copyright 2021 Jessica Sullivan and Don Kjer
- *
- * 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 .
- */
-
-#include "dgk6x.h"
-
-#ifdef RGB_MATRIX_ENABLE
-
-const is31fl3733_led_t PROGMEM g_is31fl3733_leds[IS31FL3733_LED_COUNT] = {
-/* Refer to IS31 manual for these locations
- * driver
- * | R location
- * | | G location
- * | | | B location
- * | | | | */
-
- {0, SW3_CS1, SW2_CS1, SW1_CS1}, // Esc
- {0, SW3_CS2, SW2_CS2, SW1_CS2}, // 1
- {0, SW3_CS3, SW2_CS3, SW1_CS3}, // 2
- {0, SW3_CS4, SW2_CS4, SW1_CS4}, // 3
- {0, SW3_CS5, SW2_CS5, SW1_CS5}, // 4
- {0, SW3_CS6, SW2_CS6, SW1_CS6}, // 5
- {0, SW3_CS7, SW2_CS7, SW1_CS7}, // 6
- {0, SW3_CS8, SW2_CS8, SW1_CS8}, // 7
- {0, SW3_CS9, SW2_CS9, SW1_CS9}, // 8
- {0, SW3_CS10, SW2_CS10, SW1_CS10}, // 9
- {0, SW3_CS11, SW2_CS11, SW1_CS11}, // 0
- {0, SW3_CS12, SW2_CS12, SW1_CS12}, // -
- {0, SW3_CS13, SW2_CS13, SW1_CS13}, // =
- {0, SW3_CS14, SW2_CS14, SW1_CS14}, // Bksp
-
- {0, SW6_CS1, SW5_CS1, SW4_CS1}, // Tab
- {0, SW6_CS2, SW5_CS2, SW4_CS2}, // Q
- {0, SW6_CS3, SW5_CS3, SW4_CS3}, // W
- {0, SW6_CS4, SW5_CS4, SW4_CS4}, // E
- {0, SW6_CS5, SW5_CS5, SW4_CS5}, // R
- {0, SW6_CS6, SW5_CS6, SW4_CS6}, // T
- {0, SW6_CS7, SW5_CS7, SW4_CS7}, // Y
- {0, SW6_CS8, SW5_CS8, SW4_CS8}, // U
- {0, SW6_CS9, SW5_CS9, SW4_CS9}, // I
- {0, SW6_CS10, SW5_CS10, SW4_CS10}, // O
- {0, SW6_CS11, SW5_CS11, SW4_CS11}, // P
- {0, SW6_CS12, SW5_CS12, SW4_CS12}, // [
- {0, SW6_CS13, SW5_CS13, SW4_CS13}, // ]
- {0, SW6_CS14, SW5_CS14, SW4_CS14}, // Pipe
-
- {0, SW9_CS1, SW8_CS1, SW7_CS1}, // Caps
- {0, SW9_CS2, SW8_CS2, SW7_CS2}, // A
- {0, SW9_CS3, SW8_CS3, SW7_CS3}, // S
- {0, SW9_CS4, SW8_CS4, SW7_CS4}, // D
- {0, SW9_CS5, SW8_CS5, SW7_CS5}, // F
- {0, SW9_CS6, SW8_CS6, SW7_CS6}, // G
- {0, SW9_CS7, SW8_CS7, SW7_CS7}, // H
- {0, SW9_CS8, SW8_CS8, SW7_CS8}, // J
- {0, SW9_CS9, SW8_CS9, SW7_CS9}, // K
- {0, SW9_CS10, SW8_CS10, SW7_CS10}, // L
- {0, SW9_CS11, SW8_CS11, SW7_CS11}, // :
- {0, SW9_CS12, SW8_CS12, SW7_CS12}, // '
- {0, SW9_CS14, SW8_CS14, SW7_CS14}, // Enter
-
- {0, SW12_CS1, SW11_CS1, SW10_CS1}, // LShift
- {0, SW12_CS2, SW11_CS2, SW10_CS2}, // Z
- {0, SW12_CS3, SW11_CS3, SW10_CS3}, // X
- {0, SW12_CS4, SW11_CS4, SW10_CS4}, // C
- {0, SW12_CS5, SW11_CS5, SW10_CS5}, // V
- {0, SW12_CS6, SW11_CS6, SW10_CS6}, // B
- {0, SW12_CS7, SW11_CS7, SW10_CS7}, // N
- {0, SW12_CS8, SW11_CS8, SW10_CS8}, // M
- {0, SW12_CS9, SW11_CS9, SW10_CS9}, // <
- {0, SW12_CS10, SW11_CS10, SW10_CS10}, // >
- {0, SW12_CS11, SW11_CS11, SW10_CS11}, // ?
- {0, SW12_CS12, SW11_CS12, SW10_CS12}, // RShift
-
- {1, SW3_CS1, SW2_CS1, SW1_CS1}, // LCtrl
- {1, SW3_CS2, SW2_CS2, SW1_CS2}, // LAlt
- {1, SW3_CS3, SW2_CS3, SW1_CS3}, // Windows
- {1, SW3_CS6, SW2_CS6, SW1_CS6}, // Space
- {1, SW3_CS10, SW2_CS10, SW1_CS10}, // Fn1/RAlt hades/venus
- {1, SW3_CS11, SW2_CS11, SW1_CS11}, // Fn2/Fn1
- {1, SW3_CS12, SW2_CS12, SW1_CS12}, // RCtrl/Fn2
- {1, SW3_CS13, SW2_CS13, SW1_CS13}, // LEFT/RCtrl
-};
-#endif /* RGB_MATRIX_ENABLE */
diff --git a/keyboards/durgod/dgk6x/venus_ansi/config.h b/keyboards/durgod/dgk6x/venus_ansi/config.h
new file mode 100644
index 0000000000..a03084f2e6
--- /dev/null
+++ b/keyboards/durgod/dgk6x/venus_ansi/config.h
@@ -0,0 +1,19 @@
+/* Copyright 2021 Jessica Sullivan and Don Kjer
+ *
+ * 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 .
+ */
+
+#pragma once
+
+#define CAPS_LED 28
diff --git a/keyboards/durgod/dgk6x/venus_ansi/keyboard.json b/keyboards/durgod/dgk6x/venus_ansi/keyboard.json
new file mode 100644
index 0000000000..a32a5b1065
--- /dev/null
+++ b/keyboards/durgod/dgk6x/venus_ansi/keyboard.json
@@ -0,0 +1,153 @@
+{
+ "keyboard_name": "DURGOD Venus (QMK)",
+ "manufacturer": "Hoksi Technology",
+ "url": "https://www.amazon.com/dp/B07XFP5MMZ",
+ "maintainer": "J-Sully",
+ "usb": {
+ "vid": "0xD60D",
+ "pid": "0x7EC5",
+ "device_version": "0.0.1"
+ },
+ "matrix_pins": {
+ "cols": ["B0", "B1", "B2", "B10", "B11", "B12", "B13", "B14", "B15", "C6", "C7", "C8", "C9", "C10"],
+ "rows": ["A0", "A1", "A2", "A3", "A4"]
+ },
+ "diode_direction": "ROW2COL",
+ "rgb_matrix": {
+ "layout": [
+ {"matrix": [0, 0], "x": 0, "y": 0, "flags": 1},
+ {"matrix": [0, 1], "x": 16, "y": 0, "flags": 4},
+ {"matrix": [0, 2], "x": 32, "y": 0, "flags": 4},
+ {"matrix": [0, 3], "x": 48, "y": 0, "flags": 4},
+ {"matrix": [0, 4], "x": 64, "y": 0, "flags": 4},
+ {"matrix": [0, 5], "x": 80, "y": 0, "flags": 4},
+ {"matrix": [0, 6], "x": 96, "y": 0, "flags": 4},
+ {"matrix": [0, 7], "x": 112, "y": 0, "flags": 4},
+ {"matrix": [0, 8], "x": 128, "y": 0, "flags": 4},
+ {"matrix": [0, 9], "x": 144, "y": 0, "flags": 4},
+ {"matrix": [0, 10], "x": 160, "y": 0, "flags": 4},
+ {"matrix": [0, 11], "x": 176, "y": 0, "flags": 4},
+ {"matrix": [0, 12], "x": 192, "y": 0, "flags": 4},
+ {"matrix": [0, 13], "x": 216, "y": 0, "flags": 1},
+ {"matrix": [1, 0], "x": 4, "y": 16, "flags": 4},
+ {"matrix": [1, 1], "x": 24, "y": 16, "flags": 4},
+ {"matrix": [1, 2], "x": 40, "y": 16, "flags": 4},
+ {"matrix": [1, 3], "x": 56, "y": 16, "flags": 4},
+ {"matrix": [1, 4], "x": 72, "y": 16, "flags": 4},
+ {"matrix": [1, 5], "x": 88, "y": 16, "flags": 4},
+ {"matrix": [1, 6], "x": 104, "y": 16, "flags": 4},
+ {"matrix": [1, 7], "x": 120, "y": 16, "flags": 4},
+ {"matrix": [1, 8], "x": 136, "y": 16, "flags": 4},
+ {"matrix": [1, 9], "x": 152, "y": 16, "flags": 4},
+ {"matrix": [1, 10], "x": 168, "y": 16, "flags": 4},
+ {"matrix": [1, 11], "x": 184, "y": 16, "flags": 4},
+ {"matrix": [1, 12], "x": 200, "y": 16, "flags": 4},
+ {"matrix": [1, 13], "x": 220, "y": 16, "flags": 4},
+ {"matrix": [2, 0], "x": 6, "y": 32, "flags": 4},
+ {"matrix": [2, 1], "x": 28, "y": 32, "flags": 4},
+ {"matrix": [2, 2], "x": 44, "y": 32, "flags": 4},
+ {"matrix": [2, 3], "x": 60, "y": 32, "flags": 4},
+ {"matrix": [2, 4], "x": 76, "y": 32, "flags": 4},
+ {"matrix": [2, 5], "x": 92, "y": 32, "flags": 4},
+ {"matrix": [2, 6], "x": 108, "y": 32, "flags": 4},
+ {"matrix": [2, 7], "x": 124, "y": 32, "flags": 4},
+ {"matrix": [2, 8], "x": 140, "y": 32, "flags": 4},
+ {"matrix": [2, 9], "x": 156, "y": 32, "flags": 4},
+ {"matrix": [2, 10], "x": 172, "y": 32, "flags": 4},
+ {"matrix": [2, 11], "x": 188, "y": 32, "flags": 4},
+ {"matrix": [2, 13], "x": 214, "y": 32, "flags": 1},
+ {"matrix": [3, 0], "x": 10, "y": 48, "flags": 1},
+ {"matrix": [3, 1], "x": 36, "y": 48, "flags": 4},
+ {"matrix": [3, 2], "x": 52, "y": 48, "flags": 4},
+ {"matrix": [3, 3], "x": 68, "y": 48, "flags": 4},
+ {"matrix": [3, 4], "x": 84, "y": 48, "flags": 4},
+ {"matrix": [3, 5], "x": 100, "y": 48, "flags": 4},
+ {"matrix": [3, 6], "x": 116, "y": 48, "flags": 4},
+ {"matrix": [3, 7], "x": 132, "y": 48, "flags": 4},
+ {"matrix": [3, 8], "x": 148, "y": 48, "flags": 4},
+ {"matrix": [3, 9], "x": 164, "y": 48, "flags": 4},
+ {"matrix": [3, 10], "x": 180, "y": 48, "flags": 4},
+ {"matrix": [3, 11], "x": 210, "y": 48, "flags": 1},
+ {"matrix": [4, 0], "x": 2, "y": 64, "flags": 1},
+ {"matrix": [4, 1], "x": 22, "y": 64, "flags": 1},
+ {"matrix": [4, 2], "x": 42, "y": 64, "flags": 1},
+ {"matrix": [4, 5], "x": 102, "y": 64, "flags": 4},
+ {"matrix": [4, 9], "x": 162, "y": 64, "flags": 1},
+ {"matrix": [4, 10], "x": 182, "y": 64, "flags": 1},
+ {"matrix": [4, 11], "x": 202, "y": 64, "flags": 1},
+ {"matrix": [4, 12], "x": 222, "y": 64, "flags": 1}
+ ]
+ },
+ "community_layouts": ["60_ansi"],
+ "layouts": {
+ "LAYOUT_60_ansi": {
+ "layout": [
+ {"matrix": [0, 0], "x": 0, "y": 0},
+ {"matrix": [0, 1], "x": 1, "y": 0},
+ {"matrix": [0, 2], "x": 2, "y": 0},
+ {"matrix": [0, 3], "x": 3, "y": 0},
+ {"matrix": [0, 4], "x": 4, "y": 0},
+ {"matrix": [0, 5], "x": 5, "y": 0},
+ {"matrix": [0, 6], "x": 6, "y": 0},
+ {"matrix": [0, 7], "x": 7, "y": 0},
+ {"matrix": [0, 8], "x": 8, "y": 0},
+ {"matrix": [0, 9], "x": 9, "y": 0},
+ {"matrix": [0, 10], "x": 10, "y": 0},
+ {"matrix": [0, 11], "x": 11, "y": 0},
+ {"matrix": [0, 12], "x": 12, "y": 0},
+ {"matrix": [0, 13], "x": 13, "y": 0, "w": 2},
+
+ {"matrix": [1, 0], "x": 0, "y": 1, "w": 1.5},
+ {"matrix": [1, 1], "x": 1.5, "y": 1},
+ {"matrix": [1, 2], "x": 2.5, "y": 1},
+ {"matrix": [1, 3], "x": 3.5, "y": 1},
+ {"matrix": [1, 4], "x": 4.5, "y": 1},
+ {"matrix": [1, 5], "x": 5.5, "y": 1},
+ {"matrix": [1, 6], "x": 6.5, "y": 1},
+ {"matrix": [1, 7], "x": 7.5, "y": 1},
+ {"matrix": [1, 8], "x": 8.5, "y": 1},
+ {"matrix": [1, 9], "x": 9.5, "y": 1},
+ {"matrix": [1, 10], "x": 10.5, "y": 1},
+ {"matrix": [1, 11], "x": 11.5, "y": 1},
+ {"matrix": [1, 12], "x": 12.5, "y": 1},
+ {"matrix": [1, 13], "x": 13.5, "y": 1, "w": 1.5},
+
+ {"matrix": [2, 0], "x": 0, "y": 2, "w": 1.75},
+ {"matrix": [2, 1], "x": 1.75, "y": 2},
+ {"matrix": [2, 2], "x": 2.75, "y": 2},
+ {"matrix": [2, 3], "x": 3.75, "y": 2},
+ {"matrix": [2, 4], "x": 4.75, "y": 2},
+ {"matrix": [2, 5], "x": 5.75, "y": 2},
+ {"matrix": [2, 6], "x": 6.75, "y": 2},
+ {"matrix": [2, 7], "x": 7.75, "y": 2},
+ {"matrix": [2, 8], "x": 8.75, "y": 2},
+ {"matrix": [2, 9], "x": 9.75, "y": 2},
+ {"matrix": [2, 10], "x": 10.75, "y": 2},
+ {"matrix": [2, 11], "x": 11.75, "y": 2},
+ {"matrix": [2, 13], "x": 12.75, "y": 2, "w": 2.25},
+
+ {"matrix": [3, 0], "x": 0, "y": 3, "w": 2.25},
+ {"matrix": [3, 1], "x": 2.25, "y": 3},
+ {"matrix": [3, 2], "x": 3.25, "y": 3},
+ {"matrix": [3, 3], "x": 4.25, "y": 3},
+ {"matrix": [3, 4], "x": 5.25, "y": 3},
+ {"matrix": [3, 5], "x": 6.25, "y": 3},
+ {"matrix": [3, 6], "x": 7.25, "y": 3},
+ {"matrix": [3, 7], "x": 8.25, "y": 3},
+ {"matrix": [3, 8], "x": 9.25, "y": 3},
+ {"matrix": [3, 9], "x": 10.25, "y": 3},
+ {"matrix": [3, 10], "x": 11.25, "y": 3},
+ {"matrix": [3, 11], "x": 12.25, "y": 3, "w": 2.75},
+
+ {"matrix": [4, 0], "x": 0, "y": 4, "w": 1.25},
+ {"matrix": [4, 1], "x": 1.25, "y": 4, "w": 1.25},
+ {"matrix": [4, 2], "x": 2.5, "y": 4, "w": 1.25},
+ {"matrix": [4, 5], "x": 3.75, "y": 4, "w": 6.25},
+ {"matrix": [4, 9], "x": 10, "y": 4, "w": 1.25},
+ {"matrix": [4, 10], "x": 11.25, "y": 4, "w": 1.25},
+ {"matrix": [4, 11], "x": 12.5, "y": 4, "w": 1.25},
+ {"matrix": [4, 12], "x": 13.75, "y": 4, "w": 1.25}
+ ]
+ }
+ }
+}
diff --git a/keyboards/durgod/dgk6x/venus_ansi/keymaps/default/keymap.json b/keyboards/durgod/dgk6x/venus_ansi/keymaps/default/keymap.json
new file mode 100644
index 0000000000..be4d435839
--- /dev/null
+++ b/keyboards/durgod/dgk6x/venus_ansi/keymaps/default/keymap.json
@@ -0,0 +1,38 @@
+{
+ "keyboard": "durgod/dgk6x/venus_ansi",
+ "keymap": "default",
+ "layout": "LAYOUT_60_ansi",
+ "layers": [
+ [
+ "QK_GESC", "KC_1", "KC_2", "KC_3", "KC_4", "KC_5", "KC_6", "KC_7", "KC_8", "KC_9", "KC_0", "KC_MINS", "KC_EQL", "KC_BSPC",
+ "KC_TAB", "KC_Q", "KC_W", "KC_E", "KC_R", "KC_T", "KC_Y", "KC_U", "KC_I", "KC_O", "KC_P", "KC_LBRC", "KC_RBRC", "KC_BSLS",
+ "KC_CAPS", "KC_A", "KC_S", "KC_D", "KC_F", "KC_G", "KC_H", "KC_J", "KC_K", "KC_L", "KC_SCLN", "KC_QUOT", "KC_ENT",
+ "KC_LSFT", "KC_Z", "KC_X", "KC_C", "KC_V", "KC_B", "KC_N", "KC_M", "KC_COMM", "KC_DOT", "KC_SLSH", "RSFT_T(KC_UP)",
+ "KC_LCTL", "KC_LGUI", "KC_LALT", "KC_SPC", "KC_RALT","LT(1,KC_LEFT)","LT(2,KC_DOWN)","RCTL_T(KC_RIGHT)"
+ ],
+ [
+ "KC_GRV", "KC_F1", "KC_F2", "KC_F3", "KC_F4", "KC_F5", "KC_F6", "KC_F7", "KC_F8", "KC_F9", "KC_F10", "KC_F11", "KC_F12", "KC_TRNS",
+ "KC_TRNS", "KC_TRNS", "KC_UP", "KC_TRNS", "KC_TRNS", "KC_TRNS", "KC_TRNS", "KC_TRNS", "KC_TRNS", "KC_PSCR", "KC_INS", "KC_HOME", "KC_END", "KC_DEL",
+ "KC_TRNS", "KC_LEFT", "KC_DOWN", "KC_RIGHT","KC_TRNS", "KC_TRNS", "KC_TRNS", "KC_TRNS", "KC_TRNS", "KC_TRNS", "KC_PGUP", "KC_PGDN", "KC_TRNS",
+ "KC_TRNS", "KC_TRNS", "KC_TRNS", "KC_TRNS", "KC_TRNS", "KC_TRNS", "KC_TRNS", "KC_TRNS", "KC_TRNS", "KC_TRNS", "KC_TRNS", "KC_TRNS",
+ "KC_TRNS", "GU_TOGG", "KC_TRNS", "KC_TRNS", "KC_TRNS", "KC_TRNS", "MO(3)", "KC_TRNS"
+ ],
+ [
+ "KC_TRNS", "KC_MPLY", "KC_MSTP", "KC_MPRV", "KC_MNXT", "KC_MUTE", "KC_VOLD", "KC_VOLU", "KC_TRNS", "KC_TRNS", "KC_TRNS", "RM_TOGG", "RM_NEXT", "RM_PREV",
+ "KC_TRNS", "KC_TRNS", "RM_VALU", "KC_TRNS", "KC_TRNS", "KC_TRNS", "KC_TRNS", "KC_TRNS", "KC_TRNS", "KC_TRNS", "KC_TRNS", "KC_TRNS", "KC_TRNS", "KC_TRNS",
+ "KC_TRNS", "RM_SPDD", "RM_VALD", "RM_SPDU", "KC_TRNS", "KC_TRNS", "KC_TRNS", "KC_TRNS", "KC_TRNS", "KC_TRNS", "KC_TRNS", "KC_TRNS", "KC_TRNS",
+ "KC_TRNS", "KC_TRNS", "KC_TRNS", "KC_TRNS", "KC_TRNS", "KC_TRNS", "KC_TRNS", "KC_TRNS", "KC_TRNS", "KC_TRNS", "KC_TRNS", "KC_TRNS",
+ "KC_TRNS", "KC_TRNS", "KC_TRNS", "KC_TRNS", "KC_TRNS", "MO(3)", "KC_TRNS", "KC_TRNS"
+ ],
+ [
+ "XXXXXXX", "XXXXXXX", "XXXXXXX", "XXXXXXX", "XXXXXXX", "XXXXXXX", "XXXXXXX", "XXXXXXX", "XXXXXXX", "XXXXXXX", "XXXXXXX", "XXXXXXX", "XXXXXXX", "XXXXXXX",
+ "XXXXXXX", "XXXXXXX", "RM_SATU", "XXXXXXX", "XXXXXXX", "XXXXXXX", "XXXXXXX", "XXXXXXX", "XXXXXXX", "XXXXXXX", "XXXXXXX", "XXXXXXX", "XXXXXXX", "XXXXXXX",
+ "XXXXXXX", "RM_HUED", "RM_SATD", "RM_HUEU", "XXXXXXX", "XXXXXXX", "XXXXXXX", "XXXXXXX", "XXXXXXX", "XXXXXXX", "XXXXXXX", "XXXXXXX", "XXXXXXX",
+ "XXXXXXX", "XXXXXXX", "XXXXXXX", "XXXXXXX", "XXXXXXX", "XXXXXXX", "XXXXXXX", "XXXXXXX", "XXXXXXX", "XXXXXXX", "XXXXXXX", "XXXXXXX",
+ "XXXXXXX", "XXXXXXX", "XXXXXXX", "XXXXXXX", "XXXXXXX", "KC_TRNS", "KC_TRNS", "XXXXXXX"
+ ]
+ ],
+ "author":"dkjer",
+ "notes":"",
+ "version":1
+}
diff --git a/keyboards/durgod/dgk6x/venus_ansi/keymaps/default/readme.md b/keyboards/durgod/dgk6x/venus_ansi/keymaps/default/readme.md
new file mode 100644
index 0000000000..da166eb60a
--- /dev/null
+++ b/keyboards/durgod/dgk6x/venus_ansi/keymaps/default/readme.md
@@ -0,0 +1,11 @@
+
+
+# The default keymap for Durgod Venus.
+
+Layer 0 : Standard ANSI 60 Keys layout
+
+Layer 1 : F1-12 and additional navigation keys.
+
+Layer 2 : Media and RGB matrix control
+
+Layer 3 : RGB matrix hue and saturation
diff --git a/keyboards/durgod/dgk6x/venus_ansi/readme.md b/keyboards/durgod/dgk6x/venus_ansi/readme.md
new file mode 100644
index 0000000000..d8009dcb22
--- /dev/null
+++ b/keyboards/durgod/dgk6x/venus_ansi/readme.md
@@ -0,0 +1,26 @@
+# Venus
+
+This is a standard off-the-shelf Durgod Venus 60% ANSI Layout keyboard with RGB matrix.
+
+* Keyboard Maintainer: [J-Sully](https://github.com/J-Sully) and [dkjer](https://github.com/dkjer)
+* Hardware Supported: Durgod Venus board with STM32F070RBT6
+* Hardware Availability: https://www.amazon.com/dp/B07XFP5MMZ
+
+## Instructions
+
+### Build
+
+Make command example for this keyboard (after setting up your build environment):
+
+ make durgod/dgk6x/venus_ansi:default
+
+Flashing example for this keyboard:
+
+ make durgod/dgk6x/venus_ansi:default:flash
+
+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).
+
+### Initial Flash
+
+Instructions for Flashing from initial Stock Firmware:
+* [Initial Flashing](../readme.md#initial-flash)
diff --git a/keyboards/durgod/dgk6x/venus_ansi/venus_ansi.c b/keyboards/durgod/dgk6x/venus_ansi/venus_ansi.c
new file mode 100644
index 0000000000..30735fc80e
--- /dev/null
+++ b/keyboards/durgod/dgk6x/venus_ansi/venus_ansi.c
@@ -0,0 +1,95 @@
+/* Copyright 2021 Jessica Sullivan and Don Kjer
+ *
+ * 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 .
+ */
+
+#include "dgk6x.h"
+
+#ifdef RGB_MATRIX_ENABLE
+
+const is31fl3733_led_t PROGMEM g_is31fl3733_leds[IS31FL3733_LED_COUNT] = {
+/* Refer to IS31 manual for these locations
+ * driver
+ * | R location
+ * | | G location
+ * | | | B location
+ * | | | | */
+
+ {0, SW3_CS1, SW2_CS1, SW1_CS1}, // Esc
+ {0, SW3_CS2, SW2_CS2, SW1_CS2}, // 1
+ {0, SW3_CS3, SW2_CS3, SW1_CS3}, // 2
+ {0, SW3_CS4, SW2_CS4, SW1_CS4}, // 3
+ {0, SW3_CS5, SW2_CS5, SW1_CS5}, // 4
+ {0, SW3_CS6, SW2_CS6, SW1_CS6}, // 5
+ {0, SW3_CS7, SW2_CS7, SW1_CS7}, // 6
+ {0, SW3_CS8, SW2_CS8, SW1_CS8}, // 7
+ {0, SW3_CS9, SW2_CS9, SW1_CS9}, // 8
+ {0, SW3_CS10, SW2_CS10, SW1_CS10}, // 9
+ {0, SW3_CS11, SW2_CS11, SW1_CS11}, // 0
+ {0, SW3_CS12, SW2_CS12, SW1_CS12}, // -
+ {0, SW3_CS13, SW2_CS13, SW1_CS13}, // =
+ {0, SW3_CS14, SW2_CS14, SW1_CS14}, // Bksp
+
+ {0, SW6_CS1, SW5_CS1, SW4_CS1}, // Tab
+ {0, SW6_CS2, SW5_CS2, SW4_CS2}, // Q
+ {0, SW6_CS3, SW5_CS3, SW4_CS3}, // W
+ {0, SW6_CS4, SW5_CS4, SW4_CS4}, // E
+ {0, SW6_CS5, SW5_CS5, SW4_CS5}, // R
+ {0, SW6_CS6, SW5_CS6, SW4_CS6}, // T
+ {0, SW6_CS7, SW5_CS7, SW4_CS7}, // Y
+ {0, SW6_CS8, SW5_CS8, SW4_CS8}, // U
+ {0, SW6_CS9, SW5_CS9, SW4_CS9}, // I
+ {0, SW6_CS10, SW5_CS10, SW4_CS10}, // O
+ {0, SW6_CS11, SW5_CS11, SW4_CS11}, // P
+ {0, SW6_CS12, SW5_CS12, SW4_CS12}, // [
+ {0, SW6_CS13, SW5_CS13, SW4_CS13}, // ]
+ {0, SW6_CS14, SW5_CS14, SW4_CS14}, // Pipe
+
+ {0, SW9_CS1, SW8_CS1, SW7_CS1}, // Caps
+ {0, SW9_CS2, SW8_CS2, SW7_CS2}, // A
+ {0, SW9_CS3, SW8_CS3, SW7_CS3}, // S
+ {0, SW9_CS4, SW8_CS4, SW7_CS4}, // D
+ {0, SW9_CS5, SW8_CS5, SW7_CS5}, // F
+ {0, SW9_CS6, SW8_CS6, SW7_CS6}, // G
+ {0, SW9_CS7, SW8_CS7, SW7_CS7}, // H
+ {0, SW9_CS8, SW8_CS8, SW7_CS8}, // J
+ {0, SW9_CS9, SW8_CS9, SW7_CS9}, // K
+ {0, SW9_CS10, SW8_CS10, SW7_CS10}, // L
+ {0, SW9_CS11, SW8_CS11, SW7_CS11}, // :
+ {0, SW9_CS12, SW8_CS12, SW7_CS12}, // '
+ {0, SW9_CS14, SW8_CS14, SW7_CS14}, // Enter
+
+ {0, SW12_CS1, SW11_CS1, SW10_CS1}, // LShift
+ {0, SW12_CS2, SW11_CS2, SW10_CS2}, // Z
+ {0, SW12_CS3, SW11_CS3, SW10_CS3}, // X
+ {0, SW12_CS4, SW11_CS4, SW10_CS4}, // C
+ {0, SW12_CS5, SW11_CS5, SW10_CS5}, // V
+ {0, SW12_CS6, SW11_CS6, SW10_CS6}, // B
+ {0, SW12_CS7, SW11_CS7, SW10_CS7}, // N
+ {0, SW12_CS8, SW11_CS8, SW10_CS8}, // M
+ {0, SW12_CS9, SW11_CS9, SW10_CS9}, // <
+ {0, SW12_CS10, SW11_CS10, SW10_CS10}, // >
+ {0, SW12_CS11, SW11_CS11, SW10_CS11}, // ?
+ {0, SW12_CS12, SW11_CS12, SW10_CS12}, // RShift
+
+ {1, SW3_CS1, SW2_CS1, SW1_CS1}, // LCtrl
+ {1, SW3_CS2, SW2_CS2, SW1_CS2}, // LAlt
+ {1, SW3_CS3, SW2_CS3, SW1_CS3}, // Windows
+ {1, SW3_CS6, SW2_CS6, SW1_CS6}, // Space
+ {1, SW3_CS10, SW2_CS10, SW1_CS10}, // Fn1/RAlt hades/venus
+ {1, SW3_CS11, SW2_CS11, SW1_CS11}, // Fn2/Fn1
+ {1, SW3_CS12, SW2_CS12, SW1_CS12}, // RCtrl/Fn2
+ {1, SW3_CS13, SW2_CS13, SW1_CS13}, // LEFT/RCtrl
+};
+#endif /* RGB_MATRIX_ENABLE */
diff --git a/keyboards/durgod/dgk6x/venus_iso/config.h b/keyboards/durgod/dgk6x/venus_iso/config.h
new file mode 100644
index 0000000000..f23b355905
--- /dev/null
+++ b/keyboards/durgod/dgk6x/venus_iso/config.h
@@ -0,0 +1,19 @@
+/* Copyright 2021 Jessica Sullivan and Don Kjer
+ *
+ * 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 .
+ */
+
+#pragma once
+
+#define CAPS_LED 29
diff --git a/keyboards/durgod/dgk6x/venus_iso/keyboard.json b/keyboards/durgod/dgk6x/venus_iso/keyboard.json
new file mode 100644
index 0000000000..46b88427eb
--- /dev/null
+++ b/keyboards/durgod/dgk6x/venus_iso/keyboard.json
@@ -0,0 +1,151 @@
+{
+ "manufacturer": "Hoksi Technology",
+ "keyboard_name": "DURGOD Venus (QMK)",
+ "maintainer": "pascalhansjosten",
+ "diode_direction": "ROW2COL",
+ "matrix_pins": {
+ "cols": ["B0", "B1", "B2", "B10", "B11", "B12", "B13", "B14", "B15", "C6", "C7", "C8", "C9", "C10"],
+ "rows": ["A0", "A1", "A2", "A3", "A4"]
+ },
+ "rgb_matrix": {
+ "layout": [
+ {"matrix": [0, 0], "x": 0, "y": 0, "flags": 1},
+ {"matrix": [0, 1], "x": 16, "y": 0, "flags": 4},
+ {"matrix": [0, 2], "x": 32, "y": 0, "flags": 4},
+ {"matrix": [0, 3], "x": 48, "y": 0, "flags": 4},
+ {"matrix": [0, 4], "x": 64, "y": 0, "flags": 4},
+ {"matrix": [0, 5], "x": 80, "y": 0, "flags": 4},
+ {"matrix": [0, 6], "x": 96, "y": 0, "flags": 4},
+ {"matrix": [0, 7], "x": 112, "y": 0, "flags": 4},
+ {"matrix": [0, 8], "x": 128, "y": 0, "flags": 4},
+ {"matrix": [0, 9], "x": 144, "y": 0, "flags": 4},
+ {"matrix": [0, 10], "x": 160, "y": 0, "flags": 4},
+ {"matrix": [0, 11], "x": 176, "y": 0, "flags": 4},
+ {"matrix": [0, 12], "x": 192, "y": 0, "flags": 4},
+ {"matrix": [0, 13], "x": 216, "y": 0, "flags": 1},
+ {"matrix": [1, 0], "x": 4, "y": 16, "flags": 4},
+ {"matrix": [1, 1], "x": 24, "y": 16, "flags": 4},
+ {"matrix": [1, 2], "x": 40, "y": 16, "flags": 4},
+ {"matrix": [1, 3], "x": 56, "y": 16, "flags": 4},
+ {"matrix": [1, 4], "x": 72, "y": 16, "flags": 4},
+ {"matrix": [1, 5], "x": 88, "y": 16, "flags": 4},
+ {"matrix": [1, 6], "x": 104, "y": 16, "flags": 4},
+ {"matrix": [1, 7], "x": 120, "y": 16, "flags": 4},
+ {"matrix": [1, 8], "x": 136, "y": 16, "flags": 4},
+ {"matrix": [1, 9], "x": 152, "y": 16, "flags": 4},
+ {"matrix": [1, 10], "x": 168, "y": 16, "flags": 4},
+ {"matrix": [1, 11], "x": 184, "y": 16, "flags": 4},
+ {"matrix": [1, 12], "x": 200, "y": 16, "flags": 4},
+ {"matrix": [2, 0], "x": 6, "y": 32, "flags": 4},
+ {"matrix": [2, 1], "x": 28, "y": 32, "flags": 4},
+ {"matrix": [2, 2], "x": 44, "y": 32, "flags": 4},
+ {"matrix": [2, 3], "x": 60, "y": 32, "flags": 4},
+ {"matrix": [2, 4], "x": 76, "y": 32, "flags": 4},
+ {"matrix": [2, 5], "x": 92, "y": 32, "flags": 4},
+ {"matrix": [2, 6], "x": 108, "y": 32, "flags": 4},
+ {"matrix": [2, 7], "x": 124, "y": 32, "flags": 4},
+ {"matrix": [2, 8], "x": 140, "y": 32, "flags": 4},
+ {"matrix": [2, 9], "x": 156, "y": 32, "flags": 4},
+ {"matrix": [2, 10], "x": 172, "y": 32, "flags": 4},
+ {"matrix": [2, 11], "x": 188, "y": 32, "flags": 4},
+ {"matrix": [2, 12], "x": 204, "y": 32, "flags": 4},
+ {"matrix": [2, 13], "x": 220, "y": 24, "flags": 1},
+ {"matrix": [3, 0], "x": 2, "y": 48, "flags": 1},
+ {"matrix": [3, 1], "x": 19, "y": 48, "flags": 4},
+ {"matrix": [3, 2], "x": 36, "y": 48, "flags": 4},
+ {"matrix": [3, 3], "x": 52, "y": 48, "flags": 4},
+ {"matrix": [3, 4], "x": 68, "y": 48, "flags": 4},
+ {"matrix": [3, 5], "x": 84, "y": 48, "flags": 4},
+ {"matrix": [3, 6], "x": 100, "y": 48, "flags": 4},
+ {"matrix": [3, 7], "x": 116, "y": 48, "flags": 4},
+ {"matrix": [3, 8], "x": 132, "y": 48, "flags": 4},
+ {"matrix": [3, 9], "x": 148, "y": 48, "flags": 4},
+ {"matrix": [3, 10], "x": 164, "y": 48, "flags": 4},
+ {"matrix": [3, 11], "x": 180, "y": 48, "flags": 4},
+ {"matrix": [3, 12], "x": 202, "y": 48, "flags": 1},
+ {"matrix": [4, 0], "x": 2, "y": 64, "flags": 1},
+ {"matrix": [4, 1], "x": 22, "y": 64, "flags": 1},
+ {"matrix": [4, 2], "x": 42, "y": 64, "flags": 1},
+ {"matrix": [4, 5], "x": 102, "y": 64, "flags": 4},
+ {"matrix": [4, 9], "x": 162, "y": 64, "flags": 1},
+ {"matrix": [4, 10], "x": 182, "y": 64, "flags": 1},
+ {"matrix": [4, 11], "x": 202, "y": 64, "flags": 1},
+ {"matrix": [4, 12], "x": 222, "y": 64, "flags": 4}
+ ]
+ },
+ "url": "https://www.amazon.com/dp/B07XFP5MMZ",
+ "usb": {
+ "device_version": "0.0.1",
+ "pid": "0x7EC6",
+ "vid": "0xD60D"
+ },
+ "community_layouts": ["60_iso"],
+ "layouts": {
+ "LAYOUT_60_iso": {
+ "layout": [
+ {"label": "Esc", "matrix": [0, 0], "x": 0, "y": 0},
+ {"label": "!", "matrix": [0, 1], "x": 1, "y": 0},
+ {"label": "@", "matrix": [0, 2], "x": 2, "y": 0},
+ {"label": "#", "matrix": [0, 3], "x": 3, "y": 0},
+ {"label": "$", "matrix": [0, 4], "x": 4, "y": 0},
+ {"label": "%", "matrix": [0, 5], "x": 5, "y": 0},
+ {"label": "^", "matrix": [0, 6], "x": 6, "y": 0},
+ {"label": "&", "matrix": [0, 7], "x": 7, "y": 0},
+ {"label": "*", "matrix": [0, 8], "x": 8, "y": 0},
+ {"label": "(", "matrix": [0, 9], "x": 9, "y": 0},
+ {"label": ")", "matrix": [0, 10], "x": 10, "y": 0},
+ {"label": "_", "matrix": [0, 11], "x": 11, "y": 0},
+ {"label": "+", "matrix": [0, 12], "x": 12, "y": 0},
+ {"label": "Backspace", "matrix": [0, 13], "x": 13, "y": 0, "w": 2},
+ {"label": "Tab", "matrix": [1, 0], "x": 0, "y": 1, "w": 1.5},
+ {"label": "Q", "matrix": [1, 1], "x": 1.5, "y": 1},
+ {"label": "W", "matrix": [1, 2], "x": 2.5, "y": 1},
+ {"label": "E", "matrix": [1, 3], "x": 3.5, "y": 1},
+ {"label": "R", "matrix": [1, 4], "x": 4.5, "y": 1},
+ {"label": "T", "matrix": [1, 5], "x": 5.5, "y": 1},
+ {"label": "Y", "matrix": [1, 6], "x": 6.5, "y": 1},
+ {"label": "U", "matrix": [1, 7], "x": 7.5, "y": 1},
+ {"label": "I", "matrix": [1, 8], "x": 8.5, "y": 1},
+ {"label": "O", "matrix": [1, 9], "x": 9.5, "y": 1},
+ {"label": "P", "matrix": [1, 10], "x": 10.5, "y": 1},
+ {"label": "{", "matrix": [1, 11], "x": 11.5, "y": 1},
+ {"label": "}", "matrix": [1, 12], "x": 12.5, "y": 1},
+ {"label": "CapsLock", "matrix": [2, 0], "x": 0, "y": 2, "w": 1.75},
+ {"label": "A", "matrix": [2, 1], "x": 1.75, "y": 2},
+ {"label": "S", "matrix": [2, 2], "x": 2.75, "y": 2},
+ {"label": "D", "matrix": [2, 3], "x": 3.75, "y": 2},
+ {"label": "F", "matrix": [2, 4], "x": 4.75, "y": 2},
+ {"label": "G", "matrix": [2, 5], "x": 5.75, "y": 2},
+ {"label": "H", "matrix": [2, 6], "x": 6.75, "y": 2},
+ {"label": "J", "matrix": [2, 7], "x": 7.75, "y": 2},
+ {"label": "K", "matrix": [2, 8], "x": 8.75, "y": 2},
+ {"label": "L", "matrix": [2, 9], "x": 9.75, "y": 2},
+ {"label": ":", "matrix": [2, 10], "x": 10.75, "y": 2},
+ {"label": "\"", "matrix": [2, 11], "x": 11.75, "y": 2},
+ {"label": "#", "matrix": [2, 12], "x": 12.75, "y": 2},
+ {"label": "Enter", "matrix": [2, 13], "x": 13.75, "y": 1, "w": 1.25, "h": 2},
+ {"label": "Shift", "matrix": [3, 0], "x": 0, "y": 3, "w": 1.25},
+ {"label": "\\", "matrix": [3, 1], "x": 1.25, "y": 3},
+ {"label": "Z", "matrix": [3, 2], "x": 2.25, "y": 3},
+ {"label": "X", "matrix": [3, 3], "x": 3.25, "y": 3},
+ {"label": "C", "matrix": [3, 4], "x": 4.25, "y": 3},
+ {"label": "V", "matrix": [3, 5], "x": 5.25, "y": 3},
+ {"label": "B", "matrix": [3, 6], "x": 6.25, "y": 3},
+ {"label": "N", "matrix": [3, 7], "x": 7.25, "y": 3},
+ {"label": "M", "matrix": [3, 8], "x": 8.25, "y": 3},
+ {"label": "<", "matrix": [3, 9], "x": 9.25, "y": 3},
+ {"label": ">", "matrix": [3, 10], "x": 10.25, "y": 3},
+ {"label": "?", "matrix": [3, 11], "x": 11.25, "y": 3},
+ {"label": "Shift", "matrix": [3, 12], "x": 12.25, "y": 3, "w": 1.75},
+ {"label": "Ctrl", "matrix": [4, 0], "x": 0, "y": 4, "w": 1.25},
+ {"label": "Win", "matrix": [4, 1], "x": 1.25, "y": 4, "w": 1.25},
+ {"label": "Alt", "matrix": [4, 2], "x": 2.5, "y": 4, "w": 1.25},
+ {"label": "K45", "matrix": [4, 5], "x": 3.75, "y": 4, "w": 6.25},
+ {"label": "Alt", "matrix": [4, 9], "x": 10, "y": 4, "w": 1.25},
+ {"label": "Fn1", "matrix": [4, 10], "x": 11.25, "y": 4, "w": 1.25},
+ {"label": "Fn2", "matrix": [4, 11], "x": 12.5, "y": 4, "w": 1.25},
+ {"label": "Ctrl", "matrix": [4, 12], "x": 13.75, "y": 4, "w": 1.25}
+ ]
+ }
+ }
+}
diff --git a/keyboards/durgod/dgk6x/venus_iso/keymaps/default/keymap.json b/keyboards/durgod/dgk6x/venus_iso/keymaps/default/keymap.json
new file mode 100644
index 0000000000..da53f1d9b5
--- /dev/null
+++ b/keyboards/durgod/dgk6x/venus_iso/keymaps/default/keymap.json
@@ -0,0 +1,37 @@
+{
+ "keyboard": "durgod/dgk6x/venus_iso",
+ "keymap": "default",
+ "layout": "LAYOUT_60_iso",
+ "layers": [
+ [
+ "QK_GESC", "KC_1", "KC_2", "KC_3", "KC_4", "KC_5", "KC_6", "KC_7", "KC_8", "KC_9", "KC_0", "KC_MINS", "KC_EQL", "KC_BSPC",
+ "KC_TAB", "KC_Q", "KC_W", "KC_E", "KC_R", "KC_T", "KC_Y", "KC_U", "KC_I", "KC_O", "KC_P", "KC_LBRC", "KC_RBRC",
+ "KC_CAPS", "KC_A", "KC_S", "KC_D", "KC_F", "KC_G", "KC_H", "KC_J", "KC_K", "KC_L", "KC_SCLN", "KC_QUOT", "KC_NUHS", "KC_ENT",
+ "KC_LSFT", "KC_NUBS", "KC_Z", "KC_X", "KC_C", "KC_V", "KC_B", "KC_N", "KC_M", "KC_COMM", "KC_DOT", "KC_SLSH", "RSFT_T(KC_UP)",
+ "KC_LCTL", "KC_LGUI", "KC_LALT", "KC_SPC", "KC_RALT","LT(1,KC_LEFT)","LT(2,KC_DOWN)","RCTL_T(KC_RIGHT)"
+ ],
+ [
+ "KC_GRV", "KC_F1", "KC_F2", "KC_F3", "KC_F4", "KC_F5", "KC_F6", "KC_F7", "KC_F8", "KC_F9", "KC_F10", "KC_F11", "KC_F12", "KC_TRNS",
+ "KC_TRNS", "KC_TRNS", "KC_UP", "KC_TRNS", "KC_TRNS", "KC_TRNS", "KC_TRNS", "KC_TRNS", "KC_PSCR", "KC_INS", "KC_HOME", "KC_END", "KC_DEL",
+ "KC_TRNS", "KC_LEFT", "KC_DOWN", "KC_RIGHT","KC_TRNS", "KC_TRNS", "KC_TRNS", "KC_TRNS", "KC_TRNS", "KC_TRNS", "KC_TRNS", "KC_PGUP", "KC_PGDN", "KC_TRNS",
+ "KC_TRNS", "KC_TRNS", "KC_TRNS", "KC_TRNS", "KC_TRNS", "KC_TRNS", "KC_TRNS", "KC_TRNS", "KC_TRNS", "KC_TRNS", "KC_TRNS", "KC_TRNS", "KC_TRNS",
+ "KC_TRNS", "GU_TOGG", "KC_TRNS", "KC_TRNS", "KC_TRNS", "KC_TRNS", "MO(3)", "KC_TRNS"
+ ],
+ [
+ "KC_TRNS", "KC_MPLY", "KC_MSTP", "KC_MPRV", "KC_MNXT", "KC_MUTE", "KC_VOLD", "KC_VOLU", "KC_TRNS", "KC_TRNS", "KC_TRNS", "RM_TOGG", "RM_NEXT", "RM_PREV",
+ "KC_TRNS", "KC_TRNS", "RM_VALU", "KC_TRNS", "KC_TRNS", "KC_TRNS", "KC_TRNS", "KC_TRNS", "KC_TRNS", "KC_TRNS", "KC_TRNS", "KC_TRNS", "KC_TRNS",
+ "KC_TRNS", "RM_SPDD", "RM_VALD", "RM_SPDU", "KC_TRNS", "KC_TRNS", "KC_TRNS", "KC_TRNS", "KC_TRNS", "KC_TRNS", "KC_TRNS", "KC_TRNS", "KC_TRNS", "KC_TRNS",
+ "KC_TRNS", "KC_TRNS", "KC_TRNS", "KC_TRNS", "KC_TRNS", "KC_TRNS", "KC_TRNS", "KC_TRNS", "KC_TRNS", "KC_TRNS", "KC_TRNS", "KC_TRNS", "KC_TRNS",
+ "KC_TRNS", "KC_TRNS", "KC_TRNS", "KC_TRNS", "KC_TRNS", "MO(3)", "KC_TRNS", "KC_TRNS"
+ ],
+ [
+ "XXXXXXX", "XXXXXXX", "XXXXXXX", "XXXXXXX", "XXXXXXX", "XXXXXXX", "XXXXXXX", "XXXXXXX", "XXXXXXX", "XXXXXXX", "XXXXXXX", "XXXXXXX", "XXXXXXX", "XXXXXXX",
+ "XXXXXXX", "XXXXXXX", "RM_SATU", "XXXXXXX", "XXXXXXX", "XXXXXXX", "XXXXXXX", "XXXXXXX", "XXXXXXX", "XXXXXXX", "XXXXXXX", "XXXXXXX", "XXXXXXX",
+ "XXXXXXX", "RM_HUED", "RM_SATD", "RM_HUEU", "XXXXXXX", "XXXXXXX", "XXXXXXX", "XXXXXXX", "XXXXXXX", "XXXXXXX", "XXXXXXX", "XXXXXXX", "XXXXXXX", "XXXXXXX",
+ "XXXXXXX", "XXXXXXX", "XXXXXXX", "XXXXXXX", "XXXXXXX", "XXXXXXX", "XXXXXXX", "XXXXXXX", "XXXXXXX", "XXXXXXX", "XXXXXXX", "XXXXXXX", "XXXXXXX",
+ "XXXXXXX", "XXXXXXX", "XXXXXXX", "XXXXXXX", "XXXXXXX", "KC_TRNS", "KC_TRNS", "XXXXXXX"
+ ]
+ ],
+ "author":"pascalhansjosten",
+ "version":1
+}
diff --git a/keyboards/durgod/dgk6x/venus_iso/readme.md b/keyboards/durgod/dgk6x/venus_iso/readme.md
new file mode 100644
index 0000000000..2f40fedd96
--- /dev/null
+++ b/keyboards/durgod/dgk6x/venus_iso/readme.md
@@ -0,0 +1,26 @@
+# Venus
+
+This is a standard off-the-shelf Durgod Venus 60% ISO Layout keyboard with RGB matrix.
+
+* Keyboard Maintainer: [pascalhansjosten](https://github.com/pascalhansjosten), based on the work of [J-Sully](https://github.com/J-Sully) and [dkjer](https://github.com/dkjer)
+* Hardware Supported: Durgod Venus ISO board with STM32F070RBT6
+* Hardware Availability: https://www.amazon.de/dp/B08H28DNQV/
+
+## Instructions
+
+### Build
+
+Make command example for this keyboard (after setting up your build environment):
+
+ make durgod/dgk6x/venus_iso:default
+
+Flashing example for this keyboard:
+
+ make durgod/dgk6x/venus_iso:default:flash
+
+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).
+
+### Initial Flash
+
+Instructions for Flashing from initial Stock Firmware:
+* [Initial Flashing](../readme.md#initial-flash)
diff --git a/keyboards/durgod/dgk6x/venus_iso/venus_iso.c b/keyboards/durgod/dgk6x/venus_iso/venus_iso.c
new file mode 100644
index 0000000000..4cc9bf3364
--- /dev/null
+++ b/keyboards/durgod/dgk6x/venus_iso/venus_iso.c
@@ -0,0 +1,97 @@
+/* Copyright 2021 Jessica Sullivan and Don Kjer
+ *
+ * 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 .
+ */
+
+#include "dgk6x.h"
+
+#ifdef RGB_MATRIX_ENABLE
+
+const is31fl3733_led_t PROGMEM g_is31fl3733_leds[IS31FL3733_LED_COUNT] = {
+/* Refer to IS31 manual for these locations
+ * driver
+ * | R location
+ * | | G location
+ * | | | B location
+ * | | | | */
+
+ {0, SW3_CS1, SW2_CS1, SW1_CS1}, // Esc
+ {0, SW3_CS2, SW2_CS2, SW1_CS2}, // 1
+ {0, SW3_CS3, SW2_CS3, SW1_CS3}, // 2
+ {0, SW3_CS4, SW2_CS4, SW1_CS4}, // 3
+ {0, SW3_CS5, SW2_CS5, SW1_CS5}, // 4
+ {0, SW3_CS6, SW2_CS6, SW1_CS6}, // 5
+ {0, SW3_CS7, SW2_CS7, SW1_CS7}, // 6
+ {0, SW3_CS8, SW2_CS8, SW1_CS8}, // 7
+ {0, SW3_CS9, SW2_CS9, SW1_CS9}, // 8
+ {0, SW3_CS10, SW2_CS10, SW1_CS10}, // 9
+ {0, SW3_CS11, SW2_CS11, SW1_CS11}, // 0
+ {0, SW3_CS12, SW2_CS12, SW1_CS12}, // -
+ {0, SW3_CS13, SW2_CS13, SW1_CS13}, // =
+ {0, SW3_CS14, SW2_CS14, SW1_CS14}, // Bksp
+
+ {0, SW6_CS1, SW5_CS1, SW4_CS1}, // Tab
+ {0, SW6_CS2, SW5_CS2, SW4_CS2}, // Q
+ {0, SW6_CS3, SW5_CS3, SW4_CS3}, // W
+ {0, SW6_CS4, SW5_CS4, SW4_CS4}, // E
+ {0, SW6_CS5, SW5_CS5, SW4_CS5}, // R
+ {0, SW6_CS6, SW5_CS6, SW4_CS6}, // T
+ {0, SW6_CS7, SW5_CS7, SW4_CS7}, // Y
+ {0, SW6_CS8, SW5_CS8, SW4_CS8}, // U
+ {0, SW6_CS9, SW5_CS9, SW4_CS9}, // I
+ {0, SW6_CS10, SW5_CS10, SW4_CS10}, // O
+ {0, SW6_CS11, SW5_CS11, SW4_CS11}, // P
+ {0, SW6_CS12, SW5_CS12, SW4_CS12}, // [
+ {0, SW6_CS13, SW5_CS13, SW4_CS13}, // ]
+
+ {0, SW9_CS1, SW8_CS1, SW7_CS1}, // Caps
+ {0, SW9_CS2, SW8_CS2, SW7_CS2}, // A
+ {0, SW9_CS3, SW8_CS3, SW7_CS3}, // S
+ {0, SW9_CS4, SW8_CS4, SW7_CS4}, // D
+ {0, SW9_CS5, SW8_CS5, SW7_CS5}, // F
+ {0, SW9_CS6, SW8_CS6, SW7_CS6}, // G
+ {0, SW9_CS7, SW8_CS7, SW7_CS7}, // H
+ {0, SW9_CS8, SW8_CS8, SW7_CS8}, // J
+ {0, SW9_CS9, SW8_CS9, SW7_CS9}, // K
+ {0, SW9_CS10, SW8_CS10, SW7_CS10}, // L
+ {0, SW9_CS11, SW8_CS11, SW7_CS11}, // :
+ {0, SW9_CS12, SW8_CS12, SW7_CS12}, // '
+ {0, SW9_CS13, SW8_CS13, SW7_CS13}, // NUHS
+ {0, SW9_CS14, SW8_CS14, SW7_CS14}, // Enter
+
+ {0, SW12_CS1, SW11_CS1, SW10_CS1}, // LShift
+ {0, SW12_CS2, SW11_CS2, SW10_CS2}, // NUBS
+ {0, SW12_CS3, SW11_CS3, SW10_CS3}, // Z
+ {0, SW12_CS4, SW11_CS4, SW10_CS4}, // X
+ {0, SW12_CS5, SW11_CS5, SW10_CS5}, // C
+ {0, SW12_CS6, SW11_CS6, SW10_CS6}, // V
+ {0, SW12_CS7, SW11_CS7, SW10_CS7}, // B
+ {0, SW12_CS8, SW11_CS8, SW10_CS8}, // N
+ {0, SW12_CS9, SW11_CS9, SW10_CS9}, // M
+ {0, SW12_CS10, SW11_CS10, SW10_CS10}, // <
+ {0, SW12_CS11, SW11_CS11, SW10_CS11}, // >
+ {0, SW12_CS12, SW11_CS12, SW10_CS12}, // ?
+ {0, SW12_CS13, SW11_CS13, SW10_CS13}, // RShift
+
+ {1, SW3_CS1, SW2_CS1, SW1_CS1}, // LCtrl
+ {1, SW3_CS2, SW2_CS2, SW1_CS2}, // LAlt
+ {1, SW3_CS3, SW2_CS3, SW1_CS3}, // Windows
+ {1, SW3_CS6, SW2_CS6, SW1_CS6}, // Space
+ {1, SW3_CS10, SW2_CS10, SW1_CS10}, // Fn1/RAlt hades/venus
+ {1, SW3_CS11, SW2_CS11, SW1_CS11}, // Fn2/Fn1
+ {1, SW3_CS12, SW2_CS12, SW1_CS12}, // RCtrl/Fn2
+ {1, SW3_CS13, SW2_CS13, SW1_CS13}, // LEFT/RCtrl
+};
+
+#endif /* RGB_MATRIX_ENABLE */
diff --git a/keyboards/durgod/venus/readme.md b/keyboards/durgod/venus/readme.md
index c2228583a9..685a9d2bc0 100644
--- a/keyboards/durgod/venus/readme.md
+++ b/keyboards/durgod/venus/readme.md
@@ -1,3 +1,3 @@
# Venus
-See: [Venus](../dgk6x/venus/readme.md)
+See: [Venus ANSI](../dgk6x/venus_ansi/readme.md) and [Venus ISO](../dgk6x/venus_iso/readme.md)
--
cgit v1.2.3
From efc5d63383b64291f25c8377bcfae8178dd63302 Mon Sep 17 00:00:00 2001
From: Pascal Getreuer
Date: Tue, 11 Nov 2025 03:27:12 -0800
Subject: [Core] Speculative Hold option for mod-taps: hold mods instantly
while unsettled. (#25572)
---
data/mappings/info_config.hjson | 1 +
docs/tap_hold.md | 33 +
quantum/action.c | 5 +
quantum/action.h | 2 +-
quantum/action_tapping.c | 171 +++
quantum/action_tapping.h | 30 +
quantum/action_util.c | 5 +
.../speculative_hold/default/config.h | 23 +
.../speculative_hold/default/test.mk | 21 +
.../speculative_hold/default/test_keymap.c | 20 +
.../speculative_hold/default/test_tap_hold.cpp | 794 ++++++++++++++
.../speculative_hold/flow_tap/config.h | 25 +
.../speculative_hold/flow_tap/test.mk | 15 +
.../speculative_hold/flow_tap/test_tap_hold.cpp | 1114 ++++++++++++++++++++
.../retro_shift_permissive_hold/config.h | 30 +
.../retro_shift_permissive_hold/test.mk | 17 +
.../test_retro_shift.cpp | 689 ++++++++++++
.../speculative_hold/retro_tapping/config.h | 24 +
.../speculative_hold/retro_tapping/test.mk | 15 +
.../retro_tapping/test_tap_hold.cpp | 629 +++++++++++
20 files changed, 3662 insertions(+), 1 deletion(-)
create mode 100644 tests/tap_hold_configurations/speculative_hold/default/config.h
create mode 100644 tests/tap_hold_configurations/speculative_hold/default/test.mk
create mode 100644 tests/tap_hold_configurations/speculative_hold/default/test_keymap.c
create mode 100644 tests/tap_hold_configurations/speculative_hold/default/test_tap_hold.cpp
create mode 100644 tests/tap_hold_configurations/speculative_hold/flow_tap/config.h
create mode 100644 tests/tap_hold_configurations/speculative_hold/flow_tap/test.mk
create mode 100644 tests/tap_hold_configurations/speculative_hold/flow_tap/test_tap_hold.cpp
create mode 100644 tests/tap_hold_configurations/speculative_hold/retro_shift_permissive_hold/config.h
create mode 100644 tests/tap_hold_configurations/speculative_hold/retro_shift_permissive_hold/test.mk
create mode 100644 tests/tap_hold_configurations/speculative_hold/retro_shift_permissive_hold/test_retro_shift.cpp
create mode 100644 tests/tap_hold_configurations/speculative_hold/retro_tapping/config.h
create mode 100644 tests/tap_hold_configurations/speculative_hold/retro_tapping/test.mk
create mode 100644 tests/tap_hold_configurations/speculative_hold/retro_tapping/test_tap_hold.cpp
(limited to 'data')
diff --git a/data/mappings/info_config.hjson b/data/mappings/info_config.hjson
index 1c6e86d876..8a0c93e5ac 100644
--- a/data/mappings/info_config.hjson
+++ b/data/mappings/info_config.hjson
@@ -224,6 +224,7 @@
"PERMISSIVE_HOLD_PER_KEY": {"info_key": "tapping.permissive_hold_per_key", "value_type": "flag"},
"RETRO_TAPPING": {"info_key": "tapping.retro", "value_type": "flag"},
"RETRO_TAPPING_PER_KEY": {"info_key": "tapping.retro_per_key", "value_type": "flag"},
+ "SPECULATIVE_HOLD": {"info_key": "tapping.speculative_hold", "value_type": "flag"},
"TAP_CODE_DELAY": {"info_key": "qmk.tap_keycode_delay", "value_type": "int"},
"TAP_HOLD_CAPS_DELAY": {"info_key": "qmk.tap_capslock_delay", "value_type": "int"},
"TAPPING_TERM": {"info_key": "tapping.term", "value_type": "int"},
diff --git a/docs/tap_hold.md b/docs/tap_hold.md
index bc2c505040..2aa628c535 100644
--- a/docs/tap_hold.md
+++ b/docs/tap_hold.md
@@ -779,6 +779,39 @@ Do not use `MOD_xxx` constants like `MOD_LSFT` or `MOD_RALT`, since they're 5-bi
[Auto Shift](features/auto_shift) has its own version of `retro tapping` called `retro shift`. It is extremely similar to `retro tapping`, but holding the key past `AUTO_SHIFT_TIMEOUT` results in the value it sends being shifted. Other configurations also affect it differently; see [here](features/auto_shift#retro-shift) for more information.
+### Speculative Hold
+
+Speculative Hold makes mod-tap keys more responsive by applying the modifier instantly on keydown, before the tap-hold decision is made. This is especially useful for actions like Shift+Click with a mouse, which can feel laggy with standard mod-taps.
+
+The firmware holds the modifier speculatively. Once the key's behavior is settled:
+
+* If held, the modifier remains active as expected until the key is released.
+* If tapped, the speculative modifier is canceled just before the tapping keycode is sent.
+
+Speculative Hold applies the modifier early but does not change the underlying tap-hold decision logic. Speculative Hold is compatible to use in combination with any other tap-hold options.
+
+To enable Speculative Hold, add the following to your `config.h`:
+
+```c
+#define SPECULATIVE_HOLD
+```
+
+By default, Speculative Hold applies to mod-taps using Shift, Ctrl, or Shift + Ctrl. You can override this behavior by defining the `get_speculative_hold()` callback in your keymap, for instance:
+
+```c
+bool get_speculative_hold(uint16_t keycode, keyrecord_t* record) {
+ switch (keycode) { // These keys may be speculatively held.
+ case LCTL_T(KC_ESC):
+ case LSFT_T(KC_Z):
+ case RSFT_T(KC_SLSH):
+ return true;
+ }
+ return false; // Disable otherwise.
+}
+```
+
+Some operating systems or applications assign actions to tapping a modifier key by itself, e.g., tapping GUI to open a start menu. Because Speculative Hold sends a lone modifier key press in some cases, it can falsely trigger these actions. To prevent this, set `DUMMY_MOD_NEUTRALIZER_KEYCODE` (and optionally `MODS_TO_NEUTRALIZE`) in your `config.h` in the same way as described above for [Retro Tapping](#retro-tapping).
+
## Why do we include the key record for the per key functions?
One thing that you may notice is that we include the key record for all of the "per key" functions, and may be wondering why we do that.
diff --git a/quantum/action.c b/quantum/action.c
index dd82c9ec99..aacafbe2ff 100644
--- a/quantum/action.c
+++ b/quantum/action.c
@@ -281,6 +281,11 @@ void process_record(keyrecord_t *record) {
if (IS_NOEVENT(record->event)) {
return;
}
+#ifdef SPECULATIVE_HOLD
+ if (record->event.pressed) {
+ speculative_key_settled(record);
+ }
+#endif // SPECULATIVE_HOLD
#ifdef FLOW_TAP_TERM
flow_tap_update_last_event(record);
#endif // FLOW_TAP_TERM
diff --git a/quantum/action.h b/quantum/action.h
index 7616486c6d..a459c438c1 100644
--- a/quantum/action.h
+++ b/quantum/action.h
@@ -38,7 +38,7 @@ extern "C" {
/* tapping count and state */
typedef struct {
bool interrupted : 1;
- bool reserved2 : 1;
+ bool speculated : 1;
bool reserved1 : 1;
bool reserved0 : 1;
uint8_t count : 4;
diff --git a/quantum/action_tapping.c b/quantum/action_tapping.c
index c2f45ea178..5d43dd99ea 100644
--- a/quantum/action_tapping.c
+++ b/quantum/action_tapping.c
@@ -6,8 +6,10 @@
#include "action_tapping.h"
#include "action_util.h"
#include "keycode.h"
+#include "keycode_config.h"
#include "quantum_keycodes.h"
#include "timer.h"
+#include "wait.h"
#ifndef NO_ACTION_TAPPING
@@ -51,6 +53,21 @@ __attribute__((weak)) bool get_permissive_hold(uint16_t keycode, keyrecord_t *re
}
# endif
+# ifdef SPECULATIVE_HOLD
+typedef struct {
+ keypos_t key;
+ uint8_t mods;
+} speculative_key_t;
+# define SPECULATIVE_KEYS_SIZE 8
+static speculative_key_t speculative_keys[SPECULATIVE_KEYS_SIZE] = {};
+static uint8_t num_speculative_keys = 0;
+static uint8_t prev_speculative_mods = 0;
+static uint8_t speculative_mods = 0;
+
+/** Handler to be called on incoming press events. */
+static void speculative_key_press(keyrecord_t *record);
+# endif // SPECULATIVE_HOLD
+
# if defined(CHORDAL_HOLD) || defined(FLOW_TAP_TERM)
# define REGISTERED_TAPS_SIZE 8
// Array of tap-hold keys that have been settled as tapped but not yet released.
@@ -129,6 +146,13 @@ static void debug_waiting_buffer(void);
* FIXME: Needs doc
*/
void action_tapping_process(keyrecord_t record) {
+# ifdef SPECULATIVE_HOLD
+ prev_speculative_mods = speculative_mods;
+ if (record.event.pressed) {
+ speculative_key_press(&record);
+ }
+# endif // SPECULATIVE_HOLD
+
if (process_tapping(&record)) {
if (IS_EVENT(record.event)) {
ac_dprintf("processed: ");
@@ -145,6 +169,12 @@ void action_tapping_process(keyrecord_t record) {
}
}
+# ifdef SPECULATIVE_HOLD
+ if (speculative_mods != prev_speculative_mods) {
+ send_keyboard_report();
+ }
+# endif // SPECULATIVE_HOLD
+
// process waiting_buffer
if (IS_EVENT(record.event) && waiting_buffer_head != waiting_buffer_tail) {
ac_dprintf("---- action_exec: process waiting_buffer -----\n");
@@ -708,6 +738,147 @@ void waiting_buffer_scan_tap(void) {
}
}
+# ifdef SPECULATIVE_HOLD
+static void debug_speculative_keys(void) {
+ ac_dprintf("mods = { ");
+ for (int8_t i = 0; i < num_speculative_keys; ++i) {
+ ac_dprintf("%02X ", speculative_keys[i].mods);
+ }
+ ac_dprintf("}, keys = { ");
+ for (int8_t i = 0; i < num_speculative_keys; ++i) {
+ ac_dprintf("%02X%02X ", speculative_keys[i].key.row, speculative_keys[i].key.col);
+ }
+ ac_dprintf("}\n");
+}
+
+// Find key in speculative_keys. Returns num_speculative_keys if not found.
+static int8_t speculative_keys_find(keypos_t key) {
+ uint8_t i;
+ for (i = 0; i < num_speculative_keys; ++i) {
+ if (KEYEQ(speculative_keys[i].key, key)) {
+ break;
+ }
+ }
+ return i;
+}
+
+static void speculative_key_press(keyrecord_t *record) {
+ if (num_speculative_keys >= SPECULATIVE_KEYS_SIZE) { // Overflow!
+ ac_dprintf("SPECULATIVE KEYS OVERFLOW: IGNORING EVENT\n");
+ return; // Don't trigger: speculative_keys is full.
+ }
+ if (speculative_keys_find(record->event.key) < num_speculative_keys) {
+ return; // Don't trigger: key is already in speculative_keys.
+ }
+
+ const uint16_t keycode = get_record_keycode(record, false);
+ if (!IS_QK_MOD_TAP(keycode)) {
+ return; // Don't trigger: not a mod-tap key.
+ }
+
+ uint8_t mods = mod_config(QK_MOD_TAP_GET_MODS(keycode));
+ if ((mods & 0x10) != 0) { // Unpack 5-bit mods to 8-bit representation.
+ mods <<= 4;
+ }
+ if ((~(get_mods() | speculative_mods) & mods) == 0) {
+ return; // Don't trigger: mods are already active.
+ }
+
+ // Don't do Speculative Hold when there are non-speculated buffered events,
+ // since that could result in sending keys out of order.
+ for (uint8_t i = waiting_buffer_tail; i != waiting_buffer_head; i = (i + 1) % WAITING_BUFFER_SIZE) {
+ if (!waiting_buffer[i].tap.speculated) {
+ return;
+ }
+ }
+
+ if (get_speculative_hold(keycode, record)) {
+ record->tap.speculated = true;
+ speculative_mods |= mods;
+ // Remember the keypos and mods associated with this key.
+ speculative_keys[num_speculative_keys] = (speculative_key_t){
+ .key = record->event.key,
+ .mods = mods,
+ };
+ ++num_speculative_keys;
+
+ ac_dprintf("Speculative Hold: ");
+ debug_speculative_keys();
+ }
+}
+
+uint8_t get_speculative_mods(void) {
+ return speculative_mods;
+}
+
+__attribute__((weak)) bool get_speculative_hold(uint16_t keycode, keyrecord_t *record) {
+ const uint8_t mods = mod_config(QK_MOD_TAP_GET_MODS(keycode));
+ return (mods & (MOD_LCTL | MOD_LSFT)) == mods;
+}
+
+void speculative_key_settled(keyrecord_t *record) {
+ if (num_speculative_keys == 0) {
+ return; // Early return when there are no active speculative keys.
+ }
+
+ uint8_t i = speculative_keys_find(record->event.key);
+
+ const uint16_t keycode = get_record_keycode(record, false);
+ if (IS_QK_MOD_TAP(keycode) && record->tap.count == 0) { // MT hold press.
+ if (i < num_speculative_keys) {
+ --num_speculative_keys;
+ const uint8_t cleared_mods = speculative_keys[i].mods;
+
+ if (num_speculative_keys) {
+ speculative_mods &= ~cleared_mods;
+ // Don't call send_keyboard_report() here; allow default
+ // handling to reapply the mod before the next report.
+
+ // Remove the ith entry from speculative_keys.
+ for (uint8_t j = i; j < num_speculative_keys; ++j) {
+ speculative_keys[j] = speculative_keys[j + 1];
+ }
+ } else {
+ speculative_mods = 0;
+ }
+
+ ac_dprintf("Speculative Hold: settled %02x, ", cleared_mods);
+ debug_speculative_keys();
+ }
+ } else { // Tap press event; cancel speculatively-held mod.
+ if (i >= num_speculative_keys) {
+ i = 0;
+ }
+
+ // Clear mods for the ith key and all keys that follow.
+ uint8_t cleared_mods = 0;
+ for (uint8_t j = i; j < num_speculative_keys; ++j) {
+ cleared_mods |= speculative_keys[j].mods;
+ }
+
+ num_speculative_keys = i; // Remove ith and following entries.
+
+ if ((prev_speculative_mods & cleared_mods) != 0) {
+# ifdef DUMMY_MOD_NEUTRALIZER_KEYCODE
+ neutralize_flashing_modifiers(get_mods() | prev_speculative_mods);
+# endif // DUMMY_MOD_NEUTRALIZER_KEYCODE
+ }
+
+ if (num_speculative_keys) {
+ speculative_mods &= ~cleared_mods;
+ } else {
+ speculative_mods = 0;
+ }
+
+ send_keyboard_report();
+ wait_ms(TAP_CODE_DELAY);
+
+ ac_dprintf("Speculative Hold: canceled %02x, ", cleared_mods);
+ debug_speculative_keys();
+ }
+}
+# endif // SPECULATIVE_HOLD
+
# if defined(CHORDAL_HOLD) || defined(FLOW_TAP_TERM)
static void registered_taps_add(keypos_t key) {
if (num_registered_taps >= REGISTERED_TAPS_SIZE) {
diff --git a/quantum/action_tapping.h b/quantum/action_tapping.h
index 8220952706..227e3330e1 100644
--- a/quantum/action_tapping.h
+++ b/quantum/action_tapping.h
@@ -46,6 +46,36 @@ bool get_permissive_hold(uint16_t keycode, keyrecord_t *record);
bool get_retro_tapping(uint16_t keycode, keyrecord_t *record);
bool get_hold_on_other_key_press(uint16_t keycode, keyrecord_t *record);
+#ifdef SPECULATIVE_HOLD
+/** Gets the currently active speculative mods. */
+uint8_t get_speculative_mods(void);
+
+/**
+ * Callback to say if a mod-tap key may be speculatively held.
+ *
+ * By default, speculative hold is enabled for mod-tap keys where the mod is
+ * Ctrl, Shift, and Ctrl+Shift for either hand.
+ *
+ * @param keycode Keycode of the mod-tap key.
+ * @param record Record associated with the mod-tap press event.
+ * @return True if the mod-tap key may be speculatively held.
+ */
+bool get_speculative_hold(uint16_t keycode, keyrecord_t *record);
+
+/**
+ * Handler to be called on press events after tap-holds are settled.
+ *
+ * This function is to be called in process_record() in action.c, that is, just
+ * after tap-hold events are settled as either tapped or held. When `record`
+ * corresponds to a speculatively-held key, the speculative mod is cleared.
+ *
+ * @param record Record associated with the mod-tap press event.
+ */
+void speculative_key_settled(keyrecord_t *record);
+#else
+# define get_speculative_mods() 0
+#endif // SPECULATIVE_HOLD
+
#ifdef CHORDAL_HOLD
/**
* Callback to say when a key chord before the tapping term may be held.
diff --git a/quantum/action_util.c b/quantum/action_util.c
index 0996ff908e..00cec24e3f 100644
--- a/quantum/action_util.c
+++ b/quantum/action_util.c
@@ -19,6 +19,7 @@ along with this program. If not, see .
#include "debug.h"
#include "action_util.h"
#include "action_layer.h"
+#include "action_tapping.h"
#include "timer.h"
#include "keycode_config.h"
#include
@@ -284,6 +285,10 @@ static uint8_t get_mods_for_report(void) {
}
#endif
+#ifdef SPECULATIVE_HOLD
+ mods |= get_speculative_mods();
+#endif
+
#ifdef KEY_OVERRIDE_ENABLE
// These need to be last to be able to properly control key overrides
mods &= ~suppressed_mods;
diff --git a/tests/tap_hold_configurations/speculative_hold/default/config.h b/tests/tap_hold_configurations/speculative_hold/default/config.h
new file mode 100644
index 0000000000..e4bcba13c1
--- /dev/null
+++ b/tests/tap_hold_configurations/speculative_hold/default/config.h
@@ -0,0 +1,23 @@
+/* Copyright 2022 Vladislav Kucheriavykh
+ * Copyright 2025 Google LLC
+ *
+ * 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 .
+ */
+
+#pragma once
+
+#include "test_common.h"
+
+#define SPECULATIVE_HOLD
+#define DUMMY_MOD_NEUTRALIZER_KEYCODE KC_F24
diff --git a/tests/tap_hold_configurations/speculative_hold/default/test.mk b/tests/tap_hold_configurations/speculative_hold/default/test.mk
new file mode 100644
index 0000000000..c03d99f686
--- /dev/null
+++ b/tests/tap_hold_configurations/speculative_hold/default/test.mk
@@ -0,0 +1,21 @@
+# Copyright 2022 Vladislav Kucheriavykh
+# Copyright 2025 Google LLC
+#
+# 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 .
+
+KEY_OVERRIDE_ENABLE = yes
+MAGIC_ENABLE = yes
+
+INTROSPECTION_KEYMAP_C = test_keymap.c
+
diff --git a/tests/tap_hold_configurations/speculative_hold/default/test_keymap.c b/tests/tap_hold_configurations/speculative_hold/default/test_keymap.c
new file mode 100644
index 0000000000..db65374618
--- /dev/null
+++ b/tests/tap_hold_configurations/speculative_hold/default/test_keymap.c
@@ -0,0 +1,20 @@
+// Copyright 2025 Google LLC
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// https://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#include "quantum.h"
+
+// Shift + Esc = Home
+const key_override_t home_esc_override = ko_make_basic(MOD_MASK_SHIFT, KC_ESC, KC_HOME);
+
+const key_override_t *key_overrides[] = {&home_esc_override};
diff --git a/tests/tap_hold_configurations/speculative_hold/default/test_tap_hold.cpp b/tests/tap_hold_configurations/speculative_hold/default/test_tap_hold.cpp
new file mode 100644
index 0000000000..c92ed5a2d0
--- /dev/null
+++ b/tests/tap_hold_configurations/speculative_hold/default/test_tap_hold.cpp
@@ -0,0 +1,794 @@
+// Copyright 2025 Google LLC
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// https://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#include
+
+#include "keyboard_report_util.hpp"
+#include "keycode.h"
+#include "test_common.hpp"
+#include "action_tapping.h"
+#include "test_fixture.hpp"
+#include "test_keymap_key.hpp"
+
+using testing::_;
+using testing::AnyNumber;
+using testing::InSequence;
+
+namespace {
+
+// Gets the unpacked 8-bit mods corresponding to a given mod-tap keycode.
+uint8_t unpack_mod_tap_mods(uint16_t keycode) {
+ const uint8_t mods5 = QK_MOD_TAP_GET_MODS(keycode);
+ return (mods5 & 0x10) != 0 ? (mods5 << 4) : mods5;
+}
+
+bool get_speculative_hold_all_keys(uint16_t keycode, keyrecord_t *record) {
+ return true; // Enable Speculative Hold for all mod-tap keys.
+}
+
+bool process_record_user_default(uint16_t keycode, keyrecord_t *record) {
+ return true;
+}
+
+// Indirection so that get_speculative_hold() and process_record_user() can be
+// replaced with other functions in the test cases below.
+std::function get_speculative_hold_fun = get_speculative_hold_all_keys;
+std::function process_record_user_fun = process_record_user_default;
+
+extern "C" bool get_speculative_hold(uint16_t keycode, keyrecord_t *record) {
+ return get_speculative_hold_fun(keycode, record);
+}
+
+extern "C" bool process_record_user(uint16_t keycode, keyrecord_t *record) {
+ return process_record_user_fun(keycode, record);
+}
+
+class SpeculativeHoldDefault : public TestFixture {
+ public:
+ void SetUp() override {
+ get_speculative_hold_fun = get_speculative_hold_all_keys;
+ process_record_user_fun = process_record_user_default;
+ }
+};
+
+TEST_F(SpeculativeHoldDefault, tap_mod_tap) {
+ TestDriver driver;
+ InSequence s;
+ static int process_record_user_calls = 0;
+ auto mod_tap_key = KeymapKey(0, 1, 0, SFT_T(KC_P));
+
+ set_keymap({mod_tap_key});
+
+ process_record_user_fun = [](uint16_t keycode, keyrecord_t *record) {
+ ++process_record_user_calls;
+ return true;
+ };
+
+ // Press mod-tap-hold key. Mod is held speculatively.
+ EXPECT_REPORT(driver, (KC_LSFT));
+ mod_tap_key.press();
+ idle_for(10);
+ VERIFY_AND_CLEAR(driver);
+ EXPECT_EQ(get_speculative_mods(), MOD_BIT_LSHIFT);
+ // Speculative mod holds and releases are made directly, bypassing regular
+ // event processing. No calls have been made yet to process_record_user().
+ EXPECT_EQ(process_record_user_calls, 0);
+
+ // Release mod-tap-hold key.
+ EXPECT_EMPTY_REPORT(driver); // Speculative mod canceled.
+ EXPECT_REPORT(driver, (KC_P));
+ EXPECT_EMPTY_REPORT(driver);
+ mod_tap_key.release();
+ run_one_scan_loop();
+ VERIFY_AND_CLEAR(driver);
+ // All mods are released.
+ EXPECT_EQ(get_speculative_mods(), 0);
+ EXPECT_EQ(get_mods(), 0);
+ // Two calls have now been made, for pressing and releasing KC_P.
+ EXPECT_EQ(process_record_user_calls, 2);
+
+ // Idle for tapping term of mod tap hold key.
+ idle_for(TAPPING_TERM - 10);
+ VERIFY_AND_CLEAR(driver);
+}
+
+TEST_F(SpeculativeHoldDefault, tap_mod_tap_neutralized) {
+ TestDriver driver;
+ InSequence s;
+ auto mod_tap_key = KeymapKey(0, 1, 0, GUI_T(KC_P));
+
+ set_keymap({mod_tap_key});
+
+ // Press mod-tap-hold key. Mod is held speculatively.
+ EXPECT_REPORT(driver, (KC_LGUI));
+ mod_tap_key.press();
+ idle_for(10);
+ VERIFY_AND_CLEAR(driver);
+
+ // Release mod-tap-hold key. Speculative mod is neutralized and canceled.
+ EXPECT_REPORT(driver, (KC_LGUI, DUMMY_MOD_NEUTRALIZER_KEYCODE));
+ EXPECT_REPORT(driver, (KC_LGUI));
+ EXPECT_EMPTY_REPORT(driver);
+ EXPECT_REPORT(driver, (KC_P));
+ EXPECT_EMPTY_REPORT(driver);
+ mod_tap_key.release();
+ run_one_scan_loop();
+ VERIFY_AND_CLEAR(driver);
+
+ // Idle for tapping term of mod tap hold key.
+ idle_for(TAPPING_TERM - 10);
+ VERIFY_AND_CLEAR(driver);
+}
+
+TEST_F(SpeculativeHoldDefault, hold_two_mod_taps) {
+ TestDriver driver;
+ InSequence s;
+ auto mod_tap_key1 = KeymapKey(0, 1, 0, LCTL_T(KC_A));
+ auto mod_tap_key2 = KeymapKey(0, 2, 0, RALT_T(KC_B));
+
+ set_keymap({mod_tap_key1, mod_tap_key2});
+
+ // Press first mod-tap key.
+ EXPECT_REPORT(driver, (KC_LCTL));
+ mod_tap_key1.press();
+ run_one_scan_loop();
+ VERIFY_AND_CLEAR(driver);
+ EXPECT_EQ(get_speculative_mods(), MOD_BIT_LCTRL);
+
+ // Press second mod-tap key.
+ EXPECT_REPORT(driver, (KC_LCTL, KC_RALT));
+ mod_tap_key2.press();
+ run_one_scan_loop();
+ VERIFY_AND_CLEAR(driver);
+ EXPECT_EQ(get_speculative_mods(), MOD_BIT_LCTRL | MOD_BIT_RALT);
+
+ EXPECT_NO_REPORT(driver);
+ idle_for(TAPPING_TERM + 1);
+ VERIFY_AND_CLEAR(driver);
+ EXPECT_EQ(get_speculative_mods(), 0);
+ EXPECT_EQ(get_mods(), MOD_BIT_LCTRL | MOD_BIT_RALT);
+
+ // Release first mod-tap key.
+ EXPECT_REPORT(driver, (KC_RALT));
+ mod_tap_key1.release();
+ run_one_scan_loop();
+ VERIFY_AND_CLEAR(driver);
+
+ // Release second mod-tap key.
+ EXPECT_EMPTY_REPORT(driver);
+ mod_tap_key2.release();
+ run_one_scan_loop();
+ VERIFY_AND_CLEAR(driver);
+}
+
+TEST_F(SpeculativeHoldDefault, two_mod_taps_same_mods) {
+ TestDriver driver;
+ InSequence s;
+ auto mod_tap_key1 = KeymapKey(0, 1, 0, GUI_T(KC_A));
+ auto mod_tap_key2 = KeymapKey(0, 2, 0, GUI_T(KC_B));
+
+ set_keymap({mod_tap_key1, mod_tap_key2});
+
+ // Press first mod-tap key.
+ EXPECT_REPORT(driver, (KC_LGUI));
+ mod_tap_key1.press();
+ run_one_scan_loop();
+ VERIFY_AND_CLEAR(driver);
+
+ // Tap second mod-tap key.
+ EXPECT_NO_REPORT(driver);
+ mod_tap_key2.press();
+ run_one_scan_loop();
+ mod_tap_key2.release();
+ run_one_scan_loop();
+ VERIFY_AND_CLEAR(driver);
+
+ // Release first mod-tap key.
+ EXPECT_REPORT(driver, (KC_LGUI, DUMMY_MOD_NEUTRALIZER_KEYCODE));
+ EXPECT_REPORT(driver, (KC_LGUI));
+ EXPECT_EMPTY_REPORT(driver);
+ EXPECT_REPORT(driver, (KC_A));
+ EXPECT_REPORT(driver, (KC_A, KC_B));
+ EXPECT_REPORT(driver, (KC_A));
+ EXPECT_EMPTY_REPORT(driver);
+ mod_tap_key1.release();
+ run_one_scan_loop();
+ VERIFY_AND_CLEAR(driver);
+}
+
+TEST_F(SpeculativeHoldDefault, respects_get_speculative_hold_callback) {
+ TestDriver driver;
+ InSequence s;
+ auto mod_tap_key1 = KeymapKey(0, 0, 0, LSFT_T(KC_A));
+ auto mod_tap_key2 = KeymapKey(0, 1, 0, LSFT_T(KC_B));
+ auto mod_tap_key3 = KeymapKey(0, 2, 0, LCTL_T(KC_C));
+ auto mod_tap_key4 = KeymapKey(0, 3, 0, LCTL_T(KC_D));
+ auto mod_tap_key5 = KeymapKey(0, 4, 0, RSFT_T(KC_E));
+ auto mod_tap_key6 = KeymapKey(0, 5, 0, RSFT_T(KC_F));
+
+ set_keymap({mod_tap_key1, mod_tap_key2, mod_tap_key3, mod_tap_key4, mod_tap_key5, mod_tap_key6});
+
+ // Enable Speculative Hold selectively for some of the keys.
+ get_speculative_hold_fun = [](uint16_t keycode, keyrecord_t *record) {
+ switch (keycode) {
+ case LSFT_T(KC_B):
+ case LCTL_T(KC_D):
+ case RSFT_T(KC_F):
+ return true;
+ }
+ return false;
+ };
+
+ for (KeymapKey *mod_tap_key : {&mod_tap_key2, &mod_tap_key4, &mod_tap_key6}) {
+ SCOPED_TRACE(std::string("mod_tap_key = ") + mod_tap_key->name);
+ const uint8_t mods = unpack_mod_tap_mods(mod_tap_key->code);
+
+ // Long press and release mod_tap_key.
+ // For these keys where Speculative Hold is enabled, then the mod should
+ // activate immediately on keydown.
+ EXPECT_REPORT(driver, (KC_LCTL + biton(mods)));
+ mod_tap_key->press();
+ run_one_scan_loop();
+ EXPECT_EQ(get_speculative_mods(), mods);
+ EXPECT_EQ(get_mods(), 0);
+ VERIFY_AND_CLEAR(driver);
+
+ EXPECT_NO_REPORT(driver);
+ idle_for(TAPPING_TERM + 1);
+ EXPECT_EQ(get_speculative_mods(), 0);
+ EXPECT_EQ(get_mods(), mods);
+ VERIFY_AND_CLEAR(driver);
+
+ EXPECT_EMPTY_REPORT(driver);
+ mod_tap_key->release();
+ idle_for(TAPPING_TERM + 1);
+ EXPECT_EQ(get_speculative_mods(), 0);
+ EXPECT_EQ(get_mods(), 0);
+ VERIFY_AND_CLEAR(driver);
+ }
+
+ for (KeymapKey *mod_tap_key : {&mod_tap_key1, &mod_tap_key3, &mod_tap_key5}) {
+ SCOPED_TRACE(std::string("mod_tap_key = ") + mod_tap_key->name);
+ const uint8_t mods = unpack_mod_tap_mods(mod_tap_key->code);
+
+ // Long press and release mod_tap_key.
+ // For these keys where Speculative Hold is disabled, the mod should
+ // activate when the key has settled after the tapping term.
+ EXPECT_NO_REPORT(driver);
+ mod_tap_key->press();
+ run_one_scan_loop();
+ EXPECT_EQ(get_speculative_mods(), 0);
+ EXPECT_EQ(get_mods(), 0);
+ VERIFY_AND_CLEAR(driver);
+
+ EXPECT_REPORT(driver, (KC_LCTL + biton(mods)));
+ idle_for(TAPPING_TERM + 1);
+ EXPECT_EQ(get_speculative_mods(), 0);
+ EXPECT_EQ(get_mods(), mods);
+ VERIFY_AND_CLEAR(driver);
+
+ EXPECT_EMPTY_REPORT(driver);
+ mod_tap_key->release();
+ idle_for(TAPPING_TERM + 1);
+ EXPECT_EQ(get_speculative_mods(), 0);
+ EXPECT_EQ(get_mods(), 0);
+ VERIFY_AND_CLEAR(driver);
+ }
+}
+
+TEST_F(SpeculativeHoldDefault, respects_magic_mod_config) {
+ TestDriver driver;
+ InSequence s;
+ auto mod_tap_key = KeymapKey(0, 1, 0, CTL_T(KC_P));
+
+ set_keymap({mod_tap_key});
+
+ keymap_config.swap_lctl_lgui = true;
+
+ // Press mod-tap-hold key.
+ EXPECT_REPORT(driver, (KC_LGUI));
+ mod_tap_key.press();
+ run_one_scan_loop();
+ VERIFY_AND_CLEAR(driver);
+
+ // Release mod-tap-hold key.
+ EXPECT_REPORT(driver, (KC_LGUI, DUMMY_MOD_NEUTRALIZER_KEYCODE));
+ EXPECT_REPORT(driver, (KC_LGUI));
+ EXPECT_EMPTY_REPORT(driver);
+ EXPECT_REPORT(driver, (KC_P));
+ EXPECT_EMPTY_REPORT(driver);
+ mod_tap_key.release();
+ idle_for(TAPPING_TERM + 1);
+ VERIFY_AND_CLEAR(driver);
+
+ keymap_config.swap_lctl_lgui = false;
+
+ // Press mod-tap-hold key.
+ EXPECT_REPORT(driver, (KC_LCTL));
+ mod_tap_key.press();
+ run_one_scan_loop();
+ VERIFY_AND_CLEAR(driver);
+
+ // Release mod-tap-hold key.
+ EXPECT_EMPTY_REPORT(driver);
+ EXPECT_REPORT(driver, (KC_P));
+ EXPECT_EMPTY_REPORT(driver);
+ mod_tap_key.release();
+ run_one_scan_loop();
+ VERIFY_AND_CLEAR(driver);
+}
+
+TEST_F(SpeculativeHoldDefault, key_overrides) {
+ TestDriver driver;
+ InSequence s;
+ auto mod_tap_key = KeymapKey(0, 1, 0, LSFT_T(KC_A));
+ auto esc_key = KeymapKey(0, 3, 0, KC_ESC);
+
+ set_keymap({mod_tap_key, esc_key});
+
+ // Press mod-tap Shift key.
+ EXPECT_REPORT(driver, (KC_LSFT));
+ mod_tap_key.press();
+ run_one_scan_loop();
+ VERIFY_AND_CLEAR(driver);
+
+ // Press Esc key.
+ EXPECT_EMPTY_REPORT(driver).Times(AnyNumber());
+ EXPECT_REPORT(driver, (KC_HOME));
+ esc_key.press();
+ idle_for(TAPPING_TERM + 1);
+ VERIFY_AND_CLEAR(driver);
+
+ // Release Esc key.
+ EXPECT_EMPTY_REPORT(driver).Times(AnyNumber());
+ EXPECT_REPORT(driver, (KC_LSFT));
+ esc_key.release();
+ run_one_scan_loop();
+ VERIFY_AND_CLEAR(driver);
+
+ // Release mod-tap Shift key.
+ EXPECT_EMPTY_REPORT(driver);
+ mod_tap_key.release();
+ run_one_scan_loop();
+ VERIFY_AND_CLEAR(driver);
+}
+
+TEST_F(SpeculativeHoldDefault, tap_regular_key_while_mod_tap_key_is_held) {
+ TestDriver driver;
+ InSequence s;
+ auto mod_tap_key = KeymapKey(0, 1, 0, SFT_T(KC_P));
+ auto regular_key = KeymapKey(0, 2, 0, KC_A);
+
+ set_keymap({mod_tap_key, regular_key});
+
+ // Press mod-tap-hold key.
+ EXPECT_REPORT(driver, (KC_LSFT));
+ mod_tap_key.press();
+ run_one_scan_loop();
+ VERIFY_AND_CLEAR(driver);
+
+ // Tap regular key.
+ EXPECT_NO_REPORT(driver);
+ regular_key.press();
+ run_one_scan_loop();
+ regular_key.release();
+ run_one_scan_loop();
+ VERIFY_AND_CLEAR(driver);
+
+ // Release mod-tap-hold key.
+ EXPECT_EMPTY_REPORT(driver);
+ EXPECT_REPORT(driver, (KC_P));
+ EXPECT_REPORT(driver, (KC_P, KC_A));
+ EXPECT_REPORT(driver, (KC_P));
+ EXPECT_EMPTY_REPORT(driver);
+ mod_tap_key.release();
+ run_one_scan_loop();
+ VERIFY_AND_CLEAR(driver);
+
+ // Idle for tapping term of mod tap hold key.
+ idle_for(TAPPING_TERM - 3);
+ VERIFY_AND_CLEAR(driver);
+}
+
+TEST_F(SpeculativeHoldDefault, tap_a_mod_tap_key_while_another_mod_tap_key_is_held) {
+ TestDriver driver;
+ InSequence s;
+ auto first_mod_tap_key = KeymapKey(0, 1, 0, SFT_T(KC_P));
+ auto second_mod_tap_key = KeymapKey(0, 2, 0, RSFT_T(KC_A));
+
+ set_keymap({first_mod_tap_key, second_mod_tap_key});
+
+ // Press first mod-tap-hold key.
+ EXPECT_REPORT(driver, (KC_LSFT));
+ first_mod_tap_key.press();
+ run_one_scan_loop();
+ VERIFY_AND_CLEAR(driver);
+
+ // Press second tap-hold key.
+ EXPECT_REPORT(driver, (KC_LSFT, KC_RSFT));
+ second_mod_tap_key.press();
+ run_one_scan_loop();
+ VERIFY_AND_CLEAR(driver);
+
+ // Release second tap-hold key.
+ EXPECT_NO_REPORT(driver);
+ second_mod_tap_key.release();
+ run_one_scan_loop();
+ VERIFY_AND_CLEAR(driver);
+
+ // Release first mod-tap-hold key.
+ EXPECT_EMPTY_REPORT(driver);
+ EXPECT_REPORT(driver, (KC_P));
+ EXPECT_REPORT(driver, (KC_P, KC_A));
+ EXPECT_REPORT(driver, (KC_P));
+ EXPECT_EMPTY_REPORT(driver);
+ first_mod_tap_key.release();
+ run_one_scan_loop();
+ VERIFY_AND_CLEAR(driver);
+}
+
+TEST_F(SpeculativeHoldDefault, tap_mod_tap_key_two_times) {
+ TestDriver driver;
+ InSequence s;
+ auto mod_tap_key = KeymapKey(0, 1, 0, SFT_T(KC_P));
+
+ set_keymap({mod_tap_key});
+
+ // Press mod-tap-hold key.
+ EXPECT_REPORT(driver, (KC_LSFT));
+ mod_tap_key.press();
+ run_one_scan_loop();
+ VERIFY_AND_CLEAR(driver);
+
+ // Release mod-tap-hold key.
+ EXPECT_EMPTY_REPORT(driver);
+ EXPECT_REPORT(driver, (KC_P));
+ EXPECT_EMPTY_REPORT(driver);
+ mod_tap_key.release();
+ run_one_scan_loop();
+ VERIFY_AND_CLEAR(driver);
+
+ // Press mod-tap-hold key again.
+ EXPECT_REPORT(driver, (KC_P));
+ mod_tap_key.press();
+ idle_for(TAPPING_TERM);
+ VERIFY_AND_CLEAR(driver);
+
+ // Release mod-tap-hold key.
+ EXPECT_EMPTY_REPORT(driver);
+ mod_tap_key.release();
+ run_one_scan_loop();
+ VERIFY_AND_CLEAR(driver);
+}
+
+TEST_F(SpeculativeHoldDefault, tap_mod_tap_key_twice_and_hold_on_second_time) {
+ TestDriver driver;
+ InSequence s;
+ auto mod_tap_key = KeymapKey(0, 1, 0, SFT_T(KC_P));
+
+ set_keymap({mod_tap_key});
+
+ // Press mod-tap-hold key.
+ EXPECT_REPORT(driver, (KC_LSFT));
+ mod_tap_key.press();
+ run_one_scan_loop();
+ VERIFY_AND_CLEAR(driver);
+
+ // Release mod-tap-hold key.
+ EXPECT_EMPTY_REPORT(driver);
+ EXPECT_REPORT(driver, (KC_P));
+ EXPECT_EMPTY_REPORT(driver);
+ mod_tap_key.release();
+ run_one_scan_loop();
+ VERIFY_AND_CLEAR(driver);
+
+ // Press mod-tap-hold key again.
+ EXPECT_REPORT(driver, (KC_P));
+ mod_tap_key.press();
+ idle_for(TAPPING_TERM);
+ VERIFY_AND_CLEAR(driver);
+
+ // Release mod-tap-hold key.
+ EXPECT_EMPTY_REPORT(driver);
+ mod_tap_key.release();
+ run_one_scan_loop();
+ VERIFY_AND_CLEAR(driver);
+}
+
+TEST_F(SpeculativeHoldDefault, tap_and_hold_mod_tap_key) {
+ TestDriver driver;
+ InSequence s;
+ static int process_record_user_calls = 0;
+ auto mod_tap_key = KeymapKey(0, 1, 0, SFT_T(KC_P));
+
+ set_keymap({mod_tap_key});
+
+ process_record_user_fun = [](uint16_t keycode, keyrecord_t *record) {
+ ++process_record_user_calls;
+ return true;
+ };
+
+ // Press mod-tap-hold key.
+ EXPECT_REPORT(driver, (KC_LSFT));
+ mod_tap_key.press();
+ idle_for(TAPPING_TERM - 1);
+ EXPECT_EQ(get_speculative_mods(), MOD_BIT_LSHIFT);
+ EXPECT_EQ(get_mods(), 0);
+ // Speculative mod holds and releases are made directly, bypassing regular
+ // event processing. No calls have been made yet to process_record_user().
+ EXPECT_EQ(process_record_user_calls, 0);
+ idle_for(2);
+ // Now that the key has settled, one call has been made for the hold event.
+ EXPECT_EQ(process_record_user_calls, 1);
+ EXPECT_EQ(get_speculative_mods(), 0);
+ EXPECT_EQ(get_mods(), MOD_BIT_LSHIFT);
+ VERIFY_AND_CLEAR(driver);
+
+ // Release mod-tap-hold key.
+ EXPECT_EMPTY_REPORT(driver);
+ mod_tap_key.release();
+ run_one_scan_loop();
+ EXPECT_EQ(process_record_user_calls, 2);
+ VERIFY_AND_CLEAR(driver);
+}
+
+// Test with layer tap and speculative mod tap keys on the same layer,
+// rolling from LT to MT key:
+// "LT down, MT down, (wait out tapping term), LT up, MT up."
+TEST_F(SpeculativeHoldDefault, lt_mt_same_layer_roll) {
+ TestDriver driver;
+ InSequence s;
+ auto layer_tap_key = KeymapKey(0, 0, 0, LT(1, KC_A));
+ auto mod_tap_key = KeymapKey(0, 1, 0, SFT_T(KC_B));
+ auto regular_key = KeymapKey(1, 1, 0, KC_C);
+
+ set_keymap({layer_tap_key, mod_tap_key, regular_key});
+
+ // Press layer tap key.
+ EXPECT_NO_REPORT(driver);
+ layer_tap_key.press();
+ run_one_scan_loop();
+ VERIFY_AND_CLEAR(driver);
+
+ // Press mod-tap key, after flow tap term but within tapping term. The
+ // speculative mod activates.
+ EXPECT_REPORT(driver, (KC_LSFT));
+ mod_tap_key.press();
+ run_one_scan_loop();
+ VERIFY_AND_CLEAR(driver);
+
+ // Wait for the layer tap key to settle.
+ EXPECT_EMPTY_REPORT(driver);
+ EXPECT_REPORT(driver, (KC_C));
+ idle_for(TAPPING_TERM);
+ VERIFY_AND_CLEAR(driver);
+
+ // Release keys.
+ EXPECT_EMPTY_REPORT(driver);
+ layer_tap_key.release();
+ run_one_scan_loop();
+ mod_tap_key.release();
+ run_one_scan_loop();
+ VERIFY_AND_CLEAR(driver);
+ // All mods are released.
+ EXPECT_EQ(get_mods() | get_speculative_mods(), 0);
+}
+
+// Test with layer tap and speculative mod tap keys on the same layer, trying a
+// nested press:
+// "LT down, MT down, (wait out tapping term), MT up, LT up."
+TEST_F(SpeculativeHoldDefault, lt_mt_same_layer_nested_press) {
+ TestDriver driver;
+ InSequence s;
+ auto layer_tap_key = KeymapKey(0, 0, 0, LT(1, KC_A));
+ auto mod_tap_key = KeymapKey(0, 1, 0, SFT_T(KC_B));
+ auto regular_key = KeymapKey(1, 1, 0, KC_C);
+
+ set_keymap({layer_tap_key, mod_tap_key, regular_key});
+
+ EXPECT_NO_REPORT(driver);
+ layer_tap_key.press();
+ run_one_scan_loop();
+ VERIFY_AND_CLEAR(driver);
+
+ EXPECT_REPORT(driver, (KC_LSFT));
+ mod_tap_key.press();
+ run_one_scan_loop();
+ VERIFY_AND_CLEAR(driver);
+
+ EXPECT_EMPTY_REPORT(driver);
+ EXPECT_REPORT(driver, (KC_C));
+ run_one_scan_loop();
+ idle_for(TAPPING_TERM);
+ VERIFY_AND_CLEAR(driver);
+
+ // Release keys: MT first, LT second.
+ EXPECT_EMPTY_REPORT(driver);
+ mod_tap_key.release();
+ run_one_scan_loop();
+ layer_tap_key.release();
+ run_one_scan_loop();
+ VERIFY_AND_CLEAR(driver);
+ // All mods are released.
+ EXPECT_EQ(get_mods() | get_speculative_mods(), 0);
+}
+
+// Test with layer tap and speculative mod tap keys on the same layer, trying a
+// nested press with the MT first:
+// "MT down, LT down, (wait out tapping term), LT up, MT up."
+TEST_F(SpeculativeHoldDefault, mt_lt_same_layer_nested_press) {
+ TestDriver driver;
+ InSequence s;
+ auto layer_tap_key = KeymapKey(0, 0, 0, LT(1, KC_A));
+ auto mod_tap_key = KeymapKey(0, 1, 0, SFT_T(KC_B));
+ auto regular_key = KeymapKey(1, 1, 0, KC_C);
+
+ set_keymap({layer_tap_key, mod_tap_key, regular_key});
+
+ EXPECT_REPORT(driver, (KC_LSFT));
+ mod_tap_key.press();
+ run_one_scan_loop();
+
+ EXPECT_NO_REPORT(driver);
+ layer_tap_key.press();
+ idle_for(TAPPING_TERM + 1);
+ VERIFY_AND_CLEAR(driver);
+
+ EXPECT_EMPTY_REPORT(driver);
+ layer_tap_key.release();
+ run_one_scan_loop();
+ mod_tap_key.release();
+ run_one_scan_loop();
+ VERIFY_AND_CLEAR(driver);
+ // All mods are released.
+ EXPECT_EQ(get_mods() | get_speculative_mods(), 0);
+}
+
+// Test with a speculative mod tap key reached by a layer tap key, rolling from
+// LT to MT key:
+// "LT down, MT down, (wait out tapping term), LT up, MT up."
+TEST_F(SpeculativeHoldDefault, lt_mt_different_layer_roll) {
+ TestDriver driver;
+ InSequence s;
+ auto layer_tap_key = KeymapKey(0, 0, 0, LT(1, KC_A));
+ auto regular_key = KeymapKey(0, 1, 0, KC_B);
+ auto placeholder_key = KeymapKey(1, 0, 0, KC_NO);
+ auto mod_tap_key = KeymapKey(1, 1, 0, SFT_T(KC_C));
+
+ set_keymap({layer_tap_key, regular_key, placeholder_key, mod_tap_key});
+
+ // Press layer tap key.
+ EXPECT_NO_REPORT(driver);
+ layer_tap_key.press();
+ run_one_scan_loop();
+ // Press mod tap key.
+ mod_tap_key.press();
+ idle_for(TAPPING_TERM);
+ VERIFY_AND_CLEAR(driver);
+
+ // Release keys.
+ EXPECT_REPORT(driver, (KC_LSFT));
+ layer_tap_key.release();
+ idle_for(TAPPING_TERM);
+ VERIFY_AND_CLEAR(driver);
+
+ EXPECT_EMPTY_REPORT(driver);
+ mod_tap_key.release();
+ run_one_scan_loop();
+ VERIFY_AND_CLEAR(driver);
+ // All mods are released.
+ EXPECT_EQ(get_mods() | get_speculative_mods(), 0);
+}
+
+// Test with a speculative mod tap key reached by a layer tap key, slowly
+// rolling from LT to MT key:
+// "LT down, (wait), MT down, (wait), LT up, MT up."
+TEST_F(SpeculativeHoldDefault, lt_mt_different_layer_slow_roll) {
+ TestDriver driver;
+ InSequence s;
+ auto layer_tap_key = KeymapKey(0, 0, 0, LT(1, KC_A));
+ auto regular_key = KeymapKey(0, 1, 0, KC_B);
+ auto placeholder_key = KeymapKey(1, 0, 0, KC_NO);
+ auto mod_tap_key = KeymapKey(1, 1, 0, SFT_T(KC_C));
+
+ set_keymap({layer_tap_key, regular_key, placeholder_key, mod_tap_key});
+
+ EXPECT_NO_REPORT(driver);
+ layer_tap_key.press();
+ idle_for(TAPPING_TERM + 1);
+
+ EXPECT_REPORT(driver, (KC_LSFT));
+ mod_tap_key.press();
+ run_one_scan_loop();
+ VERIFY_AND_CLEAR(driver);
+
+ EXPECT_EMPTY_REPORT(driver);
+ EXPECT_REPORT(driver, (KC_B));
+ EXPECT_EMPTY_REPORT(driver);
+ layer_tap_key.release();
+ run_one_scan_loop();
+ mod_tap_key.release();
+ run_one_scan_loop();
+ VERIFY_AND_CLEAR(driver);
+ // All mods are released.
+ EXPECT_EQ(get_mods() | get_speculative_mods(), 0);
+}
+
+// Test with a speculative mod tap key reached by a layer tap key, trying a
+// nested press:
+// "LT down, MT down, (wait out tapping term), MT up, LT up."
+TEST_F(SpeculativeHoldDefault, lt_mt_different_layer_nested_press) {
+ TestDriver driver;
+ InSequence s;
+ auto layer_tap_key = KeymapKey(0, 0, 0, LT(1, KC_A));
+ auto regular_key = KeymapKey(0, 1, 0, KC_B);
+ auto placeholder_key = KeymapKey(1, 0, 0, KC_NO);
+ auto mod_tap_key = KeymapKey(1, 1, 0, SFT_T(KC_C));
+
+ set_keymap({layer_tap_key, regular_key, placeholder_key, mod_tap_key});
+
+ EXPECT_NO_REPORT(driver);
+ layer_tap_key.press();
+ run_one_scan_loop();
+ mod_tap_key.press();
+ idle_for(TAPPING_TERM);
+ VERIFY_AND_CLEAR(driver);
+
+ // Release keys.
+ EXPECT_REPORT(driver, (KC_LSFT));
+ EXPECT_EMPTY_REPORT(driver);
+ mod_tap_key.release();
+ run_one_scan_loop();
+ layer_tap_key.release();
+ run_one_scan_loop();
+ VERIFY_AND_CLEAR(driver);
+ // All mods are released.
+ EXPECT_EQ(get_mods() | get_speculative_mods(), 0);
+}
+
+// Test with a speculative mod tap key reached by a layer tap key, trying a
+// slow nested press:
+// "LT down, (wait), MT down, MT up, LT up."
+TEST_F(SpeculativeHoldDefault, lt_mt_different_layer_slow_nested_press) {
+ TestDriver driver;
+ InSequence s;
+ auto layer_tap_key = KeymapKey(0, 0, 0, LT(1, KC_A));
+ auto regular_key = KeymapKey(0, 1, 0, KC_B);
+ auto placeholder_key = KeymapKey(1, 0, 0, KC_NO);
+ auto mod_tap_key = KeymapKey(1, 1, 0, SFT_T(KC_C));
+
+ set_keymap({layer_tap_key, regular_key, placeholder_key, mod_tap_key});
+
+ EXPECT_NO_REPORT(driver);
+ layer_tap_key.press();
+ idle_for(TAPPING_TERM + 1);
+
+ EXPECT_REPORT(driver, (KC_LSFT));
+ mod_tap_key.press();
+ run_one_scan_loop();
+ VERIFY_AND_CLEAR(driver);
+
+ EXPECT_EMPTY_REPORT(driver);
+ EXPECT_REPORT(driver, (KC_C));
+ EXPECT_EMPTY_REPORT(driver);
+ mod_tap_key.release();
+ run_one_scan_loop();
+ layer_tap_key.release();
+ run_one_scan_loop();
+ VERIFY_AND_CLEAR(driver);
+ // All mods are released.
+ EXPECT_EQ(get_mods() | get_speculative_mods(), 0);
+}
+
+} // namespace
diff --git a/tests/tap_hold_configurations/speculative_hold/flow_tap/config.h b/tests/tap_hold_configurations/speculative_hold/flow_tap/config.h
new file mode 100644
index 0000000000..6988484226
--- /dev/null
+++ b/tests/tap_hold_configurations/speculative_hold/flow_tap/config.h
@@ -0,0 +1,25 @@
+/* Copyright 2022 Vladislav Kucheriavykh
+ * Copyright 2025 Google LLC
+ *
+ * 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 .
+ */
+
+#pragma once
+
+#include "test_common.h"
+
+#define SPECULATIVE_HOLD
+#define FLOW_TAP_TERM 150
+#define PERMISSIVE_HOLD
+#define DUMMY_MOD_NEUTRALIZER_KEYCODE KC_F24
diff --git a/tests/tap_hold_configurations/speculative_hold/flow_tap/test.mk b/tests/tap_hold_configurations/speculative_hold/flow_tap/test.mk
new file mode 100644
index 0000000000..ad8675da9d
--- /dev/null
+++ b/tests/tap_hold_configurations/speculative_hold/flow_tap/test.mk
@@ -0,0 +1,15 @@
+# Copyright 2022 Vladislav Kucheriavykh
+#
+# 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 .
+
diff --git a/tests/tap_hold_configurations/speculative_hold/flow_tap/test_tap_hold.cpp b/tests/tap_hold_configurations/speculative_hold/flow_tap/test_tap_hold.cpp
new file mode 100644
index 0000000000..0433a2548d
--- /dev/null
+++ b/tests/tap_hold_configurations/speculative_hold/flow_tap/test_tap_hold.cpp
@@ -0,0 +1,1114 @@
+// Copyright 2025 Google LLC
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// https://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#include "keyboard_report_util.hpp"
+#include "keycode.h"
+#include "test_common.hpp"
+#include "action_tapping.h"
+#include "test_fixture.hpp"
+#include "test_keymap_key.hpp"
+
+using testing::_;
+using testing::AnyNumber;
+using testing::InSequence;
+
+extern "C" bool get_speculative_hold(uint16_t keycode, keyrecord_t *record) {
+ return true;
+}
+
+class SpeculativeHoldFlowTapTest : public TestFixture {};
+
+TEST_F(SpeculativeHoldFlowTapTest, tap_mod_tap) {
+ TestDriver driver;
+ InSequence s;
+ auto mod_tap_key = KeymapKey(0, 1, 0, SFT_T(KC_P));
+
+ set_keymap({mod_tap_key});
+
+ // Press mod-tap-hold key. Mod is held speculatively.
+ EXPECT_REPORT(driver, (KC_LSFT));
+ mod_tap_key.press();
+ idle_for(10);
+ VERIFY_AND_CLEAR(driver);
+ EXPECT_EQ(get_speculative_mods(), MOD_BIT_LSHIFT);
+
+ // Release mod-tap-hold key.
+ EXPECT_EMPTY_REPORT(driver); // Speculative mod canceled.
+ EXPECT_REPORT(driver, (KC_P));
+ EXPECT_EMPTY_REPORT(driver);
+ mod_tap_key.release();
+ run_one_scan_loop();
+ VERIFY_AND_CLEAR(driver);
+ // All mods are released.
+ EXPECT_EQ(get_speculative_mods(), 0);
+ EXPECT_EQ(get_mods(), 0);
+
+ // Idle for tapping term of mod tap hold key.
+ idle_for(TAPPING_TERM - 10);
+ VERIFY_AND_CLEAR(driver);
+}
+
+TEST_F(SpeculativeHoldFlowTapTest, hold_two_mod_taps) {
+ TestDriver driver;
+ InSequence s;
+ auto mod_tap_key1 = KeymapKey(0, 1, 0, LCTL_T(KC_A));
+ auto mod_tap_key2 = KeymapKey(0, 2, 0, RALT_T(KC_B));
+
+ set_keymap({mod_tap_key1, mod_tap_key2});
+
+ // Press first mod-tap key.
+ EXPECT_REPORT(driver, (KC_LCTL));
+ mod_tap_key1.press();
+ idle_for(FLOW_TAP_TERM + 1);
+ VERIFY_AND_CLEAR(driver);
+ EXPECT_EQ(get_speculative_mods(), MOD_BIT_LCTRL);
+
+ // Press second mod-tap key.
+ EXPECT_REPORT(driver, (KC_LCTL, KC_RALT));
+ mod_tap_key2.press();
+ run_one_scan_loop();
+ VERIFY_AND_CLEAR(driver);
+ EXPECT_EQ(get_speculative_mods(), MOD_BIT_LCTRL | MOD_BIT_RALT);
+
+ EXPECT_NO_REPORT(driver);
+ idle_for(TAPPING_TERM + 1);
+ VERIFY_AND_CLEAR(driver);
+ EXPECT_EQ(get_speculative_mods(), 0);
+ EXPECT_EQ(get_mods(), MOD_BIT_LCTRL | MOD_BIT_RALT);
+
+ // Release first mod-tap key.
+ EXPECT_REPORT(driver, (KC_RALT));
+ mod_tap_key1.release();
+ run_one_scan_loop();
+ VERIFY_AND_CLEAR(driver);
+
+ // Release second mod-tap key.
+ EXPECT_EMPTY_REPORT(driver);
+ mod_tap_key2.release();
+ run_one_scan_loop();
+ VERIFY_AND_CLEAR(driver);
+}
+
+TEST_F(SpeculativeHoldFlowTapTest, two_mod_taps_same_mods) {
+ TestDriver driver;
+ InSequence s;
+ auto mod_tap_key1 = KeymapKey(0, 1, 0, GUI_T(KC_A));
+ auto mod_tap_key2 = KeymapKey(0, 2, 0, GUI_T(KC_B));
+
+ set_keymap({mod_tap_key1, mod_tap_key2});
+
+ // Press first mod-tap key.
+ EXPECT_REPORT(driver, (KC_LGUI));
+ mod_tap_key1.press();
+ idle_for(FLOW_TAP_TERM + 1);
+ VERIFY_AND_CLEAR(driver);
+
+ // Tap second mod-tap key.
+ EXPECT_NO_REPORT(driver);
+ mod_tap_key2.press();
+ run_one_scan_loop();
+ VERIFY_AND_CLEAR(driver);
+
+ EXPECT_REPORT(driver, (KC_LGUI, KC_B));
+ EXPECT_REPORT(driver, (KC_LGUI));
+ mod_tap_key2.release();
+ run_one_scan_loop();
+ VERIFY_AND_CLEAR(driver);
+
+ // Release first mod-tap key.
+ EXPECT_EMPTY_REPORT(driver);
+ mod_tap_key1.release();
+ run_one_scan_loop();
+ VERIFY_AND_CLEAR(driver);
+ // All mods are released.
+ EXPECT_EQ(get_mods() | get_speculative_mods(), 0);
+}
+
+// Test an input of quick distinct taps. All should be settled as tapped.
+TEST_F(SpeculativeHoldFlowTapTest, distinct_taps) {
+ TestDriver driver;
+ InSequence s;
+ auto regular_key = KeymapKey(0, 0, 0, KC_A);
+ auto mod_tap_key1 = KeymapKey(0, 1, 0, SFT_T(KC_B));
+ auto mod_tap_key2 = KeymapKey(0, 2, 0, CTL_T(KC_C));
+ auto mod_tap_key3 = KeymapKey(0, 3, 0, ALT_T(KC_D));
+
+ set_keymap({regular_key, mod_tap_key1, mod_tap_key2, mod_tap_key3});
+
+ // Tap regular key.
+ EXPECT_REPORT(driver, (KC_A));
+ EXPECT_EMPTY_REPORT(driver);
+ tap_key(regular_key, FLOW_TAP_TERM + 1);
+ VERIFY_AND_CLEAR(driver);
+
+ // Tap mod-tap 1.
+ EXPECT_REPORT(driver, (KC_B));
+ mod_tap_key1.press();
+ run_one_scan_loop();
+ VERIFY_AND_CLEAR(driver);
+
+ EXPECT_EMPTY_REPORT(driver);
+ idle_for(FLOW_TAP_TERM + 1);
+ mod_tap_key1.release();
+ run_one_scan_loop();
+ VERIFY_AND_CLEAR(driver);
+
+ // Tap mod-tap 2.
+ EXPECT_REPORT(driver, (KC_C));
+ mod_tap_key2.press();
+ run_one_scan_loop();
+ VERIFY_AND_CLEAR(driver);
+
+ EXPECT_EMPTY_REPORT(driver);
+ idle_for(FLOW_TAP_TERM + 1);
+ mod_tap_key2.release();
+ run_one_scan_loop();
+ VERIFY_AND_CLEAR(driver);
+
+ // Tap mod-tap 3.
+ EXPECT_REPORT(driver, (KC_D));
+ mod_tap_key3.press();
+ run_one_scan_loop();
+ VERIFY_AND_CLEAR(driver);
+
+ EXPECT_EMPTY_REPORT(driver);
+ idle_for(FLOW_TAP_TERM + 1);
+ mod_tap_key3.release();
+ idle_for(FLOW_TAP_TERM + 1); // Pause between taps.
+ VERIFY_AND_CLEAR(driver);
+
+ // Tap mod-tap 1.
+ EXPECT_REPORT(driver, (KC_LSFT));
+ mod_tap_key1.press();
+ run_one_scan_loop();
+ VERIFY_AND_CLEAR(driver);
+
+ EXPECT_EMPTY_REPORT(driver);
+ EXPECT_REPORT(driver, (KC_B));
+ EXPECT_EMPTY_REPORT(driver);
+ idle_for(FLOW_TAP_TERM + 1);
+ mod_tap_key1.release();
+ run_one_scan_loop();
+ VERIFY_AND_CLEAR(driver);
+
+ // Tap mod-tap 2.
+ EXPECT_REPORT(driver, (KC_C));
+ mod_tap_key2.press();
+ run_one_scan_loop();
+ VERIFY_AND_CLEAR(driver);
+
+ EXPECT_EMPTY_REPORT(driver);
+ idle_for(TAPPING_TERM + 1);
+ mod_tap_key2.release();
+ idle_for(FLOW_TAP_TERM + 1);
+ VERIFY_AND_CLEAR(driver);
+ // All mods are released.
+ EXPECT_EQ(get_mods() | get_speculative_mods(), 0);
+}
+
+// By default, Flow Tap is disabled when mods other than Shift and AltGr are on.
+TEST_F(SpeculativeHoldFlowTapTest, hotkey_taps) {
+ TestDriver driver;
+ InSequence s;
+ auto ctrl_key = KeymapKey(0, 0, 0, KC_LCTL);
+ auto shft_key = KeymapKey(0, 1, 0, KC_LSFT);
+ auto alt_key = KeymapKey(0, 2, 0, KC_LALT);
+ auto gui_key = KeymapKey(0, 3, 0, KC_LGUI);
+ auto regular_key = KeymapKey(0, 4, 0, KC_A);
+ auto mod_tap_key = KeymapKey(0, 5, 0, RCTL_T(KC_B));
+
+ set_keymap({ctrl_key, shft_key, alt_key, gui_key, regular_key, mod_tap_key});
+
+ for (KeymapKey *mod_key : {&ctrl_key, &alt_key, &gui_key}) {
+ // Hold mod key.
+ EXPECT_REPORT(driver, (mod_key->code));
+ mod_key->press();
+ run_one_scan_loop();
+
+ // Tap regular key.
+ EXPECT_REPORT(driver, (mod_key->code, KC_A));
+ regular_key.press();
+ run_one_scan_loop();
+ VERIFY_AND_CLEAR(driver);
+
+ EXPECT_REPORT(driver, (mod_key->code));
+ regular_key.release();
+ run_one_scan_loop();
+ VERIFY_AND_CLEAR(driver);
+
+ // Press mod-tap, where Flow Tap is disabled due to the held mod.
+ EXPECT_REPORT(driver, (mod_key->code, KC_RCTL));
+ mod_tap_key.press();
+ idle_for(TAPPING_TERM + 1);
+ VERIFY_AND_CLEAR(driver);
+
+ // Release mod-tap.
+ EXPECT_REPORT(driver, (mod_key->code));
+ mod_tap_key.release();
+ run_one_scan_loop();
+
+ // Release mod key.
+ EXPECT_EMPTY_REPORT(driver);
+ mod_key->release();
+ run_one_scan_loop();
+ VERIFY_AND_CLEAR(driver);
+ }
+
+ // Hold Shift key.
+ EXPECT_REPORT(driver, (KC_LSFT));
+ shft_key.press();
+ run_one_scan_loop();
+
+ // Tap regular key.
+ EXPECT_REPORT(driver, (KC_LSFT, KC_A));
+ regular_key.press();
+ run_one_scan_loop();
+ VERIFY_AND_CLEAR(driver);
+
+ EXPECT_REPORT(driver, (KC_LSFT));
+ regular_key.release();
+ run_one_scan_loop();
+ VERIFY_AND_CLEAR(driver);
+
+ // Press mod-tap, where Flow Tap applies to settle as tapped.
+ EXPECT_REPORT(driver, (KC_LSFT, KC_B));
+ mod_tap_key.press();
+ idle_for(TAPPING_TERM + 1);
+ VERIFY_AND_CLEAR(driver);
+
+ // Release mod-tap.
+ EXPECT_REPORT(driver, (KC_LSFT));
+ mod_tap_key.release();
+ run_one_scan_loop();
+
+ // Release Shift key.
+ EXPECT_EMPTY_REPORT(driver);
+ shft_key.release();
+ run_one_scan_loop();
+ VERIFY_AND_CLEAR(driver);
+ // All mods are released.
+ EXPECT_EQ(get_mods() | get_speculative_mods(), 0);
+}
+
+// Test input with two mod-taps in a rolled press quickly after a regular key.
+TEST_F(SpeculativeHoldFlowTapTest, rolled_press) {
+ TestDriver driver;
+ InSequence s;
+ auto regular_key = KeymapKey(0, 0, 0, KC_A);
+ auto mod_tap_key1 = KeymapKey(0, 1, 0, SFT_T(KC_B));
+ auto mod_tap_key2 = KeymapKey(0, 2, 0, CTL_T(KC_C));
+
+ set_keymap({regular_key, mod_tap_key1, mod_tap_key2});
+
+ // Tap regular key.
+ EXPECT_REPORT(driver, (KC_A));
+ EXPECT_EMPTY_REPORT(driver);
+ tap_key(regular_key);
+ VERIFY_AND_CLEAR(driver);
+
+ // Press mod-tap key 1 quickly after regular key. The mod-tap should settle
+ // immediately as tapped, sending `KC_B`.
+ EXPECT_REPORT(driver, (KC_B));
+ mod_tap_key1.press();
+ run_one_scan_loop();
+ VERIFY_AND_CLEAR(driver);
+
+ // Press mod-tap key 2 quickly.
+ EXPECT_REPORT(driver, (KC_B, KC_C));
+ mod_tap_key2.press();
+ run_one_scan_loop();
+ VERIFY_AND_CLEAR(driver);
+
+ // Hold for longer than the tapping term.
+ EXPECT_NO_REPORT(driver);
+ idle_for(TAPPING_TERM + 1);
+ VERIFY_AND_CLEAR(driver);
+
+ // Release mod-tap keys.
+ EXPECT_REPORT(driver, (KC_C));
+ EXPECT_EMPTY_REPORT(driver);
+ mod_tap_key1.release();
+ run_one_scan_loop();
+ mod_tap_key2.release();
+ run_one_scan_loop();
+ VERIFY_AND_CLEAR(driver);
+ // All mods are released.
+ EXPECT_EQ(get_mods() | get_speculative_mods(), 0);
+}
+
+TEST_F(SpeculativeHoldFlowTapTest, long_flow_tap_settled_as_held) {
+ TestDriver driver;
+ InSequence s;
+ auto regular_key = KeymapKey(0, 0, 0, KC_A);
+ auto mod_tap_key = KeymapKey(0, 1, 0, SFT_T(KC_B));
+
+ set_keymap({regular_key, mod_tap_key});
+
+ // Tap regular key.
+ EXPECT_REPORT(driver, (KC_A));
+ EXPECT_EMPTY_REPORT(driver);
+ tap_key(regular_key);
+ VERIFY_AND_CLEAR(driver);
+
+ EXPECT_NO_REPORT(driver);
+ idle_for(FLOW_TAP_TERM + 1);
+ VERIFY_AND_CLEAR(driver);
+
+ // Press mod-tap key.
+ EXPECT_REPORT(driver, (KC_LSFT));
+ mod_tap_key.press();
+ run_one_scan_loop();
+ VERIFY_AND_CLEAR(driver);
+
+ // Hold for the tapping term.
+ EXPECT_NO_REPORT(driver);
+ idle_for(TAPPING_TERM);
+ VERIFY_AND_CLEAR(driver);
+
+ // Release mod-tap key.
+ EXPECT_EMPTY_REPORT(driver);
+ mod_tap_key.release();
+ run_one_scan_loop();
+ VERIFY_AND_CLEAR(driver);
+ // All mods are released.
+ EXPECT_EQ(get_mods() | get_speculative_mods(), 0);
+}
+
+TEST_F(SpeculativeHoldFlowTapTest, holding_multiple_mod_taps) {
+ TestDriver driver;
+ InSequence s;
+ auto regular_key = KeymapKey(0, 0, 0, KC_A);
+ auto mod_tap_key1 = KeymapKey(0, 1, 0, SFT_T(KC_B));
+ auto mod_tap_key2 = KeymapKey(0, 2, 0, CTL_T(KC_C));
+
+ set_keymap({regular_key, mod_tap_key1, mod_tap_key2});
+
+ // Tap regular key.
+ EXPECT_REPORT(driver, (KC_A));
+ EXPECT_EMPTY_REPORT(driver);
+ tap_key(regular_key);
+ VERIFY_AND_CLEAR(driver);
+
+ EXPECT_NO_REPORT(driver);
+ idle_for(FLOW_TAP_TERM + 1);
+ VERIFY_AND_CLEAR(driver);
+
+ // Press mod-tap keys.
+ EXPECT_REPORT(driver, (KC_LSFT));
+ mod_tap_key1.press();
+ run_one_scan_loop();
+ VERIFY_AND_CLEAR(driver);
+
+ EXPECT_REPORT(driver, (KC_LSFT, KC_LCTL));
+ mod_tap_key2.press();
+ idle_for(TAPPING_TERM - 5); // Hold almost until tapping term.
+ VERIFY_AND_CLEAR(driver);
+
+ // Press regular key.
+ EXPECT_REPORT(driver, (KC_LSFT, KC_LCTL, KC_A));
+ regular_key.press();
+ idle_for(10);
+ VERIFY_AND_CLEAR(driver);
+
+ // Release keys.
+ EXPECT_REPORT(driver, (KC_LSFT, KC_LCTL));
+ EXPECT_REPORT(driver, (KC_LCTL));
+ EXPECT_EMPTY_REPORT(driver);
+ regular_key.release();
+ run_one_scan_loop();
+ mod_tap_key1.release();
+ run_one_scan_loop();
+ mod_tap_key2.release();
+ run_one_scan_loop();
+ VERIFY_AND_CLEAR(driver);
+ // All mods are released.
+ EXPECT_EQ(get_mods() | get_speculative_mods(), 0);
+}
+
+TEST_F(SpeculativeHoldFlowTapTest, holding_mod_tap_with_regular_mod) {
+ TestDriver driver;
+ InSequence s;
+ auto regular_key = KeymapKey(0, 0, 0, KC_A);
+ auto mod_key = KeymapKey(0, 1, 0, KC_LSFT);
+ auto mod_tap_key = KeymapKey(0, 2, 0, CTL_T(KC_C));
+
+ set_keymap({regular_key, mod_key, mod_tap_key});
+
+ // Tap regular key.
+ EXPECT_REPORT(driver, (KC_A));
+ EXPECT_EMPTY_REPORT(driver);
+ tap_key(regular_key);
+ VERIFY_AND_CLEAR(driver);
+
+ EXPECT_NO_REPORT(driver);
+ idle_for(FLOW_TAP_TERM + 1);
+ VERIFY_AND_CLEAR(driver);
+
+ // Press mod and mod-tap keys.
+ EXPECT_REPORT(driver, (KC_LSFT));
+ mod_key.press();
+ run_one_scan_loop();
+ VERIFY_AND_CLEAR(driver);
+
+ EXPECT_REPORT(driver, (KC_LSFT, KC_LCTL));
+ mod_tap_key.press();
+ idle_for(TAPPING_TERM - 5); // Hold almost until tapping term.
+ VERIFY_AND_CLEAR(driver);
+
+ // Press regular key.
+ EXPECT_REPORT(driver, (KC_LSFT, KC_LCTL, KC_A));
+ regular_key.press();
+ idle_for(10);
+ VERIFY_AND_CLEAR(driver);
+
+ // Release keys.
+ EXPECT_REPORT(driver, (KC_LSFT, KC_LCTL));
+ EXPECT_REPORT(driver, (KC_LCTL));
+ EXPECT_EMPTY_REPORT(driver);
+ regular_key.release();
+ run_one_scan_loop();
+ mod_key.release();
+ run_one_scan_loop();
+ mod_tap_key.release();
+ run_one_scan_loop();
+ VERIFY_AND_CLEAR(driver);
+ // All mods are released.
+ EXPECT_EQ(get_mods() | get_speculative_mods(), 0);
+}
+
+TEST_F(SpeculativeHoldFlowTapTest, layer_tap_ignored_with_disabled_key) {
+ TestDriver driver;
+ InSequence s;
+ auto no_key = KeymapKey(0, 0, 0, KC_NO);
+ auto regular_key = KeymapKey(1, 0, 0, KC_ESC);
+ auto layer_tap_key = KeymapKey(0, 1, 0, LT(1, KC_A));
+ auto mod_tap_key = KeymapKey(0, 2, 0, CTL_T(KC_B));
+
+ set_keymap({no_key, regular_key, layer_tap_key, mod_tap_key});
+
+ EXPECT_REPORT(driver, (KC_ESC));
+ EXPECT_EMPTY_REPORT(driver);
+ layer_tap_key.press();
+ idle_for(TAPPING_TERM + 1);
+ tap_key(regular_key);
+ layer_tap_key.release();
+ run_one_scan_loop();
+ VERIFY_AND_CLEAR(driver);
+
+ EXPECT_REPORT(driver, (KC_LCTL));
+ mod_tap_key.press();
+ idle_for(TAPPING_TERM + 1);
+ VERIFY_AND_CLEAR(driver);
+
+ EXPECT_EMPTY_REPORT(driver);
+ mod_tap_key.release();
+ run_one_scan_loop();
+ VERIFY_AND_CLEAR(driver);
+ // All mods are released.
+ EXPECT_EQ(get_mods() | get_speculative_mods(), 0);
+}
+
+TEST_F(SpeculativeHoldFlowTapTest, layer_tap_ignored_with_disabled_key_complex) {
+ TestDriver driver;
+ InSequence s;
+ auto regular_key1 = KeymapKey(0, 0, 0, KC_Q);
+ auto layer_tap_key = KeymapKey(0, 1, 0, LT(1, KC_SPC));
+ auto mod_tap_key1 = KeymapKey(0, 2, 0, CTL_T(KC_T));
+ // Place RALT_T(KC_I), where Flow Tap is enabled, in the same position on
+ // layer 0 as KC_RGHT, where Flow Tap is disabled. This tests that Flow Tap
+ // tracks the keycode from the correct layer.
+ auto mod_tap_key2 = KeymapKey(0, 3, 0, RALT_T(KC_I));
+ auto regular_key2 = KeymapKey(1, 3, 0, KC_RGHT);
+
+ set_keymap({regular_key1, layer_tap_key, mod_tap_key1, mod_tap_key2, regular_key2});
+
+ // Tap regular key 1.
+ EXPECT_REPORT(driver, (KC_Q));
+ EXPECT_EMPTY_REPORT(driver);
+ tap_key(regular_key1);
+ idle_for(FLOW_TAP_TERM + 1);
+ VERIFY_AND_CLEAR(driver);
+
+ // Hold layer-tap key.
+ EXPECT_NO_REPORT(driver);
+ layer_tap_key.press();
+ run_one_scan_loop();
+ VERIFY_AND_CLEAR(driver);
+
+ // Tap regular key 2.
+ EXPECT_REPORT(driver, (KC_RALT));
+ EXPECT_EMPTY_REPORT(driver);
+ EXPECT_REPORT(driver, (KC_RGHT));
+ EXPECT_EMPTY_REPORT(driver);
+ tap_key(regular_key2);
+ VERIFY_AND_CLEAR(driver);
+
+ // Release layer-tap key.
+ EXPECT_NO_REPORT(driver);
+ layer_tap_key.release();
+ run_one_scan_loop();
+ VERIFY_AND_CLEAR(driver);
+
+ // Quickly hold mod-tap key 1.
+ EXPECT_REPORT(driver, (KC_LCTL));
+ mod_tap_key1.press();
+ run_one_scan_loop();
+ VERIFY_AND_CLEAR(driver);
+
+ EXPECT_REPORT(driver, (KC_LCTL, KC_Q));
+ EXPECT_REPORT(driver, (KC_LCTL));
+ tap_key(regular_key1);
+ VERIFY_AND_CLEAR(driver);
+
+ EXPECT_EMPTY_REPORT(driver);
+ mod_tap_key1.release();
+ run_one_scan_loop();
+ VERIFY_AND_CLEAR(driver);
+ // All mods are released.
+ EXPECT_EQ(get_mods() | get_speculative_mods(), 0);
+}
+
+TEST_F(SpeculativeHoldFlowTapTest, layer_tap_ignored_with_enabled_key) {
+ TestDriver driver;
+ InSequence s;
+ auto no_key = KeymapKey(0, 0, 0, KC_NO);
+ auto regular_key = KeymapKey(1, 0, 0, KC_C);
+ auto layer_tap_key = KeymapKey(0, 1, 0, LT(1, KC_A));
+ auto mod_tap_key = KeymapKey(0, 2, 0, CTL_T(KC_B));
+
+ set_keymap({no_key, regular_key, layer_tap_key, mod_tap_key});
+
+ EXPECT_REPORT(driver, (KC_C));
+ EXPECT_EMPTY_REPORT(driver);
+ layer_tap_key.press();
+ idle_for(TAPPING_TERM + 1);
+ tap_key(regular_key);
+ layer_tap_key.release();
+ run_one_scan_loop();
+ VERIFY_AND_CLEAR(driver);
+
+ EXPECT_REPORT(driver, (KC_B));
+ mod_tap_key.press();
+ run_one_scan_loop();
+ VERIFY_AND_CLEAR(driver);
+
+ EXPECT_EMPTY_REPORT(driver);
+ idle_for(TAPPING_TERM + 1);
+ mod_tap_key.release();
+ run_one_scan_loop();
+ VERIFY_AND_CLEAR(driver);
+ // All mods are released.
+ EXPECT_EQ(get_mods() | get_speculative_mods(), 0);
+}
+
+TEST_F(SpeculativeHoldFlowTapTest, quick_tap) {
+ TestDriver driver;
+ InSequence s;
+ auto mod_tap_key = KeymapKey(0, 1, 0, SFT_T(KC_A));
+
+ set_keymap({mod_tap_key});
+
+ EXPECT_REPORT(driver, (KC_LSFT));
+ EXPECT_EMPTY_REPORT(driver);
+ EXPECT_REPORT(driver, (KC_A));
+ EXPECT_EMPTY_REPORT(driver);
+ tap_key(mod_tap_key);
+ VERIFY_AND_CLEAR(driver);
+
+ EXPECT_REPORT(driver, (KC_A));
+ mod_tap_key.press();
+ run_one_scan_loop();
+ VERIFY_AND_CLEAR(driver);
+
+ EXPECT_NO_REPORT(driver);
+ idle_for(TAPPING_TERM + 1);
+ VERIFY_AND_CLEAR(driver);
+
+ // Release mod-tap key.
+ EXPECT_EMPTY_REPORT(driver);
+ mod_tap_key.release();
+ run_one_scan_loop();
+ VERIFY_AND_CLEAR(driver);
+ // All mods are released.
+ EXPECT_EQ(get_mods() | get_speculative_mods(), 0);
+}
+
+TEST_F(SpeculativeHoldFlowTapTest, rolling_mt_mt) {
+ TestDriver driver;
+ InSequence s;
+ auto mod_tap_key1 = KeymapKey(0, 1, 0, SFT_T(KC_A));
+ auto mod_tap_key2 = KeymapKey(0, 2, 0, CTL_T(KC_B));
+
+ set_keymap({mod_tap_key1, mod_tap_key2});
+
+ EXPECT_NO_REPORT(driver);
+ EXPECT_REPORT(driver, (KC_LSFT));
+ EXPECT_REPORT(driver, (KC_LSFT, KC_LCTL));
+ idle_for(FLOW_TAP_TERM + 1);
+ mod_tap_key1.press();
+ run_one_scan_loop();
+ mod_tap_key2.press();
+ run_one_scan_loop();
+ VERIFY_AND_CLEAR(driver);
+
+ EXPECT_EMPTY_REPORT(driver);
+ EXPECT_REPORT(driver, (KC_A));
+ EXPECT_REPORT(driver, (KC_A, KC_B));
+ EXPECT_REPORT(driver, (KC_B));
+ mod_tap_key1.release();
+ run_one_scan_loop();
+ VERIFY_AND_CLEAR(driver);
+
+ // Hold for longer than the tapping term.
+ EXPECT_NO_REPORT(driver);
+ idle_for(TAPPING_TERM + 1);
+ VERIFY_AND_CLEAR(driver);
+
+ // Release mod-tap keys.
+ EXPECT_EMPTY_REPORT(driver);
+ mod_tap_key2.release();
+ run_one_scan_loop();
+ VERIFY_AND_CLEAR(driver);
+ // All mods are released.
+ EXPECT_EQ(get_mods() | get_speculative_mods(), 0);
+}
+
+TEST_F(SpeculativeHoldFlowTapTest, rolling_lt_mt_regular) {
+ TestDriver driver;
+ InSequence s;
+ auto layer_tap_key = KeymapKey(0, 0, 0, LT(1, KC_A));
+ auto mod_tap_key = KeymapKey(0, 1, 0, CTL_T(KC_B));
+ auto regular_key = KeymapKey(0, 2, 0, KC_C);
+
+ set_keymap({layer_tap_key, mod_tap_key, regular_key});
+
+ EXPECT_REPORT(driver, (KC_LCTL));
+ idle_for(FLOW_TAP_TERM + 1);
+ layer_tap_key.press();
+ run_one_scan_loop();
+ mod_tap_key.press();
+ run_one_scan_loop();
+ regular_key.press();
+ run_one_scan_loop();
+ VERIFY_AND_CLEAR(driver);
+
+ EXPECT_EMPTY_REPORT(driver);
+ EXPECT_REPORT(driver, (KC_A));
+ EXPECT_REPORT(driver, (KC_A, KC_B));
+ EXPECT_REPORT(driver, (KC_A, KC_B, KC_C));
+ EXPECT_REPORT(driver, (KC_B, KC_C));
+ layer_tap_key.release();
+ run_one_scan_loop();
+ VERIFY_AND_CLEAR(driver);
+
+ // Hold for longer than the tapping term.
+ EXPECT_NO_REPORT(driver);
+ idle_for(TAPPING_TERM + 1);
+ VERIFY_AND_CLEAR(driver);
+
+ // Release mod-tap keys.
+ EXPECT_REPORT(driver, (KC_C));
+ EXPECT_EMPTY_REPORT(driver);
+ mod_tap_key.release();
+ run_one_scan_loop();
+ regular_key.release();
+ run_one_scan_loop();
+ VERIFY_AND_CLEAR(driver);
+ // All mods are released.
+ EXPECT_EQ(get_mods() | get_speculative_mods(), 0);
+}
+
+TEST_F(SpeculativeHoldFlowTapTest, rolling_lt_regular_mt) {
+ TestDriver driver;
+ InSequence s;
+ auto layer_tap_key = KeymapKey(0, 0, 0, LT(1, KC_A));
+ auto regular_key = KeymapKey(0, 1, 0, KC_B);
+ auto mod_tap_key = KeymapKey(0, 2, 0, CTL_T(KC_C));
+
+ set_keymap({layer_tap_key, regular_key, mod_tap_key});
+
+ EXPECT_NO_REPORT(driver);
+ idle_for(FLOW_TAP_TERM + 1);
+ layer_tap_key.press();
+ run_one_scan_loop();
+ regular_key.press();
+ run_one_scan_loop();
+ mod_tap_key.press();
+ run_one_scan_loop();
+ VERIFY_AND_CLEAR(driver);
+
+ EXPECT_REPORT(driver, (KC_A));
+ EXPECT_REPORT(driver, (KC_A, KC_B));
+ EXPECT_REPORT(driver, (KC_A, KC_B, KC_C));
+ EXPECT_REPORT(driver, (KC_B, KC_C));
+ layer_tap_key.release();
+ run_one_scan_loop();
+ VERIFY_AND_CLEAR(driver);
+
+ // Hold for longer than the tapping term.
+ EXPECT_NO_REPORT(driver);
+ idle_for(TAPPING_TERM + 1);
+ VERIFY_AND_CLEAR(driver);
+
+ // Release mod-tap keys.
+ EXPECT_REPORT(driver, (KC_C));
+ EXPECT_EMPTY_REPORT(driver);
+ regular_key.release();
+ run_one_scan_loop();
+ mod_tap_key.release();
+ run_one_scan_loop();
+ VERIFY_AND_CLEAR(driver);
+ // All mods are released.
+ EXPECT_EQ(get_mods() | get_speculative_mods(), 0);
+}
+
+TEST_F(SpeculativeHoldFlowTapTest, rolling_mt_mt_mt) {
+ TestDriver driver;
+ InSequence s;
+ auto mod_tap_key1 = KeymapKey(0, 0, 0, CTL_T(KC_A));
+ auto mod_tap_key2 = KeymapKey(0, 1, 0, GUI_T(KC_B));
+ auto mod_tap_key3 = KeymapKey(0, 2, 0, ALT_T(KC_C));
+
+ set_keymap({mod_tap_key1, mod_tap_key2, mod_tap_key3});
+
+ // Press mod-tap keys.
+ EXPECT_REPORT(driver, (KC_LCTL));
+ EXPECT_REPORT(driver, (KC_LCTL, KC_LGUI));
+ EXPECT_REPORT(driver, (KC_LCTL, KC_LGUI, KC_LALT));
+ idle_for(FLOW_TAP_TERM + 1);
+ mod_tap_key1.press();
+ run_one_scan_loop();
+ mod_tap_key2.press();
+ run_one_scan_loop();
+ mod_tap_key3.press();
+ run_one_scan_loop();
+ VERIFY_AND_CLEAR(driver);
+
+ // Release first mod-tap key.
+ EXPECT_EMPTY_REPORT(driver);
+ EXPECT_REPORT(driver, (KC_A));
+ EXPECT_REPORT(driver, (KC_A, KC_B));
+ EXPECT_REPORT(driver, (KC_A, KC_B, KC_C));
+ EXPECT_REPORT(driver, (KC_B, KC_C));
+ mod_tap_key1.release();
+ run_one_scan_loop();
+ VERIFY_AND_CLEAR(driver);
+
+ // Hold for longer than the tapping term.
+ EXPECT_NO_REPORT(driver);
+ idle_for(TAPPING_TERM + 1);
+ VERIFY_AND_CLEAR(driver);
+
+ // Release other mod-tap keys.
+ EXPECT_REPORT(driver, (KC_C));
+ EXPECT_EMPTY_REPORT(driver);
+ mod_tap_key2.release();
+ run_one_scan_loop();
+ mod_tap_key3.release();
+ run_one_scan_loop();
+ VERIFY_AND_CLEAR(driver);
+ // All mods are released.
+ EXPECT_EQ(get_mods() | get_speculative_mods(), 0);
+}
+
+TEST_F(SpeculativeHoldFlowTapTest, roll_release_132) {
+ TestDriver driver;
+ InSequence s;
+ auto mod_tap_key1 = KeymapKey(0, 0, 0, CTL_T(KC_A));
+ auto mod_tap_key2 = KeymapKey(0, 1, 0, GUI_T(KC_B));
+ auto mod_tap_key3 = KeymapKey(0, 2, 0, ALT_T(KC_C));
+
+ set_keymap({mod_tap_key1, mod_tap_key2, mod_tap_key3});
+
+ // Press mod-tap keys.
+ EXPECT_REPORT(driver, (KC_LCTL));
+ EXPECT_REPORT(driver, (KC_LCTL, KC_LGUI));
+ EXPECT_REPORT(driver, (KC_LCTL, KC_LGUI, KC_LALT));
+ idle_for(FLOW_TAP_TERM + 1);
+ mod_tap_key1.press();
+ run_one_scan_loop();
+ mod_tap_key2.press();
+ idle_for(FLOW_TAP_TERM + 1);
+ mod_tap_key3.press();
+ run_one_scan_loop();
+ VERIFY_AND_CLEAR(driver);
+
+ // Release first mod-tap key.
+ EXPECT_EMPTY_REPORT(driver);
+ EXPECT_REPORT(driver, (KC_A));
+ EXPECT_REPORT(driver, (KC_A, KC_B));
+ EXPECT_REPORT(driver, (KC_B));
+ mod_tap_key1.release();
+ run_one_scan_loop();
+ VERIFY_AND_CLEAR(driver);
+
+ // Release other mod-tap keys.
+ EXPECT_REPORT(driver, (KC_B, KC_C));
+ EXPECT_REPORT(driver, (KC_B));
+ EXPECT_EMPTY_REPORT(driver);
+ mod_tap_key3.release();
+ run_one_scan_loop();
+ mod_tap_key2.release();
+ run_one_scan_loop();
+ VERIFY_AND_CLEAR(driver);
+ // All mods are released.
+ EXPECT_EQ(get_mods() | get_speculative_mods(), 0);
+}
+
+// Test with layer tap and speculative mod tap keys on the same layer, rolling
+// from LT to MT key:
+// "LT down, MT down, (wait out tapping term), LT up, MT up."
+TEST_F(SpeculativeHoldFlowTapTest, lt_mt_same_layer_roll) {
+ TestDriver driver;
+ InSequence s;
+ auto layer_tap_key = KeymapKey(0, 0, 0, LT(1, KC_A));
+ auto mod_tap_key = KeymapKey(0, 1, 0, SFT_T(KC_B));
+ auto regular_key = KeymapKey(1, 1, 0, KC_C);
+
+ set_keymap({layer_tap_key, mod_tap_key, regular_key});
+
+ // Press layer tap key.
+ EXPECT_NO_REPORT(driver);
+ layer_tap_key.press();
+ idle_for(FLOW_TAP_TERM + 1);
+ VERIFY_AND_CLEAR(driver);
+
+ // Press mod-tap key, after flow tap term but within tapping term. The
+ // speculative mod activates.
+ EXPECT_REPORT(driver, (KC_LSFT));
+ mod_tap_key.press();
+ run_one_scan_loop();
+ VERIFY_AND_CLEAR(driver);
+
+ // Wait for the layer tap key to settle.
+ EXPECT_EMPTY_REPORT(driver);
+ EXPECT_REPORT(driver, (KC_C));
+ idle_for(TAPPING_TERM - FLOW_TAP_TERM);
+ VERIFY_AND_CLEAR(driver);
+
+ // Release keys.
+ EXPECT_EMPTY_REPORT(driver);
+ layer_tap_key.release();
+ run_one_scan_loop();
+ mod_tap_key.release();
+ idle_for(FLOW_TAP_TERM + 1);
+ VERIFY_AND_CLEAR(driver);
+ // All mods are released.
+ EXPECT_EQ(get_mods() | get_speculative_mods(), 0);
+}
+
+// Test with layer tap and speculative mod tap keys on the same layer, trying
+// a nested press from LT to MT key:
+// "LT down, MT down, (wait out tapping term), MT up, LT up."
+TEST_F(SpeculativeHoldFlowTapTest, lt_mt_same_layer_nested_press) {
+ TestDriver driver;
+ InSequence s;
+ auto layer_tap_key = KeymapKey(0, 0, 0, LT(1, KC_A));
+ auto mod_tap_key = KeymapKey(0, 1, 0, SFT_T(KC_B));
+ auto regular_key = KeymapKey(1, 1, 0, KC_C);
+
+ set_keymap({layer_tap_key, mod_tap_key, regular_key});
+
+ EXPECT_NO_REPORT(driver);
+ layer_tap_key.press();
+ idle_for(FLOW_TAP_TERM + 1);
+ VERIFY_AND_CLEAR(driver);
+
+ EXPECT_REPORT(driver, (KC_LSFT));
+ mod_tap_key.press();
+ run_one_scan_loop();
+ VERIFY_AND_CLEAR(driver);
+
+ EXPECT_EMPTY_REPORT(driver);
+ EXPECT_REPORT(driver, (KC_C));
+ idle_for(TAPPING_TERM - FLOW_TAP_TERM);
+ VERIFY_AND_CLEAR(driver);
+
+ // Release keys: MT first, LT second.
+ EXPECT_EMPTY_REPORT(driver);
+ mod_tap_key.release();
+ run_one_scan_loop();
+ layer_tap_key.release();
+ idle_for(FLOW_TAP_TERM + 1);
+ VERIFY_AND_CLEAR(driver);
+ // All mods are released.
+ EXPECT_EQ(get_mods() | get_speculative_mods(), 0);
+}
+
+// Test with layer tap and speculative mod tap keys on the same layer, trying
+// a nested press with the MT first:
+// "MT down, LT down, (wait out tapping term), LT up, MT up."
+TEST_F(SpeculativeHoldFlowTapTest, mt_lt_same_layer_nested_press) {
+ TestDriver driver;
+ InSequence s;
+ auto layer_tap_key = KeymapKey(0, 0, 0, LT(1, KC_A));
+ auto mod_tap_key = KeymapKey(0, 1, 0, SFT_T(KC_B));
+ auto regular_key = KeymapKey(1, 1, 0, KC_C);
+
+ set_keymap({layer_tap_key, mod_tap_key, regular_key});
+
+ EXPECT_REPORT(driver, (KC_LSFT));
+ mod_tap_key.press();
+ run_one_scan_loop();
+
+ EXPECT_NO_REPORT(driver);
+ layer_tap_key.press();
+ idle_for(TAPPING_TERM + 1);
+ VERIFY_AND_CLEAR(driver);
+
+ EXPECT_EMPTY_REPORT(driver);
+ layer_tap_key.release();
+ run_one_scan_loop();
+ mod_tap_key.release();
+ idle_for(FLOW_TAP_TERM + 1);
+ VERIFY_AND_CLEAR(driver);
+ // All mods are released.
+ EXPECT_EQ(get_mods() | get_speculative_mods(), 0);
+}
+
+// Test with a speculative mod tap key reached by a layer tap key, rolling from
+// LT to MT key:
+// "LT down, MT down, (wait out tapping term), LT up, MT up."
+TEST_F(SpeculativeHoldFlowTapTest, lt_mt_different_layer_roll) {
+ TestDriver driver;
+ InSequence s;
+ auto layer_tap_key = KeymapKey(0, 0, 0, LT(1, KC_A));
+ auto regular_key = KeymapKey(0, 1, 0, KC_B);
+ auto placeholder_key = KeymapKey(1, 0, 0, KC_NO);
+ auto mod_tap_key = KeymapKey(1, 1, 0, SFT_T(KC_C));
+
+ set_keymap({layer_tap_key, regular_key, placeholder_key, mod_tap_key});
+
+ // Press layer tap key.
+ EXPECT_NO_REPORT(driver);
+ layer_tap_key.press();
+ idle_for(FLOW_TAP_TERM + 1);
+ // Press mod tap key.
+ mod_tap_key.press();
+ idle_for(TAPPING_TERM - FLOW_TAP_TERM);
+ VERIFY_AND_CLEAR(driver);
+
+ // Release keys.
+ EXPECT_REPORT(driver, (KC_B));
+ layer_tap_key.release();
+ idle_for(TAPPING_TERM);
+ VERIFY_AND_CLEAR(driver);
+
+ EXPECT_EMPTY_REPORT(driver);
+ mod_tap_key.release();
+ idle_for(FLOW_TAP_TERM + 1);
+ VERIFY_AND_CLEAR(driver);
+ // All mods are released.
+ EXPECT_EQ(get_mods() | get_speculative_mods(), 0);
+}
+
+// Test with a speculative mod tap key reached by a layer tap key, slowly
+// rolling from LT to MT key:
+// "LT down, (wait), MT down, (wait), LT up, MT up."
+TEST_F(SpeculativeHoldFlowTapTest, lt_mt_different_layer_slow_roll) {
+ TestDriver driver;
+ InSequence s;
+ auto layer_tap_key = KeymapKey(0, 0, 0, LT(1, KC_A));
+ auto regular_key = KeymapKey(0, 1, 0, KC_B);
+ auto placeholder_key = KeymapKey(1, 0, 0, KC_NO);
+ auto mod_tap_key = KeymapKey(1, 1, 0, SFT_T(KC_C));
+
+ set_keymap({layer_tap_key, regular_key, placeholder_key, mod_tap_key});
+
+ EXPECT_NO_REPORT(driver);
+ layer_tap_key.press();
+ idle_for(TAPPING_TERM + 1);
+
+ EXPECT_REPORT(driver, (KC_LSFT));
+ mod_tap_key.press();
+ idle_for(FLOW_TAP_TERM + 1);
+ VERIFY_AND_CLEAR(driver);
+
+ EXPECT_EMPTY_REPORT(driver);
+ EXPECT_REPORT(driver, (KC_B));
+ EXPECT_EMPTY_REPORT(driver);
+ layer_tap_key.release();
+ run_one_scan_loop();
+ mod_tap_key.release();
+ idle_for(FLOW_TAP_TERM + 1);
+ VERIFY_AND_CLEAR(driver);
+ // All mods are released.
+ EXPECT_EQ(get_mods() | get_speculative_mods(), 0);
+}
+
+// Test with a speculative mod tap key reached by a layer tap key, trying a
+// nested press:
+// "LT down, MT down, (wait out tapping term), MT up, LT up."
+TEST_F(SpeculativeHoldFlowTapTest, lt_mt_different_layer_nested_press) {
+ TestDriver driver;
+ InSequence s;
+ auto layer_tap_key = KeymapKey(0, 0, 0, LT(1, KC_A));
+ auto regular_key = KeymapKey(0, 1, 0, KC_B);
+ auto placeholder_key = KeymapKey(1, 0, 0, KC_NO);
+ auto mod_tap_key = KeymapKey(1, 1, 0, SFT_T(KC_C));
+
+ set_keymap({layer_tap_key, regular_key, placeholder_key, mod_tap_key});
+
+ EXPECT_NO_REPORT(driver);
+ layer_tap_key.press();
+ idle_for(FLOW_TAP_TERM + 1);
+ mod_tap_key.press();
+ idle_for(TAPPING_TERM - FLOW_TAP_TERM);
+ VERIFY_AND_CLEAR(driver);
+
+ // Release keys.
+ EXPECT_REPORT(driver, (KC_C));
+ EXPECT_EMPTY_REPORT(driver);
+ mod_tap_key.release();
+ run_one_scan_loop();
+ layer_tap_key.release();
+ idle_for(FLOW_TAP_TERM + 1);
+ VERIFY_AND_CLEAR(driver);
+ // All mods are released.
+ EXPECT_EQ(get_mods() | get_speculative_mods(), 0);
+}
+
+// Test with a speculative mod tap key reached by a layer tap key, trying a
+// slow nested press:
+// "LT down, (wait), MT down, MT up, LT up."
+TEST_F(SpeculativeHoldFlowTapTest, lt_mt_different_layer_slow_nested_press) {
+ TestDriver driver;
+ InSequence s;
+ auto layer_tap_key = KeymapKey(0, 0, 0, LT(1, KC_A));
+ auto regular_key = KeymapKey(0, 1, 0, KC_B);
+ auto placeholder_key = KeymapKey(1, 0, 0, KC_NO);
+ auto mod_tap_key = KeymapKey(1, 1, 0, SFT_T(KC_C));
+
+ set_keymap({layer_tap_key, regular_key, placeholder_key, mod_tap_key});
+
+ EXPECT_NO_REPORT(driver);
+ layer_tap_key.press();
+ idle_for(TAPPING_TERM + 1);
+
+ EXPECT_REPORT(driver, (KC_LSFT));
+ mod_tap_key.press();
+ idle_for(FLOW_TAP_TERM + 1);
+ VERIFY_AND_CLEAR(driver);
+
+ EXPECT_EMPTY_REPORT(driver);
+ EXPECT_REPORT(driver, (KC_C));
+ EXPECT_EMPTY_REPORT(driver);
+ mod_tap_key.release();
+ run_one_scan_loop();
+ layer_tap_key.release();
+ idle_for(FLOW_TAP_TERM + 1);
+ VERIFY_AND_CLEAR(driver);
+ // All mods are released.
+ EXPECT_EQ(get_mods() | get_speculative_mods(), 0);
+}
diff --git a/tests/tap_hold_configurations/speculative_hold/retro_shift_permissive_hold/config.h b/tests/tap_hold_configurations/speculative_hold/retro_shift_permissive_hold/config.h
new file mode 100644
index 0000000000..5b16a60687
--- /dev/null
+++ b/tests/tap_hold_configurations/speculative_hold/retro_shift_permissive_hold/config.h
@@ -0,0 +1,30 @@
+/* Copyright 2022 Isaac Elenbaas
+ * Copyright 2025 Google LLC
+ *
+ * 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 .
+ */
+
+#pragma once
+
+#include "test_common.h"
+
+#define SPECULATIVE_HOLD
+#define PERMISSIVE_HOLD
+
+#define RETRO_SHIFT 2 * TAPPING_TERM
+// releases between AUTO_SHIFT_TIMEOUT and TAPPING_TERM are not tested
+#define AUTO_SHIFT_TIMEOUT TAPPING_TERM
+#define AUTO_SHIFT_MODIFIERS
+
+#define DUMMY_MOD_NEUTRALIZER_KEYCODE KC_F24
diff --git a/tests/tap_hold_configurations/speculative_hold/retro_shift_permissive_hold/test.mk b/tests/tap_hold_configurations/speculative_hold/retro_shift_permissive_hold/test.mk
new file mode 100644
index 0000000000..7a19bcab59
--- /dev/null
+++ b/tests/tap_hold_configurations/speculative_hold/retro_shift_permissive_hold/test.mk
@@ -0,0 +1,17 @@
+# Copyright 2022 Isaac Elenbaas
+# Copyright 2025 Google LLC
+#
+# 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 .
+
+AUTO_SHIFT_ENABLE = yes
diff --git a/tests/tap_hold_configurations/speculative_hold/retro_shift_permissive_hold/test_retro_shift.cpp b/tests/tap_hold_configurations/speculative_hold/retro_shift_permissive_hold/test_retro_shift.cpp
new file mode 100644
index 0000000000..b81485865b
--- /dev/null
+++ b/tests/tap_hold_configurations/speculative_hold/retro_shift_permissive_hold/test_retro_shift.cpp
@@ -0,0 +1,689 @@
+// Copyright 2022 Isaac Elenbaas
+// Copyright 2025 Google LLC
+//
+// 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 .
+
+#include "keyboard_report_util.hpp"
+#include "keycode.h"
+#include "test_common.hpp"
+#include "action_tapping.h"
+#include "test_fixture.hpp"
+#include "test_keymap_key.hpp"
+
+extern "C" {
+bool get_speculative_hold(uint16_t keycode, keyrecord_t *record) {
+ return true;
+}
+
+bool get_auto_shifted_key(uint16_t keycode, keyrecord_t *record) {
+ return true;
+}
+} // extern "C"
+
+using testing::_;
+using testing::AnyNumber;
+using testing::AnyOf;
+using testing::InSequence;
+
+class RetroShiftPermissiveHold : public TestFixture {};
+
+TEST_F(RetroShiftPermissiveHold, tap_regular_key_while_mod_tap_key_is_held_under_tapping_term) {
+ TestDriver driver;
+ InSequence s;
+ auto mod_tap_key = KeymapKey(0, 0, 0, LCTL_T(KC_P));
+ auto regular_key = KeymapKey(0, MATRIX_COLS - 1, 0, KC_A);
+
+ set_keymap({mod_tap_key, regular_key});
+
+ // Press mod-tap-hold key.
+ EXPECT_REPORT(driver, (KC_LCTL));
+ mod_tap_key.press();
+ run_one_scan_loop();
+ VERIFY_AND_CLEAR(driver);
+
+ // Press regular key.
+ EXPECT_NO_REPORT(driver);
+ regular_key.press();
+ run_one_scan_loop();
+ VERIFY_AND_CLEAR(driver);
+
+ // Release regular key.
+ EXPECT_CALL(driver, send_keyboard_mock(KeyboardReport(KC_LCTL))).Times(AnyNumber());
+ EXPECT_REPORT(driver, (KC_LCTL, KC_A));
+ EXPECT_REPORT(driver, (KC_LCTL));
+ regular_key.release();
+ run_one_scan_loop();
+ VERIFY_AND_CLEAR(driver);
+
+ // Release mod-tap-hold key.
+ EXPECT_EMPTY_REPORT(driver);
+ mod_tap_key.release();
+ run_one_scan_loop();
+ VERIFY_AND_CLEAR(driver);
+}
+
+TEST_F(RetroShiftPermissiveHold, tap_mod_tap_key_while_mod_tap_key_is_held_under_tapping_term) {
+ TestDriver driver;
+ InSequence s;
+ auto mod_tap_key = KeymapKey(0, 0, 0, LCTL_T(KC_P));
+ auto mod_tap_regular_key = KeymapKey(0, MATRIX_COLS - 1, 0, LALT_T(KC_A));
+
+ set_keymap({mod_tap_key, mod_tap_regular_key});
+
+ // Press mod-tap-hold key.
+ EXPECT_REPORT(driver, (KC_LCTL));
+ mod_tap_key.press();
+ run_one_scan_loop();
+ VERIFY_AND_CLEAR(driver);
+
+ // Press mod-tap-regular key.
+ EXPECT_REPORT(driver, (KC_LCTL, KC_LALT));
+ mod_tap_regular_key.press();
+ run_one_scan_loop();
+ VERIFY_AND_CLEAR(driver);
+
+ // Release mod-tap-regular key.
+ EXPECT_REPORT(driver, (KC_LCTL));
+ EXPECT_CALL(driver, send_keyboard_mock(KeyboardReport(KC_LCTL))).Times(AnyNumber());
+ EXPECT_REPORT(driver, (KC_LCTL, KC_A));
+ EXPECT_REPORT(driver, (KC_LCTL));
+ mod_tap_regular_key.release();
+ run_one_scan_loop();
+ VERIFY_AND_CLEAR(driver);
+
+ // Release mod-tap-hold key.
+ EXPECT_EMPTY_REPORT(driver);
+ mod_tap_key.release();
+ run_one_scan_loop();
+ VERIFY_AND_CLEAR(driver);
+}
+
+TEST_F(RetroShiftPermissiveHold, tap_regular_key_while_mod_tap_key_is_held_over_tapping_term) {
+ TestDriver driver;
+ InSequence s;
+ auto mod_tap_key = KeymapKey(0, 0, 0, LCTL_T(KC_P));
+ auto regular_key = KeymapKey(0, MATRIX_COLS - 1, 0, KC_A);
+
+ set_keymap({mod_tap_key, regular_key});
+
+ // Press mod-tap-hold key.
+ EXPECT_REPORT(driver, (KC_LCTL));
+ mod_tap_key.press();
+ run_one_scan_loop();
+ VERIFY_AND_CLEAR(driver);
+
+ // Press regular key.
+ EXPECT_NO_REPORT(driver);
+ regular_key.press();
+ run_one_scan_loop();
+ VERIFY_AND_CLEAR(driver);
+
+ // Release regular key.
+ EXPECT_CALL(driver, send_keyboard_mock(KeyboardReport(KC_LCTL))).Times(AnyNumber());
+ EXPECT_REPORT(driver, (KC_LCTL, KC_A));
+ EXPECT_REPORT(driver, (KC_LCTL));
+ regular_key.release();
+ run_one_scan_loop();
+ idle_for(TAPPING_TERM);
+ VERIFY_AND_CLEAR(driver);
+
+ // Release mod-tap-hold key.
+ EXPECT_EMPTY_REPORT(driver);
+ mod_tap_key.release();
+ run_one_scan_loop();
+ VERIFY_AND_CLEAR(driver);
+}
+
+TEST_F(RetroShiftPermissiveHold, tap_mod_tap_key_while_mod_tap_key_is_held_over_tapping_term) {
+ TestDriver driver;
+ InSequence s;
+ auto mod_tap_key = KeymapKey(0, 0, 0, LCTL_T(KC_P));
+ auto mod_tap_regular_key = KeymapKey(0, MATRIX_COLS - 1, 0, LALT_T(KC_A));
+
+ set_keymap({mod_tap_key, mod_tap_regular_key});
+
+ // Press mod-tap-hold key.
+ EXPECT_REPORT(driver, (KC_LCTL));
+ mod_tap_key.press();
+ run_one_scan_loop();
+ VERIFY_AND_CLEAR(driver);
+
+ // Press mod-tap-regular key.
+ EXPECT_REPORT(driver, (KC_LCTL, KC_LALT));
+ mod_tap_regular_key.press();
+ run_one_scan_loop();
+ VERIFY_AND_CLEAR(driver);
+
+ // Release mod-tap-regular key.
+ EXPECT_CALL(driver, send_keyboard_mock(KeyboardReport(KC_LCTL))).Times(AnyNumber());
+ EXPECT_REPORT(driver, (KC_LCTL, KC_A));
+ EXPECT_REPORT(driver, (KC_LCTL));
+ mod_tap_regular_key.release();
+ run_one_scan_loop();
+ idle_for(TAPPING_TERM);
+ VERIFY_AND_CLEAR(driver);
+
+ // Release mod-tap-hold key.
+ EXPECT_EMPTY_REPORT(driver);
+ mod_tap_key.release();
+ run_one_scan_loop();
+ VERIFY_AND_CLEAR(driver);
+}
+
+TEST_F(RetroShiftPermissiveHold, hold_regular_key_while_mod_tap_key_is_held_over_tapping_term) {
+ TestDriver driver;
+ InSequence s;
+ auto mod_tap_key = KeymapKey(0, 0, 0, LCTL_T(KC_P));
+ auto regular_key = KeymapKey(0, MATRIX_COLS - 1, 0, KC_A);
+
+ set_keymap({mod_tap_key, regular_key});
+
+ // Press mod-tap-hold key.
+ EXPECT_REPORT(driver, (KC_LCTL));
+ mod_tap_key.press();
+ run_one_scan_loop();
+ VERIFY_AND_CLEAR(driver);
+
+ // Press regular key.
+ EXPECT_NO_REPORT(driver);
+ regular_key.press();
+ run_one_scan_loop();
+ idle_for(AUTO_SHIFT_TIMEOUT);
+ VERIFY_AND_CLEAR(driver);
+
+ // Release regular key.
+ // clang-format off
+ EXPECT_CALL(driver, send_keyboard_mock(AnyOf(
+ KeyboardReport(KC_LCTL, KC_LSFT),
+ KeyboardReport(KC_LSFT),
+ KeyboardReport(KC_LCTL))))
+ .Times(AnyNumber());
+ // clang-format on
+ EXPECT_REPORT(driver, (KC_LCTL, KC_LSFT, KC_A));
+ // clang-format off
+ EXPECT_CALL(driver, send_keyboard_mock(AnyOf(
+ KeyboardReport(KC_LCTL, KC_LSFT),
+ KeyboardReport(KC_LSFT))))
+ .Times(AnyNumber());
+ // clang-format on
+ EXPECT_REPORT(driver, (KC_LCTL));
+ regular_key.release();
+ run_one_scan_loop();
+ VERIFY_AND_CLEAR(driver);
+
+ // Release mod-tap-hold key.
+ EXPECT_EMPTY_REPORT(driver);
+ mod_tap_key.release();
+ run_one_scan_loop();
+ VERIFY_AND_CLEAR(driver);
+}
+
+TEST_F(RetroShiftPermissiveHold, hold_mod_tap_key_while_mod_tap_key_is_held_over_tapping_term) {
+ TestDriver driver;
+ InSequence s;
+ auto mod_tap_key = KeymapKey(0, 0, 0, LCTL_T(KC_P));
+ auto mod_tap_regular_key = KeymapKey(0, MATRIX_COLS - 1, 0, LALT_T(KC_A));
+
+ set_keymap({mod_tap_key, mod_tap_regular_key});
+
+ // Press mod-tap-hold key.
+ EXPECT_REPORT(driver, (KC_LCTL));
+ mod_tap_key.press();
+ run_one_scan_loop();
+ VERIFY_AND_CLEAR(driver);
+
+ // Press mod-tap-regular key.
+ EXPECT_REPORT(driver, (KC_LCTL, KC_LALT));
+ mod_tap_regular_key.press();
+ run_one_scan_loop();
+ idle_for(AUTO_SHIFT_TIMEOUT);
+ VERIFY_AND_CLEAR(driver);
+
+ // Release mod-tap-regular key.
+ // clang-format off
+ EXPECT_CALL(driver, send_keyboard_mock(AnyOf(
+ KeyboardReport(KC_LCTL, KC_LSFT),
+ KeyboardReport(KC_LSFT),
+ KeyboardReport(KC_LCTL))))
+ .Times(AnyNumber());
+ // clang-format on
+ EXPECT_REPORT(driver, (KC_LCTL, KC_LSFT, KC_A));
+ // clang-format off
+ EXPECT_CALL(driver, send_keyboard_mock(AnyOf(
+ KeyboardReport(KC_LCTL, KC_LSFT),
+ KeyboardReport(KC_LSFT))))
+ .Times(AnyNumber());
+ // clang-format on
+ EXPECT_REPORT(driver, (KC_LCTL));
+ mod_tap_regular_key.release();
+ run_one_scan_loop();
+ idle_for(TAPPING_TERM);
+ VERIFY_AND_CLEAR(driver);
+
+ // Release mod-tap-hold key.
+ EXPECT_EMPTY_REPORT(driver);
+ mod_tap_key.release();
+ run_one_scan_loop();
+ VERIFY_AND_CLEAR(driver);
+}
+
+TEST_F(RetroShiftPermissiveHold, roll_tap_regular_key_while_mod_tap_key_is_held_under_tapping_term) {
+ TestDriver driver;
+ InSequence s;
+ auto mod_tap_key = KeymapKey(0, 0, 0, LCTL_T(KC_P));
+ auto regular_key = KeymapKey(0, MATRIX_COLS - 1, 0, KC_A);
+
+ set_keymap({mod_tap_key, regular_key});
+
+ // Press mod-tap-hold key.
+ EXPECT_REPORT(driver, (KC_LCTL));
+ mod_tap_key.press();
+ run_one_scan_loop();
+ VERIFY_AND_CLEAR(driver);
+
+ // Press regular key.
+ EXPECT_NO_REPORT(driver);
+ regular_key.press();
+ run_one_scan_loop();
+ VERIFY_AND_CLEAR(driver);
+
+ // Release mod-tap-hold key.
+ EXPECT_EMPTY_REPORT(driver);
+ EXPECT_REPORT(driver, (KC_P));
+ EXPECT_EMPTY_REPORT(driver);
+ mod_tap_key.release();
+ run_one_scan_loop();
+ VERIFY_AND_CLEAR(driver);
+
+ // Release regular key.
+ EXPECT_REPORT(driver, (KC_A));
+ EXPECT_EMPTY_REPORT(driver);
+ regular_key.release();
+ run_one_scan_loop();
+ VERIFY_AND_CLEAR(driver);
+}
+
+TEST_F(RetroShiftPermissiveHold, roll_tap_mod_tap_key_while_mod_tap_key_is_held_under_tapping_term) {
+ TestDriver driver;
+ InSequence s;
+ auto mod_tap_key = KeymapKey(0, 0, 0, LCTL_T(KC_P));
+ auto mod_tap_regular_key = KeymapKey(0, MATRIX_COLS - 1, 0, LALT_T(KC_A));
+
+ set_keymap({mod_tap_key, mod_tap_regular_key});
+
+ // Press mod-tap-hold key.
+ EXPECT_REPORT(driver, (KC_LCTL));
+ mod_tap_key.press();
+ run_one_scan_loop();
+ VERIFY_AND_CLEAR(driver);
+
+ // Press mod-tap-regular key.
+ EXPECT_REPORT(driver, (KC_LCTL, KC_LALT));
+ mod_tap_regular_key.press();
+ run_one_scan_loop();
+ VERIFY_AND_CLEAR(driver);
+
+ // Release mod-tap-hold key.
+ EXPECT_EMPTY_REPORT(driver);
+ EXPECT_REPORT(driver, (KC_P));
+ EXPECT_EMPTY_REPORT(driver);
+ mod_tap_key.release();
+ run_one_scan_loop();
+ VERIFY_AND_CLEAR(driver);
+
+ // Release mod-tap-regular key.
+ EXPECT_REPORT(driver, (KC_A));
+ EXPECT_EMPTY_REPORT(driver);
+ mod_tap_regular_key.release();
+ run_one_scan_loop();
+ VERIFY_AND_CLEAR(driver);
+}
+
+TEST_F(RetroShiftPermissiveHold, roll_hold_regular_key_while_mod_tap_key_is_held_under_tapping_term) {
+ TestDriver driver;
+ InSequence s;
+ auto mod_tap_key = KeymapKey(0, 0, 0, LCTL_T(KC_P));
+ auto regular_key = KeymapKey(0, MATRIX_COLS - 1, 0, KC_A);
+
+ set_keymap({mod_tap_key, regular_key});
+
+ // Press mod-tap-hold key.
+ EXPECT_REPORT(driver, (KC_LCTL));
+ mod_tap_key.press();
+ run_one_scan_loop();
+ VERIFY_AND_CLEAR(driver);
+
+ // Press regular key.
+ EXPECT_NO_REPORT(driver);
+ regular_key.press();
+ run_one_scan_loop();
+ VERIFY_AND_CLEAR(driver);
+
+ // Release mod-tap-hold key.
+ EXPECT_EMPTY_REPORT(driver);
+ EXPECT_REPORT(driver, (KC_P));
+ EXPECT_EMPTY_REPORT(driver);
+ EXPECT_CALL(driver, send_keyboard_mock(KeyboardReport(KC_LSFT))).Times(AnyNumber());
+ EXPECT_REPORT(driver, (KC_LSFT, KC_A));
+ EXPECT_CALL(driver, send_keyboard_mock(KeyboardReport(KC_LSFT))).Times(AnyNumber());
+ EXPECT_EMPTY_REPORT(driver);
+ mod_tap_key.release();
+ run_one_scan_loop();
+ idle_for(AUTO_SHIFT_TIMEOUT);
+ VERIFY_AND_CLEAR(driver);
+
+ // Release regular key.
+ EXPECT_NO_REPORT(driver);
+ regular_key.release();
+ run_one_scan_loop();
+ VERIFY_AND_CLEAR(driver);
+}
+
+TEST_F(RetroShiftPermissiveHold, roll_hold_mod_tap_key_while_mod_tap_key_is_held_under_tapping_term) {
+ TestDriver driver;
+ InSequence s;
+ auto mod_tap_key = KeymapKey(0, 0, 0, LCTL_T(KC_P));
+ auto mod_tap_regular_key = KeymapKey(0, MATRIX_COLS - 1, 0, LALT_T(KC_A));
+
+ set_keymap({mod_tap_key, mod_tap_regular_key});
+
+ // Press mod-tap-hold key.
+ EXPECT_REPORT(driver, (KC_LCTL));
+ mod_tap_key.press();
+ run_one_scan_loop();
+ VERIFY_AND_CLEAR(driver);
+
+ // Press mod-tap-regular key.
+ EXPECT_REPORT(driver, (KC_LCTL, KC_LALT));
+ mod_tap_regular_key.press();
+ run_one_scan_loop();
+ VERIFY_AND_CLEAR(driver);
+
+ // Release mod-tap-hold key.
+ EXPECT_EMPTY_REPORT(driver);
+ EXPECT_REPORT(driver, (KC_P));
+ EXPECT_EMPTY_REPORT(driver);
+ mod_tap_key.release();
+ run_one_scan_loop();
+ VERIFY_AND_CLEAR(driver);
+
+ // Release mod-tap-regular key.
+ EXPECT_CALL(driver, send_keyboard_mock(KeyboardReport(KC_LSFT))).Times(AnyNumber());
+ EXPECT_REPORT(driver, (KC_LSFT, KC_A));
+ EXPECT_CALL(driver, send_keyboard_mock(KeyboardReport(KC_LSFT))).Times(AnyNumber());
+ EXPECT_EMPTY_REPORT(driver);
+ idle_for(AUTO_SHIFT_TIMEOUT);
+ mod_tap_regular_key.release();
+ run_one_scan_loop();
+ VERIFY_AND_CLEAR(driver);
+}
+
+// Test with layer tap and speculative mod tap keys on the same layer, rolling
+// from LT to MT key:
+// "LT down, MT down, (wait out tapping term), LT up, MT up."
+TEST_F(RetroShiftPermissiveHold, lt_mt_same_layer_roll) {
+ TestDriver driver;
+ InSequence s;
+ auto layer_tap_key = KeymapKey(0, 0, 0, LT(1, KC_A));
+ auto mod_tap_key = KeymapKey(0, 1, 0, LCTL_T(KC_B));
+ auto regular_key = KeymapKey(1, 1, 0, KC_C);
+
+ set_keymap({layer_tap_key, mod_tap_key, regular_key});
+
+ // Press layer tap key.
+ EXPECT_NO_REPORT(driver);
+ layer_tap_key.press();
+ run_one_scan_loop();
+ VERIFY_AND_CLEAR(driver);
+
+ // Press mod-tap key, after flow tap term but within tapping term. The
+ // speculative mod activates.
+ EXPECT_REPORT(driver, (KC_LCTL));
+ mod_tap_key.press();
+ run_one_scan_loop();
+ VERIFY_AND_CLEAR(driver);
+
+ // Wait for the layer tap key to settle.
+ EXPECT_NO_REPORT(driver);
+ idle_for(TAPPING_TERM);
+ VERIFY_AND_CLEAR(driver);
+
+ // Release keys.
+ EXPECT_EMPTY_REPORT(driver);
+ EXPECT_REPORT(driver, (KC_LSFT, KC_A));
+ EXPECT_REPORT(driver, (KC_LSFT));
+ EXPECT_EMPTY_REPORT(driver);
+ EXPECT_REPORT(driver, (KC_B));
+ EXPECT_EMPTY_REPORT(driver);
+ layer_tap_key.release();
+ run_one_scan_loop();
+ mod_tap_key.release();
+ run_one_scan_loop();
+ VERIFY_AND_CLEAR(driver);
+ // All mods are released.
+ EXPECT_EQ(get_mods() | get_speculative_mods(), 0);
+}
+
+// Test with layer tap and speculative mod tap keys on the same layer, trying
+// a nested press from LT to MT key:
+// "LT down, MT down, (wait out tapping term), MT up, LT up."
+TEST_F(RetroShiftPermissiveHold, lt_mt_same_layer_nested_press) {
+ TestDriver driver;
+ InSequence s;
+ auto layer_tap_key = KeymapKey(0, 0, 0, LT(1, KC_A));
+ auto mod_tap_key = KeymapKey(0, 1, 0, LCTL_T(KC_B));
+ auto regular_key = KeymapKey(1, 1, 0, KC_C);
+
+ set_keymap({layer_tap_key, mod_tap_key, regular_key});
+
+ EXPECT_NO_REPORT(driver);
+ layer_tap_key.press();
+ run_one_scan_loop();
+ VERIFY_AND_CLEAR(driver);
+
+ EXPECT_REPORT(driver, (KC_LCTL));
+ mod_tap_key.press();
+ run_one_scan_loop();
+ VERIFY_AND_CLEAR(driver);
+
+ EXPECT_NO_REPORT(driver);
+ run_one_scan_loop();
+ idle_for(TAPPING_TERM);
+ VERIFY_AND_CLEAR(driver);
+
+ // Release keys: MT first, LT second.
+ EXPECT_EMPTY_REPORT(driver);
+ EXPECT_REPORT(driver, (KC_LSFT, KC_C));
+ EXPECT_REPORT(driver, (KC_LSFT));
+ EXPECT_EMPTY_REPORT(driver);
+ mod_tap_key.release();
+ run_one_scan_loop();
+ layer_tap_key.release();
+ run_one_scan_loop();
+ VERIFY_AND_CLEAR(driver);
+ // All mods are released.
+ EXPECT_EQ(get_mods() | get_speculative_mods(), 0);
+}
+
+// Test with layer tap and speculative mod tap keys on the same layer, trying
+// a nested press with the MT first:
+// "MT down, LT down, (wait out tapping term), LT up, MT up."
+TEST_F(RetroShiftPermissiveHold, mt_lt_same_layer_nested_press) {
+ TestDriver driver;
+ InSequence s;
+ auto layer_tap_key = KeymapKey(0, 0, 0, LT(1, KC_A));
+ auto mod_tap_key = KeymapKey(0, 1, 0, LCTL_T(KC_B));
+ auto regular_key = KeymapKey(1, 1, 0, KC_C);
+
+ set_keymap({layer_tap_key, mod_tap_key, regular_key});
+
+ EXPECT_REPORT(driver, (KC_LCTL));
+ mod_tap_key.press();
+ run_one_scan_loop();
+ VERIFY_AND_CLEAR(driver);
+
+ EXPECT_NO_REPORT(driver);
+ layer_tap_key.press();
+ idle_for(TAPPING_TERM + 1);
+ VERIFY_AND_CLEAR(driver);
+
+ EXPECT_REPORT(driver, (KC_LCTL, KC_LSFT, KC_A));
+ EXPECT_REPORT(driver, (KC_LCTL, KC_LSFT));
+ EXPECT_REPORT(driver, (KC_LCTL));
+ EXPECT_EMPTY_REPORT(driver);
+ layer_tap_key.release();
+ run_one_scan_loop();
+ mod_tap_key.release();
+ run_one_scan_loop();
+ VERIFY_AND_CLEAR(driver);
+ // All mods are released.
+ EXPECT_EQ(get_mods() | get_speculative_mods(), 0);
+}
+
+// Test with a speculative mod tap key reached by a layer tap key, rolling from
+// LT to MT key:
+// "LT down, MT down, (wait out tapping term), LT up, MT up."
+TEST_F(RetroShiftPermissiveHold, lt_mt_different_layer_roll) {
+ TestDriver driver;
+ InSequence s;
+ auto layer_tap_key = KeymapKey(0, 0, 0, LT(1, KC_A));
+ auto regular_key = KeymapKey(0, 1, 0, KC_B);
+ auto placeholder_key = KeymapKey(1, 0, 0, KC_NO);
+ auto mod_tap_key = KeymapKey(1, 1, 0, LCTL_T(KC_C));
+
+ set_keymap({layer_tap_key, regular_key, placeholder_key, mod_tap_key});
+
+ // Press layer tap key.
+ EXPECT_NO_REPORT(driver);
+ layer_tap_key.press();
+ run_one_scan_loop();
+ // Press mod tap key.
+ mod_tap_key.press();
+ idle_for(TAPPING_TERM);
+ VERIFY_AND_CLEAR(driver);
+
+ // Release keys.
+ EXPECT_REPORT(driver, (KC_A));
+ EXPECT_EMPTY_REPORT(driver);
+ layer_tap_key.release();
+ idle_for(TAPPING_TERM);
+ VERIFY_AND_CLEAR(driver);
+
+ EXPECT_REPORT(driver, (KC_LSFT, KC_B));
+ EXPECT_REPORT(driver, (KC_LSFT));
+ EXPECT_EMPTY_REPORT(driver);
+ mod_tap_key.release();
+ run_one_scan_loop();
+ VERIFY_AND_CLEAR(driver);
+ // All mods are released.
+ EXPECT_EQ(get_mods() | get_speculative_mods(), 0);
+}
+
+// Test with a speculative mod tap key reached by a layer tap key, slowly
+// rolling from LT to MT key:
+// "LT down, (wait), MT down, (wait), LT up, MT up."
+TEST_F(RetroShiftPermissiveHold, lt_mt_different_layer_slow_roll) {
+ TestDriver driver;
+ InSequence s;
+ auto layer_tap_key = KeymapKey(0, 0, 0, LT(1, KC_A));
+ auto regular_key = KeymapKey(0, 1, 0, KC_B);
+ auto placeholder_key = KeymapKey(1, 0, 0, KC_NO);
+ auto mod_tap_key = KeymapKey(1, 1, 0, LCTL_T(KC_C));
+
+ set_keymap({layer_tap_key, regular_key, placeholder_key, mod_tap_key});
+
+ EXPECT_NO_REPORT(driver);
+ layer_tap_key.press();
+ idle_for(TAPPING_TERM + 1);
+ mod_tap_key.press();
+ run_one_scan_loop();
+ VERIFY_AND_CLEAR(driver);
+
+ EXPECT_REPORT(driver, (KC_LSFT, KC_A));
+ EXPECT_REPORT(driver, (KC_LSFT));
+ EXPECT_EMPTY_REPORT(driver);
+ EXPECT_REPORT(driver, (KC_B));
+ EXPECT_EMPTY_REPORT(driver);
+ layer_tap_key.release();
+ run_one_scan_loop();
+ mod_tap_key.release();
+ run_one_scan_loop();
+ VERIFY_AND_CLEAR(driver);
+ // All mods are released.
+ EXPECT_EQ(get_mods() | get_speculative_mods(), 0);
+}
+
+// Test with a speculative mod tap key reached by a layer tap key, try a nested
+// press:
+// "LT down, MT down, (wait out tapping term), MT up, LT up."
+TEST_F(RetroShiftPermissiveHold, lt_mt_different_layer_nested_press) {
+ TestDriver driver;
+ InSequence s;
+ auto layer_tap_key = KeymapKey(0, 0, 0, LT(1, KC_A));
+ auto regular_key = KeymapKey(0, 1, 0, KC_B);
+ auto placeholder_key = KeymapKey(1, 0, 0, KC_NO);
+ auto mod_tap_key = KeymapKey(1, 1, 0, LCTL_T(KC_C));
+
+ set_keymap({layer_tap_key, regular_key, placeholder_key, mod_tap_key});
+
+ EXPECT_NO_REPORT(driver);
+ layer_tap_key.press();
+ run_one_scan_loop();
+ mod_tap_key.press();
+ idle_for(TAPPING_TERM);
+ VERIFY_AND_CLEAR(driver);
+
+ // Release keys.
+ EXPECT_REPORT(driver, (KC_LSFT, KC_C));
+ EXPECT_REPORT(driver, (KC_LSFT));
+ EXPECT_EMPTY_REPORT(driver);
+ mod_tap_key.release();
+ run_one_scan_loop();
+ layer_tap_key.release();
+ run_one_scan_loop();
+ VERIFY_AND_CLEAR(driver);
+ // All mods are released.
+ EXPECT_EQ(get_mods() | get_speculative_mods(), 0);
+}
+
+// Test with a speculative mod tap key reached by a layer tap key, try a slow
+// nested press:
+// "LT down, (wait), MT down, MT up, LT up."
+TEST_F(RetroShiftPermissiveHold, lt_mt_different_layer_slow_nested_press) {
+ TestDriver driver;
+ InSequence s;
+ auto layer_tap_key = KeymapKey(0, 0, 0, LT(1, KC_A));
+ auto regular_key = KeymapKey(0, 1, 0, KC_B);
+ auto placeholder_key = KeymapKey(1, 0, 0, KC_NO);
+ auto mod_tap_key = KeymapKey(1, 1, 0, LCTL_T(KC_C));
+
+ set_keymap({layer_tap_key, regular_key, placeholder_key, mod_tap_key});
+
+ EXPECT_NO_REPORT(driver);
+ layer_tap_key.press();
+ idle_for(TAPPING_TERM + 1);
+ mod_tap_key.press();
+ run_one_scan_loop();
+ VERIFY_AND_CLEAR(driver);
+
+ EXPECT_REPORT(driver, (KC_C));
+ EXPECT_EMPTY_REPORT(driver);
+ mod_tap_key.release();
+ run_one_scan_loop();
+ layer_tap_key.release();
+ run_one_scan_loop();
+ VERIFY_AND_CLEAR(driver);
+ // All mods are released.
+ EXPECT_EQ(get_mods() | get_speculative_mods(), 0);
+}
diff --git a/tests/tap_hold_configurations/speculative_hold/retro_tapping/config.h b/tests/tap_hold_configurations/speculative_hold/retro_tapping/config.h
new file mode 100644
index 0000000000..b20ddbdcad
--- /dev/null
+++ b/tests/tap_hold_configurations/speculative_hold/retro_tapping/config.h
@@ -0,0 +1,24 @@
+/* Copyright 2022 Vladislav Kucheriavykh
+ * Copyright 2025 Google LLC
+ *
+ * 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 .
+ */
+
+#pragma once
+
+#include "test_common.h"
+
+#define SPECULATIVE_HOLD
+#define RETRO_TAPPING
+#define DUMMY_MOD_NEUTRALIZER_KEYCODE KC_F24
diff --git a/tests/tap_hold_configurations/speculative_hold/retro_tapping/test.mk b/tests/tap_hold_configurations/speculative_hold/retro_tapping/test.mk
new file mode 100644
index 0000000000..9bc3366e14
--- /dev/null
+++ b/tests/tap_hold_configurations/speculative_hold/retro_tapping/test.mk
@@ -0,0 +1,15 @@
+# Copyright 2022 Vladislav Kucheriavykh
+# Copyright 2025 Google LLC
+#
+# 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 .
diff --git a/tests/tap_hold_configurations/speculative_hold/retro_tapping/test_tap_hold.cpp b/tests/tap_hold_configurations/speculative_hold/retro_tapping/test_tap_hold.cpp
new file mode 100644
index 0000000000..de70bf09fd
--- /dev/null
+++ b/tests/tap_hold_configurations/speculative_hold/retro_tapping/test_tap_hold.cpp
@@ -0,0 +1,629 @@
+// Copyright 2025 Google LLC
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// https://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#include "keyboard_report_util.hpp"
+#include "keycode.h"
+#include "test_common.hpp"
+#include "action_tapping.h"
+#include "test_fixture.hpp"
+#include "test_keymap_key.hpp"
+
+using testing::_;
+using testing::InSequence;
+
+extern "C" bool get_speculative_hold(uint16_t keycode, keyrecord_t *record) {
+ return true;
+}
+
+class SpeculativeHoldRetroTappingTest : public TestFixture {};
+
+TEST_F(SpeculativeHoldRetroTappingTest, roll_regular_to_lgui_mod) {
+ TestDriver driver;
+ InSequence s;
+ auto mod_tap_key = KeymapKey(0, 1, 0, LGUI_T(KC_P));
+ auto regular_key = KeymapKey(0, 2, 0, KC_B);
+
+ set_keymap({mod_tap_key, regular_key});
+
+ EXPECT_REPORT(driver, (KC_B));
+ regular_key.press();
+ run_one_scan_loop();
+ VERIFY_AND_CLEAR(driver);
+
+ EXPECT_REPORT(driver, (KC_B, KC_LGUI));
+ mod_tap_key.press();
+ idle_for(TAPPING_TERM + 1);
+ VERIFY_AND_CLEAR(driver);
+
+ EXPECT_REPORT(driver, (KC_LGUI));
+ regular_key.release();
+ run_one_scan_loop();
+ VERIFY_AND_CLEAR(driver);
+
+ // Neutralizer invoked by Speculative Hold.
+ EXPECT_REPORT(driver, (KC_LGUI, DUMMY_MOD_NEUTRALIZER_KEYCODE));
+ EXPECT_REPORT(driver, (KC_LGUI));
+ EXPECT_EMPTY_REPORT(driver);
+ EXPECT_REPORT(driver, (KC_P));
+ EXPECT_EMPTY_REPORT(driver);
+ mod_tap_key.release();
+ run_one_scan_loop();
+ VERIFY_AND_CLEAR(driver);
+}
+
+TEST_F(SpeculativeHoldRetroTappingTest, regular_to_mod_under_tap_term) {
+ TestDriver driver;
+ InSequence s;
+ auto mod_tap_key = KeymapKey(0, 1, 0, LSFT_T(KC_A));
+ auto regular_key = KeymapKey(0, 2, 0, KC_B);
+
+ set_keymap({mod_tap_key, regular_key});
+
+ EXPECT_REPORT(driver, (KC_B));
+ regular_key.press();
+ run_one_scan_loop();
+ VERIFY_AND_CLEAR(driver);
+
+ EXPECT_REPORT(driver, (KC_B, KC_LSFT));
+ mod_tap_key.press();
+ run_one_scan_loop();
+ VERIFY_AND_CLEAR(driver);
+
+ EXPECT_REPORT(driver, (KC_LSFT));
+ regular_key.release();
+ run_one_scan_loop();
+ VERIFY_AND_CLEAR(driver);
+
+ EXPECT_EMPTY_REPORT(driver);
+ EXPECT_REPORT(driver, (KC_A));
+ EXPECT_EMPTY_REPORT(driver);
+ mod_tap_key.release();
+ run_one_scan_loop();
+ VERIFY_AND_CLEAR(driver);
+}
+
+TEST_F(SpeculativeHoldRetroTappingTest, mod_under_tap_term_to_regular) {
+ TestDriver driver;
+ InSequence s;
+ auto mod_tap_key = KeymapKey(0, 1, 0, LGUI_T(KC_P));
+ auto regular_key = KeymapKey(0, 2, 0, KC_B);
+
+ set_keymap({mod_tap_key, regular_key});
+
+ EXPECT_REPORT(driver, (KC_LGUI));
+ mod_tap_key.press();
+ run_one_scan_loop();
+ VERIFY_AND_CLEAR(driver);
+
+ EXPECT_NO_REPORT(driver);
+ regular_key.press();
+ run_one_scan_loop();
+ VERIFY_AND_CLEAR(driver);
+
+ // Neutralizer invoked by Speculative Hold.
+ EXPECT_REPORT(driver, (KC_LGUI, DUMMY_MOD_NEUTRALIZER_KEYCODE));
+ EXPECT_REPORT(driver, (KC_LGUI));
+ EXPECT_EMPTY_REPORT(driver);
+ EXPECT_REPORT(driver, (KC_P));
+ EXPECT_REPORT(driver, (KC_B, KC_P));
+ EXPECT_REPORT(driver, (KC_B));
+ mod_tap_key.release();
+ run_one_scan_loop();
+ VERIFY_AND_CLEAR(driver);
+
+ EXPECT_EMPTY_REPORT(driver);
+ regular_key.release();
+ run_one_scan_loop();
+ VERIFY_AND_CLEAR(driver);
+}
+
+TEST_F(SpeculativeHoldRetroTappingTest, mod_over_tap_term_to_regular) {
+ TestDriver driver;
+ InSequence s;
+ auto mod_tap_key = KeymapKey(0, 1, 0, LSFT_T(KC_A));
+ auto regular_key = KeymapKey(0, 2, 0, KC_B);
+
+ set_keymap({mod_tap_key, regular_key});
+
+ EXPECT_REPORT(driver, (KC_LSFT));
+ mod_tap_key.press();
+ idle_for(TAPPING_TERM);
+ run_one_scan_loop();
+ VERIFY_AND_CLEAR(driver);
+
+ EXPECT_REPORT(driver, (KC_LSFT, KC_B));
+ regular_key.press();
+ run_one_scan_loop();
+ VERIFY_AND_CLEAR(driver);
+
+ EXPECT_REPORT(driver, (KC_B));
+ mod_tap_key.release();
+ run_one_scan_loop();
+ VERIFY_AND_CLEAR(driver);
+
+ EXPECT_EMPTY_REPORT(driver);
+ regular_key.release();
+ run_one_scan_loop();
+ VERIFY_AND_CLEAR(driver);
+}
+
+TEST_F(SpeculativeHoldRetroTappingTest, mod_under_tap_term_to_mod_under_tap_term) {
+ TestDriver driver;
+ InSequence s;
+ auto mod_tap_lgui = KeymapKey(0, 1, 0, LGUI_T(KC_P));
+ auto mod_tap_lsft = KeymapKey(0, 2, 0, LSFT_T(KC_A));
+
+ set_keymap({mod_tap_lgui, mod_tap_lsft});
+
+ EXPECT_REPORT(driver, (KC_LSFT));
+ mod_tap_lsft.press();
+ run_one_scan_loop();
+ VERIFY_AND_CLEAR(driver);
+
+ EXPECT_REPORT(driver, (KC_LSFT, KC_LGUI));
+ mod_tap_lgui.press();
+ run_one_scan_loop();
+ VERIFY_AND_CLEAR(driver);
+
+ EXPECT_EMPTY_REPORT(driver);
+ EXPECT_REPORT(driver, (KC_A));
+ EXPECT_EMPTY_REPORT(driver);
+ mod_tap_lsft.release();
+ run_one_scan_loop();
+ VERIFY_AND_CLEAR(driver);
+
+ EXPECT_REPORT(driver, (KC_P));
+ EXPECT_EMPTY_REPORT(driver);
+ mod_tap_lgui.release();
+ run_one_scan_loop();
+ VERIFY_AND_CLEAR(driver);
+}
+
+TEST_F(SpeculativeHoldRetroTappingTest, mod_over_tap_term_to_mod_under_tap_term) {
+ TestDriver driver;
+ InSequence s;
+ auto mod_tap_lgui = KeymapKey(0, 1, 0, LGUI_T(KC_P));
+ auto mod_tap_lsft = KeymapKey(0, 2, 0, LSFT_T(KC_A));
+
+ set_keymap({mod_tap_lgui, mod_tap_lsft});
+
+ EXPECT_REPORT(driver, (KC_LSFT));
+ mod_tap_lsft.press();
+ idle_for(TAPPING_TERM);
+ run_one_scan_loop();
+ VERIFY_AND_CLEAR(driver);
+
+ EXPECT_REPORT(driver, (KC_LSFT, KC_LGUI));
+ mod_tap_lgui.press();
+ run_one_scan_loop();
+ VERIFY_AND_CLEAR(driver);
+
+ EXPECT_NO_REPORT(driver);
+ mod_tap_lsft.release();
+ run_one_scan_loop();
+ VERIFY_AND_CLEAR(driver);
+
+ EXPECT_REPORT(driver, (KC_LSFT));
+ EXPECT_REPORT(driver, (KC_LSFT, KC_P));
+ EXPECT_REPORT(driver, (KC_P));
+ EXPECT_EMPTY_REPORT(driver);
+ mod_tap_lgui.release();
+ run_one_scan_loop();
+ VERIFY_AND_CLEAR(driver);
+}
+
+TEST_F(SpeculativeHoldRetroTappingTest, mod_under_tap_term_to_mod_over_tap_term) {
+ TestDriver driver;
+ InSequence s;
+ auto mod_tap_lgui = KeymapKey(0, 1, 0, LGUI_T(KC_P));
+ auto mod_tap_lsft = KeymapKey(0, 2, 0, LSFT_T(KC_A));
+
+ set_keymap({mod_tap_lgui, mod_tap_lsft});
+
+ EXPECT_REPORT(driver, (KC_LSFT));
+ mod_tap_lsft.press();
+ run_one_scan_loop();
+ VERIFY_AND_CLEAR(driver);
+
+ EXPECT_REPORT(driver, (KC_LSFT, KC_LGUI));
+ mod_tap_lgui.press();
+ idle_for(TAPPING_TERM);
+ run_one_scan_loop();
+ VERIFY_AND_CLEAR(driver);
+
+ EXPECT_REPORT(driver, (KC_LGUI));
+ mod_tap_lsft.release();
+ run_one_scan_loop();
+ VERIFY_AND_CLEAR(driver);
+
+ // Neutralizer invoked by Retro Tapping.
+ EXPECT_REPORT(driver, (KC_LGUI, DUMMY_MOD_NEUTRALIZER_KEYCODE));
+ EXPECT_REPORT(driver, (KC_LGUI));
+ EXPECT_EMPTY_REPORT(driver);
+ EXPECT_REPORT(driver, (KC_LSFT));
+ EXPECT_REPORT(driver, (KC_P, KC_LSFT));
+ EXPECT_REPORT(driver, (KC_LSFT));
+ EXPECT_EMPTY_REPORT(driver);
+ mod_tap_lgui.release();
+ run_one_scan_loop();
+ VERIFY_AND_CLEAR(driver);
+}
+
+TEST_F(SpeculativeHoldRetroTappingTest, mod_under_tap_term_to_mod_over_tap_term_offset) {
+ TestDriver driver;
+ InSequence s;
+ auto mod_tap_lgui = KeymapKey(0, 1, 0, LGUI_T(KC_P));
+ auto mod_tap_lsft = KeymapKey(0, 2, 0, LSFT_T(KC_A));
+
+ set_keymap({mod_tap_lgui, mod_tap_lsft});
+
+ EXPECT_REPORT(driver, (KC_LSFT));
+ mod_tap_lsft.press();
+ run_one_scan_loop();
+ VERIFY_AND_CLEAR(driver);
+
+ EXPECT_REPORT(driver, (KC_LSFT, KC_LGUI));
+ mod_tap_lgui.press();
+ run_one_scan_loop();
+ VERIFY_AND_CLEAR(driver);
+
+ EXPECT_EMPTY_REPORT(driver);
+ EXPECT_REPORT(driver, (KC_A));
+ EXPECT_EMPTY_REPORT(driver);
+ mod_tap_lsft.release();
+ run_one_scan_loop();
+ VERIFY_AND_CLEAR(driver);
+
+ EXPECT_REPORT(driver, (KC_LGUI));
+ // Neutralizer invoked by Retro Tapping.
+ EXPECT_REPORT(driver, (KC_LGUI, DUMMY_MOD_NEUTRALIZER_KEYCODE));
+ EXPECT_REPORT(driver, (KC_LGUI));
+ EXPECT_EMPTY_REPORT(driver);
+ EXPECT_REPORT(driver, (KC_P));
+ EXPECT_EMPTY_REPORT(driver);
+ idle_for(TAPPING_TERM);
+ mod_tap_lgui.release();
+ run_one_scan_loop();
+ VERIFY_AND_CLEAR(driver);
+}
+
+TEST_F(SpeculativeHoldRetroTappingTest, mod_over_tap_term_to_mod_over_tap_term) {
+ TestDriver driver;
+ InSequence s;
+ auto mod_tap_lgui = KeymapKey(0, 1, 0, LGUI_T(KC_P));
+ auto mod_tap_lsft = KeymapKey(0, 2, 0, LSFT_T(KC_A));
+
+ set_keymap({mod_tap_lgui, mod_tap_lsft});
+
+ EXPECT_REPORT(driver, (KC_LSFT));
+ mod_tap_lsft.press();
+ idle_for(TAPPING_TERM);
+ run_one_scan_loop();
+ VERIFY_AND_CLEAR(driver);
+
+ EXPECT_REPORT(driver, (KC_LSFT, KC_LGUI));
+ mod_tap_lgui.press();
+ idle_for(TAPPING_TERM);
+ run_one_scan_loop();
+ VERIFY_AND_CLEAR(driver);
+
+ EXPECT_REPORT(driver, (KC_LGUI));
+ mod_tap_lsft.release();
+ run_one_scan_loop();
+ VERIFY_AND_CLEAR(driver);
+
+ // Neutralizer invoked by Retro Tapping.
+ EXPECT_REPORT(driver, (KC_LGUI, DUMMY_MOD_NEUTRALIZER_KEYCODE));
+ EXPECT_REPORT(driver, (KC_LGUI));
+ EXPECT_EMPTY_REPORT(driver);
+ EXPECT_REPORT(driver, (KC_LSFT));
+ EXPECT_REPORT(driver, (KC_P, KC_LSFT));
+ EXPECT_REPORT(driver, (KC_LSFT));
+ EXPECT_EMPTY_REPORT(driver);
+ mod_tap_lgui.release();
+ run_one_scan_loop();
+ VERIFY_AND_CLEAR(driver);
+}
+
+TEST_F(SpeculativeHoldRetroTappingTest, mod_to_mod_to_mod) {
+ TestDriver driver;
+ InSequence s;
+ auto mod_tap_lalt = KeymapKey(0, 1, 0, LALT_T(KC_R));
+ auto mod_tap_lsft = KeymapKey(0, 2, 0, SFT_T(KC_A));
+ auto mod_tap_lctl = KeymapKey(0, 3, 0, LCTL_T(KC_C));
+
+ set_keymap({mod_tap_lalt, mod_tap_lsft, mod_tap_lctl});
+
+ EXPECT_REPORT(driver, (KC_LALT));
+ mod_tap_lalt.press();
+ idle_for(TAPPING_TERM);
+ run_one_scan_loop();
+ VERIFY_AND_CLEAR(driver);
+
+ EXPECT_REPORT(driver, (KC_LSFT, KC_LALT));
+ mod_tap_lsft.press();
+ idle_for(TAPPING_TERM);
+ run_one_scan_loop();
+ VERIFY_AND_CLEAR(driver);
+
+ EXPECT_REPORT(driver, (KC_LSFT));
+ mod_tap_lalt.release();
+ run_one_scan_loop();
+ VERIFY_AND_CLEAR(driver);
+
+ EXPECT_REPORT(driver, (KC_LCTL, KC_LSFT));
+ mod_tap_lctl.press();
+ idle_for(TAPPING_TERM);
+ run_one_scan_loop();
+ VERIFY_AND_CLEAR(driver);
+
+ EXPECT_REPORT(driver, (KC_LCTL));
+ mod_tap_lsft.release();
+ run_one_scan_loop();
+ VERIFY_AND_CLEAR(driver);
+
+ EXPECT_EMPTY_REPORT(driver);
+ EXPECT_REPORT(driver, (KC_LSFT));
+ EXPECT_REPORT(driver, (KC_C, KC_LSFT));
+ EXPECT_REPORT(driver, (KC_LSFT));
+ EXPECT_EMPTY_REPORT(driver);
+ mod_tap_lctl.release();
+ run_one_scan_loop();
+ VERIFY_AND_CLEAR(driver);
+}
+
+// Test with layer tap and speculative mod tap keys on the same layer, rolling
+// from LT to MT key:
+// "LT down, MT down, (wait out tapping term), LT up, MT up."
+TEST_F(SpeculativeHoldRetroTappingTest, lt_mt_same_layer_roll) {
+ TestDriver driver;
+ InSequence s;
+ auto layer_tap_key = KeymapKey(0, 0, 0, LT(1, KC_A));
+ auto mod_tap_key = KeymapKey(0, 1, 0, LCTL_T(KC_B));
+ auto regular_key = KeymapKey(1, 1, 0, KC_C);
+
+ set_keymap({layer_tap_key, mod_tap_key, regular_key});
+
+ // Press layer tap key.
+ EXPECT_NO_REPORT(driver);
+ layer_tap_key.press();
+ run_one_scan_loop();
+ VERIFY_AND_CLEAR(driver);
+
+ // Press mod-tap key, after flow tap term but within tapping term. The
+ // speculative mod activates.
+ EXPECT_REPORT(driver, (KC_LCTL));
+ mod_tap_key.press();
+ run_one_scan_loop();
+ VERIFY_AND_CLEAR(driver);
+
+ // Wait for the layer tap key to settle.
+ EXPECT_EMPTY_REPORT(driver);
+ EXPECT_REPORT(driver, (KC_C));
+ idle_for(TAPPING_TERM);
+ VERIFY_AND_CLEAR(driver);
+
+ // Release keys.
+ EXPECT_EMPTY_REPORT(driver);
+ layer_tap_key.release();
+ run_one_scan_loop();
+ mod_tap_key.release();
+ run_one_scan_loop();
+ VERIFY_AND_CLEAR(driver);
+ // All mods are released.
+ EXPECT_EQ(get_mods() | get_speculative_mods(), 0);
+}
+
+// Test with layer tap and speculative mod tap keys on the same layer, trying a
+// nested press:
+// "LT down, MT down, (wait out tapping term), MT up, LT up."
+TEST_F(SpeculativeHoldRetroTappingTest, lt_mt_same_layer_nested_press) {
+ TestDriver driver;
+ InSequence s;
+ auto layer_tap_key = KeymapKey(0, 0, 0, LT(1, KC_A));
+ auto mod_tap_key = KeymapKey(0, 1, 0, LCTL_T(KC_B));
+ auto regular_key = KeymapKey(1, 1, 0, KC_C);
+
+ set_keymap({layer_tap_key, mod_tap_key, regular_key});
+
+ EXPECT_NO_REPORT(driver);
+ layer_tap_key.press();
+ run_one_scan_loop();
+ VERIFY_AND_CLEAR(driver);
+
+ EXPECT_REPORT(driver, (KC_LCTL));
+ mod_tap_key.press();
+ run_one_scan_loop();
+ VERIFY_AND_CLEAR(driver);
+
+ EXPECT_EMPTY_REPORT(driver);
+ EXPECT_REPORT(driver, (KC_C));
+ idle_for(TAPPING_TERM);
+ VERIFY_AND_CLEAR(driver);
+
+ // Release keys: MT first, LT second.
+ EXPECT_EMPTY_REPORT(driver);
+ mod_tap_key.release();
+ run_one_scan_loop();
+ layer_tap_key.release();
+ run_one_scan_loop();
+ VERIFY_AND_CLEAR(driver);
+ // All mods are released.
+ EXPECT_EQ(get_mods() | get_speculative_mods(), 0);
+}
+
+// Test with layer tap and speculative mod tap keys on the same layer, trying a
+// nested press with the MT first:
+// "MT down, LT down, (wait out tapping term), LT up, MT up."
+TEST_F(SpeculativeHoldRetroTappingTest, mt_lt_same_layer_nested_press) {
+ TestDriver driver;
+ InSequence s;
+ auto layer_tap_key = KeymapKey(0, 0, 0, LT(1, KC_A));
+ auto mod_tap_key = KeymapKey(0, 1, 0, LCTL_T(KC_B));
+ auto regular_key = KeymapKey(1, 1, 0, KC_C);
+
+ set_keymap({layer_tap_key, mod_tap_key, regular_key});
+
+ EXPECT_REPORT(driver, (KC_LCTL));
+ mod_tap_key.press();
+ run_one_scan_loop();
+
+ EXPECT_NO_REPORT(driver);
+ layer_tap_key.press();
+ idle_for(TAPPING_TERM + 1);
+ VERIFY_AND_CLEAR(driver);
+
+ EXPECT_REPORT(driver, (KC_LCTL, KC_A));
+ EXPECT_REPORT(driver, (KC_LCTL));
+ EXPECT_EMPTY_REPORT(driver);
+ layer_tap_key.release();
+ run_one_scan_loop();
+ mod_tap_key.release();
+ run_one_scan_loop();
+ VERIFY_AND_CLEAR(driver);
+ // All mods are released.
+ EXPECT_EQ(get_mods() | get_speculative_mods(), 0);
+}
+
+// Test with a speculative mod tap key reached by a layer tap key, rolling from
+// LT to MT key:
+// "LT down, MT down, (wait out tapping term), LT up, MT up."
+TEST_F(SpeculativeHoldRetroTappingTest, lt_mt_different_layer_roll) {
+ TestDriver driver;
+ InSequence s;
+ auto layer_tap_key = KeymapKey(0, 0, 0, LT(1, KC_A));
+ auto regular_key = KeymapKey(0, 1, 0, KC_B);
+ auto placeholder_key = KeymapKey(1, 0, 0, KC_NO);
+ auto mod_tap_key = KeymapKey(1, 1, 0, LCTL_T(KC_C));
+
+ set_keymap({layer_tap_key, regular_key, placeholder_key, mod_tap_key});
+
+ // Press layer tap key.
+ EXPECT_NO_REPORT(driver);
+ layer_tap_key.press();
+ run_one_scan_loop();
+ // Press mod tap key.
+ mod_tap_key.press();
+ idle_for(TAPPING_TERM);
+ VERIFY_AND_CLEAR(driver);
+
+ // Release keys.
+ EXPECT_REPORT(driver, (KC_LCTL));
+ layer_tap_key.release();
+ idle_for(TAPPING_TERM);
+ VERIFY_AND_CLEAR(driver);
+
+ EXPECT_EMPTY_REPORT(driver);
+ mod_tap_key.release();
+ run_one_scan_loop();
+ VERIFY_AND_CLEAR(driver);
+ // All mods are released.
+ EXPECT_EQ(get_mods() | get_speculative_mods(), 0);
+}
+
+// Test with a speculative mod tap key reached by a layer tap key, slowly
+// rolling from LT to MT key:
+// "LT down, (wait), MT down, (wait), LT up, MT up."
+TEST_F(SpeculativeHoldRetroTappingTest, lt_mt_different_layer_slow_roll) {
+ TestDriver driver;
+ InSequence s;
+ auto layer_tap_key = KeymapKey(0, 0, 0, LT(1, KC_A));
+ auto regular_key = KeymapKey(0, 1, 0, KC_B);
+ auto placeholder_key = KeymapKey(1, 0, 0, KC_NO);
+ auto mod_tap_key = KeymapKey(1, 1, 0, LCTL_T(KC_C));
+
+ set_keymap({layer_tap_key, regular_key, placeholder_key, mod_tap_key});
+
+ EXPECT_REPORT(driver, (KC_LCTL));
+ layer_tap_key.press();
+ idle_for(TAPPING_TERM + 1);
+ mod_tap_key.press();
+ run_one_scan_loop();
+ VERIFY_AND_CLEAR(driver);
+
+ EXPECT_EMPTY_REPORT(driver);
+ EXPECT_REPORT(driver, (KC_B));
+ EXPECT_EMPTY_REPORT(driver);
+ layer_tap_key.release();
+ run_one_scan_loop();
+ mod_tap_key.release();
+ run_one_scan_loop();
+ VERIFY_AND_CLEAR(driver);
+ // All mods are released.
+ EXPECT_EQ(get_mods() | get_speculative_mods(), 0);
+}
+
+// Test with a speculative mod tap key reached by a layer tap key, trying a
+// nested press:
+// "LT down, MT down, (wait out tapping term), MT up, LT up."
+TEST_F(SpeculativeHoldRetroTappingTest, lt_mt_different_layer_nested_press) {
+ TestDriver driver;
+ InSequence s;
+ auto layer_tap_key = KeymapKey(0, 0, 0, LT(1, KC_A));
+ auto regular_key = KeymapKey(0, 1, 0, KC_B);
+ auto placeholder_key = KeymapKey(1, 0, 0, KC_NO);
+ auto mod_tap_key = KeymapKey(1, 1, 0, LCTL_T(KC_C));
+
+ set_keymap({layer_tap_key, regular_key, placeholder_key, mod_tap_key});
+
+ EXPECT_NO_REPORT(driver);
+ layer_tap_key.press();
+ run_one_scan_loop();
+ mod_tap_key.press();
+ idle_for(TAPPING_TERM);
+ VERIFY_AND_CLEAR(driver);
+
+ // Release keys.
+ EXPECT_REPORT(driver, (KC_LCTL));
+ EXPECT_EMPTY_REPORT(driver);
+ mod_tap_key.release();
+ run_one_scan_loop();
+ layer_tap_key.release();
+ run_one_scan_loop();
+ VERIFY_AND_CLEAR(driver);
+ // All mods are released.
+ EXPECT_EQ(get_mods() | get_speculative_mods(), 0);
+}
+
+// Test with a speculative mod tap key reached by a layer tap key, slowly making
+// a nested press from LT to MT key:
+// "LT down, (wait), MT down, MT up, LT up."
+TEST_F(SpeculativeHoldRetroTappingTest, lt_mt_different_layer_slow_nested_press) {
+ TestDriver driver;
+ InSequence s;
+ auto layer_tap_key = KeymapKey(0, 0, 0, LT(1, KC_A));
+ auto regular_key = KeymapKey(0, 1, 0, KC_B);
+ auto placeholder_key = KeymapKey(1, 0, 0, KC_NO);
+ auto mod_tap_key = KeymapKey(1, 1, 0, LCTL_T(KC_C));
+
+ set_keymap({layer_tap_key, regular_key, placeholder_key, mod_tap_key});
+
+ EXPECT_REPORT(driver, (KC_LCTL));
+ layer_tap_key.press();
+ idle_for(TAPPING_TERM + 1);
+ mod_tap_key.press();
+ run_one_scan_loop();
+ VERIFY_AND_CLEAR(driver);
+
+ EXPECT_EMPTY_REPORT(driver);
+ EXPECT_REPORT(driver, (KC_C));
+ EXPECT_EMPTY_REPORT(driver);
+ mod_tap_key.release();
+ run_one_scan_loop();
+ layer_tap_key.release();
+ run_one_scan_loop();
+ VERIFY_AND_CLEAR(driver);
+ // All mods are released.
+ EXPECT_EQ(get_mods() | get_speculative_mods(), 0);
+}
--
cgit v1.2.3