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