窗口们的组织体系
当窗口们被显示在X服务器上时,它们通常按照一定组织体系来排序 - 每个窗口可以有子窗口,每个子窗口又可以有自己的子窗口。让我们来查看这个组织体系的一些特性,看看它们是如何来影响例如绘画和事件等处理。
根窗口,父窗口和子窗口
每一个屏幕上都有一个根窗口。根窗口总是占据整个屏幕尺寸。这个窗口无法被销毁,改变尺寸或者图标化。当一个应用程序创建了一些窗口,它先创建至少一个顶层窗口。在被映射到屏幕上后,这个窗口成为一个根窗口的直接子窗口。这个窗口在被映射到屏幕上之前,窗口管理器被告知什么发生了,然后,窗口管理器获得特权成为新顶层窗口的"父亲"。这通常被用来增加一个会包含新窗口的窗口和绘制框架,标题栏,系统菜单等。
一旦一个顶层窗口(当然它实际上不是一个顶层窗口,因为窗口管理器已经成为它的父窗口了)被创建了,应用程序可以在它里面创建它的子窗口。一个子窗口只能在它的父窗口里显示 - 如果试图把它移动到外面,出去的部分将被父窗口的边框给切掉。任何窗口都可以包含一个以上的子窗口,在这种情况下,这些子窗口将被放置在应用的内部栈上。当一个顶层窗口被打开 - 它的所有子窗口也将随着它被打开。
以下例子演示如何在一个给定的叫"win"的窗口里打开一个子窗口。
Lets see how to create a child window inside a given window 'win'.
/* this variable will store the handle of the newly created child window. */
Window child_win;
/* these variables will store the window's width and height. */
int child_win_width = 50;
int child_win_height = 30;
/* these variables will store the window's location. */
/* position of the child window is top-left corner of the */
/* parent window. - 0,0. */
int child_win_x = 0;
int child_win_y = 0;
/* create the window, as specified earlier. */
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));
事件传递
先前我们已经讨论了事件传递 - 如果一个窗口收到了一个它不处理的事件 - 它就把该事件发到它的父窗口去。如果那个父窗口也不处理该事件 - 那个父窗口就把该事件发到它的父窗口上去,接下来依此类推。这种行为对一个简单的Xlib程序是没什么用的,但对于抽象级更高的绘图库是有用的。这些抽象级更高的绘图库通常把某个特定窗口的事件联系到一个函数上去。在这种情况下,发送事件到特定的窗口并用适当的函数来处理就非常有用。
与窗口管理器进行交互
在我们察看了如何创建和绘制窗口之后,我们回过头来看一下我们的窗口是如何与它们的环境 - 整个屏幕和其它窗口进行交互的。首先,我们的程序需要与窗口管理器进行交互。窗口管理器有责任装饰被绘制的窗口(例如增加框架,一个图标化的按钮,一个系统菜单,一个标题栏),同时在窗口被图标化时绘制图标。它还管理屏幕里的窗口排列顺序以及其它可管理的任务。我们需要给它各种提示以让它以我们需要的方式来对待我们的窗口。
窗口属性
许多与窗口管理器交流的参数都通过叫"properties"的数据来传递。X服务器把这些属性贴到各种窗口上,同时把它们存储成一种可以被各种架构的系统所能读取的格式(记住,一个X客户程序可能运行在一台远程主机上)。属性可以是各种类型 - 数字,字符串,等等。大部分的窗口管理器提示函数使用文本属性。一个叫XStringListToTextProperty()的函数可以把C语言的字符串转换成X文本属性,转换后的结果就可以传给各色Xlib函数。以下是一个例子:
/* This variable will store the newly created property. */
XTextProperty window_title_property;
/* This is the string to be translated into a property. */
char* window_title = "hello, world";
/* translate the given string into an X property. */
int rc = XStringListToTextProperty(&window_title,
1,
&window_title_property);
/* check the success of the translation. */
if (rc == 0) {
fprintf(stderr, "XStringListToTextProperty - out of memory\n");
exit(1);
}
函数XStringListToTextProperty()接收一个C字符串矩阵(在我们的例子里只有一个)和一个指向XTextProperty型变量的指针为参数,合并C字符串里的属性把值传到XTextProperty型变量里。成功时它返回一个非0值,失败时返回0(例如,没有足够的内存来完成操作)。
设置窗口名字和图标名字
我们需要做的第一件事就是给我们的窗口设置名字。使用函数XSetWMName()。窗口管理器也许会把这个名字显示在窗口标题栏或是在任务栏上。该函数接受3个参数:一个指向显示的指针,一个窗口句柄,和一个包含有我们设置的名字的XTextProperty变量。下面是我们如何做的:
/* assume that window_title_property is our XTextProperty, and is */
/* defined to contain the desired window title. */
XSetWMName(display, win, &window_title_property);
为了设置我们的窗口的图标化名字,我们将用相同的方式使用函数XSetWMIconName()。
/* this time we assume that icon_name_property is an initialized */
/* XTextProperty variable containing the desired icon name. */
XSetWMIconName(display, win, &icon_name_property);
设置满意的窗口尺寸
在各种情况下,我们希望让窗口管理器知道我们指定的窗口尺寸以及只允许用户在我们的限定下改变窗口尺寸。例如,一个终端窗口(像xterm),我们总是要求我们的窗口可以包含全部的行和列,因此我们就不能从中间截断我们的显示。在其它情况下,我们不希望我们的窗口可以被改变尺寸(像绝大部分的对话框窗口),等等。我们可以依赖窗口管理器的这个尺寸信息,虽然它可能被简单的忽视掉。我们首先需要创建一个数据结构来包含该信息,填充必要的数据,然后使用函数XSetWMNormalHints()。下面是如何操作:
/* pointer to the size hints structure. */
XSizeHints* win_size_hints = XAllocSizeHints();
if (!win_size_hints) {
fprintf(stderr, "XAllocSizeHints - out of memory\n");
exit(1);
}
/* initialize the structure appropriately. */
/* first, specify which size hints we want to fill in. */
/* in our case - setting the minimal size as well as the initial size. */
win_size_hints->flags = PSize | PMinSize;
/* next, specify the desired limits. */
/* in our case - make the window's size at least 300x200 pixels. */
/* and make its initial size 400x250. */
win_size_hints->min_width = 300;
win_size_hints->min_height = 200;
win_size_hints->base_width = 400;
win_size_hints->base_height = 250;
/* pass the size hints to the window manager. */
XSetWMNormalHints(display, win, win_size_hints);
/* finally, we can free the size hints structure. */
XFree(win_size_hints);
请查看你的手册来获取尺寸提示的完整信息。
设置各种窗口管理器提示
使用函数XSetWMHints()还可以设置许多其它的窗口管理器提示。该函数使用一个XWMHints结构来传递参数给窗口管理器。下面是例子:
/* pointer to the WM hints structure. */
XWMHints* win_hints = XAllocWMHints();
if (!win_hints) {
fprintf(stderr, "XAllocWMHints - out of memory\n");
exit(1);
}
/* initialize the structure appropriately. */
/* first, specify which hints we want to fill in. */
/* in our case - setting the state hint as well as the icon position hint. */
win_hints->flags = StateHint | IconPositionHint;
/* next, specify the desired hints data. */
/* in our case - make the window's initial state be iconized, */
/* and set the icon position to the top-left part of the screen. */
win_hints->initial_state = IconicState;
win_hints->icon_x = 0;
win_hints->icon_y = 0;
/* pass the hints to the window manager. */
XSetWMHints(display, win, win_hints);
/* finally, we can free the WM hints structure. */
XFree(win_hints);
请查阅手册以获取全部选项的详细信息。
设置一个程序的图标
在用户图标化了我们的程序的时候,为了让窗口管理器能为我们的程序设置一个图标,我们使用上面提到的函数XSetWMHints。但是,首先我们需要创建一个包含有图标数据的像素图。X服务器使用像素图来操作图片,将在后面介绍它的详细使用。在这里,我们只是向你展示如何为你的程序设置图标。我们假设你已经得到了一个X bitmap格式的图标文件。教程为了方便提供了一个图标文件"icon.bmp" ,下面是代码:
/* include the definition of the bitmap in our program. */
#include "icon.bmp";
/* pointer to the WM hints structure. */
XWMHints* win_hints;
/* load the given bitmap data and create an X pixmap containing it. */
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);
}
/* allocate a WM hints structure. */
win_hints = XAllocWMHints();
if (!win_hints) {
fprintf(stderr, "XAllocWMHints - out of memory\n");
exit(1);
}
/* initialize the structure appropriately. */
/* first, specify which size hints we want to fill in. */
/* in our case - setting the icon's pixmap. */
win_hints->flags = IconPixmapHint;
/* next, specify the desired hints data. */
/* in our case - supply the icon's desired pixmap. */
win_hints->icon_pixmap = icon_pixmap;
/* pass the hints to the window manager. */
XSetWMHints(display, win, win_hints);
/* finally, we can free the WM hints structure. */
XFree(win_hints);
你可以使用程序例如"xpaint"来创建使用X bitmap格式的文件。
我们提供文件simple-wm-hints.c来总结这一节,这段程序包括创建一个窗口,设置窗口管理器提示为在上面显示,以及一个简单的事件循环。它允许用户调整参数以察看提示是如何影响程序的行为的。这可以帮助你了解X程序的可移植性。
简单窗口操作
对我们的窗口,我们可以做更多的一些事情。例如,改变它们的尺寸,打开或关闭它们,图标化它们等。Xlib提供了一系列函数来完成上面提到的功能。
映射和解除一个窗口的映射
首先我们对窗口作的一对操作是映射它到屏幕上去和解除它的映射。映射一个窗口的操作将会使一个窗口显示在屏幕上,如我们在简单窗口程序例子里所看到的。解除映射操作将会把窗口从屏幕里移除出去(虽然作为一个逻辑结点它仍然在X服务器里)。这个可以提供产生窗口被隐藏(映射解除)和再显示(映射)的效果。例如,我们的程序里有一个对话框,我们不需要每次在需要它显示的时候都重新创建一个窗口,我们只是以映射解除的状态创建一次,在用户需要的时候简单的把它映射到屏幕上去就行了。这比每一次都创建它和销毁它要快多了,当然,这需要在客户端和服务器端同时使用更多的内存。
你应该还记得映射操作是使用函数XMapWindow()。映射解除操作是使用函数XUnmapWindow(),下面是如何使用它们:
/* make the window actually appear on the screen. */
XMapWindow(display, win);
/* make the window hidden. */
XUnmapWindow(display, win);
除非整个窗口被其它窗口给覆盖了,一个暴露事件将在映射操作后发给应用程序。
在屏幕移动一个窗口
我们想做的另一个操作是在屏幕里移动窗口。使用函数XMoveWindow()可以完成这个操作。它接受窗口的新坐标,使用的方法和函数XCreateSimpleWindow()是一样的。一下是调用的例子:
/* move the window to coordinates x=400 and y=100. */
XMoveWindow(display, win, 400, 100);
注意当窗口移动的时候,窗口的部分可能后被遮住或被重新暴露,这样我们就可能会收到暴露事件。
改变窗口尺寸
接下来我们要做的是改变一个窗口的尺寸。使用函数XResizeWindow()可以完成这个操作:
/* resize the window to width=200 and height=300 pixels. */
XResizeWindow(display, win, 200, 300);
我们可以合并移动和改变尺寸操作为一个操作,使用函数XMoveResizeWindow():
/* move the window to location x=20 y=30, and change its size */
/* to width=100 and height=150 pixels. */
XMoveResizeWindow(display, win, 20, 30, 100, 150);
改变窗口们的栈顺序 - 提升和降低
到目前为止我们已经改变了一个单独窗口的许多属性。接下来我们将看看窗口之间的属性。其中一个就是它们的栈属性。也就是说,窗口是如何在屏幕上排列的。最前面的窗口我们说它是在栈顶,最后面的窗口我们说它是在栈底。下面演示我们如何改变窗口的栈顺序:
/* move the given window to the top of the stack. */
XRaiseWindow(display, win1);
/* move the given window to the bottom of the stack. */
XLowerWindow(display, win1);
图标化和恢复一个窗口
在这里我们将要讲解的最后一个操作就是如何把一个窗口变换成图标状态和恢复它。使用函数XIconifyWindow()来把一个窗口变换成图标状态,使用函数XMapWindow()来恢复它。为了帮助理解为什么图标化函数没有一个对应的反函数,我们必须理解当一个窗口被图标化时,实际发生的事情是那个窗口被解除映射了,而它的图表被映射了。结果,如果想使哪个窗口在出现,我们只需要简单的映射它一下就行了。图标实际上是另一个窗口,只不过它与我们的窗口有非常强的联系关系。下面演示如何图标化一个窗口并恢复它:
/* iconify our window. Make its icon window appear on the same */
/* screen as our window (assuming we created our window on the */
/* default screen). */
XIconifyWindow(display, win, DefaultScreen(display));
/* de-iconify our window. the icon window will be automatically */
/* unmapped by this operation. */
XMapWindow(display, win);
获得一个窗口的信息
与可以为窗口设置许多属性相同,我们也可以要求X服务器提供这些属性的值。例如,我们可以检查窗口现在在屏幕里什么位置,当前尺寸,是否被映射了等等。函数XGetWindowAttributes()可以帮助我们获取那些信息:
/* this variable will contain the attributes of the window. */
XWindowAttributes win_attr;
/* query the window's attributes. */
Status rc = XGetWindowAttributes(display, win, &win_attr);
结构体XWindowAttributes包含了很多数据域,下面是它的一部分:
int x, y;
窗口的位置,相对于它的父窗口。
int width, height;
窗口的宽和高(单位,像素)。
int border_width
窗口的边框宽度
Window root;
根窗口,也就是我们的窗口在那个窗口里被显示了。
这个函数有些问题,就是它返回的是相对于父窗口的位置。这对一些窗口的操作(例如XMoveWindow)是没有什么意义的。为了解决这个问题,我们需要使用两步的操作。首先,我们找出窗口的父窗口的ID。然后我们在使用它来确定窗口相对于屏幕的坐标。我们使用两个前面没有介绍的函数来完成这个计算,XQueryTree()和XTranslateCoordinates()。这两个函数的功能超出了我们的需要,所以我们只关注我们需要的:
/* these variables will eventually hold the translated coordinates. */
int screen_x, screen_y;
/* this variable is here simply because it's needed by the */
/* XTranslateCoordinates function below. For its purpose, see the */
/* manual page. */
Window child_win;
/* this variable will store the ID of the parent window of our window. */
Window parent_win;
/* this variable will store the ID of the root window of the screen */
/* our window is mapped on. */
Window root_win;
/* this variable will store an array of IDs of the child windows of */
/* our window. */
Window* child_windows;
/* and this one will store the number of child windows our window has. */
int num_child_windows;
/* finally, make the query for the above values. */
XQueryTree(display, win,
&root_win,
&parent_win,
&child_windows, &num_child_windows);
/* we need to free the list of child IDs, as it was dynamically allocated */
/* by the XQueryTree function. */
XFree(child_windows);
/* next, we make the coordinates translation, from the coordinates system */
/* of the parent window, to the coordinates system of the root window, */
/* which happens to be the same as that of the screen, since the root */
/* window always spans the entire screen size. */
/* the 'x' and 'y' values are those previously returned by the */
/* XGetWindowAttributes function. */
XTranslateCoordinates(display, parent_win, root_win,
win_attr.x, win_attr.y, &screen_x, &screen_y,
&child_win);
/* at this point, screen_x and screen_y contain the location of our original */
/* window, using screen coordinates. */
你可以看到Xlib有时候会让我们处理问题时变得很麻烦。
以上的内容可以参考例子window-operations.c 程序。
使用颜色来绘制彩虹到目前为止,我们的绘制操作都只使用了黑白两色。现在我们就看看如何使用丰富的颜色来绘制。
颜色映射
首先,是没有完全足够的颜色的。屏幕控制器同时只能支持有限的颜色。因此,一个应用程序不能只是要求使用颜色“轻紫红”就盼望这个颜色能被支持。每个应用分配它自己所需要的颜色,如果全部的16或256色入口都已经在使用了,下一个颜色的分配就会失败。
结果,就介绍使用“一个颜色映射”。一个颜色映射是一个与屏幕控制器同时可以支持的颜色数相同的表。每个表中的节点都为每种颜色包含一个RGB(红,绿和蓝)。当一个应用想在屏幕上绘制时,它并不指定使用什么颜色,而是指定使用映射表里那一个节点,因此,改变表里某个节点的值将会改变程序绘制的颜色。
为了能让程序员使用他想要的颜色来绘制,提供了颜色映射分配函数。你可以要求分配一个颜色映射节点来对应一个RGB值,然后一个节点的索引值返回给你。如果表满了,这个操作将会失败。你可以接下来要求一个相近的颜色来满足你的需要。这意味着一个相近的颜色将会被绘制到屏幕上去。
在当今的X服务器使用的现代显示器上,一般都可以支持上百万的颜色,上面那个限制也许看起来挺傻的,但是记住还有很多古旧的显示卡和显示器在被使用。使用颜色映射,你可以不必考虑服务器的屏幕硬件细节就可以使用它们。在一个支持上百万的显示器上,任何颜色的分配请求都应该会成功。在一个职能支持有限颜色的显示器上可能会使用一个相近颜色来代替你的要求,这可能不好看,但你的程序仍然能工作。
分配和释放颜色映射
当你使用Xlib绘制的时候,你可以选择屏幕的标准颜色映射,或者为你的窗口分配一个新的颜色映射。在后一种情况下,每次鼠标移动到你的窗口上时,你窗口的颜色映射都将替换缺省的屏幕映射,然后你就会看到屏幕花一下其它的窗口颜色也改变了。实际上,这和你在使用“-install”选项运行X应用时效果一样。
系统定义了宏DefaultColormap来获取屏幕的标准颜色映射:
Colormap screen_colormap = DefaultColormap(display, DefaultScreen(display));
上面的调用将会返回第一个屏幕的缺省颜色映射的句柄(再多余的提醒一下,一个X服务器可以同时支持数个不同的屏幕,每个屏幕都可以有自己的资源)。
另一个选项,分配一个颜色映射,像下面这样工作:
/* first, find the default visual for our screen. */
Visual* default_visual = DefaultVisual(display, DefaultScreen(display));
/* this creates a new color map. the number of color entries in this map */
/* is determined by the number of colors supported on the given screen. */
Colormap my_colormap = XCreateColormap(display,
win,
default_visual,
AllocNone);
注意,window参数是用来只允许X服务器为指定屏幕分配颜色映射。我们接下来可以使用分配来的颜色映射给同一个屏幕里的任意一个窗口使用。
分配和释放颜色节点
一旦我们获得了颜色映射的访问,我们就可以开始分配颜色。使用函数XAllocNameColor()和XAllocClor()来完成这个工作。首先函数XAllocNameColor()获得颜色的名字(例如"红","蓝","棕"等等)然后获得能使用的实际相近颜色。函数XAllocColor()访问RGB颜色。两个函数都使用XColor结构,它有以下的一些数据域:
unsigned long pixel
颜色映射节点的索引。
unsigned short red
RGB颜色值的红色部分。
unsigned short green
RGB颜色值的绿色部分。
unsigned short blue
RGB颜色值的蓝色部分。
下面是使用的例子:
/* this structure will store the color data actually allocated for us. */
XColor system_color_1, system_color_2;
/* this structure will store the exact RGB values of the named color. */
/* it might be different from what was actually allocated. */
XColor exact_color;
/* allocate a "red" color map entry. */
Status rc = XAllocNamedColor(display,
screen_colormap,
"red",
&system_color_1,
&exact_color);
/* make sure the allocation succeeded. */
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);
}
/* allocate a color with values (30000, 10000, 0) in RGB. */
system_color_2.red = 30000;
system_color_2.green = 10000;
system_color_2.blue = 0;
Status rc = XAllocColor(display,
screen_colormap,
&system_color_2);
/* make sure the allocation succeeded. */
if (rc == 0) {
fprintf(stderr,
"XAllocColor - allocation of (30000,10000,0) color failed.\n");
}
else {
/* do something with the allocated color... */
.
.
}
使用一个眼色绘制
我们在分配了希望的颜色之后,我们可以使用它们绘制文本或图形。为了达到目的,我们需要把获得的颜色设置给一些GC(图形上下文)作为前景色和背景色,然后使用设置好的GC来进行绘制。使用函数XSetForeground()和XSetBackground()来进行,如下:
/* use the previously defined colors as the foreground and background */
/* colors for drawing using the given GC. assume my_gc is a handle to */
/* a previously allocated GC. */
XSetForeground(display, my_gc, screen_color_1.pixel);
XSetForeground(display, my_gc, screen_color_2.pixel);
如你所见,这个是个使用的例子。实际的绘制工作使用我们以前介绍的绘图函数。注意,为了使用各种颜色完成绘制工作,我们可以使用两种方法。我们可以在调用绘图函数前改变GC的值,也可以使用代表不同颜色的GC。由你自己根据情况使用哪种方法。注意,使用多个GC降消耗X服务器额外的资源,但这样可以使你的代码显的更紧凑。
作为使用颜色绘制的例子,请察看例子程序color-drawing.c 。这是程序simple-drawing.c 的一个拷贝,我们只是添加了颜色的部分在里面。
X Bitmaps和Pixmaps
一个被称为多媒体的程序所有作的一件事情就是显示图片。在X的世界里,使用bitmaps和pixmaps来实现这个功能。在为我们的程序设置图标的介绍里我们已经使用了它们。现在让我们进一步对它们进行研究,看看在一个窗口里是如何绘制它们的。
在进入之前有一点需要注意,Xlib不能处理许多现在流行的图片格式,例如gif,jpeg或tiff。这些被留给了应用程序(或者是一些图形处理库)来转换成X服务器可以接受的x bitmaps和x pixmaps。
什么是一个X Bitmap?和X Pixmap?
一个Xbitmap是一个有X窗口系统定义的双色图形格式。在保存在一个文件里的时候,bitmap数据看起来就像一段C源程序。它包括定义图片宽高的变量,一个包含比特值得矩阵(矩阵的尺寸=宽*高),和一个可选的热点位置(将会在后面的鼠标光标的部分进行解释)。
一个X pixmap是一个X窗口系统在内存里保存图像的格式。该格式可以储存黑色和白色的图片(例如X bitmaps)也可以保存带颜色的图片。这实际上是X协议唯一能支持的图片格式,任何图片如果想被显示在屏幕上前都要先被转换成这种格式。
实际上,一个X pixmap应该被认为是一个没有被绘制到屏幕上的窗口。许多在窗口上的图形操作也可以工作于X pixmap,只不过使用X pixmap ID来代替窗口ID。事实上,如果你查看手册,你会发现所有的这些函数都是接受一个叫"可画"参数而不是一个窗口参数。因为这两种类型都是可画的,它们都可以被用在例如函数XDrawArc(),XDrawText()等等。
从一个文件里读取一个Bitmap
在图标的程序里,我们已经看过如何从一个文件里把一个Bitmap装载到内存里。前面我们使用的方法是使用C预编译器"#include"来进行,下面我们看看如何直接从文件里读取。
/* this variable will contain the ID of the newly created pixmap. */
Pixmap bitmap;
/* these variables will contain the dimensions of the loaded bitmap. */
unsigned int bitmap_width, bitmap_height;
/* these variables will contain the location of the hot-spot of the */
/* loaded bitmap. */
int hotspot_x, hotspot_y;
/* this variable will contain the ID of the root window of the screen */
/* for which we want the pixmap to be created. */
Window root_win = DefaultRootWindow(display);
/* load the bitmap found in the file "icon.bmp", create a pixmap */
/* containing its data in the server, and put its ID in the 'bitmap' */
/* variable. */
int rc = XReadBitmapFile(display, root_win,
"icon.bmp",
&bitmap_width, &bitmap_height,
&bitmap,
&hotspot_x, &hotspot_y);
/* check for failure or success. */
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:
/* bitmap loaded successfully - do something with it... */
.
.
break;
}
注意对于给定的bitmap参数"root_win"什么作用也不起 - 读取的bitmap并不与这个窗口相联系。这个窗口句柄只是被用来指明bitmap所使用的屏幕。这是非常重要的,bitmap必须支持与屏幕相同数量的颜色,这样它才能发挥作用。
在一个窗口里绘制图形
一旦我们获得了从bitmap里生成的pixmap的句柄,我们就可以使用函数XCopyPlane()把它绘制到窗口里。这个函数可以帮助我们指定什么(一个窗口,甚至另一个pixmap)可以画到这个pixmap上去。
/* draw the previously loaded bitmap on the given window, at location */
/* 'x=100, y=50' in that window. we want to copy the whole bitmap, so */
/* we specify location 'x=0, y=0' of the bitmap to start the copy from, */
/* and the full size of the bitmap, to specify how much of it to copy. */
XCopyPlane(display, bitmap, win, gc,
0, 0,
bitmap_width, bitmap_height,
100, 50,
1);
如你所见,我们可以只拷贝制定的矩形区而不是整个pixmap。另外还需要注意的是函数XCopyPlane的最后一个参数(那个结尾的"1")。该参数指定了那个平面被从源里拷贝出来。对于bitmaps,我们通常只拷贝平面1。到了下面我们讨论颜色深度的时候你就能确切的明白为什么这么做。
创建一个Pixmap
有时我们需要创建一个没有初始化的pixmap,这样我们可以接下来在它上面绘制。这对图像绘制程序是非常有用的。另外,这对读取各种格式的图像数据也是非常有用的。
/* this variable will store the handle of the newly created pixmap. */
Pixmap pixmap;
/* this variable will contain the ID of the root window of the screen */
/* for which we want the pixmap to be created. */
Window root_win = DefaultRootWindow(display);
/* this variable will contain the color depth of the pixmap to create. */
/* this 'depth' specifies the number of bits used to represent a color */
/* index in the color map. the number of colors is 2 to the power of */
/* this depth. */
int depth = DefaultDepth(display, DefaultScreen(display));
/* create a new pixmap, with a width of 30 pixels, and height of 40 pixels. */
pixmap = XCreatePixmap(display, root_win, 30, 40, depth);
/* just for fun, draw a pixel in the middle of this pixmap. */
XDrawPoint(display, pixmap, gc, 15, 20);
在一个窗口里绘制一个Pixmap
我们在获得了一个pixmap的句柄后,我们就可以使用它在窗口里绘制,使用函数XCopyArea()。
/* draw the previously loaded bitmap on the given window, at location */
/* 'x=100, y=50' in that window. we want to copy the whole bitmap, so */
/* we specify location 'x=0, y=0' of the bitmap to start the copy from, */
/* and the full size of the bitmap, to specify how much of it to copy. */
XCopyPlane(display, bitmap, win, gc,
0, 0,
bitmap_width, bitmap_height,
100, 50,
1);
如你所见,我们可以拷贝指定的矩形区域而不是整个pixmap。
另外一个需要被强调注意的是 - 可以在同一个屏幕上创建不同深度的pixmap。当我们进行拷贝作业时(往一个窗口上拷贝pixmap等等),我们应该保证源和目标是用相同的深度。如果两个的深度不一样,操作将会失败。除非我们使用前面介绍的函数XCopyPlane()可以完成这个操作。在那样一种情况下,我们拷贝指定的平面到窗口上去,实际上指定了每一个被拷贝的颜色位。这个操作可以制作许多特殊的效果,但这超出了本文的范围。
释放一个Pixmap
最后,当我们完成了对一个pixmap的操作,我们应该释放它所占的资源。使用函数XFreePixmap()。
/* free the pixmap with the given ID. */
XFreePixmap(display, pixmap);
在释放一个pixmap之后 - 我们绝对不能再访问它。
作为总结这一章,看一下程序draw-pixmap.c 。
改变鼠标光标
我们经常看到改变鼠标光标的程序(经常被称为X光标)。例如,一个正在埋头工作的程序经常会把光标变成沙漏,提示用户需要等待才能处理新的请求。如果没有这么个提示方法,用户会认为程序已经卡住了。下面让我们看看如何为我们的窗口改变鼠标光标。
创建和销毁鼠标光标
系统提供了两个方法来创建光标。其中一个是使用系统预定义的形状,由Xlib支持。另一个是有程序提供一个bitmap来显示。
使用前一种方法时,我们使用一个特殊的字体名字"cursor"和相应的函数XCreateFontCursor()。该函数接受一个形状指示然后返回一个代表生成的光标的句柄。文件
列出了系统支持的光标类型,下面的是其中的三个:
XC_arrow
X服务器显示的普通光标。
XC_pencil
一个笔状的光标。
XC_watch
一个表状沙漏
使用这些符号来创建光标是非常简单的:
#include <X11/cursorfont.h> /* defines XC_watch, etc. */
/* this variable will hold the handle of the newly created cursor. */
Cursor watch_cursor;
/* create a sand watch cursor. */
watch_cursor = XCreateFontCursor(display, XC_watch);
另一种创建鼠标光标的方法时使用一对pixmaps。一个pixmap定义了光标的形状,另一个是个面具,来指定前一个的什么内容被显示。其它的内容将变成透明的。创建一个那样的光标使用函数XCreatePixmapCursor()。下面的例子里,我们将使用"icon.bmp"来创建光标。我们将假设它已经被装载到内存里去了,并已经被转换成pixmap,返回的句柄被保存到"bitmap"变量里。我们希望它是完全透明的。也就是说,只有黑色颜色的部分会被确实画在屏幕上。为了实现这个效果,我们将会既用它来做光标pixmap且做面具pixmap。希望你能明白为什么这样...
/* this variable will hold the handle of the newly created cursor. */
Cursor icon_cursor;
/* first, we need to define foreground and background colors for the cursor. */
XColor cursor_fg, cursor_bg;
/* access the default color map of our screen. */
Colormap screen_colormap = DefaultColormap(display, DefaultScreen(display));
/* allocate black and while colors. */
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);
}
/* finally, generate the cursor. make the 'hot spot' be close to the */
/* top-left corner of the cursor - location (x=5, y=4). */
icon_cursor = XCreatePixmapCursor(display, bitmap, bitmap,
&cursor_fg, &cursor_bg,
5, 4);
上面需要说明的是参数"hot spot"。当我们定义了一个光标,我们需要指明光标里的哪一个像素用来生成各种鼠标事件。通常,我们根据习惯来指定一个看起来像"hot spot"的点。例如一个箭头形状的光标,我们就会选择箭头尖为"hot spot"。
最后,我们不需要再使用光标时,我们可以使用函数XFreeCursor()来释放它的资源:
XFreeCursor(display, icon_cursor);
设置一个窗口的鼠标光标
我们在创建了光标后,就可以告诉X服务器把它贴到我们的任何窗口上去。使用函数XDefineCursor(),X服务器就会在每一次光标移进指定的窗口时改变光标的形状。稍候我们可以使用函数XUndefinCursor()来撤销刚才的指定。这样,鼠标再移进指定的窗口时就会使用缺省的光标。
/* attach the icon cursor to our window. */
XDefineCursor(display, win, icon_cursor);
/* detach the icon cursor from our window. */
XUndefineCursor(display, win);
作为例子,请查看程序cursor.c 。