update mouse branch
This commit is contained in:
parent
48be2eedd0
commit
800c269d43
44 changed files with 1666 additions and 1 deletions
3
.vscode/settings.json
vendored
3
.vscode/settings.json
vendored
|
@ -3,5 +3,6 @@
|
||||||
"*.overlay": "dts",
|
"*.overlay": "dts",
|
||||||
"*.keymap": "dts"
|
"*.keymap": "dts"
|
||||||
},
|
},
|
||||||
"python.formatting.provider": "black"
|
"python.formatting.provider": "black",
|
||||||
|
"cmake.configureOnOpen": false
|
||||||
}
|
}
|
||||||
|
|
16
app/boards/shields/dokkaebi/Kconfig.defconfig
Normal file
16
app/boards/shields/dokkaebi/Kconfig.defconfig
Normal file
|
@ -0,0 +1,16 @@
|
||||||
|
if SHIELD_DOKKAEBI_LEFT
|
||||||
|
|
||||||
|
config ZMK_KEYBOARD_NAME
|
||||||
|
default "dokkaebi"
|
||||||
|
|
||||||
|
config ZMK_SPLIT_ROLE_CENTRAL
|
||||||
|
default y
|
||||||
|
|
||||||
|
endif
|
||||||
|
|
||||||
|
if SHIELD_DOKKAEBI_LEFT || SHIELD_DOKKAEBI_RIGHT
|
||||||
|
|
||||||
|
config ZMK_SPLIT
|
||||||
|
default y
|
||||||
|
|
||||||
|
endif
|
5
app/boards/shields/dokkaebi/Kconfig.shield
Normal file
5
app/boards/shields/dokkaebi/Kconfig.shield
Normal file
|
@ -0,0 +1,5 @@
|
||||||
|
config SHIELD_DOKKAEBI_LEFT
|
||||||
|
def_bool $(shields_list_contains,dokkaebi_left)
|
||||||
|
|
||||||
|
config SHIELD_DOKKAEBI_RIGHT
|
||||||
|
def_bool $(shields_list_contains,dokkaebi_right)
|
42
app/boards/shields/dokkaebi/dokkaebi.dtsi
Normal file
42
app/boards/shields/dokkaebi/dokkaebi.dtsi
Normal file
|
@ -0,0 +1,42 @@
|
||||||
|
#include <dt-bindings/zmk/matrix_transform.h>
|
||||||
|
|
||||||
|
/ {
|
||||||
|
chosen {
|
||||||
|
zmk,kscan = &kscan0;
|
||||||
|
zmk,matrix_transform = &default_transform;
|
||||||
|
};
|
||||||
|
|
||||||
|
default_transform: keymap_transform_0 {
|
||||||
|
compatible = "zmk,matrix-transform";
|
||||||
|
columns = <14>;
|
||||||
|
rows = <4>;
|
||||||
|
// | SW6 | SW5 | SW4 | SW3 | SW2 | SW1 | | SW1 | SW2 | SW3 | SW4 | SW5 | SW6 |
|
||||||
|
// | SW12 | SW11 | SW10 | SW9 | SW8 | SW7 | | SW7 | SW8 | SW9 | SW10 | SW11 | SW12 |
|
||||||
|
// | SW18 | SW17 | SW16 | SW15 | SW14 | SW13 | | SW13 | SW14 | SW15 | SW16 | SW17 | SW18 |
|
||||||
|
// | SW24 | SW23 | SW22 | SW21 | SW20 | SW19 | SW25 | | SW25 | SW19 | SW20 | SW21 | SW22 | SW23 | SW24 |
|
||||||
|
// | SW29 | SW28 | SW27 | SW26 | | SW26 | SW27 | SW28 | SW29 |
|
||||||
|
map = <
|
||||||
|
RC(0,0) RC(0,1) RC(0,2) RC(0,3) RC(0,4) RC(0,5) RC(0,8) RC(0,9) RC(0,10) RC(0,11) RC(0,12) RC(0,13)
|
||||||
|
RC(1,0) RC(1,1) RC(1,2) RC(1,3) RC(1,4) RC(1,5) RC(1,8) RC(1,9) RC(1,10) RC(1,11) RC(1,12) RC(1,13)
|
||||||
|
RC(2,0) RC(2,1) RC(2,2) RC(2,3) RC(2,4) RC(2,5) RC(2,8) RC(2,9) RC(2,10) RC(2,11) RC(2,12) RC(2,13)
|
||||||
|
RC(3,0) RC(3,1) RC(3,2) RC(3,3) RC(3,4) RC(3,5) RC(3,8) RC(3,9) RC(3,10) RC(3,11) RC(3,12) RC(3,13)
|
||||||
|
RC(3,6) RC(2,6) RC(1,6) RC(1,7) RC(2,7) RC(3,7)
|
||||||
|
>;
|
||||||
|
};
|
||||||
|
|
||||||
|
kscan0: kscan {
|
||||||
|
compatible = "zmk,kscan-gpio-matrix";
|
||||||
|
label = "KSCAN";
|
||||||
|
|
||||||
|
diode-direction = "col2row";
|
||||||
|
debounce-press-ms = <4>;
|
||||||
|
debounce-release-ms = <20>;
|
||||||
|
row-gpios
|
||||||
|
= <&pro_micro 6 (GPIO_ACTIVE_HIGH | GPIO_PULL_DOWN)>
|
||||||
|
, <&pro_micro 7 (GPIO_ACTIVE_HIGH | GPIO_PULL_DOWN)>
|
||||||
|
, <&pro_micro 8 (GPIO_ACTIVE_HIGH | GPIO_PULL_DOWN)>
|
||||||
|
, <&pro_micro 9 (GPIO_ACTIVE_HIGH | GPIO_PULL_DOWN)>
|
||||||
|
;
|
||||||
|
|
||||||
|
};
|
||||||
|
};
|
154
app/boards/shields/dokkaebi/dokkaebi.keymap
Normal file
154
app/boards/shields/dokkaebi/dokkaebi.keymap
Normal file
|
@ -0,0 +1,154 @@
|
||||||
|
#include <behaviors.dtsi>
|
||||||
|
#include <dt-bindings/zmk/keys.h>
|
||||||
|
#include <dt-bindings/zmk/bt.h>
|
||||||
|
#include <dt-bindings/zmk/ext_power.h>
|
||||||
|
#include <dt-bindings/zmk/outputs.h>
|
||||||
|
|
||||||
|
&mt {
|
||||||
|
tapping-term-ms = <160>;
|
||||||
|
};
|
||||||
|
|
||||||
|
< {
|
||||||
|
tapping-term-ms = <175>;
|
||||||
|
quick-tap-ms = <120>;
|
||||||
|
};
|
||||||
|
|
||||||
|
/ {
|
||||||
|
combos {
|
||||||
|
compatible = "zmk,combos";
|
||||||
|
combo_lbrc {
|
||||||
|
timeout-ms = <50>;
|
||||||
|
key-positions = <24 25>;
|
||||||
|
bindings = <&kp LBKT>;
|
||||||
|
};
|
||||||
|
combo_rbrc {
|
||||||
|
timeout-ms = <50>;
|
||||||
|
key-positions = <34 35>;
|
||||||
|
bindings = <&kp RBKT>;
|
||||||
|
};
|
||||||
|
combo_lt2 {
|
||||||
|
timeout-ms = <50>;
|
||||||
|
key-positions = <48 49 50>;
|
||||||
|
bindings = <&mo 2>;
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
behaviors {
|
||||||
|
gresc: grave_escape {
|
||||||
|
compatible = "zmk,behavior-mod-morph";
|
||||||
|
label = "GRAVE_ESCAPE";
|
||||||
|
#binding-cells = <0>;
|
||||||
|
bindings = <&kp ESC>, <&kp GRAVE>;
|
||||||
|
mods = <(MOD_LGUI|MOD_LSFT|MOD_RGUI|MOD_RSFT|MOD_RALT)>;
|
||||||
|
keep-mods = <(MOD_RSFT)>;
|
||||||
|
};
|
||||||
|
td_lsftbkt: tap_dance_lsft_bkt {
|
||||||
|
compatible = "zmk,behavior-tap-dance";
|
||||||
|
label = "TAP_DANCE_LSFT_BKT";
|
||||||
|
#binding-cells = <0>;
|
||||||
|
tapping-term-ms = <175>;
|
||||||
|
bindings = <&kp LSHFT>, <&kp LBKT>;
|
||||||
|
};
|
||||||
|
td_rsftbkt: tap_dance_rsft_bkt {
|
||||||
|
compatible = "zmk,behavior-tap-dance";
|
||||||
|
label = "TAP_DANCE_RSFT_BKT";
|
||||||
|
#binding-cells = <0>;
|
||||||
|
tapping-term-ms = <175>;
|
||||||
|
bindings = <&kp RSHFT>, <&kp RBKT>;
|
||||||
|
};
|
||||||
|
gqt: global-quick-tap {
|
||||||
|
compatible = "zmk,behavior-hold-tap";
|
||||||
|
label = "GLOBAL_QUICK_TAP";
|
||||||
|
#binding-cells = <2>;
|
||||||
|
flavor = "tap-preferred";
|
||||||
|
tapping-term-ms = <160>;
|
||||||
|
quick-tap-ms = <120>;
|
||||||
|
global-quick-tap;
|
||||||
|
bindings = <&kp>, <&kp>;
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
macros {
|
||||||
|
bt_0: bt_profile_macro_0 {
|
||||||
|
label = "BT_0";
|
||||||
|
compatible = "zmk,behavior-macro";
|
||||||
|
#binding-cells = <0>;
|
||||||
|
bindings
|
||||||
|
= <&out OUT_BLE>
|
||||||
|
, <&bt BT_SEL 0>;
|
||||||
|
};
|
||||||
|
|
||||||
|
bt_1: bt_profile_macro_1 {
|
||||||
|
label = "BT_1";
|
||||||
|
compatible = "zmk,behavior-macro";
|
||||||
|
#binding-cells = <0>;
|
||||||
|
bindings
|
||||||
|
= <&out OUT_BLE>
|
||||||
|
, <&bt BT_SEL 1>;
|
||||||
|
};
|
||||||
|
|
||||||
|
bt_2: bt_profile_macro_2 {
|
||||||
|
label = "BT_2";
|
||||||
|
compatible = "zmk,behavior-macro";
|
||||||
|
#binding-cells = <0>;
|
||||||
|
bindings
|
||||||
|
= <&out OUT_BLE>
|
||||||
|
, <&bt BT_SEL 2>;
|
||||||
|
};
|
||||||
|
|
||||||
|
bt_3: bt_profile_macro_3 {
|
||||||
|
label = "BT_3";
|
||||||
|
compatible = "zmk,behavior-macro";
|
||||||
|
#binding-cells = <0>;
|
||||||
|
bindings
|
||||||
|
= <&out OUT_BLE>
|
||||||
|
, <&bt BT_SEL 3>;
|
||||||
|
};
|
||||||
|
|
||||||
|
bt_4: bt_profile_macro_4 {
|
||||||
|
label = "BT_4";
|
||||||
|
compatible = "zmk,behavior-macro";
|
||||||
|
#binding-cells = <0>;
|
||||||
|
bindings
|
||||||
|
= <&out OUT_BLE>
|
||||||
|
, <&bt BT_SEL 4>;
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
keymap {
|
||||||
|
compatible = "zmk,keymap";
|
||||||
|
|
||||||
|
default_layer {
|
||||||
|
bindings = <
|
||||||
|
&gresc &kp N1 &kp N2 &kp N3 &kp N4 &kp N5 &kp N6 &kp N7 &kp N8 &kp N9 &kp N0 &kp DEL
|
||||||
|
&kp TAB &kp Q &kp W &kp E &kp R &kp T &kp Y &kp U &kp I &kp O &kp P &kp EQUAL
|
||||||
|
&mt LGUI MINUS &kp A &kp S &kp D &kp F &kp G &kp H &kp J &kp K &kp L &kp SEMI &kp SQT
|
||||||
|
&kp LSHFT &kp Z &kp X &kp C &kp V &kp B &kp N &kp M &kp COMMA &kp DOT &kp FSLH &kp RSHFT
|
||||||
|
&kp LALT < 1 SPACE &gqt LCTRL BSPC &kp ENTER < 1 SPACE &kp RALT
|
||||||
|
>;
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
lower_layer {
|
||||||
|
bindings = <
|
||||||
|
&trans &kp F1 &kp F2 &kp F3 &kp F4 &kp F5 &kp F6 &kp F7 &kp F8 &kp F9 &kp F10 &kp HOME
|
||||||
|
&trans &trans &trans &trans &trans &kp F11 &kp F12 &trans &kp UP &trans &trans &kp END
|
||||||
|
&trans &trans &trans &trans &trans &kp C_VOL_UP &trans &kp LEFT &kp DOWN &kp RIGHT &trans &kp PG_UP
|
||||||
|
&trans &trans &trans &trans &trans &kp C_VOL_DN &trans &trans &trans &trans &kp BSLH &kp PG_DN
|
||||||
|
&trans &kp SPACE &kp LCTRL &trans &kp SPACE &kp RCTRL
|
||||||
|
>;
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
raise_layer {
|
||||||
|
bindings = <
|
||||||
|
&bt BT_CLR &bt_0 &bt_1 &bt_2 &bt_3 &bt_4 &trans &trans &trans &trans &trans &trans
|
||||||
|
&out OUT_TOG &key_repeat &trans &trans &trans &trans &trans &trans &trans &trans &trans &trans
|
||||||
|
&trans &trans &trans &trans &trans &trans &trans &trans &trans &trans &trans &trans
|
||||||
|
&trans &trans &trans &trans &trans &trans &trans &trans &trans &trans &trans &trans
|
||||||
|
&trans &trans &trans &trans &trans &trans
|
||||||
|
>;
|
||||||
|
|
||||||
|
};
|
||||||
|
};
|
||||||
|
};
|
11
app/boards/shields/dokkaebi/dokkaebi.zmk.yml
Normal file
11
app/boards/shields/dokkaebi/dokkaebi.zmk.yml
Normal file
|
@ -0,0 +1,11 @@
|
||||||
|
file_format: "1"
|
||||||
|
id: dokkaebi
|
||||||
|
name: dokkaebi
|
||||||
|
type: shield
|
||||||
|
url: https://github.com/zenithistk/dokkaebi
|
||||||
|
requires: [pro_micro]
|
||||||
|
features:
|
||||||
|
- keys
|
||||||
|
siblings:
|
||||||
|
- dokkaebi_left
|
||||||
|
- dokkaebi_right
|
13
app/boards/shields/dokkaebi/dokkaebi_left.overlay
Normal file
13
app/boards/shields/dokkaebi/dokkaebi_left.overlay
Normal file
|
@ -0,0 +1,13 @@
|
||||||
|
#include "dokkaebi.dtsi"
|
||||||
|
|
||||||
|
&kscan0 {
|
||||||
|
col-gpios
|
||||||
|
= <&pro_micro 1 GPIO_ACTIVE_HIGH>
|
||||||
|
, <&pro_micro 0 GPIO_ACTIVE_HIGH>
|
||||||
|
, <&pro_micro 21 GPIO_ACTIVE_HIGH>
|
||||||
|
, <&pro_micro 20 GPIO_ACTIVE_HIGH>
|
||||||
|
, <&pro_micro 4 GPIO_ACTIVE_HIGH>
|
||||||
|
, <&pro_micro 19 GPIO_ACTIVE_HIGH>
|
||||||
|
, <&pro_micro 5 GPIO_ACTIVE_HIGH>
|
||||||
|
;
|
||||||
|
};
|
17
app/boards/shields/dokkaebi/dokkaebi_right.overlay
Normal file
17
app/boards/shields/dokkaebi/dokkaebi_right.overlay
Normal file
|
@ -0,0 +1,17 @@
|
||||||
|
#include "dokkaebi.dtsi"
|
||||||
|
|
||||||
|
&default_transform {
|
||||||
|
col-offset = <7>;
|
||||||
|
};
|
||||||
|
|
||||||
|
&kscan0 {
|
||||||
|
col-gpios
|
||||||
|
= <&pro_micro 5 GPIO_ACTIVE_HIGH>
|
||||||
|
, <&pro_micro 19 GPIO_ACTIVE_HIGH>
|
||||||
|
, <&pro_micro 4 GPIO_ACTIVE_HIGH>
|
||||||
|
, <&pro_micro 20 GPIO_ACTIVE_HIGH>
|
||||||
|
, <&pro_micro 21 GPIO_ACTIVE_HIGH>
|
||||||
|
, <&pro_micro 0 GPIO_ACTIVE_HIGH>
|
||||||
|
, <&pro_micro 1 GPIO_ACTIVE_HIGH>
|
||||||
|
;
|
||||||
|
};
|
4
app/drivers/sensor/trackball_pim447/CMakeLists.txt
Normal file
4
app/drivers/sensor/trackball_pim447/CMakeLists.txt
Normal file
|
@ -0,0 +1,4 @@
|
||||||
|
# SPDX-License-Identifier: Apache-2.0
|
||||||
|
|
||||||
|
zephyr_library()
|
||||||
|
zephyr_library_sources(trackball_pim447.c)
|
8
app/drivers/sensor/trackball_pim447/Kconfig
Normal file
8
app/drivers/sensor/trackball_pim447/Kconfig
Normal file
|
@ -0,0 +1,8 @@
|
||||||
|
# Copyright (c) 2021 The ZMK Contributors
|
||||||
|
# SPDX-License-Identifier: MIT
|
||||||
|
|
||||||
|
config ZMK_TRACKBALL_PIM447
|
||||||
|
bool "ZMK PIM447 trackball"
|
||||||
|
depends on I2C
|
||||||
|
help
|
||||||
|
Enable I2C-based driver for Pimoroni PIM447 trackball.
|
167
app/drivers/sensor/trackball_pim447/trackball_pim447.c
Normal file
167
app/drivers/sensor/trackball_pim447/trackball_pim447.c
Normal file
|
@ -0,0 +1,167 @@
|
||||||
|
/*
|
||||||
|
* Copyright (c) 2021 The ZMK Contributors
|
||||||
|
*
|
||||||
|
* SPDX-License-Identifier: MIT
|
||||||
|
*/
|
||||||
|
|
||||||
|
#define DT_DRV_COMPAT pimoroni_trackball_pim447
|
||||||
|
|
||||||
|
#include <drivers/i2c.h>
|
||||||
|
#include <drivers/sensor.h>
|
||||||
|
|
||||||
|
#define LOG_LEVEL CONFIG_SENSOR_LOG_LEVEL
|
||||||
|
#include <logging/log.h>
|
||||||
|
LOG_MODULE_REGISTER(trackball_pim447);
|
||||||
|
|
||||||
|
#define TRACKBALL_PIM447_REG_LEFT 0x04
|
||||||
|
#define TRACKBALL_PIM447_REG_RIGHT 0x05
|
||||||
|
#define TRACKBALL_PIM447_REG_UP 0x06
|
||||||
|
#define TRACKBALL_PIM447_REG_DOWN 0x07
|
||||||
|
#define TRACKBALL_PIM447_REG_SWITCH 0x08
|
||||||
|
|
||||||
|
#define TRACKBALL_PIM447_REG_MIN TRACKBALL_PIM447_REG_LEFT
|
||||||
|
#define TRACKBALL_PIM447_REG_MAX TRACKBALL_PIM447_REG_SWITCH
|
||||||
|
|
||||||
|
struct trackball_pim447_data {
|
||||||
|
const struct device *i2c_dev;
|
||||||
|
int32_t dx;
|
||||||
|
int32_t dy;
|
||||||
|
int32_t dz;
|
||||||
|
};
|
||||||
|
|
||||||
|
static int trackball_pim447_read_reg(const struct device *dev,
|
||||||
|
uint8_t reg, uint8_t *value)
|
||||||
|
{
|
||||||
|
struct trackball_pim447_data *data = dev->data;
|
||||||
|
|
||||||
|
if (reg < TRACKBALL_PIM447_REG_MIN || reg > TRACKBALL_PIM447_REG_MAX) {
|
||||||
|
return -ENOTSUP;
|
||||||
|
}
|
||||||
|
|
||||||
|
int status = i2c_reg_read_byte(data->i2c_dev, DT_INST_REG_ADDR(0),
|
||||||
|
reg, value);
|
||||||
|
if (status < 0) {
|
||||||
|
LOG_ERR("Sensor reg read byte failed");
|
||||||
|
return status;
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int trackball_pim447_read_axis(const struct device *dev,
|
||||||
|
uint8_t reg_negative,
|
||||||
|
uint8_t reg_positive,
|
||||||
|
int32_t *value)
|
||||||
|
{
|
||||||
|
uint8_t value_negative;
|
||||||
|
uint8_t value_positive;
|
||||||
|
int status;
|
||||||
|
|
||||||
|
status = trackball_pim447_read_reg(dev, reg_negative, &value_negative);
|
||||||
|
if (status < 0) {
|
||||||
|
return status;
|
||||||
|
}
|
||||||
|
|
||||||
|
status = trackball_pim447_read_reg(dev, reg_positive, &value_positive);
|
||||||
|
if (status < 0) {
|
||||||
|
return status;
|
||||||
|
}
|
||||||
|
|
||||||
|
*value = value_positive - value_negative;
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int trackball_pim447_sample_fetch(const struct device *dev,
|
||||||
|
enum sensor_channel chan)
|
||||||
|
{
|
||||||
|
struct trackball_pim447_data *data = dev->data;
|
||||||
|
int status;
|
||||||
|
|
||||||
|
if (chan == SENSOR_CHAN_ALL || chan == SENSOR_CHAN_POS_DX) {
|
||||||
|
status = trackball_pim447_read_axis(dev,
|
||||||
|
TRACKBALL_PIM447_REG_LEFT,
|
||||||
|
TRACKBALL_PIM447_REG_RIGHT,
|
||||||
|
&data->dx);
|
||||||
|
if (status < 0) {
|
||||||
|
return status;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (chan == SENSOR_CHAN_ALL || chan == SENSOR_CHAN_POS_DY) {
|
||||||
|
status = trackball_pim447_read_axis(dev,
|
||||||
|
TRACKBALL_PIM447_REG_UP,
|
||||||
|
TRACKBALL_PIM447_REG_DOWN,
|
||||||
|
&data->dy);
|
||||||
|
if (status < 0) {
|
||||||
|
return status;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (chan == SENSOR_CHAN_ALL || chan == SENSOR_CHAN_POS_DZ) {
|
||||||
|
uint8_t value;
|
||||||
|
status = trackball_pim447_read_reg(dev,
|
||||||
|
TRACKBALL_PIM447_REG_SWITCH,
|
||||||
|
&value);
|
||||||
|
if (status < 0) {
|
||||||
|
return status;
|
||||||
|
}
|
||||||
|
|
||||||
|
data->dz = value;
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int trackball_pim447_channel_get(const struct device *dev,
|
||||||
|
enum sensor_channel chan,
|
||||||
|
struct sensor_value *val)
|
||||||
|
{
|
||||||
|
struct trackball_pim447_data *data = dev->data;
|
||||||
|
|
||||||
|
/* Not used. */
|
||||||
|
val->val2 = 0;
|
||||||
|
|
||||||
|
switch (chan) {
|
||||||
|
case SENSOR_CHAN_POS_DX:
|
||||||
|
val->val1 = data->dx;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case SENSOR_CHAN_POS_DY:
|
||||||
|
val->val1 = data->dy;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case SENSOR_CHAN_POS_DZ:
|
||||||
|
val->val1 = data->dz;
|
||||||
|
break;
|
||||||
|
|
||||||
|
default:
|
||||||
|
return -ENOTSUP;
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int trackball_pim447_init(const struct device *dev)
|
||||||
|
{
|
||||||
|
struct trackball_pim447_data *data = dev->data;
|
||||||
|
|
||||||
|
data->i2c_dev = device_get_binding(DT_INST_BUS_LABEL(0));
|
||||||
|
if (data->i2c_dev == NULL) {
|
||||||
|
LOG_ERR("Failed to get I2C device");
|
||||||
|
return -EINVAL;
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static struct trackball_pim447_data trackball_pim447_data;
|
||||||
|
|
||||||
|
static const struct sensor_driver_api trackball_pim447_api = {
|
||||||
|
.sample_fetch = trackball_pim447_sample_fetch,
|
||||||
|
.channel_get = trackball_pim447_channel_get,
|
||||||
|
};
|
||||||
|
|
||||||
|
DEVICE_DT_INST_DEFINE(0, &trackball_pim447_init, device_pm_control_nop,
|
||||||
|
&trackball_pim447_data, NULL, POST_KERNEL,
|
||||||
|
CONFIG_SENSOR_INIT_PRIORITY, &trackball_pim447_api);
|
|
@ -0,0 +1,58 @@
|
||||||
|
# Copyright (c) 2021 The ZMK Contributors
|
||||||
|
# SPDX-License-Identifier: MIT
|
||||||
|
|
||||||
|
description: Trackball PIM447
|
||||||
|
|
||||||
|
compatible: "pimoroni,trackball_pim447"
|
||||||
|
|
||||||
|
properties:
|
||||||
|
label:
|
||||||
|
type: string
|
||||||
|
required: true
|
||||||
|
|
||||||
|
reg:
|
||||||
|
type: int
|
||||||
|
required: true
|
||||||
|
|
||||||
|
move-factor:
|
||||||
|
type: int
|
||||||
|
required: false
|
||||||
|
default: 1
|
||||||
|
|
||||||
|
invert-move-x:
|
||||||
|
type: boolean
|
||||||
|
required: false
|
||||||
|
|
||||||
|
invert-move-y:
|
||||||
|
type: boolean
|
||||||
|
required: false
|
||||||
|
|
||||||
|
button:
|
||||||
|
type: int
|
||||||
|
required: false
|
||||||
|
default: 0
|
||||||
|
|
||||||
|
swap-axes:
|
||||||
|
type: boolean
|
||||||
|
required: false
|
||||||
|
|
||||||
|
scroll-divisor:
|
||||||
|
type: int
|
||||||
|
required: false
|
||||||
|
default: 2
|
||||||
|
|
||||||
|
invert-scroll-x:
|
||||||
|
type: boolean
|
||||||
|
required: false
|
||||||
|
|
||||||
|
invert-scroll-y:
|
||||||
|
type: boolean
|
||||||
|
required: false
|
||||||
|
|
||||||
|
mode:
|
||||||
|
type: int
|
||||||
|
required: false
|
||||||
|
default: 1
|
||||||
|
enum:
|
||||||
|
- 1
|
||||||
|
- 2
|
9
app/dts/behaviors/mouse_key_press.dtsi
Normal file
9
app/dts/behaviors/mouse_key_press.dtsi
Normal file
|
@ -0,0 +1,9 @@
|
||||||
|
/ {
|
||||||
|
behaviors {
|
||||||
|
/omit-if-no-ref/ mkp: behavior_mouse_key_press {
|
||||||
|
compatible = "zmk,behavior-mouse-key-press";
|
||||||
|
label = "MOUSE_KEY_PRESS";
|
||||||
|
#binding-cells = <1>;
|
||||||
|
};
|
||||||
|
};
|
||||||
|
};
|
12
app/dts/behaviors/mouse_move.dtsi
Normal file
12
app/dts/behaviors/mouse_move.dtsi
Normal file
|
@ -0,0 +1,12 @@
|
||||||
|
/ {
|
||||||
|
behaviors {
|
||||||
|
/omit-if-no-ref/ mmv: behavior_mouse_move {
|
||||||
|
compatible = "zmk,behavior-mouse-move";
|
||||||
|
label = "MOUSE_MOVE";
|
||||||
|
#binding-cells = <1>;
|
||||||
|
delay-ms = <0>;
|
||||||
|
time-to-max-speed-ms = <300>;
|
||||||
|
acceleration-exponent = <1>;
|
||||||
|
};
|
||||||
|
};
|
||||||
|
};
|
12
app/dts/behaviors/mouse_scroll.dtsi
Normal file
12
app/dts/behaviors/mouse_scroll.dtsi
Normal file
|
@ -0,0 +1,12 @@
|
||||||
|
/ {
|
||||||
|
behaviors {
|
||||||
|
/omit-if-no-ref/ mwh: msc: behavior_mouse_scroll {
|
||||||
|
compatible = "zmk,behavior-mouse-scroll";
|
||||||
|
label = "MOUSE_SCROLL";
|
||||||
|
#binding-cells = <1>;
|
||||||
|
delay-ms = <0>;
|
||||||
|
time-to-max-speed-ms = <300>;
|
||||||
|
acceleration-exponent = <0>;
|
||||||
|
};
|
||||||
|
};
|
||||||
|
};
|
56
app/dts/behaviors/trackball_pim447.dtsi
Normal file
56
app/dts/behaviors/trackball_pim447.dtsi
Normal file
|
@ -0,0 +1,56 @@
|
||||||
|
/*
|
||||||
|
* Copyright (c) 2021 The ZMK Contributors
|
||||||
|
*
|
||||||
|
* SPDX-License-Identifier: MIT
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <dt-bindings/zmk/trackball_pim447.h>
|
||||||
|
|
||||||
|
/ {
|
||||||
|
behaviors {
|
||||||
|
/omit-if-no-ref/ pim447_move: behavior_trackball_pim447_move {
|
||||||
|
compatible = "zmk,behavior-trackball-pim447";
|
||||||
|
label = "PIM447_MOVE";
|
||||||
|
mode = <PIM447_MOVE>;
|
||||||
|
#binding-cells = <0>;
|
||||||
|
};
|
||||||
|
|
||||||
|
/omit-if-no-ref/ pim447_scroll: behavior_trackball_pim447_scroll {
|
||||||
|
compatible = "zmk,behavior-trackball-pim447";
|
||||||
|
label = "PIM447_SCROLL";
|
||||||
|
mode = <PIM447_SCROLL>;
|
||||||
|
#binding-cells = <0>;
|
||||||
|
};
|
||||||
|
|
||||||
|
/omit-if-no-ref/ pim447_toggle: behavior_trackball_pim447_toggle {
|
||||||
|
compatible = "zmk,behavior-trackball-pim447";
|
||||||
|
label = "PIM447_TOGGLE";
|
||||||
|
mode = <PIM447_TOGGLE>;
|
||||||
|
#binding-cells = <0>;
|
||||||
|
};
|
||||||
|
|
||||||
|
/omit-if-no-ref/ pim447_move_scroll: behavior_trackball_pim447_move_scroll {
|
||||||
|
compatible = "zmk,behavior-trackball-pim447";
|
||||||
|
label = "PIM447_MOVE_SCROLL";
|
||||||
|
mode = <PIM447_MOVE>;
|
||||||
|
momentary;
|
||||||
|
#binding-cells = <0>;
|
||||||
|
};
|
||||||
|
|
||||||
|
/omit-if-no-ref/ pim447_scroll_move: behavior_trackball_pim447_scroll_move {
|
||||||
|
compatible = "zmk,behavior-trackball-pim447";
|
||||||
|
label = "PIM447_SCROLL_MOVE";
|
||||||
|
mode = <PIM447_SCROLL>;
|
||||||
|
momentary;
|
||||||
|
#binding-cells = <0>;
|
||||||
|
};
|
||||||
|
|
||||||
|
/omit-if-no-ref/ pim447_toggle_toggle: behavior_trackball_pim447_toggle_toggle {
|
||||||
|
compatible = "zmk,behavior-trackball-pim447";
|
||||||
|
label = "PIM447_TOGGLE_TOGGLE";
|
||||||
|
mode = <PIM447_TOGGLE>;
|
||||||
|
momentary;
|
||||||
|
#binding-cells = <0>;
|
||||||
|
};
|
||||||
|
};
|
||||||
|
};
|
|
@ -0,0 +1,5 @@
|
||||||
|
description: Mouse key press/release behavior
|
||||||
|
|
||||||
|
compatible: "zmk,behavior-mouse-key-press"
|
||||||
|
|
||||||
|
include: one_param.yaml
|
13
app/dts/bindings/behaviors/zmk,behavior-mouse-move.yaml
Normal file
13
app/dts/bindings/behaviors/zmk,behavior-mouse-move.yaml
Normal file
|
@ -0,0 +1,13 @@
|
||||||
|
description: Mouse move
|
||||||
|
|
||||||
|
compatible: "zmk,behavior-mouse-move"
|
||||||
|
|
||||||
|
include: one_param.yaml
|
||||||
|
|
||||||
|
properties:
|
||||||
|
delay-ms:
|
||||||
|
type: int
|
||||||
|
time-to-max-speed-ms:
|
||||||
|
type: int
|
||||||
|
acceleration-exponent:
|
||||||
|
type: int
|
13
app/dts/bindings/behaviors/zmk,behavior-mouse-scroll.yaml
Normal file
13
app/dts/bindings/behaviors/zmk,behavior-mouse-scroll.yaml
Normal file
|
@ -0,0 +1,13 @@
|
||||||
|
description: Mouse scroll
|
||||||
|
|
||||||
|
compatible: "zmk,behavior-mouse-scroll"
|
||||||
|
|
||||||
|
include: one_param.yaml
|
||||||
|
|
||||||
|
properties:
|
||||||
|
delay-ms:
|
||||||
|
type: int
|
||||||
|
time-to-max-speed-ms:
|
||||||
|
type: int
|
||||||
|
acceleration-exponent:
|
||||||
|
type: int
|
|
@ -0,0 +1,15 @@
|
||||||
|
# Copyright (c) 2021 The ZMK Contributors
|
||||||
|
# SPDX-License-Identifier: MIT
|
||||||
|
|
||||||
|
description: Trackball PIM447 behavior
|
||||||
|
|
||||||
|
compatible: "zmk,behavior-trackball-pim447"
|
||||||
|
|
||||||
|
include: zero_param.yaml
|
||||||
|
|
||||||
|
properties:
|
||||||
|
mode:
|
||||||
|
type: int
|
||||||
|
required: true
|
||||||
|
momentary:
|
||||||
|
type: boolean
|
30
app/include/dt-bindings/zmk/mouse.h
Normal file
30
app/include/dt-bindings/zmk/mouse.h
Normal file
|
@ -0,0 +1,30 @@
|
||||||
|
/*
|
||||||
|
* Copyright (c) 2021 The ZMK Contributors
|
||||||
|
*
|
||||||
|
* SPDX-License-Identifier: MIT
|
||||||
|
*/
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <zephyr.h>
|
||||||
|
#include <dt-bindings/zmk/mouse.h>
|
||||||
|
|
||||||
|
typedef uint16_t zmk_mouse_button_flags_t;
|
||||||
|
typedef uint16_t zmk_mouse_button_t;
|
||||||
|
|
||||||
|
struct mouse_config {
|
||||||
|
int delay_ms;
|
||||||
|
int time_to_max_speed_ms;
|
||||||
|
// acceleration exponent 0: uniform speed
|
||||||
|
// acceleration exponent 1: uniform acceleration
|
||||||
|
// acceleration exponent 2: uniform jerk
|
||||||
|
int acceleration_exponent;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct vector2d {
|
||||||
|
float x;
|
||||||
|
float y;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct k_work_q *zmk_mouse_work_q();
|
||||||
|
int zmk_mouse_init();
|
10
app/include/dt-bindings/zmk/trackball_pim447.h
Normal file
10
app/include/dt-bindings/zmk/trackball_pim447.h
Normal file
|
@ -0,0 +1,10 @@
|
||||||
|
/*
|
||||||
|
* Copyright (c) 2021 The ZMK Contributors
|
||||||
|
*
|
||||||
|
* SPDX-License-Identifier: MIT
|
||||||
|
*/
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <dt-bindings/zmk/trackball_pim447.h>
|
||||||
|
|
||||||
|
extern void zmk_trackball_pim447_set_mode(int mode);
|
27
app/include/zmk/events/mouse_button_state_changed.h
Normal file
27
app/include/zmk/events/mouse_button_state_changed.h
Normal file
|
@ -0,0 +1,27 @@
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Copyright (c) 2020 The ZMK Contributors
|
||||||
|
*
|
||||||
|
* SPDX-License-Identifier: MIT
|
||||||
|
*/
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <zephyr.h>
|
||||||
|
#include <zmk/hid.h>
|
||||||
|
#include <zmk/event_manager.h>
|
||||||
|
#include <zmk/mouse.h>
|
||||||
|
|
||||||
|
struct zmk_mouse_button_state_changed {
|
||||||
|
zmk_mouse_button_t buttons;
|
||||||
|
bool state;
|
||||||
|
int64_t timestamp;
|
||||||
|
};
|
||||||
|
|
||||||
|
ZMK_EVENT_DECLARE(zmk_mouse_button_state_changed);
|
||||||
|
|
||||||
|
static inline struct zmk_mouse_button_state_changed_event *
|
||||||
|
zmk_mouse_button_state_changed_from_encoded(uint32_t encoded, bool pressed, int64_t timestamp) {
|
||||||
|
return new_zmk_mouse_button_state_changed((struct zmk_mouse_button_state_changed){
|
||||||
|
.buttons = HID_USAGE_ID(encoded), .state = pressed, .timestamp = timestamp});
|
||||||
|
}
|
33
app/include/zmk/events/mouse_move_state_changed.h
Normal file
33
app/include/zmk/events/mouse_move_state_changed.h
Normal file
|
@ -0,0 +1,33 @@
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Copyright (c) 2020 The ZMK Contributors
|
||||||
|
*
|
||||||
|
* SPDX-License-Identifier: MIT
|
||||||
|
*/
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <zephyr.h>
|
||||||
|
#include <zmk/event_manager.h>
|
||||||
|
#include <zmk/mouse.h>
|
||||||
|
|
||||||
|
struct zmk_mouse_move_state_changed {
|
||||||
|
struct vector2d max_speed;
|
||||||
|
struct mouse_config config;
|
||||||
|
bool state;
|
||||||
|
int64_t timestamp;
|
||||||
|
};
|
||||||
|
|
||||||
|
ZMK_EVENT_DECLARE(zmk_mouse_move_state_changed);
|
||||||
|
|
||||||
|
static inline struct zmk_mouse_move_state_changed_event *
|
||||||
|
zmk_mouse_move_state_changed_from_encoded(uint32_t encoded, struct mouse_config config,
|
||||||
|
bool pressed, int64_t timestamp) {
|
||||||
|
struct vector2d max_speed = (struct vector2d){
|
||||||
|
.x = MOVE_HOR_DECODE(encoded),
|
||||||
|
.y = MOVE_VERT_DECODE(encoded),
|
||||||
|
};
|
||||||
|
|
||||||
|
return new_zmk_mouse_move_state_changed((struct zmk_mouse_move_state_changed){
|
||||||
|
.max_speed = max_speed, .config = config, .state = pressed, .timestamp = timestamp});
|
||||||
|
}
|
34
app/include/zmk/events/mouse_scroll_state_changed.h
Normal file
34
app/include/zmk/events/mouse_scroll_state_changed.h
Normal file
|
@ -0,0 +1,34 @@
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Copyright (c) 2020 The ZMK Contributors
|
||||||
|
*
|
||||||
|
* SPDX-License-Identifier: MIT
|
||||||
|
*/
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <zephyr.h>
|
||||||
|
#include <zmk/event_manager.h>
|
||||||
|
#include <zmk/mouse.h>
|
||||||
|
#include <dt-bindings/zmk/mouse.h>
|
||||||
|
|
||||||
|
struct zmk_mouse_scroll_state_changed {
|
||||||
|
struct vector2d max_speed;
|
||||||
|
struct mouse_config config;
|
||||||
|
bool state;
|
||||||
|
int64_t timestamp;
|
||||||
|
};
|
||||||
|
|
||||||
|
ZMK_EVENT_DECLARE(zmk_mouse_scroll_state_changed);
|
||||||
|
|
||||||
|
static inline struct zmk_mouse_scroll_state_changed_event *
|
||||||
|
zmk_mouse_scroll_state_changed_from_encoded(uint32_t encoded, struct mouse_config config,
|
||||||
|
bool pressed, int64_t timestamp) {
|
||||||
|
struct vector2d max_speed = (struct vector2d){
|
||||||
|
.x = SCROLL_HOR_DECODE(encoded),
|
||||||
|
.y = SCROLL_VERT_DECODE(encoded),
|
||||||
|
};
|
||||||
|
|
||||||
|
return new_zmk_mouse_scroll_state_changed((struct zmk_mouse_scroll_state_changed){
|
||||||
|
.max_speed = max_speed, .config = config, .state = pressed, .timestamp = timestamp});
|
||||||
|
}
|
39
app/include/zmk/events/mouse_tick.h
Normal file
39
app/include/zmk/events/mouse_tick.h
Normal file
|
@ -0,0 +1,39 @@
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Copyright (c) 2020 The ZMK Contributors
|
||||||
|
*
|
||||||
|
* SPDX-License-Identifier: MIT
|
||||||
|
*/
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <dt-bindings/zmk/mouse.h>
|
||||||
|
#include <zephyr.h>
|
||||||
|
#include <zmk/event_manager.h>
|
||||||
|
#include <zmk/mouse.h>
|
||||||
|
|
||||||
|
struct zmk_mouse_tick {
|
||||||
|
struct vector2d max_move;
|
||||||
|
struct vector2d max_scroll;
|
||||||
|
struct mouse_config move_config;
|
||||||
|
struct mouse_config scroll_config;
|
||||||
|
int64_t *start_time;
|
||||||
|
int64_t timestamp;
|
||||||
|
};
|
||||||
|
|
||||||
|
ZMK_EVENT_DECLARE(zmk_mouse_tick);
|
||||||
|
|
||||||
|
static inline struct zmk_mouse_tick_event *zmk_mouse_tick(struct vector2d max_move,
|
||||||
|
struct vector2d max_scroll,
|
||||||
|
struct mouse_config move_config,
|
||||||
|
struct mouse_config scroll_config,
|
||||||
|
int64_t *movement_start) {
|
||||||
|
return new_zmk_mouse_tick((struct zmk_mouse_tick){
|
||||||
|
.max_move = max_move,
|
||||||
|
.max_scroll = max_scroll,
|
||||||
|
.move_config = move_config,
|
||||||
|
.scroll_config = scroll_config,
|
||||||
|
.start_time = movement_start,
|
||||||
|
.timestamp = k_uptime_get(),
|
||||||
|
});
|
||||||
|
}
|
30
app/include/zmk/mouse.h
Normal file
30
app/include/zmk/mouse.h
Normal file
|
@ -0,0 +1,30 @@
|
||||||
|
/*
|
||||||
|
* Copyright (c) 2021 The ZMK Contributors
|
||||||
|
*
|
||||||
|
* SPDX-License-Identifier: MIT
|
||||||
|
*/
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <zephyr.h>
|
||||||
|
#include <dt-bindings/zmk/mouse.h>
|
||||||
|
|
||||||
|
typedef uint16_t zmk_mouse_button_flags_t;
|
||||||
|
typedef uint16_t zmk_mouse_button_t;
|
||||||
|
|
||||||
|
struct mouse_config {
|
||||||
|
int delay_ms;
|
||||||
|
int time_to_max_speed_ms;
|
||||||
|
// acceleration exponent 0: uniform speed
|
||||||
|
// acceleration exponent 1: uniform acceleration
|
||||||
|
// acceleration exponent 2: uniform jerk
|
||||||
|
int acceleration_exponent;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct vector2d {
|
||||||
|
float x;
|
||||||
|
float y;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct k_work_q *zmk_mouse_work_q();
|
||||||
|
int zmk_mouse_init();
|
10
app/include/zmk/trackball_pim447.h
Normal file
10
app/include/zmk/trackball_pim447.h
Normal file
|
@ -0,0 +1,10 @@
|
||||||
|
/*
|
||||||
|
* Copyright (c) 2021 The ZMK Contributors
|
||||||
|
*
|
||||||
|
* SPDX-License-Identifier: MIT
|
||||||
|
*/
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <dt-bindings/zmk/trackball_pim447.h>
|
||||||
|
|
||||||
|
extern void zmk_trackball_pim447_set_mode(int mode);
|
48
app/src/behaviors/behavior_mouse_key_press.c
Normal file
48
app/src/behaviors/behavior_mouse_key_press.c
Normal file
|
@ -0,0 +1,48 @@
|
||||||
|
/*
|
||||||
|
* Copyright (c) 2021 The ZMK Contributors
|
||||||
|
*
|
||||||
|
* SPDX-License-Identifier: MIT
|
||||||
|
*/
|
||||||
|
|
||||||
|
#define DT_DRV_COMPAT zmk_behavior_mouse_key_press
|
||||||
|
|
||||||
|
#include <device.h>
|
||||||
|
#include <drivers/behavior.h>
|
||||||
|
#include <logging/log.h>
|
||||||
|
|
||||||
|
#include <zmk/behavior.h>
|
||||||
|
#include <zmk/event_manager.h>
|
||||||
|
#include <zmk/events/mouse_button_state_changed.h>
|
||||||
|
|
||||||
|
LOG_MODULE_DECLARE(zmk, CONFIG_ZMK_LOG_LEVEL);
|
||||||
|
|
||||||
|
#if DT_HAS_COMPAT_STATUS_OKAY(DT_DRV_COMPAT)
|
||||||
|
|
||||||
|
static int behavior_mouse_key_press_init(const struct device *dev) { return 0; };
|
||||||
|
|
||||||
|
static int on_keymap_binding_pressed(struct zmk_behavior_binding *binding,
|
||||||
|
struct zmk_behavior_binding_event event) {
|
||||||
|
LOG_DBG("position %d keycode 0x%02X", event.position, binding->param1);
|
||||||
|
|
||||||
|
return ZMK_EVENT_RAISE(
|
||||||
|
zmk_mouse_button_state_changed_from_encoded(binding->param1, true, event.timestamp));
|
||||||
|
}
|
||||||
|
|
||||||
|
static int on_keymap_binding_released(struct zmk_behavior_binding *binding,
|
||||||
|
struct zmk_behavior_binding_event event) {
|
||||||
|
LOG_DBG("position %d keycode 0x%02X", event.position, binding->param1);
|
||||||
|
return ZMK_EVENT_RAISE(
|
||||||
|
zmk_mouse_button_state_changed_from_encoded(binding->param1, false, event.timestamp));
|
||||||
|
}
|
||||||
|
|
||||||
|
static const struct behavior_driver_api behavior_mouse_key_press_driver_api = {
|
||||||
|
.binding_pressed = on_keymap_binding_pressed, .binding_released = on_keymap_binding_released};
|
||||||
|
|
||||||
|
#define KP_INST(n) \
|
||||||
|
DEVICE_DT_INST_DEFINE(n, behavior_mouse_key_press_init, device_pm_control_nop, NULL, NULL, \
|
||||||
|
APPLICATION, CONFIG_KERNEL_INIT_PRIORITY_DEFAULT, \
|
||||||
|
&behavior_mouse_key_press_driver_api);
|
||||||
|
|
||||||
|
DT_INST_FOREACH_STATUS_OKAY(KP_INST)
|
||||||
|
|
||||||
|
#endif /* DT_HAS_COMPAT_STATUS_OKAY(DT_DRV_COMPAT) */
|
57
app/src/behaviors/behavior_mouse_move.c
Normal file
57
app/src/behaviors/behavior_mouse_move.c
Normal file
|
@ -0,0 +1,57 @@
|
||||||
|
/*
|
||||||
|
* Copyright (c) 2021 The ZMK Contributors
|
||||||
|
*
|
||||||
|
* SPDX-License-Identifier: MIT
|
||||||
|
*/
|
||||||
|
|
||||||
|
#define DT_DRV_COMPAT zmk_behavior_mouse_move
|
||||||
|
|
||||||
|
#include <device.h>
|
||||||
|
#include <drivers/behavior.h>
|
||||||
|
#include <logging/log.h>
|
||||||
|
|
||||||
|
#include <zmk/behavior.h>
|
||||||
|
#include <zmk/event_manager.h>
|
||||||
|
#include <zmk/events/mouse_move_state_changed.h>
|
||||||
|
#include <zmk/mouse.h>
|
||||||
|
|
||||||
|
LOG_MODULE_DECLARE(zmk, CONFIG_ZMK_LOG_LEVEL);
|
||||||
|
|
||||||
|
#if DT_HAS_COMPAT_STATUS_OKAY(DT_DRV_COMPAT)
|
||||||
|
|
||||||
|
static int behavior_mouse_move_init(const struct device *dev) { return 0; };
|
||||||
|
|
||||||
|
static int on_keymap_binding_pressed(struct zmk_behavior_binding *binding,
|
||||||
|
struct zmk_behavior_binding_event event) {
|
||||||
|
LOG_DBG("position %d keycode 0x%02X", event.position, binding->param1);
|
||||||
|
const struct device *dev = device_get_binding(binding->behavior_dev);
|
||||||
|
const struct mouse_config *config = dev->config;
|
||||||
|
return ZMK_EVENT_RAISE(
|
||||||
|
zmk_mouse_move_state_changed_from_encoded(binding->param1, *config, true, event.timestamp));
|
||||||
|
}
|
||||||
|
|
||||||
|
static int on_keymap_binding_released(struct zmk_behavior_binding *binding,
|
||||||
|
struct zmk_behavior_binding_event event) {
|
||||||
|
LOG_DBG("position %d keycode 0x%02X", event.position, binding->param1);
|
||||||
|
const struct device *dev = device_get_binding(binding->behavior_dev);
|
||||||
|
const struct mouse_config *config = dev->config;
|
||||||
|
return ZMK_EVENT_RAISE(zmk_mouse_move_state_changed_from_encoded(binding->param1, *config,
|
||||||
|
false, event.timestamp));
|
||||||
|
}
|
||||||
|
|
||||||
|
static const struct behavior_driver_api behavior_mouse_move_driver_api = {
|
||||||
|
.binding_pressed = on_keymap_binding_pressed, .binding_released = on_keymap_binding_released};
|
||||||
|
|
||||||
|
#define KP_INST(n) \
|
||||||
|
static struct mouse_config behavior_mouse_move_config_##n = { \
|
||||||
|
.delay_ms = DT_INST_PROP(n, delay_ms), \
|
||||||
|
.time_to_max_speed_ms = DT_INST_PROP(n, time_to_max_speed_ms), \
|
||||||
|
.acceleration_exponent = DT_INST_PROP(n, acceleration_exponent), \
|
||||||
|
}; \
|
||||||
|
DEVICE_DT_INST_DEFINE(n, behavior_mouse_move_init, device_pm_control_nop, NULL, \
|
||||||
|
&behavior_mouse_move_config_##n, APPLICATION, \
|
||||||
|
CONFIG_KERNEL_INIT_PRIORITY_DEFAULT, &behavior_mouse_move_driver_api);
|
||||||
|
|
||||||
|
DT_INST_FOREACH_STATUS_OKAY(KP_INST)
|
||||||
|
|
||||||
|
#endif /* DT_HAS_COMPAT_STATUS_OKAY(DT_DRV_COMPAT) */
|
58
app/src/behaviors/behavior_mouse_scroll.c
Normal file
58
app/src/behaviors/behavior_mouse_scroll.c
Normal file
|
@ -0,0 +1,58 @@
|
||||||
|
/*
|
||||||
|
* Copyright (c) 2021 The ZMK Contributors
|
||||||
|
*
|
||||||
|
* SPDX-License-Identifier: MIT
|
||||||
|
*/
|
||||||
|
|
||||||
|
#define DT_DRV_COMPAT zmk_behavior_mouse_scroll
|
||||||
|
|
||||||
|
#include <device.h>
|
||||||
|
#include <drivers/behavior.h>
|
||||||
|
#include <logging/log.h>
|
||||||
|
|
||||||
|
#include <zmk/event_manager.h>
|
||||||
|
#include <zmk/events/mouse_scroll_state_changed.h>
|
||||||
|
#include <zmk/behavior.h>
|
||||||
|
#include <zmk/hid.h>
|
||||||
|
#include <zmk/endpoints.h>
|
||||||
|
|
||||||
|
LOG_MODULE_DECLARE(zmk, CONFIG_ZMK_LOG_LEVEL);
|
||||||
|
|
||||||
|
#if DT_HAS_COMPAT_STATUS_OKAY(DT_DRV_COMPAT)
|
||||||
|
|
||||||
|
static int behavior_mouse_scroll_init(const struct device *dev) { return 0; };
|
||||||
|
|
||||||
|
static int on_keymap_binding_pressed(struct zmk_behavior_binding *binding,
|
||||||
|
struct zmk_behavior_binding_event event) {
|
||||||
|
LOG_DBG("position %d keycode 0x%02X", event.position, binding->param1);
|
||||||
|
const struct device *dev = device_get_binding(binding->behavior_dev);
|
||||||
|
const struct mouse_config *config = dev->config;
|
||||||
|
return ZMK_EVENT_RAISE(zmk_mouse_scroll_state_changed_from_encoded(binding->param1, *config,
|
||||||
|
true, event.timestamp));
|
||||||
|
}
|
||||||
|
|
||||||
|
static int on_keymap_binding_released(struct zmk_behavior_binding *binding,
|
||||||
|
struct zmk_behavior_binding_event event) {
|
||||||
|
LOG_DBG("position %d keycode 0x%02X", event.position, binding->param1);
|
||||||
|
const struct device *dev = device_get_binding(binding->behavior_dev);
|
||||||
|
const struct mouse_config *config = dev->config;
|
||||||
|
return ZMK_EVENT_RAISE(zmk_mouse_scroll_state_changed_from_encoded(binding->param1, *config,
|
||||||
|
false, event.timestamp));
|
||||||
|
}
|
||||||
|
|
||||||
|
static const struct behavior_driver_api behavior_mouse_scroll_driver_api = {
|
||||||
|
.binding_pressed = on_keymap_binding_pressed, .binding_released = on_keymap_binding_released};
|
||||||
|
|
||||||
|
#define KP_INST(n) \
|
||||||
|
static struct mouse_config behavior_mouse_scroll_config_##n = { \
|
||||||
|
.delay_ms = DT_INST_PROP(n, delay_ms), \
|
||||||
|
.time_to_max_speed_ms = DT_INST_PROP(n, time_to_max_speed_ms), \
|
||||||
|
.acceleration_exponent = DT_INST_PROP(n, acceleration_exponent), \
|
||||||
|
}; \
|
||||||
|
DEVICE_DT_INST_DEFINE(n, behavior_mouse_scroll_init, device_pm_control_nop, 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) */
|
61
app/src/behaviors/behavior_trackball_pim447.c
Normal file
61
app/src/behaviors/behavior_trackball_pim447.c
Normal file
|
@ -0,0 +1,61 @@
|
||||||
|
/*
|
||||||
|
* Copyright (c) 2021 The ZMK Contributors
|
||||||
|
*
|
||||||
|
* SPDX-License-Identifier: MIT
|
||||||
|
*/
|
||||||
|
|
||||||
|
#define DT_DRV_COMPAT zmk_behavior_trackball_pim447
|
||||||
|
|
||||||
|
#include <devicetree.h>
|
||||||
|
#include <drivers/behavior.h>
|
||||||
|
#include <zmk/trackball_pim447.h>
|
||||||
|
|
||||||
|
#if DT_HAS_COMPAT_STATUS_OKAY(DT_DRV_COMPAT)
|
||||||
|
|
||||||
|
struct config {
|
||||||
|
int mode;
|
||||||
|
bool momentary;
|
||||||
|
};
|
||||||
|
|
||||||
|
static int behavior_trackball_pim447_init(const struct device *dev) { return 0; };
|
||||||
|
|
||||||
|
static int on_keymap_binding_pressed(struct zmk_behavior_binding *binding,
|
||||||
|
struct zmk_behavior_binding_event event) {
|
||||||
|
const struct device *device = device_get_binding(binding->behavior_dev);
|
||||||
|
const struct config *config = device->config;
|
||||||
|
|
||||||
|
zmk_trackball_pim447_set_mode(config->mode);
|
||||||
|
|
||||||
|
return ZMK_BEHAVIOR_OPAQUE;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int on_keymap_binding_released(struct zmk_behavior_binding *binding,
|
||||||
|
struct zmk_behavior_binding_event event) {
|
||||||
|
const struct device *device = device_get_binding(binding->behavior_dev);
|
||||||
|
const struct config *config = device->config;
|
||||||
|
|
||||||
|
if (config->momentary) {
|
||||||
|
zmk_trackball_pim447_set_mode(PIM447_TOGGLE);
|
||||||
|
}
|
||||||
|
|
||||||
|
return ZMK_BEHAVIOR_OPAQUE;
|
||||||
|
}
|
||||||
|
|
||||||
|
static const struct behavior_driver_api behavior_trackball_pim447_driver_api = {
|
||||||
|
.binding_pressed = on_keymap_binding_pressed,
|
||||||
|
.binding_released = on_keymap_binding_released,
|
||||||
|
};
|
||||||
|
|
||||||
|
#define PIM447_INST(n) \
|
||||||
|
static const struct config config_##n = { \
|
||||||
|
.mode = DT_INST_PROP(n, mode), \
|
||||||
|
.momentary = DT_INST_PROP(n, momentary) \
|
||||||
|
}; \
|
||||||
|
\
|
||||||
|
DEVICE_DT_INST_DEFINE(n, behavior_trackball_pim447_init, device_pm_control_nop, \
|
||||||
|
NULL, &config_##n, APPLICATION, \
|
||||||
|
CONFIG_KERNEL_INIT_PRIORITY_DEFAULT, &behavior_trackball_pim447_driver_api);
|
||||||
|
|
||||||
|
DT_INST_FOREACH_STATUS_OKAY(PIM447_INST)
|
||||||
|
|
||||||
|
#endif /* DT_HAS_COMPAT_STATUS_OKAY(DT_DRV_COMPAT) */
|
10
app/src/events/mouse_button_state_changed.c
Normal file
10
app/src/events/mouse_button_state_changed.c
Normal file
|
@ -0,0 +1,10 @@
|
||||||
|
/*
|
||||||
|
* Copyright (c) 2020 The ZMK Contributors
|
||||||
|
*
|
||||||
|
* SPDX-License-Identifier: MIT
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <kernel.h>
|
||||||
|
#include <zmk/events/mouse_button_state_changed.h>
|
||||||
|
|
||||||
|
ZMK_EVENT_IMPL(zmk_mouse_button_state_changed);
|
10
app/src/events/mouse_move_state_changed.c
Normal file
10
app/src/events/mouse_move_state_changed.c
Normal file
|
@ -0,0 +1,10 @@
|
||||||
|
/*
|
||||||
|
* Copyright (c) 2020 The ZMK Contributors
|
||||||
|
*
|
||||||
|
* SPDX-License-Identifier: MIT
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <kernel.h>
|
||||||
|
#include <zmk/events/mouse_move_state_changed.h>
|
||||||
|
|
||||||
|
ZMK_EVENT_IMPL(zmk_mouse_move_state_changed);
|
10
app/src/events/mouse_scroll_state_changed.c
Normal file
10
app/src/events/mouse_scroll_state_changed.c
Normal file
|
@ -0,0 +1,10 @@
|
||||||
|
/*
|
||||||
|
* Copyright (c) 2020 The ZMK Contributors
|
||||||
|
*
|
||||||
|
* SPDX-License-Identifier: MIT
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <kernel.h>
|
||||||
|
#include <zmk/events/mouse_scroll_state_changed.h>
|
||||||
|
|
||||||
|
ZMK_EVENT_IMPL(zmk_mouse_scroll_state_changed);
|
10
app/src/events/mouse_tick.c
Normal file
10
app/src/events/mouse_tick.c
Normal file
|
@ -0,0 +1,10 @@
|
||||||
|
/*
|
||||||
|
* Copyright (c) 2020 The ZMK Contributors
|
||||||
|
*
|
||||||
|
* SPDX-License-Identifier: MIT
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <kernel.h>
|
||||||
|
#include <zmk/events/mouse_tick.h>
|
||||||
|
|
||||||
|
ZMK_EVENT_IMPL(zmk_mouse_tick);
|
38
app/src/mouse/Kconfig
Normal file
38
app/src/mouse/Kconfig
Normal file
|
@ -0,0 +1,38 @@
|
||||||
|
# Copyright (c) 2021 The ZMK Contributors
|
||||||
|
# SPDX-License-Identifier: MIT
|
||||||
|
|
||||||
|
menuconfig ZMK_MOUSE
|
||||||
|
bool "Enable ZMK mouse emulation"
|
||||||
|
default n
|
||||||
|
|
||||||
|
config ZMK_MOUSE_TICK_DURATION
|
||||||
|
int "Mouse tick duration in ms"
|
||||||
|
default 8
|
||||||
|
|
||||||
|
if ZMK_MOUSE
|
||||||
|
|
||||||
|
choice ZMK_MOUSE_WORK_QUEUE
|
||||||
|
prompt "Work queue selection for mouse events"
|
||||||
|
default ZMK_MOUSE_WORK_QUEUE_DEDICATED
|
||||||
|
|
||||||
|
config ZMK_MOUSE_WORK_QUEUE_SYSTEM
|
||||||
|
bool "Use default system work queue for mouse events"
|
||||||
|
|
||||||
|
config ZMK_MOUSE_WORK_QUEUE_DEDICATED
|
||||||
|
bool "Use dedicated work queue for mouse events"
|
||||||
|
|
||||||
|
endchoice
|
||||||
|
|
||||||
|
if ZMK_MOUSE_WORK_QUEUE_DEDICATED
|
||||||
|
|
||||||
|
config ZMK_MOUSE_DEDICATED_THREAD_STACK_SIZE
|
||||||
|
int "Stack size for dedicated mouse thread/queue"
|
||||||
|
default 2048
|
||||||
|
|
||||||
|
config ZMK_MOUSE_DEDICATED_THREAD_PRIORITY
|
||||||
|
int "Thread priority for dedicated mouse thread/queue"
|
||||||
|
default 3
|
||||||
|
|
||||||
|
endif # ZMK_MOUSE_WORK_QUEUE_DEDICATED
|
||||||
|
|
||||||
|
endif
|
160
app/src/mouse/key_listener.c
Normal file
160
app/src/mouse/key_listener.c
Normal file
|
@ -0,0 +1,160 @@
|
||||||
|
/*
|
||||||
|
* Copyright (c) 2021 The ZMK Contributors
|
||||||
|
*
|
||||||
|
* SPDX-License-Identifier: MIT
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <drivers/behavior.h>
|
||||||
|
#include <logging/log.h>
|
||||||
|
|
||||||
|
LOG_MODULE_DECLARE(zmk, CONFIG_ZMK_LOG_LEVEL);
|
||||||
|
|
||||||
|
#include <zmk/event_manager.h>
|
||||||
|
#include <zmk/events/mouse_button_state_changed.h>
|
||||||
|
#include <zmk/events/mouse_move_state_changed.h>
|
||||||
|
#include <zmk/events/mouse_scroll_state_changed.h>
|
||||||
|
#include <zmk/events/mouse_tick.h>
|
||||||
|
#include <zmk/hid.h>
|
||||||
|
#include <zmk/endpoints.h>
|
||||||
|
#include <zmk/mouse.h>
|
||||||
|
|
||||||
|
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);
|
30
app/src/mouse/main.c
Normal file
30
app/src/mouse/main.c
Normal file
|
@ -0,0 +1,30 @@
|
||||||
|
/*
|
||||||
|
* Copyright (c) 2020 The ZMK Contributors
|
||||||
|
*
|
||||||
|
* SPDX-License-Identifier: MIT
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <kernel.h>
|
||||||
|
#include <zmk/mouse.h>
|
||||||
|
|
||||||
|
#if IS_ENABLED(CONFIG_ZMK_MOUSE_WORK_QUEUE_DEDICATED)
|
||||||
|
K_THREAD_STACK_DEFINE(mouse_work_stack_area, CONFIG_ZMK_MOUSE_DEDICATED_THREAD_STACK_SIZE);
|
||||||
|
static struct k_work_q mouse_work_q;
|
||||||
|
#endif
|
||||||
|
|
||||||
|
struct k_work_q *zmk_mouse_work_q() {
|
||||||
|
#if IS_ENABLED(CONFIG_ZMK_MOUSE_WORK_QUEUE_DEDICATED)
|
||||||
|
return &mouse_work_q;
|
||||||
|
#else
|
||||||
|
return &k_sys_work_q;
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
int zmk_mouse_init() {
|
||||||
|
#if IS_ENABLED(CONFIG_ZMK_MOUSE_WORK_QUEUE_DEDICATED)
|
||||||
|
k_work_q_start(&mouse_work_q, mouse_work_stack_area,
|
||||||
|
K_THREAD_STACK_SIZEOF(mouse_work_stack_area),
|
||||||
|
CONFIG_ZMK_MOUSE_DEDICATED_THREAD_PRIORITY);
|
||||||
|
#endif
|
||||||
|
return 0;
|
||||||
|
}
|
102
app/src/mouse/tick_listener.c
Normal file
102
app/src/mouse/tick_listener.c
Normal file
|
@ -0,0 +1,102 @@
|
||||||
|
/*
|
||||||
|
* Copyright (c) 2020 The ZMK Contributors
|
||||||
|
*
|
||||||
|
* SPDX-License-Identifier: MIT
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <logging/log.h>
|
||||||
|
|
||||||
|
LOG_MODULE_DECLARE(zmk, CONFIG_ZMK_LOG_LEVEL);
|
||||||
|
|
||||||
|
#include <zmk/event_manager.h>
|
||||||
|
#include <zmk/events/mouse_tick.h>
|
||||||
|
#include <zmk/endpoints.h>
|
||||||
|
#include <zmk/mouse.h>
|
||||||
|
|
||||||
|
#include <sys/util.h> // CLAMP
|
||||||
|
|
||||||
|
#if CONFIG_MINIMAL_LIBC
|
||||||
|
static float powf(float base, float exponent) {
|
||||||
|
// poor man's power implementation rounds the exponent down to the nearest integer.
|
||||||
|
float power = 1.0f;
|
||||||
|
for (; exponent >= 1.0f; exponent--) {
|
||||||
|
power = power * base;
|
||||||
|
}
|
||||||
|
return power;
|
||||||
|
}
|
||||||
|
#else
|
||||||
|
#include <math.h>
|
||||||
|
#endif
|
||||||
|
|
||||||
|
struct vector2d move_remainder = {0};
|
||||||
|
struct vector2d scroll_remainder = {0};
|
||||||
|
|
||||||
|
static int64_t ms_since_start(int64_t start, int64_t now, int64_t delay) {
|
||||||
|
int64_t move_duration = now - (start + delay);
|
||||||
|
// start can be in the future if there's a delay
|
||||||
|
if (move_duration < 0) {
|
||||||
|
move_duration = 0;
|
||||||
|
}
|
||||||
|
return move_duration;
|
||||||
|
}
|
||||||
|
|
||||||
|
static float speed(const struct mouse_config *config, float max_speed, int64_t duration_ms) {
|
||||||
|
// Calculate the speed based on MouseKeysAccel
|
||||||
|
// See https://en.wikipedia.org/wiki/Mouse_keys
|
||||||
|
if (duration_ms > config->time_to_max_speed_ms || config->time_to_max_speed_ms == 0 ||
|
||||||
|
config->acceleration_exponent == 0) {
|
||||||
|
return max_speed;
|
||||||
|
}
|
||||||
|
float time_fraction = (float)duration_ms / config->time_to_max_speed_ms;
|
||||||
|
return max_speed * powf(time_fraction, config->acceleration_exponent);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void track_remainder(float *move, float *remainder) {
|
||||||
|
float new_move = *move + *remainder;
|
||||||
|
*remainder = new_move - (int)new_move;
|
||||||
|
*move = (int)new_move;
|
||||||
|
}
|
||||||
|
|
||||||
|
static struct vector2d update_movement(struct vector2d *remainder,
|
||||||
|
const struct mouse_config *config, struct vector2d max_speed,
|
||||||
|
int64_t now, int64_t *start_time) {
|
||||||
|
struct vector2d move = {0};
|
||||||
|
if (max_speed.x == 0 && max_speed.y == 0) {
|
||||||
|
*remainder = (struct vector2d){0};
|
||||||
|
return move;
|
||||||
|
}
|
||||||
|
|
||||||
|
int64_t move_duration = ms_since_start(*start_time, now, config->delay_ms);
|
||||||
|
move = (struct vector2d){
|
||||||
|
.x = speed(config, max_speed.x, move_duration) * CONFIG_ZMK_MOUSE_TICK_DURATION / 1000,
|
||||||
|
.y = speed(config, max_speed.y, move_duration) * CONFIG_ZMK_MOUSE_TICK_DURATION / 1000,
|
||||||
|
};
|
||||||
|
|
||||||
|
track_remainder(&(move.x), &(remainder->x));
|
||||||
|
track_remainder(&(move.y), &(remainder->y));
|
||||||
|
|
||||||
|
return move;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void mouse_tick_handler(const struct zmk_mouse_tick *tick) {
|
||||||
|
struct vector2d move = update_movement(&move_remainder, &(tick->move_config), tick->max_move,
|
||||||
|
tick->timestamp, tick->start_time);
|
||||||
|
zmk_hid_mouse_movement_update((int16_t)CLAMP(move.x, INT16_MIN, INT16_MAX),
|
||||||
|
(int16_t)CLAMP(move.y, INT16_MIN, INT16_MAX));
|
||||||
|
struct vector2d scroll = update_movement(&scroll_remainder, &(tick->scroll_config),
|
||||||
|
tick->max_scroll, tick->timestamp, tick->start_time);
|
||||||
|
zmk_hid_mouse_scroll_update((int8_t)CLAMP(scroll.x, INT8_MIN, INT8_MAX),
|
||||||
|
(int8_t)CLAMP(scroll.y, INT8_MIN, INT8_MAX));
|
||||||
|
}
|
||||||
|
|
||||||
|
int zmk_mouse_tick_listener(const zmk_event_t *eh) {
|
||||||
|
const struct zmk_mouse_tick *tick = as_zmk_mouse_tick(eh);
|
||||||
|
if (tick) {
|
||||||
|
mouse_tick_handler(tick);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
ZMK_LISTENER(zmk_mouse_tick_listener, zmk_mouse_tick_listener);
|
||||||
|
ZMK_SUBSCRIPTION(zmk_mouse_tick_listener, zmk_mouse_tick);
|
198
app/src/mouse/trackball_pim447.c
Normal file
198
app/src/mouse/trackball_pim447.c
Normal file
|
@ -0,0 +1,198 @@
|
||||||
|
/*
|
||||||
|
* Copyright (c) 2021 Cedric VINCENT
|
||||||
|
*
|
||||||
|
* SPDX-License-Identifier: MIT
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <drivers/sensor.h>
|
||||||
|
#include <logging/log.h>
|
||||||
|
#include <zmk/hid.h>
|
||||||
|
#include <zmk/endpoints.h>
|
||||||
|
#include <zmk/trackball_pim447.h>
|
||||||
|
|
||||||
|
LOG_MODULE_REGISTER(PIM447, CONFIG_SENSOR_LOG_LEVEL);
|
||||||
|
|
||||||
|
#define MOVE_FACTOR DT_PROP(DT_INST(0, pimoroni_trackball_pim447), move_factor)
|
||||||
|
#define MOVE_X_INVERT DT_PROP(DT_INST(0, pimoroni_trackball_pim447), invert_move_x)
|
||||||
|
#define MOVE_Y_INVERT DT_PROP(DT_INST(0, pimoroni_trackball_pim447), invert_move_y)
|
||||||
|
#define MOVE_X_FACTOR (MOVE_FACTOR * (MOVE_X_INVERT ? -1 : 1))
|
||||||
|
#define MOVE_Y_FACTOR (MOVE_FACTOR * (MOVE_Y_INVERT ? -1 : 1))
|
||||||
|
|
||||||
|
#define SCROLL_DIVISOR DT_PROP(DT_INST(0, pimoroni_trackball_pim447), scroll_divisor)
|
||||||
|
#define SCROLL_X_INVERT DT_PROP(DT_INST(0, pimoroni_trackball_pim447), invert_scroll_x)
|
||||||
|
#define SCROLL_Y_INVERT DT_PROP(DT_INST(0, pimoroni_trackball_pim447), invert_scroll_y)
|
||||||
|
#define SCROLL_X_DIVISOR (SCROLL_DIVISOR * (SCROLL_X_INVERT ? -1 : 1))
|
||||||
|
#define SCROLL_Y_DIVISOR (SCROLL_DIVISOR * (SCROLL_Y_INVERT ? 1 : -1))
|
||||||
|
|
||||||
|
#define BUTTON DT_PROP(DT_INST(0, pimoroni_trackball_pim447), button)
|
||||||
|
#define SWAP_AXES DT_PROP(DT_INST(0, pimoroni_trackball_pim447), swap_axes)
|
||||||
|
|
||||||
|
static int mode = DT_PROP(DT_INST(0, pimoroni_trackball_pim447), mode);
|
||||||
|
|
||||||
|
void zmk_trackball_pim447_set_mode(int new_mode)
|
||||||
|
{
|
||||||
|
switch (new_mode) {
|
||||||
|
case PIM447_MOVE:
|
||||||
|
case PIM447_SCROLL:
|
||||||
|
mode = new_mode;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case PIM447_TOGGLE:
|
||||||
|
mode = mode == PIM447_MOVE
|
||||||
|
? PIM447_SCROLL
|
||||||
|
: PIM447_MOVE;
|
||||||
|
break;
|
||||||
|
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* It feels more natural and more confortable to convert the speed
|
||||||
|
* reported by the PIM447 trackball.
|
||||||
|
*/
|
||||||
|
static int16_t convert_speed(int32_t value)
|
||||||
|
{
|
||||||
|
bool negative = (value < 0);
|
||||||
|
|
||||||
|
if (negative) {
|
||||||
|
value = -value;
|
||||||
|
}
|
||||||
|
|
||||||
|
switch (value) {
|
||||||
|
case 0: value = 0; break;
|
||||||
|
case 1: value = 1; break;
|
||||||
|
case 2: value = 4; break;
|
||||||
|
case 3: value = 8; break;
|
||||||
|
case 4: value = 18; break;
|
||||||
|
case 5: value = 32; break;
|
||||||
|
case 6: value = 50; break;
|
||||||
|
case 7: value = 72; break;
|
||||||
|
case 8: value = 98; break;
|
||||||
|
default: value = 127; break;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (negative) {
|
||||||
|
value = -value;
|
||||||
|
}
|
||||||
|
|
||||||
|
return value;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void thread_code(void *p1, void *p2, void *p3)
|
||||||
|
{
|
||||||
|
const struct device *dev;
|
||||||
|
int result;
|
||||||
|
|
||||||
|
/* PIM447 trackball initialization. */
|
||||||
|
|
||||||
|
const char *label = DT_LABEL(DT_INST(0, pimoroni_trackball_pim447));
|
||||||
|
dev = device_get_binding(label);
|
||||||
|
if (dev == NULL) {
|
||||||
|
LOG_ERR("Cannot get TRACKBALL_PIM447 device");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Event loop. */
|
||||||
|
|
||||||
|
bool button_press_sent = false;
|
||||||
|
bool button_release_sent = false;
|
||||||
|
|
||||||
|
while (true) {
|
||||||
|
struct sensor_value pos_dx, pos_dy, pos_dz;
|
||||||
|
bool send_report = false;
|
||||||
|
int clear = PIM447_NONE;
|
||||||
|
|
||||||
|
result = sensor_sample_fetch(dev);
|
||||||
|
if (result < 0) {
|
||||||
|
LOG_ERR("Failed to fetch TRACKBALL_PIM447 sample");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
result = sensor_channel_get(dev, SENSOR_CHAN_POS_DX, &pos_dx);
|
||||||
|
if (result < 0) {
|
||||||
|
LOG_ERR("Failed to get TRACKBALL_PIM447 pos_dx channel value");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
result = sensor_channel_get(dev, SENSOR_CHAN_POS_DY, &pos_dy);
|
||||||
|
if (result < 0) {
|
||||||
|
LOG_ERR("Failed to get TRACKBALL_PIM447 pos_dy channel value");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
result = sensor_channel_get(dev, SENSOR_CHAN_POS_DZ, &pos_dz);
|
||||||
|
if (result < 0) {
|
||||||
|
LOG_ERR("Failed to get TRACKBALL_PIM447 pos_dz channel value");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (pos_dx.val1 != 0 || pos_dy.val1 != 0) {
|
||||||
|
if (SWAP_AXES) {
|
||||||
|
int32_t tmp = pos_dx.val1;
|
||||||
|
pos_dx.val1 = pos_dy.val1;
|
||||||
|
pos_dy.val1 = tmp;
|
||||||
|
}
|
||||||
|
|
||||||
|
switch(mode) {
|
||||||
|
default:
|
||||||
|
case PIM447_MOVE: {
|
||||||
|
int dx = convert_speed(pos_dx.val1) * MOVE_X_FACTOR;
|
||||||
|
int dy = convert_speed(pos_dy.val1) * MOVE_Y_FACTOR;
|
||||||
|
zmk_hid_mouse_movement_set(dx, dy);
|
||||||
|
send_report = true;
|
||||||
|
clear = PIM447_MOVE;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
case PIM447_SCROLL: {
|
||||||
|
int dx = pos_dx.val1 / SCROLL_X_DIVISOR;
|
||||||
|
int dy = pos_dy.val1 / SCROLL_Y_DIVISOR;
|
||||||
|
zmk_hid_mouse_scroll_set(dx, dy);
|
||||||
|
send_report = true;
|
||||||
|
clear = PIM447_SCROLL;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (pos_dz.val1 == 0x80 && button_press_sent == false) {
|
||||||
|
zmk_hid_mouse_button_press(BUTTON);
|
||||||
|
button_press_sent = true;
|
||||||
|
button_release_sent = false;
|
||||||
|
send_report = true;
|
||||||
|
} else if (pos_dz.val1 == 0x01 && button_release_sent == false) {
|
||||||
|
zmk_hid_mouse_button_release(BUTTON);
|
||||||
|
button_press_sent = false;
|
||||||
|
button_release_sent = true;
|
||||||
|
send_report = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (send_report) {
|
||||||
|
zmk_endpoints_send_mouse_report();
|
||||||
|
|
||||||
|
switch (clear) {
|
||||||
|
case PIM447_MOVE: zmk_hid_mouse_movement_set(0, 0); break;
|
||||||
|
case PIM447_SCROLL: zmk_hid_mouse_scroll_set(0, 0); break;
|
||||||
|
default: break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
k_sleep(K_MSEC(10));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#define STACK_SIZE 1024
|
||||||
|
|
||||||
|
static K_THREAD_STACK_DEFINE(thread_stack, STACK_SIZE);
|
||||||
|
static struct k_thread thread;
|
||||||
|
|
||||||
|
int zmk_trackball_pim447_init()
|
||||||
|
{
|
||||||
|
k_thread_create(&thread, thread_stack, STACK_SIZE, thread_code,
|
||||||
|
NULL, NULL, NULL, K_PRIO_PREEMPT(8), 0, K_NO_WAIT);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
SYS_INIT(zmk_trackball_pim447_init, APPLICATION, CONFIG_APPLICATION_INIT_PRIORITY);
|
1
app/tests/mouse-keys/mmv/events.patterns
Normal file
1
app/tests/mouse-keys/mmv/events.patterns
Normal file
|
@ -0,0 +1 @@
|
||||||
|
s/.*hid_listener_keycode_//p
|
2
app/tests/mouse-keys/mmv/keycode_events.snapshot
Normal file
2
app/tests/mouse-keys/mmv/keycode_events.snapshot
Normal file
|
@ -0,0 +1,2 @@
|
||||||
|
pressed: usage_page 0x07 keycode 0x05 implicit_mods 0x00 explicit_mods 0x00
|
||||||
|
released: usage_page 0x07 keycode 0x05 implicit_mods 0x00 explicit_mods 0x00
|
26
app/tests/mouse-keys/mmv/native_posix.keymap
Normal file
26
app/tests/mouse-keys/mmv/native_posix.keymap
Normal file
|
@ -0,0 +1,26 @@
|
||||||
|
#include <behaviors.dtsi>
|
||||||
|
#include <dt-bindings/zmk/keys.h>
|
||||||
|
#include <dt-bindings/zmk/kscan_mock.h>
|
||||||
|
#include <dt-bindings/zmk/mouse.h>
|
||||||
|
|
||||||
|
/ {
|
||||||
|
keymap {
|
||||||
|
compatible = "zmk,keymap";
|
||||||
|
label ="Default keymap";
|
||||||
|
|
||||||
|
default_layer {
|
||||||
|
bindings = <
|
||||||
|
&mmv MOVE_LEFT &none
|
||||||
|
&none &none
|
||||||
|
>;
|
||||||
|
};
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
&kscan {
|
||||||
|
events = <
|
||||||
|
ZMK_MOCK_PRESS(0,0,100)
|
||||||
|
ZMK_MOCK_RELEASE(0,0,10)
|
||||||
|
>;
|
||||||
|
};
|
Loading…
Add table
Reference in a new issue