From dd5848fb96853a2c144af246475587ad09f1aefa Mon Sep 17 00:00:00 2001 From: clover caruso Date: Thu, 1 Oct 2026 12:37:40 -0700 Subject: [PATCH] fix: quitting on Wayland no longer segfaults The window state outlived winit's event loop, which disconnects the Wayland display when run_app returns. Dropping the state afterwards made the Wayland clipboard's worker thread destroy its proxies on the freed display (wl_proxy_destroy in smithay-clipboard), so every quit on GNOME and KDE Wayland ended in SIGSEGV. The state now drops in exiting(), while the connection is still open. Reproduced and verified on Fedora Silverblue 44 (GNOME 50) and Kinoite 44 (Plasma 6.6), x86_64. Fixes #54. Assisted-by: claude-opus-5.5 --- crates/snowbound/src/main.rs | 20 ++++++++++++++------ 1 file changed, 14 insertions(+), 6 deletions(-) diff --git a/crates/snowbound/src/main.rs b/crates/snowbound/src/main.rs index d9730fe23a96a4b10636f703d7f64c8833876b52..b502923934945cfba9ffb8425d6a392291144665 100644 --- a/crates/snowbound/src/main.rs +++ b/crates/snowbound/src/main.rs @@ -342,6 +342,8 @@ struct App { /// Files and folders to open, as File, Open does, once the window opens. opening: Vec, state: Option, + /// The staged update to swap in once the app has quit. + restart: Option, startup_error: Option>, } @@ -5792,6 +5794,15 @@ impl ApplicationHandler for App { .min(); event_loop.set_control_flow(next.map_or(ControlFlow::Wait, ControlFlow::WaitUntil)); } + + /// Drops the window's state while the event loop still holds the Wayland connection the + /// clipboard's thread and the surface use; `run_app` closes it on returning. + fn exiting(&mut self, _: &ActiveEventLoop) { + self.restart = self + .state + .take() + .and_then(|state| state.updates.restarting()); + } } fn write_png(path: &Path, size: [u32; 2], pixels: &[u8]) -> Result<(), Box> { @@ -6094,15 +6105,12 @@ fn launch() -> Result<(), Box> { substitutes, screenshot, state: None, + restart: None, startup_error: None, }; event_loop.run_app(&mut app)?; - let restart = app - .state - .as_ref() - .and_then(|state| state.updates.restarting()); - if let Some(staged) = restart { - update::relaunch(&staged)?; + if let Some(staged) = &app.restart { + update::relaunch(staged)?; } app.startup_error.map_or(Ok(()), Err) } -- 2.54.0