首页 > 解决方案 > XLib - 如何正确取消增量数据传输的过程?

问题描述

我已经阅读了这篇文章并在互联网上的许多其他地方寻找过,但我找不到我的问题的答案。在 C++ 程序中,我有一个类Clipboard. 此类正在监视 X11 选择事件,并在选择内容发生更改时将其提取到字符串成员中。对于大型选择,X11 使用“INCR 转移”机制。这个过程在上面的链接中有描述。获取大型选择的内容时,我想检查选择大小是否超过某个限制(例如,20 兆字节),如果超过,我不希望获取此选择以避免我的程序消耗的内存不足. 这就是我目前的大致做法:

void Clipboard::fetchLargeSelectionContent()
{
    /* We have received a property of type 'INCR' and are now going to start
       the read process 
       Variables ending with '_' are class' private members */
    Atom clipboard = XInternAtom(display_, "CLIPBOARD", false);
    Atom target = XInternAtom(display_, "UTF8_STRING", false);
    unsigned long nItemsToRead = 0;
    Atom typeReturned = None;
    int formatReturned = 0;
    unsigned long nItemsReturned = 0;
    unsigned long nBytesRemaining = 0;
    unsigned char* dataReturned = nullptr;
    // We do not want selection to exceed 20 megabytes
    std::string::size_type sizeLimit = 20 * 1024 * 1024;
    bool maxSizeExceeded = false;
    // Listen for property events on the requestor's(us) window
    XSelectInput(display_, window_, PropertyChangeMask);
    /* The xlib::getCurrentTime() function is defined in a separate header file
       and returns the current X server time */
    Time propertyReadTime = xlib::getCurrentTime();
    /* Notify the selection owner that we are ready to receive the data
       by deleting the property of type 'INCR' */
    XDeleteProperty(display_, window_, property_);
    while (true)
    {
        XEvent event{};
        XNextEvent(display_, &event);
        if (event.type == PropertyNotify &&
                event.xproperty.state == PropertyNewValue &&
                event.xproperty.display == display_ &&
                event.xproperty.window == window_ &&
                event.xproperty.time >= propertyReadTime)
        {
            // Read zero bytes from the property just to get the size of its content
            XGetWindowProperty(
                    display_,
                    window_,
                    property_,
                    0,
                    0,
                    false,
                    target,
                    &typeReturned,
                    &formatReturned,
                    &nItemsReturned,
                    &nBytesRemaining,
                    &dataReturned);
            XFree(dataReturned);
            dataReturned = nullptr;
            if (nBytesRemaining == 0)
            {
                // Transfer completed
                XDeleteProperty(display_, window_, property_);
                break;
            }
            nItemsToRead = nBytesRemaining;
            propertyReadTime = xlib::getCurrentTime();
            // Read the content and delete the property requesting a new data chunk
            XGetWindowProperty(
                    display_,
                    window_,
                    property_,
                    0,
                    nItemsToRead,
                    true,
                    target,
                    &typeReturned,
                    &formatReturned,
                    &nItemsReturned,
                    &nBytesRemaining,
                    &dataReturned);
            if (!maxSizeExceeded)
            {
                content_ += std::string(
                        reinterpret_cast<const char*>(dataReturned),
                        nItemsReturned);
                maxSizeExceeded = content_.size() > sizeLimit;
            }
            XFree(dataReturned);
            dataReturned = nullptr;
        }
    }
    if (maxSizeExceeded)
    {
        content_.clear();
        std::string().swap(content_);
    }
    XSelectInput(display_, window_, NoEventMask);
}

这种方法有效,但它有一个明显的缺点 - 即使sizeLimit超过 ,仍然会收到数据,只是不会写入content_. 但为什么我要接收我不打算存储和使用的数据呢?我需要以某种方式通知选择所有者我不再对接收新数据块感兴趣,并且它应该中止 INCR 传输过程。但我还没有找到办法。我试图简单地替换while (true)while (!maxSizeExceeded)在超出后立即中断循环sizeLimit. 但似乎选择所有者仍在等待我的程序读取它发送的数据,因为我可以看到系统内存使用量随着每个后续的 500MB 文本数据副本的显着增长。如果使用我的示例中的方法,则不会发生这种情况(读取整个数据,如果数据太大,则忽略它)。

所以,问题就在标题中——如何优雅地(正确地)中止增量数据传输的过程,甚至有什么方法可以做到吗?

标签: c++x11xlibxorg

解决方案


推荐阅读