a7n_core/input/
geometry.rs1use a7n_proto::v1::{Edge, Landing};
4
5use super::clamp_position;
6
7#[derive(Debug, Clone, Copy, PartialEq, Eq)]
8pub struct Bounds {
9 pub x: i32,
10 pub y: i32,
11 pub width: u32,
12 pub height: u32,
13}
14
15impl Bounds {
16 #[must_use]
17 pub const fn rect(self) -> (i32, i32, u32, u32) {
18 (self.x, self.y, self.width, self.height)
19 }
20
21 #[must_use]
23 pub fn contains(self, x: i64, y: i64) -> bool {
24 x >= i64::from(self.x)
25 && y >= i64::from(self.y)
26 && x < i64::from(self.x) + i64::from(self.width)
27 && y < i64::from(self.y) + i64::from(self.height)
28 }
29}
30
31impl From<(i32, i32, u32, u32)> for Bounds {
32 fn from((x, y, width, height): (i32, i32, u32, u32)) -> Self {
33 Self {
34 x,
35 y,
36 width,
37 height,
38 }
39 }
40}
41
42pub(super) fn nearest_monitor(
43 bounds: (i32, i32, u32, u32),
44 monitors: &[Bounds],
45 x: i32,
46 y: i32,
47) -> Bounds {
48 monitors
49 .iter()
50 .filter(|m| m.width > 0 && m.height > 0)
51 .min_by_key(|m| {
52 let (px, py) = clamp_position(m.rect(), x, y);
53 let dx = i128::from(x) - i128::from(px);
54 let dy = i128::from(y) - i128::from(py);
55 dx * dx + dy * dy
56 })
57 .copied()
58 .unwrap_or_else(|| bounds.into())
59}
60
61#[must_use]
63#[allow(clippy::cast_possible_truncation)] pub fn landing_point(bounds: Bounds, monitors: &[Bounds], landing: &Landing) -> (i32, i32) {
65 let edge = Edge::try_from(landing.edge).unwrap_or(Edge::Unspecified);
66 let offset = f64::from(landing.offset.clamp(0.0, 1.0));
67 let right = bounds
68 .x
69 .saturating_add_unsigned(bounds.width.saturating_sub(1));
70 let bottom = bounds
71 .y
72 .saturating_add_unsigned(bounds.height.saturating_sub(1));
73 let x = match edge {
74 Edge::Left => bounds.x,
75 Edge::Right => right,
76 _ => (f64::from(bounds.x) + f64::from(bounds.width.saturating_sub(1)) * offset) as i32,
77 };
78 let y = match edge {
79 Edge::Top => bounds.y,
80 Edge::Bottom => bottom,
81 _ => (f64::from(bounds.y) + f64::from(bounds.height.saturating_sub(1)) * offset) as i32,
82 };
83 let horizontal = matches!(edge, Edge::Left | Edge::Right);
84 let monitor = monitors
85 .iter()
86 .filter(|m| m.width > 0 && m.height > 0)
87 .min_by_key(|m| {
88 let (px, py) = clamp_position(m.rect(), x, y);
89 let distance = if horizontal {
90 i64::from(y) - i64::from(py)
91 } else {
92 i64::from(x) - i64::from(px)
93 };
94 let boundary = match edge {
95 Edge::Left => i64::from(m.x),
96 Edge::Right => -(i64::from(m.x) + i64::from(m.width) - 1),
97 Edge::Top => i64::from(m.y),
98 Edge::Bottom => -(i64::from(m.y) + i64::from(m.height) - 1),
99 Edge::Unspecified => 0,
100 };
101 (distance.abs(), boundary)
102 })
103 .copied()
104 .unwrap_or(bounds);
105 let (px, py) = clamp_position(monitor.rect(), x, y);
106 match edge {
107 Edge::Left => (monitor.x, py),
108 Edge::Right => (
109 monitor
110 .x
111 .saturating_add_unsigned(monitor.width.saturating_sub(1)),
112 py,
113 ),
114 Edge::Top => (px, monitor.y),
115 Edge::Bottom => (
116 px,
117 monitor
118 .y
119 .saturating_add_unsigned(monitor.height.saturating_sub(1)),
120 ),
121 Edge::Unspecified => (x, y),
122 }
123}