fix(client): window screenshot via crop instead of PrintWindow, fix SW_MINIMIZE
This commit is contained in:
parent
92d3907ec7
commit
9285dbbd49
2 changed files with 36 additions and 51 deletions
|
|
@ -117,68 +117,53 @@ pub fn take_screenshot() -> Result<(String, u32, u32), String> {
|
|||
}
|
||||
}
|
||||
|
||||
/// Capture a specific window using PrintWindow (works even if occluded).
|
||||
/// Capture a specific window by cropping the full screen to its rect.
|
||||
#[cfg(windows)]
|
||||
pub fn take_window_screenshot(window_id: u64) -> Result<(String, u32, u32), String> {
|
||||
use windows::Win32::Foundation::HWND;
|
||||
use windows::Win32::Graphics::Gdi::{
|
||||
CreateCompatibleBitmap, CreateCompatibleDC, DeleteDC, DeleteObject,
|
||||
GetDC, GetDIBits, PrintWindow, PW_RENDERFULLCONTENT, ReleaseDC,
|
||||
SelectObject, BITMAPINFO, BITMAPINFOHEADER, DIB_RGB_COLORS,
|
||||
};
|
||||
use windows::Win32::Foundation::{HWND, RECT};
|
||||
use windows::Win32::UI::WindowsAndMessaging::GetWindowRect;
|
||||
|
||||
let hwnd = HWND(window_id as isize);
|
||||
|
||||
unsafe {
|
||||
let (x, y, w, h) = unsafe {
|
||||
let mut rect = RECT::default();
|
||||
GetWindowRect(hwnd, &mut rect).map_err(|e| format!("GetWindowRect failed: {e}"))?;
|
||||
let width = (rect.right - rect.left) as u32;
|
||||
let height = (rect.bottom - rect.top) as u32;
|
||||
if width == 0 || height == 0 {
|
||||
return Err(format!("Window has zero size: {width}x{height}"));
|
||||
}
|
||||
let w = (rect.right - rect.left) as u32;
|
||||
let h = (rect.bottom - rect.top) as u32;
|
||||
if w == 0 || h == 0 { return Err(format!("Window has zero size: {w}x{h}")); }
|
||||
(rect.left, rect.top, w, h)
|
||||
};
|
||||
|
||||
let hdc_screen = GetDC(None);
|
||||
let hdc_mem = CreateCompatibleDC(hdc_screen);
|
||||
let hbm = CreateCompatibleBitmap(hdc_screen, width as i32, height as i32);
|
||||
let old_obj = SelectObject(hdc_mem, hbm);
|
||||
// Take full screenshot and crop to window rect
|
||||
let (full_b64, full_w, full_h) = take_screenshot()?;
|
||||
let full_bytes = base64::engine::general_purpose::STANDARD
|
||||
.decode(&full_b64).map_err(|e| format!("base64 decode: {e}"))?;
|
||||
|
||||
// PrintWindow captures the window even if it's behind others
|
||||
PrintWindow(hwnd, hdc_mem, PW_RENDERFULLCONTENT);
|
||||
// Decode PNG back to raw RGBA
|
||||
let cursor = std::io::Cursor::new(&full_bytes);
|
||||
let decoder = png::Decoder::new(cursor);
|
||||
let mut reader = decoder.read_info().map_err(|e| format!("PNG decode: {e}"))?;
|
||||
let mut img_buf = vec![0u8; reader.output_buffer_size()];
|
||||
reader.next_frame(&mut img_buf).map_err(|e| format!("PNG frame: {e}"))?;
|
||||
|
||||
let mut bmi = BITMAPINFO {
|
||||
bmiHeader: BITMAPINFOHEADER {
|
||||
biSize: std::mem::size_of::<BITMAPINFOHEADER>() as u32,
|
||||
biWidth: width as i32,
|
||||
biHeight: -(height as i32),
|
||||
biPlanes: 1,
|
||||
biBitCount: 32,
|
||||
biCompression: 0,
|
||||
biSizeImage: 0,
|
||||
biXPelsPerMeter: 0,
|
||||
biYPelsPerMeter: 0,
|
||||
biClrUsed: 0,
|
||||
biClrImportant: 0,
|
||||
},
|
||||
bmiColors: [Default::default()],
|
||||
};
|
||||
// Clamp window rect to screen bounds
|
||||
let x0 = (x.max(0) as u32).min(full_w);
|
||||
let y0 = (y.max(0) as u32).min(full_h);
|
||||
let x1 = ((x as u32 + w)).min(full_w);
|
||||
let y1 = ((y as u32 + h)).min(full_h);
|
||||
let cw = x1 - x0;
|
||||
let ch = y1 - y0;
|
||||
|
||||
let mut pixel_buf: Vec<u8> = vec![0u8; (width * height * 4) as usize];
|
||||
GetDIBits(hdc_mem, hbm, 0, height, Some(pixel_buf.as_mut_ptr() as *mut _), &mut bmi, DIB_RGB_COLORS);
|
||||
|
||||
SelectObject(hdc_mem, old_obj);
|
||||
DeleteObject(hbm);
|
||||
DeleteDC(hdc_mem);
|
||||
ReleaseDC(None, hdc_screen);
|
||||
|
||||
// BGRA → RGBA
|
||||
for chunk in pixel_buf.chunks_exact_mut(4) { chunk.swap(0, 2); }
|
||||
|
||||
let png_bytes = encode_png(&pixel_buf, width, height)?;
|
||||
let b64 = base64::engine::general_purpose::STANDARD.encode(&png_bytes);
|
||||
Ok((b64, width, height))
|
||||
// Crop: 4 bytes per pixel (RGBA)
|
||||
let mut cropped = Vec::with_capacity((cw * ch * 4) as usize);
|
||||
for row in y0..y1 {
|
||||
let start = ((row * full_w + x0) * 4) as usize;
|
||||
let end = start + (cw * 4) as usize;
|
||||
cropped.extend_from_slice(&img_buf[start..end]);
|
||||
}
|
||||
|
||||
let png_bytes = encode_png(&cropped, cw, ch)?;
|
||||
let b64 = base64::engine::general_purpose::STANDARD.encode(&png_bytes);
|
||||
Ok((b64, cw, ch))
|
||||
}
|
||||
|
||||
#[cfg(not(windows))]
|
||||
|
|
|
|||
|
|
@ -8,7 +8,7 @@ mod win_impl {
|
|||
use windows::Win32::Foundation::{BOOL, HWND, LPARAM};
|
||||
use windows::Win32::UI::WindowsAndMessaging::{
|
||||
BringWindowToTop, EnumWindows, GetWindowTextW, IsWindowVisible, SetForegroundWindow,
|
||||
ShowWindow, SW_MAXIMIZE, SW_RESTORE,
|
||||
ShowWindow, SW_MAXIMIZE, SW_MINIMIZE, SW_RESTORE,
|
||||
};
|
||||
use windows::Win32::UI::Input::KeyboardAndMouse::{
|
||||
keybd_event, KEYEVENTF_KEYUP, VK_MENU,
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue