client: screen relay switched to ximagesink

This commit is contained in:
Brad Stein 2025-12-01 00:07:09 -03:00
parent 39e4458967
commit 3aef06001b
2 changed files with 31 additions and 57 deletions

View File

@ -23,6 +23,16 @@ fn ensure_runtime_dir() {
#[tokio::main(flavor = "current_thread")]
async fn main() -> Result<()> {
// Prefer X11/Xwayland on KDE Wayland so wmctrl-based placement works
if env::var_os("WAYLAND_DISPLAY").is_some() {
if let Some(desktop) = env::var_os("XDG_CURRENT_DESKTOP") {
if desktop.to_string_lossy().to_ascii_lowercase().contains("kde") {
env::set_var("GDK_BACKEND", "x11");
env::set_var("WINIT_UNIX_BACKEND", "x11");
}
}
}
ensure_runtime_dir();
/*------------- common filter & stderr layer ------------------------*/

View File

@ -12,19 +12,6 @@ use tracing::{error, info, warn, debug};
use crate::output::{display, layout};
/* ---------- pipeline ----------------------------------------------------
* H.264/AU Decoded
* AppSrc decodebin glimagesink
* (autoplug) (overlay) |
* ----------------------------------------------------------------------*/
const PIPELINE_DESC: &str = concat!(
"appsrc name=src is-live=true format=time do-timestamp=true block=false ! ",
"queue leaky=downstream ! ",
"capsfilter caps=video/x-h264,stream-format=byte-stream,alignment=au ! ",
"h264parse disable-passthrough=true ! decodebin ! videoconvert ! ",
"glimagesink name=sink sync=false"
);
pub struct MonitorWindow {
_pipeline: gst::Pipeline,
src: gst_app::AppSrc,
@ -35,7 +22,23 @@ impl MonitorWindow {
gst::init().context("initialising GStreamer")?;
// --- Build pipeline ---------------------------------------------------
let pipeline: gst::Pipeline = gst::parse::launch(PIPELINE_DESC)?
let sink = if std::env::var("GDK_BACKEND")
.map(|v| v.contains("x11"))
.unwrap_or_else(|_| std::env::var_os("DISPLAY").is_some())
{
"ximagesink name=sink sync=false"
} else {
"glimagesink name=sink sync=false"
};
let desc = format!(
"appsrc name=src is-live=true format=time do-timestamp=true block=false ! \
queue leaky=downstream ! \
capsfilter caps=video/x-h264,stream-format=byte-stream,alignment=au ! \
h264parse disable-passthrough=true ! decodebin ! videoconvert ! {sink}"
);
let pipeline: gst::Pipeline = gst::parse::launch(&desc)?
.downcast::<gst::Pipeline>()
.expect("not a pipeline");
@ -59,6 +62,9 @@ impl MonitorWindow {
/* -------- move/resize overlay ---------------------------------- */
if let Some(sink_elem) = pipeline.by_name("sink") {
if sink_elem.find_property("window-title").is_some() {
let _ = sink_elem.set_property("window-title", &format!("Lesavka-eye-{id}"));
}
if let Ok(overlay) = sink_elem.dynamic_cast::<VideoOverlay>() {
if let Some(r) = rects.get(id as usize) {
// 1. Tell glimagesink how to crop the texture in its own window
@ -81,11 +87,6 @@ impl MonitorWindow {
run: Arc<dyn Fn(&str) -> std::io::Result<ExitStatus> + Send + Sync>,
}
let is_kde = std::env::var("XDG_CURRENT_DESKTOP")
.unwrap_or_default()
.to_ascii_lowercase()
.contains("kde");
let placer = if Command::new("swaymsg").arg("-t").arg("get_tree")
.output().is_ok()
{
@ -102,30 +103,6 @@ impl MonitorWindow {
.status()
}),
}
} else if is_kde {
// KWin DBus script: move/resize by title
Placer {
name: "kwin",
run: Arc::new(|script| {
let dbus = if Command::new("qdbus6")
.arg("--version")
.status()
.is_ok()
{
"qdbus6"
} else {
"qdbus"
};
Command::new(dbus)
.args([
"org.kde.KWin",
"/KWin",
"org.kde.KWin.evaluateScript",
script,
])
.status()
}),
}
} else {
Placer {
name: "noop",
@ -150,19 +127,6 @@ impl MonitorWindow {
x = r.x,
y = r.y,
),
// KWin JS via DBus
"kwin" => format!(
"var clients = workspace.clientList(); \
clients.forEach(function(c) {{ \
if (c.caption === \"Lesavka-eye-{id}\") {{ \
c.geometry = {{x:{x}, y:{y}, width:{w}, height:{h}}}; \
}} \
}});",
x = r.x,
y = r.y,
w = r.w,
h = r.h,
),
_ => String::new(),
};
@ -187,7 +151,7 @@ impl MonitorWindow {
});
}
}
// 3. X11 placement (kwin/i3/etc.) via wmctrl
// 3. X11 / Xwayland placement via wmctrl
else if std::env::var_os("DISPLAY").is_some() {
let title = format!("Lesavka-eye-{id}");
let w = r.w;