From b565bcedab83e176446781bd782b474c28101477 Mon Sep 17 00:00:00 2001 From: NiiightmareXD Date: Wed, 6 Dec 2023 07:25:09 -0800 Subject: [PATCH] =?UTF-8?q?Custom=20Error=20Type=20=F0=9F=A6=96=20=09modif?= =?UTF-8?q?ied:=20=20=20Cargo.lock=20=09modified:=20=20=20README.md=20=09m?= =?UTF-8?q?odified:=20=20=20examples/basic.rs=20=09modified:=20=20=20src/c?= =?UTF-8?q?apture.rs=20=09modified:=20=20=20src/d3d11.rs=20=09modified:=20?= =?UTF-8?q?=20=20src/frame.rs=20=09modified:=20=20=20src/graphics=5Fcaptur?= =?UTF-8?q?e=5Fapi.rs=20=09modified:=20=20=20src/lib.rs=20=09modified:=20?= =?UTF-8?q?=20=20src/monitor.rs=20=09modified:=20=20=20src/settings.rs=20?= =?UTF-8?q?=09modified:=20=20=20src/window.rs=20=09modified:=20=20=20windo?= =?UTF-8?q?ws-capture-python/Cargo.toml=20=09modified:=20=20=20windows-cap?= =?UTF-8?q?ture-python/src/lib.rs?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- Cargo.lock | 1 - README.md | 40 ++-- examples/basic.rs | 28 +-- src/capture.rs | 335 ++++++++++++++++++------------ src/d3d11.rs | 29 +-- src/frame.rs | 31 ++- src/graphics_capture_api.rs | 67 +++--- src/lib.rs | 8 +- src/monitor.rs | 64 +++--- src/settings.rs | 16 +- src/window.rs | 45 ++-- windows-capture-python/Cargo.toml | 4 - windows-capture-python/src/lib.rs | 143 ++----------- 13 files changed, 390 insertions(+), 421 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 77e5e48..940825d 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -563,7 +563,6 @@ name = "windows-capture-python" version = "1.0.45" dependencies = [ "pyo3", - "windows", "windows-capture", ] diff --git a/README.md b/README.md index c3f58f0..886a283 100644 --- a/README.md +++ b/README.md @@ -1,5 +1,13 @@ -# Windows Capture -![Crates.io](https://img.shields.io/crates/l/windows-capture) ![GitHub Workflow Status (with event)](https://img.shields.io/github/actions/workflow/status/NiiightmareXD/windows-capture/rust.yml) ![Crates.io](https://img.shields.io/crates/v/windows-capture) +# Windows Capture   [![Licence]][repository] [![Build Status]][repository] [![Latest Version]][crates.io] + +[Licence]: https://img.shields.io/crates/l/windows-capture +[Licence URL]: https://github.com/NiiightmareXD/windows-capture/blob/main/LICENCE + +[Build Status]: https://img.shields.io/github/actions/workflow/status/NiiightmareXD/windows-capture/rust.yml +[repository]: https://github.com/NiiightmareXD/windows-capture + +[Latest Version]: https://img.shields.io/crates/v/windows-capture +[crates.io]: https://crates.io/crates/windows-capture **Windows Capture** is a highly efficient Rust and Python library that enables you to capture the screen using the Graphics Capture API effortlessly. This library allows you to easily capture the screen of your Windows-based computer and use it for various purposes, such as creating instructional videos, taking screenshots, or recording your gameplay. With its intuitive interface and robust functionality, Windows Capture is an excellent choice for anyone looking for a reliable, easy-to-use screen-capturing solution. @@ -29,26 +37,28 @@ cargo add windows-capture ## Usage ```rust -use std::error::Error; - use windows_capture::{ capture::WindowsCaptureHandler, frame::Frame, graphics_capture_api::InternalCaptureControl, - settings::{ColorFormat, WindowsCaptureSettings}, + settings::{ColorFormat, Settings}, window::Window, }; -// Struct To Implement Methods For +// Struct To Implement The Trait For struct Capture; impl WindowsCaptureHandler for Capture { - type Flags = String; // To Get The Message From The Settings + // To Get The Message From The Settings + type Flags = String; + + // To Redirect To CaptureControl Or Start Method + type Error = Box; // Function That Will Be Called To Create The Struct The Flags Can Be Passed - // From Settings - fn new(message: Self::Flags) -> Result> { - println!("Got The Message: {message}"); + // From `WindowsCaptureSettings` + fn new(message: Self::Flags) -> Result { + println!("Got The Flag: {message}"); Ok(Self {}) } @@ -58,7 +68,7 @@ impl WindowsCaptureHandler for Capture { &mut self, frame: &mut Frame, capture_control: InternalCaptureControl, - ) -> Result<(), Box> { + ) -> Result<(), Self::Error> { println!("New Frame Arrived"); // Save The Frame As An Image To The Specified Path @@ -72,7 +82,7 @@ impl WindowsCaptureHandler for Capture { // Called When The Capture Item Closes Usually When The Window Closes, Capture // Session Will End After This Function Ends - fn on_closed(&mut self) -> Result<(), Box> { + fn on_closed(&mut self) -> Result<(), Self::Error> { println!("Capture Session Closed"); Ok(()) @@ -81,9 +91,9 @@ impl WindowsCaptureHandler for Capture { fn main() { // Checkout Docs For Other Capture Items - let foreground_window = Window::foreground().unwrap(); + let foreground_window = Window::foreground().expect("No Active Window Found"); - let settings = WindowsCaptureSettings::new( + let settings = Settings::new( // Item To Captue foreground_window, // Capture Cursor @@ -97,7 +107,7 @@ fn main() { ) .unwrap(); - // Every Error From on_closed and on_frame_arrived Will End Up Here + // Every Error From `new`, `on_frame_arrived` and `on_closed` Will End Up Here Capture::start(settings).unwrap(); } ``` diff --git a/examples/basic.rs b/examples/basic.rs index 528557c..ad56afd 100644 --- a/examples/basic.rs +++ b/examples/basic.rs @@ -1,23 +1,25 @@ -use std::error::Error; - use windows_capture::{ capture::WindowsCaptureHandler, frame::Frame, graphics_capture_api::InternalCaptureControl, - settings::{ColorFormat, WindowsCaptureSettings}, + settings::{ColorFormat, Settings}, window::Window, }; -// Struct To Implement Methods For +// Struct To Implement The Trait For struct Capture; impl WindowsCaptureHandler for Capture { - type Flags = String; // To Get The Message From The Settings + // To Get The Message From The Settings + type Flags = String; + + // To Redirect To CaptureControl Or Start Method + type Error = Box; // Function That Will Be Called To Create The Struct The Flags Can Be Passed - // From Settings - fn new(message: Self::Flags) -> Result> { - println!("Got The Message: {message}"); + // From `WindowsCaptureSettings` + fn new(message: Self::Flags) -> Result { + println!("Got The Flag: {message}"); Ok(Self {}) } @@ -27,7 +29,7 @@ impl WindowsCaptureHandler for Capture { &mut self, frame: &mut Frame, capture_control: InternalCaptureControl, - ) -> Result<(), Box> { + ) -> Result<(), Self::Error> { println!("New Frame Arrived"); // Save The Frame As An Image To The Specified Path @@ -41,7 +43,7 @@ impl WindowsCaptureHandler for Capture { // Called When The Capture Item Closes Usually When The Window Closes, Capture // Session Will End After This Function Ends - fn on_closed(&mut self) -> Result<(), Box> { + fn on_closed(&mut self) -> Result<(), Self::Error> { println!("Capture Session Closed"); Ok(()) @@ -50,9 +52,9 @@ impl WindowsCaptureHandler for Capture { fn main() { // Checkout Docs For Other Capture Items - let foreground_window = Window::foreground().unwrap(); + let foreground_window = Window::foreground().expect("No Active Window Found"); - let settings = WindowsCaptureSettings::new( + let settings = Settings::new( // Item To Captue foreground_window, // Capture Cursor @@ -66,6 +68,6 @@ fn main() { ) .unwrap(); - // Every Error From on_closed and on_frame_arrived Will End Up Here + // Every Error From `new`, `on_frame_arrived` and `on_closed` Will End Up Here Capture::start(settings).unwrap(); } diff --git a/src/capture.rs b/src/capture.rs index a003bae..dc8c85c 100644 --- a/src/capture.rs +++ b/src/capture.rs @@ -1,10 +1,10 @@ use std::{ - error::Error, mem, os::windows::prelude::AsRawHandle, sync::{ atomic::{self, AtomicBool}, - mpsc, Arc, + mpsc::{self, SendError}, + Arc, }, thread::{self, JoinHandle}, }; @@ -31,31 +31,37 @@ use windows::{ use crate::{ frame::Frame, - graphics_capture_api::{GraphicsCaptureApi, InternalCaptureControl, RESULT}, - settings::WindowsCaptureSettings, + graphics_capture_api::{self, GraphicsCaptureApi, InternalCaptureControl}, + settings::Settings, }; /// Used To Handle Capture Control Errors -#[derive(thiserror::Error, Eq, PartialEq, Clone, Copy, Debug)] -pub enum CaptureControlError { +#[derive(thiserror::Error, Debug)] +#[allow(clippy::module_name_repetitions)] +pub enum CaptureControlError { #[error("Failed To Join Thread")] FailedToJoinThread, #[error("Thread Handle Is Taken Out Of Struct")] ThreadHandleIsTaken, + #[error("Failed To Post Thread Message")] + FailedToPostThreadMessage, + #[error(transparent)] + WindowsCaptureError(#[from] WindowsCaptureError), } /// Struct Used To Control Capture Thread -pub struct CaptureControl { - thread_handle: Option>>>, +#[allow(clippy::module_name_repetitions)] +pub struct CaptureControl { + thread_handle: Option>>>, halt_handle: Arc, callback: Arc>, } -impl CaptureControl { +impl CaptureControl { /// Create A New Capture Control Struct #[must_use] pub fn new( - thread_handle: JoinHandle>>, + thread_handle: JoinHandle>>, halt_handle: Arc, callback: Arc>, ) -> Self { @@ -71,12 +77,12 @@ impl CaptureControl { pub fn is_finished(&self) -> bool { self.thread_handle .as_ref() - .map_or(true, |thread_handle| thread_handle.is_finished()) + .map_or(true, std::thread::JoinHandle::is_finished) } /// Get The Halt Handle Used To Pause The Capture Thread #[must_use] - pub fn into_thread_handle(self) -> JoinHandle>> { + pub fn into_thread_handle(self) -> JoinHandle>> { self.thread_handle.unwrap() } @@ -93,23 +99,23 @@ impl CaptureControl { } /// Wait Until The Capturing Thread Stops - pub fn wait(mut self) -> Result<(), Box> { + pub fn wait(mut self) -> Result<(), CaptureControlError> { if let Some(thread_handle) = self.thread_handle.take() { match thread_handle.join() { Ok(result) => result?, Err(_) => { - return Err(Box::new(CaptureControlError::FailedToJoinThread)); + return Err(CaptureControlError::FailedToJoinThread); } } } else { - return Err(Box::new(CaptureControlError::ThreadHandleIsTaken)); + return Err(CaptureControlError::ThreadHandleIsTaken); } Ok(()) } /// Gracefully Stop The Capture Thread - pub fn stop(mut self) -> Result<(), Box> { + pub fn stop(mut self) -> Result<(), CaptureControlError> { self.halt_handle.store(true, atomic::Ordering::Relaxed); if let Some(thread_handle) = self.thread_handle.take() { @@ -121,16 +127,16 @@ impl CaptureControl { match unsafe { PostThreadMessageW(therad_id, WM_QUIT, WPARAM::default(), LPARAM::default()) } { - Ok(_) => break, + Ok(()) => break, Err(e) => { if thread_handle.is_finished() { break; } - if e.code().0 == -2147023452 { + if e.code().0 == -2_147_023_452 { warn!("Thread Is Not In Message Loop Yet"); } else { - Err(e)?; + Err(e).map_err(|_| CaptureControlError::FailedToPostThreadMessage)?; } } } @@ -139,42 +145,70 @@ impl CaptureControl { match thread_handle.join() { Ok(result) => result?, Err(_) => { - return Err(Box::new(CaptureControlError::FailedToJoinThread)); + return Err(CaptureControlError::FailedToJoinThread); } } } else { - return Err(Box::new(CaptureControlError::ThreadHandleIsTaken)); + return Err(CaptureControlError::ThreadHandleIsTaken); } Ok(()) } } +/// Used To Handle Capture Control Errors +#[derive(thiserror::Error, Debug)] +pub enum WindowsCaptureError { + #[error("Failed To Join Thread")] + FailedToJoinThread, + #[error("Failed To Initialize WinRT")] + FailedToInitWinRT, + #[error("Failed To Create Dispatcher Queue Controller")] + FailedToCreateDispatcherQueueController, + #[error("Failed To Shutdown Dispatcher Queue")] + FailedToShutdownDispatcherQueue, + #[error("Failed To Set Dispatcher Queue Completed Handler")] + FailedToSetDispatcherQueueCompletedHandler, + #[error("Graphics Capture Error")] + GraphicsCaptureError(graphics_capture_api::Error), + #[error("Handler Error")] + HandlerError(E), + #[error(transparent)] + FailedToThreadID(SendError), +} + /// Event Handler Trait pub trait WindowsCaptureHandler: Sized { /// To Get The Message From The Settings type Flags; + /// To Redirect To `CaptureControl` Or `Start` Method + type Error: Send + Sync; + /// Starts The Capture And Takes Control Of The Current Thread - fn start( - settings: WindowsCaptureSettings, - ) -> Result<(), Box> + fn start(settings: Settings) -> Result<(), WindowsCaptureError> where Self: Send + 'static, ::Flags: Send, { // Initialize WinRT trace!("Initializing WinRT"); - unsafe { RoInitialize(RO_INIT_MULTITHREADED)? }; + unsafe { + RoInitialize(RO_INIT_MULTITHREADED) + .map_err(|_| WindowsCaptureError::FailedToInitWinRT)?; + }; // Create A Dispatcher Queue For Current Thread trace!("Creating A Dispatcher Queue For Capture Thread"); let options = DispatcherQueueOptions { - dwSize: mem::size_of::() as u32, + dwSize: u32::try_from(mem::size_of::()).unwrap(), threadType: DQTYPE_THREAD_CURRENT, apartmentType: DQTAT_COM_NONE, }; - let controller = unsafe { CreateDispatcherQueueController(options)? }; + let controller = unsafe { + CreateDispatcherQueueController(options) + .map_err(|_| WindowsCaptureError::FailedToCreateDispatcherQueueController)? + }; // Debug Thread ID let thread_id = unsafe { GetCurrentThreadId() }; @@ -182,7 +216,10 @@ pub trait WindowsCaptureHandler: Sized { // Start Capture info!("Starting Capture Thread"); - let callback = Arc::new(Mutex::new(Self::new(settings.flags)?)); + let result = Arc::new(Mutex::new(None)); + let callback = Arc::new(Mutex::new( + Self::new(settings.flags).map_err(WindowsCaptureError::HandlerError)?, + )); let mut capture = GraphicsCaptureApi::new( settings.item, callback, @@ -190,8 +227,12 @@ pub trait WindowsCaptureHandler: Sized { settings.draw_border, settings.color_format, thread_id, - )?; - capture.start_capture()?; + result.clone(), + ) + .map_err(WindowsCaptureError::GraphicsCaptureError)?; + capture + .start_capture() + .map_err(WindowsCaptureError::GraphicsCaptureError)?; // Message Loop trace!("Entering Message Loop"); @@ -205,13 +246,17 @@ pub trait WindowsCaptureHandler: Sized { // Shutdown Dispatcher Queue trace!("Shutting Down Dispatcher Queue"); - let async_action = controller.ShutdownQueueAsync()?; - async_action.SetCompleted(&AsyncActionCompletedHandler::new( - move |_, _| -> Result<(), windows::core::Error> { - unsafe { PostQuitMessage(0) }; - Ok(()) - }, - ))?; + let async_action = controller + .ShutdownQueueAsync() + .map_err(|_| WindowsCaptureError::FailedToShutdownDispatcherQueue)?; + async_action + .SetCompleted(&AsyncActionCompletedHandler::new( + move |_, _| -> Result<(), windows::core::Error> { + unsafe { PostQuitMessage(0) }; + Ok(()) + }, + )) + .map_err(|_| WindowsCaptureError::FailedToSetDispatcherQueueCompletedHandler)?; // Final Message Loop trace!("Entering Final Message Loop"); @@ -231,19 +276,20 @@ pub trait WindowsCaptureHandler: Sized { trace!("Uninitializing WinRT"); unsafe { RoUninitialize() }; - // Check RESULT - trace!("Checking RESULT"); - let result = RESULT.take().expect("Failed To Take RESULT"); - - result?; + // Check Handler Result + trace!("Checking Handler Result"); + if let Some(e) = result.lock().take() { + return Err(WindowsCaptureError::HandlerError(e)); + } Ok(()) } /// Starts The Capture Without Taking Control Of The Current Thread + #[allow(clippy::too_many_lines)] fn start_free_threaded( - settings: WindowsCaptureSettings, - ) -> Result, Box> + settings: Settings, + ) -> Result, WindowsCaptureError> where Self: Send + 'static, ::Flags: Send, @@ -251,111 +297,128 @@ pub trait WindowsCaptureHandler: Sized { let (halt_sender, halt_receiver) = mpsc::channel::>(); let (callback_sender, callback_receiver) = mpsc::channel::>>(); - let thread_handle = thread::spawn(move || -> Result<(), Box> { - // Initialize WinRT - trace!("Initializing WinRT"); - unsafe { RoInitialize(RO_INIT_MULTITHREADED)? }; - - // Create A Dispatcher Queue For Current Thread - trace!("Creating A Dispatcher Queue For Capture Thread"); - let options = DispatcherQueueOptions { - dwSize: mem::size_of::() as u32, - threadType: DQTYPE_THREAD_CURRENT, - apartmentType: DQTAT_COM_NONE, - }; - let controller = unsafe { CreateDispatcherQueueController(options)? }; - - // Debug Thread ID - let thread_id = unsafe { GetCurrentThreadId() }; - debug!("Thread ID: {thread_id}"); - - // Start Capture - info!("Starting Capture Thread"); - let callback = Arc::new(Mutex::new(Self::new(settings.flags)?)); - let mut capture = GraphicsCaptureApi::new( - settings.item, - callback.clone(), - settings.capture_cursor, - settings.draw_border, - settings.color_format, - thread_id, - )?; - capture.start_capture()?; - - // Send Halt Handle - trace!("Sending Halt Handle"); - let halt_handle = capture.halt_handle(); - halt_sender.send(halt_handle)?; - - // Send Callback - trace!("Sending Callback"); - callback_sender.send(callback)?; - - // Message Loop - trace!("Entering Message Loop"); - let mut message = MSG::default(); - unsafe { - while GetMessageW(&mut message, None, 0, 0).as_bool() { - TranslateMessage(&message); - DispatchMessageW(&message); + let thread_handle = + thread::spawn(move || -> Result<(), WindowsCaptureError> { + // Initialize WinRT + trace!("Initializing WinRT"); + unsafe { + RoInitialize(RO_INIT_MULTITHREADED) + .map_err(|_| WindowsCaptureError::FailedToInitWinRT)?; + }; + + // Create A Dispatcher Queue For Current Thread + trace!("Creating A Dispatcher Queue For Capture Thread"); + let options = DispatcherQueueOptions { + dwSize: u32::try_from(mem::size_of::()).unwrap(), + threadType: DQTYPE_THREAD_CURRENT, + apartmentType: DQTAT_COM_NONE, + }; + let controller = unsafe { + CreateDispatcherQueueController(options) + .map_err(|_| WindowsCaptureError::FailedToCreateDispatcherQueueController)? + }; + + // Debug Thread ID + let thread_id = unsafe { GetCurrentThreadId() }; + debug!("Thread ID: {thread_id}"); + + // Start Capture + info!("Starting Capture Thread"); + let result = Arc::new(Mutex::new(None)); + let callback = Arc::new(Mutex::new( + Self::new(settings.flags).map_err(WindowsCaptureError::HandlerError)?, + )); + let mut capture = GraphicsCaptureApi::new( + settings.item, + callback.clone(), + settings.capture_cursor, + settings.draw_border, + settings.color_format, + thread_id, + result.clone(), + ) + .map_err(WindowsCaptureError::GraphicsCaptureError)?; + capture + .start_capture() + .map_err(WindowsCaptureError::GraphicsCaptureError)?; + + // Send Halt Handle + trace!("Sending Halt Handle"); + let halt_handle = capture.halt_handle(); + halt_sender.send(halt_handle).unwrap(); + + // Send Callback + trace!("Sending Callback"); + callback_sender.send(callback).unwrap(); + + // Message Loop + trace!("Entering Message Loop"); + let mut message = MSG::default(); + unsafe { + while GetMessageW(&mut message, None, 0, 0).as_bool() { + TranslateMessage(&message); + DispatchMessageW(&message); + } } - } - // Shutdown Dispatcher Queue - trace!("Shutting Down Dispatcher Queue"); - let async_action = controller.ShutdownQueueAsync()?; - async_action.SetCompleted(&AsyncActionCompletedHandler::new( - move |_, _| -> Result<(), windows::core::Error> { - unsafe { PostQuitMessage(0) }; - Ok(()) - }, - ))?; - - // Final Message Loop - trace!("Entering Final Message Loop"); - let mut message = MSG::default(); - unsafe { - while GetMessageW(&mut message, None, 0, 0).as_bool() { - TranslateMessage(&message); - DispatchMessageW(&message); + // Shutdown Dispatcher Queue + trace!("Shutting Down Dispatcher Queue"); + let async_action = controller + .ShutdownQueueAsync() + .map_err(|_| WindowsCaptureError::FailedToShutdownDispatcherQueue)?; + + async_action + .SetCompleted(&AsyncActionCompletedHandler::new( + move |_, _| -> Result<(), windows::core::Error> { + unsafe { PostQuitMessage(0) }; + Ok(()) + }, + )) + .map_err(|_| WindowsCaptureError::FailedToSetDispatcherQueueCompletedHandler)?; + + // Final Message Loop + trace!("Entering Final Message Loop"); + let mut message = MSG::default(); + unsafe { + while GetMessageW(&mut message, None, 0, 0).as_bool() { + TranslateMessage(&message); + DispatchMessageW(&message); + } } - } - - // Stop Capturing - info!("Stopping Capture Thread"); - capture.stop_capture(); - // Uninitialize WinRT - trace!("Uninitializing WinRT"); - unsafe { RoUninitialize() }; + // Stop Capturing + info!("Stopping Capture Thread"); + capture.stop_capture(); - // Check RESULT - trace!("Checking RESULT"); - let result = RESULT.take().expect("Failed To Take RESULT"); + // Uninitialize WinRT + trace!("Uninitializing WinRT"); + unsafe { RoUninitialize() }; - result?; + // Check Handler Result + trace!("Checking Handler Result"); + if let Some(e) = result.lock().take() { + return Err(WindowsCaptureError::HandlerError(e)); + } - Ok(()) - }); + Ok(()) + }); - let halt_handle = match halt_receiver.recv() { - Ok(halt_handle) => halt_handle, - Err(_) => match thread_handle.join() { + let Ok(halt_handle) = halt_receiver.recv() else { + match thread_handle.join() { Ok(result) => return Err(result.err().unwrap()), Err(_) => { - return Err(Box::new(CaptureControlError::FailedToJoinThread)); + return Err(WindowsCaptureError::FailedToJoinThread); } - }, + } }; - let callback = match callback_receiver.recv() { - Ok(callback_handle) => callback_handle, - Err(_) => match thread_handle.join() { + let Ok(callback) = callback_receiver.recv() else { + match thread_handle.join() { Ok(result) => return Err(result.err().unwrap()), Err(_) => { - return Err(Box::new(CaptureControlError::FailedToJoinThread)); + return Err(WindowsCaptureError::FailedToJoinThread); } - }, + } }; Ok(CaptureControl::new(thread_handle, halt_handle, callback)) @@ -363,16 +426,16 @@ pub trait WindowsCaptureHandler: Sized { /// Function That Will Be Called To Create The Struct The Flags Can Be /// Passed From Settings - fn new(flags: Self::Flags) -> Result>; + fn new(flags: Self::Flags) -> Result; /// Called Every Time A New Frame Is Available fn on_frame_arrived( &mut self, frame: &mut Frame, capture_control: InternalCaptureControl, - ) -> Result<(), Box>; + ) -> Result<(), Self::Error>; /// Called When The Capture Item Closes Usually When The Window Closes, /// Capture Session Will End After This Function Ends - fn on_closed(&mut self) -> Result<(), Box>; + fn on_closed(&mut self) -> Result<(), Self::Error>; } diff --git a/src/d3d11.rs b/src/d3d11.rs index 1148150..8fc2980 100644 --- a/src/d3d11.rs +++ b/src/d3d11.rs @@ -1,5 +1,3 @@ -use std::error::Error; - use windows::{ core::ComInterface, Graphics::DirectX::Direct3D11::IDirect3DDevice, @@ -12,7 +10,7 @@ use windows::{ }, Direct3D11::{ D3D11CreateDevice, ID3D11Device, ID3D11DeviceContext, - D3D11_CREATE_DEVICE_BGRA_SUPPORT, D3D11_SDK_VERSION, + D3D11_CREATE_DEVICE_BGRA_SUPPORT, D3D11_CREATE_DEVICE_FLAG, D3D11_SDK_VERSION, }, Dxgi::IDXGIDevice, }, @@ -33,15 +31,16 @@ impl SendDirectX { unsafe impl Send for SendDirectX {} /// Used To Handle DirectX Errors -#[derive(thiserror::Error, Eq, PartialEq, Clone, Copy, Debug)] -pub enum DirectXErrors { +#[derive(thiserror::Error, Debug)] +pub enum Error { #[error("Failed To Create DirectX Device With The Recommended Feature Level")] FeatureLevelNotSatisfied, + #[error(transparent)] + WindowsError(#[from] windows::core::Error), } -/// Create ID3D11Device And ID3D11DeviceContext -pub fn create_d3d_device() --> Result<(ID3D11Device, ID3D11DeviceContext), Box> { +/// Create `ID3D11Device` An`ID3D11DeviceContext`xt +pub fn create_d3d_device(bgra_support: bool) -> Result<(ID3D11Device, ID3D11DeviceContext), Error> { // Set Feature Flags let feature_flags = [ D3D_FEATURE_LEVEL_11_1, @@ -62,7 +61,11 @@ pub fn create_d3d_device() None, D3D_DRIVER_TYPE_HARDWARE, None, - D3D11_CREATE_DEVICE_BGRA_SUPPORT, + if bgra_support { + D3D11_CREATE_DEVICE_BGRA_SUPPORT + } else { + D3D11_CREATE_DEVICE_FLAG(0) + }, Some(&feature_flags), D3D11_SDK_VERSION, Some(&mut d3d_device), @@ -72,16 +75,14 @@ pub fn create_d3d_device() }; if feature_level != D3D_FEATURE_LEVEL_11_1 { - return Err(Box::new(DirectXErrors::FeatureLevelNotSatisfied)); + return Err(Error::FeatureLevelNotSatisfied); } Ok((d3d_device.unwrap(), d3d_device_context.unwrap())) } -/// Create A IDirect3DDevice From ID3D11Device -pub fn create_direct3d_device( - d3d_device: &ID3D11Device, -) -> Result> { +/// Create A `IDirect3DDevice` From `ID3D11Device` +pub fn create_direct3d_device(d3d_device: &ID3D11Device) -> Result { let dxgi_device: IDXGIDevice = d3d_device.cast()?; let inspectable = unsafe { CreateDirect3D11DeviceFromDXGIDevice(&dxgi_device)? }; let device: IDirect3DDevice = inspectable.cast()?; diff --git a/src/frame.rs b/src/frame.rs index 4b1524f..efad6ed 100644 --- a/src/frame.rs +++ b/src/frame.rs @@ -1,4 +1,4 @@ -use std::{error::Error, path::Path, ptr, slice}; +use std::{path::Path, ptr, slice}; use image::{Rgb, RgbImage}; use log::trace; @@ -15,10 +15,14 @@ use windows::Win32::Graphics::{ use crate::settings::ColorFormat; /// Used To Handle Frame Errors -#[derive(thiserror::Error, Eq, PartialEq, Clone, Copy, Debug)] -pub enum FrameError { +#[derive(thiserror::Error, Debug)] +pub enum Error { #[error("Invalid Box Size")] InvalidSize, + #[error(transparent)] + ImageSaveFailed(#[from] image::error::ImageError), + #[error(transparent)] + WindowsError(#[from] windows::core::Error), } /// Frame Struct Used To Get The Frame Buffer @@ -68,7 +72,7 @@ impl<'a> Frame<'a> { } /// Get The Frame Buffer - pub fn buffer(&mut self) -> Result> { + pub fn buffer(&mut self) -> Result { // Texture Settings let texture_desc = D3D11_TEXTURE2D_DESC { Width: self.width, @@ -144,9 +148,9 @@ impl<'a> Frame<'a> { start_height: u32, end_width: u32, end_height: u32, - ) -> Result> { + ) -> Result { if start_width >= end_width || start_height >= end_height { - return Err(Box::new(FrameError::InvalidSize)); + return Err(Error::InvalidSize); } let texture_width = end_width - start_width; @@ -240,10 +244,7 @@ impl<'a> Frame<'a> { } /// Save The Frame Buffer As An Image To The Specified Path - pub fn save_as_image>( - &mut self, - path: T, - ) -> Result<(), Box> { + pub fn save_as_image>(&mut self, path: T) -> Result<(), Error> { let frame_buffer = self.buffer()?; frame_buffer.save_as_image(path)?; @@ -253,6 +254,7 @@ impl<'a> Frame<'a> { } /// Frame Buffer Struct Used To Get Raw Pixel Data +#[allow(clippy::module_name_repetitions)] pub struct FrameBuffer<'a> { raw_buffer: &'a mut [u8], buffer: &'a mut Vec, @@ -324,9 +326,7 @@ impl<'a> FrameBuffer<'a> { /// Get The Raw Pixel Data Without Padding #[allow(clippy::type_complexity)] - pub fn as_raw_nopadding_buffer( - &'a mut self, - ) -> Result<&'a mut [u8], Box> { + pub fn as_raw_nopadding_buffer(&'a mut self) -> Result<&'a mut [u8], Error> { if !self.has_padding() { return Ok(self.raw_buffer); } @@ -356,10 +356,7 @@ impl<'a> FrameBuffer<'a> { } /// Save The Frame Buffer As An Image To The Specified Path - pub fn save_as_image>( - &self, - path: T, - ) -> Result<(), Box> { + pub fn save_as_image>(&self, path: T) -> Result<(), Error> { let mut rgb_image: RgbImage = RgbImage::new(self.width, self.height); if self.color_format == ColorFormat::Rgba8 { diff --git a/src/graphics_capture_api.rs b/src/graphics_capture_api.rs index c7e6763..3ae24b8 100644 --- a/src/graphics_capture_api.rs +++ b/src/graphics_capture_api.rs @@ -1,10 +1,6 @@ -use std::{ - cell::RefCell, - error::Error, - sync::{ - atomic::{self, AtomicBool}, - Arc, - }, +use std::sync::{ + atomic::{self, AtomicBool}, + Arc, }; use log::{info, trace}; @@ -28,18 +24,14 @@ use windows::{ use crate::{ capture::WindowsCaptureHandler, - d3d11::{create_d3d_device, create_direct3d_device, SendDirectX}, + d3d11::{self, create_d3d_device, create_direct3d_device, SendDirectX}, frame::Frame, settings::ColorFormat, }; -thread_local! { - pub static RESULT: RefCell>>> = RefCell::new(Some(Ok(()))); -} - -/// Used To Handle Capture Errors -#[derive(thiserror::Error, Eq, PartialEq, Clone, Copy, Debug)] -pub enum WindowsCaptureError { +/// Used To Handle Graphics Capture Errors +#[derive(thiserror::Error, Debug)] +pub enum Error { #[error("Graphics Capture API Is Not Supported")] Unsupported, #[error("Graphics Capture API Toggling Cursor Capture Is Not Supported")] @@ -48,6 +40,10 @@ pub enum WindowsCaptureError { BorderConfigUnsupported, #[error("Already Started")] AlreadyStarted, + #[error(transparent)] + DirectXError(#[from] d3d11::Error), + #[error(transparent)] + WindowsError(#[from] windows::core::Error), } /// Struct Used To Control Capture Thread @@ -84,25 +80,28 @@ pub struct GraphicsCaptureApi { impl GraphicsCaptureApi { /// Create A New Graphics Capture Api Struct - pub fn new( + #[allow(clippy::too_many_lines)] + pub fn new + Send + 'static, E: Send + Sync + 'static>( item: GraphicsCaptureItem, callback: Arc>, capture_cursor: Option, draw_border: Option, color_format: ColorFormat, thread_id: u32, - ) -> Result> { + result: Arc>>, + ) -> Result { // Check Support if !ApiInformation::IsApiContractPresentByMajor( &HSTRING::from("Windows.Foundation.UniversalApiContract"), 8, )? { - return Err(Box::new(WindowsCaptureError::Unsupported)); + return Err(Error::Unsupported); } // Create DirectX Devices trace!("Creating DirectX Devices"); - let (d3d_device, d3d_device_context) = create_d3d_device()?; + let (d3d_device, d3d_device_context) = + create_d3d_device(color_format == ColorFormat::Bgra8)?; let direct3d_device = create_direct3d_device(&d3d_device)?; let pixel_format = if color_format == ColorFormat::Rgba8 { @@ -134,16 +133,15 @@ impl GraphicsCaptureApi { // Init let callback_closed = callback.clone(); let halt_closed = halt.clone(); + let result_closed = result.clone(); move |_, _| { halt_closed.store(true, atomic::Ordering::Relaxed); // Notify The Struct That The Capture Session Is Closed - let result = callback_closed.lock().on_closed(); - - let _ = RESULT - .replace(Some(result)) - .expect("Failed To Replace RESULT"); + if let Err(e) = callback_closed.lock().on_closed() { + *result_closed.lock() = Some(e); + } // To Stop Messge Loop unsafe { @@ -168,6 +166,7 @@ impl GraphicsCaptureApi { let halt_frame_pool = halt.clone(); let d3d_device_frame_pool = d3d_device.clone(); let context = d3d_device_context.clone(); + let result_frame_pool = result; let mut last_size = item.Size()?; let callback_frame_pool = callback; @@ -247,9 +246,9 @@ impl GraphicsCaptureApi { .on_frame_arrived(&mut frame, internal_capture_control); if stop.load(atomic::Ordering::Relaxed) || result.is_err() { - let _ = RESULT - .replace(Some(result)) - .expect("Failed To Replace RESULT"); + if let Err(e) = result { + *result_frame_pool.lock() = Some(e); + } halt_frame_pool.store(true, atomic::Ordering::Relaxed); @@ -284,10 +283,10 @@ impl GraphicsCaptureApi { } /// Start Capture - pub fn start_capture(&mut self) -> Result<(), Box> { + pub fn start_capture(&mut self) -> Result<(), Error> { // Check If The Capture Is Already Installed if self.active { - return Err(Box::new(WindowsCaptureError::AlreadyStarted)); + return Err(Error::AlreadyStarted); } // Config @@ -301,7 +300,7 @@ impl GraphicsCaptureApi { .unwrap() .SetIsCursorCaptureEnabled(self.capture_cursor.unwrap())?; } else { - return Err(Box::new(WindowsCaptureError::CursorConfigUnsupported)); + return Err(Error::CursorConfigUnsupported); } } @@ -315,7 +314,7 @@ impl GraphicsCaptureApi { .unwrap() .SetIsBorderRequired(self.draw_border.unwrap())?; } else { - return Err(Box::new(WindowsCaptureError::BorderConfigUnsupported)); + return Err(Error::BorderConfigUnsupported); } } @@ -345,7 +344,7 @@ impl GraphicsCaptureApi { } /// Check If Windows Graphics Capture Api Is Supported - pub fn is_supported() -> Result> { + pub fn is_supported() -> Result { Ok(ApiInformation::IsApiContractPresentByMajor( &HSTRING::from("Windows.Foundation.UniversalApiContract"), 8, @@ -353,7 +352,7 @@ impl GraphicsCaptureApi { } /// Check If You Can Toggle The Cursor On Or Off - pub fn is_cursor_toggle_supported() -> Result> { + pub fn is_cursor_toggle_supported() -> Result { Ok(ApiInformation::IsPropertyPresent( &HSTRING::from("Windows.Graphics.Capture.GraphicsCaptureSession"), &HSTRING::from("IsCursorCaptureEnabled"), @@ -361,7 +360,7 @@ impl GraphicsCaptureApi { } /// Check If You Can Toggle The Border On Or Off - pub fn is_border_toggle_supported() -> Result> { + pub fn is_border_toggle_supported() -> Result { Ok(ApiInformation::IsPropertyPresent( &HSTRING::from("Windows.Graphics.Capture.GraphicsCaptureSession"), &HSTRING::from("IsBorderRequired"), diff --git a/src/lib.rs b/src/lib.rs index 9c91135..f1efab6 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -103,13 +103,11 @@ //! // Every Error From on_closed and on_frame_arrived Will End Up Here //! Capture::start(settings).unwrap(); //! ``` -#![warn(clippy::semicolon_if_nothing_returned)] -#![warn(clippy::inconsistent_struct_constructor)] -#![warn(clippy::must_use_candidate)] -#![warn(clippy::ptr_as_ptr)] -#![warn(clippy::borrow_as_ptr)] +#![warn(clippy::pedantic)] #![warn(clippy::nursery)] #![warn(clippy::cargo)] +#![allow(clippy::missing_errors_doc)] +#![allow(clippy::missing_panics_doc)] pub mod capture; mod d3d11; diff --git a/src/monitor.rs b/src/monitor.rs index 7f3c0bc..80c803a 100644 --- a/src/monitor.rs +++ b/src/monitor.rs @@ -1,4 +1,4 @@ -use std::{error::Error, mem, ptr}; +use std::{mem, num::ParseIntError, ptr, string::FromUtf16Error}; use windows::{ core::PCWSTR, @@ -22,8 +22,8 @@ use windows::{ }; /// Used To Handle Monitor Errors -#[derive(thiserror::Error, Eq, PartialEq, Clone, Copy, Debug)] -pub enum MonitorErrors { +#[derive(thiserror::Error, Debug)] +pub enum Error { #[error("Failed To Find Monitor")] NotFound, #[error("Failed To Find Monitor Name")] @@ -34,6 +34,12 @@ pub enum MonitorErrors { FailedToGetMonitorInfo, #[error("Failed To Get Monitor Name")] FailedToGetMonitorName, + #[error(transparent)] + FailedToParseMonitorIndex(#[from] ParseIntError), + #[error(transparent)] + FailedToConvertWindowsString(#[from] FromUtf16Error), + #[error(transparent)] + WindowsError(#[from] windows::core::Error), } /// Represents A Monitor Device @@ -44,42 +50,42 @@ pub struct Monitor { impl Monitor { /// Get The Primary Monitor - pub fn primary() -> Result> { + pub fn primary() -> Result { let point = POINT { x: 0, y: 0 }; let monitor = unsafe { MonitorFromPoint(point, MONITOR_DEFAULTTOPRIMARY) }; if monitor.is_invalid() { - return Err(Box::new(MonitorErrors::NotFound)); + return Err(Error::NotFound); } Ok(Self { monitor }) } /// Get The Monitor From It's Index - pub fn from_index(index: usize) -> Result> { + pub fn from_index(index: usize) -> Result { if index < 1 { - return Err(Box::new(MonitorErrors::IndexIsLowerThanOne)); + return Err(Error::IndexIsLowerThanOne); } let monitor = Self::enumerate()?; let monitor = match monitor.get(index - 1) { Some(monitor) => *monitor, - None => return Err(Box::new(MonitorErrors::NotFound)), + None => return Err(Error::NotFound), }; Ok(monitor) } - pub fn index(&self) -> Result> { + pub fn index(&self) -> Result { let device_name = self.device_name()?; - Ok(device_name.replace("\\\\.\\DISPLAY1", "to").parse()?) + Ok(device_name.replace("\\\\.\\DISPLAY", "").parse()?) } /// Get Monitor Name - pub fn name(&self) -> Result> { + pub fn name(&self) -> Result { let mut monitor_info = MONITORINFOEXW { monitorInfo: MONITORINFO { - cbSize: mem::size_of::() as u32, + cbSize: u32::try_from(mem::size_of::()).unwrap(), rcMonitor: RECT::default(), rcWork: RECT::default(), dwFlags: 0, @@ -93,7 +99,7 @@ impl Monitor { ) .as_bool() } { - return Err(Box::new(MonitorErrors::FailedToGetMonitorInfo)); + return Err(Error::FailedToGetMonitorInfo); } let mut number_of_paths = 0; @@ -123,7 +129,8 @@ impl Monitor { let mut source = DISPLAYCONFIG_SOURCE_DEVICE_NAME { header: DISPLAYCONFIG_DEVICE_INFO_HEADER { r#type: DISPLAYCONFIG_DEVICE_INFO_GET_SOURCE_NAME, - size: mem::size_of::() as u32, + size: u32::try_from(mem::size_of::()) + .unwrap(), adapterId: path.sourceInfo.adapterId, id: path.sourceInfo.id, }, @@ -147,7 +154,8 @@ impl Monitor { let mut target = DISPLAYCONFIG_TARGET_DEVICE_NAME { header: DISPLAYCONFIG_DEVICE_INFO_HEADER { r#type: DISPLAYCONFIG_DEVICE_INFO_GET_TARGET_NAME, - size: mem::size_of::() as u32, + size: u32::try_from(mem::size_of::()) + .unwrap(), adapterId: path.sourceInfo.adapterId, id: path.targetInfo.id, }, @@ -171,20 +179,20 @@ impl Monitor { .collect::>(), )?; return Ok(name); - } else { - return Err(Box::new(MonitorErrors::FailedToGetMonitorInfo)); } + + return Err(Error::FailedToGetMonitorInfo); } } - Err(Box::new(MonitorErrors::NameNotFound)) + Err(Error::NameNotFound) } /// Get Monitor Device Name - pub fn device_name(&self) -> Result> { + pub fn device_name(&self) -> Result { let mut monitor_info = MONITORINFOEXW { monitorInfo: MONITORINFO { - cbSize: mem::size_of::() as u32, + cbSize: u32::try_from(mem::size_of::()).unwrap(), rcMonitor: RECT::default(), rcWork: RECT::default(), dwFlags: 0, @@ -198,7 +206,7 @@ impl Monitor { ) .as_bool() } { - return Err(Box::new(MonitorErrors::FailedToGetMonitorInfo)); + return Err(Error::FailedToGetMonitorInfo); } let device_name = String::from_utf16( @@ -215,10 +223,10 @@ impl Monitor { } /// Get Monitor Device String - pub fn device_string(&self) -> Result> { + pub fn device_string(&self) -> Result { let mut monitor_info = MONITORINFOEXW { monitorInfo: MONITORINFO { - cbSize: mem::size_of::() as u32, + cbSize: u32::try_from(mem::size_of::()).unwrap(), rcMonitor: RECT::default(), rcWork: RECT::default(), dwFlags: 0, @@ -232,11 +240,11 @@ impl Monitor { ) .as_bool() } { - return Err(Box::new(MonitorErrors::FailedToGetMonitorInfo)); + return Err(Error::FailedToGetMonitorInfo); } let mut display_device = DISPLAY_DEVICEW { - cb: mem::size_of::() as u32, + cb: u32::try_from(mem::size_of::()).unwrap(), DeviceName: [0; 32], DeviceString: [0; 128], StateFlags: 0, @@ -253,7 +261,7 @@ impl Monitor { ) .as_bool() } { - return Err(Box::new(MonitorErrors::FailedToGetMonitorName)); + return Err(Error::FailedToGetMonitorName); } let device_string = String::from_utf16( @@ -270,7 +278,7 @@ impl Monitor { } /// Get A List Of All Monitors - pub fn enumerate() -> Result, Box> { + pub fn enumerate() -> Result, Error> { let mut monitors: Vec = Vec::new(); unsafe { @@ -315,7 +323,7 @@ impl Monitor { // Automatically Convert Monitor To GraphicsCaptureItem impl TryFrom for GraphicsCaptureItem { - type Error = Box; + type Error = Error; fn try_from(value: Monitor) -> Result { // Get Capture Item From HMONITOR diff --git a/src/settings.rs b/src/settings.rs index 37b3c24..165e1c4 100644 --- a/src/settings.rs +++ b/src/settings.rs @@ -1,12 +1,10 @@ -use std::error::Error; - use windows::Graphics::Capture::GraphicsCaptureItem; /// Used To Handle Settings Errors -#[derive(thiserror::Error, Eq, PartialEq, Clone, Copy, Debug)] -pub enum SettingsErrors { +#[derive(thiserror::Error, Debug)] +pub enum Error { #[error("Failed To Convert To GraphicsCaptureItem")] - ConvertFailed, + ItemConvertFailed, } /// Kind Of Pixel Format For Frame To Have @@ -18,7 +16,7 @@ pub enum ColorFormat { /// Capture Settings, None Means Default #[derive(Eq, PartialEq, Clone, Debug)] -pub struct WindowsCaptureSettings { +pub struct Settings { pub item: GraphicsCaptureItem, pub capture_cursor: Option, pub draw_border: Option, @@ -26,7 +24,7 @@ pub struct WindowsCaptureSettings { pub flags: Flags, } -impl WindowsCaptureSettings { +impl Settings { /// Create Capture Settings pub fn new>( item: T, @@ -34,11 +32,11 @@ impl WindowsCaptureSettings { draw_border: Option, color_format: ColorFormat, flags: Flags, - ) -> Result> { + ) -> Result { Ok(Self { item: match item.try_into() { Ok(item) => item, - Err(_) => return Err(Box::new(SettingsErrors::ConvertFailed)), + Err(_) => return Err(Error::ItemConvertFailed), }, capture_cursor, draw_border, diff --git a/src/window.rs b/src/window.rs index f493a56..67c9408 100644 --- a/src/window.rs +++ b/src/window.rs @@ -1,4 +1,4 @@ -use std::{error::Error, ptr}; +use std::{ptr, string::FromUtf16Error}; use log::warn; use windows::{ @@ -18,12 +18,16 @@ use windows::{ }; /// Used To Handle Window Errors -#[derive(thiserror::Error, Eq, PartialEq, Clone, Copy, Debug)] -pub enum WindowErrors { +#[derive(thiserror::Error, Debug)] +pub enum Error { #[error("Failed To Get The Foreground Window")] NoActiveWindow, #[error("Failed To Find Window")] NotFound, + #[error(transparent)] + FailedToConvertWindowsString(#[from] FromUtf16Error), + #[error(transparent)] + WindowsError(#[from] windows::core::Error), } /// Represents A Windows @@ -33,31 +37,31 @@ pub struct Window { } impl Window { - /// Get The Currently Active Foreground Window - pub fn foreground() -> Result> { + /// Get The Currently Active Window + pub fn foreground() -> Result { let window = unsafe { GetForegroundWindow() }; if window.0 == 0 { - return Err(Box::new(WindowErrors::NoActiveWindow)); + return Err(Error::NoActiveWindow); } Ok(Self { window }) } /// Create From A Window Name - pub fn from_name(title: &str) -> Result> { + pub fn from_name(title: &str) -> Result { let title = HSTRING::from(title); let window = unsafe { FindWindowW(None, &title) }; if window.0 == 0 { - return Err(Box::new(WindowErrors::NotFound)); + return Err(Error::NotFound); } Ok(Self { window }) } /// Create From A Window Name Substring - pub fn from_contains_name(title: &str) -> Result> { + pub fn from_contains_name(title: &str) -> Result { let windows = Self::enumerate()?; let mut target_window = None; @@ -68,14 +72,14 @@ impl Window { } } - Ok(target_window.map_or_else(|| Err(Box::new(WindowErrors::NotFound)), Ok)?) + target_window.map_or_else(|| Err(Error::NotFound), Ok) } /// Get Window Title - pub fn title(&self) -> Result> { + pub fn title(&self) -> Result { let len = unsafe { GetWindowTextLengthW(self.window) }; - let mut name = vec![0u16; len as usize + 1]; + let mut name = vec![0u16; usize::try_from(len).unwrap() + 1]; if len >= 1 { let copied = unsafe { GetWindowTextW(self.window, &mut name) }; if copied == 0 { @@ -114,10 +118,10 @@ impl Window { let styles = unsafe { GetWindowLongPtrW(window, GWL_STYLE) }; let ex_styles = unsafe { GetWindowLongPtrW(window, GWL_EXSTYLE) }; - if (ex_styles & WS_EX_TOOLWINDOW.0 as isize) != 0 { + if (ex_styles & isize::try_from(WS_EX_TOOLWINDOW.0).unwrap()) != 0 { return false; } - if (styles & WS_CHILD.0 as isize) != 0 { + if (styles & isize::try_from(WS_CHILD.0).unwrap()) != 0 { return false; } } else { @@ -129,7 +133,7 @@ impl Window { } /// Get A List Of All Windows - pub fn enumerate() -> Result, Box> { + pub fn enumerate() -> Result, Error> { let mut windows: Vec = Vec::new(); unsafe { @@ -144,15 +148,6 @@ impl Window { Ok(windows) } - /// Wait Until The Window Is The Currently Active Foreground Window - pub fn activate(&self) { - loop { - if unsafe { GetForegroundWindow() } == self.window { - break; - } - } - } - /// Create From A Raw HWND #[must_use] pub const fn from_raw_hwnd(window: HWND) -> Self { @@ -179,7 +174,7 @@ impl Window { // Automatically Convert Window To GraphicsCaptureItem impl TryFrom for GraphicsCaptureItem { - type Error = Box; + type Error = Error; fn try_from(value: Window) -> Result { // Get Capture Item From HWND diff --git a/windows-capture-python/Cargo.toml b/windows-capture-python/Cargo.toml index 26b6485..ddbd1ca 100644 --- a/windows-capture-python/Cargo.toml +++ b/windows-capture-python/Cargo.toml @@ -25,8 +25,4 @@ pyo3 = { version = "0.20.0", features = [ "extension-module", "auto-initialize", ] } -windows = { version = "0.52.0", features = [ - "Win32_UI_WindowsAndMessaging", - "Win32_Foundation", -] } windows-capture = { path = ".." } diff --git a/windows-capture-python/src/lib.rs b/windows-capture-python/src/lib.rs index c208e0b..15fbe95 100644 --- a/windows-capture-python/src/lib.rs +++ b/windows-capture-python/src/lib.rs @@ -1,36 +1,21 @@ -#![warn(clippy::semicolon_if_nothing_returned)] -#![warn(clippy::inconsistent_struct_constructor)] -#![warn(clippy::must_use_candidate)] -#![warn(clippy::ptr_as_ptr)] -#![warn(clippy::borrow_as_ptr)] +#![warn(clippy::pedantic)] #![warn(clippy::nursery)] #![warn(clippy::cargo)] +#![allow(clippy::missing_errors_doc)] +#![allow(clippy::missing_panics_doc)] #![allow(clippy::redundant_pub_crate)] -use std::{ - error::Error, - os::windows::io::AsRawHandle, - sync::{ - atomic::{self, AtomicBool}, - Arc, - }, - thread::JoinHandle, -}; +use std::{error::Error, sync::Arc}; use ::windows_capture::{ - capture::{CaptureControlError, WindowsCaptureHandler}, + capture::{CaptureControl, WindowsCaptureHandler}, frame::Frame, graphics_capture_api::InternalCaptureControl, monitor::Monitor, - settings::{ColorFormat, WindowsCaptureSettings}, + settings::{ColorFormat, Settings}, window::Window, }; use pyo3::{exceptions::PyException, prelude::*, types::PyList}; -use windows::Win32::{ - Foundation::{HANDLE, LPARAM, WPARAM}, - System::Threading::GetThreadId, - UI::WindowsAndMessaging::{PostThreadMessageW, WM_QUIT}, -}; /// Fastest Windows Screen Capture Library For Python 🔥. #[pymodule] @@ -43,11 +28,14 @@ fn windows_capture(_py: Python, m: &PyModule) -> PyResult<()> { /// Internal Struct Used To Handle Free Threaded Start #[pyclass] pub struct NativeCaptureControl { - capture_control: Option, + capture_control: + Option>>, } impl NativeCaptureControl { - const fn new(capture_control: NoGenericCaptureControl) -> Self { + const fn new( + capture_control: CaptureControl>, + ) -> Self { Self { capture_control: Some(capture_control), } @@ -60,7 +48,7 @@ impl NativeCaptureControl { pub fn is_finished(&self) -> bool { self.capture_control .as_ref() - .map_or(true, |capture_control| capture_control.is_finished()) + .map_or(true, CaptureControl::is_finished) } pub fn wait(&mut self, py: Python) -> PyResult<()> { @@ -69,7 +57,7 @@ impl NativeCaptureControl { py.allow_threads(|| { if let Some(capture_control) = self.capture_control.take() { match capture_control.wait() { - Ok(_) => (), + Ok(()) => (), Err(e) => { return Err(PyException::new_err(format!( "Failed To Join The Capture Thread -> {e}" @@ -90,7 +78,7 @@ impl NativeCaptureControl { py.allow_threads(|| { if let Some(capture_control) = self.capture_control.take() { match capture_control.stop() { - Ok(_) => (), + Ok(()) => (), Err(e) => { return Err(PyException::new_err(format!( "Failed To Stop The Capture Thread -> {e}" @@ -106,87 +94,6 @@ impl NativeCaptureControl { } } -/// Because The Default Rust Capture Control Contains Generic That Is -/// Unsupported By Python -pub struct NoGenericCaptureControl { - thread_handle: Option>>>, - halt_handle: Arc, -} - -impl NoGenericCaptureControl { - #[must_use] - pub fn new( - thread_handle: JoinHandle>>, - halt_handle: Arc, - ) -> Self { - Self { - thread_handle: Some(thread_handle), - halt_handle, - } - } - - #[must_use] - pub fn is_finished(&self) -> bool { - self.thread_handle - .as_ref() - .map_or(true, |thread_handle| thread_handle.is_finished()) - } - - pub fn wait(mut self) -> Result<(), Box> { - if let Some(thread_handle) = self.thread_handle.take() { - match thread_handle.join() { - Ok(result) => result?, - Err(_) => { - return Err(Box::new(CaptureControlError::FailedToJoinThread)); - } - } - } else { - return Err(Box::new(CaptureControlError::ThreadHandleIsTaken)); - } - - Ok(()) - } - - /// Gracefully Stop The Capture Thread - pub fn stop(mut self) -> Result<(), Box> { - self.halt_handle.store(true, atomic::Ordering::Relaxed); - - if let Some(thread_handle) = self.thread_handle.take() { - let handle = thread_handle.as_raw_handle(); - let handle = HANDLE(handle as isize); - let therad_id = unsafe { GetThreadId(handle) }; - - loop { - match unsafe { - PostThreadMessageW(therad_id, WM_QUIT, WPARAM::default(), LPARAM::default()) - } { - Ok(_) => break, - Err(e) => { - if thread_handle.is_finished() { - break; - } - - if e.code().0 != -2147023452 { - Err(e)?; - } - } - } - } - - match thread_handle.join() { - Ok(result) => result?, - Err(_) => { - return Err(Box::new(CaptureControlError::FailedToJoinThread)); - } - } - } else { - return Err(Box::new(CaptureControlError::ThreadHandleIsTaken)); - } - - Ok(()) - } -} - /// Internal Struct Used For Windows Capture #[pyclass] pub struct NativeWindowsCapture { @@ -241,7 +148,7 @@ impl NativeWindowsCapture { } }; - match WindowsCaptureSettings::new( + match Settings::new( window, self.capture_cursor, self.draw_border, @@ -268,7 +175,7 @@ impl NativeWindowsCapture { } }; - match WindowsCaptureSettings::new( + match Settings::new( monitor, self.capture_cursor, self.draw_border, @@ -288,7 +195,7 @@ impl NativeWindowsCapture { }; match InnerNativeWindowsCapture::start(settings) { - Ok(_) => (), + Ok(()) => (), Err(e) => { return Err(PyException::new_err(format!( "Capture Session Threw An Exception -> {e}" @@ -311,7 +218,7 @@ impl NativeWindowsCapture { } }; - match WindowsCaptureSettings::new( + match Settings::new( window, self.capture_cursor, self.draw_border, @@ -338,7 +245,7 @@ impl NativeWindowsCapture { } }; - match WindowsCaptureSettings::new( + match Settings::new( monitor, self.capture_cursor, self.draw_border, @@ -366,10 +273,7 @@ impl NativeWindowsCapture { } }; - let halt_handle = capture_control.halt_handle(); - let thread_handle = capture_control.into_thread_handle(); - let no_generic_capture_control = NoGenericCaptureControl::new(thread_handle, halt_handle); - let capture_control = NativeCaptureControl::new(no_generic_capture_control); + let capture_control = NativeCaptureControl::new(capture_control); Ok(capture_control) } @@ -382,10 +286,9 @@ struct InnerNativeWindowsCapture { impl WindowsCaptureHandler for InnerNativeWindowsCapture { type Flags = (Arc, Arc); + type Error = Box; - fn new( - (on_frame_arrived_callback, on_closed): Self::Flags, - ) -> Result> { + fn new((on_frame_arrived_callback, on_closed): Self::Flags) -> Result { Ok(Self { on_frame_arrived_callback, on_closed, @@ -396,7 +299,7 @@ impl WindowsCaptureHandler for InnerNativeWindowsCapture { &mut self, frame: &mut Frame, capture_control: InternalCaptureControl, - ) -> Result<(), Box<(dyn Error + Send + Sync)>> { + ) -> Result<(), Self::Error> { let width = frame.width(); let height = frame.height(); let mut buffer = frame.buffer()?;