1use std::cmp;
2
3use crate::{
4 WayshotConnection,
5 error::{Error, Result},
6 output::OutputInfo,
7};
8
9pub type FreezeCallback = Box<dyn Fn(&WayshotConnection) -> Result<LogicalRegion>>;
10
11pub enum RegionCapturer {
13 Outputs(Vec<OutputInfo>),
15 Region(LogicalRegion),
17 Freeze(FreezeCallback),
21}
22
23#[derive(Debug, Default, Copy, Clone, PartialEq, Eq, Hash)]
28pub struct LogicalRegion {
29 pub inner: Region,
30}
31
32#[derive(Debug, Copy, Clone)]
56pub struct EmbeddedRegion {
57 pub relative_to: LogicalRegion,
59 pub inner: Region,
60}
61
62#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash, Default)]
67pub struct Region {
68 pub position: Position,
70 pub size: Size,
72}
73
74#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash, Default)]
75pub struct Position {
76 pub x: i32,
78 pub y: i32,
80}
81
82#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash, Default)]
83pub struct Size {
84 pub width: u32,
86 pub height: u32,
88}
89
90impl EmbeddedRegion {
91 #[tracing::instrument(ret, level = "debug")]
97 pub fn new(viewport: LogicalRegion, relative_to: LogicalRegion) -> Option<Self> {
98 let x_relative: i32 = viewport.inner.position.x - relative_to.inner.position.x;
99 let y_relative = viewport.inner.position.y - relative_to.inner.position.y;
100
101 let x1 = cmp::max(x_relative, 0);
102 let x2 = cmp::min(
103 x_relative + viewport.inner.size.width as i32,
104 relative_to.inner.size.width as i32,
105 );
106 let width = if let Ok(width) = (x2 - x1).try_into() {
107 width
108 } else {
109 return None;
110 };
111
112 let y1 = cmp::max(y_relative, 0);
113 let y2 = cmp::min(
114 y_relative + viewport.inner.size.height as i32,
115 relative_to.inner.size.height as i32,
116 );
117 let height = if let Ok(height) = (y2 - y1).try_into() {
118 height
119 } else {
120 return None;
121 };
122
123 Some(Self {
124 relative_to,
125 inner: Region {
126 position: Position { x: x1, y: y1 },
127 size: Size { width, height },
128 },
129 })
130 }
131
132 pub fn logical(&self) -> LogicalRegion {
138 LogicalRegion {
139 inner: Region {
140 position: Position {
141 x: self.relative_to.inner.position.x + self.inner.position.x,
142 y: self.relative_to.inner.position.y + self.inner.position.y,
143 },
144 size: self.inner.size,
145 },
146 }
147 }
148}
149
150impl std::fmt::Display for EmbeddedRegion {
151 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
152 write!(
153 f,
154 "{region} relative to {relative_to}",
155 region = self.inner,
156 relative_to = self.relative_to,
157 )
158 }
159}
160
161impl std::fmt::Display for Position {
162 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
163 write!(f, "({x}, {y})", x = self.x, y = self.y,)
164 }
165}
166
167impl std::fmt::Display for Size {
168 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
169 write!(
170 f,
171 "({width}x{height})",
172 width = self.width,
173 height = self.height,
174 )
175 }
176}
177
178impl std::fmt::Display for Region {
179 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
180 write!(
181 f,
182 "{position} {size}",
183 position = self.position,
184 size = self.size,
185 )
186 }
187}
188
189impl std::fmt::Display for LogicalRegion {
190 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
191 write!(f, "{inner}", inner = self.inner)
192 }
193}
194
195impl From<&OutputInfo> for LogicalRegion {
196 fn from(output_info: &OutputInfo) -> Self {
197 LogicalRegion {
198 inner: output_info.logical_region.inner,
199 }
200 }
201}
202
203impl TryFrom<&[OutputInfo]> for LogicalRegion {
204 type Error = Error;
205
206 fn try_from(output_info: &[OutputInfo]) -> std::result::Result<Self, Self::Error> {
207 let x1 = output_info
208 .iter()
209 .map(|output| output.logical_region.inner.position.x)
210 .min()
211 .ok_or(Error::NoOutputs)?;
212 let y1 = output_info
213 .iter()
214 .map(|output| output.logical_region.inner.position.y)
215 .min()
216 .ok_or(Error::NoOutputs)?;
217 let x2 = output_info
218 .iter()
219 .map(|output| {
220 output.logical_region.inner.position.x
221 + output.logical_region.inner.size.width as i32
222 })
223 .max()
224 .ok_or(Error::NoOutputs)?;
225 let y2 = output_info
226 .iter()
227 .map(|output| {
228 output.logical_region.inner.position.y
229 + output.logical_region.inner.size.height as i32
230 })
231 .max()
232 .ok_or(Error::NoOutputs)?;
233 Ok(LogicalRegion {
234 inner: Region {
235 position: Position { x: x1, y: y1 },
236 size: Size {
237 width: (x2 - x1) as u32,
238 height: (y2 - y1) as u32,
239 },
240 },
241 })
242 }
243}