Skip to content

Commit

Permalink
fixup! feat(mouse): Add mouse move and scroll support
Browse files Browse the repository at this point in the history
  • Loading branch information
minhe7735 authored Nov 22, 2024
1 parent 053913d commit ac7c0f2
Show file tree
Hide file tree
Showing 19 changed files with 575 additions and 27 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -6,3 +6,16 @@ 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
199 changes: 176 additions & 23 deletions app/src/mouse/input_processor_temp_layer.c
Original file line number Diff line number Diff line change
Expand Up @@ -9,52 +9,205 @@
#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);

#include <zmk/keymap.h>
/* 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;
}

struct k_work_delayable layer_disable_works[ZMK_KEYMAP_LAYERS_LEN];
/* 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;
}

static void layer_disable_cb(struct k_work *work) {
/* 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)) {
zmk_keymap_layer_deactivate(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;
}

static int tl_handle_event(const struct device *dev, struct input_event *event, uint32_t param1,
uint32_t param2, struct zmk_input_processor_state *state) {
if (param1 >= ZMK_KEYMAP_LAYERS_LEN) {
LOG_ERR("Passed an invalid layer id %d", param1);
/* 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;
}

if (!zmk_keymap_layer_active(param1)) {
zmk_keymap_layer_activate(param1);
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));
}

k_work_reschedule(&layer_disable_works[param1], K_MSEC(param2));
return 0;
}

static struct zmk_input_processor_driver_api tl_driver_api = {
.handle_event = tl_handle_event,
};

static int tl_init(const struct device *dev) {
for (int i = 0; i < ZMK_KEYMAP_LAYERS_LEN; i++) {
k_work_init_delayable(&layer_disable_works[i], layer_disable_cb);
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;
}

#define TL_INST(n) \
DEVICE_DT_INST_DEFINE(n, &tl_init, NULL, NULL, NULL, POST_KERNEL, \
CONFIG_KERNEL_INIT_PRIORITY_DEFAULT, &tl_driver_api);
/* 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(TL_INST)
DT_INST_FOREACH_STATUS_OKAY(TEMP_LAYER_INST)
Original file line number Diff line number Diff line change
@@ -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
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,8 @@
&kscan {
events = <
ZMK_MOCK_PRESS(0,0,100)
ZMK_MOCK_RELEASE(0,0,1000)
ZMK_MOCK_RELEASE(0,0,150)
ZMK_MOCK_PRESS(1,0,200)
ZMK_MOCK_RELEASE(1,0,250)
>;
};
};
Original file line number Diff line number Diff line change
@@ -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
Original file line number Diff line number Diff line change
@@ -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
Original file line number Diff line number Diff line change
@@ -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
Original file line number Diff line number Diff line change
@@ -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)
>;
};
Original file line number Diff line number Diff line change
@@ -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
Original file line number Diff line number Diff line change
@@ -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
Original file line number Diff line number Diff line change
@@ -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
Loading

0 comments on commit ac7c0f2

Please sign in to comment.