XLib:如何正确中止X11 INCR增量数据传输过程?
X11 INCR增量传输中止方案
INCR传输的核心约定是:selection所有者每写入一个数据块到请求方窗口的指定property后,会等待请求方删除该property,确认读取完成后才会发送下一个块。你直接跳出循环后,没有通知所有者终止传输,对方会一直持有未发送的缓存数据,导致内存占用上涨。
标准合规的实现方式有两种:
方案1:使用临时窗口接收数据(最推荐,符合ICCCM规范)
这是X11剪贴板工具的通用实现逻辑:
- 每次发起selection拉取请求时,单独创建一个临时的InputOnly类型窗口用于接收数据
- 检测到内容大小超过阈值时,直接销毁这个临时窗口
- X服务器会自动向selection所有者发送通知,告知接收窗口已销毁,所有者会自动释放所有INCR传输相关的缓存,终止后续数据发送
方案2:不销毁窗口的兼容中止方案
如果你不想额外创建临时窗口,可以按以下步骤操作:
- 检测到大小超阈值后,设置终止标记
- 释放当前已收到的property数据,不要删除该property
- 主动向selection所有者发送
SelectionNotify事件,将其中的property字段设为None,明确告知对方你放弃本次传输 - 关闭当前窗口的
PropertyChangeMask事件监听,直接跳出循环
代码修改示例
你可以在原有代码的内容追加逻辑后补充中止判断:
if (!maxSizeExceeded) { content_ += std::string( reinterpret_cast<const char*>(dataReturned), nItemsReturned); maxSizeExceeded = content_.size() > sizeLimit; // 新增中止逻辑 if (maxSizeExceeded) { XFree(dataReturned); dataReturned = nullptr; // 获取当前selection所有者的窗口ID Window selectionOwner = XGetSelectionOwner(display_, clipboard); if (selectionOwner != None) { // 构造SelectionNotify事件告知对方放弃接收 XEvent abortEvent{}; abortEvent.type = SelectionNotify; abortEvent.xselection.display = display_; abortEvent.xselection.requestor = window_; abortEvent.xselection.selection = clipboard; abortEvent.xselection.target = target; abortEvent.xselection.property = None; abortEvent.xselection.time = propertyReadTime; XSendEvent(display_, selectionOwner, False, NoEventMask, &abortEvent); } XSelectInput(display_, window_, NoEventMask); // 清理超出大小的内容 content_.clear(); std::string().swap(content_); // 直接跳出循环,无需接收后续数据 break; } } XFree(dataReturned); dataReturned = nullptr;
以上两种方案都可以正常通知所有者终止传输,不会出现内存积压的问题。
内容的提问来源于stack exchange,提问作者Alexey104
相关产品推荐
相关产品推荐

