Wayland窗口系统

本文详细介绍了窗口系统的基础知识,包括XWindowSystem、Windows的DesktopWindowManager和macOS的QuartzCompositor。重点讲解了Wayland窗口协议,包括其基本概念、架构、通信方式、协议结构以及Wayland客户端和服务器的交互过程。此外,还分析了Wayland的协议实现和对象管理,以及客户端和服务器如何通过共享内存进行高效通信。
摘要由CSDN通过智能技术生成
1. 窗口系统
1.1 窗口系统简介

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

1.2 不同OS平台的窗口系统
1.2.1 X Window System

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

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可以写入它。

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从队列中获取事件,确定哪个进程拥有事件发生的窗口,并将事件传递给进程。

2. Wayland
2.1 Wayland简介

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

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

2.2 Wayland架构
2.2.1 Wayland整体架构

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

  • 内核获取一个事件并将其发送给合成器

  • 合成器将该事件发送给需要处理该事件的客户端

  • 当客户端收到事件时,它会更新UI作为响应。接着客户端向合成器发送请求以指示其UI区域已经更新

  • 合成器收到客户端UI已更新请求,然后重新合成屏幕内容

2.2.2 Wayland协议架构

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

  • 一种底层协议,用于处理两个进程(客户端和合成器)之间的进程间通信,以及它们交换的数据的序列化过程。该层是基于消息的,通常使用内核IPC服务实现,比如Unix domain sockets。

  • 构建在其上的高级层协议,用于处理客户端和合成器需要交换的信息,以实现窗口系统的基本功能。该层实现为“异步面向对象的协议”。

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

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合成器。

3. Wayland协议
3.1 基本概念

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

如果对象的接口支持该请求,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扩展协议中起同样作用。如下所示:

3.3 协议通信信息格式

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

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

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

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位边界。如下所示:

  • object:32位对象ID。

  • new_id:同样还是32位对象ID。当客户端发起请求时(该请求需要服务端创建新对象以和客户端一一对应),客户端确定对象ID,接着将此ID告知服务端,服务端用此ID创建和客户端一一映射的对象。

  • array:以无符号32位长度开始,然后跟着数组内容,然后填充对齐到32位边界。如下所示:

  • 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接口对象创建出来。

4. Wayland协议实现浅析
4.1 几个重要的数据结构
4.1.1 wl_object

wl_object是一个很重要的数据结构,在客户端和服务端都有此数据结构的封装,是wl_proxy、wl_resource数据结构的第一个成员,wayland中所有对象的概念都是基于wl_object而言的。wl_object包括interface、implementation、id三个重要成员,每个成员的作用如下:

  • interface:是客户端和服务端交互的接口。Interface主要包含以下几项内容:

  1. name:接口的名字。

  1. version:此接口的版本号。

  1. request信息表:用于表明有哪些request以及每项request具体参数的类型。

  1. event信息表:类似request信息表,用于表明有哪些event事件以及每项event具体参数的类型。

  • implementation:在服务端此成员表示request方法的具体实现,在客户端此成员表示event事件的具体实现。

  • id:该object的ID,用于client/server端索引此object,服务端和客户端就是通过此ID建立映射。

如下所示:

4.1.2 wl_proxy

这是wl_object在客户端的封装,wayland客户端的对象都是对wl_proxy进一步的封装。wl_proxy主要包含以下成员:

  • object:wl_object,通过此成员可以获得interface等信息。

  • display:wl_display,服务端和客户端启动后最先创建的就是wl_display。

  • version:对象的版本。

4.1.3 wl_resource

这是wl_object在服务端的封装,wayland服务端的对象都是对wl_resource进一步的封装。wl_resource主要包含以下成员:

  • object:wl_object,通过此成员可以获得interface等信息。

  • destroy:用于对象销毁时回调的销毁函数。

  • link:用于将对象连接成链表。

  • client:wl_client,每有一个客户端连接到服务端时,在服务端就会创建一个wl_client结构,用于表示客户端的信息。

4.1.4 wl_global

wl_global用于表示服务端的全局对象,一个全局对象就表示服务端提供的一个服务,比如wl_compositor、wl_shm、wl_shell等。wl_global主要包括以下内容:

  • display:wl_display,服务端和客户端启动后最先创建的就是wl_display。

  • interface:wl_interface,客户端和服务端交互的接口。

  • name:全局对象的名字。

  • version:全局对象的版本。

  • bind:在wl_registry接口进行bind这个request这个操作时会回调wl_global中的bind回调函数。

  • link:用于将全局对象连接成链表。

4.1.5 wl_array

动态数组,数组中的每一项内容都是服务端和客户端对象数据结构的地址,也就是说,wl_array其实就是一个指针数组。在服务端和客户端的对象通过数组下标建立映射关系,也就是说,同一个对象在服务端和客户端数组中的位置是相同的,这样在client/server两端进行对象传递时,只需要传递数组下标就可以了。当数组内容满了的时候,如果继续插入数据会自动扩充数组的大小。wl_array的内容如下:

  • size:数组已经使用的空间大小。

  • alloc:整个数组的空间大小。

  • data:实际数据缓存区地址。

client/server端的对象映射关系如下所示:

需要注意的是,不管服务端还是客户端,映射表中的第0项都是空的,新的对象从第一项开始。

4.1.6 wl_map

wl_map是对wl_array的封装,服务端和客户端都有此数据结构,主要成员如下:

  • client_entries:wl_array类型,客户端使用。

  • server_entries:wl_array类型,服务端使用。

  • side:用于表示当前的wl_map是服务端还是客户端的。

4.1.7 wl_buffer

wl_buffer用于缓存客户端和服务端通信数据,通信数据格式见3.3章节。此buffer不同是环形缓冲区,不同于wl_array,如果填满了会回到起始处继续填充。wl_buffer包含的内容如下:

  • data[4096]:这是4KB的缓存区。

  • head:用于表示当前缓存中待发送数据的位置。

  • tail:用于表示当前缓存中已经发送完后数据的位置。

4.1.8 wl_connection

wl_connection是对wl_buffer进一步的封装,当服务端和客户端需要发送和接受数据时,都会对wl_connection数据结构进行操作。wl_connection的内容如下:

  • in:wl_buffer类型,用于缓存接受到的数据。

  • out:wl_buffer类型,用于缓存发送的数据。

  • fds_in:wl_buffer类型,如果要接收的数据有文件描述符类参数,缓存在fds_in中。

  • fds_out:wl_buffer类型,如果要发送的数据有文件描述符类参数,缓存在fds_out中。

  • fd:表示已经建立连接的socket fd。

  • want_flush:表示缓存区中的数据是否需要强制发送。

客户端和服务端通信的示意图如下所示:

4.1.9 wl_event_source

wl_event_source由服务端使用,服务端启动后会通过epool机制等待一系列的事件信息,wl_event_source就表示某个等待的事件的信息,其内容如下:

  • interface:wl_event_source_interface类型,当事件激活时会调用interface中的dispatch函数。

  • loop:wl_event_loop类型,用于描述服务端循环等待事件的epool fd相关信息。

  • link:用于形成链表。

  • fd:等待的事件fd。

服务端真正使用的表示事件资源的数据结构会对wl_event_source做进一步封装,比如wl_event_source_fd,表示等待fd可读可写事件。wl_event_source_fd有一个func成员,此成员会被wl_event_source中的interface中的dispatch函数调用,比如当有客户端链接socket fd时,此回调函数是socket_data,当客户端有数据通信时,此回调函数是wl_client_connection_data。

4.1.10 wl_closure

wl_closure用于表示函数调用信息和要调用的函数的参数信息。此数据结构是一个非常重要的数据结构,客户端和服务端在发送前都会先将数据组织到wl_closure数据结构中,然后由此数据结构解析到wl_connection中进行发送;接收时会将wl_connection中的原始数据解析成wl_closure数据结构,然后进行函数调用。

wl_closure内容如下:

  • count:表示要调用的函数中的参数个数。

  • message:表示request/event信息表。

  • opcode:表示request/event信息表中的哪一个。

  • sender_id:发送对象的ID。

  • args[20]:函数调用的每个参数值都会存放在args数组中,每次最多20个参数。

  • link:用于形成链表。

  • proxy:表明是哪个对象。

通过wl_closure进行数据通信过程示意如下所示:

4.2 几个重要的链表
4.2.1 global_list

global_list用于将服务端的所有全局对象连接成链表,在客户端进行获取注册表操作时会将全局对象链表中的每一个全局对象信息发送给客户端。global_list链表头是在服务端的wl_display数据结构中,如下所示:

4.2.2 socket_list

服务端使用,socket_list用于将所有监听的socket信息连接成链表,一般情况下链表中只有一个监听的socket,名字默认为“wayland-0”,如下所示:

4.2.3 client_list

服务端使用,每当有一个客户端和服务端连接时,服务端就会创建一个wl_client数据结构,用于表示连接信息。wl_client主要包含以下内容:

  • wl_connection:发送/接收数据都是缓存在wl_connection中。

  • wl_event_source_fd:用于表示已经连接的client fd信息。

  • wl_resource:服务端的wl_display对象。

  • link:用于形成链表。

  • wl_map:用于client/server进行映射对象。

如下所示:

4.2.4 registry_resource_list

服务端使用,每当客户端发送get_registry这个request时,服务端都会创建一个新的wl_ resource对象,这些wl_ resource对象通过registry_resource_list形成链表,如下所示:

4.2.5 display_queue

客户端使用,当客户端收到服务端发送的数据后,将其解析成wl_closure数据结构,然后将wl_closure插入到display_queue中。随后在客户端dispatch_queue的时候会将display_queue中的wl_closure取出解析进行函数调用。如下所示:

4.2.6 default_queue

default_queue作用和display_queue一样,只是在收到服务端发送的数据后,如果解析出的发送对象proxy就是客户端wl_display中的proxy,那么就会将wl_closure插入到display_queue中,否则插入到default_queue中。

dispatch_queue时会首先处理display_queue中的wl_closure,然后再处理default_queue中的wl_closure。

4.3 跨进程调用

客户端和服务端跨进程进行函数调用的大致流程如下所示:

4.3.1 wl_closure_marshal()

创建wl_closure数据结构,并根据具体的参数初始化。客户端/服务端在发送数据之前都需要调用此函数将要发送的参数等信息组织到wl_closure数据结构中。

4.3.2 wl_connection_write()

将wl_closure数据结构解析成具体要发送的数据填入到wl_connection中。在这个函数中会将对象指针替换成对象ID,以及对fd类型参数做特殊处理等。

4.3.3 wl_conntction_flush()

将wl_buffer中缓存的数据通过sendmsg接口发送出去。

4.3.4 wl_connection_read()

通过recvmsg接口将socket中的数据接收到wl_buffer中。

4.3.5 wl_closure_demarshal()

将wl_connection缓存中的数据解析成wl_closure数据结构,用来传递给wl_closure_invoke进行函数调用。每一条IPC信息都会被解析成一个wl_closure数据结构。

4.3.6 wl_closure_invoke()

根据wl_closure数据结构进行真正的函数调用,注意,这个函数中使用了libffi接口以作为函数调用的跳板。

4.4 Server端IPC信息循环处理流程
  • Server端在进行一些初始化操作之后会阻塞在wl_event_loop_dispatch中的epool_wait上。

  • epool_wait主要在等待两个事件:

  1. listen socket fd,如果有client连接,调用socket_data处理。

  1. connect socket fd,如果有client传送数据,调用wl_client_connection_data处理。

4.5 Client端IPC信息循环处理流程
  • Client端在进行初始化后调用wl_display_dispatch_queue。

  • Client阻塞在wl_display_poll中的poll上,等待server端发送数据。

  • 如果server端有数据传送,则调用read_events,最终调用到queue_event,主要做以下几件事:

  1. 将收到的数据组织成wl_closure数据结构。

  1. 将wl_closure插入到queue->event_list中。

  1. 这里有两个queue:display_queue和default_queue。具体插到哪个queue请参见2.5和4.2.6章节。

  • 调用dispatch_queue,首先处理display_queue,然后处理default_queue。主要是将queue中的wl_closure逐个进行解析,然后进行真正的函数调用。

4.6 基于SHM机制传递窗口
  • 客户端首先打开一个临时文件。

  • 然后通过ftruncate改变文件的大小,这个大小根据实际的窗口大小决定。

  • 客户端mmap临时文件fd,客户端通过socket机制将此fd发送给服务端。

  • 服务端收到fd后,同样mmap此fd,那么客户端和服务端分别使用mmap后获得的地址来访问同一块共享内存区。

  • 客户端对共享内存区进行渲染绘制,完成后通知服务端。

  • 服务端拿到共享内存区数据进行合成。

5. Demo合成器、客户端实现简析
5.1 demo合成器分析
5.1.1 wl_display_create

此函数主要进行如下工作:

  • 分配wl_display结构体空间。

  • 初始化event_loop,event_loop是服务端用于循环处理各种等待事件的机制。

  • 初始化global_list、socket_list等各种链表。

  • 初始化shm formats array,这个数组中记录了服务端将以何种格式解析共享内存区中的数据。

5.1.2 wl_display_add_socket_auto

此函数主要进行如下工作:

  • 创建wayland lock文件并锁住此文件,一般名为wayland-0.lock。

  • 创建、绑定、监听socket,名字一般为wayland-0。

  • 将上个步骤创建的wayland socket fd加入到epool中监听。

  • 将wl_socket加入到display的socket_list链表中。

5.1.3 wl_global_create

这个函数负责创建全局对象并将其插入到链表中。

  • 对interface的版本做检查。

  • 创建struct wl_global结构体空间并初始化。

  • 将全局对象插入到display中的global_list中。

  • 创建新全局对象后,给每个客户端发送registry global事件,通知客户端服务端有新全局对象创建了。

demo合成器创建了3个全局对象:wl_compositor_interface、wl_shell_interface、wl_shm_interface。每个全局对象创建的时候都会有一个bind回调函数,此函数在wl_registry对象进行bind request操作时被调用,主要作用就是根据全局对象来创建其服务端对应的resource,并设置resource对应的request实现函数。

5.1.4 wl_display_init_shm

这个函数内部调用wl_global_create来创建wl_shm_interface全局对象。

5.1.5 wl_display_add_shm_format

此函数用来设置服务端支持的共享内存像素数据格式,默认支持ARGB8888和XRGB8888。

这个函数内部调用wl_array_add,用来在wl_array中预留指定大小的空间并返回此空间的地址,这样用户就可以通过这个地址向这个空间中填写数据。

5.1.6 wl_display_run
  • 首先调用wl_display_flush_clients将client_list链表中所有客户端缓存区中的数据通过socket接口发送出去。

  • 接着调用wl_event_loop_dispatch等待接收服务端的数据。等待的事件主要是:

  1. listen socket fd,用于监听客户端连接,如果有连接,回调socket_data函数。

  1. connect socket fd,用于处理客户端数据通信,如果有数据,回调wl_client_connection_data函数。

  • 回到步骤1循环处理。

5.1.7 socket_data
  • 通过accept获得与客户端通讯的fd。

  • 创建wl_client结构,并初始化。

  • 将wl_client结构插入client_list链表中。

5.1.8 wl_client_connection_data
  • wl_connection_read:通过recvmsg将数据读取到wl_connection的缓存区中。

  • wl_connection_demarshal:将数据组织成wl_closure结构。

  • wl_closure_invoke:根据wl_closure结构进行函数调用。

5.2 demo客户端分析
5.2.1 wl_display_connect
  • 连接到服务端创建的“wayland-0”socket接口。

  • 创建并初始化wl_display数据结构。

5.2.2 wl_display_get_registry
  • 向服务端发送wl_display对象的get_registry请求。

  • 创建wl_registry对象。

5.2.3 wl_registry_add_listener
  • 设置wl_registry对象的event实现函数。

5.2.4 wl_display_roundtrip
  • 向服务端发送wl_display对象的sync请求。

  • 创建wl_callback对象。

  • 设置wl_callback对象的event实现函数。

  • 将客户端缓冲区的数据通过socket发送出去。

  • 等待接收服务端发送的数据。

  • 处理服务端发送的事件。

5.2.5 create_window
  • wl_compositor_create_surface:向服务端发送wl_compositor对象的create_surface请求,同时创建wl_surface对象。

  • wl_shell_get_shell_surface:向服务端发送wl_shell对象的get_shell_surface请求,同时创建wl_shell_surface对象。

  • wl_shell_surface_add_listener:设置wl_shell_surface对象的event实现函数。

  • 创建临时文件。

  • 通过ftruncate设置临时文件大小。

  • 通过mmap映射临时文件。

  • wl_shm_create_pool:向服务端发送wl_shm对象的create_pool请求。

  • 创建wl_shm_pool对象。

  • wl_shm_pool_create_buffer:向服务端发送wl_shm_pool对象的create_buffer请求。

5.2.6 draw_window
  • 根据create_window中mmap出来的共享内存区渲染像素数据。

  • wl_surface_attach:向服务端发送wl_surface对象的attach请求。

  • wl_surface_damage:向服务端发送wl_surface对象的damage请求。

  • wl_surface_commit:向服务端发送wl_surface对象的commit请求。

5.2.7 wl_display_dispatch
  • 将客户端缓冲区的数据通过socket发送出去。

  • 等待接收服务端发送的数据。

  • 处理服务端发送的事件。

  • 回到步骤1循环处理。

原文地址:http://www.databusworld.cn/9895.html

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值