Today had a theme, even if I only noticed it halfway through. Over and over the desktop was doing exactly the right thing internally, and you just couldn’t tell, because nothing showed up until the next event came along. A click here, an alt-tab there, and suddenly the thing you asked for a moment ago finally happened. Bugs like that are maddening, because every log says the code ran fine.
The worst offender was the autohide dock again. I know, I wrote about this one already, and I thought it was behind me. It came back, and this time the easy explanations were all wrong. People reported it would hide when a window was maximized and then never reappear on hover. I’d reproduce it, check the state, and find everything correct: the dock was at the visible position, it was mapped, its contents were laid out, it was even rendering frames. And it was invisible.
I got tired of guessing, so I built a small thing I should have had weeks ago: a modular dev overlay that lives inside the shell, an always-on-top panel where I can browse the live widget tree, watch events in real time, and read and poke the dock’s internal variables while it misbehaves. It is rough and only for me, but it paid for itself immediately. It showed the dock surface mapped, correctly sized, on the right layer, drawing at full frame rate, and still not on screen. The only thing that ever brought it back was a full unmap and remap of the surface.
That was the real answer. On this compositor a layer-shell surface that has been slid off the screen stops getting frame callbacks, and once it is in that state a plain change, moving it back, fading it in, does not get presented until something unrelated forces the scene to recomposite, which is why an alt-tab “fixed” it every time. So the dock now hides by sliding its margin off-screen, which is a real reconfigure the compositor always presents, and reveals by doing that unmap and remap on purpose so it comes back with a fresh frame. It also only counts a maximized window if it is on the same workspace and the same monitor as that dock, so a maximized window on another desk or another screen no longer drags every dock down with it (github.com/…/issues/5, github.com/…/issues/33, github.com/…/issues/59).
Once I had the theme in my head, the next one was quick. Window actions, the Quit in the dock’s right-click menu, restoring a minimized app, the minimize and maximize and snap entries in the global menu, all did nothing until you alt-tabbed, and then fired late. They were targeting the right window, they just landed a beat behind. The cause was almost embarrassing: the little wrappers that send those window requests over Wayland never flushed the connection, so each request sat in the send buffer until the next call pushed it out. The next thing you did flushed the previous thing you asked for. One flush after each request and they are instant now (github.com/…/issues/45, github.com/…/issues/23, github.com/…/issues/30).
Then there is the one I have not fixed yet, the games that still won’t open fullscreen or take the mouse through XWayland from certain launchers (github.com/…/issues/13). Rather than install a pile of games to chase it, I wrote a tiny test program: a fullscreen Vulkan window over XWayland that paints the screen a color following the pointer, so a frozen color means no input is getting in, with a switch to mimic the way games grab the whole screen. It reproduced the bug on the first try. A normal fullscreen window works fine; an override-redirect window combined with an emulated resolution change comes back uncomposed, invisible and inputless, exactly what people see. I don’t have the compositor-side fix yet, but I can now trigger it in seconds instead of hoping a game does it, which is the hard part done.
Alongside all that, a separate strand of the day was making apps that are not built with our toolkit feel like they belong, mostly around the global menu at the top of the screen. Qt apps were showing no menu bar there at all; like the accessibility-tree trick I leaned on for app menus before, the shell now reads their menus from that tree and rebuilds them, so a Qt app’s File and Edit finally appear up top (github.com/…/issues/39). Our own Files app had the opposite problem: it exports a perfectly good menu but runs as a non-unique application that never owns its own bus name, so the shell had nowhere to look it up; it now claims that name on its main instance and the menu shows. And Qt apps using client-side decorations were getting no titlebar at all, because the setting I was handing Qt was the inverse of the one I give GTK apps, so neither the toolkit nor the compositor drew one. Lining those up means Qt windows get a titlebar like everything else.
A handful of smaller things landed too, the unglamorous polish: desktop icon names are centered instead of hugging the left, long app names in the drawer wrap onto two lines instead of getting cut off, app folders in the drawer reopen properly and Esc closes the drawer again, and preference rows in the settings sidebar finally show their title and subtitle with the right weight (github.com/…/issues/43, github.com/…/issues/46, github.com/…/issues/52).
If today taught me anything, it is that “the log says it worked” and “the user can see it worked” are two different claims, and the gap between them is where a desktop feels broken. Building a way to watch the live state instead of reading tea leaves was worth more than any single fix.
If you try Singularity and something breaks, please tell me: open an issue on GitHub or come find me on Discord. The reports from machines that are not mine are what make this real.