A tug boat towing docks and a toolbar Most of our desktop applications have a toolbar, sometimes they even have multiple toolbars next to or stacked on top of each other. More complex desktop applications such as Krita, Kdenlive or LabPlot often consist of multiple sub-windows, docks, tabbed views, etc. Docks and toolbars can be undocked, moved around and arranged freely and when dragged over a part of a window snap back into the window. This allows the user to customize their work environment to their liking and needs. This worked fine on X because it lets you do anything, this post explores the situation on Wayland.

The current situation

The current behavior of dragging Chromium tabs in a Wayland session

Lets start with a more familiar example, tabs in applications such as web browsers can often be dragged between different windows of the browser and when dropped somewhere else they become their own window. Dragging a tab out of Chrome creates a new window that follows the cursor and it is seamlessly integrated back into other Chrome windows when hovering over their tab bar. When doing this in a Wayland session it tried to do the same, starting a drag operation and creating a new window, when the cursor goes back to the bar the window is reincorporated. Except that clients can’t position their own windows on Wayland leading to sub-par UX. So it creates the window which the compositor places somewhere according to its policies. This window does not move around with the cursor but vanishes when the cursor enters the tabbar of another browser window. This feels weird and broken.

The current behavior of trying to drag toolbars and docks in a Qt app in a Wayland session

Qt exhibits similar behavior to Chrome when trying to drag around toolbars in a Wayland session, they detach and appear in the middle of application but the app itself reacts as if the toolbar is being dragged around and you could reattach it in some part of the window. However since they don’t have any window decorations, once detached they can only be moved around by compositor specific means like pressing Alt and dragging when using KWin. Dragging the cursor on the displayed move handle starts the “app thinks the window moves but it isn’t” state again and it may be possible to drag it back to its window. ‘May’ because this feels even weirder than the initial action since the app can’t know on Wayland where the toolbar window was in relation to the other window . Detaching a dock results in the same experience, except that once it is detached it is actually impossible to dock it again. Floating docks receive a system titlebar so at least can be moved around more easily but contain no way to start the docking process again. This is also weird and broken.

This problem is one of the last remaining blockers for some of our more complex applications in KDE for switching to Wayland by default. While some workarounds (such as the one outlined above) exist, a proper solution is needed.

The solution

Facing this shortcoming a new extended drag protocol was proposed and implemented by Chrome developers in 2020 but unfortunately the effort stalled. My initial impression was that it provided a good starting point but maybe sometimes a bit complicated. Nevertheless I started to prototype an implementation in KWin and although not even Chrome implemented the more involved parts of the protocol I had working tab dragging not soon after. Encouraged by this I implemented the minimal client parts in Qt and could successfully drag toolbars and dock widgets around. This convinced me that a more focused protocol would be sufficient cover the different use cases.

A bit of trimming, renaming and shuffling things around resulted in the xdg-toplevel-drag protcotol. The idea behind it is that the application can attach a window to a drag operation which then the compositor moves around with the cursor. The advantage of using the existing drag mechanism is that the window under the cursor is informed of the cursor position while a normal window move does not provide any information to it. The application can then also use the normal drag and drop negotiation mechanisms to infer the state and choose an appropriate action. For example the drag being accepted means it is over an area that will incorporate the window. It’s up to the client to take immediate action or not, Chrome will move a tab to a window the moment it reaches its tabbar while Qt just shows an indicator and waits for the drop to perform any action. When a drop happens but the drag is not accepted the cursor was over some other area and a likely (non-)action of a client would be to keep the window that was being dragged around alive.

Dragging, detaching and reattaching of toolbar and dock on Wayland

The above shows the mentioned protocol in action inside Qt and KWin. I also went ahead and added support for the newly proposed protocol to Chrome since I iterated on their original work. But it also shows that the same protocol also works for its slightly different use case of attaching and detaching tabs during the ongoing drag. Below you can see Chromium using this new protocol inside KWin.

Chromium tabs being dragged around on Wayland

The way forward

This work addresses one of the main blockers for our Wayland session that we want to solve in time for the initial Plasma 6 release. Due to timeframes we know that Plasma 6 is likely to require Qt 6.6. However the feature freeze for Qt 6.6 was already in June and the protocol was not accepted at that time. To ensure the functionality is available for our deadlines the Qt implementation that is shown in this blog post was merged under a different name (changing thexdg prefix toqt). The intention here is to change it back once as soon as possible when the upstream procotol is accepted. So the applications relying on it work perfectly not only on Plasma but also on all others desktops.

Discuss this post on KDE Discuss.