A 0.30.0 user reported the setting does nothing for ⌘Space. It never could: the macOS half is an NSEvent local monitor (0.29, b2146f33), which only sees keys AppKit delivers to the app. ⌘Space/⌘Tab/Mission Control are consumed by WindowServer first. The SDL clients use a private CGS call that is compiled out under the sandbox; this app is sandboxed on both channels.
What this does
InputCapture installs a session-level CGEventTap while forwarding (torn down on release/stop; capture already releases on any focus loss). The tap forwards nothing itself — it re-posts each keyDown/keyUp into the app's own queue, stamped with the key window, so it travels the existing path: monitor (client chords, ⌘ chords → host) → StreamLayerView (rest). No second VK table, no second release bookkeeping.
Per-event gating: forwarding ∧ capture mouse model (read live — ⌃⌥⇧M flips it) ∧ app active; anything else passes through. Main run loop on purpose: a hung main thread trips the tap timeout and macOS hands the keyboard back; kCGEventTapDisabledBy* re-arms otherwise.
Accessibility is only ever asked for from Settings (genuine off→on flip of the toggle, or the new "Allow Accessibility access…" button which also opens the pane) — never at stream start, never for the default-on users this update lands on. Without the grant the setting keeps its 0.29 behaviour and the caption now says which half works.
App Review notes + 0.31.0 release notes updated.
Verified
swift build clean; swift test: 377 tests, 0 failures (2 new gating/VK tests in CommandChordTests).
Harness 1: a CGEvent → NSEvent(cgEvent:) → NSApp.postEvent round trip reaches a local monitor with keyCode+⌘ intact.
Harness 2: a windowless reposted event does not reach the first responder (sendEvent routes by event.window); re-stamped with the window number it does — hence restamp.
Still WIP — needs a live test (this machine has no Accessibility grant to test with)
Build/run, Settings › Input, toggle "Capture system shortcuts" off→on → expect the Accessibility prompt; grant it in System Settings.
Stream, capture, press ⌘Space / ⌘Tab / ⌃↑ → should reach the host (Super+Space etc.), Spotlight/Dock must not open. ⌘⎋ must still release capture; ⌃⌥⇧Q/D/S/A and ⌃⌘F must still work.
Release capture / ⌘Tab away after releasing → keyboard normal everywhere.
Revoke Accessibility mid-app → next capture simply runs without the tap (caption flips back).
A 0.30.0 user reported the setting does nothing for ⌘Space. It never could: the macOS half is an NSEvent local monitor (0.29, b2146f33), which only sees keys AppKit delivers to the app. ⌘Space/⌘Tab/Mission Control are consumed by WindowServer first. The SDL clients use a private CGS call that is compiled out under the sandbox; this app is sandboxed on both channels.
**What this does**
- `InputCapture` installs a session-level `CGEventTap` while forwarding (torn down on release/stop; capture already releases on any focus loss). The tap forwards nothing itself — it re-posts each keyDown/keyUp into the app's own queue, stamped with the key window, so it travels the existing path: monitor (client chords, ⌘ chords → host) → `StreamLayerView` (rest). No second VK table, no second release bookkeeping.
- Per-event gating: forwarding ∧ capture mouse model (read live — ⌃⌥⇧M flips it) ∧ app active; anything else passes through. Main run loop on purpose: a hung main thread trips the tap timeout and macOS hands the keyboard back; `kCGEventTapDisabledBy*` re-arms otherwise.
- Accessibility is only ever asked for from Settings (genuine off→on flip of the toggle, or the new "Allow Accessibility access…" button which also opens the pane) — never at stream start, never for the default-on users this update lands on. Without the grant the setting keeps its 0.29 behaviour and the caption now says which half works.
- App Review notes + 0.31.0 release notes updated.
**Verified**
- `swift build` clean; `swift test`: 377 tests, 0 failures (2 new gating/VK tests in `CommandChordTests`).
- Harness 1: a `CGEvent → NSEvent(cgEvent:) → NSApp.postEvent` round trip reaches a local monitor with keyCode+⌘ intact.
- Harness 2: a *windowless* reposted event does **not** reach the first responder (`sendEvent` routes by `event.window`); re-stamped with the window number it does — hence `restamp`.
**Still WIP — needs a live test (this machine has no Accessibility grant to test with)**
1. Build/run, Settings › Input, toggle "Capture system shortcuts" off→on → expect the Accessibility prompt; grant it in System Settings.
2. Stream, capture, press ⌘Space / ⌘Tab / ⌃↑ → should reach the host (Super+Space etc.), Spotlight/Dock must not open. ⌘⎋ must still release capture; ⌃⌥⇧Q/D/S/A and ⌃⌘F must still work.
3. Release capture / ⌘Tab away after releasing → keyboard normal everywhere.
4. Revoke Accessibility mid-app → next capture simply runs without the tap (caption flips back).
A 0.30.0 user reported the setting does nothing for ⌘Space. It never could:
the macOS implementation (0.29, b2146f33) is an NSEvent local monitor, which
only sees the keys AppKit delivers to the app — ⌘Q, ⌘W and their like. The
shortcuts macOS itself owns (⌘Space → Spotlight, ⌘Tab → the Dock, ⌃↑ →
Mission Control, everything under System Settings › Keyboard › Shortcuts) are
consumed by WindowServer before any app is asked. The SDL clients take those
via the private CGSSetGlobalHotKeyOperatingMode, which SDL only compiles in
outside the sandbox; this app is sandboxed on both channels.
The sandbox-legal way is a session-level CGEventTap, which needs Accessibility.
`InputCapture` now installs one while forwarding (and only then — it comes
down with setForwarding(false)/stop(), and capture already releases on any
focus loss, so the tap is never live with another app frontmost). The tap
forwards nothing itself: it takes each keyDown/keyUp off the system and
re-posts it into this app's own queue, addressed to the key window, so it
arrives exactly where the same key would have had macOS not claimed it — the
monitor first (client chords, ⌘ chords → host), then StreamLayerView (the
rest). One key path, no second VK table, no second release bookkeeping; and
the ⌘-chord keyUps macOS used to swallow now arrive too.
Two things verified in standalone harnesses rather than assumed: a reposted
event does reach a local monitor, and a windowless NSEvent(cgEvent:) does NOT
reach the first responder — NSApp.sendEvent routes key events by
event.window — hence the re-stamp onto the key window's number. The
intercept half (tap ahead of Spotlight, inside the sandbox) needs a granted
Accessibility switch this machine doesn't have; that is the live test left.
Gating per event: forwarding, capture mouse model (⌃⌥⇧M flips it
mid-capture, so it is read live rather than at install), app active. Any
other state passes the key through untouched — a tap that swallows keys for
the whole Mac is the failure mode designed against. Installed on the main
run loop on purpose: a hung main thread trips the tap timeout and macOS hands
the keyboard back; the callback re-arms on kCGEventTapDisabledBy* otherwise.
The Accessibility prompt is asked only from Settings — on a genuine off→on
flip of the toggle, or an explicit "Allow Accessibility access…" button that
also opens the pane — never at stream start, and never for the default-on
users this update lands on. Without the grant the setting keeps doing what it
did in 0.29, and its caption now says exactly which half works. App Review
notes carry the justification.
enricobuehler
marked the pull request as ready for review 2026-08-19 14:03:12 +00:00
Blocking a user prevents them from interacting with repositories, such as opening or commenting on pull requests or issues. Learn more about blocking a user.
A 0.30.0 user reported the setting does nothing for ⌘Space. It never could: the macOS half is an NSEvent local monitor (0.29,
b2146f33), which only sees keys AppKit delivers to the app. ⌘Space/⌘Tab/Mission Control are consumed by WindowServer first. The SDL clients use a private CGS call that is compiled out under the sandbox; this app is sandboxed on both channels.What this does
InputCaptureinstalls a session-levelCGEventTapwhile forwarding (torn down on release/stop; capture already releases on any focus loss). The tap forwards nothing itself — it re-posts each keyDown/keyUp into the app's own queue, stamped with the key window, so it travels the existing path: monitor (client chords, ⌘ chords → host) →StreamLayerView(rest). No second VK table, no second release bookkeeping.kCGEventTapDisabledBy*re-arms otherwise.Verified
swift buildclean;swift test: 377 tests, 0 failures (2 new gating/VK tests inCommandChordTests).CGEvent → NSEvent(cgEvent:) → NSApp.postEventround trip reaches a local monitor with keyCode+⌘ intact.sendEventroutes byevent.window); re-stamped with the window number it does — hencerestamp.Still WIP — needs a live test (this machine has no Accessibility grant to test with)