fix(qt): never let the shutdown path outlive the UI, and log the tray hop
An owner smoke reported no way to close the app but killing the process. That was not reproduced here — driven through D-Bus exactly as a panel does it, the tray's Quit exits in 0.4s and the app-menu Close with close-to-tray OFF exits in 0.5s, both code 0 — so this is insurance and instrumentation, not a claimed cure. The insurance: once the event loop is gone the process has no UI, so nothing on that path may block without a bound. The cast shutdown ran unbounded and takes a tokio Mutex the cast poll task also holds, aborted only AFTER the lock is acquired. Two seconds is the cap; a cast device that keeps playing that much longer beats an app that cannot be closed. The instrumentation: tray_qt logs when it SENDS, and nothing logged on the QML side, so "the tray does nothing" could not be told apart from "the signal arrived and QML ignored it". The three handlers now say which. That ambiguity cost a diagnosis today; one line each ends it. cc
This commit is contained in:
2 files changed
+44
-3
No files matched your search
@@ -551,10 +551,22 @@ ApplicationWindow {
|
||||
// (§2.3), so every tray click hops ksni thread -> tray_qt verb ->
|
||||
// tray_bridge::ui() -> qsignal -> here, and this block is where it finally
|
||||
// reaches one.
|
||||
// Each handler logs ONE line, and that is not debug litter: this hop is the
|
||||
// only part of the tray that Rust cannot see. `tray_qt` logs when it SENDS
|
||||
// (e.g. "[tray] quit requested"), so without a line on this side a report of
|
||||
// "the tray does nothing" cannot distinguish "the click never left ksni"
|
||||
// from "the signal arrived and QML did nothing with it" — which is exactly
|
||||
// the ambiguity that cost a diagnosis on 2026-08-04.
|
||||
Connections {
|
||||
target: QbzTray
|
||||
function onWindowShowRequested() { window.showFromTray() }
|
||||
function onWindowHideRequested() { window.hideToTray() }
|
||||
function onWindowShowRequested() {
|
||||
console.log("[qml] tray -> showFromTray")
|
||||
window.showFromTray()
|
||||
}
|
||||
function onWindowHideRequested() {
|
||||
console.log("[qml] tray -> hideToTray")
|
||||
window.hideToTray()
|
||||
}
|
||||
// The tray's left-click degenerates to present() while the mini is open
|
||||
// (§5.8.2, §13-D21): raise the MINI, and do NOT bring the main window up
|
||||
// beside it. No setWindowShown — the mini is not the main window, and
|
||||
@@ -568,7 +580,9 @@ ApplicationWindow {
|
||||
}
|
||||
}
|
||||
function onQuitRequested() {
|
||||
console.log("[qml] tray -> quit: persisting geometry")
|
||||
window.persistWindowGeometryOnExit()
|
||||
console.log("[qml] tray -> quit: calling Qt.quit()")
|
||||
Qt.quit()
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2365,9 +2365,36 @@ fn main() {
|
||||
|
||||
if let Some(app) = app.as_mut() {
|
||||
app.exec();
|
||||
// The event loop is gone: from here on the process has no UI and the
|
||||
// user cannot do anything but wait, so NOTHING on this path may block
|
||||
// without a bound.
|
||||
log::info!("[qbz-qt] event loop exited; shutting down");
|
||||
// Same reason as logout: leaving the app must stop the renderer.
|
||||
//
|
||||
// BOUNDED. This is a `block_on` on the main thread after the UI is
|
||||
// already gone, and it awaits a tokio `Mutex` that the cast poll task
|
||||
// also takes (`cast_qt.rs:1111`, aborted only AFTER the lock is
|
||||
// acquired). If anything holds that lock, the process survives with no
|
||||
// window, no tray response and no way out but `kill -9` — which is
|
||||
// exactly what an owner hit on 2026-08-04. A cast device that keeps
|
||||
// playing for two seconds longer is a far smaller failure than an app
|
||||
// that cannot be closed, so the timeout wins the tie.
|
||||
if let Some(rt) = TOKIO.get() {
|
||||
rt.block_on(async { cast_qt::service().shutdown().await });
|
||||
let stopped = rt.block_on(async {
|
||||
tokio::time::timeout(
|
||||
std::time::Duration::from_secs(2),
|
||||
cast_qt::service().shutdown(),
|
||||
)
|
||||
.await
|
||||
.is_ok()
|
||||
});
|
||||
if !stopped {
|
||||
log::warn!(
|
||||
"[qbz-qt] cast shutdown did not finish within 2s; exiting anyway \
|
||||
(a cast device may keep playing until it times out)"
|
||||
);
|
||||
}
|
||||
}
|
||||
log::info!("[qbz-qt] shutdown complete");
|
||||
}
|
||||
}
|
||||
Reference in new issue
Block a user