Skip to content

fix(recording): record the whole window in macOS Focus mode - #4254

Open
richiemcilroy wants to merge 2 commits into
mainfrom
fix/focus-mode-window-crop
Open

richiemcilroy wants to merge 2 commits into
mainfrom
fix/focus-mode-window-crop

Conversation

@richiemcilroy

@richiemcilroy richiemcilroy commented Nov 14, 2026 •

Copy link
Copy Markdown
Member

Users on 0.6.2 and 0.6.3 (macOS 27, Apple Silicon) reported that a window recorded with Focus mode on is missing its outer edge: in a terminal, the first character of every line is gone. The same window recorded with Focus mode off is complete. "Center on screen" and the aspect ratio presets make no difference.

Root cause

Focus mode records the window by itself through a ScreenCaptureKit desktop-independent window filter. ScreenCaptureKit hands that window over with its rounded corners transparent, and the encoder turns them black. To hide those corners, c0b11f5 (in #3452) made window-only capture read the window 8 pt in from every edge (WINDOW_CORNER_TRIM). That is 8 px on a 1x display and 16 px on Retina, taken off all four sides, which is a full terminal column on the left and right. The cursor crop was trimmed the same way, so it stayed aligned with the cut picture.

Nothing else in the path changes the window's bounds. The crop comes from the window's kCGWindowBounds frame (no shadow), and even_pixel_crop only drops a final odd pixel, the same as a screen-region recording.

Change

  • source_rect reads a window-only capture from the window's own top-left corner at its full crop size.
  • ScreenCaptureConfig::init no longer trims the window crop, so the frame size is the whole window. WINDOW_CORNER_TRIM and trim_window_corners are removed.
  • macos_window_crop in cursor.rs places the cursor against the whole window again, as it did before c0b11f5.
  • The real-capture Focus case in crates/verify now expects the window's full size instead of the size less the trim.

The rounded corners of a window recorded in Focus mode are black again, as they were before c0b11f5. A screen-region recording shows whatever is behind the window there; Focus mode has nothing behind the window to show. Losing a column of the window's content is the worse of the two.

Purple badge in place of the traffic lights

This is macOS behaviour, not something Cap draws. While any ScreenCaptureKit stream names a window in its filter, macOS 27 replaces that window's traffic lights with the purple sharing badge, on screen and in the captured frames. I checked it with a standalone ScreenCaptureKit program on macOS 27.0.1, without Cap: a desktopIndependentWindow filter and a display(including: [window]) filter both badge the window. A display filter that does not name the window leaves the traffic lights alone.

The Tauri app never used a window filter for recording (only for one-off thumbnails), so it never showed the badge. Focus mode needs the window filter to keep windows in front of the target out of the video. #3452 tried a display crop that leaves out the windows in front, and reverted it in e1d1c1b. This PR leaves the badge as it is.

Callers checked

  • source_rect: the first stream (to_sources) and the restart (rebuild_capturer) in macos.rs. Both pass captures_window from the same capturable_window check, so a window that is excluded from the recording still takes the display path with its display-relative crop.
  • trim_window_corners was only called by ScreenCaptureConfig::init and cursor::macos_window_crop. WINDOW_CORNER_TRIM was only read by source_rect and the verify test.
  • Studio and Instant both reach this through window_only_target. Instant without a camera uses it; Instant with a camera stays on the display crop, unchanged.
  • Display, area and non-Focus window recordings never set captures_window, so their source rect, frame size and cursor crop are unchanged. Windows and Linux do not compile any of this code.

Overlap with #3942

#3942 (open) follows a window resized during a Focus recording, and computes the new source rect the same way as the starting one. Whichever lands second needs a small merge: the resize follow should use the untrimmed window size.

Tests

In cap-recording (sources::screen_capture::macos::tests):

  • focus_reads_the_whole_window_from_its_top_left: a Focus capture reads from (0, 0) at the full window size; the region capture of the same window keeps its display position.
  • focus_reads_as_many_pixels_as_a_screen_region_at_every_scale: at 1x, 1.5x and 2x, with odd and half-point sizes, Focus and region read the same size and lose less than one even pixel of the window.
  • focus_reads_a_partly_offscreen_window_from_its_top_left: a window whose crop starts off the display is still read from its own top-left corner.

The trim tests are removed. The even-pixel test is kept.

cargo test -p cap-recording --lib screen_capture (30 passed) and cargo test -p cap-recording --lib cursor (29 passed) on macOS 27.0.1, Apple Silicon.

Proof

Mac mini, macOS 27.0.1, Apple Silicon, one 1920x1080 display at 1x. I recorded the same Terminal window (1000x604 pt, W<n>-first-column-marker on every line) through studio_recording and instant_recording with with_window_only(false) (Focus off) and with_window_only(true) (Focus on). The program is a 50-line local example that calls the builders the same way the app does; it is not committed. Each mode was built from main and from this branch.

Recording main this PR
Studio, Focus off 1000x604 1000x604
Studio, Focus on 984x588 1000x604
Instant, Focus off 1000x604 1000x604
Instant, Focus on 984x588 1000x604

Pixel comparison of Focus on against Focus off, from a frame 2 s in, 4 px in from every edge:

  • main: lines up only after shifting it back by 8 px (mean difference 61.4 unshifted, 4.5 shifted). Eight pixels are gone from each side.
  • this PR: lines up unshifted, mean difference 4.1 (Studio) and 5.0 (Instant), with 98.8% and 97.6% of pixels within 15 levels. The leftover difference is the Terminal's translucent background, which shows the desktop in a screen-region recording and nothing behind it in Focus. Main's best alignment has the same residual.
  • Outermost column: the window's 1 px border is blended with the desktop in the screen region and with black in Focus (mean difference about 20). That is the only edge difference.

Terminal, top-left corner at 4x. Left to right: Focus off, Focus on with main, Focus on with this PR.

Terminal left edge, Studio

Same for Instant:

Terminal left edge, Instant

Bottom-right corner, Studio. Main loses the corner and the last rows; this PR keeps them, with the rounded corner black:

Terminal bottom right, Studio

Whole frames, Focus off and on with this PR: terminal-fix-focus-off-vs-on.png

Other windows, Studio, Focus on:

Window main this PR
Finder, 760x480 pt (sidebar, toolbar) 744x464, lines up at an 8 px shift 760x480, lines up unshifted, mean difference 7.2
Safari, 800x652 pt 784x636 800x652
Terminal moved to x = -180, partly off the display 984x588 1000x604, the whole window including the part off the display

Finder: Focus off, main, this PR · Partly offscreen Terminal: Focus off, main, this PR

CI: real-capture (blacksmith-6vcpu-macos-26) runs recording/studio-window/focus through the full studio pipeline. On this branch it records the 640x400 stimulus window at 640x400 (154 frames), and its size check now fails on anything smaller than the whole window.

Proof on a Retina display

The mini's panel has no HiDPI mode, so I added a 1280x800 pt virtual display at 2x (2560x1600 px) with CoreGraphics' CGVirtualDisplay for the length of the run, then removed it. macOS treats it as a real Retina display: WindowServer composites at 2x and ScreenCaptureKit captures it like any other display. The 1080p panel stayed attached at 1x, so this also covers a second display at a different scale. I recorded the same Terminal window (1000x604 pt) on the 2x display, Studio and Instant, Focus off and on, from main and from this branch.

Recording main this PR
Studio, Focus off 2000x1208 2000x1208
Studio, Focus on 1968x1176 2000x1208
Instant, Focus off 2000x1208 2000x1208
Instant, Focus on 1968x1176 2000x1208
  • Size: with this PR the Focus frame is the window size x2, the same as Focus off. Main is 32 px short in each axis, 16 px off every side.
  • Alignment: this PR lines up with Focus off at zero offset; main lines up only after a 16 px shift.
  • First column: the first text pixels of each terminal line sit at x = 20 in Focus off and in this PR's Focus frame, Studio and Instant. In main's Focus frame they sit at x = 4, so 16 px of the window's left side are gone.
  • Pixel comparison: the virtual display became the main display, so other sessions' system dialogs sat on top of part of the Terminal. Focus off records them and Focus leaves them out, as intended, so I compared only the areas they don't cover. Away from the edges, the difference is 6.2 to 8.4 mean, with 96.9 to 99.6% of pixels within 15 levels; main's best alignment gives 6.0 to 8.3. Along the outer 8 px of each side, this PR gives 0.03 to 10.2 mean. The residual is the 1 px window border and the Terminal's translucent background, as at 1x. Main has none of those 8 px strips in its frame.

Terminal top-left corner on the 2x display, at 3x. Left to right: Focus off, Focus on with main, Focus on with this PR.

Retina top left, Studio

Retina top left, Instant

Bottom-right corner, Studio:

Retina bottom right, Studio

Not covered on hardware

  • A physical Retina panel: the 2x proof above used a virtual display. Fractional scales such as 1.5x are covered by the unit tests only.
  • Ghostty is not installed on the mini; Terminal, Finder and Safari stand in for it. The trim was the same 8 pt for every window, whatever app it belongs to.
  • A window resized during the recording is fix(recording): keep a resized window whole in macOS Focused window recordings #3942's subject and is unchanged here.

RetriggerView in GreptileConfidence Score: 5/5

The PR appears safe to merge.

Summary

Restores the full window in macOS Focus recordings instead of removing 8 points from every edge.

  • Focus recordings now keep the selected window's full edges.

No actionable issues found. Native macOS capture was not run in this Linux environment.

Reviews (5) · Last reviewed commit: "Merge remote-tracking branch 'origin/mai..." · Reviewed by Greptile

@sandchest-com

sandchest-com Bot commented Nov 14, 2026 •

Copy link
Copy Markdown

Sandchest

Sandchest reviewed d823bbc: nothing exploitable found in this change.

Sandchest reviews every change for vulnerabilities an attacker could use · Review details

@capy-ai capy-ai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Capy found no issues.

Open in Capy

@vercel

vercel Bot commented Nov 14, 2026 •

Copy link
Copy Markdown
Contributor

The latest updates on your projects. Learn more about Vercel for GitHub.

1 Skipped Deployment
Project Deployment Actions Updated
cap-status Ignored Ignored Preview Nov 14, 2026 7:16am UTC

@capy-ai capy-ai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Capy found no issues.

View 1 other finding in Capy.

Open in Capy

This branch was successfully deployed

1 active deployment
Preview – cap-web — d823bbc8 Deployed Nov 14, 2026 by vercel[bot]
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant