commit
c24ef37743
96 changed files with 2976 additions and 62 deletions
36
README.md
36
README.md
|
@ -1,13 +1,33 @@
|
|||
# Zephyr™ Mechanical Keyboard (ZMK) Firmware
|
||||
# ZMK Firmware: Personal fork
|
||||
|
||||
[](https://zmk.dev/community/discord/invite)
|
||||
[](https://github.com/zmkfirmware/zmk/actions)
|
||||
[](CODE_OF_CONDUCT.md)
|
||||
This is my personal ZMK fork containing various experimental features used in
|
||||
my [zmk-config](https://github.com/urob/zmk-config/). It is regularly rebased
|
||||
onto the latest upstream.
|
||||
|
||||
[ZMK Firmware](https://zmk.dev/) is an open source ([MIT](LICENSE)) 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.
|
||||
Below is a list of features currently included in the `main` branch _on top of_
|
||||
the official ZMK master branch.
|
||||
|
||||
Check out the website to learn more: https://zmk.dev/.
|
||||
- **mouse** (PR [#778](https://github.com/zmkfirmware/zmk/pull/778)) - official PR + ftc's update + [update to Zephyr 3.2](https://github.com/urob/zmk/tree/mouse-3.2) + some safeguards + enforce hog device fix
|
||||
- **swapper** (PR [#1366](https://github.com/zmkfirmware/zmk/pull/1366)) - official PR + fixes needed for Zephyr 3.2
|
||||
- **smart-word** (PR [#1451](https://github.com/zmkfirmware/zmk/pull/1451)) - official PR, updated to Zephyr-3.2
|
||||
- **fix-key-repeat** - fix [key-repeat rolling issue](https://github.com/zmkfirmware/zmk/issues/1207)
|
||||
- **on-release-for-tap-preferred** - [on-release option for tap-preferred](https://github.com/celejewski/zmk/commit/d7a8482712d87963e59b74238667346221199293) by Andrzej
|
||||
- **adv360pro** (PR [#1454](https://github.com/zmkfirmware/zmk/pull/1454)) - offical PR
|
||||
- **zen-tweaks** - [display & battery improvements](https://github.com/caksoylar/zmk/tree/caksoylar/zen-v1%2Bv2) by Cem Aksoylar
|
||||
|
||||
You can also come join our [ZMK Discord Server](https://zmk.dev/community/discord/invite).
|
||||
In order to use this branch with Github Actions, replace the contents of `west.yml` in
|
||||
your `zmk-config/config` directory with the following contents:
|
||||
|
||||
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.
|
||||
```
|
||||
manifest:
|
||||
remotes:
|
||||
- name: urob
|
||||
url-base: https://github.com/urob
|
||||
projects:
|
||||
- name: zmk
|
||||
remote: urob
|
||||
revision: main
|
||||
import: app/west.yml
|
||||
self:
|
||||
path: config
|
||||
```
|
||||
|
|
|
@ -24,6 +24,9 @@ target_sources(app PRIVATE src/stdlib.c)
|
|||
target_sources(app PRIVATE src/activity.c)
|
||||
target_sources(app PRIVATE src/kscan.c)
|
||||
target_sources(app PRIVATE src/matrix_transform.c)
|
||||
target_sources_ifdef(CONFIG_ZMK_MOUSE app PRIVATE src/mouse/key_listener.c)
|
||||
target_sources_ifdef(CONFIG_ZMK_MOUSE app PRIVATE src/mouse/main.c)
|
||||
target_sources_ifdef(CONFIG_ZMK_MOUSE app PRIVATE src/mouse/tick_listener.c)
|
||||
target_sources(app PRIVATE src/sensors.c)
|
||||
target_sources_ifdef(CONFIG_ZMK_WPM app PRIVATE src/wpm.c)
|
||||
target_sources(app PRIVATE src/event_manager.c)
|
||||
|
@ -31,6 +34,10 @@ target_sources_ifdef(CONFIG_ZMK_EXT_POWER app PRIVATE src/ext_power_generic.c)
|
|||
target_sources(app PRIVATE src/events/activity_state_changed.c)
|
||||
target_sources(app PRIVATE src/events/position_state_changed.c)
|
||||
target_sources(app PRIVATE src/events/sensor_event.c)
|
||||
target_sources_ifdef(CONFIG_ZMK_MOUSE app PRIVATE src/events/mouse_button_state_changed.c)
|
||||
target_sources_ifdef(CONFIG_ZMK_MOUSE app PRIVATE src/events/mouse_move_state_changed.c)
|
||||
target_sources_ifdef(CONFIG_ZMK_MOUSE app PRIVATE src/events/mouse_tick.c)
|
||||
target_sources_ifdef(CONFIG_ZMK_MOUSE app PRIVATE src/events/mouse_scroll_state_changed.c)
|
||||
target_sources_ifdef(CONFIG_ZMK_WPM app PRIVATE src/events/wpm_state_changed.c)
|
||||
target_sources_ifdef(CONFIG_USB_DEVICE_STACK app PRIVATE src/events/usb_conn_state_changed.c)
|
||||
target_sources(app PRIVATE src/behaviors/behavior_reset.c)
|
||||
|
@ -50,10 +57,14 @@ if ((NOT CONFIG_ZMK_SPLIT) OR CONFIG_ZMK_SPLIT_ROLE_CENTRAL)
|
|||
target_sources(app PRIVATE src/behaviors/behavior_toggle_layer.c)
|
||||
target_sources(app PRIVATE src/behaviors/behavior_to_layer.c)
|
||||
target_sources(app PRIVATE src/behaviors/behavior_transparent.c)
|
||||
target_sources_ifdef(CONFIG_ZMK_BEHAVIOR_TRI_STATE app PRIVATE src/behaviors/behavior_tri_state.c)
|
||||
target_sources(app PRIVATE src/behaviors/behavior_none.c)
|
||||
target_sources_ifdef(CONFIG_ZMK_BEHAVIOR_SENSOR_ROTATE app PRIVATE src/behaviors/behavior_sensor_rotate.c)
|
||||
target_sources_ifdef(CONFIG_ZMK_BEHAVIOR_SENSOR_ROTATE_VAR app PRIVATE src/behaviors/behavior_sensor_rotate_var.c)
|
||||
target_sources_ifdef(CONFIG_ZMK_BEHAVIOR_SENSOR_ROTATE_COMMON app PRIVATE src/behaviors/behavior_sensor_rotate_common.c)
|
||||
target_sources_ifdef(CONFIG_ZMK_MOUSE app PRIVATE src/behaviors/behavior_mouse_key_press.c)
|
||||
target_sources_ifdef(CONFIG_ZMK_MOUSE app PRIVATE src/behaviors/behavior_mouse_move.c)
|
||||
target_sources_ifdef(CONFIG_ZMK_MOUSE app PRIVATE src/behaviors/behavior_mouse_scroll.c)
|
||||
target_sources(app PRIVATE src/combo.c)
|
||||
target_sources(app PRIVATE src/behaviors/behavior_tap_dance.c)
|
||||
target_sources(app PRIVATE src/behavior_queue.c)
|
||||
|
|
17
app/Kconfig
17
app/Kconfig
|
@ -149,6 +149,10 @@ config ZMK_BLE_CONSUMER_REPORT_QUEUE_SIZE
|
|||
int "Max number of consumer HID reports to queue for sending over BLE"
|
||||
default 5
|
||||
|
||||
config ZMK_BLE_MOUSE_REPORT_QUEUE_SIZE
|
||||
int "Max number of mouse HID reports to queue for sending over BLE"
|
||||
default 20
|
||||
|
||||
config ZMK_BLE_CLEAR_BONDS_ON_START
|
||||
bool "Configuration that clears all bond information from the keyboard on startup."
|
||||
default n
|
||||
|
@ -308,6 +312,13 @@ endif
|
|||
#Display/LED Options
|
||||
endmenu
|
||||
|
||||
menu "Mouse Options"
|
||||
|
||||
rsource "src/mouse/Kconfig"
|
||||
|
||||
#Mouse Options
|
||||
endmenu
|
||||
|
||||
menu "Power Management"
|
||||
|
||||
config ZMK_BATTERY_REPORTING
|
||||
|
@ -376,6 +387,12 @@ config ZMK_MACRO_DEFAULT_TAP_MS
|
|||
int "Default time to wait (in milliseconds) between the press and release events of a tapped behavior in macros"
|
||||
default 30
|
||||
|
||||
DT_COMPAT_ZMK_BEHAVIOR_TRI_STATE := zmk,behavior-tri-state
|
||||
|
||||
config ZMK_BEHAVIOR_TRI_STATE
|
||||
bool
|
||||
default $(dt_compat_enabled,$(DT_COMPAT_ZMK_BEHAVIOR_TRI_STATE))
|
||||
|
||||
endmenu
|
||||
|
||||
menu "Advanced"
|
||||
|
|
7
app/boards/arm/adv360pro/Kconfig
Normal file
7
app/boards/arm/adv360pro/Kconfig
Normal file
|
@ -0,0 +1,7 @@
|
|||
# SPDX-License-Identifier: MIT
|
||||
|
||||
config BOARD_ENABLE_DCDC
|
||||
bool "Enable DCDC mode"
|
||||
select SOC_DCDC_NRF52X
|
||||
default y
|
||||
depends on BOARD_ADV360PRO_LEFT || BOARD_ADV360PRO_RIGHT
|
12
app/boards/arm/adv360pro/Kconfig.board
Normal file
12
app/boards/arm/adv360pro/Kconfig.board
Normal file
|
@ -0,0 +1,12 @@
|
|||
#
|
||||
# Copyright (c) 2022 The ZMK Contributors
|
||||
# SPDX-License-Identifier: MIT
|
||||
#
|
||||
|
||||
config BOARD_ADV360PRO_LEFT
|
||||
bool "adv360pro_left"
|
||||
depends on SOC_NRF52840_QIAA
|
||||
|
||||
config BOARD_ADV360PRO_RIGHT
|
||||
bool "adv360pro_right"
|
||||
depends on SOC_NRF52840_QIAA
|
64
app/boards/arm/adv360pro/Kconfig.defconfig
Normal file
64
app/boards/arm/adv360pro/Kconfig.defconfig
Normal file
|
@ -0,0 +1,64 @@
|
|||
#
|
||||
# Copyright (c) 2022 The ZMK Contributors
|
||||
# SPDX-License-Identifier: MIT
|
||||
#
|
||||
|
||||
if BOARD_ADV360PRO_LEFT
|
||||
|
||||
config ZMK_KEYBOARD_NAME
|
||||
default "Adv360 Pro"
|
||||
|
||||
config ZMK_SPLIT_ROLE_CENTRAL
|
||||
default y
|
||||
|
||||
|
||||
endif # BOARD_ADV360PRO_LEFT
|
||||
|
||||
if BOARD_ADV360PRO_RIGHT
|
||||
|
||||
config ZMK_KEYBOARD_NAME
|
||||
default "Adv360 Pro rt"
|
||||
|
||||
endif # BOARD_ADV360PRO_RIGHT
|
||||
|
||||
|
||||
if BOARD_ADV360PRO_LEFT || BOARD_ADV360PRO_RIGHT
|
||||
|
||||
config BOARD
|
||||
default "adv360pro"
|
||||
|
||||
config ZMK_SPLIT
|
||||
default y
|
||||
|
||||
config SPI
|
||||
bool
|
||||
default y
|
||||
|
||||
config BT_CTLR
|
||||
default BT
|
||||
|
||||
config ZMK_BLE
|
||||
default y
|
||||
|
||||
config ZMK_USB
|
||||
default y
|
||||
|
||||
if USB
|
||||
|
||||
config USB_NRFX
|
||||
default y
|
||||
|
||||
config USB_DEVICE_STACK
|
||||
default y
|
||||
|
||||
endif # USB
|
||||
|
||||
config ZMK_BATTERY_VOLTAGE_DIVIDER
|
||||
default y
|
||||
|
||||
config SPI
|
||||
default y
|
||||
|
||||
|
||||
|
||||
endif # BOARD_ADV360PRO_LEFT || BOARD_ADV360PRO_RIGHT
|
163
app/boards/arm/adv360pro/adv360pro.dtsi
Normal file
163
app/boards/arm/adv360pro/adv360pro.dtsi
Normal file
|
@ -0,0 +1,163 @@
|
|||
/*
|
||||
*
|
||||
* Copyright (c) 2021 Polarity Works
|
||||
* SPDX-License-Identifier: MIT
|
||||
*
|
||||
*/
|
||||
|
||||
/dts-v1/;
|
||||
#include <nordic/nrf52840_qiaa.dtsi>
|
||||
|
||||
#include <dt-bindings/led/led.h>
|
||||
#include <dt-bindings/zmk/matrix_transform.h>
|
||||
|
||||
#include "adv360pro_pinctrl.dtsi"
|
||||
|
||||
/ {
|
||||
model = "Adv360";
|
||||
compatible = "kinesis,adv360pro";
|
||||
|
||||
chosen {
|
||||
zephyr,code-partition = &code_partition;
|
||||
zephyr,sram = &sram0;
|
||||
zephyr,flash = &flash0;
|
||||
zmk,kscan = &kscan0;
|
||||
zmk,backlight = &backlight;
|
||||
zmk,battery = &vbatt;
|
||||
zmk,matrix_transform = &default_transform;
|
||||
zmk,underglow = &led_strip;
|
||||
};
|
||||
|
||||
default_transform: keymap_transform_0 {
|
||||
compatible = "zmk,matrix-transform";
|
||||
columns = <20>;
|
||||
rows = <5>;
|
||||
|
||||
|
||||
map = <
|
||||
RC(4,0) RC(4,1) RC(4,2) RC(4,3) RC(4,4) RC(4,5) RC(4,6) RC(4,13) RC(4,14) RC(4,15) RC(4,16) RC(4,17) RC(4,18) RC(4,19)
|
||||
RC(3,0) RC(3,1) RC(3,2) RC(3,3) RC(3,4) RC(3,5) RC(3,6) RC(3,13) RC(3,14) RC(3,15) RC(3,16) RC(3,17) RC(3,18) RC(3,19)
|
||||
RC(2,0) RC(2,1) RC(2,2) RC(2,3) RC(2,4) RC(2,5) RC(2,6) RC(2,8) RC(2,9) RC(2,10) RC(2,11) RC(2,13) RC(2,14) RC(2,15) RC(2,16) RC(2,17) RC(2,18) RC(2,19)
|
||||
RC(1,0) RC(1,1) RC(1,2) RC(1,3) RC(1,4) RC(1,5) RC(1,9) RC(1,10) RC(1,14) RC(1,15) RC(1,16) RC(1,17) RC(1,18) RC(1,19)
|
||||
RC(0,0) RC(0,1) RC(0,2) RC(0,3) RC(0,4) RC(0,7) RC(0,8) RC(0,9) RC(0,10) RC(0,11) RC(0,12) RC(0,15) RC(0,16) RC(0,17) RC(0,18) RC(0,19)
|
||||
|
||||
>;
|
||||
};
|
||||
ext-power {
|
||||
compatible = "zmk,ext-power-generic";
|
||||
label = "EXT_POWER";
|
||||
control-gpios = <&gpio0 13 GPIO_ACTIVE_HIGH>;
|
||||
};
|
||||
|
||||
vbatt: vbatt {
|
||||
compatible = "zmk,battery-voltage-divider";
|
||||
io-channels = <&adc 2>;
|
||||
output-ohms = <100000>;
|
||||
full-ohms = <(100000 + 100000)>;
|
||||
};
|
||||
|
||||
backlight: pwmleds {
|
||||
compatible = "pwm-leds";
|
||||
pwm_led_0 {
|
||||
pwms = <&pwm0 0 10000 PWM_POLARITY_NORMAL>;
|
||||
};
|
||||
};
|
||||
|
||||
};
|
||||
|
||||
&pwm0 {
|
||||
status = "okay";
|
||||
pinctrl-0 = <&pwm0_default>;
|
||||
pinctrl-1 = <&pwm0_sleep>;
|
||||
pinctrl-names = "default", "sleep";
|
||||
};
|
||||
|
||||
&adc {
|
||||
status = "okay";
|
||||
};
|
||||
|
||||
&gpiote {
|
||||
status = "okay";
|
||||
};
|
||||
|
||||
&gpio0 {
|
||||
status = "okay";
|
||||
};
|
||||
|
||||
&gpio1 {
|
||||
status = "okay";
|
||||
};
|
||||
|
||||
&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 = "softdevice";
|
||||
reg = <0x00000000 0x00026000>;
|
||||
};
|
||||
code_partition: partition@26000 {
|
||||
label = "code_partition";
|
||||
reg = <0x00026000 0x000c6000>;
|
||||
};
|
||||
|
||||
/*
|
||||
* The flash starting at 0x000ec000 and ending at
|
||||
* 0x000f3fff is reserved for use by the application.
|
||||
*/
|
||||
|
||||
/*
|
||||
* Storage partition will be used by FCB/LittleFS/NVS
|
||||
* if enabled.
|
||||
*/
|
||||
storage_partition: partition@ec000 {
|
||||
label = "storage";
|
||||
reg = <0x000ec000 0x00008000>;
|
||||
};
|
||||
|
||||
boot_partition: partition@f4000 {
|
||||
label = "adafruit_boot";
|
||||
reg = <0x000f4000 0x0000c000>;
|
||||
};
|
||||
};
|
||||
};
|
||||
|
||||
&usbd {
|
||||
status = "okay";
|
||||
};
|
||||
|
||||
&spi3 {
|
||||
compatible = "nordic,nrf-spim";
|
||||
status = "okay";
|
||||
pinctrl-0 = <&spi3_default>;
|
||||
pinctrl-1 = <&spi3_sleep>;
|
||||
pinctrl-names = "default", "sleep";
|
||||
|
||||
led_strip: ws2812@0 {
|
||||
compatible = "worldsemi,ws2812-spi";
|
||||
label = "WS2812";
|
||||
|
||||
/* SPI */
|
||||
reg = <0>;
|
||||
spi-max-frequency = <4000000>;
|
||||
|
||||
/* WS2812 */
|
||||
chain-length = <3>; /* number of LEDs */
|
||||
spi-one-frame = <0x70>;
|
||||
spi-zero-frame = <0x40>;
|
||||
|
||||
color-mapping = <LED_COLOR_ID_GREEN
|
||||
LED_COLOR_ID_RED
|
||||
LED_COLOR_ID_BLUE>;
|
||||
};
|
||||
};
|
52
app/boards/arm/adv360pro/adv360pro.keymap
Normal file
52
app/boards/arm/adv360pro/adv360pro.keymap
Normal file
|
@ -0,0 +1,52 @@
|
|||
#include <behaviors.dtsi>
|
||||
#include <dt-bindings/zmk/keys.h>
|
||||
#include <dt-bindings/zmk/bt.h>
|
||||
#include <dt-bindings/zmk/rgb.h>
|
||||
#include <dt-bindings/zmk/backlight.h>
|
||||
|
||||
/ {
|
||||
behaviors {
|
||||
#include "macros.dtsi"
|
||||
};
|
||||
|
||||
keymap {
|
||||
compatible = "zmk,keymap";
|
||||
|
||||
default_layer {
|
||||
bindings = <
|
||||
&kp EQUAL &kp N1 &kp N2 &kp N3 &kp N4 &kp N5 &tog 1 &mo 3 &kp N6 &kp N7 &kp N8 &kp N9 &kp N0 &kp MINUS
|
||||
&kp TAB &kp Q &kp W &kp E &kp R &kp T &none &none &kp Y &kp U &kp I &kp O &kp P &kp BSLH
|
||||
&kp ESC &kp A &kp S &kp D &kp F &kp G &none &kp LCTRL &kp LALT &kp LGUI &kp RCTRL &none &kp H &kp J &kp K &kp L &kp SEMI &kp SQT
|
||||
&kp LSHFT &kp Z &kp X &kp C &kp V &kp B &kp HOME &kp PG_UP &kp N &kp M &kp COMMA &kp DOT &kp FSLH &kp RSHFT
|
||||
&mo 2 &kp GRAVE &kp CAPS &kp LEFT &kp RIGHT &kp BSPC &kp DEL &kp END &kp PG_DN &kp ENTER &kp SPACE &kp UP &kp DOWN &kp LBKT &kp RBKT &mo 2
|
||||
>;
|
||||
};
|
||||
keypad {
|
||||
bindings = <
|
||||
&kp EQUAL &kp N1 &kp N2 &kp N3 &kp N4 &kp N5 &trans &mo 3 &kp N6 &kp KP_NUM &kp KP_EQUAL &kp KP_DIVIDE &kp KP_MULTIPLY &kp MINUS
|
||||
&kp TAB &kp Q &kp W &kp E &kp R &kp T &none &none &kp Y &kp KP_N7 &kp KP_N8 &kp KP_N9 &kp KP_MINUS &kp BSLH
|
||||
&kp ESC &kp A &kp S &kp D &kp F &kp G &none &kp LCTRL &kp LALT &kp LGUI &kp RCTRL &none &kp H &kp KP_N4 &kp KP_N5 &kp KP_N6 &kp KP_PLUS &kp SQT
|
||||
&kp LSHFT &kp Z &kp X &kp C &kp V &kp B &kp HOME &kp PG_UP &kp N &kp KP_N1 &kp KP_N2 &kp KP_N3 &kp KP_ENTER &kp RSHFT
|
||||
&mo 2 &kp GRAVE &kp CAPS &kp LEFT &kp RIGHT &kp BSPC &kp DEL &kp END &kp PG_DN &kp ENTER &kp KP_N0 &kp UP &kp DOWN &kp KP_DOT &kp RBKT &mo 2
|
||||
>;
|
||||
};
|
||||
fn {
|
||||
bindings = <
|
||||
&kp F1 &kp F2 &kp F3 &kp F4 &kp F5 &kp F6 &tog 1 &mo 3 &kp F7 &kp F8 &kp F9 &kp F10 &kp F11 &kp F12
|
||||
&trans &trans &trans &trans &trans &trans &none &none &trans &trans &trans &trans &trans &trans
|
||||
&trans &trans &trans &trans &trans &trans &none &trans &trans &trans &trans &none &trans &trans &trans &trans &trans &trans
|
||||
&trans &trans &trans &trans &trans &trans &trans &trans &trans &trans &trans &trans &trans &trans
|
||||
&trans &trans &trans &trans &trans &trans &trans &trans &trans &trans &trans &trans &trans &trans &trans &trans
|
||||
>;
|
||||
};
|
||||
mod {
|
||||
bindings = <
|
||||
&none &bt BT_SEL 0 &bt BT_SEL 1 &bt BT_SEL 2 &bt BT_SEL 3 &bt BT_SEL 4 &none &trans &none &none &none &none &none &none
|
||||
&none &none &none &none &none &none &bootloader &bootloader &none &none &none &none &none &none
|
||||
&none &none &none &none &none &none &none &none &none &bt BT_CLR &none &none &none &none &none &none &none &none
|
||||
&none &none &none &none &none &none &none &none &none &none &none &none &none &none
|
||||
&none &none &none &none &none &none &none &none &none &bl BL_TOG &rgb_ug RGB_TOG &bl BL_INC &bl BL_DEC &none &none &none
|
||||
>;
|
||||
};
|
||||
};
|
||||
};
|
19
app/boards/arm/adv360pro/adv360pro.yaml
Normal file
19
app/boards/arm/adv360pro/adv360pro.yaml
Normal file
|
@ -0,0 +1,19 @@
|
|||
identifier: adv360pro
|
||||
name: Advantage 360 Pro
|
||||
type: keyboard
|
||||
arch: arm
|
||||
toolchain:
|
||||
- zephyr
|
||||
- gnuarmemb
|
||||
- xtools
|
||||
supported:
|
||||
- gpio
|
||||
- i2c
|
||||
- counter
|
||||
- spi
|
||||
- usb_device
|
||||
- nvs
|
||||
- can
|
||||
- kscan
|
||||
- ble
|
||||
- pwm
|
16
app/boards/arm/adv360pro/adv360pro.zmk.yml
Normal file
16
app/boards/arm/adv360pro/adv360pro.zmk.yml
Normal file
|
@ -0,0 +1,16 @@
|
|||
file_format: "1"
|
||||
id: adv360pro
|
||||
name: Advantage 360 Pro
|
||||
type: board
|
||||
url: https://kinesis-ergo.com/keyboards/advantage360
|
||||
arch: arm
|
||||
features:
|
||||
- keys
|
||||
- underglow
|
||||
- backlight
|
||||
outputs:
|
||||
- usb
|
||||
- ble
|
||||
siblings:
|
||||
- adv360pro_left
|
||||
- adv360pro_right
|
36
app/boards/arm/adv360pro/adv360pro_left.dts
Normal file
36
app/boards/arm/adv360pro/adv360pro_left.dts
Normal file
|
@ -0,0 +1,36 @@
|
|||
/*
|
||||
*
|
||||
* Copyright (c) 2022 The ZMK Contributors
|
||||
* SPDX-License-Identifier: MIT
|
||||
*
|
||||
*/
|
||||
|
||||
#include "adv360pro.dtsi"
|
||||
|
||||
/{
|
||||
kscan0: kscan {
|
||||
compatible = "zmk,kscan-gpio-matrix";
|
||||
|
||||
diode-direction = "col2row";
|
||||
row-gpios
|
||||
= <&gpio1 11 (GPIO_ACTIVE_HIGH | GPIO_PULL_DOWN)>
|
||||
, <&gpio1 15 (GPIO_ACTIVE_HIGH | GPIO_PULL_DOWN)>
|
||||
, <&gpio0 3 (GPIO_ACTIVE_HIGH | GPIO_PULL_DOWN)>
|
||||
, <&gpio1 14 (GPIO_ACTIVE_HIGH | GPIO_PULL_DOWN)>
|
||||
, <&gpio1 12 (GPIO_ACTIVE_HIGH | GPIO_PULL_DOWN)>
|
||||
;
|
||||
|
||||
col-gpios
|
||||
= <&gpio0 25 GPIO_ACTIVE_HIGH>
|
||||
, <&gpio0 11 GPIO_ACTIVE_HIGH>
|
||||
, <&gpio0 2 GPIO_ACTIVE_HIGH>
|
||||
, <&gpio0 28 GPIO_ACTIVE_HIGH>
|
||||
, <&gpio0 29 GPIO_ACTIVE_HIGH>
|
||||
, <&gpio0 30 GPIO_ACTIVE_HIGH>
|
||||
, <&gpio0 31 GPIO_ACTIVE_HIGH>
|
||||
, <&gpio1 9 GPIO_ACTIVE_HIGH>
|
||||
, <&gpio0 12 GPIO_ACTIVE_HIGH>
|
||||
, <&gpio0 7 GPIO_ACTIVE_HIGH>
|
||||
;
|
||||
};
|
||||
};
|
8
app/boards/arm/adv360pro/adv360pro_left.keymap
Normal file
8
app/boards/arm/adv360pro/adv360pro_left.keymap
Normal file
|
@ -0,0 +1,8 @@
|
|||
/*
|
||||
*
|
||||
* Copyright (c) 2022 The ZMK Contributors
|
||||
* SPDX-License-Identifier: MIT
|
||||
*
|
||||
*/
|
||||
|
||||
#include "adv360pro.keymap"
|
53
app/boards/arm/adv360pro/adv360pro_left_defconfig
Normal file
53
app/boards/arm/adv360pro/adv360pro_left_defconfig
Normal file
|
@ -0,0 +1,53 @@
|
|||
#
|
||||
# Copyright (c) 2022 The ZMK Contributors
|
||||
# SPDX-License-Identifier: MIT
|
||||
#
|
||||
|
||||
CONFIG_SOC_SERIES_NRF52X=y
|
||||
CONFIG_SOC_NRF52840_QIAA=y
|
||||
CONFIG_BOARD_ADV360PRO_LEFT=y
|
||||
|
||||
# Enable MPU
|
||||
CONFIG_ARM_MPU=y
|
||||
|
||||
# enable GPIO
|
||||
CONFIG_GPIO=y
|
||||
|
||||
# Enable SPI for LEDS
|
||||
CONFIG_PINCTRL=y
|
||||
CONFIG_SPI=y
|
||||
CONFIG_SPI_NRFX=y
|
||||
|
||||
# Enable writing to flash
|
||||
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
|
||||
|
||||
# Enable 32kHz crystal
|
||||
CONFIG_CLOCK_CONTROL_NRF_K32SRC_XTAL=y
|
||||
CONFIG_CLOCK_CONTROL_NRF_K32SRC_150PPM=y
|
||||
|
||||
#RGB leds config
|
||||
CONFIG_WS2812_STRIP=y
|
||||
CONFIG_ZMK_RGB_UNDERGLOW=y
|
||||
CONFIG_ZMK_RGB_UNDERGLOW_EXT_POWER=y
|
||||
CONFIG_ZMK_RGB_UNDERGLOW_ON_START=n
|
||||
CONFIG_ZMK_RGB_UNDERGLOW_EFF_START=0
|
||||
CONFIG_ZMK_RGB_UNDERGLOW_AUTO_OFF_IDLE=y
|
||||
|
||||
#Backlighting configuration
|
||||
CONFIG_PWM=y
|
||||
CONFIG_LED_PWM=y
|
||||
CONFIG_ZMK_BACKLIGHT=y
|
||||
CONFIG_ZMK_BACKLIGHT_BRT_START=20
|
||||
CONFIG_ZMK_BACKLIGHT_AUTO_OFF_IDLE=y
|
||||
|
||||
#Misc configuration
|
||||
CONFIG_BT_CTLR_TX_PWR_PLUS_8=y
|
||||
CONFIG_ZMK_HID_REPORT_TYPE_NKRO=y
|
||||
CONFIG_ZMK_HID_CONSUMER_REPORT_USAGES_BASIC=y
|
||||
CONFIG_BUILD_OUTPUT_UF2=y
|
30
app/boards/arm/adv360pro/adv360pro_pinctrl.dtsi
Normal file
30
app/boards/arm/adv360pro/adv360pro_pinctrl.dtsi
Normal file
|
@ -0,0 +1,30 @@
|
|||
/*
|
||||
* Copyright (c) 2023 The ZMK Contributors
|
||||
* SPDX-License-Identifier: MIT
|
||||
*/
|
||||
|
||||
&pinctrl {
|
||||
spi3_default: spi3_default {
|
||||
group1 {
|
||||
psels = <NRF_PSEL(SPIM_MOSI, 0, 20)>;
|
||||
};
|
||||
};
|
||||
|
||||
spi3_sleep: spi3_sleep {
|
||||
group1 {
|
||||
psels = <NRF_PSEL(SPIM_MOSI, 0, 20)>;
|
||||
low-power-enable;
|
||||
};
|
||||
};
|
||||
pwm0_default: pwm0_default {
|
||||
group1 {
|
||||
psels = <NRF_PSEL(PWM_OUT0, 0, 17)>;
|
||||
};
|
||||
};
|
||||
pwm0_sleep: pwm0_sleep {
|
||||
group1 {
|
||||
psels = <NRF_PSEL(PWM_OUT0, 0, 17)>;
|
||||
low-power-enable;
|
||||
};
|
||||
};
|
||||
};
|
43
app/boards/arm/adv360pro/adv360pro_right.dts
Normal file
43
app/boards/arm/adv360pro/adv360pro_right.dts
Normal file
|
@ -0,0 +1,43 @@
|
|||
/*
|
||||
*
|
||||
* Copyright (c) 2022 The ZMK Contributors
|
||||
* SPDX-License-Identifier: MIT
|
||||
*
|
||||
*/
|
||||
|
||||
#include "adv360pro.dtsi"
|
||||
|
||||
/{
|
||||
|
||||
|
||||
|
||||
kscan0: kscan {
|
||||
compatible = "zmk,kscan-gpio-matrix";
|
||||
|
||||
diode-direction = "col2row";
|
||||
row-gpios
|
||||
= <&gpio0 19 (GPIO_ACTIVE_HIGH | GPIO_PULL_DOWN)>
|
||||
, <&gpio0 5 (GPIO_ACTIVE_HIGH | GPIO_PULL_DOWN)>
|
||||
, <&gpio0 31 (GPIO_ACTIVE_HIGH | GPIO_PULL_DOWN)>
|
||||
, <&gpio0 30 (GPIO_ACTIVE_HIGH | GPIO_PULL_DOWN)>
|
||||
, <&gpio0 29 (GPIO_ACTIVE_HIGH | GPIO_PULL_DOWN)>
|
||||
;
|
||||
|
||||
col-gpios
|
||||
= <&gpio0 12 GPIO_ACTIVE_HIGH>
|
||||
, <&gpio1 9 GPIO_ACTIVE_HIGH>
|
||||
, <&gpio0 7 GPIO_ACTIVE_HIGH>
|
||||
, <&gpio1 11 GPIO_ACTIVE_HIGH>
|
||||
, <&gpio1 10 GPIO_ACTIVE_HIGH>
|
||||
, <&gpio1 13 GPIO_ACTIVE_HIGH>
|
||||
, <&gpio1 15 GPIO_ACTIVE_HIGH>
|
||||
, <&gpio0 3 GPIO_ACTIVE_HIGH>
|
||||
, <&gpio0 2 GPIO_ACTIVE_HIGH>
|
||||
, <&gpio0 28 GPIO_ACTIVE_HIGH>
|
||||
;
|
||||
};
|
||||
};
|
||||
|
||||
&default_transform {
|
||||
col-offset = <10>;
|
||||
};
|
8
app/boards/arm/adv360pro/adv360pro_right.keymap
Normal file
8
app/boards/arm/adv360pro/adv360pro_right.keymap
Normal file
|
@ -0,0 +1,8 @@
|
|||
/*
|
||||
*
|
||||
* Copyright (c) 2022 The ZMK Contributors
|
||||
* SPDX-License-Identifier: MIT
|
||||
*
|
||||
*/
|
||||
|
||||
#include "adv360pro.keymap"
|
53
app/boards/arm/adv360pro/adv360pro_right_defconfig
Normal file
53
app/boards/arm/adv360pro/adv360pro_right_defconfig
Normal file
|
@ -0,0 +1,53 @@
|
|||
#
|
||||
# Copyright (c) 2022 The ZMK Contributors
|
||||
# SPDX-License-Identifier: MIT
|
||||
#
|
||||
|
||||
CONFIG_SOC_SERIES_NRF52X=y
|
||||
CONFIG_SOC_NRF52840_QIAA=y
|
||||
CONFIG_BOARD_ADV360PRO_RIGHT=y
|
||||
|
||||
# Enable MPU
|
||||
CONFIG_ARM_MPU=y
|
||||
|
||||
# enable GPIO
|
||||
CONFIG_GPIO=y
|
||||
|
||||
# Enable SPI for LEDS
|
||||
CONFIG_PINCTRL=y
|
||||
CONFIG_SPI=y
|
||||
CONFIG_SPI_NRFX=y
|
||||
|
||||
# Enable writing to flash
|
||||
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
|
||||
|
||||
# Enable 32kHz crystal
|
||||
CONFIG_CLOCK_CONTROL_NRF_K32SRC_XTAL=y
|
||||
CONFIG_CLOCK_CONTROL_NRF_K32SRC_150PPM=y
|
||||
|
||||
#RGB leds config
|
||||
CONFIG_WS2812_STRIP=y
|
||||
CONFIG_ZMK_RGB_UNDERGLOW=y
|
||||
CONFIG_ZMK_RGB_UNDERGLOW_EXT_POWER=y
|
||||
CONFIG_ZMK_RGB_UNDERGLOW_ON_START=n
|
||||
CONFIG_ZMK_RGB_UNDERGLOW_EFF_START=0
|
||||
CONFIG_ZMK_RGB_UNDERGLOW_AUTO_OFF_IDLE=y
|
||||
|
||||
#Backlighting configuration
|
||||
CONFIG_PWM=y
|
||||
CONFIG_LED_PWM=y
|
||||
CONFIG_ZMK_BACKLIGHT=y
|
||||
CONFIG_ZMK_BACKLIGHT_BRT_START=20
|
||||
CONFIG_ZMK_BACKLIGHT_AUTO_OFF_IDLE=y
|
||||
|
||||
#Misc configuration
|
||||
CONFIG_BT_CTLR_TX_PWR_PLUS_8=y
|
||||
CONFIG_ZMK_HID_REPORT_TYPE_NKRO=y
|
||||
CONFIG_ZMK_HID_CONSUMER_REPORT_USAGES_BASIC=y
|
||||
CONFIG_BUILD_OUTPUT_UF2=y
|
9
app/boards/arm/adv360pro/board.cmake
Normal file
9
app/boards/arm/adv360pro/board.cmake
Normal file
|
@ -0,0 +1,9 @@
|
|||
#
|
||||
# Copyright (c) 2022 The ZMK Contributors
|
||||
# SPDX-License-Identifier: MIT
|
||||
#
|
||||
|
||||
board_runner_args(nrfjprog "--nrf-family=NRF52" "--softreset")
|
||||
|
||||
include(${ZEPHYR_BASE}/boards/common/nrfjprog.board.cmake)
|
||||
#include(${ZEPHYR_BASE}/boards/common/blackmagicprobe.board.cmake)
|
36
app/boards/arm/adv360pro/macros.dtsi
Normal file
36
app/boards/arm/adv360pro/macros.dtsi
Normal file
|
@ -0,0 +1,36 @@
|
|||
macro_quotes: macro_quotes {
|
||||
compatible = "zmk,behavior-macro";
|
||||
label = "macro_quotes";
|
||||
#binding-cells = <0>;
|
||||
bindings = <&kp SQT>, <&kp SQT>, <&kp LEFT>;
|
||||
};
|
||||
macro_dquotes: macro_dquotes {
|
||||
compatible = "zmk,behavior-macro";
|
||||
label = "macro_dquotes";
|
||||
#binding-cells = <0>;
|
||||
bindings = <&kp DQT>, <&kp DQT>, <&kp LEFT>;
|
||||
};
|
||||
macro_braces: macro_braces {
|
||||
compatible = "zmk,behavior-macro";
|
||||
label = "macro_braces";
|
||||
#binding-cells = <0>;
|
||||
bindings = <&kp LBRC>, <&kp RBRC>, <&kp LEFT>;
|
||||
};
|
||||
macro_parens: macro_parens {
|
||||
compatible = "zmk,behavior-macro";
|
||||
label = "macro_parens";
|
||||
#binding-cells = <0>;
|
||||
bindings = <&kp LPAR>, <&kp RPAR>, <&kp LEFT>;
|
||||
};
|
||||
macro_brackets: macro_brackets {
|
||||
compatible = "zmk,behavior-macro";
|
||||
label = "macro_brackets";
|
||||
#binding-cells = <0>;
|
||||
bindings = <&kp LBKT>, <&kp RBKT>, <&kp LEFT>;
|
||||
};
|
||||
macro_kinesis: macro_kinesis {
|
||||
compatible = "zmk,behavior-macro";
|
||||
label = "macro_kinesis";
|
||||
#binding-cells = <0>;
|
||||
bindings = <&kp K>, <&kp I>, <&kp N>, <&kp E>, <&kp S>, <&kp I>, <&kp S>;
|
||||
};
|
|
@ -77,6 +77,9 @@ menuconfig CUSTOM_WIDGET_LAYER_STATUS
|
|||
menuconfig CUSTOM_WIDGET_PERIPHERAL_STATUS
|
||||
bool "custom peripheral status widget"
|
||||
|
||||
config CUSTOM_WIDGET_LAYER_STATUS_HIDE_HEADING
|
||||
bool "Hide heading strip for layer widget and center widgets on 1/3rds"
|
||||
|
||||
endif # BOARD_CORNEISH_ZEN_LEFT || BOARD_CORNEISH_ZEN_RIGHT
|
||||
|
||||
if BOARD_CORNEISH_ZEN_V1_LEFT || BOARD_CORNEISH_ZEN_V1_RIGHT
|
||||
|
|
|
@ -40,37 +40,60 @@ lv_obj_t *zmk_display_status_screen() {
|
|||
|
||||
#if IS_ENABLED(CONFIG_CUSTOM_WIDGET_BATTERY_STATUS)
|
||||
zmk_widget_battery_status_init(&battery_status_widget, screen);
|
||||
#if IS_ENABLED(CONFIG_CUSTOM_WIDGET_LAYER_STATUS_HIDE_HEADING)
|
||||
lv_obj_align(zmk_widget_battery_status_obj(&battery_status_widget), LV_ALIGN_CENTER, 0, -43);
|
||||
#else
|
||||
lv_obj_align(zmk_widget_battery_status_obj(&battery_status_widget), LV_ALIGN_TOP_MID, 0, 2);
|
||||
#endif
|
||||
#endif
|
||||
|
||||
#if IS_ENABLED(CONFIG_CUSTOM_WIDGET_OUTPUT_STATUS)
|
||||
zmk_widget_output_status_init(&output_status_widget, screen);
|
||||
#if IS_ENABLED(CONFIG_CUSTOM_WIDGET_LAYER_STATUS_HIDE_HEADING)
|
||||
lv_obj_align(zmk_widget_output_status_obj(&output_status_widget), LV_ALIGN_CENTER, 0, 0);
|
||||
#else
|
||||
lv_obj_align(zmk_widget_output_status_obj(&output_status_widget), LV_ALIGN_TOP_MID, 0, 41);
|
||||
#endif
|
||||
#endif
|
||||
|
||||
#if IS_ENABLED(CONFIG_CUSTOM_WIDGET_PERIPHERAL_STATUS)
|
||||
zmk_widget_peripheral_status_init(&peripheral_status_widget, screen);
|
||||
#if IS_ENABLED(CONFIG_CUSTOM_WIDGET_LAYER_STATUS_HIDE_HEADING)
|
||||
lv_obj_align(zmk_widget_peripheral_status_obj(&peripheral_status_widget), LV_ALIGN_CENTER, 0,
|
||||
0);
|
||||
#else
|
||||
lv_obj_align(zmk_widget_peripheral_status_obj(&peripheral_status_widget), LV_ALIGN_TOP_MID, 0,
|
||||
41);
|
||||
#endif
|
||||
#endif
|
||||
|
||||
#if IS_ENABLED(CONFIG_CUSTOM_WIDGET_LAYER_STATUS)
|
||||
#if !IS_ENABLED(CONFIG_CUSTOM_WIDGET_LAYER_STATUS_HIDE_HEADING)
|
||||
lv_obj_t *LayersHeading;
|
||||
LayersHeading = lv_img_create(screen);
|
||||
lv_obj_align(LayersHeading, LV_ALIGN_BOTTOM_MID, 0, -30);
|
||||
lv_img_set_src(LayersHeading, &layers2);
|
||||
#endif
|
||||
|
||||
zmk_widget_layer_status_init(&layer_status_widget, screen);
|
||||
lv_obj_set_style_text_font(zmk_widget_layer_status_obj(&layer_status_widget),
|
||||
&lv_font_montserrat_16, LV_PART_MAIN);
|
||||
#if IS_ENABLED(CONFIG_CUSTOM_WIDGET_LAYER_STATUS_HIDE_HEADING)
|
||||
lv_obj_align(zmk_widget_layer_status_obj(&layer_status_widget), LV_ALIGN_CENTER, 0, 43);
|
||||
#else
|
||||
lv_obj_align(zmk_widget_layer_status_obj(&layer_status_widget), LV_ALIGN_BOTTOM_MID, 0, -5);
|
||||
#endif
|
||||
#endif
|
||||
|
||||
#if !IS_ENABLED(CONFIG_ZMK_SPLIT_ROLE_CENTRAL)
|
||||
lv_obj_t *zenlogo_icon;
|
||||
zenlogo_icon = lv_img_create(screen);
|
||||
lv_img_set_src(zenlogo_icon, &zenlogo);
|
||||
#if IS_ENABLED(CONFIG_CUSTOM_WIDGET_LAYER_STATUS_HIDE_HEADING)
|
||||
lv_obj_align(zenlogo_icon, LV_ALIGN_CENTER, 0, 43);
|
||||
#else
|
||||
lv_obj_align(zenlogo_icon, LV_ALIGN_BOTTOM_MID, 0, -5);
|
||||
#endif
|
||||
#endif
|
||||
|
||||
return screen;
|
||||
|
|
|
@ -41,21 +41,52 @@ LV_IMG_DECLARE(batt_0);
|
|||
LV_IMG_DECLARE(batt_0_chg);
|
||||
|
||||
static void set_battery_symbol(lv_obj_t *icon, struct battery_status_state state) {
|
||||
uint8_t level = state.level;
|
||||
|
||||
#if IS_ENABLED(CONFIG_USB_DEVICE_STACK)
|
||||
if (level > 95) {
|
||||
lv_img_set_src(icon, state.usb_present ? &batt_100_chg : &batt_100);
|
||||
} else if (level > 74) {
|
||||
lv_img_set_src(icon, state.usb_present ? &batt_75_chg : &batt_75);
|
||||
} else if (level > 49) {
|
||||
lv_img_set_src(icon, state.usb_present ? &batt_50_chg : &batt_50);
|
||||
} else if (level > 24) {
|
||||
lv_img_set_src(icon, state.usb_present ? &batt_25_chg : &batt_25);
|
||||
static uint8_t stage_prev = 255;
|
||||
static bool usb_prev = false;
|
||||
|
||||
uint8_t level = state.level;
|
||||
bool usb_present = state.usb_present;
|
||||
uint8_t stage;
|
||||
|
||||
if (level > 87) {
|
||||
stage = 5;
|
||||
} else if (level > 62) {
|
||||
stage = 4;
|
||||
} else if (level > 37) {
|
||||
stage = 3;
|
||||
} else if (level > 12) {
|
||||
stage = 2;
|
||||
} else if (level > 5) {
|
||||
lv_img_set_src(icon, state.usb_present ? &batt_5_chg : &batt_5);
|
||||
stage = 1;
|
||||
} else {
|
||||
lv_img_set_src(icon, state.usb_present ? &batt_0_chg : &batt_0);
|
||||
stage = 0;
|
||||
}
|
||||
|
||||
// check if there is a change requiring an update
|
||||
if (usb_present != usb_prev || stage != stage_prev) {
|
||||
switch (stage) {
|
||||
case 5:
|
||||
lv_img_set_src(icon, usb_present ? &batt_100_chg : &batt_100);
|
||||
break;
|
||||
case 4:
|
||||
lv_img_set_src(icon, usb_present ? &batt_75_chg : &batt_75);
|
||||
break;
|
||||
case 3:
|
||||
lv_img_set_src(icon, usb_present ? &batt_50_chg : &batt_50);
|
||||
break;
|
||||
case 2:
|
||||
lv_img_set_src(icon, usb_present ? &batt_25_chg : &batt_25);
|
||||
break;
|
||||
case 1:
|
||||
lv_img_set_src(icon, usb_present ? &batt_5_chg : &batt_5);
|
||||
break;
|
||||
default:
|
||||
lv_img_set_src(icon, usb_present ? &batt_0_chg : &batt_0);
|
||||
break;
|
||||
}
|
||||
usb_prev = usb_present;
|
||||
stage_prev = stage;
|
||||
}
|
||||
#endif /* IS_ENABLED(CONFIG_USB_DEVICE_STACK) */
|
||||
}
|
||||
|
|
|
@ -27,6 +27,16 @@ static void set_layer_symbol(lv_obj_t *label, struct layer_status_state state) {
|
|||
const char *layer_label = state.label;
|
||||
uint8_t active_layer_index = state.index;
|
||||
|
||||
#if IS_ENABLED(CONFIG_ZMK_DISPLAY_HIDE_MOMENTARY_LAYERS)
|
||||
static uint8_t last_perm_index = 255;
|
||||
if (!zmk_keymap_layer_momentary(active_layer_index) && last_perm_index != active_layer_index) {
|
||||
last_perm_index = active_layer_index;
|
||||
LOG_DBG("Last perm layer index updated to %i", active_layer_index);
|
||||
} else {
|
||||
return;
|
||||
}
|
||||
#endif
|
||||
|
||||
if (layer_label == NULL) {
|
||||
char text[6] = {};
|
||||
|
||||
|
|
|
@ -18,4 +18,7 @@
|
|||
#include <behaviors/caps_word.dtsi>
|
||||
#include <behaviors/key_repeat.dtsi>
|
||||
#include <behaviors/backlight.dtsi>
|
||||
#include <behaviors/macros.dtsi>
|
||||
#include <behaviors/macros.dtsi>
|
||||
#include <behaviors/mouse_key_press.dtsi>
|
||||
#include <behaviors/mouse_move.dtsi>
|
||||
#include <behaviors/mouse_scroll.dtsi>
|
||||
|
|
|
@ -12,7 +12,22 @@
|
|||
compatible = "zmk,behavior-caps-word";
|
||||
label = "CAPS_WORD";
|
||||
#binding-cells = <0>;
|
||||
mods = <MOD_LSFT>;
|
||||
continue-list = <UNDERSCORE BACKSPACE DELETE>;
|
||||
ignore-alphas;
|
||||
ignore-numbers;
|
||||
ignore-modifiers;
|
||||
};
|
||||
};
|
||||
|
||||
behaviors {
|
||||
/omit-if-no-ref/ num_word: behavior_num_word {
|
||||
compatible = "zmk,behavior-caps-word";
|
||||
label = "NUM_WORD";
|
||||
#binding-cells = <0>;
|
||||
// layers = <xx>; // to be specified in user config using "&num_word { layers = <xx>; };"
|
||||
continue-list = <BACKSPACE DELETE DOT COMMA>;
|
||||
ignore-numbers;
|
||||
};
|
||||
};
|
||||
};
|
||||
|
|
9
app/dts/behaviors/mouse_key_press.dtsi
Normal file
9
app/dts/behaviors/mouse_key_press.dtsi
Normal file
|
@ -0,0 +1,9 @@
|
|||
/ {
|
||||
behaviors {
|
||||
/omit-if-no-ref/ mkp: behavior_mouse_key_press {
|
||||
compatible = "zmk,behavior-mouse-key-press";
|
||||
label = "MOUSE_KEY_PRESS";
|
||||
#binding-cells = <1>;
|
||||
};
|
||||
};
|
||||
};
|
12
app/dts/behaviors/mouse_move.dtsi
Normal file
12
app/dts/behaviors/mouse_move.dtsi
Normal file
|
@ -0,0 +1,12 @@
|
|||
/ {
|
||||
behaviors {
|
||||
/omit-if-no-ref/ mmv: behavior_mouse_move {
|
||||
compatible = "zmk,behavior-mouse-move";
|
||||
label = "MOUSE_MOVE";
|
||||
#binding-cells = <1>;
|
||||
delay-ms = <0>;
|
||||
time-to-max-speed-ms = <300>;
|
||||
acceleration-exponent = <1>;
|
||||
};
|
||||
};
|
||||
};
|
12
app/dts/behaviors/mouse_scroll.dtsi
Normal file
12
app/dts/behaviors/mouse_scroll.dtsi
Normal file
|
@ -0,0 +1,12 @@
|
|||
/ {
|
||||
behaviors {
|
||||
/omit-if-no-ref/ mwh: msc: behavior_mouse_scroll {
|
||||
compatible = "zmk,behavior-mouse-scroll";
|
||||
label = "MOUSE_SCROLL";
|
||||
#binding-cells = <1>;
|
||||
delay-ms = <0>;
|
||||
time-to-max-speed-ms = <300>;
|
||||
acceleration-exponent = <0>;
|
||||
};
|
||||
};
|
||||
};
|
|
@ -13,3 +13,11 @@ properties:
|
|||
required: true
|
||||
mods:
|
||||
type: int
|
||||
layers:
|
||||
type: int
|
||||
ignore-alphas:
|
||||
type: boolean
|
||||
ignore-numbers:
|
||||
type: boolean
|
||||
ignore-modifiers:
|
||||
type: boolean
|
||||
|
|
|
@ -0,0 +1,5 @@
|
|||
description: Mouse key press/release behavior
|
||||
|
||||
compatible: "zmk,behavior-mouse-key-press"
|
||||
|
||||
include: one_param.yaml
|
13
app/dts/bindings/behaviors/zmk,behavior-mouse-move.yaml
Normal file
13
app/dts/bindings/behaviors/zmk,behavior-mouse-move.yaml
Normal file
|
@ -0,0 +1,13 @@
|
|||
description: Mouse move
|
||||
|
||||
compatible: "zmk,behavior-mouse-move"
|
||||
|
||||
include: one_param.yaml
|
||||
|
||||
properties:
|
||||
delay-ms:
|
||||
type: int
|
||||
time-to-max-speed-ms:
|
||||
type: int
|
||||
acceleration-exponent:
|
||||
type: int
|
13
app/dts/bindings/behaviors/zmk,behavior-mouse-scroll.yaml
Normal file
13
app/dts/bindings/behaviors/zmk,behavior-mouse-scroll.yaml
Normal file
|
@ -0,0 +1,13 @@
|
|||
description: Mouse scroll
|
||||
|
||||
compatible: "zmk,behavior-mouse-scroll"
|
||||
|
||||
include: one_param.yaml
|
||||
|
||||
properties:
|
||||
delay-ms:
|
||||
type: int
|
||||
time-to-max-speed-ms:
|
||||
type: int
|
||||
acceleration-exponent:
|
||||
type: int
|
27
app/dts/bindings/behaviors/zmk,behavior-tri-state.yaml
Normal file
27
app/dts/bindings/behaviors/zmk,behavior-tri-state.yaml
Normal file
|
@ -0,0 +1,27 @@
|
|||
# Copyright (c) 2022 The ZMK Contributors
|
||||
# SPDX-License-Identifier: MIT
|
||||
|
||||
description: Tri-State Behavior
|
||||
|
||||
compatible: "zmk,behavior-tri-state"
|
||||
|
||||
include: zero_param.yaml
|
||||
|
||||
properties:
|
||||
bindings:
|
||||
type: phandle-array
|
||||
required: true
|
||||
ignored-key-positions:
|
||||
type: array
|
||||
required: false
|
||||
default: []
|
||||
ignored-layers:
|
||||
type: array
|
||||
required: false
|
||||
default: []
|
||||
timeout-ms:
|
||||
type: int
|
||||
default: -1
|
||||
tap-ms:
|
||||
type: int
|
||||
default: 5
|
|
@ -26,6 +26,7 @@
|
|||
#define HID_USAGE_GDV (0x06) // Generic Device Controls
|
||||
#define HID_USAGE_KEY (0x07) // Keyboard/Keypad
|
||||
#define HID_USAGE_LED (0x08) // LED
|
||||
#define HID_USAGE_BUTTON (0x09) // Button
|
||||
#define HID_USAGE_TELEPHONY (0x0B) // Telephony Device
|
||||
#define HID_USAGE_CONSUMER (0x0C) // Consumer
|
||||
#define HID_USAGE_DIGITIZERS (0x0D) // Digitizers
|
||||
|
|
55
app/include/dt-bindings/zmk/mouse.h
Normal file
55
app/include/dt-bindings/zmk/mouse.h
Normal file
|
@ -0,0 +1,55 @@
|
|||
/*
|
||||
* Copyright (c) 2020 The ZMK Contributors
|
||||
*
|
||||
* SPDX-License-Identifier: MIT
|
||||
*/
|
||||
#pragma once
|
||||
|
||||
/* Mouse press behavior */
|
||||
/* Left click */
|
||||
#define MB1 (0x01)
|
||||
#define LCLK (MB1)
|
||||
|
||||
/* Right click */
|
||||
#define MB2 (0x02)
|
||||
#define RCLK (MB2)
|
||||
|
||||
/* Middle click */
|
||||
#define MB3 (0x04)
|
||||
#define MCLK (MB3)
|
||||
|
||||
#define MB4 (0x08)
|
||||
|
||||
#define MB5 (0x10)
|
||||
|
||||
#define MB6 (0x20)
|
||||
|
||||
#define MB7 (0x40)
|
||||
|
||||
#define MB8 (0x80)
|
||||
|
||||
/* Mouse move behavior */
|
||||
#define MOVE_VERT(vert) ((vert)&0xFFFF)
|
||||
#define MOVE_VERT_DECODE(encoded) (int16_t)((encoded)&0x0000FFFF)
|
||||
#define MOVE_HOR(hor) (((hor)&0xFFFF) << 16)
|
||||
#define MOVE_HOR_DECODE(encoded) (int16_t)(((encoded)&0xFFFF0000) >> 16)
|
||||
|
||||
#define MOVE(hor, vert) (MOVE_HOR(hor) + MOVE_VERT(vert))
|
||||
|
||||
#define MOVE_UP MOVE_VERT(-600)
|
||||
#define MOVE_DOWN MOVE_VERT(600)
|
||||
#define MOVE_LEFT MOVE_HOR(-600)
|
||||
#define MOVE_RIGHT MOVE_HOR(600)
|
||||
|
||||
/* Mouse scroll behavior */
|
||||
#define SCROLL_VERT(vert) ((vert)&0xFFFF)
|
||||
#define SCROLL_VERT_DECODE(encoded) (int16_t)((encoded)&0x0000FFFF)
|
||||
#define SCROLL_HOR(hor) (((hor)&0xFFFF) << 16)
|
||||
#define SCROLL_HOR_DECODE(encoded) (int16_t)(((encoded)&0xFFFF0000) >> 16)
|
||||
|
||||
#define SCROLL(hor, vert) (SCROLL_HOR(hor) + SCROLL_VERT(vert))
|
||||
|
||||
#define SCROLL_UP SCROLL_VERT(10)
|
||||
#define SCROLL_DOWN SCROLL_VERT(-10)
|
||||
#define SCROLL_LEFT SCROLL_HOR(-10)
|
||||
#define SCROLL_RIGHT SCROLL_HOR(10)
|
|
@ -69,3 +69,4 @@ int zmk_endpoints_toggle_transport(void);
|
|||
struct zmk_endpoint_instance zmk_endpoints_selected(void);
|
||||
|
||||
int zmk_endpoints_send_report(uint16_t usage_page);
|
||||
int zmk_endpoints_send_mouse_report();
|
||||
|
|
27
app/include/zmk/events/mouse_button_state_changed.h
Normal file
27
app/include/zmk/events/mouse_button_state_changed.h
Normal file
|
@ -0,0 +1,27 @@
|
|||
|
||||
/*
|
||||
* Copyright (c) 2020 The ZMK Contributors
|
||||
*
|
||||
* SPDX-License-Identifier: MIT
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <zephyr/kernel.h>
|
||||
#include <zmk/hid.h>
|
||||
#include <zmk/event_manager.h>
|
||||
#include <zmk/mouse.h>
|
||||
|
||||
struct zmk_mouse_button_state_changed {
|
||||
zmk_mouse_button_t buttons;
|
||||
bool state;
|
||||
int64_t timestamp;
|
||||
};
|
||||
|
||||
ZMK_EVENT_DECLARE(zmk_mouse_button_state_changed);
|
||||
|
||||
static inline struct zmk_mouse_button_state_changed_event *
|
||||
zmk_mouse_button_state_changed_from_encoded(uint32_t encoded, bool pressed, int64_t timestamp) {
|
||||
return new_zmk_mouse_button_state_changed((struct zmk_mouse_button_state_changed){
|
||||
.buttons = ZMK_HID_USAGE_ID(encoded), .state = pressed, .timestamp = timestamp});
|
||||
}
|
33
app/include/zmk/events/mouse_move_state_changed.h
Normal file
33
app/include/zmk/events/mouse_move_state_changed.h
Normal file
|
@ -0,0 +1,33 @@
|
|||
|
||||
/*
|
||||
* Copyright (c) 2020 The ZMK Contributors
|
||||
*
|
||||
* SPDX-License-Identifier: MIT
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <zephyr/kernel.h>
|
||||
#include <zmk/event_manager.h>
|
||||
#include <zmk/mouse.h>
|
||||
|
||||
struct zmk_mouse_move_state_changed {
|
||||
struct vector2d max_speed;
|
||||
struct mouse_config config;
|
||||
bool state;
|
||||
int64_t timestamp;
|
||||
};
|
||||
|
||||
ZMK_EVENT_DECLARE(zmk_mouse_move_state_changed);
|
||||
|
||||
static inline struct zmk_mouse_move_state_changed_event *
|
||||
zmk_mouse_move_state_changed_from_encoded(uint32_t encoded, struct mouse_config config,
|
||||
bool pressed, int64_t timestamp) {
|
||||
struct vector2d max_speed = (struct vector2d){
|
||||
.x = MOVE_HOR_DECODE(encoded),
|
||||
.y = MOVE_VERT_DECODE(encoded),
|
||||
};
|
||||
|
||||
return new_zmk_mouse_move_state_changed((struct zmk_mouse_move_state_changed){
|
||||
.max_speed = max_speed, .config = config, .state = pressed, .timestamp = timestamp});
|
||||
}
|
34
app/include/zmk/events/mouse_scroll_state_changed.h
Normal file
34
app/include/zmk/events/mouse_scroll_state_changed.h
Normal file
|
@ -0,0 +1,34 @@
|
|||
|
||||
/*
|
||||
* Copyright (c) 2020 The ZMK Contributors
|
||||
*
|
||||
* SPDX-License-Identifier: MIT
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <zephyr/kernel.h>
|
||||
#include <zmk/event_manager.h>
|
||||
#include <zmk/mouse.h>
|
||||
#include <dt-bindings/zmk/mouse.h>
|
||||
|
||||
struct zmk_mouse_scroll_state_changed {
|
||||
struct vector2d max_speed;
|
||||
struct mouse_config config;
|
||||
bool state;
|
||||
int64_t timestamp;
|
||||
};
|
||||
|
||||
ZMK_EVENT_DECLARE(zmk_mouse_scroll_state_changed);
|
||||
|
||||
static inline struct zmk_mouse_scroll_state_changed_event *
|
||||
zmk_mouse_scroll_state_changed_from_encoded(uint32_t encoded, struct mouse_config config,
|
||||
bool pressed, int64_t timestamp) {
|
||||
struct vector2d max_speed = (struct vector2d){
|
||||
.x = SCROLL_HOR_DECODE(encoded),
|
||||
.y = SCROLL_VERT_DECODE(encoded),
|
||||
};
|
||||
|
||||
return new_zmk_mouse_scroll_state_changed((struct zmk_mouse_scroll_state_changed){
|
||||
.max_speed = max_speed, .config = config, .state = pressed, .timestamp = timestamp});
|
||||
}
|
39
app/include/zmk/events/mouse_tick.h
Normal file
39
app/include/zmk/events/mouse_tick.h
Normal file
|
@ -0,0 +1,39 @@
|
|||
|
||||
/*
|
||||
* Copyright (c) 2020 The ZMK Contributors
|
||||
*
|
||||
* SPDX-License-Identifier: MIT
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <dt-bindings/zmk/mouse.h>
|
||||
#include <zephyr/kernel.h>
|
||||
#include <zmk/event_manager.h>
|
||||
#include <zmk/mouse.h>
|
||||
|
||||
struct zmk_mouse_tick {
|
||||
struct vector2d max_move;
|
||||
struct vector2d max_scroll;
|
||||
struct mouse_config move_config;
|
||||
struct mouse_config scroll_config;
|
||||
int64_t *start_time;
|
||||
int64_t timestamp;
|
||||
};
|
||||
|
||||
ZMK_EVENT_DECLARE(zmk_mouse_tick);
|
||||
|
||||
static inline struct zmk_mouse_tick_event *zmk_mouse_tick(struct vector2d max_move,
|
||||
struct vector2d max_scroll,
|
||||
struct mouse_config move_config,
|
||||
struct mouse_config scroll_config,
|
||||
int64_t *movement_start) {
|
||||
return new_zmk_mouse_tick((struct zmk_mouse_tick){
|
||||
.max_move = max_move,
|
||||
.max_scroll = max_scroll,
|
||||
.move_config = move_config,
|
||||
.scroll_config = scroll_config,
|
||||
.start_time = movement_start,
|
||||
.timestamp = k_uptime_get(),
|
||||
});
|
||||
}
|
|
@ -10,6 +10,7 @@
|
|||
#include <zephyr/usb/class/usb_hid.h>
|
||||
|
||||
#include <zmk/keys.h>
|
||||
#include <zmk/mouse.h>
|
||||
#include <dt-bindings/zmk/hid_usage.h>
|
||||
#include <dt-bindings/zmk/hid_usage_pages.h>
|
||||
|
||||
|
@ -85,9 +86,85 @@ static const uint8_t zmk_hid_report_desc[] = {
|
|||
#else
|
||||
#error "A proper consumer HID report usage range must be selected"
|
||||
#endif
|
||||
/* REPORT_COUNT (CONFIG_ZMK_HID_CONSUMER_REPORT_SIZE) */
|
||||
HID_REPORT_COUNT(CONFIG_ZMK_HID_CONSUMER_REPORT_SIZE),
|
||||
/* INPUT (Data,Ary,Abs) */
|
||||
HID_INPUT(0x00),
|
||||
/* END COLLECTION */
|
||||
HID_END_COLLECTION,
|
||||
|
||||
/* USAGE_PAGE (Generic Desktop) */
|
||||
HID_USAGE_PAGE(HID_USAGE_GD),
|
||||
/* USAGE (Mouse) */
|
||||
HID_USAGE(HID_USAGE_GD_MOUSE),
|
||||
/* COLLECTION (Application) */
|
||||
HID_COLLECTION(HID_COLLECTION_APPLICATION),
|
||||
/* REPORT ID (4) */
|
||||
HID_REPORT_ID(0x04),
|
||||
/* USAGE (Pointer) */
|
||||
HID_USAGE(HID_USAGE_GD_POINTER),
|
||||
/* COLLECTION (Physical) */
|
||||
HID_COLLECTION(HID_COLLECTION_PHYSICAL),
|
||||
/* USAGE_PAGE (Button) */
|
||||
HID_USAGE_PAGE(HID_USAGE_BUTTON),
|
||||
/* USAGE_MINIMUM (0x1) (button 1?) */
|
||||
HID_USAGE_MIN8(0x01),
|
||||
/* USAGE_MAXIMUM (0x10) (button 5? Buttons up to 8 still work) */
|
||||
HID_USAGE_MAX8(0x10),
|
||||
/* LOGICAL_MINIMUM (0) */
|
||||
HID_LOGICAL_MIN8(0x00),
|
||||
/* LOGICAL_MAXIMUM (1) */
|
||||
HID_LOGICAL_MAX8(0x01),
|
||||
/* REPORT_SIZE (1) */
|
||||
HID_REPORT_SIZE(0x01),
|
||||
/* REPORT_COUNT (16) */
|
||||
HID_REPORT_COUNT(0x10),
|
||||
/* INPUT (Data,Var,Abs) */
|
||||
HID_INPUT(0x02),
|
||||
/* USAGE_PAGE (Generic Desktop) */
|
||||
HID_USAGE_PAGE(HID_USAGE_GD),
|
||||
/* LOGICAL_MINIMUM (-32767) */
|
||||
HID_LOGICAL_MIN16(0x01, 0x80),
|
||||
/* LOGICAL_MAXIMUM (32767) */
|
||||
HID_LOGICAL_MAX16(0xFF, 0x7F),
|
||||
/* REPORT_SIZE (16) */
|
||||
HID_REPORT_SIZE(0x10),
|
||||
/* REPORT_COUNT (2) */
|
||||
HID_REPORT_COUNT(0x02),
|
||||
/* USAGE (X) */ // Vertical scroll
|
||||
HID_USAGE(HID_USAGE_GD_X),
|
||||
/* USAGE (Y) */
|
||||
HID_USAGE(HID_USAGE_GD_Y),
|
||||
/* Input (Data,Var,Rel) */
|
||||
HID_INPUT(0x06),
|
||||
/* LOGICAL_MINIMUM (-127) */
|
||||
HID_LOGICAL_MIN8(0x81),
|
||||
/* LOGICAL_MAXIMUM (127) */
|
||||
HID_LOGICAL_MAX8(0x7F),
|
||||
/* REPORT_SIZE (8) */
|
||||
HID_REPORT_SIZE(0x08),
|
||||
/* REPORT_COUNT (1) */
|
||||
HID_REPORT_COUNT(0x01),
|
||||
/* USAGE (Wheel) */
|
||||
HID_USAGE(HID_USAGE_GD_WHEEL),
|
||||
/* Input (Data,Var,Rel) */
|
||||
HID_INPUT(0x06),
|
||||
/* USAGE_PAGE (Consumer) */ // Horizontal scroll
|
||||
HID_USAGE_PAGE(HID_USAGE_CONSUMER),
|
||||
/* USAGE (AC Pan) */
|
||||
0x0A,
|
||||
0x38,
|
||||
0x02,
|
||||
/* LOGICAL_MINIMUM (-127) */
|
||||
HID_LOGICAL_MIN8(0x81),
|
||||
/* LOGICAL_MAXIMUM (127) */
|
||||
HID_LOGICAL_MAX8(0x7F),
|
||||
/* REPORT_COUNT (1) */
|
||||
HID_REPORT_COUNT(0x01),
|
||||
/* Input (Data,Var,Rel) */
|
||||
HID_INPUT(0x06),
|
||||
/* END COLLECTION */
|
||||
HID_END_COLLECTION,
|
||||
/* END COLLECTION */
|
||||
HID_END_COLLECTION,
|
||||
};
|
||||
|
||||
|
@ -126,6 +203,19 @@ struct zmk_hid_consumer_report {
|
|||
struct zmk_hid_consumer_report_body body;
|
||||
} __packed;
|
||||
|
||||
struct zmk_hid_mouse_report_body {
|
||||
zmk_mouse_button_flags_t buttons;
|
||||
int16_t x;
|
||||
int16_t y;
|
||||
int8_t scroll_y;
|
||||
int8_t scroll_x;
|
||||
} __packed;
|
||||
|
||||
struct zmk_hid_mouse_report {
|
||||
uint8_t report_id;
|
||||
struct zmk_hid_mouse_report_body body;
|
||||
} __packed;
|
||||
|
||||
zmk_mod_flags_t zmk_hid_get_explicit_mods();
|
||||
int zmk_hid_register_mod(zmk_mod_t modifier);
|
||||
int zmk_hid_unregister_mod(zmk_mod_t modifier);
|
||||
|
@ -152,5 +242,16 @@ int zmk_hid_press(uint32_t usage);
|
|||
int zmk_hid_release(uint32_t usage);
|
||||
bool zmk_hid_is_pressed(uint32_t usage);
|
||||
|
||||
int zmk_hid_mouse_button_press(zmk_mouse_button_t button);
|
||||
int zmk_hid_mouse_button_release(zmk_mouse_button_t button);
|
||||
int zmk_hid_mouse_buttons_press(zmk_mouse_button_flags_t buttons);
|
||||
int zmk_hid_mouse_buttons_release(zmk_mouse_button_flags_t buttons);
|
||||
void zmk_hid_mouse_movement_set(int16_t x, int16_t y);
|
||||
void zmk_hid_mouse_scroll_set(int8_t x, int8_t y);
|
||||
void zmk_hid_mouse_movement_update(int16_t x, int16_t y);
|
||||
void zmk_hid_mouse_scroll_update(int8_t x, int8_t y);
|
||||
void zmk_hid_mouse_clear();
|
||||
|
||||
struct zmk_hid_keyboard_report *zmk_hid_get_keyboard_report();
|
||||
struct zmk_hid_consumer_report *zmk_hid_get_consumer_report();
|
||||
struct zmk_hid_mouse_report *zmk_hid_get_mouse_report();
|
||||
|
|
|
@ -13,3 +13,5 @@ int zmk_hog_init();
|
|||
|
||||
int zmk_hog_send_keyboard_report(struct zmk_hid_keyboard_report_body *body);
|
||||
int zmk_hog_send_consumer_report(struct zmk_hid_consumer_report_body *body);
|
||||
int zmk_hog_send_mouse_report(struct zmk_hid_mouse_report_body *body);
|
||||
int zmk_hog_send_mouse_report_direct(struct zmk_hid_mouse_report_body *body);
|
||||
|
|
|
@ -17,8 +17,10 @@ typedef uint32_t zmk_keymap_layers_state_t;
|
|||
uint8_t zmk_keymap_layer_default();
|
||||
zmk_keymap_layers_state_t zmk_keymap_layer_state();
|
||||
bool zmk_keymap_layer_active(uint8_t layer);
|
||||
bool zmk_keymap_layer_momentary(uint8_t layer);
|
||||
bool zmk_keymap_layers_any_momentary(zmk_keymap_layers_state_t layers_mask);
|
||||
uint8_t zmk_keymap_highest_layer_active();
|
||||
int zmk_keymap_layer_activate(uint8_t layer);
|
||||
int zmk_keymap_layer_activate(uint8_t layer, bool momentary);
|
||||
int zmk_keymap_layer_deactivate(uint8_t layer);
|
||||
int zmk_keymap_layer_toggle(uint8_t layer);
|
||||
int zmk_keymap_layer_to(uint8_t layer);
|
||||
|
|
30
app/include/zmk/mouse.h
Normal file
30
app/include/zmk/mouse.h
Normal file
|
@ -0,0 +1,30 @@
|
|||
/*
|
||||
* Copyright (c) 2021 The ZMK Contributors
|
||||
*
|
||||
* SPDX-License-Identifier: MIT
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <zephyr/kernel.h>
|
||||
#include <dt-bindings/zmk/mouse.h>
|
||||
|
||||
typedef uint16_t zmk_mouse_button_flags_t;
|
||||
typedef uint16_t zmk_mouse_button_t;
|
||||
|
||||
struct mouse_config {
|
||||
int delay_ms;
|
||||
int time_to_max_speed_ms;
|
||||
// acceleration exponent 0: uniform speed
|
||||
// acceleration exponent 1: uniform acceleration
|
||||
// acceleration exponent 2: uniform jerk
|
||||
int acceleration_exponent;
|
||||
};
|
||||
|
||||
struct vector2d {
|
||||
float x;
|
||||
float y;
|
||||
};
|
||||
|
||||
struct k_work_q *zmk_mouse_work_q();
|
||||
int zmk_mouse_init();
|
|
@ -8,4 +8,12 @@ config IL0323
|
|||
depends on SPI
|
||||
depends on HEAP_MEM_POOL_SIZE != 0
|
||||
help
|
||||
Enable driver for IL0323 compatible controller.
|
||||
Enable driver for IL0323 compatible controller.
|
||||
|
||||
config IL0323_INVERT
|
||||
bool "Invert display"
|
||||
default n
|
||||
|
||||
config IL0323_ALTERNATIVE_REFRESH
|
||||
bool "Use an alternative approach for partial refreshes"
|
||||
default n
|
||||
|
|
|
@ -124,7 +124,11 @@ static int il0323_write(const struct device *dev, const uint16_t x, const uint16
|
|||
ptl[IL0323_PTL_HRED_IDX] = x_end_idx;
|
||||
ptl[IL0323_PTL_VRST_IDX] = y;
|
||||
ptl[IL0323_PTL_VRED_IDX] = y_end_idx;
|
||||
#if IS_ENABLED(CONFIG_IL0323_ALTERNATIVE_REFRESH)
|
||||
ptl[sizeof(ptl) - 1] = 0; // limits fading outside of refresh window
|
||||
#else
|
||||
ptl[sizeof(ptl) - 1] = IL0323_PTL_PT_SCAN;
|
||||
#endif
|
||||
LOG_HEXDUMP_DBG(ptl, sizeof(ptl), "ptl");
|
||||
|
||||
il0323_busy_wait(cfg);
|
||||
|
@ -242,8 +246,12 @@ static void il0323_get_capabilities(const struct device *dev, struct display_cap
|
|||
memset(caps, 0, sizeof(struct display_capabilities));
|
||||
caps->x_resolution = EPD_PANEL_WIDTH;
|
||||
caps->y_resolution = EPD_PANEL_HEIGHT;
|
||||
caps->supported_pixel_formats = PIXEL_FORMAT_MONO10;
|
||||
caps->supported_pixel_formats = PIXEL_FORMAT_MONO10 | PIXEL_FORMAT_MONO01;
|
||||
#if IS_ENABLED(CONFIG_IL0323_INVERT)
|
||||
caps->current_pixel_format = PIXEL_FORMAT_MONO01;
|
||||
#else
|
||||
caps->current_pixel_format = PIXEL_FORMAT_MONO10;
|
||||
#endif
|
||||
caps->screen_info = SCREEN_INFO_MONO_MSB_FIRST | SCREEN_INFO_EPD;
|
||||
}
|
||||
|
||||
|
@ -254,7 +262,7 @@ static int il0323_set_orientation(const struct device *dev,
|
|||
}
|
||||
|
||||
static int il0323_set_pixel_format(const struct device *dev, const enum display_pixel_format pf) {
|
||||
if (pf == PIXEL_FORMAT_MONO10) {
|
||||
if ((pf == PIXEL_FORMAT_MONO10) || (pf == PIXEL_FORMAT_MONO10)) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
|
|
@ -32,6 +32,10 @@ struct caps_word_continue_item {
|
|||
|
||||
struct behavior_caps_word_config {
|
||||
zmk_mod_flags_t mods;
|
||||
int8_t layers;
|
||||
bool ignore_alphas;
|
||||
bool ignore_numbers;
|
||||
bool ignore_modifiers;
|
||||
uint8_t index;
|
||||
uint8_t continuations_count;
|
||||
struct caps_word_continue_item continuations[];
|
||||
|
@ -44,12 +48,22 @@ struct behavior_caps_word_data {
|
|||
static void activate_caps_word(const struct device *dev) {
|
||||
struct behavior_caps_word_data *data = dev->data;
|
||||
|
||||
const struct behavior_caps_word_config *config = dev->config;
|
||||
|
||||
if (config->layers > -1) {
|
||||
zmk_keymap_layer_activate(config->layers, false);
|
||||
}
|
||||
data->active = true;
|
||||
}
|
||||
|
||||
static void deactivate_caps_word(const struct device *dev) {
|
||||
struct behavior_caps_word_data *data = dev->data;
|
||||
|
||||
const struct behavior_caps_word_config *config = dev->config;
|
||||
|
||||
if (config->layers > -1) {
|
||||
zmk_keymap_layer_deactivate(config->layers);
|
||||
}
|
||||
data->active = false;
|
||||
}
|
||||
|
||||
|
@ -120,8 +134,10 @@ static void caps_word_enhance_usage(const struct behavior_caps_word_config *conf
|
|||
return;
|
||||
}
|
||||
|
||||
LOG_DBG("Enhancing usage 0x%02X with modifiers: 0x%02X", ev->keycode, config->mods);
|
||||
ev->implicit_modifiers |= config->mods;
|
||||
if (config->mods != 0) {
|
||||
LOG_DBG("Enhancing usage 0x%02X with modifiers: 0x%02X", ev->keycode, config->mods);
|
||||
ev->implicit_modifiers |= config->mods;
|
||||
}
|
||||
}
|
||||
|
||||
static int caps_word_keycode_state_changed_listener(const zmk_event_t *eh) {
|
||||
|
@ -145,8 +161,9 @@ static int caps_word_keycode_state_changed_listener(const zmk_event_t *eh) {
|
|||
|
||||
caps_word_enhance_usage(config, ev);
|
||||
|
||||
if (!caps_word_is_alpha(ev->keycode) && !caps_word_is_numeric(ev->keycode) &&
|
||||
!is_mod(ev->usage_page, ev->keycode) &&
|
||||
if ((!caps_word_is_alpha(ev->keycode) || !config->ignore_alphas) &&
|
||||
(!caps_word_is_numeric(ev->keycode) || !config->ignore_numbers) &&
|
||||
(!is_mod(ev->usage_page, ev->keycode) || !config->ignore_modifiers) &&
|
||||
!caps_word_is_caps_includelist(config, ev->usage_page, ev->keycode,
|
||||
ev->implicit_modifiers)) {
|
||||
LOG_DBG("Deactivating caps_word for 0x%02X - 0x%02X", ev->usage_page, ev->keycode);
|
||||
|
@ -177,7 +194,11 @@ static int behavior_caps_word_init(const struct device *dev) {
|
|||
static struct behavior_caps_word_data behavior_caps_word_data_##n = {.active = false}; \
|
||||
static struct behavior_caps_word_config behavior_caps_word_config_##n = { \
|
||||
.index = n, \
|
||||
.mods = DT_INST_PROP_OR(n, mods, MOD_LSFT), \
|
||||
.mods = DT_INST_PROP_OR(n, mods, 0), \
|
||||
.layers = DT_INST_PROP_OR(n, layers, -1), \
|
||||
.ignore_alphas = DT_INST_PROP(n, ignore_alphas), \
|
||||
.ignore_numbers = DT_INST_PROP(n, ignore_numbers), \
|
||||
.ignore_modifiers = DT_INST_PROP(n, ignore_modifiers), \
|
||||
.continuations = {LISTIFY(DT_INST_PROP_LEN(n, continue_list), BREAK_ITEM, (, ), n)}, \
|
||||
.continuations_count = DT_INST_PROP_LEN(n, continue_list), \
|
||||
}; \
|
||||
|
|
|
@ -78,6 +78,7 @@ struct active_hold_tap {
|
|||
|
||||
// initialized to -1, which is to be interpreted as "no other key has been pressed yet"
|
||||
int32_t position_of_first_other_key_pressed;
|
||||
int32_t position_of_first_other_key_released;
|
||||
};
|
||||
|
||||
// The undecided hold tap is the hold tap that needs to be decided before
|
||||
|
@ -229,6 +230,7 @@ static struct active_hold_tap *store_hold_tap(uint32_t position, uint32_t param_
|
|||
active_hold_taps[i].param_tap = param_tap;
|
||||
active_hold_taps[i].timestamp = timestamp;
|
||||
active_hold_taps[i].position_of_first_other_key_pressed = -1;
|
||||
active_hold_taps[i].position_of_first_other_key_released = -1;
|
||||
return &active_hold_taps[i];
|
||||
}
|
||||
return NULL;
|
||||
|
@ -414,27 +416,73 @@ static bool is_first_other_key_pressed_trigger_key(struct active_hold_tap *hold_
|
|||
return false;
|
||||
}
|
||||
|
||||
// Force a tap decision if the positional conditions for a hold decision are not met.
|
||||
static bool is_first_other_key_released_trigger_key(struct active_hold_tap *hold_tap) {
|
||||
for (int i = 0; i < hold_tap->config->hold_trigger_key_positions_len; i++) {
|
||||
if (hold_tap->config->hold_trigger_key_positions[i] ==
|
||||
hold_tap->position_of_first_other_key_released) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
static void decide_positional_hold(struct active_hold_tap *hold_tap) {
|
||||
// Only force a tap decision if the positional hold/tap feature is enabled.
|
||||
|
||||
// Positional conditions is not active?
|
||||
if (!(hold_tap->config->hold_trigger_key_positions_len > 0)) {
|
||||
return;
|
||||
return; // apply flavour
|
||||
}
|
||||
|
||||
// Only force a tap decision if another key was pressed after
|
||||
// the hold/tap key.
|
||||
|
||||
// Pressed key is not set?
|
||||
if (hold_tap->position_of_first_other_key_pressed == -1) {
|
||||
return;
|
||||
return; // apply flavor
|
||||
}
|
||||
|
||||
// Only force a tap decision if the first other key to be pressed
|
||||
// (after the hold/tap key) is not one of the trigger keys.
|
||||
|
||||
// Pressed key is included in positions?
|
||||
if (is_first_other_key_pressed_trigger_key(hold_tap)) {
|
||||
return;
|
||||
return; // apply flavor
|
||||
}
|
||||
|
||||
// Since the positional key conditions have failed, force a TAP decision.
|
||||
// Pressed key is not included in positions.
|
||||
// We act on press?
|
||||
if (undecided_hold_tap->config->hold_trigger_on_release == false) {
|
||||
hold_tap->status = STATUS_TAP;
|
||||
return; // ignore flavor, set TAP
|
||||
}
|
||||
|
||||
// We act on release.
|
||||
// Released key is not set?
|
||||
if (hold_tap->position_of_first_other_key_released == -1)
|
||||
{
|
||||
// Is current decision hold based on key pressed?
|
||||
if (hold_tap->status == STATUS_HOLD_INTERRUPT) {
|
||||
|
||||
// We can't decide yet if key which will be released:
|
||||
// - not in positions
|
||||
// - be released before timer
|
||||
// So we can't decide yet if we should overwrite decision to TAP.
|
||||
// We have to wait for key release.
|
||||
|
||||
hold_tap->status = STATUS_UNDECIDED;
|
||||
return; // remove flavor
|
||||
}
|
||||
|
||||
// There decision is decision:
|
||||
// - STATUS_HOLD_TIMER - tapping term reached, apply flavor
|
||||
// - STATUS_TAP - even if we set TAP later it will not change decision
|
||||
return; // apply flavor
|
||||
}
|
||||
|
||||
|
||||
// Released key is included in positions?
|
||||
if (is_first_other_key_released_trigger_key(hold_tap)) {
|
||||
return; // apply flavor
|
||||
}
|
||||
|
||||
// Released key is not included in positions.
|
||||
hold_tap->status = STATUS_TAP;
|
||||
return; // ignore flavor, set TAP
|
||||
}
|
||||
|
||||
static void decide_hold_tap(struct active_hold_tap *hold_tap,
|
||||
|
@ -470,6 +518,9 @@ static void decide_hold_tap(struct active_hold_tap *hold_tap,
|
|||
|
||||
decide_positional_hold(hold_tap);
|
||||
|
||||
if (hold_tap->status == STATUS_UNDECIDED) {
|
||||
return;
|
||||
}
|
||||
// Since the hold-tap has been decided, clean up undecided_hold_tap and
|
||||
// execute the decided behavior.
|
||||
LOG_DBG("%d decided %s (%s decision moment %s)", hold_tap->position,
|
||||
|
@ -591,15 +642,16 @@ static int position_state_changed_listener(const zmk_event_t *eh) {
|
|||
}
|
||||
|
||||
// Store the position of pressed key for positional hold-tap purposes.
|
||||
if ((undecided_hold_tap->config->hold_trigger_on_release !=
|
||||
ev->state) // key has been pressed and hold_trigger_on_release is not set, or key
|
||||
// has been released and hold_trigger_on_release is set
|
||||
&& (undecided_hold_tap->position_of_first_other_key_pressed ==
|
||||
-1) // no other key has been pressed yet
|
||||
) {
|
||||
if (ev->state && (undecided_hold_tap->position_of_first_other_key_pressed == -1)) {
|
||||
undecided_hold_tap->position_of_first_other_key_pressed = ev->position;
|
||||
}
|
||||
|
||||
// Store the position of pressed key for positional hold-tap purposes.
|
||||
if (!ev->state && undecided_hold_tap->config->hold_trigger_on_release &&
|
||||
(undecided_hold_tap->position_of_first_other_key_released == -1)) {
|
||||
undecided_hold_tap->position_of_first_other_key_released = ev->position;
|
||||
}
|
||||
|
||||
if (undecided_hold_tap->position == ev->position) {
|
||||
if (ev->state) { // keydown
|
||||
LOG_ERR("hold-tap listener should be called before before most other listeners!");
|
||||
|
|
|
@ -23,7 +23,7 @@ static int behavior_mo_init(const struct device *dev) { return 0; };
|
|||
static int mo_keymap_binding_pressed(struct zmk_behavior_binding *binding,
|
||||
struct zmk_behavior_binding_event event) {
|
||||
LOG_DBG("position %d layer %d", event.position, binding->param1);
|
||||
return zmk_keymap_layer_activate(binding->param1);
|
||||
return zmk_keymap_layer_activate(binding->param1, true);
|
||||
}
|
||||
|
||||
static int mo_keymap_binding_released(struct zmk_behavior_binding *binding,
|
||||
|
|
48
app/src/behaviors/behavior_mouse_key_press.c
Normal file
48
app/src/behaviors/behavior_mouse_key_press.c
Normal file
|
@ -0,0 +1,48 @@
|
|||
/*
|
||||
* Copyright (c) 2021 The ZMK Contributors
|
||||
*
|
||||
* SPDX-License-Identifier: MIT
|
||||
*/
|
||||
|
||||
#define DT_DRV_COMPAT zmk_behavior_mouse_key_press
|
||||
|
||||
#include <zephyr/device.h>
|
||||
#include <drivers/behavior.h>
|
||||
#include <zephyr/logging/log.h>
|
||||
|
||||
#include <zmk/behavior.h>
|
||||
#include <zmk/event_manager.h>
|
||||
#include <zmk/events/mouse_button_state_changed.h>
|
||||
|
||||
LOG_MODULE_DECLARE(zmk, CONFIG_ZMK_LOG_LEVEL);
|
||||
|
||||
#if DT_HAS_COMPAT_STATUS_OKAY(DT_DRV_COMPAT)
|
||||
|
||||
static int behavior_mouse_key_press_init(const struct device *dev) { return 0; };
|
||||
|
||||
static int on_keymap_binding_pressed(struct zmk_behavior_binding *binding,
|
||||
struct zmk_behavior_binding_event event) {
|
||||
LOG_DBG("position %d keycode 0x%02X", event.position, binding->param1);
|
||||
|
||||
return ZMK_EVENT_RAISE(
|
||||
zmk_mouse_button_state_changed_from_encoded(binding->param1, true, event.timestamp));
|
||||
}
|
||||
|
||||
static int on_keymap_binding_released(struct zmk_behavior_binding *binding,
|
||||
struct zmk_behavior_binding_event event) {
|
||||
LOG_DBG("position %d keycode 0x%02X", event.position, binding->param1);
|
||||
return ZMK_EVENT_RAISE(
|
||||
zmk_mouse_button_state_changed_from_encoded(binding->param1, false, event.timestamp));
|
||||
}
|
||||
|
||||
static const struct behavior_driver_api behavior_mouse_key_press_driver_api = {
|
||||
.binding_pressed = on_keymap_binding_pressed, .binding_released = on_keymap_binding_released};
|
||||
|
||||
#define KP_INST(n) \
|
||||
DEVICE_DT_INST_DEFINE(n, behavior_mouse_key_press_init, NULL, NULL, NULL, APPLICATION, \
|
||||
CONFIG_KERNEL_INIT_PRIORITY_DEFAULT, \
|
||||
&behavior_mouse_key_press_driver_api);
|
||||
|
||||
DT_INST_FOREACH_STATUS_OKAY(KP_INST)
|
||||
|
||||
#endif /* DT_HAS_COMPAT_STATUS_OKAY(DT_DRV_COMPAT) */
|
57
app/src/behaviors/behavior_mouse_move.c
Normal file
57
app/src/behaviors/behavior_mouse_move.c
Normal file
|
@ -0,0 +1,57 @@
|
|||
/*
|
||||
* Copyright (c) 2021 The ZMK Contributors
|
||||
*
|
||||
* SPDX-License-Identifier: MIT
|
||||
*/
|
||||
|
||||
#define DT_DRV_COMPAT zmk_behavior_mouse_move
|
||||
|
||||
#include <zephyr/device.h>
|
||||
#include <drivers/behavior.h>
|
||||
#include <zephyr/logging/log.h>
|
||||
|
||||
#include <zmk/behavior.h>
|
||||
#include <zmk/event_manager.h>
|
||||
#include <zmk/events/mouse_move_state_changed.h>
|
||||
#include <zmk/mouse.h>
|
||||
|
||||
LOG_MODULE_DECLARE(zmk, CONFIG_ZMK_LOG_LEVEL);
|
||||
|
||||
#if DT_HAS_COMPAT_STATUS_OKAY(DT_DRV_COMPAT)
|
||||
|
||||
static int behavior_mouse_move_init(const struct device *dev) { return 0; };
|
||||
|
||||
static int on_keymap_binding_pressed(struct zmk_behavior_binding *binding,
|
||||
struct zmk_behavior_binding_event event) {
|
||||
LOG_DBG("position %d keycode 0x%02X", event.position, binding->param1);
|
||||
const struct device *dev = device_get_binding(binding->behavior_dev);
|
||||
const struct mouse_config *config = dev->config;
|
||||
return ZMK_EVENT_RAISE(
|
||||
zmk_mouse_move_state_changed_from_encoded(binding->param1, *config, true, event.timestamp));
|
||||
}
|
||||
|
||||
static int on_keymap_binding_released(struct zmk_behavior_binding *binding,
|
||||
struct zmk_behavior_binding_event event) {
|
||||
LOG_DBG("position %d keycode 0x%02X", event.position, binding->param1);
|
||||
const struct device *dev = device_get_binding(binding->behavior_dev);
|
||||
const struct mouse_config *config = dev->config;
|
||||
return ZMK_EVENT_RAISE(zmk_mouse_move_state_changed_from_encoded(binding->param1, *config,
|
||||
false, event.timestamp));
|
||||
}
|
||||
|
||||
static const struct behavior_driver_api behavior_mouse_move_driver_api = {
|
||||
.binding_pressed = on_keymap_binding_pressed, .binding_released = on_keymap_binding_released};
|
||||
|
||||
#define KP_INST(n) \
|
||||
static struct mouse_config behavior_mouse_move_config_##n = { \
|
||||
.delay_ms = DT_INST_PROP(n, delay_ms), \
|
||||
.time_to_max_speed_ms = DT_INST_PROP(n, time_to_max_speed_ms), \
|
||||
.acceleration_exponent = DT_INST_PROP(n, acceleration_exponent), \
|
||||
}; \
|
||||
DEVICE_DT_INST_DEFINE(n, behavior_mouse_move_init, NULL, NULL, \
|
||||
&behavior_mouse_move_config_##n, APPLICATION, \
|
||||
CONFIG_KERNEL_INIT_PRIORITY_DEFAULT, &behavior_mouse_move_driver_api);
|
||||
|
||||
DT_INST_FOREACH_STATUS_OKAY(KP_INST)
|
||||
|
||||
#endif /* DT_HAS_COMPAT_STATUS_OKAY(DT_DRV_COMPAT) */
|
58
app/src/behaviors/behavior_mouse_scroll.c
Normal file
58
app/src/behaviors/behavior_mouse_scroll.c
Normal file
|
@ -0,0 +1,58 @@
|
|||
/*
|
||||
* Copyright (c) 2021 The ZMK Contributors
|
||||
*
|
||||
* SPDX-License-Identifier: MIT
|
||||
*/
|
||||
|
||||
#define DT_DRV_COMPAT zmk_behavior_mouse_scroll
|
||||
|
||||
#include <zephyr/device.h>
|
||||
#include <drivers/behavior.h>
|
||||
#include <zephyr/logging/log.h>
|
||||
|
||||
#include <zmk/event_manager.h>
|
||||
#include <zmk/events/mouse_scroll_state_changed.h>
|
||||
#include <zmk/behavior.h>
|
||||
#include <zmk/hid.h>
|
||||
#include <zmk/endpoints.h>
|
||||
|
||||
LOG_MODULE_DECLARE(zmk, CONFIG_ZMK_LOG_LEVEL);
|
||||
|
||||
#if DT_HAS_COMPAT_STATUS_OKAY(DT_DRV_COMPAT)
|
||||
|
||||
static int behavior_mouse_scroll_init(const struct device *dev) { return 0; };
|
||||
|
||||
static int on_keymap_binding_pressed(struct zmk_behavior_binding *binding,
|
||||
struct zmk_behavior_binding_event event) {
|
||||
LOG_DBG("position %d keycode 0x%02X", event.position, binding->param1);
|
||||
const struct device *dev = device_get_binding(binding->behavior_dev);
|
||||
const struct mouse_config *config = dev->config;
|
||||
return ZMK_EVENT_RAISE(zmk_mouse_scroll_state_changed_from_encoded(binding->param1, *config,
|
||||
true, event.timestamp));
|
||||
}
|
||||
|
||||
static int on_keymap_binding_released(struct zmk_behavior_binding *binding,
|
||||
struct zmk_behavior_binding_event event) {
|
||||
LOG_DBG("position %d keycode 0x%02X", event.position, binding->param1);
|
||||
const struct device *dev = device_get_binding(binding->behavior_dev);
|
||||
const struct mouse_config *config = dev->config;
|
||||
return ZMK_EVENT_RAISE(zmk_mouse_scroll_state_changed_from_encoded(binding->param1, *config,
|
||||
false, event.timestamp));
|
||||
}
|
||||
|
||||
static const struct behavior_driver_api behavior_mouse_scroll_driver_api = {
|
||||
.binding_pressed = on_keymap_binding_pressed, .binding_released = on_keymap_binding_released};
|
||||
|
||||
#define KP_INST(n) \
|
||||
static struct mouse_config behavior_mouse_scroll_config_##n = { \
|
||||
.delay_ms = DT_INST_PROP(n, delay_ms), \
|
||||
.time_to_max_speed_ms = DT_INST_PROP(n, time_to_max_speed_ms), \
|
||||
.acceleration_exponent = DT_INST_PROP(n, acceleration_exponent), \
|
||||
}; \
|
||||
DEVICE_DT_INST_DEFINE(n, behavior_mouse_scroll_init, NULL, NULL, \
|
||||
&behavior_mouse_scroll_config_##n, APPLICATION, \
|
||||
CONFIG_KERNEL_INIT_PRIORITY_DEFAULT, &behavior_mouse_scroll_driver_api);
|
||||
|
||||
DT_INST_FOREACH_STATUS_OKAY(KP_INST)
|
||||
|
||||
#endif /* DT_HAS_COMPAT_STATUS_OKAY(DT_DRV_COMPAT) */
|
301
app/src/behaviors/behavior_tri_state.c
Normal file
301
app/src/behaviors/behavior_tri_state.c
Normal file
|
@ -0,0 +1,301 @@
|
|||
/*
|
||||
* Copyright (c) 2022 The ZMK Contributors
|
||||
*
|
||||
* SPDX-License-Identifier: MIT
|
||||
*/
|
||||
|
||||
#define DT_DRV_COMPAT zmk_behavior_tri_state
|
||||
|
||||
#include <zephyr/device.h>
|
||||
#include <drivers/behavior.h>
|
||||
#include <zephyr/logging/log.h>
|
||||
#include <zmk/behavior.h>
|
||||
#include <zmk/behavior_queue.h>
|
||||
#include <zmk/keymap.h>
|
||||
#include <zmk/matrix.h>
|
||||
#include <zmk/event_manager.h>
|
||||
#include <zmk/events/position_state_changed.h>
|
||||
#include <zmk/events/layer_state_changed.h>
|
||||
#include <zmk/hid.h>
|
||||
|
||||
LOG_MODULE_DECLARE(zmk, CONFIG_ZMK_LOG_LEVEL);
|
||||
|
||||
#define ZMK_BHV_MAX_ACTIVE_TRI_STATES 10
|
||||
|
||||
struct behavior_tri_state_config {
|
||||
int32_t ignored_key_positions_len;
|
||||
int32_t ignored_layers_len;
|
||||
struct zmk_behavior_binding start_behavior;
|
||||
struct zmk_behavior_binding continue_behavior;
|
||||
struct zmk_behavior_binding end_behavior;
|
||||
int32_t ignored_layers;
|
||||
int32_t timeout_ms;
|
||||
int tap_ms;
|
||||
uint8_t ignored_key_positions[];
|
||||
};
|
||||
|
||||
struct active_tri_state {
|
||||
bool is_active;
|
||||
bool is_pressed;
|
||||
bool first_press;
|
||||
uint32_t position;
|
||||
const struct behavior_tri_state_config *config;
|
||||
struct k_work_delayable release_timer;
|
||||
int64_t release_at;
|
||||
bool timer_started;
|
||||
bool timer_cancelled;
|
||||
};
|
||||
|
||||
static int stop_timer(struct active_tri_state *tri_state) {
|
||||
int timer_cancel_result = k_work_cancel_delayable(&tri_state->release_timer);
|
||||
if (timer_cancel_result == -EINPROGRESS) {
|
||||
// too late to cancel, we'll let the timer handler clear up.
|
||||
tri_state->timer_cancelled = true;
|
||||
}
|
||||
return timer_cancel_result;
|
||||
}
|
||||
|
||||
static void reset_timer(int32_t timestamp, struct active_tri_state *tri_state) {
|
||||
tri_state->release_at = timestamp + tri_state->config->timeout_ms;
|
||||
int32_t ms_left = tri_state->release_at - k_uptime_get();
|
||||
if (ms_left > 0) {
|
||||
k_work_schedule(&tri_state->release_timer, K_MSEC(ms_left));
|
||||
LOG_DBG("Successfully reset tri-state timer");
|
||||
}
|
||||
}
|
||||
|
||||
void trigger_end_behavior(struct active_tri_state *si) {
|
||||
zmk_behavior_queue_add(si->position, si->config->end_behavior, true, si->config->tap_ms);
|
||||
zmk_behavior_queue_add(si->position, si->config->end_behavior, false, 0);
|
||||
}
|
||||
|
||||
void behavior_tri_state_timer_handler(struct k_work *item) {
|
||||
struct active_tri_state *tri_state = CONTAINER_OF(item, struct active_tri_state, release_timer);
|
||||
if (!tri_state->is_active || tri_state->timer_cancelled || tri_state->is_pressed) {
|
||||
return;
|
||||
}
|
||||
LOG_DBG("Tri-state deactivated due to timer");
|
||||
tri_state->is_active = false;
|
||||
trigger_end_behavior(tri_state);
|
||||
}
|
||||
|
||||
static void clear_tri_state(struct active_tri_state *tri_state) { tri_state->is_active = false; }
|
||||
|
||||
struct active_tri_state active_tri_states[ZMK_BHV_MAX_ACTIVE_TRI_STATES] = {};
|
||||
|
||||
static struct active_tri_state *find_tri_state(uint32_t position) {
|
||||
for (int i = 0; i < ZMK_BHV_MAX_ACTIVE_TRI_STATES; i++) {
|
||||
if (active_tri_states[i].position == position && active_tri_states[i].is_active) {
|
||||
return &active_tri_states[i];
|
||||
}
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static int new_tri_state(uint32_t position, const struct behavior_tri_state_config *config,
|
||||
struct active_tri_state **tri_state) {
|
||||
for (int i = 0; i < ZMK_BHV_MAX_ACTIVE_TRI_STATES; i++) {
|
||||
struct active_tri_state *const ref_tri_state = &active_tri_states[i];
|
||||
if (!ref_tri_state->is_active) {
|
||||
ref_tri_state->position = position;
|
||||
ref_tri_state->config = config;
|
||||
ref_tri_state->is_active = true;
|
||||
ref_tri_state->is_pressed = false;
|
||||
ref_tri_state->first_press = true;
|
||||
*tri_state = ref_tri_state;
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
static bool is_other_key_ignored(struct active_tri_state *tri_state, int32_t position) {
|
||||
for (int i = 0; i < tri_state->config->ignored_key_positions_len; i++) {
|
||||
if (tri_state->config->ignored_key_positions[i] == position) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
static bool is_layer_ignored(struct active_tri_state *tri_state, int32_t layer) {
|
||||
if ((BIT(layer) & tri_state->config->ignored_layers) != 0U) {
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
static int on_tri_state_binding_pressed(struct zmk_behavior_binding *binding,
|
||||
struct zmk_behavior_binding_event event) {
|
||||
const struct device *dev = device_get_binding(binding->behavior_dev);
|
||||
const struct behavior_tri_state_config *cfg = dev->config;
|
||||
struct active_tri_state *tri_state;
|
||||
tri_state = find_tri_state(event.position);
|
||||
if (tri_state == NULL) {
|
||||
if (new_tri_state(event.position, cfg, &tri_state) == -ENOMEM) {
|
||||
LOG_ERR("Unable to create new tri_state. Insufficient space in "
|
||||
"active_tri_states[].");
|
||||
return ZMK_BEHAVIOR_OPAQUE;
|
||||
}
|
||||
LOG_DBG("%d created new tri_state", event.position);
|
||||
}
|
||||
LOG_DBG("%d tri_state pressed", event.position);
|
||||
tri_state->is_pressed = true;
|
||||
if (tri_state->first_press) {
|
||||
behavior_keymap_binding_pressed((struct zmk_behavior_binding *)&cfg->start_behavior, event);
|
||||
behavior_keymap_binding_released((struct zmk_behavior_binding *)&cfg->start_behavior,
|
||||
event);
|
||||
tri_state->first_press = false;
|
||||
}
|
||||
behavior_keymap_binding_pressed((struct zmk_behavior_binding *)&cfg->continue_behavior, event);
|
||||
return ZMK_BEHAVIOR_OPAQUE;
|
||||
}
|
||||
|
||||
static void release_tri_state(struct zmk_behavior_binding_event event,
|
||||
struct zmk_behavior_binding *continue_behavior) {
|
||||
struct active_tri_state *tri_state = find_tri_state(event.position);
|
||||
if (tri_state == NULL) {
|
||||
return;
|
||||
}
|
||||
tri_state->is_pressed = false;
|
||||
behavior_keymap_binding_released(continue_behavior, event);
|
||||
reset_timer(k_uptime_get(), tri_state);
|
||||
}
|
||||
|
||||
static int on_tri_state_binding_released(struct zmk_behavior_binding *binding,
|
||||
struct zmk_behavior_binding_event event) {
|
||||
const struct device *dev = device_get_binding(binding->behavior_dev);
|
||||
const struct behavior_tri_state_config *cfg = dev->config;
|
||||
LOG_DBG("%d tri_state keybind released", event.position);
|
||||
release_tri_state(event, (struct zmk_behavior_binding *)&cfg->continue_behavior);
|
||||
return ZMK_BEHAVIOR_OPAQUE;
|
||||
}
|
||||
|
||||
static int behavior_tri_state_init(const struct device *dev) {
|
||||
static bool init_first_run = true;
|
||||
if (init_first_run) {
|
||||
for (int i = 0; i < ZMK_BHV_MAX_ACTIVE_TRI_STATES; i++) {
|
||||
k_work_init_delayable(&active_tri_states[i].release_timer,
|
||||
behavior_tri_state_timer_handler);
|
||||
clear_tri_state(&active_tri_states[i]);
|
||||
}
|
||||
}
|
||||
init_first_run = false;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct behavior_driver_api behavior_tri_state_driver_api = {
|
||||
.binding_pressed = on_tri_state_binding_pressed,
|
||||
.binding_released = on_tri_state_binding_released,
|
||||
};
|
||||
|
||||
static int tri_state_listener(const zmk_event_t *eh);
|
||||
static int tri_state_position_state_changed_listener(const zmk_event_t *eh);
|
||||
static int tri_state_layer_state_changed_listener(const zmk_event_t *eh);
|
||||
|
||||
ZMK_LISTENER(behavior_tri_state, tri_state_listener);
|
||||
ZMK_SUBSCRIPTION(behavior_tri_state, zmk_position_state_changed);
|
||||
ZMK_SUBSCRIPTION(behavior_tri_state, zmk_layer_state_changed);
|
||||
|
||||
static int tri_state_listener(const zmk_event_t *eh) {
|
||||
if (as_zmk_position_state_changed(eh) != NULL) {
|
||||
return tri_state_position_state_changed_listener(eh);
|
||||
} else if (as_zmk_layer_state_changed(eh) != NULL) {
|
||||
return tri_state_layer_state_changed_listener(eh);
|
||||
}
|
||||
return ZMK_EV_EVENT_BUBBLE;
|
||||
}
|
||||
|
||||
static int tri_state_position_state_changed_listener(const zmk_event_t *eh) {
|
||||
struct zmk_position_state_changed *ev = as_zmk_position_state_changed(eh);
|
||||
if (ev == NULL) {
|
||||
return ZMK_EV_EVENT_BUBBLE;
|
||||
}
|
||||
for (int i = 0; i < ZMK_BHV_MAX_ACTIVE_TRI_STATES; i++) {
|
||||
struct active_tri_state *tri_state = &active_tri_states[i];
|
||||
if (!tri_state->is_active) {
|
||||
continue;
|
||||
}
|
||||
if (tri_state->position == ev->position) {
|
||||
continue;
|
||||
}
|
||||
if (!is_other_key_ignored(tri_state, ev->position)) {
|
||||
LOG_DBG("Tri-State interrupted, ending at %d %d", tri_state->position, ev->position);
|
||||
tri_state->is_active = false;
|
||||
struct zmk_behavior_binding_event event = {.position = tri_state->position,
|
||||
.timestamp = k_uptime_get()};
|
||||
if (tri_state->is_pressed) {
|
||||
behavior_keymap_binding_released(
|
||||
(struct zmk_behavior_binding *)&tri_state->config->continue_behavior, event);
|
||||
}
|
||||
trigger_end_behavior(tri_state);
|
||||
return ZMK_EV_EVENT_BUBBLE;
|
||||
}
|
||||
if (ev->state) {
|
||||
stop_timer(tri_state);
|
||||
} else {
|
||||
reset_timer(ev->timestamp, tri_state);
|
||||
}
|
||||
}
|
||||
return ZMK_EV_EVENT_BUBBLE;
|
||||
}
|
||||
|
||||
static int tri_state_layer_state_changed_listener(const zmk_event_t *eh) {
|
||||
struct zmk_layer_state_changed *ev = as_zmk_layer_state_changed(eh);
|
||||
if (ev == NULL) {
|
||||
return ZMK_EV_EVENT_BUBBLE;
|
||||
}
|
||||
if (!ev->state) {
|
||||
return ZMK_EV_EVENT_BUBBLE;
|
||||
}
|
||||
for (int i = 0; i < ZMK_BHV_MAX_ACTIVE_TRI_STATES; i++) {
|
||||
struct active_tri_state *tri_state = &active_tri_states[i];
|
||||
if (!tri_state->is_active) {
|
||||
continue;
|
||||
}
|
||||
if (!is_layer_ignored(tri_state, ev->layer)) {
|
||||
LOG_DBG("Tri-State layer changed, ending at %d %d", tri_state->position, ev->layer);
|
||||
tri_state->is_active = false;
|
||||
struct zmk_behavior_binding_event event = {.position = tri_state->position,
|
||||
.timestamp = k_uptime_get()};
|
||||
if (tri_state->is_pressed) {
|
||||
behavior_keymap_binding_released(
|
||||
(struct zmk_behavior_binding *)&tri_state->config->continue_behavior, event);
|
||||
}
|
||||
behavior_keymap_binding_pressed(
|
||||
(struct zmk_behavior_binding *)&tri_state->config->end_behavior, event);
|
||||
behavior_keymap_binding_released(
|
||||
(struct zmk_behavior_binding *)&tri_state->config->end_behavior, event);
|
||||
return ZMK_EV_EVENT_BUBBLE;
|
||||
}
|
||||
}
|
||||
return ZMK_EV_EVENT_BUBBLE;
|
||||
}
|
||||
|
||||
#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 IF_BIT(n, prop, i) BIT(DT_PROP_BY_IDX(n, prop, i)) |
|
||||
|
||||
#define TRI_STATE_INST(n) \
|
||||
static struct behavior_tri_state_config behavior_tri_state_config_##n = { \
|
||||
.ignored_key_positions = DT_INST_PROP(n, ignored_key_positions), \
|
||||
.ignored_key_positions_len = DT_INST_PROP_LEN(n, ignored_key_positions), \
|
||||
.ignored_layers = DT_INST_FOREACH_PROP_ELEM(n, ignored_layers, IF_BIT) 0, \
|
||||
.ignored_layers_len = DT_INST_PROP_LEN(n, ignored_layers), \
|
||||
.timeout_ms = DT_INST_PROP(n, timeout_ms), \
|
||||
.tap_ms = DT_INST_PROP(n, tap_ms), \
|
||||
.start_behavior = _TRANSFORM_ENTRY(0, n), \
|
||||
.continue_behavior = _TRANSFORM_ENTRY(1, n), \
|
||||
.end_behavior = _TRANSFORM_ENTRY(2, n)}; \
|
||||
DEVICE_DT_INST_DEFINE(n, behavior_tri_state_init, NULL, NULL, &behavior_tri_state_config_##n, \
|
||||
APPLICATION, CONFIG_KERNEL_INIT_PRIORITY_DEFAULT, \
|
||||
&behavior_tri_state_driver_api);
|
||||
|
||||
DT_INST_FOREACH_STATUS_OKAY(TRI_STATE_INST)
|
|
@ -49,12 +49,12 @@ static const struct conditional_layer_cfg CONDITIONAL_LAYER_CFGS[] = {
|
|||
static const int32_t NUM_CONDITIONAL_LAYER_CFGS =
|
||||
sizeof(CONDITIONAL_LAYER_CFGS) / sizeof(*CONDITIONAL_LAYER_CFGS);
|
||||
|
||||
static void conditional_layer_activate(int8_t layer) {
|
||||
static void conditional_layer_activate(int8_t layer, bool momentary) {
|
||||
// This may trigger another event that could, in turn, activate additional then-layers. However,
|
||||
// the process will eventually terminate (at worst, when every layer is active).
|
||||
if (!zmk_keymap_layer_active(layer)) {
|
||||
LOG_DBG("layer %d", layer);
|
||||
zmk_keymap_layer_activate(layer);
|
||||
zmk_keymap_layer_activate(layer, momentary);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -84,6 +84,7 @@ static int layer_state_changed_listener(const zmk_event_t *ev) {
|
|||
int8_t max_then_layer = -1;
|
||||
uint32_t then_layers = 0;
|
||||
uint32_t then_layer_state = 0;
|
||||
uint32_t momentariness_state = 0;
|
||||
|
||||
conditional_layer_updates_needed = false;
|
||||
|
||||
|
@ -100,13 +101,17 @@ static int layer_state_changed_listener(const zmk_event_t *ev) {
|
|||
// also trigger activation of another.
|
||||
if ((zmk_keymap_layer_state() & mask) == mask) {
|
||||
then_layer_state |= BIT(cfg->then_layer);
|
||||
if (zmk_keymap_layers_any_momentary(mask)) {
|
||||
momentariness_state |= BIT(cfg->then_layer);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for (uint8_t layer = 0; layer <= max_then_layer; layer++) {
|
||||
if ((BIT(layer) & then_layers) != 0U) {
|
||||
if ((BIT(layer) & then_layer_state) != 0U) {
|
||||
conditional_layer_activate(layer);
|
||||
bool momentary = BIT(layer) & momentariness_state;
|
||||
conditional_layer_activate(layer, momentary);
|
||||
} else {
|
||||
conditional_layer_deactivate(layer);
|
||||
}
|
||||
|
|
|
@ -176,6 +176,17 @@ choice ZMK_LV_FONT_DEFAULT_SMALL
|
|||
select LV_FONT_UNSCII_16
|
||||
endchoice
|
||||
|
||||
config ZMK_DISPLAY_FULL_REFRESH_PERIOD
|
||||
int "(Optional) Period to issue a full refresh to the display (in seconds)"
|
||||
default 0
|
||||
help
|
||||
Period in seconds for how often to completely refresh/redraw the whole screen.
|
||||
Most useful for e-ink/EPD displays that require occasional full redraws.
|
||||
|
||||
config ZMK_DISPLAY_HIDE_MOMENTARY_LAYERS
|
||||
bool "Do not update layer widget for momentary layer changes"
|
||||
default n
|
||||
|
||||
rsource "widgets/Kconfig"
|
||||
|
||||
endif
|
||||
|
|
|
@ -50,6 +50,17 @@ struct k_work_q *zmk_display_work_q() {
|
|||
#endif
|
||||
}
|
||||
|
||||
#if CONFIG_ZMK_DISPLAY_FULL_REFRESH_PERIOD > 0
|
||||
void full_refresh_work_cb(struct k_work *work) { lv_obj_invalidate(lv_scr_act()); }
|
||||
|
||||
K_WORK_DEFINE(full_refresh_work, full_refresh_work_cb);
|
||||
|
||||
void full_refresh_timer_cb() { k_work_submit_to_queue(zmk_display_work_q(), &full_refresh_work); }
|
||||
|
||||
K_TIMER_DEFINE(full_refresh_timer, full_refresh_timer_cb, NULL);
|
||||
|
||||
#endif
|
||||
|
||||
void display_timer_cb() { k_work_submit_to_queue(zmk_display_work_q(), &display_tick_work); }
|
||||
|
||||
K_TIMER_DEFINE(display_timer, display_timer_cb, NULL);
|
||||
|
@ -57,6 +68,10 @@ K_TIMER_DEFINE(display_timer, display_timer_cb, NULL);
|
|||
void unblank_display_cb(struct k_work *work) {
|
||||
display_blanking_off(display);
|
||||
k_timer_start(&display_timer, K_MSEC(TICK_MS), K_MSEC(TICK_MS));
|
||||
#if CONFIG_ZMK_DISPLAY_FULL_REFRESH_PERIOD > 0
|
||||
k_timer_start(&full_refresh_timer, K_SECONDS(CONFIG_ZMK_DISPLAY_FULL_REFRESH_PERIOD),
|
||||
K_SECONDS(CONFIG_ZMK_DISPLAY_FULL_REFRESH_PERIOD));
|
||||
#endif
|
||||
}
|
||||
|
||||
#if IS_ENABLED(CONFIG_ZMK_DISPLAY_BLANK_ON_IDLE)
|
||||
|
@ -64,6 +79,9 @@ void unblank_display_cb(struct k_work *work) {
|
|||
void blank_display_cb(struct k_work *work) {
|
||||
k_timer_stop(&display_timer);
|
||||
display_blanking_on(display);
|
||||
#if CONFIG_ZMK_DISPLAY_FULL_REFRESH_PERIOD > 0
|
||||
k_timer_stop(&full_refresh_timer);
|
||||
#endif
|
||||
}
|
||||
K_WORK_DEFINE(blank_display_work, blank_display_cb);
|
||||
K_WORK_DEFINE(unblank_display_work, unblank_display_cb);
|
||||
|
|
|
@ -116,9 +116,7 @@ int zmk_endpoints_toggle_transport(void) {
|
|||
return zmk_endpoints_select_transport(new_transport);
|
||||
}
|
||||
|
||||
struct zmk_endpoint_instance zmk_endpoints_selected(void) {
|
||||
return current_instance;
|
||||
}
|
||||
struct zmk_endpoint_instance zmk_endpoints_selected(void) { return current_instance; }
|
||||
|
||||
static int send_keyboard_report(void) {
|
||||
struct zmk_hid_keyboard_report *keyboard_report = zmk_hid_get_keyboard_report();
|
||||
|
@ -193,6 +191,38 @@ int zmk_endpoints_send_report(uint16_t usage_page) {
|
|||
return -ENOTSUP;
|
||||
}
|
||||
|
||||
#if IS_ENABLED(CONFIG_ZMK_MOUSE)
|
||||
int zmk_endpoints_send_mouse_report() {
|
||||
struct zmk_hid_mouse_report *mouse_report = zmk_hid_get_mouse_report();
|
||||
|
||||
switch (current_instance.transport) {
|
||||
#if IS_ENABLED(CONFIG_ZMK_USB)
|
||||
case ZMK_TRANSPORT_USB: {
|
||||
int err = zmk_usb_hid_send_report((uint8_t *)mouse_report, sizeof(*mouse_report));
|
||||
if (err) {
|
||||
LOG_ERR("FAILED TO SEND OVER USB: %d", err);
|
||||
}
|
||||
return err;
|
||||
}
|
||||
#endif /* IS_ENABLED(CONFIG_ZMK_USB) */
|
||||
|
||||
#if IS_ENABLED(CONFIG_ZMK_BLE)
|
||||
case ZMK_TRANSPORT_BLE: {
|
||||
int err = zmk_hog_send_mouse_report(&mouse_report->body);
|
||||
if (err) {
|
||||
LOG_ERR("FAILED TO SEND OVER HOG: %d", err);
|
||||
}
|
||||
return err;
|
||||
}
|
||||
#endif /* IS_ENABLED(CONFIG_ZMK_BLE) */
|
||||
|
||||
default:
|
||||
LOG_ERR("Unsupported endpoint %d", current_instance.transport);
|
||||
return -ENOTSUP;
|
||||
}
|
||||
}
|
||||
#endif /* IS_ENABLED(CONFIG_ZMK_MOUSE) */
|
||||
|
||||
#if IS_ENABLED(CONFIG_SETTINGS)
|
||||
|
||||
static int endpoints_handle_set(const char *name, size_t len, settings_read_cb read_cb,
|
||||
|
@ -297,6 +327,9 @@ static int zmk_endpoints_init(const struct device *_arg) {
|
|||
static void disconnect_current_endpoint() {
|
||||
zmk_hid_keyboard_clear();
|
||||
zmk_hid_consumer_clear();
|
||||
#if IS_ENABLED(CONFIG_ZMK_MOUSE)
|
||||
zmk_hid_mouse_clear();
|
||||
#endif
|
||||
|
||||
zmk_endpoints_send_report(HID_USAGE_KEY);
|
||||
zmk_endpoints_send_report(HID_USAGE_CONSUMER);
|
||||
|
|
10
app/src/events/mouse_button_state_changed.c
Normal file
10
app/src/events/mouse_button_state_changed.c
Normal file
|
@ -0,0 +1,10 @@
|
|||
/*
|
||||
* Copyright (c) 2020 The ZMK Contributors
|
||||
*
|
||||
* SPDX-License-Identifier: MIT
|
||||
*/
|
||||
|
||||
#include <zephyr/kernel.h>
|
||||
#include <zmk/events/mouse_button_state_changed.h>
|
||||
|
||||
ZMK_EVENT_IMPL(zmk_mouse_button_state_changed);
|
10
app/src/events/mouse_move_state_changed.c
Normal file
10
app/src/events/mouse_move_state_changed.c
Normal file
|
@ -0,0 +1,10 @@
|
|||
/*
|
||||
* Copyright (c) 2020 The ZMK Contributors
|
||||
*
|
||||
* SPDX-License-Identifier: MIT
|
||||
*/
|
||||
|
||||
#include <zephyr/kernel.h>
|
||||
#include <zmk/events/mouse_move_state_changed.h>
|
||||
|
||||
ZMK_EVENT_IMPL(zmk_mouse_move_state_changed);
|
10
app/src/events/mouse_scroll_state_changed.c
Normal file
10
app/src/events/mouse_scroll_state_changed.c
Normal file
|
@ -0,0 +1,10 @@
|
|||
/*
|
||||
* Copyright (c) 2020 The ZMK Contributors
|
||||
*
|
||||
* SPDX-License-Identifier: MIT
|
||||
*/
|
||||
|
||||
#include <zephyr/kernel.h>
|
||||
#include <zmk/events/mouse_scroll_state_changed.h>
|
||||
|
||||
ZMK_EVENT_IMPL(zmk_mouse_scroll_state_changed);
|
10
app/src/events/mouse_tick.c
Normal file
10
app/src/events/mouse_tick.c
Normal file
|
@ -0,0 +1,10 @@
|
|||
/*
|
||||
* Copyright (c) 2020 The ZMK Contributors
|
||||
*
|
||||
* SPDX-License-Identifier: MIT
|
||||
*/
|
||||
|
||||
#include <zephyr/kernel.h>
|
||||
#include <zmk/events/mouse_tick.h>
|
||||
|
||||
ZMK_EVENT_IMPL(zmk_mouse_tick);
|
|
@ -16,6 +16,11 @@ static struct zmk_hid_keyboard_report keyboard_report = {
|
|||
|
||||
static struct zmk_hid_consumer_report consumer_report = {.report_id = 2, .body = {.keys = {0}}};
|
||||
|
||||
#if IS_ENABLED(CONFIG_ZMK_MOUSE)
|
||||
static struct zmk_hid_mouse_report mouse_report = {
|
||||
.report_id = 4, .body = {.buttons = 0, .x = 0, .y = 0, .scroll_x = 0, .scroll_y = 0}};
|
||||
#endif
|
||||
|
||||
// Keep track of how often a modifier was pressed.
|
||||
// Only release the modifier if the count is 0.
|
||||
static int explicit_modifier_counts[8] = {0, 0, 0, 0, 0, 0, 0, 0};
|
||||
|
@ -264,6 +269,87 @@ bool zmk_hid_is_pressed(uint32_t usage) {
|
|||
return false;
|
||||
}
|
||||
|
||||
#if IS_ENABLED(CONFIG_ZMK_MOUSE)
|
||||
// Keep track of how often a button was pressed.
|
||||
// Only release the button if the count is 0.
|
||||
static int explicit_button_counts[16] = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0};
|
||||
static zmk_mod_flags_t explicit_buttons = 0;
|
||||
|
||||
#define SET_MOUSE_BUTTONS(btns) \
|
||||
{ \
|
||||
mouse_report.body.buttons = btns; \
|
||||
LOG_DBG("Mouse buttons set to 0x%02X", mouse_report.body.buttons); \
|
||||
}
|
||||
|
||||
int zmk_hid_mouse_button_press(zmk_mouse_button_t button) {
|
||||
explicit_button_counts[button]++;
|
||||
LOG_DBG("Button %d count %d", button, explicit_button_counts[button]);
|
||||
WRITE_BIT(explicit_buttons, button, true);
|
||||
SET_MOUSE_BUTTONS(explicit_buttons);
|
||||
return 0;
|
||||
}
|
||||
|
||||
int zmk_hid_mouse_button_release(zmk_mouse_button_t button) {
|
||||
if (explicit_button_counts[button] <= 0) {
|
||||
LOG_ERR("Tried to release button %d too often", button);
|
||||
return -EINVAL;
|
||||
}
|
||||
explicit_button_counts[button]--;
|
||||
LOG_DBG("Button %d count: %d", button, explicit_button_counts[button]);
|
||||
if (explicit_button_counts[button] == 0) {
|
||||
LOG_DBG("Button %d released", button);
|
||||
WRITE_BIT(explicit_buttons, button, false);
|
||||
}
|
||||
SET_MOUSE_BUTTONS(explicit_buttons);
|
||||
return 0;
|
||||
}
|
||||
|
||||
int zmk_hid_mouse_buttons_press(zmk_mouse_button_flags_t buttons) {
|
||||
for (zmk_mod_t i = 0; i < 16; i++) {
|
||||
if (buttons & (1 << i)) {
|
||||
zmk_hid_mouse_button_press(i);
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
int zmk_hid_mouse_buttons_release(zmk_mouse_button_flags_t buttons) {
|
||||
for (zmk_mod_t i = 0; i < 16; i++) {
|
||||
if (buttons & (1 << i)) {
|
||||
zmk_hid_mouse_button_release(i);
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
void zmk_hid_mouse_movement_set(int16_t x, int16_t y) {
|
||||
mouse_report.body.x = x;
|
||||
mouse_report.body.y = y;
|
||||
LOG_DBG("Mouse movement set to 0x%02X 0x%02X ", mouse_report.body.x, mouse_report.body.y);
|
||||
}
|
||||
|
||||
void zmk_hid_mouse_movement_update(int16_t x, int16_t y) {
|
||||
mouse_report.body.x += x;
|
||||
mouse_report.body.y += y;
|
||||
LOG_DBG("Mouse movement updated to 0x%02X 0x%02X ", mouse_report.body.x, mouse_report.body.y);
|
||||
}
|
||||
|
||||
void zmk_hid_mouse_scroll_set(int8_t x, int8_t y) {
|
||||
mouse_report.body.scroll_x = x;
|
||||
mouse_report.body.scroll_y = y;
|
||||
LOG_DBG("Mouse scroll set to 0x%02X 0x%02X ", mouse_report.body.scroll_x,
|
||||
mouse_report.body.scroll_y);
|
||||
}
|
||||
|
||||
void zmk_hid_mouse_scroll_update(int8_t x, int8_t y) {
|
||||
mouse_report.body.scroll_x += x;
|
||||
mouse_report.body.scroll_y += y;
|
||||
LOG_DBG("Mouse scroll updated to 0x%02X 0x%02X ", mouse_report.body.scroll_x,
|
||||
mouse_report.body.scroll_y);
|
||||
}
|
||||
void zmk_hid_mouse_clear() { memset(&mouse_report.body, 0, sizeof(mouse_report.body)); }
|
||||
#endif /* IS_ENABLED(CONFIG_ZMK_MOUSE) */
|
||||
|
||||
struct zmk_hid_keyboard_report *zmk_hid_get_keyboard_report() {
|
||||
return &keyboard_report;
|
||||
}
|
||||
|
@ -271,3 +357,9 @@ struct zmk_hid_keyboard_report *zmk_hid_get_keyboard_report() {
|
|||
struct zmk_hid_consumer_report *zmk_hid_get_consumer_report() {
|
||||
return &consumer_report;
|
||||
}
|
||||
|
||||
#if IS_ENABLED(CONFIG_ZMK_MOUSE)
|
||||
struct zmk_hid_mouse_report *zmk_hid_get_mouse_report() {
|
||||
return &mouse_report;
|
||||
}
|
||||
#endif
|
||||
|
|
|
@ -56,6 +56,13 @@ static struct hids_report consumer_input = {
|
|||
.type = HIDS_INPUT,
|
||||
};
|
||||
|
||||
#if IS_ENABLED(CONFIG_ZMK_MOUSE)
|
||||
static struct hids_report mouse_input = {
|
||||
.id = 0x04,
|
||||
.type = HIDS_INPUT,
|
||||
};
|
||||
#endif
|
||||
|
||||
static bool host_requests_notification = false;
|
||||
static uint8_t ctrl_point;
|
||||
// static uint8_t proto_mode;
|
||||
|
@ -93,6 +100,15 @@ static ssize_t read_hids_consumer_input_report(struct bt_conn *conn,
|
|||
sizeof(struct zmk_hid_consumer_report_body));
|
||||
}
|
||||
|
||||
#if IS_ENABLED(CONFIG_ZMK_MOUSE)
|
||||
static ssize_t read_hids_mouse_input_report(struct bt_conn *conn, const struct bt_gatt_attr *attr,
|
||||
void *buf, uint16_t len, uint16_t offset) {
|
||||
struct zmk_hid_mouse_report_body *report_body = &zmk_hid_get_mouse_report()->body;
|
||||
return bt_gatt_attr_read(conn, attr, buf, len, offset, report_body,
|
||||
sizeof(struct zmk_hid_mouse_report_body));
|
||||
}
|
||||
#endif
|
||||
|
||||
// static ssize_t write_proto_mode(struct bt_conn *conn,
|
||||
// const struct bt_gatt_attr *attr,
|
||||
// const void *buf, uint16_t len, uint16_t offset,
|
||||
|
@ -139,6 +155,15 @@ BT_GATT_SERVICE_DEFINE(
|
|||
BT_GATT_CCC(input_ccc_changed, BT_GATT_PERM_READ_ENCRYPT | BT_GATT_PERM_WRITE_ENCRYPT),
|
||||
BT_GATT_DESCRIPTOR(BT_UUID_HIDS_REPORT_REF, BT_GATT_PERM_READ_ENCRYPT, read_hids_report_ref,
|
||||
NULL, &consumer_input),
|
||||
|
||||
#if IS_ENABLED(CONFIG_ZMK_MOUSE)
|
||||
BT_GATT_CHARACTERISTIC(BT_UUID_HIDS_REPORT, BT_GATT_CHRC_READ | BT_GATT_CHRC_NOTIFY,
|
||||
BT_GATT_PERM_READ_ENCRYPT, read_hids_mouse_input_report, NULL, NULL),
|
||||
BT_GATT_CCC(input_ccc_changed, BT_GATT_PERM_READ_ENCRYPT | BT_GATT_PERM_WRITE_ENCRYPT),
|
||||
BT_GATT_DESCRIPTOR(BT_UUID_HIDS_REPORT_REF, BT_GATT_PERM_READ_ENCRYPT, read_hids_report_ref,
|
||||
NULL, &mouse_input),
|
||||
#endif
|
||||
|
||||
BT_GATT_CHARACTERISTIC(BT_UUID_HIDS_CTRL_POINT, BT_GATT_CHRC_WRITE_WITHOUT_RESP,
|
||||
BT_GATT_PERM_WRITE, NULL, write_ctrl_point, &ctrl_point));
|
||||
|
||||
|
@ -261,6 +286,78 @@ int zmk_hog_send_consumer_report(struct zmk_hid_consumer_report_body *report) {
|
|||
return 0;
|
||||
};
|
||||
|
||||
#if IS_ENABLED(CONFIG_ZMK_MOUSE)
|
||||
K_MSGQ_DEFINE(zmk_hog_mouse_msgq, sizeof(struct zmk_hid_mouse_report_body),
|
||||
CONFIG_ZMK_BLE_MOUSE_REPORT_QUEUE_SIZE, 4);
|
||||
|
||||
void send_mouse_report_callback(struct k_work *work) {
|
||||
struct zmk_hid_mouse_report_body report;
|
||||
while (k_msgq_get(&zmk_hog_mouse_msgq, &report, K_NO_WAIT) == 0) {
|
||||
struct bt_conn *conn = destination_connection();
|
||||
if (conn == NULL) {
|
||||
return;
|
||||
}
|
||||
|
||||
struct bt_gatt_notify_params notify_params = {
|
||||
.attr = &hog_svc.attrs[13],
|
||||
.data = &report,
|
||||
.len = sizeof(report),
|
||||
};
|
||||
|
||||
int err = bt_gatt_notify_cb(conn, ¬ify_params);
|
||||
if (err) {
|
||||
LOG_DBG("Error notifying %d", err);
|
||||
}
|
||||
|
||||
bt_conn_unref(conn);
|
||||
}
|
||||
};
|
||||
|
||||
K_WORK_DEFINE(hog_mouse_work, send_mouse_report_callback);
|
||||
|
||||
int zmk_hog_send_mouse_report(struct zmk_hid_mouse_report_body *report) {
|
||||
int err = k_msgq_put(&zmk_hog_mouse_msgq, report, K_NO_WAIT);
|
||||
if (err) {
|
||||
switch (err) {
|
||||
case -EAGAIN: {
|
||||
LOG_WRN("Mouse message queue full, dropping report");
|
||||
return err;
|
||||
}
|
||||
default:
|
||||
LOG_WRN("Failed to queue mouse report to send (%d)", err);
|
||||
return err;
|
||||
}
|
||||
}
|
||||
|
||||
k_work_submit_to_queue(&hog_work_q, &hog_mouse_work);
|
||||
|
||||
return 0;
|
||||
};
|
||||
|
||||
int zmk_hog_send_mouse_report_direct(struct zmk_hid_mouse_report_body *report) {
|
||||
struct bt_conn *conn = destination_connection();
|
||||
if (conn == NULL) {
|
||||
return 1;
|
||||
}
|
||||
|
||||
struct bt_gatt_notify_params notify_params = {
|
||||
.attr = &hog_svc.attrs[13],
|
||||
.data = report,
|
||||
.len = sizeof(*report),
|
||||
};
|
||||
|
||||
int err = bt_gatt_notify_cb(conn, ¬ify_params);
|
||||
if (err) {
|
||||
LOG_DBG("Error notifying %d", err);
|
||||
return err;
|
||||
}
|
||||
|
||||
bt_conn_unref(conn);
|
||||
|
||||
return 0;
|
||||
};
|
||||
#endif /* IS_ENABLED(CONFIG_ZMK_MOUSE) */
|
||||
|
||||
int zmk_hog_init(const struct device *_arg) {
|
||||
static const struct k_work_queue_config queue_config = {.name = "HID Over GATT Send Work"};
|
||||
k_work_queue_start(&hog_work_q, hog_q_stack, K_THREAD_STACK_SIZEOF(hog_q_stack),
|
||||
|
|
|
@ -27,6 +27,7 @@ LOG_MODULE_DECLARE(zmk, CONFIG_ZMK_LOG_LEVEL);
|
|||
#include <zmk/events/sensor_event.h>
|
||||
|
||||
static zmk_keymap_layers_state_t _zmk_keymap_layer_state = 0;
|
||||
static zmk_keymap_layers_state_t _zmk_keymap_layer_momentary = 0;
|
||||
static uint8_t _zmk_keymap_layer_default = 0;
|
||||
|
||||
#define DT_DRV_COMPAT zmk_keymap
|
||||
|
@ -78,7 +79,7 @@ static struct zmk_behavior_binding zmk_sensor_keymap[ZMK_KEYMAP_LAYERS_LEN]
|
|||
|
||||
#endif /* ZMK_KEYMAP_HAS_SENSORS */
|
||||
|
||||
static inline int set_layer_state(uint8_t layer, bool state) {
|
||||
static inline int set_layer_state(uint8_t layer, bool state, bool momentary) {
|
||||
if (layer >= ZMK_KEYMAP_LAYERS_LEN) {
|
||||
return -EINVAL;
|
||||
}
|
||||
|
@ -93,6 +94,7 @@ static inline int set_layer_state(uint8_t layer, bool state) {
|
|||
// Don't send state changes unless there was an actual change
|
||||
if (old_state != _zmk_keymap_layer_state) {
|
||||
LOG_DBG("layer_changed: layer %d state %d", layer, state);
|
||||
WRITE_BIT(_zmk_keymap_layer_momentary, layer, momentary);
|
||||
ZMK_EVENT_RAISE(create_layer_state_changed(layer, state));
|
||||
}
|
||||
|
||||
|
@ -113,6 +115,15 @@ bool zmk_keymap_layer_active(uint8_t layer) {
|
|||
return zmk_keymap_layer_active_with_state(layer, _zmk_keymap_layer_state);
|
||||
};
|
||||
|
||||
bool zmk_keymap_layer_momentary(uint8_t layer) {
|
||||
return layer != _zmk_keymap_layer_default &&
|
||||
(_zmk_keymap_layer_momentary & (BIT(layer))) == (BIT(layer));
|
||||
};
|
||||
|
||||
bool zmk_keymap_layers_any_momentary(zmk_keymap_layers_state_t layers_mask) {
|
||||
return (_zmk_keymap_layer_momentary & layers_mask) > 0;
|
||||
};
|
||||
|
||||
uint8_t zmk_keymap_highest_layer_active() {
|
||||
for (uint8_t layer = ZMK_KEYMAP_LAYERS_LEN - 1; layer > 0; layer--) {
|
||||
if (zmk_keymap_layer_active(layer)) {
|
||||
|
@ -122,16 +133,18 @@ uint8_t zmk_keymap_highest_layer_active() {
|
|||
return zmk_keymap_layer_default();
|
||||
}
|
||||
|
||||
int zmk_keymap_layer_activate(uint8_t layer) { return set_layer_state(layer, true); };
|
||||
int zmk_keymap_layer_activate(uint8_t layer, bool momentary) {
|
||||
return set_layer_state(layer, true, momentary);
|
||||
};
|
||||
|
||||
int zmk_keymap_layer_deactivate(uint8_t layer) { return set_layer_state(layer, false); };
|
||||
int zmk_keymap_layer_deactivate(uint8_t layer) { return set_layer_state(layer, false, false); };
|
||||
|
||||
int zmk_keymap_layer_toggle(uint8_t layer) {
|
||||
if (zmk_keymap_layer_active(layer)) {
|
||||
return zmk_keymap_layer_deactivate(layer);
|
||||
}
|
||||
|
||||
return zmk_keymap_layer_activate(layer);
|
||||
return zmk_keymap_layer_activate(layer, false);
|
||||
};
|
||||
|
||||
int zmk_keymap_layer_to(uint8_t layer) {
|
||||
|
@ -139,7 +152,7 @@ int zmk_keymap_layer_to(uint8_t layer) {
|
|||
zmk_keymap_layer_deactivate(i);
|
||||
}
|
||||
|
||||
zmk_keymap_layer_activate(layer);
|
||||
zmk_keymap_layer_activate(layer, false);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
|
|
@ -17,6 +17,10 @@ LOG_MODULE_REGISTER(zmk, CONFIG_ZMK_LOG_LEVEL);
|
|||
#include <zmk/display.h>
|
||||
#include <drivers/ext_power.h>
|
||||
|
||||
#ifdef CONFIG_ZMK_MOUSE
|
||||
#include <zmk/mouse.h>
|
||||
#endif /* CONFIG_ZMK_MOUSE */
|
||||
|
||||
void main(void) {
|
||||
LOG_INF("Welcome to ZMK!\n");
|
||||
|
||||
|
@ -27,4 +31,8 @@ void main(void) {
|
|||
#ifdef CONFIG_ZMK_DISPLAY
|
||||
zmk_display_init();
|
||||
#endif /* CONFIG_ZMK_DISPLAY */
|
||||
|
||||
#ifdef CONFIG_ZMK_MOUSE
|
||||
zmk_mouse_init();
|
||||
#endif /* CONFIG_ZMK_MOUSE */
|
||||
}
|
||||
|
|
38
app/src/mouse/Kconfig
Normal file
38
app/src/mouse/Kconfig
Normal file
|
@ -0,0 +1,38 @@
|
|||
# Copyright (c) 2021 The ZMK Contributors
|
||||
# SPDX-License-Identifier: MIT
|
||||
|
||||
menuconfig ZMK_MOUSE
|
||||
bool "Enable ZMK mouse emulation"
|
||||
default n
|
||||
|
||||
config ZMK_MOUSE_TICK_DURATION
|
||||
int "Mouse tick duration in ms"
|
||||
default 8
|
||||
|
||||
if ZMK_MOUSE
|
||||
|
||||
choice ZMK_MOUSE_WORK_QUEUE
|
||||
prompt "Work queue selection for mouse events"
|
||||
default ZMK_MOUSE_WORK_QUEUE_DEDICATED
|
||||
|
||||
config ZMK_MOUSE_WORK_QUEUE_SYSTEM
|
||||
bool "Use default system work queue for mouse events"
|
||||
|
||||
config ZMK_MOUSE_WORK_QUEUE_DEDICATED
|
||||
bool "Use dedicated work queue for mouse events"
|
||||
|
||||
endchoice
|
||||
|
||||
if ZMK_MOUSE_WORK_QUEUE_DEDICATED
|
||||
|
||||
config ZMK_MOUSE_DEDICATED_THREAD_STACK_SIZE
|
||||
int "Stack size for dedicated mouse thread/queue"
|
||||
default 2048
|
||||
|
||||
config ZMK_MOUSE_DEDICATED_THREAD_PRIORITY
|
||||
int "Thread priority for dedicated mouse thread/queue"
|
||||
default 3
|
||||
|
||||
endif # ZMK_MOUSE_WORK_QUEUE_DEDICATED
|
||||
|
||||
endif
|
163
app/src/mouse/key_listener.c
Normal file
163
app/src/mouse/key_listener.c
Normal file
|
@ -0,0 +1,163 @@
|
|||
/*
|
||||
* Copyright (c) 2021 The ZMK Contributors
|
||||
*
|
||||
* SPDX-License-Identifier: MIT
|
||||
*/
|
||||
|
||||
#include <drivers/behavior.h>
|
||||
#include <zephyr/logging/log.h>
|
||||
|
||||
LOG_MODULE_DECLARE(zmk, CONFIG_ZMK_LOG_LEVEL);
|
||||
|
||||
#include <zmk/event_manager.h>
|
||||
#include <zmk/events/mouse_button_state_changed.h>
|
||||
#include <zmk/events/mouse_move_state_changed.h>
|
||||
#include <zmk/events/mouse_scroll_state_changed.h>
|
||||
#include <zmk/events/mouse_tick.h>
|
||||
#include <zmk/hid.h>
|
||||
#include <zmk/endpoints.h>
|
||||
#include <zmk/mouse.h>
|
||||
|
||||
#if !defined(CONFIG_ZMK_SPLIT) || defined(CONFIG_ZMK_SPLIT_ROLE_CENTRAL)
|
||||
static struct vector2d move_speed = {0};
|
||||
static struct vector2d scroll_speed = {0};
|
||||
static struct mouse_config move_config = (struct mouse_config){0};
|
||||
static struct mouse_config scroll_config = (struct mouse_config){0};
|
||||
static int64_t start_time = 0;
|
||||
|
||||
bool equals(const struct mouse_config *one, const struct mouse_config *other) {
|
||||
return one->delay_ms == other->delay_ms &&
|
||||
one->time_to_max_speed_ms == other->time_to_max_speed_ms &&
|
||||
one->acceleration_exponent == other->acceleration_exponent;
|
||||
}
|
||||
|
||||
static void clear_mouse_state(struct k_work *work) {
|
||||
move_speed = (struct vector2d){0};
|
||||
scroll_speed = (struct vector2d){0};
|
||||
start_time = 0;
|
||||
zmk_hid_mouse_movement_set(0, 0);
|
||||
zmk_hid_mouse_scroll_set(0, 0);
|
||||
LOG_DBG("Clearing state");
|
||||
}
|
||||
|
||||
K_WORK_DEFINE(mouse_clear, &clear_mouse_state);
|
||||
|
||||
void mouse_clear_cb(struct k_timer *dummy) {
|
||||
k_work_submit_to_queue(zmk_mouse_work_q(), &mouse_clear);
|
||||
}
|
||||
|
||||
static void mouse_tick_timer_handler(struct k_work *work) {
|
||||
zmk_hid_mouse_movement_set(0, 0);
|
||||
zmk_hid_mouse_scroll_set(0, 0);
|
||||
LOG_DBG("Raising mouse tick event");
|
||||
ZMK_EVENT_RAISE(
|
||||
zmk_mouse_tick(move_speed, scroll_speed, move_config, scroll_config, &start_time));
|
||||
zmk_endpoints_send_mouse_report();
|
||||
}
|
||||
|
||||
K_WORK_DEFINE(mouse_tick, &mouse_tick_timer_handler);
|
||||
|
||||
void mouse_timer_cb(struct k_timer *dummy) {
|
||||
LOG_DBG("Submitting mouse work to queue");
|
||||
k_work_submit_to_queue(zmk_mouse_work_q(), &mouse_tick);
|
||||
}
|
||||
|
||||
K_TIMER_DEFINE(mouse_timer, mouse_timer_cb, mouse_clear_cb);
|
||||
|
||||
static int mouse_timer_ref_count = 0;
|
||||
|
||||
void mouse_timer_ref() {
|
||||
if (mouse_timer_ref_count == 0) {
|
||||
start_time = k_uptime_get();
|
||||
k_timer_start(&mouse_timer, K_NO_WAIT, K_MSEC(CONFIG_ZMK_MOUSE_TICK_DURATION));
|
||||
}
|
||||
mouse_timer_ref_count += 1;
|
||||
}
|
||||
|
||||
void mouse_timer_unref() {
|
||||
if (mouse_timer_ref_count > 0) {
|
||||
mouse_timer_ref_count--;
|
||||
}
|
||||
if (mouse_timer_ref_count == 0) {
|
||||
k_timer_stop(&mouse_timer);
|
||||
}
|
||||
}
|
||||
|
||||
static void listener_mouse_move_pressed(const struct zmk_mouse_move_state_changed *ev) {
|
||||
move_speed.x += ev->max_speed.x;
|
||||
move_speed.y += ev->max_speed.y;
|
||||
mouse_timer_ref();
|
||||
}
|
||||
|
||||
static void listener_mouse_move_released(const struct zmk_mouse_move_state_changed *ev) {
|
||||
move_speed.x -= ev->max_speed.x;
|
||||
move_speed.y -= ev->max_speed.y;
|
||||
mouse_timer_unref();
|
||||
}
|
||||
|
||||
static void listener_mouse_scroll_pressed(const struct zmk_mouse_scroll_state_changed *ev) {
|
||||
scroll_speed.x += ev->max_speed.x;
|
||||
scroll_speed.y += ev->max_speed.y;
|
||||
mouse_timer_ref();
|
||||
}
|
||||
|
||||
static void listener_mouse_scroll_released(const struct zmk_mouse_scroll_state_changed *ev) {
|
||||
scroll_speed.x -= ev->max_speed.x;
|
||||
scroll_speed.y -= ev->max_speed.y;
|
||||
mouse_timer_unref();
|
||||
}
|
||||
|
||||
static void listener_mouse_button_pressed(const struct zmk_mouse_button_state_changed *ev) {
|
||||
LOG_DBG("buttons: 0x%02X", ev->buttons);
|
||||
zmk_hid_mouse_buttons_press(ev->buttons);
|
||||
zmk_endpoints_send_mouse_report();
|
||||
}
|
||||
|
||||
static void listener_mouse_button_released(const struct zmk_mouse_button_state_changed *ev) {
|
||||
LOG_DBG("buttons: 0x%02X", ev->buttons);
|
||||
zmk_hid_mouse_buttons_release(ev->buttons);
|
||||
zmk_endpoints_send_mouse_report();
|
||||
}
|
||||
|
||||
int mouse_listener(const zmk_event_t *eh) {
|
||||
const struct zmk_mouse_move_state_changed *mmv_ev = as_zmk_mouse_move_state_changed(eh);
|
||||
if (mmv_ev) {
|
||||
if (!equals(&move_config, &(mmv_ev->config)))
|
||||
move_config = mmv_ev->config;
|
||||
|
||||
if (mmv_ev->state) {
|
||||
listener_mouse_move_pressed(mmv_ev);
|
||||
} else {
|
||||
listener_mouse_move_released(mmv_ev);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
const struct zmk_mouse_scroll_state_changed *msc_ev = as_zmk_mouse_scroll_state_changed(eh);
|
||||
if (msc_ev) {
|
||||
if (!equals(&scroll_config, &(msc_ev->config)))
|
||||
scroll_config = msc_ev->config;
|
||||
if (msc_ev->state) {
|
||||
listener_mouse_scroll_pressed(msc_ev);
|
||||
} else {
|
||||
listener_mouse_scroll_released(msc_ev);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
const struct zmk_mouse_button_state_changed *mbt_ev = as_zmk_mouse_button_state_changed(eh);
|
||||
if (mbt_ev) {
|
||||
if (mbt_ev->state) {
|
||||
listener_mouse_button_pressed(mbt_ev);
|
||||
} else {
|
||||
listener_mouse_button_released(mbt_ev);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
ZMK_LISTENER(mouse_listener, mouse_listener);
|
||||
ZMK_SUBSCRIPTION(mouse_listener, zmk_mouse_button_state_changed);
|
||||
ZMK_SUBSCRIPTION(mouse_listener, zmk_mouse_move_state_changed);
|
||||
ZMK_SUBSCRIPTION(mouse_listener, zmk_mouse_scroll_state_changed);
|
||||
|
||||
#endif /*!defined(CONFIG_ZMK_SPLIT) || defined(CONFIG_ZMK_SPLIT_ROLE_CENTRAL) */
|
30
app/src/mouse/main.c
Normal file
30
app/src/mouse/main.c
Normal file
|
@ -0,0 +1,30 @@
|
|||
/*
|
||||
* Copyright (c) 2020 The ZMK Contributors
|
||||
*
|
||||
* SPDX-License-Identifier: MIT
|
||||
*/
|
||||
|
||||
#include <zephyr/kernel.h>
|
||||
#include <zmk/mouse.h>
|
||||
|
||||
#if IS_ENABLED(CONFIG_ZMK_MOUSE_WORK_QUEUE_DEDICATED)
|
||||
K_THREAD_STACK_DEFINE(mouse_work_stack_area, CONFIG_ZMK_MOUSE_DEDICATED_THREAD_STACK_SIZE);
|
||||
static struct k_work_q mouse_work_q;
|
||||
#endif
|
||||
|
||||
struct k_work_q *zmk_mouse_work_q() {
|
||||
#if IS_ENABLED(CONFIG_ZMK_MOUSE_WORK_QUEUE_DEDICATED)
|
||||
return &mouse_work_q;
|
||||
#else
|
||||
return &k_sys_work_q;
|
||||
#endif
|
||||
}
|
||||
|
||||
int zmk_mouse_init() {
|
||||
#if IS_ENABLED(CONFIG_ZMK_MOUSE_WORK_QUEUE_DEDICATED)
|
||||
k_work_queue_start(&mouse_work_q, mouse_work_stack_area,
|
||||
K_THREAD_STACK_SIZEOF(mouse_work_stack_area),
|
||||
CONFIG_ZMK_MOUSE_DEDICATED_THREAD_PRIORITY, NULL);
|
||||
#endif
|
||||
return 0;
|
||||
}
|
106
app/src/mouse/tick_listener.c
Normal file
106
app/src/mouse/tick_listener.c
Normal file
|
@ -0,0 +1,106 @@
|
|||
/*
|
||||
* Copyright (c) 2020 The ZMK Contributors
|
||||
*
|
||||
* SPDX-License-Identifier: MIT
|
||||
*/
|
||||
|
||||
#include <zephyr/logging/log.h>
|
||||
|
||||
LOG_MODULE_DECLARE(zmk, CONFIG_ZMK_LOG_LEVEL);
|
||||
|
||||
#include <zmk/event_manager.h>
|
||||
#include <zmk/events/mouse_tick.h>
|
||||
#include <zmk/endpoints.h>
|
||||
#include <zmk/hid.h>
|
||||
#include <zmk/mouse.h>
|
||||
|
||||
#include <zephyr/sys/util.h> // CLAMP
|
||||
|
||||
#if !defined(CONFIG_ZMK_SPLIT) || defined(CONFIG_ZMK_SPLIT_ROLE_CENTRAL)
|
||||
#if CONFIG_MINIMAL_LIBC
|
||||
static float powf(float base, float exponent) {
|
||||
// poor man's power implementation rounds the exponent down to the nearest integer.
|
||||
float power = 1.0f;
|
||||
for (; exponent >= 1.0f; exponent--) {
|
||||
power = power * base;
|
||||
}
|
||||
return power;
|
||||
}
|
||||
#else
|
||||
#include <math.h>
|
||||
#endif
|
||||
|
||||
struct vector2d move_remainder = {0};
|
||||
struct vector2d scroll_remainder = {0};
|
||||
|
||||
static int64_t ms_since_start(int64_t start, int64_t now, int64_t delay) {
|
||||
int64_t move_duration = now - (start + delay);
|
||||
// start can be in the future if there's a delay
|
||||
if (move_duration < 0) {
|
||||
move_duration = 0;
|
||||
}
|
||||
return move_duration;
|
||||
}
|
||||
|
||||
static float speed(const struct mouse_config *config, float max_speed, int64_t duration_ms) {
|
||||
// Calculate the speed based on MouseKeysAccel
|
||||
// See https://en.wikipedia.org/wiki/Mouse_keys
|
||||
if (duration_ms > config->time_to_max_speed_ms || config->time_to_max_speed_ms == 0 ||
|
||||
config->acceleration_exponent == 0) {
|
||||
return max_speed;
|
||||
}
|
||||
float time_fraction = (float)duration_ms / config->time_to_max_speed_ms;
|
||||
return max_speed * powf(time_fraction, config->acceleration_exponent);
|
||||
}
|
||||
|
||||
static void track_remainder(float *move, float *remainder) {
|
||||
float new_move = *move + *remainder;
|
||||
*remainder = new_move - (int)new_move;
|
||||
*move = (int)new_move;
|
||||
}
|
||||
|
||||
static struct vector2d update_movement(struct vector2d *remainder,
|
||||
const struct mouse_config *config, struct vector2d max_speed,
|
||||
int64_t now, int64_t *start_time) {
|
||||
struct vector2d move = {0};
|
||||
if (max_speed.x == 0 && max_speed.y == 0) {
|
||||
*remainder = (struct vector2d){0};
|
||||
return move;
|
||||
}
|
||||
|
||||
int64_t move_duration = ms_since_start(*start_time, now, config->delay_ms);
|
||||
move = (struct vector2d){
|
||||
.x = speed(config, max_speed.x, move_duration) * CONFIG_ZMK_MOUSE_TICK_DURATION / 1000,
|
||||
.y = speed(config, max_speed.y, move_duration) * CONFIG_ZMK_MOUSE_TICK_DURATION / 1000,
|
||||
};
|
||||
|
||||
track_remainder(&(move.x), &(remainder->x));
|
||||
track_remainder(&(move.y), &(remainder->y));
|
||||
|
||||
return move;
|
||||
}
|
||||
|
||||
static void mouse_tick_handler(const struct zmk_mouse_tick *tick) {
|
||||
struct vector2d move = update_movement(&move_remainder, &(tick->move_config), tick->max_move,
|
||||
tick->timestamp, tick->start_time);
|
||||
zmk_hid_mouse_movement_update((int16_t)CLAMP(move.x, INT16_MIN, INT16_MAX),
|
||||
(int16_t)CLAMP(move.y, INT16_MIN, INT16_MAX));
|
||||
struct vector2d scroll = update_movement(&scroll_remainder, &(tick->scroll_config),
|
||||
tick->max_scroll, tick->timestamp, tick->start_time);
|
||||
zmk_hid_mouse_scroll_update((int8_t)CLAMP(scroll.x, INT8_MIN, INT8_MAX),
|
||||
(int8_t)CLAMP(scroll.y, INT8_MIN, INT8_MAX));
|
||||
}
|
||||
|
||||
int zmk_mouse_tick_listener(const zmk_event_t *eh) {
|
||||
const struct zmk_mouse_tick *tick = as_zmk_mouse_tick(eh);
|
||||
if (tick) {
|
||||
mouse_tick_handler(tick);
|
||||
return 0;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
ZMK_LISTENER(zmk_mouse_tick_listener, zmk_mouse_tick_listener);
|
||||
ZMK_SUBSCRIPTION(zmk_mouse_tick_listener, zmk_mouse_tick);
|
||||
|
||||
#endif /* !defined(CONFIG_ZMK_SPLIT) || defined(CONFIG_ZMK_SPLIT_ROLE_CENTRAL) */
|
3
app/tests/caps-word/deactivate-by-mod/events.patterns
Normal file
3
app/tests/caps-word/deactivate-by-mod/events.patterns
Normal file
|
@ -0,0 +1,3 @@
|
|||
s/.*hid_listener_keycode_//p
|
||||
s/.*hid_implicit_modifiers_//p
|
||||
s/.*caps_word_enhance_usage/enhance_usage/p
|
|
@ -0,0 +1,13 @@
|
|||
enhance_usage: Enhancing usage 0x04 with modifiers: 0x02
|
||||
pressed: usage_page 0x07 keycode 0x04 implicit_mods 0x02 explicit_mods 0x00
|
||||
press: Modifiers set to 0x02
|
||||
released: usage_page 0x07 keycode 0x04 implicit_mods 0x00 explicit_mods 0x00
|
||||
release: Modifiers set to 0x00
|
||||
pressed: usage_page 0x07 keycode 0xE1 implicit_mods 0x00 explicit_mods 0x00
|
||||
press: Modifiers set to 0x02
|
||||
released: usage_page 0x07 keycode 0xE1 implicit_mods 0x00 explicit_mods 0x00
|
||||
release: Modifiers set to 0x00
|
||||
pressed: usage_page 0x07 keycode 0x04 implicit_mods 0x00 explicit_mods 0x00
|
||||
press: Modifiers set to 0x00
|
||||
released: usage_page 0x07 keycode 0x04 implicit_mods 0x00 explicit_mods 0x00
|
||||
release: Modifiers set to 0x00
|
35
app/tests/caps-word/deactivate-by-mod/native_posix_64.keymap
Normal file
35
app/tests/caps-word/deactivate-by-mod/native_posix_64.keymap
Normal file
|
@ -0,0 +1,35 @@
|
|||
#include <dt-bindings/zmk/keys.h>
|
||||
#include <behaviors.dtsi>
|
||||
#include <dt-bindings/zmk/kscan_mock.h>
|
||||
|
||||
&caps_word {
|
||||
/delete-property/ ignore-modifiers;
|
||||
};
|
||||
|
||||
/ {
|
||||
keymap {
|
||||
compatible = "zmk,keymap";
|
||||
label = "Default keymap";
|
||||
|
||||
default_layer {
|
||||
bindings = <
|
||||
&caps_word &kp A
|
||||
&kp LSHFT &none
|
||||
>;
|
||||
};
|
||||
};
|
||||
};
|
||||
|
||||
|
||||
&kscan {
|
||||
events = <
|
||||
ZMK_MOCK_PRESS(0,0,10)
|
||||
ZMK_MOCK_RELEASE(0,0,10)
|
||||
ZMK_MOCK_PRESS(0,1,10)
|
||||
ZMK_MOCK_RELEASE(0,1,10)
|
||||
ZMK_MOCK_PRESS(1,0,10)
|
||||
ZMK_MOCK_RELEASE(1,0,10)
|
||||
ZMK_MOCK_PRESS(0,1,10)
|
||||
ZMK_MOCK_RELEASE(0,1,10)
|
||||
>;
|
||||
};
|
1
app/tests/mouse-keys/mmv/events.patterns
Normal file
1
app/tests/mouse-keys/mmv/events.patterns
Normal file
|
@ -0,0 +1 @@
|
|||
s/.*hid_listener_keycode_//p
|
2
app/tests/mouse-keys/mmv/keycode_events.snapshot
Normal file
2
app/tests/mouse-keys/mmv/keycode_events.snapshot
Normal file
|
@ -0,0 +1,2 @@
|
|||
pressed: usage_page 0x07 keycode 0x05 implicit_mods 0x00 explicit_mods 0x00
|
||||
released: usage_page 0x07 keycode 0x05 implicit_mods 0x00 explicit_mods 0x00
|
26
app/tests/mouse-keys/mmv/native_posix.keymap
Normal file
26
app/tests/mouse-keys/mmv/native_posix.keymap
Normal file
|
@ -0,0 +1,26 @@
|
|||
#include <behaviors.dtsi>
|
||||
#include <dt-bindings/zmk/keys.h>
|
||||
#include <dt-bindings/zmk/kscan_mock.h>
|
||||
#include <dt-bindings/zmk/mouse.h>
|
||||
|
||||
/ {
|
||||
keymap {
|
||||
compatible = "zmk,keymap";
|
||||
label ="Default keymap";
|
||||
|
||||
default_layer {
|
||||
bindings = <
|
||||
&mmv MOVE_LEFT &none
|
||||
&none &none
|
||||
>;
|
||||
};
|
||||
};
|
||||
};
|
||||
|
||||
|
||||
&kscan {
|
||||
events = <
|
||||
ZMK_MOCK_PRESS(0,0,100)
|
||||
ZMK_MOCK_RELEASE(0,0,10)
|
||||
>;
|
||||
};
|
40
app/tests/tri-state/behavior_keymap.dtsi
Normal file
40
app/tests/tri-state/behavior_keymap.dtsi
Normal file
|
@ -0,0 +1,40 @@
|
|||
#include <dt-bindings/zmk/keys.h>
|
||||
#include <behaviors.dtsi>
|
||||
#include <dt-bindings/zmk/kscan_mock.h>
|
||||
|
||||
/ {
|
||||
behaviors {
|
||||
swap: swap {
|
||||
compatible = "zmk,behavior-tri-state";
|
||||
label = "SWAPPER";
|
||||
#binding-cells = <0>;
|
||||
bindings = <&kt LALT>, <&kp TAB>, <&kt LALT>;
|
||||
ignored-key-positions = <2 3>;
|
||||
ignored-layers = <1>;
|
||||
timeout-ms = <200>;
|
||||
};
|
||||
};
|
||||
|
||||
keymap {
|
||||
compatible = "zmk,keymap";
|
||||
label ="Default keymap";
|
||||
|
||||
default_layer {
|
||||
bindings = <
|
||||
&swap &kp A
|
||||
&kp B &tog 1>;
|
||||
};
|
||||
|
||||
extra_layer {
|
||||
bindings = <
|
||||
&kp A &kp B
|
||||
&tog 2 &trans>;
|
||||
};
|
||||
|
||||
extra_layer2 {
|
||||
bindings = <
|
||||
&kp N1 &kp N2
|
||||
&trans &kp N3>;
|
||||
};
|
||||
};
|
||||
};
|
|
@ -0,0 +1,2 @@
|
|||
s/.*hid_listener_keycode/kp/p
|
||||
s/.*on_tri_state_binding/tri_state_binding/p
|
|
@ -0,0 +1,19 @@
|
|||
tri_state_binding_pressed: 0 created new tri_state
|
||||
tri_state_binding_pressed: 0 tri_state pressed
|
||||
kp_pressed: usage_page 0x07 keycode 0xE2 implicit_mods 0x00 explicit_mods 0x00
|
||||
kp_pressed: usage_page 0x07 keycode 0x2B implicit_mods 0x00 explicit_mods 0x00
|
||||
tri_state_binding_released: 0 tri_state keybind released
|
||||
kp_released: usage_page 0x07 keycode 0x2B implicit_mods 0x00 explicit_mods 0x00
|
||||
tri_state_binding_pressed: 0 tri_state pressed
|
||||
kp_pressed: usage_page 0x07 keycode 0x2B implicit_mods 0x00 explicit_mods 0x00
|
||||
tri_state_binding_released: 0 tri_state keybind released
|
||||
kp_released: usage_page 0x07 keycode 0x2B implicit_mods 0x00 explicit_mods 0x00
|
||||
tri_state_binding_pressed: 0 tri_state pressed
|
||||
kp_pressed: usage_page 0x07 keycode 0x2B implicit_mods 0x00 explicit_mods 0x00
|
||||
tri_state_binding_released: 0 tri_state keybind released
|
||||
kp_released: usage_page 0x07 keycode 0x2B implicit_mods 0x00 explicit_mods 0x00
|
||||
kp_pressed: usage_page 0x07 keycode 0x05 implicit_mods 0x00 explicit_mods 0x00
|
||||
kp_released: usage_page 0x07 keycode 0x05 implicit_mods 0x00 explicit_mods 0x00
|
||||
kp_released: usage_page 0x07 keycode 0xE2 implicit_mods 0x00 explicit_mods 0x00
|
||||
kp_pressed: usage_page 0x07 keycode 0x04 implicit_mods 0x00 explicit_mods 0x00
|
||||
kp_released: usage_page 0x07 keycode 0x04 implicit_mods 0x00 explicit_mods 0x00
|
|
@ -0,0 +1,19 @@
|
|||
#include <dt-bindings/zmk/keys.h>
|
||||
#include <behaviors.dtsi>
|
||||
#include <dt-bindings/zmk/kscan_mock.h>
|
||||
#include "../behavior_keymap.dtsi"
|
||||
|
||||
&kscan {
|
||||
events = <
|
||||
ZMK_MOCK_PRESS(0,0,10)
|
||||
ZMK_MOCK_RELEASE(0,0,10)
|
||||
ZMK_MOCK_PRESS(0,0,10)
|
||||
ZMK_MOCK_RELEASE(0,0,10)
|
||||
ZMK_MOCK_PRESS(0,0,10)
|
||||
ZMK_MOCK_RELEASE(0,0,10)
|
||||
ZMK_MOCK_PRESS(1,0,10)
|
||||
ZMK_MOCK_RELEASE(1,0,10)
|
||||
ZMK_MOCK_PRESS(0,1,10)
|
||||
ZMK_MOCK_RELEASE(0,1,1000)
|
||||
>;
|
||||
};
|
|
@ -0,0 +1,2 @@
|
|||
s/.*hid_listener_keycode/kp/p
|
||||
s/.*on_tri_state_binding/tri_state_binding/p
|
|
@ -0,0 +1,17 @@
|
|||
tri_state_binding_pressed: 0 created new tri_state
|
||||
tri_state_binding_pressed: 0 tri_state pressed
|
||||
kp_pressed: usage_page 0x07 keycode 0xE2 implicit_mods 0x00 explicit_mods 0x00
|
||||
kp_pressed: usage_page 0x07 keycode 0x2B implicit_mods 0x00 explicit_mods 0x00
|
||||
tri_state_binding_released: 0 tri_state keybind released
|
||||
kp_released: usage_page 0x07 keycode 0x2B implicit_mods 0x00 explicit_mods 0x00
|
||||
tri_state_binding_pressed: 0 tri_state pressed
|
||||
kp_pressed: usage_page 0x07 keycode 0x2B implicit_mods 0x00 explicit_mods 0x00
|
||||
tri_state_binding_released: 0 tri_state keybind released
|
||||
kp_released: usage_page 0x07 keycode 0x2B implicit_mods 0x00 explicit_mods 0x00
|
||||
tri_state_binding_pressed: 0 tri_state pressed
|
||||
kp_pressed: usage_page 0x07 keycode 0x2B implicit_mods 0x00 explicit_mods 0x00
|
||||
tri_state_binding_released: 0 tri_state keybind released
|
||||
kp_released: usage_page 0x07 keycode 0x2B implicit_mods 0x00 explicit_mods 0x00
|
||||
kp_pressed: usage_page 0x07 keycode 0x04 implicit_mods 0x00 explicit_mods 0x00
|
||||
kp_released: usage_page 0x07 keycode 0x04 implicit_mods 0x00 explicit_mods 0x00
|
||||
kp_released: usage_page 0x07 keycode 0xE2 implicit_mods 0x00 explicit_mods 0x00
|
|
@ -0,0 +1,21 @@
|
|||
#include <dt-bindings/zmk/keys.h>
|
||||
#include <behaviors.dtsi>
|
||||
#include <dt-bindings/zmk/kscan_mock.h>
|
||||
#include "../behavior_keymap.dtsi"
|
||||
|
||||
&kscan {
|
||||
events = <
|
||||
ZMK_MOCK_PRESS(0,0,10)
|
||||
ZMK_MOCK_RELEASE(0,0,10)
|
||||
ZMK_MOCK_PRESS(0,0,10)
|
||||
ZMK_MOCK_RELEASE(0,0,10)
|
||||
ZMK_MOCK_PRESS(0,0,10)
|
||||
ZMK_MOCK_RELEASE(0,0,10)
|
||||
ZMK_MOCK_PRESS(1,1,10)
|
||||
ZMK_MOCK_RELEASE(1,1,10)
|
||||
ZMK_MOCK_PRESS(0,0,10)
|
||||
ZMK_MOCK_RELEASE(0,0,10)
|
||||
ZMK_MOCK_PRESS(1,0,10)
|
||||
ZMK_MOCK_RELEASE(1,0,10)
|
||||
>;
|
||||
};
|
2
app/tests/tri-state/swapper-int/events.patterns
Normal file
2
app/tests/tri-state/swapper-int/events.patterns
Normal file
|
@ -0,0 +1,2 @@
|
|||
s/.*hid_listener_keycode/kp/p
|
||||
s/.*on_tri_state_binding/tri_state_binding/p
|
17
app/tests/tri-state/swapper-int/keycode_events.snapshot
Normal file
17
app/tests/tri-state/swapper-int/keycode_events.snapshot
Normal file
|
@ -0,0 +1,17 @@
|
|||
tri_state_binding_pressed: 0 created new tri_state
|
||||
tri_state_binding_pressed: 0 tri_state pressed
|
||||
kp_pressed: usage_page 0x07 keycode 0xE2 implicit_mods 0x00 explicit_mods 0x00
|
||||
kp_pressed: usage_page 0x07 keycode 0x2B implicit_mods 0x00 explicit_mods 0x00
|
||||
tri_state_binding_released: 0 tri_state keybind released
|
||||
kp_released: usage_page 0x07 keycode 0x2B implicit_mods 0x00 explicit_mods 0x00
|
||||
tri_state_binding_pressed: 0 tri_state pressed
|
||||
kp_pressed: usage_page 0x07 keycode 0x2B implicit_mods 0x00 explicit_mods 0x00
|
||||
tri_state_binding_released: 0 tri_state keybind released
|
||||
kp_released: usage_page 0x07 keycode 0x2B implicit_mods 0x00 explicit_mods 0x00
|
||||
tri_state_binding_pressed: 0 tri_state pressed
|
||||
kp_pressed: usage_page 0x07 keycode 0x2B implicit_mods 0x00 explicit_mods 0x00
|
||||
tri_state_binding_released: 0 tri_state keybind released
|
||||
kp_released: usage_page 0x07 keycode 0x2B implicit_mods 0x00 explicit_mods 0x00
|
||||
kp_released: usage_page 0x07 keycode 0xE2 implicit_mods 0x00 explicit_mods 0x00
|
||||
kp_pressed: usage_page 0x07 keycode 0x04 implicit_mods 0x00 explicit_mods 0x00
|
||||
kp_released: usage_page 0x07 keycode 0x04 implicit_mods 0x00 explicit_mods 0x00
|
17
app/tests/tri-state/swapper-int/native_posix_64.keymap
Normal file
17
app/tests/tri-state/swapper-int/native_posix_64.keymap
Normal file
|
@ -0,0 +1,17 @@
|
|||
#include <dt-bindings/zmk/keys.h>
|
||||
#include <behaviors.dtsi>
|
||||
#include <dt-bindings/zmk/kscan_mock.h>
|
||||
#include "../behavior_keymap.dtsi"
|
||||
|
||||
&kscan {
|
||||
events = <
|
||||
ZMK_MOCK_PRESS(0,0,10)
|
||||
ZMK_MOCK_RELEASE(0,0,10)
|
||||
ZMK_MOCK_PRESS(0,0,10)
|
||||
ZMK_MOCK_RELEASE(0,0,10)
|
||||
ZMK_MOCK_PRESS(0,0,10)
|
||||
ZMK_MOCK_RELEASE(0,0,10)
|
||||
ZMK_MOCK_PRESS(0,1,10)
|
||||
ZMK_MOCK_RELEASE(0,1,1000)
|
||||
>;
|
||||
};
|
2
app/tests/tri-state/swapper/events.patterns
Normal file
2
app/tests/tri-state/swapper/events.patterns
Normal file
|
@ -0,0 +1,2 @@
|
|||
s/.*hid_listener_keycode/kp/p
|
||||
s/.*on_tri_state_binding/tri_state_binding/p
|
14
app/tests/tri-state/swapper/keycode_events.snapshot
Normal file
14
app/tests/tri-state/swapper/keycode_events.snapshot
Normal file
|
@ -0,0 +1,14 @@
|
|||
tri_state_binding_pressed: 0 created new tri_state
|
||||
tri_state_binding_pressed: 0 tri_state pressed
|
||||
kp_pressed: usage_page 0x07 keycode 0xE2 implicit_mods 0x00 explicit_mods 0x00
|
||||
kp_pressed: usage_page 0x07 keycode 0x2B implicit_mods 0x00 explicit_mods 0x00
|
||||
tri_state_binding_released: 0 tri_state keybind released
|
||||
kp_released: usage_page 0x07 keycode 0x2B implicit_mods 0x00 explicit_mods 0x00
|
||||
tri_state_binding_pressed: 0 tri_state pressed
|
||||
kp_pressed: usage_page 0x07 keycode 0x2B implicit_mods 0x00 explicit_mods 0x00
|
||||
tri_state_binding_released: 0 tri_state keybind released
|
||||
kp_released: usage_page 0x07 keycode 0x2B implicit_mods 0x00 explicit_mods 0x00
|
||||
tri_state_binding_pressed: 0 tri_state pressed
|
||||
kp_pressed: usage_page 0x07 keycode 0x2B implicit_mods 0x00 explicit_mods 0x00
|
||||
tri_state_binding_released: 0 tri_state keybind released
|
||||
kp_released: usage_page 0x07 keycode 0x2B implicit_mods 0x00 explicit_mods 0x00
|
15
app/tests/tri-state/swapper/native_posix_64.keymap
Normal file
15
app/tests/tri-state/swapper/native_posix_64.keymap
Normal file
|
@ -0,0 +1,15 @@
|
|||
#include <dt-bindings/zmk/keys.h>
|
||||
#include <behaviors.dtsi>
|
||||
#include <dt-bindings/zmk/kscan_mock.h>
|
||||
#include "../behavior_keymap.dtsi"
|
||||
|
||||
&kscan {
|
||||
events = <
|
||||
ZMK_MOCK_PRESS(0,0,10)
|
||||
ZMK_MOCK_RELEASE(0,0,10)
|
||||
ZMK_MOCK_PRESS(0,0,10)
|
||||
ZMK_MOCK_RELEASE(0,0,10)
|
||||
ZMK_MOCK_PRESS(0,0,10)
|
||||
ZMK_MOCK_RELEASE(0,0,10)
|
||||
>;
|
||||
};
|
2
app/tests/tri-state/timeout/events.patterns
Normal file
2
app/tests/tri-state/timeout/events.patterns
Normal file
|
@ -0,0 +1,2 @@
|
|||
s/.*hid_listener_keycode/kp/p
|
||||
s/.*on_tri_state_binding/tri_state_binding/p
|
7
app/tests/tri-state/timeout/keycode_events.snapshot
Normal file
7
app/tests/tri-state/timeout/keycode_events.snapshot
Normal file
|
@ -0,0 +1,7 @@
|
|||
tri_state_binding_pressed: 0 created new tri_state
|
||||
tri_state_binding_pressed: 0 tri_state pressed
|
||||
kp_pressed: usage_page 0x07 keycode 0xE2 implicit_mods 0x00 explicit_mods 0x00
|
||||
kp_pressed: usage_page 0x07 keycode 0x2B implicit_mods 0x00 explicit_mods 0x00
|
||||
tri_state_binding_released: 0 tri_state keybind released
|
||||
kp_released: usage_page 0x07 keycode 0x2B implicit_mods 0x00 explicit_mods 0x00
|
||||
kp_released: usage_page 0x07 keycode 0xE2 implicit_mods 0x00 explicit_mods 0x00
|
11
app/tests/tri-state/timeout/native_posix_64.keymap
Normal file
11
app/tests/tri-state/timeout/native_posix_64.keymap
Normal file
|
@ -0,0 +1,11 @@
|
|||
#include <dt-bindings/zmk/keys.h>
|
||||
#include <behaviors.dtsi>
|
||||
#include <dt-bindings/zmk/kscan_mock.h>
|
||||
#include "../behavior_keymap.dtsi"
|
||||
|
||||
&kscan {
|
||||
events = <
|
||||
ZMK_MOCK_PRESS(0,0,10)
|
||||
ZMK_MOCK_RELEASE(0,0,1000)
|
||||
>;
|
||||
};
|
|
@ -37,6 +37,18 @@ By default, the caps word will remain active when any alphanumeric character or
|
|||
};
|
||||
```
|
||||
|
||||
#### Continue on modifiers
|
||||
|
||||
By default, the caps word will remain active when any modifiers are pressed. If you
|
||||
would like to deactivate caps word when modifiers are pressed, you can delete the
|
||||
`ignored-modifiers` property in your keymap:
|
||||
|
||||
```
|
||||
&caps_word {
|
||||
/delete-property/ ignore-modifiers;
|
||||
};
|
||||
```
|
||||
|
||||
#### Applied Modifier(s)
|
||||
|
||||
In addition, if you would like _multiple_ modifiers, instead of just `MOD_LSFT`, you can override the `mods` property:
|
||||
|
|
110
docs/docs/behaviors/mouse-emulation.md
Normal file
110
docs/docs/behaviors/mouse-emulation.md
Normal file
|
@ -0,0 +1,110 @@
|
|||
---
|
||||
title: Mouse Emulation Behaviors
|
||||
sidebar_label: Mouse Emulation
|
||||
---
|
||||
|
||||
## Summary
|
||||
|
||||
Mouse emulation behaviors send mouse movements, button presses or scroll actions.
|
||||
|
||||
Please view [`dt-bindings/zmk/mouse.h`](https://github.com/zmkfirmware/zmk/blob/main/app/include/dt-bindings/zmk/mouse.h) for a comprehensive list of signals.
|
||||
|
||||
## Configuration options
|
||||
|
||||
This feature should be enabled via a config option:
|
||||
|
||||
```
|
||||
CONFIG_ZMK_MOUSE=y
|
||||
```
|
||||
|
||||
This option enables several others.
|
||||
|
||||
### Dedicated thread processing
|
||||
|
||||
`CONFIG_ZMK_MOUSE_WORK_QUEUE_DEDICATED` is enabled by default and separates the processing of mouse signals into a dedicated thread, significantly improving performance.
|
||||
|
||||
### Tick rate configuration
|
||||
|
||||
`CONFIG_ZMK_MOUSE_TICK_DURATION` sets the tick rate for mouse polling. It is set to 8 ms. by default.
|
||||
|
||||
## Keycode Defines
|
||||
|
||||
To make it easier to encode the HID keycode numeric values, most keymaps include
|
||||
the [`dt-bindings/zmk/mouse.h`](https://github.com/zmkfirmware/zmk/blob/main/app/include/dt-bindings/zmk/mouse.h) header
|
||||
provided by ZMK near the top:
|
||||
|
||||
```
|
||||
#include <dt-bindings/zmk/mouse.h>
|
||||
```
|
||||
|
||||
Doing so allows using a set of defines such as `MOVE_UP`, `MOVE_DOWN`, `LCLK` and `SCROLL_UP` with these behaviors.
|
||||
|
||||
## Mouse Button Press
|
||||
|
||||
This behavior can press/release up to 16 mouse buttons.
|
||||
|
||||
### Behavior Binding
|
||||
|
||||
- Reference: `&mkp`
|
||||
- Parameter: A `uint16` with each bit referring to a button.
|
||||
|
||||
Example:
|
||||
|
||||
```
|
||||
&mkp LCLK
|
||||
```
|
||||
|
||||
## Mouse Movement
|
||||
|
||||
This behavior is used to manipulate the cursor.
|
||||
|
||||
### Behavior Binding
|
||||
|
||||
- Reference: `&mmv`
|
||||
- Parameter: A `uint32` with the first 16 bits relating to horizontal movement
|
||||
and the last 16 - to vertical movement.
|
||||
|
||||
Example:
|
||||
|
||||
```
|
||||
&mmv MOVE_UP
|
||||
```
|
||||
|
||||
## Mouse Scrolling
|
||||
|
||||
This behaviour is used to scroll, both horizontally and vertically.
|
||||
|
||||
### Behavior Binding
|
||||
|
||||
- Reference: `&mwh`
|
||||
- Parameter: A `uint16` with the first 8 bits relating to horizontal movement
|
||||
and the last 8 - to vertical movement.
|
||||
|
||||
Example:
|
||||
|
||||
```
|
||||
&mwh SCROLL_UP
|
||||
```
|
||||
|
||||
## Acceleration
|
||||
|
||||
Both mouse movement and scrolling have independently configurable acceleration profiles with three parameters: delay before movement, time to max speed and the acceleration exponent.
|
||||
The exponent is usually set to 0 for constant speed, 1 for uniform acceleration or 2 for uniform jerk.
|
||||
|
||||
These profiles can be configured inside your keymap:
|
||||
|
||||
```
|
||||
&mmv {
|
||||
time-to-max-speed-ms = <500>;
|
||||
};
|
||||
|
||||
&mwh {
|
||||
acceleration-exponent=<1>;
|
||||
};
|
||||
|
||||
/ {
|
||||
keymap {
|
||||
...
|
||||
};
|
||||
};
|
||||
```
|
130
docs/docs/behaviors/tri-state.md
Normal file
130
docs/docs/behaviors/tri-state.md
Normal file
|
@ -0,0 +1,130 @@
|
|||
---
|
||||
title: Tri-State Behavior
|
||||
sidebar_label: Tri-State
|
||||
---
|
||||
|
||||
## Summary
|
||||
|
||||
Tri-States are a way to have something persist while other behaviors occur.
|
||||
|
||||
The tri-state key will fire the 'start' behavior when the key is pressed for the first time. Subsequent presses of the same key will output the second, 'continue' behavior, and any key position or layer state change that is not specified (see below) will trigger the 'interrupt behavior'.
|
||||
|
||||
### Basic Usage
|
||||
|
||||
The following is a basic definition of a tri-state:
|
||||
|
||||
```
|
||||
/ {
|
||||
behaviors {
|
||||
tri-state: tri-state {
|
||||
compatible = "zmk,behavior-tri-state";
|
||||
label = "TRI-STATE";
|
||||
#binding-cells = <0>;
|
||||
bindings = <&kp A>, <&kp B>, <&kt C>;
|
||||
};
|
||||
};
|
||||
|
||||
keymap {
|
||||
compatible = "zmk,keymap";
|
||||
label ="Default keymap";
|
||||
|
||||
default_layer {
|
||||
bindings = <
|
||||
&tri-state &kp D
|
||||
&kp E &kp F>;
|
||||
};
|
||||
};
|
||||
};
|
||||
```
|
||||
|
||||
Pressing `tri-state` will fire the first behavior, and output `A`, as well as the second behavior, outputting `B`. Subsequent presses of `tri-state` will output `B`. When another key is pressed or a layer change occurs, the third, 'interrupt' behavior will fire.
|
||||
|
||||
### Advanced Configuration
|
||||
|
||||
#### `timeout-ms`
|
||||
|
||||
Setting `timeout-ms` will cause the deactivation behavior to fire when the time has elapsed after releasing the Tri-State or a ignored key.
|
||||
|
||||
#### `ignored-key-positions`
|
||||
|
||||
- Including `ignored-key-positions` in your tri-state definition will let the key positions specified NOT trigger the interrupt behavior when a tri-state is active.
|
||||
- Pressing any key **NOT** listed in `ignored-key-positions` will cause the interrupt behavior to fire.
|
||||
- Note that `ignored-key-positions` is an array of key position indexes. Key positions are numbered according to your keymap, starting with 0. So if the first key in your keymap is Q, this key is in position 0. The next key (probably W) will be in position 1, et cetera.
|
||||
- See the following example, which is an implementation of the popular [Swapper](https://github.com/callum-oakley/qmk_firmware/tree/master/users/callum) from Callum Oakley:
|
||||
|
||||
```
|
||||
/ {
|
||||
behaviors {
|
||||
swap: swapper {
|
||||
compatible = "zmk,behavior-tri-state";
|
||||
label = "SWAPPER";
|
||||
#binding-cells = <0>;
|
||||
bindings = <&kt LALT>, <&kp TAB>, <&kt LALT>;
|
||||
ignored-key-positions = <1>;
|
||||
};
|
||||
};
|
||||
|
||||
keymap {
|
||||
compatible = "zmk,keymap";
|
||||
label ="Default keymap";
|
||||
|
||||
default_layer {
|
||||
bindings = <
|
||||
&swap &kp LS(TAB)
|
||||
&kp B &kp C>;
|
||||
};
|
||||
};
|
||||
};
|
||||
```
|
||||
|
||||
- The sequence `(swap, swap, LS(TAB))` produces `(LA(TAB), LA(TAB), LA(LS(TAB)))`. The LS(TAB) behavior does not fire the interrupt behavior, because it is included in `ignored-key-positions`.
|
||||
- The sequence `(swap, swap, B)` produces `(LA(TAB), LA(TAB), B)`. The B behavior **does** fire the interrupt behavior, because it is **not** included in `ignored-key-positions`.
|
||||
|
||||
#### `ignored-layers`
|
||||
|
||||
- By default, any layer change will trigger the end behavior.
|
||||
- Including `ignored-layers` in your tri-state definition will let the specified layers NOT trigger the end behavior when they become active (include the layer the behavior is on to accommodate for layer toggling).
|
||||
- Activating any layer **NOT** listed in `ignored-layers` will cause the interrupt behavior to fire.
|
||||
- Note that `ignored-layers` is an array of layer indexes. Layers are numbered according to your keymap, starting with 0. The first layer in your keymap is layer 0. The next layer will be layer 1, et cetera.
|
||||
- Looking back at the swapper implementation, we can see how `ignored-layers` can affect things
|
||||
|
||||
```
|
||||
/ {
|
||||
behaviors {
|
||||
swap: swapper {
|
||||
compatible = "zmk,behavior-tri-state";
|
||||
label = "SWAPPER";
|
||||
#binding-cells = <0>;
|
||||
bindings = <&kt LALT>, <&kp TAB>, <&kt LALT>;
|
||||
ignored-key-positions = <1 2 3>;
|
||||
ignored-layers = <1>;
|
||||
};
|
||||
};
|
||||
|
||||
keymap {
|
||||
compatible = "zmk,keymap";
|
||||
label ="Default keymap";
|
||||
|
||||
default_layer {
|
||||
bindings = <
|
||||
&swap &kp LS(TAB)
|
||||
&kp B &tog 1>;
|
||||
};
|
||||
|
||||
layer2 {
|
||||
bindings = <
|
||||
&kp DOWN &kp B
|
||||
&tog 2 &trans>;
|
||||
};
|
||||
|
||||
layer3 {
|
||||
bindings = <
|
||||
&kp LEFT &kp N2
|
||||
&trans &kp N3>;
|
||||
};
|
||||
};
|
||||
};
|
||||
```
|
||||
|
||||
- The sequence `(swap, tog 1, DOWN)` produces `(LA(TAB), LA(DOWN))`. The change to layer 1 does not fire the interrupt behavior, because it is included in `ignored-layers`, and DOWN is in the same position as the tri-state, also not firing the interrupt behavior.
|
||||
- The sequence `(swap, tog 1, tog 2, LEFT)` produces `(LA(TAB), LEFT`. The change to layer 2 **does** fire the interrupt behavior, because it is not included in `ignored-layers`.
|
|
@ -33,7 +33,7 @@ ZMK is currently missing some features found in other popular firmware. This tab
|
|||
| One Shot Keys | ✅ | ✅ | ✅ |
|
||||
| [Combo Keys](features/combos.md) | ✅ | | ✅ |
|
||||
| [Macros](behaviors/macros.md) | ✅ | ✅ | ✅ |
|
||||
| Mouse Keys | 🚧 | ✅ | ✅ |
|
||||
| Mouse Keys | ✅ | ✅ | ✅ |
|
||||
| Low Active Power Usage | ✅ | | |
|
||||
| Low Power Sleep States | ✅ | ✅ | |
|
||||
| [Low Power Mode (VCC Shutoff)](behaviors/power.md) | ✅ | ✅ | |
|
||||
|
|
|
@ -32,10 +32,12 @@ module.exports = {
|
|||
"behaviors/key-toggle",
|
||||
"behaviors/sticky-key",
|
||||
"behaviors/sticky-layer",
|
||||
"behaviors/tri-state",
|
||||
"behaviors/tap-dance",
|
||||
"behaviors/caps-word",
|
||||
"behaviors/key-repeat",
|
||||
"behaviors/sensor-rotate",
|
||||
"behaviors/mouse-emulation",
|
||||
"behaviors/reset",
|
||||
"behaviors/bluetooth",
|
||||
"behaviors/outputs",
|
||||
|
|
Loading…
Add table
Reference in a new issue