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.

1

Resources represent the server-side state of each client's instance(s) of an object.

2

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.