Skip to content

[bug/windows]: window screenshots silently never happen #2046

Description

@oshtz

Description

On Windows, taking a screenshot of a specific window never produces a screenshot: no file is written and the screenshot editor does not open. There is no error, toast, or log entry. Capturing a display works and creates the expected screenshot project.

Note: earlier revisions of this issue attributed the failure to WebView2-hosted apps and to Windows window-target selection. Follow-up investigation disproved both. The root cause is in the desktop frontend and affects window screenshots generally, regardless of which app is being captured.

Expected behavior: selecting a window in the screenshot picker captures that window.

Actual behavior: nothing happens, silently.

Reproduction

  1. On Windows, open Cap's screenshot target picker and choose Window.
  2. Hover a window and click it.
  3. Observe that no screenshot is saved and the editor does not open, with no error shown.
  4. Repeat with a display target and observe that it succeeds.

Root cause

In apps/desktop/src/routes/target-select-overlay.tsx, the window variant's onRecordingStart called commands.closeTargetSelectOverlays() while options.mode === "screenshot".

On Windows, that command closes the target-select overlay windows rather than hiding them (they are closed to release the DirectComposition surface). But takeScreenshot is invoked from that same webview immediately afterwards, so the webview is destroyed before the IPC call is dispatched. The screenshot request never reaches the Rust side, and because the caller is gone there is nothing left to surface an error.

This is the exact hazard already documented in dismissPickerForRecordingStart in the same file:

Hide rather than close: startRecording is invoked from THIS webview right after dismissal, and closing destroys the webview before the invoke is dispatched - the recording then silently never starts.

The window + screenshot branch was the one path that did not follow that rule.

Why display and area targets were unaffected: they either use dismissPickerForRecordingStart (hide-only) or do not dismiss the picker at all. Only the window branch closed the overlay outright.

Supporting evidence:

  • Across multiple Cap log files, take_screenshot appears only with target=Display. There is no logged window-target invocation, confirming the failure happens in the frontend before Rust is reached.
  • Running Cap's own scap-targets code against a live window shows selection is healthy: the window is enumerated, is_valid=true, and it reports correct display-relative bounds.

Not the cause (recorded to avoid re-investigation)

Executable-name filtering in crates/scap-targets/src/platform/win.rs was suspected but ruled out:

  • A WebView2-hosted app's visible top-level window is owned by the host process, so the ignored-executable list (matched by exact file name against the top-level window's own PID) never applied to it.
  • get_topmost_at_cursor does reject the msedgewebview2.exe-owned child HWND, but falls through to its top-level-window fallback and returns the correct host window.
  • An A/B test at a cursor point where WindowFromPoint returns the WebView2 child HWND produced identical, correct results with and without a patch resolving the HWND to its root window.

That filtering is still arguably too broad - it substring-matches "cap", which would reject unrelated executables whose names contain it - but it is not the cause of this bug and is better addressed separately.

Fix

Remove the commands.closeTargetSelectOverlays() call from the screenshot branch of the window variant's onRecordingStart. The screenshot handler in RecordingControls already hides the overlay windows, performs the capture, and closes them afterwards, and its error path shows a toast, so genuine failures become visible instead of silent.

The main window still reveals correctly because that is driven by targetModeDismissal: "screenshot", which the fix continues to set.

Additional Context

  • Cap version: main at d0491b1e59d6ffe4386524ba67047d039e46d58e
  • Operating system: Windows 11

Verification

Verified in a local pnpm dev:desktop build on Windows: a window screenshot now captures successfully and opens in the screenshot editor, writing an original.png at the exact window bounds with real image content.

Acceptance criteria

  • Selecting a window in the screenshot picker captures that window and opens the editor.
  • The Screenshot current window hotkey works.
  • Cap's own windows and overlays remain excluded from capture targets.
  • Display and area screenshot capture remain unchanged.
  • Screenshot failures surface an error to the user instead of silently producing nothing.

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