7.1.4 初始化

谈及初始化,大多给人的印象是进行一些琐碎的准备工作,但是winman中有几处却非常关键,相关代码如下:

7.1.4 初始化 - 图1

7.1.4 初始化 - 图2

下面介绍该函数主要执行的操作。

(1)连接X服务器

窗口管理器与普通的X应用并无本质区别,只是具有一点点特权,它也是X服务器的一个客户端。从服务器和客户端的体系架构角度而言,应用程序当然需要和服务器建立连接后才能通信,为此,Xlib提供了函数XOpenDisplay用于建立它们之间的连接。

(2)错误处理

Xlib将错误分为两类:一类错误是不可恢复的,这类错误是致命的,一旦发生后,应用基本不可能执行下去了,如应用程序和服务器的连接断开了,除了终止应用程序外别无选择;另外一类是协议错误,比如当应用读取某个窗口的属性时,这个窗口可能在服务器中已经不存在了。显然,这类错误不是致命的,应用完全可以自己决定是忽略错误还是终止执行。

Xlib为这两类错误都设置了默认的错误处理函数,它们的行为均是打印错误提示并终止应用。但是Xlib也分别提供了接口XSetIOErrorHandler和XSetErrorHandler允许应用设置自己的致命错误和协议错误处理函数。winman设置了协议错误处理函数为error_handler,当发生协议错误时,error_handler只打印错误信息,并不终止执行。

(3)创建Atoms

函数atom_init使用Xlib的函数XInternAtoms,一次性创建后面用到的属性的Atom,并将Atom保存在结构体WinMan的atoms数组中。相关代码如下:

7.1.4 初始化 - 图3

7.1.4 初始化 - 图4

最初,X标准协会制定了ICCC通信协议。后来,随着现代桌面的发展,又制定了EWMH,即对ICCCM进行的补充扩展。这两个协议中定义了大量的属性,在函数atominit中,以WM开头的基本是ICCCM标准中定义的,以NET开头的是EWMH中定义的。除了标准定义的属性外,应用也可以自定义属性,其中以CUSTOM开头的就是winman自定义的属性。

(4)拦截事件

函数XSelectInput可以说是窗口管理器的画龙点睛之笔了。winman按照前面的讨论,选择了根窗口的"SubstructureRedirectMask"和"SubstructureNotifyMask"。

(5)管理已存在的窗口

在窗口管理器启动之前,系统上可能已经有X应用在运行。因此,winman启动时需要管理这些已存在的窗口,函数init_clients就是做这件事的,其具体细节请参见7.1.13节。

(6)事件循环

初始化完成后,窗口管理器进入事件循环,函数wm_event_loop调用Xlib的函数XNextEvent将窗口管理器对X的请求发送给X服务器,然后检查事件队列,调用相应的事件处理函数。接下来的的章节中,我们会陆续讨论这些事件处理函数。

需要特殊指出的是Expose事件,以图7-7所示窗口布局为例。

7.1.4 初始化 - 图5

图 7-7 多个连续Expose事件发生情况

Window E有四个区域分别被Window A~D遮挡,当Window E成为当前活动窗口时,X服务器将为每一个被遮挡的部分都报告一个Expose事件,并将同一个动作引发的Expose事件连续的放到事件队列中。结构体XExposeEvent中的变量count就是用来记录一个Expose事件后面还有多少个Expose事件的。

因此,从效率的角度来讲,对于多个连续的Expose事件,应用应该忽略掉最后一个Expose事件前面所有的Expose事件,而在收到最后一个Expose事件时才进行绘制。