fix: restore tablet cursor after display sleep/wake#43
Open
lhysilicon wants to merge 7 commits into
Open
Conversation
After the display idle-sleeps and wakes, ScreenCaptureKit delivers no frames for cursor-only movement over the static virtual display, so the 0.6.8 idle keepalive re-sends a stale, cursor-less frame indefinitely and the stall-based restart never fires. The tablet shows a live picture with no cursor until a manual restart. - Hold kIOPMAssertionTypePreventUserIdleDisplaySleep while streaming so the display (and the virtual display) never idle-sleeps. - On NSWorkspace.screensDidWake/didWake, rebuild the SCStream via hardRestart(reason:), guarded by an isStreaming flag + streamGeneration token so a wake restart cannot resurrect a stopped stream; debounced 2s. - Pass kCGDisplayStreamShowCursor to the CGDisplayStream fallback (was nil). Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
|
@lhysilicon is attempting to deploy a commit to the tranvuongquocdat's projects Team on Vercel. A member of the Team first needs to authorize it. |
- kCGDisplayStreamShowCursor was obsoleted in the 26.x SDK; use the renamed CGDisplayStream.showCursor so the CGDisplayStream cursor fallback compiles. - restartStream() lacked the isStreaming + streamGeneration guard that hardRestart() has, so a stale stall-monitor restart could resurrect capture after stopStreaming(). Add the same generation-token guard at each async continuation point. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…activation - A Finder/open-launched app has no ~/.local/bin in PATH, so setupADBReverse() and adb probing failed to find adb and USB mode never tunnelled. Resolve adb via a single StatusDetector.adbExecutablePath() that also searches ~/.local/bin, Homebrew, /usr/local/bin and the Android SDK; reuse it in AppDelegate instead of a duplicate inline search. - Screen Recording grant was not re-checked after the user granted it, so Start stayed disabled until relaunch. Re-check passively on applicationDidBecomeActive (no system prompt); only the explicit permission button requests access. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Contributor
|
Is this similar to #42? |
- Touch input needs Accessibility to inject CGEvents; the app never prompted for it (promptAccessibilityPermission had no caller), so enabling touch silently did nothing. Prompt for Accessibility when touch is enabled (launch + toggle) and reflect the grant in the UI. - Connection status showed a green 'Listening' even when stopped (it keyed off IP presence, not listener state). Show real states: Server stopped / Waiting for tablet / connected. - 'Gaming Boost' claimed '1000 Mbps' and '120 Hz' in the UI but the encoder forces 50 Mbps. Rename to 'Low-Latency Mode' and state honestly (50 Mbps target, speed prioritized, quality may drop). Normal mode now respects the user's chosen bitrate (dropped the hidden max(bitrate,60) floor). Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
- Low-Latency Mode (ex-Gaming Boost): finish honest reporting — effectiveBitrate and the settings notices now reflect the real 50 Mbps target instead of 1000; refresh rate is the user's pick (not a forced 120); diagnostics/labels no longer say 'Gaming Boost'. - adb probe: single-flight + per-command timeout + generation check so slow/hung adb can't pile up overlapping detached probes; skip adb entirely in Wireless mode; cache the not-found result under the same TTL. - UI polish: real VoiceOver labels; no QR while the LAN address is empty (was 0.0.0.0), rebuild on change; Reset uses one shared Defaults source so a reset no longer flips 60Hz->120Hz; bitrate slider commits once on drag-end instead of rebuilding the encoder every 10 Mbps. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Stop previously drained frameQueue/receiveQueue synchronously on the main thread, freezing the settings window under backlog. Move teardown to a background queue with a lifecycle generation token; the UI drops to a 'stopping' state immediately and mode/resolution auto-restart happens once, after teardown. Compiles + no streaming/start regression; Stop-path smoothness not yet driven-tested via UI. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
- Port used TextField(value:format:) which only commits on Return/focus-loss, so clicking Start could read a stale port; port 0 was not rejected. Use a local string draft parsed live to 1...65535, commit before Start, disable Start when invalid. - adb status collapsed missing/unauthorized/offline/multiple-devices/command-error into one 'Not detected'. Return a small ADBStatus enum (parsed from stderr + exit code) and show the matching actionable hint in the status row. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Problem
When the Mac's display idle-sleeps (
pmset displaysleep) and then wakes, the tablet keeps showing a live picture but the mouse cursor is gone — and stays gone until the stream is manually restarted.Root cause
SCStreamConfiguration.showsCursor = truelets ScreenCaptureKit composite the cursor into captured frames. But cursor-only movement over an idle virtual display does not dirty the surface, so ScreenCaptureKit delivers no new frames. The idle keepalive added in 0.6.8 then re-sends the last cached buffer indefinitely and resetslastFrameTime, so the stall-based restart instartFrameMonitornever fires — the tablet is frozen on a stale, cursor-less frame. There was no display sleep/wake handling, so nothing rebuilt the capture.Confirmed empirically: with a static desktop, sweeping the real cursor across the virtual display's global coordinates produced zero new SCStream frames (the host stayed at the ~1 fps keepalive the whole time).
Fix
kIOPMAssertionTypePreventUserIdleDisplaySleepwhile streaming so the display (hence the virtual display) never idle-sleeps. Released instopStreaming.NSWorkspace.screensDidWakeNotification/didWakeNotification, rebuild the SCStream via a newScreenCapture.hardRestart(reason:). Guarded by anisStreamingflag + astreamGenerationtoken so a wake restart can never resurrect a stream that was stopped mid-flight, and debounced 2 s (both notifications can fire on a full-system wake).CGDisplayStreamfallback was created withproperties: nil, which omits the cursor; it now passes[kCGDisplayStreamShowCursor: true].Two files changed:
MacHost/Sources/ScreenCapture.swift,MacHost/Sources/AppDelegate.swift.Notes / testing
@Statemacro does not expand under CLT, which blocks the link (unrelated to this change). In whole-module compilation the two changed files produced no compiler errors — only the pre-existing SwiftUI/toolchain issue inSettingsWindow.swiftstopped the final binary. Please verify the build on a full-Xcode setup before merging.didChangeScreenParametersNotificationwas intentionally not hooked: it fires on benign arrangement/resolution changes and on every virtual-display create/destroy, which would cause spurious restarts;screensDidWakeis the precise signal for this bug.🤖 Generated with Claude Code