1use std::mem;
14use std::num::ParseIntError;
15use std::string::FromUtf16Error;
16
17use windows::Graphics::Capture::GraphicsCaptureItem;
18use windows::Win32::Devices::Display::{
19 DISPLAYCONFIG_DEVICE_INFO_GET_SOURCE_NAME, DISPLAYCONFIG_DEVICE_INFO_GET_TARGET_NAME,
20 DISPLAYCONFIG_DEVICE_INFO_HEADER, DISPLAYCONFIG_MODE_INFO, DISPLAYCONFIG_PATH_INFO,
21 DISPLAYCONFIG_SOURCE_DEVICE_NAME, DISPLAYCONFIG_TARGET_DEVICE_NAME, DISPLAYCONFIG_TARGET_DEVICE_NAME_FLAGS,
22 DISPLAYCONFIG_VIDEO_OUTPUT_TECHNOLOGY, DisplayConfigGetDeviceInfo, GetDisplayConfigBufferSizes,
23 QDC_ONLY_ACTIVE_PATHS, QueryDisplayConfig,
24};
25use windows::Win32::Foundation::{LPARAM, POINT, RECT, TRUE};
26use windows::Win32::Graphics::Gdi::{
27 DEVMODEW, DISPLAY_DEVICE_STATE_FLAGS, DISPLAY_DEVICEW, ENUM_CURRENT_SETTINGS, EnumDisplayDevicesW,
28 EnumDisplayMonitors, EnumDisplaySettingsW, GetMonitorInfoW, HDC, HMONITOR, MONITOR_DEFAULTTONULL, MONITORINFO,
29 MONITORINFOEXW, MonitorFromPoint,
30};
31use windows::Win32::System::WinRT::Graphics::Capture::IGraphicsCaptureItemInterop;
32use windows::core::{BOOL, HSTRING, PCWSTR};
33
34use crate::settings::GraphicsCaptureItemType;
35
36#[derive(thiserror::Error, Debug)]
37pub enum Error {
39 #[error("Failed to find the specified monitor.")]
44 NotFound,
45 #[error("Failed to get the monitor's name.")]
47 NameNotFound,
48 #[error("The monitor index must be greater than zero.")]
50 IndexIsLowerThanOne,
51 #[error("Failed to get monitor information.")]
53 FailedToGetMonitorInfo,
54 #[error("Failed to get the monitor's display settings.")]
56 FailedToGetMonitorSettings,
57 #[error("Failed to get the monitor's device name.")]
59 FailedToGetMonitorName,
60 #[error("Failed to parse the monitor index: {0}")]
64 FailedToParseMonitorIndex(#[from] ParseIntError),
65 #[error("Failed to convert a Windows string: {0}")]
69 FailedToConvertWindowsString(#[from] FromUtf16Error),
70 #[error("A Windows API call failed: {0}")]
74 WindowsError(#[from] windows::core::Error),
75}
76
77#[derive(Eq, PartialEq, Clone, Copy, Debug)]
108pub struct Monitor {
109 monitor: HMONITOR,
110}
111
112unsafe impl Send for Monitor {}
113
114impl Monitor {
115 #[inline]
121 pub fn primary() -> Result<Self, Error> {
122 let point = POINT { x: 0, y: 0 };
123 let monitor = unsafe { MonitorFromPoint(point, MONITOR_DEFAULTTONULL) };
124
125 if monitor.is_invalid() {
126 return Err(Error::NotFound);
127 }
128
129 Ok(Self { monitor })
130 }
131
132 #[inline]
140 pub fn from_index(index: usize) -> Result<Self, Error> {
141 if index < 1 {
142 return Err(Error::IndexIsLowerThanOne);
143 }
144
145 let monitor = Self::enumerate()?;
146 let monitor = match monitor.get(index - 1) {
147 Some(monitor) => *monitor,
148 None => return Err(Error::NotFound),
149 };
150
151 Ok(monitor)
152 }
153
154 #[inline]
163 pub fn index(&self) -> Result<usize, Error> {
164 let device_name = self.device_name()?;
165 Ok(device_name.replace("\\\\.\\DISPLAY", "").parse()?)
166 }
167
168 #[inline]
176 pub fn name(&self) -> Result<String, Error> {
177 let device_name = self.device_name()?;
178 let mut number_of_paths = 0;
179 let mut number_of_modes = 0;
180 unsafe {
181 GetDisplayConfigBufferSizes(QDC_ONLY_ACTIVE_PATHS, &mut number_of_paths, &mut number_of_modes).ok()?;
182 };
183
184 let mut paths = vec![DISPLAYCONFIG_PATH_INFO::default(); number_of_paths as usize];
185 let mut modes = vec![DISPLAYCONFIG_MODE_INFO::default(); number_of_modes as usize];
186 unsafe {
187 QueryDisplayConfig(
188 QDC_ONLY_ACTIVE_PATHS,
189 &mut number_of_paths,
190 paths.as_mut_ptr(),
191 &mut number_of_modes,
192 modes.as_mut_ptr(),
193 None,
194 )
195 }
196 .ok()?;
197
198 for path in &paths {
199 let mut source = DISPLAYCONFIG_SOURCE_DEVICE_NAME {
200 header: DISPLAYCONFIG_DEVICE_INFO_HEADER {
201 r#type: DISPLAYCONFIG_DEVICE_INFO_GET_SOURCE_NAME,
202 size: std::mem::size_of::<DISPLAYCONFIG_SOURCE_DEVICE_NAME>() as u32,
203 adapterId: path.sourceInfo.adapterId,
204 id: path.sourceInfo.id,
205 },
206 viewGdiDeviceName: [0; 32],
207 };
208
209 if unsafe { DisplayConfigGetDeviceInfo(&mut source.header) } != 0 {
210 continue;
211 }
212
213 let view_gdi_device_name = String::from_utf16(
214 &source
215 .viewGdiDeviceName
216 .as_slice()
217 .iter()
218 .take_while(|ch| **ch != 0x0000)
219 .copied()
220 .collect::<Vec<u16>>(),
221 )?;
222
223 if view_gdi_device_name == device_name {
224 let mut target = DISPLAYCONFIG_TARGET_DEVICE_NAME {
225 header: DISPLAYCONFIG_DEVICE_INFO_HEADER {
226 r#type: DISPLAYCONFIG_DEVICE_INFO_GET_TARGET_NAME,
227 size: std::mem::size_of::<DISPLAYCONFIG_TARGET_DEVICE_NAME>() as u32,
228 adapterId: path.sourceInfo.adapterId,
229 id: path.targetInfo.id,
230 },
231 flags: DISPLAYCONFIG_TARGET_DEVICE_NAME_FLAGS::default(),
232 outputTechnology: DISPLAYCONFIG_VIDEO_OUTPUT_TECHNOLOGY::default(),
233 edidManufactureId: 0,
234 edidProductCodeId: 0,
235 connectorInstance: 0,
236 monitorFriendlyDeviceName: [0; 64],
237 monitorDevicePath: [0; 128],
238 };
239
240 if unsafe { DisplayConfigGetDeviceInfo(&mut target.header) } != 0 {
241 continue;
242 }
243 return Ok(String::from_utf16(
244 &target
245 .monitorFriendlyDeviceName
246 .as_slice()
247 .iter()
248 .take_while(|ch| **ch != 0x0000)
249 .copied()
250 .collect::<Vec<u16>>(),
251 )?);
252 }
253 }
254
255 Err(Error::NameNotFound)
256 }
257
258 #[inline]
265 pub fn device_name(&self) -> Result<String, Error> {
266 let mut monitor_info = MONITORINFOEXW {
267 monitorInfo: MONITORINFO {
268 cbSize: u32::try_from(mem::size_of::<MONITORINFOEXW>()).unwrap(),
269 rcMonitor: RECT::default(),
270 rcWork: RECT::default(),
271 dwFlags: 0,
272 },
273 szDevice: [0; 32],
274 };
275 if unsafe { !GetMonitorInfoW(HMONITOR(self.as_raw_hmonitor()), (&raw mut monitor_info).cast()).as_bool() } {
276 return Err(Error::FailedToGetMonitorInfo);
277 }
278
279 let device_name = String::from_utf16(
280 &monitor_info.szDevice.as_slice().iter().take_while(|ch| **ch != 0x0000).copied().collect::<Vec<u16>>(),
281 )?;
282
283 Ok(device_name)
284 }
285
286 #[inline]
295 pub fn device_string(&self) -> Result<String, Error> {
296 let mut monitor_info = MONITORINFOEXW {
297 monitorInfo: MONITORINFO {
298 cbSize: u32::try_from(mem::size_of::<MONITORINFOEXW>()).unwrap(),
299 rcMonitor: RECT::default(),
300 rcWork: RECT::default(),
301 dwFlags: 0,
302 },
303 szDevice: [0; 32],
304 };
305 if unsafe { !GetMonitorInfoW(HMONITOR(self.as_raw_hmonitor()), (&raw mut monitor_info).cast()).as_bool() } {
306 return Err(Error::FailedToGetMonitorInfo);
307 }
308
309 let mut display_device = DISPLAY_DEVICEW {
310 cb: u32::try_from(mem::size_of::<DISPLAY_DEVICEW>()).unwrap(),
311 DeviceName: [0; 32],
312 DeviceString: [0; 128],
313 StateFlags: DISPLAY_DEVICE_STATE_FLAGS::default(),
314 DeviceID: [0; 128],
315 DeviceKey: [0; 128],
316 };
317
318 if unsafe {
319 !EnumDisplayDevicesW(PCWSTR::from_raw(monitor_info.szDevice.as_mut_ptr()), 0, &mut display_device, 0)
320 .as_bool()
321 } {
322 return Err(Error::FailedToGetMonitorName);
323 }
324
325 let device_string = String::from_utf16(
326 &display_device
327 .DeviceString
328 .as_slice()
329 .iter()
330 .take_while(|ch| **ch != 0x0000)
331 .copied()
332 .collect::<Vec<u16>>(),
333 )?;
334
335 Ok(device_string)
336 }
337
338 #[inline]
347 pub fn refresh_rate(&self) -> Result<u32, Error> {
348 let mut device_mode =
349 DEVMODEW { dmSize: u16::try_from(mem::size_of::<DEVMODEW>()).unwrap(), ..DEVMODEW::default() };
350 let name = HSTRING::from(self.device_name()?);
351 if unsafe { !EnumDisplaySettingsW(PCWSTR(name.as_ptr()), ENUM_CURRENT_SETTINGS, &mut device_mode).as_bool() } {
352 return Err(Error::FailedToGetMonitorSettings);
353 }
354
355 Ok(device_mode.dmDisplayFrequency)
356 }
357
358 #[inline]
367 pub fn width(&self) -> Result<u32, Error> {
368 let mut device_mode =
369 DEVMODEW { dmSize: u16::try_from(mem::size_of::<DEVMODEW>()).unwrap(), ..DEVMODEW::default() };
370 let name = HSTRING::from(self.device_name()?);
371 if unsafe { !EnumDisplaySettingsW(PCWSTR(name.as_ptr()), ENUM_CURRENT_SETTINGS, &mut device_mode).as_bool() } {
372 return Err(Error::FailedToGetMonitorSettings);
373 }
374
375 Ok(device_mode.dmPelsWidth)
376 }
377
378 #[inline]
387 pub fn height(&self) -> Result<u32, Error> {
388 let mut device_mode =
389 DEVMODEW { dmSize: u16::try_from(mem::size_of::<DEVMODEW>()).unwrap(), ..DEVMODEW::default() };
390 let name = HSTRING::from(self.device_name()?);
391 if unsafe { !EnumDisplaySettingsW(PCWSTR(name.as_ptr()), ENUM_CURRENT_SETTINGS, &mut device_mode).as_bool() } {
392 return Err(Error::FailedToGetMonitorSettings);
393 }
394
395 Ok(device_mode.dmPelsHeight)
396 }
397
398 #[inline]
404 pub fn enumerate() -> Result<Vec<Self>, Error> {
405 let mut monitors: Vec<Self> = Vec::new();
406
407 unsafe {
408 EnumDisplayMonitors(None, None, Some(Self::enum_monitors_callback), LPARAM(&raw mut monitors as isize))
409 .ok()?;
410 };
411
412 Ok(monitors)
413 }
414
415 #[inline]
417 #[must_use]
418 pub const fn from_raw_hmonitor(monitor: *mut std::ffi::c_void) -> Self {
419 Self { monitor: HMONITOR(monitor) }
420 }
421
422 #[inline]
424 #[must_use]
425 pub const fn as_raw_hmonitor(&self) -> *mut std::ffi::c_void {
426 self.monitor.0
427 }
428
429 #[inline]
431 unsafe extern "system" fn enum_monitors_callback(monitor: HMONITOR, _: HDC, _: *mut RECT, vec: LPARAM) -> BOOL {
432 let monitors = unsafe { &mut *(vec.0 as *mut Vec<Self>) };
433
434 monitors.push(Self { monitor });
435
436 TRUE
437 }
438}
439
440impl TryInto<GraphicsCaptureItemType> for Monitor {
441 type Error = windows::core::Error;
442
443 #[inline]
444 fn try_into(self) -> Result<GraphicsCaptureItemType, Self::Error> {
445 let monitor = HMONITOR(self.as_raw_hmonitor());
446
447 let interop = windows::core::factory::<GraphicsCaptureItem, IGraphicsCaptureItemInterop>()?;
448 let item = unsafe { interop.CreateForMonitor(monitor)? };
449
450 Ok(GraphicsCaptureItemType::Monitor((item, self)))
451 }
452}