Qt Wayland, Supercharged
One of the key components to using a Plasma Wayland session is obviously the Qt Wayland Client module for running Qt applications in a Wayland environment. While it has been successfully deployed to millions of devices over the years, there’s still a few areas that feel like they haven’t been touched much since its inception as part of the Qt Lighthouse project, what turned into QPA, the Qt Platform Abstraction.
Improving the SHM Backing Store
Fushan Wen complained about abysmal performance when using Kolourpaint (a faithful re-creation of the original MS Paint, just better) under Wayland. Thanks to Hotspot (my favorite profiler front-end) we found a bottleneck in Qt’s Wayland SHM (Shared Memory) Backing Store, the infrastructure that provides software-rendered applications with a canvas to draw into. The way Wayland works is basically that an application creates a wl_buffer, fills it with content, and “sends” it to the compositor. Once the compositor is done processing it (e.g. after having uploaded it to the GPU) the client may re-use that buffer again.
If we pick a different buffer to draw into because we want to start drawing before the compositor is done with the previous one, any previous content needs to be transferred over since Qt expects to see the contents it drew previously. This is where we found the choke point: Even though the application reported the correct region that changed (when drawing with a brush only the new splodges of color need to be rendered after all), Qt Wayland copied the entire buffer. Therefore, I replaced the original memcpy with proper damage tracking which significantly sped up rendering. Many thanks to Ilya Fedin for continued support while implementing this.
I then did more profiling and found that when painting on a window with alpha channel, Qt first clears the region to be painted. While there’s not much we can optimize about a “fill with color” call, we can skip doing that entirely if we have just created a new buffer, like is done repeatedly as you resize the window. The new buffer is initialized with zeroes already, therefore we can just start painting on it. Last but not least, I added support for “scrolling” the backing store, which is something Qt might ask us to do when scrolling through a view, such as a text editor or terminal window, and then have the application just fill in a small gap that’s left.
Prettified “Bradient” Decoration
I very much dislike client-side decorations for their inconsistencies and burden they put on application developers but I nevertheless grew tired of seeing how hideous Qt applications looked on colleague’s computers running Gnome Shell. That is why I spent some time on making Qt’s “Bradient” decoration plug-in not stick out like a sore thumb. While it’s a massive improvement over the blue decoration Qt Wayland originally shipped, there is still a lot to be desired.
I believe it’s quite detrimental that Mutter (Gnome’s window manager and Wayland compositor) doesn’t support server-side decorations at all under Wayland. There’s plenty of applications that display non-application content and don’t care much about providing any decoration. For example, even running kwin_wayland under Gnome for development purposes gives you a window with no title bar or window border whatsoever.
Qt likewise relies on the desktop environment to provide a window frame and its drawing of client-side decorations under Wayland is if anyting a massive band-aid. Currently, Qt’s backing store just adds a window decoration around it and then returns the application a region inside the image that excludes the decoration again. It also lacks several features that one would expect from a window title bar, like double click to maximize or clicking the icon to bring up the window manager menu. In the future I am looking forward to putting the decoration into a sub-surface (or even using libdecor) which hopefully fixes many issues related to input and format handling.
To start off, the easiest change I did was using the correct mouse cursors for resizing the window. I then found that mouse input wasn’t properly translated when the window border was too thick, something that wasn’t noticeable with the default 3px border but surely would become a problem once we added a large shadow. Next, I fixed it not updating live when system colors changed, e.g. when enabling “Dark Mode”. Additionally, the title bar now includes the application name to be consistent with the title it sends to the compositor for display in task bar and window switchers.
Still, adding a proper drop shadow is work in progress: the most important part is to actually remove the window border and rounded corners altogether when the window is maximized or tiled and to adjust the button layout machinery to cope with changing margins. However, I also need to move the qt_blurImage function which creates the drop shadow texture into a place where I can use it without Qt Graphics Views or Qt Widgets (it’s currently used internally by QGraphicsBlurEffect).
Drag’n’Drop
Furthermore, I improved drag and drop handling: Qt Wayland now tries to decode URLs as UTF-8 – Chrome sometimes sends them like this – rather than just UTF-16. I also had it ignore the mysterious “DELETED” format Firefox sends that is likely a remnant from the XDnd specification. The latter fixes dropping an image to the desktop to set it as a wallpaper since Firefox doesn’t actually let anyone read this entry and Plasma gets stuck on it until it runs into a socket timeout.
The mouse now also uses the proper drag cursors. While the compositor may overrule the preferred action, we still want the default to match other desktop environments. I also fixed keyboard modifier propagation during drag and drop. While Wayland sends an explicit keyboard modifier change (Shift/Alt/Meta pressed or released), it does so after sending the actual key press. Qt on the other hand only updates its internal modifier state in response to an actual key presses. This meant that Qt doesn’t properly update its internal state when pressing a modifier when starting to drag a file and then holding Shift to initiate a “Move” operation. To fix that, Qt Wayland emits drag and drop events (and others) using the Wayland-internal modifier state rather than the one in QGuiApplication.
Discuss this post on KDE Discuss.