如何使用XLib过滤过期事件并为X服务器请求设置超时以避免无限阻塞?
如何使用XLib过滤过期事件并为X服务器请求设置超时以避免无限阻塞?
嘿,我完全懂你这个痛点——调用XLib接口请求X服务器后,万一遇到窗口关闭、资源销毁这类情况,就可能无限阻塞在等回复的环节,太闹心了!用X同步扩展的XSyncCreateAlarm()来实现超时是个非常合理的思路,我来给你拆解具体的实现步骤,顺便把过期事件的过滤逻辑也讲清楚。
首先得确保你的程序已经初始化了X同步扩展,不然相关函数根本用不了:
int event_base, error_base; if (!XSyncQueryExtension(display, &event_base, &error_base)) { // 同步扩展不支持,得做降级处理或者报错退出 fprintf(stderr, "X Sync Extension not supported!\n"); return; }
接下来是核心的超时设置和事件循环部分。我们先创建一个超时闹钟,然后在事件循环里同时监听目标回复事件和超时触发事件,还要过滤掉那些已经失效的过期事件:
void foo(Display* display, Window target_win, Atom target_prop, int timeout_ms) { // 第一步:发送你的X服务器请求,比如读取属性 XGetWindowProperty(display, target_win, target_prop, 0, LONG_MAX, False, AnyPropertyType, nullptr, nullptr, nullptr, nullptr, nullptr); // 第二步:创建超时闹钟 XSyncAlarmAttributes alarm_attrs{}; // 设置触发条件:相对于当前服务器时间,延迟timeout_ms毫秒 alarm_attrs.trigger.counter = XSyncSystemTime; alarm_attrs.trigger.value_type = XSyncRelative; alarm_attrs.trigger.relative = timeout_ms; // 触发时发送AlarmNotify事件 alarm_attrs.events = XSyncAlarmNotify; alarm_attrs.flags = XSyncAlarmTrigger | XSyncAlarmEvents; XSyncAlarm alarm = XSyncCreateAlarm(display, &alarm_attrs); if (alarm == None) { fprintf(stderr, "Failed to create sync alarm!\n"); return; } bool got_valid_response = false; XEvent event{}; while (true) { // 阻塞等待下一个事件 XNextEvent(display, &event); // 先判断是不是超时触发的闹钟事件 if (event.type == event_base + XSyncAlarmNotify) { XSyncAlarmNotifyEvent* alarm_event = reinterpret_cast<XSyncAlarmNotifyEvent*>(&event); if (alarm_event->alarm == alarm) { // 超时了,清理闹钟并退出循环 XSyncDestroyAlarm(display, alarm); fprintf(stdout, "Request timed out!\n"); break; } } // 再判断是不是我们要的目标回复事件(这里以PropertyNotify为例) else if (event.type == PropertyNotify) { PropertyNotifyEvent* prop_event = reinterpret_cast<PropertyNotifyEvent*>(&event); // 验证事件是否和我们的请求匹配:窗口、属性都对应,且窗口还存在 bool window_exists = true; XWindowAttributes attrs{}; if (XGetWindowAttributes(display, prop_event->window, &attrs) == 0) { window_exists = false; } if (prop_event->window == target_win && prop_event->atom == target_prop && window_exists) { // 处理有效的回复事件 fprintf(stdout, "Got valid property response!\n"); // 这里可以重新调用XGetWindowProperty获取实际数据 // ... got_valid_response = true; XSyncDestroyAlarm(display, alarm); break; } else { // 事件已经过期(比如窗口已关、属性已被销毁),直接跳过 fprintf(stdout, "Skipping expired event!\n"); continue; } } else { // 其他无关事件,放回事件队列,别影响程序其他逻辑 XPutBackEvent(display, &event); } } if (got_valid_response) { // 处理成功获取到的结果 // ... } }
最后给你提几个关键注意点:
- 一定要清理闹钟:不管是拿到有效回复还是超时,都要调用
XSyncDestroyAlarm释放资源,不然会留下内存泄漏或者无效的服务器资源。 - 过滤过期事件的关键:在处理目标事件前,一定要验证关联的资源(比如窗口、属性)是否还存在,避免处理已经失效的事件。
- 无关事件放回队列:如果遇到不是我们要的事件,用
XPutBackEvent放回,保证程序其他部分的事件处理不受影响。 - 错误处理:创建闹钟、检查窗口属性这些操作都可能失败,记得加上错误判断,避免程序崩溃。
备注:内容来源于stack exchange,提问作者Alexey104
相关产品推荐
相关产品推荐

