use std::{
error::Error,
os::windows::prelude::AsRawHandle,
thread::{self, JoinHandle},
};
use log::{info, trace, warn};
use windows::{
Foundation::AsyncActionCompletedHandler,
Win32::{
Foundation::{HANDLE, LPARAM, WPARAM},
System::{
Com::{CoInitializeEx, CoUninitialize, COINIT_MULTITHREADED, COINIT_SPEED_OVER_MEMORY},
Threading::GetThreadId,
WinRT::{
CreateDispatcherQueueController, DispatcherQueueOptions, DQTAT_COM_NONE,
DQTYPE_THREAD_CURRENT,
},
},
UI::{
HiDpi::{SetProcessDpiAwareness, PROCESS_PER_MONITOR_DPI_AWARE},
WindowsAndMessaging::{
DispatchMessageW, GetMessageW, PostQuitMessage, PostThreadMessageW,
TranslateMessage, MSG, WM_QUIT,
},
},
},
};
use crate::{
frame::Frame,
graphics_capture_api::{GraphicsCaptureApi, InternalCaptureControl, RESULT},
settings::WindowsCaptureSettings,
};
#[derive(thiserror::Error, Eq, PartialEq, Clone, Copy, Debug)]
pub enum CaptureControlError {
#[error("Failed To Join Thread")]
FailedToJoin,
}
pub struct CaptureControl {
thread_handle: Option<JoinHandle<Result<(), Box<dyn Error + Send + Sync>>>>,
}
impl CaptureControl {
#[must_use]
pub fn new(thread_handle: JoinHandle<Result<(), Box<dyn Error + Send + Sync>>>) -> Self {
Self {
thread_handle: Some(thread_handle),
}
}
pub fn wait(mut self) -> Result<(), Box<dyn Error + Send + Sync>> {
if let Some(thread_handle) = self.thread_handle.take() {
match thread_handle.join() {
Ok(result) => result?,
Err(_) => {
return Err(Box::new(CaptureControlError::FailedToJoin));
}
}
}
Ok(())
}
pub fn stop(mut self) -> Result<(), Box<dyn Error + Send + Sync>> {
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 {
warn!("Thread Is Not In Message Loop Yet");
} else {
Err(e)?;
}
}
}
}
match thread_handle.join() {
Ok(result) => result?,
Err(_) => {
return Err(Box::new(CaptureControlError::FailedToJoin));
}
}
}
Ok(())
}
}
pub trait WindowsCaptureHandler: Sized {
type Flags;
fn start(
settings: WindowsCaptureSettings<Self::Flags>,
) -> Result<(), Box<dyn Error + Send + Sync>>
where
Self: Send + 'static,
<Self as WindowsCaptureHandler>::Flags: Send,
{
trace!("Initializing COM");
unsafe { CoInitializeEx(None, COINIT_MULTITHREADED | COINIT_SPEED_OVER_MEMORY)? };
trace!("Setting DPI Awarness");
unsafe { SetProcessDpiAwareness(PROCESS_PER_MONITOR_DPI_AWARE)? };
trace!("Creating A Dispatcher Queue For Capture Thread");
let options = DispatcherQueueOptions {
dwSize: std::mem::size_of::<DispatcherQueueOptions>() as u32,
threadType: DQTYPE_THREAD_CURRENT,
apartmentType: DQTAT_COM_NONE,
};
let controller = unsafe { CreateDispatcherQueueController(options)? };
info!("Starting Capture Thread");
let trigger = Self::new(settings.flags)?;
let mut capture = GraphicsCaptureApi::new(
settings.item,
trigger,
settings.capture_cursor,
settings.draw_border,
settings.color_format,
)?;
capture.start_capture()?;
trace!("Entering Message Loop");
let mut message = MSG::default();
unsafe {
while GetMessageW(&mut message, None, 0, 0).as_bool() {
TranslateMessage(&message);
DispatchMessageW(&message);
}
}
trace!("Shutting Down Dispatcher Queue");
let async_action = controller.ShutdownQueueAsync()?;
async_action.SetCompleted(&AsyncActionCompletedHandler::new(
move |_, _| -> Result<(), windows::core::Error> {
unsafe { PostQuitMessage(0) };
Ok(())
},
))?;
trace!("Entering Final Message Loop");
let mut message = MSG::default();
unsafe {
while GetMessageW(&mut message, None, 0, 0).as_bool() {
TranslateMessage(&message);
DispatchMessageW(&message);
}
}
info!("Stopping Capture Thread");
capture.stop_capture();
trace!("Uninitializing COM");
unsafe { CoUninitialize() };
trace!("Checking RESULT");
let result = RESULT.take().expect("Failed To Take RESULT");
result?;
Ok(())
}
fn start_free_threaded(settings: WindowsCaptureSettings<Self::Flags>) -> CaptureControl
where
Self: Send + 'static,
<Self as WindowsCaptureHandler>::Flags: Send,
{
let thread_handle = thread::spawn(move || Self::start(settings));
CaptureControl::new(thread_handle)
}
fn new(flags: Self::Flags) -> Result<Self, Box<dyn Error + Send + Sync>>;
fn on_frame_arrived(
&mut self,
frame: Frame,
capture_control: InternalCaptureControl,
) -> Result<(), Box<dyn Error + Send + Sync>>;
fn on_closed(&mut self) -> Result<(), Box<dyn Error + Send + Sync>>;
fn stop(&self) {
unsafe { PostQuitMessage(0) };
}
}