forked from kofal.net/zmk
feat(mouse): Add mouse move and scroll support (#2477)
* feat(mouse): Add mouse move and scroll support
* Use Zephyr input subsystem for all pointers.
* Input processors for modifying events, e.g. scaling, swapping
codes, temporary (mouse) layers, etc.
* Mouse move/scroll behaviors.
* Infrastructure in place for physical pointer input devices.
* feat: Add input split support.
* docs: Add initial pointer docs.
---------
Co-authored-by: Cem Aksoylar <caksoylar@users.noreply.github.com>
Co-authored-by: Alexander Krikun <krikun98@gmail.com>
Co-authored-by: Robert U <urob@users.noreply.github.com>
Co-authored-by: Shawn Meier <ftc@users.noreply.github.com>
Co-authored-by: Chris Andreae <chris@andreae.gen.nz>
Co-authored-by: Anant Thazhemadam <47104651+thazhemadam@users.noreply.github.com>
Co-authored-by: Erik Tollerud <erik.tollerud@gmail.com>
Co-authored-by: Nicolas Munnich <98408764+Nick-Munnich@users.noreply.github.com>
This commit is contained in:
@@ -17,6 +17,7 @@ if(CONFIG_ZMK_BEHAVIOR_LOCAL_IDS)
|
||||
endif()
|
||||
|
||||
zephyr_syscall_header(${APPLICATION_SOURCE_DIR}/include/drivers/behavior.h)
|
||||
zephyr_syscall_header(${APPLICATION_SOURCE_DIR}/include/drivers/input_processor.h)
|
||||
zephyr_syscall_header(${APPLICATION_SOURCE_DIR}/include/drivers/ext_power.h)
|
||||
|
||||
# Add your source file to the "app" target. This must come after
|
||||
@@ -37,15 +38,14 @@ 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_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)
|
||||
target_sources_ifdef(CONFIG_ZMK_EXT_POWER app PRIVATE src/behaviors/behavior_ext_power.c)
|
||||
target_sources_ifdef(CONFIG_ZMK_BEHAVIOR_SOFT_OFF app PRIVATE src/behaviors/behavior_soft_off.c)
|
||||
add_subdirectory_ifdef(CONFIG_ZMK_POINTING src/pointing/)
|
||||
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(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_ifdef(CONFIG_ZMK_BEHAVIOR_HOLD_TAP app PRIVATE src/behaviors/behavior_hold_tap.c)
|
||||
@@ -65,6 +65,7 @@ if ((NOT CONFIG_ZMK_SPLIT) OR CONFIG_ZMK_SPLIT_ROLE_CENTRAL)
|
||||
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_BEHAVIOR_STUDIO_UNLOCK app PRIVATE src/behaviors/behavior_studio_unlock.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)
|
||||
|
||||
@@ -363,12 +363,7 @@ endif # ZMK_BACKLIGHT
|
||||
|
||||
endmenu # Display/LED Options
|
||||
|
||||
menu "Mouse Options"
|
||||
|
||||
config ZMK_MOUSE
|
||||
bool "Enable ZMK mouse emulation"
|
||||
|
||||
endmenu # Mouse Options
|
||||
rsource "src/pointing/Kconfig"
|
||||
|
||||
menu "Power Management"
|
||||
|
||||
|
||||
@@ -71,8 +71,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_POINTING
|
||||
|
||||
config ZMK_BEHAVIOR_STICKY_KEY
|
||||
bool
|
||||
@@ -94,6 +93,11 @@ 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 && ZMK_POINTING
|
||||
|
||||
config ZMK_BEHAVIOR_SENSOR_ROTATE_COMMON
|
||||
bool
|
||||
|
||||
|
||||
@@ -24,7 +24,7 @@ include:
|
||||
nickname: "display"
|
||||
- board: nice_nano_v2
|
||||
shield: kyria_left
|
||||
cmake-args: "-DCONFIG_ZMK_MOUSE=y"
|
||||
cmake-args: "-DCONFIG_ZMK_POINTING=y"
|
||||
nickname: "mouse"
|
||||
- board: sparkfun_pro_micro_rp2040
|
||||
shield: reviung41
|
||||
|
||||
@@ -1,3 +1,9 @@
|
||||
/*
|
||||
* Copyright (c) 2024 The ZMK Contributors
|
||||
*
|
||||
* SPDX-License-Identifier: MIT
|
||||
*/
|
||||
|
||||
#include <behaviors/key_press.dtsi>
|
||||
#include <behaviors/key_toggle.dtsi>
|
||||
#include <behaviors/transparent.dtsi>
|
||||
@@ -19,6 +25,6 @@
|
||||
#include <behaviors/key_repeat.dtsi>
|
||||
#include <behaviors/backlight.dtsi>
|
||||
#include <behaviors/macros.dtsi>
|
||||
#include <behaviors/mouse_key_press.dtsi>
|
||||
#include <behaviors/soft_off.dtsi>
|
||||
#include <behaviors/studio_unlock.dtsi>
|
||||
#include <behaviors/mouse_keys.dtsi>
|
||||
|
||||
@@ -16,4 +16,9 @@
|
||||
#binding-cells = <1>;
|
||||
};
|
||||
};
|
||||
|
||||
mkp_input_listener: mkp_input_listener {
|
||||
compatible = "zmk,input-listener";
|
||||
device = <&mkp>;
|
||||
};
|
||||
};
|
||||
|
||||
9
app/dts/behaviors/mouse_keys.dtsi
Normal file
9
app/dts/behaviors/mouse_keys.dtsi
Normal file
@@ -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"
|
||||
25
app/dts/behaviors/mouse_move.dtsi
Normal file
25
app/dts/behaviors/mouse_move.dtsi
Normal file
@@ -0,0 +1,25 @@
|
||||
/*
|
||||
* Copyright (c) 2024 The ZMK Contributors
|
||||
*
|
||||
* SPDX-License-Identifier: MIT
|
||||
*/
|
||||
|
||||
#include <zephyr/dt-bindings/input/input-event-codes.h>
|
||||
|
||||
/ {
|
||||
behaviors {
|
||||
/omit-if-no-ref/ mmv: mouse_move {
|
||||
compatible = "zmk,behavior-input-two-axis";
|
||||
#binding-cells = <1>;
|
||||
x-input-code = <INPUT_REL_X>;
|
||||
y-input-code = <INPUT_REL_Y>;
|
||||
time-to-max-speed-ms = <300>;
|
||||
acceleration-exponent = <1>;
|
||||
};
|
||||
};
|
||||
|
||||
mmv_input_listener: mmv_input_listener {
|
||||
compatible = "zmk,input-listener";
|
||||
device = <&mmv>;
|
||||
};
|
||||
};
|
||||
26
app/dts/behaviors/mouse_scroll.dtsi
Normal file
26
app/dts/behaviors/mouse_scroll.dtsi
Normal file
@@ -0,0 +1,26 @@
|
||||
|
||||
/*
|
||||
* Copyright (c) 2024 The ZMK Contributors
|
||||
*
|
||||
* SPDX-License-Identifier: MIT
|
||||
*/
|
||||
|
||||
#include <zephyr/dt-bindings/input/input-event-codes.h>
|
||||
|
||||
/ {
|
||||
behaviors {
|
||||
/omit-if-no-ref/ msc: mouse_scroll {
|
||||
compatible = "zmk,behavior-input-two-axis";
|
||||
#binding-cells = <1>;
|
||||
x-input-code = <INPUT_REL_HWHEEL>;
|
||||
y-input-code = <INPUT_REL_WHEEL>;
|
||||
time-to-max-speed-ms = <300>;
|
||||
acceleration-exponent = <0>;
|
||||
};
|
||||
};
|
||||
|
||||
msc_input_listener: msc_input_listener {
|
||||
compatible = "zmk,input-listener";
|
||||
device = <&msc>;
|
||||
};
|
||||
};
|
||||
28
app/dts/bindings/behaviors/zmk,behavior-input-two-axis.yaml
Normal file
28
app/dts/bindings/behaviors/zmk,behavior-input-two-axis.yaml
Normal file
@@ -0,0 +1,28 @@
|
||||
# Copyright (c) 2024 The ZMK Contributors
|
||||
# SPDX-License-Identifier: MIT
|
||||
|
||||
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
|
||||
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:
|
||||
type: int
|
||||
required: true
|
||||
acceleration-exponent:
|
||||
type: int
|
||||
default: 1
|
||||
6
app/dts/bindings/input_processors/ip_common.yaml
Normal file
6
app/dts/bindings/input_processors/ip_common.yaml
Normal file
@@ -0,0 +1,6 @@
|
||||
# Copyright (c) 2024 The ZMK Contributors
|
||||
# SPDX-License-Identifier: MIT
|
||||
|
||||
properties:
|
||||
track-remainders:
|
||||
type: boolean
|
||||
13
app/dts/bindings/input_processors/ip_one_param.yaml
Normal file
13
app/dts/bindings/input_processors/ip_one_param.yaml
Normal file
@@ -0,0 +1,13 @@
|
||||
# Copyright (c) 2024 The ZMK Contributors
|
||||
# SPDX-License-Identifier: MIT
|
||||
|
||||
include: ip_common.yaml
|
||||
|
||||
properties:
|
||||
"#input-processor-cells":
|
||||
type: int
|
||||
required: true
|
||||
const: 1
|
||||
|
||||
input-processor-cells:
|
||||
- param1
|
||||
14
app/dts/bindings/input_processors/ip_two_param.yaml
Normal file
14
app/dts/bindings/input_processors/ip_two_param.yaml
Normal file
@@ -0,0 +1,14 @@
|
||||
# Copyright (c) 2024 The ZMK Contributors
|
||||
# SPDX-License-Identifier: MIT
|
||||
|
||||
include: ip_common.yaml
|
||||
|
||||
properties:
|
||||
"#input-processor-cells":
|
||||
type: int
|
||||
required: true
|
||||
const: 2
|
||||
|
||||
input-processor-cells:
|
||||
- param1
|
||||
- param2
|
||||
10
app/dts/bindings/input_processors/ip_zero_param.yaml
Normal file
10
app/dts/bindings/input_processors/ip_zero_param.yaml
Normal file
@@ -0,0 +1,10 @@
|
||||
# Copyright (c) 2024 The ZMK Contributors
|
||||
# SPDX-License-Identifier: MIT
|
||||
|
||||
include: ip_common.yaml
|
||||
|
||||
properties:
|
||||
"#input-processor-cells":
|
||||
type: int
|
||||
required: true
|
||||
const: 0
|
||||
@@ -0,0 +1,16 @@
|
||||
# Copyright (c) 2024, The ZMK Contributors
|
||||
# SPDX-License-Identifier: MIT
|
||||
|
||||
description: Input Processor for remapping certain input codes to other codes
|
||||
|
||||
compatible: "zmk,input-processor-code-mapper"
|
||||
|
||||
include: ip_zero_param.yaml
|
||||
|
||||
properties:
|
||||
type:
|
||||
type: int
|
||||
required: true
|
||||
map:
|
||||
type: array
|
||||
required: true
|
||||
@@ -0,0 +1,16 @@
|
||||
# Copyright (c) 2024, The ZMK Contributors
|
||||
# SPDX-License-Identifier: MIT
|
||||
|
||||
description: Input Processor for scaling values
|
||||
|
||||
compatible: "zmk,input-processor-scaler"
|
||||
|
||||
include: ip_two_param.yaml
|
||||
|
||||
properties:
|
||||
type:
|
||||
type: int
|
||||
required: true
|
||||
codes:
|
||||
type: array
|
||||
required: true
|
||||
@@ -0,0 +1,21 @@
|
||||
# Copyright (c) 2024, The ZMK Contributors
|
||||
# SPDX-License-Identifier: MIT
|
||||
|
||||
description: Input Processor for temporarily enabling a layer after input events
|
||||
|
||||
compatible: "zmk,input-processor-temp-layer"
|
||||
|
||||
include: ip_two_param.yaml
|
||||
|
||||
properties:
|
||||
require-prior-idle-ms:
|
||||
type: int
|
||||
required: false
|
||||
default: 0
|
||||
description: Time in milliseconds that must pass after the last keystroke before the layer can be toggled
|
||||
|
||||
excluded-positions:
|
||||
type: array
|
||||
required: false
|
||||
default: []
|
||||
description: Array of key positions that will NOT trigger layer deactivation when pressed
|
||||
@@ -0,0 +1,18 @@
|
||||
# Copyright (c) 2024, The ZMK Contributors
|
||||
# SPDX-License-Identifier: MIT
|
||||
|
||||
description: Input Processor for transforming values in various ways
|
||||
|
||||
compatible: "zmk,input-processor-transform"
|
||||
|
||||
include: ip_one_param.yaml
|
||||
|
||||
properties:
|
||||
type:
|
||||
type: int
|
||||
x-codes:
|
||||
type: array
|
||||
required: true
|
||||
y-codes:
|
||||
type: array
|
||||
required: true
|
||||
26
app/dts/bindings/zmk,input-listener.yaml
Normal file
26
app/dts/bindings/zmk,input-listener.yaml
Normal file
@@ -0,0 +1,26 @@
|
||||
# Copyright (c) 2024 The ZMK Contributors
|
||||
# SPDX-License-Identifier: MIT
|
||||
|
||||
description: |
|
||||
Listener to subscribe to input events and send HID updates after processing
|
||||
|
||||
compatible: "zmk,input-listener"
|
||||
|
||||
properties:
|
||||
device:
|
||||
type: phandle
|
||||
required: true
|
||||
input-processors:
|
||||
type: phandle-array
|
||||
|
||||
child-binding:
|
||||
description: "Listener overrides for certain layers"
|
||||
|
||||
properties:
|
||||
layers:
|
||||
type: array
|
||||
required: true
|
||||
process-next:
|
||||
type: boolean
|
||||
input-processors:
|
||||
type: phandle-array
|
||||
18
app/dts/bindings/zmk,input-split.yaml
Normal file
18
app/dts/bindings/zmk,input-split.yaml
Normal file
@@ -0,0 +1,18 @@
|
||||
# Copyright (c) 2024 The ZMK Contributors
|
||||
# SPDX-License-Identifier: MIT
|
||||
|
||||
include: [base.yaml]
|
||||
|
||||
compatible: "zmk,input-split"
|
||||
|
||||
description: Device to wire up an input device for split use.
|
||||
|
||||
properties:
|
||||
reg:
|
||||
required: true
|
||||
|
||||
device:
|
||||
type: phandle
|
||||
|
||||
input-processors:
|
||||
type: phandle-array
|
||||
10
app/dts/input/processors.dtsi
Normal file
10
app/dts/input/processors.dtsi
Normal file
@@ -0,0 +1,10 @@
|
||||
/*
|
||||
* Copyright (c) 2024 The ZMK Contributors
|
||||
*
|
||||
* SPDX-License-Identifier: MIT
|
||||
*/
|
||||
|
||||
#include <input/processors/scaler.dtsi>
|
||||
#include <input/processors/code_mapper.dtsi>
|
||||
#include <input/processors/transform.dtsi>
|
||||
#include <input/processors/temp_layer.dtsi>
|
||||
29
app/dts/input/processors/code_mapper.dtsi
Normal file
29
app/dts/input/processors/code_mapper.dtsi
Normal file
@@ -0,0 +1,29 @@
|
||||
/*
|
||||
* Copyright (c) 2024 The ZMK Contributors
|
||||
*
|
||||
* SPDX-License-Identifier: MIT
|
||||
*/
|
||||
|
||||
#include <zephyr/dt-bindings/input/input-event-codes.h>
|
||||
|
||||
/ {
|
||||
/omit-if-no-ref/ zip_xy_swap_mapper: zip_xy_swap_mapper {
|
||||
compatible = "zmk,input-processor-code-mapper";
|
||||
#input-processor-cells = <0>;
|
||||
type = <INPUT_EV_REL>;
|
||||
map
|
||||
= <INPUT_REL_Y INPUT_REL_X>
|
||||
, <INPUT_REL_X INPUT_REL_Y>
|
||||
;
|
||||
};
|
||||
|
||||
/omit-if-no-ref/ zip_xy_to_scroll_mapper: zip_xy_to_scroll_mapper {
|
||||
compatible = "zmk,input-processor-code-mapper";
|
||||
#input-processor-cells = <0>;
|
||||
type = <INPUT_EV_REL>;
|
||||
map
|
||||
= <INPUT_REL_Y INPUT_REL_WHEEL>
|
||||
, <INPUT_REL_X INPUT_REL_HWHEEL>
|
||||
;
|
||||
};
|
||||
};
|
||||
33
app/dts/input/processors/scaler.dtsi
Normal file
33
app/dts/input/processors/scaler.dtsi
Normal file
@@ -0,0 +1,33 @@
|
||||
/*
|
||||
* Copyright (c) 2024 The ZMK Contributors
|
||||
*
|
||||
* SPDX-License-Identifier: MIT
|
||||
*/
|
||||
|
||||
#include <zephyr/dt-bindings/input/input-event-codes.h>
|
||||
|
||||
/ {
|
||||
/omit-if-no-ref/ zip_x_scaler: zip_x_scaler {
|
||||
compatible = "zmk,input-processor-scaler";
|
||||
#input-processor-cells = <2>;
|
||||
type = <INPUT_EV_REL>;
|
||||
codes = <INPUT_REL_X>;
|
||||
track-remainders;
|
||||
};
|
||||
|
||||
/omit-if-no-ref/ zip_y_scaler: zip_y_scaler {
|
||||
compatible = "zmk,input-processor-scaler";
|
||||
#input-processor-cells = <2>;
|
||||
type = <INPUT_EV_REL>;
|
||||
codes = <INPUT_REL_Y>;
|
||||
track-remainders;
|
||||
};
|
||||
|
||||
/omit-if-no-ref/ zip_xy_scaler: zip_xy_scaler {
|
||||
compatible = "zmk,input-processor-scaler";
|
||||
#input-processor-cells = <2>;
|
||||
type = <INPUT_EV_REL>;
|
||||
codes = <INPUT_REL_X INPUT_REL_Y>;
|
||||
track-remainders;
|
||||
};
|
||||
};
|
||||
14
app/dts/input/processors/temp_layer.dtsi
Normal file
14
app/dts/input/processors/temp_layer.dtsi
Normal file
@@ -0,0 +1,14 @@
|
||||
/*
|
||||
* Copyright (c) 2024 The ZMK Contributors
|
||||
*
|
||||
* SPDX-License-Identifier: MIT
|
||||
*/
|
||||
|
||||
#include <zephyr/dt-bindings/input/input-event-codes.h>
|
||||
|
||||
/ {
|
||||
/omit-if-no-ref/ zip_temp_layer: zip_temp_layer {
|
||||
compatible = "zmk,input-processor-temp-layer";
|
||||
#input-processor-cells = <2>;
|
||||
};
|
||||
};
|
||||
25
app/dts/input/processors/transform.dtsi
Normal file
25
app/dts/input/processors/transform.dtsi
Normal file
@@ -0,0 +1,25 @@
|
||||
/*
|
||||
* Copyright (c) 2024 The ZMK Contributors
|
||||
*
|
||||
* SPDX-License-Identifier: MIT
|
||||
*/
|
||||
|
||||
#include <zephyr/dt-bindings/input/input-event-codes.h>
|
||||
|
||||
/ {
|
||||
/omit-if-no-ref/ zip_xy_transform: zip_xy_transform {
|
||||
compatible = "zmk,input-processor-transform";
|
||||
#input-processor-cells = <1>;
|
||||
type = <INPUT_EV_REL>;
|
||||
y-codes = <INPUT_REL_Y>;
|
||||
x-codes = <INPUT_REL_X>;
|
||||
};
|
||||
|
||||
/omit-if-no-ref/ zip_scroll_transform: zip_scroll_transform {
|
||||
compatible = "zmk,input-processor-transform";
|
||||
#input-processor-cells = <1>;
|
||||
type = <INPUT_EV_REL>;
|
||||
y-codes = <INPUT_REL_WHEEL>;
|
||||
x-codes = <INPUT_REL_HWHEEL>;
|
||||
};
|
||||
};
|
||||
67
app/include/drivers/input_processor.h
Normal file
67
app/include/drivers/input_processor.h
Normal file
@@ -0,0 +1,67 @@
|
||||
/*
|
||||
* Copyright (c) 2024 The ZMK Contributors
|
||||
*
|
||||
* SPDX-License-Identifier: MIT
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <zephyr/types.h>
|
||||
#include <stddef.h>
|
||||
#include <zephyr/sys/util.h>
|
||||
#include <string.h>
|
||||
#include <zephyr/device.h>
|
||||
#include <zephyr/input/input.h>
|
||||
|
||||
struct zmk_input_processor_entry {
|
||||
const struct device *dev;
|
||||
uint32_t param1;
|
||||
uint32_t param2;
|
||||
bool track_remainders;
|
||||
};
|
||||
|
||||
#define ZMK_INPUT_PROCESSOR_ENTRY_AT_IDX(idx, n) \
|
||||
{ \
|
||||
.dev = DEVICE_DT_GET(DT_PHANDLE_BY_IDX(n, input_processors, idx)), \
|
||||
.param1 = COND_CODE_0(DT_PHA_HAS_CELL_AT_IDX(n, input_processors, idx, param1), (0), \
|
||||
(DT_PHA_BY_IDX(n, input_processors, idx, param1))), \
|
||||
.param2 = COND_CODE_0(DT_PHA_HAS_CELL_AT_IDX(n, input_processors, idx, param2), (0), \
|
||||
(DT_PHA_BY_IDX(n, input_processors, idx, param2))), \
|
||||
.track_remainders = \
|
||||
COND_CODE_1(DT_PROP(DT_PHANDLE_BY_IDX(n, input_processors, idx), track_remainders), \
|
||||
(true), (false)), \
|
||||
}
|
||||
|
||||
struct zmk_input_processor_state {
|
||||
int16_t *remainder;
|
||||
};
|
||||
|
||||
// TODO: Need the ability to store remainders? Some data passed in?
|
||||
typedef int (*zmk_input_processor_handle_event_callback_t)(const struct device *dev,
|
||||
struct input_event *event,
|
||||
uint32_t param1, uint32_t param2,
|
||||
struct zmk_input_processor_state *state);
|
||||
|
||||
__subsystem struct zmk_input_processor_driver_api {
|
||||
zmk_input_processor_handle_event_callback_t handle_event;
|
||||
};
|
||||
|
||||
__syscall int zmk_input_processor_handle_event(const struct device *dev, struct input_event *event,
|
||||
uint32_t param1, uint32_t param2,
|
||||
struct zmk_input_processor_state *state);
|
||||
|
||||
static inline int z_impl_zmk_input_processor_handle_event(const struct device *dev,
|
||||
struct input_event *event,
|
||||
uint32_t param1, uint32_t param2,
|
||||
struct zmk_input_processor_state *state) {
|
||||
const struct zmk_input_processor_driver_api *api =
|
||||
(const struct zmk_input_processor_driver_api *)dev->api;
|
||||
|
||||
if (api->handle_event == NULL) {
|
||||
return -ENOTSUP;
|
||||
}
|
||||
|
||||
return api->handle_event(dev, event, param1, param2, state);
|
||||
}
|
||||
|
||||
#include <syscalls/input_processor.h>
|
||||
12
app/include/dt-bindings/input.h
Normal file
12
app/include/dt-bindings/input.h
Normal file
@@ -0,0 +1,12 @@
|
||||
/*
|
||||
* Copyright (c) 2024 The ZMK Contributors
|
||||
*
|
||||
* SPDX-License-Identifier: MIT
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#define ZMK_INPUT_EXPLICIT_CODE(type, code) ((type << 16) & code)
|
||||
|
||||
#define ZMK_INPUT_EXPLICIT_CODE_TYPE(val) ((val >> 16) & 0xFF)
|
||||
#define ZMK_INPUT_EXPLICIT_CODE_CODE(val) (val & 0xFFFF)
|
||||
11
app/include/dt-bindings/zmk/input_transform.h
Normal file
11
app/include/dt-bindings/zmk/input_transform.h
Normal file
@@ -0,0 +1,11 @@
|
||||
/*
|
||||
* Copyright (c) 2024 The ZMK Contributors
|
||||
*
|
||||
* SPDX-License-Identifier: MIT
|
||||
*/
|
||||
|
||||
#include <zephyr/dt-bindings/dt-util.h>
|
||||
|
||||
#define INPUT_TRANSFORM_XY_SWAP BIT(0)
|
||||
#define INPUT_TRANSFORM_X_INVERT BIT(1)
|
||||
#define INPUT_TRANSFORM_Y_INVERT BIT(2)
|
||||
@@ -1,24 +1,9 @@
|
||||
|
||||
/*
|
||||
* Copyright (c) 2020 The ZMK Contributors
|
||||
* Copyright (c) 2024 The ZMK Contributors
|
||||
*
|
||||
* SPDX-License-Identifier: MIT
|
||||
*/
|
||||
#pragma once
|
||||
|
||||
#include <zephyr/dt-bindings/dt-util.h>
|
||||
|
||||
/* Mouse press behavior */
|
||||
/* Left click */
|
||||
#define MB1 BIT(0)
|
||||
#define LCLK (MB1)
|
||||
|
||||
/* Right click */
|
||||
#define MB2 BIT(1)
|
||||
#define RCLK (MB2)
|
||||
|
||||
/* Middle click */
|
||||
#define MB3 BIT(2)
|
||||
#define MCLK (MB3)
|
||||
|
||||
#define MB4 BIT(3)
|
||||
#define MB5 BIT(4)
|
||||
#include "pointing.h"
|
||||
50
app/include/dt-bindings/zmk/pointing.h
Normal file
50
app/include/dt-bindings/zmk/pointing.h
Normal file
@@ -0,0 +1,50 @@
|
||||
/*
|
||||
* Copyright (c) 2023 The ZMK Contributors
|
||||
*
|
||||
* SPDX-License-Identifier: MIT
|
||||
*/
|
||||
#pragma once
|
||||
|
||||
#include <zephyr/dt-bindings/dt-util.h>
|
||||
|
||||
/* Mouse press behavior */
|
||||
/* Left click */
|
||||
#define MB1 BIT(0)
|
||||
#define LCLK (MB1)
|
||||
|
||||
/* Right click */
|
||||
#define MB2 BIT(1)
|
||||
#define RCLK (MB2)
|
||||
|
||||
/* Middle click */
|
||||
#define MB3 BIT(2)
|
||||
#define MCLK (MB3)
|
||||
|
||||
#define MB4 BIT(3)
|
||||
#define MB5 BIT(4)
|
||||
|
||||
#ifndef ZMK_POINTING_DEFAULT_MOVE_VAL
|
||||
#define ZMK_POINTING_DEFAULT_MOVE_VAL 600
|
||||
#endif
|
||||
|
||||
#ifndef ZMK_POINTING_DEFAULT_SCRL_VAL
|
||||
#define ZMK_POINTING_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_POINTING_DEFAULT_MOVE_VAL)
|
||||
#define MOVE_DOWN MOVE_Y(ZMK_POINTING_DEFAULT_MOVE_VAL)
|
||||
#define MOVE_LEFT MOVE_X(-ZMK_POINTING_DEFAULT_MOVE_VAL)
|
||||
#define MOVE_RIGHT MOVE_X(ZMK_POINTING_DEFAULT_MOVE_VAL)
|
||||
|
||||
#define SCRL_UP MOVE_Y(ZMK_POINTING_DEFAULT_SCRL_VAL)
|
||||
#define SCRL_DOWN MOVE_Y(-ZMK_POINTING_DEFAULT_SCRL_VAL)
|
||||
#define SCRL_LEFT MOVE_X(-ZMK_POINTING_DEFAULT_SCRL_VAL)
|
||||
#define SCRL_RIGHT MOVE_X(ZMK_POINTING_DEFAULT_SCRL_VAL)
|
||||
@@ -70,8 +70,8 @@ struct zmk_endpoint_instance zmk_endpoints_selected(void);
|
||||
|
||||
int zmk_endpoints_send_report(uint16_t usage_page);
|
||||
|
||||
#if IS_ENABLED(CONFIG_ZMK_MOUSE)
|
||||
#if IS_ENABLED(CONFIG_ZMK_POINTING)
|
||||
int zmk_endpoints_send_mouse_report();
|
||||
#endif // IS_ENABLE(CONFIG_ZMK_MOUSE)
|
||||
#endif // IS_ENABLED(CONFIG_ZMK_POINTING)
|
||||
|
||||
void zmk_endpoints_clear_current(void);
|
||||
|
||||
@@ -9,7 +9,7 @@
|
||||
|
||||
#include <zmk/hid.h>
|
||||
#include <zmk/event_manager.h>
|
||||
#include <zmk/mouse.h>
|
||||
#include <zmk/pointing.h>
|
||||
|
||||
struct zmk_mouse_button_state_changed {
|
||||
zmk_mouse_button_t buttons;
|
||||
|
||||
@@ -12,9 +12,9 @@
|
||||
#include <zephyr/usb/class/usb_hid.h>
|
||||
|
||||
#include <zmk/keys.h>
|
||||
#if IS_ENABLED(CONFIG_ZMK_MOUSE)
|
||||
#include <zmk/mouse.h>
|
||||
#endif // IS_ENABLED(CONFIG_ZMK_MOUSE)
|
||||
#if IS_ENABLED(CONFIG_ZMK_POINTING)
|
||||
#include <zmk/pointing.h>
|
||||
#endif // IS_ENABLED(CONFIG_ZMK_POINTING)
|
||||
|
||||
#include <dt-bindings/zmk/hid_usage.h>
|
||||
#include <dt-bindings/zmk/hid_usage_pages.h>
|
||||
@@ -77,6 +77,30 @@
|
||||
#define ZMK_HID_REPORT_ID_CONSUMER 0x02
|
||||
#define ZMK_HID_REPORT_ID_MOUSE 0x03
|
||||
|
||||
#ifndef HID_ITEM_TAG_PUSH
|
||||
#define HID_ITEM_TAG_PUSH 0xA
|
||||
#endif
|
||||
|
||||
#ifndef HID_ITEM_TAG_POP
|
||||
#define HID_ITEM_TAG_POP 0xB
|
||||
#endif
|
||||
|
||||
#define HID_PUSH HID_ITEM(HID_ITEM_TAG_PUSH, HID_ITEM_TYPE_GLOBAL, 0)
|
||||
|
||||
#define HID_POP HID_ITEM(HID_ITEM_TAG_POP, HID_ITEM_TYPE_GLOBAL, 0)
|
||||
|
||||
#ifndef HID_PHYSICAL_MIN8
|
||||
#define HID_PHYSICAL_MIN8(a) HID_ITEM(HID_ITEM_TAG_PHYSICAL_MIN, HID_ITEM_TYPE_GLOBAL, 1), a
|
||||
#endif
|
||||
|
||||
#ifndef HID_PHYSICAL_MAX8
|
||||
#define HID_PHYSICAL_MAX8(a) HID_ITEM(HID_ITEM_TAG_PHYSICAL_MAX, HID_ITEM_TYPE_GLOBAL, 1), a
|
||||
#endif
|
||||
|
||||
#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),
|
||||
@@ -161,7 +185,7 @@ static const uint8_t zmk_hid_report_desc[] = {
|
||||
HID_INPUT(ZMK_HID_MAIN_VAL_DATA | ZMK_HID_MAIN_VAL_ARRAY | ZMK_HID_MAIN_VAL_ABS),
|
||||
HID_END_COLLECTION,
|
||||
|
||||
#if IS_ENABLED(CONFIG_ZMK_MOUSE)
|
||||
#if IS_ENABLED(CONFIG_ZMK_POINTING)
|
||||
HID_USAGE_PAGE(HID_USAGE_GD),
|
||||
HID_USAGE(HID_USAGE_GD_MOUSE),
|
||||
HID_COLLECTION(HID_COLLECTION_APPLICATION),
|
||||
@@ -184,15 +208,51 @@ static const uint8_t zmk_hid_report_desc[] = {
|
||||
HID_USAGE_PAGE(HID_USAGE_GEN_DESKTOP),
|
||||
HID_USAGE(HID_USAGE_GD_X),
|
||||
HID_USAGE(HID_USAGE_GD_Y),
|
||||
HID_LOGICAL_MIN16(0xFF, -0x7F),
|
||||
HID_LOGICAL_MAX16(0xFF, 0x7F),
|
||||
HID_REPORT_SIZE(0x10),
|
||||
HID_REPORT_COUNT(0x02),
|
||||
HID_INPUT(ZMK_HID_MAIN_VAL_DATA | ZMK_HID_MAIN_VAL_VAR | ZMK_HID_MAIN_VAL_REL),
|
||||
HID_COLLECTION(HID_COLLECTION_LOGICAL),
|
||||
#if IS_ENABLED(CONFIG_ZMK_POINTING_SMOOTH_SCROLLING)
|
||||
HID_USAGE(HID_USAGE_GD_RESOLUTION_MULTIPLIER),
|
||||
HID_LOGICAL_MIN8(0x00),
|
||||
HID_LOGICAL_MAX8(0x0F),
|
||||
HID_PHYSICAL_MIN8(0x01),
|
||||
HID_PHYSICAL_MAX8(0x10),
|
||||
HID_REPORT_SIZE(0x04),
|
||||
HID_REPORT_COUNT(0x01),
|
||||
HID_PUSH,
|
||||
HID_FEATURE(ZMK_HID_MAIN_VAL_DATA | ZMK_HID_MAIN_VAL_VAR | ZMK_HID_MAIN_VAL_ABS),
|
||||
#endif // IS_ENABLED(CONFIG_ZMK_POINTING_SMOOTH_SCROLLING)
|
||||
HID_USAGE(HID_USAGE_GD_WHEEL),
|
||||
HID_LOGICAL_MIN8(-0x7F),
|
||||
HID_LOGICAL_MAX8(0x7F),
|
||||
HID_REPORT_SIZE(0x08),
|
||||
HID_REPORT_COUNT(0x03),
|
||||
HID_LOGICAL_MIN16(0xFF, -0x7F),
|
||||
HID_LOGICAL_MAX16(0xFF, 0x7F),
|
||||
HID_PHYSICAL_MIN8(0x00),
|
||||
HID_PHYSICAL_MAX8(0x00),
|
||||
HID_REPORT_SIZE(0x10),
|
||||
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_COLLECTION(HID_COLLECTION_LOGICAL),
|
||||
#if IS_ENABLED(CONFIG_ZMK_POINTING_SMOOTH_SCROLLING)
|
||||
HID_USAGE(HID_USAGE_GD_RESOLUTION_MULTIPLIER),
|
||||
HID_POP,
|
||||
HID_FEATURE(ZMK_HID_MAIN_VAL_DATA | ZMK_HID_MAIN_VAL_VAR | ZMK_HID_MAIN_VAL_ABS),
|
||||
#endif // IS_ENABLED(CONFIG_ZMK_POINTING_SMOOTH_SCROLLING)
|
||||
HID_USAGE_PAGE(HID_USAGE_CONSUMER),
|
||||
HID_USAGE16_SINGLE(HID_USAGE_CONSUMER_AC_PAN),
|
||||
HID_LOGICAL_MIN16(0xFF, -0x7F),
|
||||
HID_LOGICAL_MAX16(0xFF, 0x7F),
|
||||
HID_PHYSICAL_MIN8(0x00),
|
||||
HID_PHYSICAL_MAX8(0x00),
|
||||
HID_REPORT_SIZE(0x10),
|
||||
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)
|
||||
HID_END_COLLECTION,
|
||||
#endif // IS_ENABLED(CONFIG_ZMK_POINTING)
|
||||
};
|
||||
|
||||
#if IS_ENABLED(CONFIG_ZMK_USB_BOOT)
|
||||
@@ -255,12 +315,13 @@ struct zmk_hid_consumer_report {
|
||||
struct zmk_hid_consumer_report_body body;
|
||||
} __packed;
|
||||
|
||||
#if IS_ENABLED(CONFIG_ZMK_MOUSE)
|
||||
#if IS_ENABLED(CONFIG_ZMK_POINTING)
|
||||
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 {
|
||||
@@ -268,7 +329,21 @@ struct zmk_hid_mouse_report {
|
||||
struct zmk_hid_mouse_report_body body;
|
||||
} __packed;
|
||||
|
||||
#endif // IS_ENABLED(CONFIG_ZMK_MOUSE)
|
||||
#if IS_ENABLED(CONFIG_ZMK_POINTING_SMOOTH_SCROLLING)
|
||||
|
||||
struct zmk_hid_mouse_resolution_feature_report_body {
|
||||
uint8_t wheel_res : 4;
|
||||
uint8_t hwheel_res : 4;
|
||||
} __packed;
|
||||
|
||||
struct zmk_hid_mouse_resolution_feature_report {
|
||||
uint8_t report_id;
|
||||
struct zmk_hid_mouse_resolution_feature_report_body body;
|
||||
} __packed;
|
||||
|
||||
#endif // IS_ENABLED(CONFIG_ZMK_POINTING_SMOOTH_SCROLLING)
|
||||
|
||||
#endif // IS_ENABLED(CONFIG_ZMK_POINTING)
|
||||
|
||||
zmk_mod_flags_t zmk_hid_get_explicit_mods(void);
|
||||
int zmk_hid_register_mod(zmk_mod_t modifier);
|
||||
@@ -296,13 +371,18 @@ int zmk_hid_press(uint32_t usage);
|
||||
int zmk_hid_release(uint32_t usage);
|
||||
bool zmk_hid_is_pressed(uint32_t usage);
|
||||
|
||||
#if IS_ENABLED(CONFIG_ZMK_MOUSE)
|
||||
#if IS_ENABLED(CONFIG_ZMK_POINTING)
|
||||
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)
|
||||
|
||||
#endif // IS_ENABLED(CONFIG_ZMK_POINTING)
|
||||
|
||||
struct zmk_hid_keyboard_report *zmk_hid_get_keyboard_report(void);
|
||||
struct zmk_hid_consumer_report *zmk_hid_get_consumer_report(void);
|
||||
@@ -311,6 +391,6 @@ struct zmk_hid_consumer_report *zmk_hid_get_consumer_report(void);
|
||||
zmk_hid_boot_report_t *zmk_hid_get_boot_report();
|
||||
#endif
|
||||
|
||||
#if IS_ENABLED(CONFIG_ZMK_MOUSE)
|
||||
#if IS_ENABLED(CONFIG_ZMK_POINTING)
|
||||
struct zmk_hid_mouse_report *zmk_hid_get_mouse_report();
|
||||
#endif // IS_ENABLED(CONFIG_ZMK_MOUSE)
|
||||
#endif // IS_ENABLED(CONFIG_ZMK_POINTING)
|
||||
|
||||
@@ -12,6 +12,6 @@
|
||||
int zmk_hog_send_keyboard_report(struct zmk_hid_keyboard_report_body *body);
|
||||
int zmk_hog_send_consumer_report(struct zmk_hid_consumer_report_body *body);
|
||||
|
||||
#if IS_ENABLED(CONFIG_ZMK_MOUSE)
|
||||
#if IS_ENABLED(CONFIG_ZMK_POINTING)
|
||||
int zmk_hog_send_mouse_report(struct zmk_hid_mouse_report_body *body);
|
||||
#endif // IS_ENABLED(CONFIG_ZMK_MOUSE)
|
||||
#endif // IS_ENABLED(CONFIG_ZMK_POINTING)
|
||||
|
||||
14
app/include/zmk/input.h
Normal file
14
app/include/zmk/input.h
Normal file
@@ -0,0 +1,14 @@
|
||||
/*
|
||||
* Copyright (c) 2024 The ZMK Contributors
|
||||
*
|
||||
* SPDX-License-Identifier: MIT
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <dt-bindings/zmk/input.h>
|
||||
|
||||
struct zmk_input_explicit_code {
|
||||
uint8_t type;
|
||||
uint16_t code;
|
||||
};
|
||||
@@ -1,12 +1,12 @@
|
||||
/*
|
||||
* Copyright (c) 2021 The ZMK Contributors
|
||||
* Copyright (c) 2023 The ZMK Contributors
|
||||
*
|
||||
* SPDX-License-Identifier: MIT
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <dt-bindings/zmk/mouse.h>
|
||||
#include <dt-bindings/zmk/pointing.h>
|
||||
|
||||
typedef uint8_t zmk_mouse_button_flags_t;
|
||||
typedef uint16_t zmk_mouse_button_t;
|
||||
10
app/include/zmk/pointing/input_split.h
Normal file
10
app/include/zmk/pointing/input_split.h
Normal file
@@ -0,0 +1,10 @@
|
||||
/*
|
||||
* Copyright (c) 2024 The ZMK Contributors
|
||||
*
|
||||
* SPDX-License-Identifier: MIT
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
int zmk_input_split_report_peripheral_event(uint8_t reg, uint8_t type, uint16_t code, int32_t value,
|
||||
bool sync);
|
||||
26
app/include/zmk/pointing/resolution_multipliers.h
Normal file
26
app/include/zmk/pointing/resolution_multipliers.h
Normal file
@@ -0,0 +1,26 @@
|
||||
/*
|
||||
* Copyright (c) 2024 The ZMK Contributors
|
||||
*
|
||||
* SPDX-License-Identifier: MIT
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <zmk/hid.h>
|
||||
#include <zmk/endpoints.h>
|
||||
|
||||
struct zmk_pointing_resolution_multipliers {
|
||||
uint8_t wheel;
|
||||
uint8_t hor_wheel;
|
||||
};
|
||||
|
||||
struct zmk_pointing_resolution_multipliers
|
||||
zmk_pointing_resolution_multipliers_get_current_profile(void);
|
||||
struct zmk_pointing_resolution_multipliers
|
||||
zmk_pointing_resolution_multipliers_get_profile(struct zmk_endpoint_instance endpoint);
|
||||
void zmk_pointing_resolution_multipliers_set_profile(
|
||||
struct zmk_pointing_resolution_multipliers multipliers, struct zmk_endpoint_instance endpoint);
|
||||
|
||||
void zmk_pointing_resolution_multipliers_process_report(
|
||||
struct zmk_hid_mouse_resolution_feature_report_body *report,
|
||||
struct zmk_endpoint_instance endpoint);
|
||||
@@ -31,8 +31,17 @@ struct zmk_split_run_behavior_payload {
|
||||
char behavior_dev[ZMK_SPLIT_RUN_BEHAVIOR_DEV_LEN];
|
||||
} __packed;
|
||||
|
||||
struct zmk_split_input_event_payload {
|
||||
uint8_t type;
|
||||
uint16_t code;
|
||||
uint32_t value;
|
||||
uint8_t sync;
|
||||
} __packed;
|
||||
|
||||
int zmk_split_bt_position_pressed(uint8_t position);
|
||||
int zmk_split_bt_position_released(uint8_t position);
|
||||
int zmk_split_bt_sensor_triggered(uint8_t sensor_index,
|
||||
const struct zmk_sensor_channel_data channel_data[],
|
||||
size_t channel_data_size);
|
||||
|
||||
int zmk_split_bt_report_input(uint8_t reg, uint8_t type, uint16_t code, int32_t value, bool sync);
|
||||
|
||||
@@ -19,3 +19,4 @@
|
||||
#define ZMK_SPLIT_BT_CHAR_SENSOR_STATE_UUID ZMK_BT_SPLIT_UUID(0x00000003)
|
||||
#define ZMK_SPLIT_BT_UPDATE_HID_INDICATORS_UUID ZMK_BT_SPLIT_UUID(0x00000004)
|
||||
#define ZMK_SPLIT_BT_SELECT_PHYS_LAYOUT_UUID ZMK_BT_SPLIT_UUID(0x00000005)
|
||||
#define ZMK_SPLIT_BT_INPUT_EVENT_UUID ZMK_BT_SPLIT_UUID(0x00000006)
|
||||
|
||||
@@ -10,7 +10,7 @@
|
||||
|
||||
int zmk_usb_hid_send_keyboard_report(void);
|
||||
int zmk_usb_hid_send_consumer_report(void);
|
||||
#if IS_ENABLED(CONFIG_ZMK_MOUSE)
|
||||
#if IS_ENABLED(CONFIG_ZMK_POINTING)
|
||||
int zmk_usb_hid_send_mouse_report(void);
|
||||
#endif // IS_ENABLED(CONFIG_ZMK_MOUSE)
|
||||
#endif // IS_ENABLED(CONFIG_ZMK_POINTING)
|
||||
void zmk_usb_hid_set_protocol(uint8_t protocol);
|
||||
|
||||
@@ -26,6 +26,10 @@ LOG_MODULE_DECLARE(zmk, CONFIG_ZMK_LOG_LEVEL);
|
||||
#include <zmk/usb.h>
|
||||
#endif
|
||||
|
||||
#if IS_ENABLED(CONFIG_ZMK_POINTING)
|
||||
#include <zephyr/input/input.h>
|
||||
#endif
|
||||
|
||||
bool is_usb_power_present(void) {
|
||||
#if IS_ENABLED(CONFIG_USB_DEVICE_STACK)
|
||||
return zmk_usb_is_powered();
|
||||
@@ -59,12 +63,14 @@ int set_state(enum zmk_activity_state state) {
|
||||
|
||||
enum zmk_activity_state zmk_activity_get_state(void) { return activity_state; }
|
||||
|
||||
int activity_event_listener(const zmk_event_t *eh) {
|
||||
static int note_activity(void) {
|
||||
activity_last_uptime = k_uptime_get();
|
||||
|
||||
return set_state(ZMK_ACTIVITY_ACTIVE);
|
||||
}
|
||||
|
||||
static int activity_event_listener(const zmk_event_t *eh) { return note_activity(); }
|
||||
|
||||
void activity_work_handler(struct k_work *work) {
|
||||
int32_t current = k_uptime_get();
|
||||
int32_t inactive_time = current - activity_last_uptime;
|
||||
@@ -104,4 +110,16 @@ ZMK_LISTENER(activity, activity_event_listener);
|
||||
ZMK_SUBSCRIPTION(activity, zmk_position_state_changed);
|
||||
ZMK_SUBSCRIPTION(activity, zmk_sensor_event);
|
||||
|
||||
#if IS_ENABLED(CONFIG_ZMK_POINTING)
|
||||
|
||||
static void note_activity_work_cb(struct k_work *_work) { note_activity(); }
|
||||
|
||||
K_WORK_DEFINE(note_activity_work, note_activity_work_cb);
|
||||
|
||||
static void activity_input_listener(struct input_event *ev) { k_work_submit(¬e_activity_work); }
|
||||
|
||||
INPUT_CALLBACK_DEFINE(NULL, activity_input_listener);
|
||||
|
||||
#endif
|
||||
|
||||
SYS_INIT(activity_init, APPLICATION, CONFIG_APPLICATION_INIT_PRIORITY);
|
||||
|
||||
304
app/src/behaviors/behavior_input_two_axis.c
Normal file
304
app/src/behaviors/behavior_input_two_axis.c
Normal file
@@ -0,0 +1,304 @@
|
||||
/*
|
||||
* Copyright (c) 2024 The ZMK Contributors
|
||||
*
|
||||
* SPDX-License-Identifier: MIT
|
||||
*/
|
||||
|
||||
#define DT_DRV_COMPAT zmk_behavior_input_two_axis
|
||||
|
||||
#include <zephyr/device.h>
|
||||
#include <drivers/behavior.h>
|
||||
#include <zephyr/input/input.h>
|
||||
#include <zephyr/logging/log.h>
|
||||
#include <zephyr/sys/util.h> // CLAMP
|
||||
|
||||
#include <zmk/behavior.h>
|
||||
#include <dt-bindings/zmk/pointing.h>
|
||||
|
||||
#if IS_ENABLED(CONFIG_ZMK_POINTING_SMOOTH_SCROLLING)
|
||||
#include <zmk/pointing/resolution_multipliers.h>
|
||||
#endif // IS_ENABLED(CONFIG_ZMK_POINTING_SMOOTH_SCROLLING)
|
||||
|
||||
LOG_MODULE_DECLARE(zmk, CONFIG_ZMK_LOG_LEVEL);
|
||||
|
||||
struct vector2d {
|
||||
float x;
|
||||
float y;
|
||||
};
|
||||
|
||||
struct movement_state_1d {
|
||||
float remainder;
|
||||
int16_t speed;
|
||||
int64_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;
|
||||
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
|
||||
uint8_t 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 <math.h>
|
||||
#endif
|
||||
|
||||
static int64_t ticks_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;
|
||||
}
|
||||
|
||||
#if IS_ENABLED(CONFIG_ZMK_POINTING_SMOOTH_SCROLLING)
|
||||
|
||||
static uint8_t get_acceleration_exponent(const struct behavior_input_two_axis_config *config,
|
||||
uint16_t code) {
|
||||
switch (code) {
|
||||
case INPUT_REL_WHEEL:
|
||||
return (zmk_pointing_resolution_multipliers_get_current_profile().wheel > 0)
|
||||
? 0
|
||||
: config->acceleration_exponent;
|
||||
case INPUT_REL_HWHEEL:
|
||||
return (zmk_pointing_resolution_multipliers_get_current_profile().hor_wheel > 0)
|
||||
? 0
|
||||
: config->acceleration_exponent;
|
||||
default:
|
||||
return config->acceleration_exponent;
|
||||
}
|
||||
}
|
||||
|
||||
#else
|
||||
|
||||
static inline uint8_t get_acceleration_exponent(const struct behavior_input_two_axis_config *config,
|
||||
uint16_t code) {
|
||||
return config->acceleration_exponent;
|
||||
}
|
||||
|
||||
#endif // IS_ENABLED(CONFIG_ZMK_POINTING_SMOOTH_SCROLLING)
|
||||
|
||||
static float speed(const struct behavior_input_two_axis_config *config, uint16_t code,
|
||||
float max_speed, int64_t duration_ticks) {
|
||||
uint8_t accel_exp = get_acceleration_exponent(config, code);
|
||||
|
||||
if ((1000 * duration_ticks / CONFIG_SYS_CLOCK_TICKS_PER_SEC) > config->time_to_max_speed_ms ||
|
||||
config->time_to_max_speed_ms == 0 || accel_exp == 0) {
|
||||
return max_speed;
|
||||
}
|
||||
|
||||
// Calculate the speed based on MouseKeysAccel
|
||||
// See https://en.wikipedia.org/wiki/Mouse_keys
|
||||
if (duration_ticks == 0) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
float time_fraction = (float)(1000 * duration_ticks / CONFIG_SYS_CLOCK_TICKS_PER_SEC) /
|
||||
config->time_to_max_speed_ms;
|
||||
return max_speed * powf(time_fraction, accel_exp);
|
||||
}
|
||||
|
||||
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, uint16_t code,
|
||||
struct movement_state_1d *state, int64_t now) {
|
||||
float move = 0;
|
||||
if (state->speed == 0) {
|
||||
state->remainder = 0;
|
||||
return move;
|
||||
}
|
||||
|
||||
int64_t move_duration = ticks_since_start(state->start_time, now, config->delay_ms);
|
||||
LOG_DBG("Calculated speed: %f", speed(config, code, state->speed, move_duration));
|
||||
move =
|
||||
(move_duration > 0)
|
||||
? (speed(config, code, state->speed, move_duration) * config->trigger_period_ms / 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, config->x_code, &state->x, now),
|
||||
.y = update_movement_1d(config, config->y_code, &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;
|
||||
|
||||
uint64_t timestamp = k_uptime_ticks();
|
||||
|
||||
// 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);
|
||||
|
||||
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(cfg->trigger_period_ms));
|
||||
}
|
||||
}
|
||||
|
||||
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_ticks();
|
||||
} 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;
|
||||
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(cfg->trigger_period_ms));
|
||||
} else {
|
||||
k_work_cancel_delayable(&data->tick_work);
|
||||
data->state.y.remainder = 0;
|
||||
data->state.x.remainder = 0;
|
||||
}
|
||||
}
|
||||
|
||||
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);
|
||||
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 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);
|
||||
|
||||
behavior_input_two_axis_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);
|
||||
|
||||
behavior_input_two_axis_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), \
|
||||
.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), \
|
||||
}; \
|
||||
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)
|
||||
@@ -11,8 +11,9 @@
|
||||
#include <zephyr/logging/log.h>
|
||||
|
||||
#include <zmk/behavior.h>
|
||||
#include <zmk/event_manager.h>
|
||||
#include <zmk/events/mouse_button_state_changed.h>
|
||||
#include <zmk/hid.h>
|
||||
#include <zephyr/input/input.h>
|
||||
#include <zephyr/dt-bindings/input/input-event-codes.h>
|
||||
|
||||
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 = {
|
||||
|
||||
@@ -201,7 +201,7 @@ int zmk_endpoints_send_report(uint16_t usage_page) {
|
||||
return -ENOTSUP;
|
||||
}
|
||||
|
||||
#if IS_ENABLED(CONFIG_ZMK_MOUSE)
|
||||
#if IS_ENABLED(CONFIG_ZMK_POINTING)
|
||||
int zmk_endpoints_send_mouse_report() {
|
||||
switch (current_instance.transport) {
|
||||
case ZMK_TRANSPORT_USB: {
|
||||
@@ -235,7 +235,7 @@ int zmk_endpoints_send_mouse_report() {
|
||||
LOG_ERR("Unhandled endpoint transport %d", current_instance.transport);
|
||||
return -ENOTSUP;
|
||||
}
|
||||
#endif // IS_ENABLED(CONFIG_ZMK_MOUSE)
|
||||
#endif // IS_ENABLED(CONFIG_ZMK_POINTING)
|
||||
|
||||
#if IS_ENABLED(CONFIG_SETTINGS)
|
||||
|
||||
@@ -332,9 +332,9 @@ static int zmk_endpoints_init(void) {
|
||||
void zmk_endpoints_clear_current(void) {
|
||||
zmk_hid_keyboard_clear();
|
||||
zmk_hid_consumer_clear();
|
||||
#if IS_ENABLED(CONFIG_ZMK_MOUSE)
|
||||
#if IS_ENABLED(CONFIG_ZMK_POINTING)
|
||||
zmk_hid_mouse_clear();
|
||||
#endif // IS_ENABLED(CONFIG_ZMK_MOUSE)
|
||||
#endif // IS_ENABLED(CONFIG_ZMK_POINTING)
|
||||
|
||||
zmk_endpoints_send_report(HID_USAGE_KEY);
|
||||
zmk_endpoints_send_report(HID_USAGE_CONSUMER);
|
||||
|
||||
@@ -25,12 +25,13 @@ static uint8_t keys_held = 0;
|
||||
|
||||
#endif /* IS_ENABLED(CONFIG_ZMK_USB_BOOT) */
|
||||
|
||||
#if IS_ENABLED(CONFIG_ZMK_MOUSE)
|
||||
#if IS_ENABLED(CONFIG_ZMK_POINTING)
|
||||
|
||||
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)
|
||||
#endif // IS_ENABLED(CONFIG_ZMK_POINTING)
|
||||
|
||||
// Keep track of how often a modifier was pressed.
|
||||
// Only release the modifier if the count is 0.
|
||||
@@ -370,7 +371,7 @@ bool zmk_hid_is_pressed(uint32_t usage) {
|
||||
return false;
|
||||
}
|
||||
|
||||
#if IS_ENABLED(CONFIG_ZMK_MOUSE)
|
||||
#if IS_ENABLED(CONFIG_ZMK_POINTING)
|
||||
|
||||
// Keep track of how often a button was pressed.
|
||||
// Only release the button if the count is 0.
|
||||
@@ -431,16 +432,48 @@ int zmk_hid_mouse_buttons_release(zmk_mouse_button_flags_t buttons) {
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
void zmk_hid_mouse_clear(void) { memset(&mouse_report.body, 0, sizeof(mouse_report.body)); }
|
||||
|
||||
#endif // IS_ENABLED(CONFIG_ZMK_MOUSE)
|
||||
void zmk_hid_mouse_movement_set(int16_t hwheel, int16_t wheel) {
|
||||
mouse_report.body.d_x = hwheel;
|
||||
mouse_report.body.d_y = wheel;
|
||||
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 hwheel, int16_t wheel) {
|
||||
mouse_report.body.d_x += hwheel;
|
||||
mouse_report.body.d_y += wheel;
|
||||
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 hwheel, int8_t wheel) {
|
||||
mouse_report.body.d_scroll_x = hwheel;
|
||||
mouse_report.body.d_scroll_y = wheel;
|
||||
|
||||
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 hwheel, int8_t wheel) {
|
||||
mouse_report.body.d_scroll_x += hwheel;
|
||||
mouse_report.body.d_scroll_y += wheel;
|
||||
|
||||
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) {
|
||||
LOG_DBG("Mouse report cleared");
|
||||
memset(&mouse_report.body, 0, sizeof(mouse_report.body));
|
||||
}
|
||||
|
||||
#endif // IS_ENABLED(CONFIG_ZMK_POINTING)
|
||||
|
||||
struct zmk_hid_keyboard_report *zmk_hid_get_keyboard_report(void) { return &keyboard_report; }
|
||||
|
||||
struct zmk_hid_consumer_report *zmk_hid_get_consumer_report(void) { return &consumer_report; }
|
||||
|
||||
#if IS_ENABLED(CONFIG_ZMK_MOUSE)
|
||||
#if IS_ENABLED(CONFIG_ZMK_POINTING)
|
||||
|
||||
struct zmk_hid_mouse_report *zmk_hid_get_mouse_report(void) { return &mouse_report; }
|
||||
|
||||
#endif // IS_ENABLED(CONFIG_ZMK_MOUSE)
|
||||
#endif // IS_ENABLED(CONFIG_ZMK_POINTING)
|
||||
|
||||
@@ -18,6 +18,9 @@ LOG_MODULE_DECLARE(zmk, CONFIG_ZMK_LOG_LEVEL);
|
||||
#include <zmk/endpoints_types.h>
|
||||
#include <zmk/hog.h>
|
||||
#include <zmk/hid.h>
|
||||
#if IS_ENABLED(CONFIG_ZMK_POINTING_SMOOTH_SCROLLING)
|
||||
#include <zmk/pointing/resolution_multipliers.h>
|
||||
#endif // IS_ENABLED(CONFIG_ZMK_POINTING_SMOOTH_SCROLLING)
|
||||
#if IS_ENABLED(CONFIG_ZMK_HID_INDICATORS)
|
||||
#include <zmk/hid_indicators.h>
|
||||
#endif // IS_ENABLED(CONFIG_ZMK_HID_INDICATORS)
|
||||
@@ -69,14 +72,23 @@ static struct hids_report consumer_input = {
|
||||
.type = HIDS_INPUT,
|
||||
};
|
||||
|
||||
#if IS_ENABLED(CONFIG_ZMK_MOUSE)
|
||||
#if IS_ENABLED(CONFIG_ZMK_POINTING)
|
||||
|
||||
static struct hids_report mouse_input = {
|
||||
.id = ZMK_HID_REPORT_ID_MOUSE,
|
||||
.type = HIDS_INPUT,
|
||||
};
|
||||
|
||||
#endif // IS_ENABLED(CONFIG_ZMK_MOUSE)
|
||||
#if IS_ENABLED(CONFIG_ZMK_POINTING_SMOOTH_SCROLLING)
|
||||
|
||||
static struct hids_report mouse_feature = {
|
||||
.id = ZMK_HID_REPORT_ID_MOUSE,
|
||||
.type = HIDS_FEATURE,
|
||||
};
|
||||
|
||||
#endif // IS_ENABLED(CONFIG_ZMK_POINTING_SMOOTH_SCROLLING)
|
||||
|
||||
#endif // IS_ENABLED(CONFIG_ZMK_POINTING)
|
||||
|
||||
static bool host_requests_notification = false;
|
||||
static uint8_t ctrl_point;
|
||||
@@ -143,14 +155,53 @@ static ssize_t read_hids_consumer_input_report(struct bt_conn *conn,
|
||||
sizeof(struct zmk_hid_consumer_report_body));
|
||||
}
|
||||
|
||||
#if IS_ENABLED(CONFIG_ZMK_MOUSE)
|
||||
#if IS_ENABLED(CONFIG_ZMK_POINTING)
|
||||
|
||||
static ssize_t read_hids_mouse_input_report(struct bt_conn *conn, const struct bt_gatt_attr *attr,
|
||||
void *buf, uint16_t len, uint16_t offset) {
|
||||
struct zmk_hid_mouse_report_body *report_body = &zmk_hid_get_mouse_report()->body;
|
||||
return bt_gatt_attr_read(conn, attr, buf, len, offset, report_body,
|
||||
sizeof(struct zmk_hid_mouse_report_body));
|
||||
}
|
||||
#endif // IS_ENABLED(CONFIG_ZMK_MOUSE)
|
||||
|
||||
#if IS_ENABLED(CONFIG_ZMK_POINTING_SMOOTH_SCROLLING)
|
||||
|
||||
static ssize_t read_hids_mouse_feature_report(struct bt_conn *conn, const struct bt_gatt_attr *attr,
|
||||
void *buf, uint16_t len, uint16_t offset) {
|
||||
struct zmk_hid_mouse_report_body *report_body = &zmk_hid_get_mouse_report()->body;
|
||||
return bt_gatt_attr_read(conn, attr, buf, len, offset, report_body,
|
||||
sizeof(struct zmk_hid_mouse_report_body));
|
||||
}
|
||||
|
||||
static ssize_t write_hids_mouse_feature_report(struct bt_conn *conn,
|
||||
const struct bt_gatt_attr *attr, const void *buf,
|
||||
uint16_t len, uint16_t offset, uint8_t flags) {
|
||||
if (offset != 0) {
|
||||
return BT_GATT_ERR(BT_ATT_ERR_INVALID_OFFSET);
|
||||
}
|
||||
if (len != sizeof(struct zmk_hid_mouse_resolution_feature_report_body)) {
|
||||
return BT_GATT_ERR(BT_ATT_ERR_INVALID_ATTRIBUTE_LEN);
|
||||
}
|
||||
|
||||
struct zmk_hid_mouse_resolution_feature_report_body *report =
|
||||
(struct zmk_hid_mouse_resolution_feature_report_body *)buf;
|
||||
int profile = zmk_ble_profile_index(bt_conn_get_dst(conn));
|
||||
if (profile < 0) {
|
||||
return BT_GATT_ERR(BT_ATT_ERR_UNLIKELY);
|
||||
}
|
||||
|
||||
struct zmk_endpoint_instance endpoint = {.transport = ZMK_TRANSPORT_BLE,
|
||||
.ble = {
|
||||
.profile_index = profile,
|
||||
}};
|
||||
zmk_pointing_resolution_multipliers_process_report(report, endpoint);
|
||||
|
||||
return len;
|
||||
}
|
||||
|
||||
#endif // IS_ENABLED(CONFIG_ZMK_POINTING_SMOOTH_SCROLLING)
|
||||
|
||||
#endif // IS_ENABLED(CONFIG_ZMK_POINTING)
|
||||
|
||||
// static ssize_t write_proto_mode(struct bt_conn *conn,
|
||||
// const struct bt_gatt_attr *attr,
|
||||
@@ -200,13 +251,23 @@ BT_GATT_SERVICE_DEFINE(
|
||||
BT_GATT_DESCRIPTOR(BT_UUID_HIDS_REPORT_REF, BT_GATT_PERM_READ_ENCRYPT, read_hids_report_ref,
|
||||
NULL, &consumer_input),
|
||||
|
||||
#if IS_ENABLED(CONFIG_ZMK_MOUSE)
|
||||
#if IS_ENABLED(CONFIG_ZMK_POINTING)
|
||||
BT_GATT_CHARACTERISTIC(BT_UUID_HIDS_REPORT, BT_GATT_CHRC_READ | BT_GATT_CHRC_NOTIFY,
|
||||
BT_GATT_PERM_READ_ENCRYPT, read_hids_mouse_input_report, NULL, NULL),
|
||||
BT_GATT_CCC(input_ccc_changed, BT_GATT_PERM_READ_ENCRYPT | BT_GATT_PERM_WRITE_ENCRYPT),
|
||||
BT_GATT_DESCRIPTOR(BT_UUID_HIDS_REPORT_REF, BT_GATT_PERM_READ_ENCRYPT, read_hids_report_ref,
|
||||
NULL, &mouse_input),
|
||||
#endif // IS_ENABLED(CONFIG_ZMK_MOUSE)
|
||||
|
||||
#if IS_ENABLED(CONFIG_ZMK_POINTING_SMOOTH_SCROLLING)
|
||||
BT_GATT_CHARACTERISTIC(BT_UUID_HIDS_REPORT,
|
||||
BT_GATT_CHRC_READ | BT_GATT_CHRC_WRITE | BT_GATT_CHRC_WRITE_WITHOUT_RESP,
|
||||
BT_GATT_PERM_READ_ENCRYPT | BT_GATT_PERM_WRITE_ENCRYPT,
|
||||
read_hids_mouse_feature_report, write_hids_mouse_feature_report, NULL),
|
||||
BT_GATT_DESCRIPTOR(BT_UUID_HIDS_REPORT_REF, BT_GATT_PERM_READ_ENCRYPT, read_hids_report_ref,
|
||||
NULL, &mouse_feature),
|
||||
#endif // IS_ENABLED(CONFIG_ZMK_POINTING_SMOOTH_SCROLLING)
|
||||
|
||||
#endif // IS_ENABLED(CONFIG_ZMK_POINTING)
|
||||
|
||||
#if IS_ENABLED(CONFIG_ZMK_HID_INDICATORS)
|
||||
BT_GATT_CHARACTERISTIC(BT_UUID_HIDS_REPORT,
|
||||
@@ -328,7 +389,7 @@ int zmk_hog_send_consumer_report(struct zmk_hid_consumer_report_body *report) {
|
||||
return 0;
|
||||
};
|
||||
|
||||
#if IS_ENABLED(CONFIG_ZMK_MOUSE)
|
||||
#if IS_ENABLED(CONFIG_ZMK_POINTING)
|
||||
|
||||
K_MSGQ_DEFINE(zmk_hog_mouse_msgq, sizeof(struct zmk_hid_mouse_report_body),
|
||||
CONFIG_ZMK_BLE_MOUSE_REPORT_QUEUE_SIZE, 4);
|
||||
@@ -380,8 +441,7 @@ int zmk_hog_send_mouse_report(struct zmk_hid_mouse_report_body *report) {
|
||||
|
||||
return 0;
|
||||
};
|
||||
|
||||
#endif // IS_ENABLED(CONFIG_ZMK_MOUSE)
|
||||
#endif // IS_ENABLED(CONFIG_ZMK_POINTING)
|
||||
|
||||
static int zmk_hog_init(void) {
|
||||
static const struct k_work_queue_config queue_config = {.name = "HID Over GATT Send Work"};
|
||||
|
||||
@@ -1,43 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) 2021 The ZMK Contributors
|
||||
*
|
||||
* SPDX-License-Identifier: MIT
|
||||
*/
|
||||
|
||||
#include <drivers/behavior.h>
|
||||
#include <zephyr/logging/log.h>
|
||||
|
||||
LOG_MODULE_DECLARE(zmk, CONFIG_ZMK_LOG_LEVEL);
|
||||
|
||||
#include <zmk/events/mouse_button_state_changed.h>
|
||||
#include <zmk/hid.h>
|
||||
#include <zmk/endpoints.h>
|
||||
#include <zmk/mouse.h>
|
||||
|
||||
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);
|
||||
10
app/src/pointing/CMakeLists.txt
Normal file
10
app/src/pointing/CMakeLists.txt
Normal file
@@ -0,0 +1,10 @@
|
||||
# Copyright (c) 2024 The ZMK Contributors
|
||||
# SPDX-License-Identifier: MIT
|
||||
|
||||
target_sources_ifdef(CONFIG_ZMK_INPUT_LISTENER app PRIVATE input_listener.c)
|
||||
target_sources_ifdef(CONFIG_ZMK_INPUT_PROCESSOR_TRANSFORM app PRIVATE input_processor_transform.c)
|
||||
target_sources_ifdef(CONFIG_ZMK_INPUT_PROCESSOR_SCALER app PRIVATE input_processor_scaler.c)
|
||||
target_sources_ifdef(CONFIG_ZMK_INPUT_PROCESSOR_TEMP_LAYER app PRIVATE input_processor_temp_layer.c)
|
||||
target_sources_ifdef(CONFIG_ZMK_INPUT_PROCESSOR_CODE_MAPPER app PRIVATE input_processor_code_mapper.c)
|
||||
target_sources_ifdef(CONFIG_ZMK_POINTING_SMOOTH_SCROLLING app PRIVATE resolution_multipliers.c)
|
||||
target_sources_ifdef(CONFIG_ZMK_INPUT_SPLIT app PRIVATE input_split.c)
|
||||
75
app/src/pointing/Kconfig
Normal file
75
app/src/pointing/Kconfig
Normal file
@@ -0,0 +1,75 @@
|
||||
# Copyright (c) 2024 The ZMK Contributors
|
||||
# SPDX-License-Identifier: MIT
|
||||
|
||||
menu "Pointing Options"
|
||||
|
||||
# Deprecated old config, kept for backwards compat
|
||||
config ZMK_MOUSE
|
||||
bool "(Deprecated) Mouse Support"
|
||||
|
||||
config ZMK_POINTING
|
||||
bool "Pointing Device Support"
|
||||
default y if ZMK_MOUSE
|
||||
select INPUT
|
||||
select INPUT_THREAD_PRIORITY_OVERRIDE
|
||||
|
||||
if ZMK_POINTING
|
||||
|
||||
# Needed for anyone using gpio-keys for things like soft-off setup.
|
||||
config INPUT_GPIO_KEYS
|
||||
default n
|
||||
|
||||
config INPUT_THREAD_STACK_SIZE
|
||||
default 1024 if ZMK_SPLIT && !ZMK_SPLIT_ROLE_CENTRAL
|
||||
|
||||
if !ZMK_SPLIT || ZMK_SPLIT_ROLE_CENTRAL
|
||||
|
||||
config ZMK_POINTING_SMOOTH_SCROLLING
|
||||
bool "Smooth Scrolling"
|
||||
help
|
||||
Enable smooth scrolling, with hosts that support HID Resolution Multipliers
|
||||
|
||||
config ZMK_INPUT_LISTENER
|
||||
bool "Input listener for processing input events in the system"
|
||||
default y
|
||||
depends on DT_HAS_ZMK_INPUT_LISTENER_ENABLED
|
||||
|
||||
|
||||
config ZMK_INPUT_PROCESSOR_TEMP_LAYER
|
||||
bool "Temporary Layer Input Processor"
|
||||
default y
|
||||
depends on DT_HAS_ZMK_INPUT_PROCESSOR_TEMP_LAYER_ENABLED
|
||||
|
||||
endif
|
||||
|
||||
config ZMK_INPUT_PROCESSOR_TRANSFORM
|
||||
bool "Transform Input Processor"
|
||||
default y
|
||||
depends on DT_HAS_ZMK_INPUT_PROCESSOR_TRANSFORM_ENABLED
|
||||
|
||||
config ZMK_INPUT_PROCESSOR_SCALER
|
||||
bool "Scaling Input Processor"
|
||||
default y
|
||||
depends on DT_HAS_ZMK_INPUT_PROCESSOR_SCALER_ENABLED
|
||||
|
||||
config ZMK_INPUT_PROCESSOR_CODE_MAPPER
|
||||
bool "Code Mapper Input Processor"
|
||||
default y
|
||||
depends on DT_HAS_ZMK_INPUT_PROCESSOR_CODE_MAPPER_ENABLED
|
||||
|
||||
config ZMK_INPUT_SPLIT
|
||||
bool "Split input support"
|
||||
default y
|
||||
depends on DT_HAS_ZMK_INPUT_SPLIT_ENABLED && ZMK_SPLIT
|
||||
|
||||
if ZMK_INPUT_SPLIT
|
||||
|
||||
config ZMK_INPUT_SPLIT_INIT_PRIORITY
|
||||
int "Input Split initialization priority"
|
||||
default INPUT_INIT_PRIORITY
|
||||
|
||||
endif # ZMK_INPUT_SPLIT
|
||||
|
||||
endif # ZMK_POINTING
|
||||
|
||||
endmenu # Mouse Options
|
||||
373
app/src/pointing/input_listener.c
Normal file
373
app/src/pointing/input_listener.c
Normal file
@@ -0,0 +1,373 @@
|
||||
/*
|
||||
* Copyright (c) 2024 The ZMK Contributors
|
||||
*
|
||||
* SPDX-License-Identifier: MIT
|
||||
*/
|
||||
|
||||
#define DT_DRV_COMPAT zmk_input_listener
|
||||
|
||||
#include <zephyr/sys/util_macro.h>
|
||||
#include <zephyr/device.h>
|
||||
#include <zephyr/kernel.h>
|
||||
#include <zephyr/input/input.h>
|
||||
#include <zephyr/logging/log.h>
|
||||
|
||||
LOG_MODULE_DECLARE(zmk, CONFIG_ZMK_LOG_LEVEL);
|
||||
|
||||
#include <zephyr/dt-bindings/input/input-event-codes.h>
|
||||
|
||||
#include <zmk/endpoints.h>
|
||||
#include <drivers/input_processor.h>
|
||||
#include <zmk/pointing.h>
|
||||
|
||||
#if IS_ENABLED(CONFIG_ZMK_POINTING_SMOOTH_SCROLLING)
|
||||
#include <zmk/pointing/resolution_multipliers.h>
|
||||
#endif // IS_ENABLED(CONFIG_ZMK_POINTING_SMOOTH_SCROLLING)
|
||||
|
||||
#include <zmk/hid.h>
|
||||
#include <zmk/keymap.h>
|
||||
|
||||
#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,
|
||||
INPUT_LISTENER_XY_DATA_MODE_ABS,
|
||||
};
|
||||
|
||||
struct input_listener_axis_data {
|
||||
int16_t value;
|
||||
};
|
||||
|
||||
struct input_listener_xy_data {
|
||||
enum input_listener_xy_data_mode mode;
|
||||
struct input_listener_axis_data x;
|
||||
struct input_listener_axis_data y;
|
||||
};
|
||||
|
||||
struct input_listener_config_entry {
|
||||
size_t processors_len;
|
||||
const struct zmk_input_processor_entry *processors;
|
||||
};
|
||||
|
||||
struct input_listener_layer_override {
|
||||
uint32_t layer_mask;
|
||||
bool process_next;
|
||||
struct input_listener_config_entry config;
|
||||
};
|
||||
|
||||
struct input_processor_remainder_data {
|
||||
int16_t x, y, wheel, h_wheel;
|
||||
};
|
||||
|
||||
struct input_listener_processor_data {
|
||||
size_t remainders_len;
|
||||
struct input_processor_remainder_data *remainders;
|
||||
};
|
||||
|
||||
struct input_listener_config {
|
||||
struct input_listener_config_entry base;
|
||||
size_t layer_overrides_len;
|
||||
struct input_listener_layer_override layer_overrides[];
|
||||
};
|
||||
|
||||
struct input_listener_data {
|
||||
union {
|
||||
struct {
|
||||
struct input_listener_xy_data data;
|
||||
struct input_listener_xy_data wheel_data;
|
||||
|
||||
uint8_t button_set;
|
||||
uint8_t button_clear;
|
||||
} mouse;
|
||||
};
|
||||
|
||||
#if IS_ENABLED(CONFIG_ZMK_POINTING_SMOOTH_SCROLLING)
|
||||
int16_t wheel_remainder;
|
||||
int16_t h_wheel_remainder;
|
||||
#endif // IS_ENABLED(CONFIG_ZMK_POINTING_SMOOTH_SCROLLING)
|
||||
|
||||
struct input_listener_processor_data base_processor_data;
|
||||
struct input_listener_processor_data layer_override_data[];
|
||||
};
|
||||
|
||||
static void handle_rel_code(struct input_listener_data *data, struct input_event *evt) {
|
||||
switch (evt->code) {
|
||||
case INPUT_REL_X:
|
||||
data->mouse.data.mode = INPUT_LISTENER_XY_DATA_MODE_REL;
|
||||
data->mouse.data.x.value += evt->value;
|
||||
break;
|
||||
case INPUT_REL_Y:
|
||||
data->mouse.data.mode = INPUT_LISTENER_XY_DATA_MODE_REL;
|
||||
data->mouse.data.y.value += evt->value;
|
||||
break;
|
||||
case INPUT_REL_WHEEL:
|
||||
data->mouse.wheel_data.mode = INPUT_LISTENER_XY_DATA_MODE_REL;
|
||||
data->mouse.wheel_data.y.value += evt->value;
|
||||
break;
|
||||
case INPUT_REL_HWHEEL:
|
||||
data->mouse.wheel_data.mode = INPUT_LISTENER_XY_DATA_MODE_REL;
|
||||
data->mouse.wheel_data.x.value += evt->value;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
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) {
|
||||
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->mouse.button_set, btn, 1);
|
||||
} else {
|
||||
WRITE_BIT(data->mouse.button_clear, btn, 1);
|
||||
}
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
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 apply_config(const struct input_listener_config_entry *cfg,
|
||||
struct input_listener_processor_data *processor_data,
|
||||
struct input_listener_data *data, struct input_event *evt) {
|
||||
size_t remainder_index = 0;
|
||||
for (size_t p = 0; p < cfg->processors_len; p++) {
|
||||
const struct zmk_input_processor_entry *proc_e = &cfg->processors[p];
|
||||
struct input_processor_remainder_data *remainders = NULL;
|
||||
if (proc_e->track_remainders) {
|
||||
remainders = &processor_data->remainders[remainder_index++];
|
||||
}
|
||||
|
||||
int16_t *remainder = NULL;
|
||||
if (remainders) {
|
||||
if (evt->type == INPUT_EV_REL) {
|
||||
switch (evt->code) {
|
||||
case INPUT_REL_X:
|
||||
remainder = &remainders->x;
|
||||
break;
|
||||
case INPUT_REL_Y:
|
||||
remainder = &remainders->y;
|
||||
break;
|
||||
case INPUT_REL_WHEEL:
|
||||
remainder = &remainders->wheel;
|
||||
break;
|
||||
case INPUT_REL_HWHEEL:
|
||||
remainder = &remainders->h_wheel;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
struct zmk_input_processor_state state = {.remainder = remainder};
|
||||
|
||||
zmk_input_processor_handle_event(proc_e->dev, evt, proc_e->param1, proc_e->param2, &state);
|
||||
}
|
||||
}
|
||||
static void filter_with_input_config(const struct input_listener_config *cfg,
|
||||
struct input_listener_data *data, struct input_event *evt) {
|
||||
if (!evt->dev) {
|
||||
return;
|
||||
}
|
||||
|
||||
for (size_t oi = 0; oi < cfg->layer_overrides_len; oi++) {
|
||||
const struct input_listener_layer_override *override = &cfg->layer_overrides[oi];
|
||||
struct input_listener_processor_data *override_data = &data->layer_override_data[oi];
|
||||
uint32_t mask = override->layer_mask;
|
||||
uint8_t layer = 0;
|
||||
while (mask != 0) {
|
||||
if (mask & BIT(0) && zmk_keymap_layer_active(layer)) {
|
||||
apply_config(&override->config, override_data, data, evt);
|
||||
if (!override->process_next) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
layer++;
|
||||
mask = mask >> 1;
|
||||
}
|
||||
}
|
||||
|
||||
apply_config(&cfg->base, &data->base_processor_data, data, evt);
|
||||
}
|
||||
|
||||
static void clear_xy_data(struct input_listener_xy_data *data) {
|
||||
data->x.value = data->y.value = 0;
|
||||
data->mode = INPUT_LISTENER_XY_DATA_MODE_NONE;
|
||||
}
|
||||
|
||||
#if IS_ENABLED(CONFIG_ZMK_POINTING_SMOOTH_SCROLLING)
|
||||
static void apply_resolution_scaling(struct input_listener_data *data, struct input_event *evt) {
|
||||
int16_t *remainder;
|
||||
uint8_t div;
|
||||
|
||||
switch (evt->code) {
|
||||
case INPUT_REL_WHEEL:
|
||||
remainder = &data->wheel_remainder;
|
||||
div = (16 - zmk_pointing_resolution_multipliers_get_current_profile().wheel);
|
||||
break;
|
||||
case INPUT_REL_HWHEEL:
|
||||
remainder = &data->h_wheel_remainder;
|
||||
div = (16 - zmk_pointing_resolution_multipliers_get_current_profile().hor_wheel);
|
||||
break;
|
||||
default:
|
||||
return;
|
||||
}
|
||||
|
||||
int16_t val = evt->value + *remainder;
|
||||
int16_t scaled = val / (int16_t)div;
|
||||
*remainder = val - (scaled * (int16_t)div);
|
||||
evt->value = val;
|
||||
}
|
||||
#endif // IS_ENABLED(CONFIG_ZMK_POINTING_SMOOTH_SCROLLING)
|
||||
|
||||
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, data, evt);
|
||||
|
||||
#if IS_ENABLED(CONFIG_ZMK_POINTING_SMOOTH_SCROLLING)
|
||||
apply_resolution_scaling(data, evt);
|
||||
#endif // IS_ENABLED(CONFIG_ZMK_POINTING_SMOOTH_SCROLLING)
|
||||
|
||||
switch (evt->type) {
|
||||
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(config, data, evt);
|
||||
break;
|
||||
}
|
||||
|
||||
if (evt->sync) {
|
||||
if (data->mouse.wheel_data.mode == INPUT_LISTENER_XY_DATA_MODE_REL) {
|
||||
zmk_hid_mouse_scroll_set(data->mouse.wheel_data.x.value,
|
||||
data->mouse.wheel_data.y.value);
|
||||
}
|
||||
|
||||
if (data->mouse.data.mode == INPUT_LISTENER_XY_DATA_MODE_REL) {
|
||||
zmk_hid_mouse_movement_set(data->mouse.data.x.value, data->mouse.data.y.value);
|
||||
}
|
||||
|
||||
if (data->mouse.button_set != 0) {
|
||||
for (int i = 0; i < ZMK_HID_MOUSE_NUM_BUTTONS; i++) {
|
||||
if ((data->mouse.button_set & BIT(i)) != 0) {
|
||||
zmk_hid_mouse_button_press(i);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (data->mouse.button_clear != 0) {
|
||||
for (int i = 0; i < ZMK_HID_MOUSE_NUM_BUTTONS; i++) {
|
||||
if ((data->mouse.button_clear & 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->mouse.data);
|
||||
clear_xy_data(&data->mouse.wheel_data);
|
||||
|
||||
data->mouse.button_set = data->mouse.button_clear = 0;
|
||||
}
|
||||
}
|
||||
|
||||
#endif // VALID_LISTENER_COUNT > 0
|
||||
|
||||
#define ONE_FOR_TRACKED(n, elem, idx) \
|
||||
+DT_PROP(DT_PHANDLE_BY_IDX(n, input_processors, idx), track_remainders)
|
||||
#define PROCESSOR_REM_TRACKERS(n) (0 DT_FOREACH_PROP_ELEM(n, input_processors, ONE_FOR_TRACKED))
|
||||
|
||||
#define SCOPED_PROCESSOR(scope, n, id) \
|
||||
COND_CODE_1(DT_NODE_HAS_PROP(n, input_processors), \
|
||||
(static struct input_processor_remainder_data _CONCAT( \
|
||||
input_processor_remainders_##id, scope)[PROCESSOR_REM_TRACKERS(n)] = {};), \
|
||||
()) \
|
||||
static const struct zmk_input_processor_entry _CONCAT( \
|
||||
processor_##id, scope)[DT_PROP_LEN_OR(n, input_processors, 0)] = \
|
||||
COND_CODE_1(DT_NODE_HAS_PROP(n, input_processors), \
|
||||
({LISTIFY(DT_PROP_LEN(n, input_processors), ZMK_INPUT_PROCESSOR_ENTRY_AT_IDX, \
|
||||
(, ), n)}), \
|
||||
({}));
|
||||
|
||||
#define IL_EXTRACT_CONFIG(n, id, scope) \
|
||||
{ \
|
||||
.processors_len = DT_PROP_LEN_OR(n, input_processors, 0), \
|
||||
.processors = _CONCAT(processor_##id, scope), \
|
||||
}
|
||||
|
||||
#define IL_EXTRACT_DATA(n, id, scope) \
|
||||
{COND_CODE_1(DT_NODE_HAS_PROP(n, input_processors), \
|
||||
(.remainders_len = PROCESSOR_REM_TRACKERS(n), \
|
||||
.remainders = _CONCAT(input_processor_remainders_##id, scope), ), \
|
||||
())}
|
||||
|
||||
#define IL_ONE(...) +1
|
||||
|
||||
#define CHILD_CONFIG(node, parent) SCOPED_PROCESSOR(node, node, parent)
|
||||
|
||||
#define OVERRIDE_LAYER_BIT(node, prop, idx) BIT(DT_PROP_BY_IDX(node, prop, idx))
|
||||
|
||||
#define IL_OVERRIDE(node, parent) \
|
||||
{ \
|
||||
.layer_mask = DT_FOREACH_PROP_ELEM_SEP(node, layers, OVERRIDE_LAYER_BIT, (|)), \
|
||||
.process_next = DT_PROP_OR(node, process_next, false), \
|
||||
.config = IL_EXTRACT_CONFIG(node, parent, node), \
|
||||
}
|
||||
|
||||
#define IL_OVERRIDE_DATA(node, parent) IL_EXTRACT_DATA(node, parent, node)
|
||||
|
||||
#define IL_INST(n) \
|
||||
COND_CODE_1( \
|
||||
DT_NODE_HAS_STATUS(DT_INST_PHANDLE(n, device), okay), \
|
||||
(SCOPED_PROCESSOR(base, DT_DRV_INST(n), n); \
|
||||
DT_INST_FOREACH_CHILD_VARGS(n, CHILD_CONFIG, \
|
||||
n) static const struct input_listener_config config_##n = \
|
||||
{ \
|
||||
.base = IL_EXTRACT_CONFIG(DT_DRV_INST(n), n, base), \
|
||||
.layer_overrides_len = (0 DT_INST_FOREACH_CHILD(n, IL_ONE)), \
|
||||
.layer_overrides = {DT_INST_FOREACH_CHILD_SEP_VARGS(n, IL_OVERRIDE, (, ), n)}, \
|
||||
}; \
|
||||
static struct input_listener_data data_##n = \
|
||||
{ \
|
||||
.base_processor_data = IL_EXTRACT_DATA(DT_DRV_INST(n), n, base), \
|
||||
.layer_override_data = {DT_INST_FOREACH_CHILD_SEP_VARGS(n, IL_OVERRIDE_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)
|
||||
58
app/src/pointing/input_processor_code_mapper.c
Normal file
58
app/src/pointing/input_processor_code_mapper.c
Normal file
@@ -0,0 +1,58 @@
|
||||
/*
|
||||
* Copyright (c) 2024 The ZMK Contributors
|
||||
*
|
||||
* SPDX-License-Identifier: MIT
|
||||
*/
|
||||
|
||||
#define DT_DRV_COMPAT zmk_input_processor_code_mapper
|
||||
|
||||
#include <zephyr/kernel.h>
|
||||
#include <zephyr/device.h>
|
||||
#include <drivers/input_processor.h>
|
||||
|
||||
#include <zephyr/logging/log.h>
|
||||
|
||||
LOG_MODULE_DECLARE(zmk, CONFIG_ZMK_LOG_LEVEL);
|
||||
|
||||
struct cm_config {
|
||||
uint8_t type;
|
||||
size_t mapping_size;
|
||||
uint16_t mapping[];
|
||||
};
|
||||
|
||||
static int cm_handle_event(const struct device *dev, struct input_event *event, uint32_t param1,
|
||||
uint32_t param2, struct zmk_input_processor_state *state) {
|
||||
const struct cm_config *cfg = dev->config;
|
||||
|
||||
if (event->type != cfg->type) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
for (int i = 0; i < cfg->mapping_size / 2; i++) {
|
||||
if (cfg->mapping[i * 2] == event->code) {
|
||||
uint16_t orig = event->code;
|
||||
event->code = cfg->mapping[(i * 2) + 1];
|
||||
LOG_DBG("Remapped %d to %d", orig, event->code);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static struct zmk_input_processor_driver_api cm_driver_api = {
|
||||
.handle_event = cm_handle_event,
|
||||
};
|
||||
|
||||
#define TL_INST(n) \
|
||||
static const struct cm_config cm_config_##n = { \
|
||||
.type = DT_INST_PROP_OR(n, type, INPUT_EV_REL), \
|
||||
.mapping_size = DT_INST_PROP_LEN(n, map), \
|
||||
.mapping = DT_INST_PROP(n, map), \
|
||||
}; \
|
||||
BUILD_ASSERT(DT_INST_PROP_LEN(n, map) % 2 == 0, \
|
||||
"Must have an even number of mapping entries"); \
|
||||
DEVICE_DT_INST_DEFINE(n, NULL, NULL, NULL, &cm_config_##n, POST_KERNEL, \
|
||||
CONFIG_KERNEL_INIT_PRIORITY_DEFAULT, &cm_driver_api);
|
||||
|
||||
DT_INST_FOREACH_STATUS_OKAY(TL_INST)
|
||||
75
app/src/pointing/input_processor_scaler.c
Normal file
75
app/src/pointing/input_processor_scaler.c
Normal file
@@ -0,0 +1,75 @@
|
||||
/*
|
||||
* Copyright (c) 2024 The ZMK Contributors
|
||||
*
|
||||
* SPDX-License-Identifier: MIT
|
||||
*/
|
||||
|
||||
#define DT_DRV_COMPAT zmk_input_processor_scaler
|
||||
|
||||
#include <zephyr/kernel.h>
|
||||
#include <zephyr/device.h>
|
||||
#include <drivers/input_processor.h>
|
||||
|
||||
#include <zephyr/logging/log.h>
|
||||
|
||||
LOG_MODULE_DECLARE(zmk, CONFIG_ZMK_LOG_LEVEL);
|
||||
|
||||
struct scaler_config {
|
||||
uint8_t type;
|
||||
size_t codes_len;
|
||||
uint16_t codes[];
|
||||
};
|
||||
|
||||
static int scale_val(struct input_event *event, uint32_t mul, uint32_t div,
|
||||
struct zmk_input_processor_state *state) {
|
||||
int16_t value_mul = event->value * (int16_t)mul;
|
||||
|
||||
if (state && state->remainder) {
|
||||
value_mul += *state->remainder;
|
||||
}
|
||||
|
||||
int16_t scaled = value_mul / (int16_t)div;
|
||||
|
||||
if (state && state->remainder) {
|
||||
*state->remainder = value_mul - (scaled * (int16_t)div);
|
||||
}
|
||||
|
||||
LOG_DBG("scaled %d with %d/%d to %d with remainder %d", event->value, mul, div, scaled,
|
||||
(state && state->remainder) ? *state->remainder : 0);
|
||||
|
||||
event->value = scaled;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int scaler_handle_event(const struct device *dev, struct input_event *event, uint32_t param1,
|
||||
uint32_t param2, struct zmk_input_processor_state *state) {
|
||||
const struct scaler_config *cfg = dev->config;
|
||||
|
||||
if (event->type != cfg->type) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
for (int i = 0; i < cfg->codes_len; i++) {
|
||||
if (cfg->codes[i] == event->code) {
|
||||
return scale_val(event, param1, param2, state);
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static struct zmk_input_processor_driver_api scaler_driver_api = {
|
||||
.handle_event = scaler_handle_event,
|
||||
};
|
||||
|
||||
#define SCALER_INST(n) \
|
||||
static const struct scaler_config scaler_config_##n = { \
|
||||
.type = DT_INST_PROP_OR(n, type, INPUT_EV_REL), \
|
||||
.codes_len = DT_INST_PROP_LEN(n, codes), \
|
||||
.codes = DT_INST_PROP(n, codes), \
|
||||
}; \
|
||||
DEVICE_DT_INST_DEFINE(n, NULL, NULL, NULL, &scaler_config_##n, POST_KERNEL, \
|
||||
CONFIG_KERNEL_INIT_PRIORITY_DEFAULT, &scaler_driver_api);
|
||||
|
||||
DT_INST_FOREACH_STATUS_OKAY(SCALER_INST)
|
||||
213
app/src/pointing/input_processor_temp_layer.c
Normal file
213
app/src/pointing/input_processor_temp_layer.c
Normal file
@@ -0,0 +1,213 @@
|
||||
/*
|
||||
* Copyright (c) 2024 The ZMK Contributors
|
||||
*
|
||||
* SPDX-License-Identifier: MIT
|
||||
*/
|
||||
|
||||
#define DT_DRV_COMPAT zmk_input_processor_temp_layer
|
||||
|
||||
#include <zephyr/kernel.h>
|
||||
#include <zephyr/device.h>
|
||||
#include <drivers/input_processor.h>
|
||||
#include <zephyr/logging/log.h>
|
||||
#include <zmk/keymap.h>
|
||||
#include <zmk/behavior.h>
|
||||
#include <zmk/events/position_state_changed.h>
|
||||
#include <zmk/events/keycode_state_changed.h>
|
||||
|
||||
LOG_MODULE_DECLARE(zmk, CONFIG_ZMK_LOG_LEVEL);
|
||||
|
||||
/* Constants and Types */
|
||||
#define MAX_LAYERS ZMK_KEYMAP_LAYERS_LEN
|
||||
|
||||
struct temp_layer_config {
|
||||
int16_t require_prior_idle_ms;
|
||||
const uint16_t *excluded_positions;
|
||||
size_t num_positions;
|
||||
};
|
||||
|
||||
struct temp_layer_state {
|
||||
uint8_t toggle_layer;
|
||||
bool is_active;
|
||||
int64_t last_tapped_timestamp;
|
||||
};
|
||||
|
||||
struct temp_layer_data {
|
||||
const struct device *dev;
|
||||
struct temp_layer_state state;
|
||||
};
|
||||
|
||||
/* Static Work Queue Items */
|
||||
static struct k_work_delayable layer_disable_works[MAX_LAYERS];
|
||||
|
||||
/* Position Search */
|
||||
static bool position_is_excluded(const struct temp_layer_config *config, uint32_t position) {
|
||||
if (!config->excluded_positions || !config->num_positions) {
|
||||
return false;
|
||||
}
|
||||
|
||||
const uint16_t *end = config->excluded_positions + config->num_positions;
|
||||
for (const uint16_t *pos = config->excluded_positions; pos < end; pos++) {
|
||||
if (*pos == position) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/* Timing Check */
|
||||
static bool should_quick_tap(const struct temp_layer_config *config, int64_t last_tapped,
|
||||
int64_t current_time) {
|
||||
return (last_tapped + config->require_prior_idle_ms) > current_time;
|
||||
}
|
||||
|
||||
/* Layer State Management */
|
||||
static void update_layer_state(struct temp_layer_state *state, bool activate) {
|
||||
if (state->is_active == activate) {
|
||||
return;
|
||||
}
|
||||
|
||||
state->is_active = activate;
|
||||
if (activate) {
|
||||
zmk_keymap_layer_activate(state->toggle_layer);
|
||||
LOG_DBG("Layer %d activated", state->toggle_layer);
|
||||
} else {
|
||||
zmk_keymap_layer_deactivate(state->toggle_layer);
|
||||
LOG_DBG("Layer %d deactivated", state->toggle_layer);
|
||||
}
|
||||
}
|
||||
|
||||
/* Work Queue Callback */
|
||||
static void layer_disable_callback(struct k_work *work) {
|
||||
struct k_work_delayable *d_work = k_work_delayable_from_work(work);
|
||||
int layer_index = ARRAY_INDEX(layer_disable_works, d_work);
|
||||
|
||||
const struct device *dev = DEVICE_DT_INST_GET(0);
|
||||
struct temp_layer_data *data = (struct temp_layer_data *)dev->data;
|
||||
|
||||
if (zmk_keymap_layer_active(layer_index)) {
|
||||
update_layer_state(&data->state, false);
|
||||
}
|
||||
}
|
||||
|
||||
/* Event Handlers */
|
||||
static int handle_position_state_changed(const zmk_event_t *eh) {
|
||||
const struct zmk_position_state_changed *ev = as_zmk_position_state_changed(eh);
|
||||
if (!ev->state) {
|
||||
return ZMK_EV_EVENT_BUBBLE;
|
||||
}
|
||||
|
||||
const struct device *dev = DEVICE_DT_INST_GET(0);
|
||||
struct temp_layer_data *data = (struct temp_layer_data *)dev->data;
|
||||
const struct temp_layer_config *cfg = dev->config;
|
||||
|
||||
if (data->state.is_active && cfg->excluded_positions && cfg->num_positions > 0) {
|
||||
if (!position_is_excluded(cfg, ev->position)) {
|
||||
LOG_DBG("Position not excluded, deactivating layer");
|
||||
update_layer_state(&data->state, false);
|
||||
}
|
||||
}
|
||||
LOG_DBG("Position excluded, continuing");
|
||||
|
||||
return ZMK_EV_EVENT_BUBBLE;
|
||||
}
|
||||
|
||||
static int handle_keycode_state_changed(const zmk_event_t *eh) {
|
||||
const struct zmk_keycode_state_changed *ev = as_zmk_keycode_state_changed(eh);
|
||||
if (!ev->state) {
|
||||
return ZMK_EV_EVENT_BUBBLE;
|
||||
}
|
||||
|
||||
const struct device *dev = DEVICE_DT_INST_GET(0);
|
||||
struct temp_layer_data *data = (struct temp_layer_data *)dev->data;
|
||||
LOG_DBG("Setting last_tapped_timestamp to: %d", ev->timestamp);
|
||||
data->state.last_tapped_timestamp = ev->timestamp;
|
||||
|
||||
return ZMK_EV_EVENT_BUBBLE;
|
||||
}
|
||||
|
||||
static int handle_state_changed_dispatcher(const zmk_event_t *eh) {
|
||||
if (as_zmk_position_state_changed(eh) != NULL) {
|
||||
LOG_DBG("Dispatching handle_position_state_changed");
|
||||
return handle_position_state_changed(eh);
|
||||
} else if (as_zmk_keycode_state_changed(eh) != NULL) {
|
||||
LOG_DBG("Dispatching handle_keycode_state_changed");
|
||||
return handle_keycode_state_changed(eh);
|
||||
}
|
||||
|
||||
return ZMK_EV_EVENT_BUBBLE;
|
||||
}
|
||||
|
||||
/* Driver Implementation */
|
||||
static int temp_layer_handle_event(const struct device *dev, struct input_event *event,
|
||||
uint32_t param1, uint32_t param2,
|
||||
struct zmk_input_processor_state *state) {
|
||||
if (param1 >= MAX_LAYERS) {
|
||||
LOG_ERR("Invalid layer index: %d", param1);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
struct temp_layer_data *data = (struct temp_layer_data *)dev->data;
|
||||
const struct temp_layer_config *cfg = dev->config;
|
||||
|
||||
data->state.toggle_layer = param1;
|
||||
|
||||
if (!data->state.is_active &&
|
||||
!should_quick_tap(cfg, data->state.last_tapped_timestamp, k_uptime_get())) {
|
||||
update_layer_state(&data->state, true);
|
||||
}
|
||||
|
||||
if (param2 > 0) {
|
||||
k_work_reschedule(&layer_disable_works[param1], K_MSEC(param2));
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int temp_layer_init(const struct device *dev) {
|
||||
for (int i = 0; i < MAX_LAYERS; i++) {
|
||||
k_work_init_delayable(&layer_disable_works[i], layer_disable_callback);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Driver API */
|
||||
static const struct zmk_input_processor_driver_api temp_layer_driver_api = {
|
||||
.handle_event = temp_layer_handle_event,
|
||||
};
|
||||
|
||||
/* Event Listeners Conditions */
|
||||
#define NEEDS_POSITION_HANDLERS(n, ...) DT_INST_PROP_HAS_IDX(n, excluded_positions, 0)
|
||||
#define NEEDS_KEYCODE_HANDLERS(n, ...) (DT_INST_PROP_OR(n, require_prior_idle_ms, 0) > 0)
|
||||
|
||||
/* Event Handlers Registration */
|
||||
#if DT_INST_FOREACH_STATUS_OKAY_VARGS(NEEDS_POSITION_HANDLERS, ||) || \
|
||||
DT_INST_FOREACH_STATUS_OKAY_VARGS(NEEDS_KEYCODE_HANDLERS, ||)
|
||||
ZMK_LISTENER(processor_temp_layer, handle_state_changed_dispatcher);
|
||||
#endif
|
||||
|
||||
/* Individual Subscriptions */
|
||||
#if DT_INST_FOREACH_STATUS_OKAY_VARGS(NEEDS_POSITION_HANDLERS, ||)
|
||||
ZMK_SUBSCRIPTION(processor_temp_layer, zmk_position_state_changed);
|
||||
#endif
|
||||
|
||||
#if DT_INST_FOREACH_STATUS_OKAY_VARGS(NEEDS_KEYCODE_HANDLERS, ||)
|
||||
ZMK_SUBSCRIPTION(processor_temp_layer, zmk_keycode_state_changed);
|
||||
#endif
|
||||
|
||||
/* Device Instantiation */
|
||||
#define TEMP_LAYER_INST(n) \
|
||||
static struct temp_layer_data processor_temp_layer_data_##n = {}; \
|
||||
static const uint16_t excluded_positions_##n[] = DT_INST_PROP(n, excluded_positions); \
|
||||
static const struct temp_layer_config processor_temp_layer_config_##n = { \
|
||||
.require_prior_idle_ms = DT_INST_PROP_OR(n, require_prior_idle_ms, 0), \
|
||||
.excluded_positions = excluded_positions_##n, \
|
||||
.num_positions = DT_INST_PROP_LEN(n, excluded_positions), \
|
||||
}; \
|
||||
DEVICE_DT_INST_DEFINE(n, temp_layer_init, NULL, &processor_temp_layer_data_##n, \
|
||||
&processor_temp_layer_config_##n, POST_KERNEL, \
|
||||
CONFIG_KERNEL_INIT_PRIORITY_DEFAULT, &temp_layer_driver_api);
|
||||
|
||||
DT_INST_FOREACH_STATUS_OKAY(TEMP_LAYER_INST)
|
||||
92
app/src/pointing/input_processor_transform.c
Normal file
92
app/src/pointing/input_processor_transform.c
Normal file
@@ -0,0 +1,92 @@
|
||||
/*
|
||||
* Copyright (c) 2024 The ZMK Contributors
|
||||
*
|
||||
* SPDX-License-Identifier: MIT
|
||||
*/
|
||||
|
||||
#define DT_DRV_COMPAT zmk_input_processor_transform
|
||||
|
||||
#include <zephyr/kernel.h>
|
||||
#include <zephyr/device.h>
|
||||
#include <drivers/input_processor.h>
|
||||
|
||||
#include <dt-bindings/zmk/input_transform.h>
|
||||
|
||||
#include <zephyr/logging/log.h>
|
||||
|
||||
LOG_MODULE_DECLARE(zmk, CONFIG_ZMK_LOG_LEVEL);
|
||||
|
||||
#include <zmk/keymap.h>
|
||||
|
||||
struct ipt_config {
|
||||
size_t x_codes_size;
|
||||
size_t y_codes_size;
|
||||
uint8_t type;
|
||||
|
||||
const uint16_t *x_codes;
|
||||
const uint16_t *y_codes;
|
||||
};
|
||||
|
||||
static int code_idx(uint16_t code, const uint16_t *list, size_t len) {
|
||||
for (int i = 0; i < len; i++) {
|
||||
if (list[i] == code) {
|
||||
return i;
|
||||
}
|
||||
}
|
||||
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
static int ipt_handle_event(const struct device *dev, struct input_event *event, uint32_t param1,
|
||||
uint32_t param2, struct zmk_input_processor_state *state) {
|
||||
const struct ipt_config *cfg = dev->config;
|
||||
|
||||
if (event->type != cfg->type) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (param1 & INPUT_TRANSFORM_XY_SWAP) {
|
||||
int idx = code_idx(event->code, cfg->x_codes, cfg->x_codes_size);
|
||||
if (idx >= 0) {
|
||||
event->code = cfg->y_codes[idx];
|
||||
} else {
|
||||
idx = code_idx(event->code, cfg->y_codes, cfg->y_codes_size);
|
||||
|
||||
if (idx >= 0) {
|
||||
event->code = cfg->x_codes[idx];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if ((param1 & INPUT_TRANSFORM_X_INVERT &&
|
||||
code_idx(event->code, cfg->x_codes, cfg->x_codes_size) >= 0) ||
|
||||
(param1 & INPUT_TRANSFORM_Y_INVERT &&
|
||||
code_idx(event->code, cfg->y_codes, cfg->y_codes_size) >= 0)) {
|
||||
event->value = -event->value;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static struct zmk_input_processor_driver_api ipt_driver_api = {
|
||||
.handle_event = ipt_handle_event,
|
||||
};
|
||||
|
||||
static int ipt_init(const struct device *dev) { return 0; }
|
||||
|
||||
#define IPT_INST(n) \
|
||||
static const uint16_t ipt_x_codes_##n[] = DT_INST_PROP(n, x_codes); \
|
||||
static const uint16_t ipt_y_codes_##n[] = DT_INST_PROP(n, y_codes); \
|
||||
BUILD_ASSERT(ARRAY_SIZE(ipt_x_codes_##n) == ARRAY_SIZE(ipt_x_codes_##n), \
|
||||
"X and Y codes need to be the same size"); \
|
||||
static const struct ipt_config ipt_config_##n = { \
|
||||
.type = DT_INST_PROP_OR(n, type, INPUT_EV_REL), \
|
||||
.x_codes_size = DT_INST_PROP_LEN(n, x_codes), \
|
||||
.y_codes_size = DT_INST_PROP_LEN(n, y_codes), \
|
||||
.x_codes = ipt_x_codes_##n, \
|
||||
.y_codes = ipt_y_codes_##n, \
|
||||
}; \
|
||||
DEVICE_DT_INST_DEFINE(n, &ipt_init, NULL, NULL, &ipt_config_##n, POST_KERNEL, \
|
||||
CONFIG_KERNEL_INIT_PRIORITY_DEFAULT, &ipt_driver_api);
|
||||
|
||||
DT_INST_FOREACH_STATUS_OKAY(IPT_INST)
|
||||
69
app/src/pointing/input_split.c
Normal file
69
app/src/pointing/input_split.c
Normal file
@@ -0,0 +1,69 @@
|
||||
/*
|
||||
* Copyright (c) 2024 The ZMK Contributors
|
||||
*
|
||||
* SPDX-License-Identifier: MIT
|
||||
*/
|
||||
|
||||
#define DT_DRV_COMPAT zmk_input_split
|
||||
|
||||
#include <zephyr/kernel.h>
|
||||
#include <zephyr/device.h>
|
||||
#include <zephyr/input/input.h>
|
||||
#include <drivers/input_processor.h>
|
||||
|
||||
#include <zephyr/logging/log.h>
|
||||
|
||||
LOG_MODULE_DECLARE(zmk, CONFIG_ZMK_LOG_LEVEL);
|
||||
|
||||
#if IS_ENABLED(CONFIG_ZMK_SPLIT_ROLE_CENTRAL)
|
||||
|
||||
struct zis_entry {
|
||||
uint8_t reg;
|
||||
const struct device *dev;
|
||||
};
|
||||
|
||||
#define ZIS_ENTRY(n) {.reg = DT_INST_REG_ADDR(n), .dev = DEVICE_DT_GET(DT_DRV_INST(n))},
|
||||
|
||||
static const struct zis_entry proxy_inputs[] = {DT_INST_FOREACH_STATUS_OKAY(ZIS_ENTRY)};
|
||||
|
||||
int zmk_input_split_report_peripheral_event(uint8_t reg, uint8_t type, uint16_t code, int32_t value,
|
||||
bool sync) {
|
||||
LOG_DBG("Got peripheral event for %d!", reg);
|
||||
for (size_t i = 0; i < ARRAY_SIZE(proxy_inputs); i++) {
|
||||
if (reg == proxy_inputs[i].reg) {
|
||||
return input_report(proxy_inputs[i].dev, type, code, value, sync, K_NO_WAIT);
|
||||
}
|
||||
}
|
||||
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
#define ZIS_INST(n) \
|
||||
DEVICE_DT_INST_DEFINE(n, NULL, NULL, NULL, NULL, POST_KERNEL, \
|
||||
CONFIG_ZMK_INPUT_SPLIT_INIT_PRIORITY, NULL);
|
||||
|
||||
#else
|
||||
|
||||
#include <zmk/split/bluetooth/service.h>
|
||||
|
||||
#define ZIS_INST(n) \
|
||||
static const struct zmk_input_processor_entry processors_##n[] = \
|
||||
COND_CODE_1(DT_INST_NODE_HAS_PROP(n, input_processors), \
|
||||
({LISTIFY(DT_INST_PROP_LEN(n, input_processors), \
|
||||
ZMK_INPUT_PROCESSOR_ENTRY_AT_IDX, (, ), DT_DRV_INST(n))}), \
|
||||
({})); \
|
||||
BUILD_ASSERT(DT_INST_NODE_HAS_PROP(n, device), \
|
||||
"Peripheral input splits need an `input` property set"); \
|
||||
void split_input_handler_##n(struct input_event *evt) { \
|
||||
for (size_t i = 0; i < ARRAY_SIZE(processors_##n); i++) { \
|
||||
zmk_input_processor_handle_event(processors_##n[i].dev, evt, processors_##n[i].param1, \
|
||||
processors_##n[i].param2, NULL); \
|
||||
} \
|
||||
zmk_split_bt_report_input(DT_INST_REG_ADDR(n), evt->type, evt->code, evt->value, \
|
||||
evt->sync); \
|
||||
} \
|
||||
INPUT_CALLBACK_DEFINE(DEVICE_DT_GET(DT_INST_PHANDLE(n, device)), split_input_handler_##n);
|
||||
|
||||
#endif
|
||||
|
||||
DT_INST_FOREACH_STATUS_OKAY(ZIS_INST)
|
||||
50
app/src/pointing/resolution_multipliers.c
Normal file
50
app/src/pointing/resolution_multipliers.c
Normal file
@@ -0,0 +1,50 @@
|
||||
/*
|
||||
* Copyright (c) 2024 The ZMK Contributors
|
||||
*
|
||||
* SPDX-License-Identifier: MIT
|
||||
*/
|
||||
|
||||
#include <zephyr/kernel.h>
|
||||
#include <zephyr/logging/log.h>
|
||||
|
||||
#include <zmk/ble.h>
|
||||
#include <zmk/endpoints.h>
|
||||
#include <zmk/pointing/resolution_multipliers.h>
|
||||
|
||||
LOG_MODULE_DECLARE(zmk, CONFIG_ZMK_LOG_LEVEL);
|
||||
|
||||
static struct zmk_pointing_resolution_multipliers multipliers[ZMK_ENDPOINT_COUNT];
|
||||
|
||||
struct zmk_pointing_resolution_multipliers
|
||||
zmk_pointing_resolution_multipliers_get_current_profile(void) {
|
||||
return zmk_pointing_resolution_multipliers_get_profile(zmk_endpoints_selected());
|
||||
}
|
||||
|
||||
struct zmk_pointing_resolution_multipliers
|
||||
zmk_pointing_resolution_multipliers_get_profile(struct zmk_endpoint_instance endpoint) {
|
||||
const int profile = zmk_endpoint_instance_to_index(endpoint);
|
||||
return multipliers[profile];
|
||||
}
|
||||
|
||||
void zmk_pointing_resolution_multipliers_set_profile(struct zmk_pointing_resolution_multipliers m,
|
||||
struct zmk_endpoint_instance endpoint) {
|
||||
int profile = zmk_endpoint_instance_to_index(endpoint);
|
||||
|
||||
// This write is not happening on the main thread. To prevent potential data races, every
|
||||
// operation involving hid_indicators must be atomic. Currently, each function either reads
|
||||
// or writes only one entry at a time, so it is safe to do these operations without a lock.
|
||||
multipliers[profile] = m;
|
||||
}
|
||||
|
||||
void zmk_pointing_resolution_multipliers_process_report(
|
||||
struct zmk_hid_mouse_resolution_feature_report_body *report,
|
||||
struct zmk_endpoint_instance endpoint) {
|
||||
struct zmk_pointing_resolution_multipliers vals = {
|
||||
.wheel = report->wheel_res,
|
||||
.hor_wheel = report->hwheel_res,
|
||||
};
|
||||
zmk_pointing_resolution_multipliers_set_profile(vals, endpoint);
|
||||
|
||||
LOG_DBG("Update resolution multipliers: endpoint=%d, wheel=%d, hor_wheel=%d",
|
||||
endpoint.transport, vals.wheel, vals.hor_wheel);
|
||||
}
|
||||
@@ -29,6 +29,7 @@ LOG_MODULE_DECLARE(zmk, CONFIG_ZMK_LOG_LEVEL);
|
||||
#include <zmk/events/position_state_changed.h>
|
||||
#include <zmk/events/sensor_event.h>
|
||||
#include <zmk/events/battery_state_changed.h>
|
||||
#include <zmk/pointing/input_split.h>
|
||||
#include <zmk/hid_indicators_types.h>
|
||||
#include <zmk/physical_layouts.h>
|
||||
|
||||
@@ -62,6 +63,71 @@ struct peripheral_slot {
|
||||
uint8_t changed_positions[POSITION_STATE_DATA_LEN];
|
||||
};
|
||||
|
||||
#if IS_ENABLED(CONFIG_ZMK_INPUT_SPLIT)
|
||||
|
||||
static const struct bt_uuid *gatt_ccc_uuid = BT_UUID_GATT_CCC;
|
||||
static const struct bt_uuid *gatt_cpf_uuid = BT_UUID_GATT_CPF;
|
||||
|
||||
struct peripheral_input_slot {
|
||||
struct bt_conn *conn;
|
||||
struct bt_gatt_subscribe_params sub;
|
||||
uint8_t reg;
|
||||
};
|
||||
|
||||
#define COUNT_INPUT_SPLIT(n) +1
|
||||
|
||||
static struct peripheral_input_slot
|
||||
peripheral_input_slots[(0 DT_FOREACH_STATUS_OKAY(zmk_input_split, COUNT_INPUT_SPLIT))];
|
||||
|
||||
static bool input_slot_is_open(size_t i) {
|
||||
return i < ARRAY_SIZE(peripheral_input_slots) && peripheral_input_slots[i].conn == NULL;
|
||||
}
|
||||
|
||||
static bool input_slot_is_pending(size_t i) {
|
||||
return i < ARRAY_SIZE(peripheral_input_slots) && peripheral_input_slots[i].conn != NULL &&
|
||||
(!peripheral_input_slots[i].sub.value_handle ||
|
||||
!peripheral_input_slots[i].sub.ccc_handle || !peripheral_input_slots[i].reg);
|
||||
}
|
||||
|
||||
static int reserve_next_open_input_slot(struct peripheral_input_slot **slot, struct bt_conn *conn) {
|
||||
for (size_t i = 0; i < ARRAY_SIZE(peripheral_input_slots); i++) {
|
||||
if (input_slot_is_open(i)) {
|
||||
peripheral_input_slots[i].conn = conn;
|
||||
|
||||
// Clear out any previously set values
|
||||
peripheral_input_slots[i].sub.value_handle = 0;
|
||||
peripheral_input_slots[i].sub.ccc_handle = 0;
|
||||
peripheral_input_slots[i].reg = 0;
|
||||
*slot = &peripheral_input_slots[i];
|
||||
return i;
|
||||
}
|
||||
}
|
||||
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
static int find_pending_input_slot(struct peripheral_input_slot **slot, struct bt_conn *conn) {
|
||||
for (size_t i = 0; i < ARRAY_SIZE(peripheral_input_slots); i++) {
|
||||
if (peripheral_input_slots[i].conn == conn && input_slot_is_pending(i)) {
|
||||
*slot = &peripheral_input_slots[i];
|
||||
return i;
|
||||
}
|
||||
}
|
||||
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
void release_peripheral_input_subs(struct bt_conn *conn) {
|
||||
for (size_t i = 0; i < ARRAY_SIZE(peripheral_input_slots); i++) {
|
||||
if (peripheral_input_slots[i].conn == conn) {
|
||||
peripheral_input_slots[i].conn = NULL;
|
||||
// memset(&peripheral_input_slots[i], 0, sizeof(struct peripheral_input_slot));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#endif // IS_ENABLED(CONFIG_ZMK_INPUT_SPLIT)
|
||||
|
||||
static struct peripheral_slot peripherals[ZMK_SPLIT_BLE_PERIPHERAL_COUNT];
|
||||
|
||||
static bool is_scanning = false;
|
||||
@@ -230,6 +296,65 @@ static uint8_t split_central_sensor_notify_func(struct bt_conn *conn,
|
||||
}
|
||||
#endif /* ZMK_KEYMAP_HAS_SENSORS */
|
||||
|
||||
#if IS_ENABLED(CONFIG_ZMK_INPUT_SPLIT)
|
||||
|
||||
struct zmk_input_event_msg {
|
||||
uint8_t reg;
|
||||
struct zmk_split_input_event_payload payload;
|
||||
};
|
||||
|
||||
K_MSGQ_DEFINE(peripheral_input_event_msgq, sizeof(struct zmk_input_event_msg), 5, 4);
|
||||
// CONFIG_ZMK_SPLIT_BLE_CENTRAL_INPUT_QUEUE_SIZE, 4);
|
||||
|
||||
void peripheral_input_event_work_callback(struct k_work *work) {
|
||||
struct zmk_input_event_msg msg;
|
||||
while (k_msgq_get(&peripheral_input_event_msgq, &msg, K_NO_WAIT) == 0) {
|
||||
int ret = zmk_input_split_report_peripheral_event(
|
||||
msg.reg, msg.payload.type, msg.payload.code, msg.payload.value, msg.payload.sync);
|
||||
if (ret < 0) {
|
||||
LOG_WRN("Failed to report peripheral event %d", ret);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
K_WORK_DEFINE(input_event_work, peripheral_input_event_work_callback);
|
||||
|
||||
static uint8_t peripheral_input_event_notify_cb(struct bt_conn *conn,
|
||||
struct bt_gatt_subscribe_params *params,
|
||||
const void *data, uint16_t length) {
|
||||
if (!data) {
|
||||
LOG_DBG("[UNSUBSCRIBED]");
|
||||
params->value_handle = 0U;
|
||||
return BT_GATT_ITER_STOP;
|
||||
}
|
||||
|
||||
LOG_DBG("[INPUT EVENT] data %p length %u", data, length);
|
||||
|
||||
if (length != sizeof(struct zmk_split_input_event_payload)) {
|
||||
LOG_WRN("Ignoring input event notify with incorrect data length (%d)", length);
|
||||
return BT_GATT_ITER_STOP;
|
||||
}
|
||||
|
||||
struct zmk_input_event_msg msg;
|
||||
|
||||
memcpy(&msg.payload, data, MIN(length, sizeof(struct zmk_split_input_event_payload)));
|
||||
|
||||
LOG_DBG("Got an input event with type %d, code %d, value %d, sync %d", msg.payload.type,
|
||||
msg.payload.code, msg.payload.value, msg.payload.sync);
|
||||
|
||||
for (size_t i = 0; i < ARRAY_SIZE(peripheral_input_slots); i++) {
|
||||
if (&peripheral_input_slots[i].sub == params) {
|
||||
msg.reg = peripheral_input_slots[i].reg;
|
||||
k_msgq_put(&peripheral_input_event_msgq, &msg, K_NO_WAIT);
|
||||
k_work_submit(&input_event_work);
|
||||
}
|
||||
}
|
||||
|
||||
return BT_GATT_ITER_CONTINUE;
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
static uint8_t split_central_notify_func(struct bt_conn *conn,
|
||||
struct bt_gatt_subscribe_params *params, const void *data,
|
||||
uint16_t length) {
|
||||
@@ -379,6 +504,7 @@ static uint8_t split_central_battery_level_read_func(struct bt_conn *conn, uint8
|
||||
#endif /* IS_ENABLED(CONFIG_ZMK_SPLIT_BLE_CENTRAL_BATTERY_LEVEL_FETCHING) */
|
||||
|
||||
static int split_central_subscribe(struct bt_conn *conn, struct bt_gatt_subscribe_params *params) {
|
||||
atomic_set(params->flags, BT_GATT_SUBSCRIBE_FLAG_NO_RESUB);
|
||||
int err = bt_gatt_subscribe(conn, params);
|
||||
switch (err) {
|
||||
case -EALREADY:
|
||||
@@ -455,64 +581,133 @@ static uint8_t split_central_chrc_discovery_func(struct bt_conn *conn,
|
||||
}
|
||||
|
||||
LOG_DBG("[ATTRIBUTE] handle %u", attr->handle);
|
||||
const struct bt_uuid *chrc_uuid = ((struct bt_gatt_chrc *)attr->user_data)->uuid;
|
||||
switch (params->type) {
|
||||
case BT_GATT_DISCOVER_CHARACTERISTIC:
|
||||
const struct bt_uuid *chrc_uuid = ((struct bt_gatt_chrc *)attr->user_data)->uuid;
|
||||
|
||||
if (bt_uuid_cmp(chrc_uuid, BT_UUID_DECLARE_128(ZMK_SPLIT_BT_CHAR_POSITION_STATE_UUID)) == 0) {
|
||||
LOG_DBG("Found position state characteristic");
|
||||
slot->subscribe_params.disc_params = &slot->sub_discover_params;
|
||||
slot->subscribe_params.end_handle = slot->discover_params.end_handle;
|
||||
slot->subscribe_params.value_handle = bt_gatt_attr_value_handle(attr);
|
||||
slot->subscribe_params.notify = split_central_notify_func;
|
||||
slot->subscribe_params.value = BT_GATT_CCC_NOTIFY;
|
||||
split_central_subscribe(conn, &slot->subscribe_params);
|
||||
if (bt_uuid_cmp(chrc_uuid, BT_UUID_DECLARE_128(ZMK_SPLIT_BT_CHAR_POSITION_STATE_UUID)) ==
|
||||
0) {
|
||||
LOG_DBG("Found position state characteristic");
|
||||
slot->subscribe_params.disc_params = &slot->sub_discover_params;
|
||||
slot->subscribe_params.end_handle = slot->discover_params.end_handle;
|
||||
slot->subscribe_params.value_handle = bt_gatt_attr_value_handle(attr);
|
||||
slot->subscribe_params.notify = split_central_notify_func;
|
||||
slot->subscribe_params.value = BT_GATT_CCC_NOTIFY;
|
||||
split_central_subscribe(conn, &slot->subscribe_params);
|
||||
#if ZMK_KEYMAP_HAS_SENSORS
|
||||
} else if (bt_uuid_cmp(chrc_uuid, BT_UUID_DECLARE_128(ZMK_SPLIT_BT_CHAR_SENSOR_STATE_UUID)) ==
|
||||
0) {
|
||||
slot->discover_params.uuid = NULL;
|
||||
slot->discover_params.start_handle = attr->handle + 2;
|
||||
slot->discover_params.type = BT_GATT_DISCOVER_CHARACTERISTIC;
|
||||
} else if (bt_uuid_cmp(chrc_uuid,
|
||||
BT_UUID_DECLARE_128(ZMK_SPLIT_BT_CHAR_SENSOR_STATE_UUID)) == 0) {
|
||||
slot->discover_params.uuid = NULL;
|
||||
slot->discover_params.start_handle = attr->handle + 2;
|
||||
slot->discover_params.type = BT_GATT_DISCOVER_CHARACTERISTIC;
|
||||
|
||||
slot->sensor_subscribe_params.disc_params = &slot->sub_discover_params;
|
||||
slot->sensor_subscribe_params.end_handle = slot->discover_params.end_handle;
|
||||
slot->sensor_subscribe_params.value_handle = bt_gatt_attr_value_handle(attr);
|
||||
slot->sensor_subscribe_params.notify = split_central_sensor_notify_func;
|
||||
slot->sensor_subscribe_params.value = BT_GATT_CCC_NOTIFY;
|
||||
split_central_subscribe(conn, &slot->sensor_subscribe_params);
|
||||
slot->sensor_subscribe_params.disc_params = &slot->sub_discover_params;
|
||||
slot->sensor_subscribe_params.end_handle = slot->discover_params.end_handle;
|
||||
slot->sensor_subscribe_params.value_handle = bt_gatt_attr_value_handle(attr);
|
||||
slot->sensor_subscribe_params.notify = split_central_sensor_notify_func;
|
||||
slot->sensor_subscribe_params.value = BT_GATT_CCC_NOTIFY;
|
||||
split_central_subscribe(conn, &slot->sensor_subscribe_params);
|
||||
#endif /* ZMK_KEYMAP_HAS_SENSORS */
|
||||
} else if (bt_uuid_cmp(chrc_uuid, BT_UUID_DECLARE_128(ZMK_SPLIT_BT_CHAR_RUN_BEHAVIOR_UUID)) ==
|
||||
0) {
|
||||
LOG_DBG("Found run behavior handle");
|
||||
slot->discover_params.uuid = NULL;
|
||||
slot->discover_params.start_handle = attr->handle + 2;
|
||||
slot->run_behavior_handle = bt_gatt_attr_value_handle(attr);
|
||||
} else if (!bt_uuid_cmp(((struct bt_gatt_chrc *)attr->user_data)->uuid,
|
||||
BT_UUID_DECLARE_128(ZMK_SPLIT_BT_SELECT_PHYS_LAYOUT_UUID))) {
|
||||
LOG_DBG("Found select physical layout handle");
|
||||
slot->selected_physical_layout_handle = bt_gatt_attr_value_handle(attr);
|
||||
k_work_submit(&update_peripherals_selected_layouts_work);
|
||||
#if IS_ENABLED(CONFIG_ZMK_INPUT_SPLIT)
|
||||
} else if (bt_uuid_cmp(chrc_uuid, BT_UUID_DECLARE_128(ZMK_SPLIT_BT_INPUT_EVENT_UUID)) ==
|
||||
0) {
|
||||
LOG_DBG("Found an input characteristic");
|
||||
struct peripheral_input_slot *input_slot;
|
||||
int ret = reserve_next_open_input_slot(&input_slot, conn);
|
||||
if (ret < 0) {
|
||||
LOG_WRN("No available slot for peripheral input subscriptions (%d)", ret);
|
||||
|
||||
slot->discover_params.uuid = NULL;
|
||||
slot->discover_params.start_handle = attr->handle + 1;
|
||||
slot->discover_params.type = BT_GATT_DISCOVER_CHARACTERISTIC;
|
||||
} else {
|
||||
LOG_DBG("Reserved a slot for the input subscription");
|
||||
input_slot->sub.value_handle = bt_gatt_attr_value_handle(attr);
|
||||
|
||||
slot->discover_params.uuid = gatt_ccc_uuid;
|
||||
slot->discover_params.start_handle = attr->handle;
|
||||
slot->discover_params.type = BT_GATT_DISCOVER_STD_CHAR_DESC;
|
||||
}
|
||||
#endif // IS_ENABLED(CONFIG_ZMK_INPUT_SPLIT)
|
||||
} else if (bt_uuid_cmp(chrc_uuid,
|
||||
BT_UUID_DECLARE_128(ZMK_SPLIT_BT_CHAR_RUN_BEHAVIOR_UUID)) == 0) {
|
||||
LOG_DBG("Found run behavior handle");
|
||||
slot->discover_params.uuid = NULL;
|
||||
slot->discover_params.start_handle = attr->handle + 2;
|
||||
slot->run_behavior_handle = bt_gatt_attr_value_handle(attr);
|
||||
} else if (!bt_uuid_cmp(((struct bt_gatt_chrc *)attr->user_data)->uuid,
|
||||
BT_UUID_DECLARE_128(ZMK_SPLIT_BT_SELECT_PHYS_LAYOUT_UUID))) {
|
||||
LOG_DBG("Found select physical layout handle");
|
||||
slot->selected_physical_layout_handle = bt_gatt_attr_value_handle(attr);
|
||||
k_work_submit(&update_peripherals_selected_layouts_work);
|
||||
#if IS_ENABLED(CONFIG_ZMK_SPLIT_PERIPHERAL_HID_INDICATORS)
|
||||
} else if (!bt_uuid_cmp(((struct bt_gatt_chrc *)attr->user_data)->uuid,
|
||||
BT_UUID_DECLARE_128(ZMK_SPLIT_BT_UPDATE_HID_INDICATORS_UUID))) {
|
||||
LOG_DBG("Found update HID indicators handle");
|
||||
slot->update_hid_indicators = bt_gatt_attr_value_handle(attr);
|
||||
} else if (!bt_uuid_cmp(((struct bt_gatt_chrc *)attr->user_data)->uuid,
|
||||
BT_UUID_DECLARE_128(ZMK_SPLIT_BT_UPDATE_HID_INDICATORS_UUID))) {
|
||||
LOG_DBG("Found update HID indicators handle");
|
||||
slot->update_hid_indicators = bt_gatt_attr_value_handle(attr);
|
||||
#endif // IS_ENABLED(CONFIG_ZMK_SPLIT_PERIPHERAL_HID_INDICATORS)
|
||||
#if IS_ENABLED(CONFIG_ZMK_SPLIT_BLE_CENTRAL_BATTERY_LEVEL_FETCHING)
|
||||
} else if (!bt_uuid_cmp(((struct bt_gatt_chrc *)attr->user_data)->uuid,
|
||||
BT_UUID_BAS_BATTERY_LEVEL)) {
|
||||
LOG_DBG("Found battery level characteristics");
|
||||
slot->batt_lvl_subscribe_params.disc_params = &slot->sub_discover_params;
|
||||
slot->batt_lvl_subscribe_params.end_handle = slot->discover_params.end_handle;
|
||||
slot->batt_lvl_subscribe_params.value_handle = bt_gatt_attr_value_handle(attr);
|
||||
slot->batt_lvl_subscribe_params.notify = split_central_battery_level_notify_func;
|
||||
slot->batt_lvl_subscribe_params.value = BT_GATT_CCC_NOTIFY;
|
||||
split_central_subscribe(conn, &slot->batt_lvl_subscribe_params);
|
||||
} else if (!bt_uuid_cmp(((struct bt_gatt_chrc *)attr->user_data)->uuid,
|
||||
BT_UUID_BAS_BATTERY_LEVEL)) {
|
||||
LOG_DBG("Found battery level characteristics");
|
||||
slot->batt_lvl_subscribe_params.disc_params = &slot->sub_discover_params;
|
||||
slot->batt_lvl_subscribe_params.end_handle = slot->discover_params.end_handle;
|
||||
slot->batt_lvl_subscribe_params.value_handle = bt_gatt_attr_value_handle(attr);
|
||||
slot->batt_lvl_subscribe_params.notify = split_central_battery_level_notify_func;
|
||||
slot->batt_lvl_subscribe_params.value = BT_GATT_CCC_NOTIFY;
|
||||
split_central_subscribe(conn, &slot->batt_lvl_subscribe_params);
|
||||
|
||||
slot->batt_lvl_read_params.func = split_central_battery_level_read_func;
|
||||
slot->batt_lvl_read_params.handle_count = 1;
|
||||
slot->batt_lvl_read_params.single.handle = bt_gatt_attr_value_handle(attr);
|
||||
slot->batt_lvl_read_params.single.offset = 0;
|
||||
bt_gatt_read(conn, &slot->batt_lvl_read_params);
|
||||
slot->batt_lvl_read_params.func = split_central_battery_level_read_func;
|
||||
slot->batt_lvl_read_params.handle_count = 1;
|
||||
slot->batt_lvl_read_params.single.handle = bt_gatt_attr_value_handle(attr);
|
||||
slot->batt_lvl_read_params.single.offset = 0;
|
||||
bt_gatt_read(conn, &slot->batt_lvl_read_params);
|
||||
#endif /* IS_ENABLED(CONFIG_ZMK_SPLIT_BLE_CENTRAL_BATTERY_LEVEL_FETCHING) */
|
||||
}
|
||||
break;
|
||||
case BT_GATT_DISCOVER_STD_CHAR_DESC:
|
||||
#if IS_ENABLED(CONFIG_ZMK_INPUT_SPLIT)
|
||||
if (bt_uuid_cmp(slot->discover_params.uuid, BT_UUID_GATT_CCC) == 0) {
|
||||
LOG_DBG("Found input CCC descriptor");
|
||||
struct peripheral_input_slot *input_slot;
|
||||
int ret = find_pending_input_slot(&input_slot, conn);
|
||||
if (ret < 0) {
|
||||
LOG_DBG("No pending input slot (%d)", ret);
|
||||
slot->discover_params.uuid = NULL;
|
||||
slot->discover_params.start_handle = attr->handle + 1;
|
||||
slot->discover_params.type = BT_GATT_DISCOVER_CHARACTERISTIC;
|
||||
} else {
|
||||
LOG_DBG("Found pending input slot");
|
||||
input_slot->sub.ccc_handle = attr->handle;
|
||||
|
||||
slot->discover_params.uuid = gatt_cpf_uuid;
|
||||
slot->discover_params.start_handle = attr->handle + 1;
|
||||
slot->discover_params.type = BT_GATT_DISCOVER_STD_CHAR_DESC;
|
||||
}
|
||||
} else if (bt_uuid_cmp(slot->discover_params.uuid, BT_UUID_GATT_CPF) == 0) {
|
||||
LOG_DBG("Found input CPF descriptor");
|
||||
struct bt_gatt_cpf *cpf = attr->user_data;
|
||||
struct peripheral_input_slot *input_slot;
|
||||
int ret = find_pending_input_slot(&input_slot, conn);
|
||||
if (ret < 0) {
|
||||
LOG_DBG("No pending input slot (%d)", ret);
|
||||
} else {
|
||||
LOG_DBG("Found pending input slot");
|
||||
input_slot->reg = cpf->description;
|
||||
input_slot->sub.notify = peripheral_input_event_notify_cb;
|
||||
input_slot->sub.value = BT_GATT_CCC_NOTIFY;
|
||||
int err = split_central_subscribe(conn, &input_slot->sub);
|
||||
if (err < 0) {
|
||||
LOG_WRN("Failed to subscribe to input notifications %d", err);
|
||||
}
|
||||
}
|
||||
|
||||
slot->discover_params.uuid = NULL;
|
||||
slot->discover_params.start_handle = attr->handle + 1;
|
||||
slot->discover_params.type = BT_GATT_DISCOVER_CHARACTERISTIC;
|
||||
}
|
||||
#endif // IS_ENABLED(CONFIG_ZMK_INPUT_SPLIT)
|
||||
break;
|
||||
}
|
||||
|
||||
bool subscribed = slot->run_behavior_handle && slot->subscribe_params.value_handle &&
|
||||
@@ -528,6 +723,14 @@ static uint8_t split_central_chrc_discovery_func(struct bt_conn *conn,
|
||||
#if IS_ENABLED(CONFIG_ZMK_SPLIT_BLE_CENTRAL_BATTERY_LEVEL_FETCHING)
|
||||
subscribed = subscribed && slot->batt_lvl_subscribe_params.value_handle;
|
||||
#endif /* IS_ENABLED(CONFIG_ZMK_SPLIT_BLE_CENTRAL_BATTERY_LEVEL_FETCHING) */
|
||||
#if IS_ENABLED(CONFIG_ZMK_INPUT_SPLIT)
|
||||
for (size_t i = 0; i < ARRAY_SIZE(peripheral_input_slots); i++) {
|
||||
if (input_slot_is_open(i) || input_slot_is_pending(i)) {
|
||||
subscribed = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
#endif // IS_ENABLED(CONFIG_ZMK_INPUT_SPLIT)
|
||||
|
||||
return subscribed ? BT_GATT_ITER_STOP : BT_GATT_ITER_CONTINUE;
|
||||
}
|
||||
@@ -779,6 +982,10 @@ static void split_central_disconnected(struct bt_conn *conn, uint8_t reason) {
|
||||
k_work_submit(&peripheral_batt_lvl_work);
|
||||
#endif // IS_ENABLED(CONFIG_ZMK_SPLIT_BLE_CENTRAL_BATTERY_LEVEL_FETCHING)
|
||||
|
||||
#if IS_ENABLED(CONFIG_ZMK_INPUT_SPLIT)
|
||||
release_peripheral_input_subs(conn);
|
||||
#endif
|
||||
|
||||
err = release_peripheral_slot_for_conn(conn);
|
||||
|
||||
if (err < 0) {
|
||||
|
||||
@@ -7,6 +7,7 @@
|
||||
#include <zephyr/drivers/sensor.h>
|
||||
#include <zephyr/types.h>
|
||||
#include <zephyr/sys/util.h>
|
||||
#include <zephyr/sys/byteorder.h>
|
||||
#include <zephyr/init.h>
|
||||
|
||||
#include <zephyr/logging/log.h>
|
||||
@@ -175,6 +176,45 @@ static ssize_t split_svc_get_selected_phys_layout(struct bt_conn *conn,
|
||||
return bt_gatt_attr_read(conn, attrs, buf, len, offset, &selected, sizeof(selected));
|
||||
}
|
||||
|
||||
#if IS_ENABLED(CONFIG_ZMK_INPUT_SPLIT)
|
||||
|
||||
static void split_input_events_ccc(const struct bt_gatt_attr *attr, uint16_t value) {
|
||||
LOG_DBG("value %d", value);
|
||||
}
|
||||
|
||||
// Duplicated from Zephyr, since it is internal there
|
||||
struct gatt_cpf {
|
||||
uint8_t format;
|
||||
int8_t exponent;
|
||||
uint16_t unit;
|
||||
uint8_t name_space;
|
||||
uint16_t description;
|
||||
} __packed;
|
||||
|
||||
ssize_t bt_gatt_attr_read_input_split_cpf(struct bt_conn *conn, const struct bt_gatt_attr *attr,
|
||||
void *buf, uint16_t len, uint16_t offset) {
|
||||
uint16_t reg = (uint16_t)(uint32_t)attr->user_data;
|
||||
struct gatt_cpf value;
|
||||
|
||||
value.format = 0x1B; // Struct
|
||||
value.exponent = 0;
|
||||
value.unit = sys_cpu_to_le16(0x2700); // Unitless
|
||||
value.name_space = 0x01; // Bluetooth SIG
|
||||
value.description = sys_cpu_to_le16(reg);
|
||||
|
||||
return bt_gatt_attr_read(conn, attr, buf, len, offset, &value, sizeof(value));
|
||||
}
|
||||
|
||||
#define INPUT_SPLIT_CHARS(node_id) \
|
||||
BT_GATT_CHARACTERISTIC(BT_UUID_DECLARE_128(ZMK_SPLIT_BT_INPUT_EVENT_UUID), \
|
||||
BT_GATT_CHRC_NOTIFY, BT_GATT_PERM_READ_ENCRYPT, NULL, NULL, NULL), \
|
||||
BT_GATT_CCC(split_input_events_ccc, \
|
||||
BT_GATT_PERM_READ_ENCRYPT | BT_GATT_PERM_WRITE_ENCRYPT), \
|
||||
BT_GATT_DESCRIPTOR(BT_UUID_GATT_CPF, BT_GATT_PERM_READ, bt_gatt_attr_read_input_split_cpf, \
|
||||
NULL, (void *)DT_REG_ADDR(node_id)),
|
||||
|
||||
#endif
|
||||
|
||||
BT_GATT_SERVICE_DEFINE(
|
||||
split_svc, BT_GATT_PRIMARY_SERVICE(BT_UUID_DECLARE_128(ZMK_SPLIT_BT_SERVICE_UUID)),
|
||||
BT_GATT_CHARACTERISTIC(BT_UUID_DECLARE_128(ZMK_SPLIT_BT_CHAR_POSITION_STATE_UUID),
|
||||
@@ -192,10 +232,11 @@ BT_GATT_SERVICE_DEFINE(
|
||||
split_svc_sensor_state, NULL, &last_sensor_event),
|
||||
BT_GATT_CCC(split_svc_sensor_state_ccc, BT_GATT_PERM_READ_ENCRYPT | BT_GATT_PERM_WRITE_ENCRYPT),
|
||||
#endif /* ZMK_KEYMAP_HAS_SENSORS */
|
||||
DT_FOREACH_STATUS_OKAY(zmk_input_split, INPUT_SPLIT_CHARS)
|
||||
#if IS_ENABLED(CONFIG_ZMK_SPLIT_PERIPHERAL_HID_INDICATORS)
|
||||
BT_GATT_CHARACTERISTIC(BT_UUID_DECLARE_128(ZMK_SPLIT_BT_UPDATE_HID_INDICATORS_UUID),
|
||||
BT_GATT_CHRC_WRITE_WITHOUT_RESP, BT_GATT_PERM_WRITE_ENCRYPT, NULL,
|
||||
split_svc_update_indicators, NULL),
|
||||
BT_GATT_CHARACTERISTIC(BT_UUID_DECLARE_128(ZMK_SPLIT_BT_UPDATE_HID_INDICATORS_UUID),
|
||||
BT_GATT_CHRC_WRITE_WITHOUT_RESP, BT_GATT_PERM_WRITE_ENCRYPT, NULL,
|
||||
split_svc_update_indicators, NULL),
|
||||
#endif // IS_ENABLED(CONFIG_ZMK_SPLIT_PERIPHERAL_HID_INDICATORS)
|
||||
BT_GATT_CHARACTERISTIC(BT_UUID_DECLARE_128(ZMK_SPLIT_BT_SELECT_PHYS_LAYOUT_UUID),
|
||||
BT_GATT_CHRC_WRITE | BT_GATT_CHRC_READ,
|
||||
@@ -306,6 +347,29 @@ int zmk_split_bt_sensor_triggered(uint8_t sensor_index,
|
||||
}
|
||||
#endif /* ZMK_KEYMAP_HAS_SENSORS */
|
||||
|
||||
#if IS_ENABLED(CONFIG_ZMK_INPUT_SPLIT)
|
||||
|
||||
int zmk_split_bt_report_input(uint8_t reg, uint8_t type, uint16_t code, int32_t value, bool sync) {
|
||||
|
||||
for (size_t i = 0; i < split_svc.attr_count; i++) {
|
||||
if (bt_uuid_cmp(split_svc.attrs[i].uuid,
|
||||
BT_UUID_DECLARE_128(ZMK_SPLIT_BT_INPUT_EVENT_UUID)) == 0 &&
|
||||
(uint8_t)(uint32_t)split_svc.attrs[i + 2].user_data == reg) {
|
||||
struct zmk_split_input_event_payload payload = {
|
||||
.type = type,
|
||||
.code = code,
|
||||
.value = value,
|
||||
.sync = sync ? 1 : 0,
|
||||
};
|
||||
|
||||
return bt_gatt_notify(NULL, &split_svc.attrs[i], &payload, sizeof(payload));
|
||||
}
|
||||
}
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
#endif /* IS_ENABLED(CONFIG_ZMK_INPUT_SPLIT) */
|
||||
|
||||
static int service_init(void) {
|
||||
static const struct k_work_queue_config queue_config = {
|
||||
.name = "Split Peripheral Notification Queue"};
|
||||
|
||||
@@ -13,9 +13,15 @@
|
||||
#include <zmk/usb.h>
|
||||
#include <zmk/hid.h>
|
||||
#include <zmk/keymap.h>
|
||||
|
||||
#if IS_ENABLED(CONFIG_ZMK_POINTING_SMOOTH_SCROLLING)
|
||||
#include <zmk/pointing/resolution_multipliers.h>
|
||||
#endif // IS_ENABLED(CONFIG_ZMK_POINTING_SMOOTH_SCROLLING)
|
||||
|
||||
#if IS_ENABLED(CONFIG_ZMK_HID_INDICATORS)
|
||||
#include <zmk/hid_indicators.h>
|
||||
#endif // IS_ENABLED(CONFIG_ZMK_HID_INDICATORS)
|
||||
|
||||
#include <zmk/event_manager.h>
|
||||
|
||||
LOG_MODULE_DECLARE(zmk, CONFIG_ZMK_LOG_LEVEL);
|
||||
@@ -56,62 +62,112 @@ static uint8_t *get_keyboard_report(size_t *len) {
|
||||
|
||||
static int get_report_cb(const struct device *dev, struct usb_setup_packet *setup, int32_t *len,
|
||||
uint8_t **data) {
|
||||
switch (setup->wValue & HID_GET_REPORT_TYPE_MASK) {
|
||||
case HID_REPORT_TYPE_FEATURE:
|
||||
switch (setup->wValue & HID_GET_REPORT_ID_MASK) {
|
||||
#if IS_ENABLED(CONFIG_ZMK_POINTING_SMOOTH_SCROLLING)
|
||||
case ZMK_HID_REPORT_ID_MOUSE:
|
||||
static struct zmk_hid_mouse_resolution_feature_report res_feature_report;
|
||||
|
||||
/*
|
||||
* 7.2.1 of the HID v1.11 spec is unclear about handling requests for reports that do not exist
|
||||
* For requested reports that aren't input reports, return -ENOTSUP like the Zephyr subsys does
|
||||
*/
|
||||
if ((setup->wValue & HID_GET_REPORT_TYPE_MASK) != HID_REPORT_TYPE_INPUT) {
|
||||
struct zmk_endpoint_instance endpoint = {
|
||||
.transport = ZMK_TRANSPORT_USB,
|
||||
};
|
||||
|
||||
*len = sizeof(struct zmk_hid_mouse_resolution_feature_report);
|
||||
struct zmk_pointing_resolution_multipliers mult =
|
||||
zmk_pointing_resolution_multipliers_get_profile(endpoint);
|
||||
|
||||
res_feature_report.body.wheel_res = mult.wheel;
|
||||
res_feature_report.body.hwheel_res = mult.hor_wheel;
|
||||
*data = (uint8_t *)&res_feature_report;
|
||||
break;
|
||||
#endif // IS_ENABLED(CONFIG_ZMK_POINTING_SMOOTH_SCROLLING)
|
||||
default:
|
||||
return -ENOTSUP;
|
||||
}
|
||||
break;
|
||||
case HID_REPORT_TYPE_INPUT:
|
||||
switch (setup->wValue & HID_GET_REPORT_ID_MASK) {
|
||||
case ZMK_HID_REPORT_ID_KEYBOARD: {
|
||||
*data = get_keyboard_report(len);
|
||||
break;
|
||||
}
|
||||
case ZMK_HID_REPORT_ID_CONSUMER: {
|
||||
struct zmk_hid_consumer_report *report = zmk_hid_get_consumer_report();
|
||||
*data = (uint8_t *)report;
|
||||
*len = sizeof(*report);
|
||||
break;
|
||||
}
|
||||
default:
|
||||
LOG_ERR("Invalid report ID %d requested", setup->wValue & HID_GET_REPORT_ID_MASK);
|
||||
return -EINVAL;
|
||||
}
|
||||
break;
|
||||
default:
|
||||
/*
|
||||
* 7.2.1 of the HID v1.11 spec is unclear about handling requests for reports that do not
|
||||
* exist For requested reports that aren't input reports, return -ENOTSUP like the Zephyr
|
||||
* subsys does
|
||||
*/
|
||||
LOG_ERR("Unsupported report type %d requested", (setup->wValue & HID_GET_REPORT_TYPE_MASK)
|
||||
<< 8);
|
||||
return -ENOTSUP;
|
||||
}
|
||||
|
||||
switch (setup->wValue & HID_GET_REPORT_ID_MASK) {
|
||||
case ZMK_HID_REPORT_ID_KEYBOARD: {
|
||||
*data = get_keyboard_report(len);
|
||||
break;
|
||||
}
|
||||
case ZMK_HID_REPORT_ID_CONSUMER: {
|
||||
struct zmk_hid_consumer_report *report = zmk_hid_get_consumer_report();
|
||||
*data = (uint8_t *)report;
|
||||
*len = sizeof(*report);
|
||||
break;
|
||||
}
|
||||
default:
|
||||
LOG_ERR("Invalid report ID %d requested", setup->wValue & HID_GET_REPORT_ID_MASK);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int set_report_cb(const struct device *dev, struct usb_setup_packet *setup, int32_t *len,
|
||||
uint8_t **data) {
|
||||
if ((setup->wValue & HID_GET_REPORT_TYPE_MASK) != HID_REPORT_TYPE_OUTPUT) {
|
||||
LOG_ERR("Unsupported report type %d requested",
|
||||
(setup->wValue & HID_GET_REPORT_TYPE_MASK) >> 8);
|
||||
return -ENOTSUP;
|
||||
}
|
||||
switch (setup->wValue & HID_GET_REPORT_TYPE_MASK) {
|
||||
case HID_REPORT_TYPE_FEATURE:
|
||||
switch (setup->wValue & HID_GET_REPORT_ID_MASK) {
|
||||
#if IS_ENABLED(CONFIG_ZMK_POINTING_SMOOTH_SCROLLING)
|
||||
case ZMK_HID_REPORT_ID_MOUSE:
|
||||
if (*len != sizeof(struct zmk_hid_mouse_resolution_feature_report)) {
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
switch (setup->wValue & HID_GET_REPORT_ID_MASK) {
|
||||
#if IS_ENABLED(CONFIG_ZMK_HID_INDICATORS)
|
||||
case ZMK_HID_REPORT_ID_LEDS:
|
||||
if (*len != sizeof(struct zmk_hid_led_report)) {
|
||||
LOG_ERR("LED set report is malformed: length=%d", *len);
|
||||
return -EINVAL;
|
||||
} else {
|
||||
struct zmk_hid_led_report *report = (struct zmk_hid_led_report *)*data;
|
||||
struct zmk_hid_mouse_resolution_feature_report *report =
|
||||
(struct zmk_hid_mouse_resolution_feature_report *)*data;
|
||||
struct zmk_endpoint_instance endpoint = {
|
||||
.transport = ZMK_TRANSPORT_USB,
|
||||
};
|
||||
zmk_hid_indicators_process_report(&report->body, endpoint);
|
||||
|
||||
zmk_pointing_resolution_multipliers_process_report(&report->body, endpoint);
|
||||
|
||||
break;
|
||||
#endif // IS_ENABLED(CONFIG_ZMK_POINTING_SMOOTH_SCROLLING)
|
||||
default:
|
||||
return -ENOTSUP;
|
||||
}
|
||||
break;
|
||||
|
||||
case HID_REPORT_TYPE_OUTPUT:
|
||||
switch (setup->wValue & HID_GET_REPORT_ID_MASK) {
|
||||
#if IS_ENABLED(CONFIG_ZMK_HID_INDICATORS)
|
||||
case ZMK_HID_REPORT_ID_LEDS:
|
||||
if (*len != sizeof(struct zmk_hid_led_report)) {
|
||||
LOG_ERR("LED set report is malformed: length=%d", *len);
|
||||
return -EINVAL;
|
||||
} else {
|
||||
struct zmk_hid_led_report *report = (struct zmk_hid_led_report *)*data;
|
||||
struct zmk_endpoint_instance endpoint = {
|
||||
.transport = ZMK_TRANSPORT_USB,
|
||||
};
|
||||
zmk_hid_indicators_process_report(&report->body, endpoint);
|
||||
}
|
||||
break;
|
||||
#endif // IS_ENABLED(CONFIG_ZMK_HID_INDICATORS)
|
||||
default:
|
||||
LOG_ERR("Invalid report ID %d requested", setup->wValue & HID_GET_REPORT_ID_MASK);
|
||||
return -EINVAL;
|
||||
}
|
||||
break;
|
||||
default:
|
||||
LOG_ERR("Invalid report ID %d requested", setup->wValue & HID_GET_REPORT_ID_MASK);
|
||||
return -EINVAL;
|
||||
LOG_ERR("Unsupported report type %d requested",
|
||||
(setup->wValue & HID_GET_REPORT_TYPE_MASK) >> 8);
|
||||
return -ENOTSUP;
|
||||
}
|
||||
|
||||
return 0;
|
||||
@@ -164,7 +220,7 @@ int zmk_usb_hid_send_consumer_report(void) {
|
||||
return zmk_usb_hid_send_report((uint8_t *)report, sizeof(*report));
|
||||
}
|
||||
|
||||
#if IS_ENABLED(CONFIG_ZMK_MOUSE)
|
||||
#if IS_ENABLED(CONFIG_ZMK_POINTING)
|
||||
int zmk_usb_hid_send_mouse_report() {
|
||||
#if IS_ENABLED(CONFIG_ZMK_USB_BOOT)
|
||||
if (hid_protocol == HID_PROTOCOL_BOOT) {
|
||||
@@ -175,7 +231,7 @@ int zmk_usb_hid_send_mouse_report() {
|
||||
struct zmk_hid_mouse_report *report = zmk_hid_get_mouse_report();
|
||||
return zmk_usb_hid_send_report((uint8_t *)report, sizeof(*report));
|
||||
}
|
||||
#endif // IS_ENABLED(CONFIG_ZMK_MOUSE)
|
||||
#endif // IS_ENABLED(CONFIG_ZMK_POINTING)
|
||||
|
||||
static int zmk_usb_hid_init(void) {
|
||||
hid_dev = device_get_binding("HID_0");
|
||||
|
||||
6
app/tests/pointing/mkp/native_posix_64.conf
Normal file
6
app/tests/pointing/mkp/native_posix_64.conf
Normal file
@@ -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
|
||||
@@ -0,0 +1 @@
|
||||
s/.*hid_mouse_//p
|
||||
@@ -0,0 +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
|
||||
scroll_set: Mouse scroll set to 0/0
|
||||
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
|
||||
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
|
||||
scroll_set: Mouse scroll set to 0/0
|
||||
movement_set: Mouse movement set to 0/0
|
||||
@@ -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_POINTING=y
|
||||
@@ -0,0 +1,29 @@
|
||||
#include <behaviors.dtsi>
|
||||
#include <behaviors/mouse_move.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 &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)
|
||||
>;
|
||||
};
|
||||
1
app/tests/pointing/mouse-move/move_x/events.patterns
Normal file
1
app/tests/pointing/mouse-move/move_x/events.patterns
Normal file
@@ -0,0 +1 @@
|
||||
s/.*hid_mouse_//p
|
||||
24
app/tests/pointing/mouse-move/move_x/keycode_events.snapshot
Normal file
24
app/tests/pointing/mouse-move/move_x/keycode_events.snapshot
Normal file
@@ -0,0 +1,24 @@
|
||||
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/0
|
||||
scroll_set: Mouse scroll set to 0/0
|
||||
movement_set: Mouse movement set to 0/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_set: Mouse movement set to -3/0
|
||||
scroll_set: Mouse scroll set to 0/0
|
||||
movement_set: Mouse movement set to 0/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/0
|
||||
scroll_set: Mouse scroll set to 0/0
|
||||
movement_set: Mouse movement set to 0/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_set: Mouse movement set to 3/0
|
||||
scroll_set: Mouse scroll set to 0/0
|
||||
movement_set: Mouse movement set to 0/0
|
||||
@@ -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_POINTING=y
|
||||
29
app/tests/pointing/mouse-move/move_x/native_posix_64.keymap
Normal file
29
app/tests/pointing/mouse-move/move_x/native_posix_64.keymap
Normal file
@@ -0,0 +1,29 @@
|
||||
#include <behaviors.dtsi>
|
||||
#include <behaviors/mouse_move.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 &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)
|
||||
>;
|
||||
};
|
||||
1
app/tests/pointing/mouse-move/move_y/events.patterns
Normal file
1
app/tests/pointing/mouse-move/move_y/events.patterns
Normal file
@@ -0,0 +1 @@
|
||||
s/.*hid_mouse_//p
|
||||
24
app/tests/pointing/mouse-move/move_y/keycode_events.snapshot
Normal file
24
app/tests/pointing/mouse-move/move_y/keycode_events.snapshot
Normal file
@@ -0,0 +1,24 @@
|
||||
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 0/-2
|
||||
scroll_set: Mouse scroll set to 0/0
|
||||
movement_set: Mouse movement set to 0/0
|
||||
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_set: Mouse movement set to 0/-3
|
||||
scroll_set: Mouse scroll set to 0/0
|
||||
movement_set: Mouse movement set to 0/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 0/2
|
||||
scroll_set: Mouse scroll set to 0/0
|
||||
movement_set: Mouse movement set to 0/0
|
||||
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_set: Mouse movement set to 0/3
|
||||
scroll_set: Mouse scroll set to 0/0
|
||||
movement_set: Mouse movement set to 0/0
|
||||
@@ -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_POINTING=y
|
||||
29
app/tests/pointing/mouse-move/move_y/native_posix_64.keymap
Normal file
29
app/tests/pointing/mouse-move/move_y/native_posix_64.keymap
Normal file
@@ -0,0 +1,29 @@
|
||||
#include <behaviors.dtsi>
|
||||
#include <behaviors/mouse_move.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_UP &mmv MOVE_DOWN
|
||||
&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)
|
||||
>;
|
||||
};
|
||||
@@ -0,0 +1 @@
|
||||
s/.*hid_mouse_//p
|
||||
@@ -0,0 +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 -4/-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 -5/-4
|
||||
scroll_set: Mouse scroll set to 0/0
|
||||
movement_set: Mouse movement set to 0/0
|
||||
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/-5
|
||||
scroll_set: Mouse scroll set to 0/0
|
||||
movement_set: Mouse movement set to 0/0
|
||||
@@ -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_POINTING=y
|
||||
@@ -0,0 +1,37 @@
|
||||
|
||||
#include <dt-bindings/zmk/input_transform.h>
|
||||
#include <zephyr/dt-bindings/input/input-event-codes.h>
|
||||
|
||||
#include <behaviors.dtsi>
|
||||
#include <input/processors.dtsi>
|
||||
#include <dt-bindings/zmk/keys.h>
|
||||
#include <dt-bindings/zmk/kscan_mock.h>
|
||||
#include <dt-bindings/zmk/mouse.h>
|
||||
|
||||
&mmv_input_listener {
|
||||
input-processors = <&zip_xy_scaler 5 3>;
|
||||
};
|
||||
|
||||
/ {
|
||||
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)
|
||||
>;
|
||||
};
|
||||
@@ -0,0 +1 @@
|
||||
s/.*hid_mouse_//p
|
||||
@@ -0,0 +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
|
||||
scroll_set: Mouse scroll set to 0/0
|
||||
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
|
||||
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
|
||||
scroll_set: Mouse scroll set to 0/0
|
||||
movement_set: Mouse movement set to 0/0
|
||||
@@ -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_POINTING=y
|
||||
@@ -0,0 +1,34 @@
|
||||
#include <input/processors.dtsi>
|
||||
#include <behaviors.dtsi>
|
||||
#include <dt-bindings/zmk/keys.h>
|
||||
#include <dt-bindings/zmk/kscan_mock.h>
|
||||
#include <dt-bindings/zmk/mouse.h>
|
||||
#include <dt-bindings/zmk/input_transform.h>
|
||||
|
||||
&mmv_input_listener {
|
||||
input-processors = <&zip_xy_transform (INPUT_TRANSFORM_X_INVERT | INPUT_TRANSFORM_Y_INVERT)>;
|
||||
};
|
||||
|
||||
/ {
|
||||
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)
|
||||
>;
|
||||
};
|
||||
@@ -0,0 +1 @@
|
||||
s/.*hid_mouse_//p
|
||||
@@ -0,0 +1,18 @@
|
||||
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
|
||||
scroll_set: Mouse scroll set to 0/0
|
||||
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 -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 -3/0
|
||||
scroll_set: Mouse scroll set to 0/0
|
||||
movement_set: Mouse movement set to 0/0
|
||||
@@ -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_POINTING=y
|
||||
@@ -0,0 +1,33 @@
|
||||
#include <input/processors.dtsi>
|
||||
#include <behaviors.dtsi>
|
||||
#include <dt-bindings/zmk/keys.h>
|
||||
#include <dt-bindings/zmk/kscan_mock.h>
|
||||
#include <dt-bindings/zmk/mouse.h>
|
||||
|
||||
&mmv_input_listener {
|
||||
input-processors = <&zip_xy_swap_mapper>;
|
||||
};
|
||||
|
||||
/ {
|
||||
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)
|
||||
>;
|
||||
};
|
||||
@@ -0,0 +1,5 @@
|
||||
s/.*hid_mouse_//p
|
||||
s/.*set_layer_state: //p
|
||||
s/.*handle_state_changed_dispatcher: //p
|
||||
s/.*handle_position_state_changed: //p
|
||||
s/.*handle_keycode_state_changed: //p
|
||||
@@ -0,0 +1,14 @@
|
||||
layer_changed: layer 1 state 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_set: Mouse movement set to -2/0
|
||||
scroll_set: Mouse scroll set to 0/0
|
||||
movement_set: Mouse movement set to 0/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_set: Mouse movement set to -3/0
|
||||
scroll_set: Mouse scroll set to 0/0
|
||||
movement_set: Mouse movement set to 0/0
|
||||
layer_changed: layer 1 state 0
|
||||
@@ -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_POINTING=y
|
||||
@@ -0,0 +1,41 @@
|
||||
|
||||
#include <dt-bindings/zmk/input_transform.h>
|
||||
#include <zephyr/dt-bindings/input/input-event-codes.h>
|
||||
|
||||
#include <behaviors.dtsi>
|
||||
#include <input/processors.dtsi>
|
||||
#include <dt-bindings/zmk/keys.h>
|
||||
#include <dt-bindings/zmk/kscan_mock.h>
|
||||
#include <dt-bindings/zmk/mouse.h>
|
||||
|
||||
&mmv_input_listener {
|
||||
input-processors = <&zip_temp_layer 1 500>;
|
||||
};
|
||||
|
||||
/ {
|
||||
keymap {
|
||||
compatible = "zmk,keymap";
|
||||
label ="Default keymap";
|
||||
|
||||
default_layer {
|
||||
bindings = <
|
||||
&mmv MOVE_LEFT &mmv MOVE_UP
|
||||
&none &none
|
||||
>;
|
||||
};
|
||||
|
||||
mkp_layer {
|
||||
bindings = <&mkp LCLK &mkp RCLK &trans &trans>;
|
||||
};
|
||||
};
|
||||
};
|
||||
|
||||
|
||||
&kscan {
|
||||
events = <
|
||||
ZMK_MOCK_PRESS(0,0,100)
|
||||
ZMK_MOCK_RELEASE(0,0,150)
|
||||
ZMK_MOCK_PRESS(1,0,200)
|
||||
ZMK_MOCK_RELEASE(1,0,250)
|
||||
>;
|
||||
};
|
||||
@@ -0,0 +1,7 @@
|
||||
s/.*hid_mouse_//p
|
||||
s/.*set_layer_state: //p
|
||||
s/.*hid_listener_keycode/kp/p
|
||||
s/.*mo_keymap_binding/mo/p
|
||||
s/.*handle_state_changed_dispatcher: //p
|
||||
s/.*handle_position_state_changed: //p
|
||||
s/.*handle_keycode_state_changed: //p
|
||||
@@ -0,0 +1,21 @@
|
||||
Dispatching handle_position_state_changed
|
||||
Position excluded, continuing
|
||||
layer_changed: layer 1 state 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_set: Mouse movement set to -2/0
|
||||
scroll_set: Mouse scroll set to 0/0
|
||||
movement_set: Mouse movement set to 0/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_set: Mouse movement set to -3/0
|
||||
scroll_set: Mouse scroll set to 0/0
|
||||
movement_set: Mouse movement set to 0/0
|
||||
Dispatching handle_position_state_changed
|
||||
Dispatching handle_position_state_changed
|
||||
Position not excluded, deactivating layer
|
||||
layer_changed: layer 1 state 0
|
||||
Position excluded, continuing
|
||||
Dispatching handle_position_state_changed
|
||||
@@ -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_POINTING=y
|
||||
@@ -0,0 +1,46 @@
|
||||
|
||||
#include <dt-bindings/zmk/input_transform.h>
|
||||
#include <zephyr/dt-bindings/input/input-event-codes.h>
|
||||
|
||||
#include <behaviors.dtsi>
|
||||
#include <input/processors.dtsi>
|
||||
#include <dt-bindings/zmk/keys.h>
|
||||
#include <dt-bindings/zmk/kscan_mock.h>
|
||||
#include <dt-bindings/zmk/mouse.h>
|
||||
|
||||
&mmv_input_listener {
|
||||
input-processors = <&auto_mouse_layer 1 0>;
|
||||
};
|
||||
|
||||
/ {
|
||||
auto_mouse_layer: auto_mouse_layer {
|
||||
compatible = "zmk,input-processor-temp-layer";
|
||||
#input-processor-cells = <2>;
|
||||
excluded-positions = <0>;
|
||||
};
|
||||
keymap {
|
||||
compatible = "zmk,keymap";
|
||||
label ="Default keymap";
|
||||
|
||||
default_layer {
|
||||
bindings = <
|
||||
&mmv MOVE_LEFT &mmv MOVE_UP
|
||||
&none &none
|
||||
>;
|
||||
};
|
||||
|
||||
mkp_layer {
|
||||
bindings = <&mkp LCLK &mkp RCLK &trans &trans>;
|
||||
};
|
||||
};
|
||||
};
|
||||
|
||||
|
||||
&kscan {
|
||||
events = <
|
||||
ZMK_MOCK_PRESS(0,0,100)
|
||||
ZMK_MOCK_RELEASE(0,0,150)
|
||||
ZMK_MOCK_PRESS(1,0,200)
|
||||
ZMK_MOCK_RELEASE(1,0,250)
|
||||
>;
|
||||
};
|
||||
@@ -0,0 +1,7 @@
|
||||
s/.*hid_mouse_//p
|
||||
s/.*set_layer_state: //p
|
||||
s/.*hid_listener_keycode/kp/p
|
||||
s/.*mo_keymap_binding/mo/p
|
||||
s/.*handle_state_changed_dispatcher: //p
|
||||
s/.*handle_position_state_changed: //p
|
||||
s/.*handle_keycode_state_changed: //p
|
||||
@@ -0,0 +1,28 @@
|
||||
Dispatching handle_position_state_changed
|
||||
Position excluded, continuing
|
||||
layer_changed: layer 1 state 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_set: Mouse movement set to -2/0
|
||||
scroll_set: Mouse scroll set to 0/0
|
||||
movement_set: Mouse movement set to 0/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_set: Mouse movement set to -3/0
|
||||
scroll_set: Mouse scroll set to 0/0
|
||||
movement_set: Mouse movement set to 0/0
|
||||
Dispatching handle_position_state_changed
|
||||
Dispatching handle_position_state_changed
|
||||
Position excluded, continuing
|
||||
button_press: Button 0 count 1
|
||||
button_press: Mouse buttons set to 0x01
|
||||
scroll_set: Mouse scroll set to 0/0
|
||||
movement_set: Mouse movement set to 0/0
|
||||
Dispatching handle_position_state_changed
|
||||
button_release: Button 0 count: 0
|
||||
button_release: Button 0 released
|
||||
button_release: Mouse buttons set to 0x00
|
||||
scroll_set: Mouse scroll set to 0/0
|
||||
movement_set: Mouse movement set to 0/0
|
||||
@@ -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_POINTING=y
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user