Skip to content

fix(screenshot): capture display containing Thuki window on multi-monitor setups#191

Merged
quiet-node merged 1 commit into
mainfrom
worktree-eager-munching-hoare
May 26, 2026
Merged

fix(screenshot): capture display containing Thuki window on multi-monitor setups#191
quiet-node merged 1 commit into
mainfrom
worktree-eager-munching-hoare

Conversation

@quiet-node
Copy link
Copy Markdown
Owner

Overview

/screen previously always captured the primary (menu-bar) display. On a multi-monitor setup, users invoking /screen while Thuki is shown on a secondary display would receive a screenshot of the wrong monitor. This PR makes /screen capture the display Thuki's own window is currently shown on.

How it works

  1. Before dispatching the screenshot pipeline, the Tauri command resolves Thuki's WebviewWindow and asks AppKit ([NSScreen deviceDescription][NSScreenNumber]) for the CGDirectDisplayID of the screen the window lives on. This is the canonical multi-monitor query and sidesteps the coordinate-conversion mismatches that occur on mixed-DPI setups (e.g. a 2x retina primary + 1x secondary).
  2. The display ID is converted to its Quartz-coordinate rectangle via CGDisplayBounds and reduced to a center anchor point.
  3. capture_full_screen_raw accepts an Option<(f64, f64)> anchor. The anchor is hit-tested with CGGetDisplaysWithPoint; the matching display's bounds are passed to CGWindowListCreateImage.
  4. Fallback chain when any step fails (window missing, window offscreen, point outside all displays): defaults to the main (menu-bar) display, preserving prior behavior.

The CoreGraphics display helpers that previously lived inline inside lib.rs are extracted into src-tauri/src/cg_displays.rs, with one new function bounds_for_display(display_id) for ID-based lookup. The activator continues to use the existing point-based helpers without change.

Highlights

  • New module src-tauri/src/cg_displays.rs consolidates CGGetDisplaysWithPoint, CGDisplayBounds, CGMainDisplayID FFI behind three small wrappers.
  • nswindow_display_id performs raw Objective-C messaging (screendeviceDescriptionNSScreenNumber) without pulling in extra objc2-app-kit features.
  • display_bounds_center is a pure helper, unit-tested for primary, offset, negative-origin, and zero-size cases.
  • Strict main-thread discipline: the NSWindow pointer is fetched off-thread but only dereferenced inside run_on_main_thread.
  • Backwards-compatible API: capture_full_screen_pixels takes Option<(f64, f64)>; None reproduces prior single-display behavior.

Testing

  • bun run test:all:coverage — frontend + backend tests pass; backend cargo llvm-cov gate holds at 100% line and function coverage. CoreGraphics and Objective-C FFI wrappers are marked #[cfg_attr(coverage_nightly, coverage(off))] because they require a live window server and a real NSWindow to exercise; their consumers exercise the surrounding logic.
  • bun run validate-build — lint, format, typecheck, and release build complete with zero warnings/errors.
  • Manual: invoked /screen on a multi-monitor setup with Thuki shown on the secondary display; resulting screenshot is the secondary display, not the primary. Confirmed fallback to primary when Thuki is hidden.

Note

The Objective-C messaging in nswindow_display_id relies on the autorelease pool maintained by the main-thread runloop. This is the standard contract for AppKit calls dispatched via run_on_main_thread and is consistent with the existing screenshot pipeline's use of CGWindowListCreateImage.

…itor setups

Signed-off-by: Logan Nguyen <lg.131.dev@gmail.com>
@quiet-node quiet-node merged commit 611dff1 into main May 26, 2026
3 checks passed
@quiet-node quiet-node deleted the worktree-eager-munching-hoare branch May 26, 2026 18:03
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