As some of you may know, the Mir team and the MATE Desktop team have been working together for some time to bring MATE to Wayland with Mir. We’ve demonstrated a proof of concept (which you can try with the mate-wayland
snap), but there’s still considerable work to be done before we have a MATE session suitable for daily use.
One of the largest pieces of work is arguably the simplest: porting the normal applications. The functionality of Caja, MATE Terminal, Pluma, etc should all be possible using only portable GTK3 code. Unfortunately, they have been implemented using X11-specific functionality which now needs to be removed or at least made optional. This is a large amount of work only due to the number of applications and the volume of code that needs to be looked at. This document is a technical guide for whoever is doing that work.
Setting up
Development environment
Since you don’t need support for any fancy protocols, any modern Wayland compositor will do. Good options include:
- Mir via the
mate-wayland
snap$ sudo snap install --classic --edge mate-wayland
-
$ mate-wayland.mirco
(Just plain$ mate-wayland
will try to spin up a panel and background) - You can run it from a TTY, or nested inside your normal session
- Sway
- You’ve got to figure out how to get it or build it on your distro
- Can also be run either from a TTY or nested
- GNOME-on-Wayland
- If you’re already running GNOME, may be easiest to get
Can’t run nested, so only use if you’re using or willing to switch to using Wayland for your standard session- EDIT: Mutter (the compositor) can run nested with
mutter --nested --wayland --no-x11
. Thanks to Reddit user jadahl for pointing this out
-
NOT Weston
- The version shipped by most distros still doesn’t support XDG Shell stable
Running apps in Wayland
The MATE Wayland snap uses wayland-mate
for its Wayland display. Other compositors (including other Mir-based compositors) generally use wayland-0
. To launch an app in a compositor simply run something like $ WAYLAND_DISPLAY=wayland-mate lxterminal
.
If a GTK3 app completely refuses to run on Wayland, it may be because gdk_set_allowed_backends ("x11")
is being called. Comment it out for now.
configure.ac
If backend-specific code is required for an app, both X11 and Wayland should be able to be enabled or disabled at build time. Add --enable-wayland
and --enable-x11
options that work independently. See the MATE Panel configure.ac for an example. The configure.ac should define HAVE_X11
and HAVE_WAYLAND
macros that are used in the code.
Set allowed backends
If all backend-specific calls can be removed from an app,
gdk_set_allowed_backends ()
does not need to be called. Otherwise, do something like this in initialization:
#if defined(HAVE_X11) && defined(HAVE_WAYLAND)
gdk_set_allowed_backends ("wayland,x11");
#elif defined(HAVE_WAYLAND)
gdk_set_allowed_backends ("wayland");
#else
gdk_set_allowed_backends ("x11");
#endif
Removing X11-specific code
How did we get here?
From what I know, there are a number of reasons X11 is being used directly. Some of them include:
- Legacy code has not been ported to modern GTK3
- X11 functions have been used indiscriminately to fix minor issues
- In some places GTK does not provide a portable way to implement desired features (for example, the desktop background)
Try the easy options first
It’s useful to ask the following questions when encountering X11-specific code:
- Is the code dead?
- Is the function only being called from other X11-specific sections of code?
- If so, treat the function as X11-specific (see next section)
- Is the feature it’s a part of actually providing value to users 2019?
- Is there an alternative method that uses portable GTK code?
- Does the app still make sense without the feature the code provides?
- If so, make the feature X11-only (see next section)
- Is this a critical part of a really important feature with no portable alternative?
- This is when we may need to use Wayland-specific code and/or a protocol extension GTK doesn’t support
Making code X11-only
Ideally as much functionality as possible should be portable. There are, however, some cases where you need different behavior between X11 and Wayland. To make some code in a file X11-only, follow the following steps:
- Make sure the file includes
config.h
at the top - Put all X11 includes (including gdk/gdkx.h) behind
#ifdef HAVE_X11
s - Put all X11-specific globals and struct members behind
#ifdef HAVE_X11
s - Put all X11-specific functions and definitions behind
#ifdef HAVE_X11
s - Put all calls from portable functions into X11-specific functions behind both
#ifdef HAVE_X11
andif (GDK_IS_X11_DISPLAY (gdk_display_get_default ()))
checks - To provide a fallback, put an
else
after the if block just before the#endif // HAVE_X11
, and add the fallback in a following block. For example:
#ifdef HAVE_X11
if (GDK_IS_X11_DISPLAY (gdk_display_get_default ()))
{
// X11 code
}
else
#endif // HAVE_X11
{ // Not using X11
// Fallback code
}
To make entire files X11-only:
- Put this at the top of source files
#ifdef PACKAGE_NAME // only check HAVE_X11 if config.h has been included
#ifndef HAVE_X11
#error file should only be included when HAVE_X11 is enabled
#endif
#endif
- Put this at the top of header files
#ifndef HAVE_X11
#error file should only be built when HAVE_X11 is enabled
#endif
- Hide file behind a Makefile.am
if
like so
GdkScreen
GdkScreen
is going away in GTK4, and it’s generally not needed in modern GTK3. A GdkScreen
does not represent a physical monitor/output (that’s what GdkMonitor
is for). Instead it represents the strange and deprecated concept of an X11 screen (which conceptually sits between GdkMonitor
and GdkDisplay
). As of GTK 3.10, There is always exactly one screen per display and we don’t attempt to support multiple displays. MATE apps support 3.22 and up so we can assume all multi-screen logic is legacy and should be removed. gdk_x11_screen_get_screen_number ()
can be assumed to always return 0. A more complete explanation of display vs screen vs monitor can be found here, and an example of a PR removing multi-screen logic is here.
What this means is that storing or passing around a GdkScreen
is generally not useful. It doesn’t need to be completely removed yet, but it is being phased out. This is relevant to porting to Wayland because when GdkScreen
functions were deprecated and removed, MATE often jumped to X11-specific alternatives instead of refactoring properly.
There is a particular liking of the WidthOfScreen (gdk_x11_screen_get_xscreen (screen)
pattern. Unfortunately there is no single drop-in replacement for this logic. The screen size is not available on Wayland because Wayland doesn’t guarantee compositors place windows in a 2D area of a fixed size. Generally, the steps to resolve instances of this pattern are:
- Check that it’s actually needed on Wayland
- If it’s already behind an X11 check, leave it as-is
- If it’s in a function that’s only called from inside an X11 check, make the function X11-only
- Use a
GdkMonitor
if possible
- You can use
gdk_display_get_primary_monitor ()
,gdk_display_get_monitor_at_point ()
andgdk_display_get_monitor_at_window ()
to get a monitor andgdk_monitor_get_workarea ()
to get the size of a monitor once you’ve got it. Frequently this will give you equally or more correct behavior than using the size of the screen