|
|
@ -10,7 +10,6 @@ |
|
|
|
#include <drivers/behavior.h> |
|
|
|
#include <drivers/behavior.h> |
|
|
|
#include <logging/log.h> |
|
|
|
#include <logging/log.h> |
|
|
|
#include <zmk/behavior.h> |
|
|
|
#include <zmk/behavior.h> |
|
|
|
|
|
|
|
|
|
|
|
#include <zmk/matrix.h> |
|
|
|
#include <zmk/matrix.h> |
|
|
|
#include <zmk/endpoints.h> |
|
|
|
#include <zmk/endpoints.h> |
|
|
|
#include <zmk/event-manager.h> |
|
|
|
#include <zmk/event-manager.h> |
|
|
@ -18,6 +17,7 @@ |
|
|
|
#include <zmk/events/keycode-state-changed.h> |
|
|
|
#include <zmk/events/keycode-state-changed.h> |
|
|
|
#include <zmk/events/modifiers-state-changed.h> |
|
|
|
#include <zmk/events/modifiers-state-changed.h> |
|
|
|
#include <zmk/hid.h> |
|
|
|
#include <zmk/hid.h> |
|
|
|
|
|
|
|
#include <zmk/behavior.h> |
|
|
|
|
|
|
|
|
|
|
|
LOG_MODULE_DECLARE(zmk, CONFIG_ZMK_LOG_LEVEL); |
|
|
|
LOG_MODULE_DECLARE(zmk, CONFIG_ZMK_LOG_LEVEL); |
|
|
|
|
|
|
|
|
|
|
@ -40,10 +40,8 @@ struct behavior_hold_tap_behaviors { |
|
|
|
struct zmk_behavior_binding hold; |
|
|
|
struct zmk_behavior_binding hold; |
|
|
|
}; |
|
|
|
}; |
|
|
|
|
|
|
|
|
|
|
|
typedef k_timeout_t (*timer_func)(); |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
struct behavior_hold_tap_config { |
|
|
|
struct behavior_hold_tap_config { |
|
|
|
timer_func tapping_term_ms; |
|
|
|
int tapping_term_ms; |
|
|
|
struct behavior_hold_tap_behaviors *behaviors; |
|
|
|
struct behavior_hold_tap_behaviors *behaviors; |
|
|
|
enum flavor flavor; |
|
|
|
enum flavor flavor; |
|
|
|
}; |
|
|
|
}; |
|
|
@ -51,8 +49,10 @@ struct behavior_hold_tap_config { |
|
|
|
// this data is specific for each hold-tap
|
|
|
|
// this data is specific for each hold-tap
|
|
|
|
struct active_hold_tap { |
|
|
|
struct active_hold_tap { |
|
|
|
s32_t position; |
|
|
|
s32_t position; |
|
|
|
|
|
|
|
// todo: move these params into the config->behaviors->tap and
|
|
|
|
u32_t param_hold; |
|
|
|
u32_t param_hold; |
|
|
|
u32_t param_tap; |
|
|
|
u32_t param_tap; |
|
|
|
|
|
|
|
s64_t timestamp; |
|
|
|
bool is_decided; |
|
|
|
bool is_decided; |
|
|
|
bool is_hold; |
|
|
|
bool is_hold; |
|
|
|
const struct behavior_hold_tap_config *config; |
|
|
|
const struct behavior_hold_tap_config *config; |
|
|
@ -164,6 +164,7 @@ static struct active_hold_tap *find_hold_tap(u32_t position) { |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
static struct active_hold_tap *store_hold_tap(u32_t position, u32_t param_hold, u32_t param_tap, |
|
|
|
static struct active_hold_tap *store_hold_tap(u32_t position, u32_t param_hold, u32_t param_tap, |
|
|
|
|
|
|
|
s64_t timestamp, |
|
|
|
const struct behavior_hold_tap_config *config) { |
|
|
|
const struct behavior_hold_tap_config *config) { |
|
|
|
for (int i = 0; i < ZMK_BHV_HOLD_TAP_MAX_HELD; i++) { |
|
|
|
for (int i = 0; i < ZMK_BHV_HOLD_TAP_MAX_HELD; i++) { |
|
|
|
if (active_hold_taps[i].position != ZMK_BHV_HOLD_TAP_POSITION_NOT_USED) { |
|
|
|
if (active_hold_taps[i].position != ZMK_BHV_HOLD_TAP_POSITION_NOT_USED) { |
|
|
@ -175,6 +176,7 @@ static struct active_hold_tap *store_hold_tap(u32_t position, u32_t param_hold, |
|
|
|
active_hold_taps[i].config = config; |
|
|
|
active_hold_taps[i].config = config; |
|
|
|
active_hold_taps[i].param_hold = param_hold; |
|
|
|
active_hold_taps[i].param_hold = param_hold; |
|
|
|
active_hold_taps[i].param_tap = param_tap; |
|
|
|
active_hold_taps[i].param_tap = param_tap; |
|
|
|
|
|
|
|
active_hold_taps[i].timestamp = timestamp; |
|
|
|
return &active_hold_taps[i]; |
|
|
|
return &active_hold_taps[i]; |
|
|
|
} |
|
|
|
} |
|
|
|
return NULL; |
|
|
|
return NULL; |
|
|
@ -253,7 +255,7 @@ static inline char *flavor_str(enum flavor flavor) { |
|
|
|
return "UNKNOWN FLAVOR"; |
|
|
|
return "UNKNOWN FLAVOR"; |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
static void decide_hold_tap(struct active_hold_tap *hold_tap, enum decision_moment event) { |
|
|
|
static void decide_hold_tap(struct active_hold_tap *hold_tap, enum decision_moment event_type) { |
|
|
|
if (hold_tap->is_decided) { |
|
|
|
if (hold_tap->is_decided) { |
|
|
|
return; |
|
|
|
return; |
|
|
|
} |
|
|
|
} |
|
|
@ -265,11 +267,11 @@ static void decide_hold_tap(struct active_hold_tap *hold_tap, enum decision_mome |
|
|
|
|
|
|
|
|
|
|
|
switch (hold_tap->config->flavor) { |
|
|
|
switch (hold_tap->config->flavor) { |
|
|
|
case ZMK_BHV_HOLD_TAP_FLAVOR_HOLD_PREFERRED: |
|
|
|
case ZMK_BHV_HOLD_TAP_FLAVOR_HOLD_PREFERRED: |
|
|
|
decide_hold_preferred(hold_tap, event); |
|
|
|
decide_hold_preferred(hold_tap, event_type); |
|
|
|
case ZMK_BHV_HOLD_TAP_FLAVOR_BALANCED: |
|
|
|
case ZMK_BHV_HOLD_TAP_FLAVOR_BALANCED: |
|
|
|
decide_balanced(hold_tap, event); |
|
|
|
decide_balanced(hold_tap, event_type); |
|
|
|
case ZMK_BHV_HOLD_TAP_FLAVOR_TAP_PREFERRED: |
|
|
|
case ZMK_BHV_HOLD_TAP_FLAVOR_TAP_PREFERRED: |
|
|
|
decide_tap_preferred(hold_tap, event); |
|
|
|
decide_tap_preferred(hold_tap, event_type); |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
if (!hold_tap->is_decided) { |
|
|
|
if (!hold_tap->is_decided) { |
|
|
@ -277,26 +279,31 @@ static void decide_hold_tap(struct active_hold_tap *hold_tap, enum decision_mome |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
LOG_DBG("%d decided %s (%s event %d)", hold_tap->position, hold_tap->is_hold ? "hold" : "tap", |
|
|
|
LOG_DBG("%d decided %s (%s event %d)", hold_tap->position, hold_tap->is_hold ? "hold" : "tap", |
|
|
|
flavor_str(hold_tap->config->flavor), event); |
|
|
|
flavor_str(hold_tap->config->flavor), event_type); |
|
|
|
undecided_hold_tap = NULL; |
|
|
|
undecided_hold_tap = NULL; |
|
|
|
|
|
|
|
|
|
|
|
struct zmk_behavior_binding *behavior; |
|
|
|
struct zmk_behavior_binding_event event = { |
|
|
|
|
|
|
|
.position = hold_tap->position, |
|
|
|
|
|
|
|
.timestamp = hold_tap->timestamp, |
|
|
|
|
|
|
|
}; |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
struct zmk_behavior_binding binding; |
|
|
|
if (hold_tap->is_hold) { |
|
|
|
if (hold_tap->is_hold) { |
|
|
|
behavior = &hold_tap->config->behaviors->hold; |
|
|
|
binding.behavior_dev = hold_tap->config->behaviors->hold.behavior_dev; |
|
|
|
struct device *behavior_device = device_get_binding(behavior->behavior_dev); |
|
|
|
binding.param1 = hold_tap->param_hold; |
|
|
|
behavior_keymap_binding_pressed(behavior_device, hold_tap->position, hold_tap->param_hold, |
|
|
|
binding.param2 = 0; |
|
|
|
0); |
|
|
|
|
|
|
|
} else { |
|
|
|
} else { |
|
|
|
behavior = &hold_tap->config->behaviors->tap; |
|
|
|
binding.behavior_dev = hold_tap->config->behaviors->tap.behavior_dev; |
|
|
|
struct device *behavior_device = device_get_binding(behavior->behavior_dev); |
|
|
|
binding.param1 = hold_tap->param_tap; |
|
|
|
behavior_keymap_binding_pressed(behavior_device, hold_tap->position, hold_tap->param_tap, |
|
|
|
binding.param2 = 0; |
|
|
|
0); |
|
|
|
|
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
behavior_keymap_binding_pressed(&binding, event); |
|
|
|
release_captured_events(); |
|
|
|
release_captured_events(); |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
static int on_hold_tap_binding_pressed(struct device *dev, u32_t position, u32_t param_hold, |
|
|
|
static int on_hold_tap_binding_pressed(struct zmk_behavior_binding *binding, |
|
|
|
u32_t param_tap) { |
|
|
|
struct zmk_behavior_binding_event event) { |
|
|
|
|
|
|
|
struct device *dev = device_get_binding(binding->behavior_dev); |
|
|
|
const struct behavior_hold_tap_config *cfg = dev->config_info; |
|
|
|
const struct behavior_hold_tap_config *cfg = dev->config_info; |
|
|
|
|
|
|
|
|
|
|
|
if (undecided_hold_tap != NULL) { |
|
|
|
if (undecided_hold_tap != NULL) { |
|
|
@ -305,54 +312,69 @@ static int on_hold_tap_binding_pressed(struct device *dev, u32_t position, u32_t |
|
|
|
return 0; |
|
|
|
return 0; |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
struct active_hold_tap *hold_tap = store_hold_tap(position, param_hold, param_tap, cfg); |
|
|
|
struct active_hold_tap *hold_tap = |
|
|
|
|
|
|
|
store_hold_tap(event.position, binding->param1, binding->param2, event.timestamp, cfg); |
|
|
|
if (hold_tap == NULL) { |
|
|
|
if (hold_tap == NULL) { |
|
|
|
LOG_ERR("unable to store hold-tap info, did you press more than %d hold-taps?", |
|
|
|
LOG_ERR("unable to store hold-tap info, did you press more than %d hold-taps?", |
|
|
|
ZMK_BHV_HOLD_TAP_MAX_HELD); |
|
|
|
ZMK_BHV_HOLD_TAP_MAX_HELD); |
|
|
|
return 0; |
|
|
|
return 0; |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
LOG_DBG("%d new undecided hold_tap", position); |
|
|
|
LOG_DBG("%d new undecided hold_tap", event.position); |
|
|
|
undecided_hold_tap = hold_tap; |
|
|
|
undecided_hold_tap = hold_tap; |
|
|
|
k_delayed_work_submit(&hold_tap->work, cfg->tapping_term_ms()); |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// todo: once we get timing info for keypresses, start the timer relative to the original
|
|
|
|
// if this behavior was queued we have to adjust the timer to only
|
|
|
|
// keypress don't forget to simulate a timer-event before the event after that time was handled.
|
|
|
|
// wait for the remaining time.
|
|
|
|
|
|
|
|
s32_t tapping_term_ms_left = (hold_tap->timestamp + cfg->tapping_term_ms) - k_uptime_get(); |
|
|
|
|
|
|
|
if (tapping_term_ms_left > 0) { |
|
|
|
|
|
|
|
k_delayed_work_submit(&hold_tap->work, K_MSEC(tapping_term_ms_left)); |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
return 0; |
|
|
|
return 0; |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
static int on_hold_tap_binding_released(struct device *dev, u32_t position, u32_t _, u32_t __) { |
|
|
|
static int on_hold_tap_binding_released(struct zmk_behavior_binding *binding, |
|
|
|
struct active_hold_tap *hold_tap = find_hold_tap(position); |
|
|
|
struct zmk_behavior_binding_event event) { |
|
|
|
|
|
|
|
struct active_hold_tap *hold_tap = find_hold_tap(event.position); |
|
|
|
if (hold_tap == NULL) { |
|
|
|
if (hold_tap == NULL) { |
|
|
|
LOG_ERR("ACTIVE_HOLD_TAP_CLEANED_UP_TOO_EARLY"); |
|
|
|
LOG_ERR("ACTIVE_HOLD_TAP_CLEANED_UP_TOO_EARLY"); |
|
|
|
return 0; |
|
|
|
return 0; |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// If these events were queued, the timer event may be queued too late or not at all.
|
|
|
|
|
|
|
|
// We insert a timer event before the TH_KEY_UP event to verify.
|
|
|
|
int work_cancel_result = k_delayed_work_cancel(&hold_tap->work); |
|
|
|
int work_cancel_result = k_delayed_work_cancel(&hold_tap->work); |
|
|
|
|
|
|
|
if (event.timestamp > (hold_tap->timestamp + hold_tap->config->tapping_term_ms)) { |
|
|
|
|
|
|
|
decide_hold_tap(hold_tap, HT_TIMER_EVENT); |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
decide_hold_tap(hold_tap, HT_KEY_UP); |
|
|
|
decide_hold_tap(hold_tap, HT_KEY_UP); |
|
|
|
|
|
|
|
|
|
|
|
struct zmk_behavior_binding *behavior; |
|
|
|
// todo: set up the binding and data items inside of the active_hold_tap struct
|
|
|
|
|
|
|
|
struct zmk_behavior_binding_event sub_behavior_data = { |
|
|
|
|
|
|
|
.position = hold_tap->position, |
|
|
|
|
|
|
|
.timestamp = hold_tap->timestamp, |
|
|
|
|
|
|
|
}; |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
struct zmk_behavior_binding sub_behavior_binding; |
|
|
|
if (hold_tap->is_hold) { |
|
|
|
if (hold_tap->is_hold) { |
|
|
|
behavior = &hold_tap->config->behaviors->hold; |
|
|
|
sub_behavior_binding.behavior_dev = hold_tap->config->behaviors->hold.behavior_dev; |
|
|
|
struct device *behavior_device = device_get_binding(behavior->behavior_dev); |
|
|
|
sub_behavior_binding.param1 = hold_tap->param_hold; |
|
|
|
behavior_keymap_binding_released(behavior_device, hold_tap->position, hold_tap->param_hold, |
|
|
|
sub_behavior_binding.param2 = 0; |
|
|
|
0); |
|
|
|
|
|
|
|
} else { |
|
|
|
} else { |
|
|
|
behavior = &hold_tap->config->behaviors->tap; |
|
|
|
sub_behavior_binding.behavior_dev = hold_tap->config->behaviors->tap.behavior_dev; |
|
|
|
struct device *behavior_device = device_get_binding(behavior->behavior_dev); |
|
|
|
sub_behavior_binding.param1 = hold_tap->param_tap; |
|
|
|
behavior_keymap_binding_released(behavior_device, hold_tap->position, hold_tap->param_tap, |
|
|
|
sub_behavior_binding.param2 = 0; |
|
|
|
0); |
|
|
|
|
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
behavior_keymap_binding_released(&sub_behavior_binding, sub_behavior_data); |
|
|
|
|
|
|
|
|
|
|
|
if (work_cancel_result == -EINPROGRESS) { |
|
|
|
if (work_cancel_result == -EINPROGRESS) { |
|
|
|
// let the timer handler clean up
|
|
|
|
// let the timer handler clean up
|
|
|
|
// if we'd clear now, the timer may call back for an uninitialized active_hold_tap.
|
|
|
|
// if we'd clear now, the timer may call back for an uninitialized active_hold_tap.
|
|
|
|
LOG_DBG("%d hold-tap timer work in event queue", position); |
|
|
|
LOG_DBG("%d hold-tap timer work in event queue", event.position); |
|
|
|
hold_tap->work_is_cancelled = true; |
|
|
|
hold_tap->work_is_cancelled = true; |
|
|
|
} else { |
|
|
|
} else { |
|
|
|
LOG_DBG("%d cleaning up hold-tap", position); |
|
|
|
LOG_DBG("%d cleaning up hold-tap", event.position); |
|
|
|
clear_hold_tap(hold_tap); |
|
|
|
clear_hold_tap(hold_tap); |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
@ -382,6 +404,14 @@ static int position_state_changed_listener(const struct zmk_event_header *eh) { |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// If these events were queued, the timer event may be queued too late or not at all.
|
|
|
|
|
|
|
|
// We make a timer decision before the other key events are handled if the timer would
|
|
|
|
|
|
|
|
// have run out.
|
|
|
|
|
|
|
|
if (ev->timestamp > |
|
|
|
|
|
|
|
(undecided_hold_tap->timestamp + undecided_hold_tap->config->tapping_term_ms)) { |
|
|
|
|
|
|
|
decide_hold_tap(undecided_hold_tap, HT_TIMER_EVENT); |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
if (!ev->state && find_captured_keydown_event(ev->position) == NULL) { |
|
|
|
if (!ev->state && find_captured_keydown_event(ev->position) == NULL) { |
|
|
|
// no keydown event has been captured, let it bubble.
|
|
|
|
// no keydown event has been captured, let it bubble.
|
|
|
|
// we'll catch modifiers later in modifier_state_changed_listener
|
|
|
|
// we'll catch modifiers later in modifier_state_changed_listener
|
|
|
@ -463,6 +493,7 @@ static int behavior_hold_tap_init(struct device *dev) { |
|
|
|
struct behavior_hold_tap_data {}; |
|
|
|
struct behavior_hold_tap_data {}; |
|
|
|
static struct behavior_hold_tap_data behavior_hold_tap_data; |
|
|
|
static struct behavior_hold_tap_data behavior_hold_tap_data; |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/* todo: get rid of unused param1 and param2. */ |
|
|
|
#define _TRANSFORM_ENTRY(idx, node) \ |
|
|
|
#define _TRANSFORM_ENTRY(idx, node) \ |
|
|
|
{ \
|
|
|
|
{ \
|
|
|
|
.behavior_dev = DT_LABEL(DT_INST_PHANDLE_BY_IDX(node, bindings, idx)), \
|
|
|
|
.behavior_dev = DT_LABEL(DT_INST_PHANDLE_BY_IDX(node, bindings, idx)), \
|
|
|
@ -473,14 +504,11 @@ static struct behavior_hold_tap_data behavior_hold_tap_data; |
|
|
|
}, |
|
|
|
}, |
|
|
|
|
|
|
|
|
|
|
|
#define KP_INST(n) \ |
|
|
|
#define KP_INST(n) \ |
|
|
|
static k_timeout_t behavior_hold_tap_config_##n##_gettime() { \
|
|
|
|
|
|
|
|
return K_MSEC(DT_INST_PROP(n, tapping_term_ms)); \
|
|
|
|
|
|
|
|
} \
|
|
|
|
|
|
|
|
static struct behavior_hold_tap_behaviors behavior_hold_tap_behaviors_##n = { \
|
|
|
|
static struct behavior_hold_tap_behaviors behavior_hold_tap_behaviors_##n = { \
|
|
|
|
.hold = _TRANSFORM_ENTRY(0, n).tap = _TRANSFORM_ENTRY(1, n)}; \
|
|
|
|
.hold = _TRANSFORM_ENTRY(0, n).tap = _TRANSFORM_ENTRY(1, n)}; \
|
|
|
|
static struct behavior_hold_tap_config behavior_hold_tap_config_##n = { \
|
|
|
|
static struct behavior_hold_tap_config behavior_hold_tap_config_##n = { \
|
|
|
|
.behaviors = &behavior_hold_tap_behaviors_##n, \
|
|
|
|
.behaviors = &behavior_hold_tap_behaviors_##n, \
|
|
|
|
.tapping_term_ms = &behavior_hold_tap_config_##n##_gettime, \
|
|
|
|
.tapping_term_ms = DT_INST_PROP(n, tapping_term_ms), \
|
|
|
|
.flavor = DT_ENUM_IDX(DT_DRV_INST(n), flavor), \
|
|
|
|
.flavor = DT_ENUM_IDX(DT_DRV_INST(n), flavor), \
|
|
|
|
}; \
|
|
|
|
}; \
|
|
|
|
DEVICE_AND_API_INIT(behavior_hold_tap_##n, DT_INST_LABEL(n), behavior_hold_tap_init, \
|
|
|
|
DEVICE_AND_API_INIT(behavior_hold_tap_##n, DT_INST_LABEL(n), behavior_hold_tap_init, \
|
|
|
|