use windows::Graphics::Capture::GraphicsCaptureItem;
use windows::Win32::Foundation::{ERROR_CLASS_ALREADY_EXISTS, GetLastError, HWND, LPARAM, LRESULT, WPARAM};
use windows::Win32::System::LibraryLoader::GetModuleHandleW;
use windows::Win32::UI::Shell::IInitializeWithWindow;
use windows::Win32::UI::WindowsAndMessaging::{
CS_HREDRAW, CS_VREDRAW, CreateWindowExW, DefWindowProcW, DestroyWindow, DispatchMessageW, MSG, PM_REMOVE,
PeekMessageW, RegisterClassExW, TranslateMessage, WM_DESTROY, WNDCLASSEXW, WS_EX_TOOLWINDOW, WS_POPUP, WS_VISIBLE,
};
use windows::core::{Interface, w};
use windows_future::AsyncStatus;
use crate::settings::GraphicsCaptureItemType;
#[derive(thiserror::Error, Eq, PartialEq, Clone, Debug)]
pub enum Error {
#[error("Windows API error: {0}")]
WindowsError(#[from] windows::core::Error),
#[error("User canceled the picker")]
Canceled,
}
unsafe extern "system" fn wnd_proc(hwnd: HWND, msg: u32, wparam: WPARAM, lparam: LPARAM) -> LRESULT {
match msg {
WM_DESTROY => LRESULT(0),
_ => unsafe { DefWindowProcW(hwnd, msg, wparam, lparam) },
}
}
pub struct HwndGuard(HWND);
impl Drop for HwndGuard {
fn drop(&mut self) {
unsafe {
let _ = DestroyWindow(self.0);
let mut msg = MSG::default();
while PeekMessageW(&mut msg, None, 0, 0, PM_REMOVE).as_bool() {
}
}
}
}
pub struct PickedGraphicsCaptureItem {
pub item: GraphicsCaptureItem,
_guard: HwndGuard,
}
impl PickedGraphicsCaptureItem {
pub fn size(&self) -> windows::core::Result<(i32, i32)> {
let size = self.item.Size()?;
Ok((size.Width, size.Height))
}
}
pub struct GraphicsCapturePicker;
impl GraphicsCapturePicker {
pub fn pick_item() -> Result<Option<PickedGraphicsCaptureItem>, Error> {
let hinst = unsafe { GetModuleHandleW(None) }?;
let wc = WNDCLASSEXW {
cbSize: std::mem::size_of::<WNDCLASSEXW>() as u32,
style: CS_HREDRAW | CS_VREDRAW,
lpfnWndProc: Some(wnd_proc),
hInstance: hinst.into(),
lpszClassName: w!("windows-capture-picker-window"),
..Default::default()
};
if unsafe { RegisterClassExW(&wc) } == 0 {
let err = unsafe { GetLastError() };
if err != ERROR_CLASS_ALREADY_EXISTS {
return Err(Error::WindowsError(err.into()));
}
}
let hwnd = unsafe {
CreateWindowExW(
WS_EX_TOOLWINDOW,
w!("windows-capture-picker-window"),
w!("Windows Capture Picker"),
WS_POPUP | WS_VISIBLE,
-69000,
-69000,
0,
0,
None,
None,
Some(hinst.into()),
None,
)
}?;
let picker = windows::Graphics::Capture::GraphicsCapturePicker::new()?;
let initialize_with_window: IInitializeWithWindow = picker.cast()?;
unsafe { initialize_with_window.Initialize(hwnd) }?;
let op = picker.PickSingleItemAsync()?;
loop {
match op.Status()? {
AsyncStatus::Started => unsafe {
let mut msg = MSG::default();
while PeekMessageW(&mut msg, None, 0, 0, PM_REMOVE).as_bool() {
let _ = TranslateMessage(&msg);
DispatchMessageW(&msg);
}
},
AsyncStatus::Completed => break,
AsyncStatus::Canceled => return Err(Error::Canceled),
AsyncStatus::Error => return Err(Error::WindowsError(op.ErrorCode()?.into())),
_ => {}
}
}
op.GetResults()
.ok()
.map_or_else(|| Ok(None), |item| Ok(Some(PickedGraphicsCaptureItem { item, _guard: HwndGuard(hwnd) })))
}
}
impl TryInto<GraphicsCaptureItemType> for PickedGraphicsCaptureItem {
type Error = windows::core::Error;
#[inline]
fn try_into(self) -> Result<GraphicsCaptureItemType, Self::Error> {
Ok(GraphicsCaptureItemType::Unknown((self.item, self._guard)))
}
}