Add bread-screenshots + bread-capture: foundation for UI screenshot tooling
New bread-screenshots crate captures a layer-shell surface (by namespace+pid, to disambiguate from an already-running instance) or the whole focused output via grim, using bread-utils::hypr/proc. bread-utils::Monitor gains a scale field and logical_size() so output geometry accounts for HiDPI/ transform, matching breadshot's proven math. bread-utils::hypr gains find_layer() over hyprctl layers -j. bread-capture is a small orchestrator that drives an app's --screenshot mode and collects the resulting PNGs; hardcoded to breadbar's two views for now.
This commit is contained in:
parent
77bca8a1cf
commit
007082374d
7 changed files with 301 additions and 1 deletions
14
bread-screenshots/Cargo.toml
Normal file
14
bread-screenshots/Cargo.toml
Normal file
|
|
@ -0,0 +1,14 @@
|
|||
[package]
|
||||
name = "bread-screenshots"
|
||||
version.workspace = true
|
||||
edition.workspace = true
|
||||
license.workspace = true
|
||||
authors.workspace = true
|
||||
description = "Shared capture plumbing for the bread ecosystem's UI screenshot tooling: layer-surface and output geometry via Hyprland IPC, capture via grim"
|
||||
repository = "https://git.breadway.dev/Breadway/bread-ecosystem"
|
||||
keywords = ["hyprland", "wayland", "screenshot", "grim"]
|
||||
|
||||
[dependencies]
|
||||
bread-utils = { path = "../bread-utils" }
|
||||
anyhow = { workspace = true }
|
||||
tracing = { workspace = true }
|
||||
50
bread-screenshots/src/lib.rs
Normal file
50
bread-screenshots/src/lib.rs
Normal file
|
|
@ -0,0 +1,50 @@
|
|||
//! Capture primitives for the bread ecosystem's UI screenshot tooling (see
|
||||
//! `bread-capture`, the orchestrator that drives this crate's consumers).
|
||||
//!
|
||||
//! A "view" being screenshotted is either:
|
||||
//! - its own layer-shell surface (a bar, launcher, ...) — captured tightly via
|
||||
//! [`capture_layer`], geometry from `hyprctl layers`.
|
||||
//! - a transient popover/popup — not a separate layer surface under
|
||||
//! gtk4-layer-shell (it's an xdg_popup Hyprland doesn't list individually),
|
||||
//! so the only reliable capture is [`capture_output`]: whatever's currently
|
||||
//! on the focused monitor.
|
||||
|
||||
use anyhow::{bail, Context, Result};
|
||||
use std::path::Path;
|
||||
use std::time::Duration;
|
||||
|
||||
const GRIM_TIMEOUT: Duration = Duration::from_secs(5);
|
||||
|
||||
/// Capture a named layer-shell surface belonging to *this* process
|
||||
/// (`std::process::id()`), identified by its `namespace` (e.g. `"breadbar"`).
|
||||
/// Namespace alone can't identify "our own" surface when another instance
|
||||
/// under the same namespace is already running (breadbar commonly is), so
|
||||
/// this matches by pid too — see `bread_utils::hypr::find_layer`.
|
||||
pub fn capture_layer(namespace: &str, out: &Path) -> Result<()> {
|
||||
let layer = bread_utils::hypr::find_layer(namespace, std::process::id())
|
||||
.with_context(|| format!("no layer surface found for namespace={namespace}"))?;
|
||||
let geometry = format!("{},{} {}x{}", layer.x, layer.y, layer.w, layer.h);
|
||||
run_grim(&geometry, out)
|
||||
}
|
||||
|
||||
/// Capture the entire focused output (monitor). Used for views whose
|
||||
/// interesting content isn't its own layer surface — see the module doc.
|
||||
pub fn capture_output(out: &Path) -> Result<()> {
|
||||
let monitor = bread_utils::hypr::focused_monitor().context("no focused monitor found")?;
|
||||
let (w, h) = monitor.logical_size();
|
||||
let geometry = format!("{},{} {}x{}", monitor.x, monitor.y, w, h);
|
||||
run_grim(&geometry, out)
|
||||
}
|
||||
|
||||
fn run_grim(geometry: &str, out: &Path) -> Result<()> {
|
||||
if let Some(parent) = out.parent() {
|
||||
std::fs::create_dir_all(parent)
|
||||
.with_context(|| format!("creating {}", parent.display()))?;
|
||||
}
|
||||
let out_str = out.to_str().context("output path is not valid UTF-8")?;
|
||||
let result = bread_utils::proc::run("grim", &["-g", geometry, out_str], GRIM_TIMEOUT);
|
||||
if !result.success {
|
||||
bail!("grim failed for geometry {geometry}: {}", result.stderr);
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue