authorgravatar for git@paperclover.netclover caruso <git@paperclover.net> 2026-10-01 12:37:40-07:00
committergravatar for git@paperclover.netclover caruso <git@paperclover.net> 2026-10-01 16:46:23-07:00
logdd5848fb96853a2c144af246475587ad09f1aefa
tree1dff7a08ba3bde39bd1a66692ba519b12ff96f8c
parent0108e94dff934720a1cd09e3931f16b9a710936c
signature Signed by SSH key SHA256:52mNGHRsVFBDED9IAX5pe+LRWUefqTbxEReunq21QvU

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

1 files changed, 14 insertions(+), 6 deletions(-)

crates/snowbound/src/main.rs+14-6
......@@ -342,6 +342,8 @@ struct App {
342342 /// Files and folders to open, as File, Open does, once the window opens.
343343 opening: Vec<PathBuf>,
344344 state: Option<State>,
345 /// The staged update to swap in once the app has quit.
346 restart: Option<PathBuf>,
345347 startup_error: Option<Box<dyn Error>>,
346348}
347349
......@@ -5792,6 +5794,15 @@ impl ApplicationHandler<UserEvent> for App {
57925794 .min();
57935795 event_loop.set_control_flow(next.map_or(ControlFlow::Wait, ControlFlow::WaitUntil));
57945796 }
5797
5798 /// Drops the window's state while the event loop still holds the Wayland connection the
5799 /// clipboard's thread and the surface use; `run_app` closes it on returning.
5800 fn exiting(&mut self, _: &ActiveEventLoop) {
5801 self.restart = self
5802 .state
5803 .take()
5804 .and_then(|state| state.updates.restarting());
5805 }
57955806}
57965807
57975808fn write_png(path: &Path, size: [u32; 2], pixels: &[u8]) -> Result<(), Box<dyn Error>> {
......@@ -6094,15 +6105,12 @@ fn launch() -> Result<(), Box<dyn Error>> {
60946105 substitutes,
60956106 screenshot,
60966107 state: None,
6108 restart: None,
60976109 startup_error: None,
60986110 };
60996111 event_loop.run_app(&mut app)?;
6100 let restart = app
6101 .state
6102 .as_ref()
6103 .and_then(|state| state.updates.restarting());
6104 if let Some(staged) = restart {
6105 update::relaunch(&staged)?;
6112 if let Some(staged) = &app.restart {
6113 update::relaunch(staged)?;
61066114 }
61076115 app.startup_error.map_or(Ok(()), Err)
61086116}