//! Display enumeration and information use anyhow::Result; /// Information about a display/monitor #[derive(Debug, Clone)] pub struct Display { /// Unique display ID pub id: u32, /// Display name (e.g., "\\\\.\\DISPLAY1") pub name: String, /// X position in virtual screen coordinates pub x: i32, /// Y position in virtual screen coordinates pub y: i32, /// Width in pixels pub width: u32, /// Height in pixels pub height: u32, /// Whether this is the primary display pub is_primary: bool, /// Platform-specific handle (HMONITOR on Windows) #[cfg(windows)] pub handle: isize, } /// Display info for protocol messages #[derive(Debug, Clone)] pub struct DisplayInfo { pub displays: Vec, pub primary_id: u32, } impl Display { /// Total pixels in the display pub fn pixel_count(&self) -> u32 { self.width * self.height } /// Bytes needed for BGRA frame buffer pub fn buffer_size(&self) -> usize { (self.width * self.height * 4) as usize } } /// Enumerate all connected displays #[cfg(windows)] pub fn enumerate_displays() -> Result> { use windows::Win32::Graphics::Gdi::{ EnumDisplayMonitors, GetMonitorInfoW, HMONITOR, MONITORINFOEXW, }; use windows::Win32::Foundation::{BOOL, LPARAM, RECT}; use std::mem; let mut displays = Vec::new(); let mut display_id = 0u32; // Callback for EnumDisplayMonitors unsafe extern "system" fn enum_callback( hmonitor: HMONITOR, _hdc: windows::Win32::Graphics::Gdi::HDC, _rect: *mut RECT, lparam: LPARAM, ) -> BOOL { let displays = &mut *(lparam.0 as *mut Vec<(HMONITOR, u32)>); let id = displays.len() as u32; displays.push((hmonitor, id)); BOOL(1) // Continue enumeration } // Collect all monitor handles let mut monitors: Vec<(windows::Win32::Graphics::Gdi::HMONITOR, u32)> = Vec::new(); unsafe { let result = EnumDisplayMonitors( None, None, Some(enum_callback), LPARAM(&mut monitors as *mut _ as isize), ); if !result.as_bool() { anyhow::bail!("EnumDisplayMonitors failed"); } } // Get detailed info for each monitor for (hmonitor, id) in monitors { let mut info: MONITORINFOEXW = unsafe { mem::zeroed() }; info.monitorInfo.cbSize = mem::size_of::() as u32; unsafe { if GetMonitorInfoW(hmonitor, &mut info.monitorInfo as *mut _ as *mut _).as_bool() { let rect = info.monitorInfo.rcMonitor; let name = String::from_utf16_lossy( &info.szDevice[..info.szDevice.iter().position(|&c| c == 0).unwrap_or(info.szDevice.len())] ); let is_primary = (info.monitorInfo.dwFlags & 1) != 0; // MONITORINFOF_PRIMARY displays.push(Display { id, name, x: rect.left, y: rect.top, width: (rect.right - rect.left) as u32, height: (rect.bottom - rect.top) as u32, is_primary, handle: hmonitor.0 as isize, }); } } } // Sort by position (left to right, top to bottom) displays.sort_by(|a, b| { if a.y != b.y { a.y.cmp(&b.y) } else { a.x.cmp(&b.x) } }); // Reassign IDs after sorting for (i, display) in displays.iter_mut().enumerate() { display.id = i as u32; } if displays.is_empty() { anyhow::bail!("No displays found"); } Ok(displays) } #[cfg(not(windows))] pub fn enumerate_displays() -> Result> { anyhow::bail!("Display enumeration only supported on Windows") } /// Get display info for protocol pub fn get_display_info() -> Result { let displays = enumerate_displays()?; let primary_id = displays .iter() .find(|d| d.is_primary) .map(|d| d.id) .unwrap_or(0); Ok(DisplayInfo { displays, primary_id, }) }