Skip to content

fix(shortcuts): recover modifier combos from missed key releases - #138

Merged
runjuu merged 1 commit into
runjuu:mainfrom
rstuckert:fix/stale-modifier-combo
Sep 30, 2026
Merged

runjuu merged 1 commit into
runjuu:mainfrom
rstuckert:fix/stale-modifier-combo

Conversation

@rstuckert

Copy link
Copy Markdown

Purpose

Modifier-combination shortcuts (e.g. Left Shift + Left Command) can stop working until the app restarts (#133). ShortcutTriggerManager could keep a modifier in pressedModifiers after it was released. After that, every attempt counts as having an extra modifier, and the "all keys released" trigger check never runs.

Two ways to get there:

  • Releasing one side of a modifier while the other side is held. Right Shift going up while Left Shift is down leaves the shared .shift flag set, so the release reads as a press.
  • A key-up that never reaches the app.

Closes #133

Changes

  • handleFlagsChanged now calls forgetMissedReleases before handling an event. Each flagsChanged event carries the full modifier state, so it:
    • drops tracked modifiers the event reports as up;
    • treats a new press of an already-tracked key as a missed release, so its hold time starts fresh;
    • resets the hold cycle when that leaves nothing pressed, so the first attempt after a missed release works.
  • Existing rules are unchanged: an extra modifier, another key, or holding longer than 0.8 s still cancels the combo.
  • handleFlagsChanged is now internal so tests can feed it events.

Testing

  • New ModifierComboTriggerTests (9 tests) drive ShortcutTriggerManager with synthetic flagsChanged events. The 6 missed-release scenarios fail on main and pass with this change: Right Shift / Right Command released while the left one is held, a lost Control release, a lost release of a combo key with either key pressed first, other combos (Right Option + Right Command, Left Control, a three-key combo), and Press Twice. The 3 tests covering existing behavior pass on both.
  • Full suite: xcodebuild -scheme "Input Source Pro" -configuration Debug test, 105 tests, 0 failures (Xcode 27.0, macOS 27.0).
  • Manual: a patched Release build used daily for three days with a Left Shift + Left Command group; the Right Shift repro from Is this project dead? Because it seems like it. #133 no longer breaks switching.

Notes

ShortcutTriggerManager.deinit hands self to a Task, which traps once the object is freed ("deallocated with non-zero retain count"; Xcode warns about it too). The app never frees the manager, so users aren't affected, but tests would crash, so the test helper keeps its managers alive. Happy to send a separate fix.

A modifier could stay in pressedModifiers after it was released: right
Shift let go while left Shift kept .shift set read as a press, and a lost
key-up was never noticed. Every later combo then counted an extra modifier
and the trigger check never ran until that key was pressed again.

Each flagsChanged event carries the full modifier state, so drop tracked
modifiers it reports as up, treat a repeated press as a missed release,
and reset the hold cycle when nothing is left pressed.
@bpGusar

bpGusar commented Oct 2, 2026

Copy link
Copy Markdown

@runjuu Please make a new release, as this is an important fix.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Is this project dead? Because it seems like it.

3 participants