12 Windows Hierarchy

当窗口在X服务器上显示时,它们总是按照某种层次结构进行排列的 - 每个窗口都可能有子窗口,每个子窗口都可能有自己的子窗口,等等。让我们看看这个层次结构的一些属性,以及它们如何影响绘图或事件传播等操作。

12.1 Root, Parent And Child Windows

在每个屏幕上,都有一个根窗口Root。根窗口始终覆盖整个屏幕大小。这个窗口不能被销毁、调整大小或最小化。当应用程序创建窗口时,它首先必须创建至少一个顶级窗口。这个窗口成为根窗口的直接后代,直到它首次在屏幕上映射。在这个窗口被映射之前,窗口管理器会被通知即将发生的操作。然后,窗口管理器有权限重新指定新的顶级窗口的父窗口。这是为了添加一个将包含新窗口的窗口,并用于绘制其框架、标题栏、系统菜单等。

一旦创建了这样一个顶级窗口(实际上在重新指定父窗口的操作发生后,它就不再是一个顶级窗口),应用程序可以在这个窗口内创建子窗口。子窗口只能在其父窗口内显示 - 如果它被移动到外部,它将被其父窗口的边框裁剪。任何窗口都可以包含多个子窗口,如果是这种情况,这些窗口会在一个内部堆栈中被排序。当一个顶级窗口被提升时 - 它的所有后代窗口都会随之一起被提升,同时保持它们的内部顺序。如果一个子窗口被提升 - 它只会在其同级窗口中被提升。

让我们看看如何在给定的窗口 ‘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));

12.2 Events Propagation

我们之前已经讨论过事件传播 - 如果一个窗口收到一个事件并且它没有处理这个事件,那么这个事件就会被传递给这个窗口的父窗口。如果那个父窗口也没有处理这个事件,它就会被传递给父窗口的父窗口,依此类推。这种行为对于一个简单的Xlib应用程序来说可能没有太大的意义,但是当使用更高级的图形库时,这种行为就变得有意义了。这些库通常会将函数与特定窗口中发生的事件关联起来。在这种情况下,将事件传递给与适当函数关联的相关窗口是很有用的。

13 Interacting With The Window Manager

在我们了解了如何创建窗口并在其上绘图之后,我们回过头来看看我们的窗口是如何与其环境互动的 - 全屏以及其他窗口。首先,我们的应用程序需要与窗口管理器进行交互。窗口管理器负责装饰绘制的窗口(例如,添加框架、最小化按钮、系统菜单、标题栏),以及处理窗口被最小化时显示的图标。它还处理屏幕上窗口的排序以及其他管理任务。我们需要给它各种提示,告诉它我们希望如何处理我们应用程序的窗口。

13.1 Window 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字符串的数组、数组中字符串的数量(在我们的情况下是1)以及一个XTextProperty变量的指针。它将字符串列表连接起来并放入XTextProperty变量中。如果成功,它返回非零值;如果失败(例如,没有足够的内存来执行操作),则返回0。

13.2 Setting The Window Name And Icon Name

首先,我们需要为窗口设置名称。这是通过使用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);

13.3 Setting Preferred Window Size(s)

在不同的场景下,我们希望告诉窗口管理器我们希望窗口有一个指定的大小,并且只允许用户按照给定的量来调整窗口大小。例如,在一个终端应用程序(如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);

请查看你的手册来获取尺寸提示的完整信息。

13.4 Setting Miscellaneous Window Manager 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);

请查阅手册以获取全部选项的详细信息。

13.5 Setting An Application’s Icon

在用户图标化了我们的程序的时候,为了让窗口管理器能为我们的程序设置一个图标,我们使用上面提到的函数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程序的可移植性。