该教程是在X window环境下,关于图形编程“would-be”系列的第一个。可能这个教程没有什么用处。因为编程人员通常使用一个更高等级的抽象库(Xlib库的进一步封装),例如Motif、GTK、QT等等。然而,我们需要去学习,更多的了解Xlib库是如何工作并不是一件坏事。
读完本教程之后,你能够写一个非常简单的图形程序,但是那不会是一个良好用户界面的程序。对于这样的程序,应该使用之前提到过的Motif、Gtk、Qt库。
这个X窗口系统开发的目标是以灵活性为主。开发目的是这样的,但是实际工作起来又是另一回事。这个低等级库被要求去画一个窗口、处理用户输入、允许使用不同颜色去画一个图形等等。根据这些要求,我们把系分成两部分,一个决定去做什么事情的客户端和一个在屏幕画图的服务器,这个服务器读取用户输入命令,服务器发送命令到客户端。
这个模型完全是相反于我们使用客户端和服务器处理事情。在我们这个模型中,靠近机器的用户通过服务器控制,在这个过程中,客户端可能是运行在远端的机器。这个服务器控制着屏幕、鼠标、键盘。一个客户端可能连接到服务器,要求它去画一个窗口(一系列窗口),然后要求服务器发送他任何的用户输入到这些窗口。几个客户端可能连接到一个服务器,一个可能运行邮箱软件,一个可能使用浏览器等等。当输入时,它由用户发送到某个窗口,这个服务器会发送一些消息给控制这些窗口的客户端,客户端决定对输入做什么,并发送到服务器绘出图形。
整个会话过程是通过X协议完成的。该协议最初是由TCP/IP协议包执行的,运行客户运行在任何和服务器连接在相同网络的机器上。后来,X服务器被扩展为允许用户运行在本地机器上更优的访问服务器(注意到X协议可能有几百KB那么大),比如使用共享内存,或者使用Unix域socket(一个在Unix系统上的两个进程间创建逻辑通道的方法)。
总结一下就是,我们认为程序和GUI显示是一起的,其实按X11来说,程序和GUI是两部分。程序就是客户端X client,GUI显示是X server,键盘、鼠标等这些操作来自用户。用户发出指令到X server,X server把接受到了信息发送的客户端,客户端根据命令要求,处理之后,再发送到X Server,最终X server绘制出相关图形。
不同于常规的计算机程序,GUI程序通常使用异步化的编程模型,也被称为事件驱动编程。这个意味着程序大部分时间是闲着的,等待由X服务器发送的事件,然后根据这些事件作出反应。
An event may say “The user pressed the 1st button mouse in spot x,y”, or “the window you control needs to be redrawn”. In order for the program to be responsive to the user input, as well as to refresh requests, it needs to handle each event in a rather short period of time (e.g. less than 200 milliseconds, as a rule of thumb).
This also implies that the program may not perform operations that might take a long time while handling an event (such as opening a network connection to some remote server, or connecting to a database server, or even performing a long file copy operation). Instead, it needs to perform all these operations in an asynchronous manner. This may be done by using various asynchronous models to perform the longish operations, or by performing them in a different process or thread.
So the way a GUI program looks is something like that:
In order to eliminate the needs of programs to actually implement the X protocol layer, a library called ‘Xlib’ was created. This library gives a program a very low-level access to any X server. Since the protocol is standardized, A client using any implementation of Xlib may talk with any X server. This might look trivial these days, but back at the days of using character mode terminals and proprietary methods of drawing graphics on screens, this looked like a major break-through. In fact, you’ll notice the big hype going around thin-clients, windows terminal servers, etc. They are implementing today what the X protocol enabled in the late 80’s. On the other hand, the X universe is playing a catch-up game regarding CUA (common user access, a notion made by IBM to refer to the usage of a common look and feel for all programs in order to ease the lives of the users). Not having a common look and feel was a philosophy of the creators of the X window system. Obviously, it had some drawbacks that are evident today.
使用Xlib的最大概念是X display。这个结构体代表了我们和一个已经打开的X服务器的连接。它隐藏一个来自server队列消息,客户将要发送给server请求队列。在Xlib中,这个结构提的名字是Display
。当我们打开一个X Server连接,这个库就会返回一个Display结构体指针。之后,我们会应用这个指针发送消息到X server或者接收来自server的消息。
当我们要画多种图案(graphics, text, etc),我们可能要指定多个选项用来控制如何去画图- 前景色和背景色是什么,线的边缘如何连接,在绘出文本的时候使用何种字体等。为了避免给每个绘出函数提供n多参数,一个类型为‘GC’的图形上下文结构被启用。我们在这个结构中设置各种绘出选项,并且把指向这个结构的指针传递给每个绘出函数。这个是相当方便的,因为我们通常需要用相同选项执行好几个绘出请求。因而,我们初始化
图形上下文,设置所需的选项,并把这个GC结构传递给所有的绘出函数。
When various objects are created for us by the X server - such as windows, drawing areas and cursors - the relevant function returns a handle. This is some identifier for the object that actually resides in the X server’s memory - not in our application’s memory. We can later manipulate this object by supplying this handle to various Xlib functions. The server keeps a mapping between these handles and the actual objects it manages. Xlib provides various type definitions for these objects (Window, Cursor, Colormap and so on), which are all eventually mapped to simple integers. We should still use these type names when defining variables that hold handles - for portability reasons.
Various structure types are used in Xlib’s interface. Some of them are allocated directly by the user. Others are allocated using specific Xlib functions. This allows the library to initialize properly these structures. This is very handy, since these structures tend to contain a lot of variables, making it rather tedious for the poor programmer to initialize. Remember - Xlib tries to be as flexible as possible, and this means it is also as complex as it can get. Having default values will enable a beginner X programmer to use the library, without interfering with the ability of a more experienced programmer to tweak with these zillions of options.
As for freeing memory, this is done in one of two ways. In cases where we allocated the memory - we free it in the same manner (i.e. use free() to free memory allocated using malloc()). In case we used some Xlib function to allocate it, or we used some Xlib query method that returns dynamically allocated memory - we will use the XFree() function to free this memory block.
A structure of type ‘XEvent’ is used to pass events received from the X server. Xlib supports a large amount of event types. The XEvent structure contains the type of event received, as well as the data associated with the event (e.g. position on the screen where the event was generated, mouse button associated with the event, region of screen associated with a ‘redraw’ event, etc). The way to read the event’s data depends on the event type. Thus, an XEvent structure contains a C language union of all possible event types (if you’re not sure what C unions are, it is time to check your proffered C language manual…). Thus, we could have an XExpose event, an XButton event, an XMotion event, etc.
参考1:Tutorial-Basic Graphics Programming With The Xlib Library
参考2:Xlib示列程序
参考3:Tutorial中文翻译