在之前的分享中,我们提到Linux下主流的窗口系统是X窗口系统X Window System(以下:简称X)。现有的国产操作系统基本基于Linux开发,因此对X接口的抽象和封装,是合迅智灵国产化C++基础开发套件的图形绘制子系统中最为重要的组成部分。
X窗口系统简介
X的初学者经常惊讶于一个事实,那就是X其实是一个基于服务器-客户端的网络架构。服务端(X Server)负责管理来自客户端(X Client)的连接,处理键盘和鼠标等输入设备的输入,并向客户端返回像素数据作为响应;而客户端则是由一个实际的应用程序组成,而应用程序通过发送请求到服务端,读取服务端的响应,才可以顺利更新自己的界面。这个过程就像是应用程序请求X Server 说“请告诉我现在的样子”,服务端收到这个请求后,通过内置的逻辑进行复杂的计算,最后得到一张图并返回到应用程序,以完成应用程序的界面绘制。
X Window System 架构图(图源:X.org 官网)
事实上,X 的确可以通过网络实现“远程绘制界面”这样的操作。X Server 和 X Client 完全可以运行在不同的机器上,并经由网络协议通信。当然如果它们运行在同一台机器的话,就是你“本机运行 Linux 系统”的情形了。
下面是一些额外的概念:
X11:众所周知,客户端和服务端要实现通信需要遵循一定的协议。X11 就是现行协议的名称,即“X 协议的第11个版本”;
Xlib、xcb:都是实现X11协议的C语言库,程序员正是调用的这些库,在 X环境下开发图形界面程序。
X还封装了显示设备(Display)和屏幕(Screen)的概念,作为对图像输出终端硬件设备的完整抽象。显示设备负责管理一套输入设备和多个屏幕。显示设备描述了 X Server 图像的最终输出目标;屏幕可以简单理解为硬件显示屏,每个屏幕拥有长宽尺寸等属性,用于承载实际的图像输出。
比如常见的多屏幕工作站,本身是一个显示设备,只有一套输入设备(键盘鼠标/触摸),管理着多块屏幕。应用程序可以显示在任意一块屏幕上,甚至可以通过跨屏幕来进行显示,为用户带来更加流畅和个性化的使用体验,大大提高了工作效率。
多屏工作站
X事件的处理
事件的本质,即由X Server服务器端所发起的,针对X Client客户端提出的请求的响应。请求和响应的种类很多,其中输入设备就尤为典型。客户端可以注册(XSelectInput)自身所关心的事件类型,对于不关心的事件将直接丢弃来提高效率和降低逻辑复杂程度。
开发工具所封装的跨平台事件,如Qt的QEvent,以及合迅智灵的LEvent,可以实现跨平台使用,是因为开发工具在不同的窗口系统上对窗口系统的原生事件进行了适当的包装,从而可以生成统一的、跨平台的事件对象。
基于此,合迅智灵实现了统一的事件监听器。事件监听器封装了X的事件响应机制,并负责将X系统事件包装为合迅智灵LEvent,并存放于事件队列中,等待事件循环进行处理与分发。事件监听器是平台无关的抽象类,例如后续只需要单独针对Wayland的事件机制作出处理,即可实现针对Wayland平台的事件监听器。
合迅智灵LarkSDK事件机制示意图
X窗口的绘制 窗口可以作为屏幕输出承载,那么窗口是怎么输出图像的呢?目前,合迅智灵已经实现了自研的图形绘制系统,通过内置的软渲染引擎对所绘制组件内容的光栅化,可以将一个完整的应用程序窗体输出为一张二维栅格图片,存放于内存之中,这块内存区域即是窗口的后台缓存。在后台缓存中进行绘制,并不会影响到屏幕上的窗口显示。当绘制完成,只需要通过一次刷新操作(XFlush),即可将后台缓存中的图像映射到窗口中,显示到屏幕上。 窗口的绘制 不过,无论是顶层的计算器窗口,还是内部的“按钮”窗口,在窗口系统语境下都是一视同仁的。但我们其实并不需要单独为“按钮”窗口分配单独的绘图后台缓存,而只将其作为输入接收器(有点像鼠标点击的“热区”概念),不参与绘制输出。针对此需求,X提供了两种不同的窗口类型: InputOutput:可接受输入,同时也作为可绘制的实体(Drawable)承担图形输出职责,可以处理一切和图形输出相关的事件,如暴露事件(XExposeEvent)。X 客户端收到此事件之后会进行重绘尝试。 InputOnly:仅承担输入职责,在屏幕上无显示,不承担图形输出职责,不处理图形输出相关事件。其他和InputOutput类型的窗口一致。 因此,我们完全可以用前者作为顶层窗口,负责整个应用程序窗体的图形输出,而用后者负责各组件的事件逻辑输入处理,其本身的绘制可以统一交给顶层的InputOutput窗口处理。这种方案的好处是可以轻松处理组件之间的层叠和混合效果(比如半透明),但代码逻辑较为复杂,需要很好的处理每个组件相对于顶层窗口的偏移位置。这也是合迅智灵目前采用的方案。 小结 因为国内开发工具匮乏,为了高效地开发图形界面应用程序,开发人员不得不大量采用国外开发工具,这对我国基础软件的国产化构成了潜在威胁。 合迅智灵作为国内C++基础开发工具领域的首款产品,我们虽然需要正视在基础开发工具方面与国外同行的差距,但同时也具备一定的后发优势,可以基于前人的经验,走出属于自己的特色与创新的路子,创造出真正好用且适用于国内环境的优秀开发工具产品。