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;
13use crate::winrt::WinRT;
14
15#[derive(thiserror::Error, Eq, PartialEq, Clone, Debug)]
16pub enum Error {
18 #[error("Windows API error: {0}")]
20 WindowsError(#[from] windows::core::Error),
21 #[error("User canceled the picker")]
23 Canceled,
24}
25
26unsafe extern "system" fn wnd_proc(hwnd: HWND, msg: u32, wparam: WPARAM, lparam: LPARAM) -> LRESULT {
31 match msg {
32 WM_DESTROY => LRESULT(0),
33 _ => unsafe { DefWindowProcW(hwnd, msg, wparam, lparam) },
34 }
35}
36
37pub struct HwndGuard {
40 hwnd: HWND,
41 _winrt: WinRT,
42}
43
44impl Drop for HwndGuard {
45 fn drop(&mut self) {
46 unsafe {
47 let _ = DestroyWindow(self.hwnd);
48 let mut msg = MSG::default();
49 while PeekMessageW(&mut msg, None, 0, 0, PM_REMOVE).as_bool() {
50 }
52 }
53 }
54}
55
56pub struct PickedGraphicsCaptureItem {
58 pub item: GraphicsCaptureItem,
60 _guard: HwndGuard,
62}
63
64impl PickedGraphicsCaptureItem {
65 pub fn size(&self) -> windows::core::Result<(i32, i32)> {
67 let size = self.item.Size()?;
68 Ok((size.Width, size.Height))
69 }
70}
71
72pub struct GraphicsCapturePicker;
75
76impl GraphicsCapturePicker {
77 pub fn pick_item() -> Result<Option<PickedGraphicsCaptureItem>, Error> {
92 let winrt = WinRT::new()?;
96
97 let hinst = unsafe { GetModuleHandleW(None) }?;
98 let wc = WNDCLASSEXW {
99 cbSize: std::mem::size_of::<WNDCLASSEXW>() as u32,
100 style: CS_HREDRAW | CS_VREDRAW,
101 lpfnWndProc: Some(wnd_proc),
102 hInstance: hinst.into(),
103 lpszClassName: w!("windows-capture-picker-window"),
104 ..Default::default()
105 };
106
107 if unsafe { RegisterClassExW(&wc) } == 0 {
108 let err = unsafe { GetLastError() };
109 if err != ERROR_CLASS_ALREADY_EXISTS {
110 return Err(Error::WindowsError(err.into()));
111 }
112 }
113
114 let hwnd = unsafe {
115 CreateWindowExW(
116 WS_EX_TOOLWINDOW,
117 w!("windows-capture-picker-window"),
118 w!("Windows Capture Picker"),
119 WS_POPUP | WS_VISIBLE,
120 -69000,
121 -69000,
122 0,
123 0,
124 None,
125 None,
126 Some(hinst.into()),
127 None,
128 )
129 }?;
130
131 let guard = HwndGuard { hwnd, _winrt: winrt };
134
135 let picker = windows::Graphics::Capture::GraphicsCapturePicker::new()?;
136 let initialize_with_window: IInitializeWithWindow = picker.cast()?;
137 unsafe { initialize_with_window.Initialize(guard.hwnd) }?;
138
139 let op = picker.PickSingleItemAsync()?;
140
141 loop {
142 match op.Status()? {
143 AsyncStatus::Started => unsafe {
144 let mut msg = MSG::default();
145 while PeekMessageW(&mut msg, None, 0, 0, PM_REMOVE).as_bool() {
146 let _ = TranslateMessage(&msg);
148 DispatchMessageW(&msg);
149 }
150 },
151 AsyncStatus::Completed => break,
152 AsyncStatus::Canceled => return Err(Error::Canceled),
153 AsyncStatus::Error => return Err(Error::WindowsError(op.ErrorCode()?.into())),
154 _ => {}
155 }
156 }
157
158 op.GetResults()
159 .ok()
160 .map_or_else(|| Ok(None), |item| Ok(Some(PickedGraphicsCaptureItem { item, _guard: guard })))
161 }
162}
163
164impl TryInto<GraphicsCaptureItemType> for PickedGraphicsCaptureItem {
165 type Error = windows::core::Error;
166
167 #[inline]
168 fn try_into(self) -> Result<GraphicsCaptureItemType, Self::Error> {
169 Ok(GraphicsCaptureItemType::Unknown((self.item, self._guard)))
170 }
171}