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

为什么调用XRecordDisableContext()后录制回调仍会被触发?

X11 XRecord禁用录制上下文后回调仍触发的解决方案

问题根因

你遇到的问题和XRecordEnableContextAsync本身无关,核心是两个使用错误:

  1. 异步接口的所有请求(包括启用、禁用录制)都需要主动处理X11事件队列才能生效,你调用完禁用接口后没有处理剩余的pending事件,导致禁用请求没有被X服务器确认,且队列中已缓存的录制事件还会继续触发回调。
  2. 调用完异步启用接口后直接执行禁用逻辑,没有留时间处理录制事件,逻辑本身也不符合正常的录制流程。

核心解决逻辑

  1. 异步启用录制后,需要循环调用XRecordProcessEvents处理X服务器返回的录制事件,才会正常触发回调。
  2. 调用XRecordDisableContext后,必须继续处理事件队列直到所有剩余事件都被消费完,此时才不会再有新的回调触发,之后再释放资源才安全。

修正后可运行的代码示例

#include <X11/Xlib.h>
#include <X11/extensions/record.h>
#include <iostream>
#include <unistd.h>

void Callback(XPointer, XRecordInterceptData *pRecord) {
    // 过滤非服务端录制事件
    if (pRecord->category != XRecordFromServer) {
        XRecordFreeData(pRecord);
        return;
    }
    std::cout << "my logs\n";
    // 必须释放回调返回的数据,否则会内存泄漏
    XRecordFreeData(pRecord);
}

int main ()
{
    if(auto* const pDisplay = XOpenDisplay(nullptr))
    {
        XRecordClientSpec clients = XRecordAllClients;
        auto* pRange = ::XRecordAllocRange();
        pRange->device_events = XRecordRange8{KeyPress, ButtonRelease};
        auto context = ::XRecordCreateContext(pDisplay, 0, &clients, 1, &pRange, 1);
        
        // 启用异步录制
        ::XRecordEnableContextAsync(pDisplay, context, Callback, nullptr);
        
        // 示例逻辑:录制5秒后停止,可替换为你自己的停止触发逻辑(比如信号量、用户指令等)
        for(int i = 0; i < 5; i++) {
            // 处理当前所有pending的X事件,触发对应回调
            while(XPending(pDisplay)) {
                XRecordProcessEvents(pDisplay);
            }
            sleep(1);
        }
        
        // 调用禁用接口
        ::XRecordDisableContext(pDisplay, context);
        
        // 关键:消费完全部剩余事件,直到没有更多录制事件需要处理
        while(XPending(pDisplay) || XRecordProcessEvents(pDisplay) > 0);
        
        // 确认无回调后再释放所有资源
        ::XRecordFreeContext(pDisplay, context);
        ::XFree(pRange);

        ::XFlush(pDisplay);
        ::XSync(pDisplay, true);
        XCloseDisplay(pDisplay);
    }
    return 0;
}

额外说明

  • 如果使用同步接口XRecordEnableContext,该接口会阻塞当前线程直到录制上下文被禁用,所有回调都会在阻塞阶段触发,你需要单独开一个线程来调用XRecordDisableContext才能停止录制。
  • 不管用同步还是异步接口,回调拿到的XRecordInterceptData都必须手动调用XRecordFreeData释放,避免内存泄漏。

内容的提问来源于stack exchange,提问作者iammilind

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.09.28 15:06:04