7.1.13 管理已存在的窗口

在窗口管理器启动之前,可能有一些应用已经在运行。因此,在窗口管理器启动时,需要管理这些已存在的窗口。这就是winman的初始化时调用函数init_clients的目的。init_clients的实现代码如下:

7.1.13 管理已存在的窗口 - 图1

函数init_clients执行的主要操作如下:

1)调用Xlib的函数XQueryTree查询根窗口的子窗口,参数children指向保存子窗口的内存区,变量n记录子窗口的数量。

2)遍历根窗口的子窗口,如果窗口的属性override_redirect的值为False,则表明窗口需要窗口管理器管理;然后检查窗口的显示状态,如果值是IsViewable,(IsViewable表示的意思是"Window is viewable",而不是"Is window viewable?"),则表示窗口是可见的,那么init_clients就调用函数wm_new_client开始管理窗口。

3)将变量ignore_unmap累加1。为什么需要这么一个变量,并且这里将其累加1呢?对于这些窗口,在窗口管理器启动前,它们已经是可见的。而为了管理这些窗口,窗口管理器将使用Frame窗口作为它们的父窗口,因此它们当然要首先从根窗口剥离了。换句话说,X服务器需要将窗口从窗口树中当前的位置删除,并插入到新的位置。X当然不想让人看到它的这个小动作,因此,在执行这个过程前,X服务器首先取消这些窗口的显示,也就是说,X服务器先把窗口藏起来,让它们不可见,然后偷偷摸摸地把它们移动到窗口树中新的位置,移动完成后,再让窗口可见。恰恰是X服务器的这个将窗口设置为不可见的动作带来了麻烦。在X服务器取消窗口的可见状态时,窗口管理器将收到通知UnmapNotify,如果不加任何甄别,将导致init_clients刚刚管理的窗口又被销毁。显然,这不是我们希望的,因此结构体Client中设计了变量ignore_unmap来忽略这个特殊的UnmapNotify事件。

至此,我们的迷你窗口管理管理器开发完成了,我们将其复制到vita系统,并使用如下命令运行:

7.1.13 管理已存在的窗口 - 图2

如果没有问题,将看到一个类似图7-13所示的窗口。

7.1.13 管理已存在的窗口 - 图3

图 7-13 窗口管理器

根据图7-13中可见,hello_gtk的窗口不再是一个光秃秃的裸窗口了,它被添加了各种装饰,看上去更有立体感了。而且我们可以拖住标题栏移动窗口,也可以改变窗口大小,可以关闭窗口,可以最大化窗口,但是一旦最小化窗口,就再也找不到它了。下一节,我们构建了任务条来解决这个问题。