feat(supertab): modkey timeout for rotate key press
This commit is contained in:
parent
7b2edbad43
commit
53476ca289
5 changed files with 142 additions and 3 deletions
|
@ -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
|
||||||
|
|
|
@ -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)
|
||||||
|
|
BIN
docs/docs/assets/features/encoders/highlighter.gif
Normal file
BIN
docs/docs/assets/features/encoders/highlighter.gif
Normal file
Binary file not shown.
After Width: | Height: | Size: 13 MiB |
BIN
docs/docs/assets/features/encoders/supertab.gif
Normal file
BIN
docs/docs/assets/features/encoders/supertab.gif
Normal file
Binary file not shown.
After Width: | Height: | Size: 13 MiB |
|
@ -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)>;
|
||||||
|
```
|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
|
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>;
|
||||||
|
```
|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
## 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.
|
||||||
|
|
Loading…
Add table
Reference in a new issue