From c8099c012538cccee63033501585578af117faf9 Mon Sep 17 00:00:00 2001 From: enricobuehler Date: Fri, 12 Jun 2026 14:08:05 +0000 Subject: [PATCH] =?UTF-8?q?fix(vdisplay/mutter):=20stop=20screencast=20bef?= =?UTF-8?q?ore=20monitor=20reconfig=20=E2=80=94=20fixes=20>60Hz=20teardown?= =?UTF-8?q?=20crash?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The high-refresh teardown SIGSEGV was caused by ApplyMonitorsConfig disabling the still-actively-captured high-refresh virtual output. Reorder teardown: Stop the screencast FIRST (Mutter removes the virtual + auto-reverts the temporary config), then re-assert the physical layout once the virtual is gone. Never reconfigure a live virtual CRTC. With this, PUNKTFUNK_MUTTER_VIRTUAL_REFRESH=1 is stable: validated at 5120x1440@240 on Mutter 50 + NVIDIA — virtual output Meta-0@240, real 240fps, gnome-shell survives back-to-back sessions + teardowns, physical (HDMI-1) restored each time. Co-Authored-By: Claude Opus 4.8 (1M context) --- crates/punktfunk-host/src/vdisplay/mutter.rs | 30 ++++++++++++-------- 1 file changed, 18 insertions(+), 12 deletions(-) diff --git a/crates/punktfunk-host/src/vdisplay/mutter.rs b/crates/punktfunk-host/src/vdisplay/mutter.rs index 4c929e8..7ec3560 100644 --- a/crates/punktfunk-host/src/vdisplay/mutter.rs +++ b/crates/punktfunk-host/src/vdisplay/mutter.rs @@ -169,15 +169,19 @@ fn session_thread(setup_tx: Sender>, stop: Arc, tokio::time::sleep(Duration::from_millis(200)).await; } - // Restore the original monitor layout (physical primary) before tearing the session down. - // (Mutter also auto-reverts the temporary config when the virtual output disappears.) + // Tear down: STOP the screencast FIRST so Mutter removes the virtual output and auto-reverts + // the temporary monitor config (physical → primary). Reconfiguring an *actively-captured* + // high-refresh virtual output via ApplyMonitorsConfig was SIGSEGVing gnome-shell on teardown, + // so we never touch the layout while the virtual output is still live. + let _ = session.rd_session.call_method("Stop", &()).await; if let Some((dc, original)) = restore { + // Let Mutter drop the virtual output, then re-assert the physical layout deterministically + // (a no-op if the temporary config already auto-reverted) — safe now: no live virtual. + tokio::time::sleep(Duration::from_millis(300)).await; if let Err(e) = apply_config(&dc, &original).await { - tracing::warn!("mutter: monitor-layout restore failed ({e:#}); Mutter reverts the temporary config on teardown"); + tracing::warn!("mutter: monitor-layout restore after stop failed ({e:#}); Mutter auto-reverts the temporary config on teardown"); } } - // Best-effort explicit teardown before the connection drops. - let _ = session.rd_session.call_method("Stop", &()).await; }); } @@ -244,11 +248,11 @@ async fn connect(mode: Mode) -> Result { .await?; // 3. The virtual monitor. By DEFAULT we let Mutter derive the refresh from the PipeWire - // framerate (it defaults the virtual monitor to **60 Hz**) — stable. PUNKTFUNK_MUTTER_VIRTUAL_REFRESH=1 - // pins the client's exact WxH@Hz via RecordVirtual's "modes" (explicit size + refresh-rate; - // Mutter ≥ 47) for true >60 Hz — BUT a high-refresh virtual CRTC has been observed to SIGSEGV - // gnome-shell on teardown (large modes, e.g. 5120×1440@240), so it is OFF by default until that - // teardown crash is resolved. + // framerate (it defaults the virtual monitor to 60 Hz) — universally safe. + // PUNKTFUNK_MUTTER_VIRTUAL_REFRESH=1 pins the client's exact WxH@Hz via RecordVirtual's "modes" + // (explicit size + refresh-rate; Mutter ≥ 47) for true >60 Hz — validated at 5120×1440@240 on + // Mutter 50 + NVIDIA. (A high-refresh virtual CRTC used to SIGSEGV gnome-shell on teardown; the + // stop-screencast-before-any-monitor-reconfig teardown below avoids that.) let mut rec: HashMap<&str, Value> = HashMap::new(); rec.insert("cursor-mode", Value::from(CURSOR_EMBEDDED)); if virtual_refresh_enabled() && mode.refresh_hz > 60 { @@ -352,8 +356,10 @@ fn virtual_primary_enabled() -> bool { } /// Opt-in: pin the virtual output to the client's exact refresh via RecordVirtual "modes" (true -/// above-60 Hz). OFF by default — a high-refresh virtual CRTC currently SIGSEGVs gnome-shell on -/// session teardown (large modes), so don't risk the host's GNOME session until that's fixed. +/// above-60 Hz). Off by default — Mutter-derived 60 Hz is safe on every host; high-refresh virtual +/// CRTCs are validated on Mutter 50 + NVIDIA but behaviour can vary, so it stays opt-in. (The +/// teardown SIGSEGV that first motivated this gate is fixed by stopping the screencast before any +/// monitor-config change.) fn virtual_refresh_enabled() -> bool { std::env::var("PUNKTFUNK_MUTTER_VIRTUAL_REFRESH") .map(|v| {