I recently switched my linux config over to wayland. In doing so, I decided to learn how the protocol works and how to write simple desktop applications with it. The result was picky, a basic color picker.
I don't have a lot of X11 programming experience. The only interaction I've ever had with it was patching exisiting programs. You don't have to be an expert to be able to tell that it is a horrible mess completely unsuitable for modern use. Oh well, I guess it had an impressively long lifespan anyway, predating linux and somehow still being relevant is no slouch.
Clients talk with servers using messages, usually over unix sockets. The message format is called the "wire protocol", it defines its own types that libraries need to be able to decode. Messages from clients to the server are called "requests". Conversely, the server communicates with its clients using "events". There is usually only one wayland server, referred to as the "compositor", though you can nest them at will.
Wayland is object oriented at its core. Both sides operate using the notion of objects with various capabilities. Messages are really just invocations of methods on those objects.
When you open a new client-server connection, there exists
only one object, namely wl_display
.
Its existence is implicit. It is referred to as the 'core global
object'. As part of its interface it defines a
get_registry
request, which creates a registry
object wl_registry
. That object can in turn be used
to create/register other objects such as wl_shm
(shared memory management), wl_seat
(input
devices), or xdg_wm_base
(turning wayland surfaces
into desktop windows) etc.
Wayland is in many ways atomic. Changes to surfaces do nothing until commited. Some events are sent in batches with clearly defined boundaries.
While you could of course connect to the compositor's unix socket and send raw wire protocol messages, in practice most developers are going to use libwayland (or another language's bindings to it).
Handling events received from the server in code is pretty
simple. Let's say we want to listen for keyboard input events.
We first need to set up a listener for these events using the
wl_seat
object.
(wl_seat, &seat_listener, state); wl_seat_add_listener
Where:
wl_seat
is a pointer to an instance of
libwayland's struct wl_seat
state
is a void* to some arbitrary data,
usually containing application state.
seat_listener
is an instance of another struct
defined by libwayland. Listeners are essentially structs of
function pointers with fields corresponding to events.
const struct wl_seat_listener seat_listener = {
.capabilities = seat_capabilities_handler,
// .. fields omitted ..
};
The capabilties
event advertises what input
devices are available in a given seat. Let's take a look at
seat_capabilites_handler
.
static void
(void *data, struct wl_seat *seat,
seat_capabilities_handleruint32_t caps)
{
*state = data;
AppState
if (!(caps & WL_SEAT_CAPABILITY_KEYBOARD)) {
(stderr, "seat has no keyboard!\n");
fprintf(1);
exit}
->wl_keyboard = wl_seat_get_keyboard(seat);
state(state->wl_keyboard,
wl_keyboard_add_listener&keyboard_listener, state);
}
keyboard_listener
in turn contains the
key
event.
const struct wl_keyboard_listener keyboard_listener = {
.key = keyboard_key_handler,
// .. fields omitted ..
};
static void
(void *data, struct wl_keyboard *keyboard,
wl_keyboard_keyuint32_t serial, uint32_t time,
uint32_t key, enum wl_keyboard_key_state s)
{
if (key == KEY_LEFTSHIFT) {
// *do stuff*
}
}
Where KEY_LEFTSHIFT
is defined
by <linux/input-event-codes.h>
What I found interesting is that all wayland protocols are
defined in xml files, including the core wayland.xml.
wayland-scanner
is a code generation library that
converts these xml files into C code. There of course exist
implementations for other languages, like smithay's
wayland-scanner for rust.
I might play around with the compositor (server) side of the protocol in the future. The word out on the street is that creating a compositor is not for the faint of heart though, so that challenge might have to wait until a very rainy day indeed.