fix(linux/vdisplay): KWin virtual outputs stream above 60 Hz via sacrificial-birth renegotiation
KWin's ScreenCastStream builds its PipeWire format offer — including the maxFramerate cap it actively throttles delivery to — ONCE at stream creation, when the virtual output sits at its hardcoded birth 60 Hz. A kscreen custom-mode change afterwards updates the OUTPUT (readback says 240) but never the offer, so every consumer negotiates max=60 and the stream delivers 60 (pw-dump-verified on KWin 6.6.4). The only path that rebuilds the offer is the stream's own resize handling: a source SIZE change while recording re-runs buildFormats — picking up the output's current refresh — and renegotiates the live stream. So above 60 Hz, birth the output at a sacrificial height (+16), then install + select the real WxH@hz custom mode: the first frame recorded after the consumer connects triggers KWin's resize path, which renegotiates the live stream to WxH@hz. The capturer holds (requeues) buffers until the negotiated size matches — new expect_exact_dims plumbing VirtualOutput → registry (fresh creates only) → open_virtual_output → a self-disarming gate in the process callback — bounded by a 3 s deadline that accepts the producer's dims rather than wedging the session into the first-frame retry loop. set_custom_refresh reads back the full active mode; a size reject (pre-6.6 KWin) recreates plain at the real size @60. Every kscreen operation now addresses the output by its NUMERIC id, resolved by matching the managed name-prefix AND the just-created birth size (newest id wins): a mode-switch supersede reuses the per-slot output NAME (deliberately, for KWin's per-name config persistence) while the superseded sibling is still alive, and name addressing hit the FIRST match = the OLD output — on-glass that resized the live session's display out from under it (wrong-res/black), read the old output back as 'mode applied', and set the old output primary while the replacement starved at its birth mode. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
@@ -70,6 +70,12 @@ pub struct VirtualOutput {
|
||||
/// Linux-only (the keep-alive pool is Linux).
|
||||
#[cfg(target_os = "linux")]
|
||||
pub pool_gen: Option<u64>,
|
||||
/// The backend created the output at a SACRIFICIAL mode and the producer will renegotiate the
|
||||
/// live stream to `preferred_mode`'s dims (KWin's screencast only rebuilds its format offer —
|
||||
/// and thus its refresh cap — on a size change while recording; see kwin.rs `create`). The
|
||||
/// capturer must hold frames until that renegotiation lands. Linux-only.
|
||||
#[cfg(target_os = "linux")]
|
||||
pub expect_exact_dims: bool,
|
||||
}
|
||||
|
||||
impl VirtualOutput {
|
||||
@@ -93,6 +99,8 @@ impl VirtualOutput {
|
||||
reused_gen: None,
|
||||
#[cfg(target_os = "linux")]
|
||||
pool_gen: None,
|
||||
#[cfg(target_os = "linux")]
|
||||
expect_exact_dims: false,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user