天天看點

用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程式,并且看看滑鼠光标是如何被設定,改變和移除的。運作這個程式,把滑鼠指針放在建立的視窗之上,觀察。