use std::collections::HashSet;
use anyhow::{Context, Result, anyhow, bail};
use nalgebra::{Matrix3, Matrix3x2, Scale2, Translation2, Vector2};
use nix::libc::pid_t;
use pinenote_service::types::Rect;
use swayipc_async::{Connection, Node, Output, Rect as SwayRect};
use super::SwayWindow;
struct StandardNodeIterator<'a> {
queue: Vec<&'a Node>,
}
impl<'a> Iterator for StandardNodeIterator<'a> {
type Item = &'a Node;
fn next(&mut self) -> Option<&'a Node> {
match self.queue.pop() {
None => None,
Some(node) => {
self.queue.extend(node.nodes.iter());
Some(node)
}
}
}
}
fn iter_standard<'a>(node: &'a Node) -> StandardNodeIterator<'a> {
StandardNodeIterator { queue: vec![node] }
}
pub(super) fn get_all_windows_and_app(
workspace: &Node,
transform: &Matrix3<f64>,
) -> (HashSet<pid_t>, Vec<SwayWindow>) {
let mut floating_idx = 1;
workspace
.floating_nodes
.iter()
.chain(iter_standard(workspace))
.filter_map(|n| {
SwayWindow::try_from(n).ok().map(|n| {
let area = apply_transform(n.area, transform);
if n.floating {
let z_index = floating_idx;
floating_idx += 1;
SwayWindow { z_index, area, ..n }
} else {
SwayWindow { area, ..n }
}
})
})
.map(|w| (w.pid, w))
.collect()
}
pub(super) async fn get_output(ipc: &mut Connection, name: &str) -> Result<Output> {
ipc.get_outputs()
.await
.context("Failed to retrieve outputs")?
.into_iter()
.find(|o| o.name.as_str() == name)
.ok_or(anyhow!("Failed to find output '{}'", name))
}
pub(super) fn output_to_transform(output: &Output) -> Result<Matrix3<f64>> {
let scale = output
.scale
.ok_or(anyhow!("Could not get output scale"))
.and_then(|s| {
if s == -1.0 {
Err(anyhow!("Output is inactive"))
} else {
Ok(Scale2::new(s, s))
}
})?;
let SwayRect {
x,
y,
width,
height,
..
} = output.rect;
let rel_to_abs = Translation2::new(-x as f64, -y as f64);
let iso = match output
.transform
.as_deref()
.ok_or(anyhow!("Bad transform"))?
{
"normal" => nalgebra::Isometry2::identity(),
"90" => nalgebra::Isometry2::new(Vector2::new(height as f64, 0.0), 90_f64.to_radians()),
"180" => nalgebra::Isometry2::new(
Vector2::new(width as f64, height as f64),
180_f64.to_radians(),
),
"270" => nalgebra::Isometry2::new(Vector2::new(0f64, width as f64), 270_f64.to_radians()),
_ => {
bail!("Unsupported transform")
}
};
let transform = scale.to_homogeneous() * iso.to_homogeneous() * rel_to_abs.to_homogeneous();
Ok(transform)
}
pub(super) fn apply_transform(rect: Rect, transform: &Matrix3<f64>) -> Rect {
let Rect { x1, y1, x2, y2 } = rect;
let r = (transform * Matrix3x2::new(x1 as f64, x2 as f64, y1 as f64, y2 as f64, 1_f64, 1_f64))
.map(|f| f as i32);
let mut xs = [*r.index((0, 0)), *r.index((0, 1))];
let mut ys = [*r.index((1, 0)), *r.index((1, 1))];
xs.sort();
ys.sort();
Rect {
x1: xs[0],
y1: ys[0],
x2: xs[1],
y2: ys[1],
}
}