Gate switcher event monitor pump on hardware event counters - #1408
Gate switcher event monitor pump on hardware event counters#1408ProspectOre wants to merge 1 commit into
Conversation
ProviderSwitcherShortcutEventMonitor's run-loop observer fired on every cycle of the menu-tracking loop, and each nextEvent peek re-enters the run loop. During continuous pointer movement over the merged dropdown this starves the tracking session's own input processing - dropped clicks and system-wide beachball reports (steipete#1399) - while the process profiles healthy. The pump now runs only when a leftMouseDown, leftMouseUp, or keyDown hardware counter has actually changed, bounded by a pure-userspace 8ms time check, reducing it from once per run-loop cycle to once per click or key press with identical delivery semantics.
|
@clawsweeper re-review |
|
🦞🧹 I asked ClawSweeper to review this item again. |
|
Superseded by #1397, landed in The landed implementation gates the same Thanks @ProspectOre for independently isolating the same hotspot and documenting the WindowServer evidence. |
Summary
Gate the merged provider switcher's event-monitor pump on hardware event counters, so it runs once per actual click or key press instead of once per menu-tracking run-loop cycle. This fixes the felt input freezes / dropped clicks during long merged-menu sessions reported in #1399, isolated by controlled bisection on reproducing hardware.
Context
ProviderSwitcherShortcutEventMonitor(the only working delivery mechanism for switcher clicks and Command-number/arrow shortcuts during tracking — see the #1260 closure evidence) installs abeforeSourcesrun-loop observer for the tracking mode. The observer fired on every cycle of the menu-tracking loop, and eachNSApp.nextEvent(until: .distantPast, ...)peek re-enters the run loop. Under continuous pointer movement this multiplies run-loop passes and starves the tracking session's own input processing: clicks drop, the desktop beachballs, and the process profiles healthy throughout — the signature in #1399, #1387, #1364.Isolation evidence (same machine, same ~2-minute manual interaction protocol per round, WindowServer
log stream+ 1mssamplecaptures)Control: 120s holding an Apple menu-bar menu open with continuous cursor motion produced zero buffer-clear events, ruling out generic macOS tracking behavior. Full protocol and logs posted on #1399.
Change
EventCounterGate: before peeking, compareCGEventSource.counterForEventType(.combinedSessionState, ...)for exactly the three event types the monitor handles (leftMouseDown,leftMouseUp,keyDown). Unchanged counters → no pump. Identical delivery semantics: any matching hardware event changes a counter, so the very next observer fire pumps.Validation
swift test --filter 'StatusMenuSwitcherClickTests|StatusMenuPersistentRefreshTests|StatusItemControllerShutdownTests'— 25 tests pass.make check— 0 violations;git diff --checkclean.Honesty / scope notes
CGEventSource.counterForEventTypecalls are bounded to ≤125/s by the time check; the previous behavior was an unbounded full event-queue peek + nested run-loop pass per tracking cycle.