XCB 编程入门





5.00/5 (7投票s)
使用 XCB(X 协议 C 语言绑定)
引言
多年前,我曾为各种平台编写过图形用户界面 (GUI)。对于一个 Linux 应用程序,我尝试使用 X Window System 库,通常称为 Xlib。我被完成简单任务所需的代码量震惊了,于是决定改用不同的工具集。
如今,开发者可以使用 XCB(代表 X protocol C-language Binding)为 X Window System 开发底层 GUI。XCB 程序可以执行与使用 Xlib 编写的程序相同的操作,但需要的代码量大大减少。此外,XCB 提供了“延迟隐藏、直接协议访问、改进的线程支持和可扩展性”。
本文的目的是解释 XCB 应用程序开发的基础。有三个核心主题:创建窗口、处理事件和绘制图形。对于每个主题,我都提供了一个源代码文件,演示了函数和数据结构如何在实践中使用。
1. 前期要求
理解本文内容需要具备 C 编程基础知识。要编译示例代码,您需要在开发系统上安装 XCB 库 (libxcb.so) 和头文件 (xcb.h)。
2. X Window System
尽管 Wayland 和 Mir 的普及度日益增长,但 X Window System (也称为 X Windows 或简称 X) 仍然是 Linux 计算机上管理窗口最常用的框架。它最初由 MIT 在 20 世纪 80 年代开发,已广泛应用于 UNIX 和 Linux 计算机,并且可以通过 Cygwin 在 Windows 计算机上访问。XCB 和 Xlib 的目标是使程序员能够在他们的应用程序中访问 X Window System。
在处理 X 时,首先要理解的是显示器、屏幕和窗口之间的区别。根据官方 文档,显示器是“共享通用键盘和指针的监视器集合”。屏幕标识一个物理监视器,而窗口是绘制在监视器上的图形元素。重要的是要理解,一个显示器可以有多个屏幕,每个屏幕可以有多个窗口。
关于 X 的第二点需要理解的是它使用客户端-服务器通信。X 支持远程显示,鉴于通常的客户端-服务器模型,您可能期望用户的系统从远程 X 服务器接收显示信息。但 X 的工作方式并非如此。X 服务器运行在用户的计算机上,并从客户端应用程序接收显示信息。在大多数情况下,客户端和服务器运行在同一系统上。
对于每个屏幕,X 服务器管理一个占据整个区域的窗口。这被称为根窗口。当客户端应用程序连接到服务器时,服务器允许它们创建子窗口。如果用户在子窗口获得焦点时执行操作,X 服务器将以事件的形式向客户端应用程序提供有关用户操作的信息。
X 服务器管理的每个资源都有一个标识符。每个显示器都有一个名称,每个屏幕都有一个编号。在应用程序可以创建新窗口或资源之前,它需要获得一个合适的 ID。
3. 创建窗口
大多数 XCB 应用程序都通过执行三个基本任务来启动:
- 连接到 X 服务器
- 访问屏幕
- 在屏幕上创建并显示窗口
接下来的讨论将解释如何完成这些任务。本节的最后部分将介绍一个显示一个简单窗口五秒钟的应用程序。
3.1 连接到 X 服务器
无论您是用 Xlib 还是 XCB 编程,应用程序的第一步通常都涉及连接到 X 服务器。在 XCB 中,使用的函数是 xcb_connect
。
xcb_connection_t* xcb_connect(const char *display, int *screen);
第一个参数标识 X 服务器的显示名称。如果此参数设置为 NULL
,则该函数将使用 DISPLAY
环境变量的值。
第二个参数指向要连接的屏幕编号。如果此参数设置为 NULL
,则该函数将屏幕编号设置为 0。这代表第一个可用屏幕。
xcb_connection_t
结构是不透明的,因此无法知道连接是否成功创建。为此,XCB 提供了 xcb_connection_has_error
函数,其声明如下:
int xcb_connection_has_error(xcb_connection_t *c);
此函数接受 xcb_connect
返回的 xcb_connection_t*
,并返回一个标识连接是否建立的值。错误将导致非零值。
以下代码展示了这两个函数如何在实践中使用:
xcb_connection_t *conn; conn = xcb_connect(NULL, NULL); if (xcb_connection_has_error(conn)) { printf("Error opening display.\n"); exit(1); }
此代码创建到 X 服务器的连接,该服务器的名称由 DISPLAY
环境变量提供。屏幕编号为 0。
3.2 访问屏幕
在应用程序可以创建窗口之前,它需要访问屏幕。要做到这一点,第一步是访问 X 服务器及其显示环境的属性。这可以通过 xcb_get_setup
实现。
const xcb_setup_t* xcb_get_setup(xcb_connection_t *c);
返回值是一个 xcb_setup_t
,它提供了许多有用的字段,包括:
roots_len
— X 服务器管理的根窗口数量bitmap_format_scanline_unit
— 扫描行单位中的位数bitmap_format_scanline_pad
— 用于填充每行扫描线的位数bitmap_format_bit_order
— 标识屏幕中最左边的位是最低有效位还是最高有效位protocol_major_version
— 支持的 X Window System 协议的主版本protocol_minor_version
— 支持的 X Window System 协议的次版本
这个 xcb_setup_t
很重要,因为它允许我们访问 X 服务器的屏幕。这种访问可以通过另一个名为 xcb_setup_roots_iterator
的函数来实现。
xcb_screen_iterator_t xcb_setup_roots_iterator(xcb_setup_t *set);
xcb_screen_iterator_t
结构有一个类型为 xcb_screen_t*
的 data
字段,它代表一个屏幕。此结构的字段包括:
root
— 根窗口 IDroot_depth
— 屏幕的每像素位数root_visual
— 指向xcb_visualid_t
结构的指针,其中包含屏幕的颜色映射width_in_pixels
— 屏幕宽度(以像素为单位)height_in_pixels
— 屏幕高度(以像素为单位)width_in_millimeters
— 屏幕宽度(以毫米为单位)height_in_millimeters
— 屏幕高度(以毫米为单位)black_pixel
— 对应屏幕黑像素的值white_pixel
— 对应屏幕白像素的值
以下代码展示了这些结构的使用方法。它从现有连接 (conn
) 获取 xcb_setup_t
结构,访问第一个屏幕,并打印屏幕的像素尺寸。
const xcb_setup_t* setup; xcb_screen_t* screen; setup = xcb_get_setup(conn); screen = xcb_setup_roots_iterator(setup).data; printf("Screen dimensions: %d, %d\n", screen->width_in_pixels, screen->height_in_pixels);
获取屏幕是创建应用程序窗口的必要步骤。接下来的讨论将解释如何做到这一点。
3.3 创建并显示窗口
既然我们已经访问了屏幕,下一步就是创建窗口。主要使用的函数是 xcb_create_window
。
xcb_create_window(xcb_connection_t *conn, // Connection to X server uint8_t depth, // Screen depth xcb_window_t window_id, // ID of the window xcb_window_t parent_id, // ID of the parent window int16_t x, // Top-left x-coordinate int16_t y, // Top-left y-coordinate uint16_t width, // Window width in pixels uint16_t height, // Window height in pixels uint16_t border_width, // Window border width in pixels uint16_t class, xcb_visualid_t visual, uint32_t value_mask, const uint32_t *value_list);
该函数的大部分参数都很直接。window_id
是窗口的唯一标识符,可以通过调用 xcb_generate_id
来获取,其签名如下:
uint32_t xcb_generate_id(xcb_connection_t* conn)
parent_id
可以设置为前面讨论的 xcb_screen_t
结构的 root
字段。同样,visual
参数可以设置为屏幕的 root_visual
字段。
该函数的 class
参数设置为 xcb_window_class_t
枚举类型的值。这并未被文档化,但通常将 class
设置为 XCB_WINDOW_CLASS_INPUT_OUTPUT
是安全的。
value_mask
和 value_list
参数是相关的。value_mask
标识属性名称的 OR 组合,value_list
包含它们的值。属性标识符取自 xcb_cw_t
类型,其值可以在 此处 找到。一个重要的属性是 XCB_CW_BACK_PIXEL
,它设置窗口的背景颜色。
创建窗口后,必须调用 xcb_map_window
来使其可见。该函数签名如下:
xcb_void_cookie_t xcb_map_window(xcb_connection_t *conn, xcb_window_t window)
调用 xcb_map_window
后,通常会调用 xcb_flush
来强制将窗口请求发送到服务器。以下讨论中的代码演示了这一点的使用方法。
3.4 示例 - 简单窗口
simple_window.c
文件中的代码创建并显示了一个简单的 100x100 像素窗口。main
函数如下:
int main(void) { xcb_connection_t *conn; const xcb_setup_t *setup; xcb_screen_t *screen; xcb_window_t window_id; uint32_t prop_name; uint32_t prop_value; /* Connect to X server */ conn = xcb_connect(NULL, NULL); if(xcb_connection_has_error(conn)) { printf("Error opening display.\n"); exit(1); } /* Obtain setup info and access the screen */ setup = xcb_get_setup(conn); screen = xcb_setup_roots_iterator(setup).data; /* Create window */ window_id = xcb_generate_id(conn); prop_name = XCB_CW_BACK_PIXEL; prop_value = screen->white_pixel; xcb_create_window(conn, screen->root_depth, window_id, screen->root, 0, 0, 100, 100, 1, XCB_WINDOW_CLASS_INPUT_OUTPUT, screen->root_visual, prop_name, &prop_value); /* Display the window */ xcb_map_window(conn, window_id); xcb_flush(conn); /* Wait for 5 seconds */ sleep(5); /* Disconnect from X server */ xcb_disconnect(conn); return 0; }
这段代码至少有三个值得注意的地方:
- 窗口的位置是 (0, 0),大小是 100x100 像素。这由
xcb_create_window
中的参数配置。 - 窗口的背景颜色设置为白色,方法是将
XCB_CW_BACK_PIXEL
属性与screen->white_pixel
的值关联起来。 - 此代码没有显式关闭窗口。相反,它调用
xcb_disconnect
来终止与 X 服务器的连接。
如果开发系统上安装了 XCB,可以使用以下命令编译源代码文件:
gcc -o simple_window simple_window.c -lxcb
除非用户点击窗口的关闭按钮,否则由于 sleep
函数的作用,窗口将保持打开状态五秒钟。如果没有这个函数,窗口将立即关闭。大多数 XCB 应用程序都有一个事件循环,而不是调用 sleep
。下面的讨论将解释事件的工作原理。
4. 处理事件
当用户执行涉及子窗口的操作时,例如单击鼠标或按下键盘,X 服务器会向客户端应用程序发送一条消息。应用程序可以通过调用 xcb_wait_for_event
来检索此消息。
xcb_generic_event_t *xcb_wait_for_event(xcb_connection_t *c);
xcb_generic_event_t
结构有一个 response_type
字段,用于标识发生了哪个事件。XCB 支持三十多种不同类型的事件,以下是六种事件代码:
XCB_BUTTON_PRESS
— 按下鼠标按钮XCB_BUTTON_RELEASE
— 释放鼠标按钮XCB_MOTION_NOTIFY
— 鼠标移动XCB_KEY_PRESS
— 按下键盘按键XCB_KEY_RELEASE
— 释放键盘按键XCB_EXPOSE
— 窗口显示内容
要配置事件处理,xcb_create_window
的 value_mask
和 value_list
参数必须标识感兴趣的事件。具体来说,value_mask
必须设置为包含 XCB_CW_EVENT_MASK
的 OR 组合。value_list
中的相应条目必须包含一个 OR 组合,用于标识应用程序应接收哪些事件。
例如,以下代码配置新窗口以关注鼠标单击、键盘按下和暴露事件:
value_mask = XCB_CW_BACK_PIXEL | XCB_CW_EVENT_MASK; value_list[0] = screen->white_pixel; value_list[1] = XCB_EVENT_MASK_BUTTON_PRESS | XCB_EVENT_MASK_KEY_PRESS | XCB_EVENT_MASK_EXPOSURE;
value_list
中的第一个条目标识背景颜色(对应于 XCB_BACK_PIXEL
)。第二个条目包含一个 OR 组合值,用于标识感兴趣的事件。
在窗口配置为接收事件后,可以调用 xcb_wait_for_event
来接收来自 X 服务器的消息。此函数会暂停应用程序直到发生事件,通常在循环中调用。
xcb_generic_event_t event; while((event = xcb_wait_for_event(conn)) ) { switch (event->response_type) { // Respond if the event is a key press case XCB_KEY_PRESS: ... break; // Respond if the event is a mouse click case XCB_BUTTON_PRESS: ... break; // Respond if the event is something else default: ... break; free (event); }
在此代码中,while
循环在每个事件接收和处理之后调用 xcb_wait_for_event
。这意味着循环永远不会终止。正如官方 XCB 文档所承诺的那样,有一个“特殊退出事件”会更好。但我从未见过它的使用。
相反,通常会在 while
循环的条件中插入一个布尔值。当该值返回 true 时,循环将终止。本节末尾的示例代码将演示这一点的工作原理。
4.1 响应鼠标单击
如果用户单击鼠标按钮,来自 X 服务器的消息可以作为 xcb_button_press_event_t
访问,其中包含与鼠标事件相关的字段。其字段包括:
detail
— 按下的按钮time
— 鼠标单击的时间戳event_x
,event_y
— 相对于客户端窗口的事件坐标root_x
,root_y
— 相对于根窗口的事件坐标root
— 根窗口的 IDchild
— 子窗口的 ID
detail
是 xcb_button_index_t
枚举类型的值,它有六个值:
XCB_BUTTON_INDEX_ANY
— 任意鼠标按钮XCB_BUTTON_INDEX_1
— 左鼠标按钮XCB_BUTTON_INDEX_2
— 中间鼠标按钮XCB_BUTTON_INDEX_3
— 右鼠标按钮XCB_BUTTON_INDEX_4
— 鼠标向上滚动XCB_BUTTON_INDEX_5
— 鼠标向下滚动
以下代码展示了应用程序如何访问鼠标事件的信息:
while((event = xcb_wait_for_event(conn)) ) { switch(event->response_type) { ... case XCB_BUTTON_PRESS: printf("Button pressed: %d\n", ((xcb_button_press_event_t*)event)->detail); break; } }
4.2 响应键盘按下
如果用户按下键盘按键,X 服务器的事件消息可以作为 xcb_key_press_event_t
访问,其字段提供了与按键相关的信息。这些字段包括:
detail
— 标识按下键的值time
— 按键的时间戳root
— 根窗口的 IDchild
— 子窗口的 ID
您可能期望 detail
使用 ASCII 或 Unicode 来标识按键。不幸的是,事实并非如此。要确定按下的键,应用程序需要调用 xcb_keysyms.h
头文件中的函数。这些函数未被文档化,我发现很少有它们使用的例子。因此,本文将不详细介绍它们。
4.3 示例 - 事件窗口
event_window.c
中的代码创建了与 simple_window.c
相同的白色窗口。不同之处在于应用程序配置窗口以接收事件,然后在事件循环中处理事件。以下代码展示了窗口的配置方式:
/* Create window */ window_id = xcb_generate_id(conn); value_mask = XCB_CW_BACK_PIXEL | XCB_CW_EVENT_MASK; value_list[0] = screen->white_pixel; value_list[1] = XCB_EVENT_MASK_EXPOSURE | XCB_EVENT_MASK_BUTTON_PRESS | XCB_EVENT_MASK_KEY_PRESS; xcb_create_window(conn, screen->root_depth, window_id, screen->root, 0, 0, 100, 100, 1, XCB_WINDOW_CLASS_INPUT_OUTPUT, screen->root_visual, value_mask, value_list);
如所示,value_mask
中的值指定将配置窗口的背景和事件处理。value_list
中的值指定背景应为白色,应用程序应接收与鼠标单击、键盘按下和暴露相关的事件。以下代码展示了事件循环中如何处理这些事件。
/* Execute the event loop */ while (!finished && (event = xcb_wait_for_event(conn))) { switch(event->response_type) { /* Respond to key presses */ case XCB_KEY_PRESS: printf("Keycode: %d\n", ((xcb_key_press_event_t*)event)->detail); finished = 1; break; /* Respond to button presses */ case XCB_BUTTON_PRESS: printf("Button pressed: %u\n", ((xcb_button_press_event_t*)event)->detail); printf("X-coordinate: %u\n", ((xcb_button_press_event_t*)event)->event_x); printf("Y-coordinate: %u\n", ((xcb_button_press_event_t*)event)->event_y); break; case XCB_EXPOSE: break; } free(event); }
如果用户单击鼠标按钮,应用程序将打印按钮编号和单击的坐标。如果用户按下键盘按键,应用程序将打印按键代码并将 finished
设置为 1。当 finished
从 0 变为 1 时,while
循环终止,应用程序断开与 X 服务器的连接。
5. 绘制图形
空白窗口并不特别有趣。XCB 使向窗口添加形状变得简单,该过程包括两个步骤:
- 创建图形上下文
- 调用一个或多个绘图图元。
本节将讨论这两个步骤。然后,我们将介绍一个向前面介绍的简单窗口添加形状的应用程序。
需要注意的是,XCB 没有创建按钮或文本框等 GUI 小部件的函数。如果您想构建传统的用户界面,最好使用 GTK+、FLTK 或 Qt 等工具。
5.1 创建图形上下文
图形上下文保存窗口的图形配置,例如字体、线条样式和前景色。此结构可以通过 xcb_create_gc
创建。
xcb_create_gc(xcb_connection_t *conn, xcb_gcontext_t context_id, xcb_drawable_t drawable, uint32_t value_mask, const uint32_t *value_list);
context_id
是上下文的唯一标识符。可以通过调用 xcb_generate_id
来获取。
drawable
参数可以设置为屏幕的根窗口。这由前面讨论的 xcb_screen_t
结构的 root
字段提供。
最后两个参数 value_mask
和 value_list
类似于 xcb_create_window
的 value_mask
和 value_list
参数。区别在于属性名称取自 xcb_gc_t
枚举类型,完整列表可以在 此处 找到。
图形上下文的两个重要属性涉及前景色和暴露事件。可以通过将 XCB_GC_FOREGROUND
添加到 value_mask
并将 value_list
的第一个值设置为适当的颜色来设置上下文的前景色。此外,XCB_GC_GRAPHICS_EXPOSURES
属性配置上下文是否生成暴露事件。可以通过将 value_list
的第二个值设置为 0 或 1 来打开或关闭此功能。以下代码展示了这一点的工作原理:
value_mask = XCB_GC_FOREGROUND | XCB_GC_GRAPHICS_EXPOSURES; value_list[0] = s->black_pixel; value_list[1] = 0;
这会将上下文的前景色设置为黑色,并关闭暴露事件的生成。这些设置将在本文中一直使用。
5.2 绘图图元
XCB 提供了八个在窗口中绘制形状的函数。这些称为绘图图元,表 1 列出了每个函数的签名:
表 1: 绘图函数
函数 | 描述 |
---|---|
|
绘制一个或多个点 |
|
在点之间绘制连接线 |
|
绘制独立的线段 |
|
绘制一个或多个矩形 |
|
绘制一个或多个椭圆弧 |
|
绘制并填充多边形 |
|
绘制并填充一个或多个矩形 |
|
绘制并填充一个或多个椭圆弧 |
许多这些函数都有一个 coord_mode
参数,用于指定形状的坐标如何解释。它可以设置为以下两个值之一:
XCB_COORD_MODE_ORIGIN
— 所有坐标对都相对于原点 (0, 0)XCB_COORD_MODE_PREVIOUS
— 每个坐标对都相对于前一个坐标对
每个绘图图元的最后一个参数包含一个结构数组,这些结构代表不同的形状(点、线段、矩形和弧)。下面的讨论将探讨这些形状。
5.2.1 点、线和多边形
许多函数使用点来定义它们的形状。每个点都必须作为 xcb_point_t
结构给出,该结构有两个整数字段:x
和 y
。例如,以下代码显示了如何调用 xcb_poly_point
。
xcb_point_t points[4] = { {40, 40}, {40, 80}, {80, 40}, {80, 80} }; xcb_poly_point(conn, XCB_COORD_MODE_ORIGIN, window_id, context_id, 4, points);
xcb_poly_line
和 xcb_fill_poly
函数的工作方式基本相同。以下代码调用 xcb_fill_poly
来创建一个填充的五边形。
xcb_point_t points[5] = { {11, 24}, {30, 10}, {49, 24}, {42, 46}, {18, 46} }; xcb_fill_poly(conn, window_id, context_id, XCB_POLY_SHAPE_CONVEX, XCB_COORD_MODE_ORIGIN, 5, points);
此代码将 xcb_fill_poly
的 shape
参数设置为 XCB_POLY_SHAPE_CONVEX
。其他值包括 XCB_POLY_SHAPE_NONCONVEX
和 XCB_POLY_SHAPE_COMPLEX
。
5.2.2 线段
理解 xcb_poly_line
和 xcb_poly_segment
之间的区别很重要。第一个接受一个点数组,并从每个点绘制一条线到下一个点。相比之下,xcb_poly_segment
绘制的线条不一定连接。
xcb_poly_segment
的最后一个参数是 xcb_segment_t
结构数组。该结构有四个整数字段:x1
、y1
、x2
、y2
。因此,每个线段连接 (x1
, y1
) 到 (x2
, y2
)。以下代码展示了这一点的工作原理:
xcb_segment_t segments[2] = { {60, 20, 90, 40}, {60, 40, 90, 20} }; xcb_poly_segment(conn, window_id, context_id, 2, segments);
5.2.3 矩形
xcb_poly_rectangle
和 xcb_poly_fill_rectangle
都接受 xcb_rectangle_t
结构数组。该结构有四个字段:x
、y
、width
和 height
。x 和 y 字段标识矩形左上角的坐标。例如,以下代码绘制并填充一个 30x20 像素的矩形,其左上角位于 (15, 65)。
xcb_rectangle_t rect = {15, 65, 30, 20}; xcb_poly_fill_rectangle(conn, window_id, context_id, 1, &rect);
5.2.4 弧
xcb_poly_arc
和 xcb_poly_fill_arc
都接受 xcb_arc_t
结构。xcb_arc_t 结构表示椭圆的一个弧,并且有六个字段:
x
— 包含椭圆的矩形左上角的 x 坐标y
— 包含椭圆的矩形左上角的 y 坐标width
— 椭圆的宽度height
— 椭圆的高度angle1
— 弧的起始角度,以 1/64 度为单位angle2
— 弧的结束角度,以 1/64 度为单位
以下代码绘制一个椭圆的上半部分,该椭圆的矩形左上角位于 (60, 70),尺寸为 30x20 像素。
xcb_arc_t arc = {60, 70, 30, 20, 0, 180 << 6}; xcb_poly_arc(conn, window_id, context_id, 1, &arc);
重要的是要注意角度以 1/64 度为单位。这就是为什么第二个角度(应为 180 度)被指定为 180 << 6
。
5.2.5 绘图图元和暴露事件
为了正常工作,绘图图元必须在窗口绘制之后执行。这可以通过在发生暴露事件后调用函数来实现。以下讨论中的代码将演示这一点的工作原理。
5.3 示例 - 图形窗口
graphic_window.c
中的代码通过定义一系列形状结构并调用绘图图元来扩展 event_window.c
的代码。形状定义如下:
xcb_point_t points[5] = { {11, 24}, {30, 10}, {49, 24}, {42, 46}, {18, 46} }; xcb_segment_t segments[2] = { {60, 20, 90, 40}, {60, 40, 90, 20} }; xcb_rectangle_t rect = {15, 65, 30, 20}; xcb_arc_t arc = {60, 70, 30, 20, 0, 180 << 6};
graphic_window.c
中的以下代码展示了如何在事件循环中调用绘图图元。每个暴露事件都会绘制一个五边形、两条线段、一个矩形和一个椭圆弧。
/* Execute the event loop */ while (!finished && (event = xcb_wait_for_event(conn))) { switch(event->response_type) { case XCB_KEY_PRESS: printf("Keycode: %d\n", ((xcb_key_press_event_t*)event)->detail); finished = 1; break; case XCB_BUTTON_PRESS: printf("Button pressed: %u\n", ((xcb_button_press_event_t*)event)->detail); printf("X-coordinate: %u\n", ((xcb_button_press_event_t*)event)->event_x); printf("Y-coordinate: %u\n", ((xcb_button_press_event_t*)event)->event_y); break; case XCB_EXPOSE: /* Draw polygon */ xcb_fill_poly(conn, window_id, context_id, XCB_POLY_SHAPE_CONVEX, XCB_COORD_MODE_ORIGIN, 5, points); /* Draw line segments */ xcb_poly_segment(conn, window_id, context_id, 2, segments); /* Draw rectangle */ xcb_poly_fill_rectangle(conn, window_id, context_id, 1, &rect); /* Draw arc */ xcb_poly_arc(conn, window_id, context_id, 1, &arc); xcb_flush(conn); break; } free(event); }
调用绘图图元后,事件循环调用 xcb_flush
来强制将绘图请求发送到 X 服务器。
Using the Code
XCB_examples.zip
存档包含本文提到的三个源文件。我没有提供任何 makefile,但可以使用以下命令编译代码:
gcc -o simple_window simple_window.c -lxcb
gcc -o event_window event_window.c -lxcb
gcc -o graphic_window graphic_window.c -lxcb
当然,只有当 XCB 已安装在系统上时,应用程序才能正常执行。
历史
2016/4/5 - 首次提交文章