a lightweight, interval-based utility to combat digital strain through "Ma" (intentional pauses) for the eyes and body.
0
fork

Configure Feed

Select the types of activity you want to include in your feed.

feat(overlay): add monitor enumeration with fallback and tests

+116
+1
src/overlay/mod.rs
··· 1 + pub mod monitors; 1 2 pub mod password; 2 3 3 4 use std::time::Duration;
+115
src/overlay/monitors.rs
··· 1 + /// Physical position and size of one monitor, in logical pixels. 2 + #[derive(Debug, Clone, PartialEq)] 3 + pub struct MonitorInfo { 4 + pub x: i32, 5 + pub y: i32, 6 + pub width: u32, 7 + pub height: u32, 8 + pub scale_factor: f32, 9 + pub is_primary: bool, 10 + pub name: String, 11 + } 12 + 13 + /// Returns all connected monitors. Falls back to a single zeroed entry so 14 + /// callers always get at least one monitor even if enumeration fails. 15 + pub fn enumerate() -> Vec<MonitorInfo> { 16 + match enumerate_impl() { 17 + Ok(v) if !v.is_empty() => v, 18 + Ok(_) => { 19 + log::warn!("display-info returned no monitors; using single-monitor fallback"); 20 + fallback() 21 + } 22 + Err(e) => { 23 + log::warn!("monitor enumeration failed ({e}); using single-monitor fallback"); 24 + fallback() 25 + } 26 + } 27 + } 28 + 29 + fn enumerate_impl() -> anyhow::Result<Vec<MonitorInfo>> { 30 + let displays = display_info::DisplayInfo::all() 31 + .map_err(|e| anyhow::anyhow!("DisplayInfo::all failed: {e:?}"))?; 32 + Ok(displays 33 + .into_iter() 34 + .map(|d| MonitorInfo { 35 + x: d.x, 36 + y: d.y, 37 + width: d.width, 38 + height: d.height, 39 + scale_factor: d.scale_factor, 40 + is_primary: d.is_primary, 41 + name: d.name, 42 + }) 43 + .collect()) 44 + } 45 + 46 + fn fallback() -> Vec<MonitorInfo> { 47 + vec![MonitorInfo { 48 + x: 0, 49 + y: 0, 50 + width: 1920, 51 + height: 1080, 52 + scale_factor: 1.0, 53 + is_primary: true, 54 + name: "fallback".to_string(), 55 + }] 56 + } 57 + 58 + #[cfg(test)] 59 + pub fn from_raw(infos: Vec<(i32, i32, u32, u32)>) -> Vec<MonitorInfo> { 60 + infos 61 + .into_iter() 62 + .enumerate() 63 + .map(|(i, (x, y, w, h))| MonitorInfo { 64 + x, 65 + y, 66 + width: w, 67 + height: h, 68 + scale_factor: 1.0, 69 + is_primary: i == 0, 70 + name: format!("monitor-{i}"), 71 + }) 72 + .collect() 73 + } 74 + 75 + #[cfg(test)] 76 + mod tests { 77 + use super::*; 78 + 79 + #[test] 80 + fn single_monitor() { 81 + let monitors = from_raw(vec![(0, 0, 1920, 1080)]); 82 + assert_eq!(monitors.len(), 1); 83 + assert!(monitors[0].is_primary); 84 + assert_eq!(monitors[0].width, 1920); 85 + } 86 + 87 + #[test] 88 + fn dual_monitor_side_by_side() { 89 + let monitors = from_raw(vec![(0, 0, 1920, 1080), (1920, 0, 2560, 1440)]); 90 + assert_eq!(monitors.len(), 2); 91 + assert_eq!(monitors[0].x, 0); 92 + assert_eq!(monitors[1].x, 1920); 93 + assert!(monitors[0].is_primary); 94 + assert!(!monitors[1].is_primary); 95 + } 96 + 97 + #[test] 98 + fn triple_monitor_mixed_layout() { 99 + let monitors = from_raw(vec![ 100 + (0, 0, 1920, 1080), 101 + (1920, 0, 2560, 1440), 102 + (-1920, 0, 1920, 1080), 103 + ]); 104 + assert_eq!(monitors.len(), 3); 105 + // Left monitor has negative x (left of primary) 106 + assert_eq!(monitors[2].x, -1920); 107 + } 108 + 109 + #[test] 110 + fn fallback_produces_one_entry() { 111 + let f = fallback(); 112 + assert_eq!(f.len(), 1); 113 + assert_eq!(f[0].width, 1920); 114 + } 115 + }