sni/item: wait for a fresh tray menu to actually populate before showing it - #5322
Closed
daviesjamesdaniel wants to merge 2 commits into
Closed
daviesjamesdaniel wants to merge 2 commits into
daviesjamesdaniel wants to merge 2 commits into
Conversation
…ing it A tray item's context menu (dbusmenu_gtkmenu_new()) only starts an async D-Bus fetch of its real layout - it doesn't wait for it. On the very first show for a given menu, popup_at_pointer() could run while the menu widget still had none of its real children, so GTK measured an empty menu's size for the Wayland xdg_popup positioner. That size is locked in immediately and can't grow once the real items arrive a moment later, producing a visibly mis-sized/glitched popup on first open only - any later open of the same (by-then-populated, cached) menu renders fine. Confirmed live against both a trivial static 2-item menu and a real 16-item one (Steam): the client's own "layout-updated" signal fires before the corresponding GTK widgets are packed in, and the container's "add" signal isn't a reliable substitute either - it never fired once for the 16-item case despite the menu populating correctly within 200ms. Polling get_children() directly did reliably observe the real count in both cases, so the first show now waits for that count to hold steady for a few ticks (or a fixed ceiling, for a menu that's genuinely empty or an app that never responds) before popping up. A second click landing on the same still-pending first show is guarded against with a bool - without it, that second click would see a non-null gtk_menu and pop up immediately on the still-populating menu. A pointer to the pending wait is also kept on the Item so its destructor can cancel it if the tray item disappears before it settles.
- Move the poll-wait helpers into Item as static methods (matching the existing onMenuDestroyed pattern for GObject C callbacks), instead of free functions - free functions are snake_case in this codebase, but this idiom is the established one for exactly this situation. - Give the two new Item member variables the trailing underscore the naming convention requires, and move them to the private section alongside the rest of the item's internal-only state. - Trim comments that restated the commit message rather than explaining a non-obvious invariant in the code itself. - Run clang-format.
daviesjamesdaniel
marked this pull request as ready for review
September 12, 2026 19:17
Author
|
Closing in favor of #5315, which supersedes #5293 and fixes the actual regression (missing |
4 tasks
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
What does this PR do?
Fixes a tray context menu that renders mis-sized/glitched the very first time it's opened after an app starts, but always renders correctly on any later open of the same menu.
Root cause:
dbusmenu_gtkmenu_new()(inItem::makeMenu()) only starts an async D-Bus fetch of the menu's real layout - it doesn't wait for it. On the very first show for a given item,handleClick()calledpopup_at_pointer()immediately afterward, while the menu widget could still have none of its real children. GTK measures that (empty) size for the Waylandxdg_popuppositioner, and that size is locked in immediately - it can't grow once the real items arrive a moment later. Any later open of the same, by-then-populated, cached menu renders correctly, which is why this only ever shows up on first open.Evidence
WAYLAND_DEBUG=1against unpatched Waybar, opening the same tray item's menu twice in a row - first open (glitched) vs. second open (fine):Waybar's own stderr at the moment of the glitched (50x8) open:
Confirmed this correlates exactly with the visible glitch across every run during testing: present on every glitched open, absent on every clean one.
Also confirmed live, instrumenting
Item::handleClick/makeMenudirectly, that neither of the two more "obvious" signals to wait on actually works:DbusmenuClient's own"layout-updated"signal fired whilegtk_menu->get_children().size()was still0."add"signal never fired even once against a real 16-item menu (Steam's tray menu) despite it populating correctly within 200ms - pollingget_children()directly was the only approach that reliably observed the real count in both the trivial and the 16-item case.The fix
On the first-ever show of a menu, instead of popping up immediately, poll
get_children()every 30ms and wait for the count to hold steady (unchanged) for 3 consecutive ticks before showing it, with a 2-second ceiling for a menu that's genuinely empty or an app that never responds. The polling logic treats a steady run of zero differently from a steady run of a positive count - otherwise a menu that simply hasn't gained its first item yet within 3 ticks gets shown empty, which is the same bug through a different door; only a positive, unchanging count counts toward "settled."A second click landing while a first show is still pending is guarded against (it would otherwise see a non-null
gtk_menuand pop up immediately on the same still-populating menu), and the wait is cancelled cleanly if the tray item is destroyed before it settles.Tested repeatedly, live, against both a trivial 2-item menu and Steam's real ~16-item one, across many fresh app launches, on Hyprland/Wayland. No pixman/GTK errors and no visible glitch in any run after the fix, versus a reliably reproducible one before it.
Related issues
Not a duplicate of #1149 (that's about a menu's content changing after creation going stale) - this reproduces on a menu whose content never changes at all, and the mechanism is different (initial-population timing, not a later update).
Checklist
clang-formatninja -C build)