Skip to main content

dais_core/
monitor.rs

1//! Monitor management trait and types.
2//!
3//! Platform-specific implementations live in `dais-platform`.
4//! The trait is defined here so both platform backends and the engine can use it.
5
6/// Information about a connected display monitor.
7#[derive(Debug, Clone)]
8pub struct MonitorInfo {
9    /// Unique identifier for this monitor (platform-specific).
10    pub id: String,
11    /// Human-readable name (e.g., "DELL U2718Q").
12    pub name: String,
13    /// Position on the virtual desktop, top-left corner (pixels).
14    pub position: (i32, i32),
15    /// Physical resolution (width, height in pixels).
16    pub size: (u32, u32),
17    /// Usable work area on the virtual desktop, excluding taskbars/docks
18    /// where the platform can provide it. Stored as (x, y, width, height)
19    /// in physical pixels.
20    pub work_area: (i32, i32, u32, u32),
21    /// DPI scale factor (1.0 = 96dpi, 2.0 = Retina/HiDPI).
22    pub scale_factor: f64,
23    /// Whether this is the OS primary monitor.
24    pub is_primary: bool,
25}
26
27impl MonitorInfo {
28    /// The effective logical size (physical size / scale factor).
29    pub fn logical_size(&self) -> (f64, f64) {
30        (f64::from(self.size.0) / self.scale_factor, f64::from(self.size.1) / self.scale_factor)
31    }
32
33    /// The effective logical work area (physical work area / scale factor).
34    pub fn logical_work_area(&self) -> (f64, f64, f64, f64) {
35        (
36            f64::from(self.work_area.0) / self.scale_factor,
37            f64::from(self.work_area.1) / self.scale_factor,
38            f64::from(self.work_area.2) / self.scale_factor,
39            f64::from(self.work_area.3) / self.scale_factor,
40        )
41    }
42}
43
44/// Trait for platform-specific monitor enumeration.
45///
46/// Separate implementations are compiled via `cfg` for each platform
47/// in the `dais-platform` crate.
48pub trait MonitorManager: Send + Sync {
49    /// Enumerate all currently connected monitors.
50    fn available_monitors(&self) -> Vec<MonitorInfo>;
51
52    /// Get the primary monitor, if one is designated.
53    fn primary_monitor(&self) -> Option<MonitorInfo> {
54        self.available_monitors().into_iter().find(|m| m.is_primary)
55    }
56
57    /// Get a non-primary monitor (for audience display in dual mode).
58    fn secondary_monitor(&self) -> Option<MonitorInfo> {
59        self.available_monitors().into_iter().find(|m| !m.is_primary)
60    }
61
62    /// Find a monitor by name (for config-based assignment).
63    fn find_by_name(&self, name: &str) -> Option<MonitorInfo> {
64        self.available_monitors().into_iter().find(|m| m.name == name)
65    }
66
67    /// Find a monitor by a user-facing selector.
68    ///
69    /// Supported forms:
70    /// - Exact monitor name
71    /// - Exact monitor id
72    /// - 1-based ordinal like "1", "2", "3" using the detected monitor list order
73    fn find_by_selector(&self, selector: &str) -> Option<MonitorInfo> {
74        let monitors = self.available_monitors();
75
76        if let Ok(index) = selector.parse::<usize>()
77            && index > 0
78        {
79            return monitors.get(index - 1).cloned();
80        }
81
82        monitors.iter().find(|m| m.name == selector || m.id == selector).cloned()
83    }
84}