天天看點

運用xlib進行事件響應(X11 API)的小例子

轉自:http://blog.csdn.net/linuxheik/article/details/7659090

File: x11_test.cxx

#include <X11/Xlib.h>
每一個Xlib 程式都必須包含這個頭檔案
#include <stdio.h>

 int main(void) {

 Display *display = XopenDisplay(NULL);

首先打開與server 的連接配接。在你的程式可以使用display 之前,必須先建立一個和X server 的連接配接。這個連接配接建立以後,就可以使用Xlib 的函數或宏定義來獲得display 的資訊了。

參數為hardware display name,當設為NULL時,為預設的DISPLAY環境變量。這個函數傳回一個指向Display類型的結構的指針,表明已與X server建立了連接配接,并且包含了這個X serer的所有資訊。

這樣我們就可以使用display之上的所有視窗了。



 int screen = DefaultScreen(display);

得到display打開之後的視窗号。本例中,得到。我了解是:尚未建立視窗,是以視窗号為。不知道對不對。

DefaultScreen是個宏,對應的函數為XDefaultScreen。兩者作用相同。



 int width = DisplayWidth(display, screen)/;

 int height = DisplayHeight(display, screen)/;

函數DisplayWidth得到視窗的寬,DisplayHeight得到視窗的高。視窗雖然沒有建立,但是有關視窗的預設資訊在display打開之時就已經從X server處獲得了。本例,獲得預設的尺寸為*,就是顯示器的尺寸。



 int black_pixel = BlackPixel(display, screen);

 int white_pixel = WhitePixel(display, screen);

X使用一種很複雜的顔色模型,每個顔色用一個整數表示。但是當機器不同,甚至程式不同時,一個整數不一定就代表固定的某個顔色。X能夠保證的顔色隻有兩個:黑和白。用 BlackPixel和 WhitePixel可以得到這兩個顔色的值。



 Window win = XCreateSimpleWindow(display, RootWindow(display, screen), , , width, height, , black_pixel, white_pixel);

建立視窗的最普遍的函數是XCreateWindow和XCreateSimpleWindow(我目前還沒有研究兩者具體差别)。

Window XcreateSimpleWindow(

Display *display,

Window parent,

int x,

int y,

unsigned int width,

unsigned int height,

unsigned int border_width,

unsigned long border,

unsigned long background

);

其中, RootWindow函數産生的是我們建立的視窗的父視窗(我還不太明白到底是哪個);x, y為建立的視窗的起始坐标;width, height為視窗的尺寸;border_width為視窗邊框的像素數;border為邊框的顔色;background為視窗的背景色。

函數傳回建立的視窗的ID,并使得X server産生一個CreateNotify事件。



 XSelectInput(display, win, ExposureMask|StructureNotifyMask);

我們知道,X是一個伺服器-用戶端的結構。由伺服器向用戶端發送事件資訊,讓用戶端知道發生了什麼事情,然後用戶端告訴伺服器它感興趣的是什麼事情,也就是說,用戶端會對那些事件産生反應。用XSelectInput這個函數,就是告訴伺服器,這個視窗會對那些消息,也就是事件有響應。這裡講的“事件”,比如又建立,畫,改變大小等等。

XselectInput(

Display *display,

Window w,

long event_mask

);

其中,w為希望對事件作出響應的視窗;event_mask為事件号。希望視窗會有那些響應,就在參數中加上這個事件對應的名字。



 XMapWindow(display, win);

用XCreateSimpleWindow建立視窗之後,視窗并不能顯示出來,需要調用這個函數來畫視窗讓它顯示。如果這個視窗有父視窗,那麼在所有父視窗沒有畫出來之前,這個視窗即使用了這個函數,也是不能顯示出來的。必須等所有父視窗都顯示了,這個視窗才能畫。(不太明白這一步)

(沒看明白在什麼情況下)X server産生一個MapNotify事件。



 GC gc = XCreateGC(display, win, , NULL);

X并不能記住要畫的視窗的屬性,那麼每次要畫視窗時都要把它的全部屬性傳給server,為了避免每次都傳太多參數,X使用了一個結構Graphics Context,簡稱GC,存儲圖形操作的大部分屬性,比如線的寬度,風格,背景色等等。



 While()

 {

 XEvent event = {};

 XNextEvent(display, &event);

 }

接下來就是進入事件循環了。獲得事件,處理或丢棄。上面講到,調用XMapWindow函數後,X server會發出一個MapNotify事件給用戶端,這時用戶端就已經有相應操作了,就是畫視窗,是以事件循環裡并沒有寫出來。



 Return ;

 }

至此,整個程式結束。



編譯:g++ -lX11 -o x11_test x11_test.cxx

運作,螢幕上就顯示一個最簡單的視窗了。



如果我們想看看MapNotify事件到底是怎麼回事,就這樣寫:

while()

{

XEvent event;

XNextEvent(display, &event);

if(event.type == MapNotify) break;

}

//sleep(3);

return ;

運作發現,視窗一閃而過。也就是說,用戶端一接收到這個事件就顯示視窗,一顯示視窗就break了。把注釋拿掉,則顯示視窗秒鐘後才消失。

這裡看到,接收到的事件由XNextEvent函數從消息隊列裡獲得,把事件放到event.type裡并從隊列裡删除該消息。當隊列為空也就是沒有下一個事件被接收時,XNextEvent“flushes the output buffer”,也就是視窗最終被顯示,并且程式就一直停留在XNextEvent裡直到有下一個事件,除非有跳出循環的語句。



繼續改程式:

while()

{

XEvent event;

XNextEvent(display, &event);

if(event.type == MapNotify) break;

}

XDrawLine(display, win, gc, , , , );

//XFlush(display);

sleep();

return ;

跳出循環後,用XDrawLine畫一條線。這時運作程式,視窗上并沒有出現直線。因為視窗的資訊改變了,就需要XFlush函數來flush一下,讓視窗重畫。而之前之是以沒有XFlush函數,是因為XNextEvent函數隐式地調用XFlush了,而且調用後并沒有改變視窗資訊。



繼續改,添加事件:

XSelectInput(display, win, ExposureMask|KeyPressMask|StructureNotifyMask);

while()

{

XEvent event = {};

XNextEvent(display, &event);

switch(event.type)

{

case ConfigureNotify:

{

width = event.xconfigure.width;

height = event.xconfigure.height;

break;

}

case Expose:

{

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

XFillRectangle(display, win, gc, , , width, height);

XSetForeground(display, gc, BlackPixel(display, screen));

XDrawString(display, win, gc, width/, height/, "XWindow", );

break;

}

case KeyPress:

{

if(event.xkey.keycode == XKeysymToKeycode(display, XK_Escape))

{

XFreeGC(display, gc);

XCloseDisplay(display);

return ;

}

}

default: break;

}

}

return ;

事件類型StructureNotifyMask對應事件ConfigureNotify,即改變視窗狀态,比如尺寸,位置等;ExposureMask對應事件Expose(我不知道怎麼解釋這個事件),本例中是在這裡設定視窗顔色,畫字元串并設定字元串顔色;KeyPressMask對應KeyPress,即鍵盤響應,本例是當Escape鍵按下時,退出視窗。退出視窗時,需要釋放或者說銷毀GC,最開始曾打開display,在這裡需要關閉和X server的連接配接,于是也就銷毀了相關資源,關閉了視窗。

在這種循環的寫法中,退出switch,并沒有退出整個循環,XNextEvent仍然在顯示視窗,是以不需要我們自己調用XFlush。
           

有這樣一個網址:http://tronche.com/gui/x/xlib-tutorial/

繼續閱讀