bread-capture: isolate captures in a throwaway nested Hyprland instance
Captures now run inside a dedicated nested Hyprland session by default (--no-isolate to opt out), so nothing on the operator's live desktop can leak into a screenshot and the capture never flashes across their screen either. The nested instance nests as a Wayland client of the outer session (true headless was ruled out empirically: this machine's real GPU/output is already claimed by the live session, and only one process can hold logind's seat at a time), gets floated/exact-resized/focused via one-shot outer-session hyprctl dispatches targeted by pid, and has Hyprland's default background/ logo and startup warning overlays disabled via config so captures come out clean. Focusing turned out to be load-bearing, not cosmetic: an occluded nested window never gets frame callbacks from the outer compositor, so grim run inside it hangs forever waiting on a ready event that never comes.
This commit is contained in:
parent
eb090b198f
commit
bcd57b7b54
2 changed files with 272 additions and 0 deletions
|
|
@ -6,6 +6,15 @@
|
|||
//! hardcoded view list, and a flat output directory — no versioned
|
||||
//! `screenshots/vX.Y.Z/latest` structure or manifest file yet, since those
|
||||
//! only earn their complexity once more apps are wired up.
|
||||
//!
|
||||
//! By default every capture runs inside a throwaway nested Hyprland instance
|
||||
//! (see [`isolation`]) rather than the operator's live desktop, so another
|
||||
//! window (or their own differently-themed real bar) can't leak into a
|
||||
//! capture. `--no-isolate` skips that and captures directly against whatever
|
||||
//! session bread-capture itself is running in — useful for debugging the
|
||||
//! capture sequence itself, since you can then actually watch it happen.
|
||||
|
||||
mod isolation;
|
||||
|
||||
use anyhow::Result;
|
||||
use clap::Parser;
|
||||
|
|
@ -29,11 +38,31 @@ struct Cli {
|
|||
/// Directory to write captured PNGs into.
|
||||
#[arg(long, default_value = "./screenshots")]
|
||||
out_dir: PathBuf,
|
||||
|
||||
/// Capture directly against the current session instead of a nested,
|
||||
/// throwaway Hyprland instance. Off by default so captures can't pick up
|
||||
/// whatever else is on the operator's desktop.
|
||||
#[arg(long)]
|
||||
no_isolate: bool,
|
||||
|
||||
/// Width of the isolated session's capture canvas.
|
||||
#[arg(long, default_value_t = 1920)]
|
||||
isolate_width: u32,
|
||||
|
||||
/// Height of the isolated session's capture canvas.
|
||||
#[arg(long, default_value_t = 1080)]
|
||||
isolate_height: u32,
|
||||
}
|
||||
|
||||
fn main() -> Result<()> {
|
||||
let cli = Cli::parse();
|
||||
|
||||
let _isolation = if cli.no_isolate {
|
||||
None
|
||||
} else {
|
||||
Some(isolation::Isolation::start(cli.isolate_width, cli.isolate_height)?)
|
||||
};
|
||||
|
||||
let mut failed = false;
|
||||
for (view, filename) in BREADBAR_TARGETS {
|
||||
let out_path = cli.out_dir.join(filename);
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue