use std::path::PathBuf;
use anyhow::{Context, Result, bail};
use clap::{ArgGroup, Parser};
use latchshot::output::{Target, notify};
use latchshot::overlay::select;
use latchshot::{CaptureBackend, Compositor, Selection, SelectionResult};
use log::info;
#[derive(Parser)]
#[command(version, about)]
#[command(group(ArgGroup::new("destination").multiple(false)))]
#[allow(clippy::struct_excessive_bools)]
struct Args {
#[arg(short, long, value_name = "PATH", group = "destination")]
output: Option<PathBuf>,
#[arg(long, group = "destination")]
stdout: bool,
#[arg(short, long, group = "destination")]
clipboard: bool,
#[arg(long, conflicts_with_all = ["output", "stdout", "clipboard"])]
windows: bool,
#[arg(long)]
no_animation: bool,
#[arg(long)]
compositor: Option<Compositor>,
#[arg(long)]
capture: Option<CaptureBackend>,
}
fn main() -> Result<()> {
env_logger::Builder::from_env(env_logger::Env::default().default_filter_or("info")).init();
let args = Args::parse();
let compositor = args
.compositor
.or_else(Compositor::detect)
.context("failed to detect a supported compositor; pass --compositor to override")?;
info!("compositor: {compositor}");
let scene = compositor.connect()?.scene()?;
info!(
"scene: {} outputs, {} windows",
scene.outputs.len(),
scene.windows.len()
);
if args.windows {
println!("{}", serde_json::to_string_pretty(&scene).unwrap());
return Ok(());
}
if scene.outputs.is_empty() {
bail!("the compositor reported no active outputs");
}
let capture = args
.capture
.or_else(CaptureBackend::detect)
.context("failed to detect a supported capture backend; pass --capture to override")?;
info!("capture backend: {capture}");
let frame = capture.connect()?.capture(&scene)?;
let (result, frame) = select(scene, frame, !args.no_animation)?;
let selection = match result {
SelectionResult::Selected(selection) => selection,
SelectionResult::Cancelled => return Ok(()),
};
let geometry = match selection {
Selection::Window(geometry) | Selection::Region(geometry) => geometry,
};
let image = frame.crop(geometry);
let (width, height) = image.dimensions();
let target = if args.stdout {
Target::Stdout
} else if let Some(path) = args.output {
Target::File(path)
} else {
Target::Clipboard
};
target.write(&image)?;
match target {
Target::File(path) => notify(&format!("Screenshot saved to {}", path.display())),
Target::Clipboard => notify(&format!("Copied {width}×{height} screenshot to clipboard")),
Target::Stdout => {}
}
Ok(())
}