fix: Wayland popup error (Qt.Tool instead of Qt.Popup) #4
Loading…
Add table
Add a link
Reference in a new issue
No description provided.
Delete branch "fix/wayland-popup"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
Fix: Wayland "Failed to create grabbing popup" error
The error
Root cause
The
UsagePanelusedQt.Popupwindow flags. On Wayland, popup surfaces require a transient parent window — butQSystemTrayIcondoesn't create a window we can parent to. KDE Plasma 6 runs Wayland by default, so this hits every click.The fix
Qt.PopuptoQt.Tool—Qt.Tooldoesn't use the Wayland popup surface protocol, so no transient parent neededQApplicationevent filter onTrayAppthat closes the panel when the user clicks outside it (replacesQt.Popup's automatic pointer grab, which is what Wayland was rejecting)raise_()aftershow()to ensure it appears on top45 tests still passing.
🤖 Hermes automated review: minor comments
Reviewed base
1ef78c0→ headc924e15(1 file, +27/−3).CI/CD: CI / test ✅ (55s) passed for head
c924e15— local build/test skipped per CI policy. (I also ran the suite locally: 45/45 passed in 0.29s, consistent with CI.)Core fix is correct ✅
The root-cause analysis is sound. On Wayland,
Qt.Popupcreates anxdg_popupsurface, which the protocol requires to have a transient parent (xdg-shellget_popupneeds a parentxdg_surface).QSystemTrayIcon's StatusNotifierItem is not anxdg_surfaceyou can parent to, so KDE Plasma 6 (Wayland default) rejects the grab every time. Switching toQt.Toolsidesteps the popup protocol entirely — aQt.Toolwindow is a regular toplevel — so the Wayland error disappears. This is the standard fix for this class of problem.The replacement of
Qt.Popup's automatic pointer grab with an application-leveleventFilter(main.py~TrayApp.eventFilter) is the right pattern:self._app→ receives events for all objects. ✓event.type() == event.Type.MouseButtonPressandnot self._panel.underMouse()before hiding. ✓Trueto consume the event — this is the documented Qt event-filter convention and correctly reproducesQt.Popup's "click outside closes and is swallowed" semantics. ✓QApplicationmouse events, so the filter does not interfere with_on_tray_activated/toggle behavior. ✓raise_()aftershow()is correct for aQt.Tool | Qt.WindowStaysOnTopHintwindow on both X11 and Wayland. ✓No security concerns: no secrets, no shell/eval, no network/serialization changes — purely a window-management tweak.
Minor (non-blocking)
UsagePanel.closed = Signal()is dead code —main.py:178: the signal is declared on the class but is neveremit()ed anywhere (notably not fromeventFilter's hide path, nor from acloseEventoverride) and is never connected byTrayApp. Either wire it up (self._panel.hide()→self._panel.closed.emit()) so callers can react to dismissal, or remove the declaration. As-is it's an unused attribute that suggests an API that doesn't exist.main.py:172-174docstring says "Click-outside-to-close is handled via application focus events," but the actual implementation interceptsMouseButtonPressevents (mouse-button events, notFocusIn/FocusOut/ApplicationStateChange). The mouse-press approach is arguably better than focus events on Wayland (focus events are unreliable for frameless top-level windows), so the code is fine — just update the docstring to say "mouse-button events" so it matches.eventFilterdoes not fire (tray clicks bypassQApplicationmouse dispatch), so_on_tray_activated→_show_panelcorrectly seesisVisible()and hides it. No conflict. Noting this because the two close-paths could look like a race on first read; they aren't.Verdict: Clean, well-targeted Wayland fix with correct root-cause analysis. No blocking issues — just the dead
closedsignal and a docstring nit. CI green, tests green.Automated daily review. I never merge PRs. Note: this is a PR conversation comment with file:line references, not a formal Forgejo review approval — the MCP integration cannot create inline review comments or approval states.