agentmux_launcher\wrr/
rect.rs

1// Copyright 2026, AgentMux Corp.
2// SPDX-License-Identifier: Apache-2.0
3//
4// Phase B.9.1 — rectangle math for monitor-membership classification.
5//
6// Win32 `RECT` semantics: `right` and `bottom` are one past the
7// last included pixel, so two rects intersect iff they overlap on
8// both axes after the half-open boundary check. Empty rects
9// (zero area) never intersect anything by definition.
10
11use agentmux_common::ipc::Rect;
12
13/// Phase B.9.1 — does `r` overlap with `m`? Half-open semantics.
14pub fn intersects(r: &Rect, m: &Rect) -> bool {
15    if r.left >= r.right || r.top >= r.bottom {
16        return false;
17    }
18    if m.left >= m.right || m.top >= m.bottom {
19        return false;
20    }
21    r.left < m.right && r.right > m.left && r.top < m.bottom && r.bottom > m.top
22}
23
24/// Phase B.9.1 — does `r` overlap with at least one monitor in
25/// `monitors`? Empty `monitors` returns `false` — no monitors,
26/// nothing to be "on".
27pub fn intersects_any(r: &Rect, monitors: &[Rect]) -> bool {
28    monitors.iter().any(|m| intersects(r, m))
29}
30
31#[cfg(test)]
32mod tests {
33    use super::*;
34
35    fn rect(l: i32, t: i32, r: i32, b: i32) -> Rect {
36        Rect { left: l, top: t, right: r, bottom: b }
37    }
38
39    #[test]
40    fn fully_inside_intersects() {
41        let mon = rect(0, 0, 1920, 1080);
42        let win = rect(100, 100, 800, 600);
43        assert!(intersects(&win, &mon));
44    }
45
46    #[test]
47    fn touching_edge_does_not_intersect() {
48        // Half-open: `right == left` of the next means no overlap.
49        let mon = rect(0, 0, 1920, 1080);
50        let win = rect(1920, 100, 2920, 600);
51        assert!(!intersects(&win, &mon));
52    }
53
54    #[test]
55    fn fully_off_screen_no_intersect() {
56        let mon = rect(0, 0, 1920, 1080);
57        let off = rect(-9999, -9999, -9000, -9000);
58        assert!(!intersects(&off, &mon));
59    }
60
61    #[test]
62    fn intersects_any_picks_correct_monitor() {
63        let monitors = vec![
64            rect(0, 0, 1920, 1080),       // primary
65            rect(1920, 0, 3840, 1080),    // secondary right
66        ];
67        let win = rect(2000, 100, 2500, 600);
68        assert!(intersects_any(&win, &monitors));
69    }
70
71    #[test]
72    fn empty_monitors_returns_false() {
73        let win = rect(100, 100, 200, 200);
74        assert!(!intersects_any(&win, &[]));
75    }
76
77    #[test]
78    fn empty_rect_never_intersects() {
79        let mon = rect(0, 0, 1920, 1080);
80        let zero = rect(100, 100, 100, 100);
81        assert!(!intersects(&zero, &mon));
82    }
83
84    #[test]
85    fn cross_monitor_window_intersects_one() {
86        // Window straddling two monitors picks up at least one.
87        let monitors = vec![
88            rect(0, 0, 1920, 1080),
89            rect(1920, 0, 3840, 1080),
90        ];
91        let straddle = rect(1800, 100, 2200, 600);
92        assert!(intersects_any(&straddle, &monitors));
93    }
94}