Wayland窗口系统(一)

gewenbin
gewenbin
gewenbin
188
文章
15
评论
2020年12月26日21:06:44 评论 2,459

1. 窗口系统

1.1 窗口系统简介

任何窗口系统的主要组件通常称为显示服务器(Display Server),也可以称作窗口服务器(Window Server)或合成器(Compositor)。在窗口中运行并显示其GUI的任何应用程序都是显示服务器的客户端。显示服务器及其客户端通过通信协议相互通信,通信协议通常称为显示服务器协议(Display Server Protocol)。显示服务器接收并处理输入事件,例如键盘、鼠标或触摸屏并将其传递给正确的客户端,显示服务器还负责将客户端内容输出到显示器。如下所示:

Wayland窗口系统(一)

1.2 不同OS平台的窗口系统

1.2.1 X Window System

X窗口系统用于类Unix操作系统,使用C/S模型,服务器接受图形输出请求并且分发用户输入事件。服务器和客户端之间的通信协议可以通过网络进行操作:客户端和服务器可以在同一台机器上运行,也可以在不同的机器上运行,可能使用不同的架构和操作系统。如下所示:

Wayland窗口系统(一)

1.2.2 Desktop Window Manager

DWM是微软Windows系统中的窗口管理器,它最初是为了实现新的“ Windows Aero ”用户体验的一部分而创建的,这允许诸如透明度,3D窗口切换等效果。DWM以不同的方式工作,具体取决于操作系统(Windows 7或Windows Vista)以及它使用的图形驱动程序版本(WDDM 1.0或1.1)。在Windows 7和WDDM 1.1驱动程序下,DWM仅将程序的缓冲区写入视频RAM,即使它是图形设备接口(GDI)程序。这是因为Windows 7为GDI 支持(有限的)硬件加速,并且这样做不需要在系统RAM中保留缓冲区的副本,以便CPU可以写入它。

Wayland窗口系统(一)

1.2.3 Quartz Compositor

Quartz Compositor是macOS中的显示服务器(同时也是合成窗口管理器)。Quartz 2D,OpenGL,Core Image,QuickTime或其他进程的位图输出被写入特定的内存位置或后备存储区。然后,Compositor从后备存储区中读取数据,并将每个数据组合成一个用于显示的图像,将该图像写入图形卡的帧缓冲存储区。Quartz Compositor只接受栅格化数据,是唯一可以直接访问图形帧缓冲区的进程。

在管理单个窗口时,Quartz Compositor 从其渲染器接受窗口内容的位图图像及其位置。渲染器的选择取决于单个应用程序,尽管大多数使用Quartz 2D。

作为窗口管理器,Quartz Compositor还有一个事件队列,用于接收事件,例如击键和鼠标点击。Quartz Compositor从队列中获取事件,确定哪个进程拥有事件发生的窗口,并将事件传递给进程。

Wayland窗口系统(一)

2. Wayland

2.1 Wayland简介

Wayland是一个合成器与客户端通信的窗口协议,以及该协议的C库实现。合成器可以是在Linux内核模式设置和evdev输入设备,X应用程序或Wayland客户端本身上运行的独立显示服务器。客户端可以是传统应用程序,X服务器或其他显示服务器。

Wayland协议遵循C/S模型,其中客户端是请求在屏幕上显示像素缓冲区的图形应用程序,并且服务器(合成器)是控制这些缓冲区的显示的服务提供者。

2.2 Wayland架构

2.2.1 Wayland整体架构

通过跟踪从输入设备活动到其影响屏幕上的内容这一整个过程来了解Wayland架构是一个不错的方法。如下所示:

Wayland窗口系统(一)

  • 内核获取一个事件并将其发送给合成器
  • 合成器将该事件发送给需要处理该事件的客户端
  • 当客户端收到事件时,它会更新UI作为响应。接着客户端向合成器发送请求以指示其UI区域已经更新
  • 合成器收到客户端UI已更新请求,然后重新合成屏幕内容
2.2.2 Wayland协议架构

Wayland参考实现被设计为两层协议:

  • 一种底层协议,用于处理两个进程(客户端和合成器)之间的进程间通信,以及它们交换的数据的序列化过程。该层是基于消息的,通常使用内核IPC服务实现,比如Unix domain sockets。
  • 构建在其上的高级层协议,用于处理客户端和合成器需要交换的信息,以实现窗口系统的基本功能。该层实现为“异步面向对象的协议”。

Wayland协议的参考实现分为两个库:Wayland客户端调用libwayland-client库,Wayland合成器调用libwayland-server库。如下所示:

Wayland窗口系统(一)

2.2.3 Wayland渲染方式

在Wayland架构中,客户端窗口内容的渲染是由客户端自己负责的,客户端可以使用软件渲染,也可以使用硬件渲染,比如OpenGL。它知道如何编程硬件并直接渲染到缓冲区。合成器反过来可以获取缓冲区,并将其用作纹理用于合成桌面。在初始设置之后,客户端只需要告诉合成器使用哪个缓冲区以及它何时何地将新内容呈现到其中。

这使应用程序有两种方法来更新其窗口内容:

  • 将新内容渲染到新缓冲区并告诉合成器使用新的而不是旧缓冲区。应用程序可以在每次需要更新窗口内容时分配新的缓冲区,或者它可以保留两个(或更多)缓冲区并在它们之间循环使用。缓冲区管理完全受应用程序控制。
  • 将新内容渲染到之前告诉合成器要使用的缓冲区中。虽然可以直接渲染到与合成器共享的缓冲区,但这可能会与合成器产生竞争。可能发生的情况是,重新绘制窗口内容可能会被重构桌面的合成器中断。如果应用程序在清除窗口之后但在渲染内容之前被中断,则合成器将从空白缓冲区进行纹理处理。结果是应用程序窗口将在空白窗口或半渲染内容之间闪烁。避免这种情况的传统方法是将新内容渲染到后台缓冲区,然后从那里复制到合成器。后台缓冲区可以在运行中分配,并且足够大以容纳新内容,或者应用程序可以保留缓冲区。同样,这也是由应用程序控制。

在任何一种情况下,应用程序必须告诉合成器哪个区域包含新内容。当应用程序直接呈现给共享缓冲区时,需要将更新的内容通知合成器。但是,当交换缓冲区时,合成器不会假设任何更改,并且在重新绘制桌面之前需要接受来自应用程序的请求。

2.2.4 Wayland项目组成

Wayland项目主要由4个部分组成:Wayland协议、Wayland参考C库实现、Wayland合成器参考实现Weston、Wayland和Weston一些demo。如下所示:

  • Wayland协议:合成器和客户端通信的协议。
  • Wayland参考C库实现:有4个库,libwayland-client、libwayland-server、libwayland-cursor、libwayland-egl。
  • Weston:主要包括窗口管理(shell),合成器(compositor)和输入管理几个部分。
  • Demo:主要是一些简单的客户端用例,用于测试Wayland协议以及Weston合成器。

Wayland窗口系统(一)

3. Wayland协议

3.1 基本概念

Wayland协议被描述为“异步面向对象的协议”。 面向对象意味着合成器提供的服务是以一系列对象呈现的。每个对象实现一个接口,该接口具有名称,许多方法(称为请求)以及若干相关事件。客户端发往服务端的操作成为请求(request),反之成为一个事件(event)。每个请求和事件都有零个或多个参数,每个参数都有一个名称和一个数据类型。在请求不必等待同步回复或ACK这个意义上,协议是异步的,从而避免了往返延迟时间并提高了性能。如下所示:

Wayland窗口系统(一)

如果对象的接口支持该请求,Wayland客户端可以对某个对象发出请求(方法调用)。客户端还必须为此类请求的参数提供所需的数据。这是客户端从合成器请求服务的方式。合成器通过对象发出事件(也可能带有参数)将信息发送回客户端。这些事件可以由合成器发出,作为对特定请求的响应,或者异步地发生(例如来自输入设备的事件)或状态更改事件。错误事件也由合成器发出。

为了使客户端能够向对象发出请求,它首先需要告诉服务器它将用于标识该对象的ID号。合成器中有两种类型的对象:全局对象和非全局对象。合成器在创建客户端时(以及在销毁它们时)将全局对象通告给客户端,而非全局对象通常由已作为其功能的一部分的其他对象创建。

接口以及其请求和事件是定义Wayland协议的核心元素。协议的每个版本都包含一组接口,以及它们的请求和事件。另外,Wayland合成器也可以定义和实现自己的接口以用来支持新请求和事件,从而将功能扩展到核心协议之外。为了便于更改协议,每个接口除了名称外还包含“版本号”属性,此属性允许区分同一接口的不同变体。

3.2 生成协议头文件

Wayland协议定义为xml格式文件,文件在protocol目录,通信协议实现在src目录。它主要编译出三个库,libwayland-client,libwayland-server和libwayland-cursor。第一个为协议的client端实现,第二个为协议的server端实现,第三个是协议中鼠标相关处理实现。编译时会首先编译出wayland-scanner这个可执行文件,它利用expat这个库来解析xml文件,将wayland.xml生成相应的wayland-protocol.c,wayland-client-protocol.h和wayland-server-protocol.h。它们会被Wayland的client和server在编译时用到。同时wayland-scanner在生成Weston中的Wayland扩展协议中起同样作用。如下所示:

Wayland窗口系统(一)

3.3 协议通信信息格式

Wayland协议是一种异步面向对象的协议。所有请求都是对某些对象的方法调用。请求包含唯一标识服务器上对象的对象ID。每个对象都实现一个接口,请求包含一个操作码,用于标识要调用的接口中的哪个方法。

该协议是基于消息的。客户端发送到服务器的消息称为请求。从服务器到客户端的消息称为事件。消息有许多参数,每个参数都有一定的类型。

每条消息有3个部分组成,如下所示:

Wayland窗口系统(一)

3.3.1 信息头部

信息头部由两个部分组成:

  • 第一个字数据表示发送对象的ID(32位数据)。
  • 第二个字数据由2个16位数据组成。高16位数据表示整条信息的大小,以字节为单位;低16位数据表示发送的请求或者事件的操作码。
3.3.2 信息主体

余下的数据称之为payload区域,存放着请求/事件的参数值。每个参数都是32位数据对齐的,如果不对齐需要填充数据对齐,填充的数值未作规定,默认是0。

传输的参数的类型共有8种,如下所示:

  • int,uint:表示有符号/无符号32位数据的值。
  • fixed:它是带符号的十进制类型,提供1位符号位,23位整数精度和8位小数精度。可以通过相关API和double、int类型数据相互转换。
  • string:以无符号32位长度开始,然后跟着字符串内容,包括终止空字符,然后填充对齐到32位边界。如下所示:

Wayland窗口系统(一)

  • object:32位对象ID。
  • new_id:同样还是32位对象ID。当客户端发起请求时(该请求需要服务端创建新对象以和客户端一一对应),客户端确定对象ID,接着将此ID告知服务端,服务端用此ID创建和客户端一一映射的对象。
  • array:以无符号32位长度开始,然后跟着数组内容,然后填充对齐到32位边界。如下所示:

Wayland窗口系统(一)

  • fd:文件描述符不存储在payload中,而是存储在UNIX domain socket消息的辅助数据中(msg_control)。

3.4 协议接口

协议接口(interface)是客户端和服务端交互的入口。每个接口都有自己的名字、版本、方法表(request请求表)、事件表(event事件表)等信息。Wayland协议包含了一系列核心接口,比如wl_display、wl_registry等。通过一个给定的接口就可以知道该接口有哪些方法和事件,这是最重要的两个属性。

3.4.1 wl_display

我们主要关注下接口中有哪些reauest和event。

Request:

  • sync:同步请求。该请求返回一个wl_callback对象。这个接口的作用是提供客户端一个确认它前面所有的请求都被处理的保证。
  • get_registry:此请求创建并返回一个wl_registry对象,该对象允许客户端列出并绑定合成器中可用的全局对象(服务)。

Event:

  • error:用于发生不可恢复错误时发送错误事件。
  • delete_id:当客户端删除对象时,服务器将发送此事件以确认客户端已看到删除请求。当客户端收到此事件时,它将知道它可以安全地重用对象ID。
3.4.2 wl_registry

Request:

  • bind:使用指定的名称作为标识符,将新的客户端创建的对象绑定到服务器。

Event:

  • global:通知客户端服务端有哪些全局对象。
  • global_remove:通知客户端已删除的全局对象。此事件通知客户端全局对象不再可用。如果客户端使用绑定请求绑定到全局对象,则客户端现在应该销毁该对象。该对象仍然有效,但对该对象的请求将被忽略,直到客户端销毁该对象。
3.4.3 wl_callback

Request:

  • 无。

Event:

  • done:完成相关请求后通知客户端。
3.4.4 wl_compositor

Request:

  • create_surface:让合成器创建一个新的surface,此请求返回一个wl_surface对象。此surface表示窗口中的内容,不代表窗口本身,窗口本身由另外一个对象表示。
  • create_region:让合成器创建一个新的region,此请求返回一个wl_surface对象。这个对象的作用是描述一块wl_surface 上的区域,以便对这个区域进行操作。

Event:

  • 无。
3.4.5 wl_shm_pool

wl_shm_pool封装了合成器和客户端之间共享的一块内存。通过wl_shm_pool,客户端可以分配共享内存wl_buffer。通过同一个池创建的所有对象共享相同的底层映射内存。重复利用映射内存在交互式调整surface大小或许多小缓冲区时非常有用。

Request:

  • create_buffer:通过共享内存的空间来创建窗口绘制使用的 wl_buffer。
  • destroy:请求销毁共享内存池。当从此池创建的所有缓冲区都消失后,将释放已经映射的内存。
  • resize:此请求将使服务器从使用新的大小重新映射共享内存。此请求只能用于使池更大的请求。

Event:

无。

3.4.6 wl_shm

Request:

  • create_pool:创建一个新的wl_shm_pool对象。该池可用于创建基于共享内存的缓冲区对象。服务器和客户端通过mmap文件描述符fd来实现内存共享。

Event:

  • format:通知客户端有关可用于缓冲区的有效像素格式。已知格式包括argb8888和xrgb8888。
3.4.7 wl_buffer

缓冲区为wl_surface提供内容。缓冲区是通过接口创建,例如wl_drm,wl_shm等。它具有宽度和高度,可以附加到wl_surface,但是客户端提供和更新内容的机制由缓冲区接口定义。

Request:

  • destroy:请求销毁一个wl_buffer。

Event:

  • release:当合成器不再使用此wl_buffer时发送此事件。客户端然后可以自由地重用或销毁此缓冲区及其后备存储区。
3.4.8 wl_shell

这个是管理真正窗口的服务接口类。 通过这个接口,可以创建某一个wl_surface显示用窗口。目前该接口已经被官方放弃,建议使用xdg_shell替代。

Request:

  • get_shell_surface:请求创建指定wl_surface 的显示窗口对象wl_shell_surface。 一般情况下,一个wl_surface对应一个wl_shell_surface。

Event:无。

3.4.9 wl_shell_surface

此接口提供了处理窗口的请求,比如设置为顶层窗口、最大化、全屏化等。

Reauest:

  • pong、move、resize、set_toplevel、transient、set_transient、set_fullscreen、set_popup、set_maximized、set_title、set_class。

Event:

  • ping、configure、popup_done。
3.4.10 wl_surface

surface是显示在屏幕上的矩形区域。它具有位置,大小和像素内容。此接口提供对窗口内容的操作,比如销毁、缩放等。

Request:

  • destroy、attach、damage、frame、set_opaque_region、set_input_region、set_buffer_transform、set_buffer_scale、damage_buffer、commit。

Event:

  • enter、leave。

3.5 协议对象

协议对象在服务端和客户端是一一对应的,并且协议对象是对接口的进一步封装。每个对象都有一个ID,此ID在服务端和客户端是相同的(其实就是数组下标),用于将两边的对象进行一一映射。

对象在服务端分为两类,全局对象和非全局对象。全局对象代表着服务端能提供的服务,比如wl_compositor、wl_shm、wl_shell等。非全局对象由wl_display接口对象创建出来。

gewenbin
  • 本文由 发表于 2020年12月26日21:06:44
  • 转载请务必保留本文链接:http://www.databusworld.cn/9895.html
匿名

发表评论

匿名网友 填写信息

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen: