天天看点

display:The Wayland Book 节选dmabuf&XDG

source code:https://git.sr.ht/~sircmpwn/wayland-book

本文内容均节选自 《The Wayland Book》

Linux dmabuf

Most Wayland compositors do their rendering on the GPU, and many Wayland clients do their rendering on the GPU as well. With the shared memory approach, sending buffers from the client to the compositor in such cases is very inefficient, as the client has to read their data from the GPU to the CPU, then the compositor has to read it from the CPU back to the GPU to be rendered. The Linux DRM (Direct Rendering Manager) interface (which is also implemented on some BSDs) provides a means for us to export handles to GPU resources. Mesa, the predominant implementation of userspace Linux graphics drivers, implements a protocol that allows EGL users to transfer handles to their GPU buffers from the client to the compositor for rendering, without ever copying data to the GPU. The internals of how this protocol works are out of scope for this book and would be more appropriate for resources which focus on Mesa or Linux DRM in particular. However, we can provide a short summary of its use.

大多数Wayland合成程序都是在GPU上进行渲染的,许多Wayland客户端也在GPU上进行渲染。使用共享内存的方法,从客户端发送缓冲区到compositor在这种情况下是非常低效的,因为客户端必须从GPU读取他们的数据到CPU,然后compositor必须从CPU读取它到GPU被渲染。Linux DRM(直接渲染管理器)接口(也在一些BSDs上实现)为我们提供了一种将句柄导出到GPU资源的方法。Mesa是用户空间Linux图形驱动程序的主要实现,它实现了一个协议,允许EGL用户将句柄从客户端转移到他们的GPU缓冲中进行渲染,而无需将数据复制到GPU。关于这个协议如何工作的内部内容超出了本书的范围,对于那些特别关注Mesa或Linux DRM的资源更合适。但是,我们可以提供它的使用的一个简短的摘要。

  1. Use eglGetPlatformDisplayEXT in concert with EGL_PLATFORM_WAYLAND_KHR to create an EGL display.
  2. Configure the display normally, choosing a config appropriate to your circumstances with EGL_SURFACE_TYPE set to EGL_WINDOW_BIT .
  3. Use wl_egl_window_create to create a wl_egl_window for a given wl_surface .
  4. Use eglCreatePlatformWindowSurfaceEXT to create an EGLSurface for a wl_egl_window .
  5. Proceed using EGL normally, e.g. eglMakeCurrent to make current the EGL context for your surface and eglSwapBuffers to send an up-to-date buffer to the compositor and commit the surface.

Should you need to change the size of the wl_egl_window later, use wl_egl_window_resize

But I really want to know about the internals

Some Wayland programmers who don't use libwayland complain that this approach ties Mesa and libwayland tightly together, which is true. However, untangling them is not impossible - it just requires a lot of work for you in the form of implementing 

linux-dmabuf

 yourself. Consult the Wayland extension XML for details on the protocol, and Mesa's implementation at 

src/egl/drivers/dri2/platform_wayland.c

 (at the time of writing).

For the server

Unfortunately, the details for the compositor are both complicated and out-of-scope for this book. I can point you in the right direction, however: the wlroots implementation (found at 

types/wlr_linux_dmabuf_v1.c

 at the time of writing) is straightforward and should set you on the right path.

XDG shell basics

The XDG (cross-desktop group) shell is a standard protocol extension for Wayland which describes the semantics for application windows. It defines two wl_surface roles: "toplevel", for your top-level application windows, and "popup", for things like context menus, dropdown menus, tooltips, and so on - which are children of top-level windows. With these together, you can form a tree of surfaces, with a toplevel at the top and popups or additional toplevels at the leaves. The protocol also defines a positioner interface, which is used for help positioning popups with limited information about the things around your window. xdg-shell, as a protocol extension , is not defined in wayland.xml . Instead you'll find it in the wayland-protocols package. It's probably installed at a path somewhat like /usr/share/wayland-protocols/stable/xdg-shell/xdg-shell.xml on your system.

XDG(跨桌面组)shell是Wayland的标准协议扩展,它描述了应用程序窗口的语义。它定义了两个wl_surface角色:用于顶级应用程序窗口的“toplevel”,以及用于上下文菜单、下拉菜单、工具提示等内容的“popup”——它们都是顶级窗口的子元素。将这些元素结合在一起,您可以形成一个表面树,在顶部有一个顶层,在叶子上有弹出框或额外的顶层。该协议还定义了一个定位器接口,用于帮助定位窗口周围有限信息的弹出窗口。Xdg-shell作为一种协议扩展,在wayland.xml中没有定义。相反,您将在wayland-protocols中找到它。它可能安装在系统上的路径类似于/usr/share/wayland-protocols/stable/xdg-shell/xdg-shell.xml。

xdg_wm_base is the only global defined by the specification, and it provides requests for creating each of the other objects you need. The most basic implementation starts by handling the "ping" event - when the compositor sends it, you should respond with a "pong" request in a timely manner to hint that you haven't become deadlocked. Another request deals with the creation of positioners, the objects mentioned earlier, and we'll save the details on these for chapter 10. The request we want to look into first is get_xdg_surface 

static void
xdg_wm_base_ping(void *data, struct xdg_wm_base *shell, uint32_t serial)
{
    xdg_wm_base_pong(shell, serial);
}

static const struct xdg_wm_base_listener xdg_wm_base_listener = {
    xdg_wm_base_ping,
};
           

XDG surfaces

XDG toplevel is the interface which we will finally use to display an application window. The XDG toplevel interface has many requests and events for managing application windows, including dealing with minimized and maximized states, setting window titles, and so on. We'll be discussing each part of it in detail in future chapters, so let's just concern ourselves with the basics now.we know that we can obtain an xdg_surface from a wl_surface , but that it only constitutes the first step: bringing a surface into the fold of XDG shell. The next step is to turn that XDG surface into an XDG toplevel — a "top-level" application window, so named for its top-level position in the hierarchy of windows and popup menus we will eventually create with XDG shell. To create one of these, we can use the appropriate request from the xdg_surface interface.

The most important API of xdg-surface is this pair: 

configure

 and 

ack_configure

. You may recall that a goal of Wayland is to make every frame perfect. That means no frames are shown with a half-applied state change, and to accomplish this we have to synchronize these changes between the client and server. For XDG surfaces, this pair of messages is the mechanism which supports this.

xdg-surface最重要的API是这一对:configure和ack_configure。你可能还记得,Wayland的一个目标就是让每个画面都完美。这意味着没有帧显示半应用状态变化,为了完成这一点,我们必须在客户端和服务器之间同步这些变化。对于XDG表面,这对消息就是支持这一点的机制。这个代码只在创建以后的第一帧送去显示的时候出现。也就是为了保证第一帧的同步

static void
handle_xdg_surface_configure(void *data, struct xdg_surface *surface,
                 uint32_t serial)
{
    struct window *window = data;
    static int a=0;

    xdg_surface_ack_configure(surface, serial);

    if (window->wait_for_configure) {
        redraw(window, NULL, 0);
        window->wait_for_configure = false;
    }
}

static const struct xdg_surface_listener xdg_surface_listener = {
    handle_xdg_surface_configure,
};
           

In summary, the following steps will take you from zero to a window on-screen:

  1. Bind to wl_compositor and use it to create a wl_surface .
  2. Bind to xdg_wm_base and use it to create an xdg_surface with your wl_surface .
  3. Create an xdg_toplevel from the xdg_surface with xdg_surface.get_toplevel .
  4. Configure a listener for the xdg_surface and await the configure event.
  5. Bind to the buffer allocation mechanism of your choosing (such as wl_shm ) and allocate a shared buffer, then render your content to it.
  6. Use wl_surface.attach to attach the wl_buffer to the wl_surface .
  7. Use xdg_surface.ack_configure , passing it the serial from configure , acknowledging that you have prepared a suitable frame.
  8. Send a wl_surface.commit request.

Extended example code

The following code is a complete Wayland application which opens an XDG toplevel window and shows a 640x480 grid of squares on it.

#define _POSIX_C_SOURCE 200112L
#include <errno.h>
#include <fcntl.h>
#include <limits.h>
#include <stdbool.h>
#include <string.h>
#include <sys/mman.h>
#include <time.h>
#include <unistd.h>
#include <wayland-client.h>
#include "xdg-shell-client-protocol.h"

/* Shared memory support code */
static void
randname(char *buf)
{
    struct timespec ts;
    clock_gettime(CLOCK_REALTIME, &ts);
    long r = ts.tv_nsec;
    for (int i = 0; i < 6; ++i) {
        buf[i] = 'A'+(r&15)+(r&16)*2;
        r >>= 5;
    }
}

static int
create_shm_file(void)
{
    int retries = 100;
    do {
        char name[] = "/wl_shm-XXXXXX";
        randname(name + sizeof(name) - 7);
        --retries;
        int fd = shm_open(name, O_RDWR | O_CREAT | O_EXCL, 0600);
        if (fd >= 0) {
            shm_unlink(name);
            return fd;
        }
    } while (retries > 0 && errno == EEXIST);
    return -1;
}

static int
allocate_shm_file(size_t size)
{
    int fd = create_shm_file();
    if (fd < 0)
        return -1;
    int ret;
    do {
        ret = ftruncate(fd, size);
    } while (ret < 0 && errno == EINTR);
    if (ret < 0) {
        close(fd);
        return -1;
    }
    return fd;
}

/* Wayland code */
struct client_state {
    /* Globals */
    struct wl_display *wl_display;
    struct wl_registry *wl_registry;
    struct wl_shm *wl_shm;
    struct wl_compositor *wl_compositor;
    struct xdg_wm_base *xdg_wm_base;
    /* Objects */
    struct wl_surface *wl_surface;
    struct xdg_surface *xdg_surface;
    struct xdg_toplevel *xdg_toplevel;
};

static void
wl_buffer_release(void *data, struct wl_buffer *wl_buffer)
{
    /* Sent by the compositor when it's no longer using this buffer */
    wl_buffer_destroy(wl_buffer);
}

static const struct wl_buffer_listener wl_buffer_listener = {
    .release = wl_buffer_release,
};

static struct wl_buffer *
draw_frame(struct client_state *state)
{
    const int width = 640, height = 480;
    int stride = width * 4;
    int size = stride * height;

    int fd = allocate_shm_file(size);
    if (fd == -1) {
        return NULL;
    }

    uint32_t *data = mmap(NULL, size,
            PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);
    if (data == MAP_FAILED) {
        close(fd);
        return NULL;
    }

    struct wl_shm_pool *pool = wl_shm_create_pool(state->wl_shm, fd, size);
    struct wl_buffer *buffer = wl_shm_pool_create_buffer(pool, 0,
            width, height, stride, WL_SHM_FORMAT_XRGB8888);
    wl_shm_pool_destroy(pool);
    close(fd);

    /* Draw checkerboxed background */
    for (int y = 0; y < height; ++y) {
        for (int x = 0; x < width; ++x) {
            if ((x + y / 8 * 8) % 16 < 8)
                data[y * width + x] = 0xFF666666;
            else
                data[y * width + x] = 0xFFEEEEEE;
        }
    }

    munmap(data, size);
    wl_buffer_add_listener(buffer, &wl_buffer_listener, NULL);
    return buffer;
}

static void
xdg_surface_configure(void *data,
        struct xdg_surface *xdg_surface, uint32_t serial)
{
    struct client_state *state = data;
    xdg_surface_ack_configure(xdg_surface, serial);

    struct wl_buffer *buffer = draw_frame(state);
    wl_surface_attach(state->wl_surface, buffer, 0, 0);
    wl_surface_commit(state->wl_surface);
}

static const struct xdg_surface_listener xdg_surface_listener = {
    .configure = xdg_surface_configure,
};

static void
xdg_wm_base_ping(void *data, struct xdg_wm_base *xdg_wm_base, uint32_t serial)
{
    xdg_wm_base_pong(xdg_wm_base, serial);
}

static const struct xdg_wm_base_listener xdg_wm_base_listener = {
    .ping = xdg_wm_base_ping,
};

static void
registry_global(void *data, struct wl_registry *wl_registry,
        uint32_t name, const char *interface, uint32_t version)
{
    struct client_state *state = data;
    if (strcmp(interface, wl_shm_interface.name) == 0) {
        state->wl_shm = wl_registry_bind(
                wl_registry, name, &wl_shm_interface, 1);
    } else if (strcmp(interface, wl_compositor_interface.name) == 0) {
        state->wl_compositor = wl_registry_bind(
                wl_registry, name, &wl_compositor_interface, 4);
    } else if (strcmp(interface, xdg_wm_base_interface.name) == 0) {
        state->xdg_wm_base = wl_registry_bind(
                wl_registry, name, &xdg_wm_base_interface, 1);
        xdg_wm_base_add_listener(state->xdg_wm_base,
                &xdg_wm_base_listener, state);
    }
}

static void
registry_global_remove(void *data,
        struct wl_registry *wl_registry, uint32_t name)
{
    /* This space deliberately left blank */
}

static const struct wl_registry_listener wl_registry_listener = {
    .global = registry_global,
    .global_remove = registry_global_remove,
};

int
main(int argc, char *argv[])
{
    struct client_state state = { 0 };
    state.wl_display = wl_display_connect(NULL);
    state.wl_registry = wl_display_get_registry(state.wl_display);
    wl_registry_add_listener(state.wl_registry, &wl_registry_listener, &state);
    wl_display_roundtrip(state.wl_display);

    state.wl_surface = wl_compositor_create_surface(state.wl_compositor);
    state.xdg_surface = xdg_wm_base_get_xdg_surface(
            state.xdg_wm_base, state.wl_surface);
    xdg_surface_add_listener(state.xdg_surface, &xdg_surface_listener, &state);
    state.xdg_toplevel = xdg_surface_get_toplevel(state.xdg_surface);
    xdg_toplevel_set_title(state.xdg_toplevel, "Example client");
    wl_surface_commit(state.wl_surface);

    while (wl_display_dispatch(state.wl_display)) {
        /* This space deliberately left blank */
    }

    return 0;
}
           

Surface lifecycle

We mentioned earlier that Wayland is designed to update everything atomically, such that no frame is ever presented in an invalid or intermediate state. Of the many attributes that can be configured for an application window and other surfaces, the driving mechanism behind that atomicity is the wl_surface itself. Every surface has a pending state and an applied state, and no state at all when it's first created. The pending state is negotiated over the course of any number of requests from clients and events from the server, and when both sides agree that it represents a consistent surface state, the surface is committed - and the pending state is applied to the current state of the surface. Until this time, the compositor will continue to render the last consistent state; once committed, will use the new state from the next frame forward.

我们前面提到过,Wayland的设计目的是原子更新所有内容,这样就不会出现无效或中间状态的帧。在可以为应用程序窗口和其他表面配置的许多属性中,原子性背后的驱动机制是wl_surface本身。每个表面都有一个挂起状态和一个应用状态,当它第一次被创建时没有任何状态。挂起状态是在客户端请求和服务器事件的过程中协商的,当双方都同意它代表一致的表面状态时,表面就被提交-挂起状态被应用到表面的当前状态。在此之前,合成器将继续渲染最后的一致状态;一旦提交,将使用新状态从下一帧转发。

Among the state which is updated atomically are:

  • The attached wl_buffer , or the pixels making up the content of the surface
  • The region which was "damaged" since the last frame, and needs to be redrawn
  • The region which accepts input events
  • The region considered opaque [This is used by the compositor for optimization purposes.]
  • Transformations on the attached wl_buffer , to rotate or present a subset of the buffer
  • The scale factor of the buffer, used for HiDPI displays

In addition to these features of the surface, the surface's role may have additional double-buffered state like this. All of this state, along with any state associated with the role, is applied when wl_surface.commit is sent. You can send these requests several times if you change your mind, and only the most recent value for any of these properties will be considered when the surface is eventually committed. When you first create your surface, the initial state is invalid. To make it valid (or to map the surface), you need to provide the necessary information to build the first consistent state for that surface. This includes giving it a role (like xdg_toplevel ), allocating and attaching a buffer, and configuring any role-specific state for that surface. When you send a wl_surface.commit with this state correctly configured, the surface becomes valid (or mapped ) and will be presented by the compositor.

Frame callbacks//保证客户端和server端同步渲染合成的重要机制,而决策权在server端

The simplest way to update your surface is to simply render and attach new frames when it needs to change. This approach works well, for example, with event-driven applications. The user presses a key and the textbox needs to be re-rendered, so you can just re-render it immediately, damage the appropriate area, and attach a new buffer to be presented on the next frame. However, some applications may want to render frames continuously. You might be rendering frames of a video game, playing back a video, or rendering an animation. Your display has an inherent refresh rate , or the fastest rate at which it's able to display updates (generally this is a number like 60 Hz, 144 Hz, etc). It doesn't make sense to render frames any faster than this, and doing so would be a waste of resources - CPU, GPU, even the user's battery. If you send several frames between each display refresh, all but the last will be discarded and have been rendered for naught. Additionally, the compositor might not even want to show new frames for you. Your application might be off-screen, minimized, or hidden behind other windows; or only a small thumbnail of your application is being shown, so they might want to render you at a slower framerate to conserve resources. For this reason, the best way to continuously render frames in a Wayland client is to let the compositor tell you when it's ready for a new frame: using frame callbacks .

When you request a frame callback on a surface, the compositor will send a done event to the callback object once it's ready for a new frame for this surface. In the case of frame events, the callback_data is set to the current time in millisecond, from an unspecified epoch. You can compare this with your last frame to calculate the progress of an animation or to scale input events. 1 With frame callbacks in our toolbelt, why don't we update our application from chapter 7.2 so it scrolls a bit each frame? Let's start by adding a little bit of state to our client_state struct:

--- a/client.c
 +++ b/client.c
 @@ -71,6 +71,8 @@
 struct client_state {
     struct xdg_surface *xdg_surface;
     struct xdg_toplevel *xdg_toplevel;
     /* State */
 +	float offset;
 +	uint32_t last_frame;
  };
 static void wl_buffer_release(void *data, struct wl_buffer *wl_buffer) {


 Then we'll update our draw_frame function to take the offset into account:


 @@ -107,9 +109,10 @@ draw_frame(struct client_state *state)
     close(fd);
       /* Draw checkerboxed background */
 +	int offset = (int)state->offset % 8;
     for (int y = 0; y < height; ++y) {
         for (int x = 0; x < width; ++x) {
 -			if ((x + y / 8 * 8) % 16 < 8)
 +			if (((x + offset) + (y + offset) / 8 * 8) % 16 < 8)
                 data[y * width + x] = 0xFF666666;
            else
                 data[y * width + x] = 0xFFEEEEEE;




 In the main function, let's register a callback for our first new frame:




 @@ -195,6 +230,9 @@ main(int argc, char *argv[])
     xdg_toplevel_set_title(state.xdg_toplevel, "Example client");
     wl_surface_commit(state.wl_surface);
 +	 struct wl_callback *cb = wl_surface_frame(state.wl_surface);
 +	 wl_callback_add_listener(cb, &wl_surface_frame_listener, &state);
 +   while (wl_display_dispatch(state.wl_display)) {
         /* This space deliberately left blank */
     }



 Then implement it like so:



 @@ -147,6 +150,38 @@ const static struct xdg_wm_base_listener xdg_wm_base_listener = {
     .ping = xdg_wm_base_ping,  };
 +   const static struct wl_callback_listener wl_surface_frame_listener;
 +
 +   static void wl_surface_frame_done(void *data, struct wl_callback *cb, uint32_t time)
 +{
 +	/* Destroy this callback */
 +	wl_callback_destroy(cb);
 +
 +	/* Request another frame */
 +	struct client_state *state = data;
 +	cb = wl_surface_frame(state->wl_surface);
 +	wl_callback_add_listener(cb, &wl_surface_frame_listener, state);
 +
 +	/* Update scroll amount at 24 pixels per second */
 +	if (state->last_frame != 0) {
 +		int elapsed = time - state->last_frame;
 +		state->offset += elapsed / 1000.0 * 24;
 +	}
 +
 +	/* Submit a frame for this event */
 +	struct wl_buffer *buffer = draw_frame(state);
 +	wl_surface_attach(state->wl_surface, buffer, 0, 0);
 +	wl_surface_damage_buffer(state->wl_surface, 0, 0, INT32_MAX, INT32_MAX);
 +	wl_surface_commit(state->wl_surface);
 +
 +	state->last_frame = time;
 +}
 +
 +const static struct wl_callback_listener wl_surface_frame_listener = {
 +	.done = wl_surface_frame_done,
 +};
 +
  static void  registry_global(void *data, struct wl_registry *wl_registry,
         uint32_t name, const char *interface, uint32_t version)
           

 Now, with each frame, we'll

  1.   Destroy the now-used frame callback.
  2.   Request a new callback for the next frame.
  3.   Render and submit the new frame.

The third step, broken down, is:

  1.   Update our state with a new offset, using the time since the last frame to scroll at a consistent rate.
  2.   Prepare a new wl_buffer and render a frame for it.
  3.   Attach the new wl_buffer to our surface.
  4.   Damage the entire surface.
  5.   Commit the surface.

Steps 3 and 4 update the pending state for the surface, giving it a new buffer and indicating the entire surface has changed.Step 5 commits this pending state, applying it to the surface's current state, and using it on the following frame. Applying this new buffer atomically means that we never show half of the last frame, resulting in a nice tear-free experience. Compile and run the updated client to try it out for yourself!

 Damaging surfaces[告诉compositor,作为client,我重绘了哪些区域]

You may have noticed in the last example that we added this line of code when we committed a new frame for the surface:

wl_surface_damage_buffer(state->wl_surface, 0, 0, INT32_MAX, INT32_MAX);
           

If so, sharp eye! This code damages our surface, indicating to the compositor that it needs to be redrawn. Here we damage the entire surface (and well beyond it), but we could instead only damage part of it. Let's say, for example, that you've written a GUI toolkit and the user is typing into a textbox. That textbox probably only takes up a small part of the window, and each new character takes up a smaller part still. When the user presses a key, you could render just the new character appended to the text they're writing, then damage only that part of the surface. The compositor can then copy just a fraction of your surface, which can speed things up considerably - especially for embedded devices. As you blink the caret between characters, you'll want to submit damage for its updates, and when the user changes views, you'll likely damage the entire surface. This way, everyone does less work, and the user will thank you for their improved battery life.

Note : The Wayland protocol provides two requests for damaging surfaces: damage and damage_buffer . The former is effectively deprecated, and you should only use the latter. The difference between them is that damage takes into account all of the transforms affecting the surface, such as rotations, scale factor, and buffer position and clipping. The latter instead applies damage relative to the buffer, which is generally easier to reason about.

注意:Wayland协议提供了两个损害表面的请求:damage和damage_buffer。前者damage实际上已被弃用,您应该只使用后者。它们之间的区别是,damage考虑了影响表面的所有转换,如旋转、比例因子、缓冲位置和剪裁。后者反而适用于相对于缓冲区的损害,这通常更容易推理。

static void
surface_damage(struct wl_client *client,
	       struct wl_resource *resource,
	       int32_t x, int32_t y, int32_t width, int32_t height)
{
	struct weston_surface *surface = wl_resource_get_user_data(resource);

	if (width <= 0 || height <= 0)
		return;

	pixman_region32_union_rect(&surface->pending.damage_surface,
				   &surface->pending.damage_surface,
				   x, y, width, height);
}

static void
surface_damage_buffer(struct wl_client *client,
		      struct wl_resource *resource,
		      int32_t x, int32_t y, int32_t width, int32_t height)
{
	struct weston_surface *surface = wl_resource_get_user_data(resource);

	if (width <= 0 || height <= 0)
		return;

	pixman_region32_union_rect(&surface->pending.damage_buffer,
				   &surface->pending.damage_buffer,
				   x, y, width, height);
}
           

Surface regions[绘画几何区域,用于标识]

The wl_region interface defines a group of rectangles, which collectively make up an arbitrarily shaped region of geometry. Its requests allow you to do bitwise operations against the geometry it defines by adding or subtracting rectangles from it.

To make, for example, a rectangle with a hole in it, you could:

  1.   Send wl_compositor.create_region to allocate a wl_region object.
  2.   Send wl_region.add(0, 0, 512, 512) to create a 512x512 rectangle.
  3.   Send wl_region.subtract(128, 128, 256, 256) to remove a 256x256 rectangle from the middle of the region.

These areas can be disjoint as well; it needn't be a single continuous polygon. Once you've created one of these regions, you can pass it into the wl_surface interface, namely with the set_opaque_region and set_input_region requests.

The opaque region is a hint to the compositor as to which parts of your surface are considered opaque. Based on this information, they can optimize their rendering process. For example, if your surface is completely opaque and occludes another window beneath it, then the compositor won't waste any time on redrawing the window beneath yours. By default, this is empty, which assumes that any part of your surface might be transparent. This makes the default case the least efficient but the most correct.

不透明区域是给compositor的一个提示,说明你的表面的哪些部分是不透明的。基于这些信息,他们可以优化他们的渲染过程。例如,如果你的表面是完全不透明的,并且遮挡了它下面的另一个窗口,那么compositor就不会浪费时间在重新绘制你下面的窗口上。默认情况下,这个opaque是空的,这假设您的表面的任何部分可能是透明的。这使得默认情况的效率最低,但却是最正确的。

The input region indicates which parts of your surface accept pointer and touch input events. You might, for example, draw a drop-shadow underneath your surface, but input events which happen in this region should be passed to the client beneath you. Or, if your window is an unusual shape, you could create an input region in that shape. For most surface types by default, your entire surface accepts input.

输入区域指示surface的哪些部分接受指针和触摸输入事件。例如,您可以在表面下面绘制一个下拉阴影,但是在该区域发生的输入事件应该传递给您下面的client。或者,如果您的窗口是一个不寻常的形状,您可以在该形状中创建一个输入区域。默认情况下,对于大多数surface类型,整个surface都接受输入。

Both of these requests can be used to set an empty region by passing in null instead of a wl_region object. They're also both double-buffered - so send a wl_surface.commit to make your changes effective. You can destroy the wl_region object to free up its resources as soon as you've sent the set_opaque_region or set_input_region requests with it. Updating the region after you send these requests will not update the state of the surface.

这两个请求都可用于通过传入null而不是wl_region对象来设置空区域。它们都是双缓冲的——因此发送一个wl_surface.commit以使您的更改有效。在发送set_opaque_region或set_input_region请求后,可以销毁wl_region对象以释放其资源。在发送这些请求后更新区域将不会更新表面的状态。

Subsurface[子表面,基于父表面,因为可以设置相对于父表面的位置,因而可以固定屏幕位置]

There's only one surface role defined in the core Wayland protocol, wayland.xml: subsurfaces. Subsurfaces are surfaces shown on top of their parent surface. They have a position relative to the parent surface - which needn't be constrained by the bounds of their parent surface - and a z-order relative to each other.它们不需要被它们的父表面的边界所约束,也不需要相互之间的z轴顺序。

Once you have a 

wl_subsurface

 object associated with a 

wl_surface

, it becomes a child of that surface. Subsurfaces can themselves have subsurfaces, resulting in an ordered tree of surfaces beneath any top-level surface. Manipulating these children is done through the 

wl_subsurface

 interface:

一旦有了与wl_surface相关联的wl_subsurface对象,它就成为该surface的子对象。子表面本身可以有子表面,从而在任何顶层表面之下形成一个有序的表面树。通过wl_subsurface接口来操作这些子节点:

<request name="set_position">
  <arg name="x" type="int" summary="x coordinate in the parent surface"/>
  <arg name="y" type="int" summary="y coordinate in the parent surface"/>
</request>

<request name="place_above">
  <arg name="sibling" type="object" inter />
</request>

<request name="place_below">
  <arg name="sibling" type="object" inter />
</request>

<request name="set_sync" />
<request name="set_desync" />
           

简而言之,同步和不同步[指的是surface和subsurface之间的提交]请求是非缓冲的,并且立即应用。位置和z轴顺序请求被缓冲,不受表面的sync/desync属性的影响——它们总是与父表面一起提交。在相关联的wl_surface上,其余的表面状态将根据subsurface的sync/desync状态进行提交。 

High density surfaces (HiDPI)

In the past several years, a huge leap in pixel density in high-end displays has been seen, new displays packing twice as many pixels into the same physical area as we've seen in years past. We call these displays "HiDPI", short for "high dots per inch". However, these displays are so far ahead of their "LoDPI" peers that application-level changes are necessary to utilize them properly. By doubling the screen resolution in the same space, we would halve the size of all of our user interfaces if we lent them no special consideration. For most displays, this would make the text unreadable and the interactive elements uncomfortably small.