windows_capture/
graphics_capture_picker.rs1use windows::Graphics::Capture::GraphicsCaptureItem;
2use windows::Win32::Foundation::{ERROR_CLASS_ALREADY_EXISTS, GetLastError, HWND, LPARAM, LRESULT, WPARAM};
3use windows::Win32::System::LibraryLoader::GetModuleHandleW;
4use windows::Win32::UI::Shell::IInitializeWithWindow;
5use windows::Win32::UI::WindowsAndMessaging::{
6 CS_HREDRAW, CS_VREDRAW, CreateWindowExW, DefWindowProcW, DestroyWindow, DispatchMessageW, MSG, PM_REMOVE,
7 PeekMessageW, RegisterClassExW, TranslateMessage, WM_DESTROY, WNDCLASSEXW, WS_EX_TOOLWINDOW, WS_POPUP, WS_VISIBLE,
8};
9use windows::core::{Interface, w};
10use windows_future::AsyncStatus;
11
12use crate::settings::GraphicsCaptureItemType;
13
14#[derive(thiserror::Error, Eq, PartialEq, Clone, Debug)]
15pub enum Error {
17 #[error("Windows API error: {0}")]
19 WindowsError(#[from] windows::core::Error),
20 #[error("User canceled the picker")]
22 Canceled,
23}
24
25unsafe extern "system" fn wnd_proc(hwnd: HWND, msg: u32, wparam: WPARAM, lparam: LPARAM) -> LRESULT {
30 match msg {
31 WM_DESTROY => LRESULT(0),
32 _ => unsafe { DefWindowProcW(hwnd, msg, wparam, lparam) },
33 }
34}
35
36pub struct HwndGuard(HWND);
39impl Drop for HwndGuard {
40 fn drop(&mut self) {
41 unsafe {
42 let _ = DestroyWindow(self.0);
43 let mut msg = MSG::default();
44 while PeekMessageW(&mut msg, None, 0, 0, PM_REMOVE).as_bool() {
45 }
47 }
48 }
49}
50
51pub struct PickedGraphicsCaptureItem {
53 pub item: GraphicsCaptureItem,
55 _guard: HwndGuard,
57}
58
59impl PickedGraphicsCaptureItem {
60 pub fn size(&self) -> windows::core::Result<(i32, i32)> {
62 let size = self.item.Size()?;
63 Ok((size.Width, size.Height))
64 }
65}
66
67pub struct GraphicsCapturePicker;
70
71impl GraphicsCapturePicker {
72 pub fn pick_item() -> Result<Option<PickedGraphicsCaptureItem>, Error> {
87 let hinst = unsafe { GetModuleHandleW(None) }?;
88 let wc = WNDCLASSEXW {
89 cbSize: std::mem::size_of::<WNDCLASSEXW>() as u32,
90 style: CS_HREDRAW | CS_VREDRAW,
91 lpfnWndProc: Some(wnd_proc),
92 hInstance: hinst.into(),
93 lpszClassName: w!("windows-capture-picker-window"),
94 ..Default::default()
95 };
96
97 if unsafe { RegisterClassExW(&wc) } == 0 {
98 let err = unsafe { GetLastError() };
99 if err != ERROR_CLASS_ALREADY_EXISTS {
100 return Err(Error::WindowsError(err.into()));
101 }
102 }
103
104 let hwnd = unsafe {
105 CreateWindowExW(
106 WS_EX_TOOLWINDOW,
107 w!("windows-capture-picker-window"),
108 w!("Windows Capture Picker"),
109 WS_POPUP | WS_VISIBLE,
110 -69000,
111 -69000,
112 0,
113 0,
114 None,
115 None,
116 Some(hinst.into()),
117 None,
118 )
119 }?;
120
121 let picker = windows::Graphics::Capture::GraphicsCapturePicker::new()?;
122 let initialize_with_window: IInitializeWithWindow = picker.cast()?;
123 unsafe { initialize_with_window.Initialize(hwnd) }?;
124
125 let op = picker.PickSingleItemAsync()?;
126
127 loop {
128 match op.Status()? {
129 AsyncStatus::Started => unsafe {
130 let mut msg = MSG::default();
131 while PeekMessageW(&mut msg, None, 0, 0, PM_REMOVE).as_bool() {
132 let _ = TranslateMessage(&msg);
134 DispatchMessageW(&msg);
135 }
136 },
137 AsyncStatus::Completed => break,
138 AsyncStatus::Canceled => return Err(Error::Canceled),
139 AsyncStatus::Error => return Err(Error::WindowsError(op.ErrorCode()?.into())),
140 _ => {}
141 }
142 }
143
144 op.GetResults()
145 .ok()
146 .map_or_else(|| Ok(None), |item| Ok(Some(PickedGraphicsCaptureItem { item, _guard: HwndGuard(hwnd) })))
147 }
148}
149
150impl TryInto<GraphicsCaptureItemType> for PickedGraphicsCaptureItem {
151 type Error = windows::core::Error;
152
153 #[inline]
154 fn try_into(self) -> Result<GraphicsCaptureItemType, Self::Error> {
155 Ok(GraphicsCaptureItemType::Unknown((self.item, self._guard)))
156 }
157}