From 745338dc10b1a4383f4c3205dfdf39d8a05a347c Mon Sep 17 00:00:00 2001 From: dnaq Date: Fri, 24 Sep 2021 13:24:56 +0200 Subject: [PATCH 1/8] feat(shield): Add Bat43 shield See https://kbd.dailycraft.jp/bat43/ for details. --- app/boards/shields/bat43/Kconfig.defconfig | 9 ++++ app/boards/shields/bat43/Kconfig.shield | 5 ++ app/boards/shields/bat43/bat43.keymap | 50 ++++++++++++++++++++ app/boards/shields/bat43/bat43.overlay | 54 ++++++++++++++++++++++ app/boards/shields/bat43/bat43.zmk.yml | 8 ++++ 5 files changed, 126 insertions(+) create mode 100644 app/boards/shields/bat43/Kconfig.defconfig create mode 100644 app/boards/shields/bat43/Kconfig.shield create mode 100644 app/boards/shields/bat43/bat43.keymap create mode 100644 app/boards/shields/bat43/bat43.overlay create mode 100644 app/boards/shields/bat43/bat43.zmk.yml diff --git a/app/boards/shields/bat43/Kconfig.defconfig b/app/boards/shields/bat43/Kconfig.defconfig new file mode 100644 index 00000000..43de1fa8 --- /dev/null +++ b/app/boards/shields/bat43/Kconfig.defconfig @@ -0,0 +1,9 @@ +# Copyright (c) 2021 The ZMK Contributors +# SPDX-License-Identifier: MIT + +if SHIELD_BAT43 + +config ZMK_KEYBOARD_NAME + default "Bat43" + +endif diff --git a/app/boards/shields/bat43/Kconfig.shield b/app/boards/shields/bat43/Kconfig.shield new file mode 100644 index 00000000..10ee88ce --- /dev/null +++ b/app/boards/shields/bat43/Kconfig.shield @@ -0,0 +1,5 @@ +# Copyright (c) 2021 The ZMK Contributors +# SPDX-License-Identifier: MIT + +config SHIELD_BAT43 + def_bool $(shields_list_contains,bat43) diff --git a/app/boards/shields/bat43/bat43.keymap b/app/boards/shields/bat43/bat43.keymap new file mode 100644 index 00000000..34670d37 --- /dev/null +++ b/app/boards/shields/bat43/bat43.keymap @@ -0,0 +1,50 @@ +/* + * Copyright (c) 2021 The ZMK Contributors + * + * SPDX-License-Identifier: MIT + */ + +#include +#include +#include +#include + +#define LOWER 1 +#define RAISE 2 + +#define L_SPC < LOWER SPACE +#define R_RET < RAISE RET + +/ { + keymap { + compatible = "zmk,keymap"; + + default_layer { + bindings = < +&kp ESC &kp Q &kp W &kp E &kp R &kp T &kp Y &kp U &kp I &kp O &kp P &kp MINUS +&kp TAB &kp A &kp S &kp D &kp F &kp G &kp BSPC &kp H &kp J &kp K &kp L &kp SEMI &kp RSHFT +&kp LCTL &kp Z &kp X &kp C &kp V &kp B &kp N &kp M &kp COMMA &kp DOT &kp FSLH &kp RCTL + &kp LGUI &kp LANG2 L_SPC R_RET &kp LANG1 &kp RALT + &bt BT_CLR &out OUT_TOG &bt BT_SEL 0 &bt BT_SEL 1 &bt BT_SEL 2 + >; + }; + lower_layer { + bindings = < +&trans &none &none &none &none &none &none &kp EQUAL &kp PLUS &kp STAR &kp PRCNT &trans +&trans &kp N1 &kp N2 &kp N3 &kp N4 &kp N5 &trans &kp N6 &kp N7 &kp N8 &kp N9 &kp N0 &trans +&trans &none &none &none &none &none &none &none &trans &trans &trans &trans + &trans &trans &trans &trans &trans &trans + &trans &trans &trans &trans &trans + >; + }; + raise_layer { + bindings = < +&trans &kp BSLH &kp EXCL &kp AMPS &kp PIPE &none &none &kp EQUAL &kp PLUS &kp STAR &kp PRCNT &trans +&trans &kp HASH &kp GRAVE &kp DQT &kp SQT &kp TILDE &trans &kp LEFT &kp DOWN &kp UP &kp RIGHT &kp DLLR &trans +&trans &none &none &kp LBRC &kp LBKT &kp LPAR &kp RPAR &kp RBKT &kp RBRC &kp AT &kp CARET &trans + &trans &trans &trans &trans &trans &trans + &trans &trans &trans &trans &trans + >; + }; + }; +}; diff --git a/app/boards/shields/bat43/bat43.overlay b/app/boards/shields/bat43/bat43.overlay new file mode 100644 index 00000000..fc906e0f --- /dev/null +++ b/app/boards/shields/bat43/bat43.overlay @@ -0,0 +1,54 @@ +/* + * Copyright (c) 2021 The ZMK Contributors + * + * SPDX-License-Identifier: MIT + */ + +#include + +/ { + chosen { + zmk,kscan = &kscan0; + zmk,matrix_transform = &default_transform; + }; + + default_transform: keymap_transform_0 { + compatible = "zmk,matrix-transform"; + columns = <6>; + rows = <7>; + + map = < +RC(0,0) RC(0,1) RC(0,2) RC(0,3) RC(0,4) RC(0,5) RC(4,0) RC(4,1) RC(4,2) RC(4,3) RC(4,4) RC(4,5) +RC(1,0) RC(1,1) RC(1,2) RC(1,3) RC(1,4) RC(1,5) RC(3,0) RC(5,0) RC(5,1) RC(5,2) RC(5,3) RC(5,4) RC(5,5) +RC(2,0) RC(2,1) RC(2,2) RC(2,3) RC(2,4) RC(2,5) RC(6,0) RC(6,1) RC(6,2) RC(6,3) RC(6,4) RC(6,5) + RC(3,3) RC(3,4) RC(3,5) RC(7,0) RC(7,1) RC(7,2) + RC(7,5) RC(7,4) RC(7,3) RC(3,1) RC(3,2) + >; + }; + + kscan0: kscan_0 { + compatible = "zmk,kscan-gpio-matrix"; + label = "KSCAN"; + diode-direction = "col2row"; + + col-gpios + = <&pro_micro 10 GPIO_ACTIVE_HIGH> + , <&pro_micro 16 GPIO_ACTIVE_HIGH> + , <&pro_micro 14 GPIO_ACTIVE_HIGH> + , <&pro_micro 15 GPIO_ACTIVE_HIGH> + , <&pro_micro 9 GPIO_ACTIVE_HIGH> + , <&pro_micro 8 GPIO_ACTIVE_HIGH> + ; + + row-gpios + = <&pro_micro 7 (GPIO_ACTIVE_HIGH | GPIO_PULL_DOWN)> + , <&pro_micro 6 (GPIO_ACTIVE_HIGH | GPIO_PULL_DOWN)> + , <&pro_micro 5 (GPIO_ACTIVE_HIGH | GPIO_PULL_DOWN)> + , <&pro_micro 4 (GPIO_ACTIVE_HIGH | GPIO_PULL_DOWN)> + , <&pro_micro 18 (GPIO_ACTIVE_HIGH | GPIO_PULL_DOWN)> + , <&pro_micro 19 (GPIO_ACTIVE_HIGH | GPIO_PULL_DOWN)> + , <&pro_micro 20 (GPIO_ACTIVE_HIGH | GPIO_PULL_DOWN)> + , <&pro_micro 21 (GPIO_ACTIVE_HIGH | GPIO_PULL_DOWN)> + ; + }; +}; diff --git a/app/boards/shields/bat43/bat43.zmk.yml b/app/boards/shields/bat43/bat43.zmk.yml new file mode 100644 index 00000000..a84bf862 --- /dev/null +++ b/app/boards/shields/bat43/bat43.zmk.yml @@ -0,0 +1,8 @@ +file_format: "1" +id: bat43 +name: BAT43 +type: shield +url: https://kbd.dailycraft.jp/bat43/ +requires: [pro_micro] +features: + - keys From a1a8c30f7ff9f0b57ad9f278e6ccd47697855867 Mon Sep 17 00:00:00 2001 From: Nick Winans Date: Mon, 14 Mar 2022 00:36:29 -0500 Subject: [PATCH 2/8] Remove deprecated key codes from bat43 keymap --- app/boards/shields/bat43/bat43.keymap | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/boards/shields/bat43/bat43.keymap b/app/boards/shields/bat43/bat43.keymap index 34670d37..0f7e2d55 100644 --- a/app/boards/shields/bat43/bat43.keymap +++ b/app/boards/shields/bat43/bat43.keymap @@ -23,7 +23,7 @@ bindings = < &kp ESC &kp Q &kp W &kp E &kp R &kp T &kp Y &kp U &kp I &kp O &kp P &kp MINUS &kp TAB &kp A &kp S &kp D &kp F &kp G &kp BSPC &kp H &kp J &kp K &kp L &kp SEMI &kp RSHFT -&kp LCTL &kp Z &kp X &kp C &kp V &kp B &kp N &kp M &kp COMMA &kp DOT &kp FSLH &kp RCTL +&kp LCTRL &kp Z &kp X &kp C &kp V &kp B &kp N &kp M &kp COMMA &kp DOT &kp FSLH &kp RCTRL &kp LGUI &kp LANG2 L_SPC R_RET &kp LANG1 &kp RALT &bt BT_CLR &out OUT_TOG &bt BT_SEL 0 &bt BT_SEL 1 &bt BT_SEL 2 >; From 2b4d5dd7d9271dd520d9143a9bc5d95db08921a3 Mon Sep 17 00:00:00 2001 From: Carlos Filoteo Date: Sun, 13 Mar 2022 23:51:55 -0600 Subject: [PATCH 3/8] feat(shields): Add elephant42 Shield Support (#1009) * Initial Elephant42 implementation * Add underglow feature * Fix keymap * Copy corne defconfig for OLED and LED * Fix matrix positions * Add nice_nano_v2.overlay * Usability improvements to keymap * Update LED length * Delete nice_nano v1 overlay * Remove unused conf files * Add copyright/license headers * PR feedback * Try fixing formatting again * Minor format * More missed tabs * Format --- .../shields/elephant42/Kconfig.defconfig | 55 +++++++++++++++++ app/boards/shields/elephant42/Kconfig.shield | 8 +++ .../elephant42/boards/nice_nano_v2.overlay | 28 +++++++++ app/boards/shields/elephant42/elephant42.conf | 6 ++ app/boards/shields/elephant42/elephant42.dtsi | 60 +++++++++++++++++++ .../shields/elephant42/elephant42.keymap | 55 +++++++++++++++++ .../shields/elephant42/elephant42.zmk.yml | 14 +++++ .../elephant42/elephant42_left.overlay | 18 ++++++ .../elephant42/elephant42_right.overlay | 22 +++++++ 9 files changed, 266 insertions(+) create mode 100644 app/boards/shields/elephant42/Kconfig.defconfig create mode 100644 app/boards/shields/elephant42/Kconfig.shield create mode 100644 app/boards/shields/elephant42/boards/nice_nano_v2.overlay create mode 100644 app/boards/shields/elephant42/elephant42.conf create mode 100644 app/boards/shields/elephant42/elephant42.dtsi create mode 100644 app/boards/shields/elephant42/elephant42.keymap create mode 100644 app/boards/shields/elephant42/elephant42.zmk.yml create mode 100644 app/boards/shields/elephant42/elephant42_left.overlay create mode 100644 app/boards/shields/elephant42/elephant42_right.overlay diff --git a/app/boards/shields/elephant42/Kconfig.defconfig b/app/boards/shields/elephant42/Kconfig.defconfig new file mode 100644 index 00000000..db2fa230 --- /dev/null +++ b/app/boards/shields/elephant42/Kconfig.defconfig @@ -0,0 +1,55 @@ +# Copyright (c) 2021 The ZMK Contributors +# SPDX-License-Identifier: MIT + +if SHIELD_ELEPHANT42_LEFT + +config ZMK_KEYBOARD_NAME + default "Elephant42" + +config ZMK_SPLIT_BLE_ROLE_CENTRAL + default y + +endif + +if SHIELD_ELEPHANT42_LEFT || SHIELD_ELEPHANT42_RIGHT + +config ZMK_SPLIT + default y + +if ZMK_DISPLAY + +config I2C + default y + +config SSD1306 + default y + +config SSD1306_REVERSE_MODE + default y + +endif # ZMK_DISPLAY + +if LVGL + +config LVGL_HOR_RES_MAX + default 128 + +config LVGL_VER_RES_MAX + default 32 + +config LVGL_VDB_SIZE + default 64 + +config LVGL_DPI + default 148 + +config LVGL_BITS_PER_PIXEL + default 1 + +choice LVGL_COLOR_DEPTH + default LVGL_COLOR_DEPTH_1 +endchoice + +endif # LVGL + +endif \ No newline at end of file diff --git a/app/boards/shields/elephant42/Kconfig.shield b/app/boards/shields/elephant42/Kconfig.shield new file mode 100644 index 00000000..25841868 --- /dev/null +++ b/app/boards/shields/elephant42/Kconfig.shield @@ -0,0 +1,8 @@ +# Copyright (c) 2021 The ZMK Contributors +# SPDX-License-Identifier: MIT + +config SHIELD_ELEPHANT42_LEFT + def_bool $(shields_list_contains,elephant42_left) + +config SHIELD_ELEPHANT42_RIGHT + def_bool $(shields_list_contains,elephant42_right) \ No newline at end of file diff --git a/app/boards/shields/elephant42/boards/nice_nano_v2.overlay b/app/boards/shields/elephant42/boards/nice_nano_v2.overlay new file mode 100644 index 00000000..6cd5de8c --- /dev/null +++ b/app/boards/shields/elephant42/boards/nice_nano_v2.overlay @@ -0,0 +1,28 @@ +&spi1 { + compatible = "nordic,nrf-spim"; + /* Cannot be used together with i2c0. */ + status = "okay"; + mosi-pin = <6>; + // Unused pins, needed for SPI definition, but not used by the ws2812 driver itself. + sck-pin = <5>; + miso-pin = <7>; + + led_strip: ws2812@0 { + compatible = "worldsemi,ws2812-spi"; + label = "WS2812"; + /* SPI */ + reg = <0>; /* ignored, but necessary for SPI bindings */ + spi-max-frequency = <4000000>; + + /* WS2812 */ + chain-length = <27>; /* There are per-key RGB and the LAST 6 are underglow */ + spi-one-frame = <0x70>; + spi-zero-frame = <0x40>; + }; +}; + +/ { + chosen { + zmk,underglow = &led_strip; + }; +}; \ No newline at end of file diff --git a/app/boards/shields/elephant42/elephant42.conf b/app/boards/shields/elephant42/elephant42.conf new file mode 100644 index 00000000..1b41763f --- /dev/null +++ b/app/boards/shields/elephant42/elephant42.conf @@ -0,0 +1,6 @@ +# Uncomment the following lines to enable the Elephant42 RGB Underglow +# CONFIG_ZMK_RGB_UNDERGLOW=y +# CONFIG_WS2812_STRIP=y + +# Uncomment the following line to enable the Elephant42 OLED Display +# CONFIG_ZMK_DISPLAY=y \ No newline at end of file diff --git a/app/boards/shields/elephant42/elephant42.dtsi b/app/boards/shields/elephant42/elephant42.dtsi new file mode 100644 index 00000000..e2b708ca --- /dev/null +++ b/app/boards/shields/elephant42/elephant42.dtsi @@ -0,0 +1,60 @@ +/* + * Copyright (c) 2021 The ZMK Contributors + * + * SPDX-License-Identifier: MIT + */ + +#include + +/ { + chosen { + zmk,kscan = &kscan0; + zmk,matrix_transform = &default_transform; + }; + + default_transform: keymap_transform_0 { + compatible = "zmk,matrix-transform"; + columns = <12>; + rows = <4>; + map = < +RC(0,0) RC(0,1) RC(0,2) RC(0,3) RC(0,4) RC(0,5) RC(0,6) RC(0,7) RC(0,8) RC(0,9) RC(0,10) RC(0,11) +RC(1,0) RC(1,1) RC(1,2) RC(1,3) RC(1,4) RC(1,5) RC(1,6) RC(1,7) RC(1,8) RC(1,9) RC(1,10) RC(1,11) + RC(2,1) RC(2,2) RC(2,3) RC(2,4) RC(2,5) RC(2,6) RC(2,7) RC(2,8) RC(2,9) RC(2,10) + RC(3,2) RC(3,3) RC(3,4) RC(3,5) RC(3,6) RC(3,7) RC(3,8) RC(3,9) + >; + }; + + kscan0: kscan { + compatible = "zmk,kscan-gpio-matrix"; + label = "KSCAN"; + + diode-direction = "col2row"; + row-gpios + = <&pro_micro 4 (GPIO_ACTIVE_HIGH | GPIO_PULL_DOWN)> + , <&pro_micro 5 (GPIO_ACTIVE_HIGH | GPIO_PULL_DOWN)> + , <&pro_micro 6 (GPIO_ACTIVE_HIGH | GPIO_PULL_DOWN)> + , <&pro_micro 7 (GPIO_ACTIVE_HIGH | GPIO_PULL_DOWN)> + ; + + }; +}; + +&pro_micro_i2c { + status = "okay"; + + oled: ssd1306@3c { + compatible = "solomon,ssd1306fb"; + reg = <0x3c>; + label = "DISPLAY"; + width = <128>; + height = <32>; + segment-offset = <0>; + page-offset = <0>; + display-offset = <0>; + multiplex-ratio = <31>; + segment-remap; + com-invdir; + com-sequential; + prechargep = <0x22>; + }; +}; \ No newline at end of file diff --git a/app/boards/shields/elephant42/elephant42.keymap b/app/boards/shields/elephant42/elephant42.keymap new file mode 100644 index 00000000..8594c117 --- /dev/null +++ b/app/boards/shields/elephant42/elephant42.keymap @@ -0,0 +1,55 @@ +/* + * Copyright (c) 2021 The ZMK Contributors + * + * SPDX-License-Identifier: MIT + */ + +#include +#include +#include +#include + +#define LOWR 1 +#define RAIS 2 +#define ADJT 3 + +/ { + keymap { + compatible = "zmk,keymap"; + + default_layer { + bindings = < + < ADJT ESC &kp Q &kp W &kp E &kp R &kp T &kp Y &kp U &kp I &kp O &kp P &kp DEL + &mt LCTRL TAB &kp A &kp S &kp D &kp F &kp G &kp H &kp J &kp K &kp L &kp SEMI &kp SQT + &kp Z &kp X &kp C &kp V &kp B &kp N &kp M &kp COMMA &kp DOT &kp FSLH + &kp LSHFT &mo LOWR &kp LGUI &kp BSPC &kp SPACE &kp ENTER &mo RAIS &kp LALT + >; + }; + + lower { + bindings = < + &kp GRAVE &kp N1 &kp N2 &kp N3 &kp N4 &kp N5 &kp N6 &kp N7 &kp N8 &kp N9 &kp N0 &kp C_PLAY_PAUSE + &trans &trans &trans &trans &trans &trans &kp LEFT &kp DOWN &kp UP &kp RIGHT &trans &trans + &trans &trans &trans &trans &kp LBKT &kp RBKT &trans &trans &trans &trans + &trans &trans &trans &trans &trans &trans &trans &trans + >; + }; + raise { + bindings = < + &kp TILDE &trans &trans &trans &trans &trans &trans &trans &trans &kp MINUS &kp EQUAL &kp C_PLAY_PAUSE + &trans &trans &trans &trans &trans &trans &trans &trans &trans &trans &trans &kp BSLH + &trans &trans &trans &trans &kp LBKT &kp RBKT &trans &trans &trans &trans + &trans &trans &trans &trans &trans &trans &trans &trans + >; + }; + + adjust { + bindings = < + &trans &bt BT_NXT &bt BT_PRV &trans &trans &bt BT_CLR &trans &trans &trans &trans &trans &trans + &trans &trans &trans &trans &trans &trans &trans &trans &trans &trans &trans &trans + &trans &trans &trans &trans &trans &trans &trans &trans &trans &trans + &out OUT_TOG &trans &trans &trans &trans &trans &trans &trans + >; + }; + }; +}; \ No newline at end of file diff --git a/app/boards/shields/elephant42/elephant42.zmk.yml b/app/boards/shields/elephant42/elephant42.zmk.yml new file mode 100644 index 00000000..60eb3777 --- /dev/null +++ b/app/boards/shields/elephant42/elephant42.zmk.yml @@ -0,0 +1,14 @@ +file_format: "1" +id: elephant42 +name: Elephant42 +type: shield +url: https://github.com/illness072/elephant42 +requires: [pro_micro] +exposes: [i2c_oled] +features: + - keys + - display + - underglow +siblings: + - elephant42_left + - elephant42_right diff --git a/app/boards/shields/elephant42/elephant42_left.overlay b/app/boards/shields/elephant42/elephant42_left.overlay new file mode 100644 index 00000000..72fe2251 --- /dev/null +++ b/app/boards/shields/elephant42/elephant42_left.overlay @@ -0,0 +1,18 @@ +/* + * Copyright (c) 2021 The ZMK Contributors + * + * SPDX-License-Identifier: MIT + */ + +#include "elephant42.dtsi" + +&kscan0 { + col-gpios + = <&pro_micro 21 GPIO_ACTIVE_HIGH> + , <&pro_micro 20 GPIO_ACTIVE_HIGH> + , <&pro_micro 19 GPIO_ACTIVE_HIGH> + , <&pro_micro 18 GPIO_ACTIVE_HIGH> + , <&pro_micro 15 GPIO_ACTIVE_HIGH> + , <&pro_micro 14 GPIO_ACTIVE_HIGH> + ; +}; \ No newline at end of file diff --git a/app/boards/shields/elephant42/elephant42_right.overlay b/app/boards/shields/elephant42/elephant42_right.overlay new file mode 100644 index 00000000..35bd5895 --- /dev/null +++ b/app/boards/shields/elephant42/elephant42_right.overlay @@ -0,0 +1,22 @@ +/* + * Copyright (c) 2021 The ZMK Contributors + * + * SPDX-License-Identifier: MIT + */ + +#include "elephant42.dtsi" + +&default_transform { + col-offset = <6>; +}; + +&kscan0 { + col-gpios + = <&pro_micro 14 GPIO_ACTIVE_HIGH> + , <&pro_micro 15 GPIO_ACTIVE_HIGH> + , <&pro_micro 18 GPIO_ACTIVE_HIGH> + , <&pro_micro 19 GPIO_ACTIVE_HIGH> + , <&pro_micro 20 GPIO_ACTIVE_HIGH> + , <&pro_micro 21 GPIO_ACTIVE_HIGH> + ; +}; From d6a2290d5ef5da561b590f9b1d236c40005940fe Mon Sep 17 00:00:00 2001 From: Aaron Nunley Date: Fri, 29 Jan 2021 17:52:33 -0800 Subject: [PATCH 4/8] Added support for 2% Milk (Updated with metadata) Update app/boards/shields/two_percent_milk/two_percent_milk.overlay Co-Authored-By: Nick Winans --- .../two_percent_milk/Kconfig.defconfig | 9 +++++++ .../shields/two_percent_milk/Kconfig.shield | 5 ++++ .../two_percent_milk/two_percent_milk.conf | 2 ++ .../two_percent_milk/two_percent_milk.keymap | 22 +++++++++++++++++ .../two_percent_milk/two_percent_milk.overlay | 24 +++++++++++++++++++ .../two_percent_milk/two_percent_milk.zmk.yml | 8 +++++++ 6 files changed, 70 insertions(+) create mode 100644 app/boards/shields/two_percent_milk/Kconfig.defconfig create mode 100644 app/boards/shields/two_percent_milk/Kconfig.shield create mode 100644 app/boards/shields/two_percent_milk/two_percent_milk.conf create mode 100644 app/boards/shields/two_percent_milk/two_percent_milk.keymap create mode 100644 app/boards/shields/two_percent_milk/two_percent_milk.overlay create mode 100644 app/boards/shields/two_percent_milk/two_percent_milk.zmk.yml diff --git a/app/boards/shields/two_percent_milk/Kconfig.defconfig b/app/boards/shields/two_percent_milk/Kconfig.defconfig new file mode 100644 index 00000000..1046f53c --- /dev/null +++ b/app/boards/shields/two_percent_milk/Kconfig.defconfig @@ -0,0 +1,9 @@ +# Copyright (c) 2022 The ZMK Contributors +# SPDX-License-Identifier: MIT + +if SHIELD_TWO_PERCENT_MILK + +config ZMK_KEYBOARD_NAME + default "2% Milk" + +endif diff --git a/app/boards/shields/two_percent_milk/Kconfig.shield b/app/boards/shields/two_percent_milk/Kconfig.shield new file mode 100644 index 00000000..ec2c3b1f --- /dev/null +++ b/app/boards/shields/two_percent_milk/Kconfig.shield @@ -0,0 +1,5 @@ +# Copyright (c) 2022 The ZMK Contributors +# SPDX-License-Identifier: MIT + +config SHIELD_TWO_PERCENT_MILK + def_bool $(shields_list_contains,two_percent_milk) diff --git a/app/boards/shields/two_percent_milk/two_percent_milk.conf b/app/boards/shields/two_percent_milk/two_percent_milk.conf new file mode 100644 index 00000000..fb23f20c --- /dev/null +++ b/app/boards/shields/two_percent_milk/two_percent_milk.conf @@ -0,0 +1,2 @@ +# Copyright (c) 2022 The ZMK Contributors +# SPDX-License-Identifier: MIT diff --git a/app/boards/shields/two_percent_milk/two_percent_milk.keymap b/app/boards/shields/two_percent_milk/two_percent_milk.keymap new file mode 100644 index 00000000..71152de2 --- /dev/null +++ b/app/boards/shields/two_percent_milk/two_percent_milk.keymap @@ -0,0 +1,22 @@ +/* + * Copyright (c) 2022 The ZMK Contributors + * + * SPDX-License-Identifier: MIT + */ + + #include + #include + #include + + / { + keymap { + compatible = "zmk,keymap"; + + default_layer { + bindings = < + &kp X + &kp Z + >; + }; + }; + }; \ No newline at end of file diff --git a/app/boards/shields/two_percent_milk/two_percent_milk.overlay b/app/boards/shields/two_percent_milk/two_percent_milk.overlay new file mode 100644 index 00000000..d43ed321 --- /dev/null +++ b/app/boards/shields/two_percent_milk/two_percent_milk.overlay @@ -0,0 +1,24 @@ +/* + * Copyright (c) 2022 The ZMK Contributors + * + * SPDX-License-Identifier: MIT + */ + +/ { + chosen { + zmk,kscan = &kscan0; + }; + + kscan0: kscan { + compatible = "zmk,kscan-gpio-direct"; + + label = "KSCAN"; + + input-gpios + = <&pro_micro 4 (GPIO_ACTIVE_LOW | GPIO_PULL_UP)> + , <&pro_micro 5 (GPIO_ACTIVE_LOW | GPIO_PULL_UP)> + ; + + }; + +}; \ No newline at end of file diff --git a/app/boards/shields/two_percent_milk/two_percent_milk.zmk.yml b/app/boards/shields/two_percent_milk/two_percent_milk.zmk.yml new file mode 100644 index 00000000..c53c2df2 --- /dev/null +++ b/app/boards/shields/two_percent_milk/two_percent_milk.zmk.yml @@ -0,0 +1,8 @@ +file_format: "1" +id: two_percent_milk +name: 2% Milk +type: shield +url: https://github.com/Spaceboards/SpaceboardsHardware/tree/master/Keyboards/2%25%20Milk +requires: [pro_micro] +features: + - keys From 459972fddd3d9e778b91b824888de9e0bbeffbce Mon Sep 17 00:00:00 2001 From: Krzysztof Gutkowski Date: Sat, 12 Mar 2022 19:18:25 +0000 Subject: [PATCH 5/8] fix(boards): Adjust matrix for BT60v1 to fix the broken right Shift key according to the diagram from the board creators, it should be RC(3,12) instead of RC(3,13) --- app/boards/arm/bt60/bt60_v1.dts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/boards/arm/bt60/bt60_v1.dts b/app/boards/arm/bt60/bt60_v1.dts index 83da2c07..8b2f0cb4 100644 --- a/app/boards/arm/bt60/bt60_v1.dts +++ b/app/boards/arm/bt60/bt60_v1.dts @@ -22,7 +22,7 @@ RC(0,0) RC(0,1) RC(0,2) RC(0,3) RC(0,4) RC(0,5) RC(0,6) RC(0,7) RC(0,8) RC(0,9) RC(0,10) RC(0,11) RC(0,12) RC(0,13) RC(1,0) RC(1,1) RC(1,2) RC(1,3) RC(1,4) RC(1,5) RC(1,6) RC(1,7) RC(1,8) RC(1,9) RC(1,10) RC(1,11) RC(1,12) RC(1,13) RC(2,0) RC(2,1) RC(2,2) RC(2,3) RC(2,4) RC(2,5) RC(2,6) RC(2,7) RC(2,8) RC(2,9) RC(2,10) RC(2,11) RC(2,13) - RC(3,0) RC(3,2) RC(3,3) RC(3,4) RC(3,5) RC(3,6) RC(3,7) RC(3,8) RC(3,9) RC(3,10) RC(3,11) RC(3,13) + RC(3,0) RC(3,2) RC(3,3) RC(3,4) RC(3,5) RC(3,6) RC(3,7) RC(3,8) RC(3,9) RC(3,10) RC(3,11) RC(3,12) RC(4,0) RC(4,1) RC(4,2) RC(4,6) RC(4,10) RC(4,11) RC(4,12) RC(4,13) RC(4,14) >; }; From ad5a12a7bcdc7c0962fdef7c23eaa0cf14d0b7ed Mon Sep 17 00:00:00 2001 From: okke Date: Sun, 27 Feb 2022 14:11:36 +0100 Subject: [PATCH 6/8] fix(behaviors): Fix bug in nested sticky keys If multiple sticky keys with quick release were nested, only the first one was properly released. This fix makes sure all of them are released properly. Fixes https://github.com/zmkfirmware/zmk/issues/1149 --- app/src/behaviors/behavior_sticky_key.c | 14 +++- .../sticky-keys/10-sl-sl-kp/events.patterns | 1 + .../10-sl-sl-kp/keycode_events.snapshot | 8 +++ .../10-sl-sl-kp/native_posix.keymap | 65 +++++++++++++++++++ 4 files changed, 85 insertions(+), 3 deletions(-) create mode 100644 app/tests/sticky-keys/10-sl-sl-kp/events.patterns create mode 100644 app/tests/sticky-keys/10-sl-sl-kp/keycode_events.snapshot create mode 100644 app/tests/sticky-keys/10-sl-sl-kp/native_posix.keymap diff --git a/app/src/behaviors/behavior_sticky_key.c b/app/src/behaviors/behavior_sticky_key.c index 3c75a7a3..7909e1af 100644 --- a/app/src/behaviors/behavior_sticky_key.c +++ b/app/src/behaviors/behavior_sticky_key.c @@ -188,6 +188,9 @@ static int sticky_key_keycode_state_changed_listener(const zmk_event_t *eh) { if (ev == NULL) { return ZMK_EV_EVENT_BUBBLE; } + + // keep track whether the event has been reraised, so we only reraise it once + bool event_reraised = false; for (int i = 0; i < ZMK_BHV_STICKY_KEY_MAX_HELD; i++) { struct active_sticky_key *sticky_key = &active_sticky_keys[i]; if (sticky_key->position == ZMK_BHV_STICKY_KEY_POSITION_FREE) { @@ -223,10 +226,12 @@ static int sticky_key_keycode_state_changed_listener(const zmk_event_t *eh) { if (sticky_key->timer_started) { stop_timer(sticky_key); if (sticky_key->config->quick_release) { - // continue processing the event. Release the sticky key afterwards. - ZMK_EVENT_RAISE_AFTER(eh, behavior_sticky_key); + // immediately release the sticky key after the key press is handled. + if (!event_reraised) { + ZMK_EVENT_RAISE_AFTER(eh, behavior_sticky_key); + event_reraised = true; + } release_sticky_key_behavior(sticky_key, ev->timestamp); - return ZMK_EV_EVENT_CAPTURED; } } sticky_key->modified_key_usage_page = ev->usage_page; @@ -240,6 +245,9 @@ static int sticky_key_keycode_state_changed_listener(const zmk_event_t *eh) { } } } + if (event_reraised) { + return ZMK_EV_EVENT_CAPTURED; + } return ZMK_EV_EVENT_BUBBLE; } diff --git a/app/tests/sticky-keys/10-sl-sl-kp/events.patterns b/app/tests/sticky-keys/10-sl-sl-kp/events.patterns new file mode 100644 index 00000000..833100f6 --- /dev/null +++ b/app/tests/sticky-keys/10-sl-sl-kp/events.patterns @@ -0,0 +1 @@ +s/.*hid_listener_keycode_//p \ No newline at end of file diff --git a/app/tests/sticky-keys/10-sl-sl-kp/keycode_events.snapshot b/app/tests/sticky-keys/10-sl-sl-kp/keycode_events.snapshot new file mode 100644 index 00000000..fc0f29b9 --- /dev/null +++ b/app/tests/sticky-keys/10-sl-sl-kp/keycode_events.snapshot @@ -0,0 +1,8 @@ +pressed: usage_page 0x07 keycode 0x1e implicit_mods 0x00 explicit_mods 0x00 +released: usage_page 0x07 keycode 0x1e implicit_mods 0x00 explicit_mods 0x00 +pressed: usage_page 0x07 keycode 0x04 implicit_mods 0x00 explicit_mods 0x00 +released: usage_page 0x07 keycode 0x04 implicit_mods 0x00 explicit_mods 0x00 +pressed: usage_page 0x07 keycode 0x1e implicit_mods 0x00 explicit_mods 0x00 +released: usage_page 0x07 keycode 0x1e implicit_mods 0x00 explicit_mods 0x00 +pressed: usage_page 0x07 keycode 0x04 implicit_mods 0x00 explicit_mods 0x00 +released: usage_page 0x07 keycode 0x04 implicit_mods 0x00 explicit_mods 0x00 diff --git a/app/tests/sticky-keys/10-sl-sl-kp/native_posix.keymap b/app/tests/sticky-keys/10-sl-sl-kp/native_posix.keymap new file mode 100644 index 00000000..e9b87f42 --- /dev/null +++ b/app/tests/sticky-keys/10-sl-sl-kp/native_posix.keymap @@ -0,0 +1,65 @@ +#include +#include +#include + +/* + sticky layers should quick-release. + Thus, the second keypress should be on the default layer, not on the lower_layer. +*/ + +/ { + keymap { + compatible = "zmk,keymap"; + label ="Default keymap"; + + default_layer { + bindings = < + &sl 1 &kp A + &none &none>; + }; + + layer_1 { + bindings = < + &sl 2 &none + &none &none>; + }; + + layer_2 { + bindings = < + &none &kp NUM_1 + &none &none>; + }; + }; +}; + +&kscan { + events = < + /* press sl 1 */ + ZMK_MOCK_PRESS(0,0,10) + ZMK_MOCK_RELEASE(0,0,10) + /* press sl 2 */ + ZMK_MOCK_PRESS(0,0,10) + ZMK_MOCK_RELEASE(0,0,10) + /* press 1 */ + ZMK_MOCK_PRESS(0,1,10) + ZMK_MOCK_RELEASE(0,1,10) + /* press A */ + ZMK_MOCK_PRESS(0,1,10) + ZMK_MOCK_RELEASE(0,1,10) + + /* repeat test to check if cleanup is done correctly */ + /* press sl 1 */ + ZMK_MOCK_PRESS(0,0,10) + ZMK_MOCK_RELEASE(0,0,10) + /* press sl 2 */ + ZMK_MOCK_PRESS(0,0,10) + ZMK_MOCK_RELEASE(0,0,10) + /* press 1 */ + ZMK_MOCK_PRESS(0,1,10) + ZMK_MOCK_RELEASE(0,1,10) + /* press A */ + ZMK_MOCK_PRESS(0,1,10) + ZMK_MOCK_RELEASE(0,1,10) + + >; +}; \ No newline at end of file From 32ebe2cfb57be5881750b4296a8ece85921f3aa9 Mon Sep 17 00:00:00 2001 From: Kurtis Lew Date: Tue, 15 Mar 2022 22:08:42 -0700 Subject: [PATCH 7/8] feat(behaviors): Add Tap-Dance behavior --- app/CMakeLists.txt | 1 + .../behaviors/zmk,behavior-tap-dance.yaml | 16 ++ app/src/behaviors/behavior_tap_dance.c | 259 ++++++++++++++++++ app/tests/tap-dance/1a-tap1/events.patterns | 2 + .../tap-dance/1a-tap1/keycode_events.snapshot | 5 + .../tap-dance/1a-tap1/native_posix.keymap | 11 + app/tests/tap-dance/1b-tap2/events.patterns | 2 + .../tap-dance/1b-tap2/keycode_events.snapshot | 7 + .../tap-dance/1b-tap2/native_posix.keymap | 13 + app/tests/tap-dance/1c-tap3/events.patterns | 2 + .../tap-dance/1c-tap3/keycode_events.snapshot | 9 + .../tap-dance/1c-tap3/native_posix.keymap | 15 + app/tests/tap-dance/2a-hold1/events.patterns | 2 + .../2a-hold1/keycode_events.snapshot | 5 + .../tap-dance/2a-hold1/native_posix.keymap | 11 + app/tests/tap-dance/2b-hold2/events.patterns | 2 + .../2b-hold2/keycode_events.snapshot | 7 + .../tap-dance/2b-hold2/native_posix.keymap | 13 + app/tests/tap-dance/2c-hold3/events.patterns | 2 + .../2c-hold3/keycode_events.snapshot | 9 + .../tap-dance/2c-hold3/native_posix.keymap | 15 + .../tap-dance/3a-tap-int-mid/events.patterns | 2 + .../3a-tap-int-mid/keycode_events.snapshot | 10 + .../3a-tap-int-mid/native_posix.keymap | 13 + .../tap-dance/3b-tap-int-seq/events.patterns | 2 + .../3b-tap-int-seq/keycode_events.snapshot | 10 + .../3b-tap-int-seq/native_posix.keymap | 13 + .../3c-tap-int-after/events.patterns | 2 + .../3c-tap-int-after/keycode_events.snapshot | 10 + .../3c-tap-int-after/native_posix.keymap | 13 + .../tap-dance/3d-hold-int-mid/events.patterns | 2 + .../3d-hold-int-mid/keycode_events.snapshot | 10 + .../3d-hold-int-mid/native_posix.keymap | 13 + .../tap-dance/3e-hold-int-seq/events.patterns | 2 + .../3e-hold-int-seq/keycode_events.snapshot | 10 + .../3e-hold-int-seq/native_posix.keymap | 13 + .../3f-hold-int-after/events.patterns | 2 + .../3f-hold-int-after/keycode_events.snapshot | 10 + .../3f-hold-int-after/native_posix.keymap | 13 + app/tests/tap-dance/4a-single/events.patterns | 2 + .../4a-single/keycode_events.snapshot | 5 + .../tap-dance/4a-single/native_posix.keymap | 11 + .../tap-dance/5a-tdint-mid/events.patterns | 2 + .../5a-tdint-mid/keycode_events.snapshot | 10 + .../5a-tdint-mid/native_posix.keymap | 13 + .../tap-dance/5b-tdint-seq/events.patterns | 2 + .../5b-tdint-seq/keycode_events.snapshot | 10 + .../5b-tdint-seq/native_posix.keymap | 13 + .../tap-dance/5c-tdint-after/events.patterns | 2 + .../5c-tdint-after/keycode_events.snapshot | 10 + .../5c-tdint-after/native_posix.keymap | 13 + .../5d-tdint-multiple/events.patterns | 2 + .../5d-tdint-multiple/keycode_events.snapshot | 15 + .../5d-tdint-multiple/native_posix.keymap | 15 + app/tests/tap-dance/behavior_keymap.dtsi | 60 ++++ docs/docs/assets/tap-dance/timing_diagram.svg | 83 ++++++ docs/docs/behaviors/tap-dance.md | 76 +++++ docs/sidebars.js | 1 + 58 files changed, 903 insertions(+) create mode 100644 app/dts/bindings/behaviors/zmk,behavior-tap-dance.yaml create mode 100644 app/src/behaviors/behavior_tap_dance.c create mode 100644 app/tests/tap-dance/1a-tap1/events.patterns create mode 100644 app/tests/tap-dance/1a-tap1/keycode_events.snapshot create mode 100644 app/tests/tap-dance/1a-tap1/native_posix.keymap create mode 100644 app/tests/tap-dance/1b-tap2/events.patterns create mode 100644 app/tests/tap-dance/1b-tap2/keycode_events.snapshot create mode 100644 app/tests/tap-dance/1b-tap2/native_posix.keymap create mode 100644 app/tests/tap-dance/1c-tap3/events.patterns create mode 100644 app/tests/tap-dance/1c-tap3/keycode_events.snapshot create mode 100644 app/tests/tap-dance/1c-tap3/native_posix.keymap create mode 100644 app/tests/tap-dance/2a-hold1/events.patterns create mode 100644 app/tests/tap-dance/2a-hold1/keycode_events.snapshot create mode 100644 app/tests/tap-dance/2a-hold1/native_posix.keymap create mode 100644 app/tests/tap-dance/2b-hold2/events.patterns create mode 100644 app/tests/tap-dance/2b-hold2/keycode_events.snapshot create mode 100644 app/tests/tap-dance/2b-hold2/native_posix.keymap create mode 100644 app/tests/tap-dance/2c-hold3/events.patterns create mode 100644 app/tests/tap-dance/2c-hold3/keycode_events.snapshot create mode 100644 app/tests/tap-dance/2c-hold3/native_posix.keymap create mode 100644 app/tests/tap-dance/3a-tap-int-mid/events.patterns create mode 100644 app/tests/tap-dance/3a-tap-int-mid/keycode_events.snapshot create mode 100644 app/tests/tap-dance/3a-tap-int-mid/native_posix.keymap create mode 100644 app/tests/tap-dance/3b-tap-int-seq/events.patterns create mode 100644 app/tests/tap-dance/3b-tap-int-seq/keycode_events.snapshot create mode 100644 app/tests/tap-dance/3b-tap-int-seq/native_posix.keymap create mode 100644 app/tests/tap-dance/3c-tap-int-after/events.patterns create mode 100644 app/tests/tap-dance/3c-tap-int-after/keycode_events.snapshot create mode 100644 app/tests/tap-dance/3c-tap-int-after/native_posix.keymap create mode 100644 app/tests/tap-dance/3d-hold-int-mid/events.patterns create mode 100644 app/tests/tap-dance/3d-hold-int-mid/keycode_events.snapshot create mode 100644 app/tests/tap-dance/3d-hold-int-mid/native_posix.keymap create mode 100644 app/tests/tap-dance/3e-hold-int-seq/events.patterns create mode 100644 app/tests/tap-dance/3e-hold-int-seq/keycode_events.snapshot create mode 100644 app/tests/tap-dance/3e-hold-int-seq/native_posix.keymap create mode 100644 app/tests/tap-dance/3f-hold-int-after/events.patterns create mode 100644 app/tests/tap-dance/3f-hold-int-after/keycode_events.snapshot create mode 100644 app/tests/tap-dance/3f-hold-int-after/native_posix.keymap create mode 100644 app/tests/tap-dance/4a-single/events.patterns create mode 100644 app/tests/tap-dance/4a-single/keycode_events.snapshot create mode 100644 app/tests/tap-dance/4a-single/native_posix.keymap create mode 100644 app/tests/tap-dance/5a-tdint-mid/events.patterns create mode 100644 app/tests/tap-dance/5a-tdint-mid/keycode_events.snapshot create mode 100644 app/tests/tap-dance/5a-tdint-mid/native_posix.keymap create mode 100644 app/tests/tap-dance/5b-tdint-seq/events.patterns create mode 100644 app/tests/tap-dance/5b-tdint-seq/keycode_events.snapshot create mode 100644 app/tests/tap-dance/5b-tdint-seq/native_posix.keymap create mode 100644 app/tests/tap-dance/5c-tdint-after/events.patterns create mode 100644 app/tests/tap-dance/5c-tdint-after/keycode_events.snapshot create mode 100644 app/tests/tap-dance/5c-tdint-after/native_posix.keymap create mode 100644 app/tests/tap-dance/5d-tdint-multiple/events.patterns create mode 100644 app/tests/tap-dance/5d-tdint-multiple/keycode_events.snapshot create mode 100644 app/tests/tap-dance/5d-tdint-multiple/native_posix.keymap create mode 100644 app/tests/tap-dance/behavior_keymap.dtsi create mode 100644 docs/docs/assets/tap-dance/timing_diagram.svg create mode 100644 docs/docs/behaviors/tap-dance.md diff --git a/app/CMakeLists.txt b/app/CMakeLists.txt index 25f6c6cd..d853255c 100644 --- a/app/CMakeLists.txt +++ b/app/CMakeLists.txt @@ -54,6 +54,7 @@ if ((NOT CONFIG_ZMK_SPLIT) OR CONFIG_ZMK_SPLIT_BLE_ROLE_CENTRAL) target_sources(app PRIVATE src/behaviors/behavior_momentary_layer.c) target_sources(app PRIVATE src/behaviors/behavior_mod_morph.c) target_sources(app PRIVATE src/behaviors/behavior_outputs.c) + target_sources(app PRIVATE src/behaviors/behavior_tap_dance.c) target_sources(app PRIVATE src/behaviors/behavior_toggle_layer.c) target_sources(app PRIVATE src/behaviors/behavior_to_layer.c) target_sources(app PRIVATE src/behaviors/behavior_transparent.c) diff --git a/app/dts/bindings/behaviors/zmk,behavior-tap-dance.yaml b/app/dts/bindings/behaviors/zmk,behavior-tap-dance.yaml new file mode 100644 index 00000000..8f01effc --- /dev/null +++ b/app/dts/bindings/behaviors/zmk,behavior-tap-dance.yaml @@ -0,0 +1,16 @@ +# Copyright (c) 2022 The ZMK Contributors +# SPDX-License-Identifier: MIT + +description: Tap Dance Behavior + +compatible: "zmk,behavior-tap-dance" + +include: zero_param.yaml + +properties: + bindings: + type: phandle-array + required: true + tapping-term-ms: + type: int + default: 200 \ No newline at end of file diff --git a/app/src/behaviors/behavior_tap_dance.c b/app/src/behaviors/behavior_tap_dance.c new file mode 100644 index 00000000..4b35e4de --- /dev/null +++ b/app/src/behaviors/behavior_tap_dance.c @@ -0,0 +1,259 @@ +/* + * Copyright (c) 2022 The ZMK Contributors + * + * SPDX-License-Identifier: MIT + */ + +#define DT_DRV_COMPAT zmk_behavior_tap_dance + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +LOG_MODULE_DECLARE(zmk, CONFIG_ZMK_LOG_LEVEL); + +#if DT_HAS_COMPAT_STATUS_OKAY(DT_DRV_COMPAT) + +#define ZMK_BHV_TAP_DANCE_MAX_HELD 10 + +#define ZMK_BHV_TAP_DANCE_POSITION_FREE ULONG_MAX + +struct behavior_tap_dance_config { + uint32_t tapping_term_ms; + size_t behavior_count; + struct zmk_behavior_binding *behaviors; +}; + +struct active_tap_dance { + // Tap Dance Data + int counter; + uint32_t position; + uint32_t param1; + uint32_t param2; + bool is_pressed; + const struct behavior_tap_dance_config *config; + + // Timer Data + bool timer_started; + bool timer_cancelled; + bool tap_dance_decided; + int64_t release_at; + struct k_delayed_work release_timer; +}; + +struct active_tap_dance active_tap_dances[ZMK_BHV_TAP_DANCE_MAX_HELD] = {}; + +static struct active_tap_dance *find_tap_dance(uint32_t position) { + for (int i = 0; i < ZMK_BHV_TAP_DANCE_MAX_HELD; i++) { + if (active_tap_dances[i].position == position && !active_tap_dances[i].timer_cancelled) { + return &active_tap_dances[i]; + } + } + return NULL; +} + +static int new_tap_dance(uint32_t position, const struct behavior_tap_dance_config *config, + struct active_tap_dance **tap_dance) { + for (int i = 0; i < ZMK_BHV_TAP_DANCE_MAX_HELD; i++) { + struct active_tap_dance *const ref_dance = &active_tap_dances[i]; + if (ref_dance->position == ZMK_BHV_TAP_DANCE_POSITION_FREE) { + ref_dance->counter = 0; + ref_dance->position = position; + ref_dance->config = config; + ref_dance->release_at = 0; + ref_dance->is_pressed = true; + ref_dance->timer_started = true; + ref_dance->timer_cancelled = false; + ref_dance->tap_dance_decided = false; + *tap_dance = ref_dance; + return 0; + } + } + return -ENOMEM; +} + +static void clear_tap_dance(struct active_tap_dance *tap_dance) { + tap_dance->position = ZMK_BHV_TAP_DANCE_POSITION_FREE; +} + +static int stop_timer(struct active_tap_dance *tap_dance) { + int timer_cancel_result = k_delayed_work_cancel(&tap_dance->release_timer); + if (timer_cancel_result == -EINPROGRESS) { + // too late to cancel, we'll let the timer handler clear up. + tap_dance->timer_cancelled = true; + } + return timer_cancel_result; +} + +static void reset_timer(struct active_tap_dance *tap_dance, + struct zmk_behavior_binding_event event) { + tap_dance->release_at = event.timestamp + tap_dance->config->tapping_term_ms; + int32_t ms_left = tap_dance->release_at - k_uptime_get(); + if (ms_left > 0) { + k_delayed_work_submit(&tap_dance->release_timer, K_MSEC(ms_left)); + LOG_DBG("Successfully reset timer at position %d", tap_dance->position); + } +} + +static inline int press_tap_dance_behavior(struct active_tap_dance *tap_dance, int64_t timestamp) { + tap_dance->tap_dance_decided = true; + struct zmk_behavior_binding binding = tap_dance->config->behaviors[tap_dance->counter - 1]; + struct zmk_behavior_binding_event event = { + .position = tap_dance->position, + .timestamp = timestamp, + }; + return behavior_keymap_binding_pressed(&binding, event); +} + +static inline int release_tap_dance_behavior(struct active_tap_dance *tap_dance, + int64_t timestamp) { + struct zmk_behavior_binding binding = tap_dance->config->behaviors[tap_dance->counter - 1]; + struct zmk_behavior_binding_event event = { + .position = tap_dance->position, + .timestamp = timestamp, + }; + clear_tap_dance(tap_dance); + return behavior_keymap_binding_released(&binding, event); +} + +static int on_tap_dance_binding_pressed(struct zmk_behavior_binding *binding, + struct zmk_behavior_binding_event event) { + const struct device *dev = device_get_binding(binding->behavior_dev); + const struct behavior_tap_dance_config *cfg = dev->config; + struct active_tap_dance *tap_dance; + tap_dance = find_tap_dance(event.position); + if (tap_dance == NULL) { + if (new_tap_dance(event.position, cfg, &tap_dance) == -ENOMEM) { + LOG_ERR("Unable to create new tap dance. Insufficient space in active_tap_dances[]."); + return ZMK_BEHAVIOR_OPAQUE; + } + LOG_DBG("%d created new tap dance", event.position); + } + tap_dance->is_pressed = true; + LOG_DBG("%d tap dance pressed", event.position); + stop_timer(tap_dance); + // Increment the counter on keypress. If the counter has reached its maximum + // value, invoke the last binding available. + if (tap_dance->counter < cfg->behavior_count) { + tap_dance->counter++; + } + if (tap_dance->counter == cfg->behavior_count) { + // LOG_DBG("Tap dance has been decided via maximum counter value"); + press_tap_dance_behavior(tap_dance, event.timestamp); + return ZMK_EV_EVENT_BUBBLE; + } + reset_timer(tap_dance, event); + return ZMK_BEHAVIOR_OPAQUE; +} + +static int on_tap_dance_binding_released(struct zmk_behavior_binding *binding, + struct zmk_behavior_binding_event event) { + LOG_DBG("%d tap dance keybind released", event.position); + struct active_tap_dance *tap_dance = find_tap_dance(event.position); + if (tap_dance == NULL) { + LOG_ERR("ACTIVE TAP DANCE CLEARED TOO EARLY"); + return ZMK_BEHAVIOR_OPAQUE; + } + tap_dance->is_pressed = false; + if (tap_dance->tap_dance_decided) { + release_tap_dance_behavior(tap_dance, event.timestamp); + } + return ZMK_BEHAVIOR_OPAQUE; +} + +void behavior_tap_dance_timer_handler(struct k_work *item) { + struct active_tap_dance *tap_dance = CONTAINER_OF(item, struct active_tap_dance, release_timer); + if (tap_dance->position == ZMK_BHV_TAP_DANCE_POSITION_FREE) { + return; + } + if (tap_dance->timer_cancelled) { + return; + } + LOG_DBG("Tap dance has been decided via timer. Counter reached: %d", tap_dance->counter); + press_tap_dance_behavior(tap_dance, tap_dance->release_at); + if (tap_dance->is_pressed) { + return; + } + release_tap_dance_behavior(tap_dance, tap_dance->release_at); +} + +static const struct behavior_driver_api behavior_tap_dance_driver_api = { + .binding_pressed = on_tap_dance_binding_pressed, + .binding_released = on_tap_dance_binding_released, +}; + +static int tap_dance_position_state_changed_listener(const zmk_event_t *eh); + +ZMK_LISTENER(behavior_tap_dance, tap_dance_position_state_changed_listener); +ZMK_SUBSCRIPTION(behavior_tap_dance, zmk_position_state_changed); + +static int tap_dance_position_state_changed_listener(const zmk_event_t *eh) { + struct zmk_position_state_changed *ev = as_zmk_position_state_changed(eh); + if (ev == NULL) { + return ZMK_EV_EVENT_BUBBLE; + } + if (!ev->state) { + LOG_DBG("Ignore upstroke at position %d.", ev->position); + return ZMK_EV_EVENT_BUBBLE; + } + for (int i = 0; i < ZMK_BHV_TAP_DANCE_MAX_HELD; i++) { + struct active_tap_dance *tap_dance = &active_tap_dances[i]; + if (tap_dance->position == ZMK_BHV_TAP_DANCE_POSITION_FREE) { + continue; + } + if (tap_dance->position == ev->position) { + continue; + } + stop_timer(tap_dance); + LOG_DBG("Tap dance interrupted, activating tap-dance at %d", tap_dance->position); + if (!tap_dance->tap_dance_decided) { + press_tap_dance_behavior(tap_dance, ev->timestamp); + if (!tap_dance->is_pressed) { + release_tap_dance_behavior(tap_dance, ev->timestamp); + } + return ZMK_EV_EVENT_BUBBLE; + } + } + return ZMK_EV_EVENT_BUBBLE; +} + +static int behavior_tap_dance_init(const struct device *dev) { + static bool init_first_run = true; + if (init_first_run) { + for (int i = 0; i < ZMK_BHV_TAP_DANCE_MAX_HELD; i++) { + k_delayed_work_init(&active_tap_dances[i].release_timer, + behavior_tap_dance_timer_handler); + clear_tap_dance(&active_tap_dances[i]); + } + } + init_first_run = false; + return 0; +} + +#define _TRANSFORM_ENTRY(idx, node) ZMK_KEYMAP_EXTRACT_BINDING(idx, node), + +#define TRANSFORMED_BINDINGS(node) \ + { UTIL_LISTIFY(DT_INST_PROP_LEN(node, bindings), _TRANSFORM_ENTRY, DT_DRV_INST(node)) } + +#define KP_INST(n) \ + static struct zmk_behavior_binding \ + behavior_tap_dance_config_##n##_bindings[DT_INST_PROP_LEN(n, bindings)] = \ + TRANSFORMED_BINDINGS(n); \ + static struct behavior_tap_dance_config behavior_tap_dance_config_##n = { \ + .tapping_term_ms = DT_INST_PROP(n, tapping_term_ms), \ + .behaviors = behavior_tap_dance_config_##n##_bindings, \ + .behavior_count = DT_INST_PROP_LEN(n, bindings)}; \ + DEVICE_AND_API_INIT(behavior_tap_dance_##n, DT_INST_LABEL(n), behavior_tap_dance_init, NULL, \ + &behavior_tap_dance_config_##n, APPLICATION, \ + CONFIG_KERNEL_INIT_PRIORITY_DEFAULT, &behavior_tap_dance_driver_api); + +DT_INST_FOREACH_STATUS_OKAY(KP_INST) + +#endif \ No newline at end of file diff --git a/app/tests/tap-dance/1a-tap1/events.patterns b/app/tests/tap-dance/1a-tap1/events.patterns new file mode 100644 index 00000000..1768fc21 --- /dev/null +++ b/app/tests/tap-dance/1a-tap1/events.patterns @@ -0,0 +1,2 @@ +s/.*hid_listener_keycode/kp/p +s/.*on_tap_dance_binding/td_binding/p \ No newline at end of file diff --git a/app/tests/tap-dance/1a-tap1/keycode_events.snapshot b/app/tests/tap-dance/1a-tap1/keycode_events.snapshot new file mode 100644 index 00000000..38bc54c3 --- /dev/null +++ b/app/tests/tap-dance/1a-tap1/keycode_events.snapshot @@ -0,0 +1,5 @@ +td_binding_pressed: 0 created new tap dance +td_binding_pressed: 0 tap dance pressed +td_binding_released: 0 tap dance keybind released +kp_pressed: usage_page 0x07 keycode 0x04 implicit_mods 0x00 explicit_mods 0x00 +kp_released: usage_page 0x07 keycode 0x04 implicit_mods 0x00 explicit_mods 0x00 diff --git a/app/tests/tap-dance/1a-tap1/native_posix.keymap b/app/tests/tap-dance/1a-tap1/native_posix.keymap new file mode 100644 index 00000000..1e5dff06 --- /dev/null +++ b/app/tests/tap-dance/1a-tap1/native_posix.keymap @@ -0,0 +1,11 @@ +#include +#include +#include +#include "../behavior_keymap.dtsi" + +&kscan { + events = < + ZMK_MOCK_PRESS(0,0,10) + ZMK_MOCK_RELEASE(0,0,200) + >; +}; \ No newline at end of file diff --git a/app/tests/tap-dance/1b-tap2/events.patterns b/app/tests/tap-dance/1b-tap2/events.patterns new file mode 100644 index 00000000..1768fc21 --- /dev/null +++ b/app/tests/tap-dance/1b-tap2/events.patterns @@ -0,0 +1,2 @@ +s/.*hid_listener_keycode/kp/p +s/.*on_tap_dance_binding/td_binding/p \ No newline at end of file diff --git a/app/tests/tap-dance/1b-tap2/keycode_events.snapshot b/app/tests/tap-dance/1b-tap2/keycode_events.snapshot new file mode 100644 index 00000000..c23537b9 --- /dev/null +++ b/app/tests/tap-dance/1b-tap2/keycode_events.snapshot @@ -0,0 +1,7 @@ +td_binding_pressed: 0 created new tap dance +td_binding_pressed: 0 tap dance pressed +td_binding_released: 0 tap dance keybind released +td_binding_pressed: 0 tap dance pressed +td_binding_released: 0 tap dance keybind released +kp_pressed: usage_page 0x07 keycode 0x05 implicit_mods 0x00 explicit_mods 0x00 +kp_released: usage_page 0x07 keycode 0x05 implicit_mods 0x00 explicit_mods 0x00 diff --git a/app/tests/tap-dance/1b-tap2/native_posix.keymap b/app/tests/tap-dance/1b-tap2/native_posix.keymap new file mode 100644 index 00000000..c5e1c8db --- /dev/null +++ b/app/tests/tap-dance/1b-tap2/native_posix.keymap @@ -0,0 +1,13 @@ +#include +#include +#include +#include "../behavior_keymap.dtsi" + +&kscan { + events = < + ZMK_MOCK_PRESS(0,0,10) + ZMK_MOCK_RELEASE(0,0,10) + ZMK_MOCK_PRESS(0,0,10) + ZMK_MOCK_RELEASE(0,0,200) + >; +}; \ No newline at end of file diff --git a/app/tests/tap-dance/1c-tap3/events.patterns b/app/tests/tap-dance/1c-tap3/events.patterns new file mode 100644 index 00000000..1768fc21 --- /dev/null +++ b/app/tests/tap-dance/1c-tap3/events.patterns @@ -0,0 +1,2 @@ +s/.*hid_listener_keycode/kp/p +s/.*on_tap_dance_binding/td_binding/p \ No newline at end of file diff --git a/app/tests/tap-dance/1c-tap3/keycode_events.snapshot b/app/tests/tap-dance/1c-tap3/keycode_events.snapshot new file mode 100644 index 00000000..1e68bae9 --- /dev/null +++ b/app/tests/tap-dance/1c-tap3/keycode_events.snapshot @@ -0,0 +1,9 @@ +td_binding_pressed: 0 created new tap dance +td_binding_pressed: 0 tap dance pressed +td_binding_released: 0 tap dance keybind released +td_binding_pressed: 0 tap dance pressed +td_binding_released: 0 tap dance keybind released +td_binding_pressed: 0 tap dance pressed +td_binding_released: 0 tap dance keybind released +kp_pressed: usage_page 0x07 keycode 0x06 implicit_mods 0x00 explicit_mods 0x00 +kp_released: usage_page 0x07 keycode 0x06 implicit_mods 0x00 explicit_mods 0x00 diff --git a/app/tests/tap-dance/1c-tap3/native_posix.keymap b/app/tests/tap-dance/1c-tap3/native_posix.keymap new file mode 100644 index 00000000..6813393e --- /dev/null +++ b/app/tests/tap-dance/1c-tap3/native_posix.keymap @@ -0,0 +1,15 @@ +#include +#include +#include +#include "../behavior_keymap.dtsi" + +&kscan { + events = < + ZMK_MOCK_PRESS(0,0,10) + ZMK_MOCK_RELEASE(0,0,10) + ZMK_MOCK_PRESS(0,0,10) + ZMK_MOCK_RELEASE(0,0,10) + ZMK_MOCK_PRESS(0,0,10) + ZMK_MOCK_RELEASE(0,0,200) + >; +}; \ No newline at end of file diff --git a/app/tests/tap-dance/2a-hold1/events.patterns b/app/tests/tap-dance/2a-hold1/events.patterns new file mode 100644 index 00000000..1768fc21 --- /dev/null +++ b/app/tests/tap-dance/2a-hold1/events.patterns @@ -0,0 +1,2 @@ +s/.*hid_listener_keycode/kp/p +s/.*on_tap_dance_binding/td_binding/p \ No newline at end of file diff --git a/app/tests/tap-dance/2a-hold1/keycode_events.snapshot b/app/tests/tap-dance/2a-hold1/keycode_events.snapshot new file mode 100644 index 00000000..2a0965dc --- /dev/null +++ b/app/tests/tap-dance/2a-hold1/keycode_events.snapshot @@ -0,0 +1,5 @@ +td_binding_pressed: 0 created new tap dance +td_binding_pressed: 0 tap dance pressed +kp_pressed: usage_page 0x07 keycode 0xe1 implicit_mods 0x00 explicit_mods 0x00 +td_binding_released: 0 tap dance keybind released +kp_released: usage_page 0x07 keycode 0xe1 implicit_mods 0x00 explicit_mods 0x00 diff --git a/app/tests/tap-dance/2a-hold1/native_posix.keymap b/app/tests/tap-dance/2a-hold1/native_posix.keymap new file mode 100644 index 00000000..f4c7a2d2 --- /dev/null +++ b/app/tests/tap-dance/2a-hold1/native_posix.keymap @@ -0,0 +1,11 @@ +#include +#include +#include +#include "../behavior_keymap.dtsi" + +&kscan { + events = < + ZMK_MOCK_PRESS(0,0,400) + ZMK_MOCK_RELEASE(0,0,10) + >; +}; \ No newline at end of file diff --git a/app/tests/tap-dance/2b-hold2/events.patterns b/app/tests/tap-dance/2b-hold2/events.patterns new file mode 100644 index 00000000..1768fc21 --- /dev/null +++ b/app/tests/tap-dance/2b-hold2/events.patterns @@ -0,0 +1,2 @@ +s/.*hid_listener_keycode/kp/p +s/.*on_tap_dance_binding/td_binding/p \ No newline at end of file diff --git a/app/tests/tap-dance/2b-hold2/keycode_events.snapshot b/app/tests/tap-dance/2b-hold2/keycode_events.snapshot new file mode 100644 index 00000000..dbccfc94 --- /dev/null +++ b/app/tests/tap-dance/2b-hold2/keycode_events.snapshot @@ -0,0 +1,7 @@ +td_binding_pressed: 0 created new tap dance +td_binding_pressed: 0 tap dance pressed +td_binding_released: 0 tap dance keybind released +td_binding_pressed: 0 tap dance pressed +kp_pressed: usage_page 0x07 keycode 0xe2 implicit_mods 0x00 explicit_mods 0x00 +td_binding_released: 0 tap dance keybind released +kp_released: usage_page 0x07 keycode 0xe2 implicit_mods 0x00 explicit_mods 0x00 diff --git a/app/tests/tap-dance/2b-hold2/native_posix.keymap b/app/tests/tap-dance/2b-hold2/native_posix.keymap new file mode 100644 index 00000000..0fec2e40 --- /dev/null +++ b/app/tests/tap-dance/2b-hold2/native_posix.keymap @@ -0,0 +1,13 @@ +#include +#include +#include +#include "../behavior_keymap.dtsi" + +&kscan { + events = < + ZMK_MOCK_PRESS(0,0,10) + ZMK_MOCK_RELEASE(0,0,10) + ZMK_MOCK_PRESS(0,0,400) + ZMK_MOCK_RELEASE(0,0,10) + >; +}; \ No newline at end of file diff --git a/app/tests/tap-dance/2c-hold3/events.patterns b/app/tests/tap-dance/2c-hold3/events.patterns new file mode 100644 index 00000000..1768fc21 --- /dev/null +++ b/app/tests/tap-dance/2c-hold3/events.patterns @@ -0,0 +1,2 @@ +s/.*hid_listener_keycode/kp/p +s/.*on_tap_dance_binding/td_binding/p \ No newline at end of file diff --git a/app/tests/tap-dance/2c-hold3/keycode_events.snapshot b/app/tests/tap-dance/2c-hold3/keycode_events.snapshot new file mode 100644 index 00000000..3ac8e0e6 --- /dev/null +++ b/app/tests/tap-dance/2c-hold3/keycode_events.snapshot @@ -0,0 +1,9 @@ +td_binding_pressed: 0 created new tap dance +td_binding_pressed: 0 tap dance pressed +td_binding_released: 0 tap dance keybind released +td_binding_pressed: 0 tap dance pressed +td_binding_released: 0 tap dance keybind released +td_binding_pressed: 0 tap dance pressed +kp_pressed: usage_page 0x07 keycode 0xe3 implicit_mods 0x00 explicit_mods 0x00 +td_binding_released: 0 tap dance keybind released +kp_released: usage_page 0x07 keycode 0xe3 implicit_mods 0x00 explicit_mods 0x00 diff --git a/app/tests/tap-dance/2c-hold3/native_posix.keymap b/app/tests/tap-dance/2c-hold3/native_posix.keymap new file mode 100644 index 00000000..8375c6f6 --- /dev/null +++ b/app/tests/tap-dance/2c-hold3/native_posix.keymap @@ -0,0 +1,15 @@ +#include +#include +#include +#include "../behavior_keymap.dtsi" + +&kscan { + events = < + ZMK_MOCK_PRESS(0,0,10) + ZMK_MOCK_RELEASE(0,0,10) + ZMK_MOCK_PRESS(0,0,10) + ZMK_MOCK_RELEASE(0,0,10) + ZMK_MOCK_PRESS(0,0,400) + ZMK_MOCK_RELEASE(0,0,10) + >; +}; \ No newline at end of file diff --git a/app/tests/tap-dance/3a-tap-int-mid/events.patterns b/app/tests/tap-dance/3a-tap-int-mid/events.patterns new file mode 100644 index 00000000..1768fc21 --- /dev/null +++ b/app/tests/tap-dance/3a-tap-int-mid/events.patterns @@ -0,0 +1,2 @@ +s/.*hid_listener_keycode/kp/p +s/.*on_tap_dance_binding/td_binding/p \ No newline at end of file diff --git a/app/tests/tap-dance/3a-tap-int-mid/keycode_events.snapshot b/app/tests/tap-dance/3a-tap-int-mid/keycode_events.snapshot new file mode 100644 index 00000000..715d0143 --- /dev/null +++ b/app/tests/tap-dance/3a-tap-int-mid/keycode_events.snapshot @@ -0,0 +1,10 @@ +td_binding_pressed: 2 created new tap dance +td_binding_pressed: 2 tap dance pressed +kp_pressed: usage_page 0x07 keycode 0x1e implicit_mods 0x00 explicit_mods 0x00 +td_binding_pressed: 1 created new tap dance +td_binding_pressed: 1 tap dance pressed +kp_pressed: usage_page 0x07 keycode 0x16 implicit_mods 0x00 explicit_mods 0x00 +td_binding_released: 1 tap dance keybind released +kp_released: usage_page 0x07 keycode 0x16 implicit_mods 0x00 explicit_mods 0x00 +td_binding_released: 2 tap dance keybind released +kp_released: usage_page 0x07 keycode 0x1e implicit_mods 0x00 explicit_mods 0x00 diff --git a/app/tests/tap-dance/3a-tap-int-mid/native_posix.keymap b/app/tests/tap-dance/3a-tap-int-mid/native_posix.keymap new file mode 100644 index 00000000..8a62430c --- /dev/null +++ b/app/tests/tap-dance/3a-tap-int-mid/native_posix.keymap @@ -0,0 +1,13 @@ +#include +#include +#include +#include "../behavior_keymap.dtsi" + +&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) + >; +}; \ No newline at end of file diff --git a/app/tests/tap-dance/3b-tap-int-seq/events.patterns b/app/tests/tap-dance/3b-tap-int-seq/events.patterns new file mode 100644 index 00000000..1768fc21 --- /dev/null +++ b/app/tests/tap-dance/3b-tap-int-seq/events.patterns @@ -0,0 +1,2 @@ +s/.*hid_listener_keycode/kp/p +s/.*on_tap_dance_binding/td_binding/p \ No newline at end of file diff --git a/app/tests/tap-dance/3b-tap-int-seq/keycode_events.snapshot b/app/tests/tap-dance/3b-tap-int-seq/keycode_events.snapshot new file mode 100644 index 00000000..a973e426 --- /dev/null +++ b/app/tests/tap-dance/3b-tap-int-seq/keycode_events.snapshot @@ -0,0 +1,10 @@ +td_binding_pressed: 2 created new tap dance +td_binding_pressed: 2 tap dance pressed +kp_pressed: usage_page 0x07 keycode 0x1e implicit_mods 0x00 explicit_mods 0x00 +td_binding_pressed: 1 created new tap dance +td_binding_pressed: 1 tap dance pressed +kp_pressed: usage_page 0x07 keycode 0x16 implicit_mods 0x00 explicit_mods 0x00 +td_binding_released: 2 tap dance keybind released +kp_released: usage_page 0x07 keycode 0x1e implicit_mods 0x00 explicit_mods 0x00 +td_binding_released: 1 tap dance keybind released +kp_released: usage_page 0x07 keycode 0x16 implicit_mods 0x00 explicit_mods 0x00 diff --git a/app/tests/tap-dance/3b-tap-int-seq/native_posix.keymap b/app/tests/tap-dance/3b-tap-int-seq/native_posix.keymap new file mode 100644 index 00000000..4a76bdb0 --- /dev/null +++ b/app/tests/tap-dance/3b-tap-int-seq/native_posix.keymap @@ -0,0 +1,13 @@ +#include +#include +#include +#include "../behavior_keymap.dtsi" + +&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) + >; +}; \ No newline at end of file diff --git a/app/tests/tap-dance/3c-tap-int-after/events.patterns b/app/tests/tap-dance/3c-tap-int-after/events.patterns new file mode 100644 index 00000000..1768fc21 --- /dev/null +++ b/app/tests/tap-dance/3c-tap-int-after/events.patterns @@ -0,0 +1,2 @@ +s/.*hid_listener_keycode/kp/p +s/.*on_tap_dance_binding/td_binding/p \ No newline at end of file diff --git a/app/tests/tap-dance/3c-tap-int-after/keycode_events.snapshot b/app/tests/tap-dance/3c-tap-int-after/keycode_events.snapshot new file mode 100644 index 00000000..2c715537 --- /dev/null +++ b/app/tests/tap-dance/3c-tap-int-after/keycode_events.snapshot @@ -0,0 +1,10 @@ +td_binding_pressed: 2 created new tap dance +td_binding_pressed: 2 tap dance pressed +td_binding_released: 2 tap dance keybind released +kp_pressed: usage_page 0x07 keycode 0x1e implicit_mods 0x00 explicit_mods 0x00 +kp_released: usage_page 0x07 keycode 0x1e implicit_mods 0x00 explicit_mods 0x00 +td_binding_pressed: 1 created new tap dance +td_binding_pressed: 1 tap dance pressed +kp_pressed: usage_page 0x07 keycode 0x16 implicit_mods 0x00 explicit_mods 0x00 +td_binding_released: 1 tap dance keybind released +kp_released: usage_page 0x07 keycode 0x16 implicit_mods 0x00 explicit_mods 0x00 diff --git a/app/tests/tap-dance/3c-tap-int-after/native_posix.keymap b/app/tests/tap-dance/3c-tap-int-after/native_posix.keymap new file mode 100644 index 00000000..e1b6d979 --- /dev/null +++ b/app/tests/tap-dance/3c-tap-int-after/native_posix.keymap @@ -0,0 +1,13 @@ +#include +#include +#include +#include "../behavior_keymap.dtsi" + +&kscan { + events = < + ZMK_MOCK_PRESS(1,0,10) + ZMK_MOCK_RELEASE(1,0,10) + ZMK_MOCK_PRESS(0,1,10) + ZMK_MOCK_RELEASE(0,1,10) + >; +}; \ No newline at end of file diff --git a/app/tests/tap-dance/3d-hold-int-mid/events.patterns b/app/tests/tap-dance/3d-hold-int-mid/events.patterns new file mode 100644 index 00000000..1768fc21 --- /dev/null +++ b/app/tests/tap-dance/3d-hold-int-mid/events.patterns @@ -0,0 +1,2 @@ +s/.*hid_listener_keycode/kp/p +s/.*on_tap_dance_binding/td_binding/p \ No newline at end of file diff --git a/app/tests/tap-dance/3d-hold-int-mid/keycode_events.snapshot b/app/tests/tap-dance/3d-hold-int-mid/keycode_events.snapshot new file mode 100644 index 00000000..7631c4ac --- /dev/null +++ b/app/tests/tap-dance/3d-hold-int-mid/keycode_events.snapshot @@ -0,0 +1,10 @@ +td_binding_pressed: 0 created new tap dance +td_binding_pressed: 0 tap dance pressed +kp_pressed: usage_page 0x07 keycode 0xe1 implicit_mods 0x00 explicit_mods 0x00 +td_binding_pressed: 1 created new tap dance +td_binding_pressed: 1 tap dance pressed +kp_pressed: usage_page 0x07 keycode 0x16 implicit_mods 0x00 explicit_mods 0x00 +td_binding_released: 1 tap dance keybind released +kp_released: usage_page 0x07 keycode 0x16 implicit_mods 0x00 explicit_mods 0x00 +td_binding_released: 0 tap dance keybind released +kp_released: usage_page 0x07 keycode 0xe1 implicit_mods 0x00 explicit_mods 0x00 diff --git a/app/tests/tap-dance/3d-hold-int-mid/native_posix.keymap b/app/tests/tap-dance/3d-hold-int-mid/native_posix.keymap new file mode 100644 index 00000000..55a98d36 --- /dev/null +++ b/app/tests/tap-dance/3d-hold-int-mid/native_posix.keymap @@ -0,0 +1,13 @@ +#include +#include +#include +#include "../behavior_keymap.dtsi" + +&kscan { + events = < + ZMK_MOCK_PRESS(0,0,400) + ZMK_MOCK_PRESS(0,1,10) + ZMK_MOCK_RELEASE(0,1,10) + ZMK_MOCK_RELEASE(0,0,10) + >; +}; \ No newline at end of file diff --git a/app/tests/tap-dance/3e-hold-int-seq/events.patterns b/app/tests/tap-dance/3e-hold-int-seq/events.patterns new file mode 100644 index 00000000..1768fc21 --- /dev/null +++ b/app/tests/tap-dance/3e-hold-int-seq/events.patterns @@ -0,0 +1,2 @@ +s/.*hid_listener_keycode/kp/p +s/.*on_tap_dance_binding/td_binding/p \ No newline at end of file diff --git a/app/tests/tap-dance/3e-hold-int-seq/keycode_events.snapshot b/app/tests/tap-dance/3e-hold-int-seq/keycode_events.snapshot new file mode 100644 index 00000000..ca13f8bc --- /dev/null +++ b/app/tests/tap-dance/3e-hold-int-seq/keycode_events.snapshot @@ -0,0 +1,10 @@ +td_binding_pressed: 0 created new tap dance +td_binding_pressed: 0 tap dance pressed +kp_pressed: usage_page 0x07 keycode 0xe1 implicit_mods 0x00 explicit_mods 0x00 +td_binding_pressed: 1 created new tap dance +td_binding_pressed: 1 tap dance pressed +kp_pressed: usage_page 0x07 keycode 0x16 implicit_mods 0x00 explicit_mods 0x00 +td_binding_released: 0 tap dance keybind released +kp_released: usage_page 0x07 keycode 0xe1 implicit_mods 0x00 explicit_mods 0x00 +td_binding_released: 1 tap dance keybind released +kp_released: usage_page 0x07 keycode 0x16 implicit_mods 0x00 explicit_mods 0x00 diff --git a/app/tests/tap-dance/3e-hold-int-seq/native_posix.keymap b/app/tests/tap-dance/3e-hold-int-seq/native_posix.keymap new file mode 100644 index 00000000..b31e92dc --- /dev/null +++ b/app/tests/tap-dance/3e-hold-int-seq/native_posix.keymap @@ -0,0 +1,13 @@ +#include +#include +#include +#include "../behavior_keymap.dtsi" + +&kscan { + events = < + ZMK_MOCK_PRESS(0,0,400) + ZMK_MOCK_PRESS(0,1,10) + ZMK_MOCK_RELEASE(0,0,10) + ZMK_MOCK_RELEASE(0,1,10) + >; +}; \ No newline at end of file diff --git a/app/tests/tap-dance/3f-hold-int-after/events.patterns b/app/tests/tap-dance/3f-hold-int-after/events.patterns new file mode 100644 index 00000000..1768fc21 --- /dev/null +++ b/app/tests/tap-dance/3f-hold-int-after/events.patterns @@ -0,0 +1,2 @@ +s/.*hid_listener_keycode/kp/p +s/.*on_tap_dance_binding/td_binding/p \ No newline at end of file diff --git a/app/tests/tap-dance/3f-hold-int-after/keycode_events.snapshot b/app/tests/tap-dance/3f-hold-int-after/keycode_events.snapshot new file mode 100644 index 00000000..044018e0 --- /dev/null +++ b/app/tests/tap-dance/3f-hold-int-after/keycode_events.snapshot @@ -0,0 +1,10 @@ +td_binding_pressed: 0 created new tap dance +td_binding_pressed: 0 tap dance pressed +kp_pressed: usage_page 0x07 keycode 0xe1 implicit_mods 0x00 explicit_mods 0x00 +td_binding_released: 0 tap dance keybind released +kp_released: usage_page 0x07 keycode 0xe1 implicit_mods 0x00 explicit_mods 0x00 +td_binding_pressed: 1 created new tap dance +td_binding_pressed: 1 tap dance pressed +kp_pressed: usage_page 0x07 keycode 0x16 implicit_mods 0x00 explicit_mods 0x00 +td_binding_released: 1 tap dance keybind released +kp_released: usage_page 0x07 keycode 0x16 implicit_mods 0x00 explicit_mods 0x00 diff --git a/app/tests/tap-dance/3f-hold-int-after/native_posix.keymap b/app/tests/tap-dance/3f-hold-int-after/native_posix.keymap new file mode 100644 index 00000000..6397fbb3 --- /dev/null +++ b/app/tests/tap-dance/3f-hold-int-after/native_posix.keymap @@ -0,0 +1,13 @@ +#include +#include +#include +#include "../behavior_keymap.dtsi" + +&kscan { + events = < + ZMK_MOCK_PRESS(0,0,400) + ZMK_MOCK_RELEASE(0,0,10) + ZMK_MOCK_PRESS(0,1,10) + ZMK_MOCK_RELEASE(0,1,10) + >; +}; \ No newline at end of file diff --git a/app/tests/tap-dance/4a-single/events.patterns b/app/tests/tap-dance/4a-single/events.patterns new file mode 100644 index 00000000..1768fc21 --- /dev/null +++ b/app/tests/tap-dance/4a-single/events.patterns @@ -0,0 +1,2 @@ +s/.*hid_listener_keycode/kp/p +s/.*on_tap_dance_binding/td_binding/p \ No newline at end of file diff --git a/app/tests/tap-dance/4a-single/keycode_events.snapshot b/app/tests/tap-dance/4a-single/keycode_events.snapshot new file mode 100644 index 00000000..6d60e842 --- /dev/null +++ b/app/tests/tap-dance/4a-single/keycode_events.snapshot @@ -0,0 +1,5 @@ +td_binding_pressed: 1 created new tap dance +td_binding_pressed: 1 tap dance pressed +kp_pressed: usage_page 0x07 keycode 0x16 implicit_mods 0x00 explicit_mods 0x00 +td_binding_released: 1 tap dance keybind released +kp_released: usage_page 0x07 keycode 0x16 implicit_mods 0x00 explicit_mods 0x00 diff --git a/app/tests/tap-dance/4a-single/native_posix.keymap b/app/tests/tap-dance/4a-single/native_posix.keymap new file mode 100644 index 00000000..348a6827 --- /dev/null +++ b/app/tests/tap-dance/4a-single/native_posix.keymap @@ -0,0 +1,11 @@ +#include +#include +#include +#include "../behavior_keymap.dtsi" + +&kscan { + events = < + ZMK_MOCK_PRESS(0,1,10) + ZMK_MOCK_RELEASE(0,1,10) + >; +}; \ No newline at end of file diff --git a/app/tests/tap-dance/5a-tdint-mid/events.patterns b/app/tests/tap-dance/5a-tdint-mid/events.patterns new file mode 100644 index 00000000..1768fc21 --- /dev/null +++ b/app/tests/tap-dance/5a-tdint-mid/events.patterns @@ -0,0 +1,2 @@ +s/.*hid_listener_keycode/kp/p +s/.*on_tap_dance_binding/td_binding/p \ No newline at end of file diff --git a/app/tests/tap-dance/5a-tdint-mid/keycode_events.snapshot b/app/tests/tap-dance/5a-tdint-mid/keycode_events.snapshot new file mode 100644 index 00000000..301dc914 --- /dev/null +++ b/app/tests/tap-dance/5a-tdint-mid/keycode_events.snapshot @@ -0,0 +1,10 @@ +td_binding_pressed: 2 created new tap dance +td_binding_pressed: 2 tap dance pressed +kp_pressed: usage_page 0x07 keycode 0x1e implicit_mods 0x00 explicit_mods 0x00 +td_binding_pressed: 3 created new tap dance +td_binding_pressed: 3 tap dance pressed +td_binding_released: 3 tap dance keybind released +td_binding_released: 2 tap dance keybind released +kp_released: usage_page 0x07 keycode 0x1e implicit_mods 0x00 explicit_mods 0x00 +kp_pressed: usage_page 0x07 keycode 0x04 implicit_mods 0x00 explicit_mods 0x00 +kp_released: usage_page 0x07 keycode 0x04 implicit_mods 0x00 explicit_mods 0x00 diff --git a/app/tests/tap-dance/5a-tdint-mid/native_posix.keymap b/app/tests/tap-dance/5a-tdint-mid/native_posix.keymap new file mode 100644 index 00000000..2188fd02 --- /dev/null +++ b/app/tests/tap-dance/5a-tdint-mid/native_posix.keymap @@ -0,0 +1,13 @@ +#include +#include +#include +#include "../behavior_keymap.dtsi" + +&kscan { + events = < + ZMK_MOCK_PRESS(1,0,10) + ZMK_MOCK_PRESS(1,1,10) + ZMK_MOCK_RELEASE(1,1,10) + ZMK_MOCK_RELEASE(1,0,200) + >; +}; \ No newline at end of file diff --git a/app/tests/tap-dance/5b-tdint-seq/events.patterns b/app/tests/tap-dance/5b-tdint-seq/events.patterns new file mode 100644 index 00000000..1768fc21 --- /dev/null +++ b/app/tests/tap-dance/5b-tdint-seq/events.patterns @@ -0,0 +1,2 @@ +s/.*hid_listener_keycode/kp/p +s/.*on_tap_dance_binding/td_binding/p \ No newline at end of file diff --git a/app/tests/tap-dance/5b-tdint-seq/keycode_events.snapshot b/app/tests/tap-dance/5b-tdint-seq/keycode_events.snapshot new file mode 100644 index 00000000..567ec079 --- /dev/null +++ b/app/tests/tap-dance/5b-tdint-seq/keycode_events.snapshot @@ -0,0 +1,10 @@ +td_binding_pressed: 2 created new tap dance +td_binding_pressed: 2 tap dance pressed +kp_pressed: usage_page 0x07 keycode 0x1e implicit_mods 0x00 explicit_mods 0x00 +td_binding_pressed: 3 created new tap dance +td_binding_pressed: 3 tap dance pressed +td_binding_released: 2 tap dance keybind released +kp_released: usage_page 0x07 keycode 0x1e implicit_mods 0x00 explicit_mods 0x00 +td_binding_released: 3 tap dance keybind released +kp_pressed: usage_page 0x07 keycode 0x04 implicit_mods 0x00 explicit_mods 0x00 +kp_released: usage_page 0x07 keycode 0x04 implicit_mods 0x00 explicit_mods 0x00 diff --git a/app/tests/tap-dance/5b-tdint-seq/native_posix.keymap b/app/tests/tap-dance/5b-tdint-seq/native_posix.keymap new file mode 100644 index 00000000..320b7199 --- /dev/null +++ b/app/tests/tap-dance/5b-tdint-seq/native_posix.keymap @@ -0,0 +1,13 @@ +#include +#include +#include +#include "../behavior_keymap.dtsi" + +&kscan { + events = < + ZMK_MOCK_PRESS(1,0,10) + ZMK_MOCK_PRESS(1,1,10) + ZMK_MOCK_RELEASE(1,0,10) + ZMK_MOCK_RELEASE(1,1,200) + >; +}; \ No newline at end of file diff --git a/app/tests/tap-dance/5c-tdint-after/events.patterns b/app/tests/tap-dance/5c-tdint-after/events.patterns new file mode 100644 index 00000000..1768fc21 --- /dev/null +++ b/app/tests/tap-dance/5c-tdint-after/events.patterns @@ -0,0 +1,2 @@ +s/.*hid_listener_keycode/kp/p +s/.*on_tap_dance_binding/td_binding/p \ No newline at end of file diff --git a/app/tests/tap-dance/5c-tdint-after/keycode_events.snapshot b/app/tests/tap-dance/5c-tdint-after/keycode_events.snapshot new file mode 100644 index 00000000..cc1da902 --- /dev/null +++ b/app/tests/tap-dance/5c-tdint-after/keycode_events.snapshot @@ -0,0 +1,10 @@ +td_binding_pressed: 2 created new tap dance +td_binding_pressed: 2 tap dance pressed +td_binding_released: 2 tap dance keybind released +kp_pressed: usage_page 0x07 keycode 0x1e implicit_mods 0x00 explicit_mods 0x00 +kp_released: usage_page 0x07 keycode 0x1e implicit_mods 0x00 explicit_mods 0x00 +td_binding_pressed: 3 created new tap dance +td_binding_pressed: 3 tap dance pressed +td_binding_released: 3 tap dance keybind released +kp_pressed: usage_page 0x07 keycode 0x04 implicit_mods 0x00 explicit_mods 0x00 +kp_released: usage_page 0x07 keycode 0x04 implicit_mods 0x00 explicit_mods 0x00 diff --git a/app/tests/tap-dance/5c-tdint-after/native_posix.keymap b/app/tests/tap-dance/5c-tdint-after/native_posix.keymap new file mode 100644 index 00000000..17e538bd --- /dev/null +++ b/app/tests/tap-dance/5c-tdint-after/native_posix.keymap @@ -0,0 +1,13 @@ +#include +#include +#include +#include "../behavior_keymap.dtsi" + +&kscan { + events = < + ZMK_MOCK_PRESS(1,0,10) + ZMK_MOCK_RELEASE(1,0,10) + ZMK_MOCK_PRESS(1,1,10) + ZMK_MOCK_RELEASE(1,1,200) + >; +}; \ No newline at end of file diff --git a/app/tests/tap-dance/5d-tdint-multiple/events.patterns b/app/tests/tap-dance/5d-tdint-multiple/events.patterns new file mode 100644 index 00000000..1768fc21 --- /dev/null +++ b/app/tests/tap-dance/5d-tdint-multiple/events.patterns @@ -0,0 +1,2 @@ +s/.*hid_listener_keycode/kp/p +s/.*on_tap_dance_binding/td_binding/p \ No newline at end of file diff --git a/app/tests/tap-dance/5d-tdint-multiple/keycode_events.snapshot b/app/tests/tap-dance/5d-tdint-multiple/keycode_events.snapshot new file mode 100644 index 00000000..afb32824 --- /dev/null +++ b/app/tests/tap-dance/5d-tdint-multiple/keycode_events.snapshot @@ -0,0 +1,15 @@ +td_binding_pressed: 2 created new tap dance +td_binding_pressed: 2 tap dance pressed +td_binding_released: 2 tap dance keybind released +kp_pressed: usage_page 0x07 keycode 0x1e implicit_mods 0x00 explicit_mods 0x00 +kp_released: usage_page 0x07 keycode 0x1e implicit_mods 0x00 explicit_mods 0x00 +td_binding_pressed: 3 created new tap dance +td_binding_pressed: 3 tap dance pressed +td_binding_released: 3 tap dance keybind released +kp_pressed: usage_page 0x07 keycode 0x04 implicit_mods 0x00 explicit_mods 0x00 +kp_released: usage_page 0x07 keycode 0x04 implicit_mods 0x00 explicit_mods 0x00 +td_binding_pressed: 2 created new tap dance +td_binding_pressed: 2 tap dance pressed +td_binding_released: 2 tap dance keybind released +kp_pressed: usage_page 0x07 keycode 0x1e implicit_mods 0x00 explicit_mods 0x00 +kp_released: usage_page 0x07 keycode 0x1e implicit_mods 0x00 explicit_mods 0x00 diff --git a/app/tests/tap-dance/5d-tdint-multiple/native_posix.keymap b/app/tests/tap-dance/5d-tdint-multiple/native_posix.keymap new file mode 100644 index 00000000..150f6d05 --- /dev/null +++ b/app/tests/tap-dance/5d-tdint-multiple/native_posix.keymap @@ -0,0 +1,15 @@ +#include +#include +#include +#include "../behavior_keymap.dtsi" + +&kscan { + events = < + ZMK_MOCK_PRESS(1,0,10) + ZMK_MOCK_RELEASE(1,0,10) + ZMK_MOCK_PRESS(1,1,10) + ZMK_MOCK_RELEASE(1,1,10) + ZMK_MOCK_PRESS(1,0,10) + ZMK_MOCK_RELEASE(1,0,200) + >; +}; \ No newline at end of file diff --git a/app/tests/tap-dance/behavior_keymap.dtsi b/app/tests/tap-dance/behavior_keymap.dtsi new file mode 100644 index 00000000..5e95cd50 --- /dev/null +++ b/app/tests/tap-dance/behavior_keymap.dtsi @@ -0,0 +1,60 @@ +#include +#include +#include + +/ { + behaviors { + ht: hold_tap { + compatible = "zmk,behavior-hold-tap"; + label = "HOLD_TAP"; + #binding-cells = <2>; + tapping-term-ms = <200>; + quick_tap_ms = <0>; + flavor = "tap-preferred"; + bindings = <&kp>, <&kp>; + }; + + tdm: tap_dance_mixed { + compatible = "zmk,behavior-tap-dance"; + label = "TAP_DANCE_MOD"; + #binding-cells = <0>; + tapping-term-ms = <200>; + bindings = <&ht LSHIFT A>, <&ht LALT B>, <&ht LGUI C>; + }; + + tdb: tap_dance_basic { + compatible = "zmk,behavior-tap-dance"; + label = "TAP_DANCE_BASIC"; + #binding-cells = <0>; + tapping-term-ms = <200>; + bindings = <&kp N1>, <&kp N2>, <&kp N3>; + }; + + td2: tap_dance_basic_2 { + compatible = "zmk,behavior-tap-dance"; + label = "TAP_DANCE_BASIC_2"; + #binding-cells = <0>; + tapping-term-ms = <200>; + bindings = <&kp A>, <&kp B>, <&kp C>; + }; + + tds: tap_dance_single { + compatible = "zmk,behavior-tap-dance"; + label = "TAP_DANCE_SINGlE"; + #binding-cells = <0>; + tapping-term-ms = <200>; + bindings = <&kp S>; + }; + }; + + keymap { + compatible = "zmk,keymap"; + label ="Default keymap"; + + default_layer { + bindings = < + &tdm &tds + &tdb &td2>; + }; + }; +}; diff --git a/docs/docs/assets/tap-dance/timing_diagram.svg b/docs/docs/assets/tap-dance/timing_diagram.svg new file mode 100644 index 00000000..ab02dcaf --- /dev/null +++ b/docs/docs/assets/tap-dance/timing_diagram.svg @@ -0,0 +1,83 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/docs/docs/behaviors/tap-dance.md b/docs/docs/behaviors/tap-dance.md new file mode 100644 index 00000000..af49ca3c --- /dev/null +++ b/docs/docs/behaviors/tap-dance.md @@ -0,0 +1,76 @@ +--- +title: Tap-Dance Behavior +sidebar_label: Tap-Dance +--- + +## Summary + +A tap-dance key invokes a different behavior (e.g. `kp`) corresponding +to how many times it is pressed. For example, you could configure a +tap-dance key that acts as `LSHIFT` if tapped once, or Caps _Lock_ if tapped twice. +The expandability of the number of [`bindings`](#bindings) attached to a +particular tap-dance is a great way to add more functionality to a single key, +especially for keyboards with a limited number of keys. +Tap-dances are completely custom, so for every unique tap-dance key, +a new tap-dance must be defined in your keymap's `behaviors`. + +Tap-dances are designed to resolve immediately when interrupted by another keypress. +Meaning, when a keybind is pressed other than any active tap-dances, +the tap-dance will activate according to the current value of its +counter before the interrupting keybind is registered. + +### Configuration + +#### `tapping-term-ms` + +Defines the maximum elapsed time after the last tap-dance keybind press +before a binding is selected from [`bindings`](#bindings). +Default value is `200`ms. + +#### `bindings` + +An array of one or more keybinds. This list can include [any ZMK keycode](../codes/) and bindings for ZMK behaviors. + +#### Example Usage + +This example configures a tap-dance named `td0` that outputs the number of times it is pressed from 1-3. + +``` +#include +#include + +/ { + behaviors { + td0: tap_dance_0 { + compatible = "zmk,behavior-tap-dance"; + label = "TAP_DANCE_0"; + #binding-cells = <0>; + tapping-term-ms = <200>; + bindings = <&kp N1>, <&kp N2>, <&kp N3>; + }; + }; + + keymap { + compatible = "zmk,keymap"; + + default_layer { + bindings = < + &td0 + >; + }; + }; +}; +``` + +The following image describes the behavior of this particular tap-dance. + +![Timing Diagram](../assets/tap-dance/timing_diagram.svg) + +:::note +Alphanumeric [`key press`](key-press.md) bindings, like those used for `td0`, +will release as soon as an interrupting key press occurs. +For instance, if a modifier key like `LSHIFT` were to replace the `N1` +binding in the last example above, it would remain pressed until `td0`'s +binding is released and the output would instead be `J`. Any following +alphanumeric key presses would be capitalized as long as `td0` is held down. +::: diff --git a/docs/sidebars.js b/docs/sidebars.js index 647399a7..7db3e055 100644 --- a/docs/sidebars.js +++ b/docs/sidebars.js @@ -28,6 +28,7 @@ module.exports = { "behaviors/mod-morph", "behaviors/sticky-key", "behaviors/sticky-layer", + "behaviors/tap-dance", "behaviors/caps-word", "behaviors/key-repeat", "behaviors/reset", From 58c7c0ee0c252be7b1d6b03f432c17cde34e9908 Mon Sep 17 00:00:00 2001 From: Kurtis Lew Date: Wed, 16 Mar 2022 08:06:55 -0700 Subject: [PATCH 8/8] feat(docs): Add tap-dance to feature matrix --- docs/docs/intro.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/docs/docs/intro.md b/docs/docs/intro.md index b8a6316c..39b53245 100644 --- a/docs/docs/intro.md +++ b/docs/docs/intro.md @@ -23,6 +23,7 @@ ZMK is currently missing some features found in other popular firmware. This tab | Split Keyboard Support | ✅ | ✅ | ✅ | | [Keymaps and Layers](behaviors/layers.md) | ✅ | ✅ | ✅ | | [Hold-Tap](behaviors/hold-tap.md) (which includes [Mod-Tap](behaviors/mod-tap.md) and [Layer-Tap](behaviors/layers.md/#layer-tap)) | ✅ | ✅ | ✅ | +| [Tap-Dance](behaviors/tap-dance.md) | ✅ | ✅[^3] | ✅ | | [Keyboard Codes](codes/index.mdx#keyboard) | ✅ | ✅ | ✅ | | [Media](codes/index.mdx#media-controls) & [Consumer](codes/index.mdx#consumer-controls) Codes | ✅ | ✅ | ✅ | | [Encoders](features/encoders.md)[^1] | ✅ | ✅ | ✅ | @@ -42,6 +43,7 @@ ZMK is currently missing some features found in other popular firmware. This tab | AVR/8 Bit | | | ✅ | | [Wide Range of ARM Chips Supported](https://docs.zephyrproject.org/latest/boards/index.html) | ✅ | | | +[^3]: Tap-Dances are limited to single and double-tap on BlueMicro [^2]: Encoders are not currently supported on peripheral side splits. [^1]: OLEDs are currently proof of concept in ZMK.