Binding to globals
Upon creating a registry object, the server will emit the global
event for
each global available on the server. You can then bind to the globals you
require.
Binding is the process of taking a known object and assigning it an ID. Once
the client binds to the registry like this, the server emits the global
event
several times to advertise which interfaces it supports. Each of these globals
is assigned a unique name
, as an unsigned integer. The interface
string maps
to the name of the interface found in the protocol: wl_display
from the XML
above is an example of such a name. The version number is also defined here
— for more information about interface versioning, see appendix C.
To bind to any of these interfaces, we use the bind request, which works
similarly to the magical process by which we bound to the wl_registry
. For
example, consider this wire protocol exchange:
C->S 00000001 000C0001 00000002 .... .... ....
S->C 00000002 001C0000 00000001 00000007 .... .... .... ....
776C5f73 686d0000 00000001 wl_s hm.. ....
[...]
C->S 00000002 00100000 00000001 00000003 .... .... .... ....
The first message is identical to the one we've already dissected. The second
one is an event from the server: object 2 (which the client assigned the
wl_registry
to in the first message) opcode 0 ("global"), with arguments 1,
"wl_shm", and 1 — respectively the name, interface, and version of this
global. The client responds by calling opcode 0 on object ID 2
(wl_registry::bind
) and assigns object ID 3
to global name 1
—
binding to the wl_shm
global. Future events and requests for this object
are defined by the wl_shm
protocol, which you can find in wayland.xml
.
Once you've created this object, you can utilize its interface to accomplish
various tasks — in the case of wl_shm
, managing shared memory between
the client and server. Most of the remainder of this book is devoted to
explaining the usage of each of these globals.
Armed with this information, we can write our first useful Wayland client: one which simply prints all of the globals available on the server.
#include <stdint.h>
#include <stdio.h>
#include <wayland-client.h>
static void
registry_handle_global(void *data, struct wl_registry *registry,
uint32_t name, const char *interface, uint32_t version)
{
printf("interface: '%s', version: %d, name: %d\n",
interface, version, name);
}
static void
registry_handle_global_remove(void *data, struct wl_registry *registry,
uint32_t name)
{
// This space deliberately left blank
}
static const struct wl_registry_listener
registry_listener = {
.global = registry_handle_global,
.global_remove = registry_handle_global_remove,
};
int
main(int argc, char *argv[])
{
struct wl_display *display = wl_display_connect(NULL);
struct wl_registry *registry = wl_display_get_registry(display);
wl_registry_add_listener(registry, ®istry_listener, NULL);
wl_display_roundtrip(display);
return 0;
}
Feel free to reference previous chapters to interpret this program. We connect to the display (chapter 4.1), obtain the registry (this chapter), add a listener to it (chapter 3.4), then round-trip, handling the global event by printing the globals available on this compositor. Try it for yourself:
$ cc -o globals -lwayland-client globals.c
Note: this chapter the last time we're going to show wire protocol dumps in
hexadecimal, and probably the last time you'll ever see them in general. A
better way to trace your Wayland client or server is to set the
WAYLAND_DEBUG
variable in your environment to 1
before running your program.
Try it now with the "globals" program!