Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
19 changes: 13 additions & 6 deletions trollshell/src/modal.rs
Original file line number Diff line number Diff line change
Expand Up @@ -984,21 +984,28 @@ fn wire_retract_finish(revealer: &gtk::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
/// `gtk::Window::destroy` on each entry, rather than destroying from inside
/// the `borrow_mut()` (#627). `wire_retract_finish`'s `child-revealed`
/// handler takes its own `borrow()` of the same `RefCell`; if `destroy()`
/// 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
/// aborts the process. Whether `gtk_window_destroy`/`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.
///
/// Uses `destroy()`, not `close()` (#632): a drawer the user never opened on
/// this monitor is still unrealized when a hot-plug tears it down, and
/// `gtk_window_close` is a *request* routed through `close-request` that does
/// not drop GTK's internal toplevel reference — `gtk_window_destroy` is the
/// call documented to do that, and unlike `close()` it can't be vetoed by a
/// future `close-request` handler.
pub fn close_all() {
let panels: Vec<_> =
PANELS.with(|panels| panels.borrow_mut().drain().map(|(_, v)| v).collect());
for panel in panels {
panel.window.close();
panel.window.destroy();
}
reset_drawer_open_states();
// A monitor teardown that held the open plugin panel must clear the
Expand Down
15 changes: 11 additions & 4 deletions trollshell/src/overlays/frame.rs
Original file line number Diff line number Diff line change
Expand Up @@ -183,15 +183,22 @@ pub fn install(monitor: &Monitor, bar: &BarHandle) {
/// a vanished output's frame window + raw subscription don't linger (the
/// subscription has no `WeakRef` safety net, so it must be aborted explicitly —
/// mirrors `sidebar::close_all`).
///
/// Tears down with `destroy()`, not `close()` (#632): a frame overlay that
/// never showed a border on this monitor is still unrealized, and `close()`
/// neither destroys an unrealized window nor drops GTK's internal toplevel
/// reference — only `destroy()` does, and it can't be vetoed by a
/// `close-request` handler.
pub fn close_all() {
FRAMES.with(|map| {
for (_, view) in map.borrow_mut().drain() {
// Abort the raw tick-loop first so it can't queue another draw
// into the surface we're about to close, then close the window.
// The `bind_visible` apply-loop rides on the #224/#243 WeakRef
// fix: it frees itself on its next emission once the window drops.
// into the surface we're about to destroy, then destroy the
// window. The `bind_visible` apply-loop rides on the #224/#243
// WeakRef fix: it frees itself on its next emission once the
// window drops.
view.sidebar_sub.abort();
view.window.close();
view.window.destroy();
}
});
}
Expand Down
8 changes: 7 additions & 1 deletion trollshell/src/overlays/notifications.rs
Original file line number Diff line number Diff line change
Expand Up @@ -148,10 +148,16 @@ pub fn install(monitor: &Monitor) {
/// are left running: they route by connector on each emission, so a fresh
/// `install` re-keys the map and they self-heal. `SUBS_INSTALLED` therefore
/// stays set — subscriptions wire exactly once for the process lifetime.
///
/// Tears down with `destroy()`, not `close()` (#632): a toast surface that
/// never showed a notification on this monitor is still unrealized, and
/// `close()` neither destroys an unrealized window nor drops GTK's internal
/// toplevel reference — only `destroy()` does, and it can't be vetoed by a
/// `close-request` handler.
pub fn close_all() {
TOAST_WINDOWS.with(|map| {
for (_, view) in map.borrow_mut().drain() {
view.window.close();
view.window.destroy();
}
});
}
Expand Down
10 changes: 8 additions & 2 deletions trollshell/src/overlays/osd.rs
Original file line number Diff line number Diff line change
Expand Up @@ -189,21 +189,27 @@ pub fn install(monitor: &Monitor) {
/// focused-output) keep running: they route by connector each emission, so a
/// fresh `install` re-keys the map and they self-heal. `SUBS_INSTALLED` stays
/// set so they wire exactly once for the process lifetime.
///
/// Tears down with `destroy()`, not `close()` (#632): an OSD that never
/// popped on this monitor is still unrealized, and `close()` neither
/// destroys an unrealized window nor drops GTK's internal toplevel
/// reference — only `destroy()` does, and it can't be vetoed by a
/// `close-request` handler.
pub fn close_all() {
OSDS.with(|map| {
for (_, view) in map.borrow_mut().drain() {
if let Some(sub) = view.drawer_sub.borrow_mut().take() {
sub.abort();
}
// Cancel pending timers so they can't fire (holding a live `Rc`
// clone of the view) into a closed window after teardown.
// clone of the view) into a destroyed window after teardown.
if let Some(id) = view.timeout.take() {
id.remove();
}
if let Some(id) = view.fade_out_timeout.take() {
id.remove();
}
view.window.close();
view.window.destroy();
}
});
}
Expand Down
7 changes: 6 additions & 1 deletion trollshell/src/overlays/sidebar.rs
Original file line number Diff line number Diff line change
Expand Up @@ -591,6 +591,11 @@ fn wire_escape(window: &gtk::Window, monitor: Monitor) {
/// Close every sidebar surface and drop the per-monitor entries. Called
/// before rebuilding bars on hot-plug, so stale layer-shell windows don't
/// linger after a monitor disappears.
///
/// Tears down with `destroy()`, not `close()` (#632): a sidebar never opened
/// on this monitor is still unrealized, and `close()` neither destroys an
/// unrealized window nor drops GTK's internal toplevel reference — only
/// `destroy()` does, and it can't be vetoed by a `close-request` handler.
pub fn close_all() {
PANELS.with(|panels| {
for (key, panel) in panels.borrow_mut().drain() {
Expand All @@ -616,7 +621,7 @@ pub fn close_all() {
id.remove();
}
panel.open_state.set(false);
panel.window.close();
panel.window.destroy();
}
});
// SIDEBAR_OPEN is keyed per-monitor and deliberately survives a rebuild
Expand Down