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
|
||||
required: true
|
||||
const: 2
|
||||
modifier-key:
|
||||
type: int
|
||||
default: -1
|
||||
mod-timeout-ms:
|
||||
type: int
|
||||
default: -1
|
||||
|
||||
sensor-binding-cells:
|
||||
- param1
|
||||
|
|
|
@ -18,12 +18,54 @@ LOG_MODULE_DECLARE(zmk, CONFIG_ZMK_LOG_LEVEL);
|
|||
|
||||
#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,
|
||||
const struct device *sensor, int64_t timestamp) {
|
||||
struct sensor_value value;
|
||||
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;
|
||||
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);
|
||||
|
||||
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));
|
||||
|
||||
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?
|
||||
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 = {
|
||||
.sensor_binding_triggered = on_sensor_binding_triggered};
|
||||
.sensor_binding_triggered = on_sensor_binding_triggered,
|
||||
};
|
||||
|
||||
#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, \
|
||||
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);
|
||||
|
||||
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.
|
||||
|
||||
### 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
|
||||
|
||||
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