Skip to content

Fix first-run event tap retry and three smaller reliability issues#83

Open
Theoz001 wants to merge 4 commits into
aurorascharff:mainfrom
Theoz001:fix/first-run-reliability
Open

Fix first-run event tap retry and three smaller reliability issues#83
Theoz001 wants to merge 4 commits into
aurorascharff:mainfrom
Theoz001:fix/first-run-reliability

Conversation

@Theoz001

Copy link
Copy Markdown

Four small reliability fixes, split into one commit each. Based on and tested against v0.16.0 (f64d702).

1. Retry event tap creation after Accessibility permission is granted

Symptom: on first launch, ClickLight asks for Accessibility permission. If the user grants it in System Settings and comes back, clicks are still only captured through the NSEvent fallback monitor until the app is restarted.

Repro: launch the app without Accessibility permission → grant permission in System Settings → return to any app and click. The status menu shows the capture pipeline is not in Event Tap mode and nothing changes until a relaunch.

Root cause: the initial CGEvent.tapCreate at launch returns nil without permission, and nothing ever retries it.

Fix: when the app becomes active again, if permission is now trusted and the capture pipeline is not in event-tap mode, refresh the capture state. ClickEventTap.start already recreates a missing tap, so this adds only a small usesEventTap readout plus the retry hook.

2. Sample click coordinates at event time instead of delivery time

Symptom: the pulse sometimes appears slightly off from where the click actually happened if the mouse is moving quickly, and a single click can occasionally show two overlapping pulses.

Repro: click and immediately fling the pointer away — the pulse renders at the flung-to position. With both capture channels active, the same click is reported twice with different coordinates, so the 3px dedup in OverlayCoordinator fails and both pulses show.

Root cause: both the event tap and the fallback monitor read NSEvent.mouseLocation inside a DispatchQueue.main.async block, i.e. at delivery time, and at different moments per channel.

Fix: sample the position synchronously in each callback and pass it along with the event: CGEvent.location converted from Quartz to AppKit coordinates in the tap, NSEvent.mouseLocation in the monitor. Both channels now report identical coordinates for the same physical click. Also removes CoordinateMapper, which is dead code whose "point inside a screen frame means it is already AppKit-flipped" heuristic is unsound (Quartz and AppKit coordinates overlap on the main display).

3. Stop counting Preview Pad clicks in click activity stats

Symptom: playing with the Preview Pad in Settings inflates today's click total and the menu bar counter.

Repro: open Settings, click the Preview Pad a bunch of times, watch the Activity pane / menu bar count grow.

Root cause: AppDelegate deliberately excludes real clicks landing on the Settings window from ClickActivityStore, but the Preview Pad inside that same window recorded every preview click into the store — contradictory behavior.

Fix: preview interactions now only render the overlay. The preview view no longer holds an activity store reference.

4. Dim the menu bar icon while ClickLight is disabled

Symptom: toggling ClickLight on/off via the global hotkey gives no feedback; mid-demo there is no way to confirm the current state without opening the menu.

Fix: applyStatusItemAppearance already runs on every settings change (including hotkey toggles), so it now also dims the status item button (alpha 0.45) while the app is disabled. Subtle, native-looking, and immediately visible in the menu bar.

Manual testing

  • swift build and ./build-app.sh clean; app launches and quits normally.
  • Verified compile-level behavior of all four paths; fixes 1 and 2 additionally exercised in a local build over the past weeks (these are ports of fixes I have been running).
  • Per CONTRIBUTING there is no test suite; happy to add manual test notes for any specific path.

Ruczhutao added 4 commits July 17, 2026 13:17
Without Accessibility permission, the initial CGEvent.tapCreate call at
launch returns nil and capture silently falls back to the NSEvent
monitor. Granting permission afterwards never retried tap creation, so
click capture stayed degraded until a full restart.

When the app becomes active again, if permission is now trusted and the
capture pipeline is not running in event-tap mode, refresh the capture
state. ClickEventTap.start already recreates the tap when it is missing,
so this is a minimal retry hook plus a usesEventTap readout on the
capture pipeline.
Both capture channels read NSEvent.mouseLocation inside a
DispatchQueue.main.async block, so coordinates reflected wherever the
pointer happened to be at delivery rather than at the click. Pulses
drifted when the mouse moved right after clicking, and because the
event tap and the fallback monitor sampled at different moments, the
same physical click produced two different locations and slipped past
the 3px dedup in OverlayCoordinator, showing double pulses.

Sample the position synchronously in each callback and pass it along
with the event: CGEvent.location converted from Quartz to AppKit
coordinates in the tap, NSEvent.mouseLocation in the monitor. Both
channels now report identical coordinates for the same click.

Also remove CoordinateMapper, which was dead code. Its heuristic of
treating any point inside a screen frame as already AppKit-flipped is
unsound because Quartz and AppKit coordinates overlap on the main
display, and nothing called it.
AppDelegate deliberately excludes real clicks that land on the Settings
window from ClickActivityStore, but the Preview Pad inside that same
window recorded every preview click into the store, inflating the daily
totals and the menu bar counter with clicks that never happened outside
the app.

Preview interactions now only render the overlay, consistent with the
existing settings-window exclusion, and the preview view no longer
carries an activity store reference.
Toggling the enabled state via the global hotkey gave no feedback, so
during a demo there was no way to confirm whether click highlights were
actually on without opening the menu.

applyStatusItemAppearance already runs on every settings change, which
includes hotkey toggles, so it now also dims the status item button
while the app is disabled.
@vercel

vercel Bot commented Jul 17, 2026

Copy link
Copy Markdown

Someone is attempting to deploy a commit to the Aurora Scharff's projects Team on Vercel.

A member of the Team first needs to authorize it.

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.

1 participant