You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.07 21:40:05