summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--.github/workflows/build.yml8
-rw-r--r--.github/workflows/doc-checks.yml35
-rw-r--r--app/CMakeLists.txt3
-rw-r--r--app/boards/arm/bluemicro840/bluemicro840_v1.dts23
-rw-r--r--app/boards/arm/nice_nano/nice_nano.dts23
-rw-r--r--app/boards/arm/nrf52840_m2/nrf52840_m2.dts21
-rw-r--r--app/boards/arm/nrfmicro/nrfmicro_11.dts21
-rw-r--r--app/boards/arm/nrfmicro/nrfmicro_11_flipped.dts23
-rw-r--r--app/boards/arm/nrfmicro/nrfmicro_13.dts23
-rw-r--r--app/drivers/zephyr/battery_voltage_divider.c4
-rw-r--r--app/dts/behaviors.dtsi1
-rw-r--r--app/dts/behaviors/outputs.dtsi9
-rw-r--r--app/dts/bindings/behaviors/zmk,behavior-outputs.yaml10
-rw-r--r--app/include/dt-bindings/zmk/keys.h12
-rw-r--r--app/include/dt-bindings/zmk/modifiers.h28
-rw-r--r--app/include/dt-bindings/zmk/outputs.h9
-rw-r--r--app/include/zmk/ble.h1
-rw-r--r--app/include/zmk/endpoints.h8
-rw-r--r--app/include/zmk/events/keycode-state-changed.h11
-rw-r--r--app/include/zmk/events/usb-conn-state-changed.h20
-rw-r--r--app/include/zmk/handlers.h2
-rw-r--r--app/include/zmk/hid.h63
-rw-r--r--app/include/zmk/usb.h12
-rw-r--r--app/src/battery.c75
-rw-r--r--app/src/behaviors/behavior_hold_tap.c6
-rw-r--r--app/src/behaviors/behavior_outputs.c44
-rw-r--r--app/src/ble.c26
-rw-r--r--app/src/endpoints.c236
-rw-r--r--app/src/events/usb_conn_state_changed.c10
-rw-r--r--app/src/hid.c103
-rw-r--r--app/src/hid_listener.c51
-rw-r--r--app/src/keymap.c1
-rw-r--r--app/src/power.c9
-rw-r--r--app/src/rgb_underglow.c12
-rw-r--r--app/src/usb.c30
-rw-r--r--app/tests/hold-tap/balanced/1-dn-up/keycode_events.snapshot4
-rw-r--r--app/tests/hold-tap/balanced/2-dn-timer-up/keycode_events.snapshot4
-rw-r--r--app/tests/hold-tap/balanced/3a-moddn-dn-modup-up/keycode_events.snapshot8
-rw-r--r--app/tests/hold-tap/balanced/3b-moddn-dn-modup-timer-up/keycode_events.snapshot8
-rw-r--r--app/tests/hold-tap/balanced/3c-kcdn-dn-kcup-up/keycode_events.snapshot8
-rw-r--r--app/tests/hold-tap/balanced/3d-kcdn-dn-kcup-timer-up/keycode_events.snapshot8
-rw-r--r--app/tests/hold-tap/balanced/4a-dn-htdn-timer-htup-up/keycode_events.snapshot8
-rw-r--r--app/tests/hold-tap/balanced/4a-dn-kcdn-timer-kcup-up/keycode_events.snapshot8
-rw-r--r--app/tests/hold-tap/balanced/4b-dn-kcdn-kcup-timer-up/keycode_events.snapshot8
-rw-r--r--app/tests/hold-tap/balanced/4c-dn-kcdn-kcup-up/keycode_events.snapshot8
-rw-r--r--app/tests/hold-tap/balanced/4d-dn-kcdn-timer-up-kcup/keycode_events.snapshot8
-rw-r--r--app/tests/hold-tap/balanced/many-nested/keycode_events.snapshot16
-rw-r--r--app/tests/hold-tap/hold-preferred/1-dn-up/keycode_events.snapshot4
-rw-r--r--app/tests/hold-tap/hold-preferred/2-dn-timer-up/keycode_events.snapshot4
-rw-r--r--app/tests/hold-tap/hold-preferred/3a-moddn-dn-modup-up/keycode_events.snapshot8
-rw-r--r--app/tests/hold-tap/hold-preferred/3b-moddn-dn-modup-timer-up/keycode_events.snapshot8
-rw-r--r--app/tests/hold-tap/hold-preferred/3c-kcdn-dn-kcup-up/keycode_events.snapshot8
-rw-r--r--app/tests/hold-tap/hold-preferred/3d-kcdn-dn-kcup-timer-up/keycode_events.snapshot8
-rw-r--r--app/tests/hold-tap/hold-preferred/4a-dn-htdn-timer-htup-up/keycode_events.snapshot8
-rw-r--r--app/tests/hold-tap/hold-preferred/4a-dn-kcdn-timer-kcup-up/keycode_events.snapshot8
-rw-r--r--app/tests/hold-tap/hold-preferred/4b-dn-kcdn-kcup-timer-up/keycode_events.snapshot8
-rw-r--r--app/tests/hold-tap/hold-preferred/4c-dn-kcdn-kcup-up/keycode_events.snapshot8
-rw-r--r--app/tests/hold-tap/hold-preferred/4d-dn-kcdn-timer-up-kcup/keycode_events.snapshot8
-rw-r--r--app/tests/hold-tap/tap-preferred/1-dn-up/keycode_events.snapshot4
-rw-r--r--app/tests/hold-tap/tap-preferred/2-dn-timer-up/keycode_events.snapshot4
-rw-r--r--app/tests/hold-tap/tap-preferred/3a-moddn-dn-modup-up/keycode_events.snapshot8
-rw-r--r--app/tests/hold-tap/tap-preferred/3b-moddn-dn-modup-timer-up/keycode_events.snapshot8
-rw-r--r--app/tests/hold-tap/tap-preferred/3c-kcdn-dn-kcup-up/keycode_events.snapshot8
-rw-r--r--app/tests/hold-tap/tap-preferred/3d-kcdn-dn-kcup-timer-up/keycode_events.snapshot8
-rw-r--r--app/tests/hold-tap/tap-preferred/4a-dn-htdn-timer-htup-up/keycode_events.snapshot8
-rw-r--r--app/tests/hold-tap/tap-preferred/4a-dn-kcdn-timer-kcup-up/keycode_events.snapshot8
-rw-r--r--app/tests/hold-tap/tap-preferred/4b-dn-kcdn-kcup-timer-up/keycode_events.snapshot8
-rw-r--r--app/tests/hold-tap/tap-preferred/4c-dn-kcdn-kcup-up/keycode_events.snapshot8
-rw-r--r--app/tests/hold-tap/tap-preferred/4d-dn-kcdn-timer-up-kcup/keycode_events.snapshot8
-rw-r--r--app/tests/keypress/behavior_keymap.dtsi16
-rw-r--r--app/tests/keypress/cp-press-release/keycode_events.snapshot4
-rw-r--r--app/tests/keypress/cp-press-release/native_posix.keymap2
-rw-r--r--app/tests/keypress/kp-press-release/keycode_events.snapshot4
-rw-r--r--app/tests/keypress/kp-press-release/native_posix.keymap5
-rw-r--r--app/tests/modifiers/explicit/kp-lctl-dn-lctl-dn-lctl-up-lctl-up/events.patterns4
-rw-r--r--app/tests/modifiers/explicit/kp-lctl-dn-lctl-dn-lctl-up-lctl-up/keycode_events.snapshot17
-rw-r--r--app/tests/modifiers/explicit/kp-lctl-dn-lctl-dn-lctl-up-lctl-up/native_posix.keymap27
-rw-r--r--app/tests/modifiers/explicit/kp-lctl-dn-lctl-up/events.patterns4
-rw-r--r--app/tests/modifiers/explicit/kp-lctl-dn-lctl-up/keycode_events.snapshot9
-rw-r--r--app/tests/modifiers/explicit/kp-lctl-dn-lctl-up/native_posix.keymap25
-rw-r--r--app/tests/modifiers/explicit/kp-lctl-dn-lsft-dn-lctl-up-lsft-up/events.patterns4
-rw-r--r--app/tests/modifiers/explicit/kp-lctl-dn-lsft-dn-lctl-up-lsft-up/keycode_events.snapshot18
-rw-r--r--app/tests/modifiers/explicit/kp-lctl-dn-lsft-dn-lctl-up-lsft-up/native_posix.keymap27
-rw-r--r--app/tests/modifiers/explicit/kp-lctl-dn-lsft-dn-lsft-up-lctl-up/events.patterns4
-rw-r--r--app/tests/modifiers/explicit/kp-lctl-dn-lsft-dn-lsft-up-lctl-up/keycode_events.snapshot18
-rw-r--r--app/tests/modifiers/explicit/kp-lctl-dn-lsft-dn-lsft-up-lctl-up/native_posix.keymap28
-rw-r--r--app/tests/modifiers/implicit/kp-mod1-dn-mod2-dn-mod1-up-mod2-up/events.patterns4
-rw-r--r--app/tests/modifiers/implicit/kp-mod1-dn-mod2-dn-mod1-up-mod2-up/keycode_events.snapshot8
-rw-r--r--app/tests/modifiers/implicit/kp-mod1-dn-mod2-dn-mod1-up-mod2-up/native_posix.keymap27
-rw-r--r--app/tests/modifiers/implicit/kp-mod1-dn-mod2-dn-mod1-up-mod2-up/pending9
-rw-r--r--app/tests/modifiers/implicit/kp-mod1-dn-mod2-dn-mod2-up-mod1-up/events.patterns4
-rw-r--r--app/tests/modifiers/implicit/kp-mod1-dn-mod2-dn-mod2-up-mod1-up/keycode_events.snapshot8
-rw-r--r--app/tests/modifiers/implicit/kp-mod1-dn-mod2-dn-mod2-up-mod1-up/native_posix.keymap27
-rw-r--r--app/tests/modifiers/mixed/kp-lctl-dn-mod-dn-lctl-up-mod-up/events.patterns4
-rw-r--r--app/tests/modifiers/mixed/kp-lctl-dn-mod-dn-lctl-up-mod-up/keycode_events.snapshot13
-rw-r--r--app/tests/modifiers/mixed/kp-lctl-dn-mod-dn-lctl-up-mod-up/native_posix.keymap27
-rw-r--r--app/tests/modifiers/mixed/kp-lctl-dn-mod-dn-mod-up-lctl-up/events.patterns4
-rw-r--r--app/tests/modifiers/mixed/kp-lctl-dn-mod-dn-mod-up-lctl-up/keycode_events.snapshot13
-rw-r--r--app/tests/modifiers/mixed/kp-lctl-dn-mod-dn-mod-up-lctl-up/native_posix.keymap27
-rw-r--r--app/tests/momentary-layer/early-key-release/keycode_events.snapshot4
-rw-r--r--app/tests/momentary-layer/normal/keycode_events.snapshot4
-rw-r--r--app/tests/toggle-layer/early-key-release/keycode_events.snapshot8
-rw-r--r--app/tests/toggle-layer/normal/keycode_events.snapshot4
-rw-r--r--app/tests/transparent/layered/keycode_events.snapshot4
-rw-r--r--docs/.nvmrc1
-rw-r--r--docs/blog/2020-10-03-bootloader-fix.md195
-rw-r--r--docs/docs/behavior/outputs.md59
-rw-r--r--docs/docs/dev-guide-new-shield.md15
-rw-r--r--docs/docs/dev-guide-usb-logging.md26
-rw-r--r--docs/docs/feature/keymaps.md4
-rw-r--r--docs/docs/intro.md2
-rw-r--r--docs/docs/user-setup.md7
-rw-r--r--docs/package.json5
-rw-r--r--docs/sidebars.js1
114 files changed, 1562 insertions, 397 deletions
diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml
index e22dc65..e6be31a 100644
--- a/.github/workflows/build.yml
+++ b/.github/workflows/build.yml
@@ -1,4 +1,10 @@
-on: [push, pull_request]
+on:
+ push:
+ paths:
+ - "app/**"
+ pull_request:
+ paths:
+ - "app/**"
name: Build
diff --git a/.github/workflows/doc-checks.yml b/.github/workflows/doc-checks.yml
new file mode 100644
index 0000000..a31a88e
--- /dev/null
+++ b/.github/workflows/doc-checks.yml
@@ -0,0 +1,35 @@
+name: doc-checks
+
+on:
+ push:
+ paths:
+ - "docs/**"
+ pull_request:
+ paths:
+ - "docs/**"
+
+jobs:
+ lint:
+ runs-on: ubuntu-latest
+ name: ESLint
+
+ steps:
+ - uses: actions/checkout@v2
+ - uses: bahmutov/npm-install@v1
+ with:
+ working-directory: docs
+ - name: ESLint
+ run: npm run lint
+ working-directory: docs
+ prettier:
+ runs-on: ubuntu-latest
+ name: Prettier
+
+ steps:
+ - uses: actions/checkout@v2
+ - uses: bahmutov/npm-install@v1
+ with:
+ working-directory: docs
+ - name: Prettier Check
+ run: npm run prettier:check
+ working-directory: docs
diff --git a/app/CMakeLists.txt b/app/CMakeLists.txt
index 8a3971e..7a12a81 100644
--- a/app/CMakeLists.txt
+++ b/app/CMakeLists.txt
@@ -37,11 +37,13 @@ target_sources(app PRIVATE src/events/keycode_state_changed.c)
target_sources(app PRIVATE src/events/modifiers_state_changed.c)
target_sources(app PRIVATE src/events/sensor_event.c)
target_sources_ifdef(CONFIG_ZMK_BLE app PRIVATE src/events/ble_active_profile_changed.c)
+target_sources_ifdef(CONFIG_USB app PRIVATE src/events/usb_conn_state_changed.c)
if (NOT CONFIG_ZMK_SPLIT_BLE_ROLE_PERIPHERAL)
target_sources(app PRIVATE src/behaviors/behavior_key_press.c)
target_sources(app PRIVATE src/behaviors/behavior_reset.c)
target_sources(app PRIVATE src/behaviors/behavior_hold_tap.c)
target_sources(app PRIVATE src/behaviors/behavior_momentary_layer.c)
+ target_sources(app PRIVATE src/behaviors/behavior_outputs.c)
target_sources(app PRIVATE src/behaviors/behavior_toggle_layer.c)
target_sources(app PRIVATE src/behaviors/behavior_transparent.c)
target_sources(app PRIVATE src/behaviors/behavior_none.c)
@@ -52,6 +54,7 @@ endif()
target_sources_ifdef(CONFIG_ZMK_RGB_UNDERGLOW app PRIVATE src/behaviors/behavior_rgb_underglow.c)
target_sources_ifdef(CONFIG_ZMK_BLE app PRIVATE src/behaviors/behavior_bt.c)
target_sources_ifdef(CONFIG_ZMK_BLE app PRIVATE src/ble.c)
+target_sources_ifdef(CONFIG_ZMK_BLE app PRIVATE src/battery.c)
target_sources_ifdef(CONFIG_ZMK_SPLIT_BLE_ROLE_PERIPHERAL app PRIVATE src/split_listener.c)
target_sources_ifdef(CONFIG_ZMK_SPLIT_BLE_ROLE_PERIPHERAL app PRIVATE src/split/bluetooth/service.c)
target_sources_ifdef(CONFIG_ZMK_SPLIT_BLE_ROLE_CENTRAL app PRIVATE src/split/bluetooth/central.c)
diff --git a/app/boards/arm/bluemicro840/bluemicro840_v1.dts b/app/boards/arm/bluemicro840/bluemicro840_v1.dts
index 32aa2e6..141dbe1 100644
--- a/app/boards/arm/bluemicro840/bluemicro840_v1.dts
+++ b/app/boards/arm/bluemicro840/bluemicro840_v1.dts
@@ -31,7 +31,7 @@
vbatt {
compatible = "zmk,battery-voltage-divider";
- label = "VOLTAGE_DIVIDER";
+ label = "BATTERY";
io-channels = <&adc 7>;
output-ohms = <2000000>;
full-ohms = <(2000000 + 806000)>;
@@ -82,27 +82,32 @@
#address-cells = <1>;
#size-cells = <1>;
- boot_partition: partition@0 {
- label = "adafruit_boot";
- reg = <0x000000000 0x0000C000>;
+ sd_partition: partition@0 {
+ label = "softdevice";
+ reg = <0x00000000 0x00026000>;
};
code_partition: partition@26000 {
label = "code_partition";
- reg = <0x00026000 0x000d2000>;
+ reg = <0x00026000 0x000c6000>;
};
/*
- * The flash starting at 0x000f8000 and ending at
- * 0x000fffff is reserved for use by the application.
+ * The flash starting at 0x000ec000 and ending at
+ * 0x000f3fff is reserved for use by the application.
*/
/*
* Storage partition will be used by FCB/LittleFS/NVS
* if enabled.
*/
- storage_partition: partition@f8000 {
+ storage_partition: partition@ec000 {
label = "storage";
- reg = <0x000f8000 0x00008000>;
+ reg = <0x000ec000 0x00008000>;
+ };
+
+ boot_partition: partition@f4000 {
+ label = "adafruit_boot";
+ reg = <0x000f4000 0x0000c000>;
};
};
};
diff --git a/app/boards/arm/nice_nano/nice_nano.dts b/app/boards/arm/nice_nano/nice_nano.dts
index 1819541..a9bdeb3 100644
--- a/app/boards/arm/nice_nano/nice_nano.dts
+++ b/app/boards/arm/nice_nano/nice_nano.dts
@@ -37,7 +37,7 @@
vbatt {
compatible = "zmk,battery-voltage-divider";
- label = "VOLTAGE_DIVIDER";
+ label = "BATTERY";
io-channels = <&adc 2>;
output-ohms = <2000000>;
full-ohms = <(2000000 + 806000)>;
@@ -91,27 +91,32 @@
#address-cells = <1>;
#size-cells = <1>;
- boot_partition: partition@0 {
- label = "adafruit_boot";
- reg = <0x000000000 0x0000C000>;
+ sd_partition: partition@0 {
+ label = "softdevice";
+ reg = <0x00000000 0x00026000>;
};
code_partition: partition@26000 {
label = "code_partition";
- reg = <0x00026000 0x000d2000>;
+ reg = <0x00026000 0x000c6000>;
};
/*
- * The flash starting at 0x000f8000 and ending at
- * 0x000fffff is reserved for use by the application.
+ * The flash starting at 0x000ec000 and ending at
+ * 0x000f3fff is reserved for use by the application.
*/
/*
* Storage partition will be used by FCB/LittleFS/NVS
* if enabled.
*/
- storage_partition: partition@f8000 {
+ storage_partition: partition@ec000 {
label = "storage";
- reg = <0x000f8000 0x00008000>;
+ reg = <0x000ec000 0x00008000>;
+ };
+
+ boot_partition: partition@f4000 {
+ label = "adafruit_boot";
+ reg = <0x000f4000 0x0000c000>;
};
};
};
diff --git a/app/boards/arm/nrf52840_m2/nrf52840_m2.dts b/app/boards/arm/nrf52840_m2/nrf52840_m2.dts
index fb5b0ff..e998b39 100644
--- a/app/boards/arm/nrf52840_m2/nrf52840_m2.dts
+++ b/app/boards/arm/nrf52840_m2/nrf52840_m2.dts
@@ -71,27 +71,32 @@
#address-cells = <1>;
#size-cells = <1>;
- boot_partition: partition@0 {
- label = "adafruit_boot";
- reg = <0x000000000 0x0000C000>;
+ sd_partition: partition@0 {
+ label = "softdevice";
+ reg = <0x00000000 0x00026000>;
};
code_partition: partition@26000 {
label = "code_partition";
- reg = <0x00026000 0x000d2000>;
+ reg = <0x00026000 0x000c6000>;
};
/*
- * The flash starting at 0x000f8000 and ending at
- * 0x000fffff is reserved for use by the application.
+ * The flash starting at 0x000ec000 and ending at
+ * 0x000f3fff is reserved for use by the application.
*/
/*
* Storage partition will be used by FCB/LittleFS/NVS
* if enabled.
*/
- storage_partition: partition@f8000 {
+ storage_partition: partition@ec000 {
label = "storage";
- reg = <0x000f8000 0x00008000>;
+ reg = <0x000ec000 0x00008000>;
+ };
+
+ boot_partition: partition@f4000 {
+ label = "adafruit_boot";
+ reg = <0x000f4000 0x0000c000>;
};
};
};
diff --git a/app/boards/arm/nrfmicro/nrfmicro_11.dts b/app/boards/arm/nrfmicro/nrfmicro_11.dts
index 87c650e..0cec662 100644
--- a/app/boards/arm/nrfmicro/nrfmicro_11.dts
+++ b/app/boards/arm/nrfmicro/nrfmicro_11.dts
@@ -72,27 +72,32 @@
#address-cells = <1>;
#size-cells = <1>;
- boot_partition: partition@0 {
- label = "adafruit_boot";
- reg = <0x000000000 0x0000C000>;
+ sd_partition: partition@0 {
+ label = "softdevice";
+ reg = <0x00000000 0x00026000>;
};
code_partition: partition@26000 {
label = "code_partition";
- reg = <0x00026000 0x000d2000>;
+ reg = <0x00026000 0x000c6000>;
};
/*
- * The flash starting at 0x000f8000 and ending at
- * 0x000fffff is reserved for use by the application.
+ * The flash starting at 0x000ec000 and ending at
+ * 0x000f3fff is reserved for use by the application.
*/
/*
* Storage partition will be used by FCB/LittleFS/NVS
* if enabled.
*/
- storage_partition: partition@f8000 {
+ storage_partition: partition@ec000 {
label = "storage";
- reg = <0x000f8000 0x00008000>;
+ reg = <0x000ec000 0x00008000>;
+ };
+
+ boot_partition: partition@f4000 {
+ label = "adafruit_boot";
+ reg = <0x000f4000 0x0000c000>;
};
};
};
diff --git a/app/boards/arm/nrfmicro/nrfmicro_11_flipped.dts b/app/boards/arm/nrfmicro/nrfmicro_11_flipped.dts
index ea15b81..6c1bb6a 100644
--- a/app/boards/arm/nrfmicro/nrfmicro_11_flipped.dts
+++ b/app/boards/arm/nrfmicro/nrfmicro_11_flipped.dts
@@ -72,27 +72,32 @@
#address-cells = <1>;
#size-cells = <1>;
- boot_partition: partition@0 {
- label = "adafruit_boot";
- reg = <0x000000000 0x0000C000>;
+ sd_partition: partition@0 {
+ label = "softdevice";
+ reg = <0x00000000 0x00026000>;
};
code_partition: partition@26000 {
label = "code_partition";
- reg = <0x00026000 0x000d2000>;
+ reg = <0x00026000 0x000c6000>;
};
/*
- * The flash starting at 0x000f8000 and ending at
- * 0x000fffff is reserved for use by the application.
+ * The flash starting at 0x000ec000 and ending at
+ * 0x000f3fff is reserved for use by the application.
*/
/*
* Storage partition will be used by FCB/LittleFS/NVS
* if enabled.
*/
- storage_partition: partition@f8000 {
+ storage_partition: partition@ec000 {
label = "storage";
- reg = <0x000f8000 0x00008000>;
+ reg = <0x000ec000 0x00008000>;
+ };
+
+ boot_partition: partition@f4000 {
+ label = "adafruit_boot";
+ reg = <0x000f4000 0x0000c000>;
};
};
-};
+}; \ No newline at end of file
diff --git a/app/boards/arm/nrfmicro/nrfmicro_13.dts b/app/boards/arm/nrfmicro/nrfmicro_13.dts
index 5ae11ba..2910980 100644
--- a/app/boards/arm/nrfmicro/nrfmicro_13.dts
+++ b/app/boards/arm/nrfmicro/nrfmicro_13.dts
@@ -34,7 +34,7 @@
vbatt {
compatible = "zmk,battery-voltage-divider";
- label = "VOLTAGE_DIVIDER";
+ label = "BATTERY";
io-channels = <&adc 2>;
output-ohms = <2000000>;
full-ohms = <(2000000 + 820000)>;
@@ -84,27 +84,32 @@
#address-cells = <1>;
#size-cells = <1>;
- boot_partition: partition@0 {
- label = "adafruit_boot";
- reg = <0x000000000 0x0000C000>;
+ sd_partition: partition@0 {
+ label = "softdevice";
+ reg = <0x00000000 0x00026000>;
};
code_partition: partition@26000 {
label = "code_partition";
- reg = <0x00026000 0x000d2000>;
+ reg = <0x00026000 0x000c6000>;
};
/*
- * The flash starting at 0x000f8000 and ending at
- * 0x000fffff is reserved for use by the application.
+ * The flash starting at 0x000ec000 and ending at
+ * 0x000f3fff is reserved for use by the application.
*/
/*
* Storage partition will be used by FCB/LittleFS/NVS
* if enabled.
*/
- storage_partition: partition@f8000 {
+ storage_partition: partition@ec000 {
label = "storage";
- reg = <0x000f8000 0x00008000>;
+ reg = <0x000ec000 0x00008000>;
+ };
+
+ boot_partition: partition@f4000 {
+ label = "adafruit_boot";
+ reg = <0x000f4000 0x0000c000>;
};
};
};
diff --git a/app/drivers/zephyr/battery_voltage_divider.c b/app/drivers/zephyr/battery_voltage_divider.c
index 37ac024..b94e2a2 100644
--- a/app/drivers/zephyr/battery_voltage_divider.c
+++ b/app/drivers/zephyr/battery_voltage_divider.c
@@ -61,7 +61,9 @@ static int bvd_sample_fetch(struct device *dev, enum sensor_channel chan) {
struct adc_sequence *as = &drv_data->as;
// Make sure selected channel is supported
- if (chan != SENSOR_CHAN_GAUGE_VOLTAGE && chan != SENSOR_CHAN_GAUGE_STATE_OF_CHARGE) {
+ if (chan != SENSOR_CHAN_GAUGE_VOLTAGE && chan != SENSOR_CHAN_GAUGE_STATE_OF_CHARGE &&
+ chan != SENSOR_CHAN_ALL) {
+ LOG_DBG("Selected channel is not supported: %d.", chan);
return -ENOTSUP;
}
diff --git a/app/dts/behaviors.dtsi b/app/dts/behaviors.dtsi
index 36c918c..a120b84 100644
--- a/app/dts/behaviors.dtsi
+++ b/app/dts/behaviors.dtsi
@@ -10,3 +10,4 @@
#include <behaviors/rgb_underglow.dtsi>
#include <behaviors/bluetooth.dtsi>
#include <behaviors/ext_power.dtsi>
+#include <behaviors/outputs.dtsi>
diff --git a/app/dts/behaviors/outputs.dtsi b/app/dts/behaviors/outputs.dtsi
new file mode 100644
index 0000000..a534cbf
--- /dev/null
+++ b/app/dts/behaviors/outputs.dtsi
@@ -0,0 +1,9 @@
+/ {
+ behaviors {
+ out: behavior_outputs {
+ compatible = "zmk,behavior-outputs";
+ label = "OUTPUTS";
+ #binding-cells = <1>;
+ };
+ };
+};
diff --git a/app/dts/bindings/behaviors/zmk,behavior-outputs.yaml b/app/dts/bindings/behaviors/zmk,behavior-outputs.yaml
new file mode 100644
index 0000000..8bcefd9
--- /dev/null
+++ b/app/dts/bindings/behaviors/zmk,behavior-outputs.yaml
@@ -0,0 +1,10 @@
+#
+# Copyright (c) 2020, The ZMK Contributors
+# SPDX-License-Identifier: MIT
+#
+
+description: Output Selection Behavior
+
+compatible: "zmk,behavior-outputs"
+
+include: one_param.yaml
diff --git a/app/include/dt-bindings/zmk/keys.h b/app/include/dt-bindings/zmk/keys.h
index a3b2229..5a52753 100644
--- a/app/include/dt-bindings/zmk/keys.h
+++ b/app/include/dt-bindings/zmk/keys.h
@@ -3,9 +3,10 @@
*
* SPDX-License-Identifier: MIT
*/
-
#pragma once
+#include <dt-bindings/zmk/modifiers.h>
+
#define USAGE_KEYPAD 0x07
#define USAGE_CONSUMER 0x0C
@@ -143,12 +144,3 @@
#define M_MUTE 0xE2
#define M_VOLU 0xE9
#define M_VOLD 0xEA
-
-#define MOD_LCTL (1 << 0x00)
-#define MOD_LSFT (1 << 0x01)
-#define MOD_LALT (1 << 0x02)
-#define MOD_LGUI (1 << 0x03)
-#define MOD_RCTL (1 << 0x04)
-#define MOD_RSFT (1 << 0x05)
-#define MOD_RALT (1 << 0x06)
-#define MOD_RGUI (1 << 0x07) \ No newline at end of file
diff --git a/app/include/dt-bindings/zmk/modifiers.h b/app/include/dt-bindings/zmk/modifiers.h
new file mode 100644
index 0000000..b49849d
--- /dev/null
+++ b/app/include/dt-bindings/zmk/modifiers.h
@@ -0,0 +1,28 @@
+/*
+ * Copyright (c) 2020 The ZMK Contributors
+ *
+ * SPDX-License-Identifier: MIT
+ */
+#pragma once
+
+#define MOD_LCTL 0x01
+#define MOD_LSFT 0x02
+#define MOD_LALT 0x04
+#define MOD_LGUI 0x08
+#define MOD_RCTL 0x10
+#define MOD_RSFT 0x20
+#define MOD_RALT 0x40
+#define MOD_RGUI 0x80
+
+#define SELECT_MODS(keycode) (keycode >> 24)
+#define STRIP_MODS(keycode) (keycode & ~(0xFF << 24))
+#define APPLY_MODS(mods, keycode) (mods << 24 | keycode)
+
+#define LC(keycode) APPLY_MODS(MOD_LCTL, keycode)
+#define LS(keycode) APPLY_MODS(MOD_LSFT, keycode)
+#define LA(keycode) APPLY_MODS(MOD_LALT, keycode)
+#define LG(keycode) APPLY_MODS(MOD_LGUI, keycode)
+#define RC(keycode) APPLY_MODS(MOD_RCTL, keycode)
+#define RS(keycode) APPLY_MODS(MOD_RSFT, keycode)
+#define RA(keycode) APPLY_MODS(MOD_RALT, keycode)
+#define RG(keycode) APPLY_MODS(MOD_RGUI, keycode) \ No newline at end of file
diff --git a/app/include/dt-bindings/zmk/outputs.h b/app/include/dt-bindings/zmk/outputs.h
new file mode 100644
index 0000000..f24380f
--- /dev/null
+++ b/app/include/dt-bindings/zmk/outputs.h
@@ -0,0 +1,9 @@
+/*
+ * Copyright (c) 2020 The ZMK Contributors
+ *
+ * SPDX-License-Identifier: MIT
+ */
+
+#define OUT_TOG 0
+#define OUT_USB 1
+#define OUT_BLE 2 \ No newline at end of file
diff --git a/app/include/zmk/ble.h b/app/include/zmk/ble.h
index 1cf71a7..56980c6 100644
--- a/app/include/zmk/ble.h
+++ b/app/include/zmk/ble.h
@@ -15,6 +15,7 @@ int zmk_ble_prof_prev();
int zmk_ble_prof_select(u8_t index);
bt_addr_le_t *zmk_ble_active_profile_addr();
+bool zmk_ble_active_profile_is_connected();
char *zmk_ble_active_profile_name();
int zmk_ble_unpair_all();
diff --git a/app/include/zmk/endpoints.h b/app/include/zmk/endpoints.h
index aad6265..aad688e 100644
--- a/app/include/zmk/endpoints.h
+++ b/app/include/zmk/endpoints.h
@@ -9,4 +9,12 @@
#include <zmk/keys.h>
#include <zmk/hid.h>
+enum zmk_endpoint {
+ ZMK_ENDPOINT_USB,
+ ZMK_ENDPOINT_BLE,
+};
+
+int zmk_endpoints_select(enum zmk_endpoint endpoint);
+int zmk_endpoints_toggle();
+
int zmk_endpoints_send_report(u8_t usage_report);
diff --git a/app/include/zmk/events/keycode-state-changed.h b/app/include/zmk/events/keycode-state-changed.h
index 1e2c24e..26d07ec 100644
--- a/app/include/zmk/events/keycode-state-changed.h
+++ b/app/include/zmk/events/keycode-state-changed.h
@@ -7,22 +7,25 @@
#pragma once
#include <zephyr.h>
+#include <dt-bindings/zmk/modifiers.h>
#include <zmk/event-manager.h>
struct keycode_state_changed {
struct zmk_event_header header;
u8_t usage_page;
u32_t keycode;
+ u8_t implicit_modifiers;
bool state;
};
ZMK_EVENT_DECLARE(keycode_state_changed);
-inline struct keycode_state_changed *create_keycode_state_changed(u8_t usage_page, u32_t keycode,
- bool state) {
+static inline struct keycode_state_changed *
+create_keycode_state_changed(u8_t usage_page, u32_t keycode, bool state) {
struct keycode_state_changed *ev = new_keycode_state_changed();
ev->usage_page = usage_page;
- ev->keycode = keycode;
+ ev->keycode = STRIP_MODS(keycode);
+ ev->implicit_modifiers = SELECT_MODS(keycode);
ev->state = state;
return ev;
-} \ No newline at end of file
+}
diff --git a/app/include/zmk/events/usb-conn-state-changed.h b/app/include/zmk/events/usb-conn-state-changed.h
new file mode 100644
index 0000000..d6cc698
--- /dev/null
+++ b/app/include/zmk/events/usb-conn-state-changed.h
@@ -0,0 +1,20 @@
+/*
+ * Copyright (c) 2020 The ZMK Contributors
+ *
+ * SPDX-License-Identifier: MIT
+ */
+
+#pragma once
+
+#include <zephyr.h>
+#include <usb/usb_device.h>
+
+#include <zmk/event-manager.h>
+#include <zmk/usb.h>
+
+struct usb_conn_state_changed {
+ struct zmk_event_header header;
+ enum zmk_usb_conn_state conn_state;
+};
+
+ZMK_EVENT_DECLARE(usb_conn_state_changed); \ No newline at end of file
diff --git a/app/include/zmk/handlers.h b/app/include/zmk/handlers.h
index 7ce1d27..92bd7e0 100644
--- a/app/include/zmk/handlers.h
+++ b/app/include/zmk/handlers.h
@@ -6,8 +6,6 @@
#pragma once
-#include <dt-bindings/zmk/keys.h>
-
#include <zmk/keymap.h>
#include <zmk/keys.h>
diff --git a/app/include/zmk/hid.h b/app/include/zmk/hid.h
index 744de98..2426c2d 100644
--- a/app/include/zmk/hid.h
+++ b/app/include/zmk/hid.h
@@ -9,13 +9,13 @@
#include <usb/usb_device.h>
#include <usb/class/usb_hid.h>
-#include <dt-bindings/zmk/keys.h>
-
#include <zmk/keys.h>
#define COLLECTION_REPORT 0x03
-#define ZMK_HID_MAX_KEYCODE GUI
+#define ZMK_HID_KEYPAD_NKRO_SIZE 6
+
+#define ZMK_HID_CONSUMER_NKRO_SIZE 6
static const u8_t zmk_hid_report_desc[] = {
/* USAGE_PAGE (Generic Desktop) */
@@ -59,39 +59,41 @@ static const u8_t zmk_hid_report_desc[] = {
/* USAGE_PAGE (Keypad) */
HID_GI_USAGE_PAGE,
USAGE_GEN_DESKTOP_KEYPAD,
+ /* REPORT_SIZE (8) */
+ HID_GI_REPORT_SIZE,
+ 0x08,
+ /* REPORT_COUNT (1) */
+ HID_GI_REPORT_COUNT,
+ 0x01,
+ /* INPUT (Cnst,Var,Abs) */
+ HID_MI_INPUT,
+ 0x03,
+
+ /* USAGE_PAGE (Keypad) */
+ HID_GI_USAGE_PAGE,
+ USAGE_GEN_DESKTOP_KEYPAD,
/* LOGICAL_MINIMUM (0) */
HID_GI_LOGICAL_MIN(1),
0x00,
- /* LOGICAL_MAXIMUM (1) */
+ /* LOGICAL_MAXIMUM (0xFF) */
HID_GI_LOGICAL_MAX(1),
- 0x01,
+ 0xFF,
/* USAGE_MINIMUM (Reserved) */
HID_LI_USAGE_MIN(1),
0x00,
/* USAGE_MAXIMUM (Keyboard Application) */
HID_LI_USAGE_MAX(1),
- ZMK_HID_MAX_KEYCODE,
- /* REPORT_SIZE (8) */
+ 0xFF,
+ /* REPORT_SIZE (1) */
HID_GI_REPORT_SIZE,
- 0x01,
- /* REPORT_COUNT (6) */
+ 0x08,
+ /* REPORT_COUNT (ZMK_HID_KEYPAD_NKRO_SIZE) */
HID_GI_REPORT_COUNT,
- ZMK_HID_MAX_KEYCODE + 1,
+ ZMK_HID_KEYPAD_NKRO_SIZE,
/* INPUT (Data,Ary,Abs) */
HID_MI_INPUT,
- 0x02,
- /* USAGE_PAGE (Keypad) */
- HID_GI_USAGE_PAGE,
- USAGE_GEN_DESKTOP_KEYPAD,
- /* REPORT_SIZE (8) */
- HID_GI_REPORT_SIZE,
- 0x02,
- /* REPORT_COUNT (6) */
- HID_GI_REPORT_COUNT,
- 0x01,
- /* INPUT (Cnst,Var,Abs) */
- HID_MI_INPUT,
- 0x03,
+ 0x00,
+
/* END_COLLECTION */
HID_MI_COLLECTION_END,
/* USAGE_PAGE (Consumer) */
@@ -124,9 +126,9 @@ static const u8_t zmk_hid_report_desc[] = {
/* REPORT_SIZE (8) */
HID_GI_REPORT_SIZE,
0x08,
- /* REPORT_COUNT (8) */
+ /* REPORT_COUNT (ZMK_HID_CONSUMER_NKRO_SIZE) */
HID_GI_REPORT_COUNT,
- 0x06,
+ ZMK_HID_CONSUMER_NKRO_SIZE,
HID_MI_INPUT,
0x00,
/* END COLLECTION */
@@ -142,7 +144,8 @@ static const u8_t zmk_hid_report_desc[] = {
struct zmk_hid_keypad_report_body {
zmk_mod_flags modifiers;
- u8_t keys[13];
+ u8_t _reserved;
+ u8_t keys[ZMK_HID_KEYPAD_NKRO_SIZE];
} __packed;
struct zmk_hid_keypad_report {
@@ -151,7 +154,7 @@ struct zmk_hid_keypad_report {
} __packed;
struct zmk_hid_consumer_report_body {
- u8_t keys[6];
+ u8_t keys[ZMK_HID_CONSUMER_NKRO_SIZE];
} __packed;
struct zmk_hid_consumer_report {
@@ -161,13 +164,15 @@ struct zmk_hid_consumer_report {
int zmk_hid_register_mod(zmk_mod modifier);
int zmk_hid_unregister_mod(zmk_mod modifier);
-int zmk_hid_register_mods(zmk_mod_flags modifiers);
-int zmk_hid_unregister_mods(zmk_mod_flags modifiers);
+int zmk_hid_implicit_modifiers_press(zmk_mod_flags implicit_modifiers);
+int zmk_hid_implicit_modifiers_release();
int zmk_hid_keypad_press(zmk_key key);
int zmk_hid_keypad_release(zmk_key key);
+void zmk_hid_keypad_clear();
int zmk_hid_consumer_press(zmk_key key);
int zmk_hid_consumer_release(zmk_key key);
+void zmk_hid_consumer_clear();
struct zmk_hid_keypad_report *zmk_hid_get_keypad_report();
struct zmk_hid_consumer_report *zmk_hid_get_consumer_report();
diff --git a/app/include/zmk/usb.h b/app/include/zmk/usb.h
index 452fd54..30461de 100644
--- a/app/include/zmk/usb.h
+++ b/app/include/zmk/usb.h
@@ -12,8 +12,18 @@
#include <zmk/keys.h>
#include <zmk/hid.h>
+enum zmk_usb_conn_state {
+ ZMK_USB_CONN_NONE,
+ ZMK_USB_CONN_POWERED,
+ ZMK_USB_CONN_HID,
+};
+
enum usb_dc_status_code zmk_usb_get_status();
+enum zmk_usb_conn_state zmk_usb_get_conn_state();
+
+static inline bool zmk_usb_is_powered() { return zmk_usb_get_conn_state() != ZMK_USB_CONN_NONE; }
+static inline bool zmk_usb_is_hid_ready() { return zmk_usb_get_conn_state() == ZMK_USB_CONN_HID; }
#ifdef CONFIG_ZMK_USB
-int zmk_usb_hid_send_report(u8_t *report, size_t len);
+int zmk_usb_hid_send_report(const u8_t *report, size_t len);
#endif /* CONFIG_ZMK_USB */ \ No newline at end of file
diff --git a/app/src/battery.c b/app/src/battery.c
new file mode 100644
index 0000000..9496570
--- /dev/null
+++ b/app/src/battery.c
@@ -0,0 +1,75 @@
+/*
+ * Copyright (c) 2020 The ZMK Contributors
+ *
+ * SPDX-License-Identifier: MIT
+ */
+
+#include <device.h>
+#include <init.h>
+#include <kernel.h>
+#include <drivers/sensor.h>
+#include <bluetooth/services/bas.h>
+
+#include <logging/log.h>
+
+LOG_MODULE_DECLARE(zmk, CONFIG_ZMK_LOG_LEVEL);
+
+struct device *battery;
+
+static int zmk_battery_update(struct device *battery) {
+ struct sensor_value state_of_charge;
+
+ int rc = sensor_sample_fetch_chan(battery, SENSOR_CHAN_GAUGE_STATE_OF_CHARGE);
+
+ if (rc != 0) {
+ LOG_DBG("Failed to fetch battery values: %d", rc);
+ return rc;
+ }
+
+ rc = sensor_channel_get(battery, SENSOR_CHAN_GAUGE_STATE_OF_CHARGE, &state_of_charge);
+
+ if (rc != 0) {
+ LOG_DBG("Failed to get battery state of charge: %d", rc);
+ return rc;
+ }
+
+ LOG_DBG("Setting BAS GATT battery level to %d.", state_of_charge.val1);
+
+ return bt_gatt_bas_set_battery_level(state_of_charge.val1);
+}
+
+static void zmk_battery_work(struct k_work *work) {
+ int rc = zmk_battery_update(battery);
+
+ if (rc != 0) {
+ LOG_DBG("Failed to update battery value: %d.", rc);
+ }
+}
+
+K_WORK_DEFINE(battery_work, zmk_battery_work);
+
+static void zmk_battery_timer(struct k_timer *timer) { k_work_submit(&battery_work); }
+
+K_TIMER_DEFINE(battery_timer, zmk_battery_timer, NULL);
+
+static int zmk_battery_init(struct device *_arg) {
+ battery = device_get_binding("BATTERY");
+
+ if (battery == NULL) {
+ LOG_DBG("No battery device labelled BATTERY found.");
+ return -ENODEV;
+ }
+
+ int rc = zmk_battery_update(battery);
+
+ if (rc != 0) {
+ LOG_DBG("Failed to update battery value: %d.", rc);
+ return rc;
+ }
+
+ k_timer_start(&battery_timer, K_MINUTES(1), K_MINUTES(1));
+
+ return 0;
+}
+
+SYS_INIT(zmk_battery_init, APPLICATION, CONFIG_APPLICATION_INIT_PRIORITY);
diff --git a/app/src/behaviors/behavior_hold_tap.c b/app/src/behaviors/behavior_hold_tap.c
index 8b3620e..ac91e7d 100644
--- a/app/src/behaviors/behavior_hold_tap.c
+++ b/app/src/behaviors/behavior_hold_tap.c
@@ -8,6 +8,7 @@
#include <device.h>
#include <drivers/behavior.h>
+#include <dt-bindings/zmk/keys.h>
#include <logging/log.h>
#include <zmk/behavior.h>
#include <zmk/matrix.h>
@@ -16,7 +17,6 @@
#include <zmk/events/position-state-changed.h>
#include <zmk/events/keycode-state-changed.h>
#include <zmk/events/modifiers-state-changed.h>
-#include <zmk/hid.h>
#include <zmk/behavior.h>
LOG_MODULE_DECLARE(zmk, CONFIG_ZMK_LOG_LEVEL);
@@ -427,7 +427,7 @@ static int position_state_changed_listener(const struct zmk_event_header *eh) {
return ZMK_EV_EVENT_CAPTURED;
}
-static bool is_mod(struct keycode_state_changed *ev) {
+static inline bool only_mods(struct keycode_state_changed *ev) {
return ev->usage_page == USAGE_KEYPAD && ev->keycode >= LCTL && ev->keycode <= RGUI;
}
@@ -440,7 +440,7 @@ static int keycode_state_changed_listener(const struct zmk_event_header *eh) {
return 0;
}
- if (!is_mod(ev)) {
+ if (!only_mods(ev)) {
// LOG_DBG("0x%02X bubble (not a mod)", ev->keycode);
return 0;
}
diff --git a/app/src/behaviors/behavior_outputs.c b/app/src/behaviors/behavior_outputs.c
new file mode 100644
index 0000000..e5182bd
--- /dev/null
+++ b/app/src/behaviors/behavior_outputs.c
@@ -0,0 +1,44 @@
+/*
+ * Copyright (c) 2020 The ZMK Contributors
+ *
+ * SPDX-License-Identifier: MIT
+ */
+
+#define DT_DRV_COMPAT zmk_behavior_outputs
+
+#include <device.h>
+#include <devicetree.h>
+#include <drivers/behavior.h>
+
+#include <dt-bindings/zmk/outputs.h>
+
+#include <zmk/behavior.h>
+#include <zmk/endpoints.h>
+
+#include <logging/log.h>
+LOG_MODULE_DECLARE(zmk, CONFIG_ZMK_LOG_LEVEL);
+
+static int on_keymap_binding_pressed(struct zmk_behavior_binding *binding,
+ struct zmk_behavior_binding_event event) {
+ switch (binding->param1) {
+ case OUT_TOG:
+ return zmk_endpoints_toggle();
+ case OUT_USB:
+ return zmk_endpoints_select(ZMK_ENDPOINT_USB);
+ case OUT_BLE:
+ return zmk_endpoints_select(ZMK_ENDPOINT_BLE);
+ default:
+ LOG_ERR("Unknown output command: %d", binding->param1);
+ }
+
+ return -ENOTSUP;
+}
+
+static int behavior_out_init(struct device *dev) { return 0; }
+
+static const struct behavior_driver_api behavior_outputs_driver_api = {
+ .binding_pressed = on_keymap_binding_pressed,
+};
+
+DEVICE_AND_API_INIT(behavior_out, DT_INST_LABEL(0), behavior_out_init, NULL, NULL, APPLICATION,
+ CONFIG_KERNEL_INIT_PRIORITY_DEFAULT, &behavior_outputs_driver_api);
diff --git a/app/src/ble.c b/app/src/ble.c
index 9090582..f3962ae 100644
--- a/app/src/ble.c
+++ b/app/src/ble.c
@@ -94,6 +94,12 @@ static void raise_profile_changed_event() {
ZMK_EVENT_RAISE(ev);
}
+static void raise_profile_changed_event_callback(struct k_work *work) {
+ raise_profile_changed_event();
+}
+
+K_WORK_DEFINE(raise_profile_changed_event_work, raise_profile_changed_event_callback);
+
static bool active_profile_is_open() {
return !bt_addr_le_cmp(&profiles[active_profile].peer, BT_ADDR_LE_ANY);
}
@@ -111,7 +117,7 @@ void set_profile_address(u8_t index, const bt_addr_le_t *addr) {
raise_profile_changed_event();
}
-bool active_profile_is_connected() {
+bool zmk_ble_active_profile_is_connected() {
struct bt_conn *conn;
bt_addr_le_t *addr = zmk_ble_active_profile_addr();
if (!bt_addr_le_cmp(addr, BT_ADDR_LE_ANY)) {
@@ -163,9 +169,9 @@ int update_advertising() {
struct bt_conn *conn;
enum advertising_type desired_adv = ZMK_ADV_NONE;
- if (active_profile_is_open() || !active_profile_is_connected()) {
+ if (active_profile_is_open()) {
desired_adv = ZMK_ADV_CONN;
- } else if (!active_profile_is_connected()) {
+ } else if (!zmk_ble_active_profile_is_connected()) {
desired_adv = ZMK_ADV_CONN;
// Need to fix directed advertising for privacy centrals. See
// https://github.com/zephyrproject-rtos/zephyr/pull/14984 char
@@ -327,6 +333,10 @@ static int ble_profiles_handle_set(const char *name, size_t len, settings_read_c
struct settings_handler profiles_handler = {.name = "ble", .h_set = ble_profiles_handle_set};
#endif /* IS_ENABLED(CONFIG_SETTINGS) */
+static bool is_conn_active_profile(const struct bt_conn *conn) {
+ return bt_addr_le_cmp(bt_conn_get_dst(conn), &profiles[active_profile].peer) == 0;
+}
+
static void connected(struct bt_conn *conn, u8_t err) {
char addr[BT_ADDR_LE_STR_LEN];
bt_addr_le_to_str(bt_conn_get_dst(conn), addr, sizeof(addr));
@@ -352,6 +362,11 @@ static void connected(struct bt_conn *conn, u8_t err) {
}
update_advertising();
+
+ if (is_conn_active_profile(conn)) {
+ LOG_DBG("Active profile connected");
+ raise_profile_changed_event();
+ }
}
static void disconnected(struct bt_conn *conn, u8_t reason) {
@@ -364,6 +379,11 @@ static void disconnected(struct bt_conn *conn, u8_t reason) {
// We need to do this in a work callback, otherwise the advertising update will still see the
// connection for a profile as active, and not start advertising yet.
k_work_submit(&update_advertising_work);
+
+ if (is_conn_active_profile(conn)) {
+ LOG_DBG("Active profile disconnected");
+ k_work_submit(&raise_profile_changed_event_work);
+ }
}
static void security_changed(struct bt_conn *conn, bt_security_t level, enum bt_security_err err) {
diff --git a/app/src/endpoints.c b/app/src/endpoints.c
index 79d294e..0c79589 100644
--- a/app/src/endpoints.c
+++ b/app/src/endpoints.c
@@ -4,58 +4,238 @@
* SPDX-License-Identifier: MIT
*/
+#include <init.h>
+#include <settings/settings.h>
+
+#include <zmk/ble.h>
#include <zmk/endpoints.h>
#include <zmk/hid.h>
#include <zmk/usb.h>
#include <zmk/hog.h>
+#include <zmk/event-manager.h>
+#include <zmk/events/ble-active-profile-changed.h>
+#include <zmk/events/usb-conn-state-changed.h>
#include <logging/log.h>
LOG_MODULE_DECLARE(zmk, CONFIG_ZMK_LOG_LEVEL);
-int zmk_endpoints_send_report(u8_t usage_page) {
- int err;
- struct zmk_hid_keypad_report *keypad_report;
- struct zmk_hid_consumer_report *consumer_report;
- LOG_DBG("usage page 0x%02X", usage_page);
- switch (usage_page) {
- case USAGE_KEYPAD:
- keypad_report = zmk_hid_get_keypad_report();
-#ifdef CONFIG_ZMK_USB
- if (zmk_usb_hid_send_report((u8_t *)keypad_report, sizeof(struct zmk_hid_keypad_report)) !=
- 0) {
- LOG_DBG("USB Send Failed");
+#define DEFAULT_ENDPOINT \
+ COND_CODE_1(IS_ENABLED(CONFIG_ZMK_BLE), (ZMK_ENDPOINT_BLE), (ZMK_ENDPOINT_USB))
+
+static enum zmk_endpoint current_endpoint = DEFAULT_ENDPOINT;
+static enum zmk_endpoint preferred_endpoint =
+ ZMK_ENDPOINT_USB; /* Used if multiple endpoints are ready */
+
+static void update_current_endpoint();
+
+int zmk_endpoints_select(enum zmk_endpoint endpoint) {
+ LOG_DBG("Selected endpoint %d", endpoint);
+
+ if (preferred_endpoint == endpoint) {
+ return 0;
+ }
+
+ preferred_endpoint = endpoint;
+
+#if IS_ENABLED(CONFIG_SETTINGS)
+ settings_save_one("endpoints/preferred", &preferred_endpoint, sizeof(preferred_endpoint));
+#endif
+
+ update_current_endpoint();
+
+ return 0;
+}
+
+int zmk_endpoints_toggle() {
+ enum zmk_endpoint new_endpoint =
+ (preferred_endpoint == ZMK_ENDPOINT_USB) ? ZMK_ENDPOINT_BLE : ZMK_ENDPOINT_USB;
+ return zmk_endpoints_select(new_endpoint);
+}
+
+static int send_keypad_report() {
+ struct zmk_hid_keypad_report *keypad_report = zmk_hid_get_keypad_report();
+
+ switch (current_endpoint) {
+#if IS_ENABLED(CONFIG_ZMK_USB)
+ case ZMK_ENDPOINT_USB: {
+ int err = zmk_usb_hid_send_report((u8_t *)keypad_report, sizeof(*keypad_report));
+ if (err) {
+ LOG_ERR("FAILED TO SEND OVER USB: %d", err);
}
-#endif /* CONFIG_ZMK_USB */
+ return err;
+ }
+#endif /* IS_ENABLED(CONFIG_ZMK_USB) */
-#ifdef CONFIG_ZMK_BLE
- err = zmk_hog_send_keypad_report(&keypad_report->body);
+#if IS_ENABLED(CONFIG_ZMK_BLE)
+ case ZMK_ENDPOINT_BLE: {
+ int err = zmk_hog_send_keypad_report(&keypad_report->body);
if (err) {
LOG_ERR("FAILED TO SEND OVER HOG: %d", err);
}
-#endif /* CONFIG_ZMK_BLE */
+ return err;
+ }
+#endif /* IS_ENABLED(CONFIG_ZMK_BLE) */
- break;
- case USAGE_CONSUMER:
- consumer_report = zmk_hid_get_consumer_report();
-#ifdef CONFIG_ZMK_USB
- if (zmk_usb_hid_send_report((u8_t *)consumer_report,
- sizeof(struct zmk_hid_consumer_report)) != 0) {
- LOG_DBG("USB Send Failed");
+ default:
+ LOG_ERR("Unsupported endpoint %d", current_endpoint);
+ return -ENOTSUP;
+ }
+}
+
+static int send_consumer_report() {
+ struct zmk_hid_consumer_report *consumer_report = zmk_hid_get_consumer_report();
+
+ switch (current_endpoint) {
+#if IS_ENABLED(CONFIG_ZMK_USB)
+ case ZMK_ENDPOINT_USB: {
+ int err = zmk_usb_hid_send_report((u8_t *)consumer_report, sizeof(*consumer_report));
+ if (err) {
+ LOG_ERR("FAILED TO SEND OVER USB: %d", err);
}
-#endif /* CONFIG_ZMK_USB */
+ return err;
+ }
+#endif /* IS_ENABLED(CONFIG_ZMK_USB) */
-#ifdef CONFIG_ZMK_BLE
- err = zmk_hog_send_consumer_report(&consumer_report->body);
+#if IS_ENABLED(CONFIG_ZMK_BLE)
+ case ZMK_ENDPOINT_BLE: {
+ int err = zmk_hog_send_consumer_report(&consumer_report->body);
if (err) {
LOG_ERR("FAILED TO SEND OVER HOG: %d", err);
}
-#endif /* CONFIG_ZMK_BLE */
+ return err;
+ }
+#endif /* IS_ENABLED(CONFIG_ZMK_BLE) */
- break;
+ default:
+ LOG_ERR("Unsupported endpoint %d", current_endpoint);
+ return -ENOTSUP;
+ }
+}
+
+int zmk_endpoints_send_report(u8_t usage_page) {
+
+ LOG_DBG("usage page 0x%02X", usage_page);
+ switch (usage_page) {
+ case USAGE_KEYPAD:
+ return send_keypad_report();
+ case USAGE_CONSUMER:
+ return send_consumer_report();
default:
LOG_ERR("Unsupported usage page %d", usage_page);
return -ENOTSUP;
}
+}
+
+#if IS_ENABLED(CONFIG_SETTINGS)
+
+static int endpoints_handle_set(const char *name, size_t len, settings_read_cb read_cb,
+ void *cb_arg) {
+ LOG_DBG("Setting endpoint value %s", log_strdup(name));
+
+ if (settings_name_steq(name, "preferred", NULL)) {
+ if (len != sizeof(enum zmk_endpoint)) {
+ LOG_ERR("Invalid endpoint size (got %d expected %d)", len, sizeof(enum zmk_endpoint));
+ return -EINVAL;
+ }
+
+ int err = read_cb(cb_arg, &preferred_endpoint, sizeof(enum zmk_endpoint));
+ if (err <= 0) {
+ LOG_ERR("Failed to read preferred endpoint from settings (err %d)", err);
+ return err;
+ }
+
+ update_current_endpoint();
+ }
+
+ return 0;
+}
+
+struct settings_handler endpoints_handler = {.name = "endpoints", .h_set = endpoints_handle_set};
+#endif /* IS_ENABLED(CONFIG_SETTINGS) */
+
+static int zmk_endpoints_init(struct device *_arg) {
+#if IS_ENABLED(CONFIG_SETTINGS)
+ settings_subsys_init();
+
+ int err = settings_register(&endpoints_handler);
+ if (err) {
+ LOG_ERR("Failed to register the endpoints settings handler (err %d)", err);
+ return err;
+ }
+
+ settings_load();
+#endif
return 0;
}
+
+static bool is_usb_ready() {
+#if IS_ENABLED(CONFIG_ZMK_USB)
+ return zmk_usb_is_hid_ready();
+#else
+ return false;
+#endif
+}
+
+static bool is_ble_ready() {
+#if IS_ENABLED(CONFIG_ZMK_BLE)
+ return zmk_ble_active_profile_is_connected();
+#else
+ return false;
+#endif
+}
+
+static enum zmk_endpoint get_selected_endpoint() {
+ if (is_ble_ready()) {
+ if (is_usb_ready()) {
+ LOG_DBG("Both endpoints are ready. Using %d", preferred_endpoint);
+ return preferred_endpoint;
+ }
+
+ LOG_DBG("Only BLE is ready.");
+ return ZMK_ENDPOINT_BLE;
+ }
+
+ if (is_usb_ready()) {
+ LOG_DBG("Only USB is ready.");
+ return ZMK_ENDPOINT_USB;
+ }
+
+ LOG_DBG("No endpoints are ready.");
+ return DEFAULT_ENDPOINT;
+}
+
+static void disconnect_current_endpoint() {
+ zmk_hid_keypad_clear();
+ zmk_hid_consumer_clear();
+
+ zmk_endpoints_send_report(USAGE_KEYPAD);
+ zmk_endpoints_send_report(USAGE_CONSUMER);
+}
+
+static void update_current_endpoint() {
+ enum zmk_endpoint new_endpoint = get_selected_endpoint();
+
+ if (new_endpoint != current_endpoint) {
+ /* Cancel all current keypresses so keys don't stay held on the old endpoint. */
+ disconnect_current_endpoint();
+
+ current_endpoint = new_endpoint;
+ LOG_INF("Endpoint changed: %d", current_endpoint);
+ }
+}
+
+static int endpoint_listener(const struct zmk_event_header *eh) {
+ update_current_endpoint();
+ return 0;
+}
+
+ZMK_LISTENER(endpoint_listener, endpoint_listener);
+#if IS_ENABLED(CONFIG_ZMK_USB)
+ZMK_SUBSCRIPTION(endpoint_listener, usb_conn_state_changed);
+#endif
+#if IS_ENABLED(CONFIG_ZMK_BLE)
+ZMK_SUBSCRIPTION(endpoint_listener, ble_active_profile_changed);
+#endif
+
+SYS_INIT(zmk_endpoints_init, APPLICATION, CONFIG_APPLICATION_INIT_PRIORITY);
diff --git a/app/src/events/usb_conn_state_changed.c b/app/src/events/usb_conn_state_changed.c
new file mode 100644
index 0000000..d845f6d
--- /dev/null
+++ b/app/src/events/usb_conn_state_changed.c
@@ -0,0 +1,10 @@
+/*
+ * Copyright (c) 2020 The ZMK Contributors
+ *
+ * SPDX-License-Identifier: MIT
+ */
+
+#include <kernel.h>
+#include <zmk/events/usb-conn-state-changed.h>
+
+ZMK_EVENT_IMPL(usb_conn_state_changed); \ No newline at end of file
diff --git a/app/src/hid.c b/app/src/hid.c
index f80906c..1925765 100644
--- a/app/src/hid.c
+++ b/app/src/hid.c
@@ -8,53 +8,58 @@
LOG_MODULE_DECLARE(zmk, CONFIG_ZMK_LOG_LEVEL);
#include <zmk/hid.h>
+#include <dt-bindings/zmk/modifiers.h>
static struct zmk_hid_keypad_report kp_report = {
- .report_id = 1, .body = {.modifiers = 0, .keys = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}}};
+ .report_id = 1, .body = {.modifiers = 0, ._reserved = 0, .keys = {0}}};
-static struct zmk_hid_consumer_report consumer_report = {.report_id = 2,
- .body = {.keys = {0, 0, 0, 0, 0, 0}}};
+static struct zmk_hid_consumer_report consumer_report = {.report_id = 2, .body = {.keys = {0}}};
-#define _TOGGLE_MOD(mod, state) \
- if (modifier > MOD_RGUI) { \
- return -EINVAL; \
- } \
- WRITE_BIT(kp_report.body.modifiers, mod, state); \
- return 0;
+// Keep track of how often a modifier was pressed.
+// Only release the modifier if the count is 0.
+static int explicit_modifier_counts[8] = {0, 0, 0, 0, 0, 0, 0, 0};
+static zmk_mod_flags explicit_modifiers = 0;
-int zmk_hid_register_mod(zmk_mod modifier) { _TOGGLE_MOD(modifier, true); }
-int zmk_hid_unregister_mod(zmk_mod modifier) { _TOGGLE_MOD(modifier, false); }
+#define SET_MODIFIERS(mods) \
+ { \
+ kp_report.body.modifiers = mods; \
+ LOG_DBG("Modifiers set to 0x%02X", kp_report.body.modifiers); \
+ }
-int zmk_hid_register_mods(zmk_mod_flags modifiers) {
- kp_report.body.modifiers |= modifiers;
+int zmk_hid_register_mod(zmk_mod modifier) {
+ explicit_modifier_counts[modifier]++;
+ LOG_DBG("Modifier %d count %d", modifier, explicit_modifier_counts[modifier]);
+ WRITE_BIT(explicit_modifiers, modifier, true);
+ SET_MODIFIERS(explicit_modifiers);
return 0;
}
-int zmk_hid_unregister_mods(zmk_mod_flags modifiers) {
- kp_report.body.modifiers &= ~modifiers;
+int zmk_hid_unregister_mod(zmk_mod modifier) {
+ if (explicit_modifier_counts[modifier] <= 0) {
+ LOG_ERR("Tried to unregister modifier %d too often", modifier);
+ return -EINVAL;
+ }
+ explicit_modifier_counts[modifier]--;
+ LOG_DBG("Modifier %d count: %d", modifier, explicit_modifier_counts[modifier]);
+ if (explicit_modifier_counts[modifier] == 0) {
+ LOG_DBG("Modifier %d released", modifier);
+ WRITE_BIT(explicit_modifiers, modifier, false);
+ }
+ SET_MODIFIERS(explicit_modifiers);
return 0;
}
-#define KEY_OFFSET 0x02
-#define MAX_KEYS 6
-
-/*
-#define TOGGLE_BOOT_KEY(match, val) \
- for (int idx = 0; idx < MAX_KEYS; idx++) \
- { \
- if (kp_report.boot.keys[idx + KEY_OFFSET] != match) \
- { \
- continue; \
- } \
- kp_report.boot.keys[idx + KEY_OFFSET] = val; \
- break; \
+#define TOGGLE_KEYPAD(match, val) \
+ for (int idx = 0; idx < ZMK_HID_KEYPAD_NKRO_SIZE; idx++) { \
+ if (kp_report.body.keys[idx] != match) { \
+ continue; \
+ } \
+ kp_report.body.keys[idx] = val; \
+ break; \
}
-*/
-
-#define TOGGLE_KEY(code, val) WRITE_BIT(kp_report.body.keys[code / 8], code % 8, val)
#define TOGGLE_CONSUMER(match, val) \
- for (int idx = 0; idx < MAX_KEYS; idx++) { \
+ for (int idx = 0; idx < ZMK_HID_CONSUMER_NKRO_SIZE; idx++) { \
if (consumer_report.body.keys[idx] != match) { \
continue; \
} \
@@ -62,19 +67,21 @@ int zmk_hid_unregister_mods(zmk_mod_flags modifiers) {
break; \
}
+int zmk_hid_implicit_modifiers_press(zmk_mod_flags implicit_modifiers) {
+ SET_MODIFIERS(explicit_modifiers | implicit_modifiers);
+ return 0;
+}
+
+int zmk_hid_implicit_modifiers_release() {
+ SET_MODIFIERS(explicit_modifiers);
+ return 0;
+}
+
int zmk_hid_keypad_press(zmk_key code) {
if (code >= LCTL && code <= RGUI) {
return zmk_hid_register_mod(code - LCTL);
}
-
- if (code > ZMK_HID_MAX_KEYCODE) {
- return -EINVAL;
- }
-
- // TOGGLE_BOOT_KEY(0U, code);
-
- TOGGLE_KEY(code, true);
-
+ TOGGLE_KEYPAD(0U, code);
return 0;
};
@@ -82,18 +89,12 @@ int zmk_hid_keypad_release(zmk_key code) {
if (code >= LCTL && code <= RGUI) {
return zmk_hid_unregister_mod(code - LCTL);
}
-
- if (code > ZMK_HID_MAX_KEYCODE) {
- return -EINVAL;
- }
-
- // TOGGLE_BOOT_KEY(0U, code);
-
- TOGGLE_KEY(code, false);
-
+ TOGGLE_KEYPAD(code, 0U);
return 0;
};
+void zmk_hid_keypad_clear() { memset(&kp_report.body, 0, sizeof(kp_report.body)); }
+
int zmk_hid_consumer_press(zmk_key code) {
TOGGLE_CONSUMER(0U, code);
return 0;
@@ -104,6 +105,8 @@ int zmk_hid_consumer_release(zmk_key code) {
return 0;
};
+void zmk_hid_consumer_clear() { memset(&consumer_report.body, 0, sizeof(consumer_report.body)); }
+
struct zmk_hid_keypad_report *zmk_hid_get_keypad_report() {
return &kp_report;
}
diff --git a/app/src/hid_listener.c b/app/src/hid_listener.c
index 4467e6d..32e9d97 100644
--- a/app/src/hid_listener.c
+++ b/app/src/hid_listener.c
@@ -15,10 +15,11 @@ LOG_MODULE_DECLARE(zmk, CONFIG_ZMK_LOG_LEVEL);
#include <zmk/hid.h>
#include <zmk/endpoints.h>
-static int hid_listener_keycode_pressed(u8_t usage_page, u32_t keycode) {
+static int hid_listener_keycode_pressed(u8_t usage_page, u32_t keycode,
+ zmk_mod_flags implicit_modifiers) {
int err;
- LOG_DBG("usage_page 0x%02X keycode 0x%02X", usage_page, keycode);
-
+ LOG_DBG("usage_page 0x%02X keycode 0x%02X mods 0x%02X", usage_page, keycode,
+ implicit_modifiers);
switch (usage_page) {
case USAGE_KEYPAD:
err = zmk_hid_keypad_press(keycode);
@@ -35,14 +36,15 @@ static int hid_listener_keycode_pressed(u8_t usage_page, u32_t keycode) {
}
break;
}
-
+ zmk_hid_implicit_modifiers_press(implicit_modifiers);
return zmk_endpoints_send_report(usage_page);
}
-static int hid_listener_keycode_released(u8_t usage_page, u32_t keycode) {
+static int hid_listener_keycode_released(u8_t usage_page, u32_t keycode,
+ zmk_mod_flags implicit_modifiers) {
int err;
- LOG_DBG("usage_page 0x%02X keycode 0x%02X", usage_page, keycode);
-
+ LOG_DBG("usage_page 0x%02X keycode 0x%02X mods 0x%02X", usage_page, keycode,
+ implicit_modifiers);
switch (usage_page) {
case USAGE_KEYPAD:
err = zmk_hid_keypad_release(keycode);
@@ -57,44 +59,27 @@ static int hid_listener_keycode_released(u8_t usage_page, u32_t keycode) {
LOG_ERR("Unable to release keycode");
return err;
}
- break;
}
+ // There is a minor issue with this code.
+ // If LC(A) is pressed, then LS(B), then LC(A) is released, the shift for B will be released
+ // prematurely. This causes if LS(B) to repeat like Bbbbbbbb when pressed for a long time.
+ // Solving this would require keeping track of which key's implicit modifiers are currently
+ // active and only releasing modifiers at that time.
+ zmk_hid_implicit_modifiers_release();
return zmk_endpoints_send_report(usage_page);
}
-static int hid_listener_modifiers_pressed(zmk_mod_flags modifiers) {
- LOG_DBG("modifiers %d", modifiers);
-
- zmk_hid_register_mods(modifiers);
- return zmk_endpoints_send_report(USAGE_KEYPAD);
-}
-
-static int hid_listener_modifiers_released(zmk_mod_flags modifiers) {
- LOG_DBG("modifiers %d", modifiers);
-
- zmk_hid_unregister_mods(modifiers);
- return zmk_endpoints_send_report(USAGE_KEYPAD);
-}
-
int hid_listener(const struct zmk_event_header *eh) {
if (is_keycode_state_changed(eh)) {
const struct keycode_state_changed *ev = cast_keycode_state_changed(eh);
if (ev->state) {
- hid_listener_keycode_pressed(ev->usage_page, ev->keycode);
- } else {
- hid_listener_keycode_released(ev->usage_page, ev->keycode);
- }
- } else if (is_modifiers_state_changed(eh)) {
- const struct modifiers_state_changed *ev = cast_modifiers_state_changed(eh);
- if (ev->state) {
- hid_listener_modifiers_pressed(ev->modifiers);
+ hid_listener_keycode_pressed(ev->usage_page, ev->keycode, ev->implicit_modifiers);
} else {
- hid_listener_modifiers_released(ev->modifiers);
+ hid_listener_keycode_released(ev->usage_page, ev->keycode, ev->implicit_modifiers);
}
}
return 0;
}
ZMK_LISTENER(hid_listener, hid_listener);
-ZMK_SUBSCRIPTION(hid_listener, keycode_state_changed);
-ZMK_SUBSCRIPTION(hid_listener, modifiers_state_changed); \ No newline at end of file
+ZMK_SUBSCRIPTION(hid_listener, keycode_state_changed); \ No newline at end of file
diff --git a/app/src/keymap.c b/app/src/keymap.c
index 74fe60d..1d289e5 100644
--- a/app/src/keymap.c
+++ b/app/src/keymap.c
@@ -11,7 +11,6 @@ LOG_MODULE_DECLARE(zmk, CONFIG_ZMK_LOG_LEVEL);
#include <zmk/matrix.h>
#include <zmk/sensors.h>
#include <zmk/keymap.h>
-#include <dt-bindings/zmk/matrix-transform.h>
#include <drivers/behavior.h>
#include <zmk/behavior.h>
diff --git a/app/src/power.c b/app/src/power.c
index 73b3f12..bad54d2 100644
--- a/app/src/power.c
+++ b/app/src/power.c
@@ -23,14 +23,7 @@ static u32_t power_last_uptime;
bool is_usb_power_present() {
#ifdef CONFIG_USB
- enum usb_dc_status_code usb_status = zmk_usb_get_status();
- switch (usb_status) {
- case USB_DC_DISCONNECTED:
- case USB_DC_UNKNOWN:
- return false;
- default:
- return true;
- }
+ return zmk_usb_is_powered();
#else
return false;
#endif /* CONFIG_USB */
diff --git a/app/src/rgb_underglow.c b/app/src/rgb_underglow.c
index b371c94..084482e 100644
--- a/app/src/rgb_underglow.c
+++ b/app/src/rgb_underglow.c
@@ -227,6 +227,14 @@ static void zmk_rgb_underglow_tick_handler(struct k_timer *timer) {
K_TIMER_DEFINE(underglow_tick, zmk_rgb_underglow_tick_handler, NULL);
+#if IS_ENABLED(CONFIG_SETTINGS)
+static void zmk_rgb_underglow_save_state_work() {
+ settings_save_one("rgb/underglow/state", &state, sizeof(state));
+}
+
+static struct k_delayed_work underglow_save_work;
+#endif
+
static int zmk_rgb_underglow_init(struct device *_arg) {
led_strip = device_get_binding(STRIP_LABEL);
if (led_strip) {
@@ -248,6 +256,7 @@ static int zmk_rgb_underglow_init(struct device *_arg) {
#if IS_ENABLED(CONFIG_SETTINGS)
settings_register(&rgb_conf);
+ k_delayed_work_init(&underglow_save_work, zmk_rgb_underglow_save_state_work);
#endif
k_timer_start(&underglow_tick, K_NO_WAIT, K_MSEC(50));
@@ -257,7 +266,8 @@ static int zmk_rgb_underglow_init(struct device *_arg) {
int zmk_rgb_underglow_save_state() {
#if IS_ENABLED(CONFIG_SETTINGS)
- return settings_save_one("rgb/underglow/state", &state, sizeof(state));
+ k_delayed_work_cancel(&underglow_save_work);
+ return k_delayed_work_submit(&underglow_save_work, K_MINUTES(1));
#else
return 0;
#endif
diff --git a/app/src/usb.c b/app/src/usb.c
index 434b3d4..79d03c7 100644
--- a/app/src/usb.c
+++ b/app/src/usb.c
@@ -9,10 +9,11 @@
#include <usb/usb_device.h>
#include <usb/class/usb_hid.h>
-#include <dt-bindings/zmk/keys.h>
#include <zmk/hid.h>
#include <zmk/keymap.h>
+#include <zmk/event-manager.h>
+#include <zmk/events/usb-conn-state-changed.h>
LOG_MODULE_DECLARE(zmk, CONFIG_ZMK_LOG_LEVEL);
@@ -53,9 +54,34 @@ int zmk_usb_hid_send_report(const u8_t *report, size_t len) {
#endif /* CONFIG_ZMK_USB */
+static void raise_usb_status_changed_event() {
+ struct usb_conn_state_changed *ev = new_usb_conn_state_changed();
+ ev->conn_state = zmk_usb_get_conn_state();
+
+ ZMK_EVENT_RAISE(ev);
+}
+
enum usb_dc_status_code zmk_usb_get_status() { return usb_status; }
-void usb_status_cb(enum usb_dc_status_code status, const u8_t *params) { usb_status = status; };
+enum zmk_usb_conn_state zmk_usb_get_conn_state() {
+ switch (usb_status) {
+ case USB_DC_DISCONNECTED:
+ case USB_DC_UNKNOWN:
+ return ZMK_USB_CONN_NONE;
+
+ case USB_DC_ERROR:
+ case USB_DC_RESET:
+ return ZMK_USB_CONN_POWERED;
+
+ default:
+ return ZMK_USB_CONN_HID;
+ }
+}
+
+void usb_status_cb(enum usb_dc_status_code status, const u8_t *params) {
+ usb_status = status;
+ raise_usb_status_changed_event();
+};
static int zmk_usb_init(struct device *_arg) {
int usb_enable_ret;
diff --git a/app/tests/hold-tap/balanced/1-dn-up/keycode_events.snapshot b/app/tests/hold-tap/balanced/1-dn-up/keycode_events.snapshot
index 5f6a266..c088e5e 100644
--- a/app/tests/hold-tap/balanced/1-dn-up/keycode_events.snapshot
+++ b/app/tests/hold-tap/balanced/1-dn-up/keycode_events.snapshot
@@ -1,5 +1,5 @@
ht_binding_pressed: 0 new undecided hold_tap
ht_decide: 0 decided tap (balanced event 0)
-kp_pressed: usage_page 0x07 keycode 0x09
-kp_released: usage_page 0x07 keycode 0x09
+kp_pressed: usage_page 0x07 keycode 0x09 mods 0x00
+kp_released: usage_page 0x07 keycode 0x09 mods 0x00
ht_binding_released: 0 cleaning up hold-tap
diff --git a/app/tests/hold-tap/balanced/2-dn-timer-up/keycode_events.snapshot b/app/tests/hold-tap/balanced/2-dn-timer-up/keycode_events.snapshot
index ddda1ae..a8b5d1f 100644
--- a/app/tests/hold-tap/balanced/2-dn-timer-up/keycode_events.snapshot
+++ b/app/tests/hold-tap/balanced/2-dn-timer-up/keycode_events.snapshot
@@ -1,5 +1,5 @@
ht_binding_pressed: 0 new undecided hold_tap
ht_decide: 0 decided hold (balanced event 3)
-kp_pressed: usage_page 0x07 keycode 0xe1
-kp_released: usage_page 0x07 keycode 0xe1
+kp_pressed: usage_page 0x07 keycode 0xe1 mods 0x00
+kp_released: usage_page 0x07 keycode 0xe1 mods 0x00
ht_binding_released: 0 cleaning up hold-tap
diff --git a/app/tests/hold-tap/balanced/3a-moddn-dn-modup-up/keycode_events.snapshot b/app/tests/hold-tap/balanced/3a-moddn-dn-modup-up/keycode_events.snapshot
index a435103..ca458c7 100644
--- a/app/tests/hold-tap/balanced/3a-moddn-dn-modup-up/keycode_events.snapshot
+++ b/app/tests/hold-tap/balanced/3a-moddn-dn-modup-up/keycode_events.snapshot
@@ -1,7 +1,7 @@
-kp_pressed: usage_page 0x07 keycode 0xe4
+kp_pressed: usage_page 0x07 keycode 0xe4 mods 0x00
ht_binding_pressed: 0 new undecided hold_tap
ht_decide: 0 decided tap (balanced event 0)
-kp_pressed: usage_page 0x07 keycode 0x09
-kp_released: usage_page 0x07 keycode 0xe4
-kp_released: usage_page 0x07 keycode 0x09
+kp_pressed: usage_page 0x07 keycode 0x09 mods 0x00
+kp_released: usage_page 0x07 keycode 0xe4 mods 0x00
+kp_released: usage_page 0x07 keycode 0x09 mods 0x00
ht_binding_released: 0 cleaning up hold-tap
diff --git a/app/tests/hold-tap/balanced/3b-moddn-dn-modup-timer-up/keycode_events.snapshot b/app/tests/hold-tap/balanced/3b-moddn-dn-modup-timer-up/keycode_events.snapshot
index c0da94f..ef4dfa5 100644
--- a/app/tests/hold-tap/balanced/3b-moddn-dn-modup-timer-up/keycode_events.snapshot
+++ b/app/tests/hold-tap/balanced/3b-moddn-dn-modup-timer-up/keycode_events.snapshot
@@ -1,7 +1,7 @@
-kp_pressed: usage_page 0x07 keycode 0xe4
+kp_pressed: usage_page 0x07 keycode 0xe4 mods 0x00
ht_binding_pressed: 0 new undecided hold_tap
ht_decide: 0 decided hold (balanced event 3)
-kp_pressed: usage_page 0x07 keycode 0xe1
-kp_released: usage_page 0x07 keycode 0xe4
-kp_released: usage_page 0x07 keycode 0xe1
+kp_pressed: usage_page 0x07 keycode 0xe1 mods 0x00
+kp_released: usage_page 0x07 keycode 0xe4 mods 0x00
+kp_released: usage_page 0x07 keycode 0xe1 mods 0x00
ht_binding_released: 0 cleaning up hold-tap
diff --git a/app/tests/hold-tap/balanced/3c-kcdn-dn-kcup-up/keycode_events.snapshot b/app/tests/hold-tap/balanced/3c-kcdn-dn-kcup-up/keycode_events.snapshot
index ce6e7b7..70a3353 100644
--- a/app/tests/hold-tap/balanced/3c-kcdn-dn-kcup-up/keycode_events.snapshot
+++ b/app/tests/hold-tap/balanced/3c-kcdn-dn-kcup-up/keycode_events.snapshot
@@ -1,7 +1,7 @@
-kp_pressed: usage_page 0x07 keycode 0x07
+kp_pressed: usage_page 0x07 keycode 0x07 mods 0x00
ht_binding_pressed: 0 new undecided hold_tap
-kp_released: usage_page 0x07 keycode 0x07
+kp_released: usage_page 0x07 keycode 0x07 mods 0x00
ht_decide: 0 decided tap (balanced event 0)
-kp_pressed: usage_page 0x07 keycode 0x09
-kp_released: usage_page 0x07 keycode 0x09
+kp_pressed: usage_page 0x07 keycode 0x09 mods 0x00
+kp_released: usage_page 0x07 keycode 0x09 mods 0x00
ht_binding_released: 0 cleaning up hold-tap
diff --git a/app/tests/hold-tap/balanced/3d-kcdn-dn-kcup-timer-up/keycode_events.snapshot b/app/tests/hold-tap/balanced/3d-kcdn-dn-kcup-timer-up/keycode_events.snapshot
index 1ec384a..121f007 100644
--- a/app/tests/hold-tap/balanced/3d-kcdn-dn-kcup-timer-up/keycode_events.snapshot
+++ b/app/tests/hold-tap/balanced/3d-kcdn-dn-kcup-timer-up/keycode_events.snapshot
@@ -1,7 +1,7 @@
-kp_pressed: usage_page 0x07 keycode 0x07
+kp_pressed: usage_page 0x07 keycode 0x07 mods 0x00
ht_binding_pressed: 0 new undecided hold_tap
-kp_released: usage_page 0x07 keycode 0x07
+kp_released: usage_page 0x07 keycode 0x07 mods 0x00
ht_decide: 0 decided hold (balanced event 3)
-kp_pressed: usage_page 0x07 keycode 0xe1
-kp_released: usage_page 0x07 keycode 0xe1
+kp_pressed: usage_page 0x07 keycode 0xe1 mods 0x00
+kp_released: usage_page 0x07 keycode 0xe1 mods 0x00
ht_binding_released: 0 cleaning up hold-tap
diff --git a/app/tests/hold-tap/balanced/4a-dn-htdn-timer-htup-up/keycode_events.snapshot b/app/tests/hold-tap/balanced/4a-dn-htdn-timer-htup-up/keycode_events.snapshot
index 8a1980b..ae9dcc9 100644
--- a/app/tests/hold-tap/balanced/4a-dn-htdn-timer-htup-up/keycode_events.snapshot
+++ b/app/tests/hold-tap/balanced/4a-dn-htdn-timer-htup-up/keycode_events.snapshot
@@ -1,10 +1,10 @@
ht_binding_pressed: 0 new undecided hold_tap
ht_decide: 0 decided hold (balanced event 3)
-kp_pressed: usage_page 0x07 keycode 0xe1
+kp_pressed: usage_page 0x07 keycode 0xe1 mods 0x00
ht_binding_pressed: 1 new undecided hold_tap
ht_decide: 1 decided tap (balanced event 0)
-kp_pressed: usage_page 0x07 keycode 0x0d
-kp_released: usage_page 0x07 keycode 0x0d
+kp_pressed: usage_page 0x07 keycode 0x0d mods 0x00
+kp_released: usage_page 0x07 keycode 0x0d mods 0x00
ht_binding_released: 1 cleaning up hold-tap
-kp_released: usage_page 0x07 keycode 0xe1
+kp_released: usage_page 0x07 keycode 0xe1 mods 0x00
ht_binding_released: 0 cleaning up hold-tap
diff --git a/app/tests/hold-tap/balanced/4a-dn-kcdn-timer-kcup-up/keycode_events.snapshot b/app/tests/hold-tap/balanced/4a-dn-kcdn-timer-kcup-up/keycode_events.snapshot
index b89b21d..c1e03ad 100644
--- a/app/tests/hold-tap/balanced/4a-dn-kcdn-timer-kcup-up/keycode_events.snapshot
+++ b/app/tests/hold-tap/balanced/4a-dn-kcdn-timer-kcup-up/keycode_events.snapshot
@@ -1,7 +1,7 @@
ht_binding_pressed: 0 new undecided hold_tap
ht_decide: 0 decided hold (balanced event 3)
-kp_pressed: usage_page 0x07 keycode 0xe1
-kp_pressed: usage_page 0x07 keycode 0x07
-kp_released: usage_page 0x07 keycode 0x07
-kp_released: usage_page 0x07 keycode 0xe1
+kp_pressed: usage_page 0x07 keycode 0xe1 mods 0x00
+kp_pressed: usage_page 0x07 keycode 0x07 mods 0x00
+kp_released: usage_page 0x07 keycode 0x07 mods 0x00
+kp_released: usage_page 0x07 keycode 0xe1 mods 0x00
ht_binding_released: 0 cleaning up hold-tap
diff --git a/app/tests/hold-tap/balanced/4b-dn-kcdn-kcup-timer-up/keycode_events.snapshot b/app/tests/hold-tap/balanced/4b-dn-kcdn-kcup-timer-up/keycode_events.snapshot
index 798e2ee..95330e6 100644
--- a/app/tests/hold-tap/balanced/4b-dn-kcdn-kcup-timer-up/keycode_events.snapshot
+++ b/app/tests/hold-tap/balanced/4b-dn-kcdn-kcup-timer-up/keycode_events.snapshot
@@ -1,7 +1,7 @@
ht_binding_pressed: 0 new undecided hold_tap
ht_decide: 0 decided hold (balanced event 2)
-kp_pressed: usage_page 0x07 keycode 0xe1
-kp_pressed: usage_page 0x07 keycode 0x07
-kp_released: usage_page 0x07 keycode 0x07
-kp_released: usage_page 0x07 keycode 0xe1
+kp_pressed: usage_page 0x07 keycode 0xe1 mods 0x00
+kp_pressed: usage_page 0x07 keycode 0x07 mods 0x00
+kp_released: usage_page 0x07 keycode 0x07 mods 0x00
+kp_released: usage_page 0x07 keycode 0xe1 mods 0x00
ht_binding_released: 0 cleaning up hold-tap
diff --git a/app/tests/hold-tap/balanced/4c-dn-kcdn-kcup-up/keycode_events.snapshot b/app/tests/hold-tap/balanced/4c-dn-kcdn-kcup-up/keycode_events.snapshot
index 798e2ee..95330e6 100644
--- a/app/tests/hold-tap/balanced/4c-dn-kcdn-kcup-up/keycode_events.snapshot
+++ b/app/tests/hold-tap/balanced/4c-dn-kcdn-kcup-up/keycode_events.snapshot
@@ -1,7 +1,7 @@
ht_binding_pressed: 0 new undecided hold_tap
ht_decide: 0 decided hold (balanced event 2)
-kp_pressed: usage_page 0x07 keycode 0xe1
-kp_pressed: usage_page 0x07 keycode 0x07
-kp_released: usage_page 0x07 keycode 0x07
-kp_released: usage_page 0x07 keycode 0xe1
+kp_pressed: usage_page 0x07 keycode 0xe1 mods 0x00
+kp_pressed: usage_page 0x07 keycode 0x07 mods 0x00
+kp_released: usage_page 0x07 keycode 0x07 mods 0x00
+kp_released: usage_page 0x07 keycode 0xe1 mods 0x00
ht_binding_released: 0 cleaning up hold-tap
diff --git a/app/tests/hold-tap/balanced/4d-dn-kcdn-timer-up-kcup/keycode_events.snapshot b/app/tests/hold-tap/balanced/4d-dn-kcdn-timer-up-kcup/keycode_events.snapshot
index 5c9f4e3..63219ee 100644
--- a/app/tests/hold-tap/balanced/4d-dn-kcdn-timer-up-kcup/keycode_events.snapshot
+++ b/app/tests/hold-tap/balanced/4d-dn-kcdn-timer-up-kcup/keycode_events.snapshot
@@ -1,7 +1,7 @@
ht_binding_pressed: 0 new undecided hold_tap
ht_decide: 0 decided tap (balanced event 0)
-kp_pressed: usage_page 0x07 keycode 0x09
-kp_pressed: usage_page 0x07 keycode 0x07
-kp_released: usage_page 0x07 keycode 0x09
+kp_pressed: usage_page 0x07 keycode 0x09 mods 0x00
+kp_pressed: usage_page 0x07 keycode 0x07 mods 0x00
+kp_released: usage_page 0x07 keycode 0x09 mods 0x00
ht_binding_released: 0 cleaning up hold-tap
-kp_released: usage_page 0x07 keycode 0x07
+kp_released: usage_page 0x07 keycode 0x07 mods 0x00
diff --git a/app/tests/hold-tap/balanced/many-nested/keycode_events.snapshot b/app/tests/hold-tap/balanced/many-nested/keycode_events.snapshot
index 806896f..dda02dd 100644
--- a/app/tests/hold-tap/balanced/many-nested/keycode_events.snapshot
+++ b/app/tests/hold-tap/balanced/many-nested/keycode_events.snapshot
@@ -1,20 +1,20 @@
ht_binding_pressed: 0 new undecided hold_tap
ht_decide: 0 decided hold (balanced event 3)
-kp_pressed: usage_page 0x07 keycode 0xe1
+kp_pressed: usage_page 0x07 keycode 0xe1 mods 0x00
ht_binding_pressed: 1 new undecided hold_tap
ht_decide: 1 decided hold (balanced event 3)
-kp_pressed: usage_page 0x07 keycode 0xe0
+kp_pressed: usage_page 0x07 keycode 0xe0 mods 0x00
ht_binding_pressed: 2 new undecided hold_tap
ht_binding_released: 0 cleaning up hold-tap
ht_decide: 2 decided hold (balanced event 3)
-kp_pressed: usage_page 0x07 keycode 0xe3
+kp_pressed: usage_page 0x07 keycode 0xe3 mods 0x00
ht_binding_pressed: 3 new undecided hold_tap
ht_binding_released: 1 cleaning up hold-tap
ht_decide: 3 decided hold (balanced event 3)
-kp_pressed: usage_page 0x07 keycode 0xe2
-kp_released: usage_page 0x07 keycode 0xe1
-kp_released: usage_page 0x07 keycode 0xe0
-kp_released: usage_page 0x07 keycode 0xe3
+kp_pressed: usage_page 0x07 keycode 0xe2 mods 0x00
+kp_released: usage_page 0x07 keycode 0xe1 mods 0x00
+kp_released: usage_page 0x07 keycode 0xe0 mods 0x00
+kp_released: usage_page 0x07 keycode 0xe3 mods 0x00
ht_binding_released: 2 cleaning up hold-tap
-kp_released: usage_page 0x07 keycode 0xe2
+kp_released: usage_page 0x07 keycode 0xe2 mods 0x00
ht_binding_released: 3 cleaning up hold-tap
diff --git a/app/tests/hold-tap/hold-preferred/1-dn-up/keycode_events.snapshot b/app/tests/hold-tap/hold-preferred/1-dn-up/keycode_events.snapshot
index cf787d8..2eb6475 100644
--- a/app/tests/hold-tap/hold-preferred/1-dn-up/keycode_events.snapshot
+++ b/app/tests/hold-tap/hold-preferred/1-dn-up/keycode_events.snapshot
@@ -1,5 +1,5 @@
ht_binding_pressed: 0 new undecided hold_tap
ht_decide: 0 decided tap (hold-preferred event 0)
-kp_pressed: usage_page 0x07 keycode 0x09
-kp_released: usage_page 0x07 keycode 0x09
+kp_pressed: usage_page 0x07 keycode 0x09 mods 0x00
+kp_released: usage_page 0x07 keycode 0x09 mods 0x00
ht_binding_released: 0 cleaning up hold-tap
diff --git a/app/tests/hold-tap/hold-preferred/2-dn-timer-up/keycode_events.snapshot b/app/tests/hold-tap/hold-preferred/2-dn-timer-up/keycode_events.snapshot
index 03329d5..86517aa 100644
--- a/app/tests/hold-tap/hold-preferred/2-dn-timer-up/keycode_events.snapshot
+++ b/app/tests/hold-tap/hold-preferred/2-dn-timer-up/keycode_events.snapshot
@@ -1,5 +1,5 @@
ht_binding_pressed: 0 new undecided hold_tap
ht_decide: 0 decided hold (hold-preferred event 3)
-kp_pressed: usage_page 0x07 keycode 0xe1
-kp_released: usage_page 0x07 keycode 0xe1
+kp_pressed: usage_page 0x07 keycode 0xe1 mods 0x00
+kp_released: usage_page 0x07 keycode 0xe1 mods 0x00
ht_binding_released: 0 cleaning up hold-tap
diff --git a/app/tests/hold-tap/hold-preferred/3a-moddn-dn-modup-up/keycode_events.snapshot b/app/tests/hold-tap/hold-preferred/3a-moddn-dn-modup-up/keycode_events.snapshot
index adf4fe2..b7434c6 100644
--- a/app/tests/hold-tap/hold-preferred/3a-moddn-dn-modup-up/keycode_events.snapshot
+++ b/app/tests/hold-tap/hold-preferred/3a-moddn-dn-modup-up/keycode_events.snapshot
@@ -1,7 +1,7 @@
-kp_pressed: usage_page 0x07 keycode 0xe4
+kp_pressed: usage_page 0x07 keycode 0xe4 mods 0x00
ht_binding_pressed: 0 new undecided hold_tap
ht_decide: 0 decided tap (hold-preferred event 0)
-kp_pressed: usage_page 0x07 keycode 0x09
-kp_released: usage_page 0x07 keycode 0xe4
-kp_released: usage_page 0x07 keycode 0x09
+kp_pressed: usage_page 0x07 keycode 0x09 mods 0x00
+kp_released: usage_page 0x07 keycode 0xe4 mods 0x00
+kp_released: usage_page 0x07 keycode 0x09 mods 0x00
ht_binding_released: 0 cleaning up hold-tap
diff --git a/app/tests/hold-tap/hold-preferred/3b-moddn-dn-modup-timer-up/keycode_events.snapshot b/app/tests/hold-tap/hold-preferred/3b-moddn-dn-modup-timer-up/keycode_events.snapshot
index 69b64a9..ffb6aad 100644
--- a/app/tests/hold-tap/hold-preferred/3b-moddn-dn-modup-timer-up/keycode_events.snapshot
+++ b/app/tests/hold-tap/hold-preferred/3b-moddn-dn-modup-timer-up/keycode_events.snapshot
@@ -1,7 +1,7 @@
-kp_pressed: usage_page 0x07 keycode 0xe4
+kp_pressed: usage_page 0x07 keycode 0xe4 mods 0x00
ht_binding_pressed: 0 new undecided hold_tap
ht_decide: 0 decided hold (hold-preferred event 3)
-kp_pressed: usage_page 0x07 keycode 0xe1
-kp_released: usage_page 0x07 keycode 0xe4
-kp_released: usage_page 0x07 keycode 0xe1
+kp_pressed: usage_page 0x07 keycode 0xe1 mods 0x00
+kp_released: usage_page 0x07 keycode 0xe4 mods 0x00
+kp_released: usage_page 0x07 keycode 0xe1 mods 0x00
ht_binding_released: 0 cleaning up hold-tap
diff --git a/app/tests/hold-tap/hold-preferred/3c-kcdn-dn-kcup-up/keycode_events.snapshot b/app/tests/hold-tap/hold-preferred/3c-kcdn-dn-kcup-up/keycode_events.snapshot
index b06a1d7..1254fed 100644
--- a/app/tests/hold-tap/hold-preferred/3c-kcdn-dn-kcup-up/keycode_events.snapshot
+++ b/app/tests/hold-tap/hold-preferred/3c-kcdn-dn-kcup-up/keycode_events.snapshot
@@ -1,7 +1,7 @@
-kp_pressed: usage_page 0x07 keycode 0x07
+kp_pressed: usage_page 0x07 keycode 0x07 mods 0x00
ht_binding_pressed: 0 new undecided hold_tap
-kp_released: usage_page 0x07 keycode 0x07
+kp_released: usage_page 0x07 keycode 0x07 mods 0x00
ht_decide: 0 decided tap (hold-preferred event 0)
-kp_pressed: usage_page 0x07 keycode 0x09
-kp_released: usage_page 0x07 keycode 0x09
+kp_pressed: usage_page 0x07 keycode 0x09 mods 0x00
+kp_released: usage_page 0x07 keycode 0x09 mods 0x00
ht_binding_released: 0 cleaning up hold-tap
diff --git a/app/tests/hold-tap/hold-preferred/3d-kcdn-dn-kcup-timer-up/keycode_events.snapshot b/app/tests/hold-tap/hold-preferred/3d-kcdn-dn-kcup-timer-up/keycode_events.snapshot
index bf31955..ba7b48b 100644
--- a/app/tests/hold-tap/hold-preferred/3d-kcdn-dn-kcup-timer-up/keycode_events.snapshot
+++ b/app/tests/hold-tap/hold-preferred/3d-kcdn-dn-kcup-timer-up/keycode_events.snapshot
@@ -1,7 +1,7 @@
-kp_pressed: usage_page 0x07 keycode 0x07
+kp_pressed: usage_page 0x07 keycode 0x07 mods 0x00
ht_binding_pressed: 0 new undecided hold_tap
-kp_released: usage_page 0x07 keycode 0x07
+kp_released: usage_page 0x07 keycode 0x07 mods 0x00
ht_decide: 0 decided hold (hold-preferred event 3)
-kp_pressed: usage_page 0x07 keycode 0xe1
-kp_released: usage_page 0x07 keycode 0xe1
+kp_pressed: usage_page 0x07 keycode 0xe1 mods 0x00
+kp_released: usage_page 0x07 keycode 0xe1 mods 0x00
ht_binding_released: 0 cleaning up hold-tap
diff --git a/app/tests/hold-tap/hold-preferred/4a-dn-htdn-timer-htup-up/keycode_events.snapshot b/app/tests/hold-tap/hold-preferred/4a-dn-htdn-timer-htup-up/keycode_events.snapshot
index 3ed7de0..c8acfc1 100644
--- a/app/tests/hold-tap/hold-preferred/4a-dn-htdn-timer-htup-up/keycode_events.snapshot
+++ b/app/tests/hold-tap/hold-preferred/4a-dn-htdn-timer-htup-up/keycode_events.snapshot
@@ -1,10 +1,10 @@
ht_binding_pressed: 0 new undecided hold_tap
ht_decide: 0 decided hold (hold-preferred event 1)
-kp_pressed: usage_page 0x07 keycode 0xe1
+kp_pressed: usage_page 0x07 keycode 0xe1 mods 0x00
ht_binding_pressed: 1 new undecided hold_tap
ht_decide: 1 decided tap (hold-preferred event 0)
-kp_pressed: usage_page 0x07 keycode 0x0d
-kp_released: usage_page 0x07 keycode 0x0d
+kp_pressed: usage_page 0x07 keycode 0x0d mods 0x00
+kp_released: usage_page 0x07 keycode 0x0d mods 0x00
ht_binding_released: 1 cleaning up hold-tap
-kp_released: usage_page 0x07 keycode 0xe1
+kp_released: usage_page 0x07 keycode 0xe1 mods 0x00
ht_binding_released: 0 cleaning up hold-tap
diff --git a/app/tests/hold-tap/hold-preferred/4a-dn-kcdn-timer-kcup-up/keycode_events.snapshot b/app/tests/hold-tap/hold-preferred/4a-dn-kcdn-timer-kcup-up/keycode_events.snapshot
index e0b57fd..97cd07b 100644
--- a/app/tests/hold-tap/hold-preferred/4a-dn-kcdn-timer-kcup-up/keycode_events.snapshot
+++ b/app/tests/hold-tap/hold-preferred/4a-dn-kcdn-timer-kcup-up/keycode_events.snapshot
@@ -1,7 +1,7 @@
ht_binding_pressed: 0 new undecided hold_tap
ht_decide: 0 decided hold (hold-preferred event 1)
-kp_pressed: usage_page 0x07 keycode 0xe1
-kp_pressed: usage_page 0x07 keycode 0x07
-kp_released: usage_page 0x07 keycode 0x07
-kp_released: usage_page 0x07 keycode 0xe1
+kp_pressed: usage_page 0x07 keycode 0xe1 mods 0x00
+kp_pressed: usage_page 0x07 keycode 0x07 mods 0x00
+kp_released: usage_page 0x07 keycode 0x07 mods 0x00
+kp_released: usage_page 0x07 keycode 0xe1 mods 0x00
ht_binding_released: 0 cleaning up hold-tap
diff --git a/app/tests/hold-tap/hold-preferred/4b-dn-kcdn-kcup-timer-up/keycode_events.snapshot b/app/tests/hold-tap/hold-preferred/4b-dn-kcdn-kcup-timer-up/keycode_events.snapshot
index e0b57fd..97cd07b 100644
--- a/app/tests/hold-tap/hold-preferred/4b-dn-kcdn-kcup-timer-up/keycode_events.snapshot
+++ b/app/tests/hold-tap/hold-preferred/4b-dn-kcdn-kcup-timer-up/keycode_events.snapshot
@@ -1,7 +1,7 @@
ht_binding_pressed: 0 new undecided hold_tap
ht_decide: 0 decided hold (hold-preferred event 1)
-kp_pressed: usage_page 0x07 keycode 0xe1
-kp_pressed: usage_page 0x07 keycode 0x07
-kp_released: usage_page 0x07 keycode 0x07
-kp_released: usage_page 0x07 keycode 0xe1
+kp_pressed: usage_page 0x07 keycode 0xe1 mods 0x00
+kp_pressed: usage_page 0x07 keycode 0x07 mods 0x00
+kp_released: usage_page 0x07 keycode 0x07 mods 0x00
+kp_released: usage_page 0x07 keycode 0xe1 mods 0x00
ht_binding_released: 0 cleaning up hold-tap
diff --git a/app/tests/hold-tap/hold-preferred/4c-dn-kcdn-kcup-up/keycode_events.snapshot b/app/tests/hold-tap/hold-preferred/4c-dn-kcdn-kcup-up/keycode_events.snapshot
index e0b57fd..97cd07b 100644
--- a/app/tests/hold-tap/hold-preferred/4c-dn-kcdn-kcup-up/keycode_events.snapshot
+++ b/app/tests/hold-tap/hold-preferred/4c-dn-kcdn-kcup-up/keycode_events.snapshot
@@ -1,7 +1,7 @@
ht_binding_pressed: 0 new undecided hold_tap
ht_decide: 0 decided hold (hold-preferred event 1)
-kp_pressed: usage_page 0x07 keycode 0xe1
-kp_pressed: usage_page 0x07 keycode 0x07
-kp_released: usage_page 0x07 keycode 0x07
-kp_released: usage_page 0x07 keycode 0xe1
+kp_pressed: usage_page 0x07 keycode 0xe1 mods 0x00
+kp_pressed: usage_page 0x07 keycode 0x07 mods 0x00
+kp_released: usage_page 0x07 keycode 0x07 mods 0x00
+kp_released: usage_page 0x07 keycode 0xe1 mods 0x00
ht_binding_released: 0 cleaning up hold-tap
diff --git a/app/tests/hold-tap/hold-preferred/4d-dn-kcdn-timer-up-kcup/keycode_events.snapshot b/app/tests/hold-tap/hold-preferred/4d-dn-kcdn-timer-up-kcup/keycode_events.snapshot
index cac579d..2a21d92 100644
--- a/app/tests/hold-tap/hold-preferred/4d-dn-kcdn-timer-up-kcup/keycode_events.snapshot
+++ b/app/tests/hold-tap/hold-preferred/4d-dn-kcdn-timer-up-kcup/keycode_events.snapshot
@@ -1,7 +1,7 @@
ht_binding_pressed: 0 new undecided hold_tap
ht_decide: 0 decided hold (hold-preferred event 1)
-kp_pressed: usage_page 0x07 keycode 0xe1
-kp_pressed: usage_page 0x07 keycode 0x07
-kp_released: usage_page 0x07 keycode 0xe1
+kp_pressed: usage_page 0x07 keycode 0xe1 mods 0x00
+kp_pressed: usage_page 0x07 keycode 0x07 mods 0x00
+kp_released: usage_page 0x07 keycode 0xe1 mods 0x00
ht_binding_released: 0 cleaning up hold-tap
-kp_released: usage_page 0x07 keycode 0x07
+kp_released: usage_page 0x07 keycode 0x07 mods 0x00
diff --git a/app/tests/hold-tap/tap-preferred/1-dn-up/keycode_events.snapshot b/app/tests/hold-tap/tap-preferred/1-dn-up/keycode_events.snapshot
index 2a250fb..57e4fcd 100644
--- a/app/tests/hold-tap/tap-preferred/1-dn-up/keycode_events.snapshot
+++ b/app/tests/hold-tap/tap-preferred/1-dn-up/keycode_events.snapshot
@@ -1,5 +1,5 @@
ht_binding_pressed: 0 new undecided hold_tap
ht_decide: 0 decided tap (tap-preferred event 0)
-kp_pressed: usage_page 0x07 keycode 0x09
-kp_released: usage_page 0x07 keycode 0x09
+kp_pressed: usage_page 0x07 keycode 0x09 mods 0x00
+kp_released: usage_page 0x07 keycode 0x09 mods 0x00
ht_binding_released: 0 cleaning up hold-tap
diff --git a/app/tests/hold-tap/tap-preferred/2-dn-timer-up/keycode_events.snapshot b/app/tests/hold-tap/tap-preferred/2-dn-timer-up/keycode_events.snapshot
index 4f1ee63..71ba8da 100644
--- a/app/tests/hold-tap/tap-preferred/2-dn-timer-up/keycode_events.snapshot
+++ b/app/tests/hold-tap/tap-preferred/2-dn-timer-up/keycode_events.snapshot
@@ -1,5 +1,5 @@
ht_binding_pressed: 0 new undecided hold_tap
ht_decide: 0 decided hold (tap-preferred event 3)
-kp_pressed: usage_page 0x07 keycode 0xe1
-kp_released: usage_page 0x07 keycode 0xe1
+kp_pressed: usage_page 0x07 keycode 0xe1 mods 0x00
+kp_released: usage_page 0x07 keycode 0xe1 mods 0x00
ht_binding_released: 0 cleaning up hold-tap
diff --git a/app/tests/hold-tap/tap-preferred/3a-moddn-dn-modup-up/keycode_events.snapshot b/app/tests/hold-tap/tap-preferred/3a-moddn-dn-modup-up/keycode_events.snapshot
index 87d1406..0bc731f 100644
--- a/app/tests/hold-tap/tap-preferred/3a-moddn-dn-modup-up/keycode_events.snapshot
+++ b/app/tests/hold-tap/tap-preferred/3a-moddn-dn-modup-up/keycode_events.snapshot
@@ -1,7 +1,7 @@
-kp_pressed: usage_page 0x07 keycode 0xe4
+kp_pressed: usage_page 0x07 keycode 0xe4 mods 0x00
ht_binding_pressed: 0 new undecided hold_tap
ht_decide: 0 decided tap (tap-preferred event 0)
-kp_pressed: usage_page 0x07 keycode 0x09
-kp_released: usage_page 0x07 keycode 0xe4
-kp_released: usage_page 0x07 keycode 0x09
+kp_pressed: usage_page 0x07 keycode 0x09 mods 0x00
+kp_released: usage_page 0x07 keycode 0xe4 mods 0x00
+kp_released: usage_page 0x07 keycode 0x09 mods 0x00
ht_binding_released: 0 cleaning up hold-tap
diff --git a/app/tests/hold-tap/tap-preferred/3b-moddn-dn-modup-timer-up/keycode_events.snapshot b/app/tests/hold-tap/tap-preferred/3b-moddn-dn-modup-timer-up/keycode_events.snapshot
index 7455d2a..6a3398f 100644
--- a/app/tests/hold-tap/tap-preferred/3b-moddn-dn-modup-timer-up/keycode_events.snapshot
+++ b/app/tests/hold-tap/tap-preferred/3b-moddn-dn-modup-timer-up/keycode_events.snapshot
@@ -1,7 +1,7 @@
-kp_pressed: usage_page 0x07 keycode 0xe4
+kp_pressed: usage_page 0x07 keycode 0xe4 mods 0x00
ht_binding_pressed: 0 new undecided hold_tap
ht_decide: 0 decided hold (tap-preferred event 3)
-kp_pressed: usage_page 0x07 keycode 0xe1
-kp_released: usage_page 0x07 keycode 0xe4
-kp_released: usage_page 0x07 keycode 0xe1
+kp_pressed: usage_page 0x07 keycode 0xe1 mods 0x00
+kp_released: usage_page 0x07 keycode 0xe4 mods 0x00
+kp_released: usage_page 0x07 keycode 0xe1 mods 0x00
ht_binding_released: 0 cleaning up hold-tap
diff --git a/app/tests/hold-tap/tap-preferred/3c-kcdn-dn-kcup-up/keycode_events.snapshot b/app/tests/hold-tap/tap-preferred/3c-kcdn-dn-kcup-up/keycode_events.snapshot
index 3d7eaf1..e518582 100644
--- a/app/tests/hold-tap/tap-preferred/3c-kcdn-dn-kcup-up/keycode_events.snapshot
+++ b/app/tests/hold-tap/tap-preferred/3c-kcdn-dn-kcup-up/keycode_events.snapshot
@@ -1,7 +1,7 @@
-kp_pressed: usage_page 0x07 keycode 0x07
+kp_pressed: usage_page 0x07 keycode 0x07 mods 0x00
ht_binding_pressed: 0 new undecided hold_tap
-kp_released: usage_page 0x07 keycode 0x07
+kp_released: usage_page 0x07 keycode 0x07 mods 0x00
ht_decide: 0 decided tap (tap-preferred event 0)
-kp_pressed: usage_page 0x07 keycode 0x09
-kp_released: usage_page 0x07 keycode 0x09
+kp_pressed: usage_page 0x07 keycode 0x09 mods 0x00
+kp_released: usage_page 0x07 keycode 0x09 mods 0x00
ht_binding_released: 0 cleaning up hold-tap
diff --git a/app/tests/hold-tap/tap-preferred/3d-kcdn-dn-kcup-timer-up/keycode_events.snapshot b/app/tests/hold-tap/tap-preferred/3d-kcdn-dn-kcup-timer-up/keycode_events.snapshot
index 059d99c..b3b0673 100644
--- a/app/tests/hold-tap/tap-preferred/3d-kcdn-dn-kcup-timer-up/keycode_events.snapshot
+++ b/app/tests/hold-tap/tap-preferred/3d-kcdn-dn-kcup-timer-up/keycode_events.snapshot
@@ -1,7 +1,7 @@
-kp_pressed: usage_page 0x07 keycode 0x07
+kp_pressed: usage_page 0x07 keycode 0x07 mods 0x00
ht_binding_pressed: 0 new undecided hold_tap
-kp_released: usage_page 0x07 keycode 0x07
+kp_released: usage_page 0x07 keycode 0x07 mods 0x00
ht_decide: 0 decided hold (tap-preferred event 3)
-kp_pressed: usage_page 0x07 keycode 0xe1
-kp_released: usage_page 0x07 keycode 0xe1
+kp_pressed: usage_page 0x07 keycode 0xe1 mods 0x00
+kp_released: usage_page 0x07 keycode 0xe1 mods 0x00
ht_binding_released: 0 cleaning up hold-tap
diff --git a/app/tests/hold-tap/tap-preferred/4a-dn-htdn-timer-htup-up/keycode_events.snapshot b/app/tests/hold-tap/tap-preferred/4a-dn-htdn-timer-htup-up/keycode_events.snapshot
index a8cf490..7f45421 100644
--- a/app/tests/hold-tap/tap-preferred/4a-dn-htdn-timer-htup-up/keycode_events.snapshot
+++ b/app/tests/hold-tap/tap-preferred/4a-dn-htdn-timer-htup-up/keycode_events.snapshot
@@ -1,10 +1,10 @@
ht_binding_pressed: 0 new undecided hold_tap
ht_decide: 0 decided hold (tap-preferred event 3)
-kp_pressed: usage_page 0x07 keycode 0xe1
+kp_pressed: usage_page 0x07 keycode 0xe1 mods 0x00
ht_binding_pressed: 1 new undecided hold_tap
ht_decide: 1 decided tap (tap-preferred event 0)
-kp_pressed: usage_page 0x07 keycode 0x0d
-kp_released: usage_page 0x07 keycode 0x0d
+kp_pressed: usage_page 0x07 keycode 0x0d mods 0x00
+kp_released: usage_page 0x07 keycode 0x0d mods 0x00
ht_binding_released: 1 cleaning up hold-tap
-kp_released: usage_page 0x07 keycode 0xe1
+kp_released: usage_page 0x07 keycode 0xe1 mods 0x00
ht_binding_released: 0 cleaning up hold-tap
diff --git a/app/tests/hold-tap/tap-preferred/4a-dn-kcdn-timer-kcup-up/keycode_events.snapshot b/app/tests/hold-tap/tap-preferred/4a-dn-kcdn-timer-kcup-up/keycode_events.snapshot
index 92a3569..ade0d3e 100644
--- a/app/tests/hold-tap/tap-preferred/4a-dn-kcdn-timer-kcup-up/keycode_events.snapshot
+++ b/app/tests/hold-tap/tap-preferred/4a-dn-kcdn-timer-kcup-up/keycode_events.snapshot
@@ -1,7 +1,7 @@
ht_binding_pressed: 0 new undecided hold_tap
ht_decide: 0 decided hold (tap-preferred event 3)
-kp_pressed: usage_page 0x07 keycode 0xe1
-kp_pressed: usage_page 0x07 keycode 0x07
-kp_released: usage_page 0x07 keycode 0x07
-kp_released: usage_page 0x07 keycode 0xe1
+kp_pressed: usage_page 0x07 keycode 0xe1 mods 0x00
+kp_pressed: usage_page 0x07 keycode 0x07 mods 0x00
+kp_released: usage_page 0x07 keycode 0x07 mods 0x00
+kp_released: usage_page 0x07 keycode 0xe1 mods 0x00
ht_binding_released: 0 cleaning up hold-tap
diff --git a/app/tests/hold-tap/tap-preferred/4b-dn-kcdn-kcup-timer-up/keycode_events.snapshot b/app/tests/hold-tap/tap-preferred/4b-dn-kcdn-kcup-timer-up/keycode_events.snapshot
index 92a3569..ade0d3e 100644
--- a/app/tests/hold-tap/tap-preferred/4b-dn-kcdn-kcup-timer-up/keycode_events.snapshot
+++ b/app/tests/hold-tap/tap-preferred/4b-dn-kcdn-kcup-timer-up/keycode_events.snapshot
@@ -1,7 +1,7 @@
ht_binding_pressed: 0 new undecided hold_tap
ht_decide: 0 decided hold (tap-preferred event 3)
-kp_pressed: usage_page 0x07 keycode 0xe1
-kp_pressed: usage_page 0x07 keycode 0x07
-kp_released: usage_page 0x07 keycode 0x07
-kp_released: usage_page 0x07 keycode 0xe1
+kp_pressed: usage_page 0x07 keycode 0xe1 mods 0x00
+kp_pressed: usage_page 0x07 keycode 0x07 mods 0x00
+kp_released: usage_page 0x07 keycode 0x07 mods 0x00
+kp_released: usage_page 0x07 keycode 0xe1 mods 0x00
ht_binding_released: 0 cleaning up hold-tap
diff --git a/app/tests/hold-tap/tap-preferred/4c-dn-kcdn-kcup-up/keycode_events.snapshot b/app/tests/hold-tap/tap-preferred/4c-dn-kcdn-kcup-up/keycode_events.snapshot
index bc8aa8e..418312c 100644
--- a/app/tests/hold-tap/tap-preferred/4c-dn-kcdn-kcup-up/keycode_events.snapshot
+++ b/app/tests/hold-tap/tap-preferred/4c-dn-kcdn-kcup-up/keycode_events.snapshot
@@ -1,7 +1,7 @@
ht_binding_pressed: 0 new undecided hold_tap
ht_decide: 0 decided tap (tap-preferred event 0)
-kp_pressed: usage_page 0x07 keycode 0x09
-kp_pressed: usage_page 0x07 keycode 0x07
-kp_released: usage_page 0x07 keycode 0x07
-kp_released: usage_page 0x07 keycode 0x09
+kp_pressed: usage_page 0x07 keycode 0x09 mods 0x00
+kp_pressed: usage_page 0x07 keycode 0x07 mods 0x00
+kp_released: usage_page 0x07 keycode 0x07 mods 0x00
+kp_released: usage_page 0x07 keycode 0x09 mods 0x00
ht_binding_released: 0 cleaning up hold-tap
diff --git a/app/tests/hold-tap/tap-preferred/4d-dn-kcdn-timer-up-kcup/keycode_events.snapshot b/app/tests/hold-tap/tap-preferred/4d-dn-kcdn-timer-up-kcup/keycode_events.snapshot
index b106f13..c0b4c0b 100644
--- a/app/tests/hold-tap/tap-preferred/4d-dn-kcdn-timer-up-kcup/keycode_events.snapshot
+++ b/app/tests/hold-tap/tap-preferred/4d-dn-kcdn-timer-up-kcup/keycode_events.snapshot
@@ -1,7 +1,7 @@
ht_binding_pressed: 0 new undecided hold_tap
ht_decide: 0 decided tap (tap-preferred event 0)
-kp_pressed: usage_page 0x07 keycode 0x09
-kp_pressed: usage_page 0x07 keycode 0x07
-kp_released: usage_page 0x07 keycode 0x09
+kp_pressed: usage_page 0x07 keycode 0x09 mods 0x00
+kp_pressed: usage_page 0x07 keycode 0x07 mods 0x00
+kp_released: usage_page 0x07 keycode 0x09 mods 0x00
ht_binding_released: 0 cleaning up hold-tap
-kp_released: usage_page 0x07 keycode 0x07
+kp_released: usage_page 0x07 keycode 0x07 mods 0x00
diff --git a/app/tests/keypress/behavior_keymap.dtsi b/app/tests/keypress/behavior_keymap.dtsi
index 34c445c..3a45809 100644
--- a/app/tests/keypress/behavior_keymap.dtsi
+++ b/app/tests/keypress/behavior_keymap.dtsi
@@ -9,20 +9,8 @@
default_layer {
bindings = <
- &kp B &mo 1
- &kp D &kp G>;
- };
-
- lower_layer {
- bindings = <
- &cp M_NEXT &trans
- &kp L &kp J>;
- };
-
- raise_layer {
- bindings = <
- &kp W &kp U
- &kp X &kp M>;
+ &kp B &none
+ &cp M_NEXT &none>;
};
};
};
diff --git a/app/tests/keypress/cp-press-release/keycode_events.snapshot b/app/tests/keypress/cp-press-release/keycode_events.snapshot
index f1ef0ed..95d24a4 100644
--- a/app/tests/keypress/cp-press-release/keycode_events.snapshot
+++ b/app/tests/keypress/cp-press-release/keycode_events.snapshot
@@ -1,2 +1,2 @@
-pressed: usage_page 0x0c keycode 0xb5
-released: usage_page 0x0c keycode 0xb5
+pressed: usage_page 0x0c keycode 0xb5 mods 0x00
+pressed: usage_page 0x0c keycode 0xb5 mods 0x00
diff --git a/app/tests/keypress/cp-press-release/native_posix.keymap b/app/tests/keypress/cp-press-release/native_posix.keymap
index 0e86f93..bf93af7 100644
--- a/app/tests/keypress/cp-press-release/native_posix.keymap
+++ b/app/tests/keypress/cp-press-release/native_posix.keymap
@@ -1,5 +1,5 @@
#include "../behavior_keymap.dtsi"
&kscan {
- events = <ZMK_MOCK_PRESS(0,1,10) ZMK_MOCK_PRESS(0,0,10) ZMK_MOCK_RELEASE(0,0,10) ZMK_MOCK_RELEASE(0,1,10)>;
+ events = <ZMK_MOCK_PRESS(1,0,10) ZMK_MOCK_PRESS(1,0,10)>;
}; \ No newline at end of file
diff --git a/app/tests/keypress/kp-press-release/keycode_events.snapshot b/app/tests/keypress/kp-press-release/keycode_events.snapshot
index f1ef0ed..80cac20 100644
--- a/app/tests/keypress/kp-press-release/keycode_events.snapshot
+++ b/app/tests/keypress/kp-press-release/keycode_events.snapshot
@@ -1,2 +1,2 @@
-pressed: usage_page 0x0c keycode 0xb5
-released: usage_page 0x0c keycode 0xb5
+pressed: usage_page 0x07 keycode 0x05 mods 0x00
+released: usage_page 0x07 keycode 0x05 mods 0x00
diff --git a/app/tests/keypress/kp-press-release/native_posix.keymap b/app/tests/keypress/kp-press-release/native_posix.keymap
index 7f73690..279b084 100644
--- a/app/tests/keypress/kp-press-release/native_posix.keymap
+++ b/app/tests/keypress/kp-press-release/native_posix.keymap
@@ -1,8 +1,5 @@
-#include <dt-bindings/zmk/keys.h>
-#include <behaviors.dtsi>
-#include <dt-bindings/zmk/kscan-mock.h>
#include "../behavior_keymap.dtsi"
&kscan {
- events = <ZMK_MOCK_PRESS(0,1,10) ZMK_MOCK_PRESS(0,0,10) ZMK_MOCK_RELEASE(0,0,10) ZMK_MOCK_RELEASE(0,1,10)>;
+ events = <ZMK_MOCK_PRESS(0,0,10) ZMK_MOCK_RELEASE(0,0,10)>;
}; \ No newline at end of file
diff --git a/app/tests/modifiers/explicit/kp-lctl-dn-lctl-dn-lctl-up-lctl-up/events.patterns b/app/tests/modifiers/explicit/kp-lctl-dn-lctl-dn-lctl-up-lctl-up/events.patterns
new file mode 100644
index 0000000..cbf21af
--- /dev/null
+++ b/app/tests/modifiers/explicit/kp-lctl-dn-lctl-dn-lctl-up-lctl-up/events.patterns
@@ -0,0 +1,4 @@
+s/.*hid_listener_keycode_//p
+s/.*hid_register_mod/reg/p
+s/.*hid_unregister_mod/unreg/p
+s/.*zmk_hid_.*Modifiers set to /mods: Modifiers set to /p \ No newline at end of file
diff --git a/app/tests/modifiers/explicit/kp-lctl-dn-lctl-dn-lctl-up-lctl-up/keycode_events.snapshot b/app/tests/modifiers/explicit/kp-lctl-dn-lctl-dn-lctl-up-lctl-up/keycode_events.snapshot
new file mode 100644
index 0000000..6218e65
--- /dev/null
+++ b/app/tests/modifiers/explicit/kp-lctl-dn-lctl-dn-lctl-up-lctl-up/keycode_events.snapshot
@@ -0,0 +1,17 @@
+pressed: usage_page 0x07 keycode 0xe0 mods 0x00
+reg: Modifier 0 count 1
+reg: Modifiers set to 0x01
+mods: Modifiers set to 0x01
+pressed: usage_page 0x07 keycode 0xe0 mods 0x00
+reg: Modifier 0 count 2
+reg: Modifiers set to 0x01
+mods: Modifiers set to 0x01
+released: usage_page 0x07 keycode 0xe0 mods 0x00
+unreg: Modifier 0 count: 1
+unreg: Modifiers set to 0x01
+mods: Modifiers set to 0x01
+released: usage_page 0x07 keycode 0xe0 mods 0x00
+unreg: Modifier 0 count: 0
+unreg: Modifier 0 released
+unreg: Modifiers set to 0x00
+mods: Modifiers set to 0x00
diff --git a/app/tests/modifiers/explicit/kp-lctl-dn-lctl-dn-lctl-up-lctl-up/native_posix.keymap b/app/tests/modifiers/explicit/kp-lctl-dn-lctl-dn-lctl-up-lctl-up/native_posix.keymap
new file mode 100644
index 0000000..6150094
--- /dev/null
+++ b/app/tests/modifiers/explicit/kp-lctl-dn-lctl-dn-lctl-up-lctl-up/native_posix.keymap
@@ -0,0 +1,27 @@
+#include <dt-bindings/zmk/keys.h>
+#include <behaviors.dtsi>
+#include <dt-bindings/zmk/kscan-mock.h>
+
+
+&kscan {
+ events = <
+ ZMK_MOCK_PRESS(0,0,10)
+ ZMK_MOCK_PRESS(0,1,10)
+ ZMK_MOCK_RELEASE(0,0,10)
+ ZMK_MOCK_RELEASE(0,1,10)
+ >;
+};
+
+/ {
+ keymap {
+ compatible = "zmk,keymap";
+ label ="Default keymap";
+
+ default_layer {
+ bindings = <
+ &kp LCTL &kp LCTL
+ &kp LSFT &none
+ >;
+ };
+ };
+};
diff --git a/app/tests/modifiers/explicit/kp-lctl-dn-lctl-up/events.patterns b/app/tests/modifiers/explicit/kp-lctl-dn-lctl-up/events.patterns
new file mode 100644
index 0000000..cbf21af
--- /dev/null
+++ b/app/tests/modifiers/explicit/kp-lctl-dn-lctl-up/events.patterns
@@ -0,0 +1,4 @@
+s/.*hid_listener_keycode_//p
+s/.*hid_register_mod/reg/p
+s/.*hid_unregister_mod/unreg/p
+s/.*zmk_hid_.*Modifiers set to /mods: Modifiers set to /p \ No newline at end of file
diff --git a/app/tests/modifiers/explicit/kp-lctl-dn-lctl-up/keycode_events.snapshot b/app/tests/modifiers/explicit/kp-lctl-dn-lctl-up/keycode_events.snapshot
new file mode 100644
index 0000000..4232428
--- /dev/null
+++ b/app/tests/modifiers/explicit/kp-lctl-dn-lctl-up/keycode_events.snapshot
@@ -0,0 +1,9 @@
+pressed: usage_page 0x07 keycode 0xe0 mods 0x00
+reg: Modifier 0 count 1
+reg: Modifiers set to 0x01
+mods: Modifiers set to 0x01
+released: usage_page 0x07 keycode 0xe0 mods 0x00
+unreg: Modifier 0 count: 0
+unreg: Modifier 0 released
+unreg: Modifiers set to 0x00
+mods: Modifiers set to 0x00
diff --git a/app/tests/modifiers/explicit/kp-lctl-dn-lctl-up/native_posix.keymap b/app/tests/modifiers/explicit/kp-lctl-dn-lctl-up/native_posix.keymap
new file mode 100644
index 0000000..3324584
--- /dev/null
+++ b/app/tests/modifiers/explicit/kp-lctl-dn-lctl-up/native_posix.keymap
@@ -0,0 +1,25 @@
+#include <dt-bindings/zmk/keys.h>
+#include <behaviors.dtsi>
+#include <dt-bindings/zmk/kscan-mock.h>
+
+
+&kscan {
+ events = <
+ ZMK_MOCK_PRESS(0,0,10)
+ ZMK_MOCK_RELEASE(0,0,10)
+ >;
+};
+
+/ {
+ keymap {
+ compatible = "zmk,keymap";
+ label ="Default keymap";
+
+ default_layer {
+ bindings = <
+ &kp LCTL &kp LCTL
+ &kp LSFT &none
+ >;
+ };
+ };
+};
diff --git a/app/tests/modifiers/explicit/kp-lctl-dn-lsft-dn-lctl-up-lsft-up/events.patterns b/app/tests/modifiers/explicit/kp-lctl-dn-lsft-dn-lctl-up-lsft-up/events.patterns
new file mode 100644
index 0000000..cbf21af
--- /dev/null
+++ b/app/tests/modifiers/explicit/kp-lctl-dn-lsft-dn-lctl-up-lsft-up/events.patterns
@@ -0,0 +1,4 @@
+s/.*hid_listener_keycode_//p
+s/.*hid_register_mod/reg/p
+s/.*hid_unregister_mod/unreg/p
+s/.*zmk_hid_.*Modifiers set to /mods: Modifiers set to /p \ No newline at end of file
diff --git a/app/tests/modifiers/explicit/kp-lctl-dn-lsft-dn-lctl-up-lsft-up/keycode_events.snapshot b/app/tests/modifiers/explicit/kp-lctl-dn-lsft-dn-lctl-up-lsft-up/keycode_events.snapshot
new file mode 100644
index 0000000..60d829a
--- /dev/null
+++ b/app/tests/modifiers/explicit/kp-lctl-dn-lsft-dn-lctl-up-lsft-up/keycode_events.snapshot
@@ -0,0 +1,18 @@
+pressed: usage_page 0x07 keycode 0xe0 mods 0x00
+reg: Modifier 0 count 1
+reg: Modifiers set to 0x01
+mods: Modifiers set to 0x01
+pressed: usage_page 0x07 keycode 0xe1 mods 0x00
+reg: Modifier 1 count 1
+reg: Modifiers set to 0x03
+mods: Modifiers set to 0x03
+released: usage_page 0x07 keycode 0xe0 mods 0x00
+unreg: Modifier 0 count: 0
+unreg: Modifier 0 released
+unreg: Modifiers set to 0x02
+mods: Modifiers set to 0x02
+released: usage_page 0x07 keycode 0xe1 mods 0x00
+unreg: Modifier 1 count: 0
+unreg: Modifier 1 released
+unreg: Modifiers set to 0x00
+mods: Modifiers set to 0x00
diff --git a/app/tests/modifiers/explicit/kp-lctl-dn-lsft-dn-lctl-up-lsft-up/native_posix.keymap b/app/tests/modifiers/explicit/kp-lctl-dn-lsft-dn-lctl-up-lsft-up/native_posix.keymap
new file mode 100644
index 0000000..b6f9631
--- /dev/null
+++ b/app/tests/modifiers/explicit/kp-lctl-dn-lsft-dn-lctl-up-lsft-up/native_posix.keymap
@@ -0,0 +1,27 @@
+#include <dt-bindings/zmk/keys.h>
+#include <behaviors.dtsi>
+#include <dt-bindings/zmk/kscan-mock.h>
+
+
+&kscan {
+ events = <
+ ZMK_MOCK_PRESS(0,0,10)
+ ZMK_MOCK_PRESS(1,0,10)
+ ZMK_MOCK_RELEASE(0,0,10)
+ ZMK_MOCK_RELEASE(1,0,10)
+ >;
+};
+
+/ {
+ keymap {
+ compatible = "zmk,keymap";
+ label ="Default keymap";
+
+ default_layer {
+ bindings = <
+ &kp LCTL &kp LCTL
+ &kp LSFT &none
+ >;
+ };
+ };
+};
diff --git a/app/tests/modifiers/explicit/kp-lctl-dn-lsft-dn-lsft-up-lctl-up/events.patterns b/app/tests/modifiers/explicit/kp-lctl-dn-lsft-dn-lsft-up-lctl-up/events.patterns
new file mode 100644
index 0000000..cbf21af
--- /dev/null
+++ b/app/tests/modifiers/explicit/kp-lctl-dn-lsft-dn-lsft-up-lctl-up/events.patterns
@@ -0,0 +1,4 @@
+s/.*hid_listener_keycode_//p
+s/.*hid_register_mod/reg/p
+s/.*hid_unregister_mod/unreg/p
+s/.*zmk_hid_.*Modifiers set to /mods: Modifiers set to /p \ No newline at end of file
diff --git a/app/tests/modifiers/explicit/kp-lctl-dn-lsft-dn-lsft-up-lctl-up/keycode_events.snapshot b/app/tests/modifiers/explicit/kp-lctl-dn-lsft-dn-lsft-up-lctl-up/keycode_events.snapshot
new file mode 100644
index 0000000..b4755e5
--- /dev/null
+++ b/app/tests/modifiers/explicit/kp-lctl-dn-lsft-dn-lsft-up-lctl-up/keycode_events.snapshot
@@ -0,0 +1,18 @@
+pressed: usage_page 0x07 keycode 0xe0 mods 0x00
+reg: Modifier 0 count 1
+reg: Modifiers set to 0x01
+mods: Modifiers set to 0x01
+pressed: usage_page 0x07 keycode 0xe1 mods 0x00
+reg: Modifier 1 count 1
+reg: Modifiers set to 0x03
+mods: Modifiers set to 0x03
+released: usage_page 0x07 keycode 0xe1 mods 0x00
+unreg: Modifier 1 count: 0
+unreg: Modifier 1 released
+unreg: Modifiers set to 0x01
+mods: Modifiers set to 0x01
+released: usage_page 0x07 keycode 0xe0 mods 0x00
+unreg: Modifier 0 count: 0
+unreg: Modifier 0 released
+unreg: Modifiers set to 0x00
+mods: Modifiers set to 0x00
diff --git a/app/tests/modifiers/explicit/kp-lctl-dn-lsft-dn-lsft-up-lctl-up/native_posix.keymap b/app/tests/modifiers/explicit/kp-lctl-dn-lsft-dn-lsft-up-lctl-up/native_posix.keymap
new file mode 100644
index 0000000..3fedad2
--- /dev/null
+++ b/app/tests/modifiers/explicit/kp-lctl-dn-lsft-dn-lsft-up-lctl-up/native_posix.keymap
@@ -0,0 +1,28 @@
+#include <dt-bindings/zmk/keys.h>
+#include <behaviors.dtsi>
+#include <dt-bindings/zmk/kscan-mock.h>
+
+
+&kscan {
+ events = <
+ ZMK_MOCK_PRESS(0,0,10)
+ ZMK_MOCK_PRESS(1,0,10)
+ ZMK_MOCK_RELEASE(1,0,10)
+ ZMK_MOCK_RELEASE(0,0,10)
+
+ >;
+};
+
+/ {
+ keymap {
+ compatible = "zmk,keymap";
+ label ="Default keymap";
+
+ default_layer {
+ bindings = <
+ &kp LCTL &kp LCTL
+ &kp LSFT &none
+ >;
+ };
+ };
+};
diff --git a/app/tests/modifiers/implicit/kp-mod1-dn-mod2-dn-mod1-up-mod2-up/events.patterns b/app/tests/modifiers/implicit/kp-mod1-dn-mod2-dn-mod1-up-mod2-up/events.patterns
new file mode 100644
index 0000000..cbf21af
--- /dev/null
+++ b/app/tests/modifiers/implicit/kp-mod1-dn-mod2-dn-mod1-up-mod2-up/events.patterns
@@ -0,0 +1,4 @@
+s/.*hid_listener_keycode_//p
+s/.*hid_register_mod/reg/p
+s/.*hid_unregister_mod/unreg/p
+s/.*zmk_hid_.*Modifiers set to /mods: Modifiers set to /p \ No newline at end of file
diff --git a/app/tests/modifiers/implicit/kp-mod1-dn-mod2-dn-mod1-up-mod2-up/keycode_events.snapshot b/app/tests/modifiers/implicit/kp-mod1-dn-mod2-dn-mod1-up-mod2-up/keycode_events.snapshot
new file mode 100644
index 0000000..61dd271
--- /dev/null
+++ b/app/tests/modifiers/implicit/kp-mod1-dn-mod2-dn-mod1-up-mod2-up/keycode_events.snapshot
@@ -0,0 +1,8 @@
+pressed: usage_page 0x07 keycode 0x05 mods 0x02
+mods: Modifiers set to 0x02
+pressed: usage_page 0x07 keycode 0x04 mods 0x01
+mods: Modifiers set to 0x01
+released: usage_page 0x07 keycode 0x05 mods 0x02
+mods: Modifiers set to 0x01
+released: usage_page 0x07 keycode 0x04 mods 0x01
+mods: Modifiers set to 0x00
diff --git a/app/tests/modifiers/implicit/kp-mod1-dn-mod2-dn-mod1-up-mod2-up/native_posix.keymap b/app/tests/modifiers/implicit/kp-mod1-dn-mod2-dn-mod1-up-mod2-up/native_posix.keymap
new file mode 100644
index 0000000..c603c00
--- /dev/null
+++ b/app/tests/modifiers/implicit/kp-mod1-dn-mod2-dn-mod1-up-mod2-up/native_posix.keymap
@@ -0,0 +1,27 @@
+#include <dt-bindings/zmk/keys.h>
+#include <behaviors.dtsi>
+#include <dt-bindings/zmk/kscan-mock.h>
+
+
+&kscan {
+ events = <
+ ZMK_MOCK_PRESS(0,1,10)
+ ZMK_MOCK_PRESS(0,0,10)
+ ZMK_MOCK_RELEASE(0,1,10)
+ ZMK_MOCK_RELEASE(0,0,10)
+ >;
+};
+
+/ {
+ keymap {
+ compatible = "zmk,keymap";
+ label ="Default keymap";
+
+ default_layer {
+ bindings = <
+ &kp LC(A) &kp LS(B)
+ &kp LCTL &none
+ >;
+ };
+ };
+};
diff --git a/app/tests/modifiers/implicit/kp-mod1-dn-mod2-dn-mod1-up-mod2-up/pending b/app/tests/modifiers/implicit/kp-mod1-dn-mod2-dn-mod1-up-mod2-up/pending
new file mode 100644
index 0000000..3f49005
--- /dev/null
+++ b/app/tests/modifiers/implicit/kp-mod1-dn-mod2-dn-mod1-up-mod2-up/pending
@@ -0,0 +1,9 @@
+This test fails because the hid_listener_keycode_released function
+releases implicit modifiers always, even if they were not set by the
+key that's going up. Also see the comment in that function:
+
+ If LC(A) is pressed, then LS(B), then LC(A) is released, the shift for B will be released
+ prematurely. This causes if LS(B) to repeat like Bbbbbbbb when pressed for a long time.
+ Solving this would require keeping track of which key's implicit modifiers are currently
+ active and only releasing modifiers at that time.
+
diff --git a/app/tests/modifiers/implicit/kp-mod1-dn-mod2-dn-mod2-up-mod1-up/events.patterns b/app/tests/modifiers/implicit/kp-mod1-dn-mod2-dn-mod2-up-mod1-up/events.patterns
new file mode 100644
index 0000000..cbf21af
--- /dev/null
+++ b/app/tests/modifiers/implicit/kp-mod1-dn-mod2-dn-mod2-up-mod1-up/events.patterns
@@ -0,0 +1,4 @@
+s/.*hid_listener_keycode_//p
+s/.*hid_register_mod/reg/p
+s/.*hid_unregister_mod/unreg/p
+s/.*zmk_hid_.*Modifiers set to /mods: Modifiers set to /p \ No newline at end of file
diff --git a/app/tests/modifiers/implicit/kp-mod1-dn-mod2-dn-mod2-up-mod1-up/keycode_events.snapshot b/app/tests/modifiers/implicit/kp-mod1-dn-mod2-dn-mod2-up-mod1-up/keycode_events.snapshot
new file mode 100644
index 0000000..e8a231f
--- /dev/null
+++ b/app/tests/modifiers/implicit/kp-mod1-dn-mod2-dn-mod2-up-mod1-up/keycode_events.snapshot
@@ -0,0 +1,8 @@
+pressed: usage_page 0x07 keycode 0x04 mods 0x01
+mods: Modifiers set to 0x01
+pressed: usage_page 0x07 keycode 0x05 mods 0x02
+mods: Modifiers set to 0x02
+released: usage_page 0x07 keycode 0x05 mods 0x02
+mods: Modifiers set to 0x00
+released: usage_page 0x07 keycode 0x04 mods 0x01
+mods: Modifiers set to 0x00
diff --git a/app/tests/modifiers/implicit/kp-mod1-dn-mod2-dn-mod2-up-mod1-up/native_posix.keymap b/app/tests/modifiers/implicit/kp-mod1-dn-mod2-dn-mod2-up-mod1-up/native_posix.keymap
new file mode 100644
index 0000000..109d60e
--- /dev/null
+++ b/app/tests/modifiers/implicit/kp-mod1-dn-mod2-dn-mod2-up-mod1-up/native_posix.keymap
@@ -0,0 +1,27 @@
+#include <dt-bindings/zmk/keys.h>
+#include <behaviors.dtsi>
+#include <dt-bindings/zmk/kscan-mock.h>
+
+
+&kscan {
+ events = <
+ ZMK_MOCK_PRESS(0,0,10)
+ ZMK_MOCK_PRESS(0,1,10)
+ ZMK_MOCK_RELEASE(0,1,10)
+ ZMK_MOCK_RELEASE(0,0,10)
+ >;
+};
+
+/ {
+ keymap {
+ compatible = "zmk,keymap";
+ label ="Default keymap";
+
+ default_layer {
+ bindings = <
+ &kp LC(A) &kp LS(B)
+ &none &none
+ >;
+ };
+ };
+};
diff --git a/app/tests/modifiers/mixed/kp-lctl-dn-mod-dn-lctl-up-mod-up/events.patterns b/app/tests/modifiers/mixed/kp-lctl-dn-mod-dn-lctl-up-mod-up/events.patterns
new file mode 100644
index 0000000..cbf21af
--- /dev/null
+++ b/app/tests/modifiers/mixed/kp-lctl-dn-mod-dn-lctl-up-mod-up/events.patterns
@@ -0,0 +1,4 @@
+s/.*hid_listener_keycode_//p
+s/.*hid_register_mod/reg/p
+s/.*hid_unregister_mod/unreg/p
+s/.*zmk_hid_.*Modifiers set to /mods: Modifiers set to /p \ No newline at end of file
diff --git a/app/tests/modifiers/mixed/kp-lctl-dn-mod-dn-lctl-up-mod-up/keycode_events.snapshot b/app/tests/modifiers/mixed/kp-lctl-dn-mod-dn-lctl-up-mod-up/keycode_events.snapshot
new file mode 100644
index 0000000..40c5841
--- /dev/null
+++ b/app/tests/modifiers/mixed/kp-lctl-dn-mod-dn-lctl-up-mod-up/keycode_events.snapshot
@@ -0,0 +1,13 @@
+pressed: usage_page 0x07 keycode 0xe0 mods 0x00
+reg: Modifier 0 count 1
+reg: Modifiers set to 0x01
+mods: Modifiers set to 0x01
+pressed: usage_page 0x07 keycode 0x05 mods 0x02
+mods: Modifiers set to 0x03
+released: usage_page 0x07 keycode 0xe0 mods 0x00
+unreg: Modifier 0 count: 0
+unreg: Modifier 0 released
+unreg: Modifiers set to 0x00
+mods: Modifiers set to 0x00
+released: usage_page 0x07 keycode 0x05 mods 0x02
+mods: Modifiers set to 0x00
diff --git a/app/tests/modifiers/mixed/kp-lctl-dn-mod-dn-lctl-up-mod-up/native_posix.keymap b/app/tests/modifiers/mixed/kp-lctl-dn-mod-dn-lctl-up-mod-up/native_posix.keymap
new file mode 100644
index 0000000..d381e4b
--- /dev/null
+++ b/app/tests/modifiers/mixed/kp-lctl-dn-mod-dn-lctl-up-mod-up/native_posix.keymap
@@ -0,0 +1,27 @@
+#include <dt-bindings/zmk/keys.h>
+#include <behaviors.dtsi>
+#include <dt-bindings/zmk/kscan-mock.h>
+
+
+&kscan {
+ events = <
+ ZMK_MOCK_PRESS(1,0,10)
+ ZMK_MOCK_PRESS(0,1,10)
+ ZMK_MOCK_RELEASE(1,0,10)
+ ZMK_MOCK_RELEASE(0,1,10)
+ >;
+};
+
+/ {
+ keymap {
+ compatible = "zmk,keymap";
+ label ="Default keymap";
+
+ default_layer {
+ bindings = <
+ &kp LC(A) &kp LS(B)
+ &kp LCTL &none
+ >;
+ };
+ };
+};
diff --git a/app/tests/modifiers/mixed/kp-lctl-dn-mod-dn-mod-up-lctl-up/events.patterns b/app/tests/modifiers/mixed/kp-lctl-dn-mod-dn-mod-up-lctl-up/events.patterns
new file mode 100644
index 0000000..cbf21af
--- /dev/null
+++ b/app/tests/modifiers/mixed/kp-lctl-dn-mod-dn-mod-up-lctl-up/events.patterns
@@ -0,0 +1,4 @@
+s/.*hid_listener_keycode_//p
+s/.*hid_register_mod/reg/p
+s/.*hid_unregister_mod/unreg/p
+s/.*zmk_hid_.*Modifiers set to /mods: Modifiers set to /p \ No newline at end of file
diff --git a/app/tests/modifiers/mixed/kp-lctl-dn-mod-dn-mod-up-lctl-up/keycode_events.snapshot b/app/tests/modifiers/mixed/kp-lctl-dn-mod-dn-mod-up-lctl-up/keycode_events.snapshot
new file mode 100644
index 0000000..5df571b
--- /dev/null
+++ b/app/tests/modifiers/mixed/kp-lctl-dn-mod-dn-mod-up-lctl-up/keycode_events.snapshot
@@ -0,0 +1,13 @@
+pressed: usage_page 0x07 keycode 0xe0 mods 0x00
+reg: Modifier 0 count 1
+reg: Modifiers set to 0x01
+mods: Modifiers set to 0x01
+pressed: usage_page 0x07 keycode 0x05 mods 0x02
+mods: Modifiers set to 0x03
+released: usage_page 0x07 keycode 0x05 mods 0x02
+mods: Modifiers set to 0x01
+released: usage_page 0x07 keycode 0xe0 mods 0x00
+unreg: Modifier 0 count: 0
+unreg: Modifier 0 released
+unreg: Modifiers set to 0x00
+mods: Modifiers set to 0x00
diff --git a/app/tests/modifiers/mixed/kp-lctl-dn-mod-dn-mod-up-lctl-up/native_posix.keymap b/app/tests/modifiers/mixed/kp-lctl-dn-mod-dn-mod-up-lctl-up/native_posix.keymap
new file mode 100644
index 0000000..b47b5f7
--- /dev/null
+++ b/app/tests/modifiers/mixed/kp-lctl-dn-mod-dn-mod-up-lctl-up/native_posix.keymap
@@ -0,0 +1,27 @@
+#include <dt-bindings/zmk/keys.h>
+#include <behaviors.dtsi>
+#include <dt-bindings/zmk/kscan-mock.h>
+
+
+&kscan {
+ events = <
+ ZMK_MOCK_PRESS(1,0,10)
+ ZMK_MOCK_PRESS(0,1,10)
+ ZMK_MOCK_RELEASE(0,1,10)
+ ZMK_MOCK_RELEASE(1,0,10)
+ >;
+};
+
+/ {
+ keymap {
+ compatible = "zmk,keymap";
+ label ="Default keymap";
+
+ default_layer {
+ bindings = <
+ &kp LC(A) &kp LS(B)
+ &kp LCTL &none
+ >;
+ };
+ };
+};
diff --git a/app/tests/momentary-layer/early-key-release/keycode_events.snapshot b/app/tests/momentary-layer/early-key-release/keycode_events.snapshot
index 474eef7..82ebc67 100644
--- a/app/tests/momentary-layer/early-key-release/keycode_events.snapshot
+++ b/app/tests/momentary-layer/early-key-release/keycode_events.snapshot
@@ -1,4 +1,4 @@
-kp_pressed: usage_page 0x07 keycode 0x05
+kp_pressed: usage_page 0x07 keycode 0x05 mods 0x00
mo_pressed: position 1 layer 1
-kp_released: usage_page 0x07 keycode 0x05
+kp_released: usage_page 0x07 keycode 0x05 mods 0x00
mo_released: position 1 layer 1
diff --git a/app/tests/momentary-layer/normal/keycode_events.snapshot b/app/tests/momentary-layer/normal/keycode_events.snapshot
index 9dc6101..247128f 100644
--- a/app/tests/momentary-layer/normal/keycode_events.snapshot
+++ b/app/tests/momentary-layer/normal/keycode_events.snapshot
@@ -1,4 +1,4 @@
mo_pressed: position 1 layer 1
-kp_pressed: usage_page 0x0c keycode 0xb5
-kp_released: usage_page 0x0c keycode 0xb5
+kp_pressed: usage_page 0x0c keycode 0xb5 mods 0x00
+kp_released: usage_page 0x0c keycode 0xb5 mods 0x00
mo_released: position 1 layer 1
diff --git a/app/tests/toggle-layer/early-key-release/keycode_events.snapshot b/app/tests/toggle-layer/early-key-release/keycode_events.snapshot
index 3ac017c..6b4d50b 100644
--- a/app/tests/toggle-layer/early-key-release/keycode_events.snapshot
+++ b/app/tests/toggle-layer/early-key-release/keycode_events.snapshot
@@ -1,6 +1,6 @@
-kp_pressed: usage_page 0x07 keycode 0x05
+kp_pressed: usage_page 0x07 keycode 0x05 mods 0x00
tog_pressed: position 1 layer 1
-kp_released: usage_page 0x07 keycode 0x05
+kp_released: usage_page 0x07 keycode 0x05 mods 0x00
tog_released: position 1 layer 1
-kp_pressed: usage_page 0x0c keycode 0xb5
-kp_released: usage_page 0x0c keycode 0xb5
+kp_pressed: usage_page 0x0c keycode 0xb5 mods 0x00
+kp_released: usage_page 0x0c keycode 0xb5 mods 0x00
diff --git a/app/tests/toggle-layer/normal/keycode_events.snapshot b/app/tests/toggle-layer/normal/keycode_events.snapshot
index 42174cc..d03295e 100644
--- a/app/tests/toggle-layer/normal/keycode_events.snapshot
+++ b/app/tests/toggle-layer/normal/keycode_events.snapshot
@@ -1,4 +1,4 @@
tog_pressed: position 1 layer 1
tog_released: position 1 layer 1
-kp_pressed: usage_page 0x0c keycode 0xb5
-kp_released: usage_page 0x0c keycode 0xb5
+kp_pressed: usage_page 0x0c keycode 0xb5 mods 0x00
+kp_released: usage_page 0x0c keycode 0xb5 mods 0x00
diff --git a/app/tests/transparent/layered/keycode_events.snapshot b/app/tests/transparent/layered/keycode_events.snapshot
index d0bd245..5e707b8 100644
--- a/app/tests/transparent/layered/keycode_events.snapshot
+++ b/app/tests/transparent/layered/keycode_events.snapshot
@@ -1,2 +1,2 @@
-kp_pressed: usage_page 0x07 keycode 0x04
-kp_released: usage_page 0x07 keycode 0x04
+kp_pressed: usage_page 0x07 keycode 0x04 mods 0x00
+kp_released: usage_page 0x07 keycode 0x04 mods 0x00
diff --git a/docs/.nvmrc b/docs/.nvmrc
new file mode 100644
index 0000000..1a2f5bd
--- /dev/null
+++ b/docs/.nvmrc
@@ -0,0 +1 @@
+lts/* \ No newline at end of file
diff --git a/docs/blog/2020-10-03-bootloader-fix.md b/docs/blog/2020-10-03-bootloader-fix.md
new file mode 100644
index 0000000..8a9fd7f
--- /dev/null
+++ b/docs/blog/2020-10-03-bootloader-fix.md
@@ -0,0 +1,195 @@
+---
+title: Fixing the Mysterious Broken Bootloader
+author: Nick Winans
+author_title: Contributor
+author_url: https://github.com/Nicell
+author_image_url: https://avatars1.githubusercontent.com/u/9439650
+tags: [bootloader, keyboards, firmware, oss, ble]
+---
+
+Recently I was able to fix the "stuck in the bootloader" issue in
+[#322](https://github.com/zmkfirmware/zmk/pull/322) that had been plaguing us
+for quite some time. I want to go over what the issue was, how the issue was
+diagnosed, and how it was fixed.
+
+## Background
+
+What exactly is the "stuck in the bootloader" issue? Seemingly randomly, users'
+keyboards would suddenly stop working and when they would reset their keyboard
+they would get put into the bootloader instead of back into the firmware. This
+would require the user to re-flash the firmware again to get into the firmware.
+That wouldn't be so bad except for the fact that once this occurs, every reset
+would require the user to re-flash the firmware again. The only way to really
+fix this issue was to re-flash the bootloader itself, which is a huge pain.
+
+Going into this, all we knew was that this issue was most likely introduced
+somewhere in the [#133](https://github.com/zmkfirmware/zmk/pull/133), which
+added Bluetooth profile management. We've had quite a few attempts at trying to
+recreate the issue, but we never were able to get it to happen consistently.
+
+## Diagnosing the issue
+
+This issue had been happening sporadically for the past month, and I finally
+decided to dig in to see what was going on. We started in the Discord and
+discussed what was common between all of the people who have experienced this
+issue. Everyone who had this issue reported that they did quite a bit of profile
+switching. This lined up with the possible connection to the Bluetooth profile
+management pull request.
+
+### Pinpointing the cause
+
+I had a hunch that this was related to the settings system. The settings system
+is used by profile Bluetooth switching, and the settings system works directly
+with the system flash. Based on this hunch, I tried spamming the RGB underglow
+cycle behavior on my main keyboard. Sure enough after a couple minutes, I got
+stuck in the bootloader. I was even able to reproduce it again.
+
+This was an important discovery for two reasons. First, I was able to recreate
+the issue consistently, which meant I could set up logging and more closely
+monitor what the board was doing. Second, this more or less proved that it was
+specifically the settings system at fault. Both Bluetooth profile switching and
+RGB underglow cycling trigger it, and the one common piece is they save their
+state to settings.
+
+### Settings system overview
+
+To understand what's going wrong, we first need to understand how the settings
+system works. Here's a diagram to explain the flash space that the settings
+system holds for our nRF52840 based boards (nice!nano, nRFMicro, BlueMicro).
+
+![Settings Diagram](https://i.imgur.com/DF2t3Oq.png)
+
+The settings flash space lives at the end of the flash of the chip. In this case
+it starts at `0xF8000` and is `0x8000` bytes long, which is 32KB in more
+comprehensible units. Then due to the chip's architecture, this flash space is
+broken into pages, which are `0x1000` bytes in size (4KB).
+
+The backend that carries out the settings save and read operation in ZMK is
+called NVS. NVS calls these pages sectors. Due to how flash works, you can't
+write to the same bytes multiple times without erasing them first, and to erase
+bytes, you need to erase the entire sector of flash. This means when NVS writes
+to the settings flash if there's no erased space available for the new value, it
+will need to erase a sector.
+
+### Logging discoveries
+
+So first I enabled logging of the NVS module by adding
+`CONFIG_NVS_LOG_LEVEL_DBG=y` to my `.conf` file. I repeated the same test of
+spamming RGB underglow effect cycle and the resulting logs I got were this:
+
+```
+[00:00:00.000,671] <inf> fs_nvs: 8 Sectors of 4096 bytes
+[00:00:00.000,671] <inf> fs_nvs: alloc wra: 3, f70
+[00:00:00.000,671] <inf> fs_nvs: data wra: 3, f40
+// A bunch of effect cycle spam
+[00:02:34.781,188] <dbg> fs_nvs: Erasing flash at fd000, len 4096
+// A bunch more effect cycle spam
+[00:06:42.219,970] <dbg> fs_nvs: Erasing flash at ff000, len 4096
+// A bunch more effect cycle spam
+// KABOOM - bootloader issue
+```
+
+So at start up, we can see that the 8 sectors of 4KB are found by NVS properly,
+however, I wasn't sure what the second and third lines meant, but we'll get back
+to that. Nonetheless the next two logs from NVS showed erasing the sector at
+`0xFD000` and then erasing the `0xFF000` sector.
+
+![Erased Sectors](https://i.imgur.com/DmLycMJ.png)
+
+It's really odd that the third to last sector and the last sector are erased,
+and then shortly after the bootloader issue is hit. I really had no explanation
+for this behavior.
+
+### Reaching out to Zephyr
+
+At this point, I nor anyone else working on the ZMK project knew enough about
+NVS to explain what was going on here. [Pete
+Johanson](https://github.com/petejohanson), project founder, reached out on the
+Zephyr Project's Slack (ZMK is built on top of Zephyr if you weren't aware).
+Justin B and Laczen assisted by first explaining that those `alloc wra` and
+`data wra` logs from earlier are showing what data NVS found at startup.
+
+More specifically, `data wra` should be `0` when it first starts up on a clean
+flash. As we can see from my earlier logging on a clean flash I was instead
+getting `f40`. NVS is finding data in our settings sectors when they should be
+blank! We were then given the advice to double check our bootloader.
+
+### The Adafruit nRF52 Bootloader
+
+Most of the boards the contributors of ZMK use have the [Adafruit nRF52
+Bootloader](https://github.com/adafruit/Adafruit_nRF52_Bootloader), which allows
+for extremely easy flashing by dragging and dropping `.uf2` files onto the board
+as a USB drive. Every bootloader takes up a portion of the flash, and in the
+README explains that the first `0x26000` is reserved for the bootloader with the
+nRF52840, and we've properly allocated that.
+
+However, there isn't a full explanation of the flash allocation of the
+bootloader in the README. There's a possibility that the bootloader is using
+part of the same flash area we're using. I reached out on the Adafruit Discord,
+and [Dan Halbert](https://github.com/dhalbert) pointed me towards the [linker
+map](https://github.com/adafruit/Adafruit_nRF52_Bootloader/blob/master/linker/nrf52840.ld)
+of the nRF52840. Let's take a look.
+
+```
+FLASH (rx) : ORIGIN = 0xF4000, LENGTH = 0xFE000-0xF4000-2048 /* 38 KB */
+
+BOOTLOADER_CONFIG (r): ORIGIN = 0xFE000 - 2048, LENGTH = 2048
+
+/** Location of mbr params page in flash. */
+MBR_PARAMS_PAGE (rw) : ORIGIN = 0xFE000, LENGTH = 0x1000
+
+/** Location of bootloader setting in flash. */
+BOOTLOADER_SETTINGS (rw) : ORIGIN = 0xFF000, LENGTH = 0x1000
+```
+
+Here's a diagram to show this a bit better.
+
+![Adafruit Bootloader Diagram](https://i.imgur.com/TEOA31m.png)
+
+We've found the issue! As you can see from the red bar (representing our
+settings flash area), we've put the settings flash area _right on top_ of the
+Adafruit bootloader's flash space. Oops!
+
+This also shines some light on why NVS erased `0xFD000` and `0xFF000` sectors.
+It's possible there was no flash written to `0xFD000` because the bootloader
+didn't use up all of that space it has, and then there possibly weren't any
+bootloader settings set yet, so `0xFF000` could be used and erased by NVS too.
+
+After erasing `0xFF000`, NVS probably next erased a rather important part of the
+bootloader that resulted in this issue at hand. In my opinion, we're pretty
+lucky that it didn't delete an even more vital part of the bootloader. At least
+we could still get to it, so that we could re-flash the bootloader easily!
+
+## The solution
+
+Now that we've found the issue, we can pretty easily fix this. We'll need to
+move the settings flash area back so that it doesn't overlap with the
+bootloader. First we calculate the size of the of flash area the bootloader is using.
+
+```
+0x100000 (end of flash) - 0x0F4000 (start of bootloader) = 0xC000 (48KB)
+```
+
+So the bootloader is using the last 48KB of the flash, this means all we need to
+do is shift back the settings area and code space `0xC000` bytes. We'll apply
+this to all of the `.dts` files for the boards that were affected by this issue.
+
+```diff
+ code_partition: partition@26000 {
+ label = "code_partition";
+- reg = <0x00026000 0x000d2000>;
++ reg = <0x00026000 0x000c6000>;
+ };
+
+
+- storage_partition: partition@f8000 {
++ storage_partition: partition@ec000 {
+ label = "storage";
+- reg = <0x000f8000 0x00008000>;
++ reg = <0x000ec000 0x00008000>;
+ };
+```
+
+And with those changes, we should no longer run into this issue! In the process
+of these changes, we lost 48KB of space for application code, but we're only
+using around 20% of it anyways. 🎉
diff --git a/docs/docs/behavior/outputs.md b/docs/docs/behavior/outputs.md
new file mode 100644
index 0000000..ae81249
--- /dev/null
+++ b/docs/docs/behavior/outputs.md
@@ -0,0 +1,59 @@
+---
+title: Output Selection Behavior
+sidebar_label: Output Selection
+---
+
+## Summary
+
+The output behavior allows selecting whether keyboard output is sent to the
+USB or bluetooth connection when both are connected. This allows connecting a
+keyboard to USB for power but outputting to a different device over bluetooth.
+
+By default, output is sent to USB when both USB and BLE are connected.
+Once you select a different output, it will be remembered until you change it again.
+
+## Output Command Defines
+
+Output command defines are provided through the [`dt-bindings/zmk/outputs.h`](https://github.com/zmkfirmware/zmk/blob/main/app/include/dt-bindings/zmk/outputs.h)
+header, which is added at the top of the keymap file:
+
+```
+#include <dt-bindings/zmk/outputs.h>
+```
+
+This allows you to reference the actions defined in this header:
+
+| Define | Action |
+| --------- | ----------------------------------------------- |
+| `OUT_USB` | Prefer sending to USB |
+| `OUT_BLE` | Prefer sending to the current bluetooth profile |
+| `OUT_TOG` | Toggle between USB and BLE |
+
+## Output Selection Behavior
+
+The output selection behavior changes the preferred output on press.
+
+### Behavior Binding
+
+- Reference: `&out`
+- Parameter #1: Command, e.g. `OUT_BLE`
+
+### Examples
+
+1. Behavior binding to prefer sending keyboard output to USB
+
+ ```
+ &out OUT_USB
+ ```
+
+1. Behavior binding to prefer sending keyboard output to the current bluetooth profile
+
+ ```
+ &out OUT_BLE
+ ```
+
+1. Behavior binding to toggle between preferring USB and BLE
+
+ ```
+ &out OUT_TOG
+ ```
diff --git a/docs/docs/dev-guide-new-shield.md b/docs/docs/dev-guide-new-shield.md
index 94f7d1d..e1d6f25 100644
--- a/docs/docs/dev-guide-new-shield.md
+++ b/docs/docs/dev-guide-new-shield.md
@@ -27,6 +27,15 @@ ZMK support for split keyboards requires a few more files than single boards to
## New Shield Directory
+:::note
+This guide describes how to add shield to the ZMK main repository. If you are building firmware for your
+own prototype or handwired keyboard, it is recommended to use your own user config repository. Follow the
+[user setup guide](./user-setup.md) to create your user config repository first. When following the rest
+of this guide, replace the `app/` directory in the ZMK main repository with the `config/` directory in your
+user config repository. For example, `app/boards/shields/<keyboard_name>` should now be
+`config/boards/shields/<keyboard_name>`.
+:::
+
Shields for Zephyr applications go into the `boards/shields/` directory; since ZMK's Zephyr application lives in the `app/` subdirectory of the repository, that means the new shield directory should be:
```bash
@@ -66,7 +75,11 @@ that make sense to have different defaults when this shield is used. One main it
that usually has a new default value set here is the `ZMK_KEYBOARD_NAME` value,
which controls the display name of the device over USB and BLE.
-The updated new default values should always be wrapped inside a conditional on the shield config name defined in the `Kconfig.shield` file. Here's the simplest example file:
+The updated new default values should always be wrapped inside a conditional on the shield config name defined in the `Kconfig.shield` file. Here's the simplest example file.
+
+:::warning
+Do not make the keyboard name too long, otherwise the bluetooth advertising might fail and you will not be able to find your keyboard from your laptop / tablet.
+:::
```
if SHIELD_MY_BOARD
diff --git a/docs/docs/dev-guide-usb-logging.md b/docs/docs/dev-guide-usb-logging.md
index d0274d1..50884be 100644
--- a/docs/docs/dev-guide-usb-logging.md
+++ b/docs/docs/dev-guide-usb-logging.md
@@ -12,11 +12,25 @@ If you are developing ZMK on a device that does not have a built in UART for deb
Zephyr can be configured to create a USB CDC ACM device and the direct all `printk`, console output, and log
messages to that device instead.
+:::warning Battery Life Impact
+
+Enabling logging increases the power usage of your keyboard, and can have a non-trivial impact to your time on battery.
+It is recommended to only enable logging when needed, and not leaving it on by default.
+
+:::
+
## Kconfig
The following KConfig values need to be set, either by copy and pasting into the `app/prj.conf` file, or by running
`west build -t menuconfig` and manually enabling the various settings in that UI.
+:::note
+If you are debugging your own keyboard in your [user config repository](./user-setup.md), use
+`config/boards/shields/<your_keyboard>/<your_keyboard>.conf` instead of `app/prj.conf`. In Github
+Actions, you can search the `Kconfig file` build log to verify the options above have been enabled
+for you successfully.
+:::
+
```
# Turn on logging, and set ZMK logging to debug output
CONFIG_LOG=y
@@ -52,6 +66,7 @@ defaultValue="linux"
values={[
{label: 'Linux', value: 'linux'},
{label: 'Windows', value: 'win'},
+{label: 'MacOS', value: 'macos'}
]}>
<TabItem value="linux">
@@ -73,6 +88,17 @@ On Windows, you can use [PuTTY](https://www.putty.org/). Once installed, use Dev
If you already have the Ardunio IDE installed you can also use its built-in Serial Monitor.
</TabItem>
+<TabItem value="macos">
+
+On MacOS, the device name is something like `/dev/tty.usbmodemXXXXX` where `XXXXX` is some numerical ID.
+You can connect to the device with [tio](https://tio.github.io/) (can be installed via [Homebrew](https://formulae.brew.sh/formula/tio)):
+
+```
+sudo tio /dev/tty.usbmodem14401
+```
+
+You should see tio printing `Disconnected` or `Connected` when you disconnect or reconnect the USB cable.
+</TabItem>
</Tabs>
From there, you should see the various log messages from ZMK and Zephyr, depending on which systems you have set to what log levels.
diff --git a/docs/docs/feature/keymaps.md b/docs/docs/feature/keymaps.md
index 56fc2cc..a322336 100644
--- a/docs/docs/feature/keymaps.md
+++ b/docs/docs/feature/keymaps.md
@@ -68,7 +68,7 @@ In this case, the `A` is actually a define for the raw HID keycode, to make keym
For example of a binding that uses two parameters, you can see how "mod-tap" (`mt`) is bound:
```
-&mt MOD_LSFT D
+&mt LSFT D
```
Here, the first parameter is the set of modifiers that should be used for the "hold" behavior, and the second
@@ -92,7 +92,7 @@ The top two lines of most keymaps should include:
The first defines the nodes for all the available behaviors in ZMK, which will be referenced in the behavior bindings. This is how bindings like `&kp` can reference the key press behavior defined with an anchor name of `kp`.
-The second include brings in the defines for all the keycodes (e.g. `A`, `NUM_1`, `M_PLAY`) and the modifiers (e.g. `MOD_LSFT`) used for various behavior bindings.
+The second include brings in the defines for all the keycodes (e.g. `A`, `NUM_1`, `M_PLAY`) and the modifiers (e.g. `LSFT`) used for various behavior bindings.
### Root devicetree Node
diff --git a/docs/docs/intro.md b/docs/docs/intro.md
index 547265a..cb227a2 100644
--- a/docs/docs/intro.md
+++ b/docs/docs/intro.md
@@ -16,7 +16,7 @@ ZMK is currently missing some features found in other popular firmware. This tab
| ---------------------------------------------------------------------------------------------------------------------- | :-: | :-------: | :-: |
| Low Latency BLE Support | ✅ | ✅ | |
| Multi-Device BLE Support | ✅ | | |
-| USB Connectivity | ✅ | | ✅ |
+| [USB Connectivity](behavior/outputs) | ✅ | | ✅ |
| User Configuration Repositories | ✅ | | |
| Split Keyboard Support | ✅ | ✅ | ✅ |
| [Keymaps and Layers](behavior/layers) | ✅ | ✅ | ✅ |
diff --git a/docs/docs/user-setup.md b/docs/docs/user-setup.md
index 5a4921e..990a8f6 100644
--- a/docs/docs/user-setup.md
+++ b/docs/docs/user-setup.md
@@ -39,6 +39,7 @@ The remainder of this guide assumes the following prerequisites:
1. You have an active, working [GitHub](https://github.com/) account.
1. You have installed and configured the [`git`](https://git-scm.com/) version control tool.
+1. You have locally configured git to access your github account. If using [personal access tokens](https://docs.github.com/en/free-pro-team@latest/github/authenticating-to-github/creating-a-personal-access-token), please be sure it was created with the "workflow" scope option selected.
:::note
If you need to, a quick read of [Learn The Basics Of Git In Under 10 Minutes](https://www.freecodecamp.org/news/learn-the-basics-of-git-in-under-10-minutes-da548267cc91/) will help you get started.
@@ -102,6 +103,12 @@ Pick an MCU board:
### Keyboard Shield Selection
+:::note
+If you are building firmware for a new keyboard shield that is not included in the built-in
+list of shields, you can choose any shield from the list that is similar to yours to generate the repository,
+and edit / add necessary files according to the [guide for adding new keyboard shield](./dev-guide-new-shield.md).
+:::
+
When prompted, enter the number for the corresponding keyboard shield you would like to target:
```
diff --git a/docs/package.json b/docs/package.json
index f1e911e..cb42506 100644
--- a/docs/package.json
+++ b/docs/package.json
@@ -8,7 +8,10 @@
"serve": "docusaurus serve",
"swizzle": "docusaurus swizzle",
"deploy": "docusaurus deploy",
- "clear": "docusaurus clear"
+ "clear": "docusaurus clear",
+ "lint": "eslint . --ext js,jsx,md,mdx",
+ "prettier:check": "prettier --check .",
+ "prettier:format": "prettier --write ."
},
"dependencies": {
"@docusaurus/core": "^2.0.0-alpha.66",
diff --git a/docs/sidebars.js b/docs/sidebars.js
index 54f6576..c8dc79f 100644
--- a/docs/sidebars.js
+++ b/docs/sidebars.js
@@ -22,6 +22,7 @@ module.exports = {
"behavior/mod-tap",
"behavior/reset",
"behavior/bluetooth",
+ "behavior/outputs",
"behavior/lighting",
"behavior/power",
],