feat(capture): gamescope cursor via XFixes shape + QueryPointer position (Phase C)
gamescope excludes its pointer from the PipeWire node it feeds us and can't embed one either (`set_hw_cursor` is inert), so every gamescope stream was cursorless. Read the pointer from gamescope's nested Xwayland instead — XFixesGetCursorImage for shape/hotspot/visibility, core QueryPointer for position — and publish a CursorOverlay into the capturer's existing `cursor_live` slot, so the encoder blend composites it into the video exactly like the SPA_META_Cursor path. - pf-capture/src/linux/xfixes_cursor.rs (new): the XFixes reader. Connects to EVERY nested Xwayland (Gaming Mode runs one per --xwayland-count) and follows the focused one each tick (the display whose pointer moves), reading that display's own cursor shape. Un-premultiplies ARGB -> straight RGBA. Drop stops the thread. - Capturer::attach_gamescope_cursor + the PortalCapturer override spawn it into the same `cursor_live` slot; pf_vdisplay::gamescope_xwayland_cursor_targets discovers the (DISPLAY, XAUTHORITY) pairs via the GAMESCOPE_WAYLAND_DISPLAY scan. - host: SessionPlan.gamescope_cursor (set from the compositor at both resolve sites AND on a mid-stream Desktop->Gaming switch); the blend gate now builds the encoder blend for gamescope; a sibling composite arm attaches capturer.cursor() per tick for capture-mode clients (no channel needed). native NV12 is disabled for these sessions — that encode path can't blend the cursor (it assumes gamescope embeds the pointer), so we capture RGB and route to the proven CUDA VkSlotBlend / compute-CSC blend. - the pipewire thread no longer clobbers `cursor_live` with None on a buffer that carries no SPA_META_Cursor (gamescope) — that raced the XFixes writer and strobed the composited pointer on/off. On-glass (home-bazzite-2, RTX 5070 Ti, Gaming Mode): cursor visible + steady in Big Picture and CS2 menus, follows focus between the two Xwaylands, hidden in-game. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -112,6 +112,12 @@ pub struct SessionPlan {
|
||||
/// locally, so `cursor_blend` is off AND (on Windows) the capturer sets the driver's
|
||||
/// hardware cursor up via [`OutputFormat::hw_cursor`](pf_frame::OutputFormat).
|
||||
pub cursor_forward: bool,
|
||||
/// This is a gamescope session and its cursor comes from the XFixes source, NOT the
|
||||
/// (absent) `SPA_META_Cursor` (remote-desktop-sweep Phase C). Distinct from `cursor_forward`:
|
||||
/// gamescope can't embed the pointer OR carry the channel for a plain capture-mode client, so
|
||||
/// the host ALWAYS composites the XFixes-sourced cursor into the video (`cursor_blend` is set
|
||||
/// too). `build_pipeline` reads this to attach the XFixes reader to the capturer.
|
||||
pub gamescope_cursor: bool,
|
||||
}
|
||||
|
||||
impl SessionPlan {
|
||||
@@ -135,6 +141,9 @@ impl SessionPlan {
|
||||
wire_chunk: None,
|
||||
cursor_blend,
|
||||
cursor_forward,
|
||||
// Set by the resolve callers (they know the compositor); default off keeps every
|
||||
// non-gamescope plan unchanged.
|
||||
gamescope_cursor: false,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -188,9 +197,14 @@ impl SessionPlan {
|
||||
pyrowave: self.codec == crate::encode::Codec::PyroWave,
|
||||
// Producer-native NV12 (gamescope) is consumable only by the Linux Vulkan Video
|
||||
// backend — resolved HERE from the plan's codec so the capturer never reaches back
|
||||
// into encode (the same one-way edge as `gpu` above).
|
||||
// into encode (the same one-way edge as `gpu` above). BUT the native-NV12 encode path
|
||||
// has no CSC stage to fold the cursor into (it assumes gamescope embeds its pointer,
|
||||
// which it does NOT into the PipeWire node) — so a gamescope-cursor session (Phase C)
|
||||
// must capture RGB instead, routing to the compute-CSC / VkSlotBlend blend that draws
|
||||
// `frame.cursor`. Costs the RGB→NV12 CSC we'd otherwise skip; the native-NV12 cursor
|
||||
// blend is the perf-preserving follow-up.
|
||||
#[cfg(target_os = "linux")]
|
||||
nv12_native: crate::encode::linux_native_nv12_ok(self.codec),
|
||||
nv12_native: crate::encode::linux_native_nv12_ok(self.codec) && !self.gamescope_cursor,
|
||||
#[cfg(not(target_os = "linux"))]
|
||||
nv12_native: false,
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user