diff --git a/.vscode/extensions.json b/.vscode/extensions.json index 4c56e1c..d04b7df 100644 --- a/.vscode/extensions.json +++ b/.vscode/extensions.json @@ -2,7 +2,6 @@ "recommendations": [ "streetsidesoftware.code-spell-checker", "wengerk.highlight-bad-chars", - "serayuzgur.crates", "editorconfig.editorconfig", "tamasfe.even-better-toml", "rust-lang.rust-analyzer" diff --git a/Cargo.toml b/Cargo.toml index 9a82267..02712bf 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "xcap" -version = "0.0.11" +version = "0.0.12" edition = "2021" description = "XCap is a cross-platform screen capture library written in Rust. It supports Linux (X11, Wayland), MacOS, and Windows. XCap supports screenshot and video recording (to be implemented)." license = "Apache-2.0" diff --git a/src/windows/capture.rs b/src/windows/capture.rs index 446c6fe..398e451 100644 --- a/src/windows/capture.rs +++ b/src/windows/capture.rs @@ -1,4 +1,4 @@ -use image::RgbaImage; +use image::{DynamicImage, RgbaImage}; use std::{ffi::c_void, mem}; use windows::Win32::{ Foundation::HWND, @@ -11,13 +11,10 @@ use windows::Win32::{ }, }, Storage::Xps::{PrintWindow, PRINT_WINDOW_FLAGS}, - UI::WindowsAndMessaging::GetDesktopWindow, + UI::WindowsAndMessaging::{GetDesktopWindow, WINDOWINFO}, }; -use crate::{ - error::{XCapError, XCapResult}, - platform::utils::get_window_rect, -}; +use crate::error::{XCapError, XCapResult}; use super::{ boxed::{BoxHBITMAP, BoxHDC}, @@ -115,12 +112,17 @@ pub fn capture_monitor(x: i32, y: i32, width: i32, height: i32) -> XCapResult XCapResult { +pub fn capture_window( + hwnd: HWND, + scale_factor: f32, + window_info: &WINDOWINFO, +) -> XCapResult { unsafe { let box_hdc_window: BoxHDC = BoxHDC::from(hwnd); - let rect = get_window_rect(hwnd)?; - let mut width = rect.right - rect.left; - let mut height = rect.bottom - rect.top; + let rc_window = window_info.rcWindow; + + let mut width = rc_window.right - rc_window.left; + let mut height = rc_window.bottom - rc_window.top; let hgdi_obj = GetCurrentObject(*box_hdc_window, OBJ_BITMAP); let mut bitmap = BITMAP::default(); @@ -138,8 +140,8 @@ pub fn capture_window(hwnd: HWND, scale_factor: f32) -> XCapResult { height = bitmap.bmHeight; } - width = (width as f32 * scale_factor) as i32; - height = (height as f32 * scale_factor) as i32; + width = (width as f32 * scale_factor).ceil() as i32; + height = (height as f32 * scale_factor).ceil() as i32; // 内存中的HDC,使用 DeleteDC 函数释放 // https://learn.microsoft.com/zh-cn/windows/win32/api/wingdi/nf-wingdi-createcompatibledc @@ -180,6 +182,17 @@ pub fn capture_window(hwnd: HWND, scale_factor: f32) -> XCapResult { SelectObject(*box_hdc_mem, previous_object); - to_rgba_image(box_hdc_mem, box_h_bitmap, width, height) + let image = to_rgba_image(box_hdc_mem, box_h_bitmap, width, height)?; + + let mut rc_client = window_info.rcClient; + + let x = ((rc_client.left - rc_window.left) as f32 * scale_factor).ceil(); + let y = ((rc_client.top - rc_window.top) as f32 * scale_factor).ceil(); + let w = ((rc_client.right - rc_client.left) as f32 * scale_factor).floor(); + let h = ((rc_client.bottom - rc_client.top) as f32 * scale_factor).floor(); + + Ok(DynamicImage::ImageRgba8(image) + .crop(x as u32, y as u32, w as u32, h as u32) + .to_rgba8()) } } diff --git a/src/windows/impl_window.rs b/src/windows/impl_window.rs index 653cee8..83eab94 100644 --- a/src/windows/impl_window.rs +++ b/src/windows/impl_window.rs @@ -4,9 +4,9 @@ use std::{cmp::Ordering, ffi::c_void, mem, ptr}; use windows::{ core::{HSTRING, PCWSTR}, Win32::{ - Foundation::{BOOL, HWND, LPARAM, MAX_PATH, TRUE}, + Foundation::{BOOL, HWND, LPARAM, MAX_PATH, RECT, TRUE}, Graphics::{ - Dwm::{DwmGetWindowAttribute, DWMWA_CLOAKED}, + Dwm::{DwmGetWindowAttribute, DWMWA_CLOAKED, DWMWA_EXTENDED_FRAME_BOUNDS}, Gdi::{IsRectEmpty, MonitorFromWindow, MONITOR_DEFAULTTONEAREST}, }, Storage::FileSystem::{GetFileVersionInfoSizeW, GetFileVersionInfoW, VerQueryValueW}, @@ -27,11 +27,7 @@ use crate::{ platform::{boxed::BoxProcessHandle, utils::log_last_error}, }; -use super::{ - capture::capture_window, - impl_monitor::ImplMonitor, - utils::{get_window_rect, wide_string_to_string}, -}; +use super::{capture::capture_window, impl_monitor::ImplMonitor, utils::wide_string_to_string}; #[derive(Debug, Clone)] pub(crate) struct ImplWindow { @@ -140,9 +136,21 @@ fn is_valid_window(hwnd: HWND) -> bool { return false; } - let is_rect_empty = get_window_rect(hwnd).is_ok_and(|rect| IsRectEmpty(&rect).as_bool()); + let mut rect = RECT::default(); + + let get_rect_is_err = DwmGetWindowAttribute( + hwnd, + DWMWA_EXTENDED_FRAME_BOUNDS, + &mut rect as *mut RECT as *mut c_void, + mem::size_of::() as u32, + ) + .is_err(); - if is_rect_empty { + if get_rect_is_err { + return false; + } + + if IsRectEmpty(&rect).as_bool() { return false; } } @@ -308,7 +316,7 @@ impl ImplWindow { let app_name = get_app_name(hwnd)?; let hmonitor = MonitorFromWindow(hwnd, MONITOR_DEFAULTTONEAREST); - let rc_window = window_info.rcWindow; + let rc_client = window_info.rcClient; let is_minimized = IsIconic(hwnd).as_bool(); let is_maximized = IsZoomed(hwnd).as_bool(); @@ -320,10 +328,10 @@ impl ImplWindow { app_name, process_id: get_process_id(hwnd), current_monitor: ImplMonitor::new(hmonitor)?, - x: rc_window.left, - y: rc_window.top, - width: (rc_window.right - rc_window.left) as u32, - height: (rc_window.bottom - rc_window.top) as u32, + x: rc_client.left, + y: rc_client.top, + width: (rc_client.right - rc_client.left) as u32, + height: (rc_client.bottom - rc_client.top) as u32, is_minimized, is_maximized, }) @@ -355,6 +363,10 @@ impl ImplWindow { impl ImplWindow { pub fn capture_image(&self) -> XCapResult { // TODO: 在win10之后,不同窗口有不同的dpi,所以可能存在截图不全或者截图有较大空白,实际窗口没有填充满图片 - capture_window(self.hwnd, self.current_monitor.scale_factor) + capture_window( + self.hwnd, + self.current_monitor.scale_factor, + &self.window_info, + ) } } diff --git a/src/windows/utils.rs b/src/windows/utils.rs index 789b01f..96fceab 100644 --- a/src/windows/utils.rs +++ b/src/windows/utils.rs @@ -1,8 +1,5 @@ use sysinfo::System; -use windows::Win32::{ - Foundation::{GetLastError, HWND, RECT}, - UI::WindowsAndMessaging::GetWindowRect, -}; +use windows::Win32::Foundation::GetLastError; use crate::error::XCapResult; @@ -25,14 +22,6 @@ pub(super) fn get_os_major_version() -> u8 { .unwrap_or(0) } -pub(super) fn get_window_rect(hwnd: HWND) -> XCapResult { - unsafe { - let mut rect = RECT::default(); - GetWindowRect(hwnd, &mut rect)?; - Ok(rect) - } -} - pub(super) fn log_last_error(label: T) { unsafe { let err = GetLastError();