refer:
wiki http://zh.wikipedia.org/zh-cn/X_Window
http://en.wikipedia.org/wiki/X_Window_System#User_interface_issues
http://en.wikipedia.org/wiki/X_Window_System_protocols_and_architecture
http://fanqiang.chinaunix.net/program/other/2001-06-08/2097.shtml
X Window 系统的窗口显示原理
本文介绍 X Window 系统的窗口显示原理。从一个简单的 X 客户端程序入手,介绍了窗口的创建接口 XCreateWindow 和显示接口 XMapWindow 的实现,并结合窗口管理器介绍了 X Server、X Client 和窗口管理器三部分交互的原理。
0评论:
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 运行结果
图 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 窗口层级视图
对单个应用而言,部分窗口管理器如 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 的工作原理是:
- 客户端调用 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 中文社区。
W(indow) 后面,因此,人们就戏称这一版的窗口接口为 X ,有下一版新窗口之意
X系统由3个相关的部分组合起来的。
服务端(Server)
Server是控制显示器和输入设备(键盘和鼠标)等硬件的软件。
它负责检测具体那个设备有什么动作,如键盘按下、鼠标滑动,但它不能将这些动作转化为绘图数据。
它还负责 设备发出动作以后,到底该如何显示。
客户端(Client)
Client的主要工作是接受服务器传来的动作,将其转化为绘图数据,再将这写数据送去服务端。
Server和Client之间的通信
Server和Client通信的方式大致有两类,对应于X系统的两种基本操作模式。
第一类,Server和Client在同一台机器上执行,它们可以共同使用机器上任何可用的通信方式做互动式信息处理。在这种模式下,X可以同其他传统的视窗系统一样高效工作。
第二类,Client在一台机器上运行,而显示器和Server则在另一台机器上运行。因此两者的信息交换就必须通过彼此都遵守的网络协议进行,最常用的协议为TCP/IP。这种通信方式一般被称为网络透明性,这也几乎是X独一无二的特性。
用windows 远程访问 linux时
windows作为通常的c端,需要装x系统中的 x server。
我们在 Windows 中将鼠标向右移动时,首先,X server 会侦测到鼠标移动,但是他不知道应该怎么绘图!他将鼠标这个动作告知 linux 下的X Client, X Client 就会去运算,得到其实要将鼠标向右移动几个位素的,然后将这个结果告知 X server , 接下来,您就会看到 X Server 将鼠标向右移动了。
X Window Manager:特殊的 X Client ,负责管理所有的 X client 软件
刚刚前面提到,X Client 的主要工作是将来自 X Server 的数据处理成为绘图数据,再回传给 X server
而已, 所以 X client 本身是不知道他在 X Server 当中的位置、大小以及其他相关信息的。这也是上面
我们谈到的, X client 彼此不知道对方在屏幕的哪个位置啊!为了克服这个问题,因此就有 Window
Manager (WM, 窗口管理器) 的产生了。 窗口管理器也是 X client ,只是他主要在负责全部 X client
的控管,还包括提供某些特殊的功能,例如:
提供许多的控制元素,包括任务栏、背景桌面的设定等等;
管理虚拟桌面 (virtual desktop);
提供窗口控制参数,这包括窗口的大小、窗口的重迭显示、窗口的移动、窗口的最小化等等。
我们常常吩到的 KDE, GNOME, XFCE ,都是一些窗口管理器的项目。