Skip to content

fix(control): serialize inject/disable to prevent permanently stuck clicks - #48

Merged
mrprecioustech merged 1 commit into
masterfrom
fix/click-race-trackpad
Aug 10, 2026
Merged

fix(control): serialize inject/disable to prevent permanently stuck clicks#48
mrprecioustech merged 1 commit into
masterfrom
fix/click-race-trackpad

Conversation

@mrprecioustech

Copy link
Copy Markdown
Contributor

Bug

Neither the host nor the guest could click anything during remote control.

Root cause

A race between inject('down') and disable(). When the guest pressed a
button:

  1. inject('down')dispatch()await getCursorPosition() (yields event loop)
  2. During that await, control was revoked → disable()releaseAll()
  3. releaseAll read heldButtons — which was empty because trackHeldState hadn't run yet
  4. dispatch resumed → nut-js pressed the button at the OS level
  5. trackHeldState recorded {'left'} in heldButtons — too late

The OS-level button press went through but the release never came. Every
subsequent click was treated as a continuation of that drag, so nothing was
clickable — for either person — until the process was restarted.

Fix

inject() now chains onto a pendingInject promise. disable() and
emergencyStop() wait for any in-progress injection to finish before calling
releaseAll(), so heldButtons always reflects the post-trackHeldState state.

Also

Added pointer event listeners (pointerdown/pointerup/pointermove)
alongside the existing mouse event listeners, with a 100ms dedup window so
Chromium's double-fire (pointer then mouse) only results in one event. This
covers trackpads that emit pointer events without mouse events.

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

…licks

When inject('down') was in-flight (mid-await inside dispatch), disable()
released nothing because trackHeldState hadn't added the button to heldButtons
yet. Nut.js had already pressed it at the OS level, so the release never came.
Every subsequent click was then treated as a continuation of that drag — hence
"neither side can click anything."

Inject now chains onto a promise so disable() and emergencyStop() wait for any
in-progress injection to finish before reading heldButtons — releaseAll always
sees the post-trackHeldState state.

Also adds pointer event listeners so trackpads that fire pointer events without
mouse events still produce clicks on the host. A dedup ref prevents the
mouse-event double-fire Chromium also delivers.
@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 05f5546 into master Aug 10, 2026
13 checks passed
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