Merge branch 'zmkfirmware:main' into two_percent_milk

This commit is contained in:
Aaron Nunley 2021-06-22 21:27:11 -07:00 committed by GitHub
commit 7af570cdb2
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
233 changed files with 18515 additions and 18774 deletions

View file

@ -23,7 +23,7 @@ jobs:
runs-on: ubuntu-latest runs-on: ubuntu-latest
steps: steps:
- uses: actions/checkout@v2 - uses: actions/checkout@v2
- uses: DoozyX/clang-format-lint-action@v0.11 - uses: DoozyX/clang-format-lint-action@v0.12
with: with:
source: "./app" source: "./app"
extensions: "h,c" extensions: "h,c"

12
.pre-commit-config.yaml Normal file
View file

@ -0,0 +1,12 @@
fail_fast: false
repos:
- repo: https://github.com/pocc/pre-commit-hooks
rev: v1.1.1
hooks:
- id: clang-format
args:
- -i
- repo: https://github.com/pre-commit/mirrors-prettier
rev: v2.2.1
hooks:
- id: prettier

View file

@ -60,7 +60,7 @@ representative at an online or offline event.
Instances of abusive, harassing, or otherwise unacceptable behavior may be Instances of abusive, harassing, or otherwise unacceptable behavior may be
reported to the community leaders responsible for enforcement at reported to the community leaders responsible for enforcement at
conduct@zmkfirmware.dev. conduct@zmk.dev.
All complaints will be reviewed and investigated promptly and fairly. All complaints will be reviewed and investigated promptly and fairly.
All community leaders are obligated to respect the privacy and security of the All community leaders are obligated to respect the privacy and security of the

View file

@ -7,7 +7,7 @@ you have any questions, please come join us on the
## Code of Conduct ## Code of Conduct
All community members are expected to abide by the [Code of Conduct][code-of-conduct]. All community members are expected to abide by the [Code of Conduct][code-of-conduct].
For any and all conduct inquiries or concerns, please contact conduct@zmkfirmware.dev. For any and all conduct inquiries or concerns, please contact conduct@zmk.dev.
[code-of-conduct]: https://github.com/zmkfirmware/zmk/blob/main/CODE_OF_CONDUCT.md [code-of-conduct]: https://github.com/zmkfirmware/zmk/blob/main/CODE_OF_CONDUCT.md
@ -35,8 +35,8 @@ ZMK project.
### Before Submitting a Report ### Before Submitting a Report
- Review the [Frequently Asked Questions](https://zmkfirmware.dev/docs/faq). - Review the [Frequently Asked Questions](https://zmk.dev/docs/faq).
- Check the [Troubleshooting Guide](https://zmkfirmware.dev/docs/troubleshooting) for answers. - Check the [Troubleshooting Guide](https://zmk.dev/docs/troubleshooting) for answers.
- Search the [open issues](https://github.com/zmkfirmware/zmk/issues) for an existing report that - Search the [open issues](https://github.com/zmkfirmware/zmk/issues) for an existing report that
matches your problem. matches your problem.
@ -47,10 +47,10 @@ To open a report:
- Head to https://github.com/zmkfirmware/zmk/issues/new - Head to https://github.com/zmkfirmware/zmk/issues/new
- Provide an accurate summary of the issue in the title. - Provide an accurate summary of the issue in the title.
- Provide as much detail as you can about the issue including: - Provide as much detail as you can about the issue including:
- What [board/shield](https://zmkfirmware.dev/docs/faq#what-is-a-board) you are using. - What [board/shield](https://zmk.dev/docs/faq#what-is-a-board) you are using.
- A link to the user repository, if you used it to build your firmware. - A link to the user repository, if you used it to build your firmware.
- Exact steps to reproduce the problem. - Exact steps to reproduce the problem.
- Any relevant screenshots or [logs](https://zmkfirmware.dev/docs/dev-guide-usb-logging) - Any relevant screenshots or [logs](https://zmk.dev/docs/dev-guide-usb-logging)
## Testing ## Testing
@ -81,12 +81,17 @@ documentation to areas not currently covered are greatly appreciated.
- To get started, from the `docs/` directory, run `npm ci` and then `npm start`. - To get started, from the `docs/` directory, run `npm ci` and then `npm start`.
- Enhancements should be submitted as pull requests to the `main` branch of ZMK. - Enhancements should be submitted as pull requests to the `main` branch of ZMK.
### Formatting
ZMK uses `prettier` to format documentation files. You can run prettier with `npm run prettier:format`.
You can setup git to run prettier automatically when you commit by installing the pre-commit hooks: `pip3 install pre-commit`, `pre-commit install`.
## Code Contributions ## Code Contributions
### Development Setup ### Development Setup
To get your development environment setup going, start at the To get your development environment setup going, start at the
[basic setup](https://zmkfirmware.dev/docs/dev-setup) docs, and make sure you can build and flash [basic setup](https://zmk.dev/docs/dev-setup) docs, and make sure you can build and flash
your own locally built firmware. your own locally built firmware.
### Formatting ### Formatting
@ -95,6 +100,8 @@ ZMK uses `clang-format` to ensure consist formatting for our source code. Before
changes, make sure you've manually run `clang-format`, or have your IDE configured to auto-format changes, make sure you've manually run `clang-format`, or have your IDE configured to auto-format
on save. on save.
You can setup git to run `clang-format` automatically when you commit by installing the pre-commit hooks: `pip3 install pre-commit`, `pre-commit install`.
### Commit Messages ### Commit Messages
The ZMK project is working towards, but not yet enforcing, the use of The ZMK project is working towards, but not yet enforcing, the use of
@ -117,4 +124,4 @@ When opening a pull request with your changes, please:
- Requested testing by reviewers or testers. - Requested testing by reviewers or testers.
- Screenshots or logs that support understanding the change. - Screenshots or logs that support understanding the change.
[discord-invite]: https://zmkfirmware.dev/community/discord/invite [discord-invite]: https://zmk.dev/community/discord/invite

View file

@ -1,13 +1,13 @@
# Zephyr™ Mechanical Keyboard (ZMK) Firmware # Zephyr™ Mechanical Keyboard (ZMK) Firmware
[![Discord](https://img.shields.io/discord/719497620560543766)](https://zmkfirmware.dev/community/discord/invite) [![Discord](https://img.shields.io/discord/719497620560543766)](https://zmk.dev/community/discord/invite)
[![Build](https://github.com/zmkfirmware/zmk/workflows/Build/badge.svg)](https://github.com/zmkfirmware/zmk/actions) [![Build](https://github.com/zmkfirmware/zmk/workflows/Build/badge.svg)](https://github.com/zmkfirmware/zmk/actions)
[![Contributor Covenant](https://img.shields.io/badge/Contributor%20Covenant-v2.0%20adopted-ff69b4.svg)](CODE_OF_CONDUCT.md) [![Contributor Covenant](https://img.shields.io/badge/Contributor%20Covenant-v2.0%20adopted-ff69b4.svg)](CODE_OF_CONDUCT.md)
[ZMK Firmware](https://zmkfirmware.dev/) is an open source (MIT) keyboard firmware built on the [Zephyr™ Project](https://www.zephyrproject.org/) Real Time Operating System (RTOS). ZMK's goal is to provide a modern, wireless, and powerful firmware free of licensing issues. [ZMK Firmware](https://zmk.dev/) is an open source (MIT) keyboard firmware built on the [Zephyr™ Project](https://www.zephyrproject.org/) Real Time Operating System (RTOS). ZMK's goal is to provide a modern, wireless, and powerful firmware free of licensing issues.
Check out the website to learn more: https://zmkfirmware.dev/ Check out the website to learn more: https://zmk.dev/
You can also come join our [ZMK Discord Server](https://zmkfirmware.dev/community/discord/invite) You can also come join our [ZMK Discord Server](https://zmk.dev/community/discord/invite)
To review features, check out the [feature overview](https://zmkfirmware.dev/docs/). ZMK is under active development, and new features are listed with the [enhancement label](https://github.com/zmkfirmware/zmk/issues?q=is%3Aissue+is%3Aopen+label%3Aenhancement) in GitHub. Please feel free to add 👍 to the issue description of any requests to upvote the feature. To review features, check out the [feature overview](https://zmk.dev/docs/). ZMK is under active development, and new features are listed with the [enhancement label](https://github.com/zmkfirmware/zmk/issues?q=is%3Aissue+is%3Aopen+label%3Aenhancement) in GitHub. Please feel free to add 👍 to the issue description of any requests to upvote the feature.

View file

@ -337,11 +337,9 @@ config ZMK_KSCAN_EVENT_QUEUE_SIZE
config ZMK_KSCAN_MOCK_DRIVER config ZMK_KSCAN_MOCK_DRIVER
bool "Enable mock kscan driver to simulate key presses" bool "Enable mock kscan driver to simulate key presses"
default n
config ZMK_KSCAN_COMPOSITE_DRIVER config ZMK_KSCAN_COMPOSITE_DRIVER
bool "Enable composite kscan driver to combine kscan devices" bool "Enable composite kscan driver to combine kscan devices"
default n
#KSCAN Settings #KSCAN Settings
endmenu endmenu
@ -421,6 +419,9 @@ config ZMK_WPM
bool "Calculate WPM" bool "Calculate WPM"
default n default n
config SENSOR
default y
module = ZMK module = ZMK
module-str = zmk module-str = zmk
source "subsys/logging/Kconfig.template.log_config" source "subsys/logging/Kconfig.template.log_config"

View file

@ -42,7 +42,7 @@
, <7 0 &gpio0 24 0> /* D6/A7 D7*/ , <7 0 &gpio0 24 0> /* D6/A7 D7*/
, <8 0 &gpio0 10 0> /* D8/A8 B4*/ , <8 0 &gpio0 10 0> /* D8/A8 B4*/
, <9 0 &gpio1 6 0> /* D9/A9 B5*/ , <9 0 &gpio1 6 0> /* D9/A9 B5*/
, <10 0 &gpio1 13 0> /* D10/A10 B6*/ , <10 0 &gpio1 11 0> /* D10/A10 B6*/
; ;
}; };
}; };

View file

@ -21,7 +21,7 @@
leds { leds {
compatible = "gpio-leds"; compatible = "gpio-leds";
blue_led: led_0 { blue_led: led_0 {
gpios = <&gpio0 42 GPIO_ACTIVE_HIGH>; gpios = <&gpio1 10 GPIO_ACTIVE_HIGH>;
label = "Blue LED"; label = "Blue LED";
}; };
}; };
@ -29,6 +29,7 @@
ext-power { ext-power {
compatible = "zmk,ext-power-generic"; compatible = "zmk,ext-power-generic";
label = "EXT_POWER"; label = "EXT_POWER";
init-delay-ms = <20>;
control-gpios = <&gpio0 12 GPIO_ACTIVE_HIGH>; control-gpios = <&gpio0 12 GPIO_ACTIVE_HIGH>;
}; };

View file

@ -0,0 +1,8 @@
set_property(GLOBAL APPEND PROPERTY extra_post_build_commands
COMMAND ${PYTHON_EXECUTABLE} ${ZEPHYR_BASE}/../tools/uf2/utils/uf2conv.py
-c
-b 0x1000
-f 0xADA52840
-o ${PROJECT_BINARY_DIR}/${CONFIG_KERNEL_BIN_NAME}.uf2
${PROJECT_BINARY_DIR}/${CONFIG_KERNEL_BIN_NAME}.bin
)

View file

@ -0,0 +1,8 @@
# Copyright (c) 2021 Nick Winans
# SPDX-License-Identifier: MIT
config BOARD_ENABLE_DCDC
bool "Enable DCDC mode"
select SOC_DCDC_NRF52X
default y
depends on BOARD_NICE60

View file

@ -0,0 +1,6 @@
# Copyright (c) 2021 Nick Winans
# SPDX-License-Identifier: MIT
config BOARD_NICE60
bool "nice!60"
depends on SOC_NRF52840_QIAA

View file

@ -0,0 +1,31 @@
# Copyright (c) 2021 Nick Winans
# SPDX-License-Identifier: MIT
if BOARD_NICE60
config ZMK_KEYBOARD_NAME
default "nice!60"
if USB
config USB_NRFX
default y
config USB_DEVICE_STACK
default y
endif # USB
config BT_CTLR
default BT
config ZMK_BLE
default y
config ZMK_USB
default y
config ZMK_BATTERY_VOLTAGE_DIVIDER
default y
endif # BOARD_NICE60

View file

@ -0,0 +1,9 @@
# nice!60
![nice!60](https://i.imgur.com/0YWv5PE.png)
The nice!60 is a hotswap 60% made by Nice Keyboards. https://nicekeyboards.com/nice-60
## Building nice!60 ZMK firmware
```
west build -p -b nice60
```

View file

@ -0,0 +1,7 @@
# Copyright (c) 2021 Nick Winans
# SPDX-License-Identifier: MIT
set(OPENOCD_NRF5_SUBFAMILY nrf52)
board_runner_args(nrfjprog "--nrf-family=NRF52" "--softreset")
include(${ZEPHYR_BASE}/boards/common/nrfjprog.board.cmake)
include(${ZEPHYR_BASE}/boards/common/openocd-nrf5.board.cmake)

View file

@ -0,0 +1,172 @@
/*
* Copyright (c) 2021 Nick Winans
*
* SPDX-License-Identifier: MIT
*/
/dts-v1/;
#include <nordic/nrf52840_qiaa.dtsi>
#include <dt-bindings/zmk/matrix_transform.h>
/ {
model = "nice!60";
compatible = "nice,60";
chosen {
zephyr,code-partition = &code_partition;
zephyr,sram = &sram0;
zephyr,flash = &flash0;
zmk,kscan = &kscan0;
zmk,matrix_transform = &default_transform;
zmk,underglow = &led_strip;
};
default_transform: keymap_transform_0 {
compatible = "zmk,matrix-transform";
columns = <14>;
rows = <5>;
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(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,1) 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,13)
RC(4,0) RC(4,1) RC(4,2) RC(4,5) RC(4,9) RC(4,10) RC(4,11) RC(4,13)
>;
};
kscan0: kscan {
compatible = "zmk,kscan-gpio-matrix";
label = "KSCAN";
diode-direction = "col2row";
row-gpios
= <&gpio1 14 (GPIO_ACTIVE_HIGH | GPIO_PULL_DOWN)>
, <&gpio1 13 (GPIO_ACTIVE_HIGH | GPIO_PULL_DOWN)>
, <&gpio1 12 (GPIO_ACTIVE_HIGH | GPIO_PULL_DOWN)>
, <&gpio1 11 (GPIO_ACTIVE_HIGH | GPIO_PULL_DOWN)>
, <&gpio1 10 (GPIO_ACTIVE_HIGH | GPIO_PULL_DOWN)>
;
col-gpios
= <&gpio1 15 GPIO_ACTIVE_HIGH>
, <&gpio0 29 GPIO_ACTIVE_HIGH>
, <&gpio0 31 GPIO_ACTIVE_HIGH>
, <&gpio0 30 GPIO_ACTIVE_HIGH>
, <&gpio0 28 GPIO_ACTIVE_HIGH>
, <&gpio0 2 GPIO_ACTIVE_HIGH>
, <&gpio0 3 GPIO_ACTIVE_HIGH>
, <&gpio1 3 GPIO_ACTIVE_HIGH>
, <&gpio1 7 GPIO_ACTIVE_HIGH>
, <&gpio1 4 GPIO_ACTIVE_HIGH>
, <&gpio1 6 GPIO_ACTIVE_HIGH>
, <&gpio1 5 GPIO_ACTIVE_HIGH>
, <&gpio1 1 GPIO_ACTIVE_HIGH>
, <&gpio1 2 GPIO_ACTIVE_HIGH>
;
};
leds {
compatible = "gpio-leds";
blue_led: led_0 {
gpios = <&gpio0 26 GPIO_ACTIVE_HIGH>;
label = "Blue LED";
};
};
ext-power {
compatible = "zmk,ext-power-generic";
label = "EXT_POWER";
control-gpios = <&gpio0 5 GPIO_ACTIVE_LOW>;
};
vbatt {
compatible = "zmk,battery-voltage-divider";
label = "BATTERY";
io-channels = <&adc 2>;
output-ohms = <2000000>;
full-ohms = <(2000000 + 806000)>;
};
};
&adc {
status = "okay";
};
&gpiote {
status = "okay";
};
&gpio0 {
status = "okay";
};
&gpio1 {
status = "okay";
};
&spi0 {
compatible = "nordic,nrf-spim";
/* Cannot be used together with i2c0. */
status = "okay";
sck-pin = <12>;
mosi-pin = <27>;
miso-pin = <13>;
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 = <12>; /* LED strip length */
spi-one-frame = <0x70>;
spi-zero-frame = <0x40>;
};
};
&usbd {
status = "okay";
};
&flash0 {
/*
* For more information, see:
* http://docs.zephyrproject.org/latest/devices/dts/flash_partitions.html
*/
partitions {
compatible = "fixed-partitions";
#address-cells = <1>;
#size-cells = <1>;
sd_partition: partition@0 {
label = "mbr";
reg = <0x00000000 0x00001000>;
};
code_partition: partition@1000 {
label = "code_partition";
reg = <0x00001000 0x000d3000>;
};
/*
* The flash starting at 0x000d4000 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@d4000 {
label = "storage";
reg = <0x000d4000 0x00020000>;
};
boot_partition: partition@f4000 {
label = "adafruit_boot";
reg = <0x000f4000 0x0000c000>;
};
};
};

View file

@ -0,0 +1,50 @@
/*
* Copyright (c) 2021 Nick Winans
*
* SPDX-License-Identifier: MIT
*/
#include <behaviors.dtsi>
#include <dt-bindings/zmk/keys.h>
#include <dt-bindings/zmk/rgb.h>
#include <dt-bindings/zmk/bt.h>
/ {
keymap {
compatible = "zmk,keymap";
default_layer {
// ------------------------------------------------------------------------------------------
// | ESC | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 0 | - | = | BKSP |
// | TAB | Q | W | E | R | T | Y | U | I | O | P | [ | ] | "|" |
// | CAPS | A | S | D | F | G | H | J | K | L | ; | ' | ENTER |
// | SHIFT | Z | X | C | V | B | N | M | , | . | / | SHIFT |
// | CTL | WIN | ALT | SPACE | ALT | WIN | MO(1) | CTL |
// ------------------------------------------------------------------------------------------
bindings = <
&gresc &kp N1 &kp N2 &kp N3 &kp N4 &kp N5 &kp N6 &kp N7 &kp N8 &kp N9 &kp N0 &kp MINUS &kp EQUAL &kp BSPC
&kp TAB &kp Q &kp W &kp E &kp R &kp T &kp Y &kp U &kp I &kp O &kp P &kp LBKT &kp RBKT &kp BSLH
&kp CLCK &kp A &kp S &kp D &kp F &kp G &kp H &kp J &kp K &kp L &kp SEMI &kp SQT &kp RET
&kp LSHFT &kp Z &kp X &kp C &kp V &kp B &kp N &kp M &kp COMMA &kp DOT &kp FSLH &kp RSHFT
&kp LCTRL &kp LGUI &kp LALT &kp SPACE &kp RALT &kp RGUI &mo 1 &kp RCTRL
>;
};
rgb_layer {
// ------------------------------------------------------------------------------------------------
// | BT CLR | F1 | F2 | F3 | F4 | F5 | F6 | F7 | F8 | F9 | F10 | F11 | F12 | EFFECT REV |
// | BT 1 | | UP | | HUEUP | SATUP | BRIUP | SPDUP | | | | | | |
// | BT 2 | LT | DN | RT | HUEDN | SATDN | BRIDN | SPDDN | | | | | EFFECT FORW |
// | BT 3 | | | | | | | | | | | |
// | BT 4 | | | TOG RGB | PRT SCR | | | DEL |
// ------------------------------------------------------------------------------------------------
bindings = <
&bt BT_CLR &kp F1 &kp F2 &kp F3 &kp F4 &kp F5 &kp F6 &kp F7 &kp F8 &kp F9 &kp F10 &kp F11 &kp F12 &rgb_ug RGB_EFR
&bt BT_SEL 0 &trans &kp UP &trans &rgb_ug RGB_HUI &rgb_ug RGB_SAI &rgb_ug RGB_BRI &rgb_ug RGB_SPI &trans &trans &trans &trans &trans &trans
&bt BT_SEL 1 &kp LEFT &kp DOWN &kp RIGHT &rgb_ug RGB_HUD &rgb_ug RGB_SAD &rgb_ug RGB_BRD &rgb_ug RGB_SPD &trans &trans &trans &trans &rgb_ug RGB_EFF
&bt BT_SEL 2 &trans &trans &trans &trans &trans &trans &trans &trans &trans &trans &trans
&bt BT_SEL 3 &trans &trans &rgb_ug RGB_TOG &kp PSCRN &trans &trans &kp DEL
>;
};
};
};

View file

@ -0,0 +1,14 @@
identifier: nice60
name: nice!60
type: mcu
arch: arm
toolchain:
- zephyr
- gnuarmemb
supported:
- adc
- usb_device
- ble
- ieee802154
- pwm
- watchdog

View file

@ -0,0 +1,27 @@
# Copyright (c) 2021 Nick Winans
# SPDX-License-Identifier: MIT
CONFIG_SOC_SERIES_NRF52X=y
CONFIG_SOC_NRF52840_QIAA=y
CONFIG_BOARD_NICE60=y
# Enable MPU
CONFIG_ARM_MPU=y
# enable GPIO
CONFIG_GPIO=y
CONFIG_USE_DT_CODE_PARTITION=y
CONFIG_MPU_ALLOW_FLASH_WRITE=y
CONFIG_NVS=y
CONFIG_SETTINGS_NVS=y
CONFIG_FLASH=y
CONFIG_FLASH_PAGE_LAYOUT=y
CONFIG_FLASH_MAP=y
CONFIG_ZMK_RGB_UNDERGLOW=y
CONFIG_WS2812_STRIP=y
CONFIG_ZMK_RGB_UNDERGLOW_HUE_START=160
CONFIG_ZMK_RGB_UNDERGLOW_EFF_START=3

View file

@ -6,3 +6,5 @@ CONFIG_ZMK_BLE=n
CONFIG_LOG=y CONFIG_LOG=y
CONFIG_LOG_BACKEND_SHOW_COLOR=n CONFIG_LOG_BACKEND_SHOW_COLOR=n
CONFIG_ZMK_LOG_LEVEL_DBG=y CONFIG_ZMK_LOG_LEVEL_DBG=y
CONFIG_DEBUG=y
CONFIG_SYS_CLOCK_TICKS_PER_SEC=1000

View file

@ -4,7 +4,7 @@
if SHIELD_CRADIO_LEFT if SHIELD_CRADIO_LEFT
config ZMK_KEYBOARD_NAME config ZMK_KEYBOARD_NAME
default "cradio left" default "Cradio"
config ZMK_SPLIT_BLE_ROLE_CENTRAL config ZMK_SPLIT_BLE_ROLE_CENTRAL
default y default y
@ -14,11 +14,11 @@ endif
if SHIELD_CRADIO_RIGHT if SHIELD_CRADIO_RIGHT
config ZMK_KEYBOARD_NAME config ZMK_KEYBOARD_NAME
default "cradio right" default "Cradio_Right"
endif endif
if SHIELD_CRADIO_LEFT || SHIELD_CRADIO_RIGHT if SHIELD_CRADIO_RIGHT || SHIELD_CRADIO_LEFT
config ZMK_SPLIT config ZMK_SPLIT
default y default y

View file

@ -0,0 +1 @@
Cradio is a firmware for a few 34 key keyboards, including Cradio, Hypergolic and Sweep.

View file

View file

@ -7,9 +7,10 @@
#include <dt-bindings/zmk/matrix_transform.h> #include <dt-bindings/zmk/matrix_transform.h>
/ { / {
chosen { chosen {
zmk,kscan = &kscan0; zmk,kscan = &kscan0;
//zmk,matrix_transform = &default_transform; zmk,matrix_transform = &default_transform;
}; };
default_transform: keymap_transform_0 { default_transform: keymap_transform_0 {
@ -22,6 +23,7 @@
RC(0,10) RC(0,11) RC(0,12) RC(0,13) RC(0,14) RC(0,31) RC(0,30) RC(0,29) RC(0,28) RC(0,27) RC(0,10) RC(0,11) RC(0,12) RC(0,13) RC(0,14) RC(0,31) RC(0,30) RC(0,29) RC(0,28) RC(0,27)
RC(0,15) RC(0,16) RC(0,33) RC(0,32) RC(0,15) RC(0,16) RC(0,33) RC(0,32)
>; >;
};
kscan0: kscan { kscan0: kscan {
compatible = "zmk,kscan-gpio-direct"; compatible = "zmk,kscan-gpio-direct";
@ -46,5 +48,5 @@
, <&pro_micro_d 9 (GPIO_ACTIVE_LOW | GPIO_PULL_UP)> , <&pro_micro_d 9 (GPIO_ACTIVE_LOW | GPIO_PULL_UP)>
; ;
}; };
};
}; };

View file

@ -9,33 +9,92 @@
#include <dt-bindings/zmk/bt.h> #include <dt-bindings/zmk/bt.h>
&mt {
// flavor = "tap-preferred";
// tapping_term_ms = <200>;
};
/ { / {
combos {
compatible = "zmk,combos";
combo_esc {
timeout-ms = <50>;
key-positions = <0 1>;
bindings = <&kp ESC>;
};
combo_tab {
timeout-ms = <50>;
key-positions = <10 11>;
bindings = <&kp TAB>;
};
combo_ralt {
timeout-ms = <50>;
key-positions = <17 16>;
bindings = <&kp RALT>;
};
combo_lalt {
timeout-ms = <50>;
key-positions = <11 12>;
bindings = <&kp LALT>;
};
combo_lgui {
timeout-ms = <50>;
key-positions = <12 13>;
bindings = <&kp LGUI>;
};
combo_rgui {
timeout-ms = <50>;
key-positions = <17 18>;
bindings = <&kp RGUI>;
};
};
keymap { keymap {
compatible = "zmk,keymap"; compatible = "zmk,keymap";
default_layer { default_layer {
bindings = < bindings = <
&kp Q &kp W &kp E &kp R &kp T &kp Y &kp U &kp I &kp O &kp P &kp Q &kp W &kp E &kp R &kp T &kp Y &kp U &kp I &kp O &kp P
&kp A &kp S &kp D &kp F &kp G &kp H &kp J &kp K &kp L &kp SEMI &kp A &kp S &kp D &kp F &kp G &kp H &kp J &kp K &kp L &kp QUOT
&kp Z &kp X &kp C &kp V &kp B &kp N &kp M &kp COMMA &kp DOT &kp FSLH &mt LSFT Z &kp X &kp C &kp V &kp B &kp N &kp M &kp CMMA &kp DOT &mt LSFT RET
&mo 1 &kp LCTRL &kp SPACE &mo 2 &mo 1 &kp LCTL &kp SPC &mo 2
>;
};
upper_layer {
bindings = <
&kp N1 &kp N2 &kp N3 &kp N4 &kp N5 &kp N6 &kp N7 &kp N8 &kp N9 &kp N0
&bt BT_SEL 0 &bt BT_SEL 1 &bt BT_SEL 2 &bt BT_SEL 3 &bt BT_SEL 4 &kp H &kp J &kp K &kp L &kp SEMI
&kp LSHFT &trans &trans &trans &trans &trans &trans &trans &trans &trans
&mo 1 &kp LCTRL &kp SPACE &mo 2
>; >;
}; };
lower_layer { left_layer {
bindings = < bindings = <
&kp EXCL &kp AT &kp HASH &kp DOLLAR &kp PRCNT &kp CARET &kp AMPS &kp KP_MULTIPLY &kp LPAR &kp RPAR &kp NUM_1 &kp NUM_2 &kp NUM_3 &kp NUM_4 &kp NUM_5 &kp NUM_6 &kp NUM_7 &kp NUM_8 &kp NUM_9 &kp NUM_0
&trans &trans &trans &trans &trans &kp MINUS &kp EQUAL &kp LBKT &kp RBKT &kp PIPE &kp TAB &kp LC(S) &kp DQT &kp PIPE2 &kp HASH &kp MINUS &kp EQL &kp LBKT &kp RBKT &kp DEL
&trans &trans &trans &trans &trans &trans &trans &trans &kp BSLH &kp TILDE &kp ESC &kp TILDE &kp NON_US_BSLH &kp NON_US_HASH &kp TILDE2 &kp MINUS &kp GRAVE &kp LBKT &kp RBKT &kp DEL
&mo 1 &kp LCTRL &kp SPACE &mo 2 &mo 1 &kp LGUI &kp RGUI &mo 2
>;
};
right_layer {
bindings = <
&kp BANG &kp ATSN &kp HASH &kp DLLR &kp PRCT &kp CRRT &kp AMPS &kp KMLT &kp LPRN &kp RPRN
&kp HASH &kp QMARK &kp FSLH &kp COLN &kp SCLN &kp MINUS &kp KP_EQUAL &kp LBRC &kp RBRC &kp BKSP
&kp LSFT &kp KPLS &kp LBKT &kp RBKT &kp BSLH &kp UNDER &kp LEFT &kp DOWN &kp UP &kp RIGHT
&mo 3 &kp LCTL &kp SPC &mo 2
>;
};
tri_layer {
bindings = <
&kp NUM_1 &kp NUM_2 &kp NUM_3 &kp NUM_4 &kp NUM_5 &trans &trans &trans &trans &trans
&kp F1 &kp F2 &kp F3 &kp F4 &kp F5 &trans &kp PG_UP &kp K_VOL_UP &kp K_MUTE &trans
&bt BT_CLR &bt BT_NXT &bt BT_PRV &kp F6 &kp F7 &trans &kp PG_DN &kp K_VOL_DN &trans &trans
&trans &trans &trans &trans
>; >;
}; };

View file

@ -1,3 +1,2 @@
# Copyright (c) 2020 The ZMK Contributors # Copyright (c) 2020 The ZMK Contributors
# SPDX-License-Identifier: MIT # SPDX-License-Identifier: MIT

View file

@ -0,0 +1,3 @@
# Uncomment lines below to enable encoder
# CONFIG_EC11=y
# CONFIG_EC11_TRIGGER_GLOBAL_THREAD=y

View file

@ -24,6 +24,8 @@
&kp LSHFT &kp Z &kp X &kp C &kp V &kp B &kp N &kp M &kp COMMA &kp DOT &kp BSLH &kp RET &kp LSHFT &kp Z &kp X &kp C &kp V &kp B &kp N &kp M &kp COMMA &kp DOT &kp BSLH &kp RET
&trans &kp LGUI &kp LALT &kp LCTRL &mo 1 &kp SPACE &trans &mo 2 &kp LEFT &kp DOWN &kp UP &kp RIGHT &trans &kp LGUI &kp LALT &kp LCTRL &mo 1 &kp SPACE &trans &mo 2 &kp LEFT &kp DOWN &kp UP &kp RIGHT
>; >;
sensor-bindings = <&inc_dec_kp PG_UP PG_DN>;
}; };
lower { lower {
@ -33,6 +35,8 @@
&trans &kp F7 &kp F8 &kp F9 &kp F10 &kp F11 &kp F12 &kp LS(HASH) &kp LS(BSLH) &kp HOME &kp END &trans &trans &kp F7 &kp F8 &kp F9 &kp F10 &kp F11 &kp F12 &kp LS(HASH) &kp LS(BSLH) &kp HOME &kp END &trans
&trans &trans &trans &trans &trans &trans &trans &mo 3 &kp C_NEXT &kp C_VOL_DN &kp C_VOL_UP &kp C_PP &trans &trans &trans &trans &trans &trans &trans &mo 3 &kp C_NEXT &kp C_VOL_DN &kp C_VOL_UP &kp C_PP
>; >;
sensor-bindings = <&inc_dec_kp C_VOL_UP C_VOL_DN>;
}; };
raise { raise {
@ -43,6 +47,7 @@
&trans &trans &trans &trans &mo 3 &trans &trans &trans &kp C_NEXT &kp C_VOL_DN &kp C_VOL_UP &kp C_PP &trans &trans &trans &trans &mo 3 &trans &trans &trans &kp C_NEXT &kp C_VOL_DN &kp C_VOL_UP &kp C_PP
>; >;
}; };
control { control {
bindings = < bindings = <
&reset &bootloader &bt BT_CLR &bt BT_PRV &bt BT_NXT &trans &trans &trans &trans &trans &trans &trans &reset &bootloader &bt BT_CLR &bt BT_PRV &bt BT_NXT &trans &trans &trans &trans &trans &trans &trans

View file

@ -38,4 +38,18 @@
, <&pro_micro_a 1 (GPIO_ACTIVE_HIGH | GPIO_PULL_DOWN)> , <&pro_micro_a 1 (GPIO_ACTIVE_HIGH | GPIO_PULL_DOWN)>
; ;
}; };
encoder: encoder {
compatible = "alps,ec11";
label = "ENCODER";
a-gpios = <&pro_micro_a 2 (GPIO_ACTIVE_HIGH | GPIO_PULL_UP)>;
b-gpios = <&pro_micro_a 3 (GPIO_ACTIVE_HIGH | GPIO_PULL_UP)>;
resolution = <2>;
status = "okay";
};
sensors {
compatible = "zmk,keymap-sensors";
sensors = <&encoder>;
};
}; };

View file

@ -13,6 +13,9 @@
#define RSE 2 #define RSE 2
#define ADJ 3 #define ADJ 3
&lt { quick_tap_ms = <200>; };
&mt { quick_tap_ms = <200>; };
/ { / {
keymap { keymap {
compatible = "zmk,keymap"; compatible = "zmk,keymap";
@ -26,7 +29,7 @@
bindings = < bindings = <
&kp LWIN &kp GRAVE &kp Q &kp W &kp E &kp R &kp T &kp Y &kp U &kp I &kp O &kp P &kp LBKT &mt RWIN RBKT &kp LWIN &kp GRAVE &kp Q &kp W &kp E &kp R &kp T &kp Y &kp U &kp I &kp O &kp P &kp LBKT &mt RWIN RBKT
&kp LCTRL &kp A &kp S &kp D &kp F &kp G &kp H &kp J &kp K &kp L &kp SEMI &mt RCTRL SQT &kp LCTRL &kp A &kp S &kp D &kp F &kp G &kp H &kp J &kp K &kp L &kp SEMI &mt RCTRL SQT
&kp LALT &kp Z &kp X &kp C &kp V &kp B &kp N &kp M &kp COMMA &kp DOT &kp FSLH &kp BSLH &kp LALT &kp Z &kp X &kp C &kp V &kp B &kp N &kp M &kp COMMA &kp DOT &kp FSLH &mt RALT BSLH
&lt RSE TAB &mt LSHFT SPACE &lt LWR RET &lt LWR ESC &mt RSHFT BSPC &lt RSE DEL &lt RSE TAB &mt LSHFT SPACE &lt LWR RET &lt LWR ESC &mt RSHFT BSPC &lt RSE DEL
>; >;
}; };

View file

@ -9,5 +9,41 @@ config ZMK_KEYBOARD_NAME
config ZMK_USB config ZMK_USB
default y default y
endif
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

View file

@ -0,0 +1,29 @@
# Building ZMK for the Nibble
Some general notes/commands for building standard nibble layouts from the assembly documentation.
## LED Notes
If you built your nibble without the LEDs _and_ are using a nice!nano board, you'll need to change the following in your local nibble config or add them to the end of the file.
```
CONFIG_ZMK_RGB_UNDERGLOW=n
CONFIG_WS2812_STRIP=n
```
## Encoder Notes
If you built your nibble without an encoder, you'll need to change the following in your local nibble config or add them to the end of the file.
```
CONFIG_EC11=n
CONFIG_EC11_TRIGGER_GLOBAL_THREAD=n
```
## OLED Builds
If using an OLED screen, you'll need to change the following in your local nibble config or add them to the end of the file.
```
CONFIG_ZMK_DISPLAY=y
```

View file

@ -0,0 +1,4 @@
# Enable underglow
CONFIG_ZMK_RGB_UNDERGLOW=y
# Use the STRIP config specific to the LEDs you're using
CONFIG_WS2812_STRIP=y

View file

@ -0,0 +1,34 @@
/*
* Copyright (c) 2020 The ZMK Contributors
*
* SPDX-License-Identifier: MIT
*/
&spi1 {
compatible = "nordic,nrf-spim";
status = "okay";
mosi-pin = <11>;
// 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 = <10>; /* number of LEDs */
spi-one-frame = <0x70>;
spi-zero-frame = <0x40>;
};
};
/ {
chosen {
zmk,underglow = &led_strip;
};
};

View file

@ -0,0 +1,6 @@
# Copyright (c) 2020 The ZMK Contributors
# SPDX-License-Identifier: MIT
# Enable Encoders
CONFIG_EC11=y
CONFIG_EC11_TRIGGER_GLOBAL_THREAD=y

View file

@ -8,27 +8,37 @@
#include <dt-bindings/zmk/keys.h> #include <dt-bindings/zmk/keys.h>
#include <dt-bindings/zmk/bt.h> #include <dt-bindings/zmk/bt.h>
#define DEFAULT 0
#define FUNC 1
/ { / {
sensors {
compatible = "zmk,keymap-sensors";
sensors = <&encoder_1>;
};
keymap { keymap {
compatible = "zmk,keymap"; compatible = "zmk,keymap";
default_layer { default_layer {
label = "Default";
sensor-bindings = <&inc_dec_kp C_VOLUME_UP C_VOLUME_DOWN>;
bindings = < bindings = <
&kp ESC &kp N1 &kp N2 &kp N3 &kp N4 &kp N5 &kp N6 &kp N7 &kp N8 &kp N9 &kp N0 &kp MINUS &kp EQUAL &kp BSPC &kp HOME &kp ESC &kp N1 &kp N2 &kp N3 &kp N4 &kp N5 &kp N6 &kp N7 &kp N8 &kp N9 &kp N0 &kp MINUS &kp EQUAL &kp BSPC &kp HOME
&kp C_VOL_UP &kp TAB &kp Q &kp W &kp E &kp R &kp T &kp Y &kp U &kp I &kp O &kp P &kp LBKT &kp RBKT &kp BSLH &kp DEL &kp C_MUTE &kp TAB &kp Q &kp W &kp E &kp R &kp T &kp Y &kp U &kp I &kp O &kp P &kp LBKT &kp RBKT &kp BSLH &kp DEL
&kp C_VOL_DN &kp CLCK &kp A &kp S &kp D &kp F &kp G &kp H &kp J &kp K &kp L &kp SEMI &kp SQT &kp RET &kp PG_UP &trans &kp CLCK &kp A &kp S &kp D &kp F &kp G &kp H &kp J &kp K &kp L &kp SEMI &kp SQT &kp RET &kp PG_UP
&trans &kp LSHFT &trans &kp Z &kp X &kp C &kp V &kp B &kp N &kp M &kp COMMA &kp DOT &kp FSLH &kp RSHFT &kp UP &kp PG_DN &trans &kp LSHFT &trans &kp Z &kp X &kp C &kp V &kp B &kp N &kp M &kp COMMA &kp DOT &kp FSLH &kp RSHFT &kp UP &kp PG_DN
&trans &kp LCTRL &kp LGUI &kp LALT &kp SPACE &mo FUNC &kp RALT &kp RCTRL &kp LEFT &kp DOWN &kp RIGHT &trans &kp LCTRL &kp LGUI &kp LALT &kp SPACE &mo 1 &kp RALT &kp RCTRL &kp LEFT &kp DOWN &kp RIGHT
>; >;
}; };
func { function_layer {
label = "Function";
sensor-bindings = <&inc_dec_kp C_VOLUME_UP C_VOLUME_DOWN>;
bindings = < bindings = <
&kp TILDE &kp F1 &kp F2 &kp F3 &kp F4 &kp F5 &kp F6 &kp F7 &kp F8 &kp F9 &kp F10 &kp F11 &kp F12 &trans &kp END &kp TILDE &kp F1 &kp F2 &kp F3 &kp F4 &kp F5 &kp F6 &kp F7 &kp F8 &kp F9 &kp F10 &kp F11 &kp F12 &trans &kp END
&bt BT_CLR &trans &trans &trans &trans &trans &trans &trans &trans &trans &trans &trans &trans &trans &trans &bootloader &kp C_MUTE &trans &trans &trans &trans &trans &trans &trans &trans &trans &trans &trans &trans &trans &trans &bootloader
&trans &trans &trans &trans &trans &trans &trans &trans &trans &trans &trans &trans &trans &trans &trans &bt BT_CLR &trans &trans &trans &trans &trans &trans &trans &trans &trans &trans &trans &trans &trans &trans
&bt BT_PRV &trans &trans &trans &trans &trans &trans &trans &trans &trans &trans &trans &trans &trans &trans &trans &bt BT_PRV &trans &trans &trans &trans &trans &trans &trans &trans &trans &trans &trans &trans &trans &trans &trans
&bt BT_NXT &trans &trans &trans &trans &trans &trans &trans &kp C_PREV &kp C_PP &kp C_NEXT &bt BT_NXT &trans &trans &trans &trans &trans &trans &trans &kp C_PREV &kp C_PP &kp C_NEXT
>; >;

View file

@ -12,6 +12,15 @@
zmk,matrix_transform = &default_transform; zmk,matrix_transform = &default_transform;
}; };
encoder_1: encoder_1 {
compatible = "alps,ec11";
label = "Encoder 1";
a-gpios = <&pro_micro_d 9 (GPIO_ACTIVE_LOW | GPIO_PULL_UP)>;
b-gpios = <&pro_micro_d 8 (GPIO_ACTIVE_LOW | GPIO_PULL_UP)>;
resolution = <4>;
status = "okay";
};
kscan0: kscan { kscan0: kscan {
compatible = "zmk,kscan-gpio-demux"; compatible = "zmk,kscan-gpio-demux";
label = "KSCAN"; label = "KSCAN";
@ -36,8 +45,6 @@
columns = <16>; columns = <16>;
rows = <5>; rows = <5>;
//TODO: Add a keymap graphic here
map = < map = <
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(0,14) RC(0,15) 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(0,14) RC(0,15)
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(1,14) RC(1,15) 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(1,14) RC(1,15)
@ -47,3 +54,21 @@ RC(4,0) RC(4,1) RC(4,2) RC(4,3) RC(4,6) RC(4,9)
>; >;
}; };
}; };
&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>;
com-sequential;
prechargep = <0x22>;
};
};

View file

@ -18,7 +18,7 @@
compatible = "zmk,behavior-hold-tap"; compatible = "zmk,behavior-hold-tap";
label = "homerow mods"; label = "homerow mods";
#binding-cells = <2>; #binding-cells = <2>;
tapping_term_ms = <225>; tapping-term-ms = <225>;
flavor = "tap-preferred"; flavor = "tap-preferred";
bindings = <&kp>, <&kp>; bindings = <&kp>, <&kp>;
}; };

View file

@ -16,7 +16,7 @@ endif
if SHIELD_SPLITREUS62_RIGHT if SHIELD_SPLITREUS62_RIGHT
config ZMK_KEYBOARD_NAME config ZMK_KEYBOARD_NAME
default "Splitreus62 Right" default "Splitreus62 Rt"
endif endif

View file

@ -14,7 +14,7 @@
compatible = "zmk,behavior-hold-tap"; compatible = "zmk,behavior-hold-tap";
label = "Hold Tap"; label = "Hold Tap";
#binding-cells = <2>; #binding-cells = <2>;
tapping_term_ms = <200>; tapping-term-ms = <200>;
flavor = "tap-preferred"; flavor = "tap-preferred";
bindings = <&kp>, <&kp>; bindings = <&kp>, <&kp>;
}; };

View file

@ -6,7 +6,6 @@ if SHIELD_TIDBIT
config ZMK_KEYBOARD_NAME config ZMK_KEYBOARD_NAME
default "tidbit" default "tidbit"
endif
if ZMK_DISPLAY if ZMK_DISPLAY
@ -43,3 +42,5 @@ choice LVGL_COLOR_DEPTH
endchoice endchoice
endif # LVGL endif # LVGL
endif

View file

@ -1 +0,0 @@
CONFIG_SENSOR=y

View file

@ -2,7 +2,7 @@
# SPDX-License-Identifier: MIT # SPDX-License-Identifier: MIT
config ZMK_KSCAN_GPIO_DRIVER config ZMK_KSCAN_GPIO_DRIVER
bool "Enable GPIO kscan driver to simulate key presses" bool "Enable GPIO kscan driver to detect key presses"
default y default y
select GPIO select GPIO
@ -10,11 +10,9 @@ if ZMK_KSCAN_GPIO_DRIVER
config ZMK_KSCAN_MATRIX_POLLING config ZMK_KSCAN_MATRIX_POLLING
bool "Poll for key event triggers instead of using interrupts on matrix boards." bool "Poll for key event triggers instead of using interrupts on matrix boards."
default n
config ZMK_KSCAN_DIRECT_POLLING config ZMK_KSCAN_DIRECT_POLLING
bool "Poll for key event triggers instead of using interrupts on direct wired boards." bool "Poll for key event triggers instead of using interrupts on direct wired boards."
default n
endif endif

View file

@ -113,6 +113,8 @@ struct kscan_gpio_item_config {
&kscan_gpio_output_configs_##n(dev)[bit]; \ &kscan_gpio_output_configs_##n(dev)[bit]; \
gpio_pin_set(out_dev, out_cfg->pin, state); \ gpio_pin_set(out_dev, out_cfg->pin, state); \
} \ } \
/* Let the col settle before reading the rows */ \
k_usleep(1); \
\ \
for (int i = 0; i < INST_MATRIX_INPUTS(n); i++) { \ for (int i = 0; i < INST_MATRIX_INPUTS(n); i++) { \
/* Get the input device (port) */ \ /* Get the input device (port) */ \

View file

@ -83,7 +83,7 @@ static int kscan_gpio_config_interrupts(const struct device *dev, gpio_flags_t f
int err = gpio_pin_interrupt_configure(dev, cfg->pin, flags); int err = gpio_pin_interrupt_configure(dev, cfg->pin, flags);
if (err) { if (err) {
LOG_ERR("Unable to enable matrix GPIO interrupt"); LOG_ERR("Unable to enable direct GPIO interrupt");
return err; return err;
} }
} }

View file

@ -51,6 +51,11 @@ static int kscan_gpio_config_interrupts(const struct device **devices,
} }
#endif #endif
#define COND_POLLING(code) COND_CODE_1(CONFIG_ZMK_KSCAN_MATRIX_POLLING, (code), ())
#define COND_INTERRUPTS(code) COND_CODE_1(CONFIG_ZMK_KSCAN_MATRIX_POLLING, (), (code))
#define COND_POLL_OR_INTERRUPTS(pollcode, intcode) \
COND_CODE_1(CONFIG_ZMK_KSCAN_MATRIX_POLLING, pollcode, intcode)
#define INST_MATRIX_ROWS(n) DT_INST_PROP_LEN(n, row_gpios) #define INST_MATRIX_ROWS(n) DT_INST_PROP_LEN(n, row_gpios)
#define INST_MATRIX_COLS(n) DT_INST_PROP_LEN(n, col_gpios) #define INST_MATRIX_COLS(n) DT_INST_PROP_LEN(n, col_gpios)
#define INST_OUTPUT_LEN(n) \ #define INST_OUTPUT_LEN(n) \
@ -61,19 +66,21 @@ static int kscan_gpio_config_interrupts(const struct device **devices,
(INST_MATRIX_ROWS(n))) (INST_MATRIX_ROWS(n)))
#define GPIO_INST_INIT(n) \ #define GPIO_INST_INIT(n) \
COND_INTERRUPTS( \
struct kscan_gpio_irq_callback_##n { \ struct kscan_gpio_irq_callback_##n { \
struct COND_CODE_0(DT_INST_PROP(n, debounce_period), (k_work), (k_delayed_work)) * work; \ struct COND_CODE_0(DT_INST_PROP(n, debounce_period), (k_work), (k_delayed_work)) * \
work; \
struct gpio_callback callback; \ struct gpio_callback callback; \
const struct device *dev; \ const struct device *dev; \
}; \ }; \
static struct kscan_gpio_irq_callback_##n irq_callbacks_##n[INST_INPUT_LEN(n)]; \ static struct kscan_gpio_irq_callback_##n irq_callbacks_##n[INST_INPUT_LEN(n)];) \
struct kscan_gpio_config_##n { \ struct kscan_gpio_config_##n { \
struct kscan_gpio_item_config rows[INST_MATRIX_ROWS(n)]; \ struct kscan_gpio_item_config rows[INST_MATRIX_ROWS(n)]; \
struct kscan_gpio_item_config cols[INST_MATRIX_COLS(n)]; \ struct kscan_gpio_item_config cols[INST_MATRIX_COLS(n)]; \
}; \ }; \
struct kscan_gpio_data_##n { \ struct kscan_gpio_data_##n { \
kscan_callback_t callback; \ kscan_callback_t callback; \
COND_CODE_1(CONFIG_ZMK_KSCAN_MATRIX_POLLING, (struct k_timer poll_timer;), ()) \ COND_POLLING(struct k_timer poll_timer;) \
struct COND_CODE_0(DT_INST_PROP(n, debounce_period), (k_work), (k_delayed_work)) work; \ struct COND_CODE_0(DT_INST_PROP(n, debounce_period), (k_work), (k_delayed_work)) work; \
bool matrix_state[INST_MATRIX_ROWS(n)][INST_MATRIX_COLS(n)]; \ bool matrix_state[INST_MATRIX_ROWS(n)][INST_MATRIX_COLS(n)]; \
const struct device *rows[INST_MATRIX_ROWS(n)]; \ const struct device *rows[INST_MATRIX_ROWS(n)]; \
@ -102,17 +109,16 @@ static int kscan_gpio_config_interrupts(const struct device **devices,
return ( \ return ( \
COND_CODE_0(DT_ENUM_IDX(DT_DRV_INST(n), diode_direction), (cfg->rows), (cfg->cols))); \ COND_CODE_0(DT_ENUM_IDX(DT_DRV_INST(n), diode_direction), (cfg->rows), (cfg->cols))); \
} \ } \
COND_CODE_1(CONFIG_ZMK_KSCAN_MATRIX_POLLING, (), \ COND_INTERRUPTS( \
( \
static int kscan_gpio_enable_interrupts_##n(const struct device *dev) { \ static int kscan_gpio_enable_interrupts_##n(const struct device *dev) { \
return kscan_gpio_config_interrupts( \ return kscan_gpio_config_interrupts(kscan_gpio_input_devices_##n(dev), \
kscan_gpio_input_devices_##n(dev), kscan_gpio_input_configs_##n(dev), \ kscan_gpio_input_configs_##n(dev), \
INST_INPUT_LEN(n), GPIO_INT_LEVEL_ACTIVE); \ INST_INPUT_LEN(n), GPIO_INT_LEVEL_ACTIVE); \
} static int kscan_gpio_disable_interrupts_##n(const struct device *dev) { \ } static int kscan_gpio_disable_interrupts_##n(const struct device *dev) { \
return kscan_gpio_config_interrupts(kscan_gpio_input_devices_##n(dev), \ return kscan_gpio_config_interrupts(kscan_gpio_input_devices_##n(dev), \
kscan_gpio_input_configs_##n(dev), \ kscan_gpio_input_configs_##n(dev), \
INST_INPUT_LEN(n), GPIO_INT_DISABLE); \ INST_INPUT_LEN(n), GPIO_INT_DISABLE); \
})) \ }) \
static void kscan_gpio_set_output_state_##n(const struct device *dev, int value) { \ static void kscan_gpio_set_output_state_##n(const struct device *dev, int value) { \
int err; \ int err; \
for (int i = 0; i < INST_OUTPUT_LEN(n); i++) { \ for (int i = 0; i < INST_OUTPUT_LEN(n); i++) { \
@ -132,17 +138,22 @@ static int kscan_gpio_config_interrupts(const struct device **devices,
(output_index))] = value; \ (output_index))] = value; \
} \ } \
static int kscan_gpio_read_##n(const struct device *dev) { \ static int kscan_gpio_read_##n(const struct device *dev) { \
bool submit_follow_up_read = false; \ COND_INTERRUPTS(bool submit_follow_up_read = false;) \
struct kscan_gpio_data_##n *data = dev->data; \ struct kscan_gpio_data_##n *data = dev->data; \
static bool read_state[INST_MATRIX_ROWS(n)][INST_MATRIX_COLS(n)]; \ static bool read_state[INST_MATRIX_ROWS(n)][INST_MATRIX_COLS(n)]; \
int err; \
/* Disable our interrupts temporarily while we scan, to avoid */ \ /* Disable our interrupts temporarily while we scan, to avoid */ \
/* re-entry while we iterate columns and set them active one by one */ \ /* re-entry while we iterate columns and set them active one by one */ \
/* to get pressed state for each matrix cell. */ \ /* to get pressed state for each matrix cell. */ \
kscan_gpio_set_output_state_##n(dev, 0); \ COND_INTERRUPTS(kscan_gpio_set_output_state_##n(dev, 0);) \
for (int o = 0; o < INST_OUTPUT_LEN(n); o++) { \ for (int o = 0; o < INST_OUTPUT_LEN(n); o++) { \
const struct device *out_dev = kscan_gpio_output_devices_##n(dev)[o]; \ const struct device *out_dev = kscan_gpio_output_devices_##n(dev)[o]; \
const struct kscan_gpio_item_config *out_cfg = &kscan_gpio_output_configs_##n(dev)[o]; \ const struct kscan_gpio_item_config *out_cfg = &kscan_gpio_output_configs_##n(dev)[o]; \
gpio_pin_set(out_dev, out_cfg->pin, 1); \ err = gpio_pin_set(out_dev, out_cfg->pin, 1); \
if (err) { \
LOG_ERR("Failed to set output active (err %d)", err); \
return err; \
} \
for (int i = 0; i < INST_INPUT_LEN(n); i++) { \ for (int i = 0; i < INST_INPUT_LEN(n); i++) { \
const struct device *in_dev = kscan_gpio_input_devices_##n(dev)[i]; \ const struct device *in_dev = kscan_gpio_input_devices_##n(dev)[i]; \
const struct kscan_gpio_item_config *in_cfg = \ const struct kscan_gpio_item_config *in_cfg = \
@ -150,16 +161,20 @@ static int kscan_gpio_config_interrupts(const struct device **devices,
kscan_gpio_set_matrix_state_##n(read_state, i, o, \ kscan_gpio_set_matrix_state_##n(read_state, i, o, \
gpio_pin_get(in_dev, in_cfg->pin) > 0); \ gpio_pin_get(in_dev, in_cfg->pin) > 0); \
} \ } \
gpio_pin_set(out_dev, out_cfg->pin, 0); \ err = gpio_pin_set(out_dev, out_cfg->pin, 0); \
if (err) { \
LOG_ERR("Failed to set output inactive (err %d)", err); \
return err; \
} \
} \ } \
/* Set all our outputs as active again. */ \ /* Set all our outputs as active again. */ \
kscan_gpio_set_output_state_##n(dev, 1); \ COND_INTERRUPTS(kscan_gpio_set_output_state_##n(dev, 1);) \
for (int r = 0; r < INST_MATRIX_ROWS(n); r++) { \ for (int r = 0; r < INST_MATRIX_ROWS(n); r++) { \
for (int c = 0; c < INST_MATRIX_COLS(n); c++) { \ for (int c = 0; c < INST_MATRIX_COLS(n); c++) { \
bool pressed = read_state[r][c]; \ bool pressed = read_state[r][c]; \
/* Follow up reads needed because further interrupts won't fire on already tripped \ /* Follow up reads needed because further interrupts won't fire on already tripped \
* input GPIO pins */ \ * input GPIO pins */ \
submit_follow_up_read = (submit_follow_up_read || pressed); \ COND_INTERRUPTS(submit_follow_up_read = (submit_follow_up_read || pressed);) \
if (pressed != data->matrix_state[r][c]) { \ if (pressed != data->matrix_state[r][c]) { \
LOG_DBG("Sending event at %d,%d state %s", r, c, (pressed ? "on" : "off")); \ LOG_DBG("Sending event at %d,%d state %s", r, c, (pressed ? "on" : "off")); \
data->matrix_state[r][c] = pressed; \ data->matrix_state[r][c] = pressed; \
@ -167,33 +182,31 @@ static int kscan_gpio_config_interrupts(const struct device **devices,
} \ } \
} \ } \
} \ } \
COND_INTERRUPTS( \
if (submit_follow_up_read) { \ if (submit_follow_up_read) { \
COND_CODE_0(DT_INST_PROP(n, debounce_period), ({ k_work_submit(&data->work); }), ({ \ COND_CODE_0(DT_INST_PROP(n, debounce_period), ({ k_work_submit(&data->work); }), \
({ \
k_delayed_work_cancel(&data->work); \ k_delayed_work_cancel(&data->work); \
k_delayed_work_submit(&data->work, K_MSEC(5)); \ k_delayed_work_submit(&data->work, K_MSEC(5)); \
})) \ })) \
} else { \ } else { kscan_gpio_enable_interrupts_##n(dev); }) \
COND_CODE_1(CONFIG_ZMK_KSCAN_MATRIX_POLLING, (), \
(kscan_gpio_enable_interrupts_##n(dev);)) \
} \
return 0; \ return 0; \
} \ } \
static void kscan_gpio_work_handler_##n(struct k_work *work) { \ static void kscan_gpio_work_handler_##n(struct k_work *work) { \
struct kscan_gpio_data_##n *data = CONTAINER_OF(work, struct kscan_gpio_data_##n, work); \ struct kscan_gpio_data_##n *data = CONTAINER_OF(work, struct kscan_gpio_data_##n, work); \
kscan_gpio_read_##n(data->dev); \ kscan_gpio_read_##n(data->dev); \
} \ } \
static void kscan_gpio_irq_callback_handler_##n( \ COND_INTERRUPTS(static void kscan_gpio_irq_callback_handler_##n( \
const struct device *dev, struct gpio_callback *cb, gpio_port_pins_t pin) { \ const struct device *dev, struct gpio_callback *cb, gpio_port_pins_t pin) { \
struct kscan_gpio_irq_callback_##n *data = \ struct kscan_gpio_irq_callback_##n *data = \
CONTAINER_OF(cb, struct kscan_gpio_irq_callback_##n, callback); \ CONTAINER_OF(cb, struct kscan_gpio_irq_callback_##n, callback); \
COND_CODE_1(CONFIG_ZMK_KSCAN_MATRIX_POLLING, (), \ kscan_gpio_disable_interrupts_##n(data->dev); \
(kscan_gpio_disable_interrupts_##n(data->dev);)) \
COND_CODE_0(DT_INST_PROP(n, debounce_period), ({ k_work_submit(data->work); }), ({ \ COND_CODE_0(DT_INST_PROP(n, debounce_period), ({ k_work_submit(data->work); }), ({ \
k_delayed_work_cancel(data->work); \ k_delayed_work_cancel(data->work); \
k_delayed_work_submit(data->work, \ k_delayed_work_submit(data->work, \
K_MSEC(DT_INST_PROP(n, debounce_period))); \ K_MSEC(DT_INST_PROP(n, debounce_period))); \
})) \ })) \
} \ }) \
\ \
static struct kscan_gpio_data_##n kscan_gpio_data_##n = { \ static struct kscan_gpio_data_##n kscan_gpio_data_##n = { \
.rows = {[INST_MATRIX_ROWS(n) - 1] = NULL}, .cols = {[INST_MATRIX_COLS(n) - 1] = NULL}}; \ .rows = {[INST_MATRIX_ROWS(n) - 1] = NULL}, .cols = {[INST_MATRIX_COLS(n) - 1] = NULL}}; \
@ -207,25 +220,22 @@ static int kscan_gpio_config_interrupts(const struct device **devices,
return 0; \ return 0; \
}; \ }; \
static int kscan_gpio_enable_##n(const struct device *dev) { \ static int kscan_gpio_enable_##n(const struct device *dev) { \
COND_CODE_1(CONFIG_ZMK_KSCAN_MATRIX_POLLING, \ COND_POLL_OR_INTERRUPTS((struct kscan_gpio_data_##n *data = dev->data; \
(struct kscan_gpio_data_##n *data = dev->data; \ k_timer_start(&data->poll_timer, K_MSEC(10), K_MSEC(10)); \
k_timer_start(&data->poll_timer, K_MSEC(10), K_MSEC(10)); return 0;), \ return 0;), \
(int err = kscan_gpio_enable_interrupts_##n(dev); \ (int err = kscan_gpio_enable_interrupts_##n(dev); \
if (err) { return err; } return kscan_gpio_read_##n(dev);)) \ if (err) { return err; } return kscan_gpio_read_##n(dev);)) \
}; \ }; \
static int kscan_gpio_disable_##n(const struct device *dev) { \ static int kscan_gpio_disable_##n(const struct device *dev) { \
COND_CODE_1(CONFIG_ZMK_KSCAN_MATRIX_POLLING, \ COND_POLL_OR_INTERRUPTS((struct kscan_gpio_data_##n *data = dev->data; \
(struct kscan_gpio_data_##n *data = dev->data; \
k_timer_stop(&data->poll_timer); return 0;), \ k_timer_stop(&data->poll_timer); return 0;), \
(return kscan_gpio_disable_interrupts_##n(dev);)) \ (return kscan_gpio_disable_interrupts_##n(dev);)) \
}; \ }; \
COND_CODE_1(CONFIG_ZMK_KSCAN_MATRIX_POLLING, \ COND_POLLING(static void kscan_gpio_timer_handler_##n(struct k_timer *timer) { \
(static void kscan_gpio_timer_handler(struct k_timer *timer) { \
struct kscan_gpio_data_##n *data = \ struct kscan_gpio_data_##n *data = \
CONTAINER_OF(timer, struct kscan_gpio_data_##n, poll_timer); \ CONTAINER_OF(timer, struct kscan_gpio_data_##n, poll_timer); \
k_work_submit(&data->work.work); \ k_work_submit(&data->work.work); \
}), \ }) \
()) \
static int kscan_gpio_init_##n(const struct device *dev) { \ static int kscan_gpio_init_##n(const struct device *dev) { \
struct kscan_gpio_data_##n *data = dev->data; \ struct kscan_gpio_data_##n *data = dev->data; \
int err; \ int err; \
@ -244,15 +254,15 @@ static int kscan_gpio_config_interrupts(const struct device **devices,
} else { \ } else { \
LOG_DBG("Configured pin %d on %s for input", in_cfg->pin, in_cfg->label); \ LOG_DBG("Configured pin %d on %s for input", in_cfg->pin, in_cfg->label); \
} \ } \
irq_callbacks_##n[i].work = &data->work; \ COND_INTERRUPTS( \
irq_callbacks_##n[i].dev = dev; \ irq_callbacks_##n[i].work = &data->work; irq_callbacks_##n[i].dev = dev; \
gpio_init_callback(&irq_callbacks_##n[i].callback, \ gpio_init_callback(&irq_callbacks_##n[i].callback, \
kscan_gpio_irq_callback_handler_##n, BIT(in_cfg->pin)); \ kscan_gpio_irq_callback_handler_##n, BIT(in_cfg->pin)); \
err = gpio_add_callback(input_devices[i], &irq_callbacks_##n[i].callback); \ err = gpio_add_callback(input_devices[i], &irq_callbacks_##n[i].callback); \
if (err) { \ if (err) { \
LOG_ERR("Error adding the callback to the column device"); \ LOG_ERR("Error adding the callback to the input device"); \
return err; \ return err; \
} \ }) \
} \ } \
const struct device **output_devices = kscan_gpio_output_devices_##n(dev); \ const struct device **output_devices = kscan_gpio_output_devices_##n(dev); \
for (int o = 0; o < INST_OUTPUT_LEN(n); o++) { \ for (int o = 0; o < INST_OUTPUT_LEN(n); o++) { \
@ -262,8 +272,8 @@ static int kscan_gpio_config_interrupts(const struct device **devices,
LOG_ERR("Unable to find output GPIO device"); \ LOG_ERR("Unable to find output GPIO device"); \
return -EINVAL; \ return -EINVAL; \
} \ } \
err = gpio_pin_configure(output_devices[o], out_cfg->pin, \ err = \
GPIO_OUTPUT_ACTIVE | out_cfg->flags); \ gpio_pin_configure(output_devices[o], out_cfg->pin, GPIO_OUTPUT | out_cfg->flags); \
if (err) { \ if (err) { \
LOG_ERR("Unable to configure pin %d on %s for output", out_cfg->pin, \ LOG_ERR("Unable to configure pin %d on %s for output", out_cfg->pin, \
out_cfg->label); \ out_cfg->label); \
@ -271,10 +281,12 @@ static int kscan_gpio_config_interrupts(const struct device **devices,
} \ } \
} \ } \
data->dev = dev; \ data->dev = dev; \
COND_CODE_1(CONFIG_ZMK_KSCAN_MATRIX_POLLING, \
(k_timer_init(&data->poll_timer, kscan_gpio_timer_handler, NULL);), ()) \
(COND_CODE_0(DT_INST_PROP(n, debounce_period), (k_work_init), (k_delayed_work_init)))( \ (COND_CODE_0(DT_INST_PROP(n, debounce_period), (k_work_init), (k_delayed_work_init)))( \
&data->work, kscan_gpio_work_handler_##n); \ &data->work, kscan_gpio_work_handler_##n); \
COND_POLL_OR_INTERRUPTS( \
(k_timer_init(&data->poll_timer, kscan_gpio_timer_handler_##n, NULL); \
kscan_gpio_set_output_state_##n(dev, 0);), \
(kscan_gpio_set_output_state_##n(dev, 1);)) \
return 0; \ return 0; \
} \ } \
static const struct kscan_driver_api gpio_driver_api_##n = { \ static const struct kscan_driver_api gpio_driver_api_##n = { \

View file

@ -77,6 +77,9 @@ static int bvd_sample_fetch(const struct device *dev, enum sensor_channel chan)
LOG_DBG("Failed to enable ADC power GPIO: %d", rc); LOG_DBG("Failed to enable ADC power GPIO: %d", rc);
return rc; return rc;
} }
// wait for any capacitance to charge up
k_sleep(K_MSEC(10));
} }
// Read ADC // Read ADC
@ -90,7 +93,7 @@ static int bvd_sample_fetch(const struct device *dev, enum sensor_channel chan)
&val); &val);
uint16_t millivolts = val * (uint64_t)drv_cfg->full_ohm / drv_cfg->output_ohm; uint16_t millivolts = val * (uint64_t)drv_cfg->full_ohm / drv_cfg->output_ohm;
LOG_DBG("ADC raw %d ~ %d mV => %d mV\n", drv_data->adc_raw, val, millivolts); LOG_DBG("ADC raw %d ~ %d mV => %d mV", drv_data->adc_raw, val, millivolts);
uint8_t percent = lithium_ion_mv_to_pct(millivolts); uint8_t percent = lithium_ion_mv_to_pct(millivolts);
LOG_DBG("Percent: %d", percent); LOG_DBG("Percent: %d", percent);

View file

@ -6,7 +6,7 @@
/ { / {
behaviors { behaviors {
bt: behavior_bluetooth { /omit-if-no-ref/ bt: behavior_bluetooth {
compatible = "zmk,behavior-bluetooth"; compatible = "zmk,behavior-bluetooth";
label = "BLUETOOTH"; label = "BLUETOOTH";
#binding-cells = <2>; #binding-cells = <2>;

View file

@ -6,7 +6,7 @@
/ { / {
behaviors { behaviors {
ext_power: behavior_ext_power { /omit-if-no-ref/ ext_power: behavior_ext_power {
compatible = "zmk,behavior-ext-power"; compatible = "zmk,behavior-ext-power";
label = "EXT_POWER_BEHAVIOR"; label = "EXT_POWER_BEHAVIOR";
#binding-cells = <1>; #binding-cells = <1>;

View file

@ -7,7 +7,7 @@
/ { / {
behaviors { behaviors {
/* DEPRECATED: `cp` will be removed in the future */ /* DEPRECATED: `cp` will be removed in the future */
cp: kp: behavior_key_press { /omit-if-no-ref/ cp: kp: behavior_key_press {
compatible = "zmk,behavior-key-press"; compatible = "zmk,behavior-key-press";
label = "KEY_PRESS"; label = "KEY_PRESS";
#binding-cells = <1>; #binding-cells = <1>;

View file

@ -6,12 +6,12 @@
/ { / {
behaviors { behaviors {
lt: behavior_layer_tap { /omit-if-no-ref/ lt: behavior_layer_tap {
compatible = "zmk,behavior-hold-tap"; compatible = "zmk,behavior-hold-tap";
label = "LAYER_TAP"; label = "LAYER_TAP";
#binding-cells = <2>; #binding-cells = <2>;
flavor = "tap-preferred"; flavor = "tap-preferred";
tapping_term_ms = <200>; tapping-term-ms = <200>;
bindings = <&mo>, <&kp>; bindings = <&mo>, <&kp>;
}; };
}; };

View file

@ -6,12 +6,12 @@
/ { / {
behaviors { behaviors {
mt: behavior_mod_tap { /omit-if-no-ref/ mt: behavior_mod_tap {
compatible = "zmk,behavior-hold-tap"; compatible = "zmk,behavior-hold-tap";
label = "MOD_TAP"; label = "MOD_TAP";
#binding-cells = <2>; #binding-cells = <2>;
flavor = "hold-preferred"; flavor = "hold-preferred";
tapping_term_ms = <200>; tapping-term-ms = <200>;
bindings = <&kp>, <&kp>; bindings = <&kp>, <&kp>;
}; };
}; };

View file

@ -6,7 +6,7 @@
/ { / {
behaviors { behaviors {
mo: behavior_momentary_layer { /omit-if-no-ref/ mo: behavior_momentary_layer {
compatible = "zmk,behavior-momentary-layer"; compatible = "zmk,behavior-momentary-layer";
label = "MO"; label = "MO";
#binding-cells = <1>; #binding-cells = <1>;

View file

@ -6,7 +6,7 @@
/ { / {
behaviors { behaviors {
none: behavior_none { /omit-if-no-ref/ none: behavior_none {
compatible = "zmk,behavior-none"; compatible = "zmk,behavior-none";
label = "NONE"; label = "NONE";
#binding-cells = <0>; #binding-cells = <0>;

View file

@ -6,7 +6,7 @@
/ { / {
behaviors { behaviors {
out: behavior_outputs { /omit-if-no-ref/ out: behavior_outputs {
compatible = "zmk,behavior-outputs"; compatible = "zmk,behavior-outputs";
label = "OUTPUTS"; label = "OUTPUTS";
#binding-cells = <1>; #binding-cells = <1>;

View file

@ -8,13 +8,13 @@
/ { / {
behaviors { behaviors {
reset: behavior_reset { /omit-if-no-ref/ reset: behavior_reset {
compatible = "zmk,behavior-reset"; compatible = "zmk,behavior-reset";
label = "RESET"; label = "RESET";
#binding-cells = <0>; #binding-cells = <0>;
}; };
bootloader: behavior_reset_dfu { /omit-if-no-ref/ bootloader: behavior_reset_dfu {
compatible = "zmk,behavior-reset"; compatible = "zmk,behavior-reset";
label = "BOOTLOADER_RESET"; label = "BOOTLOADER_RESET";
type = <RST_UF2>; type = <RST_UF2>;

View file

@ -6,7 +6,7 @@
/ { / {
behaviors { behaviors {
rgb_ug: behavior_rgb_underglow { /omit-if-no-ref/ rgb_ug: behavior_rgb_underglow {
compatible = "zmk,behavior-rgb-underglow"; compatible = "zmk,behavior-rgb-underglow";
label = "RGB_UNDERGLOW"; label = "RGB_UNDERGLOW";
#binding-cells = <2>; #binding-cells = <2>;

View file

@ -7,7 +7,7 @@
/ { / {
behaviors { behaviors {
/* DEPRECATED: `inc_dec_cp` will be removed in the future */ /* DEPRECATED: `inc_dec_cp` will be removed in the future */
inc_dec_cp: inc_dec_kp: behavior_sensor_rotate_key_press { /omit-if-no-ref/ inc_dec_cp: inc_dec_kp: behavior_sensor_rotate_key_press {
compatible = "zmk,behavior-sensor-rotate-key-press"; compatible = "zmk,behavior-sensor-rotate-key-press";
label = "ENC_KEY_PRESS"; label = "ENC_KEY_PRESS";
#sensor-binding-cells = <2>; #sensor-binding-cells = <2>;

View file

@ -6,14 +6,14 @@
/ { / {
behaviors { behaviors {
sk: behavior_sticky_key { /omit-if-no-ref/ sk: behavior_sticky_key {
compatible = "zmk,behavior-sticky-key"; compatible = "zmk,behavior-sticky-key";
label = "STICKY_KEY"; label = "STICKY_KEY";
#binding-cells = <1>; #binding-cells = <1>;
release-after-ms = <1000>; release-after-ms = <1000>;
bindings = <&kp>; bindings = <&kp>;
}; };
sl: behavior_sticky_layer { /omit-if-no-ref/ sl: behavior_sticky_layer {
compatible = "zmk,behavior-sticky-key"; compatible = "zmk,behavior-sticky-key";
label = "STICKY_LAYER"; label = "STICKY_LAYER";
#binding-cells = <1>; #binding-cells = <1>;

View file

@ -6,7 +6,7 @@
/ { / {
behaviors { behaviors {
to: behavior_to_layer { /omit-if-no-ref/ to: behavior_to_layer {
compatible = "zmk,behavior-to-layer"; compatible = "zmk,behavior-to-layer";
label = "TO_LAYER"; label = "TO_LAYER";
#binding-cells = <1>; #binding-cells = <1>;

View file

@ -6,7 +6,7 @@
/ { / {
behaviors { behaviors {
tog: behavior_toggle_layer { /omit-if-no-ref/ tog: behavior_toggle_layer {
compatible = "zmk,behavior-toggle-layer"; compatible = "zmk,behavior-toggle-layer";
label = "TOGGLE_LAYER"; label = "TOGGLE_LAYER";
#binding-cells = <1>; #binding-cells = <1>;

View file

@ -6,7 +6,7 @@
/ { / {
behaviors { behaviors {
trans: behavior_transparent { /omit-if-no-ref/ trans: behavior_transparent {
compatible = "zmk,behavior-transparent"; compatible = "zmk,behavior-transparent";
label = "TRANS"; label = "TRANS";
#binding-cells = <0>; #binding-cells = <0>;

View file

@ -11,7 +11,14 @@ properties:
bindings: bindings:
type: phandles type: phandles
required: true required: true
tapping_term_ms: tapping-term-ms:
type: int
tapping_term_ms: # deprecated
type: int
quick-tap-ms:
type: int
default: -1
quick_tap_ms: # deprecated
type: int type: int
flavor: flavor:
type: string type: string
@ -21,3 +28,5 @@ properties:
- "hold-preferred" - "hold-preferred"
- "balanced" - "balanced"
- "tap-preferred" - "tap-preferred"
retro-tap:
type: boolean

View file

@ -20,3 +20,6 @@ child-binding:
default: 50 default: 50
slow-release: slow-release:
type: boolean type: boolean
layers:
type: array
default: [-1]

View file

@ -15,3 +15,7 @@ properties:
label: label:
type: string type: string
required: true required: true
init-delay-ms:
type: int
description: Number of milliseconds to delay after initializing driver
required: false

View file

@ -27,6 +27,7 @@ typedef int (*behavior_sensor_keymap_binding_callback_t)(struct zmk_behavior_bin
int64_t timestamp); int64_t timestamp);
__subsystem struct behavior_driver_api { __subsystem struct behavior_driver_api {
behavior_keymap_binding_callback_t binding_convert_central_state_dependent_params;
behavior_keymap_binding_callback_t binding_pressed; behavior_keymap_binding_callback_t binding_pressed;
behavior_keymap_binding_callback_t binding_released; behavior_keymap_binding_callback_t binding_released;
behavior_sensor_keymap_binding_callback_t sensor_binding_triggered; behavior_sensor_keymap_binding_callback_t sensor_binding_triggered;
@ -35,6 +36,30 @@ __subsystem struct behavior_driver_api {
* @endcond * @endcond
*/ */
/**
* @brief Handle the keymap binding which needs to be converted from relative "toggle" to absolute
* "turn on"
* @param binding Pointer to the details so of the binding
* @param event The event that triggered use of the binding
*
* @retval 0 If successful.
* @retval Negative errno code if failure.
*/
__syscall int behavior_keymap_binding_convert_central_state_dependent_params(
struct zmk_behavior_binding *binding, struct zmk_behavior_binding_event event);
static inline int z_impl_behavior_keymap_binding_convert_central_state_dependent_params(
struct zmk_behavior_binding *binding, struct zmk_behavior_binding_event event) {
const struct device *dev = device_get_binding(binding->behavior_dev);
const struct behavior_driver_api *api = (const struct behavior_driver_api *)dev->api;
if (api->binding_convert_central_state_dependent_params == NULL) {
return 0;
}
return api->binding_convert_central_state_dependent_params(binding, event);
}
/** /**
* @brief Handle the keymap binding being pressed * @brief Handle the keymap binding being pressed
* @param dev Pointer to the device structure for the driver instance. * @param dev Pointer to the device structure for the driver instance.

View file

@ -768,6 +768,7 @@
/* Keyboard Left Shift */ /* Keyboard Left Shift */
#define LEFT_SHIFT (HID_USAGE(HID_USAGE_KEY, HID_USAGE_KEY_KEYBOARD_LEFTSHIFT)) #define LEFT_SHIFT (HID_USAGE(HID_USAGE_KEY, HID_USAGE_KEY_KEYBOARD_LEFTSHIFT))
#define LSHIFT (LEFT_SHIFT)
#define LSHFT (LEFT_SHIFT) #define LSHFT (LEFT_SHIFT)
#define LSFT (LEFT_SHIFT) // WARNING: DEPRECATED (DO NOT USE) #define LSFT (LEFT_SHIFT) // WARNING: DEPRECATED (DO NOT USE)
@ -792,6 +793,7 @@
/* Keyboard Right Shift */ /* Keyboard Right Shift */
#define RIGHT_SHIFT (HID_USAGE(HID_USAGE_KEY, HID_USAGE_KEY_KEYBOARD_RIGHTSHIFT)) #define RIGHT_SHIFT (HID_USAGE(HID_USAGE_KEY, HID_USAGE_KEY_KEYBOARD_RIGHTSHIFT))
#define RSHIFT (RIGHT_SHIFT)
#define RSHFT (RIGHT_SHIFT) #define RSHFT (RIGHT_SHIFT)
#define RSFT (RIGHT_SHIFT) // WARNING: DEPRECATED (DO NOT USE) #define RSFT (RIGHT_SHIFT) // WARNING: DEPRECATED (DO NOT USE)

View file

@ -5,19 +5,23 @@
*/ */
#define RGB_TOG_CMD 0 #define RGB_TOG_CMD 0
#define RGB_HUI_CMD 1 #define RGB_ON_CMD 1
#define RGB_HUD_CMD 2 #define RGB_OFF_CMD 2
#define RGB_SAI_CMD 3 #define RGB_HUI_CMD 3
#define RGB_SAD_CMD 4 #define RGB_HUD_CMD 4
#define RGB_BRI_CMD 5 #define RGB_SAI_CMD 5
#define RGB_BRD_CMD 6 #define RGB_SAD_CMD 6
#define RGB_SPI_CMD 7 #define RGB_BRI_CMD 7
#define RGB_SPD_CMD 8 #define RGB_BRD_CMD 8
#define RGB_EFF_CMD 9 #define RGB_SPI_CMD 9
#define RGB_EFR_CMD 10 #define RGB_SPD_CMD 10
#define RGB_COLOR_HSB_CMD 11 #define RGB_EFF_CMD 11
#define RGB_EFR_CMD 12
#define RGB_COLOR_HSB_CMD 13
#define RGB_TOG RGB_TOG_CMD 0 #define RGB_TOG RGB_TOG_CMD 0
#define RGB_ON RGB_ON_CMD 0
#define RGB_OFF RGB_OFF_CMD 0
#define RGB_HUI RGB_HUI_CMD 0 #define RGB_HUI RGB_HUI_CMD 0
#define RGB_HUD RGB_HUD_CMD 0 #define RGB_HUD RGB_HUD_CMD 0
#define RGB_SAI RGB_SAI_CMD 0 #define RGB_SAI RGB_SAI_CMD 0
@ -28,5 +32,6 @@
#define RGB_SPD RGB_SPD_CMD 0 #define RGB_SPD RGB_SPD_CMD 0
#define RGB_EFF RGB_EFF_CMD 0 #define RGB_EFF RGB_EFF_CMD 0
#define RGB_EFR RGB_EFR_CMD 0 #define RGB_EFR RGB_EFR_CMD 0
#define RGB_COLOR_HSB(h, s, v) RGB_COLOR_HSB_CMD(((h) << 16) + ((s) << 8) + (v)) #define RGB_COLOR_HSB_VAL(h, s, v) (((h) << 16) + ((s) << 8) + (v))
#define RGB_COLOR_HSB(h, s, v) RGB_COLOR_HSB_CMD##(RGB_COLOR_HSB_VAL(h, s, v))
#define RGB_COLOR_HSV RGB_COLOR_HSB #define RGB_COLOR_HSV RGB_COLOR_HSB

View file

@ -0,0 +1,9 @@
/*
* Copyright (c) 2021 The ZMK Contributors
*
* SPDX-License-Identifier: MIT
*/
#pragma once
uint8_t zmk_battery_state_of_charge();

View file

@ -19,3 +19,12 @@ int zmk_keymap_layer_to(uint8_t layer);
const char *zmk_keymap_layer_label(uint8_t layer); const char *zmk_keymap_layer_label(uint8_t layer);
int zmk_keymap_position_state_changed(uint32_t position, bool pressed, int64_t timestamp); int zmk_keymap_position_state_changed(uint32_t position, bool pressed, int64_t timestamp);
#define ZMK_KEYMAP_EXTRACT_BINDING(idx, drv_inst) \
{ \
.behavior_dev = DT_LABEL(DT_PHANDLE_BY_IDX(drv_inst, bindings, idx)), \
.param1 = COND_CODE_0(DT_PHA_HAS_CELL_AT_IDX(drv_inst, bindings, idx, param1), (0), \
(DT_PHA_BY_IDX(drv_inst, bindings, idx, param1))), \
.param2 = COND_CODE_0(DT_PHA_HAS_CELL_AT_IDX(drv_inst, bindings, idx, param2), (0), \
(DT_PHA_BY_IDX(drv_inst, bindings, idx, param2))), \
}

View file

@ -6,10 +6,22 @@
#pragma once #pragma once
struct zmk_led_hsb {
uint16_t h;
uint8_t s;
uint8_t b;
};
int zmk_rgb_underglow_toggle(); int zmk_rgb_underglow_toggle();
int zmk_rgb_underglow_get_state(bool *state);
int zmk_rgb_underglow_on();
int zmk_rgb_underglow_off();
int zmk_rgb_underglow_cycle_effect(int direction); int zmk_rgb_underglow_cycle_effect(int direction);
struct zmk_led_hsb zmk_rgb_underglow_calc_hue(int direction);
struct zmk_led_hsb zmk_rgb_underglow_calc_sat(int direction);
struct zmk_led_hsb zmk_rgb_underglow_calc_brt(int direction);
int zmk_rgb_underglow_change_hue(int direction); int zmk_rgb_underglow_change_hue(int direction);
int zmk_rgb_underglow_change_sat(int direction); int zmk_rgb_underglow_change_sat(int direction);
int zmk_rgb_underglow_change_brt(int direction); int zmk_rgb_underglow_change_brt(int direction);
int zmk_rgb_underglow_change_spd(int direction); int zmk_rgb_underglow_change_spd(int direction);
int zmk_rgb_underglow_set_hsb(uint16_t hue, uint8_t saturation, uint8_t brightness); int zmk_rgb_underglow_set_hsb(struct zmk_led_hsb color);

View file

@ -17,7 +17,7 @@ testcases=$(find $path -name native_posix.keymap -exec dirname \{\} \;)
num_cases=$(echo "$testcases" | wc -l) num_cases=$(echo "$testcases" | wc -l)
if [ $num_cases -gt 1 ]; then if [ $num_cases -gt 1 ]; then
echo "" > ./build/tests/pass-fail.log echo "" > ./build/tests/pass-fail.log
echo "$testcases" | xargs -L 1 -P 4 ./run-test.sh echo "$testcases" | xargs -L 1 -P ${J:-4} ./run-test.sh
err=$? err=$?
sort -k2 ./build/tests/pass-fail.log sort -k2 ./build/tests/pass-fail.log
exit $err exit $err

View file

@ -15,10 +15,15 @@
LOG_MODULE_DECLARE(zmk, CONFIG_ZMK_LOG_LEVEL); LOG_MODULE_DECLARE(zmk, CONFIG_ZMK_LOG_LEVEL);
#include <zmk/event_manager.h> #include <zmk/event_manager.h>
#include <zmk/battery.h>
#include <zmk/events/battery_state_changed.h> #include <zmk/events/battery_state_changed.h>
const struct device *battery; const struct device *battery;
static uint8_t last_state_of_charge = 0;
uint8_t zmk_battery_state_of_charge() { return last_state_of_charge; }
static int zmk_battery_update(const struct device *battery) { static int zmk_battery_update(const struct device *battery) {
struct sensor_value state_of_charge; struct sensor_value state_of_charge;
@ -36,17 +41,23 @@ static int zmk_battery_update(const struct device *battery) {
return rc; return rc;
} }
LOG_DBG("Setting BAS GATT battery level to %d.", state_of_charge.val1); if (last_state_of_charge != state_of_charge.val1) {
last_state_of_charge = state_of_charge.val1;
rc = bt_bas_set_battery_level(state_of_charge.val1); LOG_DBG("Setting BAS GATT battery level to %d.", last_state_of_charge);
rc = bt_bas_set_battery_level(last_state_of_charge);
if (rc != 0) { if (rc != 0) {
LOG_WRN("Failed to set BAS GATT battery level (err %d)", rc); LOG_WRN("Failed to set BAS GATT battery level (err %d)", rc);
return rc; return rc;
} }
return ZMK_EVENT_RAISE(new_zmk_battery_state_changed( rc = ZMK_EVENT_RAISE(new_zmk_battery_state_changed(
(struct zmk_battery_state_changed){.state_of_charge = state_of_charge.val1})); (struct zmk_battery_state_changed){.state_of_charge = last_state_of_charge}));
}
return rc;
} }
static void zmk_battery_work(struct k_work *work) { static void zmk_battery_work(struct k_work *work) {

View file

@ -17,6 +17,8 @@ LOG_MODULE_DECLARE(zmk, CONFIG_ZMK_LOG_LEVEL);
#include <zmk/ble.h> #include <zmk/ble.h>
#if DT_HAS_COMPAT_STATUS_OKAY(DT_DRV_COMPAT)
static int on_keymap_binding_pressed(struct zmk_behavior_binding *binding, static int on_keymap_binding_pressed(struct zmk_behavior_binding *binding,
struct zmk_behavior_binding_event event) { struct zmk_behavior_binding_event event) {
switch (binding->param1) { switch (binding->param1) {
@ -49,3 +51,5 @@ static const struct behavior_driver_api behavior_bt_driver_api = {
DEVICE_AND_API_INIT(behavior_bt, DT_INST_LABEL(0), behavior_bt_init, NULL, NULL, APPLICATION, DEVICE_AND_API_INIT(behavior_bt, DT_INST_LABEL(0), behavior_bt_init, NULL, NULL, APPLICATION,
CONFIG_KERNEL_INIT_PRIORITY_DEFAULT, &behavior_bt_driver_api); CONFIG_KERNEL_INIT_PRIORITY_DEFAULT, &behavior_bt_driver_api);
#endif /* DT_HAS_COMPAT_STATUS_OKAY(DT_DRV_COMPAT) */

View file

@ -16,6 +16,24 @@
#include <logging/log.h> #include <logging/log.h>
LOG_MODULE_DECLARE(zmk, CONFIG_ZMK_LOG_LEVEL); LOG_MODULE_DECLARE(zmk, CONFIG_ZMK_LOG_LEVEL);
#if DT_HAS_COMPAT_STATUS_OKAY(DT_DRV_COMPAT)
static int
on_keymap_binding_convert_central_state_dependent_params(struct zmk_behavior_binding *binding,
struct zmk_behavior_binding_event event) {
const struct device *ext_power = device_get_binding("EXT_POWER");
if (ext_power == NULL) {
LOG_ERR("Unable to retrieve ext_power device: %d", binding->param1);
return -EIO;
}
if (binding->param1 == EXT_POWER_TOGGLE_CMD) {
binding->param1 = ext_power_get(ext_power) > 0 ? EXT_POWER_OFF_CMD : EXT_POWER_ON_CMD;
}
return 0;
}
static int on_keymap_binding_pressed(struct zmk_behavior_binding *binding, static int on_keymap_binding_pressed(struct zmk_behavior_binding *binding,
struct zmk_behavior_binding_event event) { struct zmk_behavior_binding_event event) {
const struct device *ext_power = device_get_binding("EXT_POWER"); const struct device *ext_power = device_get_binding("EXT_POWER");
@ -49,9 +67,13 @@ static int on_keymap_binding_released(struct zmk_behavior_binding *binding,
static int behavior_ext_power_init(const struct device *dev) { return 0; }; static int behavior_ext_power_init(const struct device *dev) { return 0; };
static const struct behavior_driver_api behavior_ext_power_driver_api = { static const struct behavior_driver_api behavior_ext_power_driver_api = {
.binding_convert_central_state_dependent_params =
on_keymap_binding_convert_central_state_dependent_params,
.binding_pressed = on_keymap_binding_pressed, .binding_pressed = on_keymap_binding_pressed,
.binding_released = on_keymap_binding_released, .binding_released = on_keymap_binding_released,
}; };
DEVICE_AND_API_INIT(behavior_ext_power, DT_INST_LABEL(0), behavior_ext_power_init, NULL, NULL, DEVICE_AND_API_INIT(behavior_ext_power, DT_INST_LABEL(0), behavior_ext_power_init, NULL, NULL,
APPLICATION, CONFIG_APPLICATION_INIT_PRIORITY, &behavior_ext_power_driver_api); APPLICATION, CONFIG_APPLICATION_INIT_PRIORITY, &behavior_ext_power_driver_api);
#endif /* DT_HAS_COMPAT_STATUS_OKAY(DT_DRV_COMPAT) */

View file

@ -18,10 +18,11 @@
#include <zmk/events/position_state_changed.h> #include <zmk/events/position_state_changed.h>
#include <zmk/events/keycode_state_changed.h> #include <zmk/events/keycode_state_changed.h>
#include <zmk/behavior.h> #include <zmk/behavior.h>
#include <zmk/keymap.h>
LOG_MODULE_DECLARE(zmk, CONFIG_ZMK_LOG_LEVEL); LOG_MODULE_DECLARE(zmk, CONFIG_ZMK_LOG_LEVEL);
#if DT_NODE_EXISTS(DT_DRV_INST(0)) #if DT_HAS_COMPAT_STATUS_OKAY(DT_DRV_COMPAT)
#define ZMK_BHV_HOLD_TAP_MAX_HELD 10 #define ZMK_BHV_HOLD_TAP_MAX_HELD 10
#define ZMK_BHV_HOLD_TAP_MAX_CAPTURED_EVENTS 40 #define ZMK_BHV_HOLD_TAP_MAX_CAPTURED_EVENTS 40
@ -30,31 +31,42 @@ LOG_MODULE_DECLARE(zmk, CONFIG_ZMK_LOG_LEVEL);
#define ZMK_BHV_HOLD_TAP_POSITION_NOT_USED 9999 #define ZMK_BHV_HOLD_TAP_POSITION_NOT_USED 9999
enum flavor { enum flavor {
ZMK_BHV_HOLD_TAP_FLAVOR_HOLD_PREFERRED = 0, FLAVOR_HOLD_PREFERRED,
ZMK_BHV_HOLD_TAP_FLAVOR_BALANCED = 1, FLAVOR_BALANCED,
ZMK_BHV_HOLD_TAP_FLAVOR_TAP_PREFERRED = 2, FLAVOR_TAP_PREFERRED,
}; };
struct behavior_hold_tap_behaviors { enum status {
struct zmk_behavior_binding tap; STATUS_UNDECIDED,
struct zmk_behavior_binding hold; STATUS_TAP,
STATUS_HOLD_INTERRUPT,
STATUS_HOLD_TIMER,
};
enum decision_moment {
HT_KEY_UP,
HT_OTHER_KEY_DOWN,
HT_OTHER_KEY_UP,
HT_TIMER_EVENT,
HT_QUICK_TAP,
}; };
struct behavior_hold_tap_config { struct behavior_hold_tap_config {
int tapping_term_ms; int tapping_term_ms;
struct behavior_hold_tap_behaviors *behaviors; char *hold_behavior_dev;
char *tap_behavior_dev;
int quick_tap_ms;
enum flavor flavor; enum flavor flavor;
bool retro_tap;
}; };
// this data is specific for each hold-tap // this data is specific for each hold-tap
struct active_hold_tap { struct active_hold_tap {
int32_t position; int32_t position;
// todo: move these params into the config->behaviors->tap and
uint32_t param_hold; uint32_t param_hold;
uint32_t param_tap; uint32_t param_tap;
int64_t timestamp; int64_t timestamp;
bool is_decided; enum status status;
bool is_hold;
const struct behavior_hold_tap_config *config; const struct behavior_hold_tap_config *config;
struct k_delayed_work work; struct k_delayed_work work;
bool work_is_cancelled; bool work_is_cancelled;
@ -70,6 +82,24 @@ struct active_hold_tap active_hold_taps[ZMK_BHV_HOLD_TAP_MAX_HELD] = {};
// We capture most position_state_changed events and some modifiers_state_changed events. // We capture most position_state_changed events and some modifiers_state_changed events.
const zmk_event_t *captured_events[ZMK_BHV_HOLD_TAP_MAX_CAPTURED_EVENTS] = {}; const zmk_event_t *captured_events[ZMK_BHV_HOLD_TAP_MAX_CAPTURED_EVENTS] = {};
// Keep track of which key was tapped most recently for 'quick_tap_ms'
struct last_tapped {
int32_t position;
int64_t tap_deadline;
};
struct last_tapped last_tapped;
static void store_last_tapped(struct active_hold_tap *hold_tap) {
last_tapped.position = hold_tap->position;
last_tapped.tap_deadline = hold_tap->timestamp + hold_tap->config->quick_tap_ms;
}
static bool is_quick_tap(struct active_hold_tap *hold_tap) {
return last_tapped.position == hold_tap->position &&
last_tapped.tap_deadline > hold_tap->timestamp;
}
static int capture_event(const zmk_event_t *event) { static int capture_event(const zmk_event_t *event) {
for (int i = 0; i < ZMK_BHV_HOLD_TAP_MAX_CAPTURED_EVENTS; i++) { for (int i = 0; i < ZMK_BHV_HOLD_TAP_MAX_CAPTURED_EVENTS; i++) {
if (captured_events[i] == NULL) { if (captured_events[i] == NULL) {
@ -171,8 +201,7 @@ static struct active_hold_tap *store_hold_tap(uint32_t position, uint32_t param_
continue; continue;
} }
active_hold_taps[i].position = position; active_hold_taps[i].position = position;
active_hold_taps[i].is_decided = false; active_hold_taps[i].status = STATUS_UNDECIDED;
active_hold_taps[i].is_hold = false;
active_hold_taps[i].config = config; active_hold_taps[i].config = config;
active_hold_taps[i].param_hold = param_hold; active_hold_taps[i].param_hold = param_hold;
active_hold_taps[i].param_tap = param_tap; active_hold_taps[i].param_tap = param_tap;
@ -184,29 +213,24 @@ static struct active_hold_tap *store_hold_tap(uint32_t position, uint32_t param_
static void clear_hold_tap(struct active_hold_tap *hold_tap) { static void clear_hold_tap(struct active_hold_tap *hold_tap) {
hold_tap->position = ZMK_BHV_HOLD_TAP_POSITION_NOT_USED; hold_tap->position = ZMK_BHV_HOLD_TAP_POSITION_NOT_USED;
hold_tap->is_decided = false; hold_tap->status = STATUS_UNDECIDED;
hold_tap->is_hold = false;
hold_tap->work_is_cancelled = false; hold_tap->work_is_cancelled = false;
} }
enum decision_moment {
HT_KEY_UP = 0,
HT_OTHER_KEY_DOWN = 1,
HT_OTHER_KEY_UP = 2,
HT_TIMER_EVENT = 3,
};
static void decide_balanced(struct active_hold_tap *hold_tap, enum decision_moment event) { static void decide_balanced(struct active_hold_tap *hold_tap, enum decision_moment event) {
switch (event) { switch (event) {
case HT_KEY_UP: case HT_KEY_UP:
hold_tap->is_hold = 0; hold_tap->status = STATUS_TAP;
hold_tap->is_decided = true; return;
break;
case HT_OTHER_KEY_UP: case HT_OTHER_KEY_UP:
hold_tap->status = STATUS_HOLD_INTERRUPT;
return;
case HT_TIMER_EVENT: case HT_TIMER_EVENT:
hold_tap->is_hold = 1; hold_tap->status = STATUS_HOLD_TIMER;
hold_tap->is_decided = true; return;
break; case HT_QUICK_TAP:
hold_tap->status = STATUS_TAP;
return;
default: default:
return; return;
} }
@ -215,13 +239,14 @@ static void decide_balanced(struct active_hold_tap *hold_tap, enum decision_mome
static void decide_tap_preferred(struct active_hold_tap *hold_tap, enum decision_moment event) { static void decide_tap_preferred(struct active_hold_tap *hold_tap, enum decision_moment event) {
switch (event) { switch (event) {
case HT_KEY_UP: case HT_KEY_UP:
hold_tap->is_hold = 0; hold_tap->status = STATUS_TAP;
hold_tap->is_decided = true; return;
break;
case HT_TIMER_EVENT: case HT_TIMER_EVENT:
hold_tap->is_hold = 1; hold_tap->status = STATUS_HOLD_TIMER;
hold_tap->is_decided = true; return;
break; case HT_QUICK_TAP:
hold_tap->status = STATUS_TAP;
return;
default: default:
return; return;
} }
@ -230,33 +255,113 @@ static void decide_tap_preferred(struct active_hold_tap *hold_tap, enum decision
static void decide_hold_preferred(struct active_hold_tap *hold_tap, enum decision_moment event) { static void decide_hold_preferred(struct active_hold_tap *hold_tap, enum decision_moment event) {
switch (event) { switch (event) {
case HT_KEY_UP: case HT_KEY_UP:
hold_tap->is_hold = 0; hold_tap->status = STATUS_TAP;
hold_tap->is_decided = true; return;
break;
case HT_OTHER_KEY_DOWN: case HT_OTHER_KEY_DOWN:
hold_tap->status = STATUS_HOLD_INTERRUPT;
return;
case HT_TIMER_EVENT: case HT_TIMER_EVENT:
hold_tap->is_hold = 1; hold_tap->status = STATUS_HOLD_TIMER;
hold_tap->is_decided = true; return;
break; case HT_QUICK_TAP:
hold_tap->status = STATUS_TAP;
return;
default: default:
return; return;
} }
} }
static inline char *flavor_str(enum flavor flavor) { static inline const char *flavor_str(enum flavor flavor) {
switch (flavor) { switch (flavor) {
case ZMK_BHV_HOLD_TAP_FLAVOR_HOLD_PREFERRED: case FLAVOR_HOLD_PREFERRED:
return "hold-preferred"; return "hold-preferred";
case ZMK_BHV_HOLD_TAP_FLAVOR_BALANCED: case FLAVOR_BALANCED:
return "balanced"; return "balanced";
case ZMK_BHV_HOLD_TAP_FLAVOR_TAP_PREFERRED: case FLAVOR_TAP_PREFERRED:
return "tap-preferred"; return "tap-preferred";
} default:
return "UNKNOWN FLAVOR"; return "UNKNOWN FLAVOR";
} }
}
static void decide_hold_tap(struct active_hold_tap *hold_tap, enum decision_moment event_type) { static inline const char *status_str(enum status status) {
if (hold_tap->is_decided) { switch (status) {
case STATUS_UNDECIDED:
return "undecided";
case STATUS_HOLD_TIMER:
return "hold-timer";
case STATUS_HOLD_INTERRUPT:
return "hold-interrupt";
case STATUS_TAP:
return "tap";
default:
return "UNKNOWN STATUS";
}
}
static inline const char *decision_moment_str(enum decision_moment decision_moment) {
switch (decision_moment) {
case HT_KEY_UP:
return "key-up";
case HT_OTHER_KEY_DOWN:
return "other-key-down";
case HT_OTHER_KEY_UP:
return "other-key-up";
case HT_QUICK_TAP:
return "quick-tap";
case HT_TIMER_EVENT:
return "timer";
default:
return "UNKNOWN STATUS";
}
}
static int press_binding(struct active_hold_tap *hold_tap) {
if (hold_tap->config->retro_tap && hold_tap->status == STATUS_HOLD_TIMER) {
return 0;
}
struct zmk_behavior_binding_event event = {
.position = hold_tap->position,
.timestamp = hold_tap->timestamp,
};
struct zmk_behavior_binding binding = {0};
if (hold_tap->status == STATUS_HOLD_TIMER || hold_tap->status == STATUS_HOLD_INTERRUPT) {
binding.behavior_dev = hold_tap->config->hold_behavior_dev;
binding.param1 = hold_tap->param_hold;
} else {
binding.behavior_dev = hold_tap->config->tap_behavior_dev;
binding.param1 = hold_tap->param_tap;
store_last_tapped(hold_tap);
}
return behavior_keymap_binding_pressed(&binding, event);
}
static int release_binding(struct active_hold_tap *hold_tap) {
if (hold_tap->config->retro_tap && hold_tap->status == STATUS_HOLD_TIMER) {
return 0;
}
struct zmk_behavior_binding_event event = {
.position = hold_tap->position,
.timestamp = hold_tap->timestamp,
};
struct zmk_behavior_binding binding = {0};
if (hold_tap->status == STATUS_HOLD_TIMER || hold_tap->status == STATUS_HOLD_INTERRUPT) {
binding.behavior_dev = hold_tap->config->hold_behavior_dev;
binding.param1 = hold_tap->param_hold;
} else {
binding.behavior_dev = hold_tap->config->tap_behavior_dev;
binding.param1 = hold_tap->param_tap;
}
return behavior_keymap_binding_released(&binding, event);
}
static void decide_hold_tap(struct active_hold_tap *hold_tap,
enum decision_moment decision_moment) {
if (hold_tap->status != STATUS_UNDECIDED) {
return; return;
} }
@ -266,41 +371,55 @@ static void decide_hold_tap(struct active_hold_tap *hold_tap, enum decision_mome
} }
switch (hold_tap->config->flavor) { switch (hold_tap->config->flavor) {
case ZMK_BHV_HOLD_TAP_FLAVOR_HOLD_PREFERRED: case FLAVOR_HOLD_PREFERRED:
decide_hold_preferred(hold_tap, event_type); decide_hold_preferred(hold_tap, decision_moment);
case ZMK_BHV_HOLD_TAP_FLAVOR_BALANCED: case FLAVOR_BALANCED:
decide_balanced(hold_tap, event_type); decide_balanced(hold_tap, decision_moment);
case ZMK_BHV_HOLD_TAP_FLAVOR_TAP_PREFERRED: case FLAVOR_TAP_PREFERRED:
decide_tap_preferred(hold_tap, event_type); decide_tap_preferred(hold_tap, decision_moment);
} }
if (!hold_tap->is_decided) { if (hold_tap->status == STATUS_UNDECIDED) {
return; return;
} }
LOG_DBG("%d decided %s (%s event %d)", hold_tap->position, hold_tap->is_hold ? "hold" : "tap", LOG_DBG("%d decided %s (%s decision moment %s)", hold_tap->position,
flavor_str(hold_tap->config->flavor), event_type); status_str(hold_tap->status), flavor_str(hold_tap->config->flavor),
decision_moment_str(decision_moment));
undecided_hold_tap = NULL; undecided_hold_tap = NULL;
press_binding(hold_tap);
struct zmk_behavior_binding_event event = {
.position = hold_tap->position,
.timestamp = hold_tap->timestamp,
};
struct zmk_behavior_binding binding;
if (hold_tap->is_hold) {
binding.behavior_dev = hold_tap->config->behaviors->hold.behavior_dev;
binding.param1 = hold_tap->param_hold;
binding.param2 = 0;
} else {
binding.behavior_dev = hold_tap->config->behaviors->tap.behavior_dev;
binding.param1 = hold_tap->param_tap;
binding.param2 = 0;
}
behavior_keymap_binding_pressed(&binding, event);
release_captured_events(); release_captured_events();
} }
static void decide_retro_tap(struct active_hold_tap *hold_tap) {
if (!hold_tap->config->retro_tap) {
return;
}
if (hold_tap->status == STATUS_HOLD_TIMER) {
release_binding(hold_tap);
LOG_DBG("%d retro tap", hold_tap->position);
hold_tap->status = STATUS_TAP;
press_binding(hold_tap);
return;
}
}
static void update_hold_status_for_retro_tap(uint32_t ignore_position) {
for (int i = 0; i < ZMK_BHV_HOLD_TAP_MAX_HELD; i++) {
struct active_hold_tap *hold_tap = &active_hold_taps[i];
if (hold_tap->position == ignore_position ||
hold_tap->position == ZMK_BHV_HOLD_TAP_POSITION_NOT_USED ||
hold_tap->config->retro_tap == false) {
continue;
}
if (hold_tap->status == STATUS_HOLD_TIMER) {
LOG_DBG("Update hold tap %d status to hold-interrupt", hold_tap->position);
hold_tap->status = STATUS_HOLD_INTERRUPT;
press_binding(hold_tap);
}
}
}
static int on_hold_tap_binding_pressed(struct zmk_behavior_binding *binding, static int on_hold_tap_binding_pressed(struct zmk_behavior_binding *binding,
struct zmk_behavior_binding_event event) { struct zmk_behavior_binding_event event) {
const struct device *dev = device_get_binding(binding->behavior_dev); const struct device *dev = device_get_binding(binding->behavior_dev);
@ -323,12 +442,14 @@ static int on_hold_tap_binding_pressed(struct zmk_behavior_binding *binding,
LOG_DBG("%d new undecided hold_tap", event.position); LOG_DBG("%d new undecided hold_tap", event.position);
undecided_hold_tap = hold_tap; undecided_hold_tap = hold_tap;
if (is_quick_tap(hold_tap)) {
decide_hold_tap(hold_tap, HT_QUICK_TAP);
}
// if this behavior was queued we have to adjust the timer to only // if this behavior was queued we have to adjust the timer to only
// wait for the remaining time. // wait for the remaining time.
int32_t tapping_term_ms_left = (hold_tap->timestamp + cfg->tapping_term_ms) - k_uptime_get(); int32_t tapping_term_ms_left = (hold_tap->timestamp + cfg->tapping_term_ms) - k_uptime_get();
if (tapping_term_ms_left > 0) {
k_delayed_work_submit(&hold_tap->work, K_MSEC(tapping_term_ms_left)); k_delayed_work_submit(&hold_tap->work, K_MSEC(tapping_term_ms_left));
}
return ZMK_BEHAVIOR_OPAQUE; return ZMK_BEHAVIOR_OPAQUE;
} }
@ -349,24 +470,8 @@ static int on_hold_tap_binding_released(struct zmk_behavior_binding *binding,
} }
decide_hold_tap(hold_tap, HT_KEY_UP); decide_hold_tap(hold_tap, HT_KEY_UP);
decide_retro_tap(hold_tap);
// todo: set up the binding and data items inside of the active_hold_tap struct release_binding(hold_tap);
struct zmk_behavior_binding_event sub_behavior_data = {
.position = hold_tap->position,
.timestamp = hold_tap->timestamp,
};
struct zmk_behavior_binding sub_behavior_binding;
if (hold_tap->is_hold) {
sub_behavior_binding.behavior_dev = hold_tap->config->behaviors->hold.behavior_dev;
sub_behavior_binding.param1 = hold_tap->param_hold;
sub_behavior_binding.param2 = 0;
} else {
sub_behavior_binding.behavior_dev = hold_tap->config->behaviors->tap.behavior_dev;
sub_behavior_binding.param1 = hold_tap->param_tap;
sub_behavior_binding.param2 = 0;
}
behavior_keymap_binding_released(&sub_behavior_binding, sub_behavior_data);
if (work_cancel_result == -EINPROGRESS) { if (work_cancel_result == -EINPROGRESS) {
// let the timer handler clean up // let the timer handler clean up
@ -389,6 +494,8 @@ static const struct behavior_driver_api behavior_hold_tap_driver_api = {
static int position_state_changed_listener(const zmk_event_t *eh) { static int position_state_changed_listener(const zmk_event_t *eh) {
struct zmk_position_state_changed *ev = as_zmk_position_state_changed(eh); struct zmk_position_state_changed *ev = as_zmk_position_state_changed(eh);
update_hold_status_for_retro_tap(ev->position);
if (undecided_hold_tap == NULL) { if (undecided_hold_tap == NULL) {
LOG_DBG("%d bubble (no undecided hold_tap active)", ev->position); LOG_DBG("%d bubble (no undecided hold_tap active)", ev->position);
return ZMK_EV_EVENT_BUBBLE; return ZMK_EV_EVENT_BUBBLE;
@ -489,23 +596,14 @@ static int behavior_hold_tap_init(const struct device *dev) {
struct behavior_hold_tap_data {}; struct behavior_hold_tap_data {};
static struct behavior_hold_tap_data behavior_hold_tap_data; static struct behavior_hold_tap_data behavior_hold_tap_data;
/* todo: get rid of unused param1 and param2. */
#define _TRANSFORM_ENTRY(idx, node) \
{ \
.behavior_dev = DT_LABEL(DT_INST_PHANDLE_BY_IDX(node, bindings, idx)), \
.param1 = COND_CODE_0(DT_INST_PHA_HAS_CELL_AT_IDX(node, bindings, idx, param1), (0), \
(DT_INST_PHA_BY_IDX(node, bindings, idx, param1))), \
.param2 = COND_CODE_0(DT_INST_PHA_HAS_CELL_AT_IDX(node, bindings, idx, param2), (0), \
(DT_INST_PHA_BY_IDX(node, bindings, idx, param2))), \
},
#define KP_INST(n) \ #define KP_INST(n) \
static struct behavior_hold_tap_behaviors behavior_hold_tap_behaviors_##n = { \
.hold = _TRANSFORM_ENTRY(0, n).tap = _TRANSFORM_ENTRY(1, n)}; \
static struct behavior_hold_tap_config behavior_hold_tap_config_##n = { \ static struct behavior_hold_tap_config behavior_hold_tap_config_##n = { \
.behaviors = &behavior_hold_tap_behaviors_##n, \
.tapping_term_ms = DT_INST_PROP(n, tapping_term_ms), \ .tapping_term_ms = DT_INST_PROP(n, tapping_term_ms), \
.hold_behavior_dev = DT_LABEL(DT_INST_PHANDLE_BY_IDX(n, bindings, 0)), \
.tap_behavior_dev = DT_LABEL(DT_INST_PHANDLE_BY_IDX(n, bindings, 1)), \
.quick_tap_ms = DT_INST_PROP(n, quick_tap_ms), \
.flavor = DT_ENUM_IDX(DT_DRV_INST(n), flavor), \ .flavor = DT_ENUM_IDX(DT_DRV_INST(n), flavor), \
.retro_tap = DT_INST_PROP(n, retro_tap), \
}; \ }; \
DEVICE_AND_API_INIT(behavior_hold_tap_##n, DT_INST_LABEL(n), behavior_hold_tap_init, \ DEVICE_AND_API_INIT(behavior_hold_tap_##n, DT_INST_LABEL(n), behavior_hold_tap_init, \
&behavior_hold_tap_data, &behavior_hold_tap_config_##n, APPLICATION, \ &behavior_hold_tap_data, &behavior_hold_tap_config_##n, APPLICATION, \
@ -513,4 +611,4 @@ static struct behavior_hold_tap_data behavior_hold_tap_data;
DT_INST_FOREACH_STATUS_OKAY(KP_INST) DT_INST_FOREACH_STATUS_OKAY(KP_INST)
#endif #endif /* DT_HAS_COMPAT_STATUS_OKAY(DT_DRV_COMPAT) */

View file

@ -15,8 +15,7 @@
LOG_MODULE_DECLARE(zmk, CONFIG_ZMK_LOG_LEVEL); LOG_MODULE_DECLARE(zmk, CONFIG_ZMK_LOG_LEVEL);
struct behavior_none_config {}; #if DT_HAS_COMPAT_STATUS_OKAY(DT_DRV_COMPAT)
struct behavior_none_data {};
static int behavior_none_init(const struct device *dev) { return 0; }; static int behavior_none_init(const struct device *dev) { return 0; };
@ -35,10 +34,7 @@ static const struct behavior_driver_api behavior_none_driver_api = {
.binding_released = on_keymap_binding_released, .binding_released = on_keymap_binding_released,
}; };
static const struct behavior_none_config behavior_none_config = {}; DEVICE_AND_API_INIT(behavior_none, DT_INST_LABEL(0), behavior_none_init, NULL, NULL, APPLICATION,
CONFIG_KERNEL_INIT_PRIORITY_DEFAULT, &behavior_none_driver_api);
static struct behavior_none_data behavior_none_data; #endif /* DT_HAS_COMPAT_STATUS_OKAY(DT_DRV_COMPAT) */
DEVICE_AND_API_INIT(behavior_none, DT_INST_LABEL(0), behavior_none_init, &behavior_none_data,
&behavior_none_config, APPLICATION, CONFIG_KERNEL_INIT_PRIORITY_DEFAULT,
&behavior_none_driver_api);

View file

@ -18,6 +18,8 @@
#include <logging/log.h> #include <logging/log.h>
LOG_MODULE_DECLARE(zmk, CONFIG_ZMK_LOG_LEVEL); LOG_MODULE_DECLARE(zmk, CONFIG_ZMK_LOG_LEVEL);
#if DT_HAS_COMPAT_STATUS_OKAY(DT_DRV_COMPAT)
static int on_keymap_binding_pressed(struct zmk_behavior_binding *binding, static int on_keymap_binding_pressed(struct zmk_behavior_binding *binding,
struct zmk_behavior_binding_event event) { struct zmk_behavior_binding_event event) {
switch (binding->param1) { switch (binding->param1) {
@ -42,3 +44,5 @@ static const struct behavior_driver_api behavior_outputs_driver_api = {
DEVICE_AND_API_INIT(behavior_out, DT_INST_LABEL(0), behavior_out_init, NULL, NULL, APPLICATION, 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); CONFIG_KERNEL_INIT_PRIORITY_DEFAULT, &behavior_outputs_driver_api);
#endif /* DT_HAS_COMPAT_STATUS_OKAY(DT_DRV_COMPAT) */

View file

@ -15,6 +15,7 @@
LOG_MODULE_DECLARE(zmk, CONFIG_ZMK_LOG_LEVEL); LOG_MODULE_DECLARE(zmk, CONFIG_ZMK_LOG_LEVEL);
#if DT_HAS_COMPAT_STATUS_OKAY(DT_DRV_COMPAT)
struct behavior_reset_config { struct behavior_reset_config {
int type; int type;
}; };
@ -45,3 +46,5 @@ static const struct behavior_driver_api behavior_reset_driver_api = {
CONFIG_KERNEL_INIT_PRIORITY_DEFAULT, &behavior_reset_driver_api); CONFIG_KERNEL_INIT_PRIORITY_DEFAULT, &behavior_reset_driver_api);
DT_INST_FOREACH_STATUS_OKAY(RST_INST) DT_INST_FOREACH_STATUS_OKAY(RST_INST)
#endif /* DT_HAS_COMPAT_STATUS_OKAY(DT_DRV_COMPAT) */

View file

@ -16,13 +16,85 @@
LOG_MODULE_DECLARE(zmk, CONFIG_ZMK_LOG_LEVEL); LOG_MODULE_DECLARE(zmk, CONFIG_ZMK_LOG_LEVEL);
#if DT_HAS_COMPAT_STATUS_OKAY(DT_DRV_COMPAT)
static int behavior_rgb_underglow_init(const struct device *dev) { return 0; } static int behavior_rgb_underglow_init(const struct device *dev) { return 0; }
static int
on_keymap_binding_convert_central_state_dependent_params(struct zmk_behavior_binding *binding,
struct zmk_behavior_binding_event event) {
switch (binding->param1) {
case RGB_TOG_CMD: {
bool state;
int err = zmk_rgb_underglow_get_state(&state);
if (err) {
LOG_ERR("Failed to get RGB underglow state (err %d)", err);
return err;
}
binding->param1 = state ? RGB_OFF_CMD : RGB_ON_CMD;
break;
}
case RGB_BRI_CMD: {
struct zmk_led_hsb color = zmk_rgb_underglow_calc_brt(1);
binding->param1 = RGB_COLOR_HSB_CMD;
binding->param2 = RGB_COLOR_HSB_VAL(color.h, color.s, color.b);
break;
}
case RGB_BRD_CMD: {
struct zmk_led_hsb color = zmk_rgb_underglow_calc_brt(-1);
binding->param1 = RGB_COLOR_HSB_CMD;
binding->param2 = RGB_COLOR_HSB_VAL(color.h, color.s, color.b);
break;
}
case RGB_HUI_CMD: {
struct zmk_led_hsb color = zmk_rgb_underglow_calc_hue(1);
binding->param1 = RGB_COLOR_HSB_CMD;
binding->param2 = RGB_COLOR_HSB_VAL(color.h, color.s, color.b);
break;
}
case RGB_HUD_CMD: {
struct zmk_led_hsb color = zmk_rgb_underglow_calc_hue(-1);
binding->param1 = RGB_COLOR_HSB_CMD;
binding->param2 = RGB_COLOR_HSB_VAL(color.h, color.s, color.b);
break;
}
case RGB_SAI_CMD: {
struct zmk_led_hsb color = zmk_rgb_underglow_calc_sat(1);
binding->param1 = RGB_COLOR_HSB_CMD;
binding->param2 = RGB_COLOR_HSB_VAL(color.h, color.s, color.b);
break;
}
case RGB_SAD_CMD: {
struct zmk_led_hsb color = zmk_rgb_underglow_calc_sat(-1);
binding->param1 = RGB_COLOR_HSB_CMD;
binding->param2 = RGB_COLOR_HSB_VAL(color.h, color.s, color.b);
break;
}
default:
return 0;
}
LOG_DBG("RGB relative convert to absolute (%d/%d)", binding->param1, binding->param2);
return 0;
};
static int on_keymap_binding_pressed(struct zmk_behavior_binding *binding, static int on_keymap_binding_pressed(struct zmk_behavior_binding *binding,
struct zmk_behavior_binding_event event) { struct zmk_behavior_binding_event event) {
switch (binding->param1) { switch (binding->param1) {
case RGB_TOG_CMD: case RGB_TOG_CMD:
return zmk_rgb_underglow_toggle(); return zmk_rgb_underglow_toggle();
case RGB_ON_CMD:
return zmk_rgb_underglow_on();
case RGB_OFF_CMD:
return zmk_rgb_underglow_off();
case RGB_HUI_CMD: case RGB_HUI_CMD:
return zmk_rgb_underglow_change_hue(1); return zmk_rgb_underglow_change_hue(1);
case RGB_HUD_CMD: case RGB_HUD_CMD:
@ -44,8 +116,9 @@ static int on_keymap_binding_pressed(struct zmk_behavior_binding *binding,
case RGB_EFR_CMD: case RGB_EFR_CMD:
return zmk_rgb_underglow_cycle_effect(-1); return zmk_rgb_underglow_cycle_effect(-1);
case RGB_COLOR_HSB_CMD: case RGB_COLOR_HSB_CMD:
return zmk_rgb_underglow_set_hsb((binding->param2 >> 16) & 0xFFFF, return zmk_rgb_underglow_set_hsb((struct zmk_led_hsb){.h = (binding->param2 >> 16) & 0xFFFF,
(binding->param2 >> 8) & 0xFF, binding->param2 & 0xFF); .s = (binding->param2 >> 8) & 0xFF,
.b = binding->param2 & 0xFF});
} }
return -ENOTSUP; return -ENOTSUP;
@ -57,6 +130,8 @@ static int on_keymap_binding_released(struct zmk_behavior_binding *binding,
} }
static const struct behavior_driver_api behavior_rgb_underglow_driver_api = { static const struct behavior_driver_api behavior_rgb_underglow_driver_api = {
.binding_convert_central_state_dependent_params =
on_keymap_binding_convert_central_state_dependent_params,
.binding_pressed = on_keymap_binding_pressed, .binding_pressed = on_keymap_binding_pressed,
.binding_released = on_keymap_binding_released, .binding_released = on_keymap_binding_released,
}; };
@ -64,3 +139,5 @@ static const struct behavior_driver_api behavior_rgb_underglow_driver_api = {
DEVICE_AND_API_INIT(behavior_rgb_underglow, DT_INST_LABEL(0), behavior_rgb_underglow_init, NULL, DEVICE_AND_API_INIT(behavior_rgb_underglow, DT_INST_LABEL(0), behavior_rgb_underglow_init, NULL,
NULL, APPLICATION, CONFIG_KERNEL_INIT_PRIORITY_DEFAULT, NULL, APPLICATION, CONFIG_KERNEL_INIT_PRIORITY_DEFAULT,
&behavior_rgb_underglow_driver_api); &behavior_rgb_underglow_driver_api);
#endif /* DT_HAS_COMPAT_STATUS_OKAY(DT_DRV_COMPAT) */

View file

@ -16,6 +16,8 @@
LOG_MODULE_DECLARE(zmk, CONFIG_ZMK_LOG_LEVEL); LOG_MODULE_DECLARE(zmk, CONFIG_ZMK_LOG_LEVEL);
#if DT_HAS_COMPAT_STATUS_OKAY(DT_DRV_COMPAT)
static int behavior_sensor_rotate_key_press_init(const struct device *dev) { return 0; }; static int behavior_sensor_rotate_key_press_init(const struct device *dev) { return 0; };
static int on_sensor_binding_triggered(struct zmk_behavior_binding *binding, static int on_sensor_binding_triggered(struct zmk_behavior_binding *binding,
@ -63,3 +65,5 @@ static const struct behavior_driver_api behavior_sensor_rotate_key_press_driver_
&behavior_sensor_rotate_key_press_driver_api); &behavior_sensor_rotate_key_press_driver_api);
DT_INST_FOREACH_STATUS_OKAY(KP_INST) DT_INST_FOREACH_STATUS_OKAY(KP_INST)
#endif /* DT_HAS_COMPAT_STATUS_OKAY(DT_DRV_COMPAT) */

View file

@ -18,6 +18,7 @@
#include <zmk/events/keycode_state_changed.h> #include <zmk/events/keycode_state_changed.h>
#include <zmk/events/modifiers_state_changed.h> #include <zmk/events/modifiers_state_changed.h>
#include <zmk/hid.h> #include <zmk/hid.h>
#include <zmk/keymap.h>
LOG_MODULE_DECLARE(zmk, CONFIG_ZMK_LOG_LEVEL); LOG_MODULE_DECLARE(zmk, CONFIG_ZMK_LOG_LEVEL);
@ -176,6 +177,11 @@ static const struct behavior_driver_api behavior_sticky_key_driver_api = {
.binding_released = on_sticky_key_binding_released, .binding_released = on_sticky_key_binding_released,
}; };
static int sticky_key_keycode_state_changed_listener(const zmk_event_t *eh);
ZMK_LISTENER(behavior_sticky_key, sticky_key_keycode_state_changed_listener);
ZMK_SUBSCRIPTION(behavior_sticky_key, zmk_keycode_state_changed);
static int sticky_key_keycode_state_changed_listener(const zmk_event_t *eh) { static int sticky_key_keycode_state_changed_listener(const zmk_event_t *eh) {
struct zmk_keycode_state_changed *ev = as_zmk_keycode_state_changed(eh); struct zmk_keycode_state_changed *ev = as_zmk_keycode_state_changed(eh);
if (ev == NULL) { if (ev == NULL) {
@ -211,7 +217,10 @@ static int sticky_key_keycode_state_changed_listener(const zmk_event_t *eh) {
if (sticky_key->timer_started) { if (sticky_key->timer_started) {
stop_timer(sticky_key); stop_timer(sticky_key);
if (sticky_key->config->quick_release) { if (sticky_key->config->quick_release) {
// continue processing the event. Release the sticky key afterwards.
ZMK_EVENT_RAISE_AFTER(eh, behavior_sticky_key);
release_sticky_key_behavior(sticky_key, ev->timestamp); release_sticky_key_behavior(sticky_key, ev->timestamp);
return ZMK_EV_EVENT_CAPTURED;
} }
} }
sticky_key->modified_key_usage_page = ev->usage_page; sticky_key->modified_key_usage_page = ev->usage_page;
@ -228,9 +237,6 @@ static int sticky_key_keycode_state_changed_listener(const zmk_event_t *eh) {
return ZMK_EV_EVENT_BUBBLE; return ZMK_EV_EVENT_BUBBLE;
} }
ZMK_LISTENER(behavior_sticky_key, sticky_key_keycode_state_changed_listener);
ZMK_SUBSCRIPTION(behavior_sticky_key, zmk_keycode_state_changed);
void behavior_sticky_key_timer_handler(struct k_work *item) { void behavior_sticky_key_timer_handler(struct k_work *item) {
struct active_sticky_key *sticky_key = struct active_sticky_key *sticky_key =
CONTAINER_OF(item, struct active_sticky_key, release_timer); CONTAINER_OF(item, struct active_sticky_key, release_timer);
@ -260,18 +266,10 @@ static int behavior_sticky_key_init(const struct device *dev) {
struct behavior_sticky_key_data {}; struct behavior_sticky_key_data {};
static struct behavior_sticky_key_data behavior_sticky_key_data; static struct behavior_sticky_key_data behavior_sticky_key_data;
#define _TRANSFORM_ENTRY(idx, node) \
{ \
.behavior_dev = DT_LABEL(DT_INST_PHANDLE_BY_IDX(node, bindings, idx)), \
.param1 = COND_CODE_0(DT_INST_PHA_HAS_CELL_AT_IDX(node, bindings, idx, param1), (0), \
(DT_INST_PHA_BY_IDX(node, bindings, idx, param1))), \
.param2 = COND_CODE_0(DT_INST_PHA_HAS_CELL_AT_IDX(node, bindings, idx, param2), (0), \
(DT_INST_PHA_BY_IDX(node, bindings, idx, param2))), \
},
#define KP_INST(n) \ #define KP_INST(n) \
static struct behavior_sticky_key_config behavior_sticky_key_config_##n = { \ static struct behavior_sticky_key_config behavior_sticky_key_config_##n = { \
.behavior = _TRANSFORM_ENTRY(0, n).release_after_ms = DT_INST_PROP(n, release_after_ms), \ .behavior = ZMK_KEYMAP_EXTRACT_BINDING(0, DT_DRV_INST(n)), \
.release_after_ms = DT_INST_PROP(n, release_after_ms), \
.quick_release = DT_INST_PROP(n, quick_release), \ .quick_release = DT_INST_PROP(n, quick_release), \
}; \ }; \
DEVICE_AND_API_INIT(behavior_sticky_key_##n, DT_INST_LABEL(n), behavior_sticky_key_init, \ DEVICE_AND_API_INIT(behavior_sticky_key_##n, DT_INST_LABEL(n), behavior_sticky_key_init, \

View file

@ -15,6 +15,8 @@
LOG_MODULE_DECLARE(zmk, CONFIG_ZMK_LOG_LEVEL); LOG_MODULE_DECLARE(zmk, CONFIG_ZMK_LOG_LEVEL);
#if DT_HAS_COMPAT_STATUS_OKAY(DT_DRV_COMPAT)
static int behavior_to_init(const struct device *dev) { return 0; }; static int behavior_to_init(const struct device *dev) { return 0; };
static int to_keymap_binding_pressed(struct zmk_behavior_binding *binding, static int to_keymap_binding_pressed(struct zmk_behavior_binding *binding,
@ -37,3 +39,5 @@ static const struct behavior_driver_api behavior_to_driver_api = {
DEVICE_AND_API_INIT(behavior_to, DT_INST_LABEL(0), behavior_to_init, NULL, NULL, APPLICATION, DEVICE_AND_API_INIT(behavior_to, DT_INST_LABEL(0), behavior_to_init, NULL, NULL, APPLICATION,
CONFIG_KERNEL_INIT_PRIORITY_DEFAULT, &behavior_to_driver_api); CONFIG_KERNEL_INIT_PRIORITY_DEFAULT, &behavior_to_driver_api);
#endif /* DT_HAS_COMPAT_STATUS_OKAY(DT_DRV_COMPAT) */

View file

@ -15,6 +15,8 @@
LOG_MODULE_DECLARE(zmk, CONFIG_ZMK_LOG_LEVEL); LOG_MODULE_DECLARE(zmk, CONFIG_ZMK_LOG_LEVEL);
#if DT_HAS_COMPAT_STATUS_OKAY(DT_DRV_COMPAT)
struct behavior_tog_config {}; struct behavior_tog_config {};
struct behavior_tog_data {}; struct behavior_tog_data {};
@ -44,3 +46,5 @@ static struct behavior_tog_data behavior_tog_data;
DEVICE_AND_API_INIT(behavior_tog, DT_INST_LABEL(0), behavior_tog_init, &behavior_tog_data, DEVICE_AND_API_INIT(behavior_tog, DT_INST_LABEL(0), behavior_tog_init, &behavior_tog_data,
&behavior_tog_config, APPLICATION, CONFIG_KERNEL_INIT_PRIORITY_DEFAULT, &behavior_tog_config, APPLICATION, CONFIG_KERNEL_INIT_PRIORITY_DEFAULT,
&behavior_tog_driver_api); &behavior_tog_driver_api);
#endif /* DT_HAS_COMPAT_STATUS_OKAY(DT_DRV_COMPAT) */

View file

@ -15,8 +15,7 @@
LOG_MODULE_DECLARE(zmk, CONFIG_ZMK_LOG_LEVEL); LOG_MODULE_DECLARE(zmk, CONFIG_ZMK_LOG_LEVEL);
struct behavior_transparent_config {}; #if DT_HAS_COMPAT_STATUS_OKAY(DT_DRV_COMPAT)
struct behavior_transparent_data {};
static int behavior_transparent_init(const struct device *dev) { return 0; }; static int behavior_transparent_init(const struct device *dev) { return 0; };
@ -35,10 +34,8 @@ static const struct behavior_driver_api behavior_transparent_driver_api = {
.binding_released = on_keymap_binding_released, .binding_released = on_keymap_binding_released,
}; };
static const struct behavior_transparent_config behavior_transparent_config = {}; DEVICE_AND_API_INIT(behavior_transparent, DT_INST_LABEL(0), behavior_transparent_init, NULL, NULL,
APPLICATION, CONFIG_KERNEL_INIT_PRIORITY_DEFAULT,
&behavior_transparent_driver_api);
static struct behavior_transparent_data behavior_transparent_data; #endif /* DT_HAS_COMPAT_STATUS_OKAY(DT_DRV_COMPAT) */
DEVICE_AND_API_INIT(behavior_transparent, DT_INST_LABEL(0), behavior_transparent_init,
&behavior_transparent_data, &behavior_transparent_config, APPLICATION,
CONFIG_KERNEL_INIT_PRIORITY_DEFAULT, &behavior_transparent_driver_api);

View file

@ -7,6 +7,7 @@
#include <device.h> #include <device.h>
#include <init.h> #include <init.h>
#include <errno.h>
#include <math.h> #include <math.h>
#include <stdlib.h> #include <stdlib.h>
#include <stdio.h> #include <stdio.h>
@ -63,6 +64,8 @@ static uint8_t active_profile;
#define DEVICE_NAME CONFIG_BT_DEVICE_NAME #define DEVICE_NAME CONFIG_BT_DEVICE_NAME
#define DEVICE_NAME_LEN (sizeof(DEVICE_NAME) - 1) #define DEVICE_NAME_LEN (sizeof(DEVICE_NAME) - 1)
BUILD_ASSERT(DEVICE_NAME_LEN <= 16, "ERROR: BLE device name is too long. Max length: 16");
#define IS_HOST_PERIPHERAL \ #define IS_HOST_PERIPHERAL \
(!IS_ENABLED(CONFIG_ZMK_SPLIT) || IS_ENABLED(CONFIG_ZMK_SPLIT_BLE_ROLE_CENTRAL)) (!IS_ENABLED(CONFIG_ZMK_SPLIT) || IS_ENABLED(CONFIG_ZMK_SPLIT_BLE_ROLE_CENTRAL))
#define IS_SPLIT_PERIPHERAL \ #define IS_SPLIT_PERIPHERAL \
@ -248,6 +251,10 @@ static int ble_save_profile() {
} }
int zmk_ble_prof_select(uint8_t index) { int zmk_ble_prof_select(uint8_t index) {
if (index >= PROFILE_COUNT) {
return -ERANGE;
}
LOG_DBG("profile %d", index); LOG_DBG("profile %d", index);
if (active_profile == index) { if (active_profile == index) {
return 0; return 0;
@ -429,7 +436,7 @@ static void le_param_updated(struct bt_conn *conn, uint16_t interval, uint16_t l
bt_addr_le_to_str(bt_conn_get_dst(conn), addr, sizeof(addr)); bt_addr_le_to_str(bt_conn_get_dst(conn), addr, sizeof(addr));
LOG_DBG("%s: interval %d latency %d timeout %d", addr, interval, latency, timeout); LOG_DBG("%s: interval %d latency %d timeout %d", log_strdup(addr), interval, latency, timeout);
} }
static struct bt_conn_cb conn_callbacks = { static struct bt_conn_cb conn_callbacks = {

View file

@ -17,6 +17,7 @@
#include <zmk/events/position_state_changed.h> #include <zmk/events/position_state_changed.h>
#include <zmk/hid.h> #include <zmk/hid.h>
#include <zmk/matrix.h> #include <zmk/matrix.h>
#include <zmk/keymap.h>
LOG_MODULE_DECLARE(zmk, CONFIG_ZMK_LOG_LEVEL); LOG_MODULE_DECLARE(zmk, CONFIG_ZMK_LOG_LEVEL);
@ -33,6 +34,8 @@ struct combo_cfg {
// the virtual key position is a key position outside the range used by the keyboard. // the virtual key position is a key position outside the range used by the keyboard.
// it is necessary so hold-taps can uniquely identify a behavior. // it is necessary so hold-taps can uniquely identify a behavior.
int32_t virtual_key_position; int32_t virtual_key_position;
int32_t layers_len;
int8_t layers[];
}; };
struct active_combo { struct active_combo {
@ -104,17 +107,35 @@ static int initialize_combo(struct combo_cfg *new_combo) {
return 0; return 0;
} }
static bool combo_active_on_layer(struct combo_cfg *combo, uint8_t layer) {
if (combo->layers[0] == -1) {
// -1 in the first layer position is global layer scope
return true;
}
for (int j = 0; j < combo->layers_len; j++) {
if (combo->layers[j] == layer) {
return true;
}
}
return false;
}
static int setup_candidates_for_first_keypress(int32_t position, int64_t timestamp) { static int setup_candidates_for_first_keypress(int32_t position, int64_t timestamp) {
int number_of_combo_candidates = 0;
uint8_t highest_active_layer = zmk_keymap_highest_layer_active();
for (int i = 0; i < CONFIG_ZMK_COMBO_MAX_COMBOS_PER_KEY; i++) { for (int i = 0; i < CONFIG_ZMK_COMBO_MAX_COMBOS_PER_KEY; i++) {
struct combo_cfg *combo = combo_lookup[position][i]; struct combo_cfg *combo = combo_lookup[position][i];
if (combo == NULL) { if (combo == NULL) {
return i; return number_of_combo_candidates;
}
if (combo_active_on_layer(combo, highest_active_layer)) {
candidates[number_of_combo_candidates].combo = combo;
candidates[number_of_combo_candidates].timeout_at = timestamp + combo->timeout_ms;
number_of_combo_candidates++;
} }
candidates[i].combo = combo;
candidates[i].timeout_at = timestamp + combo->timeout_ms;
// LOG_DBG("combo timeout %d %d %d", position, i, candidates[i].timeout_at); // LOG_DBG("combo timeout %d %d %d", position, i, candidates[i].timeout_at);
} }
return CONFIG_ZMK_COMBO_MAX_COMBOS_PER_KEY; return number_of_combo_candidates;
} }
static int filter_candidates(int32_t position) { static int filter_candidates(int32_t position) {
@ -167,11 +188,18 @@ static int64_t first_candidate_timeout() {
static inline bool candidate_is_completely_pressed(struct combo_cfg *candidate) { static inline bool candidate_is_completely_pressed(struct combo_cfg *candidate) {
// this code assumes set(pressed_keys) <= set(candidate->key_positions) // this code assumes set(pressed_keys) <= set(candidate->key_positions)
// this invariant is enforced by filter_candidates // this invariant is enforced by filter_candidates
// the only thing we need to do is check if len(pressed_keys) == len(combo->key_positions) // since events may have been reraised after clearing one or more slots at
return pressed_keys[candidate->key_position_len - 1] != NULL; // the start of pressed_keys (see: release_pressed_keys), we have to check
// that each key needed to trigger the combo was pressed, not just the last.
for (int i = 0; i < candidate->key_position_len; i++) {
if (pressed_keys[i] == NULL) {
return false;
}
}
return true;
} }
static void cleanup(); static int cleanup();
static int filter_timed_out_candidates(int64_t timestamp) { static int filter_timed_out_candidates(int64_t timestamp) {
int num_candidates = 0; int num_candidates = 0;
@ -203,7 +231,7 @@ static int clear_candidates() {
} }
static int capture_pressed_key(const zmk_event_t *ev) { static int capture_pressed_key(const zmk_event_t *ev) {
for (int i = 0; i < CONFIG_ZMK_COMBO_MAX_COMBOS_PER_KEY; i++) { for (int i = 0; i < CONFIG_ZMK_COMBO_MAX_KEYS_PER_COMBO; i++) {
if (pressed_keys[i] != NULL) { if (pressed_keys[i] != NULL) {
continue; continue;
} }
@ -215,24 +243,26 @@ static int capture_pressed_key(const zmk_event_t *ev) {
const struct zmk_listener zmk_listener_combo; const struct zmk_listener zmk_listener_combo;
static void release_pressed_keys() { static int release_pressed_keys() {
// release the first key that was pressed for (int i = 0; i < CONFIG_ZMK_COMBO_MAX_KEYS_PER_COMBO; i++) {
if (pressed_keys[0] == NULL) {
return;
}
ZMK_EVENT_RELEASE(pressed_keys[0])
pressed_keys[0] = NULL;
// reprocess events (see tests/combo/fully-overlapping-combos-3 for why this is needed)
for (int i = 1; i < CONFIG_ZMK_COMBO_MAX_COMBOS_PER_KEY; i++) {
if (pressed_keys[i] == NULL) {
return;
}
const zmk_event_t *captured_event = pressed_keys[i]; const zmk_event_t *captured_event = pressed_keys[i];
if (pressed_keys[i] == NULL) {
return i;
}
pressed_keys[i] = NULL; pressed_keys[i] = NULL;
if (i == 0) {
LOG_DBG("combo: releasing position event %d",
as_zmk_position_state_changed(captured_event)->position);
ZMK_EVENT_RELEASE(captured_event)
} else {
// reprocess events (see tests/combo/fully-overlapping-combos-3 for why this is needed)
LOG_DBG("combo: reraising position event %d",
as_zmk_position_state_changed(captured_event)->position);
ZMK_EVENT_RAISE(captured_event); ZMK_EVENT_RAISE(captured_event);
} }
} }
return CONFIG_ZMK_COMBO_MAX_KEYS_PER_COMBO;
}
static inline int press_combo_behavior(struct combo_cfg *combo, int32_t timestamp) { static inline int press_combo_behavior(struct combo_cfg *combo, int32_t timestamp) {
struct zmk_behavior_binding_event event = { struct zmk_behavior_binding_event event = {
@ -339,14 +369,14 @@ static bool release_combo_key(int32_t position, int64_t timestamp) {
return false; return false;
} }
static void cleanup() { static int cleanup() {
k_delayed_work_cancel(&timeout_task); k_delayed_work_cancel(&timeout_task);
clear_candidates(); clear_candidates();
if (fully_pressed_combo != NULL) { if (fully_pressed_combo != NULL) {
activate_combo(fully_pressed_combo); activate_combo(fully_pressed_combo);
fully_pressed_combo = NULL; fully_pressed_combo = NULL;
} }
release_pressed_keys(); return release_pressed_keys();
} }
static void update_timeout_task() { static void update_timeout_task() {
@ -378,6 +408,7 @@ static int position_state_down(const zmk_event_t *ev, struct zmk_position_state_
update_timeout_task(); update_timeout_task();
struct combo_cfg *candidate_combo = candidates[0].combo; struct combo_cfg *candidate_combo = candidates[0].combo;
LOG_DBG("combo: capturing position event %d", data->position);
int ret = capture_pressed_key(ev); int ret = capture_pressed_key(ev);
switch (num_candidates) { switch (num_candidates) {
case 0: case 0:
@ -397,13 +428,18 @@ static int position_state_down(const zmk_event_t *ev, struct zmk_position_state_
} }
} }
static int position_state_up(struct zmk_position_state_changed *ev) { static int position_state_up(const zmk_event_t *ev, struct zmk_position_state_changed *data) {
cleanup(); int released_keys = cleanup();
if (release_combo_key(ev->position, ev->timestamp)) { if (release_combo_key(data->position, data->timestamp)) {
return ZMK_EV_EVENT_HANDLED; return ZMK_EV_EVENT_HANDLED;
} else {
return 0;
} }
if (released_keys > 1) {
// The second and further key down events are re-raised. To preserve
// correct order for e.g. hold-taps, reraise the key up event too.
ZMK_EVENT_RAISE(ev);
return ZMK_EV_EVENT_CAPTURED;
}
return 0;
} }
static void combo_timeout_handler(struct k_work *item) { static void combo_timeout_handler(struct k_work *item) {
@ -426,7 +462,7 @@ static int position_state_changed_listener(const zmk_event_t *ev) {
if (data->state) { // keydown if (data->state) { // keydown
return position_state_down(ev, data); return position_state_down(ev, data);
} else { // keyup } else { // keyup
return position_state_up(data); return position_state_up(ev, data);
} }
} }
@ -451,6 +487,8 @@ ZMK_SUBSCRIPTION(combo, zmk_position_state_changed);
.behavior = KEY_BINDING_TO_STRUCT(0, n), \ .behavior = KEY_BINDING_TO_STRUCT(0, n), \
.virtual_key_position = ZMK_KEYMAP_LEN + __COUNTER__, \ .virtual_key_position = ZMK_KEYMAP_LEN + __COUNTER__, \
.slow_release = DT_PROP(n, slow_release), \ .slow_release = DT_PROP(n, slow_release), \
.layers = DT_PROP(n, layers), \
.layers_len = DT_PROP_LEN(n, layers), \
}; };
#define INITIALIZE_COMBO(n) initialize_combo(&combo_config_##n); #define INITIALIZE_COMBO(n) initialize_combo(&combo_config_##n);

View file

@ -38,12 +38,20 @@ void display_timer_cb() { k_work_submit(&display_tick_work); }
K_TIMER_DEFINE(display_timer, display_timer_cb, NULL); K_TIMER_DEFINE(display_timer, display_timer_cb, NULL);
static void start_display_updates() { static void start_display_updates() {
if (display == NULL) {
return;
}
display_blanking_off(display); display_blanking_off(display);
k_timer_start(&display_timer, K_MSEC(10), K_MSEC(10)); k_timer_start(&display_timer, K_MSEC(10), K_MSEC(10));
} }
static void stop_display_updates() { static void stop_display_updates() {
if (display == NULL) {
return;
}
display_blanking_on(display); display_blanking_on(display);
k_timer_stop(&display_timer); k_timer_stop(&display_timer);

View file

@ -22,13 +22,13 @@ int zmk_event_manager_handle_from(zmk_event_t *event, uint8_t start_index) {
uint8_t len = __event_subscriptions_end - __event_subscriptions_start; uint8_t len = __event_subscriptions_end - __event_subscriptions_start;
for (int i = start_index; i < len; i++) { for (int i = start_index; i < len; i++) {
struct zmk_event_subscription *ev_sub = __event_subscriptions_start + i; struct zmk_event_subscription *ev_sub = __event_subscriptions_start + i;
if (ev_sub->event_type == event->event) { if (ev_sub->event_type != event->event) {
continue;
}
ret = ev_sub->listener->callback(event); ret = ev_sub->listener->callback(event);
if (ret < 0) {
LOG_DBG("Listener returned an error: %d", ret);
goto release;
} else if (ret > 0) {
switch (ret) { switch (ret) {
case ZMK_EV_EVENT_BUBBLE:
continue;
case ZMK_EV_EVENT_HANDLED: case ZMK_EV_EVENT_HANDLED:
LOG_DBG("Listener handled the event"); LOG_DBG("Listener handled the event");
ret = 0; ret = 0;
@ -38,8 +38,9 @@ int zmk_event_manager_handle_from(zmk_event_t *event, uint8_t start_index) {
event->last_listener_index = i; event->last_listener_index = i;
// Listeners are expected to free events they capture // Listeners are expected to free events they capture
return 0; return 0;
} default:
} LOG_DBG("Listener returned an error: %d", ret);
goto release;
} }
} }

View file

@ -23,6 +23,7 @@ struct ext_power_generic_config {
const char *label; const char *label;
const uint8_t pin; const uint8_t pin;
const uint8_t flags; const uint8_t flags;
const uint16_t init_delay_ms;
}; };
struct ext_power_generic_data { struct ext_power_generic_data {
@ -171,6 +172,10 @@ static int ext_power_generic_init(const struct device *dev) {
ext_power_enable(dev); ext_power_enable(dev);
#endif #endif
if (config->init_delay_ms) {
k_msleep(config->init_delay_ms);
}
return 0; return 0;
} }
@ -210,7 +215,8 @@ static int ext_power_generic_pm_control(const struct device *dev, uint32_t ctrl_
static const struct ext_power_generic_config config = { static const struct ext_power_generic_config config = {
.label = DT_INST_GPIO_LABEL(0, control_gpios), .label = DT_INST_GPIO_LABEL(0, control_gpios),
.pin = DT_INST_GPIO_PIN(0, control_gpios), .pin = DT_INST_GPIO_PIN(0, control_gpios),
.flags = DT_INST_GPIO_FLAGS(0, control_gpios)}; .flags = DT_INST_GPIO_FLAGS(0, control_gpios),
.init_delay_ms = DT_INST_PROP_OR(0, init_delay_ms, 0)};
static struct ext_power_generic_data data = { static struct ext_power_generic_data data = {
.status = false, .status = false,
@ -223,13 +229,15 @@ static const struct ext_power_api api = {.enable = ext_power_generic_enable,
.disable = ext_power_generic_disable, .disable = ext_power_generic_disable,
.get = ext_power_generic_get}; .get = ext_power_generic_get};
#define ZMK_EXT_POWER_INIT_PRIORITY 81
#ifdef CONFIG_DEVICE_POWER_MANAGEMENT #ifdef CONFIG_DEVICE_POWER_MANAGEMENT
DEVICE_DEFINE(ext_power_generic, DT_INST_LABEL(0), ext_power_generic_init, DEVICE_DEFINE(ext_power_generic, DT_INST_LABEL(0), ext_power_generic_init,
&ext_power_generic_pm_control, &data, &config, APPLICATION, &ext_power_generic_pm_control, &data, &config, POST_KERNEL,
CONFIG_APPLICATION_INIT_PRIORITY, &api); ZMK_EXT_POWER_INIT_PRIORITY, &api);
#else #else
DEVICE_AND_API_INIT(ext_power_generic, DT_INST_LABEL(0), ext_power_generic_init, &data, &config, DEVICE_AND_API_INIT(ext_power_generic, DT_INST_LABEL(0), ext_power_generic_init, &data, &config,
APPLICATION, CONFIG_APPLICATION_INIT_PRIORITY, &api); POST_KERNEL, ZMK_EXT_POWER_INIT_PRIORITY, &api);
#endif /* CONFIG_DEVICE_POWER_MANAGEMENT */ #endif /* CONFIG_DEVICE_POWER_MANAGEMENT */
#endif /* DT_HAS_COMPAT_STATUS_OKAY(DT_DRV_COMPAT) */ #endif /* DT_HAS_COMPAT_STATUS_OKAY(DT_DRV_COMPAT) */

View file

@ -28,18 +28,10 @@ static uint8_t _zmk_keymap_layer_default = 0;
#define ZMK_KEYMAP_NODE DT_DRV_INST(0) #define ZMK_KEYMAP_NODE DT_DRV_INST(0)
#define ZMK_KEYMAP_LAYERS_LEN (DT_INST_FOREACH_CHILD(0, LAYER_CHILD_LEN) 0) #define ZMK_KEYMAP_LAYERS_LEN (DT_INST_FOREACH_CHILD(0, LAYER_CHILD_LEN) 0)
#define LAYER_NODE(l) DT_PHANDLE_BY_IDX(ZMK_KEYMAP_NODE, layers, l) #define BINDING_WITH_COMMA(idx, drv_inst) ZMK_KEYMAP_EXTRACT_BINDING(idx, drv_inst),
#define _TRANSFORM_ENTRY(idx, layer) \ #define TRANSFORMED_LAYER(node) \
{ \ {UTIL_LISTIFY(DT_PROP_LEN(node, bindings), BINDING_WITH_COMMA, node)},
.behavior_dev = DT_LABEL(DT_PHANDLE_BY_IDX(layer, bindings, idx)), \
.param1 = COND_CODE_0(DT_PHA_HAS_CELL_AT_IDX(layer, bindings, idx, param1), (0), \
(DT_PHA_BY_IDX(layer, bindings, idx, param1))), \
.param2 = COND_CODE_0(DT_PHA_HAS_CELL_AT_IDX(layer, bindings, idx, param2), (0), \
(DT_PHA_BY_IDX(layer, bindings, idx, param2))), \
},
#define TRANSFORMED_LAYER(node) {UTIL_LISTIFY(DT_PROP_LEN(node, bindings), _TRANSFORM_ENTRY, node)},
#if ZMK_KEYMAP_HAS_SENSORS #if ZMK_KEYMAP_HAS_SENSORS
#define _TRANSFORM_SENSOR_ENTRY(idx, layer) \ #define _TRANSFORM_SENSOR_ENTRY(idx, layer) \
@ -161,7 +153,9 @@ const char *zmk_keymap_layer_label(uint8_t layer) {
} }
int zmk_keymap_apply_position_state(int layer, uint32_t position, bool pressed, int64_t timestamp) { int zmk_keymap_apply_position_state(int layer, uint32_t position, bool pressed, int64_t timestamp) {
struct zmk_behavior_binding *binding = &zmk_keymap[layer][position]; // We want to make a copy of this, since it may be converted from
// relative to absolute before being invoked
struct zmk_behavior_binding binding = zmk_keymap[layer][position];
const struct device *behavior; const struct device *behavior;
struct zmk_behavior_binding_event event = { struct zmk_behavior_binding_event event = {
.layer = layer, .layer = layer,
@ -170,19 +164,25 @@ int zmk_keymap_apply_position_state(int layer, uint32_t position, bool pressed,
}; };
LOG_DBG("layer: %d position: %d, binding name: %s", layer, position, LOG_DBG("layer: %d position: %d, binding name: %s", layer, position,
log_strdup(binding->behavior_dev)); log_strdup(binding.behavior_dev));
behavior = device_get_binding(binding->behavior_dev); behavior = device_get_binding(binding.behavior_dev);
if (!behavior) { if (!behavior) {
LOG_DBG("No behavior assigned to %d on layer %d", position, layer); LOG_DBG("No behavior assigned to %d on layer %d", position, layer);
return 1; return 1;
} }
int err = behavior_keymap_binding_convert_central_state_dependent_params(&binding, event);
if (err) {
LOG_ERR("Failed to convert relative to absolute behavior binding (err %d)", err);
return err;
}
if (pressed) { if (pressed) {
return behavior_keymap_binding_pressed(binding, event); return behavior_keymap_binding_pressed(&binding, event);
} else { } else {
return behavior_keymap_binding_released(binding, event); return behavior_keymap_binding_released(&binding, event);
} }
} }

View file

@ -47,7 +47,7 @@ void zmk_kscan_process_msgq(struct k_work *item) {
while (k_msgq_get(&zmk_kscan_msgq, &ev, K_NO_WAIT) == 0) { while (k_msgq_get(&zmk_kscan_msgq, &ev, K_NO_WAIT) == 0) {
bool pressed = (ev.state == ZMK_KSCAN_EVENT_STATE_PRESSED); bool pressed = (ev.state == ZMK_KSCAN_EVENT_STATE_PRESSED);
uint32_t position = zmk_matrix_transform_row_column_to_position(ev.row, ev.column); uint32_t position = zmk_matrix_transform_row_column_to_position(ev.row, ev.column);
LOG_DBG("Row: %d, col: %d, position: %d, pressed: %s\n", ev.row, ev.column, position, LOG_DBG("Row: %d, col: %d, position: %d, pressed: %s", ev.row, ev.column, position,
(pressed ? "true" : "false")); (pressed ? "true" : "false"));
ZMK_EVENT_RAISE(new_zmk_position_state_changed((struct zmk_position_state_changed){ ZMK_EVENT_RAISE(new_zmk_position_state_changed((struct zmk_position_state_changed){
.state = pressed, .position = position, .timestamp = k_uptime_get()})); .state = pressed, .position = position, .timestamp = k_uptime_get()}));

View file

@ -17,11 +17,17 @@
#include <drivers/led_strip.h> #include <drivers/led_strip.h>
#include <drivers/ext_power.h> #include <drivers/ext_power.h>
#include <zmk/rgb_underglow.h>
LOG_MODULE_DECLARE(zmk, CONFIG_ZMK_LOG_LEVEL); LOG_MODULE_DECLARE(zmk, CONFIG_ZMK_LOG_LEVEL);
#define STRIP_LABEL DT_LABEL(DT_CHOSEN(zmk_underglow)) #define STRIP_LABEL DT_LABEL(DT_CHOSEN(zmk_underglow))
#define STRIP_NUM_PIXELS DT_PROP(DT_CHOSEN(zmk_underglow), chain_length) #define STRIP_NUM_PIXELS DT_PROP(DT_CHOSEN(zmk_underglow), chain_length)
#define HUE_MAX 360
#define SAT_MAX 100
#define BRT_MAX 100
enum rgb_underglow_effect { enum rgb_underglow_effect {
UNDERGLOW_EFFECT_SOLID, UNDERGLOW_EFFECT_SOLID,
UNDERGLOW_EFFECT_BREATHE, UNDERGLOW_EFFECT_BREATHE,
@ -30,16 +36,8 @@ enum rgb_underglow_effect {
UNDERGLOW_EFFECT_NUMBER // Used to track number of underglow effects UNDERGLOW_EFFECT_NUMBER // Used to track number of underglow effects
}; };
struct led_hsb {
uint16_t h;
uint8_t s;
uint8_t b;
};
struct rgb_underglow_state { struct rgb_underglow_state {
uint16_t hue; struct zmk_led_hsb color;
uint8_t saturation;
uint8_t brightness;
uint8_t animation_speed; uint8_t animation_speed;
uint8_t current_effect; uint8_t current_effect;
uint16_t animation_step; uint16_t animation_step;
@ -56,13 +54,13 @@ static struct rgb_underglow_state state;
static const struct device *ext_power; static const struct device *ext_power;
#endif #endif
static struct led_rgb hsb_to_rgb(struct led_hsb hsb) { static struct led_rgb hsb_to_rgb(struct zmk_led_hsb hsb) {
double r, g, b; double r, g, b;
uint8_t i = hsb.h / 60; uint8_t i = hsb.h / 60;
double v = hsb.b / 100.0; double v = hsb.b / ((float)BRT_MAX);
double s = hsb.s / 100.0; double s = hsb.s / ((float)SAT_MAX);
double f = hsb.h / 360.0 * 6 - i; double f = hsb.h / ((float)HUE_MAX) * 6 - i;
double p = v * (1 - s); double p = v * (1 - s);
double q = v * (1 - f * s); double q = v * (1 - f * s);
double t = v * (1 - (1 - f) * s); double t = v * (1 - (1 - f) * s);
@ -105,33 +103,16 @@ static struct led_rgb hsb_to_rgb(struct led_hsb hsb) {
return rgb; return rgb;
} }
static void zmk_rgb_underglow_off() {
for (int i = 0; i < STRIP_NUM_PIXELS; i++) {
pixels[i] = (struct led_rgb){r : 0, g : 0, b : 0};
}
led_strip_update_rgb(led_strip, pixels, STRIP_NUM_PIXELS);
}
static void zmk_rgb_underglow_effect_solid() { static void zmk_rgb_underglow_effect_solid() {
for (int i = 0; i < STRIP_NUM_PIXELS; i++) { for (int i = 0; i < STRIP_NUM_PIXELS; i++) {
int hue = state.hue; pixels[i] = hsb_to_rgb(state.color);
int sat = state.saturation;
int brt = state.brightness;
struct led_hsb hsb = {hue, sat, brt};
pixels[i] = hsb_to_rgb(hsb);
} }
} }
static void zmk_rgb_underglow_effect_breathe() { static void zmk_rgb_underglow_effect_breathe() {
for (int i = 0; i < STRIP_NUM_PIXELS; i++) { for (int i = 0; i < STRIP_NUM_PIXELS; i++) {
int hue = state.hue; struct zmk_led_hsb hsb = state.color;
int sat = state.saturation; hsb.b = abs(state.animation_step - 1200) / 12;
int brt = abs(state.animation_step - 1200) / 12;
struct led_hsb hsb = {hue, sat, brt};
pixels[i] = hsb_to_rgb(hsb); pixels[i] = hsb_to_rgb(hsb);
} }
@ -145,32 +126,26 @@ static void zmk_rgb_underglow_effect_breathe() {
static void zmk_rgb_underglow_effect_spectrum() { static void zmk_rgb_underglow_effect_spectrum() {
for (int i = 0; i < STRIP_NUM_PIXELS; i++) { for (int i = 0; i < STRIP_NUM_PIXELS; i++) {
int hue = state.animation_step; struct zmk_led_hsb hsb = state.color;
int sat = state.saturation; hsb.h = state.animation_step;
int brt = state.brightness;
struct led_hsb hsb = {hue, sat, brt};
pixels[i] = hsb_to_rgb(hsb); pixels[i] = hsb_to_rgb(hsb);
} }
state.animation_step += state.animation_speed; state.animation_step += state.animation_speed;
state.animation_step = state.animation_step % 360; state.animation_step = state.animation_step % HUE_MAX;
} }
static void zmk_rgb_underglow_effect_swirl() { static void zmk_rgb_underglow_effect_swirl() {
for (int i = 0; i < STRIP_NUM_PIXELS; i++) { for (int i = 0; i < STRIP_NUM_PIXELS; i++) {
int hue = (360 / STRIP_NUM_PIXELS * i + state.animation_step) % 360; struct zmk_led_hsb hsb = state.color;
int sat = state.saturation; hsb.h = (HUE_MAX / STRIP_NUM_PIXELS * i + state.animation_step) % HUE_MAX;
int brt = state.brightness;
struct led_hsb hsb = {hue, sat, brt};
pixels[i] = hsb_to_rgb(hsb); pixels[i] = hsb_to_rgb(hsb);
} }
state.animation_step += state.animation_speed * 2; state.animation_step += state.animation_speed * 2;
state.animation_step = state.animation_step % 360; state.animation_step = state.animation_step % HUE_MAX;
} }
static void zmk_rgb_underglow_tick(struct k_work *work) { static void zmk_rgb_underglow_tick(struct k_work *work) {
@ -196,10 +171,6 @@ K_WORK_DEFINE(underglow_work, zmk_rgb_underglow_tick);
static void zmk_rgb_underglow_tick_handler(struct k_timer *timer) { static void zmk_rgb_underglow_tick_handler(struct k_timer *timer) {
if (!state.on) { if (!state.on) {
zmk_rgb_underglow_off();
k_timer_stop(timer);
return; return;
} }
@ -255,9 +226,11 @@ static int zmk_rgb_underglow_init(const struct device *_arg) {
#endif #endif
state = (struct rgb_underglow_state){ state = (struct rgb_underglow_state){
hue : CONFIG_ZMK_RGB_UNDERGLOW_HUE_START, color : {
saturation : CONFIG_ZMK_RGB_UNDERGLOW_SAT_START, h : CONFIG_ZMK_RGB_UNDERGLOW_HUE_START,
brightness : CONFIG_ZMK_RGB_UNDERGLOW_BRT_START, s : CONFIG_ZMK_RGB_UNDERGLOW_SAT_START,
b : CONFIG_ZMK_RGB_UNDERGLOW_BRT_START,
},
animation_speed : CONFIG_ZMK_RGB_UNDERGLOW_SPD_START, animation_speed : CONFIG_ZMK_RGB_UNDERGLOW_SPD_START,
current_effect : CONFIG_ZMK_RGB_UNDERGLOW_EFF_START, current_effect : CONFIG_ZMK_RGB_UNDERGLOW_EFF_START,
animation_step : 0, animation_step : 0,
@ -292,20 +265,65 @@ int zmk_rgb_underglow_save_state() {
#endif #endif
} }
int zmk_rgb_underglow_get_state(bool *on_off) {
if (!led_strip)
return -ENODEV;
*on_off = state.on;
return 0;
}
int zmk_rgb_underglow_on() {
if (!led_strip)
return -ENODEV;
#if IS_ENABLED(CONFIG_ZMK_RGB_UNDERGLOW_EXT_POWER)
if (ext_power != NULL) {
int rc = ext_power_enable(ext_power);
if (rc != 0) {
LOG_ERR("Unable to enable EXT_POWER: %d", rc);
}
}
#endif
state.on = true;
state.animation_step = 0;
k_timer_start(&underglow_tick, K_NO_WAIT, K_MSEC(50));
return zmk_rgb_underglow_save_state();
}
int zmk_rgb_underglow_off() {
if (!led_strip)
return -ENODEV;
#if IS_ENABLED(CONFIG_ZMK_RGB_UNDERGLOW_EXT_POWER)
if (ext_power != NULL) {
int rc = ext_power_disable(ext_power);
if (rc != 0) {
LOG_ERR("Unable to disable EXT_POWER: %d", rc);
}
}
#endif
for (int i = 0; i < STRIP_NUM_PIXELS; i++) {
pixels[i] = (struct led_rgb){r : 0, g : 0, b : 0};
}
led_strip_update_rgb(led_strip, pixels, STRIP_NUM_PIXELS);
k_timer_stop(&underglow_tick);
state.on = false;
return zmk_rgb_underglow_save_state();
}
int zmk_rgb_underglow_cycle_effect(int direction) { int zmk_rgb_underglow_cycle_effect(int direction) {
if (!led_strip) if (!led_strip)
return -ENODEV; return -ENODEV;
if (state.current_effect == 0 && direction < 0) { state.current_effect += UNDERGLOW_EFFECT_NUMBER + direction;
state.current_effect = UNDERGLOW_EFFECT_NUMBER - 1; state.current_effect %= UNDERGLOW_EFFECT_NUMBER;
return 0;
}
state.current_effect += direction;
if (state.current_effect >= UNDERGLOW_EFFECT_NUMBER) {
state.current_effect = 0;
}
state.animation_step = 0; state.animation_step = 0;
@ -313,63 +331,61 @@ int zmk_rgb_underglow_cycle_effect(int direction) {
} }
int zmk_rgb_underglow_toggle() { int zmk_rgb_underglow_toggle() {
if (!led_strip) return state.on ? zmk_rgb_underglow_off() : zmk_rgb_underglow_on();
return -ENODEV;
state.on = !state.on;
#if IS_ENABLED(CONFIG_ZMK_RGB_UNDERGLOW_EXT_POWER)
if (ext_power != NULL) {
int rc;
if (state.on) {
rc = ext_power_enable(ext_power);
} else {
rc = ext_power_disable(ext_power);
} }
if (rc != 0) { int zmk_rgb_underglow_set_hsb(struct zmk_led_hsb color) {
LOG_ERR("Unable to toggle EXT_POWER: %d", rc); if (color.h > HUE_MAX || color.s > SAT_MAX || color.b > BRT_MAX) {
}
}
#endif
if (state.on) {
state.animation_step = 0;
k_timer_start(&underglow_tick, K_NO_WAIT, K_MSEC(50));
} else {
zmk_rgb_underglow_off();
k_timer_stop(&underglow_tick);
}
return zmk_rgb_underglow_save_state();
}
int zmk_rgb_underglow_set_hsb(uint16_t hue, uint8_t saturation, uint8_t brightness) {
if (hue > 360 || saturation > 100 || brightness > 100) {
return -ENOTSUP; return -ENOTSUP;
} }
state.hue = hue; state.color = color;
state.saturation = saturation;
state.brightness = brightness;
return 0; return 0;
} }
struct zmk_led_hsb zmk_rgb_underglow_calc_hue(int direction) {
struct zmk_led_hsb color = state.color;
color.h += HUE_MAX + (direction * CONFIG_ZMK_RGB_UNDERGLOW_HUE_STEP);
color.h %= HUE_MAX;
return color;
}
struct zmk_led_hsb zmk_rgb_underglow_calc_sat(int direction) {
struct zmk_led_hsb color = state.color;
int s = color.s + (direction * CONFIG_ZMK_RGB_UNDERGLOW_SAT_STEP);
if (s < 0) {
s = 0;
} else if (s > SAT_MAX) {
s = SAT_MAX;
}
color.s = s;
return color;
}
struct zmk_led_hsb zmk_rgb_underglow_calc_brt(int direction) {
struct zmk_led_hsb color = state.color;
int b = color.b + (direction * CONFIG_ZMK_RGB_UNDERGLOW_BRT_STEP);
if (b < 0) {
b = 0;
} else if (b > BRT_MAX) {
b = BRT_MAX;
}
color.b = b;
return color;
}
int zmk_rgb_underglow_change_hue(int direction) { int zmk_rgb_underglow_change_hue(int direction) {
if (!led_strip) if (!led_strip)
return -ENODEV; return -ENODEV;
if (state.hue == 0 && direction < 0) { state.color = zmk_rgb_underglow_calc_hue(direction);
state.hue = 360 - CONFIG_ZMK_RGB_UNDERGLOW_HUE_STEP;
return 0;
}
state.hue += direction * CONFIG_ZMK_RGB_UNDERGLOW_HUE_STEP;
state.hue = state.hue % 360;
return zmk_rgb_underglow_save_state(); return zmk_rgb_underglow_save_state();
} }
@ -378,15 +394,7 @@ int zmk_rgb_underglow_change_sat(int direction) {
if (!led_strip) if (!led_strip)
return -ENODEV; return -ENODEV;
if (state.saturation == 0 && direction < 0) { state.color = zmk_rgb_underglow_calc_sat(direction);
return 0;
}
state.saturation += direction * CONFIG_ZMK_RGB_UNDERGLOW_SAT_STEP;
if (state.saturation > 100) {
state.saturation = 100;
}
return zmk_rgb_underglow_save_state(); return zmk_rgb_underglow_save_state();
} }
@ -395,15 +403,7 @@ int zmk_rgb_underglow_change_brt(int direction) {
if (!led_strip) if (!led_strip)
return -ENODEV; return -ENODEV;
if (state.brightness == 0 && direction < 0) { state.color = zmk_rgb_underglow_calc_brt(direction);
return 0;
}
state.brightness += direction * CONFIG_ZMK_RGB_UNDERGLOW_BRT_STEP;
if (state.brightness > 100) {
state.brightness = 100;
}
return zmk_rgb_underglow_save_state(); return zmk_rgb_underglow_save_state();
} }

View file

@ -1,2 +1 @@
s/.*hid_listener_keycode_//p s/.*hid_listener_keycode_//p
s/.*combo//p

View file

@ -1,2 +1 @@
s/.*hid_listener_keycode_//p s/.*hid_listener_keycode_//p
s/.*combo//p

View file

@ -1,2 +1 @@
s/.*hid_listener_keycode_//p s/.*hid_listener_keycode_//p
s/.*combo//p

View file

@ -0,0 +1 @@
s/.*hid_listener_keycode_//p

View file

@ -0,0 +1,8 @@
pressed: usage_page 0x07 keycode 0x1b implicit_mods 0x00 explicit_mods 0x00
released: usage_page 0x07 keycode 0x1b 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 0x1c implicit_mods 0x00 explicit_mods 0x00
released: usage_page 0x07 keycode 0x1c 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

View file

@ -0,0 +1,78 @@
#include <dt-bindings/zmk/keys.h>
#include <behaviors.dtsi>
#include <dt-bindings/zmk/kscan-mock.h>
/* it is useful to set timeout to a large value when attaching a debugger. */
#define TIMEOUT (60*60*1000)
/ {
combos {
compatible = "zmk,combos";
combo_one {
timeout-ms = <TIMEOUT>;
key-positions = <0 1>;
bindings = <&kp X>;
layers = <0>;
};
combo_two {
timeout-ms = <TIMEOUT>;
key-positions = <0 1>;
bindings = <&kp Y>;
layers = <1>;
};
combo_three {
timeout-ms = <TIMEOUT>;
key-positions = <0 2>;
bindings = <&kp Z>;
};
};
keymap {
compatible = "zmk,keymap";
label ="Default keymap";
default_layer {
bindings = <
&kp A &kp B
&kp C &tog 1
>;
};
filtered_layer {
bindings = <
&kp A &kp B
&kp C &tog 0
>;
};
};
};
&kscan {
events = <
/* Combo One */
ZMK_MOCK_PRESS(0,0,10)
ZMK_MOCK_PRESS(0,1,10)
ZMK_MOCK_RELEASE(0,0,10)
ZMK_MOCK_RELEASE(0,1,10)
/* Combo Three */
ZMK_MOCK_PRESS(0,0,10)
ZMK_MOCK_PRESS(1,1,10)
ZMK_MOCK_RELEASE(0,0,10)
ZMK_MOCK_RELEASE(1,1,10)
/* Toggle Layer */
ZMK_MOCK_PRESS(1,1,10)
ZMK_MOCK_RELEASE(1,1,10)
/* Combo Two */
ZMK_MOCK_PRESS(0,0,10)
ZMK_MOCK_PRESS(0,1,10)
ZMK_MOCK_RELEASE(0,0,10)
ZMK_MOCK_RELEASE(0,1,10)
/* Combo Three */
ZMK_MOCK_PRESS(0,0,10)
ZMK_MOCK_PRESS(1,1,10)
ZMK_MOCK_RELEASE(0,0,10)
ZMK_MOCK_RELEASE(1,1,10)
>;
};

View file

@ -0,0 +1 @@
s/.*hid_listener_keycode_//p

Some files were not shown because too many files have changed in this diff Show more