From d7665693dfc61736a13c64f5c666c5c8346fcc71 Mon Sep 17 00:00:00 2001 From: Francesco Bonacci Date: Mon, 25 May 2026 19:26:22 +0000 Subject: [PATCH] fix(cua-driver-rs)(windows)(capture): size PrintWindow buffer to GetWindowRect, not GetClientRect MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit `screenshot_window_bytes_with_occlusion_unsafe`'s non-XAML path allocated its capture bitmap from `GetClientRect`, then called `PrintWindow(hwnd, mem_dc, PW_RENDERFULLCONTENT)`. PrintWindow draws the entire window at 1:1 starting at (0,0), but the destination bitmap is only client-sized, so any non-client content past the client rect gets clipped silently. This bites VCL/SAL dialogs hard. LibreOffice's "Document Recovery" (the post-discard summary modal) is 607x271 with a Save/Cancel button row at the bottom. GetClientRect reports 605x239 — the button strip lives in VCL's bottom command area which sits OUTSIDE the standard Win32 client area. The returned screenshot showed title bar + body + the directory picker, but the buttons were just gone. Users (and LLM agents) couldn't see what they were supposed to click. Fix: switch to GetWindowRect so the buffer matches the actual rendered window dimensions. PrintWindow with PW_RENDERFULLCONTENT then draws the full window content into a correctly-sized bitmap. Verified end-to-end: same dialog now returns 621x278, both Save and Cancel are visible in the bottom-right corner of the image. Co-Authored-By: Claude Opus 4.7 --- .../rust/crates/platform-windows/src/capture.rs | 17 ++++++++++++++--- 1 file changed, 14 insertions(+), 3 deletions(-) diff --git a/libs/cua-driver/rust/crates/platform-windows/src/capture.rs b/libs/cua-driver/rust/crates/platform-windows/src/capture.rs index c689cec765..d91c5d8b7e 100644 --- a/libs/cua-driver/rust/crates/platform-windows/src/capture.rs +++ b/libs/cua-driver/rust/crates/platform-windows/src/capture.rs @@ -211,7 +211,7 @@ pub fn screenshot_window(hwnd: u64) -> Result<(String, u32, u32)> { } unsafe fn screenshot_window_bytes_with_occlusion_unsafe(hwnd: u64) -> Result<(Vec, bool)> { - use windows::Win32::UI::WindowsAndMessaging::GetClientRect; + use windows::Win32::UI::WindowsAndMessaging::GetWindowRect; use windows::Win32::Foundation::RECT; let hwnd_raw = hwnd; @@ -262,12 +262,23 @@ unsafe fn screenshot_window_bytes_with_occlusion_unsafe(hwnd: u64) -> Result<(Ve } } + // Size the capture buffer to the WHOLE window (GetWindowRect), not + // just the client area (GetClientRect). PrintWindow draws the entire + // window at 1:1 starting at (0,0) — if the buffer is sized to the + // client area only, anything in the non-client region clips. The + // surprising case is VCL/SAL dialogs (LibreOffice's "Document + // Recovery", any of its Confirmation modals): VCL puts the bottom + // button strip OUTSIDE the standard Win32 client area, so a + // client-sized buffer loses the Save/Cancel/OK row at the bottom. + // Window-sized buffer captures title bar + body + non-client trim + // correctly; the small extra rows at the top (window frame) are + // worth it to never silently truncate buttons. let mut rect = RECT::default(); - GetClientRect(hwnd, &mut rect)?; + GetWindowRect(hwnd, &mut rect)?; let w = (rect.right - rect.left) as i32; let h = (rect.bottom - rect.top) as i32; if w <= 0 || h <= 0 { - bail!("Window has zero/negative client size: {}x{}", w, h); + bail!("Window has zero/negative size: {}x{}", w, h); } let screen_dc = GetWindowDC(hwnd);