We’re developing our own Wayland Compositor, and we noticed most projects just use libwayland-server. However, we want more control over the on-the-write protocol I/O and better integration with our C++ codebase.
If you’ve written a Wayland Compositor you know that the protocol is defined in XML format. So, we decided to create our compiler for the Wayland protocols.
In these protocol definitions there are interfaces with requests and events defined. These are the most important abstractions for communication between a Wayland Compositor and its clients.
Protocol
You can see this example from wayland.xml from 1.0 version:
As you can see, a request called get_registry
is defined with only one
single argument of type new_id
.
The get_registry
is one of the most important requests a client
makes right in the beginning, to be able to get the wl_registry
,
which the client can listen on events to get other global objects and
which it can use to bind client objects to the compositor.
Since the protocol doesn’t define return values from requests, the way
to create objects is to have the client define ids for them in
getters. That’s what the type new_id
does.
The client calls get_registry
and passes a id, usually 0, to be set
as the id of the registry that it is supposedly getting.
Now the client knows that the registry id is 0, since it got it with that argument.
The get_registry
request also triggers the global event for each
global object the wayland compositor has and needs to publish to the
client.
Compiler
The VWM Wayland Compositor has a compiler that reads multiple XML wayland protocol and creates a single class that handles requests and translates events to the on-the-write binary protocol of the wayland.
The protocol is quite simple and is implemented in our compiled result as simple POD copies over the write and special handling for strings and file descriptor arguments.
For example, the result of the get_registry
request is compiled as:
The constant 1
represents the index of the request in the interface
wl_display
. The server_protocol
class has one member function that
interprets the message to a request or event and calls the appropriate
function by using CRTP, aka Curiously Recurring Template Parameter, to
customize the Wayland Compositor behavior. Which is where the
wl_display_get_registry
is defined.
As we can see in client.hpp:464
where wl_display_get_registry
is implemented:
Conclusion
As you can see, taking control of the protocol and handling is
possible and a small part of the work needed to write a Wayland
Compositor. It is also a rewarding task that allows extending the
Wayland protocol to other mediums beside UNIX sockets. The biggest
challenge is supporting shm_buffer
over slower mediums.
This also allows implementing faster and more scalable protocol handling code.