diff --git a/trollshell/src/modal.rs b/trollshell/src/modal.rs index 6df45d5..df4caa4 100644 --- a/trollshell/src/modal.rs +++ b/trollshell/src/modal.rs @@ -950,6 +950,15 @@ fn wire_retract_finish(revealer: >k::Revealer, key: String) { } let closed_plugin_panel = PANELS.with(|panels| { let panels = panels.borrow(); + // Post-#627, `close_all` drains every entry out of `PANELS` before + // closing any window, so a synchronous `child-revealed` emission + // during one of those closes now always lands here (this key is + // already gone). Benign: `close_all`'s own tail performs + // everything the `Some` arm below would have — + // `set_visible(false)` on a window that's being dropped anyway, + // `*current = None`, `open_state.set(false)`, and + // (unconditionally) `set_active_panel(None)` — so returning + // `false` here just skips redundant work, not a missed teardown. let Some(panel) = panels.get(&key) else { return false; }; @@ -973,12 +982,24 @@ fn wire_retract_finish(revealer: >k::Revealer, key: String) { } /// Close and remove all drawers (called before rebuilding bars on hot-plug). +/// +/// Drains `PANELS` into a local `Vec` and drops the borrow *before* calling +/// `gtk::Window::close` on each entry, rather than closing from inside the +/// `borrow_mut()` (#627). `wire_retract_finish`'s `child-revealed` handler +/// takes its own `borrow()` of the same `RefCell`; if `close()` ever +/// triggered that signal synchronously, running it under an active +/// `borrow_mut()` would panic, and a panic unwinding through a glib callback +/// aborts the process. Whether `gtk_window_close`/`gtk_revealer_unmap` can +/// emit `notify::child-revealed` synchronously is not verified either way — +/// draining first removes the dependency on that question entirely, which is +/// the point: not holding the borrow is free, so there's no need to resolve +/// it first. pub fn close_all() { - PANELS.with(|panels| { - for (_, panel) in panels.borrow_mut().drain() { - panel.window.close(); - } - }); + let panels: Vec<_> = + PANELS.with(|panels| panels.borrow_mut().drain().map(|(_, v)| v).collect()); + for panel in panels { + panel.window.close(); + } reset_drawer_open_states(); // A monitor teardown that held the open plugin panel must clear the // selection too, so the v1 "hot-unplug just closes the plugin page with the