天天看点

用Xlib库进行基本图形编程

目录 

1、前言 

2、X Window系统的客户服务器模式 

3、GUI编程-同步化的编程模型 

4、基本的Xlib概念 

         1、X Display 

         2、GC-图形上下文 

         3、对象句柄 

         4、Xlib 结构体的内存分配 

         5、事件 

5、编译给予Xlib的程序 

6、打开和关闭一个连接到X服务器的连接 

7、检查关于Display的基本信息 

8、创建一个简单的窗口-我们的“hello world”程序 

9、在窗口中绘画 

         1、分配图形上下文(GC) 

         2、基本绘图-点,线,框,圆... 

10、X事件 

         1、使用事件遮罩给事件型别注册 

         2、接收事件-撰写事件循环 

         3、暴露事件 

         4、获得用户输入 

                 1、鼠标按钮点击和释放事件 

                 2、鼠标移动事件 

                 3、鼠标指针进入和离开事件 

                 4、键盘焦点 

                 5、键盘按下和释放事件 

         5、X事件-完整的例子 

11、处理文本和字体 

         1、字体结构 

         2、载入字体 

         3、把字体赋给图形上下文 

         4、在窗口中绘出文本 

12、窗口阶层 

         1、根,父和子窗口 

         2、事件传播 

13、和窗口管理器交互 

         1、窗口属性 

         2、设置窗口名和图标名 

         3、设置最佳窗口尺寸 

         4、设置窗口管理器的杂项 

         5、设置应用程序的图标 

14、简单窗口操作 

         1、映射和取消映射窗口 

         2、在屏幕上移动窗口 

         3、缩放窗口 

         4、改变窗口的堆叠次序-提高会放低 

         5、标识会取消标识窗口 

         6、获得窗口的信息 

15、使用颜色来绘出彩虹 

         1、色表 

         2、分配和释放色表 

         3、分配和释放单个颜色 

         4、用颜色绘画 

16、X Bitmap和Pixmap 

         1、什么是X Bitmap?什么又是X Pixmap? 

         2、从文件中载入Bitmap 

         3、在窗口中画出Bitmap 

         4、创建Pixmap 

         5、在窗口中画出Pixmap 

         6、释放Pixmap 

17、改变鼠标光标 

         1、创建和释放鼠标光标 

         2、设置窗口的鼠标光标 

Preface 

前言 

This tutorial is the first in a series of "would-be" tutorials about graphical programming in the X window environment. By itself, it is useless. A real X programmer usually uses a much higher level of abstraction, such as using Motif (or its free version, lesstiff), GTK, QT and similar libraries. However, we need to start somewhere. More than this, knowing how things work down below is never a bad idea. 

该教程是“可能会有”的关于在X Window环境下进行图形化编程的教程的第一个系列。其自身是用处不大的。一个真正的X程序员一般使用一个更高层次的抽象,比如用Motif(或者它的的免费版本,lesstiff),GTK,QT和类似的库。然而,我们需要从一个地方开始入手。不仅如此,知道表象之下的事情是如何工作的决不会是坏的主意。 

After reading this tutorial, one would be able to write very simple graphical programs, but not programs with a descent user interface. For such programs, one of the previously mentioned libraries would be used.  

在读完本教程后,你可能能够些简单的图形程序,但是那不会是一个有良好用户界面的程序。对于这样的程序,也许就要用到上述的库中的某一个。 

The Client And Server Model Of The X Window System 

X Window系统的客户和服务器模型 

The X window system was developed with one major goal - flexibility. The idea was that the way things look is one thing, but the way things work is another matter. Thus, the lower levels provide the tools required to draw windows, handle user input, allow drawing graphics using colors (or black and white screens), etc. To this point, a decision was made to separate the system into two parts. A client that decides what to do, and a server that actually draws on the screen and reads user input in order to send it to the client for processing. 

X window系统开发之初有一个最大的目标-灵活性。想法是这样的东西看上如如何一回事,东西如何工作的又是另外一回事。因而,底层提供在画窗口,处理用户输入,允许使用颜色画图形(或者黑白屏幕)等动作中需要的工作。就这点决定了把系统分为两个部分。客户决定作什么,而服务器实际在屏幕上画图并读出用户输入以发给客户进行处理。 

This model is the complete opposite of what one is used to when dealing with clients and servers. In our case, the user seats near the machine controlled by the server, while the client might be running on a remote machine. The server controls the screen, mouse and keyboard. A client may connect to the server, request that it draws a window (or several windows), and ask the server to send it any input the user sends to these windows. Thus, several clients may connect to a single X server - one might be running an email software, one running a WWW browser, etc. When input it sent by the user to some window, the server sends a message to the client controlling this window for processing. The client decides what to do with this input, and sends the server requests for drawing in the window. 

这个模型正好和人们在客户和服务器中所习惯的行为相反。在我们的例子,用户坐在由服务器控制的机器旁边,而客户可能运行于一个远程的机器上。服务器控制屏幕,鼠标和键盘。客户可能连接到了服务器,发出画一个(或者多个)窗口的请求,并要求服务器把任何用户发送给这些窗口的输入给他。因而,几个客户可能连接到了同一个X服务器-一个可能在运行email软件,一个可能在运行WWW浏览器,等等。当由用户发送输入给某些窗口时,服务器向控制这些窗口的客户发送消息以供处理。客户决定对输入作什么,并给服务器发送请求来在窗口中绘图。 

The whole session is carried out using the X message protocol. This protocol was originally carried over the TCP/IP protocol suite, allowing the client to run on any machine connected to the same network that the server is. Later on the X servers were extended to allow clients running on the local machine more optimized access to the server (note that an X protocol message may be several hundreds of KB in size), such as using shared memory, or using Unix domain sockets (a method for creating a logical channel on a Unix system between two processes).  

整个会话过程是用X消息协议执行的。该协议最初时由TCP/IP协议包执行的,允许客户运行于任何和服务器连接在相同网络上的机器上。后来,X服务器被扩展为允许客户运行在本地机器上更优的访问服务器(注意到X协议消息可能有几百KB那么大),比如使用共享内存,或者使用Unix域sockets(一个在Unix系统上的两个进程间创建逻辑通道的方法)。 

GUI programming - the Asynchronous Programming Model 

GUI编程-同步化的编程模型 

Unlike conventional computer programs, that carry some serial nature, a GUI program usually uses an asynchronous programming model, also known as "event-driven programming". This means that that program mostly sits idle, waiting for events sent by the X server, and then acts upon these events. 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). 

不同于包含某种顺序化执行内质的传统的计算机程序。GUI程序通常使用同步化的编程模型,也被称为“事件驱动编程”。这个意味着程序大部分时候时闲着的,等待由X服务器发送的事件,然后根据这些事件作出反应。事件可能时”用户在点x,y处按下第一个按钮“,或者时”你控制的串口需要重画“。为了程序能够响应用户输入以及刷新请求,它需要在一个相当短的时间内处理每个事件(比如作为一个大体的规则,小于200毫秒)。 

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: 

因而GUI程序看上去大概是这样: 

    1. Perform initialization routines. 

    2. Connect to the X server. 

    3. Perform X-related initialization. 

    4. While not finished: 

          1. Receive the next event from the X server. 

          2. handle the event, possibly sending various drawing requests to the X server. 

          3. If the event was a quit message, exit the loop.  

    5. Close down the connection to the X server. 

    6. Perform cleanup operations.  

    1、执行初始化 

    2、连接到X服务器 

    3、执行X相关的初始化工作 

    4、在没有结束之前: 

         1、接收来自于X服务器的下一个事件 

         2、处理事件,也许向X服务器发送多种绘画请求 

         3、如果事件是退出消息,跳出循环 

    5、关闭连接到X服务器的连接 

    6、执行清理操作 

Basic Xlib Notions 

基本的Xlib概念 

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.  

为了消除程序事件实现X协议层的需求,一个称为‘Xlib’的库被创造出来。该库给程序提供了一个对任何X服务器非常底层的访问。因为协议是标准化的,客户使用Xlib的任何一种实现都可以和和X服务器通话。这些可能在今天看来没什么大用,但回到使用字符模式终端和专有方法在屏幕上绘图的日子,这是一个很大的突破。事实上,你将注意到咱爱瘦客户,窗口终端服务器,等等周围进行的各种虚伪的骗局。他们在今天实现X协议在80年代晚期已经能够作的事情。另外一方面,X universe在玩一个关于CUA(共通用户感观,一个由IBM制造的概念,指的是对所有程序使用共通的观感以使得用户能够更加轻松)的catch-up游戏。没有共通的感观是X window系统创造者的哲学。明显,它有许多在今天看来显然的缺陷。 

The X Display 

The major notion of using Xlib is the X display. This is a structure representing the connection we have open with a given X server. It hides a queue of messages coming from the server, and a queue of pending requests that our client intends to send to the server. In Xlib, this structure is named 'Display'. When we open a connection to an X server, the library returns a pointer to such a structure. Later, we supply this pointer to any Xlib function that should send messages to the X server or receive messages from this server.  

X Display 

使用Xlib的最大的概念是X display。这是一个代表我们和一个给定X服务器打开的连接的结构体。它隐藏了服务器的消息队列,客户将要发送给服务器的请求队列。在Xlib中,这个结构体被命名为‘Display’。当我们打开一个连接到X服务器的连接的时候,库返回一个指向这种结构体的指针。然后,我们把这个指针提供给任何需要发送消息给X服务器或者从这个服务器接收消息的Xlib函数。 

The GC - Graphics Context 

When we perform various drawing operations (graphics, text, etc), we may specify various options for controlling how the data will be drawn - what foreground and background colors to use, how line edges will be connected, what font to use when drawing some text, etc). In order to avoid the need to supply zillions of parameters to each drawing function, a graphical context structure, of type 'GC' is used. We set the various drawing options in this structure, and then pass a pointer to this structure to any drawing routines. This is rather handy, as we often needs to perform several drawing requests with the same options. Thus, we would initialize a graphical context, set the desired options, and pass this GC structure to all drawing functions.  

GC -图形上下文 

当我们执行各种绘出(图形,文本,等)操作的时候,我们可能要指定几个选项以控制数据怎么被绘出 - 前景色和背景色是什么,线的边缘如何连接,在绘出文本的时候使用何种字体,等。为了避免给每个绘出函数提供n多参数,一个类型为‘GC’的图形上下文结构被启用。我们在这个结构中设置各种绘出选项,并且把指向这个结构的指针传递给每个绘出函数。这个是相当方便的,因为我们通常需要用相同选项执行好几个绘出请求。因而,我们初始化图形上下文,设置所需的选项,并把这个GC结构传递给所有的绘出函数。 

Object Handles 

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.  

对象句柄 

当各种对象被创造出来给X服务器使用 - 例如窗口,绘画区域和光标 - 相关的函数返回一个句柄。这是实际存在于X服务器的内存中的对象的标识符。我们能够在后面通过把这些句柄提供给各种Xlib函数来操纵这些对象。服务器保存了这些句柄和它们管理的对象之间的映射。Xlib提供各种型别定义给这些对象(窗口,光标,色表等等),它们实际上最终映射为简单的整数。我们在定义保存这些句柄的变量的时候仍然应当使用这些型别名-为了有更好的可移植性。 

Memory Allocation For Xlib Structures 

为Xlib结构体分配内存 

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. 

在Xlib的接口中使用了各种结构型别。他们中的一些直接由用户分配内存。其他的使用Xlib函数分配。这使得库能够恰当的初始化这些结构。这非常方便,因为这些结构倾向于包含很多变量,使得对于差劲点的程序员非常难于初始化它们。记住-Xlib尝试着尽可能的了灵活,而且这意味着他也是尽可能的复杂。由缺省值使得初学X的程序员能够使用这个库,而不打扰有经验的程序员在n多选项中作调整的可能。 

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. 

对于释放内存,由两种方法完成。在我们分配内存的情况-我们用相同方法释放它们(也就是使用free()来释放由malloc()分配的内存)。在我们用某Xlib函数分配的时候,或者我们使用返回动态分配的内存的Xlib查询方法的时候-我们使用XFree()函数来释放这些内存块。 

Events 

事件 

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.  

型别‘XEvent’的结构被用来传递从X服务器接收来的事件。Xlib支持很大数量的事件型别。XEvent结构包含接收事件的类型,以及与该事件相关的数据(例如事件产生的屏幕位置,与事件相关的鼠标按钮,和‘redraw’事件相关的屏幕区域,等)。读取事件的数据的方法和事件类型有关。因而,XEvent结构包含一个C语言对于所有可能事件型别的联合(如果你不确知C的联合是什么,该是查查你的C语言手册的时候...)。因而,我们能够有一个XExpose事件,XButton事件,XMotion事件,等。 

Compiling Xlib-Based Programs 

编译基于Xlib的程序 

Compiling Xlib-Based programs requires linking them with the Xlib library. This is done using a compilation command like this: 

编译基于Xlib的需要把他们和Xlib库进行链接。这是通过使用如下的编译命令行来完成的: 

cc prog.c -o prog -lX11 

If the compiler complains that it cannot find the X11 library, try adding a '-L' flag, like this: 

如果编译器抱怨它找不到X11库,尝试加上‘-L’标志,像这样: 

cc prog.c -o prog -L/usr/X11/lib -lX11 

or perhaps this (for a system with release 6 of X11): 

或者也许是这样(对于用X11的release 6的系统): 

cc prog.c -o prog -L/usr/X11R6/lib -lX11 

On SunOs 4 systems, the X libraries are placed in /usr/openwin/lib: 

在SunOs 4系统上,X库被放置于/usr/openwin/lib: 

cc prog.c -o prog -L/usr/openwin/lib -lX11 

and so on... 

等等... 

Opening And Closing The Connection To An X Server 

打开和关闭连接到X服务器的连接 

An X program first needs to open the connection to the X server. When we do that, we need to specify the address of the host running the X server, as well as the display number. The X window system can support several displays all connected to the same machine. However, usually there is only one such display, which is display number '0'. If we wanted to connect to the local display (i.e. the display of the machine on which our client program runs), we could specify the display as ":0". To connect to the first display of a machine whose address is "simey", we could use the address "simey:0". Here is how the connection is opened: 

X程序首先需要打开连接到X服务器的连接。在我们完成这件工作的时候,我们需要指定运行X服务器的机器的地址,以及display号码。X window系统能够支持全部连接于同一个机器的好几个display。然而,通常只有一个这样的display,它的display号是‘0’。如果我们想要连接到本地display(也就是我们客户程序所运行的机器的display),我们可以指定display为’:0‘。要连接到地址为”simey“的机器的第一个display,我们能够使用地址”simey:0“。这儿是连接是如何被打开的: 

#include <X11/Xlib.h>    

Display* display; 

display = XOpenDisplay("simey:0"); 

if (display == NULL) { 

     fprintf(stderr, "Cannot connect to X server %s\n", "simey:0"); 

     exit (-1); 

Note that is common for X programs to check if the environment variable 'DISPLAY' is defined, and if it is, use its contents as the parameter to the XOpenDisplay() function. 

注意,对于X程序来说检查系统变量‘DISPLAY’是否被定义了是很常见的,而且如果是的话,使用它的内容作为XOpenDisplay()函数的参数。 

When the program finished its business and needs to close the connection the X server, it does something like this: 

当程序完成了它的使命并且需要关闭连接到X服务器的连接的时候,它如下动作: 

XCloseDisplay(display); 

This would cause all windows created by the program (if any are left) to be automatically closed by the server, and any resources stored on the server on behalf of the clients - to be freed. Note that this does not cause our client program to terminate - we could use the normal exit() function to do that. 

这将导致所有由程序创造的窗口(如果还有剩下的话)自动被服务器关闭,而且为了客户的利益任何留在服务器上的资源-将被释放。注意这将不会导致我们的客户程序终止-我们使用普通的exit()函数来完成。 

Checking Basic Information About A Display 

检查关于Display的基本信息 

Once we opened a connection to an X server, we should check some basic information about it: what screens it has, what is the size (width and height) of the screen, how many colors it supports (black and white? grey scale? 256 colors? more?), and so on. We will show a code snippet that makes few of these checks, with comments explaining each function as it is being used. We assume that 'display' is a pointer to a 'Display' structure, as returned by a previous call to XOpenDisplay(). 

一旦我们打开了一个连接到X服务器的连接,我们应当检查有关它的一些基本信息:他有什么样的屏幕,尺寸是多少(宽和高),它支持多少颜色(黑白?灰度?256色?更多?),以及等等。我们将展示一些作一些这样检查的代码片段,以及在使用中解释每个函数的注释。我们假定‘display’是一个指向‘Display’的结构的指针,由前面对XOpenDisplay()的调用返回的。 

int screen_num; 

int screen_width; 

int screen_height; 

Window root_window; 

unsigned long white_pixel; 

unsigned long black_pixel; 

screen_num = DefaultScreen(display); 

screen_width = DisplayWidth(display, screen_num); 

screen_height = DisplayHeight(display, screen_num); 

root_window = RootWindow(display, screen_num); 

white_pixel = WhitePixel(display, screen_num); 

black_pixel = BlackPixel(display, screen_num); 

There are various other macros to get more information about the screen, that you can find in any Xlib reference. There are also function equivalents for some of these macros (e.g. XWhitePixel, which does the same as WhitePixel). 

有各种其他的宏来得到关于屏幕的更多信息,你可以从任何Xlib参考书中得到它们。还有和这些宏完成相同功能的函数(例如XWhitePixel,它和WhitePixel干一样的事情)。 

Creating A Basic Window - Our "hello world" Program 

创建一个简单窗口 - 我们的“hello world”程序 

After we got some basic information about our screen, we can get to creating our first window. Xlib supplies several functions for creating new windows, one of which is XCreateSimpleWindow(). This function gets quite a few parameters determining the window's size, its position, and so on. Here is a complete list of these parameters: 

在我们获得了一些有关我们的屏幕的基本信息之后,我们可以开始创建我们第一个窗口。Xlib提供数个函数来创建新窗口,其中的一个是XCreateSimpleWindow()。这个函数或者少量几个决定窗口的大小和位置等的参数。这有一个这些参数的完整列表: 

Display* display 

     Pointer to the Display structure. 

Window parent 

     The ID of an existing window that should be the parent of the new window. 

int x 

     X Position of the top-left corner of the window (given as number of pixels from the left of the screen). 

int y 

     Y Position of the top-left corner of the window (given as number of pixels from the top of the screen). 

unsigned int width 

     Width of the new window, in pixels. 

unsigned int height 

     Height of the new window, in pixels. 

unsigned int border_width 

     Width of the window's border, in pixels. 

unsigned long border 

     Color to be used to paint the window's border. 

unsigned long background 

     Color to be used to paint the window's background. 

Lets create a simple window, whose width is 1/3 of the screen's width, height is 1/3 of the screen's height, background color is white, border color is black, and border width is 2 pixels. The window will be placed at the top-left corner of the screen. 

让我们创建一个简单窗口,它的宽是屏幕宽的1/3,高是屏幕高的1/3,背景色是白的,边框颜色是黑色的,而且边框宽2象素。窗口将会放置在屏幕的左上角。 

Window win; 

int win_width; 

int win_height; 

int win_x; 

int win_y; 

win_width = DisplayWidth(display, screen_num) / 3; 

win_height = DisplayHeight(display, screen_num) / 3; 

win_x = win_y = 0; 

win = XCreateSimpleWindow(display, 

                           RootWindow(display, screen_num), 

                           win_x, win_y, 

                           win_width, win_height, 

                           win_border_width, BlackPixel(display, screen_num), 

                           WhitePixel(display, screen_num)); 

The fact that we created the window does not mean it will be drawn on screen. By default, newly created windows are not mapped on the screen - they are invisible. In order to make our window visible, we use the XMapWindow() function, as follows: 

我们创造了窗口的事实并不意味着它会被画在屏幕上。缺省的,新创建的窗口不会被映射于屏幕之上 - 它们是不可见的。为了使得我们的窗口可见,我们使用XMapWindow()函数,如下: 

XMapWindow(win); 

To see all the code we have gathered so far, take a look at the simple-window.c program. You'll see two more function not explained so far - XFlush() and XSync(). The XFlush() function flushes all pending requests to the X server - much like the fflush() function is used to flash standard output. The XSync() function also flushes all pending requests to the X server, and then waits until the X server finishes processing these requests. In a normal program this will not be necessary (you'll see why when we get to write a normal X program), but for now we put it there. Try compiling the program either with or without these function calls to see the difference in its behavior. 

要看我们至今积累写出的所有代码,看看simple-window.c程序。你将看到至今没有解释的两个另外的函数 - XFlush()和XSync()函数用来清除仍未发送给X服务器的请求 - 很想用来清除标准输出的fflush()函数。XSync()函数也清除所有仍未发送给X服务器的消息,而且等待X服务器结束处理所有这些请求。在一个通常的程序中,这将不会是必要的(你可以看到为什么在我们开始写一个普通的X程序的时候),但对于现在我们把它放在那儿。尝试着有和去掉这些函数调用来编译程序,以观察它们行为上的不同点。 

Drawing In A Window 

在窗口中绘图 

Drawing in a window can be done using various graphical functions - drawing pixels, lines, circles, rectangles, etc. In order to draw in a window, we first need to define various general drawing parameters - what line width to use, which color to draw with, etc. This is done using a graphical context (GC).  

在窗口中绘图能够通过使用各种图形函数来完成 - 画点,线,圆,矩形,等。为了能够在窗口中绘图,我们首先需要定义几种通用的绘图参数 - 线宽使用多少的,绘图的颜色是什么,等。这个是用图形上下文(GC)来完成的。 

Allocating A Graphics Context (GC) 

分配图形上下文(GC) 

As we said, a graphical context defines several attributes to be used with the various drawing functions. For this, we define a graphical context. We can use more than one graphical context with a single window, in order to draw in multiple styles (different colors, different line widths, etc.). Allocating a new GC is done using the XCreateGC() function, as follows (in this code fragment, we assume "display" is a pointer to a Display structure, and "win" is the ID of a previously created window): 

如我所说,图形上下文给出几个用于绘图函数的属性。因此,我们定义一个图形上下文。我们能够在一个窗口中使用多余一个的图形上下文,以达到用多种风格(不同的颜色,线宽,等)绘图。分配一个新的GC是通过使用XCreateGC()函数来完成的,如下(在这个代码片段中,我们假定“display”是一个只想Display结构的指针,而起“win”是先前创建的窗口的ID): 

GC gc; 

XGCValues values = CapButt | JoinBevel; 

unsigned long valuemask = GCCapStyle | GCJoinStyle; 

gc = XCreateGC(display, win, valuemask, &values); 

if (gc < 0) { 

     fprintf(stderr, "XCreateGC: \n"); 

Note should be taken regarding the roles of "valuemask" and "values". Since a graphics context has zillions of attributes, and since often we don't want to define few of them, we need to be able to tell the XCreateGC() which attributes we want to set. This is what the "valuemask" variable is for. We then use the "values" variable to specify actual values for the attributes we defined in the "valuesmask". Thus, for each constant used in "values", we'll use the matching constant in "valuesmask". In this case, we defined a graphics context with two attributes: 

注意“valuesmask”和“values”的角色。因为图形上下文有n多属性,并且我们不想定义它们中的一些,我们需要能够告诉XCreateGC()哪些属性是我们想要设置的。这就是“valuesmask”变量的用处。我们然后使用“values”变量来指定我们在“valuesmask”中定义的属性的值。因而,对于每个在“values”中使用的常量,我们将使用在“valuesmask”中相应的常量。在此例中,我们用两个属性定义图形上下文: 

    1. When drawing a multiple-part line, the lines should be joined in a 'Bevelian' style. 

    2. A line's end-point will be drawn straight (as opposed to ending the line in a round shape, if its width is more than 1 pixel wide).  

    1、当在画多部分的线的时候,线应该以‘Bevelian’风格连接起来。 

    2、线的终点将被直的画出来(与以圆角结束线相对,如果它的宽度大于一个象素)。 

The rest of the attributes of this GC will be set to their default values. 

这个GC的剩余属性将由它们的缺省值设定。 

Once we created a graphics context, we can use it in drawing functions. We can also modify its parameters using various functions. Here are a few examples: 

一旦我们创建了一个图形上下文,我们能够在绘图函数中使用它。我们还能够各种函数修改它的参数。这儿有几个例子: 

XSetForeground(display, gc, WhitePixel(display, screen_num)); 

XSetBackground(display, gc, BlackPixel(display, screen_num)); 

XSetFillStyle(display, gc, FillSolid); 

XSetLineAttributes(display, gc, 2, LineSolid, CapRound, JoinRound); 

for complete information on the various attributes available in a graphics context, refer to the manual page of XCreateGC(). We will use just a few simple attributes in our tutorial, to avoid over-complicating it. 

要获关于在图形上下文中有的各种属性的完整信息,参考XCreateGC()的手册页。我们将在我们的教程中仅仅使用几个简单的属性,以避搞得过度复杂了。 

Drawing Primitives - Point, Line, Box, Circle... 

基本绘图-点,线,框,圆... 

After we have created a GC, we can draw on a window using this GC, with a set of Xlib functions, collectively called "drawing primitives". Without much fuss, lets see how they are used. We assume that "gc" is a previously initialized GC, and that 'win' contains the handle of a previously created window. 

在我们创建了GC之后,我们能够使用这个GC在窗口上用一套Xlib函数绘画了,这些函数合成为“基本绘图函数”。废话不多说了,让我们看看它们是如何使用的吧。我们假定”gc“是先前初始化了的GC,而且‘win’包含了先前创建的窗口的句柄。 

XDrawPoint(display, win, gc, 5, 5); 

XDrawLine(display, win, gc, 20, 20, 40, 100); 

int x = 30, y = 40; 

int h = 15, w = 45; 

int angle1 = 0, angle2 = 2.109; 

XDrawArc(display, win, gc, x-(w/2), y-(h/2), w, h, angle1, angle2); 

XDrawArc(display, win, gc, 50-(15/2), 100-(15/2), 15, 15, 0, 360*64); 

   { 

     XPoint points[] = { 

       {0, 0}, 

       {15, 15}, 

       {0, 15}, 

       {0, 0} 

     }; 

     int npoints = sizeof(points)/sizeof(XPoint); 

     XDrawLines(display, win, gc, points, npoints, CoordModeOrigin); 

   } 

XDrawRectangle(display, win, gc, 120, 150, 50, 60); 

XFillRectangle(display, win, gc, 60, 150, 50, 60); 

Hopefully, you got the point by now. We will mention a few more functions that may be used in a similar fashion. For example, XFillArc() takes the same parameters as XDrawArc(), but draws only the inside of this arc (like XFillRectangle() does to a rectangle drawn using the XDrawRectangle() function). There is also an XFillPolygon() function that fills the inside of a polygon. It takes almost the same parameters as XDrawLines(). However, if the last point in the array has a different location than the first point in the array, the XFillPolygon() function automatically adds another "virtual" lines, connecting these two points. Another difference between the two functions, is that XFillPolygon() takes an additional parameters, shape, that is used to help the X server optimize its operation. You can read about it in your manual pages. There are also plural versions for these functions, namely XFillArcs() and XFillRectangles(). 

但愿你跟上了我的进度。我们还将提到更多的一些使用上差不多的函数。例如,XFillArc()和XDrawArc()带有相同的参数,但是只画出弧的内部(像XFillRectangle()函数所作的和用XDrawRectangle()函数画出的矩形一样)。还有一个填充多边形内部的XFillPolygon()函数。它和XDrawLines()基本上有相同的参数。然而,如果数组的最后一个点和第一个点处于不同的位置,XFillPolygon()函数自动添加一条”virtual“线,连接这两个点。两个函数的另外一个不同点就是XFillPolygon()带另外一个参数,shape。它用来帮助X服务器优化它的行为。你能够在手册页上学到这些。对于这些函数还有复数版本,名字为XFillArcs()和XFillRectangles()。 

The source code for a program doing these drawings is found in the file simple-drawing.c. 

完成这些绘画的程序的源代码位于文件simple-drawing.c中。 

X Events 

X 事件 

In an Xlib program, everything is driven by events. Event painting on the screen is sometimes done as a response to an event - an "expose" event. If part of a program's window that was hidden, gets exposed (e.g. the window was raised above other windows), the X server will send an "expose" event to let the program know it should repaint that part of the window. User input (key presses, mouse movement, etc.) is also received as a set of events.  

在Xlib程序中,所有的事情都是被事件驱动的。事件绘图有时是对事件-一个”暴露的“事件-的反应。如果程序窗口被隐藏的一部分重又暴露了(例如窗口从另外一个窗口后面升上来了),X服务器将发送一个”暴露的“事件让程序知道它应当重新画处窗口的这个部分。用户输入(按键,鼠标移动,等)也是作为一套事件被接收的。 

Registering For Event Types Using Event Masks 

使用事件遮罩给事件型别注册 

After a program creates a window (or several windows), it should tell the X server what types of events it wishes to receive for this window. By default, no events are sent to the program. It may register for various mouse (also called "pointer") events, keyboard events, expose events and so on. This is done for optimizing the server-to-client connection (i.e. why send a program (that might even be running at the other side of the globe) an event it is not interested in?). 

在程序创建了一个窗口(或者几个窗口)之后,它应当告诉X服务器它想让这个窗口接收什么型别的事件。缺省的,没有事件发送给程序。它可能注册各种鼠标(也被称为”指针“)事件,键盘事件,暴露事件等等。这是用于优化服务器和客户之间的连接(也就是,为什么要发送给程序(那可能是运行于地球的另外一边的)它不感兴趣的事件的?)。 

In Xlib, we use the XSelectInput() function to register for events. This function accepts 3 parameters - the display structure, an ID of a window, and a mask of the event types it wishes to get. The window ID parameter allows us to register for receiving different types of events for different windows. Here is how we register for "expose" events for a window whose ID is 'win': 

在Xlib中,我们使用XSelectInput()函数来注册事件。这个函数接收3个参数 - display结构,窗口的ID,以及它想要收到的事件型别的遮罩。窗口ID这个参数使得我们能够为不同的窗口注册接收不同型别的事件。这儿是我们如何给ID为‘win’的窗口注册”暴露”事件的: 

XSelectInput(display, win, ExposureMask); 

ExposureMask is a constant defined in the "X.h" header file. If we wanted to register to several event types, we can logically "or" them, as follows: 

ExposureMask 是定义在头文件“X.h”中的常量。如果我们想要注册好几种事件型别,我们用逻辑或进行连接,如下: 

XSelectInput(display, win, ExposureMask | ButtonPressMask); 

This registers for "expose" events as well as for mouse button presses inside the given window. You should note that a mask may represent several event sub-types. 

遮注册了“暴露”事件以及鼠标按钮在给定窗口按下的事件。你应当注意到一个遮罩有可能代表了好几种事件子型别。 

Note: A common bug programmers do is adding code to handle new event types in their program, while forgetting to add the masks for these events in the call to XSelectInput(). Such a programmer then could sit down for hours debugging his program, wondering "why doesn't my program notice that i released the button??", only to find that they registered for button press events, but not for button release events.  

注意:一个常见的蹩脚程序员所作的是在它们的程序中添加代码来处理新的事件型别,而忘记了在调用XSelectInput()中添加这些事件的遮罩。这样的程序员然后坐下来花数个小时调试它们的程序,奇怪于“为什么我的程序没注意到我释放鼠标??”,最后只是发现它们忘记了只注册鼠标按下事件,而不是鼠标释放事件。 

Receiving Events - Writing The Events Loop 

接收事件-撰写事件循环 

After we have registered for the event types we are interested in, we need to enter a loop of receiving events and handling them. There are various ways to write such a loop, but the basic loop looks like this: 

在我们为感兴趣的事件型别注册了之后,我们需要进入接收事件和处理它们的循环。有好几种撰写这样的循环的办法,但基本的循环是这样的: 

XEvent an_event; 

while (1) { 

     XNextEvent(display, &an_event); 

     switch (an_event.type) { 

       case Expose: 

         . 

         . 

         break; 

       default:  

         break; 

     } 

The XNextEvent() function fetches the next event coming from the X server. If no event is waiting, it blocks until one is received. When it returns, the event's data is placed in the XEvent variable given to the function as the second parameter. After that, the "type" field of this variable specifies what type of event we got. Expose is the event type that tells us there is a part of the window that needs to be redrawn. After we handle this event, we go back and wait for the next event to process. Obviously, we will need to give the user some way of terminating the program. This is usually done by handling a special "quit" event, as we'll soon see. 

XNextEvent()函数取得从X服务器发送来的下一个事件。如果没有事件在等待,它阻塞在那知道接收到了一个。当它返回了,事件的数据被放置给函数的第二个参数XEvent变量中。之后,变量的“type”域指定了我们得到的事件的型别。事件的型别是Expose告诉我们窗口的一部分布需要重画。在我们处理了事件之后,我们回过头来继续等待下一个要处理的。明显,我们需要给用户某种终止程序的途径。如我们即将看到的,这通常是通过处理“quit”事件来完成那个的。 

Expose Events 

暴露事件 

The "expose" event is one of the most basic events an application may receive. It will be sent to us in one of several cases: 

“暴露”事件是程序可能接收的最基本的事件中的一个。它在一下情况中将发送给我们: 

     * A window that covered part of our window has moved away, exposing part (or all) of our window. 

     * Our window was raised above other windows. 

     * Our window mapped for the first time. 

     * Our window was de-iconified.  

     * 覆盖我们一部分窗口的窗口被移开了,暴露我们窗口的部分(或者全部)。 

     * 我们的窗口从其他窗口后面升上来了 

     * 我们的窗口第一次映射 

     * 我们的窗口被取消标识了。 

You should note the implicit assumption hidden here - the contents of our window is lost when it is being obscured (covered) by other windows. One may wonder why the X server does not save this contents. The answer is - to save memory. After all, the number of windows on a display at a given time may be very large, and storing the contents of all of them might require a lot of memory (for instance, a 256 color bitmap covering 400 pixels by 400 pixels takes 160KB of memory to store. Now think about 20 windows, some much larger than this size). Actually, there is a way to tell the X server to store the contents of a window in special cases, as we will see later. 

你应当注意背后隐藏的假设 - 我们窗口的内容在被其他窗口遮盖时候丢失了。你可能奇怪X服务器为什么不保存这些内容。答案是 - 为了节省内存。毕竟,窗口在display上的数量在给定时间是非常巨大的,而且保存它们的所有内容可能需要很多的内存(例如,大小为400*400象素的256色位图占据160KB的内存存储。现在想想20个窗口,比这个数字要大得多)。事实上,在特殊情况下有告诉X服务器保存窗口内容的办法,我们将在后面看到。 

When we get an "expose" event, we should take the event's data from the "xexpose" member of the XEvent structure (in our code example we refer to it as "an_event.xexpose"). It contains several interesting fields: 

当我们得到一个“暴露”事件的时候,我们应当从XEvent结构的"xexpose“成员处取出事件的数据(在我们的代码例子中用”an_event.xexpose“引用它)。它包含几个有趣的域: 

count 

     Number of other expose events waiting in the server's events queue. This may be useful if we got several expose events in a row - we will usually avoid redrawing the window until we get the last of them (i.e. until count is 0).  

     在服务器中的事件队列等待的其他暴露事件的数量。这个可能在我们一次接连得到好几个的时候有用 - 我们通常将避免重画知道我们得到它们的最后一个(也就是知道count为0的时候)。 

Window window 

     The ID of the window this expose event was sent for (in case our application registered for events on several windows).  

     暴露事件被发送的窗口的ID(如果我们的程序在几个窗口中注册事件)。 

int x, y 

     The x and y coordinates (in pixels) from the top-left of the window, of the window's region that needs to be redrawn.  

     需要重画的窗口区域的从窗口左上角开始的x和y坐标(象素为单位)。 

int width, height 

     The width and height (in pixels) of the window's region that needs to be redraw. 

     需要重画的窗口区域的宽和高(象素为单位)。  

In our demo programs, we will tend to ignore the region supplied, and simply re-draw all the window. However, this is very inefficient, and we will try to demonstrate some techniques for drawing only the relevant section of screen later on. 

在我们的演示程序中,我们将倾向于忽略提供的区域,而仅仅重画整个屏幕。然而,这是非常没有效率的,而且我们将尝试在后面演示一些仅仅画出相关屏幕部分的技术。 

As an example, here is how we will draw a line across our window, whenever we receive "expose" events. Assume this 'case' is part of the event loop's switch command. 

作为例子,这是我们将如何横跨我们的窗口画一条线,每当我们接收到”暴露“事件的时候。假设这个’case‘是事件循环switch语句的一部分。 

   case Expose: 

     if (an_event.xexpose.count > 0) 

         break; 

     XDrawLine(display, win, gc, 0, 100, 400, 100); 

     break; 

Getting User Input 

获得用户输入 

User input traditionally comes from two sources - the mouse and the keyboard. Various event types exist to notify us of user input - a key being pressed on the keyboard, a key being released on the keyboard, the mouse moving over our window, the mouse entering (or leaving) our window and so on.  

传统上用户输入有两个来源 - 鼠标和键盘。存在多种事件型别来通知我们用户的输入 - 键盘上的按键被按下,在键盘上释放按键,鼠标移动于我们的窗口之上,鼠标进入(或者离开)我们的窗口等等。 

Mouse Button Click And Release Events 

鼠标按钮点击和释放事件 

The first event type we'll deal with is a mouse button-press (or button release) event in our window. In order to register to such an event type, we would add one (or more) of the following masks to the event types we specify for the XSelectInput() function: 

我们将要处理的第一个事件型别是在我们窗口中鼠标按钮按下(或者按键放开)事件。为了注册这样的一个事件,我们将添加一下遮罩中一个(或者更多)来在XSelectInput()函数中指定事件型别: 

ButtonPressMask 

     Notify us of any button that was pressed in one of our windows. 

ButtonReleaseMask 

     Notify us of any button that was released over one of our windows. 

The event types to be checked for in our event-loop switch, are any of the following: 

ButtonPress 

     A button was pressed over one of our windows. 

ButtonRelease 

     A button was released over one of our windows. 

The event structure for these event types is accessed as "an_event.xbutton", and contains the following interesting fields: 

这些事件型别的事件结构是通过"an_event.xbutton“来访问的,并且包括一下有趣的域: 

Window window 

     The ID of the window this button event was sent for (in case our application registered for events on several windows). 

     鼠标事件发送给的窗口ID(如果我们的程序在几个窗口中注册了事件)。 

int x, y 

     The x and y coordinates (in pixels) from the top-left of the window, of the mouse pointer, during the click. 

     在点击时,鼠标指针从窗口左上角为原点的x和y坐标(象素为单位)。 

int button 

     The number of mouse button that was clicked. May be a value such as Button1, Button2, Button3. 

     被点击的鼠标按钮的编号。可能是像Button1, Button2, Button3这样的值。 

Time time 

     time (in millisecond) the event took place in. May be used to calculate "double-click" situations by an application (e.g. if the mouse button was clicked two times in a duration shorter than a given amount, assume this was a double-click). 

     事件发生的时间(毫秒为单位)。可能用于在程序中计算”双击“的情况(例如,如果鼠标按钮在小于给定时间内被点击两次,就认定这个为双击)。 

As an example, here is how we will draw a black pixel at the mouse position, whenever we receive "button press" events, with the 1st mouse button, and erase that pixel (i.e. draw a white pixel) when the 2nd mouse button is pressed. We assume the existence of two GCs, gc_draw with foreground color set to black, and gc_erase, with foreground color set to white. 

作为例子,这儿是我们如何每当接收到”鼠标按下“事件时,当按下的是第一个鼠标按钮的时候在鼠标点击位置画一个黑点的,而是第二个的时候擦除该点(也就是画个白点)。我们假定存在两个GC,gc_draw设置为前景色为黑,而gc_erase前景色为白。 

Assume that the following 'case' is part of the event loop's switch command. 

假定一下'case’是事件循环的swtich语句的一部分。 

   case ButtonPress: 

     x = an_event.xbutton.x; 

     y = an_event.xbutton.y; 

     the_win = an_event.xbutton.window; 

     switch (an_event.xbutton.button) { 

         case Button1: 

             XDrawPoint(display, the_win, gc_draw, x, y); 

             break; 

         case Button2: 

             XDrawPoint(display, the_win, gc_erase, x, y); 

             break; 

         default:  

             break; 

     } 

     break; 

Mouse Movement Events 

鼠标移动事件 

Similar to mouse button press and release events, we also can be notified of various mouse movement events. These can be split into two families. One is of mouse pointer movement while no buttons are pressed, and the second is a mouse pointer motion while one (or more) of the buttons are pressed (this is sometimes called "a mouse drag operation", or just "dragging"). The following event masks may be added in the call to XSelectInput() for our application to be notified of such events: 

与鼠标按下和释放事件类似,我们也可以得到各种鼠标移动事件的通知。这些能够划分为两类。一类是按钮没有被按下时鼠标指针的移动,而第二类时当一个或者多个按钮被按下的时候鼠标指针的移动(这有时被称为“鼠标托放操作”,或者仅仅“拖放”)。下面的事件遮罩可以加到XSelectInput()的调用中以让我们的应用程序得到这些事件的通知。 

PointerMotionMask 

     Events of the pointer moving in one of the windows controlled by our application, while no mouse button is held pressed. 

指针移动遮罩 

     当没有鼠标按钮被按下时,由程序控制的窗口中的一个的指针移动的事件 

ButtonMotionMask 

     Events of the pointer moving while one (or more) of the mouse buttons is held pressed. 

按钮移动遮罩 

     当鼠标的一个(或者更多)的鼠标按钮被按下的时候指针移动的事件。 

Button1MotionMask 

     Same as ButtonMotionMask, but only when the 1st mouse button is held pressed. 

按钮1移动遮罩 

     核按钮移动遮罩一样,只不过当第一个鼠标按钮被按下的时候。 

Button2MotionMask, Button3MotionMask, Button4MotionMask, Button5MotionMask 

     Likewise, for 2nd mouse button, or 3rd, 4th or 5th. 

鼠标2移动遮罩,鼠标3移动遮罩,鼠标4移动遮罩,鼠标5移动遮罩 

     类似的,用于第二个鼠标按钮,或者第三,第四,第五。 

The event types to be checked for in our event-loop switch, are any of the following: 

在我们的事件循环swtich语句中要检查的事件型别,是以下的任何一个: 

MotionNotify 

     The mouse pointer moved in one of the windows for which we requested to be notified of such events. 

移动通知 

     在我们需要得到这个消息通知的窗口中移动的鼠标指针。 

The event structure for these event types is accessed as "an_event.xbutton", and contains the following interesting fields: 

这些事件型别的事件结构是以”an_event.xbutton“来访问的,并且包含一下有趣的域: 

Window window 

     The ID of the window this mouse motion event was sent for (in case our application registered for events on several windows). 

     鼠标移动事件发送给的窗口的ID(如果我们的应用程序给几个窗口注册了事件)。 

int x, y 

     The x and y coordinates (in pixels) from the top-left of the window, of the mouse pointer, when the event was generated. 

     事件发生的时候,以窗口的左上角为原点鼠标指针所位于的x和y坐标(象素为单位)。 

unsigned int state 

     A mask of the buttons (or keys) held down during this event - if any. This field is a bitwise OR of any of the following: 

     按钮(或者按键)在事件发生时按下的遮罩 - 如果有的话。改域是以下值的位或: 

   # Button1Mask 

   # Button2Mask 

   # Button3Mask 

   # Button4Mask 

   # Button5Mask 

   # ShiftMask 

   # LockMask 

   # ControlMask 

   # Mod1Mask 

   # Mod2Mask 

   # Mod3Mask 

   # Mod4Mask 

   # Mod5Mask 

     Their names are self explanatory, where the first 5 refer to mouse buttons that are being pressed, while the rest refer to various "special keys" that are being pressed (Mod1 is usually the 'ALT' key or the 'META' key).  

     它们的名字是可以自明的,前五个是指被按下的鼠标按钮,而剩下的指的是被按下的“特殊按键”(Mod1通常是‘ALT’或者‘META’键)。 

Time time 

     time (in millisecond) the event took place in. 

     事件发生所处的事件(毫秒为单位)。 

As an example, the following code handles a "draw mode" for a painting program, that is, if the user moves the mouse while the 1st mouse button is being held down, then we 'draw' on the screen. Note that this code has a flow: Since mouse movement may generate many events, it might be that we won't get a mouse motion event for each pixel the mouse moved over. Our program should be able to cope with such a situation. One way to do that would be to remember the last pixel the mouse was dragged over, and draw a line between that position and the new mouse pointer position. Assume that the following 'case' is part of the event loop's switch command. 

作为例子,以下的代码处理一个绘图程序的“绘图模式”,也就是说如果用户在鼠标1键按下的时候移动了,那么我们在屏幕上“绘图”。注意代码有一个惯性:因为鼠标移动可能产生许多事件,可能我们不会在每个鼠标移到的点都得到鼠标移动事件。我们的程序应当能够处理这么一个情况。解决的一个办法可能是记住鼠标托过的上一个点,并在和新的鼠标指针位置之间画直线。假定下面的‘case’是事件循环的switch语句的一部分。 

   case MotionNotify: 

     x = an_event.xmotion.x; 

     y = an_event.xmotion.y; 

     the_win = an_event.xbutton.window; 

     if (an_event.xmotion.state & Button1Mask) { 

         XDrawPoint(display, the_win, gc_draw, x, y); 

     } 

     break; 

Mouse Pointer Enter And Leave Events 

鼠标指针进入和离开事件 

Another type of event that applications might be interested at, is a mouse pointer entering a window the program controls, or leaving such a window. Some programs use these events to show the user that the application is now in focus. In order to register for such an event type, we would add one (or more) of the following masks to the event types we specify for the XSelectInput() function: 

另一个应用程序可能感兴趣的事件型别,是鼠标指针进入或者离开程序控制的窗口。一些程序使用这些事件来向用户展示应用程序现在在焦点状态。为了注册这么一个事件型别,我们将把下面的一个(或者多个)遮罩添加到我们给XSelectInput()函数指定的事件型别中: 

EnterWindowMask 

     Notify us when the mouse pointer enters any of our controlled windows. 

     当鼠标指针进入我们控制的任何窗口时通知我们。 

LeaveWindowMask 

     Notify us when the mouse pointer leaves any of our controlled windows. 

     当鼠标指针离开我们控制的任何窗口时通知我们。 

The event types to be checked for in our event-loop switch, are any of the following: 

要在我们的事件循环swtich中检查的事件型别是以下的这些: 

EnterNotify 

     The mouse pointer just entered one of our controlled windows. 

     鼠标指针刚刚进入了我们控制的窗口 

LeaveNotify 

     The mouse pointer just left one of our controlled windows. 

     鼠标指针刚刚离开了我们控制的窗口 

The event structure for these event types is accessed as "an_event.xcrossing", and contains the following interesting fields: 

这些事件型别的事件结构是通过“an_event.xcrossing"来访问的,并且包含以下有趣的域: 

Window window 

     The ID of the window this button event was sent for (in case our application registered for events on several windows). 

     鼠标事件发送给的窗口的ID(如果我们的程序给几个程序注册了事件)。 

Window subwindow 

     The ID of the child window from which the mouse entered our window (in an EnterNotify event), or into which the mouse pointer has moved (in a LeaveNotify event), or None, if the mouse moved from outside our window. 

     鼠标进入到我们的窗口(在EnterNotify事件中),或者鼠标指针移出到的子窗口ID(在LeaveNotify事件中),或者两者均否,如果鼠标从我们的窗口外边移入。

int x, y 

     The x and y coordinates (in pixels) from the top-left of the window, of the mouse pointer, when the event was generated. 

     事件产生的时候,鼠标指针以窗口的左上角为原点的x和y坐标(象素为单位)。 

int mode 

     The number of mouse button that was clicked. May be a value such as Button1, Button2, Button3. 

     鼠标指针点击的编号。可能是如Button1, Button2, Button3这样的值。 

Time time 

     time (in millisecond) the event took place in. May be used to calculate "double-click" situations by an application (e.g. if the mouse button was clicked two times in a duration shorter than a given amount, assume this was a double-click). 

     事件发生的时间(毫秒为单位)。可能用于在程序中计算”双击“的情况(例如,如果鼠标按钮在小于给定时间内被点击两次,就认定这个为双击)。 

unsigned int state 

     A mask of the buttons (or keys) held down during this event - if any. This field is a bitwise OR of any of the following: 

   # Button1Mask 

   # Button2Mask 

   # Button3Mask 

   # Button4Mask 

   # Button5Mask 

   # ShiftMask 

   # LockMask 

   # ControlMask 

   # Mod1Mask 

   # Mod2Mask 

   # Mod3Mask 

   # Mod4Mask 

   # Mod5Mask 

     Their names are self explanatory, where the first 5 refer to mouse buttons that are being pressed, while the rest refer to various "special keys" that are being pressed (Mod1 is usually the 'ALT' key or the 'META' key).  

Bool focus 

     Set to True if the window has the keyboard focus, False otherwise. 

     如果窗口拥有键盘焦点,设置为真。否则反之。 

The Keyboard Focus 

键盘焦点 

There may be many windows on a screen, but only a single keyboard attached to them. How does the X server then know which window should be sent a given keyboard input? This is done using the keyboard focus. Only a single window on the screen may have the keyboard focus at a given time. There are Xlib functions that allow a program to set the keyboard focus to a given window. The user can usually set the keyboard focus using the window manager (often by clicking on the title bar of the desired window). Once our window has the keyboard focus, every key press or key release will cause an event to be sent to our program (if it registered for these event types...). 

屏幕上有许多窗口,但是仅仅有一个键盘和他附着。X服务器如何知道键盘输入是发送给哪个窗口的呢?这个是通过键盘焦点来完成的。在给定的时间,屏幕上只有一个窗口能够有键盘焦点。存在Xlib函数来使得程序给某个窗口设置键盘焦点。用户通常能够使用窗口管理器来安排键盘焦点(通常是通过点击所需窗口的标题栏)。一旦我们的窗口拥有了键盘焦点,每个键的按下和放开都将导致事件发送给我们的程序(如果它注册了这些事件类型...)。 

Keyboard Press And Release Events 

键盘按下和释放事件 

If a window controlled by our program currently holds the keyboard focus, it can receive key press and key release events. In order to register for such events, any of the following masks may be added to the call to XSelectInput(): 

如果由我们程序控制的窗口当前保有键盘焦点,它能够接收键的按下和释放事件。为了注册这些事件,下面的遮罩要加到XSelectInput()的调用中去: 

KeyPressMask 

     Notify our program when a key was pressed while any of its controlled windows had the keyboard focus. 

KeyPressMask 

     Notify our program when a key was released while any of its controlled windows had the keyboard focus. 

The event types to be checked for in our event-loop switch, are any of the following: 

KeyPress 

     A key was just pressed on the keyboard while any of our windows had the keyboard focus. 

KeyRelease 

     A key was just released on the keyboard while any of our windows had the keyboard focus. 

The event structure for these event types is accessed as "an_event.xkey", and contains the following interesting fields: 

Window window 

     The ID of the window this button event was sent for (in case our application registered for events on several windows). 

unsigned int keycode 

     The code of the key that was pressed (or released). This is some internal X code, that should be translated into a key symbol, as will be explained below. 

int x, y 

     The x and y coordinates (in pixels) from the top-left of the window, of the mouse pointer, when the event was generated. 

Time time 

     time (in millisecond) the event took place in. May be used to calculate "double-click" situations by an application (e.g. if the mouse button was clicked two times in a duration shorter than a given amount, assume this was a double-click). 

unsigned int state 

     A mask of the buttons (or modifier keys) held down during this event - if any. This field is a bitwise OR of any of the following: 

   # Button1Mask 

   # Button2Mask 

   # Button3Mask 

   # Button4Mask 

   # Button5Mask 

   # ShiftMask 

   # LockMask 

   # ControlMask 

   # Mod1Mask 

   # Mod2Mask 

   # Mod3Mask 

   # Mod4Mask 

   # Mod5Mask 

     Their names are self explanatory, where the first 5 refer to mouse buttons that are being pressed, while the rest refer to various "special keys" that are being pressed (Mod1 is usually the 'ALT' key or the 'META' key).  

As we mentioned, the key code is rather meaningless on its own, and is affected by the specific keyboard device attached to the machine running the X server. To actually use this code, we translate it into a key symbol, which is standardized. We may use the XKeycodeToKeysym() function to do the translation. This function gets 3 parameters: a pointer to the display, the key code to be translated, and an index (we'll supply '0' for this parameter). Standard Xlib key codes are found in the include file "X11/keysymdef.h". As an example for using the key press events together with the XKeycodeToKeysym function, we'll show how to handle key presses of this sort: Pressing '1' will cause painting the pixel where the mouse pointer is currently located. Pressing the DEL key will cause to erase that pixel (using a 'gc_erase' GC). Pressing any of the letters (a to z, upper case or lower case) will cause it to be printed to standard output. Any other key pressed will be ignored. Assume that the following 'case' is part of the event loop's switch command.  

如果我们提到过的,按键代码于其本身是相当没有意义的,并且是受到了附着于运行X服务器的机器的键盘设备的影响。为了真正使用这些代码,我们要把他们翻译为按键符号,它们是标准的。我们能用XKeycodeToKeysym()函数来完成翻译工作。这个函数需要3个参数:只想display的指针,要翻译的按键代码,和一个索引(我们将用‘0’来作这个参数)。标准Xlib案件代码可以在”X11/keysymdef.h“中找到。作为一个使用键按下事件和XKeycodeToKeysym函数的例子,我们将展示如何处理这种的按键:按下‘1’将导致鼠标当前所在的点涂黑。按下DEL键将导致点的擦除(使用‘gc_erase’GC)。按下任何字母(a到z,大写或者小写)将使得他们显示在标准输出上。任何其他键的按下都被忽略。假定下面的‘case’是事件循环的switch语句的一部分。 

   case KeyPress: 

     x = an_event.xkey.x; 

     y = an_event.xkey.y; 

     the_win = an_event.xkey.window; 

     { 

         KeySym key_symbol = XKeycodeToKeysym(display, an_event.xkey.keycode, 0); 

         switch (key_symbol) { 

             case XK_1: 

             case XK_KP_1:  

                 XDrawPoint(display, the_win, gc_draw, x, y); 

                 break; 

             case XK_Delete:  

                 XDrawPoint(display, the_win, gc_erase, x, y); 

                 break; 

             default:   

                 if (key_symbol >= XK_A && key_symbol <= XK_Z) { 

                     int ascii_key = key_symbol - XK_A + 'A'; 

                     printf("Key pressed - '%c'\n", ascii_key); 

                 } 

                 if (key_symbol >= XK_a && key_symbol <= XK_z) { 

                     int ascii_key = key_symbol - XK_a + 'a'; 

                     printf("Key pressed - '%c'\n", ascii_key); 

                 } 

                 break; 

         } 

     } 

     break; 

As you can see, key symbols refer to the physical key on the keyboard in some manner, so one should be careful to properly check all possible cases (as we did for the '1' key in the above example). We also assume that the letter keys have consecutive key symbol values, or else the range checking tricks and the key symbol to ASCII code translations wouldn't have worked. 

如你所见,按键符号以某种方法与键盘上的屋里按键对应,因而你应当谨慎以恰当检查到所有的可能情况(如我们在上面例子中对‘1’所作的那样)。我们假定字母键有连续的符号值,否则范围检查的技巧与按键符号到ASCII代码之间的翻译将不能正常工作。 

X Events - A Complete Example 

X Events - 一个完整的例子 

As an example for handling events, we will show the events.c program. This program creates a window, makes some drawings on it, and then enters an event loop. If it gets an expose event - it redraws the whole window. If it gets a left button press (or motion) event, it draws the pixel under the mouse pointer in black color. If it gets a middle button press (or motion) event, it draws the pixel under the mouse pointer in white color (i.e. erases this pixel). Some note should be taken as to how these picture changes are handled. It is not sufficient to just draw the pixel with the appropriate color. We need to make a note of this color change, so on the next expose event we will draw the pixel again with the proper color. For this purpose, we use a huge (1000 by 1000) array representing the window's pixels. Initially, all cells in the array are initialized to '0'. When drawing a pixel in black, we set the matching array cell to '1'. When drawing a pixel in white, we set the matching array cell to '-1'. we cannot just reset it back to '0', otherwise the original drawing we painted on the screen will always be erased. Finally, when the user presses on any on the keyboard, the program exits. 

作为一个处理事件的例子,我们将展示events.c程序。这个程序创建一个窗口,在其上作一些绘画,然后进入事件循环。如果它得到一个暴露时间 - 它重画整个窗口。如果它得到一个左键按下(或者移动)事件,它用白色画在鼠标指针下面的点(也就是说擦除这个点)。应当注意这些画面的改变是怎么被处理的。仅仅用恰当的颜色画出点是不够的。我们需要注意到颜色的改变,因而下一个暴露事件我们将能够再用合适的颜色画点。就这个目的,我们使用一个巨大的(1000乘1000)数组代表窗口的点。初始时,数组中的所有的元素被初始化为‘0’。当以黑色画点的时候,我们设置相应的数组元素为‘1’。当以白色画点的时候我们设置相应的数组元素为‘-1’。我们不能仅仅把他们重新设置为‘0’,否则我们原来画在屏幕上的东西总是会被擦除。最终,当用户按下键盘的任意键,程序退出。 

When running this program, you will note that motion events often skip pixels. If the mouse was moved from one point to another in a quick motion, we will not get motion events for each pixel the mouse pointer moved over. Thus, if it was our intention to handle these gaps properly, we would need to remember the location of the last motion event, and then perhaps draw a line between that location and the location of the next motion event. This is what painting programs normally do. 

当运行这个程序时,你应注意到移动事件常常漏掉点。 如果鼠标快速从一个点移到另一个点,我们将不会在某个鼠标指针移过的点处得到移动事件。因而,如果我们故意好好处理这些间隙,我们也许需要记住上次移动事件发生的位置,然后再那个地点和下一个移动事件的地点之间画一条线。这就是绘画程序通常干的事情。 

Handling Text And Fonts 

处理文本和字体 

Besides drawing graphics on a window, we often want to draw text. Text strings have two major properties - the characters to be drawn, and the font with which they are drawn. In order to draw text we need to first request the X server to load a font. We then assign the font to a GC, and finally we draw the text in a window, using the GC.  

除去在窗口上画图,我们经常需要绘出文本。文本字符串有两个主要的属性-要画出的字符,和用什么字体画出。为了画出文本,我们首先需要X服务器载入字体。我们然后把字体赋给GC,最终我们在窗口上使用那个GC画出文本。 

The Font Structure 

字体结构 

In order to support flexible fonts, a font structure is defined, of type XFontStruct. This structure is used to contain information about a font, and is passed to several functions that handle fonts selection and text drawing.  

为了支持灵活的字体,定义了类型为XFontStruct的字体结构。这个结构体用来包含有关一个字体的信息,并传递给好几个处理字体选择和文本绘出的函数。 

Loading A Font 

载入字体 

As a first step to draw text, we use a font loading function, such as XLoadQueryFont(). This function asks the X server to load data that defines a font with a given name. If the font is found, it is loaded by the server, and an XFontStruct pointer is returned. If the font is not found, or the loading operation fails, a NULL value is returned. Each font may have two names. One is a long string, that specifies the full properties of the font (font family, font size, italic/bold/underline attributes, etc.). The other is a short nickname for the font, configured for the specific X server. As an example, we will try to load a "*-helvetica-*-12-*" font (the '*' characters work the same as wildcard characters in a shell): 

作为画出文本的第一步,我们使用字体装载函数,比如XLoadQueryFont()。这个函数要求X服务器来载入用一个给定名字定义的字体的数据。如果字体没有被找到,或者载入操作失败了,返回NULL。每个字体都可能有两个名字。一个是长字符串,确定了字体的完整属性(字体名,大小,斜体/粗体/下划线 属性,等)。另一个是字体的短的昵称,为特定X服务器配置的。作为例子,我们试着载入一个名为”*-helvetica-*-12-*“字体(‘*’字符和shell中的通配符是同样的作用)。 

XFontStruct* font_info; 

char* font_name = "*-helvetica-*-12-*"; 

font_info = XLoadQueryFont(display, fond_name); 

if (!font_info) { 

     fprintf(stderr, "XLoadQueryFont: failed loading font '%s'\n", font_name); 

Assigning A Font To A Graphics Context 

把字体赋给图形上下文 

After we load the font, we need to assign it to a GC. assuming that 'gc' is a variable of type GC that's already initialized, here is how this is done: 

在我们载入字体之后,我们需要把它赋给一个GC。假定‘gc’是一个类型为GC,并已经初始化的变量,下面是这个过程如何完成的: 

XSetFont(display, gc, font_info->fid); 

The 'fid' field in an XFontStruct is an identified used to identify the loaded font in various requests. 

XFontStruct中的'fid'是一个用来标识已经在各种请求中载入的字体。 

Drawing Text In A Window 

在窗口中绘出文本 

Once we got our wanted font loaded and assigned to our GC, we can draw text in our window, using a function such as XDrawString(). This function will draw a given text string at a given location on the window. The location would be that of the lower-left corner of the drawn text string. Here is how it is used: 

一旦我们把我们要用的字体载入并赋给了GC,那么就能在窗口上用像XDrawString()这样的函数绘出文本。这个函数将在窗口的一个给定位置画出给定的文本字符串。位置将成为画出的文本字符串的左下角。这儿是它如何使用的: 

int x, y; 

x = 0; 

y = 0; 

XDrawString(display, win, gc, x, y, "hello world", strlen("hello world")); 

char* text_string = "middle of the road"; 

int string_width = XTextWidth(font_info, text_string, strlen(text_string)); 

int fond_height = font_info->ascent + font_info->descent; 

x = (win_width - string_width) / 2; 

y = (win_width - font_height) / 2; 

XDrawString(display, win, gc, x, y, "hello world", strlen("hello world")); 

Some notes must be made in order to make this code snippet clear: 

为了使得代码片段更加清楚,必须添加一些注解: 

     * The XTextWidth() function is used to "predict" the width of a given text string, as it will be drawn using a given font. This may be used to determine where to draw the left end of the string so it will look like it's occupying the middle of the window, for example. 

     * XTextWidth()函数被用来在它将要被使用给定字体画出同时,”预测“给定字符串的宽。比如这个能用来决定在哪儿画出字符串的左端使得它看来像它占据了窗口的正中。 

     * A font has two attributes named "ascent" and "descent", used to specify the height of the font. Basically, a font's characters are drawn relative to some imaginary horizontal line. part of a character is drawn above this line, and part of it is drawn below this line. the highest letter is drawn at most "font_info->ascent" pixels above this line, while the letter with the lowest part will be drawn at most "font_info->descent" pixels below that line. Thus, the sum of these two numbers determines the height of the font.  

     * 一个字体有两个称为”ascent“和”descent“的属性,用来指定字体的高。基本的,一种字体的字符是就像对某个映象述水平线来画的。字符的部分高出该线,部分又低于这线。字符的部分最高画时超出该线“font_info->ascent”个象素,而字母的最低部分将不会低于该线”font_info->descent“个象素。因而,这个两个数字的和决定了字体的高。 

The source code for a program drawing these texts is found in the file simple-text.c. 

画出这些文本的程序的源代码可以在simple-text.c文件中找到。 

Windows Hierarchy 

窗口阶层 

When windows are displayed on an X server, they are always ordered in some hierarchy - every window may have child windows, each child window may have its own child windows, etc. Lets see a few properties of this hierarchy, and how they effect the operations like drawing or events propagation. 

当窗口显示于X服务器上时,他们总是以某中阶层状排列的 - 每个窗口都可能有子窗口,每个子窗口可能又有他们自己的子窗口,等等。让我们看看一些关于这种阶层的事情,以及它们时如何影响像绘图或者事件传播这些操作的。 

Root, Parent And Child Windows 

根,父和子窗口 

On each screen, there is a root window. The root window always spans the entire screen size. This window cannot be destroyed, resized or iconified. When an application creates windows, it first has to create at least one top-level window. This window becomes a direct descendant of the root window, until it is first mapped on the screen. Before this window is mapped, the window manager is notified of the operation about to take place. The window manager then has the privilege of re-parenting the new top-level window. This is used to add a window that will contain the new window, and will be used to draw its frame, title bar, system menu, etc. 

在每个屏幕上,都有一个根窗口。根窗口总是占据了整个屏幕的大小。这个窗口不能被销毁,缩放或者图标化。当应用程序创建窗口时,它首先必须至少创建一个顶层的窗口。这个窗口成为根窗口的一个直接的后代,直到它第一次映射于屏幕上。窗口映射之前,窗口管理器被通知了将要发生的操作。窗口然后有特权重新确定这个新的顶层窗口的父窗口。这个是用来增加将要包含新窗口的窗口,以及画出的框架,标题栏,系统按钮,等等。 

Once such a top-level window (which is actually not a top-level window after the re-parenting operation takes place) is created, the application can create child windows inside this window. A child can only be displayed inside its parent window - If it is moved outside, its being clipped by the border of its parent window. Any window may contain more than one child window, and if this is the case, these windows are being ordered in an internal stack. When a top-level window is being raised - all its descendant windows are being raised with it, while retaining their internal ordering. If a child window is raised - it is being raised only amongst its sibling windows. 

一旦这么一个顶层窗口被创建了(实际上在重新确定父窗口的操作发生后其实不再是顶层窗口了),应用程序能够在这个窗口中创建子窗口。子窗口只能在它的父窗口中显示 - 如果他被移出,它被它的父窗口的边缘给切割了。任何窗口都可能包含多于一个的窗口,并且如果是这样,这些窗口以一种内部堆叠次序排好。当顶层窗口被升上来 - 它的所有后代窗口在保持他们内部排序的同时跟着它升上来。如果一个子窗口被升上来 - 他只是在他的兄弟窗口间升上来了。 

Lets see how to create a child window inside a given window 'win'. 

让我们看看如何创建在一个给定窗口‘win’中创建子窗口 

Window child_win; 

int child_win_width = 50; 

int child_win_height = 30; 

int child_win_x = 0; 

int child_win_y = 0; 

child_win = XCreateSimpleWindow(display, 

                                 win, 

                                 child_win_x, child_win_y, 

                                 child_win_width, child_win_height, 

                                 child_win_border_width, 

                                 BlackPixel(display, screen_num), 

                                 WhitePixel(display, screen_num)); 

Events Propagation 

事件传播 

We have discussed events propagation earlier - If a window is being sent an event and it does not process the event - the event is being passed to the this window's parent window. If that parent window does not handle this event - it is being passed to the parent's parent window, and so on. This behavior does not make a lot of sense for a simple Xlib application, but it does make sense when higher-level graphic libraries are used. They usually associate functions with events occurring in specific windows. In such a case, it is useful to pass the event to the relevant window, with which a proper function is associated.  

前面我们已经讨论过事件传播了 - 如果窗口被发送了一个事件而且它没有处理这个事件 - 事件被传递给这个窗口的父窗口。如果父窗口没有处理这个事件 - 它被传递给父窗口的父窗口,以此类推。这个行为对于简单的Xlib程序没有什么大的关系,但它在使用高层的图形库时有用。他们通常把函数和发生在指定窗口中发生的事件关联起来。在这么一个情况下,把事件发送给相关窗口,于其中一个合适函数关联着,是有用的。 

Interacting With The Window Manager 

与窗口管理器交互 

After we have seen how to create windows and draw on them, we take one step back, and look at how our windows are interacting with their environment - the full screen, and the other windows. First of all, our application needs to interact with the window manager. The window manager is responsible to decorating drawn windows (i.e. adding a frame, an iconify button, a system menu, a title bar), as well as to handling icons shown when windows are being iconified. It also handles ordering of windows on the screen, and other administrative tasks. We need to give it various hints as to how we want it to treat our application's windows. 

在我们已经看到了如何创建窗口并在其上绘图之后,我们回过一步来,看看我们的窗口如何与他们的环境交互-整个屏幕,以及其他的窗口。首先,我们的程序需要与窗口管理器交互。窗口管理器负责装饰画好的窗口(也就是添加框架,图标按钮,系统菜单,标题栏),以及在窗口被图标化的时候处理显示的图标。它还处理屏幕上的窗口排序,以及其他管理之职。我们需要给他各种信息来说明我们想要它如何对待我们程序的窗口。 

Window Properties 

窗口属性 

Many of the parameters communicated to the window manager are passed using data called "properties". These properties are attached by the X server to different windows, and are stored in a format that makes it possible to read them from different machines, that may use different architectures (remember that an X client program may run on a remote machine). Properties may be of various types - numbers, strings, etc. Most window manager hints functions use text properties. A function named XStringListToTextProperty() may be used to turn a normal C string into an X text property, that can later be passed to various Xlib functions. Here is how to use it: 

许多与窗口管理器通信的参数是使用名为“属性”的数据传递的。这些属性由X服务器附着给不同的窗口,并且储存在一个使得能够从不同的机器,不同的架构读取的格式(记住X客户程序可能运行于一个远程机器)。属性可能有多种类型 - 数字,字符串,等。大部分窗口管理器要求函数使用文本属性。一个名为XStringListToTextProperty()能用于把普通的C字符串变成X文本属性,然后传递给各种Xlib函数。下面是如何使用它: 

XTextProperty window_title_property; 

char* window_title = "hello, world"; 

int rc = XStringListToTextProperty(&window_title, 

                                    1, 

                                    &window_title_property); 

if (rc == 0) { 

     fprintf(stderr, "XStringListToTextProperty - out of memory\n"); 

     exit(1); 

the XStringListToTextProperty() function accepts an array of C strings, a count of the number of strings in the array (1 in our case), and a pointer to an XTextProperty variable. It concatenates the list of strings and places it in the XTextProperty variable. It returns a non-zero value on success, or 0 on failure (e.g. not enough memory to perform the operation). 

XStringListToTextProperty()函数接受一个C字符串的数组,数组中字符串的个数(在我们的例子中是1),以及一个指向XTextProperty变量的指针。它连接字符串的列表并把它放置在XTextProperty变量中。它成功时返回一个非零的值,或者失败时返回0(例如没有足够的内存完成操作)。 

Setting The Window Name And Icon Name 

设置窗口名和图标名 

The first thing would be to set the name for our window. This is done using the XSetWMName() function. This name may be used by the window manager as the title of the window (in the title bar), in a task list, etc. This function accepts 3 parameters: a pointer to the display, a window handle, and an XTextProperty containing the desired title. Here is how it is used: 

第一件事情是设置我们窗口的名字。这个是通过使用XSetWMName()函数来完成的。这个名字被窗口管理器用作窗口的标题(在标题栏上),在任务列表中,等等。这个函数接受3个参数:一个指向display的指针,一个窗口句柄,以及一个包含希望的标题的XTextProperty。这儿是它如何被使用的: 

XSetWMName(display, win, &window_title_property); 

in order to set the name of the iconized version of our window, we will use the XSetWMIconName() function in a similar way: 

为了设置窗口的图标化版本的名字,我们将以一种类似的办法使用XSetWMIconName()函数: 

XSetWMIconName(display, win, &icon_name_property); 

Setting Preferred Window Size(s) 

设置最佳窗口尺寸 

In various cases, we wish to let the window manager know that we want to have a given size for our window, and to only let the user resize our window in given quantities. For example, in a terminal application (like xterm), we want our window to always contain complete rows and columns, so the text we display won't be cut off in the middle. In other cases we do not want our window to be resized at all (like in many dialog boxes), etc. We can relay this information to the window manager, although it may simply ignore our request. We need to first create a data structure to hold the information, fill it with proper data, and use the XSetWMNormalHints() function. Here is how this may be done: 

在各种情况中,我们想要让窗口管理器知道我们想要我们的窗口有一个给定的尺寸,以及想让我们的用户以给定数量缩放我们的窗口。例如,在一个终端程序中(像xterm),我们想让我们的窗口总是包含完整的行列,因而我们显示的文本不会在中间截断。在其他情况中,我们完全不想让我们的窗口缩放(像许多对话框一样),等等。我们能把这些信息传递给窗口管理器,虽然它可能只是简单的忽略我们的请求。我们首先要创建保存信息的数据结构,用恰当的数据填充,然后使用XSetWMNormalHints()函数。这儿是这些如何完成的: 

XSizeHints* win_size_hints = XAllocSizeHints(); 

if (!win_size_hints) { 

     fprintf(stderr, "XAllocSizeHints - out of memory\n"); 

     exit(1); 

win_size_hints->flags = PSize | PMinSize; 

win_size_hints->min_width = 300; 

win_size_hints->min_height = 200; 

win_size_hints->base_width = 400; 

win_size_hints->base_height = 250; 

XSetWMNormalHints(display, win, win_size_hints); 

XFree(win_size_hints); 

For full info about the other size hints we may supply, see your manual pages. 

Setting Miscellaneous Window Manager Hints 

设置窗口管理器的杂项 

There are some other window manager hints that may be set using the XSetWMHints() function. This function uses a XWMHints structure to pass the data to the window manager. Here is how it may be used: 

还有其他一些需要通过XSetWMHints()函数设置的窗口管理器选项。这个函数使用XWMHints结构传递数据给窗口管理器。这儿是他们如何使用的: 

XWMHints* win_hints = XAllocWMHints(); 

if (!win_hints) { 

     fprintf(stderr, "XAllocWMHints - out of memory\n"); 

     exit(1); 

win_hints->flags = StateHint | IconPositionHint; 

win_hints->initial_state = IconicState; 

win_hints->icon_x = 0; 

win_hints->icon_y = 0; 

XSetWMHints(display, win, win_hints); 

XFree(win_hints); 

Other options settable using this function are specified in the manual page. 

Setting An Application's Icon 

设置程序的图标 

In order to set an icon to be used by the window manager, when a user iconifies our application, we use the XSetWMHints() function mentioned above. However, we first need to create a pixmap that contains the icon's data. Pixmaps are the way that X servers manipulate images, and will be explained fully later in this tutorial. For now, we'll just show you how to set the icon for your application. We assume you got a bitmap file representing this icon, stored using an X bitmap format. An example of such a file is the "icon.bmp" file that comes with this tutorial. Anyway, here is the code: 

为了我们应用程序图标化时,被设置窗口管理器使用的图标,我们使用前面提到的XSetWMHints()函数来完成。然而,我们首先需要创建一个包含图标数据的pixmap。Pixmap是X服务器操纵图象的办法,并且会在后面详细的解释。现在,我们仅仅向你展示如何给你的应用程序设置图标。我们假定你得到一个代表这个图标的位图文件,以X位图的格式保存着。一个这样的名字叫"icon.bmp“的文件附带于这个教程而在。不管,这是代码: 

#include "icon.bmp"; 

XWMHints* win_hints; 

Pixmap icon_pixmap = XCreateBitmapFromData(display, 

                                            win, 

                                            icon_bitmap_bits, 

                                            icon_bitmap_width, 

                                            icon_bitmap_height); 

if (!icon_pixmap) { 

     fprintf(stderr, "XCreateBitmapFromData - error creating pixmap\n"); 

     exit(1); 

win_hints = XAllocWMHints(); 

if (!win_hints) { 

     fprintf(stderr, "XAllocWMHints - out of memory\n"); 

     exit(1); 

win_hints->flags = IconPixmapHint; 

win_hints->icon_pixmap = icon_pixmap; 

XSetWMHints(display, win, win_hints); 

XFree(win_hints); 

you may use programs such as "xpaint" to create files using the X bitmap format. 

你可以使用像“xpaint”这样的程序来创建使用X位图格式的文件。 

To conclude this section, we supply a simple-wm-hints.c, that creates a window, sets the window manager hints shown above, and runs into a very simple event loop, allowing the user to play with the window and see how these hints affect the behavior of the application. Try playing with the various hints, and try to run the program under different window managers, to see the differences in its behavior under each of them. This will teach you something about X programs portability.  

为了完善本节,我们提供simple-wm-hints.c,它创建窗口,设置前面所展示的窗口管理器选项,并且进入一个十分简单的事件循环,使得用户能够摆弄窗口并看到这些选项是如何影响应用程序的行为的。尝试各种选项,以及尝试在不同窗口管理器下运行程序,来观察在每个环境下的行为上的不同点。它将交给你一些关于X程序移植性的事情。 

Simple Window Operations 

简单窗口操作 

One more thing we can do to our windows is manipulate them on the screen - resize them, move them, raise or lower them, iconify them and so on. A set of window operations functions are supplied by Xlib for this purpose. 

我们能够对我们窗口作的另外一件事情是在屏幕上操纵它们-缩放,移动,升起来或者降下去,图标化等等。为了这个目的,Xlib提供一套窗口操作函数。 

Mapping And UN-mapping A Window 

映射和取消映射窗口 

The first pair of operations we can apply on a window is mapping it, or un-mapping it. Mapping a window causes the window to appear on the screen, as we have seen in our simple window program example. UN-mapping it causes it to be removed from the screen (although the window as a logical entity still exists). This gives the effect of making a window hidden (unmapped) and shown again (mapped). For example, if we have a dialog box window in our program, instead of creating it every time the user asks to open it, we can create the window once, in an unmapped mode, and when the user asks to open it, we simply map the window on the screen. When the user clicked the 'OK' or 'Cancel' button, we simply UN-map the window. This is much faster than creating and destroying the window, however, the cost is wasted resources, both on the client side, and on the X server side. 

我们能够应用于窗口的第一对操作是把它映射和取消映射。映射窗口导致它显示在屏幕上,如我们在我们的简单窗口程序例子中所见到的那样。取消映射导致它从屏幕上移出(虽然窗口作为一个逻辑实体仍然存在着)。这产生了隐藏窗口(取消映射)以及又显示出来(映射)的效果。例如,如果我们在程序中有一个对话框。我们不在每次用户要求打开它的时候都创建它,我们能只以未映射的模式创建窗口一次,而当用户要求打开它的时候,我们简单地把窗口映射到屏幕上。当用户点击'OK'或者'Cancel'按钮地时候,我们简单地把窗口取消映射。这个笔创建和销毁窗口要快许多,只是以浪费客户端和X服务器端的资源为代价。 

You will remember That the map operation can be done using the XMapWindow() operation. The UN-mapping operation can be done using the XUnmapWindow() operation. Here is how to apply them: 

你应记住映射操作能够使用XMapWindow()操作来完成。取消映射操作能够使用XUnmapWindow()操作来完成。这儿是如何应用它们: 

XMapWindow(display, win); 

XUnmapWindow(display, win); 

The mapping operation will cause an Expose event to be sent to our application, unless the window is completely covered by other windows. 

映射操作将导致爆肚事件发送给我们的程序,除非完全没有被其他窗口覆盖着。 

Moving A Window Around The Screen 

在屏幕上移动窗口 

Another operation we might want to do with windows is to move them to a different location. This can be done using the XMoveWindow() function. It will accept the new coordinates of the window, in the same fashion that XCreateSimpleWindow() got them when the window was created. The function is invoked like this: 

另外一个我们可能想要作用于窗口的操作是把他们移动到不同的位置。这个能够通过使用XMoveWindow()函数来完成。它和创建窗口时XCreateSimple()相同方式接受窗口的新坐标。这个函数像这样来调用: 

XMoveWindow(display, win, 400, 100); 

Note that when the window is moved, it might get partially exposed or partially hidden by other windows, and thus we might get Expose events due to this operation. 

注意当窗口被移动的时候,它可能会被部分暴露或者部分被其他窗口遮盖,因而我们可能因为这个操作的缘故和收到暴露事件。 

Resizing A Window 

缩放窗口 

Yet another operation we can do is to change the size of a window. This is done using the XResizeWindow() function: 

再有另外一个我们可那要作的操作时改变窗口的尺寸。这个使用XResizeWindow()函数完成: 

XResizeWindow(display, win, 200, 300); 

We can also combine the move and resize operations using the single XMoveResizeWindow() function: 

我们还能使用一个XMoveResizeWindow()函数来合并移动和缩放操作: 

XMoveResizeWindow(display, win, 20, 30, 100, 150); 

Changing Windows Stacking Order - Raise And Lower 

改变窗口的叠放顺序 - 提高或者降低 

Until now we changed properties of a single window. We'll see that there are properties that relate to the window and other windows. One of them is the stacking order. That is, the order in which the windows are layered on top of each other. the front-most window is said to be on the top of the stack, while the back-most window is at the bottom of the stack. Here is how we manipulate our windows stacking order: 

直到现在我们改变的只是单一窗口的属性。我们将看到还有相关到窗口和其他窗口之间的属性。它们中的一个就是叠放顺序。也就是,窗口被放置在各自之上的顺序。最前面的窗口被称为堆叠的顶部,而最后面的窗口是位于堆叠的底部。这儿是我们如何操纵我们窗口的叠放顺序: 

XRaiseWindow(display, win1); 

XLowerWindow(display, win1); 

Iconifying And De-Iconifying A Window 

窗口图标化和取消图标化 

One last operation we will show here is the ability to change a window into an iconified mode and vice versa. This is done using the XIconifyWindow() function - to iconify the window, and the XMapWindow() to de-iconify it. To understand why there is no inverse function for XIconifyWindow(), we must realize that when a window is iconified, what actually happens is that the window is being unmapped, and instead its icon window is being mapped. Thus, to make the original window reappear, we simply need to map it again. The icon is indeed another window that is simply related strongly to our normal window - it is not a different state of our window. Here is how to iconify a window and then de-iconify it: 

我们将在这儿展示的最后一个操作是把窗口变为图标化模式和反过来操作的能力。这个是使用XIconifyWindow()函数完成 - 来图标化窗口,以及XMapWindow()来取消图标化。为了弄清楚为什么对于XInconifyWindow()没有一个反转的函数,我们必须意识到当窗口图标化的时候,实际发生的是窗口被取消映射了,而作为替代窗口的图标被映射了。因而,为了使得原来的窗口重新显示,我们简单需要重又映射一遍。图标确实是另外一个不过严重与我们的普通窗口相关联的另外一个窗口-它不是我们窗口的另外一个状态。这儿是如何图标化一个窗口并且然后把它取消图标化: 

XIconifyWindow(display, win, DefaultScreen(display)); 

XMapWindow(display, win); 

Getting Info About A Window 

获得关于一个窗口的信息 

Just like we can set various attributes of our windows, we can also ask the X server supply the current values of these attributes. For example, we can check where a window is located on screen, what is its current size, whether it is mapped or not, etc. The XGetWindowAttributes() function may be used to get this information. Here is how it is used: 

正如你能够设置我们窗口的各种属性,我们也能要求X服务器提供这些属性的当前值。例如,我们能检查窗口位于屏幕上的什么位置,当前的大小是多少,是否已经映射或者没有,等等。XGetWindowAttributes()函数能用来得到这些信息。这儿是它如何使用的: 

XWindowAttributes win_attr; 

Status rc = XGetWindowAttributes(display, win, &win_attr); 

The XWindowAttributes structure contains many fields - here are some of them: 

XWindowAttributes结构包含许多域 - 这儿是它们中的一些: 

int x, y; 

     Location of the window, relative to its parent window. 

int width, height; 

     width and height of the window (in pixels). 

int border_width 

     width of the window's border. 

Window root; 

     handle of the root widow of the screen on which our window is displayed.  

One problem with this function, is that it returns the location of the window relative to its parent window. This makes these coordinates rather useless for any window manipulation functions (e.g. XMoveWindow). In order to overcome this problem, we need to take a two-step operation. First, we find out the ID of the parent window of our window. We then translate the above relative coordinates to screen coordinates. We use two new functions for this calculation, namely XQueryTree() and XTranslateCoordinates(). These two functions do more than we need, so we will concentrate on the relevant information only. 

这个函数的一个问题是它返回窗口相对于它的父窗口的位置。这使得这些坐标对于任何窗口操作函数相当的没有用处(例如:XMoveWindow)。为了克服这个问题,我们需要进行两步操作。首先,我们找出我们窗口的父窗口的ID。然后我们翻译上面的相对坐标为屏幕坐标。我们使用两个新的函数来完成这个计算,名字为XQueryTree()和XTranslateCoordinates()。这两个函数能够作比我们所需要的更多,因而我们只集中注意力于相关信息。 

int screen_x, screen_y; 

Window child_win; 

Window parent_win; 

Window root_win; 

Window* child_windows; 

int num_child_windows; 

XQueryTree(display, win, 

            &root_win, 

            &parent_win, 

            &child_windows, &num_child_windows); 

XFree(child_windows); 

XTranslateCoordinates(display, parent_win, root_win, 

                       win_attr.x, win_attr.y, &screen_x, &screen_y, 

                       &child_win); 

As you can see, Xlib sometimes makes us work hard for things that could have been much easier, if its interfaces and functions were a little more consistent. 

如你能看到的,Xlib有时使得我们努力于一些能够更容易完成的事情上,如果它的接口和函数能够更一致一些的话。 

As an example of how these operations all work, check out our window-operations.c program.  

作为这些操作一起工作的例子,看看我们的window-operations.c程序。 

Using Colors To Paint The Rainbow 

使用色彩来绘出彩虹 

Up until now, all our painting operations were done using black and white. We will (finally) see now how to draw using colors.  

直到现在,我们所有的绘画操作都是使用黑白色来完成。我们现在将(最终)看到如何使用彩色来绘画。 

Color Maps 

色表 

In the beginning, there were not enough colors. Screen controllers could only support a limited number of colors simultaneously (16 initially, and then 256). Because of this, an application could not just ask to draw in a "light purple-red" color, and expect that color to be available. Each application allocated the colors it needed, and when all 16 or 256 color entries were in use, the next color allocation would fail.  

起初,没有足够的色彩。屏幕控制器仅能同时支持有限数目的色彩(最初是16,然后是256)。因为这个,程序不能仅仅要求以“light-purple-red”色彩绘画,并且期望颜色是可用的。每个程序分配它需要的色彩,并且当所有16或者256色项都在使用中时,下一种颜色的分配将失败。 

Thus, the notion of "a color map" was introduced. A color map is a table whose size is the same as the number of simultaneous colors a given screen controller. Each entry contained the RGB (Red, Green and Blue) values of a different color (all colors can be drawn using some combination of red, green and blue). When an application wants to draw on the screen, it does not specify which color to use. Rather, it specifies which color entry of some color map to be used during this drawing. Change the value in this color map entry - and the drawing will use a different color.  

因而,“色表”的概念被引入了。色表时一个尺寸和给定屏幕控制器的同时发色数相同的表格。每个项包含不同颜色的RGB(红,绿,蓝)的值(使用红,绿蓝能够画出所有颜色)。当程序想要在屏幕上画画,它不指定要使用的颜色。而是,它指定在画画中要使用的某个色表中的表项。改变在这个色表中的项的值-然后绘画将使用不同的颜色。 

In order to be able to draw using colors that got something to do with what the programmer intended, color map allocation functions were supplied. You could ask to allocate a color map entry for a color with a set of RGB values. If one already existed, you'd get its index in the table. If none existed, and the table was not full, a new cell would be allocated to contain the given RGB values, and its index returned. If the table was full, the procedure would fail. You could then ask to get a color map entry with a color that is closest to the one you were asking for. This would mean that the actual drawing on the screen would be done using colors similar to what you wanted, but not the same.  

为了能够使用程序员中意的颜色来绘画,色表分配函数被提供了。你能够要求给一个RGB颜色集的颜色分配色表表项。如果它已经存在,你可能要获得它在表中的索引。如果不存在,而且表没有满,一个新的表项将分配来包含给定的RGB值,并返回它的索引。如果表格是满的,这个过程将失败。你然后可能要求获得一个最接近于你想要的色彩的色表表项。这也许意味着屏幕上的实际绘画可能使用类似于你想要,而不是相同的颜色。 

On today's more modern screens, where one runs an X server with support for 1 million colors, this limitation looks a little silly, but remember that there are still older computers with older graphics cards out there. Using color maps, support for these screens becomes transparent to you. On a display supporting 1 million colors, any color entry allocation request would succeed. On a display supporting a limited number of colors, some color allocation requests would return similar colors. It won't look as good, but your application would still work.  

在今天运行X服务器的更现代的屏幕上,支持百万种颜色,这个限制看上去有些好笑。但是记住总是有电脑用的是旧的图形卡。使用色表,对这些屏幕的支持对于你来说变得是透明得。在一个支持百万种颜色得显示器上,任何色彩分配请求都会成功。在一个支持有限颜色的显示器上,某些颜色分配请求将返回近似的颜色。它看上去不会那么好,但你的程序还能运行。 

Allocating And Freeing Color Maps 

分配和释放色表 

When you draw using Xlib, you can choose to use the standard color map of the screen your window is displayed on, or you can allocate a new color map and apply it to a window. In the latter case, each time the mouse moves onto your window, the screen color map will be replaced by your window's color map, and you'll see all the other windows on screen change their colors into something quite bizzar. In fact, this is the effect you get with X applications that use the "-install" command-line option.  

当你使用Xlib绘图的时候,你能够选择使用你的窗口将要显示在的屏幕的标准色表,你也可以分配一个新的色表并且应用于你的窗口。在后面的例子中,每次鼠标一如你的窗口,屏幕色表将被你窗口的色表给代替,而且你将看到窗口上的其他窗口把他们的颜色变得有一些混乱。实际上,这就是你使用"-install"命令行选项给X程序带来的效果 

In order to access the screen's standard color map, the DefaultColormap macro is defined:  

为了访问屏幕的标准色表,定义了DefaultColormap宏: 

Colormap screen_colormap = DefaultColormap(display, DefaultScreen(display)); 

this will return a handle for the color map used by default on the first screen (again, remember that an X server may support several different screens, each of which might have its own resources).  

这将返回第一个屏幕缺省使用的色表的句柄(再一次,记住X服务器可能支持好几个不同的屏幕,每一个都能有自己的资源)。 

The other option, that of allocating a new color map, works as follows:  

分配一个新色表的其他的选项工作起来如下: 

Visual* default_visual = DefaultVisual(display, DefaultScreen(display)); 

Colormap my_colormap = XCreateColormap(display, 

                                        win, 

                                        default_visual, 

                                        AllocNone); 

Note that the window parameter is only used to allow the X server to create the color map for the given screen. We can then use this color map for any window drawn on the same screen.  

注意window参数仅仅用于允许X服务器给给定屏幕创建色表。我们然后可以为在同一屏幕上的任意一个屏幕使用这个色表绘画。 

Allocating And Freeing A Color Entry 

分配和释放一个颜色 

Once we got access to some color map, we can start allocating colors. This is done using the XAllocNamedColor() and XAllocColor() functions. The first XAllocNamedColor() accepts a color name (e.g. "red", "blue", "brown" and so on) and allocates the the closest color that can be actually drawn on the screen. The XAllocColor() accepts an RGB color, and allocates the closest color that can be drawn on the screen. Both functions use the XColor structure, that has the following relevant fields:  

一旦我们得到了某个色表的访问权限,我们可以开始反配颜色了。这个是通过使用XAllocNamedColor()和XAllocColor()函数来完成的。第一个XAllocNamedColor()接受一个颜色名字(例如,“红色”,“蓝色”,“棕色”等)以及分配能够实际绘于屏幕上的最接近的颜色。XAllocColor()接受一个RGB色值,并且分配能够绘于屏幕上的最接近的颜色。两个函数都使用XColor结构,它有以下相关域: 

unsigned long pixel  

This is the index of the color map entry that can be used to draw in this color.  

unsigned short red  

the red part of the RGB value of the color.  

unsigned short green  

the green part of the RGB value of the color.  

unsigned short blue  

the blue part of the RGB value of the color.  

Here is an example of using these functions:  

-------------------------------------------------------------------------------- 

XColor system_color_1, system_color_2; 

XColor exact_color; 

Status rc = XAllocNamedColor(display, 

                              screen_colormap, 

                              "red", 

                              &system_color_1, 

                              &exact_color); 

if (rc == 0) { 

     fprintf(stderr, 

             "XAllocNamedColor - allocation of 'red' color failed.\n"); 

else { 

     printf("Color entry for 'red' - allocated as (%d,%d,%d) in RGB values.\n", 

            system_color_1.red, system_color_1.green, system_color_1.blue); 

system_color_2.red = 30000; 

system_color_2.green = 10000; 

system_color_2.blue = 0; 

Status rc = XAllocColor(display, 

                         screen_colormap, 

                         &system_color_2); 

if (rc == 0) { 

     fprintf(stderr, 

             "XAllocColor - allocation of (30000,10000,0) color failed.\n"); 

else { 

     . 

     . 

-------------------------------------------------------------------------------- 

-------------------------------------------------------------------------------- 

Drawing With A Color 

使用颜色绘图 

After we have allocated the desired colors, we can use them when drawing text or graphics. To do that, we need to set these colors as the foreground and background colors of some GC (Graphics Context), and then use this GC to make the drawing. This is done using the XSetForeground() and XSetBackground() functions, as follows:  

在我们已经分配了所需的颜色后,我们能在绘出文本和图形的过程中使用他们。要这么作,我们需要设置这些颜色为某GC(图形上下文)的前景色和背景色,然后使用这个GC进行绘图。整个过程使用XSetForeground()和XSetBackground()函数,如下: 

XSetForeground(display, my_gc, screen_color_1.pixel); 

XSetForeground(display, my_gc, screen_color_2.pixel); 

As you see, this is rather simple. The actual drawing is done using the same functions we have seen earlier. Note that in order to draw using many different colors, we can do one of two things. We can either change the foreground and/or background colors of the GC before any drawing function, or we can use several different GCs to draw in different colors. The decision as of which option to use is yours. Note that allocating many GCs will use more resources of the X server, but this will sometime lead to more compact code, and will might it easier to replace the drawn colors.  

如你所见,这个相当简单。实际的绘画使用我们已经在前面看过的函数来完成。注意为了使用许多不同的颜色来绘图,我们能采取这两个办法中的一个。我们要么在调用任何绘图函数之前改变GC的前景以及/或者背景色,或者我们能使用几个不同的GC以不同的颜色绘画。最终采用那种办法是你各人选择的事情。注意分配虚度GC将使用X服务器更多的资源,但有时能使得代码更紧凑,而且替换画出的颜色要容易些。 

As an example to drawing using colors, look at the color-drawing.c program. This is a copy of the simple-drawing.c program, except that here we also allocate colors and use them to paint the rainbow...  

作为一个使用颜色绘图的例子,看看color-drawing.c程序。这是simple-drawing.c程序的复制,除去我们还分配了颜色,并用它们来画彩虹……。 

X Bitmaps And Pixmaps 

X Bitmap 和 Pixmap 

One thing many so-called "Multi-Media" applications need to do, is display images. In the X world, this is done using bitmaps and pixmaps. We have already seen some usage of them when setting an icon for our application. Lets study them further, and see how to draw these images inside a window, along side the simple graphics and text we have seen so far.  

许多所谓的“多媒体”程序需要做的一件事情是显示图片。在X世界中,这个是通过使用bitmap和pixmap来完成的。我门已经在给我们程序设置图标时看到了他们的一些使用。让我们进一步学习他们,来看看如何在窗口中与我们已经看过了的简单图形和文本一起,画出这些图像。 

One thing to note before delving further, is that Xlib supplies no means of manipulating popular image formats, such as gif, jpeg or tiff. It is left up to the programmer (or to higher level graphics libraries) to translate these image formats into formats that the X server is familiar with - x bitmaps, and x pixmaps.  

在进行更深的钻研之前要注意一件事情是Xlib没有提供一种操作流行图像格式的办法,如gif,jpeg或者tiff。这些是留给程序员(或者是更高层的图形库)来翻译这些图像格式为X服务器熟悉的格式 - x bitmap与x pixmap。 

What Is An X Bitmap? An X Pixmap? 

什么是X Bitmap?X Pixmap? 

An X bitmap is a two-color image stored in a format specific to the X window system. When stored in a file, the bitmap data looks like a C source file. It contains variables defining the width and height of the bitmap, an array containing the bit values of the bitmap (the size of the array = width * height), and an optional hot-spot location (will be explained later, when discussing mouse cursors).  

X bitmap是以X window系统规定的格式存储的双色图像。当存为文件时,bitmap数据看上去和C源代码一样。它包含定义bitmap宽和高的变量,一个包含bitmap的各个位的值的数组(数组的尺寸=width*height),以及可选的热区位置(将在后面讨论鼠标光标时解释)。 

A X pixmap is a format used to store images in the memory of an X server. This format can store both black and white images (such as x bitmaps) as well as color images. It is the only image format supported by the X protocol, and any image to be drawn on screen, should be first translated into this format.  

X pixmap时用来在X服务器内存中储存图像的格式。该格式既能够储存黑白图像(和X bitmap一样)也可以时彩色图像。这个时X协议唯一支持的图像格式,而且显示于屏幕上的图像首先也要翻译为这种格式。 

In actuality, an X pixmap can be thought of as a window that does not appear on the screen. Many graphics operations that work on windows, will also work on pixmaps - just supply the pixmap ID instead of a window ID. In fact, if you check the manual pages, you will see that all these functions accept a 'Drawable', not a 'Window'. since both windows and pixmaps are drawables, they both can be used to "draw on" using functions such as XDrawArc(), XDrawText(), etc.  

实际上,X pixmap能够认为是一个不显示在屏幕上的窗口。许多工作于窗口之上的图形操作,也能够工作于pixmap之上 - 仅仅以pixmap ID替代window ID来提供。事实上,如果你查阅手册,你能看到所有这些函数结构一个“可画”对象,不是一个“窗口”。窗口和pixmap都是可画的,因为他们都能用像XDrawArc()函数来“在其上做画”。 

Loading A Bitmap From A File 

从文件载入Bitmap 

We have already seen how to load a bitmap from a file to memory, when we demonstrated setting an icon for an application. The method we showed earlier required the inclusion of the bitmap file in our program, using the C pre-processor '#include' directive. We will see here how we can access the file directly.  

在我们演示给程序设置图标时,我们已经看到如何从文件载入bitmap到内存中。我们前面展示的方法需要在我们的程序中使用C预处理器的“#include”指示符包含bitmap文件。我们将在这儿看到如何直接访问文件。 

Pixmap bitmap; 

unsigned int bitmap_width, bitmap_height; 

int hotspot_x, hotspot_y; 

Window root_win = DefaultRootWindow(display); 

int rc = XReadBitmapFile(display, root_win, 

                          "icon.bmp", 

                          &bitmap_width, &bitmap_height, 

                          &bitmap, 

                          &hotspot_x, &hotspot_y); 

switch (rc) { 

     case BitmapOpenFailed: 

         fprintf(stderr, "XReadBitmapFile - could not open file 'icon.bmp'.\n"); 

         break; 

     case BitmapFileInvalid: 

         fprintf(stderr, 

                 "XReadBitmapFile - file '%s' doesn't contain a valid bitmap.\n", 

                 "icon.bmp"); 

         break; 

     case BitmapNoMemory: 

         fprintf(stderr, "XReadBitmapFile - not enough memory.\n"); 

         break; 

     case BitmapSuccess: 

         . 

         . 

         break; 

Note that the 'root_win' parameter has nothing to do with the given bitmap - the bitmap is not associated with this window. This window handle is used just to specify the screen that we want the pixmap to be created for. This is important, as the pixmap must support the same number of colors as the screen does, in order to make it useful.  

注意'root_win'参数对于给定bitmap没有影响 - bitmap没有关联到这个窗口。这个窗口句柄仅仅用于指定我们想要bitmap创建到的窗口。这很重要,因为pixmap为了使得它有用,必须支持屏幕一样多的色彩数。 

Drawing A Bitmap In A Window 

在窗口中画出Bitmap 

Once we got a handle to the pixmap generated from a bitmap, we can draw it on some window, using the XCopyPlane() function. This function allows us to specify what drawable (a window, or even another pixmap) to draw the given pixmap onto, and at what location in that drawable.  

一旦我们得到一个从bitmap产生的pixmap的句柄,我们能够使用XCopyPlane(),在某个窗口中把它画出来。这个函数允许我们指定可画的物体(一个窗口,甚至是另外一个pixmap)来把给定的pixmap画到上面去,以及指定画出的位置。 

。 

XCopyPlane(display, bitmap, win, gc, 

           0, 0, 

           bitmap_width, bitmap_height, 

           100, 50, 

           1); 

As you can see, we could also copy a given rectangle of the pixmap, instead of the whole pixmap. Also note the last parameter to the XCopyPlane function (the '1' at the end). This parameter specifies which plane of the source image we want to copy to the target window. For bitmaps, we always copy plane number 1. This will become clearer when we discuss color depths below.  

如你所能看到的,我们还能拷贝pixmap个给定矩形内的内容,而不是整个pixmap。还要注意传递给XCopyPlane函数额最后一个参数(最后的'1')。该参数指定了我们想要拷贝到目标窗口的源图像是什么plane的。对于bitmap,我们总是拷贝第一号plane。这在我们下面 讨论色深的时候会变得明白一点。 

Creating A Pixmap 

创建Pixmap 

Sometimes we want to create an un-initialized pixmap, so we can later draw into it. This is useful for image drawing programs (creating a new empty canvas will cause the creation of a new pixmap on which the drawing can be stored). It is also useful when reading various image formats - we load the image data into memory, cerate a pixmap on the server, and then draw the decoded image data onto that pixmap.  

有时我们想要创建一个未初始化的pixmap,然后我们能在它上面画图。这对于作图程序来说有用(创建一个新的空画布将导致一个新的将要在其上储存绘画内容的pixmap的创建)。在读取各种图像格式的时候也有用处 - 我们把图像数据载入到内存中,在服务器上创建pixmap,然后把图像数据解码之后画到pixmap上。 

Pixmap pixmap; 

Window root_win = DefaultRootWindow(display); 

int depth = DefaultDepth(display, DefaultScreen(display)); 

pixmap = XCreatePixmap(display, root_win, 30, 40, depth); 

XDrawPoint(display, pixmap, gc, 15, 20); 

Drawing A Pixmap In A Window 

在窗口中画出Pixmap 

Once we got a handle to pixmap, we can draw it on some window, using the XCopyArea() function. This function allows us to specify what drawable (a window, or even another pixmap) to draw the given pixmap onto, and at what location in that drawable.  

一旦我们获得了pixmap的句柄,我们能在某个窗口上用XCopyArea()函数把它画出来。这个函数允许我们指定给定pixmap要画到的是那个可画对象(窗口,甚至是另一个pixmap),以及画出的位置。 

XCopyArea(display, bitmap, win, gc, 

           0, 0, 

           bitmap_width, bitmap_height, 

           100, 50); 

As you can see, we could also copy a given rectangle of the pixmap, instead of the whole pixmap.  

如你能看到的,我们也能拷贝pixmap的给定区域,而不是整个pixmap。 

One important note should be made - it is possible to create pixmaps of different depths on the same screen. When we perform copy operations (a pixmap onto a window, etc), we should make sure that both source and target have the same depth. If they have a different depth, the operation would fail. The exception to this is if we copy a specific bit plane of the source pixmap, using the XCopyPlane() function shown earlier. In such an event, we can copy a specific plain to the target window - in actuality, setting a specific bit in the color of each pixel copied. This can be used to generate strange graphic effects in window, but is beyond the scope of out tutorial.  

一点重要的值得注意的是 - 能够在同一窗口上创建不同色深的pixmap。当我们执行拷贝操作的时候(把pixmap拷贝到窗口,等),我们应当确保源和目标有相同的色深。如果他们色深不同,操作将失败。例外是如果我们使用前面展示的XCopyPlane()函数,拷贝源pixmap的指定bit plane。在这种情况下,我们能拷贝指定plain到目标窗口上 - 实际上,设置一个指定位位每个拷贝象素的颜色。这能用于在窗口中产生奇异的图形效果,但是超过了我们教程的范围。 

Freeing A Pixmap 

释放Pixmap 

Finally, when we are done using a given pixmap, we should free it, in order to free resources of the X server. This is done using the XFreePixmap() function:  

最后,当我们用完给定pixmap,为了释放X服务器的资源,我们应当释放它。这通过使用XFreePixmap()函数来完成。 

XFreePixmap(display, pixmap); 

After freeing a pixmap - we must not try accessing it again.  

在释放pixmap之后 - 我们不能再次访问了。 

To summarize this section, take a look at the draw-pixmap.c program, to see a pixmap being created using a bitmap file, and then tiled up on a window on your screen.  

总结本节,看看draw-pixmap.c程序,看看使用bitmap文件创建出一个pixmap,并且然后再屏幕上显示于窗口中。 

Messing With The Mouse Cursor 

改变鼠标光标 

Often we see programs that modify the shape of the mouse pointer (also called the X pointer) when in certain states. For example, a busy application would often display a sand clock over its main window, to give the user a visual hint that they should wait. Without such a visual hint, the user might think that the application got stuck. Lets see how we can change the mouse cursor for our windows.  

我们时常看到程序在某个状态下改变鼠标指针(也称为X指针)的外形。例如,一个繁忙中的程序时常在它的主窗口上显示沙漏时钟,以给用户一个你应当等待的可视化提示。没有这么一个视觉提示,用户可能认为程序死掉了。让我们看看我们如何改变我们窗口的鼠标光标。 

Creating And Destroying A Mouse Cursor 

创建和销毁鼠标光标 

There are two methods for creating cursors. One of them is by using a set of pre-defined cursors, that are supplied by Xlib. The other is by using user-supplied bitmaps.  

有两种创建光标的办法。其中的一种是使用一套预先定义好了的光标,它们是由Xlib提供的。另一个是使用用户提供的bitmap。 

In the first method, we use a special font named "cursor", and the function XCreateFontCursor(). This function accepts a shape identifier, and returns a handle to the generated cursor. The list of allowed font identifiers is found in the include file <X11/cursorfont.h>. Here are a few such cursors:  

在第一种方法中,我们使用一个名为“光标”的特殊字体,和XCreateFontCursor()函数。这个函数接受一个外形标识符,并且返回产生的光标的句柄。能用的字体标识符的列表可以在<X11/cursorfont.h>中找到。这儿是一些这样的光标: 

XC_arrow  

The normal pointing-arrow cursor displayed by the server.  

XC_pencil  

A cursor shaped as a pencil.  

XC_watch  

A sand watch.  

And creating a cursor using these symbols is very easy:  

#include <X11/cursorfont.h>     

Cursor watch_cursor; 

watch_cursor = XCreateFontCursor(display, XC_watch); 

The other methods of creating a cursor is by using a pair of pixmaps with depth of one (that is, two color pixmaps). One pixmap defines the shape of the cursor, while the other works as a mask, specifying which pixels of the cursor will be actually drawn. The rest of the pixels will be transparent. Creating such a cursor is done using the XCreatePixmapCursor() function. As an example, we will create a cursor using the "icon.bmp" bitmap. We will assume that it was already loaded into memory, and turned into a pixmap, and its handle is stored in the 'bitmap' variable. We will want it to be fully transparent. That is, only the parts of it that are black will be drawn, while the white parts will be transparent. To achieve this effect, we will use the icon both as the cursor pixmap and as the mask pixmap. Try to figure out why...  

另一个创建光标的方法是使用一对色深为1的pixmap(也就是,双色的pixmap)。一个pixmap定义了光标的外形,另一个作为一个遮罩,指定了光标的哪些象素是要画出来的。剩下的象素将是透明的。创建这样的光标是使用XCreatePixmapCursor()函数来实现的。作为一个例子,我们将使用"icon.bmp"bitmap来创建光标。我们将假定它已经载入了内存,并且转换为pixmap了,而且它的句柄储存在'bitmap'变量中。我们想它全部是透明的。也就是,只有黑色的部分将被画出来,而白色的部分将是透明的。为了达到这个效果,我们是同时把图标用作光标的pixmap和遮罩pixmap。试着想想看为什么…… 

Cursor icon_cursor; 

XColor cursor_fg, cursor_bg; 

Colormap screen_colormap = DefaultColormap(display, DefaultScreen(display)); 

Status rc = XAllocNamedColor(display, 

                              screen_colormap, 

                              "black", 

                              &cursor_fg, 

                              &cursor_fg); 

if (rc == 0) { 

     fprintf(stderr, "XAllocNamedColor - cannot allocate 'black' ??!!??\n"); 

     exit(1); 

Status rc = XAllocNamedColor(display, 

                              screen_colormap, 

                              "white", 

                              &cursor_bg, 

                              &cursor_bg); 

if (rc == 0) { 

     fprintf(stderr, "XAllocNamedColor - cannot allocate 'white' ??!!??\n"); 

     exit(1); 

icon_cursor = XCreatePixmapCursor(display, bitmap, bitmap, 

                                   &cursor_fg, &cursor_bg, 

                                   5, 4); 

One thing to be explained is the 'hot spot' parameters. When we define a cursor, we need to define which pixel of the cursor is the pointer delivered to the user in the various mouse events. Usually, we will choose a location of the cursor that visually looks like a hot spot. For example, in an arrow cursor, the tip of the arrow will be defined as the hot spot.  

这儿要解释的一件事情是'热区'参数。当我们定义了光标,我们需要定义光标的那个象素是投递给用户的各种鼠标事件的指针位置。通常,我们将选择通常看来像热区的光标位置。例如,在一个箭头状的光标中,箭的尖端将被定义为热区。 

Finally, when we are done with a cursor and no longer need it, we can release it using the XFreeCursor() function:  

最后,当我们用完了光标不再需要它了,我们能够使用XFreeCursor()函数释放它: 

XFreeCursor(display, icon_cursor); 

Setting A Window's Mouse Cursor 

设置窗口的鼠标光标 

After we have created a cursor, we can tell the X server to attach this cursor to any of our windows. This is done using the XDefineCursor(), and causes the X server to change the mouse pointer to the shape of that cursor, each time the mouse pointer moves into and across that window. We can later detach this cursor from our window using the XUndefineCursor() function. This will cause the default cursor to be shown when the mouse enter that windows.  

在我们创建了光标之后,我们能告诉X服务器把这个光标附着在我们任意一个窗口上。这是通过使用XDefineCursor()来完成的,并且导致X服务器在鼠标每次移入或者通过那个窗口时,把鼠标指针改变为那个光标的样子。我们然后用XUndefineCursor()函数把光标和我们的窗口解开。这个将导致当进入能够窗口时,缺省的鼠标显示出来。 

XDefineCursor(display, win, icon_cursor); 

XUndefineCursor(display, win); 

As an example, look at our cursor.c program, and see how mouse cursors are set, changed and removed. Run the program, place the mouse pointer over the created window, and watch.  

作为一个例子,看看我们的cursor.c程序,并且看看鼠标光标是如何被设置,改变和移除的。运行这个程序,把鼠标指针放在创建的窗口之上,观察。