Wayland 的上层设计

你的电脑有输入和输出设备,它们各自负责接收你的信息并将其显示给你。输入设备例如:

  • 键盘
  • 麦克风
  • 触摸板
  • 触摸屏
  • 数位板

而输出设备通常是桌面上的显示器、笔记本或其他移动设备的屏幕。这些显示资源在你的应用程序之间共用,而 Wayland 混成器在其中起到给客户端分派输入事件并在合适位置显示程序窗口的作用。将所有应用程序窗口组合在一起显示在屏幕上的过程被称为“混成”,因此我们将执行此操作的软件称作混成器。

现状

有许多不同的软件组成了桌面生态系统。诸如用于渲染的 Mesa 及其Linux KMS/DRM 子系统,负责缓存分配的 GBM (Generic Buffer Management 通用缓冲区管理),用户空间库 libdrm, libinput, evdev 等。但是不用担心,理解 Wayland 几乎不需要具备这些体系的专业知识,并且这些内容都大大超出了本书范围。事实上,Wayland 协议是相当保守和抽象的,很容易就能构建出一个基于 Wayland 的桌面并且大多数软件无需额外实现什么就能运行。话虽如此,但从表面上理解这些部分是什么,以及它们是如何工作,仍是十分有用的。让我们自底向上逐步展开。

硬件部分

一台典型的计算机配备了一些重要的硬件。在机箱外面,我们还有显示器、键盘、鼠标,或许还有麦克风和一个可爱的 USB 保温杯。机箱内部有一系列与这些设备接口相连的组件。例如可能你的键盘和鼠标正在使用由系统专有控制器负责的 USB 接口,你的显示器正连接着 GPU。

这些系统有他们自己的任务和状态。例如,GPU 有以显存形式提供的像素缓冲区,并将这些像素扫描输出到显示器上。GPU 还提供经过特调整的处理器,它们虽然在其他方面有所不如,但可以很好地处理高度并行的任务(例如为 1080P 显示器上的 2,073,600 个像素计算正确的颜色)。USB 控制器的工作同样复杂的令人称奇,它要实现枯燥的 USB 规范以接受来自键盘的输入事件,或精心调控杯垫的温度,从而避免诉讼和让你感到不快的冷咖啡。

在这个层面上,硬件几乎不了解系统上正在运行哪些应用程序。硬件提供了执行工作的命令接口,并告知相应的操作——而不在乎是谁发出的。因此,只允许一个组件与之对话......

内核部分

这一责任落到了内核身上。内核是一头复杂的“野兽”,因此我们只关注与 Wayland 相关的部分。Linux 内核的任务是提供一个抽象的硬件,因此可以在用户态安全的访问它们,我们的 Wayland 混成器也同样运行在用户态。对于图形(称为 DRM 或直接渲染管理器 direct rendering manager)来说,可以在用户态有效地为 GPU 分配任务。DRM 另一个重要的子系统是 KMS(kernel mode setting),其用于枚举显示设备并为其设置属性,例如其选定的分辨率(也称为“模式”)。输入设备通过名为 evdev 的接口进行抽象。

大多数内核接口都可以以特殊的设备文件形式存在于 /dev 供用户态调用。以 DRM 为例,这些文件位于 /dev/dri/ 中,通常以主要节点 primary node(例如 card0)的形式进行特权操作(如模式设置),且以渲染节点 render node(如 renderD128)的形式进行非特权操作(如渲染或视频解码),而对于设备节点 device nodes 则为 /dev/input/event*

$ ls /dev/dri/
by-path  card0  renderD128

用户态

现在我们来看用户态。在这里,应用程序于硬件隔离,必须通过内核提供的设备节点才能运行。

libdrm

大多数 Linux 内核接口都有一个对应的用户态,它为使用这些设备节点提供了令人满意的 C 语言 API。libdrm 库是其中之一,它是 DRM 子系统的用户态部分。Wayland 混成器使用它进行模式设置和其他 DRM 操作,但 Wayland 客户端通常不直接使用 libdrm。

Mesa

Mesa 是 Linux 图形栈中最为重要的部分之一。它除了为 Linux 提供 OpenGL(和 Vulkan)的厂家优化实现之外,还提供了 GBM(Generic Buffer Management)库,这是一种在 libdrm 之上的抽象层,用于在 GPU 上分配缓冲区。大多数 Wayland 混成器将通过 Mesa 同时使用 GBM 和 OpenGL,且多数客户端至少使用 OpenGL 或 Vulkan 其中一种实现。

libinput

如同 libdrm 是 DRM 子系统的抽象那样,libinput 提供了 evdev 用户态的抽象。它负责从内核接收输入设备的输入事件,将其解码为可用的形式,并传递给 Wayland 混成器。混成器需要特殊的权限才能使用 evdev 设备文件,从而迫使 Wayland 客户端通过混成器接收输入事件,这样可以防止键盘被记录等。

(e)udev

用户空间负责初步处理来自内核的新设备,在 /dev 中配置目标设备节点的权限,并将这些更改的信息发送给系统上正在运行的程序。大多数系统使用 udev(或 eudev)。Wayland 混成器使用 udev 枚举输入设备和 GPU,并在出现新设备或者拔出旧设备的时候接收通知。

xkbcommon

XKB(X Keyboard 的缩写)是 Xorg 服务的原始键盘处理子系统。几年前,开发者把它从 Xorg 代码树中提取出来,并做成了一个独立的键盘处理库且不再与 X 有任何实际的联系。Libinput(以及 Wayland 混成器)以扫描码的形式提供键盘事件,其准确含义因键盘而异。xkbcommon 负责将这些扫描到的码转化为有意义的通用键盘符号,如 65 转化为 XKB_KEY_Space。它还包含了一个状态机,该状态机知道在按住 shift 键的同时按下 “1” 会变成 “!”。

pixman

这是一个客户端和混成器都使用的简单库,其可以有效的处理像素缓冲区,使用相交的矩形进行数学运算,以及执行其他类似的像素操作任务。

libwayland

libwayland 是 Wayland 协议最常用的 C 语言实现,它处理许多底层的线协议。同时也提供了一个从 Wayland 协议定义(XML 文件)生成高级代码的工具。我们将在第 1.3 章以及整本书中详细讨论 libwayland。

其他

到目前为止,提到的每个部分在整个 Linux 桌面生态系统中都是一致的。而除此以外还有更多的组件。许多图形应用程序根本不了解 Wayland,而是选择诸如 GTK+、QT、SDL 和 GLFW 之类的库来进行处理。许多混成器选择像 wlroots 这样的软件来抽象它们所负责的部分,而其它的一类混成器则在内部实现所有功能。