capture_loop: honour ENBOXER_ENABLE_TOPLEVEL via capture_toplevel (Bug #8)

Bug from Grok round-1 #8. capture_loop hard-coded grim and ignored the
toplevel-export gate. If the operator set ENBOXER_ENABLE_TOPLEVEL=1
they got no effect at all -- the loop just kept grimming the source
rect.

Fix: when the gate is set, build a synthetic Client (the hit has only
the source rect; pick_output_for queries live monitors to find one
covering the rect) and try capture_toplevel first. On success show
the frame; on failure fall through to grim so the operator at least
sees the visible background rather than an empty frame.

cargo test 96+/0; clippy clean.
This commit is contained in:
en 2026-09-16 08:04:14 +02:00
parent a36443bd86
commit 5da77b28b3

View File

@ -330,6 +330,30 @@ pub async fn capture_loop(rx: watch::Receiver<Vec<FeedHit>>, hub: Arc<OverlayHub
for f in feeds {
let dest = frame_path(&f.name);
let (x, y, w, h) = f.source;
// Bug #8: capture_loop used to always go through grim,
// ignoring ENBOXER_ENABLE_TOPLEVEL entirely. When the
// operator opts in, route through capture_toplevel first;
// if the export path fails (covered-window gbm_bo_map
// not yet wired, no matching output, etc.) the grim
// fallback below still produces a frame.
if toplevel_enabled() {
let transient = crate::hypr::Client {
address: format!("hit:{}", f.name),
class: "enboxer-vfx".into(),
title: f.name.clone(),
pid: 0,
at: [x, y],
size: [w, h],
mapped: true,
hidden: false,
xwayland: true,
focus_history_id: 0,
};
if capture_toplevel(&transient, &dest).await.is_ok() {
let _ = hub.show_frame(&f.name, &dest).await;
continue;
}
}
if capture_region(x, y, w, h, &dest).await.is_ok() {
let _ = hub.show_frame(&f.name, &dest).await;
}