Linux X11环境下高效监听特定屏幕像素变化的C实现方案问询
问题
我正在用C语言编写程序,需要在Linux X11环境下检测屏幕上特定像素的变化:获取该像素颜色,当颜色符合特定值时执行操作(例如向控制台打印内容)。要求程序运行极快且尽可能低CPU占用。
目前我使用以下代码:
// Get the pixel color const char *getpixelcol() { XColor c; Display *d = XOpenDisplay((char *)NULL); if (d == NULL) { return "1"; } int x = 959; // Pixel x int y = 554; // Pixel y XImage *image; image = XGetImage(d, XRootWindow(d, XDefaultScreen(d)), x, y, 1, 1, AllPlanes, XYPixmap); if (image == NULL) { XCloseDisplay(d); return "2"; } c.pixel = XGetPixel(image, 0, 0); XFree(image); XQueryColor(d, XDefaultColormap(d, XDefaultScreen(d)), &c); XCloseDisplay(d); // print colour of pixel in format #rrggbb // printf("#%06X\n", c.pixel); // fflush(stdout); char *hex; asprintf(&hex, "#%06X", c.pixel); fflush(stdout); return (hex); }
上述代码可获取指定坐标(x=959,y=554)像素的颜色。我将这段代码放在无延迟的永久while循环中执行,这种临时方法虽能工作,但不够理想也不高效。我想了解是否有更好的方式来「监听」特定像素的变化,优先采用C语言实现,其他编程语言也可接受。我并非专业开发者,对C语言也不熟悉,若有表述不当之处敬请谅解。
注:我使用Linux系统,仅需适用于X11的解决方案。
高效解决方案
1. 使用XDamage扩展(推荐,低CPU占用)
XDamage是X11官方提供的扩展,专门用于监听窗口区域的像素变化。它不需要主动轮询,只有当指定区域发生像素变更时才会触发事件,CPU占用极低,是最优方案。
实现要点:
- 确保系统安装XDamage开发库(如Debian系的
libxdamage-dev),编译时需链接-lXdamage -lX11 -lXfixes - 复用Display连接,避免每次操作重复打开/关闭连接
- 创建Damage对象监听目标像素所在的1x1区域
- 进入X事件循环,仅在收到Damage事件时检查像素颜色
示例代码:
#include <X11/Xlib.h> #include <X11/extensions/Xdamage.h> #include <stdio.h> #include <stdlib.h> #define TARGET_X 959 #define TARGET_Y 554 #define TARGET_COLOR 0xFFFFFF // 替换为你需要检测的颜色值 int main() { Display *d = XOpenDisplay(NULL); if (!d) { fprintf(stderr, "无法打开X显示\n"); return 1; } int screen = DefaultScreen(d); Window root_window = RootWindow(d, screen); // 检查并初始化XDamage扩展 int damage_event, damage_error; if (!XDamageQueryExtension(d, &damage_event, &damage_error)) { fprintf(stderr, "系统不支持XDamage扩展\n"); XCloseDisplay(d); return 1; } // 创建Damage对象,监听目标像素的1x1区域 Damage damage = XDamageCreate(d, root_window, XDamageReportRawRectangles); XDamageSubtract(d, damage, None, None); // 清除初始状态的损伤记录 // 注册监听Damage事件 XSelectInput(d, root_window, StructureNotifyMask | DamageNotify); XEvent ev; while (1) { XNextEvent(d, &ev); if (ev.type == damage_event + XDamageNotify) { XDamageNotifyEvent *damage_ev = (XDamageNotifyEvent*)&ev; // 检查损伤区域是否包含目标像素 if (damage_ev->area.x <= TARGET_X && damage_ev->area.x + damage_ev->area.width > TARGET_X && damage_ev->area.y <= TARGET_Y && damage_ev->area.y + damage_ev->area.height > TARGET_Y) { // 获取目标像素颜色 XImage *image = XGetImage(d, root_window, TARGET_X, TARGET_Y, 1, 1, AllPlanes, XYPixmap); if (image) { unsigned long pixel = XGetPixel(image, 0, 0); XFree(image); if (pixel == TARGET_COLOR) { printf("检测到目标颜色\n"); fflush(stdout); } } // 清除已处理的损伤记录,避免重复触发 XDamageSubtract(d, damage, None, None); } } } XDamageDestroy(d, damage); XCloseDisplay(d); return 0; }
2. 优化轮询方式(简单易实现)
如果不想依赖XDamage扩展,可以优化原有代码,大幅降低CPU占用:
- 复用Display连接:原有代码每次调用
getpixelcol都打开/关闭Display,这是最大的性能瓶颈 - 添加固定延迟:每次循环后休眠一段时间(如10毫秒),避免无限制占用CPU
- 仅在颜色变化时执行操作,减少不必要的计算
示例代码:
#include <X11/Xlib.h> #include <stdio.h> #include <stdlib.h> #include <unistd.h> #define TARGET_X 959 #define TARGET_Y 554 #define TARGET_COLOR 0xFFFFFF #define POLL_INTERVAL_US 10000 // 10毫秒,可根据需求调整 unsigned long get_pixel_color(Display *d, Window root, int x, int y) { XImage *image = XGetImage(d, root, x, y, 1, 1, AllPlanes, XYPixmap); if (!image) return 0; unsigned long pixel = XGetPixel(image, 0, 0); XFree(image); return pixel; } int main() { Display *d = XOpenDisplay(NULL); if (!d) { fprintf(stderr, "无法打开X显示\n"); return 1; } int screen = DefaultScreen(d); Window root = RootWindow(d, screen); unsigned long last_color = get_pixel_color(d, root, TARGET_X, TARGET_Y); while (1) { unsigned long current_color = get_pixel_color(d, root, TARGET_X, TARGET_Y); if (current_color != last_color) { last_color = current_color; if (current_color == TARGET_COLOR) { printf("检测到目标颜色\n"); fflush(stdout); } } usleep(POLL_INTERVAL_US); } XCloseDisplay(d); return 0; }
3. Python替代方案(更易上手)
如果对C语言不熟悉,可以用Python结合python-xlib库实现,同样支持XDamage监听:
from Xlib import X, display from Xlib.ext import damage import time TARGET_X = 959 TARGET_Y = 554 TARGET_COLOR = 0xFFFFFF # 初始化X连接 d = display.Display() root = d.screen().root # 启用Damage扩展 d.extension_required('DAMAGE') damage_obj = damage.Damage(d, root, damage.DamageReportRawRectangles) damage_obj.clear() # 注册监听事件 root.change_attributes(event_mask=X.StructureNotifyMask | damage.DamageNotify) while True: ev = d.next_event() if ev.type == damage.DamageNotify: # 检查损伤区域是否包含目标像素 area = ev.area if area.x <= TARGET_X <= area.x + area.width and area.y <= TARGET_Y <= area.y + area.height: # 获取像素颜色 image = root.get_image(TARGET_X, TARGET_Y, 1, 1, X.ZPixmap, 0xffffffff) pixel = image.pixel if pixel == TARGET_COLOR: print("检测到目标颜色") # 清除已处理的损伤记录 damage_obj.clear()
内容的提问来源于stack exchange,提问作者Narcotix
相关产品推荐
相关产品推荐

