精华内容
下载资源
问答
  • Ubuntu 再次拥抱 Wayland

    千次阅读 2021-02-03 19:14:40
    【编者按】在 Ubuntu 17.10 版当中,Ubuntu 开发人员已经将 Wayland 设置为默认会话,由于并不完善,开发人员在后续版本当中还是更换到了 Xorg。现在他们说,已经准备好了再给 Wayland 一次机会。不过,在此前的 ...

    【编者按】在 Ubuntu 17.10 版当中,Ubuntu 开发人员已经将 Wayland 设置为默认会话,由于并不完善,开发人员在后续版本当中还是更换到了 Xorg。现在他们说,已经准备好了再给 Wayland 一次机会。不过,在此前的 Ubuntu 登录界面当中,只要点击齿轮按钮,也是可以选择 Wayland 的。

    在这里插入图片描述

    作者 | 八宝粥

    出品 | CSDN(id:CSDNnews)

    在 Ubuntu 的讨论去当中,Sebastien Bacher 解释说:“我们当时发现的一些障碍已得到解决(桌面共享),这就是上游关注的焦点。我们认为现在是再试一次的正确时机,它应该给我们足够的时间在下一次 LTS (Long-term support 长期支持版)之前获得适当的反馈并解决问题。” 不过,他还表示,Nividia 用户将依然默认使用 Xorg,希望在 LTS 之前解决这个问题。

    此外,尽管 Gnome 40 版本已经发布,而 Fedora 也宣布在新的桌面版上搭载该版本,Ubuntu 21.04 依然选择了 GNOME 3.38。


    关于 wayland

    官方描述:Wayland 旨在作为 X 的更简单的替代品,更容易开发和维护。是提供 compositor 和 Cient 交互的工具,以及该协议的 C 库实现。compositor 可以是在 Linux 内核模式设置和 Evdev 输入设备上的独立显示器,X 应用或者 Wayland 客户端本身。客户端可以是传统的应用程序、X 服务器或者其他显示服务器。

    Wayland 项目的一部分也是 Wayland compositor 的 Weston 参考实现。Weston 可以作为 X 客户端运行,也可以在 Linux KMS 下运行,并附带一些演示客户端。

    上文提到了各种的 X、Xorg之类的,这里也来解释一下。X Window,简称 X 或者 X11,诞生于 1984 年的 MIT,同样是软件系统和网络协议,Xorg 是 X 协议的服务器实现,同类型的还有 xfree86,Xnest 等等。

    在 Wayland 官网上,有两张流程图展示了 X 和 Wayland 运行当中的区别。
    在这里插入图片描述
    在这里插入图片描述

    对比之下就可以看出,由于应用程序的直接渲染,无需向 Wayland compositor 请求,而且没有接受请求后的区域计算,而 X server 还会去做一些不必要的本职工作。于是 Wayland 的效率优势就十分明显了。

    Xorg 和 Wayland 之争,每年都引起热议,从横空出世在 Fedora23 上将将令人满意的状态,到后来即使 Xorg 有设计缺陷也要两害相权被选中,再到后来 Fedora32 上的对比测试与 Xorg 不相上下。实际上,Wayland 就像 Fedora 一样,永远处在一个“有追求”的状态。


    Wayland 网友怎么说?

    关于 Wayland 将变成 Ubuntu 21.04 的预装,网友怎么说呢?

    在这里插入图片描述

    每周都有那么几次不想用它!

    在这里插入图片描述

    Wayland 就是未来!

    [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-SwrEaloE-1612350857176)(image/20210203_191051_43.png)]

    屏幕独立缩放就得用 Wayland 呀!

    [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-qCEbjYON-1612350857179)(image/20210203_191155_31.png)]

    网友A:Nividia 你在干嘛?快跟上呀!!! 网友B:怪不得 Linus 恨 Nividia

    X,还是 Wayland ?这是一个问题

    【头图】
    来自 Wayland 官网

    【参考链接】
    1.https://www.omgubuntu.co.uk/2021/01/ubuntu-21-04-will-use-wayland-by-default
    2.https://www.secjuice.com/wayland-vs-xorg/
    3.https://wayland.freedesktop.org/
    4.https://zh.wikipedia.org/wiki/Wayland

    展开全文
  • wayland简介

    2020-06-20 15:20:23
    Wayland Wayland旨在作为X的更简单的替代品,更易于开发和维护。 Wayland复用了所有Linux内核的...Wayland合成器可以是在Linux内核模式设置和evdev输入设备上运行的独立显示服务器 了解wayland之前需要先了解X

    Wayland


    Wayland旨在作为X的更简单的替代品,更易于开发和维护。

    • Wayland复用了所有Linux内核的图形、输入输出技术:KMS、evdev,因此已支持的驱动可以直接拿来用。
    • Wayland没有传统的Server/Client的模式,取而代之的是:Compositor/Client
    • Wayland是供合成器与其客户对话的协议,以及该协议的C库实现
    • Wayland合成器可以是在Linux内核模式设置和evdev输入设备上运行的独立显示服务器


    了解wayland之前需要先了解X

    Linux 本身是没有图形化界面的,所谓的图形化界面系统只不过中 Linux 下的应用程序。

    什么是 X

    • X 协议由 X server 和 X client 组成。
    • X server 管理主机上与显示相关的硬件设置(如显卡、硬盘、鼠标等),它负责屏幕画面的绘制与显示,以及将输入设置(如键盘、鼠标)的动作告知 X client。
    • X client (即 X 应用程序) 则主要负责事件的处理。
    • 大多数基于Linux和Unix的系统都依赖X作为用于构建位图图形界面的底层协议。在这些系统上,X堆栈已增长为可以包含在客户端库,帮助程序库或主机操作系统内核中的功能。
      对PCI资源管理,显示配置管理,直接渲染和内存管理等功能的支持已集成到X堆栈中,从而产生了一些局限性,例如对独立应用程序的有限支持,在其他项目中的重复,以及将多个元素组合在一起的系统的高度复杂性(例如fb驱动程序和X驱动程序之间的内存映射处理或VT切换)。

    而且,X已经成长为合并了诸如屏幕外渲染和场景合成之类的功能,但受到X体系结构的限制,例如,composition的X实现增加了其他上下文切换,并使输入重定向之类的事情变得困难。

    X的工作流程:

    • 内核从输入设备获取事件,并通过evdev输入驱动程序将其发送到X。内核通过驱动设备并将不同的设备特定事件协议转换为linux evdev输入事件标准来完成所有艰苦的工作。
    • X服务器确定事件影响哪个窗口,并将其发送到在该窗口上为该事件选择的客户端。X服务器实际上并不知道如何正确执行此操作,因为屏幕上的窗口位置是由合成器控制的,并且可能以X服务器无法理解的许多方式进行转换(缩小,旋转,摆动,等等)。
    • 客户查看事件并决定要做什么。UI通常必须响应事件而改变-也许单击了复选框或指针输入了必须突出显示的按钮。因此,客户端将渲染请求发送回X服务器。
    • X服务器接收到渲染请求后,会将其发送给驱动程序,以使其对硬件进行编程以进行渲染。X服务器还计算渲染的边界区域,并将其作为损伤事件发送到合成器。
    • 损坏事件告诉合成器窗口中发生了某些更改,并且必须重新合成可见该窗口的屏幕部分。合成器负责根据其场景图和X窗口的内容渲染整个屏幕内容。但是,它必须通过X服务器来呈现它。
    • X服务器从合成器接收渲染请求,然后将合成器后缓冲区复制到前缓冲区或进行页面翻转。在一般情况下,X服务器必须执行此步骤,以便它可以考虑重叠的窗口,这可能需要裁剪并确定是否可以翻页。但是,对于始终为全屏显示的合成器,这是另一个不必要的上下文切换。

    如上所述,这种方法存在一些问题。X服务器没有信息来决定哪个窗口应该接收事件,它也不能将屏幕坐标转换为窗口局部坐标。即使X将屏幕的最终绘制工作移交给了合成管理器,X仍然控制着前缓冲区和模式设置。 X服务器用于处理的大多数复杂性现在都可以在内核或自包含的库中找到(KMS,evdev,mesa,fontconfig,freetype,cairo,Qt等)。

    通常,X服务器现在只是一个中间人,它在应用程序和合成器之间引入了一个额外的步骤,在合成器和硬件之间引入了一个额外的步骤。


    walyland工作流程

    在Wayland中,合成器是显示服务器。我们将KMS和evdev的控制权转移给合成器。wayland协议允许合成器将输入事件直接发送到客户端,并让客户端将损坏事件直接发送到合成器:

    • 内核获取一个事件,并将其发送到合成器。这与X情况类似,这非常好,因为我们可以重用内核中的所有输入驱动程序。
    • 合成器通过其场景图进行查看,以确定应该接收该事件的窗口。场景图与屏幕上的内容相对应,并且合成器了解它可能已应用于场景图中的元素的转换。因此,合成器可以选择右窗口,并通过应用逆变换将屏幕坐标转换为窗口局部坐标。可以应用于窗口的转换类型仅限于合成器可以执行的操作,只要它可以计算输入事件的逆转换即可。
    • 与X情况一样,当客户端收到事件时,它会更新UI以作为响应。但是在路途中,渲染发生在客户端中,而客户端只是向合成器发送请求以指示更新的区域。
    • 合成器从其客户端收集损坏请求,然后重新合成屏幕。然后,合成器可以直接发出ioctl来调度带有KMS的翻页。

    walyland渲染

    在上面的概述中遗漏的细节之一是客户如何在Wayland下实际渲染。通过从图片中删除X服务器,还删除了X客户端通常呈现的机制。但是已经在X下的DRI2中使用了另一种机制:直接渲染。通过直接渲染,客户端和服务器共享视频内存缓冲区。客户端链接到诸如OpenGL之类的渲染库,该库知道如何对硬件进行编程并将其直接渲染到缓冲区中。

    当合成器合成桌面时,合成器又可以获取该缓冲区并将其用作纹理。初始设置后,客户端仅需要告诉合成器要使用哪个缓冲区以及何时何地向其提供了新内容。

    这为应用程序留下了两种更新窗口内容的方法

    将新内容呈现到新缓冲区中,并告知合成器使用该内容代替旧缓冲区。应用程序可以在每次需要更新窗口内容时分配一个新的缓冲区,也可以保留两个(或多个)缓冲区并在它们之间循环。缓冲区管理完全在应用程序控制之下。将新内容呈现到以前告诉合成器使用的缓冲区中。
    尽管可以直接渲染到与合成器共享的缓冲区中,但这可能会与合成器竞争。合成器重新绘制桌面可能会中断重新绘制窗口内容。如果应用程序在清除窗口后但呈现内容之前被中断,则合成器将从空白缓冲区进行贴图。结果是应用程序窗口将在空白窗口或半渲染内容之间闪烁。
    避免这种情况的传统方法是将新内容呈现到后台缓冲区,然后从那里复制到合成器表面。后台缓冲区可以动态分配,大小足以容纳新内容,或者应用程序可以保留缓冲区。同样,这在应用程序控制之下。
    无论哪种情况,应用程序都必须告诉合成器表面的哪个区域容纳新内容。当应用程序直接呈现到共享缓冲区时,需要注意合成器存在新内容。而且,在交换缓冲区时,合成器不假定任何更改,并且需要应用程序发出请求才能重新绘制桌面。即使应用程序将新缓冲区传递给合成器,该缓冲区只有一小部分也可能会有所不同,例如闪烁的光标或微调框。


    合成器的类型
    合成器的类型不同,具体取决于它们在操作系统的总体体系结构中所扮演的角色。例如,系统合成器可用于引导系统,处理多用户切换,可能的控制台终端仿真器等。一个不同的合成器,一个会话合成器 将提供实际的桌面环境。不同类型的合成器可以通过多种方式共存。

    • 系统合成器:

    系统合成器可以从早期启动一直运行到关机为止。它有效地替代了内核vt系统,并可以与系统的图形启动设置和多座支持配合使用。
    系统合成器可以托管不同类型的会话合成器,并让我们在多个会话之间切换(快速用户切换或安全/个人桌面切换)。
    系统合成器的Linux实现通常使用libudev,例如egl,kms,evdev和cairo。
    对于全屏客户端,系统合成器可以重新编程视频扫描地址,以直接从客户端提供的缓冲区中读取。

    • 会话合成器

    会话合成器负责单个用户会话。如果存在系统合成器,则会话合成器将嵌套在系统合成器下运行。嵌套是可行的,因为该协议是异步的。当涉及嵌套时,往返将太昂贵。如果没有系统合成器,则会话合成器可以直接在硬件上运行。

    • 嵌入式合成器

    X11允许客户端从其他客户端嵌入窗口,或者让客户端将其他客户端渲染的像素图内容复制到其窗口中。这通常用于面板中的applet,浏览器插件等。Wayland不允许直接这样做,但是客户端可以带外传送GEM缓冲区名称,例如,使用D-Bus,或在面板启动小程序时使用命令行参数。另一种选择是使用嵌套的Wayland实例。为此,Wayland服务器必须是主机应用程序链接到的库。然后,主机应用程序会将Wayland服务器套接字名称传递给嵌入式应用程序,并且需要实现Wayland合成器接口。主机应用程序将客户端表面作为其窗口的一部分进行合成,也就是说,在网页或面板中。嵌套Wayland服务器的好处在于,它提供嵌入式客户端向主机通知缓冲区更新所需的请求,以及用于转发来自主机应用程序的输入事件的机制。
    这种设置的一个示例是将Flash Player嵌入为专用合成器的firefox。


    Wayland协议和操作模型
    Wayland协议是一种异步的面向对象的协议。所有请求都是对某个对象的方法调用。该请求包括一个对象ID,该ID唯一地标识服务器上的一个对象。每个对象都实现一个接口,并且请求包括一个操作码,该操作码标识接口中要调用的方法。

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

    此外,该协议可以指定枚举的准名称来特定数字枚举值。这些本质上仅是描述性的:在有线格式级别,枚举只是整数。但是它们还具有辅助目的,以增强类型安全性或添加用于语言绑定或其他此类代码的上下文。仅在引入这些属性之前编写的代码,在此之后仍然有效时,才支持后一种用法。换句话说,添加枚举不会破坏API。

    枚举可以仅定义为一组整数,也可以定义为位域。这是通过枚举定义中的布尔值属性指定的。如果此属性为true,则打算主要使用按位操作来访问该枚举,例如,当任意多个枚举可以一起进行“或”运算时;如果为false,或者省略了属性,则枚举参数只是一系列数值。

    服务器将事件发送回客户端,每个事件都是从一个对象发出的。事件可能是错误条件。事件包括对象ID和事件操作码,客户端可以从中确定事件的类型。事件是响应请求(在这种情况下,请求和事件构成往返)而生成的,或者是在服务器状态更改时自发生成的。

    • 状态更改时发出事件。客户端必须侦听这些更改并缓存状态。不需要查询服务器状态

    • 服务器将广播许多全局对象的存在,而这些对象又将广播其当前状态。


    连线格式
    每条消息的结构都是32位字。值以主机的字节顺序表示。标题中包含2个字:

    第一个字是发送者的对象ID(32位)。
    第二个具有2个部分的16位。高16位是消息大小,以字节为单位,从标头开始(即最小值为8)。低位是请求/事件操作码。

    有效负载描述了请求/事件参数。每个参数始终与32位对齐。在需要填充的地方,填充字节的值是不确定的。没有描述类型的前缀,但是可以从xml规范中隐式地推断出来。

    参数类型的表示如下:

    int,uint

    该值是有符号/无符号int的32位值。

    fixed

    签名24.8个十进制数字。它是带符号的十进制类型,提供一个符号位,23位的整数精度和8位的十进制精度。在C API方面,这是不透明的结构,带有转换辅助函数,往返于double和int之间。

    string

    以无符号的32位长度开始,然后是字符串内容,包括终止空字节,然后填充到32位边界。

    object

    32位对象ID。

    new_id

    32位对象ID。根据请求,客户端确定ID。带有new_id的唯一事件是全局变量的通告,并且服务器将使用0x10000以下的ID。

    array

    从32位数组大小(以字节为单位)开始,然后逐字逐行排列数组内容,最后填充到32位边界。

    fd

    文件描述符不是存储在消息缓冲区中,而是存储在UNIX域套接字消息(msg_control)的辅助数据中。

    该协议包括几个用于与服务器交互的接口。每个接口都如上所述提供请求,事件和错误(实际上只是特殊事件)。特定的合成器实现可能有自己的接口作为扩展提供,但是总有几种可能会出现。


    核心接口

    wl_display

    核心全局对象

    wl_registry

    全局注册表对象
    wl_callback
    回调对象

    wl_compositor

    合成器单例

    wl_shm_pool

    共享内存池

    wl_shm

    共享内存支持

    wl_buffer

    wl_surface的内容

    wl_data_offer

    提供传输数据

    wl_data_source

    提供传输数据

    wl_data_device

    数据传输装置

    wl_data_device_manager

    数据传输接口

    wl_shell

    创建桌面样式的表面

    wl_shell_surface

    桌面式元数据界面

    wl_surface

    屏幕上的表面

    wl_seat

    输入设备组

    wl_pointer

    指针输入设备

    wl_keyboard

    键盘输入设备

    wl_touch

    触摸屏输入设备

    wl_output

    合成器输出区域

    wl_region

    区域界面

    wl_subcompositor

    次表面合成

    wl_subsurface

    wl_surface的接口

    • 拖放
      使用该wl_data_device.start_drag请求开始拖放操作 。此请求将导致指针抓取,该抓取将在数据设备上生成输入,移动和离开事件。数据源作为start_drag的参数提供,并且与之关联的数据提供在wl_data_device.enter事件中的指针下方提供给客户端表面 。在wl_data_device.data_offer事件进入事件之前,将数据报价引入到客户端 。

    希望客户端通过wl_data_offer.accept使用其接受的mime类型调用请求来向数据发送客户端提供反馈。 如果接收方客户端不支持任何广告的mime类型,则应为接受请求提供NULL。接受请求使发送客户端接收到wl_data_source.target具有所选mime类型的事件。

    拖动结束后,接收方客户端会收到一个 wl_data_device.drop事件,预计将使用该wl_data_offer.receive请求传输数据 。

    展开全文
  • Wayland Architecture

    2021-04-16 14:56:23
    Wayland Architecture 理解Wayland架构及其与X的不同之处的一种好方法是跟踪事件从"输入设备到屏幕上"出现的变化。 这就是我们现在使用的X的逻辑架构: 内核从输入设备获取事件,然后通过evdev输入驱动程序将...

    翻译于: https://wayland.freedesktop.org/architecture.html

    Wayland Architecture

    理解Wayland架构及其与X的不同之处的一种好方法是跟踪事件从"输入设备到屏幕上"出现的变化

    这就是我们现在使用的X的逻辑架构:

     

    X architecture diagram

    1. 内核从输入设备获取事件,然后通过evdev输入驱动程序将其发送到X。内核通过驱动设备并将不同的设备事件转换为linux evdev输入标准事件来完成所有的艰难工作。
    2. X服务器确认事件影响哪个窗口,并将其发送到在该窗口上为该事件选择的客户端。X服务器实际上并不知道如何正确执行此操作,因为屏幕上的窗口位置是由合成器控制的,并且可能以X服务器无法理解的多种方式进行转换(缩小,旋转,摆动,等等)。
    3. 客户端查看事件并决定要做什么。UI通常必须响应事件而改变-也许单击了复选框或指针输入了必须突出显示的按钮。因此,客户端将渲染请求发送回X服务器。
    4. X服务器接收到渲染请求后,会将其发送给驱动程序,以使其对硬件进行编程以进行渲染。X服务器还计算渲染的边界区域,并将其作为damage事件发送到合成器。 //比weston麻烦的地方,绘画要通过server传给driver
    5. damage事件告诉合成器窗口中发生了某些更改,并且必须重新合成可见该窗口的屏幕部分。合成器负责根据其场景图和X窗口的内容渲染整个屏幕内容。但是,它必须通过X服务器来呈现它。
    6. X服务器从合成器接收渲染请求,然后将合成器后缓冲区复制到前缓冲区或进行页面翻转。在一般情况下,X服务器必须执行此步骤,以便它可以考虑重叠的窗口,这可能需要裁剪并确定是否可以翻页。但是,对于始终为全屏显示的合成器,这是另一个不必要的上下文切换。

    如上所述,这种方法存在一些问题。X服务器没有信息来决定哪个窗口应该接收事件它也不能将屏幕坐标转换为窗口局部坐标。尽管X已将屏幕的最终绘制工作移交给了合成管理器,但X仍控制着前缓冲区和模式设置。X服务器用来处理的大多数复杂性现在都可以在内核或自包含的库中找到(KMS,evdev,mesa,fontconfig,freetype,cairo,Qt等)。通常,X服务器现在只是一个中间人,它在应用程序和合成器之间引入了一个额外的步骤在合成器和硬件之间引入了一个额外的步骤

    在Wayland中,合成器显示服务器。我们将KMS和evdev的控制权转移给合成器。wayland协议允许合成器将输入事件直接发送到客户端,并让客户端将damage事件直接发送到合成器:


    Wayland architecture diagram

    1. 内核获取一个事件,并将其发送到合成器。这与X情况类似,这非常好,因为我们可以重用内核中的所有输入驱动程序。
    2. 合成器通过其场景图进行查看,以确定应该接收该事件的窗口。场景图与屏幕上的内容相对应,并且合成器了解它可能已应用于场景图中的元素的转换。因此,合成器可以选择右窗口,并通过应用逆变换将屏幕坐标转换为窗口局部坐标。可以应用于窗口的转换类型仅限于合成器可以执行的操作,只要它可以计算输入事件的逆转换即可。
    3. 与X情况一样,当客户端收到事件时,它会更新UI作为响应。但是在wayland中,渲染发生在客户端中,并且客户端只是向合成器发送请求以指示已更新的区域。
    4. 合成器从其客户端收集damage请求,然后重新合成屏幕。然后,合成器可以直接发出ioctl来调度带有KMS的翻页。

    Wayland Rendering

    在上面的概述中,我遗漏的细节之一是客户在Wayland下的实际渲染方式。通过从图片中删除X服务器,我们还删除了X客户端通常呈现的机制。但是,我们已经在X下的DRI2中使用了另一种机制:直接渲染。通过直接渲染,客户端和服务器共享内存缓冲区。客户端链接到诸如OpenGL之类的渲染库,该库知道如何对硬件进行编程并将其直接渲染到缓冲区中。当合成器合成桌面时,合成器又可以获取该缓冲区并将其用作纹理。初始设置后,客户端仅需要告诉合成器要使用哪个缓冲区以及何时何地向其提交了新内容。

    这为应用程序留下了两种更新窗口内容的方法:

    1. 将新内容呈现到新缓冲区中,并告知合成器使用该内容代替旧缓冲区。应用程序可以在每次需要更新窗口内容时分配一个新的缓冲区,也可以保留两个(或多个)缓冲区并在它们之间循环。缓冲区管理完全在应用程序控制之下。
    2. 将新内容呈现到以前告诉合成器使用的缓冲区中。尽管可以直接渲染到与合成器共享的缓冲区中,但这可能会与合成器竞争。可能发生的情况是,合成器重新绘制桌面可能会中断重新绘制窗口内容。如果应用程序在清除窗口后,呈现内容之前被中断,则合成器将从空白缓冲区进行贴图。结果是应用程序窗口将在空白窗口或半渲染内容之间闪烁。避免这种情况的传统方法是将新内容呈现到后台缓冲区中,然后从那里复制到合成器表面中。后台缓冲区可以动态分配,大小足以容纳新内容,或者应用程序可以保留缓冲区。同样,这在应用程序控制之下。

    无论哪种情况,应用程序都必须告诉合成器表面的哪个区域包含新内容。当应用程序直接呈现到共享缓冲区时,需要注意合成器存在新内容。而且在交换缓冲区时,合成器不会假设任何更改,并且需要应用程序发出请求才能重新绘制桌面。即使应用程序将新缓冲区传递给合成器,该缓冲区只有一小部分也可能会有所不同,例如闪烁的光标或微调框。

    Hardware Enabling for Wayland

    通常,硬件启用包括模式设置/显示和EGL / GLES2。最重要的是,Wayland需要一种在进程之间高效共享缓冲区的方法。有两个方面,客户端和服务器端。

    在客户端,我们定义了Wayland EGL平台。在EGL模型中,该模型由本机类型(EGLNativeDisplayType,EGLNativeWindowType和EGLNativePixmapType)和一种创建这些类型的方法组成。换句话说,是将EGL堆栈及其缓冲区共享机制绑定到通用Wayland API的粘合代码。EGL堆栈有望提供Wayland EGL平台的实现。完整的API在wayland-egl.h 标头中。mesa EGL堆栈中的开源实现位于 platform_wayland.c中

    在后台,EGL堆栈有望定义一个特定于供应商的协议扩展,该协议扩展使客户端EGL堆栈可与合成器通信缓冲区详细信息以共享缓冲区。wayland-egl.h API的要点是将其抽象化,然后让客户端为Wayland曲面创建EGLSurface并开始渲染。开源堆栈使用drm Wayland扩展,该扩展使客户端可以发现要使用和认证的drm设备,然后与合成器共享drm(GEM)缓冲区

    Wayland的服务器端是合成器和垂直领域的核心用户体验,通常将任务切换器,应用程序启动器,锁屏集成在一个整体应用程序中。服务器在模式设置API(内核模式设置,OpenWF Display或类似功能)之上运行,并结合使用EGL / GLES2合成器和硬件覆盖(如果有)来组合最终的UI。启用模式设置,EGL / GLES2和叠加层应该成为标准硬件升级的一部分。Wayland启用的额外要求是EGL_WL_bind_wayland_display 扩展,该扩展使合成器可以从通用Wayland共享缓冲区中创建EGLImage。它类似于EGL_KHR_image_pixmap 扩展名,可以从X像素图创建EGLImage。

    该扩展程序有一个设置步骤,在该步骤中,您必须将EGL显示器绑定到Wayland显示器。然后,当合成器从客户端接收通用Wayland缓冲区时(通常在客户端调用eglSwapBuffers时),它将能够将struct wl_buffer指针作为EGLClientBuffer参数传递给eglCreateImageKHR,并将EGL_WAYLAND_BUFFER_WL作为目标。这将创建一个EGLImage,合成器随后可以将其用作纹理,或传递给模式设置代码以用作覆盖平面。同样,这是由供应商特定的协议扩展实现的,该扩展在服务器端将接收有关共享缓冲区的驱动程序特定的详细信息,并在用户调用eglCreateImageKHR时将其转换为EGL映像。

    展开全文
  • wayland case

    2019-07-05 15:36:30
    Wayland协议主要提供了Client端应用与Server端Compositor的通信机制,Weston是Server端Compositor的一个参考实现。Wayland协议中最基础的是提供了一种面向对象的跨进程过程调用的功能,在作用上类似于Android中的...
    原文地址:http://blog.csdn.net/jinzhuojun/article/details/40264449

    Wayland协议主要提供了Client端应用与Server端Compositor的通信机制,Weston是Server端Compositor的一个参考实现。Wayland协议中最基础的是提供了一种面向对象的跨进程过程调用的功能,在作用上类似于Android中的Binder。与Binder不同的是,在Wayland中Client和Server底层通过domain socket进行连接。和Binder一样,domain socket支持在进程间传递fd,这为传递graphic buffer和shared memory等提供了基础。Client和Server端一方面各自在消息循环中等待socket上的数据,拿到数据后经过反序列化处理生成本地的函数调用,然后进行调用;另一方面将本地的远端调用请求封装序列化后通过socket发出。另外,由于是基于消息循环的处理模型,意味着这种调用不是同步,但通过等待Server端处理完成后的事件再返回可以达到同步调用的效果。

    下面从几个基本方面介绍Wayland协议的跨进程过程调用是如何实现的。

    一、基本工作流程


    以Weston自带的例程simple-shm为例,先感受一下Client如何通过Wayland协议和Compositor通信。
    1. 连接Server,绑定服务
    display->display = wl_display_connect() // 通过socket建立与Server端的连接,得到wl_display。它即代表了Server端的display资源,同时也是代理对象wl_proxy。Client可以通过它来向Server端提交调用请求和接收事件。
    display->registry = wl_display_get_registry(display->display) // 申请创建registry,得到代理对象wl_registry。这个对象相当于Client在Server端放的一个用于嗅探资源的Observer。Client通过它得到Server端有哪些Global对象的信息。Server端有一系列的Global对象,如wl_compositor, wl_shm等,串在display->global_list链表里。Global对象在概念上类似于Service服务,因此Server端相当于充当了ServiceManager的角色。
    wl_registry_add_listener(display->registry, &registry_listener,...) // 让Client监听刚才创建的wl_registry代理对象。这样, 当Client调用wl_display_get_registry()函数或者有新的Global对象加入到Server端时,Client就会收到event通知。
    wl_display_roundtrip() // 等待前面的请求全被Server端处理完,它同步了Client和Server端。这意味着到这个函数返回时,Server端有几个Global对象,回调处理函数registry_handle_global()应该就已经被调用过几次了。 registry_handle_global()中会判断是当前这次event代表何种Global对象,然后调用wl_registry_bind()进行绑定,得到远程服务对象的本地代理对象。这些代理对象类型可以是wl_shm, wl_compositor等,但本质上都是wl_proxy类型。这步的作用类似于Android中的bindService(),它会得到一个远端Service的本地代理。

    2. 创建窗口
    window->surface = wl_compositor_create_surface() // 通过刚才绑定的wl_compositor服务创建Server端的weston_surface,返回代理对象 wl_surface。
    xdg_shell_get_xdg_surface(..., window->surface, ...) // 通过刚才绑定的xdg_shell服务创建Server端的shell_surface,返回代理对象 xdg_surface。有些例子中用的是wl_shell_surface,它和xdg_surface的作用是一样的。xdg_surface是作为wl_shell_surface将来的替代品,但还没进Wayland核心协议。
    为什么一个窗口要创建两个surface呢?因为Wayland协议假设Server端对Surface的管理分两个层次。以Weston为例,Compositor只负责合成(代码主要在compositor.c),它相当于Android中的SurfaceFligner,它所看到的主要是weston_surface。而Weston在启动时会加载shell模块(如desktop-shell.so,代码主要在desktop-shell/shell.c),它相当于Android中的WindowManagerService,它所看到的主要是shell_surface。shell_surface在结构上是weston_surface的进一步封装,为了做窗口管理。这样,合成渲染和窗口管理的模块既可以方便地相互访问又保证了较低的耦合度。

    3. 分配buffer与绘制
    wl_surface_damage() // 告诉Compositor该surface哪块区域是脏的,需要重绘。一开始是整个窗口区域。
    redraw() //  接下来调用redraw()开始绘制的循环,这里是双buffer的软件渲染。
        window_next_buffer() // 取一个buffer,用作绘制。
            create_shm_buffer() // 如果该buffer尚未分配则用之前绑定的wl_shm服务分配一块共享内存。
                fd = os_create_anonymous_file() // 为了创建共享内存,先创建一个临时文件作为内存映射的backing file。
                mmap(..., fd,...) // 将该文件先映射到Client端的内存空间。
                pool = wl_shm_create_pool(..., fd,...) // 通过wl_shm服务创建共享内存池。将刚才的fd作为参数传过去,这样Server端就可以和Client通过这个fd映射到同一块物理内存。
                buffer->buffer = wl_shm_pool_create_buffer(pool, ...) // 通过这个共享内存池在Server端分配buffer,返回wl_buffer为其本地代理对象。
                wl_buffer_add_listener(buffer->buffer, &buffer_listener,...) // 监听这个buffer代理对象,当Server端不再用这个buffer时,会发送release事件。这样,Client就可以重用这个buffer作下一帧的绘制。
        paint_pixels() // Client在buffer上绘制自己的内容。
        wl_surface_attach()// 将绘制好的buffer attach到surface上。作用上类似于Android中的updateTexImage(),即把某一个buffer与surface绑定。
        wl_surface_damage()// 告诉Server端的Compositor这个surface哪块区域是脏区域,需要重新绘制。 
        window->callback = wl_surface_frame() // 在Server端创建Frame callback,它会被放在该surface下的frame_callback_list列表中。返回它的代理对象wl_callback。
        wl_callback_add_listener(window->callback, &frame_listener, ...) // 监听前面得到的callback代理对象。在Server端Compositor在完成一帧的合成渲染后,会往这些callback对象发done的事件(参考weston_output_repaint())。Client收到后会调用参数中wl_callback_listener中的done事件对应的方法,这里是redraw()函数。这样,就形成了一个循环。
        wl_surface_commit() // 在0.99版本后,为了保证原子性及使surface属性的改动顺序无关,Server端对于surface的属性(damage region, input region, opaque region, etc.)都是双buffer的(weston_surface_state)。所以commit前的改动都存在backing buffer中。只有当Client调用wl_surface_commit()时,这些改动才生效。

    与Android作个类比,这里的wl_surface对应SF中的Layer,wl_buffer对应GraphicBuffer。Weston对应SF+WMS。一个surface对应用户视角看到的一个窗口。为了使Client和Server并行作业,一般会用多个buffer。和Android比较不同的是,Android是Server端来维护buffer的生命周期,而 Wayland中是Client端来做的。

    二、连接的建立


    首先Server端的Compositor在启动时会在$XDG_RUNTIME_DIR目录下创建用于监听的socket,默认为wayland-0。然后把该socket fd加入event loop等待的fd列表。参考实现位于 weston_create_listening_socket() -> wl_display_add_socket_auto() -> _wl_display_add_socket()。当有Client连接时,回调处理函数为socket_data(),其中会调用wl_os_accept_cloexec()得到与该Client相连的socket fd。然后调用wl_client_create(),创建wl_client。Server端会为每一个连接的Client创建wl_client,这些对象被串在display->client_list这个列表里。wl_client中的wl_connection代表这个与Client的连接,其中包含了in buffer和out buffer,分别作为输入和输出的缓冲区。注意这个in buffer和out buffer都是双份的,一份for普通数据,一份for fd,因为fd需要以out-of-band形式传输,要特殊处理。wl_event_loop_add_fd()会把这个与Client连接的socket fd加入到event loop等待的列表中,回调处理函数为wl_client_connection_data()。
    weston_create_listening_socket()
        wl_display_add_socket_auto()
            wl_socket_init_for_display_name() // $XDG_RUNTIME_DIR/wayland-0,
            _wl_display_add_socket() 
                wl_os_socket_cloexec() // create socket
                bind() 
                listen() 
                wl_event_loop_add_fd(.., socket_data,...) // 创建wl_event_source_fd,它代表一个基于socket fd的事件源。处理函数是wl_event_source_fd_dispatch(),其中会调用这里参数里的回调函数 socket_data()。
                    add_source() // 把刚创建的监听socket fd,通过epoll_ctl()附加到loop->epoll_fd上。这样消息循环就可以在上面等待Client的连接了。

    当有Client连接到Server的监听socket上,会调用刚才注册的回调函数socket_data(),然后会调用wl_os_accept_cloexec()->accept()创建与Client连接的fd。接着调用wl_client_create()创建Client对象。
    socket_data()
        wl_os_accept_cloexec()
        wl_client_create()
            client->connection = wl_connection_create() 
             wl_map_init(&client->objects) // 初始化wl_map对象,用于映射Server和Client端的对象。
            bind_display() // 绑定Client端的wl_display代理对象与Server端的display资源对象。
                client->display_resource = wl_resource_create(.., &wl_display_interface,...) // display资源对象的接口就是wl_display_interface,request的实现在display_interface中。这里创建wl_resource结构,其中resource->object是一个可供Client调用的远端对象的抽象,其中的成员interface和implementation分别代表其接口和实现。然后用wl_map_insert_at()插入到client->objects的wl_map结构中供以后查询。
                wl_resource_set_implementation(..., &display_interface, ...)  // 对Client而言,Server端提供的接口实现是request部分。

    Client端要连接Server端,是通过调用wl_display_connect()。其中会创建socket并且调用connect()连接Server端创建的监听端口。得到与Server端连接的socket fd后调用wl_display_connect_to_fd()创建wl_display。wl_display是Server端的display资源在Client端的代理对象,它的第一个元素wl_proxy,因此它可以与wl_proxy互转。和Server端一样,也会创建一个wl_connection包含读写缓冲区。
    wl_display_connect()
        fd =connect_to_socket() // 尝试连接$XDG_RUNTIME_DIR/wayland-0,返回与Server端相连的socket fd。
            wl_os_socket_cloexec() 
            connect()
        wl_display_connect_to_fd()// 创建和返回wl_display。
            display->proxy.object_interface = &wl_display_interface; // 设置wl_display的接口。
            display->proxy.object.implementation = (void(**)(void)) &display_listener // 对Server而言,Client端提供的接口实现是event部分。
            display->connection = wl_connection_create() 

    可以看到display在Client端的wl_proxy和Server端wl_resource都包含了它完整的接口描述wl_display_interface。但wl_proxy只包含了event的实现display_listener,wl_resource只包含了request的实现display_interface。

    三、消息处理模型


    在Server端,Compositor初始化完后会调用wl_display_run()进入大循环。这个函数主体是:
    while (...) {
        wl_display_flush_clients() // 将对应的out buffer通过socket发出去。
        wl_event_loop_dispatch() // 处理消息循环。
    }
    wl_event_loop代表主消息循环,wl_event_loop_dispatch()的大多数时间会通过epoll_wait()等待在wl_event_loop的epoll_fd上。epoll是类似于select, poll的机制,可以让调用者等待在一坨fd上,直到其中有fd可读、可写或错误。这个event loop和其中的epoll_fd是Compositor在wl_display_create() ->  wl_event_loop_create()时创建的。

    wl_event_source代表wl_event_loop中等待的事件源。它有多种类型,比如wl_event_source_fd, wl_event_source_timer和wl_event_source_signal。它们分别代表由socket fd, timerfd和signalfd触发的事件源。wl_event_source_idle比较特殊,当消息循环处理完那些epoll_fd上等到的事件后,在下一次阻塞在epoll_wait()上前,会先处理这个idle list里的事件。比如有Client更新graphic buffer后会调用weston_output_schedule_repaint() -> wl_event_loop_add_idle(),就是往这个idle list里加一个消息。wl_event_source_fd的创建为例,在Client连接到Server时,Server会调用wl_client_create() -> wl_event_loop_add_fd()->add_source()将之加入到display的loop上,其处理回调函数为wl_client_connection_data(),意味着当主消息循环在这个Client对应的socket上读到数据时,就会调用wl_client_connection_data()进行处理。

    在Client端,当需要与Server端交换数据时,最终会调用wl_display_dispatch_queue()。其中最主要的是三件事:
    1. wl_connection_flush()将当前out buffer中的数据通过socket发往Server端。这些数据是之前在wl_connection_write()中写入的。
    2. 通过poll()在socket上等待数据,并通过read_events()将这些数据处理生成函数闭包结构wl_closure,然后放到display的wl_event_queue.event_list事件列表中。wl_closure可以看作是一个函数调用实例,里面包含了一个函数调用需要的所有信息。
    3. dispatch_queue()->dispatch_event()用于处理前面添加到队列的事件。这里就是把队列中的wl_closure拿出来生成trampoline后进行调用。

    四、跨进程过程调用


    术语上,Wayland中把Client发给Server的跨进程函数调用称为request,反方向的跨进程函数调用称为event。本质上,它们处理的方式是类似的。要让两个进程通过socket进行函数调用,首先需要将调用抽象成数据流的形式。函数的接口部分是同时链接到Client和Server端的库中的,其中包含了对象所支持的request和event的函数签名。因此这部分不用传输,只要传输目标对象id,方法id和参数列表这些信息就可以了。这些信息会通过wl_closure_marshal()写入wl_closure结构,再由serialize_closure()变成数据流。等到了目标进程后,会从数据流通过wl_connection_demarshal()转回wl_closure。 这个过程类似于Android中的Parcel机制。那么 问题来了,参数中的整形,字符串什么的都好搞,拷贝就行。但如果参数中包含对象,我们不能把整个对象拷贝过去,也不能传引用过去。那么需要一种机制来作同一对象在Server和Client端的映射,这是通过wl_map实现的。wl_map在Client和Server端各有一个,它们分别存了wl_proxy和wl_resource的数组,且是一一对应的。这些对象在这个数组中的索引作为它们的id。这样,参数中的对象只要传id,这个id被传到目的地后会通过查找这个wl_map表来得到本地相应的对象。在功能上类似于Android中的BpXXX和BnXXX。 wl_proxy和wl_resource都包含wl_object对象。这个wl_object和面向对象语言里的对象概念类似,它有interface成员描述了这个对象所实现的接口,implementation是这些接口的实现函数的函数指针数组,id就是在wl_map结构里数组中的索引。前面所说的Client绑定Server端资源的过程就是在Client端创建wl_proxy,在Server端创建wl_resource。然后Client就可以通过wl_proxy调用Server端对应wl_resource的request,Server端就可以通过wl_resource调用Client端对应wl_proxy的event。这个映射过程如下图所示(以wl_registry为例):


    与Android不同的是,Android中请求到达Server端,调用时需要在目标对象中有一段Stub来生成调用的上下文。 而Wayland中,这是由libffi完成的。

    Wayland核心协议是通过protocol/wayland.xml这个文件定义的。它通过wayland_scanner这个程序扫描后会生成wayland-protocol.c, wayland-client-protocol.h和wayland-server-protocol.h三个文件。wayland-client-protocol.h是给Client用的;wayland-server-protocol.h是给Server用的; wayland-protocol.c描述了接口,Client和Server都会用。 这里以wl_display的get_registry()这个request为例,分析下跨进程的过程调用是如何实现的。

    首先在wayland.xml中申明wl_display有get_registry这个request:
       54     <request name="get_registry">
       55       <description summary="get global registry object">
       56     This request creates a registry object that allows the client
       57     to list and bind the global objects available from the
       58     compositor.
       59       </description>
       60       <arg name="registry" type="new_id" interface="wl_registry"/>
       61     </request>
    这里的参数类型是new_id,说明需要新建一个代理对象。其它的如object代表一个对象,fd代表代表文件描述符等。

    wayland-protocol.c中描述了wl_display这个对象的request和event信息,其中包含了它们的函数签名。get_registry是request中的一项。
     147 static const struct wl_message wl_display_requests[] = {
     148     { "sync", "n", types + 8 },
     149     { "get_registry", "n", types + 9 },
     150 };
     151 
     152 static const struct wl_message wl_display_events[] = {
     153     { "error", "ous", types + 0 },
     154     { "delete_id", "u", types + 0 },
     155 };
     156 
     157 WL_EXPORT const struct wl_interface wl_display_interface = {
     158     "wl_display", 1,
     159     2, wl_display_requests,
     160     2, wl_display_events,
     161 };

    wayland-server-protocol.h中:
      115 struct wl_display_interface {
    ...
      143     void (*get_registry)(struct wl_client *client,
      144                  struct wl_resource *resource,
      145                  uint32_t registry);
    这个声明是让Server端定义implementation中的实现函数列表用的,如:
      761 static const struct wl_display_interface display_interface = {
      762     display_sync,
      763     display_get_registry
      764 };

    wayland-client-protocol.h中:
      184 static inline struct wl_registry *
      185 wl_display_get_registry(struct wl_display *wl_display)
      186 {
      187     struct wl_proxy *registry; 
      188 
      189     registry = wl_proxy_marshal_constructor((struct wl_proxy *) wl_display,
      190              WL_DISPLAY_GET_REGISTRY, &wl_registry_interface, NULL);
      191 
      192     return (struct wl_registry *) registry;
      193 }   
    这是给Client端用来发起request的。当客户端调用wl_display_get_registry(),由于要返回代理对象,所以调用wl_proxy_mashal_constructor()。 返回的wl_registry是一个代理对象。
    wl_display_get_registry()
        wl_proxy_marshal_constructor()
            wl_argument_from_va_list() // 将上面传来的参数按wl_display_interface->methods[WL_DISPLAY_GET_REGISTRY]中签名描述的类型放到wl_argument数组中。
            wl_proxy_marshal_array_constructor() 
                new_proxy = create_outgoing_proxy()  // 因为get_registry()的request参数中有new_id类型,所以要创建一个代理对象。
                     proxy_create() //创建wl_proxy。设置interface等信息,然后将该wl_proxy插入到display->objects的wl_map中,返回值为id,其实就是在wl_map中数组中的索引值。这个值是会被发到Server端的,这样Server端就可以在Server端的wl_map数组的相同索引值的位置插入相应的wl_resource。这样逻辑上,就创建了wl_proxy和wl_resource的映射关系。以后,Client和Server间要相互引用对象只要传这个id就可以了。
                closure = wl_closure_marshal() //创建wl_closure并根据前面的参数列表初始化。先将前面生成的wl_argument数组拷贝到wl_closure的args成员中。然后根据类型做调整,如将wl_proxy的对象指针改为id,因为传个本地指针到另一个进程是没意义的。
                wl_closure_send() // 发送前面生成的wl_closure。
                    copy_fds_to_connection() // 将参数中的fd放到专门的fd out buffer中。因为它们在发送时是要特殊处理的。
                    serialize_closure() //将wl_closure转化为byte stream。像类型为object的参数会转化为id。
                    wl_connection_write() // 放到connection的out buffer,准备通过socket发送 。
    到这里本地的wl_registry代理对象创建完成,并且准备向Server发出request。当下次执行到wl_display_dispatch_queue()时,会调用wl_connection_flush()把connection中out buffer的request通过socket发出去。当然,在往out buffer写时发现满了也会调用wl_connection_flush()往socket发数据。

    到了Server端,前面提到会调用处理函数wl_client_connection_data()进行处理:
    wl_client_connection_data()
        wl_connection_flush() //向Client发送数据。
        wl_connection_read() //从Client接收处理。
        while (...) // 循环处理从socket中读到的数据。
            wl_connection_copy() // 每次从缓冲中读入64字节。它相当于一个request的header,后面会跟参数数据。其中前4个字节代表是向哪个对象发出request的。后面4个字节包含opcode(代表是这个object的哪个request),及后面参数的长度。
            wl_map_lookup() // 在wl_map中查找目标资源对象wl_resource。其成员object中有该对象的接口和实现列表。结合前面的opcode就可以得到相应的request的描述,用wl_message表示。如 { "get_registry", "n", types + 9 }。
            wl_connection_demarshal()   // 根据interface中的函数签名信息生成函数闭包wl_closure。主要是通过wl_message中对参数的描述从缓冲区中把参数读到wl_closure的args成员中。wl_closure的args成员是wl_argument的数组。因为这里无法预先知道参数类型,所以wl_argument是个union。
            wl_closure_lookup_objects() // wl_closure中的参数中如果有object的话,现在还只有id号。这步会通过wl_map把id号转为wl_object。
            wl_closure_invoke() //使用libffi库生成trampoline code,跳过去执行。

    在这个场景下,由于之前在bind_display()中把client->display_resource的implementation设为:
      761 static const struct wl_display_interface display_interface = {
      762     display_sync,
      763     display_get_registry
      764 };
    所以接下来会调用到display_get_registry()。这个函数里会创建wl_registry对应的wl_resource对象。创建好后会放到display->registry_resource_list中。前面提到过,这个registry资源逻辑上的作用是Client放在Server端的Observer,它用于监听Server端有哪些Global对象(Service服务)。display_get_registry()函数接下去会对于每一个Global对象 向该Client新建的registry发送事件 。另外在有Global对象创建和销毁时(wl_global_create()和wl_global_destroy()),Server会向所有的registry发送事件进行通知。因此,Global对象可以理解为可动态加载的Service。

    那么,这些Global对象具体都是些什么呢?为了故事的完整性,这里就插播段题外话。Server端的Compositor在启动时一般会注册一些Global对象,逻辑上其实就是一些服务。 通过Wayland提供的wl_global_create()添加:
    wl_global_create()
          global->name = display->id++; // Global对象的id号。
          global->interface = interface;
          wl_list_insert(display->global_list.prev, &global->link); //  display->global_list保存了Global对象的列表。
          wl_list_for_each(resource, &display->registry_resource_list, link) // 向之前注册过的registry对象发送这个新创建Global对象的event。
              wl_resource_post_event(resource,  WL_REGISTRY_GLOBAL, global->name, global->interface->name, global->version);
    以wl_compositor这个Global对象为例, Server端调用 wl_global_create(display, & wl_compositor_interface , 3,  ec, compositor_bind)。然后当 Client端调用wl_display_get_registry()时,Server端的display_get_registry()会 对每个Global对象 向Client发送global事件,因此Server端有几个Global对象就会发几个event。Client收到event后调用先前注册的回调registry_handle_global()。根据interface name判断当前发来的是哪一个,然后调用wl_reigistry_bind(..., &wl_compositor_interface,..)绑定资源,同时创建本地代理对象。接着S erver端相应地调用registry_bind(),其中会调用先前在wl_global_create()中注册的回调函数,即compositor_bind()。接着经过 wl_resource_create(), wl_resource_set_implementation()等创建wl_resource对象。也就是说,对于同一个Global对象,每有Client绑定一次,就会创建一个wl_resource对象。换句话说,对于Server来说,每一个Client有一个命名空间,同一个Global对象在每一个Client命名空间的wl_resource是不一样的。这样,对于一个Global对象(Service服务),在Client端创建了wl_proxy,在Server端创建了wl_resource,它们就这样绑定起来了。wl_proxy.object包含了event的处理函数,这是对Server端暴露的接口,而wl_resource.object包含了request的处理函数,这是对Client暴露的接口。

    回到故事主线上,前面是从Client端调用Server端对象的request的流程,从Server端向Client端对象发送event并调用其回调函数的过程也是类似的。下面以display_get_registry()中向Client端发送global事件为例分析下流程。S erver端通过wl_resource_post_event()来向Client发送event。
    wl_resource_post_event()
        wl_resource_post_event_array()
            wl_closure_marshal() // 封装成wl_closure,其中会转化object等对象。
            wl_closure_send()
                copy_fds_to_connection()
                serialize_closure() // 将closure序列化成数据流,因为将要放到socket上传输。
                wl_connection_write()
    这样event就被放到connection的out buffer中,等待从socket上发送。 那么,Client是怎么读取和处理这些event呢? 首先Client端需要监听这个wl_proxy,这是通过调用wl_registry_add_listener()->wl_proxy_add_listener()设置的。该函数的参数中包含了这个event的处理函数列表registry_listener,它对应的接口在前面调用wl_display_get_registry()时已设置成wl_registry_interface。wl_registry_interface是在前面根据wayland.xml自动生成的一部分。这里体现了event与request的一点细微差别,request是Server端都要处理的,而event在Client可以选择不监听。
      
    然后在Client的主循环中会调用wl_display_dispatch_queue()来处理收到的event和发出out buffer中的request:
    wl_display_dispatch_queue()
        dispatch_queue()
        wl_connection_flush()
        read_events() // 从connection的in buffer中读出数据,转为wl_closure,插入到queue->event_list,等待后续处理。
            wl_connection_read()
            queue_event() //这块处理有点像Server端的wl_client_connection_data(),区别在于这里用的是wl_reigstry_interface的events列表而不是methods列表。
                wl_connection_copy()
                wl_map_lookup() // 查找目标代理对象wl_proxy。
                wl_connection_demarshal() // 从connection的缓冲区中读入数据,结合函数签名生成wl_closure。
                create_proxies() 
                wl_closure_lookup_objects() 
        dispatch_queue() // 将前面插入到queue当中的event(wl_closure)依次拿出来处理。
            dispatch_event(queue) //  display->display_queue->event_list的每一个元素是一个wl_closure,代表一个函数调用实例,最后通过wl_closure_invoke()进行调用。
               wl_closure_invoke()

    这样该event的相应处理回调函数就被调用了,在这个场景中,即registry_handle_global()。下图简单地描绘了整个流程。 



    展开全文
  • Wayland 架构

    2020-04-27 21:29:07
    本篇文章翻译自 Wayland 官方网站的Wayland architecture。 Wayland 架构 理解 Wayland 架构及其与 X 的区别的绝佳途径是看一下从输入设备接收事件到屏幕发生变化的整个流程。 这是我们目前使用的 X 架构: ...
  • QtWayland

    千次阅读 2016-07-11 16:06:34
    什么是QtWayland? QtWayland是封装了Wayland功能的Qt 5模块。QtWayland被分为一个客户端(client)和一个服务端(server)。客户端是wayland平台插件,提供了运行Wayland客户端Qt程序的方法。服务端是QtCompositor应用...
  • Qt5 on Wayland

    千次阅读 2020-07-09 01:17:11
    简 述: 翻译一篇关于和 wayland 有关的技术文章, 其英文标题为 Qt 5 on Wayland. Qt 5工具箱中的Wayland支持在Qt Platform Abstraction(QPA) Wayland插件中进行。构建和使用QtWayland的最低要求是Wayland(和...
  • 之前移植Gtk时,发现如果以Wayland为后端设置窗口居中的话,会出现第一个窗口可以居中,但是后面创建的弹窗窗口出现的位置都是随机的。后来发现了解决办法: GTK对于Wayland作为后端时有做特殊处理: 在Wayland...
  • wayland helloworld (二) 之Wayland鼠标消息

    千次阅读 2014-05-17 17:03:22
    Wayland鼠标消息
  • wl_display_connect is the most common way for clients to establish a Wayland connection. The signature is: struct wl_display *wl_display_connect(const char *name); The “name” argument is the na
  • Wayland与Weston简介

    万次阅读 多人点赞 2015-08-08 21:11:20
    简单地说,Wayland是一套display server(Wayland compositor)与client间的通信协议,而Weston是Wayland compositor的参考实现。其官网为http://wayland.freedesktop.org/。它们定位于在Linux上替换X图形系统。X图形...
  • Wayland架构、渲染、硬件支持

    千次阅读 2020-07-09 01:15:42
    文章目录Wayland 架构Wayland 渲染Wayland的 硬件支持 简 述: 翻译一篇关于和 wayland 有关的技术文章, 其英文标题为Wayland Architecture . Wayland 架构 若是想要更好的理解 Wayland 架构及其与 X (X11 or X ...
  • 关闭centos wayland

    2020-12-24 15:56:31
    看图形界面是: Wayland还是X11 参考:https://linuxconfig.org/how-to-disable-wayland-and-enable-xorg-display-server-on-ubuntu-18-04-bionic-beaver-linux 2、关闭Wayland方法: sudo vim /etc/gdm/custom...
  • wayland相关知识

    2018-11-09 11:05:43
    版权声明:本文为博主原创文章,未经博主允许不得转载。... ... 看了一天Wayland,如果不做个笔记,估计晚上没到家就忘光了(话说越来越健忘了,不知是什么毛病...
  • Wayland协议解析 四 Wayland的所有接口

    千次阅读 2018-10-18 09:18:39
    在介绍这些类的时候我必须给大家把wayland的interface分个类. Global Object, 我把它叫做服务类, 就是提供给客户端各种功能的接口. 就相当于是说,对于客户端来说,我给你提供了哪些服务,你就只能通过这些服务来进行...
  • Wayland消息队列

    千次阅读 2014-05-18 14:04:15
    Wayland允许创建多个消息队列,使用wl_display_create_queue()创建,新建的消息队列可以绑定到一个wl_proxy对象上。 消息循环 与Win32消息循环不同,Wayland消息循环只需要一个函数: int ret = 0
  • display:Wayland Architecture

    2019-07-04 10:51:53
    Wayland Architecture A good way to understand the wayland architecture and how it is different from X is to follow an event from the input device to the point where the change it affects appears on s...
  • Wayland协议解析 三 Wayland的工作原理

    千次阅读 2018-10-18 09:17:11
    首先,需要了解wayland的工作原理,需要了解几个其他的内容。 int mkstemp(char *template); // 创建临时文件 (标C接口) long int strtol(const char *nptr,char **endptr,int base); // 字符串转数字 ssize_t ...
  • 为了更好的学习wayland,我们可以先学习wayland中定义的一些数据结构.因为贯穿wayland的所有东西都是基于这些数据结构.   首先介绍wl_array struct wl_array { /** Array size */ size_t size; /** Allocated ...
  • Wayland相关名词解释

    千次阅读 2016-01-12 18:38:49
    看了一天Wayland,如果不做个笔记,估计晚上没到家就忘光了(话说越来越健忘了,不知是什么毛病)。 1. Wayland: 是一个叫Wayland compositor的Display server与其客户端之间通信的协议。一个叫做Kristian Hø...
  • wayland进程间调用

    千次阅读 2017-11-15 10:36:32
    以Weston自带的例程simple-shm为例,先感受一下Client如何通过Wayland协议和Compositor通信。 1. 连接Server,绑定服务 1) display->display = wl_display_connect()// 通过socket建立与Server端的连接,得到wl_...
  • Wayland是什么

    千次阅读 2011-06-15 16:32:00
    Ubuntu”或其他地方看到了这篇文章:Ubuntu 决定未来将启用 Wayland X-Server。Wayland是什么呢?它是X Window?还是要取代X Window?它的优势在哪里?Linux桌面/移动会因此有什么变化?在本篇中,我将回顾历史,...
  • wayland protocal and programming

    千次阅读 2019-07-24 15:54:52
    Written in 2012 before wayland 1.0 released, most of them are outdated as in May 2017. wayland是异步的面向对象的协议,所有请求都是在某个对象上的方法调用。每个对象实现一个接口,描述一系列操作。请求...
  • 通过XKB_DEFAULT *环境变量支持键盘设置。 按F1退出 与客户进行测试: 韦斯顿·简·史密斯 韦斯顿简单蛋 温斯顿终端 错误: 有时simple-egl在egl_init处失败[已解决?] 有时simple-egl失败,并且该进程在CPU...
  • wayland的client端和server端的跨进程通信是通过socket实现的。本文首先对server端的socket的生成,绑定,监听进行分析,以wayland的源码中自带的weston代码为例,在server端的main函数中,会调用weston_create_...
  • Wayland (简体中文)

    2021-01-08 17:29:03
    Screen capture#Wayland 翻译状态:本文是 Wayland 的翻译。上次翻译日期:2020-08-08。如果英文版本有所更改,则您可以帮助同步翻译。 Wayland 是一个用于 混成窗口管理器 与其客户端对话的协议,也是一个实现该...
  • 基于wayland/weston源码编译简介下载工具设置环境变量构建wayland构建wayland-protocols构建weston运行westonweston的一些测试程序 简介 官网:https://wayland.freedesktop.org/ 简单地说,Wayland是一套display ...

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 1,275
精华内容 510
关键字:

wayland设置