Skip to content

macOS: cross-process window capture always fails (NSApp windowWithWindowNumber: only sees own-process windows -> 0x0 SCStreamConfiguration -> start_capture panic) #186

Description

@benjamin-small

Summary

Capturing any cross-process window on macOS fails at start_capture with "Failed due to an invalid parameter" (and panics, since the failure is .expect()ed). Only display capture and same-process windows work. Root cause diagnosed below; verified on 0.0.8 and present verbatim on main.

Symptom

thread 'main' panicked at scap-0.0.8/src/capturer/engine/mod.rs:93:38:
Failed to start capture: "Failed due to an invalid parameter"

Reproduces with any Target::Window belonging to another application (Chrome, etc.); target enumeration succeeds, the failure is at stream start.

Root cause

Both get_target_dimensions and get_scale_factor in src/targets/mac/mod.rs resolve windows via AppKit:

let ns_app: id = NSApp();
let ns_window: id = msg_send![ns_app, windowWithWindowNumber: cg_win_id as NSUInteger];

NSApp().windowWithWindowNumber: can only return windows owned by the calling process. For any other app's window it returns nil; messaging nil yields zeroed scalars, so:

  • get_target_dimensions(0, 0)
  • get_scale_factor0.0

get_output_frame_size then computes 0 * 0, the SCStreamConfiguration gets width: 0, height: 0, and SCStream::start_capture rejects it with "invalid parameter" — which Engine::start converts into a panic via .expect(). Cross-process window capture can never have worked through this path.

Suggested fix

ScreenCaptureKit already knows every shareable window's size — the same SCShareableContent listing used by get_all_targets:

  • get_target_dimensions: look up the window by window_id in SCShareableContent::current().windows and use its width/height (cross-process by design, same permission gate as capture itself).
  • get_scale_factor: the AppKit lookup has the same nil problem; the containing display's backing scale (or main-display scale as an approximation) works cross-process.
  • Consider returning Result from start_capture instead of .expect() so embedding applications can handle a failed start.

Working implementation

We hit this in production and published a patched fork while this is open: https://github.com/benjamin-small/scap-vc (patch in the initial commits; verified by capturing a live Chrome window frame, 3232x1988 BGRA). Happy to turn it into a PR against main if useful.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions