From 315ffaf144ec376cd571c71f4010bbdfb967bc5f Mon Sep 17 00:00:00 2001 From: enricobuehler Date: Wed, 22 Jul 2026 15:34:58 +0200 Subject: [PATCH] fix(linux/vdisplay): mutter virtual monitor ships the cursor as SPA_META_Cursor MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit RecordVirtual was created with cursor-mode EMBEDDED, which never delivers cursor metadata — the cursor channel had nothing to forward (client-drawn cursor invisible) and the always-built encoder blend had nothing to composite (host-drawn cursor invisible too). Metadata mode feeds both: non-forwarding sessions get the blended pointer (same pixels Mutter would have embedded), forwarding sessions strip the overlay and send shape/state. The capturer already negotiates the meta on every stream (meta_param). Co-Authored-By: Claude Fable 5 --- crates/pf-vdisplay/src/vdisplay/linux/mutter.rs | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/crates/pf-vdisplay/src/vdisplay/linux/mutter.rs b/crates/pf-vdisplay/src/vdisplay/linux/mutter.rs index 3ef19cb3..55da2043 100644 --- a/crates/pf-vdisplay/src/vdisplay/linux/mutter.rs +++ b/crates/pf-vdisplay/src/vdisplay/linux/mutter.rs @@ -46,8 +46,13 @@ const BUS_DC: &str = "org.gnome.Mutter.DisplayConfig"; /// e.g. when our virtual output is torn down — so we never persist a layout to monitors.xml). const APPLY_TEMPORARY: u32 = 1; -/// Mutter cursor mode: render the cursor into the stream (matches the KWin/gamescope backends). -const CURSOR_EMBEDDED: u32 = 1; +/// Mutter cursor mode: ship the pointer as `SPA_META_Cursor` metadata instead of burning it into +/// the frames. The capturer always negotiates the meta (pf-capture `meta_param`) and the encoder +/// blend composites it for sessions where the client does not draw the cursor itself — while a +/// cursor-forwarding session strips the overlay and sends shape/state over the cursor channel. +/// Embedded mode would leave BOTH paths blind: no metadata means nothing to forward AND nothing +/// to blend, and Mutter's own embedded painting is what the pre-channel path relied on. +const CURSOR_METADATA: u32 = 2; /// Serializes, process-wide, every Mutter operation that adds/removes a virtual monitor or applies /// a monitor configuration. Each of these makes Mutter rebuild its monitor topology, and @@ -424,7 +429,7 @@ async fn connect(mode: Mode, preferred_scale: Option) -> Result = HashMap::new(); - rec.insert("cursor-mode", Value::from(CURSOR_EMBEDDED)); + rec.insert("cursor-mode", Value::from(CURSOR_METADATA)); if mode.refresh_hz > 60 || preferred_scale.is_some() { let mut vmode: HashMap<&str, Value> = HashMap::new(); vmode.insert("size", Value::from((mode.width, mode.height)));