Xlib 扩展开发指南
1. 扩展概述
在 X 系统里,核心协议能够借助扩展来实现功能的演进。所以,扩展不应被视为二等公民,在未来,你喜爱的扩展或许会成为 X 标准的一部分。为了让扩展的使用与核心协议的使用几乎没有差别,扩展应采用惰性评估机制,在首次被调用时自动完成初始化,避免在应用程序里显式地对其进行初始化。
同时要注意,一个 X 扩展通常由多个请求构成,把 10 个新特性定义成 10 个独立的扩展并非好做法,而应该将它们封装成一个扩展,利用次要操作码来区分不同的请求。编写 Xlib 存根所需的符号和宏在
<X11/Xlibint.h>
中列出。
2. 基本协议支持例程
扩展的基本协议请求主要有
XQueryExtension
和
XListExtensions
这两个函数。
2.1 XQueryExtension 函数
Bool XQueryExtension(Display *display, const char *name, int *major_opcode_return, int *first_event_return, int *first_error_return);
-
display:指定与 X 服务器的连接。 -
name:指定扩展的名称。 -
major_opcode_return:返回主操作码。 -
first_event_return:若存在,返回第一个事件代码。 -
first_error_return:若存在,返回第一个错误代码。
该函数用于判断指定名称的扩展是否存在。若扩展不存在,返回
False
;若存在,则返回
True
。若扩展存在,会将扩展的主操作码返回给
major_opcode_return
;若不存在,则返回 0。次要操作码和请求格式因扩展而异。若扩展涉及额外的事件类型,会将基本事件类型代码返回给
first_event_return
;若不涉及,则返回 0。事件的格式由扩展决定。若扩展涉及额外的错误代码,会将基本错误代码返回给
first_error_return
;若不涉及,则返回 0。错误中额外数据的格式也由扩展决定。
需要注意的是,如果扩展名称不是以主机可移植字符编码表示,结果将取决于具体实现。并且,大小写是有区别的,像 “thing”、”Thing” 和 “thinG” 会被视为不同的名称。
2.2 XListExtensions 函数
char **XListExtensions(Display *display, int *nextensions_return);
-
display:指定与 X 服务器的连接。 -
nextensions_return:返回列出的扩展数量。
此函数会返回服务器支持的所有扩展的列表。若服务器返回的数据采用拉丁可移植字符编码,那么返回的字符串将采用主机可移植字符编码;否则,结果取决于具体实现。
2.3 XFreeExtensionList 函数
void XFreeExtensionList(char **list);
-
list:指定扩展名称列表。
该函数用于释放
XListExtensions
分配的内存。
下面是这些函数的调用流程 mermaid 流程图:
graph TD;
A[开始] --> B[调用 XQueryExtension];
B --> C{扩展是否存在};
C -- 是 --> D[获取主操作码、事件代码、错误代码];
C -- 否 --> E[返回 False];
D --> F[调用 XListExtensions];
F --> G[获取扩展列表];
G --> H[调用 XFreeExtensionList 释放内存];
H --> I[结束];
E --> I;
3. 挂钩到 Xlib
这些函数可让你挂钩到库中,通常不是应用程序程序员使用,而是由需要扩展核心 X 协议和 X 库接口的人员使用。生成 X 协议请求的函数一般被称为存根。
在扩展中,存根首先要检查自身是否已在某个连接上完成初始化,若未初始化,则应调用
XInitExtension
尝试在该连接上进行初始化。
3.1 XExtCodes 结构体
typedef struct _XExtCodes { /* public to extension, cannot be changed */
int extension; /* extension number */
int major_opcode; /* major op-code assigned by server */
int first_event; /* first event number for the extension */
int first_error; /* first error number for the extension */
} XExtCodes;
该结构体用于返回
XInitExtension
的信息,在
<X11/Xlib.h>
中定义。
3.2 XInitExtension 函数
XExtCodes *XInitExtension(Display *display, const char *name);
-
display:指定与 X 服务器的连接。 -
name:指定扩展的名称。
此函数用于判断指定名称的扩展是否存在。若存在,会为维护该连接上的扩展信息分配存储空间,将其链接到该连接的扩展列表中,然后返回存根实现者访问该扩展所需的信息;若扩展不存在,则返回
NULL
。同样,若扩展名称不是以主机可移植字符编码表示,结果将取决于具体实现,且大小写有别。
XExtCodes
结构体中的扩展编号在后续调用中是必需的,该编号仅在单个连接中是唯一的。
3.3 XAddExtension 函数
XExtCodes *XAddExtension(Display *display);
-
display:指定与 X 服务器的连接。
对于本地 Xlib 扩展,该函数会分配
XExtCodes
结构体,增加扩展编号计数,并将扩展链接到扩展列表中,这样就可以在不要求服务器扩展的情况下对 Xlib 进行扩展。
下面是挂钩到 Xlib 的操作步骤列表:
1. 存根检查是否在连接上初始化。
2. 若未初始化,调用
XInitExtension
进行初始化。
3. 根据需要使用
XAddExtension
进行本地扩展。
Xlib 扩展开发指南
4. 库的钩子函数
这些函数允许你定义在各种情况下被调用的过程,包括图形上下文(GC)的创建、复制、释放,字体的创建和释放,扩展定义的事件在网络格式和主机格式之间的转换,以及错误处理等。所有这些函数都会返回之前为该扩展定义的过程。
4.1 XESetCloseDisplay 函数
int XESetCloseDisplay(Display *display, int extension, int (*proc)());
-
display:指定与 X 服务器的连接。 -
extension:指定扩展编号。 -
proc:指定在关闭显示时调用的过程。
该函数定义了在调用
XCloseDisplay
时要调用的过程,返回之前定义的过程,通常为
NULL
。当
XCloseDisplay
被调用时,你的过程会以如下参数被调用:
int (*proc)(Display *display, XExtCodes *codes);
4.2 XESetCreateGC 函数
int *XESetCreateGC(Display *display, int extension, int (*proc)());
-
display:指定与 X 服务器的连接。 -
extension:指定扩展编号。 -
proc:指定在创建新 GC 时调用的过程。
此函数定义了在创建新 GC 时要调用的过程,返回之前定义的过程,通常为
NULL
。当创建 GC 时,你的过程会以如下参数被调用:
int (*proc)(Display *display, GC gc, XExtCodes *codes);
4.3 XESetCopyGC 函数
int *XESetCopyGC(Display *display, int extension, int (*proc)());
-
display:指定与 X 服务器的连接。 -
extension:指定扩展编号。 -
proc:指定在复制 GC 组件时调用的过程。
该函数定义了在复制 GC 时要调用的过程,返回之前定义的过程,通常为
NULL
。当复制 GC 时,你的过程会以如下参数被调用:
int (*proc)(Display *display, GC gc, XExtCodes *codes);
4.4 XESetFreeGC 函数
int *XESetFreeGC(Display *display, int extension, int (*proc)());
-
display:指定与 X 服务器的连接。 -
extension:指定扩展编号。 -
proc:指定在释放 GC 时调用的过程。
此函数定义了在释放 GC 时要调用的过程,返回之前定义的过程,通常为
NULL
。当释放 GC 时,你的过程会以如下参数被调用:
int (*proc)(Display *display, GC gc, XExtCodes *codes);
4.5 XESetCreateFont 函数
int *XESetCreateFont(Display *display, int extension, int (*proc)());
-
display:指定与 X 服务器的连接。 -
extension:指定扩展编号。 -
proc:指定在创建字体时调用的过程。
该函数定义了在调用
XLoadQueryFont
和
XQueryFont
时要调用的过程,返回之前定义的过程,通常为
NULL
。当调用
XLoadQueryFont
或
XQueryFont
时,你的过程会以如下参数被调用:
int (*proc)(Display *display, XFontStruct *fs, XExtCodes *codes);
4.6 XESetFreeFont 函数
int *XESetFreeFont(Display *display, int extension, int (*proc)());
-
display:指定与 X 服务器的连接。 -
extension:指定扩展编号。 -
proc:指定在释放字体时调用的过程。
此函数定义了在调用
XFreeFont
时要调用的过程,返回之前定义的过程,通常为
NULL
。当调用
XFreeFont
时,你的过程会以如下参数被调用:
int (*proc)(Display *display, XFontStruct *fs, XExtCodes *codes);
4.7 XESetWireToEvent 函数
int *XESetWireToEvent(Display *display, int event_number, int (*proc)());
-
display:指定与 X 服务器的连接。 -
event_number:指定事件代码。 -
proc:指定在转换事件时调用的过程。
该函数定义了在将事件从网络格式(
xEvent
)转换为主机格式(
XEvent
)时要调用的过程。事件编号定义了要为哪个协议事件编号安装转换过程。
XESetWireToEvent
返回之前定义的过程。你可以用自己的函数替换核心事件转换函数,但不建议这样做。当 Xlib 需要将事件从网络格式转换为主机格式时,你的过程会以如下参数被调用:
int (*proc)(Display *display, XEvent *re, xEvent *event);
你的过程必须返回状态以指示转换是否成功。
re
参数是指向应存储主机格式事件的位置的指针,
event
参数是 32 字节的网络事件结构。在你创建的
XEvent
结构中,必须填充事件结构的五个必需成员。你应该用为
xEvent
结构指定的类型填充
type
成员,并将所有其他成员从
xEvent
结构(网络格式)复制到
XEvent
结构(主机格式)。如果事件应放入队列,则转换过程应返回
True
;否则返回
False
。要初始化事件的序列号组件,请调用
_XSetLastRequestRead
并使用返回值。
4.8 _XSetLastRequestRead 函数
unsigned long _XSetLastRequestRead(Display *display, xEvent *rep);
-
display:指定与 X 服务器的连接。 -
rep:指定网络事件结构。
该函数从事件中的部分序列号计算并返回完整的序列号。
4.9 XESetEventToWire 函数
unsigned long *XESetEventToWire(Display *display, int event_number, unsigned long (*proc)());
-
display:指定与 X 服务器的连接。 -
event_number:指定事件代码。 -
proc:指定在转换事件时调用的过程。
此函数定义了在将事件从主机格式(
XEvent
)转换为网络格式(
xEvent
)时要调用的过程。事件编号定义了要为哪个协议事件编号安装转换过程。
XESetEventToWire
返回之前定义的过程。当 Xlib 需要将事件从主机格式转换为网络格式时,你的过程会以如下参数被调用:
unsigned long (*proc)(Display *display, XEvent *re, xEvent *event);
re
参数是指向主机格式事件的指针,
event
参数是指向应存储 32 字节网络事件结构的位置的指针。你应该用
XEvent
结构中的类型填充
type
,然后将所有其他成员从主机格式复制到
xEvent
结构。
4.10 XESetWireToError 函数
Bool *XESetWireToError(Display *display, int error_number, Bool (*proc)());
-
display:指定与 X 服务器的连接。 -
error_number:指定错误代码。 -
proc:指定在收到错误时调用的过程。
该函数定义了在将扩展错误从网络格式转换为主机格式时要调用的过程。错误编号定义了要为哪个协议错误代码安装转换过程。
XESetWireToError
返回之前定义的过程。当 Xlib 需要将错误从网络格式转换为主机格式时,过程会以如下参数被调用:
int (*proc)(Display *display, XErrorEvent *he, xError *we);
he
参数是指向应存储主机格式错误的位置的指针。
he
指向的结构保证与
XEvent
结构一样大,因此可以转换为比
XErrorEvent
更大的类型以存储额外的值。如果 Xlib 要完全忽略该错误(例如,几个协议错误结构将合并为一个 Xlib 错误),则函数应返回
False
;否则应返回
True
。
4.11 XESetError 函数
int *XESetError(Display *display, int extension, int (*proc)());
-
display:指定与 X 服务器的连接。 -
extension:指定扩展编号。 -
proc:指定在收到错误时调用的过程。
在 Xlib 内部,有时你可能希望在发生错误时抑制外部错误处理的调用。这允许在调用时返回状态,但代价是调用变为同步(不过大多数此类函数是查询操作,无论如何通常都会编程为同步)。当 Xlib 在
_XReply
中检测到协议错误时,你的过程会以如下参数被调用:
int (*proc)(Display *display, xError *err, XExtCodes *codes, int ret_code);
err
参数是指向 32 字节网络格式错误的指针。
codes
参数是指向扩展代码结构的指针。
ret_code
参数是你可能希望
_XReply
返回的值。如果你的过程返回 0 值,则不抑制错误,调用客户端的错误处理程序;如果返回非零值,则抑制错误,
_XReply
返回
ret_code
的值。
4.12 XESetErrorString 函数
char *XESetErrorString(Display *display, int extension, char (*proc)());
-
display:指定与 X 服务器的连接。 -
extension:指定扩展编号。 -
proc:指定获取错误字符串时调用的过程。
XGetErrorText
函数为用户返回错误的字符串。
XESetErrorString
允许你定义一个过程,该过程应返回指向错误消息的指针。例如:
int (*proc)(Display *display, int code, XExtCodes *codes, char *buffer, int nbytes);
对于检测到的每个错误,你的过程会以错误代码作为参数被调用。你应该将包含错误消息的以空字符结尾的字符串的
nbytes
个字符复制到
buffer
中。
4.13 XESetPrintErrorValues 函数
void *XESetPrintErrorValues(Display *display, int extension, void (*proc)());
-
display:指定与 X 服务器的连接。 -
extension:指定扩展编号。 -
proc:指定在打印错误时调用的过程。
该函数定义了在打印扩展错误时要调用的过程,用于打印错误值。用于包含超出核心 X 错误的额外错误值的扩展错误。返回之前定义的过程。当 Xlib 需要打印错误时,过程会以如下参数被调用:
void (*proc)(Display *display, XErrorEvent *ev, FILE *fp);
ev
指向的结构保证与
XEvent
结构一样大,因此可以转换为比
XErrorEvent
更大的类型以获取使用
XESetWireToError
设置的额外值。
fp
参数的底层类型取决于系统;在符合 POSIX 的系统上,
fp
应转换为
FILE*
类型。
4.14 XESetFlushGC 函数
int *XESetFlushGC(Display *display, int extension, int (*proc)());
-
display:指定与 X 服务器的连接。 -
extension:指定扩展编号。 -
proc:指定在刷新 GC 时调用的过程。
该函数设置的过程与
XESetCopyGC
函数设置的过程具有相同的接口,但在服务器中需要更新 GC 缓存时调用。
4.15 XESetBeforeFlush 函数
int *XESetBeforeFlush(Display *display, int extension, void (*proc)());
-
display:指定与 X 服务器的连接。 -
extension:指定扩展编号。 -
proc:指定在数据即将发送到服务器时调用的过程。
当数据即将发送时,你的过程会以如下参数被调用一次或多次:
void (*proc)(Display *display, XExtCodes *codes, char *data, int len);
data
参数指定传出数据缓冲区的一部分,其长度(以字节为单位)由
len
参数指定。你的过程不能更改数据的内容,也不能对同一显示进行额外的协议请求。
下面是这些钩子函数的使用场景表格:
| 函数名 | 使用场景 |
| ---- | ---- |
| XESetCloseDisplay | 关闭显示时 |
| XESetCreateGC | 创建新 GC 时 |
| XESetCopyGC | 复制 GC 时 |
| XESetFreeGC | 释放 GC 时 |
| XESetCreateFont | 创建字体时 |
| XESetFreeFont | 释放字体时 |
| XESetWireToEvent | 事件从网络格式转换为主机格式时 |
| XESetEventToWire | 事件从主机格式转换为网络格式时 |
| XESetWireToError | 错误从网络格式转换为主机格式时 |
| XESetError | 检测到协议错误时 |
| XESetErrorString | 获取错误字符串时 |
| XESetPrintErrorValues | 打印扩展错误时 |
| XESetFlushGC | 刷新 GC 时 |
| XESetBeforeFlush | 数据即将发送到服务器时 |
5. 挂钩到 Xlib 数据结构
各种 Xlib 数据结构提供了扩展过程将扩展提供的数据链接到列表的机制。这些结构包括
GC
、
Visual
、
Screen
、
ScreenFormat
、
Display
和
XFontStruct
。由于列表指针总是结构中的第一个成员,因此可以使用一组单一的过程来操作这些列表上的数据。
5.1 XExtData 结构体
typedef struct _XExtData {
int number; /* number returned by XInitExtension */
struct _XExtData *next; /* next item on list of data for structure */
int (*free_private)(); /* if defined, called to free private */
XPointer private_data; /* data private to this extension. */
} XExtData;
当上述任何数据结构被释放时,会遍历列表并调用结构的释放过程(如果有)。如果
free
为
NULL
,则库会释放
private_data
成员指向的数据和结构本身。
5.2 XEDataObject 联合体
union {
Display *display;
GC gc;
Visual *visual;
Screen *screen;
ScreenFormat *pixmap_format;
XFontStruct *font
} XEDataObject;
5.3 XEHeadOfExtensionList 函数
XExtData **XEHeadOfExtensionList(XEDataObject object);
-
object:指定对象。
该函数返回指向附加到指定对象的扩展结构列表的指针。与
XAddToExtensionList
配合使用,
XEHeadOfExtensionList
允许扩展将任意数据附加到
XEDataObject
包含的任何类型的结构中。
5.4 XAddToExtensionList 函数
void XAddToExtensionList(void *structure, XExtData *ext_data);
-
structure:指定扩展列表。 -
ext_data:指定要添加的扩展数据结构。
structure
参数是指向上述枚举的数据结构之一的指针。在调用此函数之前,必须用扩展编号初始化
ext_data->number
。
5.5 XFindOnExtensionList 函数
XExtData *XFindOnExtensionList(void *structure, int number);
-
structure:指定扩展列表。 -
number:指定扩展编号。
该函数用于在扩展列表中查找具有指定扩展编号的扩展数据结构。
下面是操作这些数据结构的步骤列表:
1. 定义
XExtData
结构体并初始化
number
成员。
2. 使用
XEHeadOfExtensionList
获取扩展列表的头指针。
3. 使用
XAddToExtensionList
将扩展数据结构添加到列表中。
4. 使用
XFindOnExtensionList
在列表中查找特定的扩展数据结构。
通过以上这些函数和数据结构,你可以灵活地扩展 Xlib 的功能,满足不同的应用需求。
超级会员免费看
2

被折叠的 条评论
为什么被折叠?



