Porting Qt applications to Wayland

During Akademy I hold a session about porting applications to Wayland. I collected some of the general problems I saw in various KDE projects and want to highlight them in this blog post, too.

Starting Wayland

First the basics: how to start a Wayland compositor? Assuming one is using Plasma 5.4 (aka master) the way to start a nested KWin is:

kwin_wayland --xwayland

Important: ensure that you have Xwayland installed!

If you run nested this will open a window, if it turns black, everything works. If it doesn’t turn black: contact me (please check whether you use free drivers first).

As an alternative you can also start Weston:

weston

If you run into issues with your application running under Wayland, please always test whether the problem is also visible on Weston before blaming me 😛

Starting applications on Wayland

Now we have our black KWin window and we want to get a window to it. There are several ways to start it. As an example I’ll use kate.

Environment variable

Maybe the easiest way is to just use an environment variable to switch the QPA platform to Wayland:

export QT_QPA_PLATFORM=wayland
kate

And a Kate window will open on the nested KWin.

The environment variable is set to wayland in our startplasmacompositor script to start a full Plasma on Wayland session on tty.

Command line switch

Every Qt application also has a command line switch to select the windowing system:

kate --platform wayland

Starting applications together with KWin

KWin supports starting applications once the Wayland and Xwayland servers are started. You can add the programs to start to the command which starts KWin:

kwin_wayland --xwayland "kate --platform wayland"

Or to simplify with environment variables:

export QT_QPA_PLATFORM=wayland
kwin_wayland --xwayland kate

Of course it’s also possible to just start a Konsole and then launch applications from inside the Konsole. Please note that any key combination with ctrl-key is unfortunately broken when running Konsole as a Wayland application.

X11 Specific code

Many applications have X11 specific code. Being it implicitly or explicitly. So we need to move away from it and in most cases it means also improving the situation on X11 because the solution is wrong.

Opening X connection

A common mistake I saw in Qt applications is to open a connection to the X server. E.g. like that:

Display *dpy = XOpenDisplay(NULL);

Now this code is already wrong on X11 as it opens another connection which is not the Qt display connection. It might be on the wrong head, it might be on the wrong X server. From an integration point of view it’s broken.

So the solution is to always use QX11Info from QtX11Extras to integrate with the Display:

Display *dpy = QX11Info::display();

Runtime checks in addition to compile time checks

Before Qt5 the way to support multiple windowing systems was to use compile time switches, like:

#if HAVE_X11
#include <QX11Info>

void doSomethingWithX11()
{
    XFlush(QX11Info::display());
}
#endif

Now this will crash on Wayland. It will be compiled in, but crashes when running on Wayland. So you need to improve. Let’s try:

#if HAVE_X11
#include <QX11Info>

void doSomethingWithX11()
{
    Display *dpy = QX11Info::display();
    if (!dpy) {
        return;
    }
    XFlush(dpy);
}
#endif

Now this code will also crash! The problem is that QX11Info::display() goes through Qt’s native interface and uses the key “display”. QtWayland also provides the key “display” and returns a wl_display for it, which gets casted into a void in the native interface and then to Display* in QX11Info and boom at interesting places.

The proper way is to check the platform name:

#if HAVE_X11
#include <QX11Info>

void doSomethingWithX11()
{
    if (!QX11Info::isPlatformX11()) {
        return;
    }
    XFlush(QX11Info::display());
}
#endif

My recommendation is to keep the check in a local variable if the check is needed multiple times as in the end it’s a string comparison each time. Ideally you should restructure your code to have platform specific subclasses or similar refactored solutions.

General Changes with Wayland

Now here it becomes interesting: runtime changes. The most important information for application developers is probably that there are no more global window ids.

No global window ids

This has quite some implications for your application. E.g. QWindow::winId() returns an internal incremented number. Neither the compositor nor any other application knows anything about your internal window id counter. Similarly QWindow::fromWinId is not able to map a window id to a QWindow any more. This of course affects use cases like sending a window id through DBus to another process to use it for setting the transient hint. Also it means that KWindowSystem API has a hard time to support the calls on Wayland and in most cases it will just print a warning (this might improve, though).

Setting window icon

On X11 the applications exported the window icon as pixmap data. This is no longer possible on Wayland. Instead the compositor maps the window icon from the desktop file and the application announces the desktop file for a window. In Qt this is done by using the reverse domain name combined with the binary name. The domain name is set with KAboutData and defaults to “kde.org”. So for example in the case of kate the desktop file should be called “org.kde.kate.desktop”. So please fix the naming of your desktop files. If you want to test please be aware that this requires Qt 5.5 to work properly.

No longer working API

Quite some of the API calls of QWindow cannot be supported by Wayland on default (some of them might work on KWin, but please don’t implement against KWin!).
A short list of those API calls I know that they cannot work:

  • QWindow::setPosition
  • QWindow::setMouseGrabEnabled
  • QWindow::setKeyboardGrabEnabled
  • QWindow::alert
  • QWindow::lower
  • QWindow::raise
  • QWindow::requestActivate

In addition it’s no longer possible to grab the image of the screen using e.g. QScreen::grabWindow (most obvious for the case of “root” window). Also warping the pointer using QCursor::setPos is no longer supported. Please note that warping the pointer is also a bad idea on X11 and you shouldn’t do that.

In case your application set the Qt::BypassWindowManagerHint on your QWindow/QWidget you need to do some porting as QtWayland doesn’t show such windows. Unfortunately that needs a case by case evaluation of the use case and the solution I presented during my Akademy talk should not be applied for applications.

Porting to Wayland

Don’t port

The solution to port to Wayland is: DON’T PORT! Mostly your X11 specific code or behavior is wrong and could be solved in better ways. Instead of porting you might want to rethink your problem or change to existing abstracted API in e.g. Qt or KWindowSystem. I have seen many cases where X11 API was used as a workaround by e.g. raising windows, force activating them and so on. If one things about it one realizes that there seems to be an obvious problem. So don’t try to port that but rethink your problem and improve not just on Wayland, but also on X11.

But I really need platform specific code

Ok, ok, I get it! Now let’s start with the obvious: your code needs to be compile time and runtime checked. Let’s start with how to not do it:

#if HAVE_X11
    if (!QX11Info::isPlatformX11()) {
         return;
    }
    // here goes the X11 specific code
#else
    // here goes the Wayland specific code
#endif

The problem with this code snippet is that X11 support is probably also available when you use Wayland. Also the else part is not just Wayland, but also any other platform.

As I said the proper solution is to make it compile and runtime checked:

#if HAVE_X11
    if (QX11Info::isPlatformX11()) {
        callX11SpecificImplementation();
    }
#endif
#if HAVE_WAYLAND
    if (QGuiApplication::platformName().startsWith(
            QLatin1String("wayland"), Qt::CaseInsensitive)) {
        callWaylandSpecificImplementation();
    }
#endif

Be aware that your code might also run on other platforms on Wayland, e.g. eglfs or other custom windowing systems. So don’t crash if you don’t hit any of the code paths 😉

18 Replies to “Porting Qt applications to Wayland”

  1. Thanks for collecting all that and sharing here, Martin.
    Just, please, please also add this info to our KDE knowledge base, techbase.:
    *will be easier found
    * can be collectively maintained, (e..g. extending the “No longer working API” and perhaps adding alternatives)
    Having all info for developers spread over personal blog posts (like sadly had been the trend for some of the KF5 porting info bits) does not really improve your KDE fellow contributors’ life 🙂

  2. “The problem is that QX11Info::display() goes through Qt’s native interface and uses the key “display”. QtWayland also provides the key “display” and returns a wl_display for it”

    Isn’t that a horrible bug which ought to be fixed? e.g. by putting a check right at the top of QX11Info::display() along the lines of

    if (!QX11Info::isPlatformX11()) return NULL;

    ?

    1. I thought about contributing a patch upstream when hit into it. But after some thought about it I decided that this obvious fix would be wrong. As then you could easily run into undefined behavior in other places. All of the QX11Info API would need to be protected and in some cases that could result in different problems.

      1. If you’re using QX11Info then you should be sure you’re on X11, and the API to know that is provided. That said, if it’s missing anything then please report it.

  3. I have this small Qt app that I’ve written as a convenience for myself which I fear will simply be unportable to Wayland once that gets pushed down my throat via distributions. What it does is it stores a bunch of URLs and associated username+password in a KWallet. It registers a global shortcut via XGrabKey() (which I’m pretty sure works regardless of what WM/DE happens to be running). When this global shortcut is triggered it detects the screen on which the currently focused window is (by using QScreen functionality combined with reading the _NET_ACTIVE_WINDOW of the root window – which, again, works regardless of WM/DE) and on that screen it shows a window which allows you to pick/search for the password you want. Once can then either copy the password to the clipboard (which may be possible on Wayland, not sure) or have the application generate fake keypress events to send the password directly to the window which had the focus when the shortcut was initially triggered.

    Can something like this even be done on Wayland without resorting to WM/DE specific functionality ?

    1. No as you are neither able to grab keys nor to get information about the active window. Overall it sounds like something doable with e.g. KWin scripts, though.

      1. Well that’s unfortunate. Sounds to me like all this Wayland business is a huge step backwards in regards to being able to be desktop environment agnostic. Sure, sure, “security”. I’d rather have more flexibility than “security” myself. Anyway, thank you for your answer.

        1. > I’d rather have more flexibility than “security” myself.

          I’ve heard there is that “Windows” thing for that.

          1. Wow. ‘Get back to Windows’. For a moment there I thought I was back in the ’90s.

            TL:DR for everything below: regardless of how you spin it cutting features for “security” is never the right way to go.

            Rant: let’s talk about my example above: you can no longer decide the position of your application’s own windows and you can no longer register global keyboard shortcuts.

            Say I write a program that has multiple top-level windows (I don’t know, something like the Gimp maybe). The user sets up their environment by placing/resizing the windows to their liking. Can the application restore the layout on the next start ? Nope. Does the compositor somehow magically restore the application state ? Nope. So the user has to rearrange windows on every application start ? Yep. BUT OH BOY is he secure now !

            Is it stupid that X allows you to move/resize the windows of OTHER apps ? Yes, absolutely. Is it stupid that on Wayland you can’t even do that to your own windows ? 100%.

            Now say I write a program that (GASP!) needs global keyboard shortcuts – something like a media player maybe. Can the program register said shortcuts ? No because ‘omg keyloggers!’. This is just stupid – you could have Wayland/compositors only allow global shortcuts that involved an actual modifier key to be pressed (ctrl/alt/meta) and everything would have been fine. But no, just cut everything out. A+++ job.

            Sad thing is we already have a perfectly working example of how all of this functionality could have been preserved while making it more secure – the permissions system in Android. On start the application could maybe send a dbus request to Wayland specifying the permissions it needed. The Wayland could talk to the compositor which would pop up a ‘permissions requested’ dialog. The user could then pick and choose which permissions the app has. Then for extra points the Desktop Environment could have this nice settings page that lists all apps and their permissions where the user can grant/revoke permissions at will.

            1. A permission system is still possible, so is adding a system to register shortcuts or allow applications to move their windows.

              Wayland does not “cut features to add security” it starts secure and allows to add features in a secure way, e.g. through an interactive permission system, like you wrote yourself

          1. You could but as you said in your comment below there is no way to do it cross-compositor. For all it’s security flaws at least X had functionality that (more or less) worked reliably across all window mangers. As it stands it looks like on Wayland certain classes of applications will need to be targeted to specific compositors/DEs to get the functionality they need.

            1. Not necessarily.
              There are already established examples of functionality being made available for situations when they are needed, e.g. for desktop shells.

              Actually quite like X11, where window manager hints were added when the need for that arose.
              Just with the difference that Wayland requires that to be handled in a defined way and thus enabling developers to make it a secure way.

    2. “once [Wayland] gets pushed down my throat via distributions. ”

      Sounds like you’re using a distro whose philosophy doesn’t match your own. Why not switch to one that does? Gentoo, Arch or LFS might be a good fit. Or maybe even Slackware.

      (I use none of these, but I am glad that they exist and serve the audiences they do.)

      1. As a matter of fact I do use Gentoo AND Arch on pretty much all my machines. It’s not a question of ‘pick the right distro’ unfortunately. It may not be very soon but a time will come when people will simply stop developing for X (this includes drivers and X itself). When that happens distributions will have no choice but to ‘push Wayland down my throat’. I do have a choice right now, doesn’t mean that choice will be mine to make in the future.

  4. Is the lack of support for ForeignWindows a temporary limitation? I.e. is there some discussion or or even work being done on an extension that would allow compositor and applications to communicate meaningful IDs with each other?

    Sharing IDs between compositor and applications is of course doable when depending on a certain compositor, what I mean is: are people working on this in a cross-compositor fashion?

    Mostly for things like the “transientFor”, at least all those targetting mobile devices will need this for “pickers”, won’t they?

Comments are closed.