Registering globals
Registering globals with libwayland-server is done somewhat differently. When you generate "server-code" with wayland-scanner, it creates interfaces (which are analogous to listeners) and glue code for sending events. The first task is to register the global, with a function to rig up a resource1 when the global is bound. In terms of code, the result looks something like this:
static void
wl_output_handle_bind(struct wl_client *client, void *data,
uint32_t version, uint32_t id)
{
struct my_state *state = data;
// TODO
}
int
main(int argc, char *argv[])
{
struct wl_display *display = wl_display_create();
struct my_state state = { ... };
// ...
wl_global_create(wl_display, &wl_output_interface,
1, &state, wl_output_handle_bind);
// ...
}
If you take this code and, for example, patch it into the server example chapter
4.1, you'll make a wl_output
global visible to the "globals" program we wrote
last time2. However, any attempts to bind to this global will run into our
TODO. To fill this in, we need to provide an implementation of the wl_output
interface as well.
static void
wl_output_handle_resource_destroy(struct wl_resource *resource)
{
struct my_output *client_output = wl_resource_get_user_data(resource);
// TODO: Clean up resource
remove_to_list(client_output->state->client_outputs, client_output);
}
static void
wl_output_handle_release(struct wl_client *client, struct wl_resource *resource)
{
wl_resource_destroy(resource);
}
static const struct wl_output_interface
wl_output_implementation = {
.release = wl_output_handle_release,
};
static void
wl_output_handle_bind(struct wl_client *client, void *data,
uint32_t version, uint32_t id)
{
struct my_state *state = data;
struct my_output *client_output = calloc(1, sizeof(struct client_output));
struct wl_resource *resource = wl_resource_create(
client, &wl_output_interface, wl_output_interface.version, id);
wl_resource_set_implementation(resource, &wl_output_implementation,
client_output, wl_output_handle_resource_destroy);
client_output->resource = resource;
client_output->state = state;
// TODO: Send geometry event, et al
add_to_list(state->client_outputs, client_output);
}
This is a lot to take in, so let's explain it one piece at a time. At the
bottom, we've extended our "bind" handler to create a wl_resource
to track the
server-side state for this object (using the ID that the client allocated). As
we do this, we provide wl_resource_create
with a pointer to our implementation
of the interface — wl_output_implementation
, a constant static struct in
this file. The type (struct wl_output_interface
) is generated by
wayland-scanner
and contains one function pointer for each request supported
by this interface. We also take the opportunity to allocate a small container
for storing any additional state we need that libwayland doesn't handle for us,
the specific nature of which varies from protocol to protocol.
Note: there are two distinct things here which share the same name:
struct wl_output_interface
is an instance of an interface, where
wl_output_interface
is a global constant variable generated by
wayland-scanner
which contains metadata related to the implementation (such as
version, used in the example above).
Our wl_output_handle_release
function is called when the client sends the
release
request, indicating that they no longer need this resource — so
we destroy it. This in turn triggers the wl_output_handle_resource_destroy
function, which later we'll extend to free any of the state we allocated for it
earlier. This function is also passed into wl_resource_create
as the
destructor, and will be called if the client terminates without explicitly
sending the release
request.
The other remaining "TODO" in our code is to send the "name" event, as well as a
few others. If we review wayland.xml
, we see this event on the interface:
<event name="geometry">
<description summary="properties of the output">
The geometry event describes geometric properties of the output.
The event is sent when binding to the output object and whenever
any of the properties change.
The physical size can be set to zero if it doesn't make sense for this
output (e.g. for projectors or virtual outputs).
</description>
<arg name="x" type="int" />
<arg name="y" type="int" />
<arg name="physical_width" type="int" />
<arg name="physical_height" type="int" />
<arg name="subpixel" type="int" enum="subpixel" />
<arg name="make" type="string" />
<arg name="model" type="string" />
<arg name="transform" type="int" enum="transform" />
</event>
It seems to be our responsibility to send this event when the output is bound. This is easy enough to add:
static void
wl_output_handle_bind(struct wl_client *client, void *data,
uint32_t version, uint32_t id)
{
struct my_state *state = data;
struct my_output *client_output = calloc(1, sizeof(struct client_output));
struct wl_resource *resource = wl_resource_create(
client, &wl_output_implementation, wl_output_interface.version, id);
wl_resource_set_implementation(resource, wl_output_implementation,
client_output, wl_output_handle_resource_destroy);
client_output->resource = resource;
client_output->state = state;
wl_output_send_geometry(resource, 0, 0, 1920, 1080,
WL_OUTPUT_SUBPIXEL_UNKNOWN, "Foobar, Inc",
"Fancy Monitor 9001 4K HD 120 FPS Noscope",
WL_OUTPUT_TRANSFORM_NORMAL);
add_to_list(state->client_outputs, client_output);
}
Note: wl_output::geometry
is shown here for illustrative purposes, but in
practice there are some special considerations for its use. Review the protocol
XML before implementing this event in your client or server.
Resources represent the server-side state of each client's instance(s) of an object.
A slightly more sophisticated version of our "globals" program called weston-info
is available from the Weston project, if you're interested in something more robust.