diff --git a/app/CMakeLists.txt b/app/CMakeLists.txt index ffb4b08cf520..fcb038cc7232 100644 --- a/app/CMakeLists.txt +++ b/app/CMakeLists.txt @@ -27,9 +27,7 @@ target_sources(app PRIVATE src/stdlib.c) target_sources(app PRIVATE src/activity.c) target_sources(app PRIVATE src/kscan.c) target_sources(app PRIVATE src/matrix_transform.c) -target_sources_ifdef(CONFIG_ZMK_MOUSE app PRIVATE src/mouse/key_listener.c) target_sources_ifdef(CONFIG_ZMK_MOUSE app PRIVATE src/mouse/main.c) -target_sources_ifdef(CONFIG_ZMK_MOUSE app PRIVATE src/mouse/tick_listener.c) target_sources(app PRIVATE src/sensors.c) target_sources_ifdef(CONFIG_ZMK_WPM app PRIVATE src/wpm.c) target_sources(app PRIVATE src/event_manager.c) @@ -37,10 +35,6 @@ target_sources_ifdef(CONFIG_ZMK_EXT_POWER app PRIVATE src/ext_power_generic.c) target_sources(app PRIVATE src/events/activity_state_changed.c) target_sources(app PRIVATE src/events/position_state_changed.c) target_sources(app PRIVATE src/events/sensor_event.c) -target_sources(app PRIVATE src/events/mouse_button_state_changed.c) -target_sources_ifdef(CONFIG_ZMK_MOUSE app PRIVATE src/events/mouse_move_state_changed.c) -target_sources_ifdef(CONFIG_ZMK_MOUSE app PRIVATE src/events/mouse_tick.c) -target_sources_ifdef(CONFIG_ZMK_MOUSE app PRIVATE src/events/mouse_scroll_state_changed.c) target_sources_ifdef(CONFIG_ZMK_WPM app PRIVATE src/events/wpm_state_changed.c) target_sources_ifdef(CONFIG_USB_DEVICE_STACK app PRIVATE src/events/usb_conn_state_changed.c) target_sources(app PRIVATE src/behaviors/behavior_reset.c) @@ -48,6 +42,7 @@ target_sources_ifdef(CONFIG_ZMK_EXT_POWER app PRIVATE src/behaviors/behavior_ext if ((NOT CONFIG_ZMK_SPLIT) OR CONFIG_ZMK_SPLIT_ROLE_CENTRAL) target_sources(app PRIVATE src/hid.c) target_sources_ifdef(CONFIG_ZMK_MOUSE app PRIVATE src/mouse/main.c) + target_sources_ifdef(CONFIG_ZMK_MOUSE app PRIVATE src/mouse/hid_input_listener.c) target_sources(app PRIVATE src/behaviors/behavior_key_press.c) target_sources_ifdef(CONFIG_ZMK_BEHAVIOR_KEY_TOGGLE app PRIVATE src/behaviors/behavior_key_toggle.c) target_sources(app PRIVATE src/behaviors/behavior_hold_tap.c) @@ -66,8 +61,7 @@ if ((NOT CONFIG_ZMK_SPLIT) OR CONFIG_ZMK_SPLIT_ROLE_CENTRAL) target_sources_ifdef(CONFIG_ZMK_BEHAVIOR_SENSOR_ROTATE_VAR app PRIVATE src/behaviors/behavior_sensor_rotate_var.c) target_sources_ifdef(CONFIG_ZMK_BEHAVIOR_SENSOR_ROTATE_COMMON app PRIVATE src/behaviors/behavior_sensor_rotate_common.c) target_sources_ifdef(CONFIG_ZMK_BEHAVIOR_MOUSE_KEY_PRESS app PRIVATE src/behaviors/behavior_mouse_key_press.c) - target_sources_ifdef(CONFIG_ZMK_MOUSE app PRIVATE src/behaviors/behavior_mouse_move.c) - target_sources_ifdef(CONFIG_ZMK_MOUSE app PRIVATE src/behaviors/behavior_mouse_scroll.c) + target_sources_ifdef(CONFIG_ZMK_BEHAVIOR_INPUT_TWO_AXIS app PRIVATE src/behaviors/behavior_input_two_axis.c) target_sources(app PRIVATE src/combo.c) target_sources(app PRIVATE src/behaviors/behavior_tap_dance.c) target_sources(app PRIVATE src/behavior_queue.c) diff --git a/app/Kconfig.behaviors b/app/Kconfig.behaviors index 11bc8c5900f1..537b53d8a723 100644 --- a/app/Kconfig.behaviors +++ b/app/Kconfig.behaviors @@ -12,6 +12,12 @@ config ZMK_BEHAVIOR_MOUSE_KEY_PRESS depends on DT_HAS_ZMK_BEHAVIOR_MOUSE_KEY_PRESS_ENABLED imply ZMK_MOUSE +config ZMK_BEHAVIOR_INPUT_TWO_AXIS + bool + default y + depends on DT_HAS_ZMK_BEHAVIOR_INPUT_TWO_AXIS_ENABLED + imply ZMK_MOUSE + config ZMK_BEHAVIOR_SENSOR_ROTATE_COMMON bool default n @@ -31,4 +37,4 @@ config ZMK_BEHAVIOR_SENSOR_ROTATE_VAR config ZMK_BEHAVIOR_MACRO bool default y - depends on DT_HAS_ZMK_BEHAVIOR_MACRO_ENABLED || DT_HAS_ZMK_BEHAVIOR_MACRO_ONE_PARAM_ENABLED || DT_HAS_ZMK_BEHAVIOR_MACRO_TWO_PARAM_ENABLED \ No newline at end of file + depends on DT_HAS_ZMK_BEHAVIOR_MACRO_ENABLED || DT_HAS_ZMK_BEHAVIOR_MACRO_ONE_PARAM_ENABLED || DT_HAS_ZMK_BEHAVIOR_MACRO_TWO_PARAM_ENABLED diff --git a/app/dts/behaviors/mouse_move.dtsi b/app/dts/behaviors/mouse_move.dtsi index 2e165f3a6e43..d18eae939fab 100644 --- a/app/dts/behaviors/mouse_move.dtsi +++ b/app/dts/behaviors/mouse_move.dtsi @@ -1,10 +1,13 @@ +#include + / { behaviors { /omit-if-no-ref/ mmv: behavior_mouse_move { - compatible = "zmk,behavior-mouse-move"; + compatible = "zmk,behavior-input-two-axis"; label = "MOUSE_MOVE"; #binding-cells = <1>; - delay-ms = <0>; + x-input-code = ; + y-input-code = ; time-to-max-speed-ms = <300>; acceleration-exponent = <1>; }; diff --git a/app/dts/behaviors/mouse_scroll.dtsi b/app/dts/behaviors/mouse_scroll.dtsi index f2cb86f6d882..e08a9e9fd194 100644 --- a/app/dts/behaviors/mouse_scroll.dtsi +++ b/app/dts/behaviors/mouse_scroll.dtsi @@ -1,10 +1,13 @@ +#include + / { behaviors { /omit-if-no-ref/ msc: behavior_mouse_scroll { - compatible = "zmk,behavior-mouse-scroll"; + compatible = "zmk,behavior-input-two-axis"; label = "MOUSE_SCROLL"; #binding-cells = <1>; - delay-ms = <0>; + x-input-code = ; + y-input-code = ; time-to-max-speed-ms = <300>; acceleration-exponent = <0>; }; diff --git a/app/dts/bindings/behaviors/zmk,behavior-input-two-axis.yaml b/app/dts/bindings/behaviors/zmk,behavior-input-two-axis.yaml new file mode 100644 index 000000000000..820ee7035b8a --- /dev/null +++ b/app/dts/bindings/behaviors/zmk,behavior-input-two-axis.yaml @@ -0,0 +1,21 @@ +description: Two axis input behavior + +compatible: "zmk,behavior-input-two-axis" + +include: one_param.yaml + +properties: + x-input-code: + type: int + required: true + y-input-code: + type: int + required: true + delay-ms: + type: int + time-to-max-speed-ms: + type: int + required: true + acceleration-exponent: + type: int + default: 1 diff --git a/app/dts/bindings/behaviors/zmk,behavior-mouse-move.yaml b/app/dts/bindings/behaviors/zmk,behavior-mouse-move.yaml index 73ec34ec2dbe..ae18d9e4112f 100644 --- a/app/dts/bindings/behaviors/zmk,behavior-mouse-move.yaml +++ b/app/dts/bindings/behaviors/zmk,behavior-mouse-move.yaml @@ -5,9 +5,17 @@ compatible: "zmk,behavior-mouse-move" include: one_param.yaml properties: + x-code: + type: int + required: true + y-code: + type: int + required: true delay-ms: type: int time-to-max-speed-ms: type: int + required: true acceleration-exponent: type: int + default: 1 diff --git a/app/dts/bindings/behaviors/zmk,behavior-mouse-scroll.yaml b/app/dts/bindings/behaviors/zmk,behavior-mouse-scroll.yaml deleted file mode 100644 index 5a932bc59042..000000000000 --- a/app/dts/bindings/behaviors/zmk,behavior-mouse-scroll.yaml +++ /dev/null @@ -1,13 +0,0 @@ -description: Mouse scroll - -compatible: "zmk,behavior-mouse-scroll" - -include: one_param.yaml - -properties: - delay-ms: - type: int - time-to-max-speed-ms: - type: int - acceleration-exponent: - type: int diff --git a/app/include/dt-bindings/zmk/mouse.h b/app/include/dt-bindings/zmk/mouse.h index 861f9a90712e..ea34e1243f16 100644 --- a/app/include/dt-bindings/zmk/mouse.h +++ b/app/include/dt-bindings/zmk/mouse.h @@ -44,15 +44,7 @@ #define MOVE_LEFT MOVE_X(-ZMK_MOUSE_DEFAULT_MOVE_VAL) #define MOVE_RIGHT MOVE_X(ZMK_MOUSE_DEFAULT_MOVE_VAL) -/* Mouse scroll behavior */ -#define SCRL_Y(vert) ((vert)&0xFFFF) -#define SCRL_Y_DECODE(encoded) (int16_t)((encoded)&0x0000FFFF) -#define SCRL_X(hor) (((hor)&0xFFFF) << 16) -#define SCRL_X_DECODE(encoded) (int16_t)(((encoded)&0xFFFF0000) >> 16) - -#define SCROLL(hor, vert) (SCRL_X(hor) + SCRL_Y(vert)) - -#define SCRL_UP SCRL_Y(ZMK_MOUSE_DEFAULT_SCRL_VAL) -#define SCRL_DOWN SCRL_Y(-ZMK_MOUSE_DEFAULT_SCRL_VAL) -#define SCRL_LEFT SCRL_X(-ZMK_MOUSE_DEFAULT_SCRL_VAL) -#define SCRL_RIGHT SCRL_X(ZMK_MOUSE_DEFAULT_SCRL_VAL) +#define SCRL_UP MOVE_Y(ZMK_MOUSE_DEFAULT_SCRL_VAL) +#define SCRL_DOWN MOVE_Y(-ZMK_MOUSE_DEFAULT_SCRL_VAL) +#define SCRL_LEFT MOVE_X(-ZMK_MOUSE_DEFAULT_SCRL_VAL) +#define SCRL_RIGHT MOVE_X(ZMK_MOUSE_DEFAULT_SCRL_VAL) diff --git a/app/include/zmk/events/mouse_move_state_changed.h b/app/include/zmk/events/mouse_move_state_changed.h deleted file mode 100644 index d1ebad87e0e4..000000000000 --- a/app/include/zmk/events/mouse_move_state_changed.h +++ /dev/null @@ -1,33 +0,0 @@ - -/* - * Copyright (c) 2020 The ZMK Contributors - * - * SPDX-License-Identifier: MIT - */ - -#pragma once - -#include -#include -#include - -struct zmk_mouse_move_state_changed { - struct vector2d max_speed; - struct mouse_config config; - bool state; - int64_t timestamp; -}; - -ZMK_EVENT_DECLARE(zmk_mouse_move_state_changed); - -static inline struct zmk_mouse_move_state_changed_event * -zmk_mouse_move_state_changed_from_encoded(uint32_t encoded, struct mouse_config config, - bool pressed, int64_t timestamp) { - struct vector2d max_speed = (struct vector2d){ - .x = MOVE_X_DECODE(encoded), - .y = MOVE_Y_DECODE(encoded), - }; - - return new_zmk_mouse_move_state_changed((struct zmk_mouse_move_state_changed){ - .max_speed = max_speed, .config = config, .state = pressed, .timestamp = timestamp}); -} diff --git a/app/include/zmk/events/mouse_scroll_state_changed.h b/app/include/zmk/events/mouse_scroll_state_changed.h deleted file mode 100644 index bf03308f1d50..000000000000 --- a/app/include/zmk/events/mouse_scroll_state_changed.h +++ /dev/null @@ -1,34 +0,0 @@ - -/* - * Copyright (c) 2020 The ZMK Contributors - * - * SPDX-License-Identifier: MIT - */ - -#pragma once - -#include -#include -#include -#include - -struct zmk_mouse_scroll_state_changed { - struct vector2d max_speed; - struct mouse_config config; - bool state; - int64_t timestamp; -}; - -ZMK_EVENT_DECLARE(zmk_mouse_scroll_state_changed); - -static inline struct zmk_mouse_scroll_state_changed_event * -zmk_mouse_scroll_state_changed_from_encoded(uint32_t encoded, struct mouse_config config, - bool pressed, int64_t timestamp) { - struct vector2d max_speed = (struct vector2d){ - .x = SCRL_X_DECODE(encoded), - .y = SCRL_Y_DECODE(encoded), - }; - - return new_zmk_mouse_scroll_state_changed((struct zmk_mouse_scroll_state_changed){ - .max_speed = max_speed, .config = config, .state = pressed, .timestamp = timestamp}); -} diff --git a/app/include/zmk/events/mouse_tick.h b/app/include/zmk/events/mouse_tick.h deleted file mode 100644 index 75a041e96b4e..000000000000 --- a/app/include/zmk/events/mouse_tick.h +++ /dev/null @@ -1,39 +0,0 @@ - -/* - * Copyright (c) 2020 The ZMK Contributors - * - * SPDX-License-Identifier: MIT - */ - -#pragma once - -#include -#include -#include -#include - -struct zmk_mouse_tick { - struct vector2d max_move; - struct vector2d max_scroll; - struct mouse_config move_config; - struct mouse_config scroll_config; - struct mouse_times start_times; - int64_t timestamp; -}; - -ZMK_EVENT_DECLARE(zmk_mouse_tick); - -static inline struct zmk_mouse_tick_event *zmk_mouse_tick(struct vector2d max_move, - struct vector2d max_scroll, - struct mouse_config move_config, - struct mouse_config scroll_config, - struct mouse_times movement_start) { - return new_zmk_mouse_tick((struct zmk_mouse_tick){ - .max_move = max_move, - .max_scroll = max_scroll, - .move_config = move_config, - .scroll_config = scroll_config, - .start_times = movement_start, - .timestamp = k_uptime_get(), - }); -} diff --git a/app/include/zmk/mouse.h b/app/include/zmk/mouse.h index c2ae6252df59..28dab60d1433 100644 --- a/app/include/zmk/mouse.h +++ b/app/include/zmk/mouse.h @@ -11,26 +11,4 @@ typedef uint8_t zmk_mouse_button_flags_t; typedef uint16_t zmk_mouse_button_t; -struct mouse_config { - int delay_ms; - int time_to_max_speed_ms; - // acceleration exponent 0: uniform speed - // acceleration exponent 1: uniform acceleration - // acceleration exponent 2: uniform jerk - int acceleration_exponent; -}; - -struct vector2d { - float x; - float y; -}; - -struct mouse_times { - uint64_t m_x; - uint64_t m_y; - uint64_t s_x; - uint64_t s_y; -}; - -struct k_work_q *zmk_mouse_work_q(); -int zmk_mouse_init(); +int zmk_mouse_init(void); diff --git a/app/src/behaviors/behavior_input_two_axis.c b/app/src/behaviors/behavior_input_two_axis.c new file mode 100644 index 000000000000..44551e1bb3d5 --- /dev/null +++ b/app/src/behaviors/behavior_input_two_axis.c @@ -0,0 +1,270 @@ +/* + * Copyright (c) 2021 The ZMK Contributors + * + * SPDX-License-Identifier: MIT + */ + +#define DT_DRV_COMPAT zmk_behavior_input_two_axis + +#include +#include +#include +#include +#include // CLAMP + +#include +#include + +LOG_MODULE_DECLARE(zmk, CONFIG_ZMK_LOG_LEVEL); + +struct vector2d { + float x; + float y; +}; + +struct movement_state_1d { + float remainder; + int16_t speed; + uint64_t start_time; +}; + +struct movement_state_2d { + struct movement_state_1d x; + struct movement_state_1d y; +}; + +struct behavior_input_two_axis_data { + struct k_work_delayable tick_work; + const struct device *dev; + + struct movement_state_2d state; +}; + +struct behavior_input_two_axis_config { + int16_t x_code; + int16_t y_code; + int delay_ms; + int time_to_max_speed_ms; + // acceleration exponent 0: uniform speed + // acceleration exponent 1: uniform acceleration + // acceleration exponent 2: uniform jerk + int acceleration_exponent; +}; + +#if CONFIG_MINIMAL_LIBC +static float powf(float base, float exponent) { + // poor man's power implementation rounds the exponent down to the nearest integer. + float power = 1.0f; + for (; exponent >= 1.0f; exponent--) { + power = power * base; + } + return power; +} +#else +#include +#endif + +static int64_t ms_since_start(int64_t start, int64_t now, int64_t delay) { + if (start == 0) { + return 0; + } + int64_t move_duration = now - (start + delay); + // start can be in the future if there's a delay + if (move_duration < 0) { + move_duration = 0; + } + return move_duration; +} + +static float speed(const struct behavior_input_two_axis_config *config, float max_speed, + int64_t duration_ms) { + // Calculate the speed based on MouseKeysAccel + // See https://en.wikipedia.org/wiki/Mouse_keys + if (duration_ms == 0) { + return 0; + } + + if (duration_ms > config->time_to_max_speed_ms || config->time_to_max_speed_ms == 0 || + config->acceleration_exponent == 0) { + return max_speed; + } + float time_fraction = (float)duration_ms / config->time_to_max_speed_ms; + return max_speed * powf(time_fraction, config->acceleration_exponent); +} + +static void track_remainder(float *move, float *remainder) { + float new_move = *move + *remainder; + *remainder = new_move - (int)new_move; + *move = (int)new_move; +} + +static float update_movement_1d(const struct behavior_input_two_axis_config *config, + struct movement_state_1d *state, int64_t now) { + float move = 0; + if (state->speed == 0) { + state->remainder = 0; + return move; + } + + int64_t move_duration = ms_since_start(state->start_time, now, config->delay_ms); + move = + (move_duration > 0) + ? (speed(config, state->speed, move_duration) * CONFIG_ZMK_MOUSE_TICK_DURATION / 1000) + : 0; + + track_remainder(&(move), &(state->remainder)); + + return move; +} +static struct vector2d update_movement_2d(const struct behavior_input_two_axis_config *config, + struct movement_state_2d *state, int64_t now) { + struct vector2d move = {0}; + + move = (struct vector2d){ + .x = update_movement_1d(config, &state->x, now), + .y = update_movement_1d(config, &state->y, now), + }; + + return move; +} + +static bool is_non_zero_1d_movement(int16_t speed) { return speed != 0; } + +static bool is_non_zero_2d_movement(struct movement_state_2d *state) { + return is_non_zero_1d_movement(state->x.speed) || is_non_zero_1d_movement(state->y.speed); +} + +static bool should_be_working(struct behavior_input_two_axis_data *data) { + return is_non_zero_2d_movement(&data->state); +} + +static void tick_work_cb(struct k_work *work) { + struct k_work_delayable *d_work = k_work_delayable_from_work(work); + struct behavior_input_two_axis_data *data = + CONTAINER_OF(d_work, struct behavior_input_two_axis_data, tick_work); + const struct device *dev = data->dev; + const struct behavior_input_two_axis_config *cfg = dev->config; + + uint32_t timestamp = k_uptime_get(); + + LOG_INF("tick start times: %lld %lld %lld", data->state.x.start_time, data->state.y.start_time, + timestamp); + + struct vector2d move = update_movement_2d(cfg, &data->state, timestamp); + + int ret = 0; + bool have_x = is_non_zero_1d_movement(move.x); + bool have_y = is_non_zero_1d_movement(move.y); + if (have_x) { + ret = input_report_rel(dev, cfg->x_code, (int16_t)CLAMP(move.x, INT16_MIN, INT16_MAX), + !have_y, K_NO_WAIT); + } + if (have_y) { + ret = input_report_rel(dev, cfg->y_code, (int16_t)CLAMP(move.y, INT16_MIN, INT16_MAX), true, + K_NO_WAIT); + } + + if (should_be_working(data)) { + k_work_schedule(&data->tick_work, K_MSEC(CONFIG_ZMK_MOUSE_TICK_DURATION)); + } +} + +static void set_start_times_for_activity(struct movement_state_2d *state) { + if (state->x.speed != 0 && state->x.start_time == 0) { + state->x.start_time = k_uptime_get(); + } + + if (state->y.speed != 0 && state->y.start_time == 0) { + state->y.start_time = k_uptime_get(); + } +} + +static void update_work_scheduling(const struct device *dev) { + struct behavior_input_two_axis_data *data = dev->data; + + set_start_times_for_activity(&data->state); + + if (should_be_working(data)) { + k_work_schedule(&data->tick_work, K_MSEC(CONFIG_ZMK_MOUSE_TICK_DURATION)); + } else { + k_work_cancel_delayable(&data->tick_work); + } +} + +int zmk_input_synth_pointer_adjust_speed(const struct device *dev, int16_t dx, int16_t dy) { + struct behavior_input_two_axis_data *data = dev->data; + + LOG_DBG("Adjusting: %d %d", dx, dy); + data->state.x.speed += dx; + data->state.y.speed += dy; + + LOG_DBG("After: %d %d", data->state.x.speed, data->state.y.speed); + + update_work_scheduling(dev); + + return 0; +} + +// static void process_key_state(const struct device *dev, int32_t val, bool pressed) { +// for (int i = 0; i < ZMK_HID_MOUSE_NUM_BUTTONS; i++) { +// if (val & BIT(i)) { +// WRITE_BIT(val, i, 0); +// input_report_key(dev, INPUT_BTN_0 + i, pressed ? 1 : 0, val == 0, K_FOREVER); +// } +// } +// } + +static int behavior_input_two_axis_init(const struct device *dev) { + struct behavior_input_two_axis_data *data = dev->data; + + data->dev = dev; + k_work_init_delayable(&data->tick_work, tick_work_cb); + + return 0; +}; + +static int on_keymap_binding_pressed(struct zmk_behavior_binding *binding, + struct zmk_behavior_binding_event event) { + + const struct device *behavior_dev = device_get_binding(binding->behavior_dev); + + LOG_DBG("position %d keycode 0x%02X", event.position, binding->param1); + + int16_t x = MOVE_X_DECODE(binding->param1); + int16_t y = MOVE_Y_DECODE(binding->param1); + + zmk_input_synth_pointer_adjust_speed(behavior_dev, x, y); + return 0; +} + +static int on_keymap_binding_released(struct zmk_behavior_binding *binding, + struct zmk_behavior_binding_event event) { + const struct device *behavior_dev = device_get_binding(binding->behavior_dev); + + LOG_DBG("position %d keycode 0x%02X", event.position, binding->param1); + + int16_t x = MOVE_X_DECODE(binding->param1); + int16_t y = MOVE_Y_DECODE(binding->param1); + + zmk_input_synth_pointer_adjust_speed(behavior_dev, -x, -y); + return 0; +} + +static const struct behavior_driver_api behavior_input_two_axis_driver_api = { + .binding_pressed = on_keymap_binding_pressed, .binding_released = on_keymap_binding_released}; + +#define KP_INST(n) \ + static struct behavior_input_two_axis_data behavior_input_two_axis_data_##n = {}; \ + static struct behavior_input_two_axis_config behavior_input_two_axis_config_##n = { \ + .x_code = DT_INST_PROP(n, x_input_code), \ + .y_code = DT_INST_PROP(n, y_input_code), \ + .delay_ms = DT_INST_PROP_OR(n, delay_ms, 0), \ + .time_to_max_speed_ms = DT_INST_PROP(n, time_to_max_speed_ms), \ + .acceleration_exponent = DT_INST_PROP_OR(n, acceleration_exponent, 1), \ + }; \ + DEVICE_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(KP_INST) diff --git a/app/src/behaviors/behavior_mouse_key_press.c b/app/src/behaviors/behavior_mouse_key_press.c index 6718155768cd..65598b59abde 100644 --- a/app/src/behaviors/behavior_mouse_key_press.c +++ b/app/src/behaviors/behavior_mouse_key_press.c @@ -11,8 +11,9 @@ #include #include -#include -#include +#include +#include +#include LOG_MODULE_DECLARE(zmk, CONFIG_ZMK_LOG_LEVEL); @@ -20,19 +21,31 @@ LOG_MODULE_DECLARE(zmk, CONFIG_ZMK_LOG_LEVEL); static int behavior_mouse_key_press_init(const struct device *dev) { return 0; }; +static void process_key_state(const struct device *dev, int32_t val, bool pressed) { + for (int i = 0; i < ZMK_HID_MOUSE_NUM_BUTTONS; i++) { + if (val & BIT(i)) { + WRITE_BIT(val, i, 0); + input_report_key(dev, INPUT_BTN_0 + i, pressed ? 1 : 0, val == 0, K_FOREVER); + } + } +} + static int on_keymap_binding_pressed(struct zmk_behavior_binding *binding, struct zmk_behavior_binding_event event) { LOG_DBG("position %d keycode 0x%02X", event.position, binding->param1); - return ZMK_EVENT_RAISE( - zmk_mouse_button_state_changed_from_encoded(binding->param1, true, event.timestamp)); + process_key_state(device_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 ZMK_EVENT_RAISE( - zmk_mouse_button_state_changed_from_encoded(binding->param1, false, event.timestamp)); + + process_key_state(device_get_binding(binding->behavior_dev), binding->param1, false); + + return 0; } static const struct behavior_driver_api behavior_mouse_key_press_driver_api = { diff --git a/app/src/behaviors/behavior_mouse_move.c b/app/src/behaviors/behavior_mouse_move.c index 0d591f003245..f077fe5dc126 100644 --- a/app/src/behaviors/behavior_mouse_move.c +++ b/app/src/behaviors/behavior_mouse_move.c @@ -8,50 +8,262 @@ #include #include +#include #include +#include // CLAMP #include -#include -#include -#include +#include LOG_MODULE_DECLARE(zmk, CONFIG_ZMK_LOG_LEVEL); -#if DT_HAS_COMPAT_STATUS_OKAY(DT_DRV_COMPAT) +struct vector2d { + float x; + float y; +}; -static int behavior_mouse_move_init(const struct device *dev) { return 0; }; +struct movement_state_1d { + float remainder; + int16_t speed; + uint64_t start_time; +}; + +struct movement_state_2d { + struct movement_state_1d x; + struct movement_state_1d y; +}; + +struct behavior_mouse_move_data { + struct k_work_delayable tick_work; + const struct device *dev; + + struct movement_state_2d state; +}; + +struct behavior_mouse_move_config { + int16_t x_code; + int16_t y_code; + int delay_ms; + int time_to_max_speed_ms; + // acceleration exponent 0: uniform speed + // acceleration exponent 1: uniform acceleration + // acceleration exponent 2: uniform jerk + int acceleration_exponent; +}; + +#if CONFIG_MINIMAL_LIBC +static float powf(float base, float exponent) { + // poor man's power implementation rounds the exponent down to the nearest integer. + float power = 1.0f; + for (; exponent >= 1.0f; exponent--) { + power = power * base; + } + return power; +} +#else +#include +#endif + +static int64_t ms_since_start(int64_t start, int64_t now, int64_t delay) { + if (start == 0) { + return 0; + } + int64_t move_duration = now - (start + delay); + // start can be in the future if there's a delay + if (move_duration < 0) { + move_duration = 0; + } + return move_duration; +} + +static float speed(const struct behavior_mouse_move_config *config, float max_speed, + int64_t duration_ms) { + // Calculate the speed based on MouseKeysAccel + // See https://en.wikipedia.org/wiki/Mouse_keys + if (duration_ms == 0) { + return 0; + } + + if (duration_ms > config->time_to_max_speed_ms || config->time_to_max_speed_ms == 0 || + config->acceleration_exponent == 0) { + return max_speed; + } + float time_fraction = (float)duration_ms / config->time_to_max_speed_ms; + return max_speed * powf(time_fraction, config->acceleration_exponent); +} + +static void track_remainder(float *move, float *remainder) { + float new_move = *move + *remainder; + *remainder = new_move - (int)new_move; + *move = (int)new_move; +} + +static float update_movement_1d(const struct behavior_mouse_move_config *config, + struct movement_state_1d *state, int64_t now) { + float move = 0; + if (state->speed == 0) { + state->remainder = 0; + return move; + } + + int64_t move_duration = ms_since_start(state->start_time, now, config->delay_ms); + move = + (move_duration > 0) + ? (speed(config, state->speed, move_duration) * CONFIG_ZMK_MOUSE_TICK_DURATION / 1000) + : 0; + + track_remainder(&(move), &(state->remainder)); + + return move; +} +static struct vector2d update_movement_2d(const struct behavior_mouse_move_config *config, + struct movement_state_2d *state, int64_t now) { + struct vector2d move = {0}; + + move = (struct vector2d){ + .x = update_movement_1d(config, &state->x, now), + .y = update_movement_1d(config, &state->y, now), + }; + + return move; +} + +static bool is_non_zero_1d_movement(int16_t speed) { return speed != 0; } + +static bool is_non_zero_2d_movement(struct movement_state_2d *state) { + return is_non_zero_1d_movement(state->x.speed) || is_non_zero_1d_movement(state->y.speed); +} + +static bool should_be_working(struct behavior_mouse_move_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_mouse_move_data *data = + CONTAINER_OF(d_work, struct behavior_mouse_move_data, tick_work); + const struct device *dev = data->dev; + const struct behavior_mouse_move_config *cfg = dev->config; + + uint32_t timestamp = k_uptime_get(); + + LOG_INF("tick start times: %lld %lld %lld", data->state.x.start_time, data->state.y.start_time, + timestamp); + + struct vector2d move = update_movement_2d(cfg, &data->state, timestamp); + + int ret = 0; + bool have_x = is_non_zero_1d_movement(move.x); + bool have_y = is_non_zero_1d_movement(move.y); + if (have_x) { + ret = input_report_rel(dev, cfg->x_code, (int16_t)CLAMP(move.x, INT16_MIN, INT16_MAX), + !have_y, K_NO_WAIT); + } + if (have_y) { + ret = input_report_rel(dev, cfg->y_code, (int16_t)CLAMP(move.y, INT16_MIN, INT16_MAX), true, + K_NO_WAIT); + } + + if (should_be_working(data)) { + k_work_schedule(&data->tick_work, K_MSEC(CONFIG_ZMK_MOUSE_TICK_DURATION)); + } +} + +static void set_start_times_for_activity(struct movement_state_2d *state) { + if (state->x.speed != 0 && state->x.start_time == 0) { + state->x.start_time = k_uptime_get(); + } + + if (state->y.speed != 0 && state->y.start_time == 0) { + state->y.start_time = k_uptime_get(); + } +} + +static void update_work_scheduling(const struct device *dev) { + struct behavior_mouse_move_data *data = dev->data; + + set_start_times_for_activity(&data->state); + + if (should_be_working(data)) { + k_work_schedule(&data->tick_work, K_MSEC(CONFIG_ZMK_MOUSE_TICK_DURATION)); + } else { + k_work_cancel_delayable(&data->tick_work); + } +} + +int zmk_input_synth_pointer_adjust_speed(const struct device *dev, int16_t dx, int16_t dy) { + struct behavior_mouse_move_data *data = dev->data; + + LOG_DBG("Adjusting: %d %d", dx, dy); + data->state.x.speed += dx; + data->state.y.speed += dy; + + LOG_DBG("After: %d %d", data->state.x.speed, data->state.y.speed); + + update_work_scheduling(dev); + + return 0; +} + +// static void process_key_state(const struct device *dev, int32_t val, bool pressed) { +// for (int i = 0; i < ZMK_HID_MOUSE_NUM_BUTTONS; i++) { +// if (val & BIT(i)) { +// WRITE_BIT(val, i, 0); +// input_report_key(dev, INPUT_BTN_0 + i, pressed ? 1 : 0, val == 0, K_FOREVER); +// } +// } +// } + +static int behavior_mouse_move_init(const struct device *dev) { + struct behavior_mouse_move_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 = device_get_binding(binding->behavior_dev); + LOG_DBG("position %d keycode 0x%02X", event.position, binding->param1); - const struct device *dev = device_get_binding(binding->behavior_dev); - const struct mouse_config *config = dev->config; - return ZMK_EVENT_RAISE( - zmk_mouse_move_state_changed_from_encoded(binding->param1, *config, true, event.timestamp)); + + int16_t x = MOVE_X_DECODE(binding->param1); + int16_t y = MOVE_Y_DECODE(binding->param1); + + zmk_input_synth_pointer_adjust_speed(behavior_dev, x, y); + return 0; } static int on_keymap_binding_released(struct zmk_behavior_binding *binding, struct zmk_behavior_binding_event event) { + const struct device *behavior_dev = device_get_binding(binding->behavior_dev); + LOG_DBG("position %d keycode 0x%02X", event.position, binding->param1); - const struct device *dev = device_get_binding(binding->behavior_dev); - const struct mouse_config *config = dev->config; - return ZMK_EVENT_RAISE(zmk_mouse_move_state_changed_from_encoded(binding->param1, *config, - false, event.timestamp)); + + int16_t x = MOVE_X_DECODE(binding->param1); + int16_t y = MOVE_Y_DECODE(binding->param1); + + zmk_input_synth_pointer_adjust_speed(behavior_dev, -x, -y); + return 0; } static const struct behavior_driver_api behavior_mouse_move_driver_api = { .binding_pressed = on_keymap_binding_pressed, .binding_released = on_keymap_binding_released}; #define KP_INST(n) \ - static struct mouse_config behavior_mouse_move_config_##n = { \ - .delay_ms = DT_INST_PROP(n, delay_ms), \ + static struct behavior_mouse_move_data behavior_mouse_move_data_##n = {}; \ + static struct behavior_mouse_move_config behavior_mouse_move_config_##n = { \ + .x_code = DT_INST_PROP(n, x_code), \ + .y_code = DT_INST_PROP(n, y_code), \ + .delay_ms = DT_INST_PROP_OR(n, delay_ms, 0), \ .time_to_max_speed_ms = DT_INST_PROP(n, time_to_max_speed_ms), \ - .acceleration_exponent = DT_INST_PROP(n, acceleration_exponent), \ + .acceleration_exponent = DT_INST_PROP_OR(n, acceleration_exponent, 1), \ }; \ - DEVICE_DT_INST_DEFINE(n, behavior_mouse_move_init, NULL, NULL, \ - &behavior_mouse_move_config_##n, APPLICATION, \ + DEVICE_DT_INST_DEFINE(n, behavior_mouse_move_init, NULL, &behavior_mouse_move_data_##n, \ + &behavior_mouse_move_config_##n, POST_KERNEL, \ CONFIG_KERNEL_INIT_PRIORITY_DEFAULT, &behavior_mouse_move_driver_api); DT_INST_FOREACH_STATUS_OKAY(KP_INST) - -#endif /* DT_HAS_COMPAT_STATUS_OKAY(DT_DRV_COMPAT) */ diff --git a/app/src/behaviors/behavior_mouse_scroll.c b/app/src/behaviors/behavior_mouse_scroll.c deleted file mode 100644 index d71671724d0b..000000000000 --- a/app/src/behaviors/behavior_mouse_scroll.c +++ /dev/null @@ -1,58 +0,0 @@ -/* - * Copyright (c) 2021 The ZMK Contributors - * - * SPDX-License-Identifier: MIT - */ - -#define DT_DRV_COMPAT zmk_behavior_mouse_scroll - -#include -#include -#include - -#include -#include -#include -#include -#include - -LOG_MODULE_DECLARE(zmk, CONFIG_ZMK_LOG_LEVEL); - -#if DT_HAS_COMPAT_STATUS_OKAY(DT_DRV_COMPAT) - -static int behavior_mouse_scroll_init(const struct device *dev) { return 0; }; - -static int on_keymap_binding_pressed(struct zmk_behavior_binding *binding, - struct zmk_behavior_binding_event event) { - LOG_DBG("position %d keycode 0x%02X", event.position, binding->param1); - const struct device *dev = device_get_binding(binding->behavior_dev); - const struct mouse_config *config = dev->config; - return ZMK_EVENT_RAISE(zmk_mouse_scroll_state_changed_from_encoded(binding->param1, *config, - true, event.timestamp)); -} - -static int on_keymap_binding_released(struct zmk_behavior_binding *binding, - struct zmk_behavior_binding_event event) { - LOG_DBG("position %d keycode 0x%02X", event.position, binding->param1); - const struct device *dev = device_get_binding(binding->behavior_dev); - const struct mouse_config *config = dev->config; - return ZMK_EVENT_RAISE(zmk_mouse_scroll_state_changed_from_encoded(binding->param1, *config, - false, event.timestamp)); -} - -static const struct behavior_driver_api behavior_mouse_scroll_driver_api = { - .binding_pressed = on_keymap_binding_pressed, .binding_released = on_keymap_binding_released}; - -#define KP_INST(n) \ - static struct mouse_config behavior_mouse_scroll_config_##n = { \ - .delay_ms = DT_INST_PROP(n, delay_ms), \ - .time_to_max_speed_ms = DT_INST_PROP(n, time_to_max_speed_ms), \ - .acceleration_exponent = DT_INST_PROP(n, acceleration_exponent), \ - }; \ - DEVICE_DT_INST_DEFINE(n, behavior_mouse_scroll_init, NULL, NULL, \ - &behavior_mouse_scroll_config_##n, APPLICATION, \ - CONFIG_KERNEL_INIT_PRIORITY_DEFAULT, &behavior_mouse_scroll_driver_api); - -DT_INST_FOREACH_STATUS_OKAY(KP_INST) - -#endif /* DT_HAS_COMPAT_STATUS_OKAY(DT_DRV_COMPAT) */ diff --git a/app/src/events/mouse_move_state_changed.c b/app/src/events/mouse_move_state_changed.c deleted file mode 100644 index 07a789b24ef8..000000000000 --- a/app/src/events/mouse_move_state_changed.c +++ /dev/null @@ -1,10 +0,0 @@ -/* - * Copyright (c) 2020 The ZMK Contributors - * - * SPDX-License-Identifier: MIT - */ - -#include -#include - -ZMK_EVENT_IMPL(zmk_mouse_move_state_changed); diff --git a/app/src/events/mouse_scroll_state_changed.c b/app/src/events/mouse_scroll_state_changed.c deleted file mode 100644 index f6216d39d0c3..000000000000 --- a/app/src/events/mouse_scroll_state_changed.c +++ /dev/null @@ -1,10 +0,0 @@ -/* - * Copyright (c) 2020 The ZMK Contributors - * - * SPDX-License-Identifier: MIT - */ - -#include -#include - -ZMK_EVENT_IMPL(zmk_mouse_scroll_state_changed); diff --git a/app/src/events/mouse_tick.c b/app/src/events/mouse_tick.c deleted file mode 100644 index 846b3faee89d..000000000000 --- a/app/src/events/mouse_tick.c +++ /dev/null @@ -1,10 +0,0 @@ -/* - * Copyright (c) 2020 The ZMK Contributors - * - * SPDX-License-Identifier: MIT - */ - -#include -#include - -ZMK_EVENT_IMPL(zmk_mouse_tick); diff --git a/app/src/hid.c b/app/src/hid.c index 3aefe4d29ae4..8a0589573315 100644 --- a/app/src/hid.c +++ b/app/src/hid.c @@ -431,31 +431,33 @@ int zmk_hid_mouse_buttons_release(zmk_mouse_button_flags_t buttons) { void zmk_hid_mouse_movement_set(int16_t x, int16_t y) { mouse_report.body.d_x = x; mouse_report.body.d_y = y; - LOG_DBG("Mouse movement set to 0x%02X 0x%02X ", mouse_report.body.d_x, mouse_report.body.d_y); + LOG_DBG("Mouse movement set to %d/%d", mouse_report.body.d_x, mouse_report.body.d_y); } void zmk_hid_mouse_movement_update(int16_t x, int16_t y) { mouse_report.body.d_x += x; mouse_report.body.d_y += y; - LOG_DBG("Mouse movement updated to 0x%02X 0x%02X ", mouse_report.body.d_x, - mouse_report.body.d_y); + LOG_DBG("Mouse movement updated to %d/%d", mouse_report.body.d_x, mouse_report.body.d_y); } void zmk_hid_mouse_scroll_set(int8_t x, int8_t y) { mouse_report.body.d_scroll_x = x; mouse_report.body.d_scroll_y = y; - LOG_DBG("Mouse scroll set to X: 0x%02X Y: 0x%02X", mouse_report.body.d_scroll_x, + LOG_DBG("Mouse scroll set to %d/%d", mouse_report.body.d_scroll_x, mouse_report.body.d_scroll_y); } void zmk_hid_mouse_scroll_update(int8_t x, int8_t y) { mouse_report.body.d_scroll_x += x; mouse_report.body.d_scroll_y += y; - LOG_DBG("Mouse scroll updated to X: 0x%02X Y: 0x%02X", mouse_report.body.d_scroll_x, + LOG_DBG("Mouse scroll updated to X: %d/%d", mouse_report.body.d_scroll_x, mouse_report.body.d_scroll_y); } -void zmk_hid_mouse_clear() { memset(&mouse_report.body, 0, sizeof(mouse_report.body)); } +void zmk_hid_mouse_clear() { + LOG_DBG("Mouse report cleared"); + memset(&mouse_report.body, 0, sizeof(mouse_report.body)); +} #endif // IS_ENABLED(CONFIG_ZMK_MOUSE) diff --git a/app/src/mouse/Kconfig b/app/src/mouse/Kconfig index 9c40c51f5b96..8eb64f4cc847 100644 --- a/app/src/mouse/Kconfig +++ b/app/src/mouse/Kconfig @@ -4,6 +4,8 @@ config ZMK_MOUSE bool "Enable ZMK mouse emulation" default n + select INPUT + select INPUT_THREAD_PRIORITY_OVERRIDE config ZMK_MOUSE_TICK_DURATION int "Mouse tick duration in ms" diff --git a/app/src/mouse/hid_input_listener.c b/app/src/mouse/hid_input_listener.c new file mode 100644 index 000000000000..6c053755f877 --- /dev/null +++ b/app/src/mouse/hid_input_listener.c @@ -0,0 +1,72 @@ +/* + * Copyright (c) 2020 The ZMK Contributors + * + * SPDX-License-Identifier: MIT + */ + +#include +#include +#include + +#include +#include +#include + +void handle_rel_code(struct input_event *evt) { + switch (evt->code) { + case INPUT_REL_X: + zmk_hid_mouse_movement_update(evt->value, 0); + break; + case INPUT_REL_Y: + zmk_hid_mouse_movement_update(0, evt->value); + break; + case INPUT_REL_WHEEL: + zmk_hid_mouse_scroll_update(0, evt->value); + break; + case INPUT_REL_HWHEEL: + zmk_hid_mouse_scroll_update(evt->value, 0); + break; + default: + break; + } +} + +void handle_key_code(struct input_event *evt) { + int8_t btn; + + switch (evt->code) { + case INPUT_BTN_0: + case INPUT_BTN_1: + case INPUT_BTN_2: + case INPUT_BTN_3: + case INPUT_BTN_4: + btn = evt->code - INPUT_BTN_0; + if (evt->value > 0) { + zmk_hid_mouse_button_press(btn); + } else { + zmk_hid_mouse_button_release(btn); + } + break; + default: + break; + } +} + +void input_handler(struct input_event *evt) { + switch (evt->type) { + case INPUT_EV_REL: + handle_rel_code(evt); + break; + case INPUT_EV_KEY: + handle_key_code(evt); + break; + } + + if (evt->sync) { + zmk_endpoints_send_mouse_report(); + zmk_hid_mouse_scroll_set(0, 0); + zmk_hid_mouse_movement_set(0, 0); + } +} + +INPUT_CALLBACK_DEFINE(NULL, input_handler); diff --git a/app/src/mouse/key_listener.c b/app/src/mouse/key_listener.c deleted file mode 100644 index bd06efebd9f5..000000000000 --- a/app/src/mouse/key_listener.c +++ /dev/null @@ -1,201 +0,0 @@ -/* - * Copyright (c) 2021 The ZMK Contributors - * - * SPDX-License-Identifier: MIT - */ - -#include -#include - -LOG_MODULE_DECLARE(zmk, CONFIG_ZMK_LOG_LEVEL); - -#include -#include -#include -#include -#include -#include -#include -#include - -#if !defined(CONFIG_ZMK_SPLIT) || defined(CONFIG_ZMK_SPLIT_ROLE_CENTRAL) -static struct vector2d move_speed = {0}; -static struct vector2d scroll_speed = {0}; -static struct mouse_config move_config = (struct mouse_config){0}; -static struct mouse_config scroll_config = (struct mouse_config){0}; -static struct mouse_times start_times = (struct mouse_times){0}; - -bool equals(const struct mouse_config *one, const struct mouse_config *other) { - return one->delay_ms == other->delay_ms && - one->time_to_max_speed_ms == other->time_to_max_speed_ms && - one->acceleration_exponent == other->acceleration_exponent; -} - -static void clear_mouse_state(struct k_work *work) { - move_speed = (struct vector2d){0}; - scroll_speed = (struct vector2d){0}; - start_times = (struct mouse_times){0}; - zmk_hid_mouse_movement_set(0, 0); - zmk_hid_mouse_scroll_set(0, 0); - LOG_DBG("Clearing state"); -} - -K_WORK_DEFINE(mouse_clear, &clear_mouse_state); - -void mouse_clear_cb(struct k_timer *dummy) { - k_work_submit_to_queue(zmk_mouse_work_q(), &mouse_clear); -} - -static void mouse_tick_timer_handler(struct k_work *work) { - zmk_hid_mouse_movement_set(0, 0); - zmk_hid_mouse_scroll_set(0, 0); - LOG_DBG("Raising mouse tick event"); - ZMK_EVENT_RAISE( - zmk_mouse_tick(move_speed, scroll_speed, move_config, scroll_config, start_times)); - zmk_endpoints_send_mouse_report(); -} - -K_WORK_DEFINE(mouse_tick, &mouse_tick_timer_handler); - -void mouse_timer_cb(struct k_timer *dummy) { - LOG_DBG("Submitting mouse work to queue"); - k_work_submit_to_queue(zmk_mouse_work_q(), &mouse_tick); -} - -K_TIMER_DEFINE(mouse_timer, mouse_timer_cb, mouse_clear_cb); - -static struct { - int m_x; - int m_y; - int s_x; - int s_y; -} mouse_timer_ref_counts = {0, 0, 0, 0}; - -void mouse_timer_ref(bool m_x, bool m_y, bool s_x, bool s_y) { - if (m_x && mouse_timer_ref_counts.m_x == 0) { - start_times.m_x = k_uptime_get(); - } - if (m_y && mouse_timer_ref_counts.m_y == 0) { - start_times.m_y = k_uptime_get(); - } - if (s_x && mouse_timer_ref_counts.s_x == 0) { - start_times.s_x = k_uptime_get(); - } - if (s_y && mouse_timer_ref_counts.s_y == 0) { - start_times.s_y = k_uptime_get(); - } - if (mouse_timer_ref_counts.m_x == 0 && mouse_timer_ref_counts.m_y == 0 && - mouse_timer_ref_counts.s_x == 0 && mouse_timer_ref_counts.s_y == 0) { - k_timer_start(&mouse_timer, K_NO_WAIT, K_MSEC(CONFIG_ZMK_MOUSE_TICK_DURATION)); - } - if (m_x) { - mouse_timer_ref_counts.m_x++; - } - if (m_y) { - mouse_timer_ref_counts.m_y++; - } - if (s_x) { - mouse_timer_ref_counts.s_x++; - } - if (s_y) { - mouse_timer_ref_counts.s_y++; - } -} - -void mouse_timer_unref(bool m_x, bool m_y, bool s_x, bool s_y) { - if (m_x && mouse_timer_ref_counts.m_x > 0) { - mouse_timer_ref_counts.m_x--; - } - if (m_y && mouse_timer_ref_counts.m_y > 0) { - mouse_timer_ref_counts.m_y--; - } - if (s_x && mouse_timer_ref_counts.s_x > 0) { - mouse_timer_ref_counts.s_x--; - } - if (s_y && mouse_timer_ref_counts.s_y > 0) { - mouse_timer_ref_counts.s_y--; - } - if (mouse_timer_ref_counts.m_x == 0 && mouse_timer_ref_counts.m_y == 0 && - mouse_timer_ref_counts.s_x == 0 && mouse_timer_ref_counts.s_y == 0) { - k_timer_stop(&mouse_timer); - } -} - -static void listener_mouse_move_pressed(const struct zmk_mouse_move_state_changed *ev) { - move_speed.x += ev->max_speed.x; - move_speed.y += ev->max_speed.y; - mouse_timer_ref(ev->max_speed.x != 0, ev->max_speed.y != 0, false, false); -} - -static void listener_mouse_move_released(const struct zmk_mouse_move_state_changed *ev) { - move_speed.x -= ev->max_speed.x; - move_speed.y -= ev->max_speed.y; - mouse_timer_unref(ev->max_speed.x != 0, ev->max_speed.y != 0, false, false); -} - -static void listener_mouse_scroll_pressed(const struct zmk_mouse_scroll_state_changed *ev) { - scroll_speed.x += ev->max_speed.x; - scroll_speed.y += ev->max_speed.y; - mouse_timer_ref(false, false, ev->max_speed.x != 0, ev->max_speed.y != 0); -} - -static void listener_mouse_scroll_released(const struct zmk_mouse_scroll_state_changed *ev) { - scroll_speed.x -= ev->max_speed.x; - scroll_speed.y -= ev->max_speed.y; - mouse_timer_unref(false, false, ev->max_speed.x != 0, ev->max_speed.y != 0); -} - -static void listener_mouse_button_pressed(const struct zmk_mouse_button_state_changed *ev) { - LOG_DBG("buttons: 0x%02X", ev->buttons); - zmk_hid_mouse_buttons_press(ev->buttons); - zmk_endpoints_send_mouse_report(); -} - -static void listener_mouse_button_released(const struct zmk_mouse_button_state_changed *ev) { - LOG_DBG("buttons: 0x%02X", ev->buttons); - zmk_hid_mouse_buttons_release(ev->buttons); - zmk_endpoints_send_mouse_report(); -} - -int mouse_listener(const zmk_event_t *eh) { - const struct zmk_mouse_move_state_changed *mmv_ev = as_zmk_mouse_move_state_changed(eh); - if (mmv_ev) { - if (!equals(&move_config, &(mmv_ev->config))) - move_config = mmv_ev->config; - - if (mmv_ev->state) { - listener_mouse_move_pressed(mmv_ev); - } else { - listener_mouse_move_released(mmv_ev); - } - return 0; - } - const struct zmk_mouse_scroll_state_changed *msc_ev = as_zmk_mouse_scroll_state_changed(eh); - if (msc_ev) { - if (!equals(&scroll_config, &(msc_ev->config))) - scroll_config = msc_ev->config; - if (msc_ev->state) { - listener_mouse_scroll_pressed(msc_ev); - } else { - listener_mouse_scroll_released(msc_ev); - } - return 0; - } - const struct zmk_mouse_button_state_changed *mbt_ev = as_zmk_mouse_button_state_changed(eh); - if (mbt_ev) { - if (mbt_ev->state) { - listener_mouse_button_pressed(mbt_ev); - } else { - listener_mouse_button_released(mbt_ev); - } - return 0; - } - return 0; -} - -ZMK_LISTENER(mouse_listener, mouse_listener); -ZMK_SUBSCRIPTION(mouse_listener, zmk_mouse_button_state_changed); -ZMK_SUBSCRIPTION(mouse_listener, zmk_mouse_move_state_changed); -ZMK_SUBSCRIPTION(mouse_listener, zmk_mouse_scroll_state_changed); - -#endif /*!defined(CONFIG_ZMK_SPLIT) || defined(CONFIG_ZMK_SPLIT_ROLE_CENTRAL) */ diff --git a/app/src/mouse/main.c b/app/src/mouse/main.c index e5b5252f8de8..e1227425ea58 100644 --- a/app/src/mouse/main.c +++ b/app/src/mouse/main.c @@ -20,7 +20,7 @@ struct k_work_q *zmk_mouse_work_q() { #endif } -int zmk_mouse_init() { +int zmk_mouse_init(void) { #if IS_ENABLED(CONFIG_ZMK_MOUSE_WORK_QUEUE_DEDICATED) k_work_queue_start(&mouse_work_q, mouse_work_stack_area, K_THREAD_STACK_SIZEOF(mouse_work_stack_area), diff --git a/app/src/mouse/tick_listener.c b/app/src/mouse/tick_listener.c deleted file mode 100644 index 90aaf1119dd9..000000000000 --- a/app/src/mouse/tick_listener.c +++ /dev/null @@ -1,111 +0,0 @@ -/* - * Copyright (c) 2020 The ZMK Contributors - * - * SPDX-License-Identifier: MIT - */ - -#include - -LOG_MODULE_DECLARE(zmk, CONFIG_ZMK_LOG_LEVEL); - -#include -#include -#include -#include -#include - -#include // CLAMP - -#if !defined(CONFIG_ZMK_SPLIT) || defined(CONFIG_ZMK_SPLIT_ROLE_CENTRAL) -#if CONFIG_MINIMAL_LIBC -static float powf(float base, float exponent) { - // poor man's power implementation rounds the exponent down to the nearest integer. - float power = 1.0f; - for (; exponent >= 1.0f; exponent--) { - power = power * base; - } - return power; -} -#else -#include -#endif - -struct vector2d move_remainder = {0}; -struct vector2d scroll_remainder = {0}; - -static int64_t ms_since_start(int64_t start, int64_t now, int64_t delay) { - int64_t move_duration = now - (start + delay); - // start can be in the future if there's a delay - if (move_duration < 0) { - move_duration = 0; - } - return move_duration; -} - -static float speed(const struct mouse_config *config, float max_speed, int64_t duration_ms) { - // Calculate the speed based on MouseKeysAccel - // See https://en.wikipedia.org/wiki/Mouse_keys - if (duration_ms > config->time_to_max_speed_ms || config->time_to_max_speed_ms == 0 || - config->acceleration_exponent == 0) { - return max_speed; - } - float time_fraction = (float)duration_ms / config->time_to_max_speed_ms; - return max_speed * powf(time_fraction, config->acceleration_exponent); -} - -static void track_remainder(float *move, float *remainder) { - float new_move = *move + *remainder; - *remainder = new_move - (int)new_move; - *move = (int)new_move; -} - -static struct vector2d update_movement(struct vector2d *remainder, - const struct mouse_config *config, struct vector2d max_speed, - int64_t now, int64_t start_time_x, int64_t start_time_y) { - struct vector2d move = {0}; - if (max_speed.x == 0 && max_speed.y == 0) { - *remainder = (struct vector2d){0}; - return move; - } - - int64_t move_duration_x = ms_since_start(start_time_x, now, config->delay_ms); - int64_t move_duration_y = ms_since_start(start_time_y, now, config->delay_ms); - move = (struct vector2d){ - .x = speed(config, max_speed.x, move_duration_x) * CONFIG_ZMK_MOUSE_TICK_DURATION / 1000, - .y = speed(config, max_speed.y, move_duration_y) * CONFIG_ZMK_MOUSE_TICK_DURATION / 1000, - }; - - track_remainder(&(move.x), &(remainder->x)); - track_remainder(&(move.y), &(remainder->y)); - - return move; -} - -static void mouse_tick_handler(const struct zmk_mouse_tick *tick) { - LOG_INF("tick start times: %lld %lld %lld %lld", tick->start_times.m_x, tick->start_times.m_y, - tick->start_times.s_x, tick->start_times.s_y); - struct vector2d move = - update_movement(&move_remainder, &(tick->move_config), tick->max_move, tick->timestamp, - tick->start_times.m_x, tick->start_times.m_y); - zmk_hid_mouse_movement_update((int16_t)CLAMP(move.x, INT16_MIN, INT16_MAX), - (int16_t)CLAMP(move.y, INT16_MIN, INT16_MAX)); - struct vector2d scroll = - update_movement(&scroll_remainder, &(tick->scroll_config), tick->max_scroll, - tick->timestamp, tick->start_times.s_x, tick->start_times.s_y); - zmk_hid_mouse_scroll_update((int8_t)CLAMP(scroll.x, INT8_MIN, INT8_MAX), - (int8_t)CLAMP(scroll.y, INT8_MIN, INT8_MAX)); -} - -int zmk_mouse_tick_listener(const zmk_event_t *eh) { - const struct zmk_mouse_tick *tick = as_zmk_mouse_tick(eh); - if (tick) { - mouse_tick_handler(tick); - return 0; - } - return 0; -} - -ZMK_LISTENER(zmk_mouse_tick_listener, zmk_mouse_tick_listener); -ZMK_SUBSCRIPTION(zmk_mouse_tick_listener, zmk_mouse_tick); - -#endif /* !defined(CONFIG_ZMK_SPLIT) || defined(CONFIG_ZMK_SPLIT_ROLE_CENTRAL) */ diff --git a/app/tests/mouse-keys/mmv/events.patterns b/app/tests/mouse-keys/mmv/events.patterns deleted file mode 100644 index 833100f6ac43..000000000000 --- a/app/tests/mouse-keys/mmv/events.patterns +++ /dev/null @@ -1 +0,0 @@ -s/.*hid_listener_keycode_//p \ No newline at end of file diff --git a/app/tests/mouse-keys/mmv/keycode_events.snapshot b/app/tests/mouse-keys/mmv/keycode_events.snapshot deleted file mode 100644 index 259501ba3d96..000000000000 --- a/app/tests/mouse-keys/mmv/keycode_events.snapshot +++ /dev/null @@ -1,2 +0,0 @@ -pressed: usage_page 0x07 keycode 0x05 implicit_mods 0x00 explicit_mods 0x00 -released: usage_page 0x07 keycode 0x05 implicit_mods 0x00 explicit_mods 0x00 diff --git a/app/tests/mouse-keys/mouse-move/move_diagonal/events.patterns b/app/tests/mouse-keys/mouse-move/move_diagonal/events.patterns new file mode 100644 index 000000000000..812126fb8285 --- /dev/null +++ b/app/tests/mouse-keys/mouse-move/move_diagonal/events.patterns @@ -0,0 +1 @@ +s/.*hid_mouse_//p \ No newline at end of file diff --git a/app/tests/mouse-keys/mouse-move/move_diagonal/keycode_events.snapshot b/app/tests/mouse-keys/mouse-move/move_diagonal/keycode_events.snapshot new file mode 100644 index 000000000000..91c4f16b9c03 --- /dev/null +++ b/app/tests/mouse-keys/mouse-move/move_diagonal/keycode_events.snapshot @@ -0,0 +1,22 @@ +movement_update: Mouse movement updated to -1/0 +scroll_set: Mouse scroll set to 0/0 +movement_set: Mouse movement set to 0/0 +movement_update: Mouse movement updated to -2/0 +movement_update: Mouse movement updated to -2/-2 +scroll_set: Mouse scroll set to 0/0 +movement_set: Mouse movement set to 0/0 +movement_update: Mouse movement updated to -2/0 +movement_update: Mouse movement updated to -2/-2 +scroll_set: Mouse scroll set to 0/0 +movement_set: Mouse movement set to 0/0 +movement_update: Mouse movement updated to -3/0 +movement_update: Mouse movement updated to -3/-2 +scroll_set: Mouse scroll set to 0/0 +movement_set: Mouse movement set to 0/0 +movement_update: Mouse movement updated to -3/0 +movement_update: Mouse movement updated to -3/-3 +scroll_set: Mouse scroll set to 0/0 +movement_set: Mouse movement set to 0/0 +movement_update: Mouse movement updated to 0/-3 +scroll_set: Mouse scroll set to 0/0 +movement_set: Mouse movement set to 0/0 diff --git a/app/tests/mouse-keys/mouse-move/move_diagonal/native_posix_64.keymap b/app/tests/mouse-keys/mouse-move/move_diagonal/native_posix_64.keymap new file mode 100644 index 000000000000..9dfa5b2a81c1 --- /dev/null +++ b/app/tests/mouse-keys/mouse-move/move_diagonal/native_posix_64.keymap @@ -0,0 +1,28 @@ +#include +#include +#include +#include + +/ { + keymap { + compatible = "zmk,keymap"; + label ="Default keymap"; + + default_layer { + bindings = < + &mmv MOVE_LEFT &mmv MOVE_UP + &none &none + >; + }; + }; +}; + + +&kscan { + events = < + ZMK_MOCK_PRESS(0,0,10) + ZMK_MOCK_PRESS(0,1,100) + ZMK_MOCK_RELEASE(0,0,10) + ZMK_MOCK_RELEASE(0,1,10) + >; +}; \ No newline at end of file diff --git a/app/tests/mouse-keys/mouse-move/move_x/events.patterns b/app/tests/mouse-keys/mouse-move/move_x/events.patterns new file mode 100644 index 000000000000..812126fb8285 --- /dev/null +++ b/app/tests/mouse-keys/mouse-move/move_x/events.patterns @@ -0,0 +1 @@ +s/.*hid_mouse_//p \ No newline at end of file diff --git a/app/tests/mouse-keys/mouse-move/move_x/keycode_events.snapshot b/app/tests/mouse-keys/mouse-move/move_x/keycode_events.snapshot new file mode 100644 index 000000000000..027e83e9f066 --- /dev/null +++ b/app/tests/mouse-keys/mouse-move/move_x/keycode_events.snapshot @@ -0,0 +1,27 @@ +movement_update: Mouse movement updated to -1/0 +scroll_set: Mouse scroll set to 0/0 +movement_set: Mouse movement set to 0/0 +movement_update: Mouse movement updated to -2/0 +scroll_set: Mouse scroll set to 0/0 +movement_set: Mouse movement set to 0/0 +movement_update: Mouse movement updated to -2/0 +scroll_set: Mouse scroll set to 0/0 +movement_set: Mouse movement set to 0/0 +movement_update: Mouse movement updated to -3/0 +scroll_set: Mouse scroll set to 0/0 +movement_set: Mouse movement set to 0/0 +movement_update: Mouse movement updated to 3/0 +scroll_set: Mouse scroll set to 0/0 +movement_set: Mouse movement set to 0/0 +movement_update: Mouse movement updated to 5/0 +scroll_set: Mouse scroll set to 0/0 +movement_set: Mouse movement set to 0/0 +movement_update: Mouse movement updated to 5/0 +scroll_set: Mouse scroll set to 0/0 +movement_set: Mouse movement set to 0/0 +movement_update: Mouse movement updated to 6/0 +scroll_set: Mouse scroll set to 0/0 +movement_set: Mouse movement set to 0/0 +movement_update: Mouse movement updated to 6/0 +scroll_set: Mouse scroll set to 0/0 +movement_set: Mouse movement set to 0/0 \ No newline at end of file diff --git a/app/tests/mouse-keys/mouse-move/move_x/native_posix_64.keymap b/app/tests/mouse-keys/mouse-move/move_x/native_posix_64.keymap new file mode 100644 index 000000000000..aacacebba907 --- /dev/null +++ b/app/tests/mouse-keys/mouse-move/move_x/native_posix_64.keymap @@ -0,0 +1,28 @@ +#include +#include +#include +#include + +/ { + keymap { + compatible = "zmk,keymap"; + label ="Default keymap"; + + default_layer { + bindings = < + &mmv MOVE_LEFT &mmv MOVE_RIGHT + &none &none + >; + }; + }; +}; + + +&kscan { + events = < + ZMK_MOCK_PRESS(0,0,100) + ZMK_MOCK_RELEASE(0,0,10) + ZMK_MOCK_PRESS(0,1,100) + ZMK_MOCK_RELEASE(0,1,10) + >; +}; \ No newline at end of file diff --git a/app/tests/mouse-keys/mouse-move/move_y/events.patterns b/app/tests/mouse-keys/mouse-move/move_y/events.patterns new file mode 100644 index 000000000000..812126fb8285 --- /dev/null +++ b/app/tests/mouse-keys/mouse-move/move_y/events.patterns @@ -0,0 +1 @@ +s/.*hid_mouse_//p \ No newline at end of file diff --git a/app/tests/mouse-keys/mouse-move/move_y/keycode_events.snapshot b/app/tests/mouse-keys/mouse-move/move_y/keycode_events.snapshot new file mode 100644 index 000000000000..178d5e7453a3 --- /dev/null +++ b/app/tests/mouse-keys/mouse-move/move_y/keycode_events.snapshot @@ -0,0 +1,27 @@ +movement_update: Mouse movement updated to 0/-1 +scroll_set: Mouse scroll set to 0/0 +movement_set: Mouse movement set to 0/0 +movement_update: Mouse movement updated to 0/-2 +scroll_set: Mouse scroll set to 0/0 +movement_set: Mouse movement set to 0/0 +movement_update: Mouse movement updated to 0/-2 +scroll_set: Mouse scroll set to 0/0 +movement_set: Mouse movement set to 0/0 +movement_update: Mouse movement updated to 0/-3 +scroll_set: Mouse scroll set to 0/0 +movement_set: Mouse movement set to 0/0 +movement_update: Mouse movement updated to 0/3 +scroll_set: Mouse scroll set to 0/0 +movement_set: Mouse movement set to 0/0 +movement_update: Mouse movement updated to 0/5 +scroll_set: Mouse scroll set to 0/0 +movement_set: Mouse movement set to 0/0 +movement_update: Mouse movement updated to 0/5 +scroll_set: Mouse scroll set to 0/0 +movement_set: Mouse movement set to 0/0 +movement_update: Mouse movement updated to 0/6 +scroll_set: Mouse scroll set to 0/0 +movement_set: Mouse movement set to 0/0 +movement_update: Mouse movement updated to 0/6 +scroll_set: Mouse scroll set to 0/0 +movement_set: Mouse movement set to 0/0 diff --git a/app/tests/mouse-keys/mmv/native_posix.keymap b/app/tests/mouse-keys/mouse-move/move_y/native_posix_64.keymap similarity index 79% rename from app/tests/mouse-keys/mmv/native_posix.keymap rename to app/tests/mouse-keys/mouse-move/move_y/native_posix_64.keymap index dbb3f014c9f7..1d914136e280 100644 --- a/app/tests/mouse-keys/mmv/native_posix.keymap +++ b/app/tests/mouse-keys/mouse-move/move_y/native_posix_64.keymap @@ -10,7 +10,7 @@ default_layer { bindings = < - &mmv MOVE_LEFT &none + &mmv MOVE_UP &mmv MOVE_DOWN &none &none >; }; @@ -22,5 +22,7 @@ events = < ZMK_MOCK_PRESS(0,0,100) ZMK_MOCK_RELEASE(0,0,10) + ZMK_MOCK_PRESS(0,1,100) + ZMK_MOCK_RELEASE(0,1,10) >; }; \ No newline at end of file