How to live on GNOME with pure Wayland

Here in 2023 Wayland become the default graphics session type for at least 2 most popular Linux distros: Fedora and Ubuntu.

However it is still not ideal, even though GTK4 and Qt6 claim full support for Wayland, many other toolkits have issues with it or Wayland backend is yet in progress.

This is usually eliminated by using XWayland that is being started transparently when you launch an application that is not capable of Wayland.

I have switched to Wayland a few years ago with Fedora 32. The main reason for me was the support of fractional scaling since I use 2 screens with different DPI and I usually set 125% scale for laptop screen, which makes UI size more or less the same on both of them.

Enabling fractional scaling in GNOME

It’s still an experimental option in Mutter, you have to enable it explicitly:

$ gsettings set org.gnome.mutter experimental-features "['scale-monitor-framebuffer']"

Re-login, go to Settings -> Displays -> your display and you will see options like 125%, 175% and 225%.

Fractional scaling in GNOME works in the following way: it renders the window at next integer scale first, and then downsizes the image in the compositor to 1.25x. It allows to move the window between monitors and get the right scale on any of them without restart. In GNOME 44 there’s a wp_fractional_scale_v1 protocol supported in Qt6 and GTK4, for drawing directly at desired scale.

The problem is that only applications using Wayland backend are capable of fractional scaling, while those running through XWayland are getting blurry.

Possible solution is to force using Wayland backend for the most applications. I gathered some known bugs and workarounds for toolkits I’m using every day and gonna share this below.

Wayland experience with different toolkits

  • GTK4: works perfectly out-of-the-box.

  • GTK3: pretty good experience, but it still has a long-standing bug with GtkEntryCompletion widget in multi-monitor configurations: #2794. It was reported by me in 2020 with related merge request, and as of now (2023) there are 3 merge requests resolving this, but developers still not accepted nor reviewed any of them.
    The solution is to use my GTK3 COPR repo for Fedora with patched gtk3 package.

  • Qt6: pretty good experience out-of-the-box.

  • Qt5: works well with KDE patchset however some features like XDG Activation are still missing. I have backported support for XDG Activation as well some other quirks from Qt6.
    One can use my Qt5 COPR repo with patched qt5-qtwayland package. To force Wayland backend, use QT_QPA_PLATFORM=wayland environment variable or the following command-line argument:

$ ./qt5-app ... -platform wayland
  • QGnomePlatform: Fedora ships custom Qt Platform Theme QGnomePlatform which mimics GTK-style window decorations and uses adwaita-qt widget style that follows Adwaita theme.
    I have pushed number of fixes to their upstream, but version shipped in Fedora is pretty old, so the solution is the same - use gnomeplatform and adwaita-qt5 packages from my Qt5 COPR repo.

  • Electron: Ozone platform works on Wayland quite well and since version 12 it supports GTK3-style titlebars via libdecor wrapper, which can be enabled by the following command-line arguments:

$ ./electron-app ... --enable-features=UseOzonePlatform,WaylandWindowDecorations \
                     --ozone-platform=wayland

I’ve written a simple bash script that launches an app on Wayland backend for Qt and Electron. It also handles case of running through Flatpak and falls back to X11 backend if launched from X11 session (gist):

#!/bin/sh

platform_args=""
flatpak_args=""

# Check platform argument
case "$1" in
    -electron)
        platform=electron
        ;;
    -qt5)
        platform=qt
        ;;
    *)
        echo "Usage: $0 -electron|-qt5 [command]"
        exit 1
esac

# Extract app name and make $@ point to it's args
app="$2"
shift 2

if [ "$XDG_SESSION_TYPE" = "wayland" ]; then
    # Check if an application is started through flatpak
    test "$(basename "$app")" = "flatpak" && using_flatpak=1

    # On GNOME retrieve cursor size from settings and pass to an application
    gnome_cursor_size="$(gsettings get org.gnome.desktop.interface cursor-size 2>/dev/null)"
    if [ -n "$gnome_cursor_size" ]; then
        if [ -n "$using_flatpak" ]; then
            flatpak_args="--env=XCURSOR_SIZE=$gnome_cursor_size --env=DISPLAY="
        else
            export XCURSOR_SIZE="$gnome_cursor_size"
            export DISPLAY=
        fi
    fi

    case "$platform" in
        electron)
            platform_args="--enable-features=UseOzonePlatform,WaylandWindowDecorations"
            platform_args="$platform_args --ozone-platform=wayland"
            ;;
        qt)
            platform_args="-platform wayland"
            if [ -n "$using_flatpak" ]; then
                flatpak_args="$flatpak_args --env=QT_QPA_PLATFORM=wayland"
            else
                export QT_QPA_PLATFORM=wayland
            fi
            ;;
    esac
fi

exec "$app" $flatpak_args "$@" $platform_args

Call it wayland-launch, put somewhere in /usr/local/bin/ and use like so:

$ wayland-launch -electron code
$ wayland-launch -electron flatpak run com.slack.Slack
$ wayland-launch -qt5 keepassx

Keeping monitor configuration between Wayland and X11 sessions

The last thing I’m going to cover here is that display configuration will be reset when launching X11 session if you have at least one screen with fractional scaling.

It happens because on X11 Mutter doesn’t support fractional scales and treats all the configuration as invalid.

The solution is to have separate monitor configurations for X11 and Wayland. Monitor configuration is stored in ~/.config/monitors.xml file and we can fool Mutter by making it a symbolic link to either X11 or Wayland config, depending on current session type:

  • Create ~/.config/monitors.xml.wayland and ~/.config/monitors.xml.x11:
$ cp ~/.config/monitors.xml ~/.config/monitors.xml.wayland
$ cp ~/.config/monitors.xml ~/.config/monitors.xml.x11
  • Make it a symbolic link to the currently used configuration:
$ ln -sf ~/.config/monitors.xml.wayland ~/.config/monitors.xml
  • Now add the following to ~/.bash_profile to switch the symlink on session start:
if [ "${XDG_SESSION_TYPE}" = "wayland" -o "${XDG_SESSION_TYPE}" = "x11" ]
then
    MONITORS_XML="${HOME}/.config/monitors.xml"
    if [ -L "${MONITORS_XML}~" ]
    then
        cp "${MONITORS_XML}" $(readlink -f "${MONITORS_XML}~")
        rm -f "${MONITORS_XML}~"
    fi

    ln -sf ${MONITORS_XML}{.${XDG_SESSION_TYPE},}
fi
  • Profit! Separate monitor configurations for X11 and Wayland will never intersect.