On the Road to Plasma 6, Vol. Ⅲ
Another month, another Plasma 6 update. I’ve been pretty busy during the past weeks, mostly further improving the Wayland session, fractional scaling, and dealing with Qt bugs. Working under the hood like this is tremendously important albeit somewhat ungrateful when there aren’t any pretty pictures to show.
A key reason for being able to work so efficiently on kwin_wayland nowadays is that our apps can survive a restart of the Wayland compositor. I can just recompile KWin and restart it like I could on X11. Check out David’s blog post to learn more, including some spectacular video demos, and things we could do with that capability in the future!
I’m a huge fan of screen edges, so I have my bottom left, bottom center, and bottom right edge configured to trigger the window overview, KRunner, and peek at desktop, respectively. I noticed that under Wayland while selecting loads of text and bumping into the bottom of the screen, expecting the view to scroll, the screen edge triggered instead, interrupting the selection operation. This was fixed by simply checking whether the mouse is currently pressed when triggering the edge. Of course, windows can still be moved past the sides to switch desktops. Unfortunately, this also means that an edge cannot be triggered by dragging a file. This never really worked on X either, and the overview effect isn’t prepared to handle drag events anyway, but in the future I’d really like to be able to start dragging a file, drag it into the overview effect, and drop it on another window.
Speaking of drag and drop, I also fixed the mouse cursor being stuck after dropping a file. Qt has an “override cursor” that is set to a hand (or the like) while dragging but the code for resetting it wasn’t called from the Wayland drag handler. Additionally, I made child surface positioning in KWin more spec-compliant. Using an XDG Positioner, a client can specify the relative position of a popup window and define rules for how it should be placed when there isn’t enough room (for instance, flip it to the other side or just move it around slightly) and whether those rules should be applied once or continuously. As a result, GTK fly-outs dodge screen edges while moving around, whereas context menus in Qt apps just stick to their parent window stoically.
Regarding mouse cursors, there’s now a cursor-shape protocol which lets applications set a cursor by name, e.g. crosshair, wait, col_resize, as opposed to painting their own cursor surface. I never understood the original design decision behind requiring clients to know how to parse Xcursor images, pick the right cursor theme, size, etc. There are sure use cases for custom cursor pixmaps but your run-of-the-mill desktop app probably just wants a pointer and an IBeam. With cursor-shape, theme handling is delegated to the compositor which can then load the correct theme once and re-use it for all supporting clients.
I fixed a bug in the QtWayland implementation where switching from shape cursors to bitmap cursors didn’t work as could be observed with Kolourpaint’s tool cursors. Moreover, Qt no longer allocates a cursor surface at all if the app never used a bitmap cursor. While we switched the default modifier key for moving windows from Alt to Meta to free Alt for applications like Blender, two decades of muscle memory aren’t easily retrained, so I kept it as it was before. Unfortunately, this meant that during Alt+Tab it would move the window grid around instead of interacting with it since “I am pressing Alt”. As a remedy, KWin now checks whether a window is movable before stealing mouse events for initiating an unrestricted move operation.
I have always been an opponent to fractional scaling – there are no “half pixels”. Nevertheless, I have now switched my 14″ laptop to use 175% scaling and naturally began fixing all the bugs. Wayland core protocol only supports integer scaling, a client can specify whether a buffer is at 1x or 2x (or 3x, or…) scale, and the compositor matches that up with its output scale. Recently, using the Viewporter and Fractional scale protocol, a compositor can instead announce that a client is supposed to render natively with a scale of – in my case – 210/120. Yet, since fractional scale is a property of an individual window, it doesn’t map well to QScreen or QGuiApplication APIs which look at the output that is still technically at 2x scale.
This manifests itself in blurry or pixelated icons when using QIcon::pixmap without specifying a device pixel ratio, or using qApp->devicePixelRatio(). I fixed both Breeze style and Dolphin to rasterize icons at the scale of the window. Sadly, sometimes an app starts rendering before the fractional scale is set and you end up with a single frame at 2x which is then replaced by another frame at the correct scale. I fixed both KWin and QtWayland to not assume a (potentially incorrect) scale factor before the actual scale factor has been determined but the underlying issue of rendering prematurely still needs to be investigated.
On the Qt front I finally figured out why Plasma 6 kept crashing when receiving KDE Connect notifications. Technically, it wasn’t KDE Connect’s fault but it is one of the more prominent users of “resident” notifications. Normally, a notification is removed once it times out because that’s how the notification spec works and if we didn’t, we’d potentially cause memory leaks in apps. However, in order to provide a more interactive notification history, an app can explicitly set the “resident” property and control a notification even after it timed out. When it does, the relevant flag is set on the notification which causes the popup model to filter it out and thus remove the popup. Qt then tried to signal that the property has changed on the popup which was deleted in the meantime, leading to a crash. The fix was basically a one-liner but investigating it was quite a challenge: have you ever tried to print the contents of a QV4::String (to figure out the name of the property it accessed when it crashed) during a post-mortem coredump analysis?
Qt 6.6 Beta 3, I believe, also broke invoking certain methods on attached properties, which Plasma makes heavy use of. This lead to, among other things, System Tray icons not reacting to mouse clicks anymore. Luckily, a bunch of fellow Plasma hackers were on an even more bleeding-edge Qt build than I was, and as a result the issue could be spotted and resolved quickly. Qt 6 also treats trailing null bytes in QByteArray differently from Qt 5, which broke the XDG portal file picker, since it might have tried to start in a folder named “Downloads\0” instead of “Downloads”. Furthermore, Qt 6.6 gained API for reading file time stamps in UTC, saving timezone conversions. This is now used by KConfig and KService and saves like a couple of tens of milliseconds on startup of all our apps. A penny saved is a penny got.
Finally, we have decided on a release timeline for Plasma 6! An Alpha release will be made in November, accompanied by a soft feature freeze. Beta releases will follow throughout December and January, and the final release is slated for February 2024. This makes it even more important that you give Plasma 6 a spin and report – or fix – all the bugs! Check out our Community Wiki for a comprehensive list of open issues and instructions on how to run it. Needless to say you can also Donate to KDE to fund development and the necessary infrastructure.
Discuss this post on KDE Discuss.