// client/src/output/layout.rs use super::display::MonitorInfo; use tracing::debug; #[derive(Clone, Copy, Debug)] pub struct Rect { pub x: i32, pub y: i32, pub w: i32, pub h: i32, } /// Compute rectangles for N video streams (all 16:9 here). pub fn assign_rectangles( monitors: &[MonitorInfo], streams: &[(&str, i32, i32)], // (name, w, h) ) -> Vec { let mut rects = vec![ Rect { x: 0, y: 0, w: 0, h: 0 }; streams.len() ]; match monitors.len() { 0 => return rects, // impossible, but keep compiler happy 1 => { // One monitor: side-by-side layout let m = &monitors[0].geometry; let total_native_width: i32 = streams.iter().map(|(_, w, _)| *w).sum(); let scale = f64::min( m.width() as f64 / total_native_width as f64, m.height() as f64 / streams[0].2 as f64, ); debug!("one-monitor scale = {}", scale); let mut x = m.x(); for (idx, &(_, w, h)) in streams.iter().enumerate() { let ww = (w as f64 * scale).round() as i32; let hh = (h as f64 * scale).round() as i32; rects[idx] = Rect { x, y: m.y(), w: ww, h: hh, }; x += ww; } } _ => { // ≥2 monitors: map 1-to-1 until we run out for (idx, stream) in streams.iter().enumerate() { if idx >= monitors.len() { break; } let m = &monitors[idx]; let geom = m.geometry; let (w, h) = (stream.1, stream.2); let scale = f64::min( geom.width() as f64 / w as f64, geom.height() as f64 / h as f64, ); debug!("monitor#{idx} scale = {scale}"); let ww = (w as f64 * scale).round() as i32; let hh = (h as f64 * scale).round() as i32; let xx = geom.x() + (geom.width() - ww) / 2; let yy = geom.y() + (geom.height() - hh) / 2; rects[idx] = Rect { x: xx, y: yy, w: ww, h: hh, }; } } } debug!("📐 final rectangles = {:?}", rects); rects }