From fd152baff08cdb643f694b9d1b1343193562325d Mon Sep 17 00:00:00 2001 From: Nicolas Munnich <98408764+Nick-Munnich@users.noreply.github.com> Date: Thu, 25 Jul 2024 21:00:22 +0200 Subject: [PATCH 01/23] feat(docs): Add troubleshooting notes on experimental bluetooth options (#2387) Co-authored-by: Cem Aksoylar --- docs/docs/config/bluetooth.md | 14 ++++++------- .../troubleshooting/connection-issues.mdx | 21 ++++++++++++++----- 2 files changed, 23 insertions(+), 12 deletions(-) diff --git a/docs/docs/config/bluetooth.md b/docs/docs/config/bluetooth.md index 02d203510dd..83fb9ec09d8 100644 --- a/docs/docs/config/bluetooth.md +++ b/docs/docs/config/bluetooth.md @@ -9,10 +9,10 @@ See [Configuration Overview](index.md) for instructions on how to change these s ## Kconfig -| Option | Type | Description | Default | -| -------------------------------------- | ---- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | ------- | -| `CONFIG_ZMK_BLE_EXPERIMENTAL_CONN` | bool | Enables a combination of settings that are planned to be default in future versions of ZMK to improve connection stability. This includes changes to timing on BLE pairing initiation, restores use of the updated/new LLCP implementation, and disables 2M PHY support. | n | -| `CONFIG_ZMK_BLE_EXPERIMENTAL_SEC` | bool | Enables a combination of settings that are planned to be officially supported in the future. This includes enabling BT Secure Connection passkey entry, and allows overwrite of keys from previously paired hosts. | n | -| `CONFIG_ZMK_BLE_EXPERIMENTAL_FEATURES` | bool | Aggregate config that enables both `CONFIG_ZMK_BLE_EXPERIMENTAL_CONN` and `CONFIG_ZMK_BLE_EXPERIMENTAL_SEC`. | n | -| `CONFIG_ZMK_BLE_PASSKEY_ENTRY` | bool | Enable passkey entry during pairing for enhanced security. (Note: After enabling this, you will need to re-pair all previously paired hosts.) | n | -| `CONFIG_BT_GATT_ENFORCE_SUBSCRIPTION` | bool | Low level setting for GATT subscriptions. Set to `n` to work around an annoying Windows bug with battery notifications. | y | +| Option | Type | Description | Default | +| -------------------------------------- | ---- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | ------- | +| `CONFIG_ZMK_BLE_EXPERIMENTAL_CONN` | bool | Enables a combination of settings that are planned to be default in future versions of ZMK to improve connection stability. Currently this only disables 2M PHY support. | n | +| `CONFIG_ZMK_BLE_EXPERIMENTAL_SEC` | bool | Enables a combination of settings that are planned to be officially supported in the future. This includes enabling BT Secure Connection passkey entry, and allows overwrite of keys from previously paired hosts. | n | +| `CONFIG_ZMK_BLE_EXPERIMENTAL_FEATURES` | bool | Aggregate config that enables both `CONFIG_ZMK_BLE_EXPERIMENTAL_CONN` and `CONFIG_ZMK_BLE_EXPERIMENTAL_SEC`. | n | +| `CONFIG_ZMK_BLE_PASSKEY_ENTRY` | bool | Enable passkey entry during pairing for enhanced security. (Note: After enabling this, you will need to re-pair all previously paired hosts.) | n | +| `CONFIG_BT_GATT_ENFORCE_SUBSCRIPTION` | bool | Low level setting for GATT subscriptions. Set to `n` to work around an annoying Windows bug with battery notifications. | y | diff --git a/docs/docs/troubleshooting/connection-issues.mdx b/docs/docs/troubleshooting/connection-issues.mdx index 5fdd1c836a0..f077702c5bb 100644 --- a/docs/docs/troubleshooting/connection-issues.mdx +++ b/docs/docs/troubleshooting/connection-issues.mdx @@ -91,6 +91,20 @@ The settings reset firmware has Bluetooth disabled to prevent the two sides from ::: +## Unable to Connect to Device + +### Additional Bluetooth Options + +Some devices and operating systems may have additional restrictions that they require be met before allowing a bluetooth peripheral to pair with them. If your keyboard is visible to your host but you are having issues trouble connecting or no input is registered, this might be the cause. Some of ZMK's [experimental bluetooth settings](../config/bluetooth.md) may suffice to resolve the issue. In particular: + +- Disabling PHY 2Mbps ([`CONFIG_BT_CTLR_PHY_2M=n`](https://docs.zephyrproject.org/3.5.0/kconfig.html#CONFIG_BT_CTLR_PHY_2M)) helps to pair and connect for certain wireless chipset firmware versions, particularly on Windows (Realtek and Intel chips) and older Intel Macs with Broadcom chipsets. +- Enabling passkey entry ([`CONFIG_ZMK_BLE_PASSKEY_ENTRY=y`](../config/bluetooth.md)) helps for certain Windows computers (work-managed ones in particular). This may also manifest in not sending keystrokes. + +### Issues With Dual Boot Setups + +Since ZMK associates pairing/bond keys with hardware addresses of hosts, you cannot pair to two different operating systems in a dual boot system at the same time. +While you can find [documented workarounds](https://wiki.archlinux.org/title/bluetooth#Dual_boot_pairing) that involve copying pairing keys across operating systems and use both OS with a single profile, they can be fairly involved and should be followed with caution. + ## Issues While Connected ### Unreliable/Weak Connection @@ -111,11 +125,6 @@ This setting can also improve the connection strength between the keyboard halve If you want to test Bluetooth output on your keyboard and are powering it through the USB connection rather than a battery, you will be able to pair with a host device but may not see keystrokes sent. In this case you need to use the [output selection behavior](../behaviors/outputs.md) to prefer sending keystrokes over Bluetooth rather than USB. This might be necessary even if you are not powering from a device capable of receiving USB inputs, such as a USB charger. -### Issues With Dual Boot Setups - -Since ZMK associates pairing/bond keys with hardware addresses of hosts, you cannot pair to two different operating systems in a dual boot system at the same time. -While you can find [documented workarounds](https://wiki.archlinux.org/title/bluetooth#Dual_boot_pairing) that involve copying pairing keys across operating systems and use both OS with a single profile, they can be fairly involved and should be followed with caution. - ### macOS Connected but Not Working If you attempt to pair a ZMK keyboard from macOS in a way that causes a bonding issue, macOS may report the keyboard as connected, but fail to actually work. If this occurs: @@ -138,3 +147,5 @@ If this doesn't help, try following the procedure above but replace step 3 with - Restart the Windows device - Open "Device Manager," turn on "Show hidden devices" from the "View" menu, then find and delete the keyboard under the "Bluetooth" item + +Some Windows devices may also require passkey entry, described under ["Unable to Connect to Device"](#unable-to-connect-to-device). From 97294aa341508e7f243f1abed670188c3596e673 Mon Sep 17 00:00:00 2001 From: Nicolas Munnich <98408764+Nick-Munnich@users.noreply.github.com> Date: Thu, 25 Jul 2024 21:00:55 +0200 Subject: [PATCH 02/23] chore: Add a recommended extension for .mdx files (#2381) --- .vscode/extensions.json | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/.vscode/extensions.json b/.vscode/extensions.json index 0819f71e7a0..7a1eac2aa5c 100644 --- a/.vscode/extensions.json +++ b/.vscode/extensions.json @@ -4,6 +4,7 @@ "ms-python.python", "ms-vscode.cpptools", "plorefice.devicetree", - "twxs.cmake" + "twxs.cmake", + "unifiedjs.vscode-mdx" ] } From f92dce43e9c76bdba76359ec1f90e606cd609ee0 Mon Sep 17 00:00:00 2001 From: Nicolas Munnich <98408764+Nick-Munnich@users.noreply.github.com> Date: Thu, 25 Jul 2024 21:08:08 +0200 Subject: [PATCH 03/23] feat(docs): Add modules feature page (#2380) Co-authored-by: Cem Aksoylar --- docs/docs/config/index.md | 16 +- docs/docs/development/build-flash.mdx | 4 + docs/docs/development/new-shield.mdx | 7 + docs/docs/features/beta-testing.mdx | 100 ---------- docs/docs/features/modules.mdx | 194 +++++++++++++++++++ docs/sidebars.js | 260 ++++++++++++++------------ 6 files changed, 360 insertions(+), 221 deletions(-) delete mode 100644 docs/docs/features/beta-testing.mdx create mode 100644 docs/docs/features/modules.mdx diff --git a/docs/docs/config/index.md b/docs/docs/config/index.md index d542d435f1e..666cf4d9a68 100644 --- a/docs/docs/config/index.md +++ b/docs/docs/config/index.md @@ -28,12 +28,14 @@ When using a split keyboard, you can use a single file without the `_left` or `_ ### Board Folder -ZMK will search for config files in either of: +ZMK will search for config files in: - [`zmk/app/boards/arm/`](https://github.com/zmkfirmware/zmk/tree/main/app/boards/arm) -- `zmk-config/config/boards/arm/` +- `zmk-config/boards/arm/` +- `/boards/arm/` +- `zmk-config/config/boards/arm/` (For backwards compatibility only, do not use.) -...where `` is the name of the board. These files describe the hardware of the board. +...where `` is the name of the board and `` is the root directory of any [included module](../features/modules.mdx). These files describe the hardware of the board. ZMK will search the board folder for the following config files: @@ -48,12 +50,14 @@ For more documentation on creating and configuring a new board, see [Zephyr's bo ### Shield Folder -When building with a shield, ZMK will search for config files in either of: +When building with a shield, ZMK will search for config files in: - [`zmk/app/boards/shields/`](https://github.com/zmkfirmware/zmk/tree/main/app/boards/shields) -- `zmk-config/config/boards/shields/` +- `zmk-config/boards/shields/` +- `/boards/shields/` +- `zmk-config/config/boards/shields/` (For backwards compatibility only, do not use.) -...where `` is the name of the shield. These files describe the hardware of the shield that the board is plugged into. +...where `` is the name of the shield and `` is the root directory of any [included module](../features/modules.mdx). These files describe the hardware of the shield that the board is plugged into. ZMK will search the shield folder for the following config files: diff --git a/docs/docs/development/build-flash.mdx b/docs/docs/development/build-flash.mdx index 20e9e20a34c..cfcb39ee312 100644 --- a/docs/docs/development/build-flash.mdx +++ b/docs/docs/development/build-flash.mdx @@ -108,6 +108,10 @@ west build -b nice_nano -- -DSHIELD=kyria_left -DZMK_CONFIG="C:/Users/myUser/Doc The above command must still be invoked from the `zmk/app` directory as noted above, rather than the config directory. Otherwise, you will encounter errors such as `ERROR: source directory "." does not contain a CMakeLists.txt; is this really what you want to build?`. Alternatively you can add the `-s /path/to/zmk/app` flag to your `west` command. ::: +:::warning +If your config is also a [module](../features/modules.mdx), then you should also add the root (the folder in which the `zephyr` folder is found) of your `zmk-config` as an [external module to build with](#building-with-external-modules). +::: + In order to make your `zmk-config` folder available when building within the VSCode Remote Container, you need to create a docker volume named `zmk-config` by binding it to the full path of your config directory. If you have run the VSCode Remote Container before, it is likely that docker has created this volume automatically -- we need to delete the default volume before binding it to the correct path. Follow the following steps: diff --git a/docs/docs/development/new-shield.mdx b/docs/docs/development/new-shield.mdx index 60299abf92e..ae97f6afe75 100644 --- a/docs/docs/development/new-shield.mdx +++ b/docs/docs/development/new-shield.mdx @@ -51,6 +51,13 @@ Follow these steps to create your new repository: - Select Public or Private, depending on your preference. - Click the green "Create repository" button +The repository is a combination of the directories and files required of a ZMK config, and those required of a shield module. To create a shield module, the following components are needed: + +- The `boards/shields` directory, where the keyboard's files will go +- The `zephyr/module.yml` file, which identifies and describes the module. See the [Zephyr documentation](https://docs.zephyrproject.org/3.5.0/develop/modules.html#module-yaml-file-description) for details on customising this file. For the purposes of creating a shield module, the default found in the template can be left untouched. + +Neither of these should be moved out of their parent directory. The other files and directories such as `config` are not necessary for the purposes of a shield module, but rather intended to be used for user configuration and testing. + ## New Shield Directory :::note diff --git a/docs/docs/features/beta-testing.mdx b/docs/docs/features/beta-testing.mdx deleted file mode 100644 index 1a8d28633cb..00000000000 --- a/docs/docs/features/beta-testing.mdx +++ /dev/null @@ -1,100 +0,0 @@ ---- -title: Beta Testing -sidebar_label: Beta Testing ---- - -import Tabs from "@theme/Tabs"; -import TabItem from "@theme/TabItem"; - -You may find that ZMK does not support a feature or keyboard that you are interesting in using. You may find that someone -has already taken the time to submit the feature you need as a [Pull Request](https://github.com/zmkfirmware/zmk/pulls). If you find the feature you need as a pull request, -this page is for you! - -## Developer Repositories and Branches - -For a developer to submit a pull request to ZMK, they must first clone the original ZMK repository. After they have a copy -of the source code, they may create a feature branch to work within. When they have finished, they will publish the feature -branch and create the pull request. - -### Finding the Repository Page from the Pull Request - -![PR Repository](../assets/features/beta-testing/pr-repo-branch.png) - -### Finding the Repository URL - -![Repository URL](../assets/features/beta-testing/repo-url.png) - -### Finding the Repository Branch - -![Repository URL](../assets/features/beta-testing/repo-branch.png) - -## Testing Features - -Testing features will require you to modify the `west.yml` file. You will need to add a new remote for the pull request you -would like to test, and change the selected remote and revision (or branch) for the `zmk` project. - -### Examples - - - - -```yaml -manifest: - remotes: - - name: zmkfirmware - url-base: https://github.com/zmkfirmware - projects: - - name: zmk - remote: zmkfirmware - revision: main - import: app/west.yml - self: - path: config -``` - - - - -```yaml -manifest: - remotes: - - name: zmkfirmware - url-base: https://github.com/zmkfirmware - - name: okke-formsma - url-base: https://github.com/okke-formsma - projects: - - name: zmk - remote: okke-formsma - revision: macros - import: app/west.yml - self: - path: config -``` - - - - -```yaml -manifest: - remotes: - - name: zmkfirmware - url-base: https://github.com/zmkfirmware - - name: mcrosson - url-base: https://github.com/mcrosson - projects: - - name: zmk - remote: mcrosson - revision: feat-behavior-sleep - import: app/west.yml - self: - path: config -``` - - - diff --git a/docs/docs/features/modules.mdx b/docs/docs/features/modules.mdx new file mode 100644 index 00000000000..e25bc72aecc --- /dev/null +++ b/docs/docs/features/modules.mdx @@ -0,0 +1,194 @@ +--- +title: Modules +sidebar_label: Modules +--- + +import Tabs from "@theme/Tabs"; +import TabItem from "@theme/TabItem"; + +ZMK makes use of [Zephyr modules](https://docs.zephyrproject.org/3.5.0/develop/modules.html) to include additional source code or configuration files into its build. You can think of them as similar to plugins or themes. The most common uses of this feature are: + +- Building firmware for a keyboard external to ZMK's tree +- Adding functionality to ZMK, such as a driver or a behavior + +A common ZMK setup thus consists of the following separate components, commonly housed in their respective Git repositories: + +- A single ZMK config maintained by the user, containing the `.conf` and `.keymap` files for one or multiple keyboards. This is also where files from ZMK or modules should be overridden/modified, if there is a need. +- Any number of ZMK modules, maintained by the module's owner. Some modules may contain multiple keyboards or functionalities. If all of your keyboards and functionalities are internal to ZMK's tree, then no modules are necessary. +- The ZMK firmware itself, maintained by its contributors. + +:::note +The shift to using modules for keyboards is a relatively recent one, and not all designs may be properly configured to be used as a module. If this is the case for your keyboard, then we would strongly suggest asking your vendor or designer to rectify this. +::: + +## Building With Modules + +### GitHub Actions + +When [using GitHub Actions to build ZMK](../user-setup.mdx), adding modules is as simple as modifying the `west.yml` found in your `zmk-config`'s `config` directory. The recommended way of doing so is: + +1. Find the URL base (the parent URL) for the module and add it as an entry to the [remotes](https://docs.zephyrproject.org/3.5.0/develop/west/manifest.html#remotes). +2. Add the module as an entry to the [projects](https://docs.zephyrproject.org/3.5.0/develop/west/manifest.html#projects). + Aside from the mandatory `name`, `remote`, and the commonly used `revision` properties, take note of the `import` property under `projects`. Some modules may have other modules as dependencies. This property allows the specifying of an additional west manifest file found in the tree of the module, which will automatically import all dependencies. + +For more information on `west.yml`, see [West Manifests](https://docs.zephyrproject.org/3.5.0/develop/west/manifest.html). + +#### Examples + + + + +```yaml +manifest: + remotes: + - name: zmkfirmware + url-base: https://github.com/zmkfirmware + projects: + - name: zmk + remote: zmkfirmware + revision: main + import: app/west.yml + self: + path: config +``` + + + + +```yaml +manifest: + remotes: + - name: zmkfirmware + url-base: https://github.com/zmkfirmware + - name: module_a_base + url-base: https://github.com/alice + projects: + - name: zmk + remote: zmkfirmware + revision: main + import: app/west.yml + - name: module_a + remote: module_a_base + revision: main + self: + path: config +``` + + + + +```yaml +manifest: + remotes: + - name: zmkfirmware + url-base: https://github.com/zmkfirmware + - name: module_a_base + url-base: https://github.com/alice + - name: module_b_base + url-base: https://github.com/bob + projects: + - name: zmk + remote: zmkfirmware + revision: main + import: app/west.yml + - name: module_a + remote: module_a_base + revision: main + - name: module_b + remote: module_b_base + revision: main + import: west.yml + self: + path: config +``` + + + + +### Building Locally + +To add a module to your build when building locally, you will need to clone/copy said module into your local file tree. You can then build using the module as described in [Building with External Modules](../development/build-flash.mdx#building-with-external-modules). + +## Beta Testing + +You may find that there are some features which you desire for which there is a [Pull Request](https://github.com/zmkfirmware/zmk/pulls), but no module. If this is the case, you can still make use of the feature. + +### Developer Repositories and Branches + +For a developer to submit a pull request to ZMK, they must first clone the original ZMK repository. After they have a copy +of the source code, they may create a feature branch to work within. When they have finished, they will publish the feature +branch and create the pull request. + +#### Finding the repository page from the Pull Request + +![PR Repository](../assets/features/beta-testing/pr-repo-branch.png) + +#### Finding the repository URL + +![Repository URL](../assets/features/beta-testing/repo-url.png) + +#### Finding the repository branch + +![Repository URL](../assets/features/beta-testing/repo-branch.png) + +## Testing Features + +### GitHub Actions + +When [using GitHub Actions to build ZMK](../user-setup.mdx), once you have obtained the correct URL, you'll need to modify the `west.yml` file similarly to [Building With Modules](#building-with-modules). Add the remote for the branch like in said section. The difference is that you will need to change the selected remote and revision (or branch) for the `zmk` project. + +#### Example + + + + +```yaml +manifest: + remotes: + - name: zmkfirmware + url-base: https://github.com/zmkfirmware + projects: + - name: zmk + remote: zmkfirmware + revision: main + import: app/west.yml + self: + path: config +``` + + + + +```yaml +manifest: + remotes: + - name: zmkfirmware + url-base: https://github.com/zmkfirmware + - name: forkedzmk + url-base: https://github.com/forkedzmk + projects: + - name: zmk + remote: forkedzmk + revision: specificpr + import: app/west.yml + self: + path: config +``` + + + + +### Building Locally + +When building from a pull request locally, you'll need to [perform the local user setup](../development/setup/index.md), but using the repository of the pull request rather than the official ZMK repository. You can then [build and flash](../development/build-flash.mdx) as usual. diff --git a/docs/sidebars.js b/docs/sidebars.js index e8c715c8c76..1233ccf1b42 100644 --- a/docs/sidebars.js +++ b/docs/sidebars.js @@ -1,120 +1,150 @@ module.exports = { - docs: { - "Getting Started": [ - "intro", - "hardware", - "faq", - "user-setup", - "customization", - { - type: "category", - label: "Troubleshooting", - link: { - type: "doc", - id: "troubleshooting/index", - }, - collapsed: true, - items: [ - "troubleshooting/building-issues", - "troubleshooting/flashing-issues", - "troubleshooting/connection-issues", - ], + docs: [ + { + type: "category", + label: "Getting Started", + link: { + type: "doc", + id: "intro", }, - ], - Features: [ - "features/keymaps", - "features/bluetooth", - "features/combos", - "features/conditional-layers", - "features/debouncing", - "features/displays", - "features/encoders", - "features/underglow", - "features/backlight", - "features/battery", - "features/soft-off", - "features/beta-testing", - ], - Behaviors: [ - "behaviors/index", - "behaviors/key-press", - "behaviors/layers", - "behaviors/misc", - "behaviors/hold-tap", - "behaviors/mod-tap", - "behaviors/mod-morph", - "behaviors/macros", - "behaviors/key-toggle", - "behaviors/sticky-key", - "behaviors/sticky-layer", - "behaviors/tap-dance", - "behaviors/caps-word", - "behaviors/key-repeat", - "behaviors/sensor-rotate", - "behaviors/mouse-emulation", - "behaviors/reset", - "behaviors/bluetooth", - "behaviors/outputs", - "behaviors/underglow", - "behaviors/backlight", - "behaviors/power", - "behaviors/soft-off", - ], - Codes: [ - "codes/index", - "codes/keyboard-keypad", - "codes/modifiers", - "codes/editing", - "codes/media", - "codes/applications", - "codes/input-assist", - "codes/power", - ], - Configuration: [ - "config/index", - "config/backlight", - "config/battery", - "config/behaviors", - "config/bluetooth", - "config/combos", - "config/displays", - "config/encoders", - "config/keymap", - "config/kscan", - "config/power", - "config/underglow", - "config/system", - ], - Development: [ - "development/clean-room", - "development/pre-commit", - "development/documentation", - { - type: "category", - label: "Setup", - collapsed: true, - items: [ - "development/setup/index", - "development/setup/docker", - "development/setup/native", - ], + collapsed: false, + items: [ + "hardware", + "faq", + "user-setup", + "customization", + { + type: "category", + label: "Troubleshooting", + link: { + type: "doc", + id: "troubleshooting/index", + }, + collapsed: true, + items: [ + "troubleshooting/building-issues", + "troubleshooting/flashing-issues", + "troubleshooting/connection-issues", + ], + }, + ], + }, + { + Features: [ + "features/keymaps", + "features/bluetooth", + "features/combos", + "features/conditional-layers", + "features/debouncing", + "features/displays", + "features/encoders", + "features/modules", + "features/underglow", + "features/backlight", + "features/battery", + "features/soft-off", + ], + }, + { + type: "category", + label: "Behaviors", + link: { + type: "doc", + id: "behaviors/index", }, - "development/build-flash", - "development/boards-shields-keymaps", - "development/posix-board", - "development/tests", - "development/usb-logging", - "development/ide-integration", - { - type: "category", - label: "Guides", - collapsed: false, - items: [ - "development/new-shield", - "development/hardware-metadata-files", - "development/new-behavior", - ], + collapsed: true, + items: [ + "behaviors/key-press", + "behaviors/layers", + "behaviors/misc", + "behaviors/hold-tap", + "behaviors/mod-tap", + "behaviors/mod-morph", + "behaviors/macros", + "behaviors/key-toggle", + "behaviors/sticky-key", + "behaviors/sticky-layer", + "behaviors/tap-dance", + "behaviors/caps-word", + "behaviors/key-repeat", + "behaviors/sensor-rotate", + "behaviors/mouse-emulation", + "behaviors/reset", + "behaviors/bluetooth", + "behaviors/outputs", + "behaviors/underglow", + "behaviors/backlight", + "behaviors/power", + "behaviors/soft-off", + ], + }, + { + Codes: [ + "codes/index", + "codes/keyboard-keypad", + "codes/modifiers", + "codes/editing", + "codes/media", + "codes/applications", + "codes/input-assist", + "codes/power", + ], + }, + { + type: "category", + label: "Configuration", + link: { + type: "doc", + id: "config/index", }, - ], - }, + collapsed: true, + items: [ + "config/backlight", + "config/battery", + "config/behaviors", + "config/bluetooth", + "config/combos", + "config/displays", + "config/encoders", + "config/keymap", + "config/kscan", + "config/power", + "config/underglow", + "config/system", + ], + }, + { + Development: [ + "development/clean-room", + "development/pre-commit", + "development/documentation", + { + type: "category", + label: "Setup", + collapsed: true, + items: [ + "development/setup/index", + "development/setup/docker", + "development/setup/native", + ], + }, + "development/build-flash", + "development/boards-shields-keymaps", + "development/posix-board", + "development/tests", + "development/usb-logging", + "development/ide-integration", + { + type: "category", + label: "Guides", + collapsed: false, + items: [ + "development/new-shield", + "development/hardware-metadata-files", + "development/new-behavior", + ], + }, + ], + }, + ], }; From 9e7fcde868d4ea0d4bd51fcae0afba0349a2bd81 Mon Sep 17 00:00:00 2001 From: Nicolas Munnich <98408764+Nick-Munnich@users.noreply.github.com> Date: Thu, 1 Aug 2024 05:53:36 +0200 Subject: [PATCH 04/23] feat(docs): Improve the layers-related documentation (#2390) Fixes #1284 --- docs/docs/behaviors/layers.md | 4 +++ docs/docs/features/keymaps.mdx | 61 ++++++++++++++++++++++++++-------- docs/docs/keymap-example.md | 36 +++++++++++++------- 3 files changed, 75 insertions(+), 26 deletions(-) diff --git a/docs/docs/behaviors/layers.md b/docs/docs/behaviors/layers.md index 511fbe08b7a..d0e8067916e 100644 --- a/docs/docs/behaviors/layers.md +++ b/docs/docs/behaviors/layers.md @@ -8,6 +8,10 @@ sidebar_label: Layers Often, you may want a certain key position to alter which layers are enabled, change the default layer, etc. Some of those behaviors are still in the works; the ones that are working now are documented here. +:::note +Multiple layers can be active at the same time and activating a layer will not deactivate layers higher up in the "layer stack". See [Layers](../features/keymaps.mdx#layers) for more information. +::: + ## Defines to Refer to Layers When working with layers, you may have several different key positions with bindings that enable/disable those layers. diff --git a/docs/docs/features/keymaps.mdx b/docs/docs/features/keymaps.mdx index 95df3e86842..c1608204c0f 100644 --- a/docs/docs/features/keymaps.mdx +++ b/docs/docs/features/keymaps.mdx @@ -4,7 +4,6 @@ sidebar_label: Keymaps --- import KeymapExample from "../keymap-example.md"; -import KeymapExampleFile from "../keymap-example-file.md"; ZMK uses a declarative approach to keymaps instead of using C code for all keymap configuration. Right now, ZMK uses the devicetree syntax to declare those keymaps; future work is envisioned for @@ -38,21 +37,24 @@ For the full set of possible behaviors, see the [overview page for behaviors](.. ## Layers Like many mechanical keyboard firmwares, ZMK keymaps are composed of a collection of layers, with a -minimum of at least one layer that is the default, usually near the bottom of the "stack". Each layer +minimum of at least one layer that is the default, usually near the bottom of the "layer stack". Each layer in ZMK contains a set of bindings that bind a certain behavior to a certain key position in that layer. | ![Diagram of three layers](../assets/features/keymaps/layer-diagram.png) | | :---------------------------------------------------------------------------------------------------------------------------------------------------------------------------: | | _A simplified diagram showing three layers. The layout of each layer is the same (they all contain four keys), but the behavior bindings within each layer can be different._ | -In addition to the base default layer (which can be changed), certain bound behaviors may also -enable/disable additional layers "on top" of the default layer. +All layers are assigned and referred to by a natural number, with the base layer being layer `0`. It is common to [use the C preprocessor to "name" layers](../behaviors/layers.md#defines-to-refer-to-layers), making them more legible. -When a key location is pressed/released, the stack of all active layers from "top to bottom" is used, -and the event is sent to the behavior bound at that position in each layer, for it to perform whatever -actions it wants to in reaction to the event. Those behaviors can choose to "handle" the event, and stop -it from being passed to any lower layers, or may choose to "pass it along", and let the next layer -in the stack _also_ get the event. +The default layer (the base layer with index 0) is always enabled. Certain bound behaviors may enable/disable additional layers. + +When a key location is pressed/released, the _highest-valued currently active_ layer is used. The press/release event is sent to the behavior bound at that position in said layer, for it to perform whatever actions it wants to in reaction to the event. The behavior can choose to "consume" the event, or "pass it along" and let the next highest-valued active layer _also_ get the event (whose behavior may continue "passing it along"). + +Note that the _activation_ order isn't relevant for determining the priority of active layers, it is determined _only_ by the definition order. + +:::tip +If you wish to use multiple base layers (with a [toggle](../behaviors/layers.md#toggle-layer)), e.g. one for QWERTY and another for Colemak layouts, you will want these layers to have the lowest value possible. In other words, one should be layer `0`, and the other should be layer `1`. This allows other momentary layers activated on top of them to work with both. +::: ## Behavior Bindings @@ -121,18 +123,49 @@ Nested under the devicetree root, is the keymap node. The node _name_ itself is ### Layers -Each layer of your keymap will be nested under the keymap node. Here is a sample -that defines just one layer for this keymap: +Each layer of your keymap will be nested under the keymap node. Here is an example of a layer in a 6-key macropad. - +```dts + keymap { + compatible = "zmk,keymap"; + + default_layer { // Layer 0 +// ---------------------------------------------- +// | Z | M | K | +// | A | B | C | + bindings = < + &kp Z &kp M &kp K + &kp A &kp B &kp C + >; + }; + }; +``` Each layer should have: 1. A `bindings` property this will be a list of [behavior bindings](../behaviors/index.mdx), one for each key position for the keyboard. 1. (Optional) A `sensor-bindings` property that will be a list of behavior bindings for each sensor on the keyboard. (Currently, only encoders are supported as sensor hardware, but in the future devices like trackpoints would be supported the same way) +### Multiple Layers + +Layers are numbered in the order that they appear in keymap node - the first layer is `0`, the second layer is `1`, etc. + +Here is an example of a trio of layers for a simple 6-key macropad: + + + +:::note +Even if layer `1` was to be activated after `2`, layer `2` would still have priority as it is higher valued. Behaviors such as [To Layer (`&to`)](../behaviors/layers.md#to-layer) can be used to enable one layer _and disable all other non-default layers_, though. +::: + ### Complete Example -Putting this all together, a complete [`kyria.keymap`](https://github.com/zmkfirmware/zmk/blob/main/app/boards/shields/kyria/kyria.keymap) looks like: +Some examples of complete keymaps for a keyboard are: - +- [`corne.keymap`](https://github.com/zmkfirmware/zmk/blob/main/app/boards/shields/corne/corne.keymap) +- [`kyria.keymap`](https://github.com/zmkfirmware/zmk/blob/main/app/boards/shields/kyria/kyria.keymap) +- [`lily58.keymap`](https://github.com/zmkfirmware/zmk/blob/main/app/boards/shields/lily58/lily58.keymap) + +:::tip +Every keyboard comes with a "default keymap". For additional examples, the [ZMK tree's `app/boards` folder](https://github.com/zmkfirmware/zmk/blob/main/app/boards) can be browsed. +::: diff --git a/docs/docs/keymap-example.md b/docs/docs/keymap-example.md index e526d5427a9..57f40762ea4 100644 --- a/docs/docs/keymap-example.md +++ b/docs/docs/keymap-example.md @@ -2,20 +2,32 @@ keymap { compatible = "zmk,keymap"; - default_layer { -// -------------------------------------------------------------------------------------------------------------------------------------------------------------------- -// | ESC | Q | W | E | R | T | | Y | U | I | O | P | \ | -// | TAB | A | S | D | F | G | | H | J | K | L | ; | ' | -// | SHIFT | Z | X | C | V | B | CTRL+A | CTRL+C | | CTRL+V | CTRL+X | N | M | , | . | / | R CTRL | -// | GUI | DEL | RETURN | SPACE | ESCAPE | | RETURN | SPACE | TAB | BSPC | R ALT | + default_layer { // Layer 0 +// ---------------------------------------------- +// | Z | M | K | +// | &mo 1 | LEFT SHIFT | &mo 2 | bindings = < - &kp ESC &kp Q &kp W &kp E &kp R &kp T &kp Y &kp U &kp I &kp O &kp P &kp BSLH - &kp TAB &kp A &kp S &kp D &kp F &kp G &kp H &kp J &kp K &kp L &kp SEMI &kp SQT - &kp LSHIFT &kp Z &kp X &kp C &kp V &kp B &kp LC(A) &kp LC(C) &kp LC(V) &kp LC(X) &kp N &kp M &kp COMMA &kp DOT &kp FSLH &kp RCTRL - &kp LGUI &kp DEL &kp RET &kp SPACE &kp ESC &kp RET &kp SPACE &kp TAB &kp BSPC &kp RALT + &kp Z &kp M &kp K + &mo 1 &kp LSHIFT &mo 2 + >; + }; + abc { // Layer 1 +// ---------------------------------------------- +// | A | B | C | +// | &trans | &trans | &trans | + bindings = < + &kp A &kp B &kp C + &trans &trans &trans + >; + }; + xyz { // Layer 2 +// ---------------------------------------------- +// | X | Y | Z | +// | LEFT CTRL | LEFT ALT | &trans | + bindings = < + &kp X &kp Y &kp Z + &kp LCTRL &kp LALT &trans >; - - sensor-bindings = <&inc_dec_kp C_VOL_UP C_VOL_DN &inc_dec_kp PG_UP PG_DN>; }; }; ``` From b080befebb8114cf0d539eeeb0f2d415c694800c Mon Sep 17 00:00:00 2001 From: Cem Aksoylar Date: Mon, 29 Jul 2024 10:40:16 -0700 Subject: [PATCH 05/23] fix(docs): Fix link for matrix transform --- docs/docs/config/kscan.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/docs/config/kscan.md b/docs/docs/config/kscan.md index 4fb0cf168ad..097165be64f 100644 --- a/docs/docs/config/kscan.md +++ b/docs/docs/config/kscan.md @@ -342,7 +342,7 @@ Transforms should be used any time the physical layout of a keyboard's keys does Transforms can also be used for keyboards with multiple layouts. You can define multiple matrix transform nodes, one for each layout, and users can select which one they want from the `/chosen` node in their keymaps. -See the [new shield guide](../development/new-shield.mdx#optional-matrix-transform) for more documentation on how to define a matrix transform. +See the [new shield guide](../development/new-shield.mdx#matrix-transform) for more documentation on how to define a matrix transform. ### Devicetree From b9a025c76ec77417aca11b38f4dd9da60884e9a4 Mon Sep 17 00:00:00 2001 From: Cem Aksoylar Date: Fri, 2 Aug 2024 09:57:15 -0700 Subject: [PATCH 06/23] fix(docs): Document number of peripherals Kconfig (#2389) Fixes #2373 --- docs/docs/config/system.md | 1 + 1 file changed, 1 insertion(+) diff --git a/docs/docs/config/system.md b/docs/docs/config/system.md index 5d63ca529f4..fef9f53a5b5 100644 --- a/docs/docs/config/system.md +++ b/docs/docs/config/system.md @@ -125,6 +125,7 @@ Following split keyboard settings are defined in [zmk/app/src/split/Kconfig](htt | `CONFIG_ZMK_SPLIT_ROLE_CENTRAL` | bool | `y` for central device, `n` for peripheral | | | `CONFIG_ZMK_SPLIT_PERIPHERAL_HID_INDICATORS` | bool | Enable split keyboard support for passing indicator state to peripherals | n | | `CONFIG_ZMK_SPLIT_BLE` | bool | Use BLE to communicate between split keyboard halves | y | +| `CONFIG_ZMK_SPLIT_BLE_CENTRAL_PERIPHERALS` | int | Number of peripherals that will connect to the central | 1 | | `CONFIG_ZMK_SPLIT_BLE_CENTRAL_BATTERY_LEVEL_FETCHING` | bool | Enable fetching split peripheral battery levels to the central side | n | | `CONFIG_ZMK_SPLIT_BLE_CENTRAL_BATTERY_LEVEL_PROXY` | bool | Enable central reporting of split battery levels to hosts | n | | `CONFIG_ZMK_SPLIT_BLE_CENTRAL_BATTERY_LEVEL_QUEUE_SIZE` | int | Max number of battery level events to queue when received from peripherals | `CONFIG_ZMK_SPLIT_BLE_CENTRAL_PERIPHERALS` | From 47a6715aa6d380b97c505e40a581e7efb3bed042 Mon Sep 17 00:00:00 2001 From: Cem Aksoylar Date: Fri, 26 Jul 2024 14:27:25 -0700 Subject: [PATCH 07/23] fix(docs): Fix toggle layer docs --- docs/docs/behaviors/layers.md | 43 +++-------------------------------- 1 file changed, 3 insertions(+), 40 deletions(-) diff --git a/docs/docs/behaviors/layers.md b/docs/docs/behaviors/layers.md index d0e8067916e..7c95246d0d5 100644 --- a/docs/docs/behaviors/layers.md +++ b/docs/docs/behaviors/layers.md @@ -37,7 +37,7 @@ again. ### Behavior Binding - Reference: `&mo` -- Parameter: The layer number to enable/disable, e.g. `1` +- Parameter: The layer number to enable while held, e.g. `1` Example: @@ -52,7 +52,7 @@ The "layer-tap" behavior enables a layer when a key is held, and outputs a [keyp ### Behavior Binding - Reference: `<` -- Parameter: The layer number to enable when held, e.g. `1` +- Parameter: The layer number to enable while held, e.g. `1` - Parameter: The keycode to send when tapped, e.g. `A` Example: @@ -103,7 +103,7 @@ Example: ## Toggle Layer -The "toggle layer" behavior enables a layer until the layer is manually disabled. +The "toggle layer" behavior enables a layer if it is currently disabled, or disables it if enabled. ### Behavior Binding @@ -116,43 +116,6 @@ Example: &tog LOWER ``` -"Toggle layer" for a : - -```dts -#define DEFAULT 0 -#define NAVI 1 - -#define NONE 0 - -/ { - keymap { - compatible = "zmk,keymap"; - - default_layer { - bindings = < - &tog NAVI &kp KP_DIVIDE &kp KP_MULTIPLY &kp KP_MINUS - &kp NUMBER_7 &kp NUMBER_8 &kp NUMBER_9 &kp KP_PLUS - &kp NUMBER_4 &kp NUMBER_5 &kp NUMBER_6 &kp KP_PLUS - &kp NUMBER_1 &kp NUMBER_2 &kp NUMBER_3 &kp RETURN - &kp NUMBER_0 &kp NUMBER_0 &kp DOT &kp RETURN - >; - }; - - nav_layer { - bindings = < - &tog NAVI &kp KP_DIVIDE &kp KP_MULTIPLY &kp KP_MINUS - &kp HOME &kp UP &kp PAGE_UP &kp KP_PLUS - &kp LEFT &none &kp RIGHT &kp KP_PLUS - &kp END &kp DOWN &kp PAGE_DOWN &kp RETURN - &kp INSERT &kp INSERT &kp DEL &kp RETURN - >; - }; - }; -}; -``` - -It is possible to use "toggle layer" to have keys that raise and lower the layers as well. - ## Conditional Layers The "conditional layers" feature enables a particular layer when all layers in a specified set are active. From 78ed721c36115efeba877b7a419139bade905f65 Mon Sep 17 00:00:00 2001 From: Peter Johanson Date: Fri, 2 Aug 2024 17:26:56 -0600 Subject: [PATCH 08/23] fix(pm): Properly configure a wakeup as input. * Other drivers properly disconnect/de-config pins now, so we need to be sure the wakeup trigger connects the wake pin as input. --- app/src/gpio_key_wakeup_trigger.c | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/app/src/gpio_key_wakeup_trigger.c b/app/src/gpio_key_wakeup_trigger.c index 308c4973d67..d22523a4930 100644 --- a/app/src/gpio_key_wakeup_trigger.c +++ b/app/src/gpio_key_wakeup_trigger.c @@ -36,7 +36,12 @@ static int zmk_gpio_key_wakeup_trigger_init(const struct device *dev) { static int gpio_key_wakeup_trigger_pm_resume(const struct device *dev) { const struct gpio_key_wakeup_trigger_config *config = dev->config; - int ret = gpio_pin_interrupt_configure_dt(&config->trigger, GPIO_INT_LEVEL_ACTIVE); + int ret = gpio_pin_configure_dt(&config->trigger, GPIO_INPUT); + if (ret < 0) { + LOG_ERR("Failed to configure wakeup trigger key GPIO pin as input (%d)", ret); + return ret; + } + ret = gpio_pin_interrupt_configure_dt(&config->trigger, GPIO_INT_LEVEL_ACTIVE); if (ret < 0) { LOG_ERR("Failed to configure wakeup trigger key GPIO pin interrupt (%d)", ret); return ret; From cd25c12ce99d1ee708d76854a79980c7fb48a331 Mon Sep 17 00:00:00 2001 From: Peter Johanson Date: Sat, 3 Aug 2024 14:00:31 -0600 Subject: [PATCH 09/23] fix: Proper device PM support for composite kscan. * Clean up composite kscan to allow multiple instances properly. * Implement PM hook and properly suspend/resume the child devices. Fixes: #2388 --- app/module/drivers/kscan/kscan_composite.c | 107 ++++++++++++++++----- 1 file changed, 82 insertions(+), 25 deletions(-) diff --git a/app/module/drivers/kscan/kscan_composite.c b/app/module/drivers/kscan/kscan_composite.c index 2a3643245c5..a064903a745 100644 --- a/app/module/drivers/kscan/kscan_composite.c +++ b/app/module/drivers/kscan/kscan_composite.c @@ -7,6 +7,7 @@ #define DT_DRV_COMPAT zmk_kscan_composite #include +#include #include #include LOG_MODULE_DECLARE(zmk, CONFIG_ZMK_LOG_LEVEL); @@ -26,10 +27,10 @@ struct kscan_composite_child_config { .row_offset = DT_PROP(inst, row_offset), \ .column_offset = DT_PROP(inst, column_offset)}, -const struct kscan_composite_child_config kscan_composite_children[] = { - DT_FOREACH_CHILD(MATRIX_NODE_ID, CHILD_CONFIG)}; - -struct kscan_composite_config {}; +struct kscan_composite_config { + const struct kscan_composite_child_config *children; + size_t children_len; +}; struct kscan_composite_data { kscan_callback_t callback; @@ -38,51 +39,80 @@ struct kscan_composite_data { }; static int kscan_composite_enable_callback(const struct device *dev) { - for (int i = 0; i < ARRAY_SIZE(kscan_composite_children); i++) { - const struct kscan_composite_child_config *cfg = &kscan_composite_children[i]; + const struct kscan_composite_config *cfg = dev->config; + + for (int i = 0; i < cfg->children_len; i++) { + const struct kscan_composite_child_config *child_cfg = &cfg->children[i]; + +#if IS_ENABLED(CONFIG_PM_DEVICE_RUNTIME) + if (!pm_device_runtime_is_enabled(dev) && pm_device_runtime_is_enabled(child_cfg->child)) { + pm_device_runtime_get(child_cfg->child); + } +#elif IS_ENABLED(CONFIG_PM_DEVICE) + pm_device_action_run(child_cfg->child, PM_DEVICE_ACTION_RESUME); +#endif // IS_ENABLED(CONFIG_PM_DEVICE) - kscan_enable_callback(cfg->child); + kscan_enable_callback(child_cfg->child); } return 0; } static int kscan_composite_disable_callback(const struct device *dev) { - for (int i = 0; i < ARRAY_SIZE(kscan_composite_children); i++) { - const struct kscan_composite_child_config *cfg = &kscan_composite_children[i]; + const struct kscan_composite_config *cfg = dev->config; + for (int i = 0; i < cfg->children_len; i++) { + const struct kscan_composite_child_config *child_cfg = &cfg->children[i]; + + kscan_disable_callback(child_cfg->child); - kscan_disable_callback(cfg->child); +#if IS_ENABLED(CONFIG_PM_DEVICE_RUNTIME) + if (!pm_device_runtime_is_enabled(dev) && pm_device_runtime_is_enabled(child_cfg->child)) { + pm_device_runtime_put(child_cfg->child); + } +#elif IS_ENABLED(CONFIG_PM_DEVICE) + pm_device_action_run(child_cfg->child, PM_DEVICE_ACTION_SUSPEND); +#endif // IS_ENABLED(CONFIG_PM_DEVICE) } return 0; } +#define KSCAN_COMP_INST_DEV(n) DEVICE_DT_GET(DT_DRV_INST(n)), + +static const struct device *all_instances[] = {DT_INST_FOREACH_STATUS_OKAY(KSCAN_COMP_INST_DEV)}; + static void kscan_composite_child_callback(const struct device *child_dev, uint32_t row, uint32_t column, bool pressed) { // TODO: Ideally we can get this passed into our callback! - const struct device *dev = DEVICE_DT_GET(DT_DRV_INST(0)); - struct kscan_composite_data *data = dev->data; + for (int i = 0; i < ARRAY_SIZE(all_instances); i++) { - for (int i = 0; i < ARRAY_SIZE(kscan_composite_children); i++) { - const struct kscan_composite_child_config *cfg = &kscan_composite_children[i]; + const struct device *dev = all_instances[i]; + const struct kscan_composite_config *cfg = dev->config; + struct kscan_composite_data *data = dev->data; - if (cfg->child != child_dev) { - continue; - } + for (int c = 0; c < cfg->children_len; c++) { + const struct kscan_composite_child_config *child_cfg = &cfg->children[c]; + + if (child_cfg->child != child_dev) { + continue; + } - data->callback(dev, row + cfg->row_offset, column + cfg->column_offset, pressed); + data->callback(dev, row + child_cfg->row_offset, column + child_cfg->column_offset, + pressed); + } } } static int kscan_composite_configure(const struct device *dev, kscan_callback_t callback) { + const struct kscan_composite_config *cfg = dev->config; struct kscan_composite_data *data = dev->data; if (!callback) { return -EINVAL; } - for (int i = 0; i < ARRAY_SIZE(kscan_composite_children); i++) { - const struct kscan_composite_child_config *cfg = &kscan_composite_children[i]; + for (int i = 0; i < cfg->children_len; i++) { + const struct kscan_composite_child_config *child_cfg = &cfg->children[i]; - kscan_config(cfg->child, &kscan_composite_child_callback); + kscan_config(child_cfg->child, &kscan_composite_child_callback); } data->callback = callback; @@ -95,6 +125,10 @@ static int kscan_composite_init(const struct device *dev) { data->dev = dev; +#if IS_ENABLED(CONFIG_PM_DEVICE) + pm_device_init_suspended(dev); +#endif + return 0; } @@ -104,9 +138,32 @@ static const struct kscan_driver_api mock_driver_api = { .disable_callback = kscan_composite_disable_callback, }; -static const struct kscan_composite_config kscan_composite_config = {}; +#if IS_ENABLED(CONFIG_PM_DEVICE) -static struct kscan_composite_data kscan_composite_data; +static int kscan_composite_pm_action(const struct device *dev, enum pm_device_action action) { + switch (action) { + case PM_DEVICE_ACTION_SUSPEND: + return kscan_composite_disable_callback(dev); + case PM_DEVICE_ACTION_RESUME: + return kscan_composite_enable_callback(dev); + default: + return -ENOTSUP; + } +} -DEVICE_DT_INST_DEFINE(0, kscan_composite_init, NULL, &kscan_composite_data, &kscan_composite_config, - POST_KERNEL, CONFIG_ZMK_KSCAN_COMPOSITE_INIT_PRIORITY, &mock_driver_api); +#endif // IS_ENABLED(CONFIG_PM_DEVICE) + +#define KSCAN_COMP_DEV(n) \ + static const struct kscan_composite_child_config kscan_composite_children_##n[] = { \ + DT_INST_FOREACH_CHILD(n, CHILD_CONFIG)}; \ + static const struct kscan_composite_config kscan_composite_config_##n = { \ + .children = kscan_composite_children_##n, \ + .children_len = ARRAY_SIZE(kscan_composite_children_##n), \ + }; \ + static struct kscan_composite_data kscan_composite_data_##n; \ + PM_DEVICE_DT_INST_DEFINE(n, kscan_composite_pm_action); \ + DEVICE_DT_INST_DEFINE(n, kscan_composite_init, PM_DEVICE_DT_INST_GET(n), \ + &kscan_composite_data_##n, &kscan_composite_config_##n, POST_KERNEL, \ + CONFIG_ZMK_KSCAN_COMPOSITE_INIT_PRIORITY, &mock_driver_api); + +DT_INST_FOREACH_STATUS_OKAY(KSCAN_COMP_DEV) From 94c17d6f88ba48a0d96065789b966e0603f6d9f5 Mon Sep 17 00:00:00 2001 From: Cem Aksoylar Date: Wed, 15 Nov 2023 21:11:41 -0800 Subject: [PATCH 10/23] feat(mouse): Add mouse move and scroll support [WIP] Co-authored-by: Alexander Krikun Co-authored-by: Robert U Co-authored-by: Shawn Meier --- app/CMakeLists.txt | 10 +- app/Kconfig | 8 +- app/dts/behaviors.dtsi | 3 + app/dts/behaviors/mouse_move.dtsi | 12 ++ app/dts/behaviors/mouse_scroll.dtsi | 12 ++ .../behaviors/zmk,behavior-mouse-move.yaml | 13 ++ .../behaviors/zmk,behavior-mouse-scroll.yaml | 13 ++ app/include/dt-bindings/zmk/mouse.h | 36 +++- .../zmk/events/mouse_move_state_changed.h | 33 ++++ .../zmk/events/mouse_scroll_state_changed.h | 34 ++++ app/include/zmk/events/mouse_tick.h | 39 +++++ app/include/zmk/hid.h | 28 ++- app/include/zmk/mouse.h | 19 +- app/src/behaviors/behavior_mouse_move.c | 57 ++++++ app/src/behaviors/behavior_mouse_scroll.c | 58 +++++++ app/src/events/mouse_move_state_changed.c | 10 ++ app/src/events/mouse_scroll_state_changed.c | 10 ++ app/src/events/mouse_tick.c | 10 ++ app/src/hid.c | 33 +++- app/src/hog.c | 1 - app/src/main.c | 7 + app/src/mouse.c | 43 ----- app/src/mouse/Kconfig | 38 ++++ app/src/mouse/key_listener.c | 163 ++++++++++++++++++ app/src/mouse/main.c | 30 ++++ app/src/mouse/tick_listener.c | 106 ++++++++++++ app/tests/mouse-keys/mmv/events.patterns | 1 + .../mouse-keys/mmv/keycode_events.snapshot | 2 + app/tests/mouse-keys/mmv/native_posix.keymap | 26 +++ docs/docs/intro.md | 2 +- 30 files changed, 794 insertions(+), 63 deletions(-) create mode 100644 app/dts/behaviors/mouse_move.dtsi create mode 100644 app/dts/behaviors/mouse_scroll.dtsi create mode 100644 app/dts/bindings/behaviors/zmk,behavior-mouse-move.yaml create mode 100644 app/dts/bindings/behaviors/zmk,behavior-mouse-scroll.yaml create mode 100644 app/include/zmk/events/mouse_move_state_changed.h create mode 100644 app/include/zmk/events/mouse_scroll_state_changed.h create mode 100644 app/include/zmk/events/mouse_tick.h create mode 100644 app/src/behaviors/behavior_mouse_move.c create mode 100644 app/src/behaviors/behavior_mouse_scroll.c create mode 100644 app/src/events/mouse_move_state_changed.c create mode 100644 app/src/events/mouse_scroll_state_changed.c create mode 100644 app/src/events/mouse_tick.c delete mode 100644 app/src/mouse.c create mode 100644 app/src/mouse/Kconfig create mode 100644 app/src/mouse/key_listener.c create mode 100644 app/src/mouse/main.c create mode 100644 app/src/mouse/tick_listener.c create mode 100644 app/tests/mouse-keys/mmv/events.patterns create mode 100644 app/tests/mouse-keys/mmv/keycode_events.snapshot create mode 100644 app/tests/mouse-keys/mmv/native_posix.keymap diff --git a/app/CMakeLists.txt b/app/CMakeLists.txt index ab2e1502ac4..b3218ff0a2b 100644 --- a/app/CMakeLists.txt +++ b/app/CMakeLists.txt @@ -27,6 +27,9 @@ target_sources(app PRIVATE src/behavior.c) target_sources_ifdef(CONFIG_ZMK_KSCAN_SIDEBAND_BEHAVIORS app PRIVATE src/kscan_sideband_behaviors.c) target_sources(app PRIVATE src/matrix_transform.c) target_sources(app PRIVATE src/physical_layouts.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) @@ -37,6 +40,9 @@ 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(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) @@ -44,7 +50,7 @@ target_sources_ifdef(CONFIG_ZMK_EXT_POWER app PRIVATE src/behaviors/behavior_ext target_sources_ifdef(CONFIG_ZMK_BEHAVIOR_SOFT_OFF app PRIVATE src/behaviors/behavior_soft_off.c) if ((NOT CONFIG_ZMK_SPLIT) OR CONFIG_ZMK_SPLIT_ROLE_CENTRAL) target_sources(app PRIVATE src/hid.c) - target_sources_ifdef(CONFIG_ZMK_MOUSE app PRIVATE src/mouse.c) + target_sources_ifdef(CONFIG_ZMK_MOUSE app PRIVATE src/mouse/main.c) target_sources(app PRIVATE src/behaviors/behavior_key_press.c) target_sources_ifdef(CONFIG_ZMK_BEHAVIOR_KEY_TOGGLE app PRIVATE src/behaviors/behavior_key_toggle.c) target_sources(app PRIVATE src/behaviors/behavior_hold_tap.c) @@ -63,6 +69,8 @@ if ((NOT CONFIG_ZMK_SPLIT) OR CONFIG_ZMK_SPLIT_ROLE_CENTRAL) 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_BEHAVIOR_MOUSE_KEY_PRESS 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) diff --git a/app/Kconfig b/app/Kconfig index a45f2dc23f0..7250fcfcaf7 100644 --- a/app/Kconfig +++ b/app/Kconfig @@ -373,13 +373,7 @@ endif #Display/LED Options endmenu -menu "Mouse Options" - -config ZMK_MOUSE - bool "Enable ZMK mouse emulation" - -#Mouse Options -endmenu +rsource "src/mouse/Kconfig" menu "Power Management" diff --git a/app/dts/behaviors.dtsi b/app/dts/behaviors.dtsi index fde75271891..27bc78a6c2c 100644 --- a/app/dts/behaviors.dtsi +++ b/app/dts/behaviors.dtsi @@ -21,3 +21,6 @@ #include #include #include +#include +#include +#include diff --git a/app/dts/behaviors/mouse_move.dtsi b/app/dts/behaviors/mouse_move.dtsi new file mode 100644 index 00000000000..2e165f3a6e4 --- /dev/null +++ b/app/dts/behaviors/mouse_move.dtsi @@ -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>; + }; + }; +}; diff --git a/app/dts/behaviors/mouse_scroll.dtsi b/app/dts/behaviors/mouse_scroll.dtsi new file mode 100644 index 00000000000..f2cb86f6d88 --- /dev/null +++ b/app/dts/behaviors/mouse_scroll.dtsi @@ -0,0 +1,12 @@ +/ { + behaviors { + /omit-if-no-ref/ 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>; + }; + }; +}; diff --git a/app/dts/bindings/behaviors/zmk,behavior-mouse-move.yaml b/app/dts/bindings/behaviors/zmk,behavior-mouse-move.yaml new file mode 100644 index 00000000000..73ec34ec2db --- /dev/null +++ b/app/dts/bindings/behaviors/zmk,behavior-mouse-move.yaml @@ -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 diff --git a/app/dts/bindings/behaviors/zmk,behavior-mouse-scroll.yaml b/app/dts/bindings/behaviors/zmk,behavior-mouse-scroll.yaml new file mode 100644 index 00000000000..5a932bc5904 --- /dev/null +++ b/app/dts/bindings/behaviors/zmk,behavior-mouse-scroll.yaml @@ -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 diff --git a/app/include/dt-bindings/zmk/mouse.h b/app/include/dt-bindings/zmk/mouse.h index 582518aff7e..861f9a90712 100644 --- a/app/include/dt-bindings/zmk/mouse.h +++ b/app/include/dt-bindings/zmk/mouse.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 2020 The ZMK Contributors + * Copyright (c) 2023 The ZMK Contributors * * SPDX-License-Identifier: MIT */ @@ -22,3 +22,37 @@ #define MB4 BIT(3) #define MB5 BIT(4) + +#ifndef ZMK_MOUSE_DEFAULT_MOVE_VAL +#define ZMK_MOUSE_DEFAULT_MOVE_VAL 600 +#endif + +#ifndef ZMK_MOUSE_DEFAULT_SCRL_VAL +#define ZMK_MOUSE_DEFAULT_SCRL_VAL 10 +#endif + +/* Mouse move behavior */ +#define MOVE_Y(vert) ((vert)&0xFFFF) +#define MOVE_Y_DECODE(encoded) (int16_t)((encoded)&0x0000FFFF) +#define MOVE_X(hor) (((hor)&0xFFFF) << 16) +#define MOVE_X_DECODE(encoded) (int16_t)(((encoded)&0xFFFF0000) >> 16) + +#define MOVE(hor, vert) (MOVE_X(hor) + MOVE_Y(vert)) + +#define MOVE_UP MOVE_Y(-ZMK_MOUSE_DEFAULT_MOVE_VAL) +#define MOVE_DOWN MOVE_Y(ZMK_MOUSE_DEFAULT_MOVE_VAL) +#define MOVE_LEFT MOVE_X(-ZMK_MOUSE_DEFAULT_MOVE_VAL) +#define MOVE_RIGHT MOVE_X(ZMK_MOUSE_DEFAULT_MOVE_VAL) + +/* Mouse scroll behavior */ +#define SCRL_Y(vert) ((vert)&0xFFFF) +#define SCRL_Y_DECODE(encoded) (int16_t)((encoded)&0x0000FFFF) +#define SCRL_X(hor) (((hor)&0xFFFF) << 16) +#define SCRL_X_DECODE(encoded) (int16_t)(((encoded)&0xFFFF0000) >> 16) + +#define SCROLL(hor, vert) (SCRL_X(hor) + SCRL_Y(vert)) + +#define SCRL_UP SCRL_Y(ZMK_MOUSE_DEFAULT_SCRL_VAL) +#define SCRL_DOWN SCRL_Y(-ZMK_MOUSE_DEFAULT_SCRL_VAL) +#define SCRL_LEFT SCRL_X(-ZMK_MOUSE_DEFAULT_SCRL_VAL) +#define SCRL_RIGHT SCRL_X(ZMK_MOUSE_DEFAULT_SCRL_VAL) diff --git a/app/include/zmk/events/mouse_move_state_changed.h b/app/include/zmk/events/mouse_move_state_changed.h new file mode 100644 index 00000000000..d1ebad87e0e --- /dev/null +++ b/app/include/zmk/events/mouse_move_state_changed.h @@ -0,0 +1,33 @@ + +/* + * Copyright (c) 2020 The ZMK Contributors + * + * SPDX-License-Identifier: MIT + */ + +#pragma once + +#include +#include +#include + +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_X_DECODE(encoded), + .y = MOVE_Y_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}); +} diff --git a/app/include/zmk/events/mouse_scroll_state_changed.h b/app/include/zmk/events/mouse_scroll_state_changed.h new file mode 100644 index 00000000000..bf03308f1d5 --- /dev/null +++ b/app/include/zmk/events/mouse_scroll_state_changed.h @@ -0,0 +1,34 @@ + +/* + * Copyright (c) 2020 The ZMK Contributors + * + * SPDX-License-Identifier: MIT + */ + +#pragma once + +#include +#include +#include +#include + +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 = SCRL_X_DECODE(encoded), + .y = SCRL_Y_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}); +} diff --git a/app/include/zmk/events/mouse_tick.h b/app/include/zmk/events/mouse_tick.h new file mode 100644 index 00000000000..2f69b04564f --- /dev/null +++ b/app/include/zmk/events/mouse_tick.h @@ -0,0 +1,39 @@ + +/* + * Copyright (c) 2020 The ZMK Contributors + * + * SPDX-License-Identifier: MIT + */ + +#pragma once + +#include +#include +#include +#include + +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(), + }); +} diff --git a/app/include/zmk/hid.h b/app/include/zmk/hid.h index 41f559b5189..aa59f369efe 100644 --- a/app/include/zmk/hid.h +++ b/app/include/zmk/hid.h @@ -61,6 +61,10 @@ #define ZMK_HID_REPORT_ID_CONSUMER 0x02 #define ZMK_HID_REPORT_ID_MOUSE 0x03 +// Needed until Zephyr offers a 2 byte usage macro +#define HID_USAGE16(idx) \ + HID_ITEM(HID_ITEM_TAG_USAGE, HID_ITEM_TYPE_LOCAL, 2), (idx & 0xFF), (idx >> 8 & 0xFF) + static const uint8_t zmk_hid_report_desc[] = { HID_USAGE_PAGE(HID_USAGE_GEN_DESKTOP), HID_USAGE(HID_USAGE_GD_KEYBOARD), @@ -169,11 +173,18 @@ static const uint8_t zmk_hid_report_desc[] = { HID_USAGE(HID_USAGE_GD_X), HID_USAGE(HID_USAGE_GD_Y), HID_USAGE(HID_USAGE_GD_WHEEL), - HID_LOGICAL_MIN8(-0x7F), - HID_LOGICAL_MAX8(0x7F), - HID_REPORT_SIZE(0x08), + HID_LOGICAL_MIN16(0xFF, -0x7F), + HID_LOGICAL_MAX16(0xFF, 0x7F), + HID_REPORT_SIZE(0x10), HID_REPORT_COUNT(0x03), HID_INPUT(ZMK_HID_MAIN_VAL_DATA | ZMK_HID_MAIN_VAL_VAR | ZMK_HID_MAIN_VAL_REL), + HID_USAGE_PAGE(HID_USAGE_CONSUMER), + HID_USAGE16(HID_USAGE_CONSUMER_AC_PAN), + HID_LOGICAL_MIN16(0xFF, -0x7F), + HID_LOGICAL_MAX16(0xFF, 0x7F), + HID_REPORT_SIZE(0x08), + HID_REPORT_COUNT(0x01), + HID_INPUT(ZMK_HID_MAIN_VAL_DATA | ZMK_HID_MAIN_VAL_VAR | ZMK_HID_MAIN_VAL_REL), HID_END_COLLECTION, HID_END_COLLECTION, #endif // IS_ENABLED(CONFIG_ZMK_MOUSE) @@ -242,9 +253,10 @@ struct zmk_hid_consumer_report { #if IS_ENABLED(CONFIG_ZMK_MOUSE) struct zmk_hid_mouse_report_body { zmk_mouse_button_flags_t buttons; - int8_t d_x; - int8_t d_y; - int8_t d_wheel; + int16_t d_x; + int16_t d_y; + int16_t d_scroll_y; + int16_t d_scroll_x; } __packed; struct zmk_hid_mouse_report { @@ -285,6 +297,10 @@ 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(void); #endif // IS_ENABLED(CONFIG_ZMK_MOUSE) diff --git a/app/include/zmk/mouse.h b/app/include/zmk/mouse.h index d873f15689a..1944ea47aa7 100644 --- a/app/include/zmk/mouse.h +++ b/app/include/zmk/mouse.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 2021 The ZMK Contributors + * Copyright (c) 2023 The ZMK Contributors * * SPDX-License-Identifier: MIT */ @@ -10,3 +10,20 @@ typedef uint8_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(); diff --git a/app/src/behaviors/behavior_mouse_move.c b/app/src/behaviors/behavior_mouse_move.c new file mode 100644 index 00000000000..0d591f00324 --- /dev/null +++ b/app/src/behaviors/behavior_mouse_move.c @@ -0,0 +1,57 @@ +/* + * Copyright (c) 2021 The ZMK Contributors + * + * SPDX-License-Identifier: MIT + */ + +#define DT_DRV_COMPAT zmk_behavior_mouse_move + +#include +#include +#include + +#include +#include +#include +#include + +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) */ diff --git a/app/src/behaviors/behavior_mouse_scroll.c b/app/src/behaviors/behavior_mouse_scroll.c new file mode 100644 index 00000000000..d71671724d0 --- /dev/null +++ b/app/src/behaviors/behavior_mouse_scroll.c @@ -0,0 +1,58 @@ +/* + * Copyright (c) 2021 The ZMK Contributors + * + * SPDX-License-Identifier: MIT + */ + +#define DT_DRV_COMPAT zmk_behavior_mouse_scroll + +#include +#include +#include + +#include +#include +#include +#include +#include + +LOG_MODULE_DECLARE(zmk, CONFIG_ZMK_LOG_LEVEL); + +#if DT_HAS_COMPAT_STATUS_OKAY(DT_DRV_COMPAT) + +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) */ diff --git a/app/src/events/mouse_move_state_changed.c b/app/src/events/mouse_move_state_changed.c new file mode 100644 index 00000000000..07a789b24ef --- /dev/null +++ b/app/src/events/mouse_move_state_changed.c @@ -0,0 +1,10 @@ +/* + * Copyright (c) 2020 The ZMK Contributors + * + * SPDX-License-Identifier: MIT + */ + +#include +#include + +ZMK_EVENT_IMPL(zmk_mouse_move_state_changed); diff --git a/app/src/events/mouse_scroll_state_changed.c b/app/src/events/mouse_scroll_state_changed.c new file mode 100644 index 00000000000..f6216d39d0c --- /dev/null +++ b/app/src/events/mouse_scroll_state_changed.c @@ -0,0 +1,10 @@ +/* + * Copyright (c) 2020 The ZMK Contributors + * + * SPDX-License-Identifier: MIT + */ + +#include +#include + +ZMK_EVENT_IMPL(zmk_mouse_scroll_state_changed); diff --git a/app/src/events/mouse_tick.c b/app/src/events/mouse_tick.c new file mode 100644 index 00000000000..846b3faee89 --- /dev/null +++ b/app/src/events/mouse_tick.c @@ -0,0 +1,10 @@ +/* + * Copyright (c) 2020 The ZMK Contributors + * + * SPDX-License-Identifier: MIT + */ + +#include +#include + +ZMK_EVENT_IMPL(zmk_mouse_tick); diff --git a/app/src/hid.c b/app/src/hid.c index 582db6763de..cfa8fb7b1ea 100644 --- a/app/src/hid.c +++ b/app/src/hid.c @@ -27,8 +27,9 @@ static uint8_t keys_held = 0; #if IS_ENABLED(CONFIG_ZMK_MOUSE) -static struct zmk_hid_mouse_report mouse_report = {.report_id = ZMK_HID_REPORT_ID_MOUSE, - .body = {.buttons = 0}}; +static struct zmk_hid_mouse_report mouse_report = { + .report_id = ZMK_HID_REPORT_ID_MOUSE, + .body = {.buttons = 0, .d_x = 0, .d_y = 0, .d_scroll_y = 0}}; #endif // IS_ENABLED(CONFIG_ZMK_MOUSE) @@ -430,6 +431,34 @@ int zmk_hid_mouse_buttons_release(zmk_mouse_button_flags_t buttons) { } return 0; } + +void zmk_hid_mouse_movement_set(int16_t x, int16_t y) { + mouse_report.body.d_x = x; + mouse_report.body.d_y = y; + LOG_DBG("Mouse movement set to 0x%02X 0x%02X ", mouse_report.body.d_x, mouse_report.body.d_y); +} + +void zmk_hid_mouse_movement_update(int16_t x, int16_t y) { + mouse_report.body.d_x += x; + mouse_report.body.d_y += y; + LOG_DBG("Mouse movement updated to 0x%02X 0x%02X ", mouse_report.body.d_x, + mouse_report.body.d_y); +} + +void zmk_hid_mouse_scroll_set(int8_t x, int8_t y) { + mouse_report.body.d_scroll_x = x; + mouse_report.body.d_scroll_y = y; + LOG_DBG("Mouse scroll set to X: 0x%02X Y: 0x%02X", mouse_report.body.d_scroll_x, + mouse_report.body.d_scroll_y); +} + +void zmk_hid_mouse_scroll_update(int8_t x, int8_t y) { + mouse_report.body.d_scroll_x += x; + mouse_report.body.d_scroll_y += y; + LOG_DBG("Mouse scroll updated to X: 0x%02X Y: 0x%02X", mouse_report.body.d_scroll_x, + mouse_report.body.d_scroll_y); +} + void zmk_hid_mouse_clear(void) { memset(&mouse_report.body, 0, sizeof(mouse_report.body)); } #endif // IS_ENABLED(CONFIG_ZMK_MOUSE) diff --git a/app/src/hog.c b/app/src/hog.c index 82fafc29cd7..f5d4377aaf5 100644 --- a/app/src/hog.c +++ b/app/src/hog.c @@ -380,7 +380,6 @@ int zmk_hog_send_mouse_report(struct zmk_hid_mouse_report_body *report) { return 0; }; - #endif // IS_ENABLED(CONFIG_ZMK_MOUSE) static int zmk_hog_init(void) { diff --git a/app/src/main.c b/app/src/main.c index 60df1a45d83..a6ea2abd109 100644 --- a/app/src/main.c +++ b/app/src/main.c @@ -14,6 +14,10 @@ LOG_MODULE_REGISTER(zmk, CONFIG_ZMK_LOG_LEVEL); #include +#ifdef CONFIG_ZMK_MOUSE +#include +#endif /* CONFIG_ZMK_MOUSE */ + int main(void) { LOG_INF("Welcome to ZMK!\n"); @@ -26,5 +30,8 @@ int main(void) { zmk_display_init(); #endif /* CONFIG_ZMK_DISPLAY */ +#ifdef CONFIG_ZMK_MOUSE + zmk_mouse_init(); +#endif /* CONFIG_ZMK_MOUSE */ return 0; } diff --git a/app/src/mouse.c b/app/src/mouse.c deleted file mode 100644 index c1b9ac0261e..00000000000 --- a/app/src/mouse.c +++ /dev/null @@ -1,43 +0,0 @@ -/* - * Copyright (c) 2021 The ZMK Contributors - * - * SPDX-License-Identifier: MIT - */ - -#include -#include - -LOG_MODULE_DECLARE(zmk, CONFIG_ZMK_LOG_LEVEL); - -#include -#include -#include -#include - -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_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); diff --git a/app/src/mouse/Kconfig b/app/src/mouse/Kconfig new file mode 100644 index 00000000000..0b299844d17 --- /dev/null +++ b/app/src/mouse/Kconfig @@ -0,0 +1,38 @@ +# Copyright (c) 2023 The ZMK Contributors +# SPDX-License-Identifier: MIT + +menuconfig ZMK_MOUSE + bool "Mouse Emulation" + +if ZMK_MOUSE + +config ZMK_MOUSE_TICK_DURATION + int "Mouse tick duration in ms" + default 8 + + +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 diff --git a/app/src/mouse/key_listener.c b/app/src/mouse/key_listener.c new file mode 100644 index 00000000000..91c4c5ce215 --- /dev/null +++ b/app/src/mouse/key_listener.c @@ -0,0 +1,163 @@ +/* + * Copyright (c) 2021 The ZMK Contributors + * + * SPDX-License-Identifier: MIT + */ + +#include +#include + +LOG_MODULE_DECLARE(zmk, CONFIG_ZMK_LOG_LEVEL); + +#include +#include +#include +#include +#include +#include +#include +#include + +#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) */ diff --git a/app/src/mouse/main.c b/app/src/mouse/main.c new file mode 100644 index 00000000000..e5b5252f8de --- /dev/null +++ b/app/src/mouse/main.c @@ -0,0 +1,30 @@ +/* + * Copyright (c) 2020 The ZMK Contributors + * + * SPDX-License-Identifier: MIT + */ + +#include +#include + +#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; +} \ No newline at end of file diff --git a/app/src/mouse/tick_listener.c b/app/src/mouse/tick_listener.c new file mode 100644 index 00000000000..f84b29f72ff --- /dev/null +++ b/app/src/mouse/tick_listener.c @@ -0,0 +1,106 @@ +/* + * Copyright (c) 2020 The ZMK Contributors + * + * SPDX-License-Identifier: MIT + */ + +#include + +LOG_MODULE_DECLARE(zmk, CONFIG_ZMK_LOG_LEVEL); + +#include +#include +#include +#include +#include + +#include // 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 +#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) */ diff --git a/app/tests/mouse-keys/mmv/events.patterns b/app/tests/mouse-keys/mmv/events.patterns new file mode 100644 index 00000000000..833100f6ac4 --- /dev/null +++ b/app/tests/mouse-keys/mmv/events.patterns @@ -0,0 +1 @@ +s/.*hid_listener_keycode_//p \ No newline at end of file diff --git a/app/tests/mouse-keys/mmv/keycode_events.snapshot b/app/tests/mouse-keys/mmv/keycode_events.snapshot new file mode 100644 index 00000000000..259501ba3d9 --- /dev/null +++ b/app/tests/mouse-keys/mmv/keycode_events.snapshot @@ -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 diff --git a/app/tests/mouse-keys/mmv/native_posix.keymap b/app/tests/mouse-keys/mmv/native_posix.keymap new file mode 100644 index 00000000000..dbb3f014c9f --- /dev/null +++ b/app/tests/mouse-keys/mmv/native_posix.keymap @@ -0,0 +1,26 @@ +#include +#include +#include +#include + +/ { + 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) + >; +}; \ No newline at end of file diff --git a/docs/docs/intro.md b/docs/docs/intro.md index e11eda71f02..c0c70ab0b3f 100644 --- a/docs/docs/intro.md +++ b/docs/docs/intro.md @@ -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) | ✅ | ✅ | | From 04022d24858026a1e0c6b80baed2a31bb3a5fe4e Mon Sep 17 00:00:00 2001 From: Cem Aksoylar Date: Sun, 15 Oct 2023 23:27:45 -0700 Subject: [PATCH 11/23] feat(mouse): Split move/scroll & x/y acceleration --- app/include/zmk/events/mouse_tick.h | 6 +-- app/include/zmk/mouse.h | 7 +++ app/src/mouse/key_listener.c | 70 ++++++++++++++++++++++------- app/src/mouse/tick_listener.c | 21 +++++---- 4 files changed, 77 insertions(+), 27 deletions(-) diff --git a/app/include/zmk/events/mouse_tick.h b/app/include/zmk/events/mouse_tick.h index 2f69b04564f..75a041e96b4 100644 --- a/app/include/zmk/events/mouse_tick.h +++ b/app/include/zmk/events/mouse_tick.h @@ -17,7 +17,7 @@ struct zmk_mouse_tick { struct vector2d max_scroll; struct mouse_config move_config; struct mouse_config scroll_config; - int64_t *start_time; + struct mouse_times start_times; int64_t timestamp; }; @@ -27,13 +27,13 @@ static inline struct zmk_mouse_tick_event *zmk_mouse_tick(struct vector2d max_mo struct vector2d max_scroll, struct mouse_config move_config, struct mouse_config scroll_config, - int64_t *movement_start) { + struct mouse_times 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, + .start_times = movement_start, .timestamp = k_uptime_get(), }); } diff --git a/app/include/zmk/mouse.h b/app/include/zmk/mouse.h index 1944ea47aa7..c2ae6252df5 100644 --- a/app/include/zmk/mouse.h +++ b/app/include/zmk/mouse.h @@ -25,5 +25,12 @@ struct vector2d { float y; }; +struct mouse_times { + uint64_t m_x; + uint64_t m_y; + uint64_t s_x; + uint64_t s_y; +}; + struct k_work_q *zmk_mouse_work_q(); int zmk_mouse_init(); diff --git a/app/src/mouse/key_listener.c b/app/src/mouse/key_listener.c index 91c4c5ce215..bd06efebd9f 100644 --- a/app/src/mouse/key_listener.c +++ b/app/src/mouse/key_listener.c @@ -23,7 +23,7 @@ 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; +static struct mouse_times start_times = (struct mouse_times){0}; bool equals(const struct mouse_config *one, const struct mouse_config *other) { return one->delay_ms == other->delay_ms && @@ -34,7 +34,7 @@ bool equals(const struct mouse_config *one, const struct mouse_config *other) { static void clear_mouse_state(struct k_work *work) { move_speed = (struct vector2d){0}; scroll_speed = (struct vector2d){0}; - start_time = 0; + start_times = (struct mouse_times){0}; zmk_hid_mouse_movement_set(0, 0); zmk_hid_mouse_scroll_set(0, 0); LOG_DBG("Clearing state"); @@ -51,7 +51,7 @@ static void mouse_tick_timer_handler(struct k_work *work) { 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_mouse_tick(move_speed, scroll_speed, move_config, scroll_config, start_times)); zmk_endpoints_send_mouse_report(); } @@ -64,21 +64,59 @@ void mouse_timer_cb(struct k_timer *dummy) { K_TIMER_DEFINE(mouse_timer, mouse_timer_cb, mouse_clear_cb); -static int mouse_timer_ref_count = 0; +static struct { + int m_x; + int m_y; + int s_x; + int s_y; +} mouse_timer_ref_counts = {0, 0, 0, 0}; -void mouse_timer_ref() { - if (mouse_timer_ref_count == 0) { - start_time = k_uptime_get(); +void mouse_timer_ref(bool m_x, bool m_y, bool s_x, bool s_y) { + if (m_x && mouse_timer_ref_counts.m_x == 0) { + start_times.m_x = k_uptime_get(); + } + if (m_y && mouse_timer_ref_counts.m_y == 0) { + start_times.m_y = k_uptime_get(); + } + if (s_x && mouse_timer_ref_counts.s_x == 0) { + start_times.s_x = k_uptime_get(); + } + if (s_y && mouse_timer_ref_counts.s_y == 0) { + start_times.s_y = k_uptime_get(); + } + if (mouse_timer_ref_counts.m_x == 0 && mouse_timer_ref_counts.m_y == 0 && + mouse_timer_ref_counts.s_x == 0 && mouse_timer_ref_counts.s_y == 0) { k_timer_start(&mouse_timer, K_NO_WAIT, K_MSEC(CONFIG_ZMK_MOUSE_TICK_DURATION)); } - mouse_timer_ref_count += 1; + if (m_x) { + mouse_timer_ref_counts.m_x++; + } + if (m_y) { + mouse_timer_ref_counts.m_y++; + } + if (s_x) { + mouse_timer_ref_counts.s_x++; + } + if (s_y) { + mouse_timer_ref_counts.s_y++; + } } -void mouse_timer_unref() { - if (mouse_timer_ref_count > 0) { - mouse_timer_ref_count--; +void mouse_timer_unref(bool m_x, bool m_y, bool s_x, bool s_y) { + if (m_x && mouse_timer_ref_counts.m_x > 0) { + mouse_timer_ref_counts.m_x--; + } + if (m_y && mouse_timer_ref_counts.m_y > 0) { + mouse_timer_ref_counts.m_y--; + } + if (s_x && mouse_timer_ref_counts.s_x > 0) { + mouse_timer_ref_counts.s_x--; + } + if (s_y && mouse_timer_ref_counts.s_y > 0) { + mouse_timer_ref_counts.s_y--; } - if (mouse_timer_ref_count == 0) { + if (mouse_timer_ref_counts.m_x == 0 && mouse_timer_ref_counts.m_y == 0 && + mouse_timer_ref_counts.s_x == 0 && mouse_timer_ref_counts.s_y == 0) { k_timer_stop(&mouse_timer); } } @@ -86,25 +124,25 @@ void mouse_timer_unref() { 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(); + mouse_timer_ref(ev->max_speed.x != 0, ev->max_speed.y != 0, false, false); } 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(); + mouse_timer_unref(ev->max_speed.x != 0, ev->max_speed.y != 0, false, false); } 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(); + mouse_timer_ref(false, false, ev->max_speed.x != 0, ev->max_speed.y != 0); } 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(); + mouse_timer_unref(false, false, ev->max_speed.x != 0, ev->max_speed.y != 0); } static void listener_mouse_button_pressed(const struct zmk_mouse_button_state_changed *ev) { diff --git a/app/src/mouse/tick_listener.c b/app/src/mouse/tick_listener.c index f84b29f72ff..90aaf1119dd 100644 --- a/app/src/mouse/tick_listener.c +++ b/app/src/mouse/tick_listener.c @@ -61,17 +61,18 @@ static void track_remainder(float *move, float *remainder) { static struct vector2d update_movement(struct vector2d *remainder, const struct mouse_config *config, struct vector2d max_speed, - int64_t now, int64_t *start_time) { + int64_t now, int64_t start_time_x, int64_t start_time_y) { 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); + int64_t move_duration_x = ms_since_start(start_time_x, now, config->delay_ms); + int64_t move_duration_y = ms_since_start(start_time_y, 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, + .x = speed(config, max_speed.x, move_duration_x) * CONFIG_ZMK_MOUSE_TICK_DURATION / 1000, + .y = speed(config, max_speed.y, move_duration_y) * CONFIG_ZMK_MOUSE_TICK_DURATION / 1000, }; track_remainder(&(move.x), &(remainder->x)); @@ -81,12 +82,16 @@ static struct vector2d update_movement(struct vector2d *remainder, } 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); + LOG_INF("tick start times: %lld %lld %lld %lld", tick->start_times.m_x, tick->start_times.m_y, + tick->start_times.s_x, tick->start_times.s_y); + struct vector2d move = + update_movement(&move_remainder, &(tick->move_config), tick->max_move, tick->timestamp, + tick->start_times.m_x, tick->start_times.m_y); 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); + struct vector2d scroll = + update_movement(&scroll_remainder, &(tick->scroll_config), tick->max_scroll, + tick->timestamp, tick->start_times.s_x, tick->start_times.s_y); zmk_hid_mouse_scroll_update((int8_t)CLAMP(scroll.x, INT8_MIN, INT8_MAX), (int8_t)CLAMP(scroll.y, INT8_MIN, INT8_MAX)); } From c26b4817b16648f660925ac717145e1dd8af8c77 Mon Sep 17 00:00:00 2001 From: Cem Aksoylar Date: Wed, 15 Nov 2023 23:41:26 -0800 Subject: [PATCH 12/23] fix(mouse): Bump up default mouse tick duration to prevent crashes --- app/src/mouse/Kconfig | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/src/mouse/Kconfig b/app/src/mouse/Kconfig index 0b299844d17..f44d2d1db1b 100644 --- a/app/src/mouse/Kconfig +++ b/app/src/mouse/Kconfig @@ -8,7 +8,7 @@ if ZMK_MOUSE config ZMK_MOUSE_TICK_DURATION int "Mouse tick duration in ms" - default 8 + default 16 choice ZMK_MOUSE_WORK_QUEUE From e38fd7887842f8085849828eebe0d7262c6618ec Mon Sep 17 00:00:00 2001 From: Peter Johanson Date: Tue, 21 Nov 2023 00:30:25 +0000 Subject: [PATCH 13/23] refactor: Use input system for pointer events. --- app/CMakeLists.txt | 10 +- app/Kconfig.behaviors | 6 + app/dts/behaviors/mouse_move.dtsi | 10 +- app/dts/behaviors/mouse_scroll.dtsi | 10 +- .../zmk,behavior-input-two-axis.yaml | 21 ++ .../behaviors/zmk,behavior-mouse-move.yaml | 13 - .../behaviors/zmk,behavior-mouse-scroll.yaml | 13 - app/include/dt-bindings/zmk/mouse.h | 16 +- .../zmk/events/mouse_move_state_changed.h | 33 --- .../zmk/events/mouse_scroll_state_changed.h | 34 --- app/include/zmk/events/mouse_tick.h | 39 --- app/include/zmk/mouse.h | 24 +- app/src/behaviors/behavior_input_two_axis.c | 270 ++++++++++++++++++ app/src/behaviors/behavior_mouse_key_press.c | 25 +- app/src/behaviors/behavior_mouse_move.c | 57 ---- app/src/behaviors/behavior_mouse_scroll.c | 58 ---- app/src/events/mouse_move_state_changed.c | 10 - app/src/events/mouse_scroll_state_changed.c | 10 - app/src/events/mouse_tick.c | 10 - app/src/hid.c | 14 +- app/src/mouse/Kconfig | 2 + app/src/mouse/hid_input_listener.c | 72 +++++ app/src/mouse/key_listener.c | 201 ------------- app/src/mouse/main.c | 2 +- app/src/mouse/tick_listener.c | 111 ------- app/tests/mouse-keys/mmv/events.patterns | 1 - .../mouse-keys/mmv/keycode_events.snapshot | 2 - .../mouse-move/move_diagonal/events.patterns | 1 + .../move_diagonal/keycode_events.snapshot | 22 ++ .../move_diagonal/native_posix_64.keymap | 28 ++ .../mouse-move/move_x/events.patterns | 1 + .../mouse-move/move_x/keycode_events.snapshot | 27 ++ .../mouse-move/move_x/native_posix_64.keymap | 28 ++ .../mouse-move/move_y/events.patterns | 1 + .../mouse-move/move_y/keycode_events.snapshot | 27 ++ .../move_y/native_posix_64.keymap} | 4 +- 36 files changed, 556 insertions(+), 657 deletions(-) create mode 100644 app/dts/bindings/behaviors/zmk,behavior-input-two-axis.yaml delete mode 100644 app/dts/bindings/behaviors/zmk,behavior-mouse-move.yaml delete mode 100644 app/dts/bindings/behaviors/zmk,behavior-mouse-scroll.yaml delete mode 100644 app/include/zmk/events/mouse_move_state_changed.h delete mode 100644 app/include/zmk/events/mouse_scroll_state_changed.h delete mode 100644 app/include/zmk/events/mouse_tick.h create mode 100644 app/src/behaviors/behavior_input_two_axis.c delete mode 100644 app/src/behaviors/behavior_mouse_move.c delete mode 100644 app/src/behaviors/behavior_mouse_scroll.c delete mode 100644 app/src/events/mouse_move_state_changed.c delete mode 100644 app/src/events/mouse_scroll_state_changed.c delete mode 100644 app/src/events/mouse_tick.c create mode 100644 app/src/mouse/hid_input_listener.c delete mode 100644 app/src/mouse/key_listener.c delete mode 100644 app/src/mouse/tick_listener.c delete mode 100644 app/tests/mouse-keys/mmv/events.patterns delete mode 100644 app/tests/mouse-keys/mmv/keycode_events.snapshot create mode 100644 app/tests/mouse-keys/mouse-move/move_diagonal/events.patterns create mode 100644 app/tests/mouse-keys/mouse-move/move_diagonal/keycode_events.snapshot create mode 100644 app/tests/mouse-keys/mouse-move/move_diagonal/native_posix_64.keymap create mode 100644 app/tests/mouse-keys/mouse-move/move_x/events.patterns create mode 100644 app/tests/mouse-keys/mouse-move/move_x/keycode_events.snapshot create mode 100644 app/tests/mouse-keys/mouse-move/move_x/native_posix_64.keymap create mode 100644 app/tests/mouse-keys/mouse-move/move_y/events.patterns create mode 100644 app/tests/mouse-keys/mouse-move/move_y/keycode_events.snapshot rename app/tests/mouse-keys/{mmv/native_posix.keymap => mouse-move/move_y/native_posix_64.keymap} (79%) diff --git a/app/CMakeLists.txt b/app/CMakeLists.txt index b3218ff0a2b..822383d2a0d 100644 --- a/app/CMakeLists.txt +++ b/app/CMakeLists.txt @@ -27,9 +27,7 @@ target_sources(app PRIVATE src/behavior.c) target_sources_ifdef(CONFIG_ZMK_KSCAN_SIDEBAND_BEHAVIORS app PRIVATE src/kscan_sideband_behaviors.c) target_sources(app PRIVATE src/matrix_transform.c) target_sources(app PRIVATE src/physical_layouts.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) @@ -39,10 +37,6 @@ target_sources_ifdef(CONFIG_ZMK_GPIO_KEY_WAKEUP_TRIGGER app PRIVATE src/gpio_key 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(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) @@ -51,6 +45,7 @@ target_sources_ifdef(CONFIG_ZMK_BEHAVIOR_SOFT_OFF app PRIVATE src/behaviors/beha if ((NOT CONFIG_ZMK_SPLIT) OR CONFIG_ZMK_SPLIT_ROLE_CENTRAL) target_sources(app PRIVATE src/hid.c) target_sources_ifdef(CONFIG_ZMK_MOUSE app PRIVATE src/mouse/main.c) + target_sources_ifdef(CONFIG_ZMK_MOUSE app PRIVATE src/mouse/hid_input_listener.c) target_sources(app PRIVATE src/behaviors/behavior_key_press.c) target_sources_ifdef(CONFIG_ZMK_BEHAVIOR_KEY_TOGGLE app PRIVATE src/behaviors/behavior_key_toggle.c) target_sources(app PRIVATE src/behaviors/behavior_hold_tap.c) @@ -69,8 +64,7 @@ if ((NOT CONFIG_ZMK_SPLIT) OR CONFIG_ZMK_SPLIT_ROLE_CENTRAL) 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_BEHAVIOR_MOUSE_KEY_PRESS 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_ifdef(CONFIG_ZMK_BEHAVIOR_INPUT_TWO_AXIS app PRIVATE src/behaviors/behavior_input_two_axis.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) diff --git a/app/Kconfig.behaviors b/app/Kconfig.behaviors index d3f4537ec22..3aa256a9475 100644 --- a/app/Kconfig.behaviors +++ b/app/Kconfig.behaviors @@ -56,6 +56,12 @@ config ZMK_BEHAVIOR_SOFT_OFF default y depends on DT_HAS_ZMK_BEHAVIOR_SOFT_OFF_ENABLED && ZMK_PM_SOFT_OFF +config ZMK_BEHAVIOR_INPUT_TWO_AXIS + bool + default y + depends on DT_HAS_ZMK_BEHAVIOR_INPUT_TWO_AXIS_ENABLED + imply ZMK_MOUSE + config ZMK_BEHAVIOR_SENSOR_ROTATE_COMMON bool diff --git a/app/dts/behaviors/mouse_move.dtsi b/app/dts/behaviors/mouse_move.dtsi index 2e165f3a6e4..0bfc87d04fd 100644 --- a/app/dts/behaviors/mouse_move.dtsi +++ b/app/dts/behaviors/mouse_move.dtsi @@ -1,10 +1,12 @@ +#include + / { behaviors { - /omit-if-no-ref/ mmv: behavior_mouse_move { - compatible = "zmk,behavior-mouse-move"; - label = "MOUSE_MOVE"; + /omit-if-no-ref/ mmv: mouse_move { + compatible = "zmk,behavior-input-two-axis"; #binding-cells = <1>; - delay-ms = <0>; + x-input-code = ; + y-input-code = ; time-to-max-speed-ms = <300>; acceleration-exponent = <1>; }; diff --git a/app/dts/behaviors/mouse_scroll.dtsi b/app/dts/behaviors/mouse_scroll.dtsi index f2cb86f6d88..69c48feeea3 100644 --- a/app/dts/behaviors/mouse_scroll.dtsi +++ b/app/dts/behaviors/mouse_scroll.dtsi @@ -1,10 +1,12 @@ +#include + / { behaviors { - /omit-if-no-ref/ msc: behavior_mouse_scroll { - compatible = "zmk,behavior-mouse-scroll"; - label = "MOUSE_SCROLL"; + /omit-if-no-ref/ msc: mouse_scroll { + compatible = "zmk,behavior-input-two-axis"; #binding-cells = <1>; - delay-ms = <0>; + x-input-code = ; + y-input-code = ; time-to-max-speed-ms = <300>; acceleration-exponent = <0>; }; diff --git a/app/dts/bindings/behaviors/zmk,behavior-input-two-axis.yaml b/app/dts/bindings/behaviors/zmk,behavior-input-two-axis.yaml new file mode 100644 index 00000000000..820ee7035b8 --- /dev/null +++ b/app/dts/bindings/behaviors/zmk,behavior-input-two-axis.yaml @@ -0,0 +1,21 @@ +description: Two axis input behavior + +compatible: "zmk,behavior-input-two-axis" + +include: one_param.yaml + +properties: + x-input-code: + type: int + required: true + y-input-code: + type: int + required: true + delay-ms: + type: int + time-to-max-speed-ms: + type: int + required: true + acceleration-exponent: + type: int + default: 1 diff --git a/app/dts/bindings/behaviors/zmk,behavior-mouse-move.yaml b/app/dts/bindings/behaviors/zmk,behavior-mouse-move.yaml deleted file mode 100644 index 73ec34ec2db..00000000000 --- a/app/dts/bindings/behaviors/zmk,behavior-mouse-move.yaml +++ /dev/null @@ -1,13 +0,0 @@ -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 diff --git a/app/dts/bindings/behaviors/zmk,behavior-mouse-scroll.yaml b/app/dts/bindings/behaviors/zmk,behavior-mouse-scroll.yaml deleted file mode 100644 index 5a932bc5904..00000000000 --- a/app/dts/bindings/behaviors/zmk,behavior-mouse-scroll.yaml +++ /dev/null @@ -1,13 +0,0 @@ -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 diff --git a/app/include/dt-bindings/zmk/mouse.h b/app/include/dt-bindings/zmk/mouse.h index 861f9a90712..ea34e1243f1 100644 --- a/app/include/dt-bindings/zmk/mouse.h +++ b/app/include/dt-bindings/zmk/mouse.h @@ -44,15 +44,7 @@ #define MOVE_LEFT MOVE_X(-ZMK_MOUSE_DEFAULT_MOVE_VAL) #define MOVE_RIGHT MOVE_X(ZMK_MOUSE_DEFAULT_MOVE_VAL) -/* Mouse scroll behavior */ -#define SCRL_Y(vert) ((vert)&0xFFFF) -#define SCRL_Y_DECODE(encoded) (int16_t)((encoded)&0x0000FFFF) -#define SCRL_X(hor) (((hor)&0xFFFF) << 16) -#define SCRL_X_DECODE(encoded) (int16_t)(((encoded)&0xFFFF0000) >> 16) - -#define SCROLL(hor, vert) (SCRL_X(hor) + SCRL_Y(vert)) - -#define SCRL_UP SCRL_Y(ZMK_MOUSE_DEFAULT_SCRL_VAL) -#define SCRL_DOWN SCRL_Y(-ZMK_MOUSE_DEFAULT_SCRL_VAL) -#define SCRL_LEFT SCRL_X(-ZMK_MOUSE_DEFAULT_SCRL_VAL) -#define SCRL_RIGHT SCRL_X(ZMK_MOUSE_DEFAULT_SCRL_VAL) +#define SCRL_UP MOVE_Y(ZMK_MOUSE_DEFAULT_SCRL_VAL) +#define SCRL_DOWN MOVE_Y(-ZMK_MOUSE_DEFAULT_SCRL_VAL) +#define SCRL_LEFT MOVE_X(-ZMK_MOUSE_DEFAULT_SCRL_VAL) +#define SCRL_RIGHT MOVE_X(ZMK_MOUSE_DEFAULT_SCRL_VAL) diff --git a/app/include/zmk/events/mouse_move_state_changed.h b/app/include/zmk/events/mouse_move_state_changed.h deleted file mode 100644 index d1ebad87e0e..00000000000 --- a/app/include/zmk/events/mouse_move_state_changed.h +++ /dev/null @@ -1,33 +0,0 @@ - -/* - * Copyright (c) 2020 The ZMK Contributors - * - * SPDX-License-Identifier: MIT - */ - -#pragma once - -#include -#include -#include - -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_X_DECODE(encoded), - .y = MOVE_Y_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}); -} diff --git a/app/include/zmk/events/mouse_scroll_state_changed.h b/app/include/zmk/events/mouse_scroll_state_changed.h deleted file mode 100644 index bf03308f1d5..00000000000 --- a/app/include/zmk/events/mouse_scroll_state_changed.h +++ /dev/null @@ -1,34 +0,0 @@ - -/* - * Copyright (c) 2020 The ZMK Contributors - * - * SPDX-License-Identifier: MIT - */ - -#pragma once - -#include -#include -#include -#include - -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 = SCRL_X_DECODE(encoded), - .y = SCRL_Y_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}); -} diff --git a/app/include/zmk/events/mouse_tick.h b/app/include/zmk/events/mouse_tick.h deleted file mode 100644 index 75a041e96b4..00000000000 --- a/app/include/zmk/events/mouse_tick.h +++ /dev/null @@ -1,39 +0,0 @@ - -/* - * Copyright (c) 2020 The ZMK Contributors - * - * SPDX-License-Identifier: MIT - */ - -#pragma once - -#include -#include -#include -#include - -struct zmk_mouse_tick { - struct vector2d max_move; - struct vector2d max_scroll; - struct mouse_config move_config; - struct mouse_config scroll_config; - struct mouse_times start_times; - 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, - struct mouse_times 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_times = movement_start, - .timestamp = k_uptime_get(), - }); -} diff --git a/app/include/zmk/mouse.h b/app/include/zmk/mouse.h index c2ae6252df5..28dab60d143 100644 --- a/app/include/zmk/mouse.h +++ b/app/include/zmk/mouse.h @@ -11,26 +11,4 @@ typedef uint8_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 mouse_times { - uint64_t m_x; - uint64_t m_y; - uint64_t s_x; - uint64_t s_y; -}; - -struct k_work_q *zmk_mouse_work_q(); -int zmk_mouse_init(); +int zmk_mouse_init(void); diff --git a/app/src/behaviors/behavior_input_two_axis.c b/app/src/behaviors/behavior_input_two_axis.c new file mode 100644 index 00000000000..5a94895e289 --- /dev/null +++ b/app/src/behaviors/behavior_input_two_axis.c @@ -0,0 +1,270 @@ +/* + * Copyright (c) 2021 The ZMK Contributors + * + * SPDX-License-Identifier: MIT + */ + +#define DT_DRV_COMPAT zmk_behavior_input_two_axis + +#include +#include +#include +#include +#include // CLAMP + +#include +#include + +LOG_MODULE_DECLARE(zmk, CONFIG_ZMK_LOG_LEVEL); + +struct vector2d { + float x; + float y; +}; + +struct movement_state_1d { + float remainder; + int16_t speed; + uint64_t start_time; +}; + +struct movement_state_2d { + struct movement_state_1d x; + struct movement_state_1d y; +}; + +struct behavior_input_two_axis_data { + struct k_work_delayable tick_work; + const struct device *dev; + + struct movement_state_2d state; +}; + +struct behavior_input_two_axis_config { + int16_t x_code; + int16_t y_code; + 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; +}; + +#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 +#endif + +static int64_t ms_since_start(int64_t start, int64_t now, int64_t delay) { + if (start == 0) { + return 0; + } + 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 behavior_input_two_axis_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 == 0) { + return 0; + } + + 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 float update_movement_1d(const struct behavior_input_two_axis_config *config, + struct movement_state_1d *state, int64_t now) { + float move = 0; + if (state->speed == 0) { + state->remainder = 0; + return move; + } + + int64_t move_duration = ms_since_start(state->start_time, now, config->delay_ms); + move = + (move_duration > 0) + ? (speed(config, state->speed, move_duration) * CONFIG_ZMK_MOUSE_TICK_DURATION / 1000) + : 0; + + track_remainder(&(move), &(state->remainder)); + + return move; +} +static struct vector2d update_movement_2d(const struct behavior_input_two_axis_config *config, + struct movement_state_2d *state, int64_t now) { + struct vector2d move = {0}; + + move = (struct vector2d){ + .x = update_movement_1d(config, &state->x, now), + .y = update_movement_1d(config, &state->y, now), + }; + + return move; +} + +static bool is_non_zero_1d_movement(int16_t speed) { return speed != 0; } + +static bool is_non_zero_2d_movement(struct movement_state_2d *state) { + return is_non_zero_1d_movement(state->x.speed) || is_non_zero_1d_movement(state->y.speed); +} + +static bool should_be_working(struct behavior_input_two_axis_data *data) { + return is_non_zero_2d_movement(&data->state); +} + +static void tick_work_cb(struct k_work *work) { + struct k_work_delayable *d_work = k_work_delayable_from_work(work); + struct behavior_input_two_axis_data *data = + CONTAINER_OF(d_work, struct behavior_input_two_axis_data, tick_work); + const struct device *dev = data->dev; + const struct behavior_input_two_axis_config *cfg = dev->config; + + uint32_t timestamp = k_uptime_get(); + + LOG_INF("tick start times: %lld %lld %lld", data->state.x.start_time, data->state.y.start_time, + timestamp); + + struct vector2d move = update_movement_2d(cfg, &data->state, timestamp); + + int ret = 0; + bool have_x = is_non_zero_1d_movement(move.x); + bool have_y = is_non_zero_1d_movement(move.y); + if (have_x) { + ret = input_report_rel(dev, cfg->x_code, (int16_t)CLAMP(move.x, INT16_MIN, INT16_MAX), + !have_y, K_NO_WAIT); + } + if (have_y) { + ret = input_report_rel(dev, cfg->y_code, (int16_t)CLAMP(move.y, INT16_MIN, INT16_MAX), true, + K_NO_WAIT); + } + + if (should_be_working(data)) { + k_work_schedule(&data->tick_work, K_MSEC(CONFIG_ZMK_MOUSE_TICK_DURATION)); + } +} + +static void set_start_times_for_activity(struct movement_state_2d *state) { + if (state->x.speed != 0 && state->x.start_time == 0) { + state->x.start_time = k_uptime_get(); + } + + if (state->y.speed != 0 && state->y.start_time == 0) { + state->y.start_time = k_uptime_get(); + } +} + +static void update_work_scheduling(const struct device *dev) { + struct behavior_input_two_axis_data *data = dev->data; + + set_start_times_for_activity(&data->state); + + if (should_be_working(data)) { + k_work_schedule(&data->tick_work, K_MSEC(CONFIG_ZMK_MOUSE_TICK_DURATION)); + } else { + k_work_cancel_delayable(&data->tick_work); + } +} + +int zmk_input_synth_pointer_adjust_speed(const struct device *dev, int16_t dx, int16_t dy) { + struct behavior_input_two_axis_data *data = dev->data; + + LOG_DBG("Adjusting: %d %d", dx, dy); + data->state.x.speed += dx; + data->state.y.speed += dy; + + LOG_DBG("After: %d %d", data->state.x.speed, data->state.y.speed); + + update_work_scheduling(dev); + + return 0; +} + +// static void process_key_state(const struct device *dev, int32_t val, bool pressed) { +// for (int i = 0; i < ZMK_HID_MOUSE_NUM_BUTTONS; i++) { +// if (val & BIT(i)) { +// WRITE_BIT(val, i, 0); +// input_report_key(dev, INPUT_BTN_0 + i, pressed ? 1 : 0, val == 0, K_FOREVER); +// } +// } +// } + +static int behavior_input_two_axis_init(const struct device *dev) { + struct behavior_input_two_axis_data *data = dev->data; + + data->dev = dev; + k_work_init_delayable(&data->tick_work, tick_work_cb); + + return 0; +}; + +static int on_keymap_binding_pressed(struct zmk_behavior_binding *binding, + struct zmk_behavior_binding_event event) { + + const struct device *behavior_dev = zmk_behavior_get_binding(binding->behavior_dev); + + LOG_DBG("position %d keycode 0x%02X", event.position, binding->param1); + + int16_t x = MOVE_X_DECODE(binding->param1); + int16_t y = MOVE_Y_DECODE(binding->param1); + + zmk_input_synth_pointer_adjust_speed(behavior_dev, x, y); + return 0; +} + +static int on_keymap_binding_released(struct zmk_behavior_binding *binding, + struct zmk_behavior_binding_event event) { + const struct device *behavior_dev = zmk_behavior_get_binding(binding->behavior_dev); + + LOG_DBG("position %d keycode 0x%02X", event.position, binding->param1); + + int16_t x = MOVE_X_DECODE(binding->param1); + int16_t y = MOVE_Y_DECODE(binding->param1); + + zmk_input_synth_pointer_adjust_speed(behavior_dev, -x, -y); + return 0; +} + +static const struct behavior_driver_api behavior_input_two_axis_driver_api = { + .binding_pressed = on_keymap_binding_pressed, .binding_released = on_keymap_binding_released}; + +#define ITA_INST(n) \ + static struct behavior_input_two_axis_data behavior_input_two_axis_data_##n = {}; \ + static struct behavior_input_two_axis_config behavior_input_two_axis_config_##n = { \ + .x_code = DT_INST_PROP(n, x_input_code), \ + .y_code = DT_INST_PROP(n, y_input_code), \ + .delay_ms = DT_INST_PROP_OR(n, delay_ms, 0), \ + .time_to_max_speed_ms = DT_INST_PROP(n, time_to_max_speed_ms), \ + .acceleration_exponent = DT_INST_PROP_OR(n, acceleration_exponent, 1), \ + }; \ + BEHAVIOR_DT_INST_DEFINE( \ + n, behavior_input_two_axis_init, NULL, &behavior_input_two_axis_data_##n, \ + &behavior_input_two_axis_config_##n, POST_KERNEL, CONFIG_KERNEL_INIT_PRIORITY_DEFAULT, \ + &behavior_input_two_axis_driver_api); + +DT_INST_FOREACH_STATUS_OKAY(ITA_INST) diff --git a/app/src/behaviors/behavior_mouse_key_press.c b/app/src/behaviors/behavior_mouse_key_press.c index 9064a1aa5c8..66b54fce05b 100644 --- a/app/src/behaviors/behavior_mouse_key_press.c +++ b/app/src/behaviors/behavior_mouse_key_press.c @@ -11,8 +11,9 @@ #include #include -#include -#include +#include +#include +#include LOG_MODULE_DECLARE(zmk, CONFIG_ZMK_LOG_LEVEL); @@ -20,19 +21,31 @@ LOG_MODULE_DECLARE(zmk, CONFIG_ZMK_LOG_LEVEL); static int behavior_mouse_key_press_init(const struct device *dev) { return 0; }; +static void process_key_state(const struct device *dev, int32_t val, bool pressed) { + for (int i = 0; i < ZMK_HID_MOUSE_NUM_BUTTONS; i++) { + if (val & BIT(i)) { + WRITE_BIT(val, i, 0); + input_report_key(dev, INPUT_BTN_0 + i, pressed ? 1 : 0, val == 0, K_FOREVER); + } + } +} + 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 raise_zmk_mouse_button_state_changed_from_encoded(binding->param1, true, - event.timestamp); + process_key_state(zmk_behavior_get_binding(binding->behavior_dev), binding->param1, true); + + return 0; } 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 raise_zmk_mouse_button_state_changed_from_encoded(binding->param1, false, - event.timestamp); + + process_key_state(zmk_behavior_get_binding(binding->behavior_dev), binding->param1, false); + + return 0; } static const struct behavior_driver_api behavior_mouse_key_press_driver_api = { diff --git a/app/src/behaviors/behavior_mouse_move.c b/app/src/behaviors/behavior_mouse_move.c deleted file mode 100644 index 0d591f00324..00000000000 --- a/app/src/behaviors/behavior_mouse_move.c +++ /dev/null @@ -1,57 +0,0 @@ -/* - * Copyright (c) 2021 The ZMK Contributors - * - * SPDX-License-Identifier: MIT - */ - -#define DT_DRV_COMPAT zmk_behavior_mouse_move - -#include -#include -#include - -#include -#include -#include -#include - -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) */ diff --git a/app/src/behaviors/behavior_mouse_scroll.c b/app/src/behaviors/behavior_mouse_scroll.c deleted file mode 100644 index d71671724d0..00000000000 --- a/app/src/behaviors/behavior_mouse_scroll.c +++ /dev/null @@ -1,58 +0,0 @@ -/* - * Copyright (c) 2021 The ZMK Contributors - * - * SPDX-License-Identifier: MIT - */ - -#define DT_DRV_COMPAT zmk_behavior_mouse_scroll - -#include -#include -#include - -#include -#include -#include -#include -#include - -LOG_MODULE_DECLARE(zmk, CONFIG_ZMK_LOG_LEVEL); - -#if DT_HAS_COMPAT_STATUS_OKAY(DT_DRV_COMPAT) - -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) */ diff --git a/app/src/events/mouse_move_state_changed.c b/app/src/events/mouse_move_state_changed.c deleted file mode 100644 index 07a789b24ef..00000000000 --- a/app/src/events/mouse_move_state_changed.c +++ /dev/null @@ -1,10 +0,0 @@ -/* - * Copyright (c) 2020 The ZMK Contributors - * - * SPDX-License-Identifier: MIT - */ - -#include -#include - -ZMK_EVENT_IMPL(zmk_mouse_move_state_changed); diff --git a/app/src/events/mouse_scroll_state_changed.c b/app/src/events/mouse_scroll_state_changed.c deleted file mode 100644 index f6216d39d0c..00000000000 --- a/app/src/events/mouse_scroll_state_changed.c +++ /dev/null @@ -1,10 +0,0 @@ -/* - * Copyright (c) 2020 The ZMK Contributors - * - * SPDX-License-Identifier: MIT - */ - -#include -#include - -ZMK_EVENT_IMPL(zmk_mouse_scroll_state_changed); diff --git a/app/src/events/mouse_tick.c b/app/src/events/mouse_tick.c deleted file mode 100644 index 846b3faee89..00000000000 --- a/app/src/events/mouse_tick.c +++ /dev/null @@ -1,10 +0,0 @@ -/* - * Copyright (c) 2020 The ZMK Contributors - * - * SPDX-License-Identifier: MIT - */ - -#include -#include - -ZMK_EVENT_IMPL(zmk_mouse_tick); diff --git a/app/src/hid.c b/app/src/hid.c index cfa8fb7b1ea..d6b2344c2ae 100644 --- a/app/src/hid.c +++ b/app/src/hid.c @@ -435,31 +435,33 @@ int zmk_hid_mouse_buttons_release(zmk_mouse_button_flags_t buttons) { void zmk_hid_mouse_movement_set(int16_t x, int16_t y) { mouse_report.body.d_x = x; mouse_report.body.d_y = y; - LOG_DBG("Mouse movement set to 0x%02X 0x%02X ", mouse_report.body.d_x, mouse_report.body.d_y); + LOG_DBG("Mouse movement set to %d/%d", mouse_report.body.d_x, mouse_report.body.d_y); } void zmk_hid_mouse_movement_update(int16_t x, int16_t y) { mouse_report.body.d_x += x; mouse_report.body.d_y += y; - LOG_DBG("Mouse movement updated to 0x%02X 0x%02X ", mouse_report.body.d_x, - mouse_report.body.d_y); + LOG_DBG("Mouse movement updated to %d/%d", mouse_report.body.d_x, mouse_report.body.d_y); } void zmk_hid_mouse_scroll_set(int8_t x, int8_t y) { mouse_report.body.d_scroll_x = x; mouse_report.body.d_scroll_y = y; - LOG_DBG("Mouse scroll set to X: 0x%02X Y: 0x%02X", mouse_report.body.d_scroll_x, + LOG_DBG("Mouse scroll set to %d/%d", mouse_report.body.d_scroll_x, mouse_report.body.d_scroll_y); } void zmk_hid_mouse_scroll_update(int8_t x, int8_t y) { mouse_report.body.d_scroll_x += x; mouse_report.body.d_scroll_y += y; - LOG_DBG("Mouse scroll updated to X: 0x%02X Y: 0x%02X", mouse_report.body.d_scroll_x, + LOG_DBG("Mouse scroll updated to X: %d/%d", mouse_report.body.d_scroll_x, mouse_report.body.d_scroll_y); } -void zmk_hid_mouse_clear(void) { memset(&mouse_report.body, 0, sizeof(mouse_report.body)); } +void zmk_hid_mouse_clear(void) { + LOG_DBG("Mouse report cleared"); + memset(&mouse_report.body, 0, sizeof(mouse_report.body)); +} #endif // IS_ENABLED(CONFIG_ZMK_MOUSE) diff --git a/app/src/mouse/Kconfig b/app/src/mouse/Kconfig index f44d2d1db1b..972c0c6f4b5 100644 --- a/app/src/mouse/Kconfig +++ b/app/src/mouse/Kconfig @@ -3,6 +3,8 @@ menuconfig ZMK_MOUSE bool "Mouse Emulation" + select INPUT + select INPUT_THREAD_PRIORITY_OVERRIDE if ZMK_MOUSE diff --git a/app/src/mouse/hid_input_listener.c b/app/src/mouse/hid_input_listener.c new file mode 100644 index 00000000000..6c053755f87 --- /dev/null +++ b/app/src/mouse/hid_input_listener.c @@ -0,0 +1,72 @@ +/* + * Copyright (c) 2020 The ZMK Contributors + * + * SPDX-License-Identifier: MIT + */ + +#include +#include +#include + +#include +#include +#include + +void handle_rel_code(struct input_event *evt) { + switch (evt->code) { + case INPUT_REL_X: + zmk_hid_mouse_movement_update(evt->value, 0); + break; + case INPUT_REL_Y: + zmk_hid_mouse_movement_update(0, evt->value); + break; + case INPUT_REL_WHEEL: + zmk_hid_mouse_scroll_update(0, evt->value); + break; + case INPUT_REL_HWHEEL: + zmk_hid_mouse_scroll_update(evt->value, 0); + break; + default: + break; + } +} + +void handle_key_code(struct input_event *evt) { + int8_t btn; + + switch (evt->code) { + case INPUT_BTN_0: + case INPUT_BTN_1: + case INPUT_BTN_2: + case INPUT_BTN_3: + case INPUT_BTN_4: + btn = evt->code - INPUT_BTN_0; + if (evt->value > 0) { + zmk_hid_mouse_button_press(btn); + } else { + zmk_hid_mouse_button_release(btn); + } + break; + default: + break; + } +} + +void input_handler(struct input_event *evt) { + switch (evt->type) { + case INPUT_EV_REL: + handle_rel_code(evt); + break; + case INPUT_EV_KEY: + handle_key_code(evt); + break; + } + + if (evt->sync) { + zmk_endpoints_send_mouse_report(); + zmk_hid_mouse_scroll_set(0, 0); + zmk_hid_mouse_movement_set(0, 0); + } +} + +INPUT_CALLBACK_DEFINE(NULL, input_handler); diff --git a/app/src/mouse/key_listener.c b/app/src/mouse/key_listener.c deleted file mode 100644 index bd06efebd9f..00000000000 --- a/app/src/mouse/key_listener.c +++ /dev/null @@ -1,201 +0,0 @@ -/* - * Copyright (c) 2021 The ZMK Contributors - * - * SPDX-License-Identifier: MIT - */ - -#include -#include - -LOG_MODULE_DECLARE(zmk, CONFIG_ZMK_LOG_LEVEL); - -#include -#include -#include -#include -#include -#include -#include -#include - -#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 struct mouse_times start_times = (struct mouse_times){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_times = (struct mouse_times){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_times)); - 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 struct { - int m_x; - int m_y; - int s_x; - int s_y; -} mouse_timer_ref_counts = {0, 0, 0, 0}; - -void mouse_timer_ref(bool m_x, bool m_y, bool s_x, bool s_y) { - if (m_x && mouse_timer_ref_counts.m_x == 0) { - start_times.m_x = k_uptime_get(); - } - if (m_y && mouse_timer_ref_counts.m_y == 0) { - start_times.m_y = k_uptime_get(); - } - if (s_x && mouse_timer_ref_counts.s_x == 0) { - start_times.s_x = k_uptime_get(); - } - if (s_y && mouse_timer_ref_counts.s_y == 0) { - start_times.s_y = k_uptime_get(); - } - if (mouse_timer_ref_counts.m_x == 0 && mouse_timer_ref_counts.m_y == 0 && - mouse_timer_ref_counts.s_x == 0 && mouse_timer_ref_counts.s_y == 0) { - k_timer_start(&mouse_timer, K_NO_WAIT, K_MSEC(CONFIG_ZMK_MOUSE_TICK_DURATION)); - } - if (m_x) { - mouse_timer_ref_counts.m_x++; - } - if (m_y) { - mouse_timer_ref_counts.m_y++; - } - if (s_x) { - mouse_timer_ref_counts.s_x++; - } - if (s_y) { - mouse_timer_ref_counts.s_y++; - } -} - -void mouse_timer_unref(bool m_x, bool m_y, bool s_x, bool s_y) { - if (m_x && mouse_timer_ref_counts.m_x > 0) { - mouse_timer_ref_counts.m_x--; - } - if (m_y && mouse_timer_ref_counts.m_y > 0) { - mouse_timer_ref_counts.m_y--; - } - if (s_x && mouse_timer_ref_counts.s_x > 0) { - mouse_timer_ref_counts.s_x--; - } - if (s_y && mouse_timer_ref_counts.s_y > 0) { - mouse_timer_ref_counts.s_y--; - } - if (mouse_timer_ref_counts.m_x == 0 && mouse_timer_ref_counts.m_y == 0 && - mouse_timer_ref_counts.s_x == 0 && mouse_timer_ref_counts.s_y == 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(ev->max_speed.x != 0, ev->max_speed.y != 0, false, false); -} - -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(ev->max_speed.x != 0, ev->max_speed.y != 0, false, false); -} - -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(false, false, ev->max_speed.x != 0, ev->max_speed.y != 0); -} - -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(false, false, ev->max_speed.x != 0, ev->max_speed.y != 0); -} - -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) */ diff --git a/app/src/mouse/main.c b/app/src/mouse/main.c index e5b5252f8de..e1227425ea5 100644 --- a/app/src/mouse/main.c +++ b/app/src/mouse/main.c @@ -20,7 +20,7 @@ struct k_work_q *zmk_mouse_work_q() { #endif } -int zmk_mouse_init() { +int zmk_mouse_init(void) { #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), diff --git a/app/src/mouse/tick_listener.c b/app/src/mouse/tick_listener.c deleted file mode 100644 index 90aaf1119dd..00000000000 --- a/app/src/mouse/tick_listener.c +++ /dev/null @@ -1,111 +0,0 @@ -/* - * Copyright (c) 2020 The ZMK Contributors - * - * SPDX-License-Identifier: MIT - */ - -#include - -LOG_MODULE_DECLARE(zmk, CONFIG_ZMK_LOG_LEVEL); - -#include -#include -#include -#include -#include - -#include // 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 -#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_x, int64_t start_time_y) { - struct vector2d move = {0}; - if (max_speed.x == 0 && max_speed.y == 0) { - *remainder = (struct vector2d){0}; - return move; - } - - int64_t move_duration_x = ms_since_start(start_time_x, now, config->delay_ms); - int64_t move_duration_y = ms_since_start(start_time_y, now, config->delay_ms); - move = (struct vector2d){ - .x = speed(config, max_speed.x, move_duration_x) * CONFIG_ZMK_MOUSE_TICK_DURATION / 1000, - .y = speed(config, max_speed.y, move_duration_y) * 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) { - LOG_INF("tick start times: %lld %lld %lld %lld", tick->start_times.m_x, tick->start_times.m_y, - tick->start_times.s_x, tick->start_times.s_y); - struct vector2d move = - update_movement(&move_remainder, &(tick->move_config), tick->max_move, tick->timestamp, - tick->start_times.m_x, tick->start_times.m_y); - 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_times.s_x, tick->start_times.s_y); - 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) */ diff --git a/app/tests/mouse-keys/mmv/events.patterns b/app/tests/mouse-keys/mmv/events.patterns deleted file mode 100644 index 833100f6ac4..00000000000 --- a/app/tests/mouse-keys/mmv/events.patterns +++ /dev/null @@ -1 +0,0 @@ -s/.*hid_listener_keycode_//p \ No newline at end of file diff --git a/app/tests/mouse-keys/mmv/keycode_events.snapshot b/app/tests/mouse-keys/mmv/keycode_events.snapshot deleted file mode 100644 index 259501ba3d9..00000000000 --- a/app/tests/mouse-keys/mmv/keycode_events.snapshot +++ /dev/null @@ -1,2 +0,0 @@ -pressed: usage_page 0x07 keycode 0x05 implicit_mods 0x00 explicit_mods 0x00 -released: usage_page 0x07 keycode 0x05 implicit_mods 0x00 explicit_mods 0x00 diff --git a/app/tests/mouse-keys/mouse-move/move_diagonal/events.patterns b/app/tests/mouse-keys/mouse-move/move_diagonal/events.patterns new file mode 100644 index 00000000000..812126fb828 --- /dev/null +++ b/app/tests/mouse-keys/mouse-move/move_diagonal/events.patterns @@ -0,0 +1 @@ +s/.*hid_mouse_//p \ No newline at end of file diff --git a/app/tests/mouse-keys/mouse-move/move_diagonal/keycode_events.snapshot b/app/tests/mouse-keys/mouse-move/move_diagonal/keycode_events.snapshot new file mode 100644 index 00000000000..91c4f16b9c0 --- /dev/null +++ b/app/tests/mouse-keys/mouse-move/move_diagonal/keycode_events.snapshot @@ -0,0 +1,22 @@ +movement_update: Mouse movement updated to -1/0 +scroll_set: Mouse scroll set to 0/0 +movement_set: Mouse movement set to 0/0 +movement_update: Mouse movement updated to -2/0 +movement_update: Mouse movement updated to -2/-2 +scroll_set: Mouse scroll set to 0/0 +movement_set: Mouse movement set to 0/0 +movement_update: Mouse movement updated to -2/0 +movement_update: Mouse movement updated to -2/-2 +scroll_set: Mouse scroll set to 0/0 +movement_set: Mouse movement set to 0/0 +movement_update: Mouse movement updated to -3/0 +movement_update: Mouse movement updated to -3/-2 +scroll_set: Mouse scroll set to 0/0 +movement_set: Mouse movement set to 0/0 +movement_update: Mouse movement updated to -3/0 +movement_update: Mouse movement updated to -3/-3 +scroll_set: Mouse scroll set to 0/0 +movement_set: Mouse movement set to 0/0 +movement_update: Mouse movement updated to 0/-3 +scroll_set: Mouse scroll set to 0/0 +movement_set: Mouse movement set to 0/0 diff --git a/app/tests/mouse-keys/mouse-move/move_diagonal/native_posix_64.keymap b/app/tests/mouse-keys/mouse-move/move_diagonal/native_posix_64.keymap new file mode 100644 index 00000000000..9dfa5b2a81c --- /dev/null +++ b/app/tests/mouse-keys/mouse-move/move_diagonal/native_posix_64.keymap @@ -0,0 +1,28 @@ +#include +#include +#include +#include + +/ { + keymap { + compatible = "zmk,keymap"; + label ="Default keymap"; + + default_layer { + bindings = < + &mmv MOVE_LEFT &mmv MOVE_UP + &none &none + >; + }; + }; +}; + + +&kscan { + events = < + ZMK_MOCK_PRESS(0,0,10) + ZMK_MOCK_PRESS(0,1,100) + ZMK_MOCK_RELEASE(0,0,10) + ZMK_MOCK_RELEASE(0,1,10) + >; +}; \ No newline at end of file diff --git a/app/tests/mouse-keys/mouse-move/move_x/events.patterns b/app/tests/mouse-keys/mouse-move/move_x/events.patterns new file mode 100644 index 00000000000..812126fb828 --- /dev/null +++ b/app/tests/mouse-keys/mouse-move/move_x/events.patterns @@ -0,0 +1 @@ +s/.*hid_mouse_//p \ No newline at end of file diff --git a/app/tests/mouse-keys/mouse-move/move_x/keycode_events.snapshot b/app/tests/mouse-keys/mouse-move/move_x/keycode_events.snapshot new file mode 100644 index 00000000000..027e83e9f06 --- /dev/null +++ b/app/tests/mouse-keys/mouse-move/move_x/keycode_events.snapshot @@ -0,0 +1,27 @@ +movement_update: Mouse movement updated to -1/0 +scroll_set: Mouse scroll set to 0/0 +movement_set: Mouse movement set to 0/0 +movement_update: Mouse movement updated to -2/0 +scroll_set: Mouse scroll set to 0/0 +movement_set: Mouse movement set to 0/0 +movement_update: Mouse movement updated to -2/0 +scroll_set: Mouse scroll set to 0/0 +movement_set: Mouse movement set to 0/0 +movement_update: Mouse movement updated to -3/0 +scroll_set: Mouse scroll set to 0/0 +movement_set: Mouse movement set to 0/0 +movement_update: Mouse movement updated to 3/0 +scroll_set: Mouse scroll set to 0/0 +movement_set: Mouse movement set to 0/0 +movement_update: Mouse movement updated to 5/0 +scroll_set: Mouse scroll set to 0/0 +movement_set: Mouse movement set to 0/0 +movement_update: Mouse movement updated to 5/0 +scroll_set: Mouse scroll set to 0/0 +movement_set: Mouse movement set to 0/0 +movement_update: Mouse movement updated to 6/0 +scroll_set: Mouse scroll set to 0/0 +movement_set: Mouse movement set to 0/0 +movement_update: Mouse movement updated to 6/0 +scroll_set: Mouse scroll set to 0/0 +movement_set: Mouse movement set to 0/0 \ No newline at end of file diff --git a/app/tests/mouse-keys/mouse-move/move_x/native_posix_64.keymap b/app/tests/mouse-keys/mouse-move/move_x/native_posix_64.keymap new file mode 100644 index 00000000000..aacacebba90 --- /dev/null +++ b/app/tests/mouse-keys/mouse-move/move_x/native_posix_64.keymap @@ -0,0 +1,28 @@ +#include +#include +#include +#include + +/ { + keymap { + compatible = "zmk,keymap"; + label ="Default keymap"; + + default_layer { + bindings = < + &mmv MOVE_LEFT &mmv MOVE_RIGHT + &none &none + >; + }; + }; +}; + + +&kscan { + events = < + ZMK_MOCK_PRESS(0,0,100) + ZMK_MOCK_RELEASE(0,0,10) + ZMK_MOCK_PRESS(0,1,100) + ZMK_MOCK_RELEASE(0,1,10) + >; +}; \ No newline at end of file diff --git a/app/tests/mouse-keys/mouse-move/move_y/events.patterns b/app/tests/mouse-keys/mouse-move/move_y/events.patterns new file mode 100644 index 00000000000..812126fb828 --- /dev/null +++ b/app/tests/mouse-keys/mouse-move/move_y/events.patterns @@ -0,0 +1 @@ +s/.*hid_mouse_//p \ No newline at end of file diff --git a/app/tests/mouse-keys/mouse-move/move_y/keycode_events.snapshot b/app/tests/mouse-keys/mouse-move/move_y/keycode_events.snapshot new file mode 100644 index 00000000000..178d5e7453a --- /dev/null +++ b/app/tests/mouse-keys/mouse-move/move_y/keycode_events.snapshot @@ -0,0 +1,27 @@ +movement_update: Mouse movement updated to 0/-1 +scroll_set: Mouse scroll set to 0/0 +movement_set: Mouse movement set to 0/0 +movement_update: Mouse movement updated to 0/-2 +scroll_set: Mouse scroll set to 0/0 +movement_set: Mouse movement set to 0/0 +movement_update: Mouse movement updated to 0/-2 +scroll_set: Mouse scroll set to 0/0 +movement_set: Mouse movement set to 0/0 +movement_update: Mouse movement updated to 0/-3 +scroll_set: Mouse scroll set to 0/0 +movement_set: Mouse movement set to 0/0 +movement_update: Mouse movement updated to 0/3 +scroll_set: Mouse scroll set to 0/0 +movement_set: Mouse movement set to 0/0 +movement_update: Mouse movement updated to 0/5 +scroll_set: Mouse scroll set to 0/0 +movement_set: Mouse movement set to 0/0 +movement_update: Mouse movement updated to 0/5 +scroll_set: Mouse scroll set to 0/0 +movement_set: Mouse movement set to 0/0 +movement_update: Mouse movement updated to 0/6 +scroll_set: Mouse scroll set to 0/0 +movement_set: Mouse movement set to 0/0 +movement_update: Mouse movement updated to 0/6 +scroll_set: Mouse scroll set to 0/0 +movement_set: Mouse movement set to 0/0 diff --git a/app/tests/mouse-keys/mmv/native_posix.keymap b/app/tests/mouse-keys/mouse-move/move_y/native_posix_64.keymap similarity index 79% rename from app/tests/mouse-keys/mmv/native_posix.keymap rename to app/tests/mouse-keys/mouse-move/move_y/native_posix_64.keymap index dbb3f014c9f..1d914136e28 100644 --- a/app/tests/mouse-keys/mmv/native_posix.keymap +++ b/app/tests/mouse-keys/mouse-move/move_y/native_posix_64.keymap @@ -10,7 +10,7 @@ default_layer { bindings = < - &mmv MOVE_LEFT &none + &mmv MOVE_UP &mmv MOVE_DOWN &none &none >; }; @@ -22,5 +22,7 @@ events = < ZMK_MOCK_PRESS(0,0,100) ZMK_MOCK_RELEASE(0,0,10) + ZMK_MOCK_PRESS(0,1,100) + ZMK_MOCK_RELEASE(0,1,10) >; }; \ No newline at end of file From b2f61631884c15bff8a8c67e648197ee3f450ff4 Mon Sep 17 00:00:00 2001 From: Peter Johanson Date: Thu, 23 Nov 2023 22:23:13 +0000 Subject: [PATCH 14/23] feat(mouse): Add input configs for data mods. * Add ability to swap X/Y, invert X and Y values, and apply a scalar multiplier/divisor. --- app/CMakeLists.txt | 1 + app/dts/bindings/zmk,input-configs.yaml | 24 +++++++++++ app/include/zmk/mouse/input_config.h | 21 ++++++++++ app/src/behaviors/behavior_input_two_axis.c | 16 +++---- app/src/mouse/hid_input_listener.c | 38 +++++++++++++++++ app/src/mouse/input_config.c | 42 +++++++++++++++++++ .../move_diagonal_scaling/events.patterns | 1 + .../keycode_events.snapshot | 22 ++++++++++ .../native_posix_64.keymap | 38 +++++++++++++++++ .../move_diagonal_xy_invert/events.patterns | 1 + .../keycode_events.snapshot | 22 ++++++++++ .../native_posix_64.keymap | 38 +++++++++++++++++ .../move_diagonal_xy_swap/events.patterns | 1 + .../keycode_events.snapshot | 22 ++++++++++ .../native_posix_64.keymap | 37 ++++++++++++++++ .../mouse-move/move_x/keycode_events.snapshot | 11 ++--- .../mouse-move/move_y/keycode_events.snapshot | 11 ++--- 17 files changed, 325 insertions(+), 21 deletions(-) create mode 100644 app/dts/bindings/zmk,input-configs.yaml create mode 100644 app/include/zmk/mouse/input_config.h create mode 100644 app/src/mouse/input_config.c create mode 100644 app/tests/mouse-keys/mouse-move/input-configs/move_diagonal_scaling/events.patterns create mode 100644 app/tests/mouse-keys/mouse-move/input-configs/move_diagonal_scaling/keycode_events.snapshot create mode 100644 app/tests/mouse-keys/mouse-move/input-configs/move_diagonal_scaling/native_posix_64.keymap create mode 100644 app/tests/mouse-keys/mouse-move/input-configs/move_diagonal_xy_invert/events.patterns create mode 100644 app/tests/mouse-keys/mouse-move/input-configs/move_diagonal_xy_invert/keycode_events.snapshot create mode 100644 app/tests/mouse-keys/mouse-move/input-configs/move_diagonal_xy_invert/native_posix_64.keymap create mode 100644 app/tests/mouse-keys/mouse-move/input-configs/move_diagonal_xy_swap/events.patterns create mode 100644 app/tests/mouse-keys/mouse-move/input-configs/move_diagonal_xy_swap/keycode_events.snapshot create mode 100644 app/tests/mouse-keys/mouse-move/input-configs/move_diagonal_xy_swap/native_posix_64.keymap diff --git a/app/CMakeLists.txt b/app/CMakeLists.txt index 822383d2a0d..e3a165372e5 100644 --- a/app/CMakeLists.txt +++ b/app/CMakeLists.txt @@ -28,6 +28,7 @@ target_sources_ifdef(CONFIG_ZMK_KSCAN_SIDEBAND_BEHAVIORS app PRIVATE src/kscan_s target_sources(app PRIVATE src/matrix_transform.c) target_sources(app PRIVATE src/physical_layouts.c) target_sources_ifdef(CONFIG_ZMK_MOUSE app PRIVATE src/mouse/main.c) +target_sources_ifdef(CONFIG_ZMK_MOUSE app PRIVATE src/mouse/input_config.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) diff --git a/app/dts/bindings/zmk,input-configs.yaml b/app/dts/bindings/zmk,input-configs.yaml new file mode 100644 index 00000000000..83bd4a1939d --- /dev/null +++ b/app/dts/bindings/zmk,input-configs.yaml @@ -0,0 +1,24 @@ +description: | + Allows post-processing of input events based on the configuration + +compatible: "zmk,input-configs" + +child-binding: + description: "A configuration for a given input device" + + properties: + device: + type: phandle + required: true + xy-swap: + type: boolean + x-invert: + type: boolean + y-invert: + type: boolean + scale-multiplier: + type: int + default: 1 + scale-divisor: + type: int + default: 1 diff --git a/app/include/zmk/mouse/input_config.h b/app/include/zmk/mouse/input_config.h new file mode 100644 index 00000000000..0a37b346caf --- /dev/null +++ b/app/include/zmk/mouse/input_config.h @@ -0,0 +1,21 @@ +/* + * Copyright (c) 2023 The ZMK Contributors + * + * SPDX-License-Identifier: MIT + */ + +#pragma once + +#include +#include + +struct zmk_input_config { + const struct device *dev; + bool xy_swap; + bool x_invert; + bool y_invert; + uint16_t scale_multiplier; + uint16_t scale_divisor; +}; + +const struct zmk_input_config *zmk_input_config_get_for_device(const struct device *dev); \ No newline at end of file diff --git a/app/src/behaviors/behavior_input_two_axis.c b/app/src/behaviors/behavior_input_two_axis.c index 5a94895e289..5a6afe63384 100644 --- a/app/src/behaviors/behavior_input_two_axis.c +++ b/app/src/behaviors/behavior_input_two_axis.c @@ -169,15 +169,17 @@ static void tick_work_cb(struct k_work *work) { } } -static void set_start_times_for_activity(struct movement_state_2d *state) { - if (state->x.speed != 0 && state->x.start_time == 0) { - state->x.start_time = k_uptime_get(); - } - - if (state->y.speed != 0 && state->y.start_time == 0) { - state->y.start_time = k_uptime_get(); +static void set_start_times_for_activity_1d(struct movement_state_1d *state) { + if (state->speed != 0 && state->start_time == 0) { + state->start_time = k_uptime_get(); + } else if (state->speed == 0) { + state->start_time = 0; } } +static void set_start_times_for_activity(struct movement_state_2d *state) { + set_start_times_for_activity_1d(&state->x); + set_start_times_for_activity_1d(&state->y); +} static void update_work_scheduling(const struct device *dev) { struct behavior_input_two_axis_data *data = dev->data; diff --git a/app/src/mouse/hid_input_listener.c b/app/src/mouse/hid_input_listener.c index 6c053755f87..712bb5675c2 100644 --- a/app/src/mouse/hid_input_listener.c +++ b/app/src/mouse/hid_input_listener.c @@ -9,6 +9,7 @@ #include #include +#include #include #include @@ -52,7 +53,44 @@ void handle_key_code(struct input_event *evt) { } } +static void swap_xy(struct input_event *evt) { + switch (evt->code) { + case INPUT_REL_X: + evt->code = INPUT_REL_Y; + break; + case INPUT_REL_Y: + evt->code = INPUT_REL_X; + break; + } +} + +static void filter_with_input_config(struct input_event *evt) { + if (!evt->dev) { + return; + } + + const struct zmk_input_config *cfg = zmk_input_config_get_for_device(evt->dev); + + if (!cfg) { + return; + } + + if (cfg->xy_swap) { + swap_xy(evt); + } + + if ((cfg->x_invert && evt->code == INPUT_REL_X) || + (cfg->y_invert && evt->code == INPUT_REL_Y)) { + evt->value = -(evt->value); + } + + evt->value = (int16_t)((evt->value * cfg->scale_multiplier) / cfg->scale_divisor); +} + void input_handler(struct input_event *evt) { + // First, filter to update the event data as needed. + filter_with_input_config(evt); + switch (evt->type) { case INPUT_EV_REL: handle_rel_code(evt); diff --git a/app/src/mouse/input_config.c b/app/src/mouse/input_config.c new file mode 100644 index 00000000000..745fb49bac5 --- /dev/null +++ b/app/src/mouse/input_config.c @@ -0,0 +1,42 @@ +/* + * Copyright (c) 2023 The ZMK Contributors + * + * SPDX-License-Identifier: MIT + */ + +#include +#include + +#define DT_DRV_COMPAT zmk_input_configs + +#if DT_HAS_COMPAT_STATUS_OKAY(DT_DRV_COMPAT) + +#define CHILD_CONFIG(inst) \ + { \ + .dev = DEVICE_DT_GET(DT_PHANDLE(inst, device)), \ + .xy_swap = DT_PROP(inst, xy_swap), \ + .x_invert = DT_PROP(inst, x_invert), \ + .y_invert = DT_PROP(inst, y_invert), \ + .scale_multiplier = DT_PROP(inst, scale_multiplier), \ + .scale_divisor = DT_PROP(inst, scale_divisor), \ + }, + +const struct zmk_input_config configs[] = {DT_INST_FOREACH_CHILD(0, CHILD_CONFIG)}; + +const struct zmk_input_config *zmk_input_config_get_for_device(const struct device *dev) { + for (int i = 0; i < ARRAY_SIZE(configs); i++) { + if (configs[i].dev == dev) { + return &configs[i]; + } + } + + return NULL; +} + +#else + +const struct zmk_input_config *zmk_input_config_get_for_device(const struct device *dev) { + return NULL; +} + +#endif \ No newline at end of file diff --git a/app/tests/mouse-keys/mouse-move/input-configs/move_diagonal_scaling/events.patterns b/app/tests/mouse-keys/mouse-move/input-configs/move_diagonal_scaling/events.patterns new file mode 100644 index 00000000000..812126fb828 --- /dev/null +++ b/app/tests/mouse-keys/mouse-move/input-configs/move_diagonal_scaling/events.patterns @@ -0,0 +1 @@ +s/.*hid_mouse_//p \ No newline at end of file diff --git a/app/tests/mouse-keys/mouse-move/input-configs/move_diagonal_scaling/keycode_events.snapshot b/app/tests/mouse-keys/mouse-move/input-configs/move_diagonal_scaling/keycode_events.snapshot new file mode 100644 index 00000000000..99fa148876f --- /dev/null +++ b/app/tests/mouse-keys/mouse-move/input-configs/move_diagonal_scaling/keycode_events.snapshot @@ -0,0 +1,22 @@ +movement_update: Mouse movement updated to -1/0 +scroll_set: Mouse scroll set to 0/0 +movement_set: Mouse movement set to 0/0 +movement_update: Mouse movement updated to -3/0 +movement_update: Mouse movement updated to -3/-3 +scroll_set: Mouse scroll set to 0/0 +movement_set: Mouse movement set to 0/0 +movement_update: Mouse movement updated to -3/0 +movement_update: Mouse movement updated to -3/-3 +scroll_set: Mouse scroll set to 0/0 +movement_set: Mouse movement set to 0/0 +movement_update: Mouse movement updated to -5/0 +movement_update: Mouse movement updated to -5/-3 +scroll_set: Mouse scroll set to 0/0 +movement_set: Mouse movement set to 0/0 +movement_update: Mouse movement updated to -5/0 +movement_update: Mouse movement updated to -5/-5 +scroll_set: Mouse scroll set to 0/0 +movement_set: Mouse movement set to 0/0 +movement_update: Mouse movement updated to 0/-5 +scroll_set: Mouse scroll set to 0/0 +movement_set: Mouse movement set to 0/0 diff --git a/app/tests/mouse-keys/mouse-move/input-configs/move_diagonal_scaling/native_posix_64.keymap b/app/tests/mouse-keys/mouse-move/input-configs/move_diagonal_scaling/native_posix_64.keymap new file mode 100644 index 00000000000..b1ddbc1d8d0 --- /dev/null +++ b/app/tests/mouse-keys/mouse-move/input-configs/move_diagonal_scaling/native_posix_64.keymap @@ -0,0 +1,38 @@ +#include +#include +#include +#include + +/ { + keymap { + compatible = "zmk,keymap"; + label ="Default keymap"; + + default_layer { + bindings = < + &mmv MOVE_LEFT &mmv MOVE_UP + &none &none + >; + }; + }; + + input_configs { + compatible = "zmk,input-configs"; + + mmv { + device = <&mmv>; + scale-multiplier = <5>; + scale-divisor = <3>; + }; + }; +}; + + +&kscan { + events = < + ZMK_MOCK_PRESS(0,0,10) + ZMK_MOCK_PRESS(0,1,100) + ZMK_MOCK_RELEASE(0,0,10) + ZMK_MOCK_RELEASE(0,1,10) + >; +}; \ No newline at end of file diff --git a/app/tests/mouse-keys/mouse-move/input-configs/move_diagonal_xy_invert/events.patterns b/app/tests/mouse-keys/mouse-move/input-configs/move_diagonal_xy_invert/events.patterns new file mode 100644 index 00000000000..812126fb828 --- /dev/null +++ b/app/tests/mouse-keys/mouse-move/input-configs/move_diagonal_xy_invert/events.patterns @@ -0,0 +1 @@ +s/.*hid_mouse_//p \ No newline at end of file diff --git a/app/tests/mouse-keys/mouse-move/input-configs/move_diagonal_xy_invert/keycode_events.snapshot b/app/tests/mouse-keys/mouse-move/input-configs/move_diagonal_xy_invert/keycode_events.snapshot new file mode 100644 index 00000000000..94faacceb61 --- /dev/null +++ b/app/tests/mouse-keys/mouse-move/input-configs/move_diagonal_xy_invert/keycode_events.snapshot @@ -0,0 +1,22 @@ +movement_update: Mouse movement updated to 1/0 +scroll_set: Mouse scroll set to 0/0 +movement_set: Mouse movement set to 0/0 +movement_update: Mouse movement updated to 2/0 +movement_update: Mouse movement updated to 2/2 +scroll_set: Mouse scroll set to 0/0 +movement_set: Mouse movement set to 0/0 +movement_update: Mouse movement updated to 2/0 +movement_update: Mouse movement updated to 2/2 +scroll_set: Mouse scroll set to 0/0 +movement_set: Mouse movement set to 0/0 +movement_update: Mouse movement updated to 3/0 +movement_update: Mouse movement updated to 3/2 +scroll_set: Mouse scroll set to 0/0 +movement_set: Mouse movement set to 0/0 +movement_update: Mouse movement updated to 3/0 +movement_update: Mouse movement updated to 3/3 +scroll_set: Mouse scroll set to 0/0 +movement_set: Mouse movement set to 0/0 +movement_update: Mouse movement updated to 0/3 +scroll_set: Mouse scroll set to 0/0 +movement_set: Mouse movement set to 0/0 diff --git a/app/tests/mouse-keys/mouse-move/input-configs/move_diagonal_xy_invert/native_posix_64.keymap b/app/tests/mouse-keys/mouse-move/input-configs/move_diagonal_xy_invert/native_posix_64.keymap new file mode 100644 index 00000000000..4767e0ac55e --- /dev/null +++ b/app/tests/mouse-keys/mouse-move/input-configs/move_diagonal_xy_invert/native_posix_64.keymap @@ -0,0 +1,38 @@ +#include +#include +#include +#include + +/ { + keymap { + compatible = "zmk,keymap"; + label ="Default keymap"; + + default_layer { + bindings = < + &mmv MOVE_LEFT &mmv MOVE_UP + &none &none + >; + }; + }; + + input_configs { + compatible = "zmk,input-configs"; + + mmv { + device = <&mmv>; + x-invert; + y-invert; + }; + }; +}; + + +&kscan { + events = < + ZMK_MOCK_PRESS(0,0,10) + ZMK_MOCK_PRESS(0,1,100) + ZMK_MOCK_RELEASE(0,0,10) + ZMK_MOCK_RELEASE(0,1,10) + >; +}; \ No newline at end of file diff --git a/app/tests/mouse-keys/mouse-move/input-configs/move_diagonal_xy_swap/events.patterns b/app/tests/mouse-keys/mouse-move/input-configs/move_diagonal_xy_swap/events.patterns new file mode 100644 index 00000000000..812126fb828 --- /dev/null +++ b/app/tests/mouse-keys/mouse-move/input-configs/move_diagonal_xy_swap/events.patterns @@ -0,0 +1 @@ +s/.*hid_mouse_//p \ No newline at end of file diff --git a/app/tests/mouse-keys/mouse-move/input-configs/move_diagonal_xy_swap/keycode_events.snapshot b/app/tests/mouse-keys/mouse-move/input-configs/move_diagonal_xy_swap/keycode_events.snapshot new file mode 100644 index 00000000000..5ca5300c1a7 --- /dev/null +++ b/app/tests/mouse-keys/mouse-move/input-configs/move_diagonal_xy_swap/keycode_events.snapshot @@ -0,0 +1,22 @@ +movement_update: Mouse movement updated to 0/-1 +scroll_set: Mouse scroll set to 0/0 +movement_set: Mouse movement set to 0/0 +movement_update: Mouse movement updated to 0/-2 +movement_update: Mouse movement updated to -2/-2 +scroll_set: Mouse scroll set to 0/0 +movement_set: Mouse movement set to 0/0 +movement_update: Mouse movement updated to 0/-2 +movement_update: Mouse movement updated to -2/-2 +scroll_set: Mouse scroll set to 0/0 +movement_set: Mouse movement set to 0/0 +movement_update: Mouse movement updated to 0/-3 +movement_update: Mouse movement updated to -2/-3 +scroll_set: Mouse scroll set to 0/0 +movement_set: Mouse movement set to 0/0 +movement_update: Mouse movement updated to 0/-3 +movement_update: Mouse movement updated to -3/-3 +scroll_set: Mouse scroll set to 0/0 +movement_set: Mouse movement set to 0/0 +movement_update: Mouse movement updated to -3/0 +scroll_set: Mouse scroll set to 0/0 +movement_set: Mouse movement set to 0/0 diff --git a/app/tests/mouse-keys/mouse-move/input-configs/move_diagonal_xy_swap/native_posix_64.keymap b/app/tests/mouse-keys/mouse-move/input-configs/move_diagonal_xy_swap/native_posix_64.keymap new file mode 100644 index 00000000000..153270231fa --- /dev/null +++ b/app/tests/mouse-keys/mouse-move/input-configs/move_diagonal_xy_swap/native_posix_64.keymap @@ -0,0 +1,37 @@ +#include +#include +#include +#include + +/ { + keymap { + compatible = "zmk,keymap"; + label ="Default keymap"; + + default_layer { + bindings = < + &mmv MOVE_LEFT &mmv MOVE_UP + &none &none + >; + }; + }; + + input_configs { + compatible = "zmk,input-configs"; + + mmv { + device = <&mmv>; + xy-swap; + }; + }; +}; + + +&kscan { + events = < + ZMK_MOCK_PRESS(0,0,10) + ZMK_MOCK_PRESS(0,1,100) + ZMK_MOCK_RELEASE(0,0,10) + ZMK_MOCK_RELEASE(0,1,10) + >; +}; \ No newline at end of file diff --git a/app/tests/mouse-keys/mouse-move/move_x/keycode_events.snapshot b/app/tests/mouse-keys/mouse-move/move_x/keycode_events.snapshot index 027e83e9f06..dfbc4a76eab 100644 --- a/app/tests/mouse-keys/mouse-move/move_x/keycode_events.snapshot +++ b/app/tests/mouse-keys/mouse-move/move_x/keycode_events.snapshot @@ -10,18 +10,15 @@ movement_set: Mouse movement set to 0/0 movement_update: Mouse movement updated to -3/0 scroll_set: Mouse scroll set to 0/0 movement_set: Mouse movement set to 0/0 -movement_update: Mouse movement updated to 3/0 +movement_update: Mouse movement updated to 1/0 scroll_set: Mouse scroll set to 0/0 movement_set: Mouse movement set to 0/0 -movement_update: Mouse movement updated to 5/0 +movement_update: Mouse movement updated to 2/0 scroll_set: Mouse scroll set to 0/0 movement_set: Mouse movement set to 0/0 -movement_update: Mouse movement updated to 5/0 +movement_update: Mouse movement updated to 2/0 scroll_set: Mouse scroll set to 0/0 movement_set: Mouse movement set to 0/0 -movement_update: Mouse movement updated to 6/0 +movement_update: Mouse movement updated to 3/0 scroll_set: Mouse scroll set to 0/0 movement_set: Mouse movement set to 0/0 -movement_update: Mouse movement updated to 6/0 -scroll_set: Mouse scroll set to 0/0 -movement_set: Mouse movement set to 0/0 \ No newline at end of file diff --git a/app/tests/mouse-keys/mouse-move/move_y/keycode_events.snapshot b/app/tests/mouse-keys/mouse-move/move_y/keycode_events.snapshot index 178d5e7453a..4fd9a25202c 100644 --- a/app/tests/mouse-keys/mouse-move/move_y/keycode_events.snapshot +++ b/app/tests/mouse-keys/mouse-move/move_y/keycode_events.snapshot @@ -10,18 +10,15 @@ movement_set: Mouse movement set to 0/0 movement_update: Mouse movement updated to 0/-3 scroll_set: Mouse scroll set to 0/0 movement_set: Mouse movement set to 0/0 -movement_update: Mouse movement updated to 0/3 -scroll_set: Mouse scroll set to 0/0 -movement_set: Mouse movement set to 0/0 -movement_update: Mouse movement updated to 0/5 +movement_update: Mouse movement updated to 0/1 scroll_set: Mouse scroll set to 0/0 movement_set: Mouse movement set to 0/0 -movement_update: Mouse movement updated to 0/5 +movement_update: Mouse movement updated to 0/2 scroll_set: Mouse scroll set to 0/0 movement_set: Mouse movement set to 0/0 -movement_update: Mouse movement updated to 0/6 +movement_update: Mouse movement updated to 0/2 scroll_set: Mouse scroll set to 0/0 movement_set: Mouse movement set to 0/0 -movement_update: Mouse movement updated to 0/6 +movement_update: Mouse movement updated to 0/3 scroll_set: Mouse scroll set to 0/0 movement_set: Mouse movement set to 0/0 From 4e53004502b8ec58da238365c148b906547bc7d6 Mon Sep 17 00:00:00 2001 From: Peter Johanson Date: Mon, 11 Dec 2023 15:45:50 -0800 Subject: [PATCH 15/23] refactor(mouse): Remove mouse work queue, Kconfig * Remove now-unused mouse work queue and related mouse main file. * Move ticks config into a DTS property on the two axis input behavior. --- app/CMakeLists.txt | 2 -- .../zmk,behavior-input-two-axis.yaml | 4 +++ app/include/zmk/mouse.h | 2 -- app/src/behaviors/behavior_input_two_axis.c | 20 ++++++----- app/src/main.c | 3 -- app/src/mouse/Kconfig | 34 +------------------ app/src/mouse/main.c | 30 ---------------- 7 files changed, 16 insertions(+), 79 deletions(-) delete mode 100644 app/src/mouse/main.c diff --git a/app/CMakeLists.txt b/app/CMakeLists.txt index e3a165372e5..e8ff7293efd 100644 --- a/app/CMakeLists.txt +++ b/app/CMakeLists.txt @@ -27,7 +27,6 @@ target_sources(app PRIVATE src/behavior.c) target_sources_ifdef(CONFIG_ZMK_KSCAN_SIDEBAND_BEHAVIORS app PRIVATE src/kscan_sideband_behaviors.c) target_sources(app PRIVATE src/matrix_transform.c) target_sources(app PRIVATE src/physical_layouts.c) -target_sources_ifdef(CONFIG_ZMK_MOUSE app PRIVATE src/mouse/main.c) target_sources_ifdef(CONFIG_ZMK_MOUSE app PRIVATE src/mouse/input_config.c) target_sources(app PRIVATE src/sensors.c) target_sources_ifdef(CONFIG_ZMK_WPM app PRIVATE src/wpm.c) @@ -45,7 +44,6 @@ target_sources_ifdef(CONFIG_ZMK_EXT_POWER app PRIVATE src/behaviors/behavior_ext target_sources_ifdef(CONFIG_ZMK_BEHAVIOR_SOFT_OFF app PRIVATE src/behaviors/behavior_soft_off.c) if ((NOT CONFIG_ZMK_SPLIT) OR CONFIG_ZMK_SPLIT_ROLE_CENTRAL) target_sources(app PRIVATE src/hid.c) - target_sources_ifdef(CONFIG_ZMK_MOUSE app PRIVATE src/mouse/main.c) target_sources_ifdef(CONFIG_ZMK_MOUSE app PRIVATE src/mouse/hid_input_listener.c) target_sources(app PRIVATE src/behaviors/behavior_key_press.c) target_sources_ifdef(CONFIG_ZMK_BEHAVIOR_KEY_TOGGLE app PRIVATE src/behaviors/behavior_key_toggle.c) diff --git a/app/dts/bindings/behaviors/zmk,behavior-input-two-axis.yaml b/app/dts/bindings/behaviors/zmk,behavior-input-two-axis.yaml index 820ee7035b8..0c138e03929 100644 --- a/app/dts/bindings/behaviors/zmk,behavior-input-two-axis.yaml +++ b/app/dts/bindings/behaviors/zmk,behavior-input-two-axis.yaml @@ -11,6 +11,10 @@ properties: y-input-code: type: int required: true + trigger-period-ms: + type: int + default: 16 + description: The time (in ms) between generated inputs when an input has non-zero speed. delay-ms: type: int time-to-max-speed-ms: diff --git a/app/include/zmk/mouse.h b/app/include/zmk/mouse.h index 28dab60d143..c898f001098 100644 --- a/app/include/zmk/mouse.h +++ b/app/include/zmk/mouse.h @@ -10,5 +10,3 @@ typedef uint8_t zmk_mouse_button_flags_t; typedef uint16_t zmk_mouse_button_t; - -int zmk_mouse_init(void); diff --git a/app/src/behaviors/behavior_input_two_axis.c b/app/src/behaviors/behavior_input_two_axis.c index 5a6afe63384..0146df868ef 100644 --- a/app/src/behaviors/behavior_input_two_axis.c +++ b/app/src/behaviors/behavior_input_two_axis.c @@ -43,12 +43,13 @@ struct behavior_input_two_axis_data { struct behavior_input_two_axis_config { int16_t x_code; int16_t y_code; - int delay_ms; - int time_to_max_speed_ms; + uint16_t delay_ms; + uint16_t time_to_max_speed_ms; + uint8_t trigger_period_ms; // acceleration exponent 0: uniform speed // acceleration exponent 1: uniform acceleration // acceleration exponent 2: uniform jerk - int acceleration_exponent; + uint8_t acceleration_exponent; }; #if CONFIG_MINIMAL_LIBC @@ -107,10 +108,9 @@ static float update_movement_1d(const struct behavior_input_two_axis_config *con } int64_t move_duration = ms_since_start(state->start_time, now, config->delay_ms); - move = - (move_duration > 0) - ? (speed(config, state->speed, move_duration) * CONFIG_ZMK_MOUSE_TICK_DURATION / 1000) - : 0; + move = (move_duration > 0) + ? (speed(config, state->speed, move_duration) * config->trigger_period_ms / 1000) + : 0; track_remainder(&(move), &(state->remainder)); @@ -165,7 +165,7 @@ static void tick_work_cb(struct k_work *work) { } if (should_be_working(data)) { - k_work_schedule(&data->tick_work, K_MSEC(CONFIG_ZMK_MOUSE_TICK_DURATION)); + k_work_schedule(&data->tick_work, K_MSEC(cfg->trigger_period_ms)); } } @@ -183,11 +183,12 @@ static void set_start_times_for_activity(struct movement_state_2d *state) { static void update_work_scheduling(const struct device *dev) { struct behavior_input_two_axis_data *data = dev->data; + const struct behavior_input_two_axis_config *cfg = dev->config; set_start_times_for_activity(&data->state); if (should_be_working(data)) { - k_work_schedule(&data->tick_work, K_MSEC(CONFIG_ZMK_MOUSE_TICK_DURATION)); + k_work_schedule(&data->tick_work, K_MSEC(cfg->trigger_period_ms)); } else { k_work_cancel_delayable(&data->tick_work); } @@ -260,6 +261,7 @@ static const struct behavior_driver_api behavior_input_two_axis_driver_api = { static struct behavior_input_two_axis_config behavior_input_two_axis_config_##n = { \ .x_code = DT_INST_PROP(n, x_input_code), \ .y_code = DT_INST_PROP(n, y_input_code), \ + .trigger_period_ms = DT_INST_PROP(n, trigger_period_ms), \ .delay_ms = DT_INST_PROP_OR(n, delay_ms, 0), \ .time_to_max_speed_ms = DT_INST_PROP(n, time_to_max_speed_ms), \ .acceleration_exponent = DT_INST_PROP_OR(n, acceleration_exponent, 1), \ diff --git a/app/src/main.c b/app/src/main.c index a6ea2abd109..fcf1d11531f 100644 --- a/app/src/main.c +++ b/app/src/main.c @@ -30,8 +30,5 @@ int main(void) { zmk_display_init(); #endif /* CONFIG_ZMK_DISPLAY */ -#ifdef CONFIG_ZMK_MOUSE - zmk_mouse_init(); -#endif /* CONFIG_ZMK_MOUSE */ return 0; } diff --git a/app/src/mouse/Kconfig b/app/src/mouse/Kconfig index 972c0c6f4b5..2dbbf90c71c 100644 --- a/app/src/mouse/Kconfig +++ b/app/src/mouse/Kconfig @@ -1,40 +1,8 @@ # Copyright (c) 2023 The ZMK Contributors # SPDX-License-Identifier: MIT -menuconfig ZMK_MOUSE +config ZMK_MOUSE bool "Mouse Emulation" select INPUT select INPUT_THREAD_PRIORITY_OVERRIDE -if ZMK_MOUSE - -config ZMK_MOUSE_TICK_DURATION - int "Mouse tick duration in ms" - default 16 - - -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 diff --git a/app/src/mouse/main.c b/app/src/mouse/main.c deleted file mode 100644 index e1227425ea5..00000000000 --- a/app/src/mouse/main.c +++ /dev/null @@ -1,30 +0,0 @@ -/* - * Copyright (c) 2020 The ZMK Contributors - * - * SPDX-License-Identifier: MIT - */ - -#include -#include - -#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(void) { -#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; -} \ No newline at end of file From 77acca1981f7e33eb424e787ed4c3e65311fcbbf Mon Sep 17 00:00:00 2001 From: Peter Johanson Date: Mon, 11 Dec 2023 16:28:31 -0800 Subject: [PATCH 16/23] fix(mouse): Fixes for logging. * Corrected logging for two-axis input timestamps. --- app/src/behaviors/behavior_input_two_axis.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/app/src/behaviors/behavior_input_two_axis.c b/app/src/behaviors/behavior_input_two_axis.c index 0146df868ef..028373c2f34 100644 --- a/app/src/behaviors/behavior_input_two_axis.c +++ b/app/src/behaviors/behavior_input_two_axis.c @@ -145,10 +145,10 @@ static void tick_work_cb(struct k_work *work) { const struct device *dev = data->dev; const struct behavior_input_two_axis_config *cfg = dev->config; - uint32_t timestamp = k_uptime_get(); + uint64_t timestamp = k_uptime_get(); - LOG_INF("tick start times: %lld %lld %lld", data->state.x.start_time, data->state.y.start_time, - timestamp); + LOG_INF("x start: %llu, y start: %llu, current timestamp: %llu", data->state.x.start_time, + data->state.y.start_time, timestamp); struct vector2d move = update_movement_2d(cfg, &data->state, timestamp); From 5513584a4871941f15ebc80bd6c0e4a6ac43c9eb Mon Sep 17 00:00:00 2001 From: Peter Johanson Date: Mon, 12 Feb 2024 20:12:34 +0000 Subject: [PATCH 17/23] refactor: Proper per-device input listeners. * Buffer data from input devices and only surface to HID once synd'd. --- app/CMakeLists.txt | 3 +- app/dts/behaviors.dtsi | 2 - app/dts/behaviors/mouse_move.dtsi | 5 + app/dts/behaviors/mouse_scroll.dtsi | 5 + app/dts/bindings/zmk,input-configs.yaml | 24 --- app/dts/bindings/zmk,input-listener.yaml | 21 +++ app/include/zmk/mouse/input_config.h | 21 --- app/src/mouse/hid_input_listener.c | 110 ------------- app/src/mouse/input_config.c | 42 ----- app/src/mouse/input_listener.c | 188 +++++++++++++++++++++++ 10 files changed, 220 insertions(+), 201 deletions(-) delete mode 100644 app/dts/bindings/zmk,input-configs.yaml create mode 100644 app/dts/bindings/zmk,input-listener.yaml delete mode 100644 app/include/zmk/mouse/input_config.h delete mode 100644 app/src/mouse/hid_input_listener.c delete mode 100644 app/src/mouse/input_config.c create mode 100644 app/src/mouse/input_listener.c diff --git a/app/CMakeLists.txt b/app/CMakeLists.txt index e8ff7293efd..9edbe9c31d0 100644 --- a/app/CMakeLists.txt +++ b/app/CMakeLists.txt @@ -27,7 +27,6 @@ target_sources(app PRIVATE src/behavior.c) target_sources_ifdef(CONFIG_ZMK_KSCAN_SIDEBAND_BEHAVIORS app PRIVATE src/kscan_sideband_behaviors.c) target_sources(app PRIVATE src/matrix_transform.c) target_sources(app PRIVATE src/physical_layouts.c) -target_sources_ifdef(CONFIG_ZMK_MOUSE app PRIVATE src/mouse/input_config.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) @@ -44,7 +43,7 @@ target_sources_ifdef(CONFIG_ZMK_EXT_POWER app PRIVATE src/behaviors/behavior_ext target_sources_ifdef(CONFIG_ZMK_BEHAVIOR_SOFT_OFF app PRIVATE src/behaviors/behavior_soft_off.c) if ((NOT CONFIG_ZMK_SPLIT) OR CONFIG_ZMK_SPLIT_ROLE_CENTRAL) target_sources(app PRIVATE src/hid.c) - target_sources_ifdef(CONFIG_ZMK_MOUSE app PRIVATE src/mouse/hid_input_listener.c) + target_sources_ifdef(CONFIG_ZMK_MOUSE app PRIVATE src/mouse/input_listener.c) target_sources(app PRIVATE src/behaviors/behavior_key_press.c) target_sources_ifdef(CONFIG_ZMK_BEHAVIOR_KEY_TOGGLE app PRIVATE src/behaviors/behavior_key_toggle.c) target_sources(app PRIVATE src/behaviors/behavior_hold_tap.c) diff --git a/app/dts/behaviors.dtsi b/app/dts/behaviors.dtsi index 27bc78a6c2c..6695af40f86 100644 --- a/app/dts/behaviors.dtsi +++ b/app/dts/behaviors.dtsi @@ -21,6 +21,4 @@ #include #include #include -#include -#include #include diff --git a/app/dts/behaviors/mouse_move.dtsi b/app/dts/behaviors/mouse_move.dtsi index 0bfc87d04fd..c43455c3953 100644 --- a/app/dts/behaviors/mouse_move.dtsi +++ b/app/dts/behaviors/mouse_move.dtsi @@ -11,4 +11,9 @@ acceleration-exponent = <1>; }; }; + + mmv_input_listener { + compatible = "zmk,input-listener"; + device = <&mmv>; + }; }; diff --git a/app/dts/behaviors/mouse_scroll.dtsi b/app/dts/behaviors/mouse_scroll.dtsi index 69c48feeea3..27b6aafe1d8 100644 --- a/app/dts/behaviors/mouse_scroll.dtsi +++ b/app/dts/behaviors/mouse_scroll.dtsi @@ -11,4 +11,9 @@ acceleration-exponent = <0>; }; }; + + msc_input_listener { + compatible = "zmk,input-listener"; + device = <&msc>; + }; }; diff --git a/app/dts/bindings/zmk,input-configs.yaml b/app/dts/bindings/zmk,input-configs.yaml deleted file mode 100644 index 83bd4a1939d..00000000000 --- a/app/dts/bindings/zmk,input-configs.yaml +++ /dev/null @@ -1,24 +0,0 @@ -description: | - Allows post-processing of input events based on the configuration - -compatible: "zmk,input-configs" - -child-binding: - description: "A configuration for a given input device" - - properties: - device: - type: phandle - required: true - xy-swap: - type: boolean - x-invert: - type: boolean - y-invert: - type: boolean - scale-multiplier: - type: int - default: 1 - scale-divisor: - type: int - default: 1 diff --git a/app/dts/bindings/zmk,input-listener.yaml b/app/dts/bindings/zmk,input-listener.yaml new file mode 100644 index 00000000000..a883557db39 --- /dev/null +++ b/app/dts/bindings/zmk,input-listener.yaml @@ -0,0 +1,21 @@ +description: | + Listener to subscribe to input events and send HID updates after processing + +compatible: "zmk,input-listener" + +properties: + device: + type: phandle + required: true + xy-swap: + type: boolean + x-invert: + type: boolean + y-invert: + type: boolean + scale-multiplier: + type: int + default: 1 + scale-divisor: + type: int + default: 1 diff --git a/app/include/zmk/mouse/input_config.h b/app/include/zmk/mouse/input_config.h deleted file mode 100644 index 0a37b346caf..00000000000 --- a/app/include/zmk/mouse/input_config.h +++ /dev/null @@ -1,21 +0,0 @@ -/* - * Copyright (c) 2023 The ZMK Contributors - * - * SPDX-License-Identifier: MIT - */ - -#pragma once - -#include -#include - -struct zmk_input_config { - const struct device *dev; - bool xy_swap; - bool x_invert; - bool y_invert; - uint16_t scale_multiplier; - uint16_t scale_divisor; -}; - -const struct zmk_input_config *zmk_input_config_get_for_device(const struct device *dev); \ No newline at end of file diff --git a/app/src/mouse/hid_input_listener.c b/app/src/mouse/hid_input_listener.c deleted file mode 100644 index 712bb5675c2..00000000000 --- a/app/src/mouse/hid_input_listener.c +++ /dev/null @@ -1,110 +0,0 @@ -/* - * Copyright (c) 2020 The ZMK Contributors - * - * SPDX-License-Identifier: MIT - */ - -#include -#include -#include - -#include -#include -#include -#include - -void handle_rel_code(struct input_event *evt) { - switch (evt->code) { - case INPUT_REL_X: - zmk_hid_mouse_movement_update(evt->value, 0); - break; - case INPUT_REL_Y: - zmk_hid_mouse_movement_update(0, evt->value); - break; - case INPUT_REL_WHEEL: - zmk_hid_mouse_scroll_update(0, evt->value); - break; - case INPUT_REL_HWHEEL: - zmk_hid_mouse_scroll_update(evt->value, 0); - break; - default: - break; - } -} - -void handle_key_code(struct input_event *evt) { - int8_t btn; - - switch (evt->code) { - case INPUT_BTN_0: - case INPUT_BTN_1: - case INPUT_BTN_2: - case INPUT_BTN_3: - case INPUT_BTN_4: - btn = evt->code - INPUT_BTN_0; - if (evt->value > 0) { - zmk_hid_mouse_button_press(btn); - } else { - zmk_hid_mouse_button_release(btn); - } - break; - default: - break; - } -} - -static void swap_xy(struct input_event *evt) { - switch (evt->code) { - case INPUT_REL_X: - evt->code = INPUT_REL_Y; - break; - case INPUT_REL_Y: - evt->code = INPUT_REL_X; - break; - } -} - -static void filter_with_input_config(struct input_event *evt) { - if (!evt->dev) { - return; - } - - const struct zmk_input_config *cfg = zmk_input_config_get_for_device(evt->dev); - - if (!cfg) { - return; - } - - if (cfg->xy_swap) { - swap_xy(evt); - } - - if ((cfg->x_invert && evt->code == INPUT_REL_X) || - (cfg->y_invert && evt->code == INPUT_REL_Y)) { - evt->value = -(evt->value); - } - - evt->value = (int16_t)((evt->value * cfg->scale_multiplier) / cfg->scale_divisor); -} - -void input_handler(struct input_event *evt) { - // First, filter to update the event data as needed. - filter_with_input_config(evt); - - switch (evt->type) { - case INPUT_EV_REL: - handle_rel_code(evt); - break; - case INPUT_EV_KEY: - handle_key_code(evt); - break; - } - - if (evt->sync) { - zmk_endpoints_send_mouse_report(); - zmk_hid_mouse_scroll_set(0, 0); - zmk_hid_mouse_movement_set(0, 0); - } -} - -INPUT_CALLBACK_DEFINE(NULL, input_handler); diff --git a/app/src/mouse/input_config.c b/app/src/mouse/input_config.c deleted file mode 100644 index 745fb49bac5..00000000000 --- a/app/src/mouse/input_config.c +++ /dev/null @@ -1,42 +0,0 @@ -/* - * Copyright (c) 2023 The ZMK Contributors - * - * SPDX-License-Identifier: MIT - */ - -#include -#include - -#define DT_DRV_COMPAT zmk_input_configs - -#if DT_HAS_COMPAT_STATUS_OKAY(DT_DRV_COMPAT) - -#define CHILD_CONFIG(inst) \ - { \ - .dev = DEVICE_DT_GET(DT_PHANDLE(inst, device)), \ - .xy_swap = DT_PROP(inst, xy_swap), \ - .x_invert = DT_PROP(inst, x_invert), \ - .y_invert = DT_PROP(inst, y_invert), \ - .scale_multiplier = DT_PROP(inst, scale_multiplier), \ - .scale_divisor = DT_PROP(inst, scale_divisor), \ - }, - -const struct zmk_input_config configs[] = {DT_INST_FOREACH_CHILD(0, CHILD_CONFIG)}; - -const struct zmk_input_config *zmk_input_config_get_for_device(const struct device *dev) { - for (int i = 0; i < ARRAY_SIZE(configs); i++) { - if (configs[i].dev == dev) { - return &configs[i]; - } - } - - return NULL; -} - -#else - -const struct zmk_input_config *zmk_input_config_get_for_device(const struct device *dev) { - return NULL; -} - -#endif \ No newline at end of file diff --git a/app/src/mouse/input_listener.c b/app/src/mouse/input_listener.c new file mode 100644 index 00000000000..a00455bc9aa --- /dev/null +++ b/app/src/mouse/input_listener.c @@ -0,0 +1,188 @@ +/* + * Copyright (c) 2020 The ZMK Contributors + * + * SPDX-License-Identifier: MIT + */ + +#define DT_DRV_COMPAT zmk_input_listener + +#include +#include +#include +#include + +#include +#include +#include + +enum input_listener_xy_data_mode { + INPUT_LISTENER_XY_DATA_MODE_NONE, + INPUT_LISTENER_XY_DATA_MODE_REL, + INPUT_LISTENER_XY_DATA_MODE_ABS, +}; + +struct input_listener_xy_data { + enum input_listener_xy_data_mode mode; + int16_t x; + int16_t y; +}; + +struct input_listener_data { + struct input_listener_xy_data data; + struct input_listener_xy_data wheel_data; + + uint8_t button_set; + uint8_t button_clear; +}; + +struct input_listener_config { + bool xy_swap; + bool x_invert; + bool y_invert; + uint16_t scale_multiplier; + uint16_t scale_divisor; +}; + +static void handle_rel_code(struct input_listener_data *data, struct input_event *evt) { + switch (evt->code) { + case INPUT_REL_X: + data->data.mode = INPUT_LISTENER_XY_DATA_MODE_REL; + data->data.x += evt->value; + break; + case INPUT_REL_Y: + data->data.mode = INPUT_LISTENER_XY_DATA_MODE_REL; + data->data.y += evt->value; + break; + case INPUT_REL_WHEEL: + data->wheel_data.mode = INPUT_LISTENER_XY_DATA_MODE_REL; + data->wheel_data.y += evt->value; + break; + case INPUT_REL_HWHEEL: + data->wheel_data.mode = INPUT_LISTENER_XY_DATA_MODE_REL; + data->wheel_data.x += evt->value; + break; + default: + break; + } +} + +static void handle_key_code(struct input_listener_data *data, struct input_event *evt) { + int8_t btn; + + switch (evt->code) { + case INPUT_BTN_0: + case INPUT_BTN_1: + case INPUT_BTN_2: + case INPUT_BTN_3: + case INPUT_BTN_4: + btn = evt->code - INPUT_BTN_0; + if (evt->value > 0) { + WRITE_BIT(data->button_set, btn, 1); + } else { + WRITE_BIT(data->button_clear, btn, 1); + } + break; + default: + break; + } +} + +static void swap_xy(struct input_event *evt) { + switch (evt->code) { + case INPUT_REL_X: + evt->code = INPUT_REL_Y; + break; + case INPUT_REL_Y: + evt->code = INPUT_REL_X; + break; + } +} + +static void filter_with_input_config(const struct input_listener_config *cfg, + struct input_event *evt) { + if (!evt->dev) { + return; + } + + if (cfg->xy_swap) { + swap_xy(evt); + } + + if ((cfg->x_invert && evt->code == INPUT_REL_X) || + (cfg->y_invert && evt->code == INPUT_REL_Y)) { + evt->value = -(evt->value); + } + + evt->value = (int16_t)((evt->value * cfg->scale_multiplier) / cfg->scale_divisor); +} + +static void clear_xy_data(struct input_listener_xy_data *data) { + data->x = data->y = 0; + data->mode = INPUT_LISTENER_XY_DATA_MODE_NONE; +} + +static void input_handler(const struct input_listener_config *config, + struct input_listener_data *data, struct input_event *evt) { + // First, filter to update the event data as needed. + filter_with_input_config(config, evt); + + switch (evt->type) { + case INPUT_EV_REL: + handle_rel_code(data, evt); + break; + case INPUT_EV_KEY: + handle_key_code(data, evt); + break; + } + + if (evt->sync) { + if (data->wheel_data.mode == INPUT_LISTENER_XY_DATA_MODE_REL) { + zmk_hid_mouse_scroll_set(data->wheel_data.x, data->wheel_data.y); + } + + if (data->data.mode == INPUT_LISTENER_XY_DATA_MODE_REL) { + zmk_hid_mouse_movement_set(data->data.x, data->data.y); + } + + if (data->button_set != 0) { + for (int i = 0; i < ZMK_HID_MOUSE_NUM_BUTTONS; i++) { + if ((data->button_set & BIT(i)) != 0) { + zmk_hid_mouse_button_press(i); + } + } + } + + if (data->button_clear != 0) { + for (int i = 0; i < ZMK_HID_MOUSE_NUM_BUTTONS; i++) { + if ((data->button_set & BIT(i)) != 0) { + zmk_hid_mouse_button_release(i); + } + } + } + + zmk_endpoints_send_mouse_report(); + zmk_hid_mouse_scroll_set(0, 0); + zmk_hid_mouse_movement_set(0, 0); + + clear_xy_data(&data->data); + clear_xy_data(&data->wheel_data); + + data->button_set = data->button_clear = 0; + } +} + +#define IL_INST(n) \ + static const struct input_listener_config config_##n = { \ + .xy_swap = DT_INST_PROP(n, xy_swap), \ + .x_invert = DT_INST_PROP(n, x_invert), \ + .y_invert = DT_INST_PROP(n, y_invert), \ + .scale_multiplier = DT_INST_PROP(n, scale_multiplier), \ + .scale_divisor = DT_INST_PROP(n, scale_divisor), \ + }; \ + static struct input_listener_data data_##n = {}; \ + void input_handler_##n(struct input_event *evt) { \ + input_handler(&config_##n, &data_##n, evt); \ + } \ + INPUT_CALLBACK_DEFINE(DEVICE_DT_GET(DT_INST_PHANDLE(n, device)), input_handler_##n); + +DT_INST_FOREACH_STATUS_OKAY(IL_INST) From f8fa8aa1b9bf4b229c1a3784fe585c0c53b13cc6 Mon Sep 17 00:00:00 2001 From: Peter Johanson Date: Mon, 19 Feb 2024 01:55:06 -0800 Subject: [PATCH 18/23] chore: Minor input behavior clean-up. --- app/src/behaviors/behavior_input_two_axis.c | 15 +++------------ 1 file changed, 3 insertions(+), 12 deletions(-) diff --git a/app/src/behaviors/behavior_input_two_axis.c b/app/src/behaviors/behavior_input_two_axis.c index 028373c2f34..eaf84a766af 100644 --- a/app/src/behaviors/behavior_input_two_axis.c +++ b/app/src/behaviors/behavior_input_two_axis.c @@ -194,7 +194,7 @@ static void update_work_scheduling(const struct device *dev) { } } -int zmk_input_synth_pointer_adjust_speed(const struct device *dev, int16_t dx, int16_t dy) { +int behavior_input_two_axis_adjust_speed(const struct device *dev, int16_t dx, int16_t dy) { struct behavior_input_two_axis_data *data = dev->data; LOG_DBG("Adjusting: %d %d", dx, dy); @@ -208,15 +208,6 @@ int zmk_input_synth_pointer_adjust_speed(const struct device *dev, int16_t dx, i return 0; } -// static void process_key_state(const struct device *dev, int32_t val, bool pressed) { -// for (int i = 0; i < ZMK_HID_MOUSE_NUM_BUTTONS; i++) { -// if (val & BIT(i)) { -// WRITE_BIT(val, i, 0); -// input_report_key(dev, INPUT_BTN_0 + i, pressed ? 1 : 0, val == 0, K_FOREVER); -// } -// } -// } - static int behavior_input_two_axis_init(const struct device *dev) { struct behavior_input_two_axis_data *data = dev->data; @@ -236,7 +227,7 @@ static int on_keymap_binding_pressed(struct zmk_behavior_binding *binding, int16_t x = MOVE_X_DECODE(binding->param1); int16_t y = MOVE_Y_DECODE(binding->param1); - zmk_input_synth_pointer_adjust_speed(behavior_dev, x, y); + behavior_input_two_axis_adjust_speed(behavior_dev, x, y); return 0; } @@ -249,7 +240,7 @@ static int on_keymap_binding_released(struct zmk_behavior_binding *binding, int16_t x = MOVE_X_DECODE(binding->param1); int16_t y = MOVE_Y_DECODE(binding->param1); - zmk_input_synth_pointer_adjust_speed(behavior_dev, -x, -y); + behavior_input_two_axis_adjust_speed(behavior_dev, -x, -y); return 0; } From dad5926fd0a88980de3969b7d8db1682e0bd8514 Mon Sep 17 00:00:00 2001 From: Peter Johanson Date: Mon, 19 Feb 2024 10:23:16 +0000 Subject: [PATCH 19/23] fix: Testing fixes for listener refactor. --- app/dts/behaviors.dtsi | 8 ++++++-- app/dts/behaviors/mouse_key_press.dtsi | 5 +++++ app/dts/behaviors/mouse_keys.dtsi | 9 +++++++++ app/dts/behaviors/mouse_move.dtsi | 6 ++++++ app/dts/behaviors/mouse_scroll.dtsi | 6 ++++++ app/src/mouse/input_listener.c | 2 +- app/tests/mouse-keys/mkp/native_posix_64.keymap | 1 + .../keycode_events.snapshot | 16 ++++++---------- .../move_diagonal_scaling/native_posix_64.keymap | 1 + .../keycode_events.snapshot | 16 ++++++---------- .../native_posix_64.keymap | 1 + .../keycode_events.snapshot | 16 ++++++---------- .../move_diagonal_xy_swap/native_posix_64.keymap | 1 + .../move_diagonal/keycode_events.snapshot | 16 ++++++---------- .../move_diagonal/native_posix_64.keymap | 1 + .../mouse-move/move_x/keycode_events.snapshot | 16 ++++++++-------- .../mouse-move/move_x/native_posix_64.keymap | 1 + .../mouse-move/move_y/keycode_events.snapshot | 16 ++++++++-------- .../mouse-move/move_y/native_posix_64.keymap | 1 + 19 files changed, 80 insertions(+), 59 deletions(-) create mode 100644 app/dts/behaviors/mouse_keys.dtsi diff --git a/app/dts/behaviors.dtsi b/app/dts/behaviors.dtsi index 6695af40f86..6ff4b69cf95 100644 --- a/app/dts/behaviors.dtsi +++ b/app/dts/behaviors.dtsi @@ -1,3 +1,9 @@ +/* + * Copyright (c) 2024 The ZMK Contributors + * + * SPDX-License-Identifier: MIT + */ + #include #include #include @@ -19,6 +25,4 @@ #include #include #include -#include #include -#include diff --git a/app/dts/behaviors/mouse_key_press.dtsi b/app/dts/behaviors/mouse_key_press.dtsi index 975c24aaafb..729166eab40 100644 --- a/app/dts/behaviors/mouse_key_press.dtsi +++ b/app/dts/behaviors/mouse_key_press.dtsi @@ -5,4 +5,9 @@ #binding-cells = <1>; }; }; + + mkp_input_listener { + compatible = "zmk,input-listener"; + device = <&mkp>; + }; }; diff --git a/app/dts/behaviors/mouse_keys.dtsi b/app/dts/behaviors/mouse_keys.dtsi new file mode 100644 index 00000000000..f9a99fede0c --- /dev/null +++ b/app/dts/behaviors/mouse_keys.dtsi @@ -0,0 +1,9 @@ +/* + * Copyright (c) 2024 The ZMK Contributors + * + * SPDX-License-Identifier: MIT + */ + +#include "mouse_key_press.dtsi" +#include "mouse_move.dtsi" +#include "mouse_scroll.dtsi" \ No newline at end of file diff --git a/app/dts/behaviors/mouse_move.dtsi b/app/dts/behaviors/mouse_move.dtsi index c43455c3953..47dc6cece92 100644 --- a/app/dts/behaviors/mouse_move.dtsi +++ b/app/dts/behaviors/mouse_move.dtsi @@ -1,3 +1,9 @@ +/* + * Copyright (c) 2024 The ZMK Contributors + * + * SPDX-License-Identifier: MIT + */ + #include / { diff --git a/app/dts/behaviors/mouse_scroll.dtsi b/app/dts/behaviors/mouse_scroll.dtsi index 27b6aafe1d8..e9d141f845c 100644 --- a/app/dts/behaviors/mouse_scroll.dtsi +++ b/app/dts/behaviors/mouse_scroll.dtsi @@ -1,3 +1,9 @@ +/* + * Copyright (c) 2024 The ZMK Contributors + * + * SPDX-License-Identifier: MIT + */ + #include / { diff --git a/app/src/mouse/input_listener.c b/app/src/mouse/input_listener.c index a00455bc9aa..0acc434997f 100644 --- a/app/src/mouse/input_listener.c +++ b/app/src/mouse/input_listener.c @@ -154,7 +154,7 @@ static void input_handler(const struct input_listener_config *config, if (data->button_clear != 0) { for (int i = 0; i < ZMK_HID_MOUSE_NUM_BUTTONS; i++) { - if ((data->button_set & BIT(i)) != 0) { + if ((data->button_clear & BIT(i)) != 0) { zmk_hid_mouse_button_release(i); } } diff --git a/app/tests/mouse-keys/mkp/native_posix_64.keymap b/app/tests/mouse-keys/mkp/native_posix_64.keymap index 8e3071d4317..8b955846963 100644 --- a/app/tests/mouse-keys/mkp/native_posix_64.keymap +++ b/app/tests/mouse-keys/mkp/native_posix_64.keymap @@ -1,4 +1,5 @@ #include +#include #include #include #include diff --git a/app/tests/mouse-keys/mouse-move/input-configs/move_diagonal_scaling/keycode_events.snapshot b/app/tests/mouse-keys/mouse-move/input-configs/move_diagonal_scaling/keycode_events.snapshot index 99fa148876f..6b9fa770b11 100644 --- a/app/tests/mouse-keys/mouse-move/input-configs/move_diagonal_scaling/keycode_events.snapshot +++ b/app/tests/mouse-keys/mouse-move/input-configs/move_diagonal_scaling/keycode_events.snapshot @@ -1,22 +1,18 @@ -movement_update: Mouse movement updated to -1/0 +movement_set: Mouse movement set to -1/0 scroll_set: Mouse scroll set to 0/0 movement_set: Mouse movement set to 0/0 -movement_update: Mouse movement updated to -3/0 -movement_update: Mouse movement updated to -3/-3 +movement_set: Mouse movement set to -2/-2 scroll_set: Mouse scroll set to 0/0 movement_set: Mouse movement set to 0/0 -movement_update: Mouse movement updated to -3/0 -movement_update: Mouse movement updated to -3/-3 +movement_set: Mouse movement set to -2/-2 scroll_set: Mouse scroll set to 0/0 movement_set: Mouse movement set to 0/0 -movement_update: Mouse movement updated to -5/0 -movement_update: Mouse movement updated to -5/-3 +movement_set: Mouse movement set to -3/-2 scroll_set: Mouse scroll set to 0/0 movement_set: Mouse movement set to 0/0 -movement_update: Mouse movement updated to -5/0 -movement_update: Mouse movement updated to -5/-5 +movement_set: Mouse movement set to -3/-3 scroll_set: Mouse scroll set to 0/0 movement_set: Mouse movement set to 0/0 -movement_update: Mouse movement updated to 0/-5 +movement_set: Mouse movement set to 0/-3 scroll_set: Mouse scroll set to 0/0 movement_set: Mouse movement set to 0/0 diff --git a/app/tests/mouse-keys/mouse-move/input-configs/move_diagonal_scaling/native_posix_64.keymap b/app/tests/mouse-keys/mouse-move/input-configs/move_diagonal_scaling/native_posix_64.keymap index b1ddbc1d8d0..6351799bd2e 100644 --- a/app/tests/mouse-keys/mouse-move/input-configs/move_diagonal_scaling/native_posix_64.keymap +++ b/app/tests/mouse-keys/mouse-move/input-configs/move_diagonal_scaling/native_posix_64.keymap @@ -1,4 +1,5 @@ #include +#include #include #include #include diff --git a/app/tests/mouse-keys/mouse-move/input-configs/move_diagonal_xy_invert/keycode_events.snapshot b/app/tests/mouse-keys/mouse-move/input-configs/move_diagonal_xy_invert/keycode_events.snapshot index 94faacceb61..6b9fa770b11 100644 --- a/app/tests/mouse-keys/mouse-move/input-configs/move_diagonal_xy_invert/keycode_events.snapshot +++ b/app/tests/mouse-keys/mouse-move/input-configs/move_diagonal_xy_invert/keycode_events.snapshot @@ -1,22 +1,18 @@ -movement_update: Mouse movement updated to 1/0 +movement_set: Mouse movement set to -1/0 scroll_set: Mouse scroll set to 0/0 movement_set: Mouse movement set to 0/0 -movement_update: Mouse movement updated to 2/0 -movement_update: Mouse movement updated to 2/2 +movement_set: Mouse movement set to -2/-2 scroll_set: Mouse scroll set to 0/0 movement_set: Mouse movement set to 0/0 -movement_update: Mouse movement updated to 2/0 -movement_update: Mouse movement updated to 2/2 +movement_set: Mouse movement set to -2/-2 scroll_set: Mouse scroll set to 0/0 movement_set: Mouse movement set to 0/0 -movement_update: Mouse movement updated to 3/0 -movement_update: Mouse movement updated to 3/2 +movement_set: Mouse movement set to -3/-2 scroll_set: Mouse scroll set to 0/0 movement_set: Mouse movement set to 0/0 -movement_update: Mouse movement updated to 3/0 -movement_update: Mouse movement updated to 3/3 +movement_set: Mouse movement set to -3/-3 scroll_set: Mouse scroll set to 0/0 movement_set: Mouse movement set to 0/0 -movement_update: Mouse movement updated to 0/3 +movement_set: Mouse movement set to 0/-3 scroll_set: Mouse scroll set to 0/0 movement_set: Mouse movement set to 0/0 diff --git a/app/tests/mouse-keys/mouse-move/input-configs/move_diagonal_xy_invert/native_posix_64.keymap b/app/tests/mouse-keys/mouse-move/input-configs/move_diagonal_xy_invert/native_posix_64.keymap index 4767e0ac55e..3392dd0de2e 100644 --- a/app/tests/mouse-keys/mouse-move/input-configs/move_diagonal_xy_invert/native_posix_64.keymap +++ b/app/tests/mouse-keys/mouse-move/input-configs/move_diagonal_xy_invert/native_posix_64.keymap @@ -1,4 +1,5 @@ #include +#include #include #include #include diff --git a/app/tests/mouse-keys/mouse-move/input-configs/move_diagonal_xy_swap/keycode_events.snapshot b/app/tests/mouse-keys/mouse-move/input-configs/move_diagonal_xy_swap/keycode_events.snapshot index 5ca5300c1a7..6b9fa770b11 100644 --- a/app/tests/mouse-keys/mouse-move/input-configs/move_diagonal_xy_swap/keycode_events.snapshot +++ b/app/tests/mouse-keys/mouse-move/input-configs/move_diagonal_xy_swap/keycode_events.snapshot @@ -1,22 +1,18 @@ -movement_update: Mouse movement updated to 0/-1 +movement_set: Mouse movement set to -1/0 scroll_set: Mouse scroll set to 0/0 movement_set: Mouse movement set to 0/0 -movement_update: Mouse movement updated to 0/-2 -movement_update: Mouse movement updated to -2/-2 +movement_set: Mouse movement set to -2/-2 scroll_set: Mouse scroll set to 0/0 movement_set: Mouse movement set to 0/0 -movement_update: Mouse movement updated to 0/-2 -movement_update: Mouse movement updated to -2/-2 +movement_set: Mouse movement set to -2/-2 scroll_set: Mouse scroll set to 0/0 movement_set: Mouse movement set to 0/0 -movement_update: Mouse movement updated to 0/-3 -movement_update: Mouse movement updated to -2/-3 +movement_set: Mouse movement set to -3/-2 scroll_set: Mouse scroll set to 0/0 movement_set: Mouse movement set to 0/0 -movement_update: Mouse movement updated to 0/-3 -movement_update: Mouse movement updated to -3/-3 +movement_set: Mouse movement set to -3/-3 scroll_set: Mouse scroll set to 0/0 movement_set: Mouse movement set to 0/0 -movement_update: Mouse movement updated to -3/0 +movement_set: Mouse movement set to 0/-3 scroll_set: Mouse scroll set to 0/0 movement_set: Mouse movement set to 0/0 diff --git a/app/tests/mouse-keys/mouse-move/input-configs/move_diagonal_xy_swap/native_posix_64.keymap b/app/tests/mouse-keys/mouse-move/input-configs/move_diagonal_xy_swap/native_posix_64.keymap index 153270231fa..d4cf5031252 100644 --- a/app/tests/mouse-keys/mouse-move/input-configs/move_diagonal_xy_swap/native_posix_64.keymap +++ b/app/tests/mouse-keys/mouse-move/input-configs/move_diagonal_xy_swap/native_posix_64.keymap @@ -1,4 +1,5 @@ #include +#include #include #include #include diff --git a/app/tests/mouse-keys/mouse-move/move_diagonal/keycode_events.snapshot b/app/tests/mouse-keys/mouse-move/move_diagonal/keycode_events.snapshot index 91c4f16b9c0..6b9fa770b11 100644 --- a/app/tests/mouse-keys/mouse-move/move_diagonal/keycode_events.snapshot +++ b/app/tests/mouse-keys/mouse-move/move_diagonal/keycode_events.snapshot @@ -1,22 +1,18 @@ -movement_update: Mouse movement updated to -1/0 +movement_set: Mouse movement set to -1/0 scroll_set: Mouse scroll set to 0/0 movement_set: Mouse movement set to 0/0 -movement_update: Mouse movement updated to -2/0 -movement_update: Mouse movement updated to -2/-2 +movement_set: Mouse movement set to -2/-2 scroll_set: Mouse scroll set to 0/0 movement_set: Mouse movement set to 0/0 -movement_update: Mouse movement updated to -2/0 -movement_update: Mouse movement updated to -2/-2 +movement_set: Mouse movement set to -2/-2 scroll_set: Mouse scroll set to 0/0 movement_set: Mouse movement set to 0/0 -movement_update: Mouse movement updated to -3/0 -movement_update: Mouse movement updated to -3/-2 +movement_set: Mouse movement set to -3/-2 scroll_set: Mouse scroll set to 0/0 movement_set: Mouse movement set to 0/0 -movement_update: Mouse movement updated to -3/0 -movement_update: Mouse movement updated to -3/-3 +movement_set: Mouse movement set to -3/-3 scroll_set: Mouse scroll set to 0/0 movement_set: Mouse movement set to 0/0 -movement_update: Mouse movement updated to 0/-3 +movement_set: Mouse movement set to 0/-3 scroll_set: Mouse scroll set to 0/0 movement_set: Mouse movement set to 0/0 diff --git a/app/tests/mouse-keys/mouse-move/move_diagonal/native_posix_64.keymap b/app/tests/mouse-keys/mouse-move/move_diagonal/native_posix_64.keymap index 9dfa5b2a81c..7e4d7af2a1d 100644 --- a/app/tests/mouse-keys/mouse-move/move_diagonal/native_posix_64.keymap +++ b/app/tests/mouse-keys/mouse-move/move_diagonal/native_posix_64.keymap @@ -1,4 +1,5 @@ #include +#include #include #include #include diff --git a/app/tests/mouse-keys/mouse-move/move_x/keycode_events.snapshot b/app/tests/mouse-keys/mouse-move/move_x/keycode_events.snapshot index dfbc4a76eab..678f71c9ac2 100644 --- a/app/tests/mouse-keys/mouse-move/move_x/keycode_events.snapshot +++ b/app/tests/mouse-keys/mouse-move/move_x/keycode_events.snapshot @@ -1,24 +1,24 @@ -movement_update: Mouse movement updated to -1/0 +movement_set: Mouse movement set to -1/0 scroll_set: Mouse scroll set to 0/0 movement_set: Mouse movement set to 0/0 -movement_update: Mouse movement updated to -2/0 +movement_set: Mouse movement set to -2/0 scroll_set: Mouse scroll set to 0/0 movement_set: Mouse movement set to 0/0 -movement_update: Mouse movement updated to -2/0 +movement_set: Mouse movement set to -2/0 scroll_set: Mouse scroll set to 0/0 movement_set: Mouse movement set to 0/0 -movement_update: Mouse movement updated to -3/0 +movement_set: Mouse movement set to -3/0 scroll_set: Mouse scroll set to 0/0 movement_set: Mouse movement set to 0/0 -movement_update: Mouse movement updated to 1/0 +movement_set: Mouse movement set to 1/0 scroll_set: Mouse scroll set to 0/0 movement_set: Mouse movement set to 0/0 -movement_update: Mouse movement updated to 2/0 +movement_set: Mouse movement set to 2/0 scroll_set: Mouse scroll set to 0/0 movement_set: Mouse movement set to 0/0 -movement_update: Mouse movement updated to 2/0 +movement_set: Mouse movement set to 2/0 scroll_set: Mouse scroll set to 0/0 movement_set: Mouse movement set to 0/0 -movement_update: Mouse movement updated to 3/0 +movement_set: Mouse movement set to 3/0 scroll_set: Mouse scroll set to 0/0 movement_set: Mouse movement set to 0/0 diff --git a/app/tests/mouse-keys/mouse-move/move_x/native_posix_64.keymap b/app/tests/mouse-keys/mouse-move/move_x/native_posix_64.keymap index aacacebba90..89d50e2b839 100644 --- a/app/tests/mouse-keys/mouse-move/move_x/native_posix_64.keymap +++ b/app/tests/mouse-keys/mouse-move/move_x/native_posix_64.keymap @@ -1,4 +1,5 @@ #include +#include #include #include #include diff --git a/app/tests/mouse-keys/mouse-move/move_y/keycode_events.snapshot b/app/tests/mouse-keys/mouse-move/move_y/keycode_events.snapshot index 4fd9a25202c..d20154d5507 100644 --- a/app/tests/mouse-keys/mouse-move/move_y/keycode_events.snapshot +++ b/app/tests/mouse-keys/mouse-move/move_y/keycode_events.snapshot @@ -1,24 +1,24 @@ -movement_update: Mouse movement updated to 0/-1 +movement_set: Mouse movement set to 0/-1 scroll_set: Mouse scroll set to 0/0 movement_set: Mouse movement set to 0/0 -movement_update: Mouse movement updated to 0/-2 +movement_set: Mouse movement set to 0/-2 scroll_set: Mouse scroll set to 0/0 movement_set: Mouse movement set to 0/0 -movement_update: Mouse movement updated to 0/-2 +movement_set: Mouse movement set to 0/-2 scroll_set: Mouse scroll set to 0/0 movement_set: Mouse movement set to 0/0 -movement_update: Mouse movement updated to 0/-3 +movement_set: Mouse movement set to 0/-3 scroll_set: Mouse scroll set to 0/0 movement_set: Mouse movement set to 0/0 -movement_update: Mouse movement updated to 0/1 +movement_set: Mouse movement set to 0/1 scroll_set: Mouse scroll set to 0/0 movement_set: Mouse movement set to 0/0 -movement_update: Mouse movement updated to 0/2 +movement_set: Mouse movement set to 0/2 scroll_set: Mouse scroll set to 0/0 movement_set: Mouse movement set to 0/0 -movement_update: Mouse movement updated to 0/2 +movement_set: Mouse movement set to 0/2 scroll_set: Mouse scroll set to 0/0 movement_set: Mouse movement set to 0/0 -movement_update: Mouse movement updated to 0/3 +movement_set: Mouse movement set to 0/3 scroll_set: Mouse scroll set to 0/0 movement_set: Mouse movement set to 0/0 diff --git a/app/tests/mouse-keys/mouse-move/move_y/native_posix_64.keymap b/app/tests/mouse-keys/mouse-move/move_y/native_posix_64.keymap index 1d914136e28..5b02246b05f 100644 --- a/app/tests/mouse-keys/mouse-move/move_y/native_posix_64.keymap +++ b/app/tests/mouse-keys/mouse-move/move_y/native_posix_64.keymap @@ -1,4 +1,5 @@ #include +#include #include #include #include From 0c3536cfc7c5a6d6c328ec17938b66a54a88fcf5 Mon Sep 17 00:00:00 2001 From: Peter Johanson Date: Wed, 10 Apr 2024 02:13:51 +0000 Subject: [PATCH 20/23] refactor(mouse): Tweak behavior inclusion, listener code * Always import mouse keys behavior and their associated listeners. * Tweak listener code to only add listener nodes when listener and the associated input device are enabled. --- app/dts/behaviors.dtsi | 1 + app/dts/behaviors/mouse_key_press.dtsi | 2 +- app/dts/behaviors/mouse_move.dtsi | 2 +- app/dts/behaviors/mouse_scroll.dtsi | 3 ++- app/src/mouse/input_listener.c | 36 +++++++++++++++++--------- 5 files changed, 29 insertions(+), 15 deletions(-) diff --git a/app/dts/behaviors.dtsi b/app/dts/behaviors.dtsi index 6ff4b69cf95..7009d2fff8f 100644 --- a/app/dts/behaviors.dtsi +++ b/app/dts/behaviors.dtsi @@ -26,3 +26,4 @@ #include #include #include +#include diff --git a/app/dts/behaviors/mouse_key_press.dtsi b/app/dts/behaviors/mouse_key_press.dtsi index 729166eab40..66e327e849e 100644 --- a/app/dts/behaviors/mouse_key_press.dtsi +++ b/app/dts/behaviors/mouse_key_press.dtsi @@ -6,7 +6,7 @@ }; }; - mkp_input_listener { + mkp_input_listener: mkp_input_listener { compatible = "zmk,input-listener"; device = <&mkp>; }; diff --git a/app/dts/behaviors/mouse_move.dtsi b/app/dts/behaviors/mouse_move.dtsi index 47dc6cece92..09b93520c1f 100644 --- a/app/dts/behaviors/mouse_move.dtsi +++ b/app/dts/behaviors/mouse_move.dtsi @@ -18,7 +18,7 @@ }; }; - mmv_input_listener { + mmv_input_listener: mmv_input_listener { compatible = "zmk,input-listener"; device = <&mmv>; }; diff --git a/app/dts/behaviors/mouse_scroll.dtsi b/app/dts/behaviors/mouse_scroll.dtsi index e9d141f845c..b482efded67 100644 --- a/app/dts/behaviors/mouse_scroll.dtsi +++ b/app/dts/behaviors/mouse_scroll.dtsi @@ -1,3 +1,4 @@ + /* * Copyright (c) 2024 The ZMK Contributors * @@ -18,7 +19,7 @@ }; }; - msc_input_listener { + msc_input_listener: msc_input_listener { compatible = "zmk,input-listener"; device = <&msc>; }; diff --git a/app/src/mouse/input_listener.c b/app/src/mouse/input_listener.c index 0acc434997f..6863e00eac4 100644 --- a/app/src/mouse/input_listener.c +++ b/app/src/mouse/input_listener.c @@ -15,6 +15,13 @@ #include #include +#define ONE_IF_DEV_OK(n) \ + COND_CODE_1(DT_NODE_HAS_STATUS(DT_INST_PHANDLE(n, device), okay), (1 +), (0 +)) + +#define VALID_LISTENER_COUNT (DT_INST_FOREACH_STATUS_OKAY(ONE_IF_DEV_OK) 0) + +#if VALID_LISTENER_COUNT > 0 + enum input_listener_xy_data_mode { INPUT_LISTENER_XY_DATA_MODE_NONE, INPUT_LISTENER_XY_DATA_MODE_REL, @@ -171,18 +178,23 @@ static void input_handler(const struct input_listener_config *config, } } +#endif // VALID_LISTENER_COUNT > 0 + #define IL_INST(n) \ - static const struct input_listener_config config_##n = { \ - .xy_swap = DT_INST_PROP(n, xy_swap), \ - .x_invert = DT_INST_PROP(n, x_invert), \ - .y_invert = DT_INST_PROP(n, y_invert), \ - .scale_multiplier = DT_INST_PROP(n, scale_multiplier), \ - .scale_divisor = DT_INST_PROP(n, scale_divisor), \ - }; \ - static struct input_listener_data data_##n = {}; \ - void input_handler_##n(struct input_event *evt) { \ - input_handler(&config_##n, &data_##n, evt); \ - } \ - INPUT_CALLBACK_DEFINE(DEVICE_DT_GET(DT_INST_PHANDLE(n, device)), input_handler_##n); + COND_CODE_1( \ + DT_NODE_HAS_STATUS(DT_INST_PHANDLE(n, device), okay), \ + (static const struct input_listener_config config_##n = \ + { \ + .xy_swap = DT_INST_PROP(n, xy_swap), \ + .x_invert = DT_INST_PROP(n, x_invert), \ + .y_invert = DT_INST_PROP(n, y_invert), \ + .scale_multiplier = DT_INST_PROP(n, scale_multiplier), \ + .scale_divisor = DT_INST_PROP(n, scale_divisor), \ + }; \ + static struct input_listener_data data_##n = {}; \ + void input_handler_##n(struct input_event *evt) { \ + input_handler(&config_##n, &data_##n, evt); \ + } INPUT_CALLBACK_DEFINE(DEVICE_DT_GET(DT_INST_PHANDLE(n, device)), input_handler_##n);), \ + ()) DT_INST_FOREACH_STATUS_OKAY(IL_INST) From 37ba9eb527d842f98abfc27ea56e86050ffe8dc7 Mon Sep 17 00:00:00 2001 From: Peter Johanson Date: Tue, 9 Apr 2024 21:43:07 -0700 Subject: [PATCH 21/23] fix(mouse): Fix up tests and make ZMK_MOUSE explicit. --- app/Kconfig.behaviors | 6 ++---- app/include/zmk/hid.h | 4 ---- app/tests/mouse-keys/mkp/native_posix_64.conf | 6 ++++++ app/tests/mouse-keys/mkp/native_posix_64.keymap | 1 - .../keycode_events.snapshot | 10 +++++----- .../move_diagonal_scaling/native_posix_64.conf | 6 ++++++ .../move_diagonal_scaling/native_posix_64.keymap | 16 +++++----------- .../keycode_events.snapshot | 12 ++++++------ .../move_diagonal_xy_invert/native_posix_64.conf | 6 ++++++ .../native_posix_64.keymap | 16 +++++----------- .../keycode_events.snapshot | 6 +++--- .../move_diagonal_xy_swap/native_posix_64.conf | 6 ++++++ .../move_diagonal_xy_swap/native_posix_64.keymap | 14 ++++---------- .../move_diagonal/native_posix_64.conf | 6 ++++++ .../mouse-move/move_x/native_posix_64.conf | 6 ++++++ .../mouse-move/move_y/native_posix_64.conf | 6 ++++++ 16 files changed, 72 insertions(+), 55 deletions(-) create mode 100644 app/tests/mouse-keys/mkp/native_posix_64.conf create mode 100644 app/tests/mouse-keys/mouse-move/input-configs/move_diagonal_scaling/native_posix_64.conf create mode 100644 app/tests/mouse-keys/mouse-move/input-configs/move_diagonal_xy_invert/native_posix_64.conf create mode 100644 app/tests/mouse-keys/mouse-move/input-configs/move_diagonal_xy_swap/native_posix_64.conf create mode 100644 app/tests/mouse-keys/mouse-move/move_diagonal/native_posix_64.conf create mode 100644 app/tests/mouse-keys/mouse-move/move_x/native_posix_64.conf create mode 100644 app/tests/mouse-keys/mouse-move/move_y/native_posix_64.conf diff --git a/app/Kconfig.behaviors b/app/Kconfig.behaviors index 3aa256a9475..ebc388c9351 100644 --- a/app/Kconfig.behaviors +++ b/app/Kconfig.behaviors @@ -48,8 +48,7 @@ config ZMK_BEHAVIOR_KEY_TOGGLE config ZMK_BEHAVIOR_MOUSE_KEY_PRESS bool default y - depends on DT_HAS_ZMK_BEHAVIOR_MOUSE_KEY_PRESS_ENABLED - imply ZMK_MOUSE + depends on DT_HAS_ZMK_BEHAVIOR_MOUSE_KEY_PRESS_ENABLED && ZMK_MOUSE config ZMK_BEHAVIOR_SOFT_OFF bool @@ -59,8 +58,7 @@ config ZMK_BEHAVIOR_SOFT_OFF config ZMK_BEHAVIOR_INPUT_TWO_AXIS bool default y - depends on DT_HAS_ZMK_BEHAVIOR_INPUT_TWO_AXIS_ENABLED - imply ZMK_MOUSE + depends on DT_HAS_ZMK_BEHAVIOR_INPUT_TWO_AXIS_ENABLED && ZMK_MOUSE config ZMK_BEHAVIOR_SENSOR_ROTATE_COMMON bool diff --git a/app/include/zmk/hid.h b/app/include/zmk/hid.h index aa59f369efe..efc6664ed8e 100644 --- a/app/include/zmk/hid.h +++ b/app/include/zmk/hid.h @@ -61,10 +61,6 @@ #define ZMK_HID_REPORT_ID_CONSUMER 0x02 #define ZMK_HID_REPORT_ID_MOUSE 0x03 -// Needed until Zephyr offers a 2 byte usage macro -#define HID_USAGE16(idx) \ - HID_ITEM(HID_ITEM_TAG_USAGE, HID_ITEM_TYPE_LOCAL, 2), (idx & 0xFF), (idx >> 8 & 0xFF) - static const uint8_t zmk_hid_report_desc[] = { HID_USAGE_PAGE(HID_USAGE_GEN_DESKTOP), HID_USAGE(HID_USAGE_GD_KEYBOARD), diff --git a/app/tests/mouse-keys/mkp/native_posix_64.conf b/app/tests/mouse-keys/mkp/native_posix_64.conf new file mode 100644 index 00000000000..65ed54bb204 --- /dev/null +++ b/app/tests/mouse-keys/mkp/native_posix_64.conf @@ -0,0 +1,6 @@ +CONFIG_GPIO=n +CONFIG_ZMK_BLE=n +CONFIG_LOG=y +CONFIG_LOG_BACKEND_SHOW_COLOR=n +CONFIG_ZMK_LOG_LEVEL_DBG=y +CONFIG_ZMK_MOUSE=y diff --git a/app/tests/mouse-keys/mkp/native_posix_64.keymap b/app/tests/mouse-keys/mkp/native_posix_64.keymap index 8b955846963..8e3071d4317 100644 --- a/app/tests/mouse-keys/mkp/native_posix_64.keymap +++ b/app/tests/mouse-keys/mkp/native_posix_64.keymap @@ -1,5 +1,4 @@ #include -#include #include #include #include diff --git a/app/tests/mouse-keys/mouse-move/input-configs/move_diagonal_scaling/keycode_events.snapshot b/app/tests/mouse-keys/mouse-move/input-configs/move_diagonal_scaling/keycode_events.snapshot index 6b9fa770b11..15d31600960 100644 --- a/app/tests/mouse-keys/mouse-move/input-configs/move_diagonal_scaling/keycode_events.snapshot +++ b/app/tests/mouse-keys/mouse-move/input-configs/move_diagonal_scaling/keycode_events.snapshot @@ -1,18 +1,18 @@ movement_set: Mouse movement set to -1/0 scroll_set: Mouse scroll set to 0/0 movement_set: Mouse movement set to 0/0 -movement_set: Mouse movement set to -2/-2 +movement_set: Mouse movement set to -3/-3 scroll_set: Mouse scroll set to 0/0 movement_set: Mouse movement set to 0/0 -movement_set: Mouse movement set to -2/-2 +movement_set: Mouse movement set to -3/-3 scroll_set: Mouse scroll set to 0/0 movement_set: Mouse movement set to 0/0 -movement_set: Mouse movement set to -3/-2 +movement_set: Mouse movement set to -5/-3 scroll_set: Mouse scroll set to 0/0 movement_set: Mouse movement set to 0/0 -movement_set: Mouse movement set to -3/-3 +movement_set: Mouse movement set to -5/-5 scroll_set: Mouse scroll set to 0/0 movement_set: Mouse movement set to 0/0 -movement_set: Mouse movement set to 0/-3 +movement_set: Mouse movement set to 0/-5 scroll_set: Mouse scroll set to 0/0 movement_set: Mouse movement set to 0/0 diff --git a/app/tests/mouse-keys/mouse-move/input-configs/move_diagonal_scaling/native_posix_64.conf b/app/tests/mouse-keys/mouse-move/input-configs/move_diagonal_scaling/native_posix_64.conf new file mode 100644 index 00000000000..65ed54bb204 --- /dev/null +++ b/app/tests/mouse-keys/mouse-move/input-configs/move_diagonal_scaling/native_posix_64.conf @@ -0,0 +1,6 @@ +CONFIG_GPIO=n +CONFIG_ZMK_BLE=n +CONFIG_LOG=y +CONFIG_LOG_BACKEND_SHOW_COLOR=n +CONFIG_ZMK_LOG_LEVEL_DBG=y +CONFIG_ZMK_MOUSE=y diff --git a/app/tests/mouse-keys/mouse-move/input-configs/move_diagonal_scaling/native_posix_64.keymap b/app/tests/mouse-keys/mouse-move/input-configs/move_diagonal_scaling/native_posix_64.keymap index 6351799bd2e..df8cda8cfdf 100644 --- a/app/tests/mouse-keys/mouse-move/input-configs/move_diagonal_scaling/native_posix_64.keymap +++ b/app/tests/mouse-keys/mouse-move/input-configs/move_diagonal_scaling/native_posix_64.keymap @@ -1,9 +1,13 @@ #include -#include #include #include #include +&mmv_input_listener { + scale-multiplier = <5>; + scale-divisor = <3>; +}; + / { keymap { compatible = "zmk,keymap"; @@ -16,16 +20,6 @@ >; }; }; - - input_configs { - compatible = "zmk,input-configs"; - - mmv { - device = <&mmv>; - scale-multiplier = <5>; - scale-divisor = <3>; - }; - }; }; diff --git a/app/tests/mouse-keys/mouse-move/input-configs/move_diagonal_xy_invert/keycode_events.snapshot b/app/tests/mouse-keys/mouse-move/input-configs/move_diagonal_xy_invert/keycode_events.snapshot index 6b9fa770b11..33bb267b073 100644 --- a/app/tests/mouse-keys/mouse-move/input-configs/move_diagonal_xy_invert/keycode_events.snapshot +++ b/app/tests/mouse-keys/mouse-move/input-configs/move_diagonal_xy_invert/keycode_events.snapshot @@ -1,18 +1,18 @@ -movement_set: Mouse movement set to -1/0 +movement_set: Mouse movement set to 1/0 scroll_set: Mouse scroll set to 0/0 movement_set: Mouse movement set to 0/0 -movement_set: Mouse movement set to -2/-2 +movement_set: Mouse movement set to 2/2 scroll_set: Mouse scroll set to 0/0 movement_set: Mouse movement set to 0/0 -movement_set: Mouse movement set to -2/-2 +movement_set: Mouse movement set to 2/2 scroll_set: Mouse scroll set to 0/0 movement_set: Mouse movement set to 0/0 -movement_set: Mouse movement set to -3/-2 +movement_set: Mouse movement set to 3/2 scroll_set: Mouse scroll set to 0/0 movement_set: Mouse movement set to 0/0 -movement_set: Mouse movement set to -3/-3 +movement_set: Mouse movement set to 3/3 scroll_set: Mouse scroll set to 0/0 movement_set: Mouse movement set to 0/0 -movement_set: Mouse movement set to 0/-3 +movement_set: Mouse movement set to 0/3 scroll_set: Mouse scroll set to 0/0 movement_set: Mouse movement set to 0/0 diff --git a/app/tests/mouse-keys/mouse-move/input-configs/move_diagonal_xy_invert/native_posix_64.conf b/app/tests/mouse-keys/mouse-move/input-configs/move_diagonal_xy_invert/native_posix_64.conf new file mode 100644 index 00000000000..65ed54bb204 --- /dev/null +++ b/app/tests/mouse-keys/mouse-move/input-configs/move_diagonal_xy_invert/native_posix_64.conf @@ -0,0 +1,6 @@ +CONFIG_GPIO=n +CONFIG_ZMK_BLE=n +CONFIG_LOG=y +CONFIG_LOG_BACKEND_SHOW_COLOR=n +CONFIG_ZMK_LOG_LEVEL_DBG=y +CONFIG_ZMK_MOUSE=y diff --git a/app/tests/mouse-keys/mouse-move/input-configs/move_diagonal_xy_invert/native_posix_64.keymap b/app/tests/mouse-keys/mouse-move/input-configs/move_diagonal_xy_invert/native_posix_64.keymap index 3392dd0de2e..9b07e1b9808 100644 --- a/app/tests/mouse-keys/mouse-move/input-configs/move_diagonal_xy_invert/native_posix_64.keymap +++ b/app/tests/mouse-keys/mouse-move/input-configs/move_diagonal_xy_invert/native_posix_64.keymap @@ -1,9 +1,13 @@ #include -#include #include #include #include +&mmv_input_listener { + x-invert; + y-invert; +}; + / { keymap { compatible = "zmk,keymap"; @@ -16,16 +20,6 @@ >; }; }; - - input_configs { - compatible = "zmk,input-configs"; - - mmv { - device = <&mmv>; - x-invert; - y-invert; - }; - }; }; diff --git a/app/tests/mouse-keys/mouse-move/input-configs/move_diagonal_xy_swap/keycode_events.snapshot b/app/tests/mouse-keys/mouse-move/input-configs/move_diagonal_xy_swap/keycode_events.snapshot index 6b9fa770b11..40daa64f0f6 100644 --- a/app/tests/mouse-keys/mouse-move/input-configs/move_diagonal_xy_swap/keycode_events.snapshot +++ b/app/tests/mouse-keys/mouse-move/input-configs/move_diagonal_xy_swap/keycode_events.snapshot @@ -1,4 +1,4 @@ -movement_set: Mouse movement set to -1/0 +movement_set: Mouse movement set to 0/-1 scroll_set: Mouse scroll set to 0/0 movement_set: Mouse movement set to 0/0 movement_set: Mouse movement set to -2/-2 @@ -7,12 +7,12 @@ movement_set: Mouse movement set to 0/0 movement_set: Mouse movement set to -2/-2 scroll_set: Mouse scroll set to 0/0 movement_set: Mouse movement set to 0/0 -movement_set: Mouse movement set to -3/-2 +movement_set: Mouse movement set to -2/-3 scroll_set: Mouse scroll set to 0/0 movement_set: Mouse movement set to 0/0 movement_set: Mouse movement set to -3/-3 scroll_set: Mouse scroll set to 0/0 movement_set: Mouse movement set to 0/0 -movement_set: Mouse movement set to 0/-3 +movement_set: Mouse movement set to -3/0 scroll_set: Mouse scroll set to 0/0 movement_set: Mouse movement set to 0/0 diff --git a/app/tests/mouse-keys/mouse-move/input-configs/move_diagonal_xy_swap/native_posix_64.conf b/app/tests/mouse-keys/mouse-move/input-configs/move_diagonal_xy_swap/native_posix_64.conf new file mode 100644 index 00000000000..65ed54bb204 --- /dev/null +++ b/app/tests/mouse-keys/mouse-move/input-configs/move_diagonal_xy_swap/native_posix_64.conf @@ -0,0 +1,6 @@ +CONFIG_GPIO=n +CONFIG_ZMK_BLE=n +CONFIG_LOG=y +CONFIG_LOG_BACKEND_SHOW_COLOR=n +CONFIG_ZMK_LOG_LEVEL_DBG=y +CONFIG_ZMK_MOUSE=y diff --git a/app/tests/mouse-keys/mouse-move/input-configs/move_diagonal_xy_swap/native_posix_64.keymap b/app/tests/mouse-keys/mouse-move/input-configs/move_diagonal_xy_swap/native_posix_64.keymap index d4cf5031252..719bca98f14 100644 --- a/app/tests/mouse-keys/mouse-move/input-configs/move_diagonal_xy_swap/native_posix_64.keymap +++ b/app/tests/mouse-keys/mouse-move/input-configs/move_diagonal_xy_swap/native_posix_64.keymap @@ -1,9 +1,12 @@ #include -#include #include #include #include +&mmv_input_listener { + xy-swap; +}; + / { keymap { compatible = "zmk,keymap"; @@ -16,15 +19,6 @@ >; }; }; - - input_configs { - compatible = "zmk,input-configs"; - - mmv { - device = <&mmv>; - xy-swap; - }; - }; }; diff --git a/app/tests/mouse-keys/mouse-move/move_diagonal/native_posix_64.conf b/app/tests/mouse-keys/mouse-move/move_diagonal/native_posix_64.conf new file mode 100644 index 00000000000..65ed54bb204 --- /dev/null +++ b/app/tests/mouse-keys/mouse-move/move_diagonal/native_posix_64.conf @@ -0,0 +1,6 @@ +CONFIG_GPIO=n +CONFIG_ZMK_BLE=n +CONFIG_LOG=y +CONFIG_LOG_BACKEND_SHOW_COLOR=n +CONFIG_ZMK_LOG_LEVEL_DBG=y +CONFIG_ZMK_MOUSE=y diff --git a/app/tests/mouse-keys/mouse-move/move_x/native_posix_64.conf b/app/tests/mouse-keys/mouse-move/move_x/native_posix_64.conf new file mode 100644 index 00000000000..65ed54bb204 --- /dev/null +++ b/app/tests/mouse-keys/mouse-move/move_x/native_posix_64.conf @@ -0,0 +1,6 @@ +CONFIG_GPIO=n +CONFIG_ZMK_BLE=n +CONFIG_LOG=y +CONFIG_LOG_BACKEND_SHOW_COLOR=n +CONFIG_ZMK_LOG_LEVEL_DBG=y +CONFIG_ZMK_MOUSE=y diff --git a/app/tests/mouse-keys/mouse-move/move_y/native_posix_64.conf b/app/tests/mouse-keys/mouse-move/move_y/native_posix_64.conf new file mode 100644 index 00000000000..65ed54bb204 --- /dev/null +++ b/app/tests/mouse-keys/mouse-move/move_y/native_posix_64.conf @@ -0,0 +1,6 @@ +CONFIG_GPIO=n +CONFIG_ZMK_BLE=n +CONFIG_LOG=y +CONFIG_LOG_BACKEND_SHOW_COLOR=n +CONFIG_ZMK_LOG_LEVEL_DBG=y +CONFIG_ZMK_MOUSE=y From d45cb3c26fc113357dfc035965e51c84e13e218d Mon Sep 17 00:00:00 2001 From: Peter Johanson Date: Wed, 10 Apr 2024 04:55:14 +0000 Subject: [PATCH 22/23] feat(mouse): Updated mouse keys docs. --- docs/docs/behaviors/index.mdx | 2 + docs/docs/behaviors/mouse-emulation.md | 71 ++++++++++++++++++++++++-- 2 files changed, 68 insertions(+), 5 deletions(-) diff --git a/docs/docs/behaviors/index.mdx b/docs/docs/behaviors/index.mdx index bdacc209ad7..64bbea793b7 100644 --- a/docs/docs/behaviors/index.mdx +++ b/docs/docs/behaviors/index.mdx @@ -43,6 +43,8 @@ Below is a summary of pre-defined behavior bindings and user-definable behaviors | Binding | Behavior | Description | | ------- | ----------------------------------------------------------- | ------------------------------- | | `&mkp` | [Mouse Button Press](mouse-emulation.md#mouse-button-press) | Emulates pressing mouse buttons | +| `&mmv` | [Mouse Button Press](mouse-emulation.md#mouse-move) | Emulates mouse movement | +| `&msc` | [Mouse Button Press](mouse-emulation.md#mouse-scroll) | Emulates mouse scrolling | ## Reset Behaviors diff --git a/docs/docs/behaviors/mouse-emulation.md b/docs/docs/behaviors/mouse-emulation.md index 7b80bae65d9..db14925265a 100644 --- a/docs/docs/behaviors/mouse-emulation.md +++ b/docs/docs/behaviors/mouse-emulation.md @@ -5,8 +5,7 @@ sidebar_label: Mouse Emulation ## Summary -Mouse emulation behaviors send mouse events. Currently, only mouse button presses are supported, but movement -and scroll action support is planned for the future. +Mouse emulation behaviors send mouse events, including mouse button presses, cursor movement and scrolling. :::warning[Refreshing the HID descriptor] @@ -17,14 +16,12 @@ The mouse functionality will not work over BLE until that is done. ## Configuration Option -This feature can be enabled or disabled explicitly via a config option: +To use any of the behaviors documented here, the ZMK mouse feature must be enabled explicitly via a config option: ``` CONFIG_ZMK_MOUSE=y ``` -If you use the mouse key press behavior in your keymap, the feature will automatically be enabled for you. - ## Mouse Button Defines To make it easier to encode the HID mouse button numeric values, include @@ -69,3 +66,67 @@ This example will send press of the fourth mouse button when the binding is trig ``` &mkp MB4 ``` + +## Mouse Move + +This behavior sends mouse X/Y movement events to the connected host. + +### Behavior Binding + +- Reference: `&mmv` +- Parameter: A `uint32` with 16-bits each used for vertical and horizontal velocity. + +The following defines can be passed for the parameter: + +| Define | Action | +| :----------- | :--------- | +| `MOVE_UP` | Move up | +| `MOVE_DOWN` | Move down | +| `MOVE_LEFT` | Move left | +| `MOVE_RIGHT` | Move right | + +### Examples + +The following will send a scroll down event to the host when pressed/held: + +``` +&mmv MOVE_DOWN +``` + +The following will send a scroll left event to the host when pressed/held: + +``` +&mmv MOVE_LEFT +``` + +## Mouse Scroll + +This behavior sends vertical and horizontal scroll events to the connected host. + +### Behavior Binding + +- Reference: `&msc` +- Parameter: A `uint32` with 16-bits each used for vertical and horizontal velocity. + +The following defines can be passed for the parameter: + +| Define | Action | +| :----------- | :--------- | +| `MOVE_UP` | Move up | +| `MOVE_DOWN` | Move down | +| `MOVE_LEFT` | Move left | +| `MOVE_RIGHT` | Move right | + +### Examples + +The following will send a scroll down event to the host when pressed/held: + +``` +&msc MOVE_DOWN +``` + +The following will send a scroll left event to the host when pressed/held: + +``` +&msc MOVE_LEFT +``` From bc75de59f74e9913c810ab358e4d26cdfebfb157 Mon Sep 17 00:00:00 2001 From: Peter Johanson Date: Sat, 3 Aug 2024 17:09:40 -0600 Subject: [PATCH 23/23] fix: Various fixes for building. --- app/include/zmk/hid.h | 6 ++- app/src/mouse/input_listener.c | 84 +++++++++++++++++++++------------- 2 files changed, 58 insertions(+), 32 deletions(-) diff --git a/app/include/zmk/hid.h b/app/include/zmk/hid.h index efc6664ed8e..3c4a3a9175f 100644 --- a/app/include/zmk/hid.h +++ b/app/include/zmk/hid.h @@ -61,6 +61,10 @@ #define ZMK_HID_REPORT_ID_CONSUMER 0x02 #define ZMK_HID_REPORT_ID_MOUSE 0x03 +#define HID_USAGE16(a, b) HID_ITEM(HID_ITEM_TAG_USAGE, HID_ITEM_TYPE_LOCAL, 2), a, b + +#define HID_USAGE16_SINGLE(a) HID_USAGE16((a & 0xFF), ((a >> 8) & 0xFF)) + static const uint8_t zmk_hid_report_desc[] = { HID_USAGE_PAGE(HID_USAGE_GEN_DESKTOP), HID_USAGE(HID_USAGE_GD_KEYBOARD), @@ -175,7 +179,7 @@ static const uint8_t zmk_hid_report_desc[] = { HID_REPORT_COUNT(0x03), HID_INPUT(ZMK_HID_MAIN_VAL_DATA | ZMK_HID_MAIN_VAL_VAR | ZMK_HID_MAIN_VAL_REL), HID_USAGE_PAGE(HID_USAGE_CONSUMER), - HID_USAGE16(HID_USAGE_CONSUMER_AC_PAN), + HID_USAGE16_SINGLE(HID_USAGE_CONSUMER_AC_PAN), HID_LOGICAL_MIN16(0xFF, -0x7F), HID_LOGICAL_MAX16(0xFF, 0x7F), HID_REPORT_SIZE(0x08), diff --git a/app/src/mouse/input_listener.c b/app/src/mouse/input_listener.c index 6863e00eac4..6f6e07cdd34 100644 --- a/app/src/mouse/input_listener.c +++ b/app/src/mouse/input_listener.c @@ -9,10 +9,14 @@ #include #include #include +#include + +LOG_MODULE_DECLARE(zmk, CONFIG_ZMK_LOG_LEVEL); + #include -#include #include +#include #include #define ONE_IF_DEV_OK(n) \ @@ -35,11 +39,15 @@ struct input_listener_xy_data { }; struct input_listener_data { - struct input_listener_xy_data data; - struct input_listener_xy_data wheel_data; - - uint8_t button_set; - uint8_t button_clear; + union { + struct { + struct input_listener_xy_data data; + struct input_listener_xy_data wheel_data; + + uint8_t button_set; + uint8_t button_clear; + } mouse; + }; }; struct input_listener_config { @@ -53,27 +61,31 @@ struct input_listener_config { static void handle_rel_code(struct input_listener_data *data, struct input_event *evt) { switch (evt->code) { case INPUT_REL_X: - data->data.mode = INPUT_LISTENER_XY_DATA_MODE_REL; - data->data.x += evt->value; + data->mouse.data.mode = INPUT_LISTENER_XY_DATA_MODE_REL; + data->mouse.data.x += evt->value; break; case INPUT_REL_Y: - data->data.mode = INPUT_LISTENER_XY_DATA_MODE_REL; - data->data.y += evt->value; + data->mouse.data.mode = INPUT_LISTENER_XY_DATA_MODE_REL; + data->mouse.data.y += evt->value; break; case INPUT_REL_WHEEL: - data->wheel_data.mode = INPUT_LISTENER_XY_DATA_MODE_REL; - data->wheel_data.y += evt->value; + data->mouse.wheel_data.mode = INPUT_LISTENER_XY_DATA_MODE_REL; + data->mouse.wheel_data.y += evt->value; break; case INPUT_REL_HWHEEL: - data->wheel_data.mode = INPUT_LISTENER_XY_DATA_MODE_REL; - data->wheel_data.x += evt->value; + data->mouse.wheel_data.mode = INPUT_LISTENER_XY_DATA_MODE_REL; + data->mouse.wheel_data.x += evt->value; break; default: break; } } -static void handle_key_code(struct input_listener_data *data, struct input_event *evt) { +static void handle_abs_code(const struct input_listener_config *config, + struct input_listener_data *data, struct input_event *evt) {} + +static void handle_key_code(const struct input_listener_config *config, + struct input_listener_data *data, struct input_event *evt) { int8_t btn; switch (evt->code) { @@ -84,9 +96,9 @@ static void handle_key_code(struct input_listener_data *data, struct input_event case INPUT_BTN_4: btn = evt->code - INPUT_BTN_0; if (evt->value > 0) { - WRITE_BIT(data->button_set, btn, 1); + WRITE_BIT(data->mouse.button_set, btn, 1); } else { - WRITE_BIT(data->button_clear, btn, 1); + WRITE_BIT(data->mouse.button_clear, btn, 1); } break; default: @@ -105,6 +117,14 @@ static void swap_xy(struct input_event *evt) { } } +static inline bool is_x_data(const struct input_event *evt) { + return evt->type == INPUT_EV_REL && evt->code == INPUT_REL_X; +} + +static inline bool is_y_data(const struct input_event *evt) { + return evt->type == INPUT_EV_REL && evt->code == INPUT_REL_Y; +} + static void filter_with_input_config(const struct input_listener_config *cfg, struct input_event *evt) { if (!evt->dev) { @@ -115,8 +135,7 @@ static void filter_with_input_config(const struct input_listener_config *cfg, swap_xy(evt); } - if ((cfg->x_invert && evt->code == INPUT_REL_X) || - (cfg->y_invert && evt->code == INPUT_REL_Y)) { + if ((cfg->x_invert && is_x_data(evt)) || (cfg->y_invert && is_y_data(evt))) { evt->value = -(evt->value); } @@ -137,31 +156,34 @@ static void input_handler(const struct input_listener_config *config, case INPUT_EV_REL: handle_rel_code(data, evt); break; + case INPUT_EV_ABS: + handle_abs_code(config, data, evt); + break; case INPUT_EV_KEY: - handle_key_code(data, evt); + handle_key_code(config, data, evt); break; } if (evt->sync) { - if (data->wheel_data.mode == INPUT_LISTENER_XY_DATA_MODE_REL) { - zmk_hid_mouse_scroll_set(data->wheel_data.x, data->wheel_data.y); + if (data->mouse.wheel_data.mode == INPUT_LISTENER_XY_DATA_MODE_REL) { + zmk_hid_mouse_scroll_set(data->mouse.wheel_data.x, data->mouse.wheel_data.y); } - if (data->data.mode == INPUT_LISTENER_XY_DATA_MODE_REL) { - zmk_hid_mouse_movement_set(data->data.x, data->data.y); + if (data->mouse.data.mode == INPUT_LISTENER_XY_DATA_MODE_REL) { + zmk_hid_mouse_movement_set(data->mouse.data.x, data->mouse.data.y); } - if (data->button_set != 0) { + if (data->mouse.button_set != 0) { for (int i = 0; i < ZMK_HID_MOUSE_NUM_BUTTONS; i++) { - if ((data->button_set & BIT(i)) != 0) { + if ((data->mouse.button_set & BIT(i)) != 0) { zmk_hid_mouse_button_press(i); } } } - if (data->button_clear != 0) { + if (data->mouse.button_clear != 0) { for (int i = 0; i < ZMK_HID_MOUSE_NUM_BUTTONS; i++) { - if ((data->button_clear & BIT(i)) != 0) { + if ((data->mouse.button_clear & BIT(i)) != 0) { zmk_hid_mouse_button_release(i); } } @@ -171,10 +193,10 @@ static void input_handler(const struct input_listener_config *config, zmk_hid_mouse_scroll_set(0, 0); zmk_hid_mouse_movement_set(0, 0); - clear_xy_data(&data->data); - clear_xy_data(&data->wheel_data); + clear_xy_data(&data->mouse.data); + clear_xy_data(&data->mouse.wheel_data); - data->button_set = data->button_clear = 0; + data->mouse.button_set = data->mouse.button_clear = 0; } }