gpt4 book ai didi

c++ - Xdnd drop 支持错误的实现

转载 作者:行者123 更新时间:2023-12-04 12:55:52 24 4
gpt4 key购买 nike

前段时间我在VTK中实现了Xdnd drop支持实现。除了 Thunar 文件管理器外,它工作得很好。当时所有其他文件管理器都运行良好。我们当时将此限制视为 Thunar 错误。
我实现的功能非常简单:

  • 将应用程序的窗口设置为 XdndAware
  • 收到位置信息并回复我们准备接收
  • 收到丢弃消息并请求选择
  • 接收选择通知并恢复URI
  • 将 URI 转换为我们可以使用的内容

  • 没什么特别的,我什至没有触及列表类型。
    快进几年,现在海豚用户无法将文件正确放入我们的应用程序中。自 dolphin 启动以来,URI 始终是第一个删除的文件。重新启动我们的应用程序没有任何效果。 pcmanfm 完全没有错误。
    这不是 dolphin 错误,文件可以从 dolphin 放到 Blender 或 firefox 上而不会出现问题。
    所以我们的实现中肯定有一个错误,但我已经盯着代码看了一段时间,我尝试过的一切都没有效果,除了完全破坏 Xdnd 支持。
    以下是实现中有趣的部分:
    //------------------------------------------------------------------------------
    vtkXRenderWindowInteractor::vtkXRenderWindowInteractor()
    {
    this->Internal = new vtkXRenderWindowInteractorInternals;
    this->DisplayId = nullptr;
    this->WindowId = 0;
    this->KillAtom = 0;
    this->XdndSource = 0;
    this->XdndPositionAtom = 0;
    this->XdndDropAtom = 0;
    this->XdndActionCopyAtom = 0;
    this->XdndStatusAtom = 0;
    this->XdndFinishedAtom = 0;
    }

    [...]


    //------------------------------------------------------------------------------
    void vtkXRenderWindowInteractor::Enable()
    {
    // avoid cycles of calling Initialize() and Enable()
    if (this->Enabled)
    {
    return;
    }

    // Add the event handler to the system.
    // If we change the types of events processed by this handler, then
    // we need to change the Disable() routine to match. In order for Disable()
    // to work properly, both the callback function AND the client data
    // passed to XtAddEventHandler and XtRemoveEventHandler must MATCH
    // PERFECTLY
    XSelectInput(this->DisplayId, this->WindowId,
    KeyPressMask | KeyReleaseMask | ButtonPressMask | ButtonReleaseMask | ExposureMask |
    StructureNotifyMask | EnterWindowMask | LeaveWindowMask | PointerMotionHintMask |
    PointerMotionMask);

    // Setup for capturing the window deletion
    this->KillAtom = XInternAtom(this->DisplayId, "WM_DELETE_WINDOW", False);
    XSetWMProtocols(this->DisplayId, this->WindowId, &this->KillAtom, 1);

    // Enable drag and drop
    Atom xdndAwareAtom = XInternAtom(this->DisplayId, "XdndAware", False);
    char xdndVersion = 5;
    XChangeProperty(this->DisplayId, this->WindowId, xdndAwareAtom, XA_ATOM, 32, PropModeReplace,
    (unsigned char*)&xdndVersion, 1);
    this->XdndPositionAtom = XInternAtom(this->DisplayId, "XdndPosition", False);
    this->XdndDropAtom = XInternAtom(this->DisplayId, "XdndDrop", False);
    this->XdndActionCopyAtom = XInternAtom(this->DisplayId, "XdndActionCopy", False);
    this->XdndStatusAtom = XInternAtom(this->DisplayId, "XdndStatus", False);
    this->XdndFinishedAtom = XInternAtom(this->DisplayId, "XdndFinished", False);

    this->Enabled = 1;

    this->Modified();
    }

    [...]

    //------------------------------------------------------------------------------
    void vtkXRenderWindowInteractor::DispatchEvent(XEvent* event)
    {
    int xp, yp;

    switch (event->type)
    {
    [...]

    // Selection request for drag and drop has been delivered
    case SelectionNotify:
    {
    // Sanity checks
    if (!event->xselection.property || !this->XdndSource)
    {
    return;
    }

    // Recover the dropped file
    char* data = nullptr;
    Atom actualType;
    int actualFormat;
    unsigned long itemCount, bytesAfter;
    XGetWindowProperty(this->DisplayId, event->xselection.requestor, event->xselection.property,
    0, LONG_MAX, False, event->xselection.target, &actualType, &actualFormat, &itemCount,
    &bytesAfter, (unsigned char**)&data);

    // Conversion checks
    if ((event->xselection.target != AnyPropertyType && actualType != event->xselection.target) ||
    itemCount == 0)
    {
    return;
    }

    // Recover filepaths from uris and invoke DropFilesEvent
    std::stringstream uris(data);
    std::string uri, protocol, hostname, filePath;
    std::string unused0, unused1, unused2, unused3;
    vtkNew<vtkStringArray> filePaths;
    while (std::getline(uris, uri, '\n'))
    {
    if (vtksys::SystemTools::ParseURL(
    uri, protocol, unused0, unused1, hostname, unused3, filePath, true))
    {
    if (protocol == "file" && (hostname.empty() || hostname == "localhost"))
    {
    // The uris can be crlf delimited, remove ending \r if any
    if (filePath.back() == '\r')
    {
    filePath.pop_back();
    }

    // The extracted filepath miss the first slash
    filePath.insert(0, "/");

    filePaths->InsertNextValue(filePath);
    }
    }
    }
    this->InvokeEvent(vtkCommand::DropFilesEvent, filePaths);
    XFree(data);

    // Inform the source the the drag and drop operation was sucessfull
    XEvent reply;
    memset(&reply, 0, sizeof(reply));

    reply.type = ClientMessage;
    reply.xclient.window = event->xclient.data.l[0];
    reply.xclient.message_type = this->XdndFinishedAtom;
    reply.xclient.format = 32;
    reply.xclient.data.l[0] = this->WindowId;
    reply.xclient.data.l[1] = itemCount;
    reply.xclient.data.l[2] = this->XdndActionCopyAtom;

    XSendEvent(this->DisplayId, this->XdndSource, False, NoEventMask, &reply);
    XFlush(this->DisplayId);
    this->XdndSource = 0;
    }
    break;


    case ClientMessage:
    {
    if (event->xclient.message_type == this->XdndPositionAtom)
    {
    // Drag and drop event inside the window

    // Recover the position
    int xWindow, yWindow;
    int xRoot = event->xclient.data.l[2] >> 16;
    int yRoot = event->xclient.data.l[2] & 0xffff;
    Window root = DefaultRootWindow(this->DisplayId);
    Window child;
    XTranslateCoordinates(
    this->DisplayId, root, this->WindowId, xRoot, yRoot, &xWindow, &yWindow, &child);

    // Convert it to VTK compatible location
    double location[2];
    location[0] = static_cast<double>(xWindow);
    location[1] = static_cast<double>(this->Size[1] - yWindow - 1);
    this->InvokeEvent(vtkCommand::UpdateDropLocationEvent, location);

    // Reply that we are ready to copy the dragged data
    XEvent reply;
    memset(&reply, 0, sizeof(reply));

    reply.type = ClientMessage;
    reply.xclient.window = event->xclient.data.l[0];
    reply.xclient.message_type = this->XdndStatusAtom;
    reply.xclient.format = 32;
    reply.xclient.data.l[0] = this->WindowId;
    reply.xclient.data.l[1] = 1; // Always accept the dnd with no rectangle
    reply.xclient.data.l[2] = 0; // Specify an empty rectangle
    reply.xclient.data.l[3] = 0;
    reply.xclient.data.l[4] = this->XdndActionCopyAtom;

    XSendEvent(this->DisplayId, event->xclient.data.l[0], False, NoEventMask, &reply);
    XFlush(this->DisplayId);
    }
    else if (event->xclient.message_type == this->XdndDropAtom)
    {
    // Item dropped in the window
    // Store the source of the drag and drop
    this->XdndSource = event->xclient.data.l[0];

    // Ask for a conversion of the selection. This will trigger a SelectioNotify event later.
    Atom xdndSelectionAtom = XInternAtom(this->DisplayId, "XdndSelection", False);
    XConvertSelection(this->DisplayId, xdndSelectionAtom,
    XInternAtom(this->DisplayId, "UTF8_STRING", False), xdndSelectionAtom, this->WindowId,
    CurrentTime);
    }
    else if (static_cast<Atom>(event->xclient.data.l[0]) == this->KillAtom)
    {
    this->ExitCallback();
    }
    }
    break;
    }
    }
    [...]
    和标题:
    #include "vtkRenderWindowInteractor.h"
    #include "vtkRenderingUIModule.h" // For export macro
    #include <X11/Xlib.h> // Needed for X types in the public interface

    class vtkCallbackCommand;
    class vtkXRenderWindowInteractorInternals;

    class VTKRENDERINGUI_EXPORT vtkXRenderWindowInteractor : public vtkRenderWindowInteractor
    {
    public:
    static vtkXRenderWindowInteractor* New();
    vtkTypeMacro(vtkXRenderWindowInteractor, vtkRenderWindowInteractor);
    void PrintSelf(ostream& os, vtkIndent indent) override;

    /**
    * Initializes the event handlers without an XtAppContext. This is
    * good for when you don't have a user interface, but you still
    * want to have mouse interaction.
    */
    void Initialize() override;

    /**
    * Break the event loop on 'q','e' keypress. Want more ???
    */
    void TerminateApp() override;

    /**
    * Run the event loop and return. This is provided so that you can
    * implement your own event loop but yet use the vtk event handling as
    * well.
    */
    void ProcessEvents() override;

    ///@{
    /**
    * Enable/Disable interactions. By default interactors are enabled when
    * initialized. Initialize() must be called prior to enabling/disabling
    * interaction. These methods are used when a window/widget is being
    * shared by multiple renderers and interactors. This allows a "modal"
    * display where one interactor is active when its data is to be displayed
    * and all other interactors associated with the widget are disabled
    * when their data is not displayed.
    */
    void Enable() override;
    void Disable() override;
    ///@}

    /**
    * Update the Size data member and set the associated RenderWindow's
    * size.
    */
    void UpdateSize(int, int) override;

    /**
    * Re-defines virtual function to get mouse position by querying X-server.
    */
    void GetMousePosition(int* x, int* y) override;

    void DispatchEvent(XEvent*);

    protected:
    vtkXRenderWindowInteractor();
    ~vtkXRenderWindowInteractor() override;

    /**
    * Update the Size data member and set the associated RenderWindow's
    * size but do not resize the XWindow.
    */
    void UpdateSizeNoXResize(int, int);

    // Using static here to avoid destroying context when many apps are open:
    static int NumAppInitialized;

    Display* DisplayId;
    Window WindowId;
    Atom KillAtom;
    int PositionBeforeStereo[2];
    vtkXRenderWindowInteractorInternals* Internal;

    // Drag and drop related
    Window XdndSource;
    Atom XdndPositionAtom;
    Atom XdndDropAtom;
    Atom XdndActionCopyAtom;
    Atom XdndStatusAtom;
    Atom XdndFinishedAtom;

    ///@{
    /**
    * X-specific internal timer methods. See the superclass for detailed
    * documentation.
    */
    int InternalCreateTimer(int timerId, int timerType, unsigned long duration) override;
    int InternalDestroyTimer(int platformTimerId) override;
    ///@}

    void FireTimers();

    /**
    * This will start up the X event loop and never return. If you
    * call this method it will loop processing X events until the
    * application is exited.
    */
    void StartEventLoop() override;

    private:
    vtkXRenderWindowInteractor(const vtkXRenderWindowInteractor&) = delete;
    void operator=(const vtkXRenderWindowInteractor&) = delete;
    };

    #endif
    完整的文件可以在这里看到:
    https://gitlab.kitware.com/vtk/vtk/-/blob/master/Rendering/UI/vtkXRenderWindowInteractor.cxx
    你可以按照我的思路和调试在这里:
    https://gitlab.kitware.com/f3d/f3d/-/issues/228
    要测试此代码,一个简单的方法是使用 F3D,它使用已删除的文件,但一个简单的 VTK 应用程序也应该可以工作:
    https://gitlab.kitware.com/f3d/f3d

    最佳答案

    当前的海豚问题
    从一些测试来看,问题出在 XdndFinished 的准备和发送上。 ClientMessage处理 SelectionNotify 时回到拖放源事件。
    代替:

    reply.xclient.window = event->xclient.data.l[0];
    该行应该是:
    reply.xclient.window = this->XdndSource;
    这将对齐 XClientMessageEvent window目标窗口参数为 XSendEvent 的成员.这可能是一个简单的复制粘贴错误,如 xclientSelectionNotify 无效事件类型。很可能是 window 的实际值以前没有被检查过,但最近已更改,因此出现错误。
    spec很好地涵盖了这一点,并且还提出了一些其他需要考虑的事项:
  • 对于 data.l[1] :“如果当前目标接受放置并成功执行接受的放置操作,则设置位 0。(版本 5 中的新增功能)”,因此使用 itemCount因为当计数为偶数时,值在技术上是不正确的
  • 如处理XdndPosition不需要实际跟踪当前位置的位置(即,如果您只是将整个窗口用作放置目标),您可以发送 XdndStatus回复 XdndEnter留言

  • 以前的 Thunar 问题
    进一步研究这一点,我对 Thunar 的前一个问题进行了一些故障排除,归结为代码处理 XdndDrop假设传入数据的格式可以转换为 UTF8_STRING .此 diff GLFW 处理几乎完全相同的问题。
    如果,在处理 XdndEnter 时消息,您检查 xclient.data.l[2] 的值通过 xclient.data.l[4]您可以看到 Dolphin 报告支持以下格式:
  • text/uri-list
  • text/x-moz-url
  • text/plain

  • 而 Thunar 仅支持以下内容:
  • text/uri-list

  • 最简单的解决方案是:
  • 处理时跟踪支持的格式 XdndEnter
  • 将此格式提供给 XConvertSelection处理时XdndDrop (而不是 UTF8_STRING)
  • 处理 SelectionNotify 时适当处理格式事件

  • 更完整地说,如果 xclient.data.l[1] 的位 0设置在 XdndEnter消息,你应该得到 XdndTypeList拖放源窗口的属性,并基于该属性选择格式,而不是消息本身包含的格式。

    关于c++ - Xdnd drop 支持错误的实现,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/67750236/

    24 4 0
    Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
    广告合作:1813099741@qq.com 6ren.com