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}