feat(supertab): modkey timeout for rotate key press

This commit is contained in:
Bernhard Urban-Forster 2021-12-30 20:39:10 +01:00
parent 7b2edbad43
commit 53476ca289
5 changed files with 142 additions and 3 deletions

View file

@ -13,6 +13,12 @@ properties:
type: int type: int
required: true required: true
const: 2 const: 2
modifier-key:
type: int
default: -1
mod-timeout-ms:
type: int
default: -1
sensor-binding-cells: sensor-binding-cells:
- param1 - param1

View file

@ -18,12 +18,54 @@ LOG_MODULE_DECLARE(zmk, CONFIG_ZMK_LOG_LEVEL);
#if DT_HAS_COMPAT_STATUS_OKAY(DT_DRV_COMPAT) #if DT_HAS_COMPAT_STATUS_OKAY(DT_DRV_COMPAT)
static int behavior_sensor_rotate_key_press_init(const struct device *dev) { return 0; }; #define ZMK_BHV_SENSOR_MAX_MODS 2
struct behavior_sensor_rotate_key_press_config {
int modifier_key;
int mod_timeout_ms;
};
struct active_mod_press {
struct k_delayed_work work;
const struct behavior_sensor_rotate_key_press_config *config;
int64_t last_timestamp;
};
struct active_mod_press active_mod_presses[ZMK_BHV_SENSOR_MAX_MODS] = {};
void behavior_sensor_rotate_key_press_work_handler(struct k_work *item) {
struct active_mod_press *c = CONTAINER_OF(item, struct active_mod_press, work);
const struct behavior_sensor_rotate_key_press_config *cfg = c->config;
/* timeout expired, release modifier */
ZMK_EVENT_RAISE(
zmk_keycode_state_changed_from_encoded(cfg->modifier_key, false, k_uptime_get()));
c->config = NULL;
c->last_timestamp = 0;
}
static int behavior_sensor_rotate_key_press_init(const struct device *dev) {
static bool init_first_run = true;
if (init_first_run) {
init_first_run = false;
for (int i = 0; i < ZMK_BHV_SENSOR_MAX_MODS; i++) {
k_delayed_work_init(&active_mod_presses[i].work,
behavior_sensor_rotate_key_press_work_handler);
active_mod_presses[i].config = NULL;
active_mod_presses[i].last_timestamp = 0;
}
}
return 0;
}
static int on_sensor_binding_triggered(struct zmk_behavior_binding *binding, static int on_sensor_binding_triggered(struct zmk_behavior_binding *binding,
const struct device *sensor, int64_t timestamp) { const struct device *sensor, int64_t timestamp) {
struct sensor_value value; struct sensor_value value;
int err; int err;
const struct device *dev = device_get_binding(binding->behavior_dev);
const struct behavior_sensor_rotate_key_press_config *cfg = dev->config;
uint32_t keycode; uint32_t keycode;
LOG_DBG("inc keycode 0x%02X dec keycode 0x%02X", binding->param1, binding->param2); LOG_DBG("inc keycode 0x%02X dec keycode 0x%02X", binding->param1, binding->param2);
@ -47,8 +89,52 @@ static int on_sensor_binding_triggered(struct zmk_behavior_binding *binding,
LOG_DBG("SEND %d", keycode); LOG_DBG("SEND %d", keycode);
bool mod_timeout_enabled = cfg->mod_timeout_ms != -1;
struct active_mod_press *c = NULL;
if (mod_timeout_enabled) {
// lookup slot
for (int i = 0; i < ZMK_BHV_SENSOR_MAX_MODS; i++) {
if (active_mod_presses[i].config == cfg) {
c = &active_mod_presses[i];
c->config = cfg;
}
}
// nothing found, take a slot
if (c == NULL) {
for (int i = 0; i < ZMK_BHV_SENSOR_MAX_MODS; i++) {
if (active_mod_presses[i].config == NULL) {
c = &active_mod_presses[i];
c->config = cfg;
}
}
}
if (c == NULL) {
LOG_WRN("increase ZMK_BHV_SENSOR_MAX_MODS");
}
if ((timestamp - c->last_timestamp) < cfg->mod_timeout_ms) {
/* another input, restart timeout */
k_delayed_work_cancel(&c->work);
} else {
/* first time, activate modifier key */
ZMK_EVENT_RAISE(
zmk_keycode_state_changed_from_encoded(cfg->modifier_key, true, timestamp));
// TODO: Better way to do this?
k_msleep(5);
}
}
ZMK_EVENT_RAISE(zmk_keycode_state_changed_from_encoded(keycode, true, timestamp)); ZMK_EVENT_RAISE(zmk_keycode_state_changed_from_encoded(keycode, true, timestamp));
if (mod_timeout_enabled) {
c->last_timestamp = timestamp;
k_delayed_work_submit(&c->work, K_MSEC(cfg->mod_timeout_ms));
}
// TODO: Better way to do this? // TODO: Better way to do this?
k_msleep(5); k_msleep(5);
@ -56,11 +142,18 @@ static int on_sensor_binding_triggered(struct zmk_behavior_binding *binding,
} }
static const struct behavior_driver_api behavior_sensor_rotate_key_press_driver_api = { static const struct behavior_driver_api behavior_sensor_rotate_key_press_driver_api = {
.sensor_binding_triggered = on_sensor_binding_triggered}; .sensor_binding_triggered = on_sensor_binding_triggered,
};
#define KP_INST(n) \ #define KP_INST(n) \
const struct behavior_sensor_rotate_key_press_config \
behavior_sensor_rotate_key_press_config_##n = { \
.modifier_key = DT_INST_PROP(n, modifier_key), \
.mod_timeout_ms = DT_INST_PROP(n, mod_timeout_ms), \
}; \
DEVICE_DT_INST_DEFINE(n, behavior_sensor_rotate_key_press_init, device_pm_control_nop, NULL, \ DEVICE_DT_INST_DEFINE(n, behavior_sensor_rotate_key_press_init, device_pm_control_nop, NULL, \
NULL, APPLICATION, CONFIG_KERNEL_INIT_PRIORITY_DEFAULT, \ &behavior_sensor_rotate_key_press_config_##n, APPLICATION, \
CONFIG_KERNEL_INIT_PRIORITY_DEFAULT, \
&behavior_sensor_rotate_key_press_driver_api); &behavior_sensor_rotate_key_press_driver_api);
DT_INST_FOREACH_STATUS_OKAY(KP_INST) DT_INST_FOREACH_STATUS_OKAY(KP_INST)

Binary file not shown.

After

Width:  |  Height:  |  Size: 13 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 13 MiB

View file

@ -43,6 +43,46 @@ sensor-bindings = <&inc_dec_kp C_VOL_UP C_VOL_DN &inc_dec_kp PG_UP PG_DN>;
Here, the left encoder is configured to control volume up and down while the right encoder sends either Page Up or Page Down. Here, the left encoder is configured to control volume up and down while the right encoder sends either Page Up or Page Down.
### Modifier with built-in delay
In addition to the keycodes above a modifier (`modifier-key`) can be configured that is pressed simultaneously with the configured keycodes, and then held until another keycode is send via an encoder action or a timeout (`mod-timeout-ms`) is reached.
Example for application switching:
```
sm: supermods {
compatible = "zmk,behavior-sensor-rotate-key-press";
label = "encoder supermod";
#sensor-binding-cells = <2>;
modifier-key = <LWIN>;
mod-timeout-ms = <1200>;
};
[...]
sensor-bindings = <&sm TAB LS(TAB)>;
```
![Application switching with rotary encoder](../assets/features/encoders/supertab.gif)
Example for selecting characters in an text editor:
```
hlm: highlightmods {
compatible = "zmk,behavior-sensor-rotate-key-press";
label = "encoder highlighter";
#sensor-binding-cells = <2>;
modifier-key = <LSHFT>;
mod-timeout-ms = <500>;
};
[...]
sensor-bindings = <&hlm RIGHT LEFT>;
```
![Select characters with rotary encoder](../assets/features/encoders/highlighter.gif)
## Adding Encoder Support ## Adding Encoder Support
See the [New Keyboard Shield](../development/new-shield.md#encoders) documentation for how to add or modify additional encoders to your shield. See the [New Keyboard Shield](../development/new-shield.md#encoders) documentation for how to add or modify additional encoders to your shield.