用Xlib库进行基本图形编程
用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> /* defines common Xlib functions and structs. */
.
.
/* this variable will contain the pointer to the Display structure */
/* returned when opening a connection. */
Display* display;
/* open the connection to the display "simey:0". */
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()的调用返回的。
/* this variable will be used to store the "default" screen of the */
/* X server. usually an X server has only one screen, so we're only */
/* interested in that screen. */
int screen_num;
/* these variables will store the size of the screen, in pixels. */
int screen_width;
int screen_height;
/* this variable will be used to store the ID of the root window of our */
/* screen. Each screen always has a root window that covers the whole */
/* screen, and always exists. */
Window root_window;
/* these variables will be used to store the IDs of the black and white */
/* colors of the given screen. More on this will be explained later. */
unsigned long white_pixel;
unsigned long black_pixel;
/* check the number of the default screen for our X server. */
screen_num = DefaultScreen(display);
/* find the width of the default screen of our X server, in pixels. */
screen_width = DisplayWidth(display, screen_num);
/* find the height of the default screen of our X server, in pixels. */
screen_height = DisplayHeight(display, screen_num);
/* find the ID of the root window of the screen. */
root_window = RootWindow(display, screen_num);
/* find the value of a white pixel on this screen. */
white_pixel = WhitePixel(display, screen_num);
/* find the value of a black pixel on this screen. */
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象素。窗口将会放置在屏幕的左上角。
/* this variable will store the ID of the newly created window. */
Window win;
/* these variables will store the window's width and height. */
int win_width;
int win_height;
/* these variables will store the window's location. */
int win_x;
int win_y;
/* calculate the window's width and height. */
win_width = DisplayWidth(display, screen_num) / 3;
win_height = DisplayHeight(display, screen_num) / 3;
/* position of the window is top-left corner - 0,0. */
win_x = win_y = 0;
/* create the window, as specified earlier. */
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):
/* this variable will contain the handle to the returned graphics context. */
GC gc;
/* these variables are used to specify various attributes for the GC. */
/* initial values for the GC. */
XGCValues values = CapButt | JoinBevel;
/* which values in 'values' to check when creating the GC. */
unsigned long valuemask = GCCapStyle | GCJoinStyle;
/* create a new graphical context. */
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:
一旦我们创建了一个图形上下文,我们能够在绘图函数中使用它。我们还能够各种函数修改它的参数。这
儿有几个例子:
/* change the foreground color of this GC to white. */
XSetForeground(display, gc, WhitePixel(display, screen_num));
/* change the background color of this GC to black. */
XSetBackground(display, gc, BlackPixel(display, screen_num));
/* change the fill style of this GC to 'solid'. */
XSetFillStyle(display, gc, FillSolid);
/* change the line drawing attributes of this GC to the given values. */
/* the parameters are: Display structure, GC, line width (in pixels), */
/* line drawing style, cap (line's end) drawing style, and lines */
/* join style. */
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’包含了先前创建的窗口的句柄。
/* draw a pixel at position '5,60' (line 5, column 60) of the given window. */
XDrawPoint(display, win, gc, 5, 5);
/* draw a line between point '20,20' and point '40,100' of the window. */
XDrawLine(display, win, gc, 20, 20, 40, 100);
/* draw an arc whose center is at position 'x,y', its width (if it was a */
/* full ellipse) is 'w', and height is 'h'. Start the arc at angle 'angle1' */
/* (angle 0 is the hour '3' on a clock, and positive numbers go */
/* counter-clockwise. the angles are in units of 1/64 of a degree (so 360*64 */
/* is 360 degrees). */
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);
/* now use the XDrawArc() function to draw a circle whose diameter */
/* is 15 pixels, and whose center is at location '50,100'. */
XDrawArc(display, win, gc, 50-(15/2), 100-(15/2), 15, 15, 0, 360*64);
/* the XDrawLines() function draws a set of consecutive lines, whose */
/* edges are given in an array of XPoint structures. */
/* The following block will draw a triangle. We use a block here, since */
/* the C language allows defining new variables only in the beginning of */
/* a block. */
{
/* this array contains the pixels to be used as the line's end-points. */
XPoint points[] = {
{0, 0},
{15, 15},
{0, 15},
{0, 0}
};
/* and this is the number of pixels in the array. The number of drawn */
/* lines will be 'npoints - 1'. */
int npoints = sizeof(points)/sizeof(XPoint);
/* draw a small triangle at the top-left corner of the window. */
/* the triangle is made of a set of consecutive lines, whose */
/* end-point pixels are specified in the 'points' array. */
XDrawLines(display, win, gc, points, npoints, CoordModeOrigin);
}
/* draw a rectangle whose top-left corner is at '120,150', its width is */
/* 50 pixels, and height is 60 pixels. */
XDrawRectangle(display, win, gc, 120, 150, 50, 60);
/* draw a filled rectangle of the same size as above, to the left of the */
/* previous rectangle. note that this rectangle is one pixel smaller than */
/* the previous line, since 'XFillRectangle()' assumes it is filling up */
/* an already drawn rectangle. This may be used to draw a rectangle using */
/* one color, and later to fill it using another color. */
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:
在我们为感兴趣的事件型别注册了之后,我们需要进入接收事件和处理它们的循环。有好几种撰写这样的
循环的办法,但基本的循环是这样的:
/* this structure will contain the event's data, once received. */
XEvent an_event;
/* enter an "endless" loop of handling events. */
while (1) {
XNextEvent(display, &an_event);
switch (an_event.type) {
case Expose:
/* handle this event type... */
.
.
break;
default: /* unknown event type - ignore it. */
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 we have several other expose events waiting, don't redraw. */
/* we will do the redrawing when we receive the last of them. */
if (an_event.xexpose.count > 0)
break;
/* ok, now draw the line... */
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:
/* store the mouse button coordinates in 'int' variables. */
/* also store the ID of the window on which the mouse was */
/* pressed. */
x = an_event.xbutton.x;
y = an_event.xbutton.y;
the_win = an_event.xbutton.window;
/* check which mouse button was pressed, and act accordingly. */
switch (an_event.xbutton.button) {
case Button1:
/* draw a pixel at the mouse position. */
XDrawPoint(display, the_win, gc_draw, x, y);
break;
case Button2:
/* erase a pixel at the mouse position. */
XDrawPoint(display, the_win, gc_erase, x, y);
break;
default: /* probably 3rd button - just ignore this event. */
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:
/* store the mouse button coordinates in 'int' variables. */
/* also store the ID of the window on which the mouse was */
/* pressed. */
x = an_event.xmotion.x;
y = an_event.xmotion.y;
the_win = an_event.xbutton.window;
/* if the 1st mouse button was held during this event, draw a pixel */
/* at the mouse pointer location. */
if (an_event.xmotion.state & Button1Mask) {
/* draw a pixel at the mouse position. */
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’来作这个参数)。标准Xl