X Window 系统的窗口显示原理

来源:互联网 发布:广发淘宝卡没有集分宝 编辑:程序博客网 时间:2024/05/22 15:40

http://www.ibm.com/developerworks/cn/linux/l-cn-xwin/index.html#major3

 

徐 星, 高级软件工程师, 上海某手机设计公司
徐星,武汉大学电子系硕士。毕业后一直从事手机 GUI 研发 , 对 Qt、GTK、X Window 系统有较深入的理解。在国内外期刊发表论文三篇,有发明专利两项。主要研究方向是 X Window 系统、OpenGL 等。

简介: 本文介绍 X Window 系统的窗口显示原理。从一个简单的 X 客户端程序入手,介绍了窗口的创建接口 XCreateWindow 和显示接口 XMapWindow 的实现,并结合窗口管理器介绍了 X Server、X Client 和窗口管理器三部分交互的原理。

平均分 4 星 共 21 个评分 平均分 (21个评分)
为本文评分

X Window 系统介绍

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 运行结果
图 1. Ubuntu 9.10 运行结果

图 2. Maemo Fremantle 模拟器运行结果
图 2. Maemo Fremantle 模拟器运行结果

在示例代码 hello.c 中,和窗口显示相关的接口主要有:XCreateWindow,XMapWindow,XDrawPoint。在示例代码之外,X Server 和窗口管理器同时在发挥着各自的关键作用。本文将结合 X Server、窗口管理器、示例 hello.c 代码来解释窗口显示的原理。


创建窗口:XCreateWindow

在 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 窗口层级视图
图 3. GTK 窗口层级视图

对单个应用而言,部分窗口管理器如 Matchbox Window Manager 只支持一个应用级顶层窗口,譬如 Maemo Fremantle 使用的就是该类型的窗口管理器。这就是为什么 Maemo Fremantle 模拟器运行 hello.c 的显示结果 ( 图 2) 不是设定的尺寸 200X200,而是延伸到整个屏幕宽度。在 Maemo Fremantle 模拟器顶部显示的状态条其实是主界面显示的,该部分是 Dock 类型的窗口,和 hello.c 无关。

CreateWindow 调用结束的时候给客户端发送 CreateNotify 事件。但是 GTK 没有处理该事件。


映射窗口:XMapWindow

XMapWindow 对应的 X Server 实现是 MapWindow 函数。该代码较长,而且涉及到 X Client、X Server、窗口管理器的多次交互。

MapWindow 的工作原理是:

  1. 客户端调用 MapWindow 请求映射 Client 窗口。如果该窗口的 overrideRedirect 为假,表示该 MapWindow 调用为普通客户端发起。则发送 MapRequest 到窗口管理器。请求窗口管理器进一步处理。
  2. 窗口管理器收到 MapRequest,创建一个 Frame 窗口,并通过 XReparentWindow 调用,将客户端的窗口设置为 Frame 窗口的子窗口。Frame 窗口的 overrideRedirect 为真。
  3. 窗口管理器调用 XMapSubwindows,第二个参数为 Frame 窗口。由于 Frame 窗口的 overrideRedirect 为真,MapSubwindows 会对该 Frame 窗口的子窗口做映射。并发送 MapNotify 事件、Expose 事件给客户端。客户端在 Expose 事件中绘制客户端窗口内容。
  4. 窗口管理器调用 XMapWindow,第二个参数为 Frame 窗口。同样由于 Frame 窗口的 overrideRedirect 为真,这次 MapWindow 也不发射 MapRequest 事件了,从而映射了 Frame 窗口。
  5. X Server 在映射 Frame 窗口之后,发送 Expose 事件给窗口管理器,以通知窗口管理器绘制窗口的边框等。

至此,客户端窗口的内容,窗口的边框都被显示在屏幕上了。

下图 4 是 X Client、X Server、窗口管理器的交互序列图:


图 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 中文社区。

关于作者

徐星,武汉大学电子系硕士。毕业后一直从事手机 GUI 研发 , 对 Qt、GTK、X Window 系统有较深入的理解。在国内外期刊发表论文三篇,有发明专利两项。主要研究方向是 X Window 系统、OpenGL 等。