Skip to content

fix(control): release buttons on quit & settle clicks before pressing - #49

Merged
mrprecioustech merged 1 commit into
masterfrom
fix/click-release-dispose
Aug 10, 2026
Merged

fix(control): release buttons on quit & settle clicks before pressing#49
mrprecioustech merged 1 commit into
masterfrom
fix/click-release-dispose

Conversation

@mrprecioustech

Copy link
Copy Markdown
Contributor

Bug 1: Host needs a reboot after remote control

will-quit fired dispose() via void — never awaited, Electron tore down
the process before the async cleanup finished. Even when it completed, it
only released buttons in heldButtons and exited immediately when the set
was empty. A press that ever escaped tracking stayed down at the OS level
for the whole session.

Fix: will-quit now blocks quit with preventDefault(), runs disposal
with a 2-second deadline, and terminates via app.exit(). dispose() always
calls backend.emergencyStop() which releases L/R/M unconditionally.

Bug 2: Guest can move but can't click

nut-js was configured with autoDelayMs = 0, so the pointer move and
button press were delivered back-to-back. macOS applies the synthetic move
asynchronously, so the press could be delivered before the pointer had
moved — the click lands wherever the pointer used to be.

Fix: A one-frame (16ms) settle before every button action lets the
window server catch up. Also makes the press-release gap long enough for
target apps to register it as a real click instead of a sub-millisecond blip.

Bug 3: disable() didn't call emergencyStop

Taking back control released tracked buttons only (releaseAll). If tracking
had a gap, the host regaining control still had a stuck mouse. emergencyStop
(which unconditionally releases all mouse buttons) now runs after every
disable(). Releasing an unpressed button is a harmless no-op.

Validation: lint, typecheck, 1451 tests, 34 script tests, format, build — all green.

Two bugs, both causing the host to be unable to click after a remote-control
session — in the worst case requiring a reboot.

**On quit, the OS-level release never ran.** `will-quit` fired `dispose()`
via `void`, so Electron tore down the process before the async cleanup
finished. Even if it had completed, `dispose()` → `releaseAll()` only released
buttons in `heldButtons` and exited immediately when the set was empty — a
press that had ever escaped tracking stayed down for the whole OS session.

Fixed: `will-quit` now blocks quit with preventDefault(), runs disposal with
a 2-second deadline, and terminates via app.exit(). `dispose()` always calls
`backend.emergencyStop()` which releases LEFT/RIGHT/MIDDLE unconditionally.

**disable() ran releaseAll without emergencyStop.** Taking back control
released tracked buttons only, so the host regaining control still had a stuck
mouse if the tracking had a gap. `emergencyStop` now follows `releaseAll` on
every disable, and releasing a button that is not pressed is a harmless no-op.

**Clicks could land before the pointer arrived.** nut-js was configured with
autoDelayMs = 0, so a position change and button event were delivered
back-to-back. macOS applies the synthetic move asynchronously, so the press
could be delivered before the pointer had moved — the click landed wherever
the pointer used to be. A one-frame settle before each button action lets the
window server catch up. The restore after a click also benefits: without a gap
the whole cycle is a sub-millisecond blip that many controls ignore outright.
@github-actions

Copy link
Copy Markdown

ThreatCrush Security Scan

160 finding(s)

HIGH/CRITICAL: 5 | MEDIUM: 49 | LOW: 106

Severity Rule Location
HIGH secret-generic-credential apps/livekit/fly.toml:12
HIGH manifest-typosquat apps/mobile/package.json:43
HIGH secret-generic-credential apps/turn/fly.toml:11
HIGH secret-generic-credential docs/API.md:648
HIGH secret-generic-credential docs/API.md:654
MEDIUM insecure-temp-file apps/desktop/src/main/streaming/index.test.ts:64
MEDIUM sql-template-interpolation apps/desktop/src/renderer/components/capture/CameraBubble.tsx:129
MEDIUM insecure-temp-file apps/installer/scripts/install.sh:370
MEDIUM insecure-temp-file apps/installer/scripts/install.sh:393
MEDIUM insecure-temp-file apps/installer/scripts/install.sh:402
MEDIUM insecure-temp-file apps/installer/scripts/install.sh:438
MEDIUM js-unescaped-html-sink apps/web/src/app/blog/[slug]/page.tsx:48
MEDIUM js-unescaped-html-sink apps/web/src/app/blog/[slug]/page.tsx:73
MEDIUM js-unescaped-html-sink apps/web/src/app/c/[handle]/page.tsx:191
MEDIUM js-unescaped-html-sink apps/web/src/app/l/[joinCode]/page.tsx:165
MEDIUM js-unescaped-html-sink apps/web/src/app/layout.tsx:128
MEDIUM js-unescaped-html-sink apps/web/src/app/live/page.tsx:145
MEDIUM js-unescaped-html-sink apps/web/src/app/page.tsx:122
MEDIUM js-unescaped-html-sink apps/web/src/app/pricing/page.tsx:284
MEDIUM js-open-redirect apps/web/src/app/pricing/UpgradeButton.tsx:50
MEDIUM js-unescaped-html-sink apps/web/src/app/u/[username]/page.tsx:282
MEDIUM manifest-install-lifecycle-script package.json:54
MEDIUM sql-template-interpolation packages/ai-core/src/prompts.ts:36
MEDIUM insecure-temp-file packages/remote-input/src/backends/waylandYdotool.test.ts:81
MEDIUM insecure-temp-file packages/remote-input/src/backends/waylandYdotool.test.ts:96
MEDIUM insecure-temp-file packages/remote-input/src/backends/waylandYdotool.test.ts:112
MEDIUM insecure-temp-file packages/remote-input/src/backends/waylandYdotool.test.ts:132
MEDIUM insecure-temp-file packages/remote-input/src/backends/waylandYdotool.test.ts:152
MEDIUM insecure-temp-file packages/remote-input/src/backends/waylandYdotool.test.ts:178
MEDIUM insecure-temp-file packages/remote-input/src/backends/waylandYdotool.test.ts:200
MEDIUM sql-template-interpolation scripts/lib/package-managers/apt.ts:55
MEDIUM js-shell-exec-interpolation scripts/lib/package-managers/apt.ts:154
MEDIUM js-shell-exec-interpolation scripts/lib/package-managers/apt.ts:160
MEDIUM js-shell-exec-interpolation scripts/lib/package-managers/apt.ts:208
MEDIUM js-shell-exec-interpolation scripts/lib/package-managers/apt.ts:313
MEDIUM js-shell-exec-interpolation scripts/lib/package-managers/aur.ts:310
MEDIUM sql-template-interpolation scripts/lib/package-managers/aur.ts:341
MEDIUM js-shell-exec-interpolation scripts/lib/package-managers/aur.ts:341
MEDIUM js-shell-exec-interpolation scripts/lib/package-managers/chocolatey.ts:264
MEDIUM js-shell-exec-interpolation scripts/lib/package-managers/chocolatey.ts:288
MEDIUM sql-template-interpolation scripts/lib/package-managers/gentoo.ts:274
MEDIUM sql-template-interpolation scripts/lib/package-managers/homebrew.ts:152
MEDIUM sql-template-interpolation scripts/lib/package-managers/nix.ts:225
MEDIUM sql-template-interpolation scripts/lib/package-managers/rpm.ts:55
MEDIUM js-shell-exec-interpolation scripts/lib/package-managers/rpm.ts:201
MEDIUM js-shell-exec-interpolation scripts/lib/package-managers/rpm.ts:261
MEDIUM sql-template-interpolation scripts/lib/package-managers/scoop.ts:145
MEDIUM js-shell-exec-interpolation scripts/release.mjs:145
MEDIUM js-shell-exec-interpolation scripts/release.mjs:146
MEDIUM js-shell-exec-interpolation scripts/release.mjs:147

…and 110 more. Full results in the Security tab.

Snippets are redacted; ThreatCrush never prints matched credential material.

@mrprecioustech
mrprecioustech merged commit 279cb5f into master Aug 10, 2026
13 checks passed
@mrprecioustech
mrprecioustech deleted the fix/click-release-dispose branch August 10, 2026 13:38
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