7. XDG shell 基础


XDG(跨桌面组)shell是Wayland的标准协议扩展,它描述了应用程序窗口的语义。它定义了两个wl_surface角色:“toplevel”,用于顶层应用程序窗口,以及“popup”,用于上下文菜单、下拉菜单、工具提示等顶级窗口的子项。将它们放在一起,您可以形成surface的树,顶级位于顶部,弹出窗口或额外的顶级位于叶子上。该协议还定义了一个定位器接口,用于帮助定位弹出窗口,并限制有关窗口周围事物的信息。

xdg-shell作为协议扩展,不在wayland.xml中定义。相反,您可以在wayland-protocols软件包中找到它。它可能安装在系统上的/usr/share/wayland-protocols/stable/xdg-shell/xdg-shell.xml之类的路径上。

xdg_wm_base

xdg_wm_base是规范中定义的唯一全局变量,它提供了创建所需的其他对象的请求。最基本的实现是从处理“ping”事件开始的,当合成器发送它时,您应该及时使用“pong”请求进行响应,以提示您没有陷入死锁。另一个请求处理定位器的创建,这是前面提到的对象,我们将在第10章中详细介绍它们。我们首先要查看的请求是get_xdg_surface。

7.1 XDG surfaces

在xdg-shell领域中的surface被称为xdg_surface,此接口带来了两种类型的XDG surface(顶级和弹出窗口)共同使用的一小部分功能。每种类型的XDG surface的语义仍然存在很大差异,因此必须通过额外的角色明确指定。

xdg_surface接口提供了其他请求,用于分配更具体的顶级和弹出窗口角色。一旦我们将对象绑定到xdg_wm_base全局,我们就可以使用get_xdg_surface请求获取一个wl_surface的id。

<request name="get_xdg_surface">
  <arg name="id" type="new_id" interface="xdg_surface"/>
  <arg name="surface" type="object" interface="wl_surface"/>
</request>

xdg_surface接口除了包含请求为surface分配更具体的顶级或弹出窗口角色外,还包括一些对两种角色都重要的功能。在继续学习顶级和弹出窗口特定的语义之前,让我们回顾一下这些功能。

<event name="configure">
  <arg name="serial" type="uint" summary="serial of the configure event"/>
</event>

<request name="ack_configure">
  <arg name="serial" type="uint" summary="the serial from the configure event"/>
</request>

xdg_surface最重要的API是一对configure和ack_configure。您可能还记得Wayland的目标是使每一帧都完美无缺。这意味着不会显示一半应用状态更改的帧,为了实现这一点,必须在客户端和服务器之间同步这些更改。对于XDG surface,这对消息是支持此目标的机制。

现在我们只介绍基础知识,因此我们将总结这两个事件的重要性如下:来自服务器的事件会通知您surface的配置(或重新配置),并将其应用于挂起状态。当配置事件到达时,应用挂起的更改,使用ack_configure来确认您已完成操作,并呈现和提交新的一帧。我们将在下一章中展示这一点,并在第8.1章中详细解释。

<request name="set_window_geometry">
  <arg name="x" type="int"/>
  <arg name="y" type="int"/>
  <arg name="width" type="int"/>
  <arg name="height" type="int"/>
</request>

set_window_geometry请求主要用于使用客户端装饰的应用程序,以区分其surface中被视为窗口的一部分和不被视为窗口的部分。最常见的是,这用于排除被认为是窗口一部分的客户端阴影。合成器可以使用此信息来管理自己的行为,以安排和与窗口进行交互。

7.2 应用窗口

We have shaved many yaks to get here(俚语),但现在是时候了:XDG顶级层是用于显示应用程序窗口的接口。 XDG顶级层接口包含许多请求和事件,用于管理应用程序窗口,包括处理最小化和最大化状态、设置窗口标题等。 我们将在以后的章节中详细讨论它的每个部分,所以现在只关心基础知识。

根据上一章的知识,我们知道可以从wl_surface获取xdg_surface,但这只是第一步:将surface带入XDG shell的怀抱。 下一步是将该XDG surface变成XDG顶级层-“顶级”应用程序窗口,之所以这样命名,是因为它在最终使用XDG shell创建的窗口和弹出菜单层次结构中的顶级位置。 要创建其中一个,我们可以使用xdg_surface接口的适当请求:

<request name="get_toplevel">
  <arg name="id" type="new_id" interface="xdg_toplevel"/>
</request>

这个新的xdg_toplevel接口为我们提供了许多请求和事件,用于管理应用程序窗口的生命周期。第10章深入探讨了这些,但我知道你迫不及待地想在屏幕上看到一些东西。如果你按照以下步骤操作,处理前一章讨论的XDG surface的configure和ack_configure装备,并将wl_buffer附加到我们的wl_surface并提交,应用程序窗口将出现并将缓冲区内容呈现给用户。下一章提供了示例代码,实现了上述操作。此外,它还利用了我们尚未介绍的一个额外的XDG顶级请求:

<request name="set_title">
  <arg name="title" type="string"/>
</request>

这应该是相当显而易见的。还有一个类似的,我们没有在示例代码中使用,但可能适合你的应用程序:

<request name="set_app_id">
  <arg name="app_id" type="string"/>
</request>

标题通常显示在窗口装饰、任务栏等中,而应用程序ID用于标识你的应用程序或将你的窗口组合在一起。你可以通过将窗口标题设置为“应用程序窗口-Wayland协议-Firefox”,并将应用程序ID设置为“firefox”来利用这些功能。

总之,以下步骤将帮助你从零开始在屏幕上创建一个窗口:

  • 绑定到wl_compositor并使用它创建一个wl_surface。
  • 绑定到xdg_wm_base并使用它创建一个带有你的wl_surface的xdg_surface。
  • 从xdg_surface使用xdg_surface.get_toplevel创建一个xdg_顶级层。
  • 为xdg_surface配置一个监听器并等待configure事件。
  • 绑定到你选择的缓冲区分配机制(例如wl_shm)并分配一个共享缓冲区,然后将你* 的内容渲染到其中。
  • 使用wl_surface.attach将wl_buffer附加到wl_surface。
  • 使用xdg_surface.ack_configure,传递来自configure的序列号,确认你已经准备好了一帧合适的画面。
  • 发送一个wl_surface.commit请求。

翻到下一页以查看这些步骤的演示。

7.3 扩展示例代码

使用到目前为止我们所学的知识,我们现在可以编写一个Wayland客户端,以便在屏幕上显示一些内容。以下代码是一个完整的Wayland应用程序,它打开一个XDG顶级窗口,并在其上显示一个640x480的方块网格。像这样编译它:

wayland-scanner private-code \
  < /usr/share/wayland-protocols/stable/xdg-shell/xdg-shell.xml \
  > xdg-shell-protocol.c
wayland-scanner client-header \
  < /usr/share/wayland-protocols/stable/xdg-shell/xdg-shell.xml \
  > xdg-shell-client-protocol.h
cc -o client client.c xdg-shell-protocol.c -lwayland-client -lrt

然后运行./client以查看其运行情况,或使用WAYLAND_DEBUG=1 ./client以包含大量有用的调试信息。Tada!在以后的章节中,我们将在此基础上构建客户端,因此请将此代码保存在安全的地方。

#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;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
根据提供的信息,“installing : xdg-utils-1.1.0-0.16.20120809git.1.alios7.noarch 87/92 installing(正在安装:xdg-utils-1.1.0-0.16.20120809git.1.alios7.noarch 87/92)”是在进行软件安装过程中的显示信息。 这段信息表示正在安装一个名为xdg-utils的软件包。软件包的版本号为1.1.0,构建号为0.16,构建日期为2012年8月9日。包的类型为noarch,适用于任何体系结构。该软件包是由alios7构建,并且该软件包在安装顺序中是在总共92个软件包中的第87个。 安装软件包的目的是为了增强Linux系统的桌面集成功能。xdg-utils是一个由freedesktop.org提供的软件套件,提供一系列与桌面环境集成相关的实用工具。通过安装xdg-utils,用户可以更方便地进行程序的启动、文件的打开和浏览器的调用等操作。 在安装过程中,系统会检查软件包的依赖关系,以确保所需的其他软件包已经安装并满足要求。安装过程还可能涉及从软件源或安装媒体下载软件包文件,并将其解压到相应的目录中。 安装软件包的具体过程通常是自动化的,用户只需要等待安装完成。在安装过程中可能还会显示其他信息,如进度百分比或其他配置选项。完成安装后,用户可以根据软件包的使用指南来使用新安装的软件。 总而言之,“installing : xdg-utils-1.1.0-0.16.20120809git.1.alios7.noarch 87/92 installing”是软件安装过程中的显示信息,表示正在安装xdg-utils软件包,该软件包是用于增强Linux系统的桌面集成功能的实用工具套件。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值