use image::RgbaImage;
use libwayshot::{output::OutputInfo, WayshotConnection};
use zyris_caps::{display_scale, no_such_display, Display};
use super::internal;
const SCREENCOPY: &str = "zwlr_screencopy_manager_v1";
pub(super) fn is_available() -> bool {
let Ok(conn) = WayshotConnection::new() else { return false };
if conn.get_all_outputs().is_empty() {
return false;
}
conn.globals.contents().with_list(|globals| {
globals.iter().any(|global| global.interface == SCREENCOPY)
})
}
fn covers_origin(output: &OutputInfo) -> bool {
let region = output.logical_region.inner;
let (x, y) = (region.position.x, region.position.y);
x <= 0
&& y <= 0
&& x + region.size.width as i32 > 0
&& y + region.size.height as i32 > 0
}
fn describe(output: &OutputInfo) -> Display {
let region = output.logical_region.inner;
let factor = display_scale(match region.size.height {
0 => 1.0,
logical => output.physical_size.height as f32 / logical as f32,
});
Display {
id: output.name.clone(),
name: if output.description.is_empty() {
output.name.clone()
} else {
output.description.clone()
},
x: (region.position.x as f32 * factor).round() as i32,
y: (region.position.y as f32 * factor).round() as i32,
width: output.physical_size.width,
height: output.physical_size.height,
scale_factor: factor,
primary: covers_origin(output),
}
}
fn select<'a>(outputs: &'a [OutputInfo], wanted: Option<&str>) -> zyris::Result<&'a OutputInfo> {
let Some(wanted) = wanted else {
return outputs
.iter()
.find(|o| covers_origin(o))
.or_else(|| outputs.first())
.ok_or_else(|| internal("no displays are attached"));
};
outputs
.iter()
.find(|o| o.name == wanted)
.or_else(|| outputs.iter().find(|o| o.description == wanted))
.ok_or_else(|| no_such_display(wanted))
}
pub(super) fn displays() -> zyris::Result<Vec<Display>> {
let conn = WayshotConnection::new().map_err(internal)?;
Ok(conn.get_all_outputs().iter().map(describe).collect())
}
pub(super) fn capture(display: Option<&str>) -> zyris::Result<(String, RgbaImage)> {
let conn = WayshotConnection::new().map_err(internal)?;
let outputs = conn.get_all_outputs();
let output = select(outputs, display)?;
let image = conn.screenshot_single_output(output, false).map_err(internal)?;
Ok((output.name.clone(), image.into_rgba8()))
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn saying_yes_here_means_a_capture_will_work() {
if std::env::var_os("WAYLAND_DISPLAY").is_none() {
return;
}
if is_available() {
let taken = capture(None);
assert!(
taken.is_ok(),
"this session advertises {SCREENCOPY} and `is_available` said yes, but capturing \
failed with {:?} — the probe and the capture disagree, which is the state that \
made every screenshot on GNOME fail",
taken.err()
);
}
}
}