5.2 Wayland book Registering globals
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/