Fix key: permission warning and support modifier-only chords - #3
Open
nanderss wants to merge 1 commit into
Open
Conversation
Two problems with key: actions.
The startup warning tests AXIsProcessTrusted(), which reports
Accessibility, but told the user to grant Input Monitoring. CGEvent.post
needs Accessibility; nothing here listens to input, so Input Monitoring
is never required. Granting the advertised permission left post()
silently doing nothing, with no error anywhere.
A shortcut naming only modifiers ("ctrl+shift") was rejected, because
the last +-separated component is taken as the key and no modifier
appears in the keycode table. Such a chord has no key to press, so send
it as flagsChanged instead and hold it for as long as the deck button is
held: press builds the chord up one modifier at a time carrying the
cumulative flags, release unwinds it in reverse. Shortcuts containing a
real key are unaffected.
Latched modifiers are difficult to recover from, so guard three ways: a
30s watchdog releases the chord if a buttonRelease packet is ever
dropped, pressing a second chord button releases the first rather than
stacking, and shutdown() releases before teardown -- called on config
reload and now after the run loop exits, so SIGTERM unwinds cleanly.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Two issues with
key:actions.The permission warning names the wrong setting
The startup check tests
AXIsProcessTrusted(), which reports Accessibility, but the message tells the user to grant Input Monitoring:CGEvent.post(tap:)needs Accessibility. Input Monitoring gates listening to input, which this daemon never does — and the deck itself is a vendor-usage-page device (0xFFA0), which macOS doesn't gate at all, so its events flow with no permission granted.Following the message grants a permission that changes nothing, after which
post()silently no-ops and everykey:action dies with no error anywhere. Note the README currently states "Accessibility — not required", which is the opposite of what the code checks; happy to send a README fix separately.Modifier-only chords are unparseable
parts.last!is taken as the key and everything before it as modifiers, soctrl+shiftparses as "keyctrlwith modifiershift" and is rejected withunknown key 'ctrl'. Such a chord has no key to press, soCGEvent(virtualKey:)can't express it.These are sent as
flagsChangedinstead, and held for as long as the deck button is held — press builds the chord up one modifier at a time carrying the cumulative flags, release unwinds it in reverse, mirroring how real hardware reports a chord. This drives apps that trigger on a held modifier combo. Shortcuts containing a real key are unaffected and take the original path.buttonReleasepreviously returned immediately; it's now wired and matched by button ID, so releasing a different button can't drop the chord.Latched modifiers are nasty to recover from, so there are three guards:
buttonReleasepacket is ever dropped, and logs that it didshutdown()releases before teardown — already called on config reload, and now also after the run loop exits, so SIGTERM andlaunchctl bootoutunwind cleanlySIGKILL is the one case nothing can cover.
Tested on an AJAZZ AKP03, macOS 26.5:
key:cmd+spaceopens Spotlight, andkey:shift+ctrlon a button holds the chord for the duration of the press and triggers a third-party app listening for it.