http://www.ibm.com/developerworks/cn/linux/l-cn-xwin/index.html#major3
简介: 本文介绍 X Window 系统的窗口显示原理。从一个简单的 X 客户端程序入手,介绍了窗口的创建接口 XCreateWindow 和显示接口 XMapWindow 的实现,并结合窗口管理器介绍了 X Server、X Client 和窗口管理器三部分交互的原理。
为本文评分
X Window 系统是一个基于网络的图形界面系统,它于 1984 年在麻省理工学院开发,有将近 20 年的应用历史。X Window 系统广泛的应用于桌面 Linux(如 Fedora、Debian、Ubuntu 等),嵌入式 Linux(如 Nokia 的 Maemo、Intel 的 Moblin 等)。随着 Nokia 和 Intel 高调的将 Maemo 和 Moblin 合并为 Meego,X Window 系统的应用将被推向一个新的高潮。
X Window 是 C/S 架构,涵盖 X Server、X 协议、X Client 三部分内容。其 X Client 有三种开发模式:基于 XLib、基于 GTK、基于 Qt。
本文将以一个基于 XLib 的应用来介绍 X Window 的窗口显示原理。
示例代码将显示一个 200X200 的白色背景窗口,并在窗口的中间绘制一个 100 个点,连接成一条横线。按任意按键可以退出该程序。
清单 1. hello.c
#include <stdio.h> #include <stdlib.h> #include <X11/Xlib.h> #define WINDOW_SIZE 200 int main (int argc, char *argv[]) { Display *dpy; XSetWindowAttributes attributes; Window win; GC gc; XKeyEvent event; int i; // 连接到 X Server,创建到 X Server 的套接字连接 dpy = XOpenDisplay(NULL); // 创建 200X200 的白色背景窗口 attributes.background_pixel = XWhitePixel(dpy, 0); win = XCreateWindow(dpy, XRootWindow(dpy, 0), 0, 0, WINDOW_SIZE, WINDOW_SIZE, 0, DefaultDepth(dpy, 0), InputOutput, DefaultVisual(dpy, 0), CWBackPixel,&attributes); // 选择输入事件。 XSelectInput(dpy, win, ExposureMask | KeyPressMask ); // 创建绘图上下文 gc = XCreateGC(dpy, win, 0, NULL); //Map 窗口 XMapWindow(dpy, win); // 事件主循环。主要处理 Expose 事件和 KeyPress 事件 while(1) { XNextEvent(dpy,(XEvent *)&event); switch(event.type) { // 处理 Expose 事件 case Expose: { // 绘制 100 个点 for (i=0;i<WINDOW_SIZE/2;i++) XDrawPoint(dpy, win, gc, WINDOW_SIZE/4+i, WINDOW_SIZE/2); }break; // 处理按键事件 case KeyPress: { XFreeGC(dpy, gc); XCloseDisplay(dpy); exit(0); }break; default: { }break; } } return(0); } |
编译:gcc -o hello hello.c -lX11
图 1. Ubuntu 9.10 运行结果
图 2. Maemo Fremantle 模拟器运行结果
在示例代码 hello.c 中,和窗口显示相关的接口主要有:XCreateWindow,XMapWindow,XDrawPoint。在示例代码之外,X Server 和窗口管理器同时在发挥着各自的关键作用。本文将结合 X Server、窗口管理器、示例 hello.c 代码来解释窗口显示的原理。
在 X Window 系统中,客户端申请的 GC、Pixmap、Window 等资源位于服务器 X Server 端。而客户端创建的另一些资源如 XImage,不由 X Server 管理,而是由客户端自行管理。
在客户端调用 XCreateWindow 创建一个窗口时,X Server 会为它建立一个 Window 类型的数据结构。该结构中描述了窗口的大小、坐标等信息。窗口实际上是屏幕的一块区域,子窗口是父窗口的一部分,所有的窗口有一个共同的根即根窗口。
客户端调用 XCreateWindow 接口时,对应的 X Server 实现是 CreateWindow 函数:
清单 2. CreateWindow 实现
WindowPtr CreateWindow(Window wid, WindowPtr pParent, int x, int y, unsigned w, unsigned h, unsigned bw, unsigned class, Mask vmask, XID *vlist, int depth, ClientPtr client, VisualID visual, int *error) { /* 省略非关键代码部分 */ pScreen = pParent->drawable.pScreen; pWin->drawable = pParent->drawable;/* 子窗口是父窗口的一部分 */ pWin->devPrivates = NULL; pWin->drawable.depth = depth; /* 省略非关键代码部分 */ pWin->origin.x = x + (int)bw; pWin->origin.y = y + (int)bw; pWin->drawable.width = w;/* 窗口管理信息 */ pWin->drawable.height = h; pWin->drawable.x = pParent->drawable.x + x + (int)bw; pWin->drawable.y = pParent->drawable.y + y + (int)bw; … } |
在 GTK 中,调用 gdk_window_new 会创建一个 X 窗口。GTK 提供了三种类型的顶层窗口:GDK_WINDOW_TOPLEVEL、GDK_WINDOW_DIALOG、GDK_WINDOW_TEMP。这些顶层窗口的父亲是 GDK_SCREEN_XROOTWIN,即根窗口。而 GDK_WINDOW_CHILD 类型的窗口其父亲由用户创建窗口时指定。GDK_WINDOW_CHILD 类型的窗口对应的是 GTK 的控件,如 GtkButton、GtkEntry 等。
如前述,所有的窗口都是父窗口的一部分。所有窗口的根是根窗口。根窗口由 X Server 在启动时创建,对应整个屏幕区域。
以 GTK 为例子,GTK 窗口层级视图是下图 3 的样子:
图 3. GTK 窗口层级视图
对单个应用而言,部分窗口管理器如 Matchbox Window Manager 只支持一个应用级顶层窗口,譬如 Maemo Fremantle 使用的就是该类型的窗口管理器。这就是为什么 Maemo Fremantle 模拟器运行 hello.c 的显示结果 ( 图 2) 不是设定的尺寸 200X200,而是延伸到整个屏幕宽度。在 Maemo Fremantle 模拟器顶部显示的状态条其实是主界面显示的,该部分是 Dock 类型的窗口,和 hello.c 无关。
CreateWindow 调用结束的时候给客户端发送 CreateNotify 事件。但是 GTK 没有处理该事件。
XMapWindow 对应的 X Server 实现是 MapWindow 函数。该代码较长,而且涉及到 X Client、X Server、窗口管理器的多次交互。
MapWindow 的工作原理是:
- 客户端调用 MapWindow 请求映射 Client 窗口。如果该窗口的 overrideRedirect 为假,表示该 MapWindow 调用为普通客户端发起。则发送 MapRequest 到窗口管理器。请求窗口管理器进一步处理。
- 窗口管理器收到 MapRequest,创建一个 Frame 窗口,并通过 XReparentWindow 调用,将客户端的窗口设置为 Frame 窗口的子窗口。Frame 窗口的 overrideRedirect 为真。
- 窗口管理器调用 XMapSubwindows,第二个参数为 Frame 窗口。由于 Frame 窗口的 overrideRedirect 为真,MapSubwindows 会对该 Frame 窗口的子窗口做映射。并发送 MapNotify 事件、Expose 事件给客户端。客户端在 Expose 事件中绘制客户端窗口内容。
- 窗口管理器调用 XMapWindow,第二个参数为 Frame 窗口。同样由于 Frame 窗口的 overrideRedirect 为真,这次 MapWindow 也不发射 MapRequest 事件了,从而映射了 Frame 窗口。
- X Server 在映射 Frame 窗口之后,发送 Expose 事件给窗口管理器,以通知窗口管理器绘制窗口的边框等。
至此,客户端窗口的内容,窗口的边框都被显示在屏幕上了。
下图 4 是 X Client、X Server、窗口管理器的交互序列图:
图 4. X Client、X Server、窗口管理器交互序列图
X Window 是一个功能非常强大的 C/S 图形显示系统,具有很好的跨网络性能,也易于进行扩展。了解其窗口显示原理对程序员进行 GTK/QT 编程是有帮助的。
- David Rosenthal 的文章 Inter-Client Communication Conventions Manual 对 X Window System 通信协议有详细的介绍。
- Matthew Allum 的论文 Matchbox: Window Management Not for the Desktop 从用户层面介绍了 Matchbox 窗口管理器。
- 参考 Wikipedia 对 X Window 的简介。
- 在 developerWorks Linux 专区 寻找为 Linux 开发人员(包括 Linux 新手入门)准备的更多参考资料,查阅我们 最受欢迎的文章和教程。
- 在 developerWorks 上查阅所有 Linux 技巧 和 Linux 教程。
- 欢迎加入 My developerWorks 中文社区。