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 resource when the global is bound. In terms of code, the result looks something like this:

使用 libwayland-server 注册全局变量的方式(与 client 相比)有些不同。在使用 wayland-scanner 生成 “服务端代码” 时,它会创建接口(类似于侦听器)和用于发送事件的粘合代码。(服务端的)第一个任务 是注册 golbal,实现一个在 global 被绑定时,能够装配 资源1的函数。代码如下所示:

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.

如果你把这段代码以打 patch 的形式塞入第 4.1章展示的服务器代码里,使用上次我们写的 “global” 程序2, 将会(在输出 global 名称时)看见 wl_output。但是,就目前而言,任何绑定到这个 global 的动作都会得到 TODO。 我们需要填补这个函数,实现 wl_output 接口。

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_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;

    // 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.

这(段代码)里面有很多东西,让我们逐一解释。在最下面,我们扩展了 “bind” 处理函数,创建一个 wl_resource 来跟踪 服务端的 object 的状态(使用客户端分配的 ID)。我们为 wl_resource_create 提供指向 wl_output_implementation 的指针, (wl_output_implementation 是这个 global 的接口实现),它是一个 static 类型的 struct。(struct wl_output_interface) 由wayland-scanner 生成,并为接口支持的每个请求包含一个函数调用指针。我们还利用这个机会分配一个小容器来存储我们需要的、 libwayland 不能为我们处理的任何附加状态,它的具体性质因协议的不同而不同。

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).

注意:这里有两个不同的东西具有相同的名称:struct wl_output_interface 是一个接口的实例, (而 bind 函数中的 wl_output_interface.version中的)wl_output_interface 是由 wayland-scanner 生成的全局结构体, 它包含与实现相关的基本数据(例如版本,如上例子)。

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.

我们的 wl_output_handle_release 函数在客户端发送 release 请求时调用,表明(client)他们不再需要这个资源——所以我们销毁它。 这反过来会触发 wl_output_handle_resource_destroy 函数,稍后我们将扩展该函数以释放我们之前为其分配的任何状态。 此函数也作为析构函数传递到 wl_resource_create 中,如果客户端终止而未显式发送释放请求,也将调用该函数。

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:

我们代码中剩下的另一个 “TODO” 是发送 “name” 事件,以及其他一些事件。如果我们查看 wayland.xml,会在接口定义部分看到这个事件:

<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:

我们有责任在 output 被绑定时发送这个事件,添加实现也很容易:

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.

注意:这里展示的 wl_output::geometry 是为了说明问题,但在实际使用场景中,它的使用有一些特殊的考虑。 在客户端或服务器中实现此事件之前,请检查协议XML。

原文链接:https://wayland-book.com/

  1. 资源代表每个客户端的实例(object)的服务器端状态。 

  2. 如果你对更加强大的相关实现感兴趣,可以从 Weston 项目中获得一个实现稍微复杂的名为 “weston_info” 的 “global” 程序。  2