use std::cmp;
use crate::{
WayshotConnection,
error::{Error, Result},
output::OutputInfo,
};
pub type FreezeCallback = Box<dyn Fn(&WayshotConnection) -> Result<LogicalRegion>>;
pub enum RegionCapturer {
Outputs(Vec<OutputInfo>),
Region(LogicalRegion),
Freeze(FreezeCallback),
}
#[derive(Debug, Default, Copy, Clone, PartialEq, Eq, Hash)]
pub struct LogicalRegion {
pub inner: Region,
}
#[derive(Debug, Copy, Clone)]
pub struct EmbeddedRegion {
pub relative_to: LogicalRegion,
pub inner: Region,
}
#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash, Default)]
pub struct Region {
pub position: Position,
pub size: Size,
}
#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash, Default)]
pub struct Position {
pub x: i32,
pub y: i32,
}
#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash, Default)]
pub struct Size {
pub width: u32,
pub height: u32,
}
impl EmbeddedRegion {
#[tracing::instrument(ret, level = "debug")]
pub fn new(viewport: LogicalRegion, relative_to: LogicalRegion) -> Option<Self> {
let x_relative: i32 = viewport.inner.position.x - relative_to.inner.position.x;
let y_relative = viewport.inner.position.y - relative_to.inner.position.y;
let x1 = cmp::max(x_relative, 0);
let x2 = cmp::min(
x_relative + viewport.inner.size.width as i32,
relative_to.inner.size.width as i32,
);
let width = if let Ok(width) = (x2 - x1).try_into() {
width
} else {
return None;
};
let y1 = cmp::max(y_relative, 0);
let y2 = cmp::min(
y_relative + viewport.inner.size.height as i32,
relative_to.inner.size.height as i32,
);
let height = if let Ok(height) = (y2 - y1).try_into() {
height
} else {
return None;
};
Some(Self {
relative_to,
inner: Region {
position: Position { x: x1, y: y1 },
size: Size { width, height },
},
})
}
pub fn logical(&self) -> LogicalRegion {
LogicalRegion {
inner: Region {
position: Position {
x: self.relative_to.inner.position.x + self.inner.position.x,
y: self.relative_to.inner.position.y + self.inner.position.y,
},
size: self.inner.size,
},
}
}
}
impl std::fmt::Display for EmbeddedRegion {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(
f,
"{region} relative to {relative_to}",
region = self.inner,
relative_to = self.relative_to,
)
}
}
impl std::fmt::Display for Position {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "({x}, {y})", x = self.x, y = self.y,)
}
}
impl std::fmt::Display for Size {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(
f,
"({width}x{height})",
width = self.width,
height = self.height,
)
}
}
impl std::fmt::Display for Region {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(
f,
"{position} {size}",
position = self.position,
size = self.size,
)
}
}
impl std::fmt::Display for LogicalRegion {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "{inner}", inner = self.inner)
}
}
impl From<&OutputInfo> for LogicalRegion {
fn from(output_info: &OutputInfo) -> Self {
LogicalRegion {
inner: output_info.logical_region.inner,
}
}
}
impl TryFrom<&[OutputInfo]> for LogicalRegion {
type Error = Error;
fn try_from(output_info: &[OutputInfo]) -> std::result::Result<Self, Self::Error> {
let x1 = output_info
.iter()
.map(|output| output.logical_region.inner.position.x)
.min()
.ok_or(Error::NoOutputs)?;
let y1 = output_info
.iter()
.map(|output| output.logical_region.inner.position.y)
.min()
.ok_or(Error::NoOutputs)?;
let x2 = output_info
.iter()
.map(|output| {
output.logical_region.inner.position.x
+ output.logical_region.inner.size.width as i32
})
.max()
.ok_or(Error::NoOutputs)?;
let y2 = output_info
.iter()
.map(|output| {
output.logical_region.inner.position.y
+ output.logical_region.inner.size.height as i32
})
.max()
.ok_or(Error::NoOutputs)?;
Ok(LogicalRegion {
inner: Region {
position: Position { x: x1, y: y1 },
size: Size {
width: (x2 - x1) as u32,
height: (y2 - y1) as u32,
},
},
})
}
}