Merge branch 'conditional_mod_tap' into mercury

This commit is contained in:
jmding@gmail.com 2021-09-13 01:58:07 +00:00
commit 4d4cf9dfed

View file

@ -51,11 +51,6 @@ enum decision_moment {
HT_QUICK_TAP, HT_QUICK_TAP,
}; };
struct decision_trigger_event {
enum decision_moment decision_moment;
int32_t other_key_position;
};
struct behavior_hold_tap_config { struct behavior_hold_tap_config {
int tapping_term_ms; int tapping_term_ms;
char *hold_behavior_dev; char *hold_behavior_dev;
@ -77,9 +72,6 @@ struct active_hold_tap {
const struct behavior_hold_tap_config *config; const struct behavior_hold_tap_config *config;
struct k_delayed_work work; struct k_delayed_work work;
bool work_is_cancelled; bool work_is_cancelled;
// initialized to -1, meaning no other key has been pressed yet
int32_t position_of_first_other_key_pressed;
}; };
// The undecided hold tap is the hold tap that needs to be decided before // The undecided hold tap is the hold tap that needs to be decided before
@ -100,8 +92,6 @@ struct last_tapped {
struct last_tapped last_tapped; struct last_tapped last_tapped;
bool is_key_position_pressed[ZMK_KEYMAP_LEN] = {0};
static void store_last_tapped(struct active_hold_tap *hold_tap) { static void store_last_tapped(struct active_hold_tap *hold_tap) {
last_tapped.position = hold_tap->position; last_tapped.position = hold_tap->position;
last_tapped.tap_deadline = hold_tap->timestamp + hold_tap->config->quick_tap_ms; last_tapped.tap_deadline = hold_tap->timestamp + hold_tap->config->quick_tap_ms;
@ -218,7 +208,6 @@ static struct active_hold_tap *store_hold_tap(uint32_t position, uint32_t param_
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; active_hold_taps[i].timestamp = timestamp;
active_hold_taps[i].position_of_first_other_key_pressed = -1;
return &active_hold_taps[i]; return &active_hold_taps[i];
} }
return NULL; return NULL;
@ -230,67 +219,16 @@ static void clear_hold_tap(struct active_hold_tap *hold_tap) {
hold_tap->work_is_cancelled = false; hold_tap->work_is_cancelled = false;
} }
static bool static void decide_balanced(struct active_hold_tap *hold_tap, enum decision_moment event) {
does_position_of_first_other_keypress_permit_hold_behavior(struct active_hold_tap *hold_tap) { switch (event) {
if (hold_tap->config->hold_enabler_keys_len == 0) {
return true;
}
for (int i = 0; i < hold_tap->config->hold_enabler_keys_len; i++) {
if (hold_tap->config->hold_enabler_keys[i] ==
hold_tap->position_of_first_other_key_pressed) {
return true;
}
}
return false;
}
static void store_position_if_is_first_other_key_pressed(
struct active_hold_tap *active_hold_tap,
struct decision_trigger_event *decision_trigger_event) {
// The value of position_of_first_other_key_pressed is initialized to -1 when
// on_hold_tap_binding_pressed calls store_hold_tap.
// If position_of_first_other_key_pressed is >= 0, then this is not the first
// other key to have been pressed.
if (active_hold_tap->position_of_first_other_key_pressed >= 0) {
return;
}
// If the decision_moment is not an other-key-down event, nothing to do.
if (decision_trigger_event->decision_moment != HT_OTHER_KEY_DOWN) {
return;
}
// If the position of the key being pressed is the hold-tap key, something went wrong.
if (active_hold_tap->position == decision_trigger_event->other_key_position) {
LOG_DBG("ERROR hold-tap key cannot be pressed while still active");
return;
}
// All checks passed. Store the position of the other key in the active_hold_tap.
active_hold_tap->position_of_first_other_key_pressed =
decision_trigger_event->other_key_position;
return;
}
static void decide_balanced(struct active_hold_tap *hold_tap,
struct decision_trigger_event *decision_trigger_event) {
switch (decision_trigger_event->decision_moment) {
case HT_KEY_UP: case HT_KEY_UP:
hold_tap->status = STATUS_TAP; hold_tap->status = STATUS_TAP;
return; return;
case HT_OTHER_KEY_UP: case HT_OTHER_KEY_UP:
if (does_position_of_first_other_keypress_permit_hold_behavior(hold_tap)) {
hold_tap->status = STATUS_HOLD_INTERRUPT; hold_tap->status = STATUS_HOLD_INTERRUPT;
} else {
hold_tap->status = STATUS_TAP;
}
return; return;
case HT_TIMER_EVENT: case HT_TIMER_EVENT:
if (does_position_of_first_other_keypress_permit_hold_behavior(hold_tap)) {
hold_tap->status = STATUS_HOLD_TIMER; hold_tap->status = STATUS_HOLD_TIMER;
} else {
hold_tap->status = STATUS_TAP;
}
return; return;
case HT_QUICK_TAP: case HT_QUICK_TAP:
hold_tap->status = STATUS_TAP; hold_tap->status = STATUS_TAP;
@ -300,18 +238,13 @@ static void decide_balanced(struct active_hold_tap *hold_tap,
} }
} }
static void decide_tap_preferred(struct active_hold_tap *hold_tap, static void decide_tap_preferred(struct active_hold_tap *hold_tap, enum decision_moment event) {
struct decision_trigger_event *decision_trigger_event) { switch (event) {
switch (decision_trigger_event->decision_moment) {
case HT_KEY_UP: case HT_KEY_UP:
hold_tap->status = STATUS_TAP; hold_tap->status = STATUS_TAP;
return; return;
case HT_TIMER_EVENT: case HT_TIMER_EVENT:
if (does_position_of_first_other_keypress_permit_hold_behavior(hold_tap)) {
hold_tap->status = STATUS_HOLD_TIMER; hold_tap->status = STATUS_HOLD_TIMER;
} else {
hold_tap->status = STATUS_TAP;
}
return; return;
case HT_QUICK_TAP: case HT_QUICK_TAP:
hold_tap->status = STATUS_TAP; hold_tap->status = STATUS_TAP;
@ -321,25 +254,16 @@ static void decide_tap_preferred(struct active_hold_tap *hold_tap,
} }
} }
static void decide_hold_preferred(struct active_hold_tap *hold_tap, static void decide_hold_preferred(struct active_hold_tap *hold_tap, enum decision_moment event) {
struct decision_trigger_event *decision_trigger_event) { switch (event) {
switch (decision_trigger_event->decision_moment) {
case HT_KEY_UP: case HT_KEY_UP:
hold_tap->status = STATUS_TAP; hold_tap->status = STATUS_TAP;
return; return;
case HT_OTHER_KEY_DOWN: case HT_OTHER_KEY_DOWN:
if (does_position_of_first_other_keypress_permit_hold_behavior(hold_tap)) {
hold_tap->status = STATUS_HOLD_INTERRUPT; hold_tap->status = STATUS_HOLD_INTERRUPT;
} else {
hold_tap->status = STATUS_TAP;
}
return; return;
case HT_TIMER_EVENT: case HT_TIMER_EVENT:
if (does_position_of_first_other_keypress_permit_hold_behavior(hold_tap)) {
hold_tap->status = STATUS_HOLD_TIMER; hold_tap->status = STATUS_HOLD_TIMER;
} else {
hold_tap->status = STATUS_TAP;
}
return; return;
case HT_QUICK_TAP: case HT_QUICK_TAP:
hold_tap->status = STATUS_TAP; hold_tap->status = STATUS_TAP;
@ -437,8 +361,8 @@ static int release_binding(struct active_hold_tap *hold_tap) {
return behavior_keymap_binding_released(&binding, event); return behavior_keymap_binding_released(&binding, event);
} }
static void decide_hold_tap(struct active_hold_tap *hold_tap, static void decide_hold_tap(struct active_hold_tap *hold_tap, enum decision_moment decision_moment,
struct decision_trigger_event *decision_trigger_event) { int32_t other_key_down_position) {
if (hold_tap->status != STATUS_UNDECIDED) { if (hold_tap->status != STATUS_UNDECIDED) {
return; return;
} }
@ -448,23 +372,43 @@ static void decide_hold_tap(struct active_hold_tap *hold_tap,
return; return;
} }
store_position_if_is_first_other_key_pressed(hold_tap, decision_trigger_event); // For conditional hold-tap behaviors that have failed to meet the required prerequisites
switch (hold_tap->config->flavor) { // for a hold decision, force a tap decision.
case FLAVOR_HOLD_PREFERRED: if (decision_moment == HT_OTHER_KEY_DOWN && hold_tap->config->hold_enabler_keys_len > 0) {
decide_hold_preferred(hold_tap, decision_trigger_event); bool does_other_key_down_enable_hold_behavior = false;
case FLAVOR_BALANCED: for (int i = 0; i < hold_tap->config->hold_enabler_keys_len; i++) {
decide_balanced(hold_tap, decision_trigger_event); does_other_key_down_enable_hold_behavior =
case FLAVOR_TAP_PREFERRED: does_other_key_down_enable_hold_behavior ||
decide_tap_preferred(hold_tap, decision_trigger_event); (hold_tap->config->hold_enabler_keys[i] == other_key_down_position);
} }
if (!does_other_key_down_enable_hold_behavior) {
hold_tap->status = STATUS_TAP;
}
}
// If the hold-tap behavior is still undecided, attempt to decide it.
if (hold_tap->status == STATUS_UNDECIDED) {
switch (hold_tap->config->flavor) {
case FLAVOR_HOLD_PREFERRED:
decide_hold_preferred(hold_tap, decision_moment);
case FLAVOR_BALANCED:
decide_balanced(hold_tap, decision_moment);
case FLAVOR_TAP_PREFERRED:
decide_tap_preferred(hold_tap, decision_moment);
}
}
// If the hold-tap behavior is still undecided, exit out.
if (hold_tap->status == STATUS_UNDECIDED) { if (hold_tap->status == STATUS_UNDECIDED) {
return; return;
} }
// Since the hold-tap has been decided, clean up undecided_hold_tap and
// execute the decided behavior.
LOG_DBG("%d decided %s (%s decision moment %s)", hold_tap->position, LOG_DBG("%d decided %s (%s decision moment %s)", hold_tap->position,
status_str(hold_tap->status), flavor_str(hold_tap->config->flavor), status_str(hold_tap->status), flavor_str(hold_tap->config->flavor),
decision_moment_str(decision_trigger_event->decision_moment)); decision_moment_str(decision_moment));
undecided_hold_tap = NULL; undecided_hold_tap = NULL;
press_binding(hold_tap); press_binding(hold_tap);
release_captured_events(); release_captured_events();
@ -521,10 +465,8 @@ static int on_hold_tap_binding_pressed(struct zmk_behavior_binding *binding,
LOG_DBG("%d new undecided hold_tap", event.position); LOG_DBG("%d new undecided hold_tap", event.position);
undecided_hold_tap = hold_tap; undecided_hold_tap = hold_tap;
struct decision_trigger_event decision_trigger_event;
decision_trigger_event.decision_moment = HT_QUICK_TAP;
if (is_quick_tap(hold_tap)) { if (is_quick_tap(hold_tap)) {
decide_hold_tap(hold_tap, &decision_trigger_event); decide_hold_tap(hold_tap, HT_QUICK_TAP, NULL);
} }
// if this behavior was queued we have to adjust the timer to only // if this behavior was queued we have to adjust the timer to only
@ -547,14 +489,10 @@ static int on_hold_tap_binding_released(struct zmk_behavior_binding *binding,
// We insert a timer event before the TH_KEY_UP event to verify. // 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)) { if (event.timestamp > (hold_tap->timestamp + hold_tap->config->tapping_term_ms)) {
struct decision_trigger_event decision_trigger_event; decide_hold_tap(hold_tap, HT_TIMER_EVENT, NULL);
decision_trigger_event.decision_moment = HT_TIMER_EVENT;
decide_hold_tap(hold_tap, &decision_trigger_event);
} }
struct decision_trigger_event decision_trigger_event; decide_hold_tap(hold_tap, HT_KEY_UP, NULL);
decision_trigger_event.decision_moment = HT_KEY_UP;
decide_hold_tap(hold_tap, &decision_trigger_event);
decide_retro_tap(hold_tap); decide_retro_tap(hold_tap);
release_binding(hold_tap); release_binding(hold_tap);
@ -579,9 +517,6 @@ static const struct behavior_driver_api behavior_hold_tap_driver_api = {
static int position_state_changed_listener(const zmk_event_t *eh) { static int position_state_changed_listener(const zmk_event_t *eh) {
struct zmk_position_state_changed *ev = as_zmk_position_state_changed(eh); struct zmk_position_state_changed *ev = as_zmk_position_state_changed(eh);
// Update key position state.
is_key_position_pressed[ev->position] = ev->state;
update_hold_status_for_retro_tap(ev->position); update_hold_status_for_retro_tap(ev->position);
if (undecided_hold_tap == NULL) { if (undecided_hold_tap == NULL) {
@ -604,9 +539,7 @@ static int position_state_changed_listener(const zmk_event_t *eh) {
// have run out. // have run out.
if (ev->timestamp > if (ev->timestamp >
(undecided_hold_tap->timestamp + undecided_hold_tap->config->tapping_term_ms)) { (undecided_hold_tap->timestamp + undecided_hold_tap->config->tapping_term_ms)) {
struct decision_trigger_event decision_trigger_event; decide_hold_tap(undecided_hold_tap, HT_TIMER_EVENT, NULL);
decision_trigger_event.decision_moment = HT_TIMER_EVENT;
decide_hold_tap(undecided_hold_tap, &decision_trigger_event);
} }
if (!ev->state && find_captured_keydown_event(ev->position) == NULL) { if (!ev->state && find_captured_keydown_event(ev->position) == NULL) {
@ -620,10 +553,8 @@ static int position_state_changed_listener(const zmk_event_t *eh) {
LOG_DBG("%d capturing %d %s event", undecided_hold_tap->position, ev->position, LOG_DBG("%d capturing %d %s event", undecided_hold_tap->position, ev->position,
ev->state ? "down" : "up"); ev->state ? "down" : "up");
capture_event(eh); capture_event(eh);
struct decision_trigger_event decision_trigger_event; decide_hold_tap(undecided_hold_tap, ev->state ? HT_OTHER_KEY_DOWN : HT_OTHER_KEY_UP,
decision_trigger_event.decision_moment = ev->state ? HT_OTHER_KEY_DOWN : HT_OTHER_KEY_UP; ev->state ? ev->position : NULL);
decision_trigger_event.other_key_position = ev->position;
decide_hold_tap(undecided_hold_tap, &decision_trigger_event);
return ZMK_EV_EVENT_CAPTURED; return ZMK_EV_EVENT_CAPTURED;
} }
@ -669,9 +600,7 @@ void behavior_hold_tap_timer_work_handler(struct k_work *item) {
if (hold_tap->work_is_cancelled) { if (hold_tap->work_is_cancelled) {
clear_hold_tap(hold_tap); clear_hold_tap(hold_tap);
} else { } else {
struct decision_trigger_event decision_trigger_event; decide_hold_tap(hold_tap, HT_TIMER_EVENT, NULL);
decision_trigger_event.decision_moment = HT_TIMER_EVENT;
decide_hold_tap(hold_tap, &decision_trigger_event);
} }
} }