diff --git a/app/src/behaviors/behavior_hold_tap.c b/app/src/behaviors/behavior_hold_tap.c index 6c2109b7..b977a970 100644 --- a/app/src/behaviors/behavior_hold_tap.c +++ b/app/src/behaviors/behavior_hold_tap.c @@ -51,11 +51,6 @@ enum decision_moment { HT_QUICK_TAP, }; -struct decision_trigger_event { - enum decision_moment decision_moment; - int32_t other_key_position; -}; - struct behavior_hold_tap_config { int tapping_term_ms; char *hold_behavior_dev; @@ -77,9 +72,6 @@ struct active_hold_tap { const struct behavior_hold_tap_config *config; struct k_delayed_work work; 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 @@ -100,8 +92,6 @@ struct 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) { last_tapped.position = hold_tap->position; 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_tap = param_tap; active_hold_taps[i].timestamp = timestamp; - active_hold_taps[i].position_of_first_other_key_pressed = -1; return &active_hold_taps[i]; } return NULL; @@ -230,67 +219,16 @@ static void clear_hold_tap(struct active_hold_tap *hold_tap) { hold_tap->work_is_cancelled = false; } -static bool -does_position_of_first_other_keypress_permit_hold_behavior(struct active_hold_tap *hold_tap) { - 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) { +static void decide_balanced(struct active_hold_tap *hold_tap, enum decision_moment event) { + switch (event) { case HT_KEY_UP: hold_tap->status = STATUS_TAP; return; case HT_OTHER_KEY_UP: - if (does_position_of_first_other_keypress_permit_hold_behavior(hold_tap)) { - hold_tap->status = STATUS_HOLD_INTERRUPT; - } else { - hold_tap->status = STATUS_TAP; - } + hold_tap->status = STATUS_HOLD_INTERRUPT; return; case HT_TIMER_EVENT: - if (does_position_of_first_other_keypress_permit_hold_behavior(hold_tap)) { - hold_tap->status = STATUS_HOLD_TIMER; - } else { - hold_tap->status = STATUS_TAP; - } + hold_tap->status = STATUS_HOLD_TIMER; return; case HT_QUICK_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, - struct decision_trigger_event *decision_trigger_event) { - switch (decision_trigger_event->decision_moment) { +static void decide_tap_preferred(struct active_hold_tap *hold_tap, enum decision_moment event) { + switch (event) { case HT_KEY_UP: hold_tap->status = STATUS_TAP; return; case HT_TIMER_EVENT: - if (does_position_of_first_other_keypress_permit_hold_behavior(hold_tap)) { - hold_tap->status = STATUS_HOLD_TIMER; - } else { - hold_tap->status = STATUS_TAP; - } + hold_tap->status = STATUS_HOLD_TIMER; return; case HT_QUICK_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, - struct decision_trigger_event *decision_trigger_event) { - switch (decision_trigger_event->decision_moment) { +static void decide_hold_preferred(struct active_hold_tap *hold_tap, enum decision_moment event) { + switch (event) { case HT_KEY_UP: hold_tap->status = STATUS_TAP; return; case HT_OTHER_KEY_DOWN: - if (does_position_of_first_other_keypress_permit_hold_behavior(hold_tap)) { - hold_tap->status = STATUS_HOLD_INTERRUPT; - } else { - hold_tap->status = STATUS_TAP; - } + hold_tap->status = STATUS_HOLD_INTERRUPT; return; case HT_TIMER_EVENT: - if (does_position_of_first_other_keypress_permit_hold_behavior(hold_tap)) { - hold_tap->status = STATUS_HOLD_TIMER; - } else { - hold_tap->status = STATUS_TAP; - } + hold_tap->status = STATUS_HOLD_TIMER; return; case HT_QUICK_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); } -static void decide_hold_tap(struct active_hold_tap *hold_tap, - struct decision_trigger_event *decision_trigger_event) { +static void decide_hold_tap(struct active_hold_tap *hold_tap, enum decision_moment decision_moment, + int32_t other_key_down_position) { if (hold_tap->status != STATUS_UNDECIDED) { return; } @@ -448,23 +372,43 @@ static void decide_hold_tap(struct active_hold_tap *hold_tap, return; } - store_position_if_is_first_other_key_pressed(hold_tap, decision_trigger_event); - switch (hold_tap->config->flavor) { - case FLAVOR_HOLD_PREFERRED: - decide_hold_preferred(hold_tap, decision_trigger_event); - case FLAVOR_BALANCED: - decide_balanced(hold_tap, decision_trigger_event); - case FLAVOR_TAP_PREFERRED: - decide_tap_preferred(hold_tap, decision_trigger_event); + // For conditional hold-tap behaviors that have failed to meet the required prerequisites + // for a hold decision, force a tap decision. + if (decision_moment == HT_OTHER_KEY_DOWN && hold_tap->config->hold_enabler_keys_len > 0) { + bool does_other_key_down_enable_hold_behavior = false; + for (int i = 0; i < hold_tap->config->hold_enabler_keys_len; i++) { + does_other_key_down_enable_hold_behavior = + does_other_key_down_enable_hold_behavior || + (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) { 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, 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; press_binding(hold_tap); 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); 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)) { - 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 @@ -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. int work_cancel_result = k_delayed_work_cancel(&hold_tap->work); if (event.timestamp > (hold_tap->timestamp + hold_tap->config->tapping_term_ms)) { - struct decision_trigger_event decision_trigger_event; - decision_trigger_event.decision_moment = HT_TIMER_EVENT; - decide_hold_tap(hold_tap, &decision_trigger_event); + decide_hold_tap(hold_tap, HT_TIMER_EVENT, NULL); } - struct decision_trigger_event decision_trigger_event; - decision_trigger_event.decision_moment = HT_KEY_UP; - decide_hold_tap(hold_tap, &decision_trigger_event); + decide_hold_tap(hold_tap, HT_KEY_UP, NULL); decide_retro_tap(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) { 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); if (undecided_hold_tap == NULL) { @@ -604,9 +539,7 @@ static int position_state_changed_listener(const zmk_event_t *eh) { // have run out. if (ev->timestamp > (undecided_hold_tap->timestamp + undecided_hold_tap->config->tapping_term_ms)) { - struct decision_trigger_event decision_trigger_event; - decision_trigger_event.decision_moment = HT_TIMER_EVENT; - decide_hold_tap(undecided_hold_tap, &decision_trigger_event); + decide_hold_tap(undecided_hold_tap, HT_TIMER_EVENT, 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, ev->state ? "down" : "up"); capture_event(eh); - struct decision_trigger_event decision_trigger_event; - decision_trigger_event.decision_moment = ev->state ? HT_OTHER_KEY_DOWN : HT_OTHER_KEY_UP; - decision_trigger_event.other_key_position = ev->position; - decide_hold_tap(undecided_hold_tap, &decision_trigger_event); + decide_hold_tap(undecided_hold_tap, ev->state ? HT_OTHER_KEY_DOWN : HT_OTHER_KEY_UP, + ev->state ? ev->position : NULL); 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) { clear_hold_tap(hold_tap); } else { - struct decision_trigger_event decision_trigger_event; - decision_trigger_event.decision_moment = HT_TIMER_EVENT; - decide_hold_tap(hold_tap, &decision_trigger_event); + decide_hold_tap(hold_tap, HT_TIMER_EVENT, NULL); } }