所有的 InputOutput 窗口都可以有零个或者多个像素的边框宽度,一个可选的背景,一个事件压制掩码(它压制来自孩子的事件传播),和一个 property 列表。窗口的边框和背景可以是一个实心颜色或者是一个称为平铺的调色板。除了根窗口之外的所有的窗口都有父亲并且都是由它们的父亲所裁剪。如果一个窗口是 在压栈压在另一个窗口的上面,它遮挡用于输入的其它窗口。如果一个窗口有背景(大多数都有),它遮挡其它用于输出的窗口。尝试输出到被遮挡的区域将不起任 何作用,并且对于被遮挡区域不会生成输入事件。
窗口也会与 property 列表相关联。
InputOutput 和 InputOnly 窗口都有下面的共同属性,但是 InputOnly 窗口只有这些属性:
- win-gravity
- event-mask
- do-not-propagate-mask
- override-redirect
- cursor
如果你为一个 InputOnly 窗口指定任何其它的属性,就会产生一个 BadMatch 错误。
InputOnly 窗口是用于在不必要使用 InputOutput 窗口的情况下控制输入事件的。InputOnly 窗口是不可见的;可能只被用于控制像光标、输入事件生成以及捕获等的一些东西;并且不能用在任何图形请求中。注意 InputOnly 窗口不能作为 InputOutput 窗口的下级窗口。
窗口有可编程宽度的边框和调色板以及背景调色板或者平铺贴片。像素值可以用作实心颜色。如果没有产生更进一步对它们明确地引用,那么背景和边框位图可以在 创建窗口之后立即释放。调色板要么可以与它的父亲有关,要么完全无关。如果 ParentRelative,就会使用父亲的背景。当窗口是第一次创建,它们在屏幕上并不可见(还没有被映射)。对于在屏幕上不可见并且没有回填窗口的 任何输出都将会被丢弃。某个应用程序可能希望在窗口被映射到屏幕之前很久创建这个窗口。当窗口最终被映射到屏幕上(使用 XMapWindow),如果并没有维护回填,X server 为窗口生成 Expose 事件。
窗口管理器可以覆盖你为顶层窗口选择的大小、边框宽度和位置。你的程序必须准备使用顶层窗口的实际大小和位置。对于客户应用程序而言去重新改变它自己的大 小是不可接受的,除非是为了直接响应人的指令才这么做。而是你的程序要么使用给它的空间,要么如果对于任何有用的工作而言空间太小的话,你的程序可能要求 用户去改变窗口的大小。对于窗口管理器而言,你的顶层窗口的边框被认为是可以改变的。
要设置窗口的属性,设置 XSetWindowAttributes 结构适当的成员,并且与你随后调用 XCreateWindow 和 XChangeWindowAttributes 中对应的值位掩码做或(OR)运算,或者使用任何设置适当属性的其它便利函数之一。用于值掩码位的符号和 XSetWindowAttributes 结构是:
/* Window attribute value mask bits */
#define CWBackPixmap (1L << 0)
#define CWBackPixel (1L << 1)
#define CWBorderPixmap (1L << 2)
#define CWBorderPixel (1L << 3)
#define CWBitGravity (1L << 4)
#define CWWinGravity (1L << 5)
#define CWBackingStore (1L << 6)
#define CWBackingPlanes (1L << 7)
#define CWBackingPixel (1L << 8)
#define CWOverrideRedirect (1L << 9)
#define CWSaveUnder (1L << 10)
#define CWEventMask (1L << 11)
#define CWDontPropagate (1L << 12)
#define CWColormap (1L << 13)
#define CWCursor (1L << 14)
/* Values */
typedef struct {
Pixmap background_pixmap;
unsigned long background_pixel;
Pixmap border_pixmap;
unsigned long border_pixel;
int bit_gravity;
int win_gravity;
int backing_store;
unsigned long backing_planes;
usngined long bakcing_pixel;
Bool save_under;
long event_mask;
long do_not_propagate_mask;
Bool override_redirect;
Colormap colormap;
Cursor cursor;
} XSetWindowAttributes;
下面列出了每一个窗口属性的缺省值并且标志着这个属性对于 InputOutput 和 InputOnly 窗口而言是否可以接受:
属性 |
缺省值 |
InputOutput |
InputOnly |
background-pixmap |
None |
Yes |
No |
background-pixel |
Undefined |
Yes |
No |
border-pixmap |
CopyFromParent |
Yes |
No |
border-pixel |
Undefined |
Yes |
No |
bit-gravity |
ForgetGravity |
Yes |
No |
win-gravity |
NorthWestGravity |
Yes |
Yes |
backing-store |
NotUseful |
Yes |
No |
backing-planes |
All ones |
Yes |
No |
backing-pixel |
zero |
Yes |
No |
save-under |
False |
Yes |
No |
event-mask |
empty set |
Yes |
Yes |
do-not-propagate-mask |
empty set |
Yes |
Yes |
override-redirect |
False |
Yes |
Yes |
colormap |
CopyFromParent |
Yes |
No |
cursor |
None |
Yes |
Yes |
背景属性
只有 InputOutput 窗口可以有背景。你可以通过使用像素或者位图来设置 InputOutput 窗口的背景。
窗口的 background-pixmap 属性指定的位图被用于窗口的背景。这个位图可以是任何大小,尽管一些大小可以比另一些快。窗口的 background-pixel 属性指定用于以一种单一颜色绘制窗口的背景。
你可以将 background-pixmap 设置成一个位图、None(缺省)或者 ParentRelative。你可以将窗口的 background-pixel 设置成任何像素值(没有缺省值)。如果你指定了 background-pixel,它要么覆盖缺省的 background-pixmap,要么覆盖你可能在 background-pixmap 中设置的任何值。用 background-pixel 填充的未定义大小的 pixmap 用作背景。对背景像素并不进行范围检查;它简单地截取成适当的位数。
如果你设置 background-pixmap,它覆盖缺省值。这个 background-pixmap 和窗口必须有相同的深度,或者产生 BadMatch 错误。如果你将 background-pixmap 设为 None,窗口就没有定义的背景。如果你将 background-pixmap 设置为 ParentRelative:
- 使用父窗口的 background-pixmap。不过,子窗口必须与其父亲有相同的深度,或者产生 BadMatch 错误。
- 如果父窗口的 background-pixmap 为 None,那么窗口的 background-pixmap 也是 None。
- 背景平铺的原点总是与父窗口的背景平铺原点对齐。如果 background-pixmap 并不是 ParentRelative,背景平铺原点就是子窗口的原点。
是通过设置 background-pixmap 或者 background-pixel 来设置新背景,都覆盖任何以前的背景。如果没有对它进一步明确的引用(X server 将在需要的时候保持一份拷贝来使用)的话,background-pixmap 可以立即被释放。如果你随后将位图用于背景的话,结果是未定义的,因为 X 实现是自由地创建位图的副本或者使用相同的位图。
当窗口的区域并没有有效的内容时,并且要么区域是可见的,要么服务器正在维护回填,服务器自动地用窗口背景平铺区域,除非窗口的背景为 None。如果背景是 None,来自其它与这个窗口具有相同深度窗口的前一屏内容被简单地留在适当的位置,只要内容是来自这个窗口父亲或者父亲的后继。否则被暴露区域的内容是 未定义的。随后为这个区域生成 Expose 事件,即使 background-pixmap 是 None。
边框属性
只有 InputOnly 窗口可以有边框。你可以通过使用像素或者位图来设置 InputOutput 窗口的边框。
窗口的 border-pixmap 属性指定用于窗口边框的位图。窗口的 border-pixel 属性指定由用于窗口边框的像素填充的未定义大小的位图。在背景像素上不必执行范围检查;它简单地截取成合适的位数。边框平铺的原点总是与背景平铺原点一样。
你也可以将 border-pixmap 设置为任意大小的位图(一些可能比另一些快)或者 CopyFromParent(缺省)。你可以将 border-pixel 设置为任何像素值(非缺省)。
如果你设置 border-pixmap,它覆盖缺省值。这个 border-pixmap 和窗口必须有相同的深度,或者产生 BadMatch 错误。如果你将 border-pixmap 设置为 CopyFromParent,父窗口的 border-pixmap 就会被复制。对父窗口边框属性的后续更改并不会影响子窗口。不过,子窗口与父窗口必须有相同的深度,或者产生 BadMatch 错误。
如果没有更进一步对这个 border-pixmap 的引用,它可立即释放它。如果你随后将位图用于边框,后果是未定义的,因为 X 实现对于是创建一个位图的副本还是使用相同的位图是自由的。如果你指定一个 border-pixel,它要么覆盖缺省的 border-pixmap,要么你可以将 border-pixmap 设置成任何值。在窗口边框中的所有像素都将被设置为 border-pixel。不论是通过设置 border-pixel 还是通过设置 border-pixmap 来设置新边框,都会覆盖前面的边框。
向一个窗口输出总是会在这个窗口的范围以内进行裁剪。因此,图形操作从不会影响窗口边框。
Override Redirect 标志
要控制窗口的放置或者增加装饰,窗口管理器常常需要解释(重定向)任何映射或者配置请求。不过,弹出式窗口常常需要被映射但无须窗口管理器这样做。要控制 InputOutput 或者 InputOnly 窗口是否忽略这些结构控制功能,使用 override-redirect 标志。
Override-redirect 标志指定在这个窗口上的映射和配置是否应当在覆盖其父窗口的 SubstructureRedirectMask。你可以将 override-redirect 标志设置为 True 或者 False(缺省)。窗口管理器使用这个信息以避免影响弹出式窗口。
控制输入焦点
Xlib 提供了可用于移动鼠标指针位置以及设置和获取输入焦点的函数。
要将鼠标指针移动到屏幕上的任意一处时,使用 XWarpPointer。
XWarpPointer(display, src_w, dest_w, src_x, src_y, src_width, src_height, dest_x, dest_y )
Display *display ;
Window scr_w , dest_w ;
int src_x , src_y ;
unsigned int src_width , src_height ;
int dest_x , dest_y ;
要设置焦点,使用 XSetInputFocus。
XSetInputFocus(display, focus, revert_to, time )
Display *display ;
Window focus ;
int revert_to ;
Time time ;
display 指定要连接到的 X server。
focus 指定的窗口、PointerRoot、或者 None。
revert_to 如果窗口变得不可视的话,其指定输入焦点应该回到何处。你可以指定 RevertToParent、RevertToRoot、或者 RevertToNone。
time 指定时间。你可以发送一个时间戳或者 CurrentTime。
XSetInputFocus 函数改变输入焦点和 last-focus-change 时间。如果指定的时间比当前 last-focus-change 时间还早或者比当前 X server 时间还晚的话,它不产生任何影响。否则,last-focus-change 时间就被设置为指定的时间(CurrentTime 由当前 X server 时间所取代)。XSetInputFocus 导致 X server 生成 FocusIn 和 FocusOut 事件。
取决于焦点参数,会发生如下情况:
- 如果焦点是 None,所有的键盘事件都会被丢弃,直到设置了一个新的焦点窗口,并且 revert_to 参数被忽略。
- 如果焦点是一个窗口,它就变成键盘的焦点窗口。如果生成的键盘事件会正常地报告给这个窗口或者它的后代之一的话,那么这个事件报告照常。否则,事件报告给相关的焦点窗口。
- 如果焦点是 PointerRoot,焦点窗口就动态地趋向于在第一个键盘事件上鼠标指针所指向的任何屏幕的根窗口。在这种情况下,忽略 revert_to 参数。
指定的焦点窗口在 XSetInputFocus 被调用时必须是可视的,或者产生一个 BadMatch 错误结果。如果焦点窗口后来变成不可视的话,X server 会评估 revert_to 参数来按如下方案决定新的焦点窗口:
- 如果 revert_to 是 RevertToParent,焦点还原到父窗口(或者最近的可视祖先窗口),并且新的 revert_to 值变成 RevertToNone。
- 如果 revert_to 是 RevertToPointerRoot 或者 RevertToNone,焦点分别还原到 PointerRoot 或者 None。当焦点还原时,X server 生成 FocusIn 和 FocusOut 事件,但是 last-focus-change 时间并不受影响。
XSetInputFocus 可以生成 BadMatch、BadValue、以及 BadWindow 错误。
要获得当前的输入焦点,使用 XGetInputFocus。
XGetInputFocus(display, focus_return, revert_to_return )
Display *display ;
Window *focus_return ;
int *revert_to_return ;
映射窗口
如果在一个窗口上已经调用过 XMapWindow 的话,它就被视作已映射的。它出于以下几个原因之一在屏幕上可能并不可见:
- 它是由另外一个不透明的窗口所遮挡。
- 它的祖先之一并没有被映射。
- 它整个地被某个祖先所剪切。
当窗口的部分或者全部在屏幕上可见时才生成 Exposure 事件。客户程序只有在它已经请求了 Expose 事件的情况下才能接收到它们。当窗口被解除映射时,按照栈顺序保留它们的位置。
窗口管理器可能想去控制子窗口的放置。如果窗口管理器在父窗口(通常是一个根窗口)中已经选择了 SubstructureRedirectMask 的话,那么由其它客户程序在子窗口上发起的映射请求就不会被执行,并且会向窗口管理器发送一个 MapRequest 请求。不过,如果子窗口的 override-redirect 标志被设置为 True(通常只是在弹出菜单上)的话,映射请求会被执行。
一个铺开窗口的窗口管理器可能决定重新定位并且重新调整其它客户程序的窗口大小,然后决定将窗口映射到它的最终位置。想提供装饰的窗口管理器可能会将子窗 口的父属关系重新调整到第一个帧。只有单个客户程序在一个时间可以选择 SubstructureRedirectMask。
类似地,单个的客户程序可以在父窗口上选择 ResizeRedirectMask。然后,其它客户程序对窗口的大小进行调整的尝试都会被压制,并且客户程序会接收到 ResizeRequest 事件。
要映射一个给定的窗口,使用 XMapWindow。
XMapWindow(display, w)
Display *display;
Window w;
display 指定到 X server 的连接。
w 指定窗口。
XMapWindow 函数映射窗口及其有映射请求的所有子窗口。映射一个具有未映射祖先的窗口并不能显示这个窗口,而是将它标记为具备在这个祖先变成映 射后显示的条件。这样的窗口称为不可视的。当它的所有祖先都被映射时,如果这个窗口没有被其它的窗口所遮盖的话,它就变成可视的并且会在屏幕上看见。如果 这个窗口已经被映射过了,这个函数也没有任何影响。
如果窗口的 override-redirect 是 False,并且如果一些其它的客户程序已经在父窗口上选择了 SubstructureRedirectMask 的话,那么 X server 生成一个 MapRequest 事件,并且 XMapWindow 函数并不映射窗口。否则,窗口会被映射,并且 X server 生成 MapNotify 事件。
如果窗口变成可视的,并且它早期的内容并没有被记下,X server 用它的背景平铺这个窗口。如果窗口的背景没有定义,现有的屏幕内容就不会被更新,并且 X server 生成零个或者多个 Expose 事件。如果回填是在窗口没有被映射的时候进行维护,那么就没有 Expose 事件生成。如果回填将会在现在维护,全部窗口的暴露就总会被生成。否则,只有可见的区域会被报告。类似的平铺和暴露会发生在任何级别的新的可视窗口上。
如果窗口是 InputOutput 窗口,XMapWindow 在每一个它所主导要被显示的 InputOutput 窗口上生成 Expose 事件。如果客户程序映射并且描绘了窗口,并且如果客户程序开始处理事件,那么窗口会被描绘两次。要避免这一点,首先询问 Expose 事件,然后映射窗口,以使客户程序正常处理输入事件。事件列表将为每一个出现在屏幕上的窗口包括 Expose。客户程序对 Expose 事件的正常响应应当是重绘窗口。这种方式通常导致更简单的程序以及与窗口管理器之间的正确交互。
XMapWindow 可以生成 BadWindow 错误。
要映射并且提升窗口,使用 XMapRaised。
XMapRaised(display, w)
Display *display;
Window w;
XMapRaised 函数本质上是类似于 XMapWindow 中其映射窗口以及窗口的所有有映射请求的子窗口。不过,它也提升指定的窗口到显式栈的栈顶。
XMapRaised 可以生成多个 BadWindow 错误。
要为某个指定的窗口映射所有的子窗口,使用 XMapSubwindows。
XMapSubwindows(display, w)
Display *display;
Window w;
XMapSubwindows 函数按照从顶到底的栈顺序为指定的窗口映射其所有的子窗口。X server 在每一个新显示的窗口上生成 Expose 事件。这可能比每次映射许多窗口更加有效,因为 server 只需要一次就能对所有的窗口,而不是一个窗口执行很多工作。
配置窗口
Xlib 提供了可用于移动窗口、重新改变窗口大小、移动并重新改变窗口大小,或者改变窗口边框宽度等的函数。要改变这些参数之一,设置 XWindowChanges 结构的恰当成员并且或上(OR)在随后调用的 XConfigureWindow 中对应的值掩码。对于值掩码位和 XWindowChanges 结构的符号是:
/* Configure window value mask bits */
#define CWX (1<<0)
#define CWY(1<<1)
#define CWWidth(1<<2)
#define CWHeight(1<<3)
#define CWBorderWidth(1<<4)
#define CWSibling(1<<5)
#define CWStackMode(1<<6)
/* Values */
typedef struct {
int x, y;
int width, height;
int border_width;
Window sibling;
int stack_mode;
} XWindowChanges;
成员 x 和 y 用于设置窗口的 x 和 y 坐标,它是与父窗口的原点有关的,并且指示窗口的左上角的位置。宽度和高度成员用于设置窗口的内部大小,不包括边框,并且必须非零,或者产生 BadValue 错误。尝试配置根窗口不会产生任何效果。
成员 border_width 成员用于设置以像素为单位的边框宽度。注意只是设置边框宽度使窗口的外层左上角在一个固定的位置,而移动相对于窗口原点的绝对位置。如果尝试将 InputOnly 窗口的边框宽度属性设置为非零,会产生一个 BadMatch 错误。
成员 sibling 用于为栈操作设置兄弟窗口。成员 stack_mode 成员用于设置窗口如何被重新调整栈位置并且可以被设置为 Above、Below、TopIf、BottomIf、或者 Opposite。
如果窗口的 override-redirect 标记是 False 并且如果一些其它的客户程序在父窗口上选择了 SubstructureRedirectMask 的话,X server 生成一个 ConfigureRequest 事件,并且不再执行进一步的处理。否则,如果一些其它的客户程序在窗口上选择了 ResizeRedirectMask 并且窗口的内部宽度和高度改变的话,会生成一个 ResizeRequest 事件,并且取而代之使用当前的内部宽度和高度。注意
窗口的 override-redirect 标志对于 ResizeRedirectMask 没有影响并且父窗口上的 SubstructureRedirectMask 已经先于这个窗口的 ResizeRedirectMask。
当窗口的几何形状按所指定的发生了改变,这个窗口会在兄弟中改变栈顺序,并且如果窗口的状态实际上发生了改变,会生成一个 ConfigureNotify 事件。GravityNotify 事件在 ConfigureNotify 事件之后生成。如果窗口内部的宽度和高度已经真正地发生了改变,窗口的孩子都会按照指定的那样受影响。
如果窗口的大小真正地发生了改变,窗口的子窗口按照它们的窗口重心移动。取决于窗口的位重心,窗口的内容也可能被移动。
如果窗口的区域应当被遮挡,但是现在还没有的话,会在这些原来被遮挡窗口上执行暴露处理,包括窗口自身和它的下层窗口。宽度和高度的增加,会导致在窗口的任何新区域和窗口内容丢失的任何区域上也执行暴露处理。
重新排列栈顺序的检查(特别是对于 BottomIf、TopIf、和 Opposite 的计算)是遵照窗口最终的大小和位置(由请求的其它参数来控制)来执行的,而不是其初始位置。如果指定了一个兄弟窗口而不是一个 stack_mode,生成一个 BadMatch 错误。
如果兄弟窗口和 stack_mode 都被指定的话,窗口按如下方式重新调整栈顺序:
Above 窗口正好放在兄弟窗口之上。
Below 窗口正好放在兄弟窗口之下。
TopIf 如果兄弟窗口包藏住了这个窗口,那么它放置在栈顶。
BottomIf 如果窗口包藏住了兄弟窗口,这个窗口放在栈底。
Opposite 如果兄弟窗口包藏住了这个窗口,那么它放在栈顶。如果这个窗口包藏住了兄弟窗口,那么它放在栈底。
如果指定了 stack_mode 而没有指定兄弟窗口,那么这个窗口按照下列方式调整栈顺序:
Above 窗口正好放在兄弟窗口之上。
Below 窗口正好放在兄弟窗口之下。
TopIf 如果兄弟窗口包藏住了这个窗口,那么它放置在栈顶。
BottomIf 如果窗口包藏住了兄弟窗口,这个窗口放在栈底。
Opposite 如果兄弟窗口包藏住了这个窗口,那么它放在栈顶。如果这个窗口包藏住了兄弟窗口,那么它放在栈底。
尝试去配置根窗口将不会产生任何效果。
要配置窗口的大小、位置、栈序或者边框,使用 XConfigureWindow。
XConfigureWindow(display, w, value_mask, values )
Display *display ;
Window w ;
unsigned int value_mask ;
XWindowChanges *values ;
display 指定到 X server 的连接。
w 指定要被重新配置的窗口。
value_mask 指定值结构中的哪些值要被设置。这个掩码是有效的配置窗口值位的包括或(OR)位运算。
values 指定 XWindowChanges 结构。
XConfigureWindow 函数使用在 XWindowChanges 结构中指定的值来重新配置窗口的大小、位置、边框和栈序。没有指定的是从窗口的现有几何状态中拿过来的。
如果指定了兄弟窗口而没有 stack_mode 或者如果窗口实际上并不是兄弟窗口,会生成 BadMatch 错误。注意对于 BottomIf、TopIf 和 Oppsite 的计算都是遵照窗口最终的几何状态(由发送到 XConfigureWindow 的其它参数来控制)来执行的,而不是它的最初几何状态。窗口的任何回填内容,它的下层窗口以及其它新的可见窗口都要么被丢弃,要么被改变以反映出当前的屏 幕内容(取决于实现)。
XConfigureWindow 可以生成 BadMatch、BadValue、和 BadWindow 错误。
要移动窗口而不改变它的大小,使用 XMoveWindow。
XMoveWindow(display, w, x, y )
Display *display ;
Window w ;
int x, y ;
display 指定连接到的 X server。
w 指定要被移动的窗口。窗口的边框,如果窗口没有边框就是窗口自身。
x, y 指定定义窗口边框左上角像素新位置的 x 和 y 坐标,或者如果窗口没有边框,就是窗口自身。
XMoveWindow 函数移动指定的窗口到指定的 x 和 y 坐标,但是它并不改变窗口的大小,提升窗口或者改变窗口的映射状态。移动已映射的窗口可能会或者可能不会丢失窗口的内容,取决于窗口是否被非子窗口所遮档 以及是否没有回填存在。如果窗口的内容丢失,X server 生成 Expose 事件。移动已映射的窗口在任何以前被遮挡的窗口上生成 Expose 事件。
如果窗口的 override-redirect 是 False 并且一些其它的客户程序在父窗口上选择 SubstructureRedirectMask,X server 生成 ConfigureRequest 事件,并不执行进一步的处理。否则,窗口被移动。
XMoveWindow 可以生成 BadWindow 错误。
事件(Event)
一个事件是作为一些设备活动由 X server 异步生成的数据,或者作为由 Xlib 函数发送请求的副作用而产生的数据。
所有的事件结构都有下列的通用成员:
typedef struct {
int type;
unsigned long serial;
Bool send_event;
Display *display;
Window window;
} XAnyEvent;
除了为每一种事件类型声明单独的结构之外,XEvent 结构是一个为每一种事件类型而声明的单独结构的联合。取决于类型,你应当通过使用 XEvent 联合来访问每一个事件的成员。
typedef union _XEvent {
int type;
XAnyEvent xany;
XKeyEvent xkey;
XButtonEvent xbutton;
XMotionEvent xmotion;
XCrossingEvent xcrossing;
XFocusChangeEvent xfocus;
XExposeEvent xexpose;
XGraphicsExposeEvent xgraphicsexpose;
XNoExposeEvent xnoexpose;
XVisibilityEvent xvisibility;
XCreateWindowEvent xcreatewindow;
XDestroyWindowEvent xdestroywindow;
XUnmapEvent xunmap;
XMapEvent xmap;
XMapRequestEvent xmaprequest;
XReparentEvent xreparent;
XConfigureEvent xconfigure;
XGravityEvent xgravity;
XResizeRequestEvent xresizerequest;
XConfigureRequestEvent xconfigurerequest;
XCirculateEvent xcirculate;
XCirculateRequestEvent xcirculaterequest;
XPropertyEvent xproperty;
XSelectionClearEvent xselectionclear;
XSelectionRequestEvent xselectionrequest;
XSelectionEvent xselection;
XColormapEvent xcolormap;
XClientMessageEvent xclient;
XMappingEvent xmapping;
XErrorEvent xerror;
XKeymapEvent xkeymap;
long pad[24];
} XEvent;
Expose 事件
X server 会向希望得到窗口区域的内容何时丢失的客户程序报告 Expose 事件。其中 X server 生成 Expose 事件的情形并不像其它事件一样明确。不过,X server 从不对你指定为 InputOnly 的窗口生成 Expose 事件。X server 可以在窗口的区域没有可用的内容时生成 Expose 事件,并且区域要么是可见的,区域是可视的并且 server 正在(可能是最近)维护窗口上的存储回填,要么窗口是不可视的,但 server 正在(可能是最近)尊重了窗口的 Always 或者 WhenMapped 存储回填属性。区域分解成一组(或任意多组)矩形框,并且为每一个矩形生成一个 Expose 事件。对于任何已有的窗口,X server 保证连续地报告由一些引起 Expose 事件的动作,例如像提升窗口,所暴露的全部区域。
要接收 Expose 事件,将窗口的事件掩码属性中的 ExposureMask 置位。
这个事件类型的结构包含:
typedef struct {
int type;
unsigned long serial;
Bool send_event;
Display *display;
Window window;
int x, y;
int width, height;
int count;
} XExposeEvent;
窗口成员被设置为暴露的(危险的)窗口。成员 x 和 y 设置为相对于窗口中心的坐标,并且表示矩形的左上角。成员 width 和 height 被置为矩形的大小(范围)。成员 count 设置为随后紧跟的 Expose 事件的数目。如果 count 是零,就表示没有更多的 Expose 事件紧随这个窗口。不过,如果 count 不是零,紧随这个窗口的 Expose 事件的数目至少是有一个。并不想去优化区别其窗口子区域重新显示的简单应用程序只是忽略所有 count 值为非零的 Expose 事件并且对 count 为零的事件执行全部的重新显示。
事件队列管理
Xlib 维护一个事件队列。不过,操作系统也可能在其还没有读入事件队列的网络连接中缓冲数据。
要检查事件队列中的事件数目,使用 XEventsQueued。
int XEventsQueued(display, mode )
Display *display ;
int mode ;
display 指定到 X server 的连接。
mode 指定模式。你可以发送 QueuedAlready、QueuedAfterFlush、或者 QueuedAfterReadiing。
如果模式是 QueuedAlready,XEventsQueued 返回已经在事件队列中的事件数目(并且从不执行系统调用)。如果模式是 QueuedAfterFlush,如果已经在事件队列中的事件数目不是零的话,XEventsQueued 返回它。如果没有事件在队列中,XEventsQueued 刷新输出缓冲区,尝试将更多的事件从应用程序的连接中读出,并且返回读取的数目。如果 mode 是 QueuedAfterReading,并且如果已经在队列中的事件数目不是零,XEventsQueued 返回它。如果队列中没有事件,XEventsQueued 尝试从应用程序的连接中读出更多的事件但并不刷新输出缓冲区,并且返回读出的数目。
如果队列中已有事件,XEventsQueued 总是立即返回而无须任何 I/O。模式为 QueuedAfterFlush 的 XEventsQueued 在行为上与 XPending 是一致的。模式为 QueuedAlready 的 XEventsQueued 与 XQLength 函数一致。
用 XPending 返回未决的事件数目,
int XPending(display )
Display *display ;
display 指定到 X server 的连接。
XPending 函数返回已经从 X server 接收到但还没有从队列中移出的事件数目。XPending 与模式指定为 QueuedAfterFlush 的 XEventsQueued 一致。
维护事件队列
Xlib 提供用于维护事件队列的函数。包括三部分:
- 获取事件,随后将它们从队列中移出
- 在队列中窥探事件而不用将它们移出
- 获取与事件掩码或者你提供的任意断言过程匹配的事件
1. 返回下一个事件
要得到下一个事件并将其从队列中移出,使用 XNextEvent。
XNextEvent(display, event_return )
Display *display ;
XEvent *event_return ;
display 指定到 X server 的连接。
event_return 返回队列中的下一个事件。
XNextEvent 函数从事件队列拷贝第一个事件到指定的 XEvent 结构中,然后将这个事件从队列中移出。如果事件队列是空的,XNextEvent 刷新输出缓冲区并阻塞直到收到事件。
使用 XPeekEvent 来窥探事件队列。
XPeekEvent(display, event_return )
Display *display ;
XEvent *event_return ;
display 指定到 X server 的连接。
event_return 返回与相关结构匹配事件的一个副本。
XPeekEvent 函数从队列中返回第一个事件,但是它并不将事件从队列中移出。如果队列为空,XPeekEvent 刷新输出缓冲区并阻塞直到接收到事件。它随后将事件复制到客户程序补充的 XEvent 结构中,而不将事件从事件队列中移出。
2. 使用断言过程选择事件
在这一部分中讨论的每一个函数都要求你传送一个断言过程来判断某个事件是否与你想要的相匹配。你的断言过程必须决定事件是否有用,并且绝不能调用 Xlib 函数。特别是,断言过程是从事件程序内部调用的,这个事件程序必须锁定数据结构以使事件队列在多线程环境中是一致的。
断言过程以及其相关的参数是:
Bool (*predicate)(display, event, arg )
Display *display ;
XEvent *event ;
char *arg ;
display 指定到 X server 的连接。
event 指定 XEvent 结构。
arg 指定从 XIfEvent、XCheckIfEvent、或者 XPeekIfEvent 函数中传送入的参数。
队列中的每一个事件都调用断言过程一次,直到它找到一个匹配的事件。找到匹配的以后,断言过程必须返回 True。如果它不能找到匹配的,必须返回 False。
为了匹配事件,要检查事件队列,并且如果找到,用 XIfEvent 将事件从队列中移出。
XIfEvent(display, event_return, predicate, arg )
Display *display ;
XEvent *event_return ;
Bool (*predicate )();
char *arg ;
display 指定到 X server 的连接。
event_return 返回与相关结构匹配的事件。
predicate 指定用来判断队列中的下一个事件是否匹配你所要的东西的过程。
arg 指定由用户补充,将会发送给断言过程的参数。
XIfEvent 函数只有当指定的断言过程为这个事件返回 True 才算完成,这表示队列中有一个事件匹配。如果 XIfEvent 阻塞等待另外的事件,那么它刷新输出缓冲区。XIfEvent 将匹配的事件从队列中移去并且将结构复制到客户程序补充的 XEvent 结构。
为匹配事件检查队列,而并不阻塞的话,使用 XCheckIfEvent。
Bool XCheckIfEvent(display, event_return, predicate, arg )
Display *display ;
XEvent *event_return ;
Bool (*predicate )();
char *arg ;
display 指定到 X server 的连接。
event_return 返回与相关结构匹配的事件副本。
predicate 指定用来判断队列中下一个事件是否匹配你想要的东西的过程。
arg 指定由用户补充,将发送给断言过程的参数。
当断言过程找到一个匹配,XCheckIfEvent 复制匹配的事件到客户程序补充的 XEvent 结构中并且返回 True。(这个事件已经从队列中移出。)如果断言过程没有找到匹配的事件,XCheckIfEvent 返回 False,并且输出缓冲区将被刷新。所有早先存放在队列中的事件并不会被丢弃。
为了匹配事件检查队列而又不用将事件从队列中移出,用 XPeekIfEvent。
XPeekIfEvent(display, event_return, predicate, arg)
Display *display;
XEvent *event_return;
Bool (*predicate)();
char *arg;
3. 使用窗口或者事件掩码选择事件
XWindowEvent(display, w, event_mask, event_return )
Display *display ;
Window w ;
long event_mask ;
XEvent *event_return ;
Bool XCheckWindowEvent(display, w, event_mask, event_return )
Display *display ;
Window w ;
long event_mask ;
XEvent *event_return ;
XMaskEvent(display, event_mask, event_return )
Display *display ;
long event_mask ;
XEvent *event_return ;
Bool XCheckMaskEvent(display, event_mask, event_return )
Display *display ;
long event_mask ;
XEvent *event_return ;
Bool XCheckTypedEvent(display, event_type, event_return )
Display *display ;
int event_type ;
XEvent *event_return ;
Bool XCheckTypedWindowEvent(display, w, event_type, event_return )
Display *display ;
Window w ;
int event_type ;
XEvent *event_return ;
将事件放回队列
要将事件放回事件队列,使用 XPutBackEvent。
XPutBackEvent(display, event )
Display *display ;
XEvent *event ;
XPutBackEvent 函数通过将事件复制到队列中从而将事件放置在显示的事件队列的前头。如果你读取事件,然后决定你宁愿稍候再去处理它,那么这个函数可能比较有用。对你可以连续调用 XPutBackEvent 的次数没有限制。
发送事件给其它应用程序
要发送事件给指定的窗口,使用 XSendEvent。这个函数通常在 selection 的处理中。例如,当 selection 已经被转换并且作为 property 储存的时候,selection 的拥有者应当使用 XSendEvent 来发送 SelectionNotify 事件给请求者。
Status XSendEvent(display, w, propagate, event_mask, event_send )
Display *display ;
Window w ;
Bool propagate ;
long event_mask ;
XEvent *event_send ;
display 指定到 X server 的连接。
w 指定事件要发送到的窗口、PointerWindow、或者 InputFocus。
propagate 指定一个布尔值。
event_mask 指定事件掩码。
event_send 指定要发送的事件。
XSendEvent 函数标识目的地窗口,判断哪一个客户程序应当接受指定的事件,并且忽略任何活动的抓取。这个函数要求你发送一个事件掩码。这个函数使用 w 参数所标识的目的地窗口如下:
- 如果 w 是 PointerWindow,目的地窗口就是包含鼠标指针的窗口。
- 如果 w 是 InputFocus 并且如果焦点窗口包含鼠标指针的话,目的地窗口就是包含鼠标指针的窗口;否则,目的地窗口就是焦点窗口。
要判断哪一个客户程序应当接收指定的事件,XSendEvent 对于传播参数的使用如下:
- 如果 event_mask 是空集,事件应当发送给创建目的地窗口的客户程序。如果客户程序不再存在,就不用发送事件。
- 如果 propagate 是 False,事件就会被发送给将目的地选择为 event_mask 参数中任何事件类型的每一个客户程序。
- 如果 propagate 是 True 并且没有客户程序已经将目的地选择为 event_mask 中的任何事件类型,那么对于一些已经选择了 event_mask 中类型的客户程序,并且对于类型在其 do-not-propagate-mask 中的不可干预窗口而言,目的地应当被最接近于目的地的祖先所取代。如果没有这样的窗口存在,或者说如果窗口是焦点窗口的祖先,并且 InputFocus 最初被指定作为目的地,那么事件不会发送给任何客户程序。否则,事件会被传播到将最终的目的地选择为 event_mask 中指定类型的每一个客户程序。
如果到包装协议格式的转换失败的话, XSendEvent 返回零,否则返回非零。
XSendEvent 可以生成 BadValue 和 BadWindow 错误。
KeyPress、KeyRelease、ButtonPress、ButtonRelease、MotionNotif
root, event: WINDOW
child: WINDOW or None
same-screen: BOOL
root-x, root-y, event-x, event-y: INT16
detail: <see below>
state: SETofKEYBUTMASK
time: TIMESTAMP
在某个键或者鼠标按钮逻辑上改变了状态的时候,或者在鼠标的指针逻辑上移动位置的时候生成这些事件。如果设备事件处理被冻结的话,这些逻辑改变的生成可能 滞后于物理上的改变。注意 所有的键都生成 KeyPress 和 KeyRelease,尽管这些键映射到编辑位。事件的来源是鼠标指针指进的窗口。接到报告相关事件的窗口是被调用的事件窗口。事件窗口是通过从源窗口开 始并且从第一个客户程序在其上选择有兴趣的事件(所提供的没有干预的窗口阻止通过在其 do-not-propgagate-mask 中包含事件类型进行的事件生成)的窗口开始逐层进行查找而来的。用于报告的实际窗口可以通过活动捕获来进行修改,并且在键盘事件的情况下,可以通过焦点窗 口来修改。
root 是源窗口的根窗口,并且 root-x 和 root-y 都是在事件发生时刻相对于根原点的鼠标指针坐标。事件是事件窗口。如果事件窗口在同一个屏幕上是作为根窗口,那么 event-x 和 event-y 都是相对于事件窗口原点的鼠标指针坐标。否则,event-x 和 event-y 都是零。如果源窗口是事件窗口的下级窗口,那么孩子就被设置成作为源窗口祖先的事件窗口的孩子。否则,它被设置为 None。状态组件给出了事件之前的按钮和编辑键的逻辑状态。细节的组件类型随事件类型而变化:
Event |
Component |
KeyPress, KeyRelease |
KEYCODE |
ButtonPress, ButtonRelease |
BUTTON |
MotionNotify |
{Normal, Hint} |
MotionNotify 事件只是在窗口中的运动开始和结束时生成。运动事件的粒度并不能保证,但是客户程序对运动事件的选择保证在鼠标指针移动并且休息的时候至少得到一个事件。 选择 PointerMotion 接收事件独立于鼠标指针的状态。通过选择一些 Button[1-5]Motion 的子集来代替,MotionNotify 事件将只会在一个或者多个指定的按钮被按下时才会接收到。通过选择 ButtonMotion,MotionNotify 事件将只是在至少有一个按钮被按下时就会接收到。类型总是 MotionNotify 的事件独立于选择。如果 PointerMotionHint 被选择,那么服务器会自由地只为事件窗口发送一个 MotionNotify 事件(带有详细的 Hint)给客户程序,指针离开事件窗口,或者客户程序发起 QueryPointer 或者 GetMotionEvents 请求。
选择事件
有两种途径来选择你希望报告给你的客户程序的事件。一是在你调用 XCreateWindow 和 XChangeWindowAttributes 的时候设置 XSetWindowAttributes 结构的 event_mask 成员。另一个是使用 XSelectInput。
XSelectInput(display, w, event_mask)
Display *display;
Window w;
long event_mask;
XSelectionInput 函数请求 X server 提供与指定的事件掩码相关的事件。最初,X 将不会报告任何这些事件。报告与窗口相关的事件。如果窗口并不对设备事件感兴趣,它通常传播到感兴趣的最近的祖先,除非 do-not-propagate 掩码禁止了它。
设置窗口的事件掩码属性会覆盖任何以前对相同窗口的属性设置调用,而不是对其它的客户程序。多个客户程序可能对相同窗口选择相同的事件有以下的限制:
- 多个客户程序在相同的窗口上选择事件,因为它们的事件掩码是不脱节的。当 X server 生成一个事件时,它会将这个事件报告给所有感兴趣的客户程序。
- 一次只有一个客户程序可以选择 CirculateRequest、ConfigureRequest 或者 MapRequest 事件,这些是与事件掩码 SubstructureRedirectMask 相关的。
- 一次只有一个客户程序可以选择 ResizeRequest 事件,它与事件掩码 ResizeRedirectMask 相关。
- 一次只有一个客户程序可以选择与事件掩码 ButtonPressMask 相关的 ButtonPress 事件。
服务器报告事件给所有感兴趣的客户程序。
XSelectInput 可以生成一个 BadWindow 错误。
鼠标指针的剥夺
Xlib 提供了用于控制来自指针输入的函数,通常是鼠标的指针。窗口管理器最经常使用这些功能来实现某些用户接口的风格。一些工具箱也需要将这些功能用于某些特殊用途。
通常,一旦键盘和鼠标事件发生,X server 将它们递送给恰当的客户程序,这个客户程序由窗口和输入焦点所决定。X server 在事件递送方面提供足够的控制以允许窗口管理器来进一步支持鼠标以及不同的其它用户接口风格。许多这些用户接口中取决于事件的同步递送,鼠标指针和键盘事 件的递送可以单独地进行控制。
当鼠标按钮或者键盘按键被剥夺后,事件将被发送到剥夺它们的客户程序而不是正常情况下应该接收到它们的客户程序。如果键盘或者鼠标指针处于异步模式下,后 续的鼠标和键盘事件将继续被处理。如果键盘或者鼠标指针处于同步模式,就没有进一步的事件要处理,直到剥夺的客户程序允许事件(参见 XAllowEvents)。在这段时间间隔内,键盘和鼠标指针认为是被冻结的。触发这个剥夺的事件也可以重放。
注意如果设备事件处理被冻结的话,设备的逻辑状态(正如客户应用程序所看到)可能滞后于其物理状态 。
有两种类型的剥夺:主动的和被动的。主动的剥夺发生在单个的客户显式地剥夺键盘或者鼠标指针(参见 XGrabPointer 和 XGrabKeyboard)的时候。被动的剥夺发生在客户程序在某个窗口中剥夺某个特定的键盘按键或者鼠标按钮,并且剥夺将会在这个按键或者按钮真正被 按下的时候被激活。被动剥夺对于实现可靠的弹出菜单来说是很便利的。例如,你可以保证弹出是通过剥夺某个按钮请求的同步行为在松开指针按钮之前被映射。按 下事件将会触发剥夺并冻结指针事件的进一步处理,直到你有机会去映射弹出窗口。
对于许多操作来说,有一些函数接受一个时间参数。X server 在不同的事件中包括一个时间戳。有一个称为 CurrentTime 的特殊时间表示当前的服务器时间。X server 在输入焦点最后被改变时,在键盘最后被剥夺时,在鼠标指针最后被剥夺时,或者在某个 selection 最后被改变时维护这个时间。你的应用程序对某个事件的反应可能会慢。你常常需要用一些办法来指定在另一个应用程序控制键盘、指针或者 selection 的期间,你的请求不应当发生。通过在请求中提供来自事件的时间戳,如果某个人在这期间已经另外执行了一个操作,你可以安排这个操作并不生效。
时间戳是一个以毫秒表达的时间值。它一般是自最后一次服务器重置起的时间。时间戳评估时间段(大约在 49.7 天之后)。服务给出它的由时间戳 T 表示的当前时间,总是通过将时间戳空间的一半视为在时间上比 T 要迟的方式解释来自客户程序的时间戳。命名为 CurrentTime 的时间戳值从不会由服务器生成。这个值被保留用于表示当前服务器时间的请求中。
对于在这一部分的许多函数中,你发送鼠标指针事件掩码位。有效的指针事件掩码位是:ButtonPressMask、 ButtonReleaseMask、EnterWindowMask、LeaveWindowMask、PointerMotionMask、 PointerMotionHintMask、Button1MotionMask、Button2MotionMask、 Button3MotionMask、Button4MotionMask、Button5MotionMask、ButtonMotionMask 以及 KeyMapStateMask。对于本部分中的其它函数,它发送按键掩码位。有效的按键掩码位是:ShiftMask、LockMask、 ControlMask、Mod1Mask、Mod2Mask、Mod3Mask、Mod4Mask 以及 Mod5Mask。
要剥夺鼠标指针,使用 XGrabPointer。
int XGrabPointer(display, grab_window, owner_events, event_mask, pointer_mode, keyboard_mode, confine_to, cursor, time )
Display *display ;
Window grab_window ;
Bool owner_events ;
unsigned int event_mask ;
int pointer_mode , keyboard_mode ;
Window confine_to ;
Cursor cursor ;
Time time ;
display 指定到 X server 的连接。
grab_window 指定剥夺的窗口。
owner_events 指定一个布尔值表示是否照常报告指针事件或者如果是通过事件掩码选择的话,遵照剥夺窗口来报告。
event_mask 指定哪些指针事件要被报告给客户程序。掩码是有效鼠标指针事件掩码位的“或”(OR)位运算。
pointer_mode 指定进一步的鼠标指针事件处理。你可以发送 GrabModeSync 或者 GrabModeAsync。
keyboard_mode 指定键盘事件的进一步处理。你可以发送 GrabModeSync 或者 GrabModeAsync。
confine_to 指定限制鼠标指针的窗口或者 None。
cursor 指定在剥夺期间显示的光标或者 None。
time 指定时间,你可以发送一个时间戳或者 CurrentTime。
XGrabPointer 函数主动地剥夺鼠标指针的控制并且如果剥夺成功的话返回 GrabSuccess。进一步的鼠标指针事件只报告给进行剥夺的客户程序。XGrabPointer 通过这个客户程序覆盖任何主动的剥夺。如果 owner_events 是 False,报告关于 grab_window 的所有生成指针事件并且只报告通过 event_mask 选择的事件。如果 owner_events 是 True 并且如果生成的指针事件会正常地报告给这个客户程序的话,它就照常报告。否则,报告产于 grab_window 的事件并且只报告由 event_mask 选择的事件。对于 owner_events 的两个值的任何一个来说,没有报告的事件都被丢弃。
如果 pointer_mode 是 GrabModeAsync,指针事件处理照常继续。如果指针当前被这个客户程序冻结,对于指针的事件处理就被挂起。如果 pointer_mode 是 GrabModeSync,作为客户应用程序来看,指针的状态看来是冻结的,并且 X server 并不进一步地生成指针事件,直到进行剥夺的客户程序调用 XAllowEvents 或者直到指针的剥夺被释放。当鼠标指针被冻结时,实际上的指针改变并不会丢失;它们被简单地放置在服务器的队列中以备后来处理。
如果 keyboard_mode 是 GrabModeAsync,键盘事件处理不会因剥夺的激活所影响。如果 keyboard_mode 是 GrabModeSync,作为客户应用程序来看,键盘的状态看来是冻结的,并且 X server 不会生成进一步的事件,直到进行剥夺的客户程序调用 XAllowEvents 或者直到指针的剥夺被释放。当鼠标指针被冻结的时候,实际上的键盘改变并不会丢失;它们被简单地放置在服务器的队列中以备后来处理。
如果指定了光标,它就会被显示,而不管鼠标指针指在什么窗口内。如果指定的是 None,当指针指向 grab_window 或者其子窗口之一时,显示用于这个窗口的正常光标;否则,显示用于 grab_window 的光标。
如果指定了 confine_to 窗口,指针被限制停留在包含它的那个窗口内。这个 confine_to 窗口需要与 grab_window 没有任何关系。如果指针最初并不在 confine_to 窗口中,在剥夺激活之前它就自动地被最近的边界所弯曲,并且进入/离开事件照常生成。如果 confine_to 窗口随后被重新配置,指针就被自动弯曲,作为必要的条件,要将其保持在包含它的窗口中。
参数 time 允许你去避免某些在如果应用程序花费了较长的时间进行响应下或者如果有较长的网络延迟下出现的情况。考虑你有两个应用程序的情况,这两个应用程序在点击的 时候正常地剥夺指针。如果这两个应用程序指定了来自事件的时间戳,第二个应用程序可能会更快地唤醒并且在第一个程序之前成功地剥夺指针。第一个应用程序然 后将会得到一个其它应用程序在它的请求被处理之前已经剥夺了指针的标志。
XGrabPointer 生成 EnterNotify 和 LeaveNotify 事件。
如果 grab_window 或者 confine_to 窗口并不是可视的,或者如果 confine_to 窗口完全位于根窗口的边界之外,XGrabPointer 失败并且返回 GrabNotViewable。如果指针被某些其它的客户程序主动地剥夺,它会失败并且返回 AlreadyGrabbed。如果鼠标指针被其它应用程序的某个剥夺所冻结,它会失败并且返回 GrabFrozen。如果指定的时间早于 last-pointer-grab 时间或者迟于当前 X server 的时间,它会失败并且返回 GrabInvalidTime。否则,last-pointer-grab 时间被设置为指定的时间(CurrentTime 被当前 X server 时间所取代)。
XGrabPointer 可以生成 BadCursor、BadValue、和 BadWindow 错误。
要取消剥夺指针,使用 XUngrabPointer。
XUngrabPointer(display, time )
Display *display ;
Time time ;
display 指定到 X server 的连接。
time 指定时间。你可以发送一个时间戳或者 CurrentTime。
如果这个客户程序通过 XGrabPointer、XGrabButton、或者某个一般的键被按下来主动地剥夺,XUngrabPointer 函数释放指针和任何排进队列的事件。如果指定的时间早于 last-pointer-grab 或者晚于当前 X server 的时间,XUngrabPointer 并不释放指针。它也生成 EnterNotify 和 LeaveNotify 事件。如果主动的指针剥夺的事件窗口或者 confine_to 窗口变成不可视的或者如果窗口的重新配置引起 confine_to 窗口完全处于根窗口的边界之外的话,X server 自动地执行一个 UngrabPointer 请求。
要改变激活的指针剥夺,使用 XChangeActivePointerGrab。
XChangeActivePointerGrab(display, event_mask, cursor, time )
Display *display ;
unsigned int event_mask ;
Cursor cursor ;
Time time ;
display 指定到 X server 的连接。
event_mask 指定哪些指针事件被报告给客户程序。掩码是有效指针事件掩码位的“或”(OR)运算。
cursor 指定要被显式的光标或者 None。
time 指定时间。你可以发送时间戳或者 CurrentTime。
如果鼠标指针主动地被客户程序所剥夺并且如果指定的时间不早于 last-pointer-grab 并且不晚于当前的 X server 的时间,那么 XChangeActivePointerGrab 函数改变指定的动态参数。这个函数对于 XGrabButton 的被动参数不起作用。对于 event_mask 和 cursor 的解释和在上面说明的 XGrabPointer 中一样。
XChangeActivePointerGrab 可以生成 BadCursor 和 BadValue 错误。
要剥夺鼠标指针的按钮,使用 XGrabButton。
XGrabButton(display, button, modifiers, grab_window, owner_events, event_mask, pointer_mode, keyboard_mode, confine_to, cursor)
Display *display;
unsigned int button;
unsigned int modifiers;
Window grab_window;
Bool owner_events;
unsigned int event_mask;
int pointer_mode, keyboard_mode;
Window confine_to;
Cursor cursor;
display 指定到 X server 的连接。
button 指定要被剥夺的指针按钮或者 AnyButton。
modifier 指定一组按键掩码或者 AnyModifier。这个掩码是有效的按键掩码位的“或”(OR)运算。
grab_window 指定剥夺窗口
owner_events 指定一个布尔值标识指针事件是否照常报告或者只报告由事件掩码选择的关于剥夺窗口的事件。
event_mask 指定哪些事件要报告给客户程序。掩码是有效指针事件掩码位的或(OR)运算。
pointer_mode 指定鼠标指针事件的进一步处理。你可以发送 GrabModeSync 或者 GrabModeAsync。
keyboard_mode 指定键盘事件的进一步处理。你可以发送 GrabModeSync 或者 GrabModeAsync。
confine_to 指定窗口以限制鼠标指针的指向或者 None。
cursor 指定要被显示的光标或者 None。
XGrabButton 函数建立一个被动的剥夺。以后,指针被主动剥夺(用 XGrabPointer),last-pointer-grab 时间被设置为按钮被按下的时间(在 ButtonPress 事件中传送),并且如果下列所有的条件值都是真的话,ButtonPress 事件就被报告:
- 指针没有被剥夺,并且指定的按钮逻辑上是当指定的编辑(modifier)键被逻辑上按下时才被按下,并且没有其它按钮或者编辑键逻辑上被按下。
- 这个 grab_window 包含鼠标指针。
- 这个 confine_to 窗口(如果有)是可视的。
- 在相同的按钮/按键组合上的被动剥夺并不会存在于 grab_window 的任何祖先窗口上。
剩余参数的解释与 XGrabPointer 一样。主动的剥夺在当鼠标指针的逻辑状态上所有的按钮都被释放时自动地终止(独立于逻辑编辑键的状态)。
注意如果设备事件处理被冻结的话,设备的逻辑状态(作为客户应用程序来看)可能滞后于物理状态。
这个请求会覆盖所有通过相同的客户程序在相同窗口上的用相同的按钮/按键组合产生的所有以前的剥夺。值为 AnyModifier 的编辑标志等于发起对所有可能编辑标志组合(包括非编辑标志的组合)的剥夺。并不要求指定的所有编辑标志当前都被指派了 KeyCode。值为 AnyButton 的按钮等于发起对所有可能按钮的请求。否则不要求指定的按钮当前被指派了一个物理按钮。
如果一些其它的客户程序已经用相同的按钮/按键组合在相同的窗口上发起了一个 XGrabButton,那么会产生一个 BadAccess 错误。当使用 AnyModifier 或者 AnyButton 时,如果有对于任何组合有冲突的剥夺的话,那么请求会完全失败,并且产生一个 BadAccess 错误(没有建立起剥夺)。XGrabButton 对于主动的剥夺没有作用。
XGrabButton 可以生成 BadCursor、BadValue、以及 BadWindow 错误。
要取消对指针按钮的剥夺,使用 XUngrabButton。
XUngrabButton(display, button, modifiers, grab_window)
Display *display;
unsigned int button;
unsigned int modifiers;
Window grab_window;
display 指定到 X server 的连接。
button 指定 要被释放的按钮或者 AnyButton。
modifiers 指定一组按键掩码或者 AnyModifier。掩码是有效位按键掩码位的或(OR)运算。
grab_window 指定剥夺窗口。
如果窗口被这个客户程序剥夺的话,XUngrabButton 函数释放在指定窗口上的被动按钮/按键组合。值为 AnyModifier 的编辑标志等于发起对所有可能的编辑组合的取消剥夺请求。值为 AnyButton 的按钮等于发起对所有可能按钮的请求。XUngrabButton 对于主动的剥夺没有作用。
XUngrabButton 可以生成 BadValue 和 BadWindow 错误。
键盘剥夺
Xlib 提供用于剥夺和取消剥夺键盘以及允许事件的函数。
int XGrabKeyboard(display, grab_window, owner_events, pointer_mode, keyboard_mode, time )
Display *display ;
Window grab_window ;
Bool owner_events ;
int pointer_mode , keyboard_mode ;
Time time ;
display 指定到 X server 的连接。
grab_window 指定剥夺窗口。
owner_events 指定一个布尔值指示键盘事件是否照常报告。
pointer_mode 指定进一步的指针事件处理。你可以发送 GrabModeSync 或者 GrabModeAsync。
keyboard_mode 指定进一步的键盘事件处理。你可以发送 GrabModeSync 或者 GrabModeAsync。
time 指定时间。你可以发送一个时间戳或者 CurrentTime。
XUngrabKeyboard(display, time )
Display *display ;
Time time ;
display 指定到 X server 的连接。
time 指定时间。你可以发送时间戳或者 CurrentTime。
要被动地剥夺键盘的某个键,使用 XGrabKey。
XGrabKey(display, keycode, modifiers, grab_window, owner_events, pointer_mode, keyboard_mode)
Display *display;
int keycode;
unsigned int modifiers;
Window grab_window;
Bool owner_events;
int pointer_mode, keyboard_mode;
display 指定到 X server 的连接。
keycode 指定 KeyCode 或者 AnyKey。
modifiers 指定一组按键掩码或者 AnyModifer。掩码是有效按键掩码位的或(OR)运算。
grab_window 指定剥夺窗口。
owner_events 指定一个布尔值来标识键盘事件是否照常报告。
pointer_mode 指定对指针事件的进一步处理。你可以发送 GrabModeSync 或者 GrabModeAsync。
keyboard_mode 指定对键盘事件的进一步处理。你可以发送 GrabModeSync 或者 GrabModeAsync。
要取消剥夺一个按键,使用 XUngrabKey。
XUngrabKey(display, keycode, modifiers, grab_window )
Display *display ;
int keycode ;
unsigned int modifiers ;
Window grab_window ;
display 指定到 X server 的连接。
keycode 指定 KeyCode 或者 AnyKey。
modifiers 指定一组按键掩码或者 AnyModifier。掩码是有效按键掩码位的或(OR)运算。
grab_window 指定剥夺窗口。
为了允许进一步的事件在设置被冻结时可以被处理,使用 XAllowEvents。
XAllowEvents(display, event_mode, time)
Display *display;
int event_mode;
Time time;
display 指定到 X server 的连接。
event_mode 指定事件模式。你可以发送 AsyncPointer、SyncPointer、AsyncKeyboard、SyncKeyboard、ReplayPointer、 ReplayKeyboard、AsyncBoth、或者 SyncBoth。
time 指定时间。你可以发送一个时间戳或者 CurrentTime。
如果客户程序已经引起设备冻结,那么 XAllowEvents 函数释放一些放入队列的事件。如果指定的时间早于针对客户程序的最近一次主动剥夺的 last-grab-time 或者如果指定的时间晚于当前 X server 的时间,那么它没有作用。取决于 event_mode 参数,会发生下列的情况:
AsyncPointer |
如果指针是被客户程序所冻结,指针事件处理照常继续。如果指针被客户程序以两次分开的剥夺的形式冻结两次,AsyncPointer 为这两次冻结解冻。如果指针并没有被客户程序冻结,AsyncPointer 没有作用,但是指针不需要由客户程序来剥夺。 |
SyncPointer |
如果指针是被客户程序所冻结并且是主动地剥夺,指针事件处理照常继续,直到十一个 ButtonPress 或者 ButtonRelease 事件报告给客户程序。在此时,指针再次出现冻结。不过,如果报告的事件引起指针剥夺被释放,那么指针并不会冻结。如果指针没有被客户程序所冻结或者如果指 针没有被客户程序所剥夺,那么 SyncPointer 就没有作用。 |
ReplayPointer |
如果指针是被客户程序主动地剥夺,并且作为发送到客户程序的某个事件的结果而被冻结(来自 XGrabButton 的激活或者来自前一个具有模式 SyncPointer 的 XAllowEvents,而不是来自 XGrabPointer),那么指针剥夺被释放并且事件被完全重新处理。不过,此时,函数忽略在剥夺刚刚被释放的 grab_window 或者其上(到根窗口)窗口的任何被动的剥夺。如果指针并没有被客户程序剥夺或者如果指针并没有作为某个事件的结果而被冻结,那么请求就没有作用。 |
AsyncKeyboard |
如果键盘是被客户程序所冻结,键盘事件处理照常继续。如果键盘被客户程序以两次分开的剥夺的形式冻结两次,AsyncKeyboard 为这两次冻结解冻。如果键盘并没有被客户程序冻结,AsyncKeyboard 没有作用,但是指针不需要由客户程序来剥夺。 |
SyncKeyboard |
如果键盘是被客户程序所冻结并且是主动地剥夺,键盘事件处理照常继续,直到十一个 KeyPress 或者 KeyRelease 事件报告给客户程序。在此时,键盘再次出现冻结。不过,如果报告的事件引起键盘剥夺被释放,那么键盘并不会冻结。如果键盘没有被客户程序所冻结或者如果键 盘没有被客户程序所剥夺,那么 SyncKeyboard 就没有作用。 |
ReplayKeyboard |
如果键盘是被客户程序主动地剥夺,并且作为发送到客户程序的某个事件的结果而被冻结(来自 XGrabKeyboard 的激活或者来自前一个具有模式 SyncKeyboard 的 XAllowEvents,而不是来自 XGrabKeyboard),那么指针剥夺被释放并且事件被完全重新处理。不过,此时,函数忽略在剥夺刚刚被释放的 grab_window 或者其上(到根窗口)窗口的任何被动的剥夺。如果键盘并没有被客户程序剥夺或者如果键盘并没有作为某个事件的结果而被冻结,那么请求就没有作用。 |
SyncBoth |
如果指针和键盘两者都是被客户程序所冻结,对于这两个设备的事件处理照常,直到下一个 ButtonPress、ButtonRelease、KeyPress、或者 KeyRelease 事件报告给为了剥夺某个设备(按钮事件用于指针,按键事件用于键盘)的客户程序,在此时设备再次出现冻结。不过,如果报告的事件引起剥夺被释放,然后设备 并不冻结(但是如果其它的设备仍然被剥夺的话,那么对于它的后续事件将仍然引起这两个设备冻结)。SyncBoth 没有影响,除非指针和键盘都被客户程序冻结。如果指针或者键盘是被客户程序以两次分开的剥夺的方式冻结两次的话,SyncBoth 为这两次冻结解冻(但是以后对于 SyncBoth 的冻结将只冻结每个设备一次)。 |
AsyncBoth |
如果指针和键盘都是被客户程序所冻结的话,对于这两个设置的事件处理照常。如果某个设备被客户程序以两次分开剥夺的方式冻结了两次的话,AsyncBoth 为这两次冻结解冻。AsyncBoth 没有影响,除非指针和键盘都是被客户程序所冻结。 |
AsyncPointer、SyncPointer、和 ReplayPointer 对于键盘事件的处理没有作用。AsyncKeyboard、SyncKeyboard、和 ReplayKeyboard 对于指针事件的处理没有作用。同时激活对于指针的剥夺和键盘的剥夺(被相同或者不同的客户程序)这两者是可能的。如果某个设备以其中任何一种剥夺方式的行 为而被冻结,对于这个设备而言没有事件处理要被执行。对于单个设备而言因为两种剥夺而被冻结是有可能的。在这种情况下,在事件可以被再次处理之前,代表两 种剥夺的冻结必须被释放。如果某个设备被单个客户程序冻结两次,那么单个的 AllowEvents 释放这两次冻结。
XAllowEvents 可以生成 BadValue 错误。
服务器的剥夺
Xlib 提供用于剥夺和取消剥夺服务器的函数。这些函数可以用于控制通过窗口系统服务器到其它连接上的输出处理。当服务器被剥夺时,并没有请求的处理或者任何其它 连接关闭会发生。客户程序对其连接的自动关闭取消对服务器的剥夺。尽管非常不鼓励剥夺服务器,它有时是必要的。
要剥夺服务器,使用 XGrabServer。
XGrabServer(display)
Display *display;
display 指定到 X server 的连接。
XGrabServer 函数禁止对请求的处理并且关闭除这个请求所到达的连接之外的所有其它连接。除非绝对必要,你不应当剥夺服务器。
要取消剥夺服务器,使用 XUngrabServer。
XUngrabServer(display)
Display *display;
display 指定到 X server 的连接。
XUngrabServer 函数重新开始请求的处理并关闭其它的连接。你应当尽可能地避免剥夺服务器。