VirtualBox

Changeset 99595 in vbox


Ignore:
Timestamp:
May 4, 2023 8:58:54 AM (19 months ago)
Author:
vboxsync
Message:

Guest Additions/VBoxClient: Moved the X11-specific code for drag'n drop into an own submodule and using the actual service as a (runtime) wrapper (for also the Wayland-specific code later). bugref:10427

Location:
trunk/src/VBox/Additions/x11/VBoxClient
Files:
1 added
2 edited
1 copied

Legend:

Unmodified
Added
Removed
  • trunk/src/VBox/Additions/x11/VBoxClient/Makefile.kmk

    r99590 r99595  
    142142        $(if $(VBOX_WITH_DRAG_AND_DROP_GH),VBOX_WITH_DRAG_AND_DROP_GH,)
    143143 VBoxClient_SOURCES  += \
    144         draganddrop.cpp
     144        draganddrop.cpp \
     145        draganddrop-x11.cpp
    145146 VBoxClient_LIBS     += \
    146147        $(VBOX_LIB_VBGL_R3) \
  • trunk/src/VBox/Additions/x11/VBoxClient/draganddrop-x11.cpp

    r99557 r99595  
    11/* $Id$ */
    22/** @file
    3  * X11 guest client - Drag and drop implementation.
     3 * Guest Additions - VBoxClient X11 drag'n drop implementation.
    44 */
    55
     
    5454#include "VBox/HostServices/DragAndDropSvc.h"
    5555#include "VBoxClient.h"
     56#include "draganddrop.h"
    5657
    5758
     
    132133/** The notification header text for VBClShowNotify(). */
    133134#define VBOX_DND_SHOWNOTIFY_HEADER              VBOX_PRODUCT " Drag'n Drop"
    134 
    135 /**
    136  * Structure for storing new X11 events and HGCM messages
    137  * into a single event queue.
    138  */
    139 typedef struct DNDEVENT
    140 {
    141     enum DnDEventType
    142     {
    143         /** Unknown event, do not use. */
    144         DnDEventType_Unknown = 0,
    145         /** VBGLR3DNDEVENT event. */
    146         DnDEventType_HGCM,
    147         /** X11 event. */
    148         DnDEventType_X11,
    149         /** Blow the type up to 32-bit. */
    150         DnDEventType_32BIT_HACK = 0x7fffffff
    151     };
    152     /** Event type. */
    153     DnDEventType enmType;
    154     union
    155     {
    156         PVBGLR3DNDEVENT hgcm;
    157         XEvent x11;
    158     };
    159 #ifdef IN_GUEST
    160     RTMEM_IMPLEMENT_NEW_AND_DELETE();
    161 #endif
    162 } DNDEVENT;
    163 /** Pointer to a DnD event. */
    164 typedef DNDEVENT *PDNDEVENT;
    165135
    166136enum XA_Type
     
    259229} XdndMsg;
    260230
    261 class DragAndDropService;
     231class VBClX11DnDSvc;
    262232
    263233/** List of Atoms. */
    264234#define VBoxDnDAtomList RTCList<Atom>
    265235
    266 class xHelpers
     236class VBClX11DnDXHelpers
    267237{
    268238public:
    269239
    270     static xHelpers *getInstance(Display *pDisplay = 0)
     240    static VBClX11DnDXHelpers *getInstance(Display *pDisplay = 0)
    271241    {
    272242        if (!m_pInstance)
    273243        {
    274244            AssertPtrReturn(pDisplay, NULL);
    275             m_pInstance = new xHelpers(pDisplay);
     245            m_pInstance = new VBClX11DnDXHelpers(pDisplay);
    276246        }
    277247
     
    335305    RTMEM_IMPLEMENT_NEW_AND_DELETE();
    336306#endif
    337     xHelpers(Display *pDisplay)
     307    VBClX11DnDXHelpers(Display *pDisplay)
    338308      : m_pDisplay(pDisplay)
    339309    {
     
    345315
    346316    /* Private member vars */
    347     static xHelpers   *m_pInstance;
     317    static VBClX11DnDXHelpers   *m_pInstance;
    348318    Display           *m_pDisplay;
    349319    Atom               m_xAtoms[XA_End];
     
    352322
    353323/* Some xHelpers convenience defines. */
    354 #define gX11 xHelpers::getInstance()
    355 #define xAtom(xa) xHelpers::getInstance()->xAtom((xa))
    356 #define xAtomToString(xa) xHelpers::getInstance()->xAtomToString((xa))
     324#define gX11 VBClX11DnDXHelpers::getInstance()
     325#define xAtom(xa) VBClX11DnDXHelpers::getInstance()->xAtom((xa))
     326#define xAtomToString(xa) VBClX11DnDXHelpers::getInstance()->xAtomToString((xa))
    357327
    358328/*********************************************************************************************************************************
     
    360330 ********************************************************************************************************************************/
    361331
    362 xHelpers *xHelpers::m_pInstance = NULL;
     332VBClX11DnDXHelpers *VBClX11DnDXHelpers::m_pInstance = NULL;
    363333
    364334/* Has to be in sync with the XA_Type enum. */
    365 const char *xHelpers::m_xAtomNames[] =
     335const char *VBClX11DnDXHelpers::m_xAtomNames[] =
    366336{
    367337    /* States */
     
    398368};
    399369
    400 RTCString xHelpers::xErrorToString(int xRc) const
     370RTCString VBClX11DnDXHelpers::xErrorToString(int xRc) const
    401371{
    402372    switch (xRc)
     
    425395
    426396/** @todo Make this iterative. */
    427 Window xHelpers::applicationWindowBelowCursor(Window wndParent) const
     397Window VBClX11DnDXHelpers::applicationWindowBelowCursor(Window wndParent) const
    428398{
    429399    /* No parent, nothing to do. */
     
    480450 ** @todo Move all proxy window-related stuff into this class! Clean up this mess.
    481451 */
    482 class VBoxDnDProxyWnd
     452class VBClX11DnDProxyWnd
    483453{
    484454
     
    487457    RTMEM_IMPLEMENT_NEW_AND_DELETE();
    488458#endif
    489     VBoxDnDProxyWnd(void);
    490     virtual ~VBoxDnDProxyWnd(void);
     459    VBClX11DnDProxyWnd(void);
     460    virtual ~VBClX11DnDProxyWnd(void);
    491461
    492462public:
     
    509479
    510480/** This class only serve to avoid dragging in generic new() and delete(). */
    511 class WrappedXEvent
     481class VBClX11DnDWrappedXEvent
    512482{
    513483public:
     
    518488    RTMEM_IMPLEMENT_NEW_AND_DELETE();
    519489#endif
    520     WrappedXEvent(const XEvent &a_rSrcEvent)
     490    VBClX11DnDWrappedXEvent(const XEvent &a_rSrcEvent)
    521491    {
    522492        m_Event = a_rSrcEvent;
    523493    }
    524494
    525     WrappedXEvent()
     495    VBClX11DnDWrappedXEvent()
    526496    {
    527497        RT_ZERO(m_Event);
    528498    }
    529499
    530     WrappedXEvent &operator=(const XEvent &a_rSrcEvent)
     500    VBClX11DnDWrappedXEvent &operator=(const XEvent &a_rSrcEvent)
    531501    {
    532502        m_Event = a_rSrcEvent;
     
    541511 * For now only one DragInstance will exits when the app is running.
    542512 */
    543 class DragInstance
     513class VBClX11DnDInst
    544514{
    545515public:
     
    565535    RTMEM_IMPLEMENT_NEW_AND_DELETE();
    566536#endif
    567     DragInstance(Display *pDisplay, DragAndDropService *pParent);
     537    VBClX11DnDInst(Display *pDisplay, VBClX11DnDSvc *pParent);
    568538
    569539public:
     
    632602    VBGLR3GUESTDNDCMDCTX        m_dndCtx;
    633603    /** Pointer to service instance. */
    634     DragAndDropService         *m_pParent;
     604    VBClX11DnDSvc              *m_pParent;
    635605    /** Pointer to X display operating on. */
    636606    Display                    *m_pDisplay;
     
    642612    Window                      m_wndRoot;
    643613    /** Proxy window. */
    644     VBoxDnDProxyWnd             m_wndProxy;
     614    VBClX11DnDProxyWnd          m_wndProxy;
    645615    /** Current source/target window handle. */
    646616    Window                      m_wndCur;
     
    670640    volatile uint32_t           m_enmState;
    671641    /** The instance's own X event queue. */
    672     RTCMTList<WrappedXEvent>    m_eventQueueList;
     642    RTCMTList<VBClX11DnDWrappedXEvent>    m_eventQueueList;
    673643    /** Critical section for providing serialized access to list event queue's contents. */
    674644    RTCRITSECT                  m_eventQueueCS;
     
    684654};
    685655
    686 /**
    687  * Service class which implements drag'n drop.
    688  */
    689 class DragAndDropService
    690 {
    691 public:
    692     DragAndDropService(void)
    693       : m_pDisplay(NULL)
    694       , m_hHGCMThread(NIL_RTTHREAD)
    695       , m_hX11Thread(NIL_RTTHREAD)
    696       , m_hEventSem(NIL_RTSEMEVENT)
    697       , m_pCurDnD(NULL)
    698       , m_fStop(false)
    699     {
    700         RT_ZERO(m_dndCtx);
    701     }
    702 
    703     int  init(void);
    704     int  worker(bool volatile *pfShutdown);
    705     void reset(void);
    706     void stop(void);
    707     int  term(void);
    708 
    709 private:
    710 
    711     static DECLCALLBACK(int) hgcmEventThread(RTTHREAD hThread, void *pvUser);
    712     static DECLCALLBACK(int) x11EventThread(RTTHREAD hThread, void *pvUser);
    713 
    714     /* Private member vars */
    715     Display             *m_pDisplay;
    716     /** Our (thread-safe) event queue with mixed events (DnD HGCM / X11). */
    717     RTCMTList<DNDEVENT>  m_eventQueue;
    718     /** Critical section for providing serialized access to list
    719      *  event queue's contents. */
    720     RTCRITSECT           m_eventQueueCS;
    721     /** Thread handle for the HGCM message pumping thread. */
    722     RTTHREAD             m_hHGCMThread;
    723     /** Thread handle for the X11 message pumping thread. */
    724     RTTHREAD             m_hX11Thread;
    725     /** This service' DnD command context. */
    726     VBGLR3GUESTDNDCMDCTX m_dndCtx;
    727     /** Event semaphore for new DnD events. */
    728     RTSEMEVENT           m_hEventSem;
    729     /** Pointer to the allocated DnD instance.
    730         Currently we only support and handle one instance at a time. */
    731     DragInstance        *m_pCurDnD;
    732     /** Stop indicator flag to signal the thread that it should shut down. */
    733     bool                 m_fStop;
    734 
    735     friend class DragInstance;
    736 } g_Svc;
    737656
    738657/*********************************************************************************************************************************
     
    740659 ********************************************************************************************************************************/
    741660
    742 DragInstance::DragInstance(Display *pDisplay, DragAndDropService *pParent)
     661VBClX11DnDInst::VBClX11DnDInst(Display *pDisplay, VBClX11DnDSvc *pParent)
    743662    : m_pParent(pParent)
    744663    , m_pDisplay(pDisplay)
     
    762681 * Stops this drag instance.
    763682 */
    764 void DragInstance::stop(void)
     683void VBClX11DnDInst::stop(void)
    765684{
    766685    LogFlowFuncEnter();
     
    777696 * @return VBox status code.
    778697 */
    779 int DragInstance::term(void)
     698int VBClX11DnDInst::term(void)
    780699{
    781700    LogFlowFuncEnter();
     
    806725 * Resets this drag instance.
    807726 */
    808 void DragInstance::reset(void)
     727void VBClX11DnDInst::reset(void)
    809728{
    810729    LogFlowFuncEnter();
     
    871790 * @param   uScreenID             X' screen ID to use.
    872791 */
    873 int DragInstance::init(uint32_t uScreenID)
     792int VBClX11DnDInst::init(uint32_t uScreenID)
    874793{
    875794    int rc = VbglR3DnDConnect(&m_dndCtx);
     
    1016935 * @param   e                       X11 event to handle.
    1017936 */
    1018 int DragInstance::onX11ClientMessage(const XEvent &e)
     937int VBClX11DnDInst::onX11ClientMessage(const XEvent &e)
    1019938{
    1020939    AssertReturn(e.type == ClientMessage, VERR_INVALID_PARAMETER);
     
    13311250}
    13321251
    1333 int DragInstance::onX11MotionNotify(const XEvent &e)
     1252int VBClX11DnDInst::onX11MotionNotify(const XEvent &e)
    13341253{
    13351254    RT_NOREF1(e);
     
    13481267 * @remark
    13491268 */
    1350 int DragInstance::onX11SelectionClear(const XEvent &e)
     1269int VBClX11DnDInst::onX11SelectionClear(const XEvent &e)
    13511270{
    13521271    RT_NOREF1(e);
     
    13641283 * @param   e                       X11 event to handle.
    13651284 */
    1366 int DragInstance::onX11SelectionNotify(const XEvent &e)
     1285int VBClX11DnDInst::onX11SelectionNotify(const XEvent &e)
    13671286{
    13681287    AssertReturn(e.type == SelectionNotify, VERR_INVALID_PARAMETER);
     
    14061325 * @param   evReq               X11 event to handle.
    14071326 */
    1408 int DragInstance::onX11SelectionRequest(const XEvent &evReq)
     1327int VBClX11DnDInst::onX11SelectionRequest(const XEvent &evReq)
    14091328{
    14101329    AssertReturn(evReq.type == SelectionRequest, VERR_INVALID_PARAMETER);
     
    15661485 * @param   e                       X11 event to handle.
    15671486 */
    1568 int DragInstance::onX11Event(const XEvent &e)
     1487int VBClX11DnDInst::onX11Event(const XEvent &e)
    15691488{
    15701489    int rc;
     
    16191538}
    16201539
    1621 int DragInstance::waitForStatusChange(uint32_t enmState, RTMSINTERVAL uTimeoutMS /* = 30000 */)
     1540int VBClX11DnDInst::waitForStatusChange(uint32_t enmState, RTMSINTERVAL uTimeoutMS /* = 30000 */)
    16221541{
    16231542    const uint64_t uiStart = RTTimeMilliTS();
     
    16521571 * @param   uTimeoutMS              Timeout (in ms) to wait for the event.
    16531572 */
    1654 bool DragInstance::waitForX11Msg(XEvent &evX, int iType, RTMSINTERVAL uTimeoutMS /* = 100 */)
     1573bool VBClX11DnDInst::waitForX11Msg(XEvent &evX, int iType, RTMSINTERVAL uTimeoutMS /* = 100 */)
    16551574{
    16561575    LogFlowThisFunc(("iType=%d, uTimeoutMS=%RU32, cEventQueue=%zu\n", iType, uTimeoutMS, m_eventQueueList.size()));
     
    17091628 * @param   uTimeoutMS              Timeout (in ms) to wait for the event.
    17101629 */
    1711 bool DragInstance::waitForX11ClientMsg(XClientMessageEvent &evMsg, Atom aType,
     1630bool VBClX11DnDInst::waitForX11ClientMsg(XClientMessageEvent &evMsg, Atom aType,
    17121631                                       RTMSINTERVAL uTimeoutMS /* = 100 */)
    17131632{
     
    17811700 * @param   dndListActionsAllowed   (ORed) List of supported actions from the host.
    17821701 */
    1783 int DragInstance::hgEnter(const RTCList<RTCString> &lstFormats, uint32_t dndListActionsAllowed)
     1702int VBClX11DnDInst::hgEnter(const RTCList<RTCString> &lstFormats, uint32_t dndListActionsAllowed)
    17841703{
    17851704    LogFlowThisFunc(("mode=%RU32, state=%RU32\n", m_enmMode, m_enmState));
     
    18591778 *                display area.
    18601779 */
    1861 int DragInstance::hgLeave(void)
     1780int VBClX11DnDInst::hgLeave(void)
    18621781{
    18631782    if (g_cVerbosity)
     
    18801799 *                                  as soon as the operation successfully finishes.
    18811800 */
    1882 int DragInstance::hgMove(uint32_t uPosX, uint32_t uPosY, VBOXDNDACTION dndActionDefault)
     1801int VBClX11DnDInst::hgMove(uint32_t uPosX, uint32_t uPosY, VBOXDNDACTION dndActionDefault)
    18831802{
    18841803    LogFlowThisFunc(("mode=%RU32, state=%RU32\n", m_enmMode, m_enmState));
     
    20681987 *                                  as soon as the operation successfully finishes.
    20691988 */
    2070 int DragInstance::hgDrop(uint32_t uPosX, uint32_t uPosY, VBOXDNDACTION dndActionDefault)
     1989int VBClX11DnDInst::hgDrop(uint32_t uPosX, uint32_t uPosY, VBOXDNDACTION dndActionDefault)
    20711990{
    20721991    RT_NOREF3(uPosX, uPosY, dndActionDefault);
     
    21082027 * @param   pMeta               Pointer to meta data from host.
    21092028 */
    2110 int DragInstance::hgDataReceive(PVBGLR3GUESTDNDMETADATA pMeta)
     2029int VBClX11DnDInst::hgDataReceive(PVBGLR3GUESTDNDMETADATA pMeta)
    21112030{
    21122031    LogFlowThisFunc(("enmMode=%RU32, enmState=%RU32\n", m_enmMode, m_enmState));
     
    22202139 * @returns IPRT status code.
    22212140 */
    2222 int DragInstance::checkForSessionChange(void)
     2141int VBClX11DnDInst::checkForSessionChange(void)
    22232142{
    22242143    uint64_t uSessionID;
     
    22472166 * @returns IPRT status code.
    22482167 */
    2249 int DragInstance::ghIsDnDPending(void)
     2168int VBClX11DnDInst::ghIsDnDPending(void)
    22502169{
    22512170    LogFlowThisFunc(("mode=%RU32, state=%RU32\n", m_enmMode, m_enmState));
     
    24182337 * @param   dndActionRequested      Requested action to perform on the guest.
    24192338 */
    2420 int DragInstance::ghDropped(const RTCString &strFormat, VBOXDNDACTION dndActionRequested)
     2339int VBClX11DnDInst::ghDropped(const RTCString &strFormat, VBOXDNDACTION dndActionRequested)
    24212340{
    24222341    LogFlowThisFunc(("mode=%RU32, state=%RU32, strFormat=%s, dndActionRequested=0x%x\n",
     
    26332552 * @returns IPRT status code.
    26342553 */
    2635 int DragInstance::mouseCursorFakeMove(void)
     2554int VBClX11DnDInst::mouseCursorFakeMove(void)
    26362555{
    26372556    int iScreenID = XDefaultScreen(m_pDisplay);
     
    26812600 * @param   iPosY                   Absolute Y coordinate.
    26822601 */
    2683 int DragInstance::mouseCursorMove(int iPosX, int iPosY)
     2602int VBClX11DnDInst::mouseCursorMove(int iPosX, int iPosY)
    26842603{
    26852604    int const iScreenID = XDefaultScreen(m_pDisplay);
     
    27262645 * @param   fPress                  Whether to press or release the mouse button.
    27272646 */
    2728 void DragInstance::mouseButtonSet(Window wndDest, int rx, int ry, int iButton, bool fPress)
     2647void VBClX11DnDInst::mouseButtonSet(Window wndDest, int rx, int ry, int iButton, bool fPress)
    27292648{
    27302649    LogFlowThisFunc(("wndDest=%#x, rx=%d, ry=%d, iBtn=%d, fPress=%RTbool\n",
     
    28082727 * @param   piRootY                 Y coordinate relative to the root window's origin. Optional.
    28092728 */
    2810 int DragInstance::proxyWinShow(int *piRootX /* = NULL */, int *piRootY /* = NULL */) const
     2729int VBClX11DnDInst::proxyWinShow(int *piRootX /* = NULL */, int *piRootY /* = NULL */) const
    28112730{
    28122731    /* piRootX is optional. */
     
    28682787 * Hides the (invisible) proxy window.
    28692788 */
    2870 int DragInstance::proxyWinHide(void)
     2789int VBClX11DnDInst::proxyWinHide(void)
    28712790{
    28722791    LogFlowFuncEnter();
     
    28872806 * @param   wndThis                 Window to retrieve name for.
    28882807 */
    2889 char *DragInstance::wndX11GetNameA(Window wndThis) const
     2808char *VBClX11DnDInst::wndX11GetNameA(Window wndThis) const
    28902809{
    28912810    char *pszName = NULL;
     
    29112830 * @param   wndThis                 Window to clear the list for.
    29122831 */
    2913 void DragInstance::wndXDnDClearActionList(Window wndThis) const
     2832void VBClX11DnDInst::wndXDnDClearActionList(Window wndThis) const
    29142833{
    29152834    XDeleteProperty(m_pDisplay, wndThis, xAtom(XA_XdndActionList));
     
    29212840 * @param   wndThis                 Window to clear the list for.
    29222841 */
    2923 void DragInstance::wndXDnDClearFormatList(Window wndThis) const
     2842void VBClX11DnDInst::wndXDnDClearFormatList(Window wndThis) const
    29242843{
    29252844    XDeleteProperty(m_pDisplay, wndThis, xAtom(XA_XdndTypeList));
     
    29332852 * @param   lstActions              Reference to VBoxDnDAtomList to store the action into.
    29342853 */
    2935 int DragInstance::wndXDnDGetActionList(Window wndThis, VBoxDnDAtomList &lstActions) const
     2854int VBClX11DnDInst::wndXDnDGetActionList(Window wndThis, VBoxDnDAtomList &lstActions) const
    29362855{
    29372856    Atom iActType = None;
     
    29782897 * @param   lstTypes                Reference to VBoxDnDAtomList to store the formats into.
    29792898 */
    2980 int DragInstance::wndXDnDGetFormatList(Window wndThis, VBoxDnDAtomList &lstTypes) const
     2899int VBClX11DnDInst::wndXDnDGetFormatList(Window wndThis, VBoxDnDAtomList &lstTypes) const
    29812900{
    29822901    Atom iActType = None;
     
    30222941 * @param   lstActions              Reference to list of XDnD actions to set.
    30232942 */
    3024 int DragInstance::wndXDnDSetActionList(Window wndThis, const VBoxDnDAtomList &lstActions) const
     2943int VBClX11DnDInst::wndXDnDSetActionList(Window wndThis, const VBoxDnDAtomList &lstActions) const
    30252944{
    30262945    if (lstActions.isEmpty())
     
    30442963 * @param   lstFormats              Reference to list of XDnD formats to set.
    30452964 */
    3046 int DragInstance::wndXDnDSetFormatList(Window wndThis, Atom atmProp, const VBoxDnDAtomList &lstFormats) const
     2965int VBClX11DnDInst::wndXDnDSetFormatList(Window wndThis, Atom atmProp, const VBoxDnDAtomList &lstFormats) const
    30472966{
    30482967    if (lstFormats.isEmpty())
     
    30652984 * @param   lstAtoms                Reference to VBoxDnDAtomList list to store results in.
    30662985 */
    3067 int DragInstance::appendFormatsToList(const RTCList<RTCString> &lstFormats, VBoxDnDAtomList &lstAtoms) const
     2986int VBClX11DnDInst::appendFormatsToList(const RTCList<RTCString> &lstFormats, VBoxDnDAtomList &lstAtoms) const
    30682987{
    30692988    for (size_t i = 0; i < lstFormats.size(); ++i)
     
    30813000 * @param   lstAtoms                Reference to VBoxDnDAtomList list to store results in.
    30823001 */
    3083 int DragInstance::appendDataToList(const void *pvData, uint32_t cbData, VBoxDnDAtomList &lstAtoms) const
     3002int VBClX11DnDInst::appendDataToList(const void *pvData, uint32_t cbData, VBoxDnDAtomList &lstAtoms) const
    30843003{
    30853004    RT_NOREF1(lstAtoms);
     
    31233042 */
    31243043/* static */
    3125 Atom DragInstance::toAtomAction(VBOXDNDACTION dndAction)
     3044Atom VBClX11DnDInst::toAtomAction(VBOXDNDACTION dndAction)
    31263045{
    31273046    /* Ignore is None. */
     
    31403059 */
    31413060/* static */
    3142 int DragInstance::toAtomActions(VBOXDNDACTIONLIST dndActionList, VBoxDnDAtomList &lstAtoms)
     3061int VBClX11DnDInst::toAtomActions(VBOXDNDACTIONLIST dndActionList, VBoxDnDAtomList &lstAtoms)
    31433062{
    31443063    if (hasDnDCopyAction(dndActionList))
     
    31593078 */
    31603079/* static */
    3161 uint32_t DragInstance::toHGCMAction(Atom atom)
     3080uint32_t VBClX11DnDInst::toHGCMAction(Atom atom)
    31623081{
    31633082    uint32_t uAction = VBOX_DND_ACTION_IGNORE;
     
    31803099 */
    31813100/* static */
    3182 uint32_t DragInstance::toHGCMActions(const VBoxDnDAtomList &lstActions)
     3101uint32_t VBClX11DnDInst::toHGCMActions(const VBoxDnDAtomList &lstActions)
    31833102{
    31843103    uint32_t uActions = VBOX_DND_ACTION_IGNORE;
     
    31943113 ********************************************************************************************************************************/
    31953114
    3196 VBoxDnDProxyWnd::VBoxDnDProxyWnd(void)
     3115VBClX11DnDProxyWnd::VBClX11DnDProxyWnd(void)
    31973116    : pDisp(NULL)
    31983117    , hWnd(0)
     
    32053124}
    32063125
    3207 VBoxDnDProxyWnd::~VBoxDnDProxyWnd(void)
     3126VBClX11DnDProxyWnd::~VBClX11DnDProxyWnd(void)
    32083127{
    32093128    destroy();
    32103129}
    32113130
    3212 int VBoxDnDProxyWnd::init(Display *pDisplay)
     3131int VBClX11DnDProxyWnd::init(Display *pDisplay)
    32133132{
    32143133    /** @todo What about multiple screens? Test this! */
     
    32223141}
    32233142
    3224 void VBoxDnDProxyWnd::destroy(void)
    3225 {
    3226 
    3227 }
    3228 
    3229 int VBoxDnDProxyWnd::sendFinished(Window hWndSource, VBOXDNDACTION dndAction)
     3143void VBClX11DnDProxyWnd::destroy(void)
     3144{
     3145
     3146}
     3147
     3148int VBClX11DnDProxyWnd::sendFinished(Window hWndSource, VBOXDNDACTION dndAction)
    32303149{
    32313150    /* Was the drop accepted by the host? That is, anything than ignoring. */
     
    32443163    m.data.l[XdndFinishedWindow] = hWnd;                                                         /* Target window. */
    32453164    m.data.l[XdndFinishedFlags]  = fDropAccepted ? RT_BIT(0) : 0;                                /* Was the drop accepted? */
    3246     m.data.l[XdndFinishedAction] = fDropAccepted ? DragInstance::toAtomAction(dndAction) : None; /* Action used on accept. */
     3165    m.data.l[XdndFinishedAction] = fDropAccepted ? VBClX11DnDInst::toAtomAction(dndAction) : None; /* Action used on accept. */
    32473166
    32483167    int xRc = XSendEvent(pDisp, hWndSource, True, NoEventMask, reinterpret_cast<XEvent*>(&m));
     
    32633182
    32643183/** @copydoc VBCLSERVICE::pfnInit */
    3265 int DragAndDropService::init(void)
     3184int VBClX11DnDSvc::init(void)
    32663185{
    32673186    LogFlowFuncEnter();
     
    32753194    }
    32763195
    3277     xHelpers *pHelpers = xHelpers::getInstance(m_pDisplay);
     3196    VBClX11DnDXHelpers *pHelpers = VBClX11DnDXHelpers::getInstance(m_pDisplay);
    32783197    if (!pHelpers)
    32793198        return VERR_NO_MEMORY;
     
    33273246
    33283247/** @copydoc VBCLSERVICE::pfnWorker */
    3329 int DragAndDropService::worker(bool volatile *pfShutdown)
     3248int VBClX11DnDSvc::worker(bool volatile *pfShutdown)
    33303249{
    33313250    int rc;
    33323251    do
    33333252    {
    3334         m_pCurDnD = new DragInstance(m_pDisplay, this);
     3253        m_pCurDnD = new VBClX11DnDInst(m_pDisplay, this);
    33353254        if (!m_pCurDnD)
    33363255        {
     
    33593278        do
    33603279        {
    3361             DNDEVENT e;
     3280            VBCLDNDEVENT e;
    33623281            RT_ZERO(e);
    33633282
     
    33973316                }
    33983317
    3399                 if (e.enmType == DNDEVENT::DnDEventType_HGCM)
     3318                if (e.enmType == VBCLDNDEVENT::DnDEventType_HGCM)
    34003319                {
    34013320                    PVBGLR3DNDEVENT pVbglR3Event = e.hgcm;
     
    35203439                        break;
    35213440                }
    3522                 else if (e.enmType == DNDEVENT::DnDEventType_X11)
     3441                else if (e.enmType == VBCLDNDEVENT::DnDEventType_X11)
    35233442                {
    35243443                    LogFlowThisFunc(("X11 event (type %#x)\n", e.x11.type));
     
    35593478 * Resets the DnD service' data.
    35603479 */
    3561 void DragAndDropService::reset(void)
     3480void VBClX11DnDSvc::reset(void)
    35623481{
    35633482    LogFlowFuncEnter();
     
    35763495            switch (m_eventQueue[i].enmType)
    35773496            {
    3578                 case DNDEVENT::DnDEventType_HGCM:
     3497                case VBCLDNDEVENT::DnDEventType_HGCM:
    35793498                {
    35803499                    VbglR3DnDEventFree(m_eventQueue[i].hgcm);
     
    35983517
    35993518/** @copydoc VBCLSERVICE::pfnStop */
    3600 void DragAndDropService::stop(void)
     3519void VBClX11DnDSvc::stop(void)
    36013520{
    36023521    LogFlowFuncEnter();
     
    36163535
    36173536/** @copydoc VBCLSERVICE::pfnTerm */
    3618 int DragAndDropService::term(void)
     3537int VBClX11DnDSvc::term(void)
    36193538{
    36203539    int rc = VINF_SUCCESS;
     
    36713590    }
    36723591
    3673     xHelpers::destroyInstance();
     3592    VBClX11DnDXHelpers::destroyInstance();
    36743593
    36753594    return rc;
     
    36863605 */
    36873606/* static */
    3688 DECLCALLBACK(int) DragAndDropService::hgcmEventThread(RTTHREAD hThread, void *pvUser)
     3607DECLCALLBACK(int) VBClX11DnDSvc::hgcmEventThread(RTTHREAD hThread, void *pvUser)
    36893608{
    36903609    AssertPtrReturn(pvUser, VERR_INVALID_PARAMETER);
    3691     DragAndDropService *pThis = static_cast<DragAndDropService*>(pvUser);
     3610    VBClX11DnDSvc *pThis = static_cast<VBClX11DnDSvc*>(pvUser);
    36923611
    36933612    /* Let the service instance know in any case. */
     
    36993618    /* Number of invalid messages skipped in a row. */
    37003619    int cMsgSkippedInvalid = 0;
    3701     DNDEVENT e;
     3620    VBCLDNDEVENT e;
    37023621
    37033622    do
    37043623    {
    37053624        RT_ZERO(e);
    3706         e.enmType = DNDEVENT::DnDEventType_HGCM;
     3625        e.enmType = VBCLDNDEVENT::DnDEventType_HGCM;
    37073626
    37083627        /* Wait for new events. */
     
    37593678 */
    37603679/* static */
    3761 DECLCALLBACK(int) DragAndDropService::x11EventThread(RTTHREAD hThread, void *pvUser)
     3680DECLCALLBACK(int) VBClX11DnDSvc::x11EventThread(RTTHREAD hThread, void *pvUser)
    37623681{
    37633682    AssertPtrReturn(pvUser, VERR_INVALID_PARAMETER);
    3764     DragAndDropService *pThis = static_cast<DragAndDropService*>(pvUser);
     3683    VBClX11DnDSvc *pThis = static_cast<VBClX11DnDSvc*>(pvUser);
    37653684    AssertPtr(pThis);
    37663685
     
    37753694    VBClLogVerbose(2, "X11 thread started\n");
    37763695
    3777     DNDEVENT e;
     3696    VBCLDNDEVENT e;
    37783697    RT_ZERO(e);
    3779     e.enmType = DNDEVENT::DnDEventType_X11;
     3698    e.enmType = VBCLDNDEVENT::DnDEventType_X11;
    37803699
    37813700    do
     
    38303749    return rc;
    38313750}
    3832 /**
    3833  * @interface_method_impl{VBCLSERVICE,pfnInit}
    3834  */
    3835 static DECLCALLBACK(int) vbclDnDInit(void)
    3836 {
    3837     return g_Svc.init();
    3838 }
    3839 
    3840 /**
    3841  * @interface_method_impl{VBCLSERVICE,pfnWorker}
    3842  */
    3843 static DECLCALLBACK(int) vbclDnDWorker(bool volatile *pfShutdown)
    3844 {
    3845     return g_Svc.worker(pfShutdown);
    3846 }
    3847 
    3848 /**
    3849  * @interface_method_impl{VBCLSERVICE,pfnStop}
    3850  */
    3851 static DECLCALLBACK(void) vbclDnDStop(void)
    3852 {
    3853     g_Svc.stop();
    3854 }
    3855 
    3856 /**
    3857  * @interface_method_impl{VBCLSERVICE,pfnTerm}
    3858  */
    3859 static DECLCALLBACK(int) vbclDnDTerm(void)
    3860 {
    3861     return g_Svc.term();
    3862 }
    3863 
    3864 VBCLSERVICE g_SvcDragAndDrop =
    3865 {
    3866     "dnd",                         /* szName */
    3867     "Drag'n'Drop",                 /* pszDescription */
    3868     ".vboxclient-draganddrop",     /* pszPidFilePathTemplate */
    3869     NULL,                          /* pszUsage */
    3870     NULL,                          /* pszOptions */
    3871     NULL,                          /* pfnOption */
    3872     vbclDnDInit,                   /* pfnInit */
    3873     vbclDnDWorker,                 /* pfnWorker */
    3874     vbclDnDStop,                   /* pfnStop*/
    3875     vbclDnDTerm                    /* pfnTerm */
    3876 };
    3877 
     3751
  • trunk/src/VBox/Additions/x11/VBoxClient/draganddrop.cpp

    r98474 r99595  
    11/* $Id$ */
    22/** @file
    3  * X11 guest client - Drag and drop implementation.
     3 * Guest Additions - Common drag'n drop wrapper service.
    44 */
    55
    66/*
    7  * Copyright (C) 2011-2023 Oracle and/or its affiliates.
     7 * Copyright (C) 2023 Oracle and/or its affiliates.
    88 *
    99 * This file is part of VirtualBox base platform packages, as
     
    2626 */
    2727
    28 #include <X11/Xlib.h>
    29 #include <X11/Xutil.h>
    30 #include <X11/Xatom.h>
    31 #ifdef VBOX_DND_WITH_XTEST
    32 # include <X11/extensions/XTest.h>
    33 #endif
    34 
    35 #include <iprt/asm.h>
    36 #include <iprt/buildconfig.h>
    37 #include <iprt/critsect.h>
    38 #include <iprt/thread.h>
    39 #include <iprt/time.h>
    40 
    41 #include <iprt/cpp/mtlist.h>
    42 #include <iprt/cpp/ministring.h>
    43 
    44 #include <limits.h>
    45 
    46 #ifdef LOG_GROUP
    47 # undef LOG_GROUP
    48 #endif
    49 #define LOG_GROUP LOG_GROUP_GUEST_DND
    50 #include <VBox/log.h>
    51 #include <VBox/VBoxGuestLib.h>
    52 #include <VBox/version.h>
     28#include <iprt/mem.h>
    5329
    5430#include "VBox/HostServices/DragAndDropSvc.h"
    5531#include "VBoxClient.h"
     32#include "draganddrop.h"
    5633
    5734
    58 /* Enable this to handle drag'n drop "promises".
    59  * This is needed for supporting certain applications (i.e. PcManFM on LXDE),
    60  * which require the drag'n drop meta data a lot earlier than actually needed.
    61  * That behavior is similar to macOS' drag'n drop promises, hence the name.
    62  *
    63  * Those applications query the data right while dragging over them (see GtkWidget::drag-motion),
    64  * instead of when the source dropped the data (GtkWidget::drag-drop).
    65  *
    66  * This might be entirely implementation-specific, so not being a bug in GTK/GDK. Also see #9820.
    67  */
    68 #ifdef VBOX_WITH_DRAG_AND_DROP_PROMISES
    69 # undef VBOX_WITH_DRAG_AND_DROP_PROMISES
    70 #endif
     35/** Pointer to the DnD interface class. */
     36static VBClDnDSvc *g_pSvc = NULL;
    7137
    72 /**
    73  * For X11 guest Xdnd is used. See http://www.acc.umu.se/~vatten/XDND.html for
    74  * a walk trough.
    75  *
    76  * Also useful pages:
    77  *     - https://www.freedesktop.org/wiki/Draganddropwarts/
    78  *     - https://www.freedesktop.org/wiki/Specifications/XDNDRevision/
    79  *
    80  * Host -> Guest:
    81  *     For X11 this means mainly forwarding all the events from HGCM to the
    82  *     appropriate X11 events. There exists a proxy window, which is invisible and
    83  *     used for all the X11 communication. On a HGCM Enter event, we set our proxy
    84  *     window as XdndSelection owner with the given mime-types. On every HGCM move
    85  *     event, we move the X11 mouse cursor to the new position and query for the
    86  *     window below that position. Depending on if it is XdndAware, a new window or
    87  *     a known window, we send the appropriate X11 messages to it. On HGCM drop, we
    88  *     send a XdndDrop message to the current window and wait for a X11
    89  *     SelectionMessage from the target window. Because we didn't have the data in
    90  *     the requested mime-type, yet, we save that message and ask the host for the
    91  *     data. When the data is successfully received from the host, we put the data
    92  *     as a property to the window and send a X11 SelectionNotify event to the
    93  *     target window.
    94  *
    95  * Guest -> Host:
    96  *     This is a lot more trickery than H->G. When a pending event from HGCM
    97  *     arrives, we ask if there currently is an owner of the XdndSelection
    98  *     property. If so, our proxy window is shown (1x1, but without backing store)
    99  *     and some mouse event is triggered. This should be followed by an XdndEnter
    100  *     event send to the proxy window. From this event we can fetch the necessary
    101  *     info of the MIME types and allowed actions and send this back to the host.
    102  *     On a drop request from the host, we query for the selection and should get
    103  *     the data in the specified mime-type. This data is send back to the host.
    104  *     After that we send a XdndLeave event to the source window.
    105  *
    106  ** @todo Cancelling (e.g. with ESC key) doesn't work.
    107  ** @todo INCR (incremental transfers) support.
    108  ** @todo Really check for the Xdnd version and the supported features.
    109  ** @todo Either get rid of the xHelpers class or properly unify the code with the drag instance class.
    110  */
    11138
    112 /*********************************************************************************************************************************
    113  * Definitions                                                                                                                   *
    114  ********************************************************************************************************************************/
    115 
    116 /** The Xdnd protocol version we support. */
    117 #define VBOX_XDND_VERSION                       (5)
    118 
    119 /** No flags specified. */
    120 #define VBOX_XDND_STATUS_FLAG_NONE              0
    121 /** Whether the target window accepts the data being dragged over or not. */
    122 #define VBOX_XDND_STATUS_FLAG_ACCEPT            RT_BIT(0)
    123 /** Whether the target window wants XdndPosition messages while dragging stuff over it. */
    124 #define VBOX_XDND_STATUS_FLAG_WANTS_POS         RT_BIT(1)
    125 
    126 /** Whether the target window accepted the drop data or not. */
    127 #define VBOX_XDND_FINISHED_FLAG_SUCCEEDED       RT_BIT(0)
    128 
    129 /** How many X properties our proxy window can hold. */
    130 #define VBOX_MAX_XPROPERTIES (LONG_MAX-1)
    131 
    132 /** The notification header text for VBClShowNotify(). */
    133 #define VBOX_DND_SHOWNOTIFY_HEADER              VBOX_PRODUCT " Drag'n Drop"
    134 
    135 /**
    136  * Structure for storing new X11 events and HGCM messages
    137  * into a single event queue.
    138  */
    139 typedef struct DNDEVENT
    140 {
    141     enum DnDEventType
    142     {
    143         /** Unknown event, do not use. */
    144         DnDEventType_Unknown = 0,
    145         /** VBGLR3DNDEVENT event. */
    146         DnDEventType_HGCM,
    147         /** X11 event. */
    148         DnDEventType_X11,
    149         /** Blow the type up to 32-bit. */
    150         DnDEventType_32BIT_HACK = 0x7fffffff
    151     };
    152     /** Event type. */
    153     DnDEventType enmType;
    154     union
    155     {
    156         PVBGLR3DNDEVENT hgcm;
    157         XEvent x11;
    158     };
    159 #ifdef IN_GUEST
    160     RTMEM_IMPLEMENT_NEW_AND_DELETE();
    161 #endif
    162 } DNDEVENT;
    163 /** Pointer to a DnD event. */
    164 typedef DNDEVENT *PDNDEVENT;
    165 
    166 enum XA_Type
    167 {
    168     /* States */
    169     XA_WM_STATE = 0,
    170     /* Properties */
    171     XA_TARGETS,
    172     XA_MULTIPLE,
    173     XA_INCR,
    174     /* Mime Types */
    175     XA_image_bmp,
    176     XA_image_jpg,
    177     XA_image_tiff,
    178     XA_image_png,
    179     XA_text_uri_list,
    180     XA_text_uri,
    181     XA_text_plain,
    182     XA_TEXT,
    183     /* Xdnd */
    184     XA_XdndSelection,
    185     XA_XdndAware,
    186     XA_XdndEnter,
    187     XA_XdndLeave,
    188     XA_XdndTypeList,
    189     XA_XdndActionList,
    190     XA_XdndPosition,
    191     XA_XdndActionCopy,
    192     XA_XdndActionMove,
    193     XA_XdndActionLink,
    194     XA_XdndStatus,
    195     XA_XdndDrop,
    196     XA_XdndFinished,
    197     /* Our own stop marker */
    198     XA_dndstop,
    199     /* End marker */
    200     XA_End
    201 };
    202 
    203 /**
    204  * Xdnd message value indices, sorted by message type.
    205  */
    206 typedef enum XdndMsg
    207 {
    208     /** XdndEnter. */
    209     XdndEnterTypeCount = 3,         /* Maximum number of types in XdndEnter message. */
    210 
    211     XdndEnterWindow = 0,            /* Source window (sender). */
    212     XdndEnterFlags,                 /* Version in high byte, bit 0 => more data types. */
    213     XdndEnterType1,                 /* First available data type. */
    214     XdndEnterType2,                 /* Second available data type. */
    215     XdndEnterType3,                 /* Third available data type. */
    216 
    217     XdndEnterMoreTypesFlag = 1,     /* Set if there are more than XdndEnterTypeCount. */
    218     XdndEnterVersionRShift = 24,    /* Right shift to position version number. */
    219     XdndEnterVersionMask   = 0xFF,  /* Mask to get version after shifting. */
    220 
    221     /** XdndHere. */
    222     XdndHereWindow = 0,             /* Source window (sender). */
    223     XdndHereFlags,                  /* Reserved. */
    224     XdndHerePt,                     /* X + Y coordinates of mouse (root window coords). */
    225     XdndHereTimeStamp,              /* Timestamp for requesting data. */
    226     XdndHereAction,                 /* Action requested by user. */
    227 
    228     /** XdndPosition. */
    229     XdndPositionWindow = 0,         /* Source window (sender). */
    230     XdndPositionFlags,              /* Flags. */
    231     XdndPositionXY,                 /* X/Y coordinates of the mouse position relative to the root window. */
    232     XdndPositionTimeStamp,          /* Time stamp for retrieving the data. */
    233     XdndPositionAction,             /* Action requested by the user. */
    234 
    235     /** XdndStatus. */
    236     XdndStatusWindow = 0,           /* Target window (sender).*/
    237     XdndStatusFlags,                /* Flags returned by target. */
    238     XdndStatusNoMsgXY,              /* X + Y of "no msg" rectangle (root window coords). */
    239     XdndStatusNoMsgWH,              /* Width + height of "no msg" rectangle. */
    240     XdndStatusAction,               /* Action accepted by target. */
    241 
    242     XdndStatusAcceptDropFlag = 1,   /* Set if target will accept the drop. */
    243     XdndStatusSendHereFlag   = 2,   /* Set if target wants a stream of XdndPosition. */
    244 
    245     /** XdndLeave. */
    246     XdndLeaveWindow = 0,            /* Source window (sender). */
    247     XdndLeaveFlags,                 /* Reserved. */
    248 
    249     /** XdndDrop. */
    250     XdndDropWindow = 0,             /* Source window (sender). */
    251     XdndDropFlags,                  /* Reserved. */
    252     XdndDropTimeStamp,              /* Timestamp for requesting data. */
    253 
    254     /** XdndFinished. */
    255     XdndFinishedWindow = 0,         /* Target window (sender). */
    256     XdndFinishedFlags,              /* Since version 5: Bit 0 is set if the current target accepted the drop. */
    257     XdndFinishedAction              /* Since version 5: Contains the action performed by the target. */
    258 
    259 } XdndMsg;
    260 
    261 class DragAndDropService;
    262 
    263 /** List of Atoms. */
    264 #define VBoxDnDAtomList RTCList<Atom>
    265 
    266 class xHelpers
    267 {
    268 public:
    269 
    270     static xHelpers *getInstance(Display *pDisplay = 0)
    271     {
    272         if (!m_pInstance)
    273         {
    274             AssertPtrReturn(pDisplay, NULL);
    275             m_pInstance = new xHelpers(pDisplay);
    276         }
    277 
    278         return m_pInstance;
    279     }
    280 
    281     static void destroyInstance(void)
    282     {
    283         if (m_pInstance)
    284         {
    285             delete m_pInstance;
    286             m_pInstance = NULL;
    287         }
    288     }
    289 
    290     inline Display *display()    const { return m_pDisplay; }
    291     inline Atom xAtom(XA_Type e) const { return m_xAtoms[e]; }
    292 
    293     inline Atom stringToxAtom(const char *pcszString) const
    294     {
    295         return XInternAtom(m_pDisplay, pcszString, False);
    296     }
    297     inline RTCString xAtomToString(Atom atom) const
    298     {
    299         if (atom == None) return "None";
    300 
    301         char* pcsAtom = XGetAtomName(m_pDisplay, atom);
    302         RTCString strAtom(pcsAtom);
    303         XFree(pcsAtom);
    304 
    305         return strAtom;
    306     }
    307 
    308     inline RTCString xAtomListToString(const VBoxDnDAtomList &formatList, const RTCString &strSep = DND_FORMATS_SEPARATOR_STR)
    309     {
    310         RTCString format;
    311         for (size_t i = 0; i < formatList.size(); ++i)
    312             format += xAtomToString(formatList.at(i)) + strSep;
    313         return format;
    314     }
    315 
    316     /**
    317      * Returns a filtered X11 atom list.
    318      *
    319      * @returns Filtered list.
    320      * @param   formatList      Atom list to convert.
    321      * @param   filterList      Atom list to filter out.
    322      */
    323     inline VBoxDnDAtomList xAtomListFiltered(const VBoxDnDAtomList &formatList, const VBoxDnDAtomList &filterList)
    324     {
    325         VBoxDnDAtomList tempList = formatList;
    326         tempList.filter(filterList);
    327         return tempList;
    328     }
    329 
    330     RTCString xErrorToString(int xRc) const;
    331     Window applicationWindowBelowCursor(Window parentWin) const;
    332 
    333 private:
    334 #ifdef RT_NEED_NEW_AND_DELETE
    335     RTMEM_IMPLEMENT_NEW_AND_DELETE();
    336 #endif
    337     xHelpers(Display *pDisplay)
    338       : m_pDisplay(pDisplay)
    339     {
    340         /* Not all x11 atoms we use are defined in the headers. Create the
    341          * additional one we need here. */
    342         for (int i = 0; i < XA_End; ++i)
    343             m_xAtoms[i] = XInternAtom(m_pDisplay, m_xAtomNames[i], False);
    344     };
    345 
    346     /* Private member vars */
    347     static xHelpers   *m_pInstance;
    348     Display           *m_pDisplay;
    349     Atom               m_xAtoms[XA_End];
    350     static const char *m_xAtomNames[XA_End];
    351 };
    352 
    353 /* Some xHelpers convenience defines. */
    354 #define gX11 xHelpers::getInstance()
    355 #define xAtom(xa) xHelpers::getInstance()->xAtom((xa))
    356 #define xAtomToString(xa) xHelpers::getInstance()->xAtomToString((xa))
    357 
    358 /*********************************************************************************************************************************
    359  * xHelpers implementation.                                                                                                      *
    360  ********************************************************************************************************************************/
    361 
    362 xHelpers *xHelpers::m_pInstance = NULL;
    363 
    364 /* Has to be in sync with the XA_Type enum. */
    365 const char *xHelpers::m_xAtomNames[] =
    366 {
    367     /* States */
    368     "WM_STATE",
    369     /* Properties */
    370     "TARGETS",
    371     "MULTIPLE",
    372     "INCR",
    373     /* Mime Types */
    374     "image/bmp",
    375     "image/jpg",
    376     "image/tiff",
    377     "image/png",
    378     "text/uri-list",
    379     "text/uri",
    380     "text/plain",
    381     "TEXT",
    382     /* Xdnd */
    383     "XdndSelection",
    384     "XdndAware",
    385     "XdndEnter",
    386     "XdndLeave",
    387     "XdndTypeList",
    388     "XdndActionList",
    389     "XdndPosition",
    390     "XdndActionCopy",
    391     "XdndActionMove",
    392     "XdndActionLink",
    393     "XdndStatus",
    394     "XdndDrop",
    395     "XdndFinished",
    396     /* Our own stop marker */
    397     "dndstop"
    398 };
    399 
    400 RTCString xHelpers::xErrorToString(int xRc) const
    401 {
    402     switch (xRc)
    403     {
    404         case Success:           return RTCStringFmt("%d (Success)", xRc);           break;
    405         case BadRequest:        return RTCStringFmt("%d (BadRequest)", xRc);        break;
    406         case BadValue:          return RTCStringFmt("%d (BadValue)", xRc);          break;
    407         case BadWindow:         return RTCStringFmt("%d (BadWindow)", xRc);         break;
    408         case BadPixmap:         return RTCStringFmt("%d (BadPixmap)", xRc);         break;
    409         case BadAtom:           return RTCStringFmt("%d (BadAtom)", xRc);           break;
    410         case BadCursor:         return RTCStringFmt("%d (BadCursor)", xRc);         break;
    411         case BadFont:           return RTCStringFmt("%d (BadFont)", xRc);           break;
    412         case BadMatch:          return RTCStringFmt("%d (BadMatch)", xRc);          break;
    413         case BadDrawable:       return RTCStringFmt("%d (BadDrawable)", xRc);       break;
    414         case BadAccess:         return RTCStringFmt("%d (BadAccess)", xRc);         break;
    415         case BadAlloc:          return RTCStringFmt("%d (BadAlloc)", xRc);          break;
    416         case BadColor:          return RTCStringFmt("%d (BadColor)", xRc);          break;
    417         case BadGC:             return RTCStringFmt("%d (BadGC)", xRc);             break;
    418         case BadIDChoice:       return RTCStringFmt("%d (BadIDChoice)", xRc);       break;
    419         case BadName:           return RTCStringFmt("%d (BadName)", xRc);           break;
    420         case BadLength:         return RTCStringFmt("%d (BadLength)", xRc);         break;
    421         case BadImplementation: return RTCStringFmt("%d (BadImplementation)", xRc); break;
    422     }
    423     return RTCStringFmt("%d (unknown)", xRc);
    424 }
    425 
    426 /** @todo Make this iterative. */
    427 Window xHelpers::applicationWindowBelowCursor(Window wndParent) const
    428 {
    429     /* No parent, nothing to do. */
    430     if(wndParent == 0)
    431         return 0;
    432 
    433     Window wndApp = 0;
    434     int cProps = -1;
    435 
    436     /* Fetch all x11 window properties of the parent window. */
    437     Atom *pProps = XListProperties(m_pDisplay, wndParent, &cProps);
    438     if (cProps > 0)
    439     {
    440         /* We check the window for the WM_STATE property. */
    441         for (int i = 0; i < cProps; ++i)
    442         {
    443             if (pProps[i] == xAtom(XA_WM_STATE))
    444             {
    445                 /* Found it. */
    446                 wndApp = wndParent;
    447                 break;
    448             }
    449         }
    450 
    451         /* Cleanup */
    452         XFree(pProps);
    453     }
    454 
    455     if (!wndApp)
    456     {
    457         Window wndChild, wndTemp;
    458         int tmp;
    459         unsigned int utmp;
    460 
    461         /* Query the next child window of the parent window at the current
    462          * mouse position. */
    463         XQueryPointer(m_pDisplay, wndParent, &wndTemp, &wndChild, &tmp, &tmp, &tmp, &tmp, &utmp);
    464 
    465         /* Recursive call our self to dive into the child tree. */
    466         wndApp = applicationWindowBelowCursor(wndChild);
    467     }
    468 
    469     return wndApp;
    470 }
    471 
    472 #ifdef DEBUG
    473 # define VBOX_DND_FN_DECL_LOG(x) inline x /* For LogFlowXXX logging. */
    474 #else
    475 # define VBOX_DND_FN_DECL_LOG(x) x
    476 #endif
    477 
    478 /**
    479  * Class which handles a single drag'n drop proxy window.
    480  ** @todo Move all proxy window-related stuff into this class! Clean up this mess.
    481  */
    482 class VBoxDnDProxyWnd
    483 {
    484 
    485 public:
    486 #ifdef RT_NEED_NEW_AND_DELETE
    487     RTMEM_IMPLEMENT_NEW_AND_DELETE();
    488 #endif
    489     VBoxDnDProxyWnd(void);
    490     virtual ~VBoxDnDProxyWnd(void);
    491 
    492 public:
    493 
    494     int init(Display *pDisplay);
    495     void destroy();
    496 
    497     int sendFinished(Window hWndSource, VBOXDNDACTION dndAction);
    498 
    499 public:
    500 
    501     Display *pDisp;
    502     /** Proxy window handle. */
    503     Window   hWnd;
    504     int      iX;
    505     int      iY;
    506     int      iWidth;
    507     int      iHeight;
    508 };
    509 
    510 /** This class only serve to avoid dragging in generic new() and delete(). */
    511 class WrappedXEvent
    512 {
    513 public:
    514     XEvent m_Event;
    515 
    516 public:
    517 #ifdef RT_NEED_NEW_AND_DELETE
    518     RTMEM_IMPLEMENT_NEW_AND_DELETE();
    519 #endif
    520     WrappedXEvent(const XEvent &a_rSrcEvent)
    521     {
    522         m_Event = a_rSrcEvent;
    523     }
    524 
    525     WrappedXEvent()
    526     {
    527         RT_ZERO(m_Event);
    528     }
    529 
    530     WrappedXEvent &operator=(const XEvent &a_rSrcEvent)
    531     {
    532         m_Event = a_rSrcEvent;
    533         return *this;
    534     }
    535 };
    536 
    537 /**
    538  * Class for handling a single drag and drop operation, that is,
    539  * one source and one target at a time.
    540  *
    541  * For now only one DragInstance will exits when the app is running.
    542  */
    543 class DragInstance
    544 {
    545 public:
    546 
    547     enum State
    548     {
    549         Uninitialized = 0,
    550         Initialized,
    551         Dragging,
    552         Dropped,
    553         State_32BIT_Hack = 0x7fffffff
    554     };
    555 
    556     enum Mode
    557     {
    558         Unknown = 0,
    559         HG,
    560         GH,
    561         Mode_32Bit_Hack = 0x7fffffff
    562     };
    563 
    564 #ifdef RT_NEED_NEW_AND_DELETE
    565     RTMEM_IMPLEMENT_NEW_AND_DELETE();
    566 #endif
    567     DragInstance(Display *pDisplay, DragAndDropService *pParent);
    568 
    569 public:
    570 
    571     int  init(uint32_t uScreenID);
    572     int  term(void);
    573     void stop(void);
    574     void reset(void);
    575 
    576     /* X11 message processing. */
    577     int onX11ClientMessage(const XEvent &e);
    578     int onX11MotionNotify(const XEvent &e);
    579     int onX11SelectionClear(const XEvent &e);
    580     int onX11SelectionNotify(const XEvent &e);
    581     int onX11SelectionRequest(const XEvent &evReq);
    582     int onX11Event(const XEvent &e);
    583     int  waitForStatusChange(uint32_t enmState, RTMSINTERVAL uTimeoutMS = 30000);
    584     bool waitForX11Msg(XEvent &evX, int iType, RTMSINTERVAL uTimeoutMS = 100);
    585     bool waitForX11ClientMsg(XClientMessageEvent &evMsg, Atom aType, RTMSINTERVAL uTimeoutMS = 100);
    586 
    587     /* Session handling. */
    588     int checkForSessionChange(void);
    589 
    590 #ifdef VBOX_WITH_DRAG_AND_DROP_GH
    591     /* Guest -> Host handling. */
    592     int ghIsDnDPending(void);
    593     int ghDropped(const RTCString &strFormat, VBOXDNDACTION dndActionRequested);
    594 #endif
    595 
    596     /* Host -> Guest handling. */
    597     int hgEnter(const RTCList<RTCString> &formats, VBOXDNDACTIONLIST dndListActionsAllowed);
    598     int hgLeave(void);
    599     int hgMove(uint32_t uPosX, uint32_t uPosY, VBOXDNDACTION dndActionDefault);
    600     int hgDrop(uint32_t uPosX, uint32_t uPosY, VBOXDNDACTION dndActionDefault);
    601     int hgDataReceive(PVBGLR3GUESTDNDMETADATA pMeta);
    602 
    603     /* X11 helpers. */
    604     int  mouseCursorFakeMove(void);
    605     int  mouseCursorMove(int iPosX, int iPosY);
    606     void mouseButtonSet(Window wndDest, int rx, int ry, int iButton, bool fPress);
    607     int proxyWinShow(int *piRootX = NULL, int *piRootY = NULL) const;
    608     int proxyWinHide(void);
    609 
    610     /* X11 window helpers. */
    611     char *wndX11GetNameA(Window wndThis) const;
    612 
    613     /* Xdnd protocol helpers. */
    614     void wndXDnDClearActionList(Window wndThis) const;
    615     void wndXDnDClearFormatList(Window wndThis) const;
    616     int wndXDnDGetActionList(Window wndThis, VBoxDnDAtomList &lstActions) const;
    617     int wndXDnDGetFormatList(Window wndThis, VBoxDnDAtomList &lstTypes) const;
    618     int wndXDnDSetActionList(Window wndThis, const VBoxDnDAtomList &lstActions) const;
    619     int wndXDnDSetFormatList(Window wndThis, Atom atmProp, const VBoxDnDAtomList &lstFormats) const;
    620 
    621     /* Atom / HGCM formatting helpers. */
    622     int             appendFormatsToList(const RTCList<RTCString> &lstFormats, VBoxDnDAtomList &lstAtoms) const;
    623     int             appendDataToList(const void *pvData, uint32_t cbData, VBoxDnDAtomList &lstAtoms) const;
    624     static Atom     toAtomAction(VBOXDNDACTION dndAction);
    625     static int      toAtomActions(VBOXDNDACTIONLIST dndActionList, VBoxDnDAtomList &lstAtoms);
    626     static uint32_t toHGCMAction(Atom atom);
    627     static uint32_t toHGCMActions(const VBoxDnDAtomList &actionsList);
    628 
    629 protected:
    630 
    631     /** The instance's own DnD context. */
    632     VBGLR3GUESTDNDCMDCTX        m_dndCtx;
    633     /** Pointer to service instance. */
    634     DragAndDropService         *m_pParent;
    635     /** Pointer to X display operating on. */
    636     Display                    *m_pDisplay;
    637     /** X screen ID to operate on. */
    638     int                         m_screenID;
    639     /** Pointer to X screen operating on. */
    640     Screen                     *m_pScreen;
    641     /** Root window handle. */
    642     Window                      m_wndRoot;
    643     /** Proxy window. */
    644     VBoxDnDProxyWnd             m_wndProxy;
    645     /** Current source/target window handle. */
    646     Window                      m_wndCur;
    647     /** The XDnD protocol version the current source/target window is using.
    648      *  Set to 0 if not available / not set yet. */
    649     uint8_t                     m_uXdndVer;
    650     /** Last mouse X position (in pixels, absolute to root window).
    651      *  Set to -1 if not set yet. */
    652     int                         m_lastMouseX;
    653     /** Last mouse Y position (in pixels, absolute to root window).
    654      *  Set to -1 if not set yet. */
    655     int                         m_lastMouseY;
    656     /** List of default (Atom) formats required for X11 Xdnd handling.
    657      *  This list will be included by \a m_lstAtomFormats. */
    658     VBoxDnDAtomList             m_lstAtomFormatsX11;
    659     /** List of (Atom) formats the current source/target window supports. */
    660     VBoxDnDAtomList             m_lstAtomFormats;
    661     /** List of (Atom) actions the current source/target window supports. */
    662     VBoxDnDAtomList             m_lstAtomActions;
    663     /** Buffer for answering the target window's selection request. */
    664     void                       *m_pvSelReqData;
    665     /** Size (in bytes) of selection request data buffer. */
    666     uint32_t                    m_cbSelReqData;
    667     /** Current operation mode. */
    668     volatile uint32_t           m_enmMode;
    669     /** Current state of operation mode. */
    670     volatile uint32_t           m_enmState;
    671     /** The instance's own X event queue. */
    672     RTCMTList<WrappedXEvent>    m_eventQueueList;
    673     /** Critical section for providing serialized access to list event queue's contents. */
    674     RTCRITSECT                  m_eventQueueCS;
    675     /** Event for notifying this instance in case of a new event. */
    676     RTSEMEVENT                  m_eventQueueEvent;
    677     /** Critical section for data access. */
    678     RTCRITSECT                  m_dataCS;
    679     /** List of allowed formats. */
    680     RTCList<RTCString>          m_lstAllowedFormats;
    681     /** Number of failed attempts by the host
    682      *  to query for an active drag and drop operation on the guest. */
    683     uint16_t                    m_cFailedPendingAttempts;
    684 };
    685 
    686 /**
    687  * Service class which implements drag'n drop.
    688  */
    689 class DragAndDropService
    690 {
    691 public:
    692     DragAndDropService(void)
    693       : m_pDisplay(NULL)
    694       , m_hHGCMThread(NIL_RTTHREAD)
    695       , m_hX11Thread(NIL_RTTHREAD)
    696       , m_hEventSem(NIL_RTSEMEVENT)
    697       , m_pCurDnD(NULL)
    698       , m_fStop(false)
    699     {
    700         RT_ZERO(m_dndCtx);
    701     }
    702 
    703     int  init(void);
    704     int  worker(bool volatile *pfShutdown);
    705     void reset(void);
    706     void stop(void);
    707     int  term(void);
    708 
    709 private:
    710 
    711     static DECLCALLBACK(int) hgcmEventThread(RTTHREAD hThread, void *pvUser);
    712     static DECLCALLBACK(int) x11EventThread(RTTHREAD hThread, void *pvUser);
    713 
    714     /* Private member vars */
    715     Display             *m_pDisplay;
    716     /** Our (thread-safe) event queue with mixed events (DnD HGCM / X11). */
    717     RTCMTList<DNDEVENT>  m_eventQueue;
    718     /** Critical section for providing serialized access to list
    719      *  event queue's contents. */
    720     RTCRITSECT           m_eventQueueCS;
    721     /** Thread handle for the HGCM message pumping thread. */
    722     RTTHREAD             m_hHGCMThread;
    723     /** Thread handle for the X11 message pumping thread. */
    724     RTTHREAD             m_hX11Thread;
    725     /** This service' DnD command context. */
    726     VBGLR3GUESTDNDCMDCTX m_dndCtx;
    727     /** Event semaphore for new DnD events. */
    728     RTSEMEVENT           m_hEventSem;
    729     /** Pointer to the allocated DnD instance.
    730         Currently we only support and handle one instance at a time. */
    731     DragInstance        *m_pCurDnD;
    732     /** Stop indicator flag to signal the thread that it should shut down. */
    733     bool                 m_fStop;
    734 
    735     friend class DragInstance;
    736 } g_Svc;
    737 
    738 /*********************************************************************************************************************************
    739  * DragInstanc implementation.                                                                                                   *
    740  ********************************************************************************************************************************/
    741 
    742 DragInstance::DragInstance(Display *pDisplay, DragAndDropService *pParent)
    743     : m_pParent(pParent)
    744     , m_pDisplay(pDisplay)
    745     , m_pScreen(0)
    746     , m_wndRoot(0)
    747     , m_wndCur(0)
    748     , m_uXdndVer(0)
    749     , m_pvSelReqData(NULL)
    750     , m_cbSelReqData(0)
    751     , m_enmMode(Unknown)
    752     , m_enmState(Uninitialized)
    753 {
    754     /* Append default targets we support.
    755      * Note: The order is sorted by preference; be careful when changing this. */
    756     m_lstAtomFormatsX11.append(xAtom(XA_TARGETS));
    757     m_lstAtomFormatsX11.append(xAtom(XA_MULTIPLE));
    758     /** @todo Support INC (incremental transfers). */
    759 }
    760 
    761 /**
    762  * Stops this drag instance.
    763  */
    764 void DragInstance::stop(void)
    765 {
    766     LogFlowFuncEnter();
    767 
    768     int rc2 = VbglR3DnDDisconnect(&m_dndCtx);
    769     AssertRC(rc2);
    770 
    771     LogFlowFuncLeave();
    772 }
    773 
    774 /**
    775  * Terminates (destroys) this drag instance.
    776  *
    777  * @return VBox status code.
    778  */
    779 int DragInstance::term(void)
    780 {
    781     LogFlowFuncEnter();
    782 
    783     if (m_wndProxy.hWnd != 0)
    784         XDestroyWindow(m_pDisplay, m_wndProxy.hWnd);
    785 
    786     int rc = VbglR3DnDDisconnect(&m_dndCtx);
    787     AssertRCReturn(rc, rc);
    788 
    789     if (m_pvSelReqData)
    790         RTMemFree(m_pvSelReqData);
    791 
    792     rc = RTSemEventDestroy(m_eventQueueEvent);
    793     AssertRCReturn(rc, rc);
    794 
    795     rc = RTCritSectDelete(&m_eventQueueCS);
    796     AssertRCReturn(rc, rc);
    797 
    798     rc = RTCritSectDelete(&m_dataCS);
    799     AssertRCReturn(rc, rc);
    800 
    801     LogFlowFuncLeaveRC(rc);
    802     return rc;
    803 }
    804 
    805 /**
    806  * Resets this drag instance.
    807  */
    808 void DragInstance::reset(void)
    809 {
    810     LogFlowFuncEnter();
    811 
    812     /* Hide the proxy win. */
    813     proxyWinHide();
    814 
    815     int rc2 = RTCritSectEnter(&m_dataCS);
    816     if (RT_SUCCESS(rc2))
    817     {
    818         /* If we are currently the Xdnd selection owner, clear that. */
    819         Window pWnd = XGetSelectionOwner(m_pDisplay, xAtom(XA_XdndSelection));
    820         if (pWnd == m_wndProxy.hWnd)
    821             XSetSelectionOwner(m_pDisplay, xAtom(XA_XdndSelection), None, CurrentTime);
    822 
    823         /* Clear any other DnD specific data on the proxy window. */
    824         wndXDnDClearFormatList(m_wndProxy.hWnd);
    825         wndXDnDClearActionList(m_wndProxy.hWnd);
    826 
    827         m_lstAtomActions.clear();
    828 
    829         /* First, clear the formats list and apply the X11-specific default formats,
    830          * required for making Xdnd to work. */
    831         m_lstAtomFormats.clear();
    832         m_lstAtomFormats.append(m_lstAtomFormatsX11);
    833 
    834         m_wndCur                 = 0;
    835         m_uXdndVer               = 0;
    836         m_lastMouseX             = -1;
    837         m_lastMouseY             = -1;
    838         m_enmState               = Initialized;
    839         m_enmMode                = Unknown;
    840         m_cFailedPendingAttempts = 0;
    841 
    842         /* Reset the selection request buffer. */
    843         if (m_pvSelReqData)
    844         {
    845             RTMemFree(m_pvSelReqData);
    846             m_pvSelReqData = NULL;
    847 
    848             Assert(m_cbSelReqData);
    849             m_cbSelReqData = 0;
    850         }
    851 
    852         rc2 = RTCritSectEnter(&m_eventQueueCS);
    853         if (RT_SUCCESS(rc2))
    854         {
    855             m_eventQueueList.clear();
    856 
    857             rc2 = RTCritSectLeave(&m_eventQueueCS);
    858             AssertRC(rc2);
    859         }
    860 
    861         RTCritSectLeave(&m_dataCS);
    862     }
    863 
    864     LogFlowFuncLeave();
    865 }
    866 
    867 /**
    868  * Initializes this drag instance.
    869  *
    870  * @return  IPRT status code.
    871  * @param   uScreenID             X' screen ID to use.
    872  */
    873 int DragInstance::init(uint32_t uScreenID)
    874 {
    875     int rc = VbglR3DnDConnect(&m_dndCtx);
    876     /* Note: Can return VINF_PERMISSION_DENIED if HGCM host service is not available. */
    877     if (rc != VINF_SUCCESS)
    878         return rc;
    879 
    880     if (g_cVerbosity)
    881     {
    882         RTCString strBody = RTCStringFmt("Connected (screen %RU32, verbosity %u)", uScreenID, g_cVerbosity);
    883         VBClShowNotify(VBOX_DND_SHOWNOTIFY_HEADER, strBody.c_str());
    884     }
    885 
    886     do
    887     {
    888         rc = RTSemEventCreate(&m_eventQueueEvent);
    889         if (RT_FAILURE(rc))
    890             break;
    891 
    892         rc = RTCritSectInit(&m_eventQueueCS);
    893         if (RT_FAILURE(rc))
    894             break;
    895 
    896         rc = RTCritSectInit(&m_dataCS);
    897         if (RT_FAILURE(rc))
    898             break;
    899 
    900         /*
    901          * Enough screens configured in the x11 server?
    902          */
    903         if ((int)uScreenID > ScreenCount(m_pDisplay))
    904         {
    905             rc = VERR_INVALID_PARAMETER;
    906             break;
    907         }
    908 #if 0
    909         /* Get the screen number from the x11 server. */
    910         pDrag->screen = ScreenOfDisplay(m_pDisplay, uScreenID);
    911         if (!pDrag->screen)
    912         {
    913             rc = VERR_GENERAL_FAILURE;
    914             break;
    915         }
    916 #endif
    917         m_screenID = uScreenID;
    918 
    919         /* Now query the corresponding root window of this screen. */
    920         m_wndRoot = RootWindow(m_pDisplay, m_screenID);
    921         if (!m_wndRoot)
    922         {
    923             rc = VERR_GENERAL_FAILURE;
    924             break;
    925         }
    926 
    927         /*
    928          * Create an invisible window which will act as proxy for the DnD
    929          * operation. This window will be used for both the GH and HG
    930          * direction.
    931          */
    932         XSetWindowAttributes attr;
    933         RT_ZERO(attr);
    934         attr.event_mask            =   EnterWindowMask  | LeaveWindowMask
    935                                      | ButtonMotionMask | ButtonPressMask | ButtonReleaseMask;
    936         attr.override_redirect     = True;
    937         attr.do_not_propagate_mask = NoEventMask;
    938 
    939         if (g_cVerbosity >= 3)
    940         {
    941             attr.background_pixel      = XWhitePixel(m_pDisplay, m_screenID);
    942             attr.border_pixel          = XBlackPixel(m_pDisplay, m_screenID);
    943             m_wndProxy.hWnd = XCreateWindow(m_pDisplay, m_wndRoot               /* Parent */,
    944                                             100, 100,                           /* Position */
    945                                             100, 100,                           /* Width + height */
    946                                             2,                                  /* Border width */
    947                                             CopyFromParent,                     /* Depth */
    948                                             InputOutput,                        /* Class */
    949                                             CopyFromParent,                     /* Visual */
    950                                               CWBackPixel
    951                                             | CWBorderPixel
    952                                             | CWOverrideRedirect
    953                                             | CWDontPropagate,                  /* Value mask */
    954                                             &attr);                             /* Attributes for value mask */
    955         }
    956 
    957         m_wndProxy.hWnd = XCreateWindow(m_pDisplay, m_wndRoot                   /* Parent */,
    958                                         0, 0,                                   /* Position */
    959                                         1, 1,                                   /* Width + height */
    960                                         0,                                      /* Border width */
    961                                         CopyFromParent,                         /* Depth */
    962                                         InputOnly,                              /* Class */
    963                                         CopyFromParent,                         /* Visual */
    964                                         CWOverrideRedirect | CWDontPropagate,   /* Value mask */
    965                                         &attr);                                 /* Attributes for value mask */
    966 
    967         if (!m_wndProxy.hWnd)
    968         {
    969             VBClLogError("Error creating proxy window\n");
    970             rc = VERR_GENERAL_FAILURE;
    971             break;
    972         }
    973 
    974         rc = m_wndProxy.init(m_pDisplay);
    975         if (RT_FAILURE(rc))
    976         {
    977             VBClLogError("Error initializing proxy window, rc=%Rrc\n", rc);
    978             break;
    979         }
    980 
    981         if (g_cVerbosity >= 3) /* Make debug window visible. */
    982         {
    983             XFlush(m_pDisplay);
    984             XMapWindow(m_pDisplay, m_wndProxy.hWnd);
    985             XRaiseWindow(m_pDisplay, m_wndProxy.hWnd);
    986             XFlush(m_pDisplay);
    987         }
    988 
    989         VBClLogInfo("Proxy window=%#x (debug mode: %RTbool), root window=%#x ...\n",
    990                     m_wndProxy.hWnd, RT_BOOL(g_cVerbosity >= 3), m_wndRoot);
    991 
    992         /* Set the window's name for easier lookup. */
    993         XStoreName(m_pDisplay, m_wndProxy.hWnd, "VBoxClientWndDnD");
    994 
    995         /* Make the new window Xdnd aware. */
    996         Atom atmVer = VBOX_XDND_VERSION;
    997         XChangeProperty(m_pDisplay, m_wndProxy.hWnd, xAtom(XA_XdndAware), XA_ATOM, 32, PropModeReplace,
    998                         reinterpret_cast<unsigned char*>(&atmVer), 1);
    999     } while (0);
    1000 
    1001     if (RT_SUCCESS(rc))
    1002     {
    1003         reset();
    1004     }
    1005     else
    1006         VBClLogError("Initializing drag instance for screen %RU32 failed with rc=%Rrc\n", uScreenID, rc);
    1007 
    1008     LogFlowFuncLeaveRC(rc);
    1009     return rc;
    1010 }
    1011 
    1012 /**
    1013  * Callback handler for a generic client message from a window.
    1014  *
    1015  * @return  IPRT status code.
    1016  * @param   e                       X11 event to handle.
    1017  */
    1018 int DragInstance::onX11ClientMessage(const XEvent &e)
    1019 {
    1020     AssertReturn(e.type == ClientMessage, VERR_INVALID_PARAMETER);
    1021 
    1022     LogFlowThisFunc(("mode=%RU32, state=%RU32\n", m_enmMode, m_enmState));
    1023     LogFlowThisFunc(("Event wnd=%#x, msg=%s\n", e.xclient.window, xAtomToString(e.xclient.message_type).c_str()));
    1024 
    1025     int rc = VINF_SUCCESS;
    1026 
    1027     char *pszWndCurName = wndX11GetNameA(m_wndCur);
    1028     AssertPtrReturn(pszWndCurName, VERR_NO_MEMORY);
    1029 
    1030     switch (m_enmMode)
    1031     {
    1032         case HG:
    1033         {
    1034             /*
    1035              * Client messages are used to inform us about the status of a XdndAware
    1036              * window, in response of some events we send to them.
    1037              */
    1038 
    1039             /* The target window informs us of the current Xdnd status. */
    1040             if (e.xclient.message_type == xAtom(XA_XdndStatus))
    1041             {
    1042                 Window wndTgt = static_cast<Window>(e.xclient.data.l[XdndStatusWindow]);
    1043 
    1044                 char *pszWndTgtName = wndX11GetNameA(wndTgt);
    1045                 AssertPtrBreakStmt(pszWndTgtName, VERR_NO_MEMORY);
    1046 
    1047                 /* Does the target accept the drop? */
    1048                 bool const fAcceptDrop    = RT_BOOL(e.xclient.data.l[XdndStatusFlags] & VBOX_XDND_STATUS_FLAG_ACCEPT);
    1049                 /* Does the target want XdndPosition messages? */
    1050                 bool const fWantsPosition = RT_BOOL(e.xclient.data.l[XdndStatusFlags] & VBOX_XDND_STATUS_FLAG_WANTS_POS);
    1051 
    1052                 /*
    1053                  * The XdndStatus message tell us if the window will accept the DnD
    1054                  * event and with which action. We immediately send this info down to
    1055                  * the host as a response of a previous DnD message.
    1056                  */
    1057                 RTCString strActions = xAtomToString(e.xclient.data.l[XdndStatusAction]);
    1058 
    1059                 VBClLogInfo("Target window %#x ('%s')\n", wndTgt, pszWndTgtName);
    1060                 VBClLogInfo("    - %s accept data (actions '%s')\n", fAcceptDrop ? "does" : "does not", strActions.c_str());
    1061                 VBClLogInfo("    - %s want position messages\n", fWantsPosition ? "does" : "does not");
    1062 
    1063                 uint16_t const x  = RT_HI_U16((uint32_t)e.xclient.data.l[XdndStatusNoMsgXY]);
    1064                 uint16_t const y  = RT_LO_U16((uint32_t)e.xclient.data.l[XdndStatusNoMsgXY]);
    1065                 uint16_t const cx = RT_HI_U16((uint32_t)e.xclient.data.l[XdndStatusNoMsgWH]);
    1066                 uint16_t const cy = RT_LO_U16((uint32_t)e.xclient.data.l[XdndStatusNoMsgWH]);
    1067 
    1068                 if (cx && cy)
    1069                 {
    1070                     VBClLogInfo("Target window %#x ('%s') reported dead area at %RU16,%RU16 (%RU16 x %RU16)\n",
    1071                                 wndTgt, pszWndTgtName, x, y, cx, cy);
    1072                     /** @todo Save dead area and don't send XdndPosition messages anymore into it. */
    1073                 }
    1074 
    1075                 if (m_wndCur == wndTgt)
    1076                 {
    1077                     VBOXDNDACTION dndAction = VBOX_DND_ACTION_IGNORE; /* Default is ignoring. */
    1078                     /** @todo Compare this with the allowed actions. */
    1079                     if (fAcceptDrop)
    1080                         dndAction = toHGCMAction(static_cast<Atom>(e.xclient.data.l[XdndStatusAction]));
    1081 
    1082                     rc = VbglR3DnDHGSendAckOp(&m_dndCtx, dndAction);
    1083                 }
    1084                 else
    1085                     VBClLogInfo("Target window %#x ('%s') is not our current window, skipping\n", wndTgt, pszWndTgtName);
    1086 
    1087                 RTStrFree(pszWndTgtName);
    1088             }
    1089             /* The target window informs us that it finished the Xdnd operation and that we may free all data. */
    1090             else if (e.xclient.message_type == xAtom(XA_XdndFinished))
    1091             {
    1092                 Window wndTarget = static_cast<Window>(e.xclient.data.l[XdndFinishedWindow]);
    1093 
    1094                 char *pszWndTgtName = wndX11GetNameA(wndTarget);
    1095                 AssertPtrBreakStmt(pszWndTgtName, VERR_NO_MEMORY);
    1096 
    1097                 if (m_uXdndVer >= 5)
    1098                 {
    1099                     const bool  fSucceeded = e.xclient.data.l[XdndFinishedFlags] & VBOX_XDND_FINISHED_FLAG_SUCCEEDED;
    1100             #if 0 /** @todo Returns garbage -- investigate this! */
    1101                     //const char *pcszAction = fSucceeded ? xAtomToString(e.xclient.data.l[XdndFinishedAction]).c_str() : NULL;
    1102             #endif
    1103                     VBClLogInfo("Target window %#x ('%s') has %s the data\n",
    1104                                 wndTarget, pszWndTgtName, fSucceeded ? "accepted" : "rejected");
    1105                 }
    1106                 else /* Xdnd < version 5 did not have the XdndFinishedFlags / XdndFinishedAction properties. */
    1107                     VBClLogInfo("Target window %#x ('%s') has accepted the data\n", wndTarget, pszWndTgtName);
    1108 
    1109                 RTStrFree(pszWndTgtName);
    1110 
    1111                 reset();
    1112             }
    1113             else
    1114             {
    1115                 LogFlowThisFunc(("Unhandled client message '%s'\n", xAtomToString(e.xclient.message_type).c_str()));
    1116                 rc = VERR_NOT_SUPPORTED;
    1117             }
    1118 
    1119             break;
    1120         }
    1121 
    1122         case Unknown: /* Mode not set (yet). */
    1123             RT_FALL_THROUGH();
    1124         case GH:
    1125         {
    1126             /*
    1127              * This message marks the beginning of a new drag and drop
    1128              * operation on the guest.
    1129              */
    1130             if (e.xclient.message_type == xAtom(XA_XdndEnter))
    1131             {
    1132                 /*
    1133                  * Get the window which currently has the XA_XdndSelection
    1134                  * bit set.
    1135                  */
    1136                 Window wndSel = XGetSelectionOwner(m_pDisplay, xAtom(XA_XdndSelection));
    1137                 char *pszWndSelName = wndX11GetNameA(wndSel);
    1138                 AssertPtrBreakStmt(pszWndSelName, VERR_NO_MEMORY);
    1139 
    1140                 mouseButtonSet(m_wndProxy.hWnd, -1, -1, 1, true /* fPress */);
    1141 
    1142                 /*
    1143                  * Update our state and the window handle to process.
    1144                  */
    1145                 rc = RTCritSectEnter(&m_dataCS);
    1146                 if (RT_SUCCESS(rc))
    1147                 {
    1148                     uint8_t const uXdndVer = (uint8_t)e.xclient.data.l[XdndEnterFlags] >> XdndEnterVersionRShift;
    1149 
    1150                     VBClLogInfo("Entered new source window %#x ('%s'), supports Xdnd version %u\n", wndSel, pszWndSelName, uXdndVer);
    1151 #ifdef DEBUG
    1152                     XWindowAttributes xwa;
    1153                     XGetWindowAttributes(m_pDisplay, m_wndCur, &xwa);
    1154                     LogFlowThisFunc(("wndCur=%#x, x=%d, y=%d, width=%d, height=%d\n", m_wndCur, xwa.x, xwa.y, xwa.width, xwa.height));
    1155 #endif
    1156                     /*
    1157                      * Retrieve supported formats.
    1158                      */
    1159 
    1160                     /* Check if the MIME types are in the message itself or if we need
    1161                      * to fetch the XdndTypeList property from the window. */
    1162                     bool fMoreTypes = e.xclient.data.l[XdndEnterFlags] & XdndEnterMoreTypesFlag;
    1163                     if (!fMoreTypes)
    1164                     {
    1165                         /* Only up to 3 format types supported. */
    1166                         /* Start with index 2 (first item). */
    1167                         for (int i = 2; i < 5; i++)
    1168                         {
    1169                             LogFlowThisFunc(("\t%s\n", gX11->xAtomToString(e.xclient.data.l[i]).c_str()));
    1170                             m_lstAtomFormats.append(e.xclient.data.l[i]);
    1171                         }
    1172                     }
    1173                     else
    1174                     {
    1175                         /* More than 3 format types supported. */
    1176                         rc = wndXDnDGetFormatList(wndSel, m_lstAtomFormats);
    1177                     }
    1178 
    1179                     if (RT_FAILURE(rc))
    1180                     {
    1181                         VBClLogError("Error retrieving supported formats, rc=%Rrc\n", rc);
    1182                         break;
    1183                     }
    1184 
    1185                     /*
    1186                      * Retrieve supported actions.
    1187                      */
    1188                     if (uXdndVer >= 2) /* More than one action allowed since protocol version 2. */
    1189                     {
    1190                         rc = wndXDnDGetActionList(wndSel, m_lstAtomActions);
    1191                     }
    1192                     else /* Only "copy" action allowed on legacy applications. */
    1193                         m_lstAtomActions.append(XA_XdndActionCopy);
    1194 
    1195                     if (RT_FAILURE(rc))
    1196                     {
    1197                         VBClLogError("Error retrieving supported actions, rc=%Rrc\n", rc);
    1198                         break;
    1199                     }
    1200 
    1201                     VBClLogInfo("Source window %#x ('%s')\n", wndSel, pszWndSelName);
    1202                     VBClLogInfo("    - supports the formats ");
    1203                     for (size_t i = 0; i < m_lstAtomFormats.size(); i++)
    1204                     {
    1205                         if (i > 0)
    1206                             VBClLogInfo(", ");
    1207                         VBClLogInfo("%s", gX11->xAtomToString(m_lstAtomFormats[i]).c_str());
    1208                     }
    1209                     VBClLogInfo("\n");
    1210                     VBClLogInfo("    - supports the actions ");
    1211                     for (size_t i = 0; i < m_lstAtomActions.size(); i++)
    1212                     {
    1213                         if (i > 0)
    1214                             VBClLogInfo(", ");
    1215                         VBClLogInfo("%s", gX11->xAtomToString(m_lstAtomActions[i]).c_str());
    1216                     }
    1217                     VBClLogInfo("\n");
    1218 
    1219                     AssertBreakStmt(wndSel == (Window)e.xclient.data.l[XdndEnterWindow],
    1220                                     rc = VERR_INVALID_PARAMETER); /* Source window. */
    1221 
    1222                     m_wndCur   = wndSel;
    1223                     m_uXdndVer = uXdndVer;
    1224                     m_enmMode  = GH;
    1225                     m_enmState = Dragging;
    1226 
    1227                     RTCritSectLeave(&m_dataCS);
    1228                 }
    1229 
    1230                 RTStrFree(pszWndSelName);
    1231             }
    1232             else if (   e.xclient.message_type == xAtom(XA_XdndPosition)
    1233                      && m_wndCur               == static_cast<Window>(e.xclient.data.l[XdndPositionWindow]))
    1234             {
    1235                 if (m_enmState != Dragging) /* Wrong mode? Bail out. */
    1236                 {
    1237                     reset();
    1238                     break;
    1239                 }
    1240 #ifdef LOG_ENABLED
    1241                 int32_t iPos      = e.xclient.data.l[XdndPositionXY];
    1242                 Atom    atmAction = m_uXdndVer >= 2 /* Actions other than "copy" or only supported since protocol version 2. */
    1243                                   ? e.xclient.data.l[XdndPositionAction] : xAtom(XA_XdndActionCopy);
    1244                 LogFlowThisFunc(("XA_XdndPosition: wndProxy=%#x, wndCur=%#x, x=%RI32, y=%RI32, strAction=%s\n",
    1245                                  m_wndProxy.hWnd, m_wndCur, RT_HIWORD(iPos), RT_LOWORD(iPos),
    1246                                  xAtomToString(atmAction).c_str()));
    1247 #endif
    1248                 bool fAcceptDrop = true;
    1249 
    1250                 /* Reply with a XdndStatus message to tell the source whether
    1251                  * the data can be dropped or not. */
    1252                 XClientMessageEvent m;
    1253                 RT_ZERO(m);
    1254                 m.type         = ClientMessage;
    1255                 m.display      = m_pDisplay;
    1256                 m.window       = e.xclient.data.l[XdndPositionWindow];
    1257                 m.message_type = xAtom(XA_XdndStatus);
    1258                 m.format       = 32;
    1259                 m.data.l[XdndStatusWindow]  = m_wndProxy.hWnd;
    1260                 m.data.l[XdndStatusFlags]   = fAcceptDrop ? VBOX_XDND_STATUS_FLAG_ACCEPT : VBOX_XDND_STATUS_FLAG_NONE; /* Whether to accept the drop or not. */
    1261 
    1262                 /* We don't want any new XA_XdndPosition messages while being
    1263                  * in our proxy window. */
    1264                 m.data.l[XdndStatusNoMsgXY] = RT_MAKE_U32(m_wndProxy.iY, m_wndProxy.iX);
    1265                 m.data.l[XdndStatusNoMsgWH] = RT_MAKE_U32(m_wndProxy.iHeight, m_wndProxy.iWidth);
    1266 
    1267                 /** @todo Handle default action! */
    1268                 m.data.l[XdndStatusAction]  = fAcceptDrop ? toAtomAction(VBOX_DND_ACTION_COPY) : None;
    1269 
    1270                 int xRc = XSendEvent(m_pDisplay, e.xclient.data.l[XdndPositionWindow],
    1271                                      False /* Propagate */, NoEventMask, reinterpret_cast<XEvent *>(&m));
    1272                 if (xRc == 0)
    1273                     VBClLogError("Error sending position status event to current window %#x ('%s'): %s\n",
    1274                                  m_wndCur, pszWndCurName, gX11->xErrorToString(xRc).c_str());
    1275             }
    1276             else if (   e.xclient.message_type == xAtom(XA_XdndLeave)
    1277                      && m_wndCur               == static_cast<Window>(e.xclient.data.l[XdndLeaveWindow]))
    1278             {
    1279                 LogFlowThisFunc(("XA_XdndLeave\n"));
    1280                 VBClLogInfo("Guest to host transfer canceled by the guest source window\n");
    1281 
    1282                 /* Start over. */
    1283                 reset();
    1284             }
    1285             else if (   e.xclient.message_type == xAtom(XA_XdndDrop)
    1286                      && m_wndCur               == static_cast<Window>(e.xclient.data.l[XdndDropWindow]))
    1287             {
    1288                 LogFlowThisFunc(("XA_XdndDrop\n"));
    1289 
    1290                 if (m_enmState != Dropped) /* Wrong mode? Bail out. */
    1291                 {
    1292                     /* Can occur when dragging from guest->host, but then back in to the guest again. */
    1293                     VBClLogInfo("Could not drop on own proxy window\n"); /* Not fatal. */
    1294 
    1295                     /* Let the source know. */
    1296                     rc = m_wndProxy.sendFinished(m_wndCur, VBOX_DND_ACTION_IGNORE);
    1297 
    1298                     /* Start over. */
    1299                     reset();
    1300                     break;
    1301                 }
    1302 
    1303                 m_eventQueueList.append(e);
    1304                 rc = RTSemEventSignal(m_eventQueueEvent);
    1305             }
    1306             else /* Unhandled event, abort. */
    1307             {
    1308                 VBClLogInfo("Unhandled event from wnd=%#x, msg=%s\n", e.xclient.window, xAtomToString(e.xclient.message_type).c_str());
    1309 
    1310                 /* Let the source know. */
    1311                 rc = m_wndProxy.sendFinished(m_wndCur, VBOX_DND_ACTION_IGNORE);
    1312 
    1313                 /* Start over. */
    1314                 reset();
    1315             }
    1316             break;
    1317         }
    1318 
    1319         default:
    1320         {
    1321             AssertMsgFailed(("Drag and drop mode not implemented: %RU32\n", m_enmMode));
    1322             rc = VERR_NOT_IMPLEMENTED;
    1323             break;
    1324         }
    1325     }
    1326 
    1327     RTStrFree(pszWndCurName);
    1328 
    1329     LogFlowThisFunc(("Returning rc=%Rrc\n", rc));
    1330     return rc;
    1331 }
    1332 
    1333 int DragInstance::onX11MotionNotify(const XEvent &e)
    1334 {
    1335     RT_NOREF1(e);
    1336     LogFlowThisFunc(("mode=%RU32, state=%RU32\n", m_enmMode, m_enmState));
    1337 
    1338     return VINF_SUCCESS;
    1339 }
    1340 
    1341 /**
    1342  * Callback handler for being notified if some other window now
    1343  * is the owner of the current selection.
    1344  *
    1345  * @return  IPRT status code.
    1346  * @param   e                       X11 event to handle.
    1347  *
    1348  * @remark
    1349  */
    1350 int DragInstance::onX11SelectionClear(const XEvent &e)
    1351 {
    1352     RT_NOREF1(e);
    1353     LogFlowThisFunc(("mode=%RU32, state=%RU32\n", m_enmMode, m_enmState));
    1354 
    1355     return VINF_SUCCESS;
    1356 }
    1357 
    1358 /**
    1359  * Callback handler for a XDnD selection notify from a window. This is needed
    1360  * to let the us know if a certain window has drag'n drop data to share with us,
    1361  * e.g. our proxy window.
    1362  *
    1363  * @return  IPRT status code.
    1364  * @param   e                       X11 event to handle.
    1365  */
    1366 int DragInstance::onX11SelectionNotify(const XEvent &e)
    1367 {
    1368     AssertReturn(e.type == SelectionNotify, VERR_INVALID_PARAMETER);
    1369 
    1370     LogFlowThisFunc(("mode=%RU32, state=%RU32\n", m_enmMode, m_enmState));
    1371 
    1372     int rc;
    1373 
    1374     switch (m_enmMode)
    1375     {
    1376         case GH:
    1377         {
    1378             if (m_enmState == Dropped)
    1379             {
    1380                 m_eventQueueList.append(e);
    1381                 rc = RTSemEventSignal(m_eventQueueEvent);
    1382             }
    1383             else
    1384                 rc = VERR_WRONG_ORDER;
    1385             break;
    1386         }
    1387 
    1388         default:
    1389         {
    1390             LogFlowThisFunc(("Unhandled: wnd=%#x, msg=%s\n",
    1391                              e.xclient.data.l[0], xAtomToString(e.xclient.message_type).c_str()));
    1392             rc = VERR_INVALID_STATE;
    1393             break;
    1394         }
    1395     }
    1396 
    1397     LogFlowThisFunc(("Returning rc=%Rrc\n", rc));
    1398     return rc;
    1399 }
    1400 
    1401 /**
    1402  * Callback handler for a XDnD selection request from a window. This is needed
    1403  * to retrieve the data required to complete the actual drag'n drop operation.
    1404  *
    1405  * @returns IPRT status code.
    1406  * @param   evReq               X11 event to handle.
    1407  */
    1408 int DragInstance::onX11SelectionRequest(const XEvent &evReq)
    1409 {
    1410     AssertReturn(evReq.type == SelectionRequest, VERR_INVALID_PARAMETER);
    1411 
    1412     const XSelectionRequestEvent *pEvReq = &evReq.xselectionrequest;
    1413 
    1414     char *pszWndSrcName = wndX11GetNameA(pEvReq->owner);
    1415     AssertPtrReturn(pszWndSrcName, VERR_INVALID_POINTER);
    1416     char *pszWndTgtName = wndX11GetNameA(pEvReq->requestor);
    1417     AssertPtrReturn(pszWndTgtName, VERR_INVALID_POINTER);
    1418 
    1419     LogFlowThisFunc(("mode=%RU32, state=%RU32\n", m_enmMode, m_enmState));
    1420     LogFlowThisFunc(("Event owner=%#x ('%s'), requestor=%#x ('%s'), selection=%s, target=%s, prop=%s, time=%u\n",
    1421                      pEvReq->owner, pszWndSrcName,
    1422                      pEvReq->requestor, pszWndTgtName,
    1423                      xAtomToString(pEvReq->selection).c_str(),
    1424                      xAtomToString(pEvReq->target).c_str(),
    1425                      xAtomToString(pEvReq->property).c_str(),
    1426                      pEvReq->time));
    1427 
    1428     VBClLogInfo("Window '%s' is asking '%s' for '%s' / '%s'\n",
    1429                 pszWndTgtName, pszWndSrcName, xAtomToString(pEvReq->selection).c_str(), xAtomToString(pEvReq->property).c_str());
    1430 
    1431     RTStrFree(pszWndSrcName);
    1432     /* Note: pszWndTgtName will be free'd below. */
    1433 
    1434     int rc;
    1435 
    1436     switch (m_enmMode)
    1437     {
    1438         case HG:
    1439         {
    1440             rc = VINF_SUCCESS;
    1441 
    1442             /*
    1443              * Start by creating a refusal selection notify message.
    1444              * That way we only need to care for the success case.
    1445              */
    1446 
    1447             XEvent evResp;
    1448             RT_ZERO(evResp);
    1449 
    1450             XSelectionEvent *pEvResp = &evResp.xselection;
    1451 
    1452             pEvResp->type      = SelectionNotify;
    1453             pEvResp->display   = pEvReq->display;
    1454             pEvResp->requestor = pEvReq->requestor;
    1455             pEvResp->selection = pEvReq->selection;
    1456             pEvResp->target    = pEvReq->target;
    1457             pEvResp->property  = None;                          /* "None" means refusal. */
    1458             pEvResp->time      = pEvReq->time;
    1459 
    1460             if (g_cVerbosity)
    1461             {
    1462                 VBClLogVerbose(1, "Supported formats by VBoxClient:\n");
    1463                 for (size_t i = 0; i < m_lstAtomFormats.size(); i++)
    1464                     VBClLogVerbose(1, "\t%s\n", xAtomToString(m_lstAtomFormats.at(i)).c_str());
    1465             }
    1466 
    1467             /* Is the requestor asking for the possible MIME types? */
    1468             if (pEvReq->target == xAtom(XA_TARGETS))
    1469             {
    1470                 VBClLogInfo("Target window %#x ('%s') asking for target list\n", pEvReq->requestor, pszWndTgtName);
    1471 
    1472                 /* If so, set the window property with the formats on the requestor
    1473                  * window. */
    1474                 rc = wndXDnDSetFormatList(pEvReq->requestor, pEvReq->property, m_lstAtomFormats);
    1475                 if (RT_SUCCESS(rc))
    1476                     pEvResp->property = pEvReq->property;
    1477             }
    1478             /* Is the requestor asking for a specific MIME type (we support)? */
    1479             else if (m_lstAtomFormats.contains(pEvReq->target))
    1480             {
    1481                 VBClLogInfo("Target window %#x ('%s') is asking for data as '%s'\n",
    1482                             pEvReq->requestor, pszWndTgtName, xAtomToString(pEvReq->target).c_str());
    1483 
    1484 #ifdef VBOX_WITH_DRAG_AND_DROP_PROMISES
    1485 # error "Implement me!"
    1486 #else
    1487                 /* Did we not drop our stuff to the guest yet? Bail out. */
    1488                 if (m_enmState != Dropped)
    1489                 {
    1490                     VBClLogError("Data not dropped by the host on the guest yet (client state %RU32, mode %RU32), refusing selection request by guest\n",
    1491                                  m_enmState, m_enmMode);
    1492                 }
    1493                 /* Did we not store the requestor's initial selection request yet? Then do so now. */
    1494                 else
    1495                 {
    1496 #endif /* VBOX_WITH_DRAG_AND_DROP_PROMISES */
    1497                     /* Get the data format the requestor wants from us. */
    1498                     VBClLogInfo("Target window %#x ('%s') requested data from host as '%s', rc=%Rrc\n",
    1499                                 pEvReq->requestor, pszWndTgtName, xAtomToString(pEvReq->target).c_str(), rc);
    1500 
    1501                     /* Make a copy of the MIME data to be passed back. The X server will be become
    1502                      * the new owner of that data, so no deletion needed. */
    1503                     /** @todo Do we need to do some more conversion here? XConvertSelection? */
    1504                     AssertMsgBreakStmt(m_pvSelReqData != NULL, ("Selection request data is NULL\n"),   rc = VERR_INVALID_PARAMETER);
    1505                     AssertMsgBreakStmt(m_cbSelReqData  > 0,    ("Selection request data size is 0\n"), rc = VERR_INVALID_PARAMETER);
    1506 
    1507                     void    const *pvData = RTMemDup(m_pvSelReqData, m_cbSelReqData);
    1508                     AssertMsgBreakStmt(pvData != NULL, ("Duplicating selection request failed\n"), rc = VERR_NO_MEMORY);
    1509                     uint32_t const cbData = m_cbSelReqData;
    1510 
    1511                     /* Always return the requested property. */
    1512                     evResp.xselection.property = pEvReq->property;
    1513 
    1514                     /* Note: Always seems to return BadRequest. Seems fine. */
    1515                     int xRc = XChangeProperty(pEvResp->display, pEvResp->requestor, pEvResp->property,
    1516                                               pEvResp->target, 8, PropModeReplace,
    1517                                               reinterpret_cast<const unsigned char*>(pvData), cbData);
    1518 
    1519                     LogFlowFunc(("Changing property '%s' (of type '%s') of window %#x ('%s'): %s\n",
    1520                                  xAtomToString(pEvReq->property).c_str(),
    1521                                  xAtomToString(pEvReq->target).c_str(),
    1522                                  pEvReq->requestor, pszWndTgtName,
    1523                                  gX11->xErrorToString(xRc).c_str()));
    1524                     RT_NOREF(xRc);
    1525 #ifndef VBOX_WITH_DRAG_AND_DROP_PROMISES
    1526                 }
    1527 #endif
    1528             }
    1529             /* Anything else. */
    1530             else
    1531             {
    1532                 VBClLogError("Refusing unknown command/format '%s' of wnd=%#x ('%s')\n",
    1533                              xAtomToString(pEvReq->target).c_str(), pEvReq->requestor, pszWndTgtName);
    1534                 rc = VERR_NOT_SUPPORTED;
    1535             }
    1536 
    1537             VBClLogVerbose(1, "Offering type '%s', property '%s' to window %#x ('%s') ...\n",
    1538                            xAtomToString(pEvReq->target).c_str(),
    1539                            xAtomToString(pEvReq->property).c_str(), pEvReq->requestor, pszWndTgtName);
    1540 
    1541             int xRc = XSendEvent(pEvReq->display, pEvReq->requestor, True /* Propagate */, 0, &evResp);
    1542             if (xRc == 0)
    1543                 VBClLogError("Error sending SelectionNotify(1) event to window %#x ('%s'): %s\n",
    1544                              pEvReq->requestor, pszWndTgtName, gX11->xErrorToString(xRc).c_str());
    1545 
    1546             XFlush(pEvReq->display);
    1547             break;
    1548         }
    1549 
    1550         default:
    1551             rc = VERR_INVALID_STATE;
    1552             break;
    1553     }
    1554 
    1555     RTStrFree(pszWndTgtName);
    1556     pszWndTgtName = NULL;
    1557 
    1558     LogFlowThisFunc(("Returning rc=%Rrc\n", rc));
    1559     return rc;
    1560 }
    1561 
    1562 /**
    1563  * Handles X11 events, called by x11EventThread.
    1564  *
    1565  * @returns IPRT status code.
    1566  * @param   e                       X11 event to handle.
    1567  */
    1568 int DragInstance::onX11Event(const XEvent &e)
    1569 {
    1570     int rc;
    1571 
    1572     LogFlowThisFunc(("X11 event, type=%d\n", e.type));
    1573     switch (e.type)
    1574     {
    1575         /*
    1576          * This can happen if a guest->host drag operation
    1577          * goes back from the host to the guest. This is not what
    1578          * we want and thus resetting everything.
    1579          */
    1580         case ButtonPress:
    1581             RT_FALL_THROUGH();
    1582         case ButtonRelease:
    1583         {
    1584             VBClLogInfo("Mouse button %s\n", e.type == ButtonPress ? "pressed" : "released");
    1585 
    1586             reset();
    1587 
    1588             rc = VINF_SUCCESS;
    1589             break;
    1590         }
    1591 
    1592         case ClientMessage:
    1593             rc = onX11ClientMessage(e);
    1594             break;
    1595 
    1596         case SelectionClear:
    1597             rc = onX11SelectionClear(e);
    1598             break;
    1599 
    1600         case SelectionNotify:
    1601             rc = onX11SelectionNotify(e);
    1602             break;
    1603 
    1604         case SelectionRequest:
    1605             rc = onX11SelectionRequest(e);
    1606             break;
    1607 
    1608         case MotionNotify:
    1609             rc = onX11MotionNotify(e);
    1610             break;
    1611 
    1612         default:
    1613             rc = VERR_NOT_IMPLEMENTED;
    1614             break;
    1615     }
    1616 
    1617     LogFlowThisFunc(("rc=%Rrc\n", rc));
    1618     return rc;
    1619 }
    1620 
    1621 int DragInstance::waitForStatusChange(uint32_t enmState, RTMSINTERVAL uTimeoutMS /* = 30000 */)
    1622 {
    1623     const uint64_t uiStart = RTTimeMilliTS();
    1624     volatile uint32_t enmCurState;
    1625 
    1626     int rc = VERR_TIMEOUT;
    1627 
    1628     LogFlowFunc(("enmState=%RU32, uTimeoutMS=%RU32\n", enmState, uTimeoutMS));
    1629 
    1630     do
    1631     {
    1632         enmCurState = ASMAtomicReadU32(&m_enmState);
    1633         if (enmCurState == enmState)
    1634         {
    1635             rc = VINF_SUCCESS;
    1636             break;
    1637         }
    1638     }
    1639     while (RTTimeMilliTS() - uiStart < uTimeoutMS);
    1640 
    1641     LogFlowThisFunc(("Returning %Rrc\n", rc));
    1642     return rc;
    1643 }
    1644 
    1645 #ifdef VBOX_WITH_DRAG_AND_DROP_GH
    1646 /**
    1647  * Waits for an X11 event of a specific type.
    1648  *
    1649  * @returns IPRT status code.
    1650  * @param   evX                     Reference where to store the event into.
    1651  * @param   iType                   Event type to wait for.
    1652  * @param   uTimeoutMS              Timeout (in ms) to wait for the event.
    1653  */
    1654 bool DragInstance::waitForX11Msg(XEvent &evX, int iType, RTMSINTERVAL uTimeoutMS /* = 100 */)
    1655 {
    1656     LogFlowThisFunc(("iType=%d, uTimeoutMS=%RU32, cEventQueue=%zu\n", iType, uTimeoutMS, m_eventQueueList.size()));
    1657 
    1658     bool fFound = false;
    1659     uint64_t const tsStartMs = RTTimeMilliTS();
    1660 
    1661     do
    1662     {
    1663         /* Check if there is a client message in the queue. */
    1664         for (size_t i = 0; i < m_eventQueueList.size(); i++)
    1665         {
    1666             int rc2 = RTCritSectEnter(&m_eventQueueCS);
    1667             if (RT_SUCCESS(rc2))
    1668             {
    1669                 XEvent e = m_eventQueueList.at(i).m_Event;
    1670 
    1671                 fFound = e.type == iType;
    1672                 if (fFound)
    1673                 {
    1674                     m_eventQueueList.removeAt(i);
    1675                     evX = e;
    1676                 }
    1677 
    1678                 rc2 = RTCritSectLeave(&m_eventQueueCS);
    1679                 AssertRC(rc2);
    1680 
    1681                 if (fFound)
    1682                     break;
    1683             }
    1684         }
    1685 
    1686         if (fFound)
    1687             break;
    1688 
    1689         int rc2 = RTSemEventWait(m_eventQueueEvent, 25 /* ms */);
    1690         if (   RT_FAILURE(rc2)
    1691             && rc2 != VERR_TIMEOUT)
    1692         {
    1693             LogFlowFunc(("Waiting failed with rc=%Rrc\n", rc2));
    1694             break;
    1695         }
    1696     }
    1697     while (RTTimeMilliTS() - tsStartMs < uTimeoutMS);
    1698 
    1699     LogFlowThisFunc(("Returning fFound=%RTbool, msRuntime=%RU64\n", fFound, RTTimeMilliTS() - tsStartMs));
    1700     return fFound;
    1701 }
    1702 
    1703 /**
    1704  * Waits for an X11 client message of a specific type.
    1705  *
    1706  * @returns IPRT status code.
    1707  * @param   evMsg                   Reference where to store the event into.
    1708  * @param   aType                   Event type to wait for.
    1709  * @param   uTimeoutMS              Timeout (in ms) to wait for the event.
    1710  */
    1711 bool DragInstance::waitForX11ClientMsg(XClientMessageEvent &evMsg, Atom aType,
    1712                                        RTMSINTERVAL uTimeoutMS /* = 100 */)
    1713 {
    1714     LogFlowThisFunc(("aType=%s, uTimeoutMS=%RU32, cEventQueue=%zu\n",
    1715                      xAtomToString(aType).c_str(), uTimeoutMS, m_eventQueueList.size()));
    1716 
    1717     bool fFound = false;
    1718     const uint64_t uiStart = RTTimeMilliTS();
    1719     do
    1720     {
    1721         /* Check if there is a client message in the queue. */
    1722         for (size_t i = 0; i < m_eventQueueList.size(); i++)
    1723         {
    1724             int rc2 = RTCritSectEnter(&m_eventQueueCS);
    1725             if (RT_SUCCESS(rc2))
    1726             {
    1727                 XEvent e = m_eventQueueList.at(i).m_Event;
    1728                 if (   e.type                 == ClientMessage
    1729                     && e.xclient.message_type == aType)
    1730                 {
    1731                     m_eventQueueList.removeAt(i);
    1732                     evMsg = e.xclient;
    1733 
    1734                     fFound = true;
    1735                 }
    1736 
    1737                 if (e.type == ClientMessage)
    1738                 {
    1739                     LogFlowThisFunc(("Client message: Type=%ld (%s)\n",
    1740                                      e.xclient.message_type, xAtomToString(e.xclient.message_type).c_str()));
    1741                 }
    1742                 else
    1743                     LogFlowThisFunc(("X message: Type=%d\n", e.type));
    1744 
    1745                 rc2 = RTCritSectLeave(&m_eventQueueCS);
    1746                 AssertRC(rc2);
    1747 
    1748                 if (fFound)
    1749                     break;
    1750             }
    1751         }
    1752 
    1753         if (fFound)
    1754             break;
    1755 
    1756         int rc2 = RTSemEventWait(m_eventQueueEvent, 25 /* ms */);
    1757         if (   RT_FAILURE(rc2)
    1758             && rc2 != VERR_TIMEOUT)
    1759         {
    1760             LogFlowFunc(("Waiting failed with rc=%Rrc\n", rc2));
    1761             break;
    1762         }
    1763     }
    1764     while (RTTimeMilliTS() - uiStart < uTimeoutMS);
    1765 
    1766     LogFlowThisFunc(("Returning fFound=%RTbool, msRuntime=%RU64\n", fFound, RTTimeMilliTS() - uiStart));
    1767     return fFound;
    1768 }
    1769 #endif /* VBOX_WITH_DRAG_AND_DROP_GH */
    1770 
    1771 /*
    1772  * Host -> Guest
    1773  */
    1774 
    1775 /**
    1776  * Host -> Guest: Event signalling that the host's (mouse) cursor just entered the VM's (guest's) display
    1777  *                area.
    1778  *
    1779  * @returns IPRT status code.
    1780  * @param   lstFormats              List of supported formats from the host.
    1781  * @param   dndListActionsAllowed   (ORed) List of supported actions from the host.
    1782  */
    1783 int DragInstance::hgEnter(const RTCList<RTCString> &lstFormats, uint32_t dndListActionsAllowed)
    1784 {
    1785     LogFlowThisFunc(("mode=%RU32, state=%RU32\n", m_enmMode, m_enmState));
    1786 
    1787     if (m_enmMode != Unknown)
    1788         return VERR_INVALID_STATE;
    1789 
    1790     reset();
    1791 
    1792 #ifdef DEBUG
    1793     LogFlowThisFunc(("dndListActionsAllowed=0x%x, lstFormats=%zu: ", dndListActionsAllowed, lstFormats.size()));
    1794     for (size_t i = 0; i < lstFormats.size(); ++i)
    1795         LogFlow(("'%s' ", lstFormats.at(i).c_str()));
    1796     LogFlow(("\n"));
    1797 #endif
    1798 
    1799     int rc;
    1800 
    1801     do
    1802     {
    1803         /* Check if the VM session has changed and reconnect to the HGCM service if necessary. */
    1804         rc = checkForSessionChange();
    1805         AssertRCBreak(rc);
    1806 
    1807         /* Append all actual (MIME) formats we support to the list.
    1808          * These must come last, after the default Atoms above. */
    1809         rc = appendFormatsToList(lstFormats, m_lstAtomFormats);
    1810         AssertRCBreak(rc);
    1811 
    1812         rc = wndXDnDSetFormatList(m_wndProxy.hWnd, xAtom(XA_XdndTypeList), m_lstAtomFormats);
    1813         AssertRCBreak(rc);
    1814 
    1815         /* Announce the possible actions. */
    1816         VBoxDnDAtomList lstActions;
    1817         rc = toAtomActions(dndListActionsAllowed, lstActions);
    1818         AssertRCBreak(rc);
    1819 
    1820         rc = wndXDnDSetActionList(m_wndProxy.hWnd, lstActions);
    1821         AssertRCBreak(rc);
    1822 
    1823         /* Set the DnD selection owner to our window. */
    1824         /** @todo Don't use CurrentTime -- according to ICCCM section 2.1. */
    1825         XSetSelectionOwner(m_pDisplay, xAtom(XA_XdndSelection), m_wndProxy.hWnd, CurrentTime);
    1826 
    1827         if (g_cVerbosity)
    1828         {
    1829             RTCString strMsg("Enter: Host -> Guest\n");
    1830             strMsg += RTCStringFmt("Allowed actions: ");
    1831             for (size_t i = 0; i < lstActions.size(); i++)
    1832             {
    1833                 if (i > 0)
    1834                     strMsg += ", ";
    1835                 strMsg += DnDActionToStr(toHGCMAction(lstActions.at(i)));
    1836             }
    1837             strMsg += " - Formats: ";
    1838             for (size_t i = 0; i < lstFormats.size(); i++)
    1839             {
    1840                 if (i > 0)
    1841                     strMsg += ", ";
    1842                 strMsg += lstFormats.at(i);
    1843             }
    1844 
    1845             VBClShowNotify(VBOX_DND_SHOWNOTIFY_HEADER, strMsg.c_str());
    1846         }
    1847 
    1848         m_enmMode  = HG;
    1849         m_enmState = Dragging;
    1850 
    1851     } while (0);
    1852 
    1853     LogFlowFuncLeaveRC(rc);
    1854     return rc;
    1855 }
    1856 
    1857 /**
    1858  * Host -> Guest: Event signalling that the host's (mouse) cursor has left the VM's (guest's)
    1859  *                display area.
    1860  */
    1861 int DragInstance::hgLeave(void)
    1862 {
    1863     if (g_cVerbosity)
    1864         VBClShowNotify(VBOX_DND_SHOWNOTIFY_HEADER, "Leave: Host -> Guest");
    1865 
    1866     if (m_enmMode == HG) /* Only reset if in the right operation mode. */
    1867         reset();
    1868 
    1869     return VINF_SUCCESS;
    1870 }
    1871 
    1872 /**
    1873  * Host -> Guest: Event signalling that the host's (mouse) cursor has been moved within the VM's
    1874  *                (guest's) display area.
    1875  *
    1876  * @returns IPRT status code.
    1877  * @param   uPosX                   Relative X position within the guest's display area.
    1878  * @param   uPosY                   Relative Y position within the guest's display area.
    1879  * @param   dndActionDefault        Default action the host wants to perform on the guest
    1880  *                                  as soon as the operation successfully finishes.
    1881  */
    1882 int DragInstance::hgMove(uint32_t uPosX, uint32_t uPosY, VBOXDNDACTION dndActionDefault)
    1883 {
    1884     LogFlowThisFunc(("mode=%RU32, state=%RU32\n", m_enmMode, m_enmState));
    1885     LogFlowThisFunc(("uPosX=%RU32, uPosY=%RU32, dndActionDefault=0x%x\n", uPosX, uPosY, dndActionDefault));
    1886 
    1887     if (   m_enmMode  != HG
    1888         || m_enmState != Dragging)
    1889     {
    1890         return VERR_INVALID_STATE;
    1891     }
    1892 
    1893     int rc  = VINF_SUCCESS;
    1894     int xRc = Success;
    1895 
    1896     /* Move the mouse cursor within the guest. */
    1897     mouseCursorMove(uPosX, uPosY);
    1898 
    1899     /* Search for the application window below the cursor. */
    1900     Window wndBelowCursor       = gX11->applicationWindowBelowCursor(m_wndRoot);
    1901     char *pszWndBelowCursorName = wndX11GetNameA(wndBelowCursor);
    1902     AssertPtrReturn(pszWndBelowCursorName, VERR_NO_MEMORY);
    1903 
    1904     uint8_t uBelowCursorXdndVer = 0; /* 0 means the current window is _not_ XdndAware. */
    1905 
    1906     if (wndBelowCursor != None)
    1907     {
    1908         /* Temp stuff for the XGetWindowProperty call. */
    1909         Atom atmTmp;
    1910         int fmt;
    1911         unsigned long cItems, cbRemaining;
    1912         unsigned char *pcData = NULL;
    1913 
    1914         /* Query the XdndAware property from the window. We are interested in
    1915          * the version and if it is XdndAware at all. */
    1916         xRc = XGetWindowProperty(m_pDisplay, wndBelowCursor, xAtom(XA_XdndAware),
    1917                                  0, 2, False, AnyPropertyType,
    1918                                  &atmTmp, &fmt, &cItems, &cbRemaining, &pcData);
    1919         if (xRc != Success)
    1920         {
    1921             VBClLogError("Error getting properties of cursor window=%#x: %s\n", wndBelowCursor, gX11->xErrorToString(xRc).c_str());
    1922         }
    1923         else
    1924         {
    1925             if (pcData == NULL || fmt != 32 || cItems != 1)
    1926             {
    1927                 /** @todo Do we need to deal with this? */
    1928                 VBClLogError("Wrong window properties for window %#x: pcData=%#x, iFmt=%d, cItems=%ul\n",
    1929                              wndBelowCursor, pcData, fmt, cItems);
    1930             }
    1931             else
    1932             {
    1933                 /* Get the current window's Xdnd version. */
    1934                 uBelowCursorXdndVer = (uint8_t)reinterpret_cast<long *>(pcData)[0];
    1935             }
    1936 
    1937             XFree(pcData);
    1938         }
    1939     }
    1940 
    1941     char *pszWndCurName = wndX11GetNameA(m_wndCur);
    1942     AssertPtrReturn(pszWndCurName, VERR_NO_MEMORY);
    1943 
    1944     LogFlowThisFunc(("wndCursor=%x ('%s', Xdnd version %u), wndCur=%x ('%s', Xdnd version %u)\n",
    1945                      wndBelowCursor, pszWndBelowCursorName, uBelowCursorXdndVer, m_wndCur, pszWndCurName, m_uXdndVer));
    1946 
    1947     if (   wndBelowCursor != m_wndCur
    1948         && m_uXdndVer)
    1949     {
    1950         VBClLogInfo("Left old window %#x ('%s'), supported Xdnd version %u\n", m_wndCur, pszWndCurName, m_uXdndVer);
    1951 
    1952         /* We left the current XdndAware window. Announce this to the current indow. */
    1953         XClientMessageEvent m;
    1954         RT_ZERO(m);
    1955         m.type                    = ClientMessage;
    1956         m.display                 = m_pDisplay;
    1957         m.window                  = m_wndCur;
    1958         m.message_type            = xAtom(XA_XdndLeave);
    1959         m.format                  = 32;
    1960         m.data.l[XdndLeaveWindow] = m_wndProxy.hWnd;
    1961 
    1962         xRc = XSendEvent(m_pDisplay, m_wndCur, False, NoEventMask, reinterpret_cast<XEvent*>(&m));
    1963         if (xRc == 0)
    1964             VBClLogError("Error sending leave event to old window %#x: %s\n", m_wndCur, gX11->xErrorToString(xRc).c_str());
    1965 
    1966         /* Reset our current window. */
    1967         m_wndCur   = 0;
    1968         m_uXdndVer = 0;
    1969     }
    1970 
    1971     /*
    1972      * Do we have a new Xdnd-aware window which now is under the cursor?
    1973      */
    1974     if (   wndBelowCursor != m_wndCur
    1975         && uBelowCursorXdndVer)
    1976     {
    1977         VBClLogInfo("Entered new window %#x ('%s'), supports Xdnd version=%u\n",
    1978                     wndBelowCursor, pszWndBelowCursorName, uBelowCursorXdndVer);
    1979 
    1980         /*
    1981          * We enter a new window. Announce the XdndEnter event to the new
    1982          * window. The first three mime types are attached to the event (the
    1983          * others could be requested by the XdndTypeList property from the
    1984          * window itself).
    1985          */
    1986         XClientMessageEvent m;
    1987         RT_ZERO(m);
    1988         m.type         = ClientMessage;
    1989         m.display      = m_pDisplay;
    1990         m.window       = wndBelowCursor;
    1991         m.message_type = xAtom(XA_XdndEnter);
    1992         m.format       = 32;
    1993         m.data.l[XdndEnterWindow] = m_wndProxy.hWnd;
    1994         m.data.l[XdndEnterFlags]  = RT_MAKE_U32_FROM_U8(
    1995                                     /* Bit 0 is set if the source supports more than three data types. */
    1996                                     m_lstAtomFormats.size() > 3 ? RT_BIT(0) : 0,
    1997                                     /* Reserved for future use. */
    1998                                     0, 0,
    1999                                     /* Protocol version to use. */
    2000                                     RT_MIN(VBOX_XDND_VERSION, uBelowCursorXdndVer));
    2001         m.data.l[XdndEnterType1]  = m_lstAtomFormats.value(0, None); /* First data type to use. */
    2002         m.data.l[XdndEnterType2]  = m_lstAtomFormats.value(1, None); /* Second data type to use. */
    2003         m.data.l[XdndEnterType3]  = m_lstAtomFormats.value(2, None); /* Third data type to use. */
    2004 
    2005         xRc = XSendEvent(m_pDisplay, wndBelowCursor, False, NoEventMask, reinterpret_cast<XEvent*>(&m));
    2006         if (xRc == 0)
    2007             VBClLogError("Error sending enter event to window %#x: %s\n", wndBelowCursor, gX11->xErrorToString(xRc).c_str());
    2008     }
    2009 
    2010     if (uBelowCursorXdndVer)
    2011     {
    2012         Assert(wndBelowCursor != None);
    2013 
    2014         Atom atmAction = toAtomAction(dndActionDefault);
    2015         LogFlowThisFunc(("strAction=%s\n", xAtomToString(atmAction).c_str()));
    2016 
    2017         VBClLogInfo("Sent position event (%RU32 x %RU32) to window %#x ('%s') with actions '%s'\n",
    2018                     uPosX, uPosY, wndBelowCursor, pszWndBelowCursorName, xAtomToString(atmAction).c_str());
    2019 
    2020         /*
    2021          * Send a XdndPosition event with the proposed action to the guest.
    2022          */
    2023         XClientMessageEvent m;
    2024         RT_ZERO(m);
    2025         m.type         = ClientMessage;
    2026         m.display      = m_pDisplay;
    2027         m.window       = wndBelowCursor;
    2028         m.message_type = xAtom(XA_XdndPosition);
    2029         m.format       = 32;
    2030         m.data.l[XdndPositionWindow]    = m_wndProxy.hWnd;               /* X window ID of source window. */
    2031         m.data.l[XdndPositionFlags]     = 0;                             /* Reserved, set to 0. */
    2032         m.data.l[XdndPositionXY]        = RT_MAKE_U32(uPosY, uPosX);     /* Cursor coordinates relative to the root window. */
    2033         m.data.l[XdndPositionTimeStamp] = CurrentTime;                   /* Timestamp for retrieving data. */
    2034         m.data.l[XdndPositionAction]    = atmAction;                     /* Actions requested by the user. */
    2035 
    2036         xRc = XSendEvent(m_pDisplay, wndBelowCursor, False, NoEventMask, reinterpret_cast<XEvent*>(&m));
    2037         if (xRc == 0)
    2038             VBClLogError("Error sending position event to current window %#x: %s\n", wndBelowCursor, gX11->xErrorToString(xRc).c_str());
    2039     }
    2040 
    2041     if (uBelowCursorXdndVer == 0)
    2042     {
    2043         /* No window to process, so send a ignore ack event to the host. */
    2044         rc = VbglR3DnDHGSendAckOp(&m_dndCtx, VBOX_DND_ACTION_IGNORE);
    2045     }
    2046     else
    2047     {
    2048         Assert(wndBelowCursor != None);
    2049 
    2050         m_wndCur   = wndBelowCursor;
    2051         m_uXdndVer = uBelowCursorXdndVer;
    2052     }
    2053 
    2054     RTStrFree(pszWndBelowCursorName);
    2055     RTStrFree(pszWndCurName);
    2056 
    2057     LogFlowFuncLeaveRC(rc);
    2058     return rc;
    2059 }
    2060 
    2061 /**
    2062  * Host -> Guest: Event signalling that the host has dropped the data over the VM (guest) window.
    2063  *
    2064  * @returns IPRT status code.
    2065  * @param   uPosX                   Relative X position within the guest's display area.
    2066  * @param   uPosY                   Relative Y position within the guest's display area.
    2067  * @param   dndActionDefault        Default action the host wants to perform on the guest
    2068  *                                  as soon as the operation successfully finishes.
    2069  */
    2070 int DragInstance::hgDrop(uint32_t uPosX, uint32_t uPosY, VBOXDNDACTION dndActionDefault)
    2071 {
    2072     RT_NOREF3(uPosX, uPosY, dndActionDefault);
    2073     LogFlowThisFunc(("wndCur=%RU32, wndProxy=%RU32, mode=%RU32, state=%RU32\n", m_wndCur, m_wndProxy.hWnd, m_enmMode, m_enmState));
    2074     LogFlowThisFunc(("uPosX=%RU32, uPosY=%RU32, dndActionDefault=0x%x\n", uPosX, uPosY, dndActionDefault));
    2075 
    2076     if (   m_enmMode  != HG
    2077         || m_enmState != Dragging)
    2078     {
    2079         return VERR_INVALID_STATE;
    2080     }
    2081 
    2082     /* Set the state accordingly. */
    2083     m_enmState = Dropped;
    2084 
    2085     /*
    2086      * Ask the host to send the raw data, as we don't (yet) know which format
    2087      * the guest exactly expects. As blocking in a SelectionRequest message turned
    2088      * out to be very unreliable (e.g. with KDE apps) we request to start transferring
    2089      * file/directory data (if any) here.
    2090      */
    2091     char szFormat[] = { "text/uri-list" };
    2092 
    2093     int rc = VbglR3DnDHGSendReqData(&m_dndCtx, szFormat);
    2094     VBClLogInfo("Drop event from host resulted in: %Rrc\n", rc);
    2095 
    2096     if (g_cVerbosity)
    2097         VBClShowNotify(VBOX_DND_SHOWNOTIFY_HEADER, "Drop: Host -> Guest");
    2098 
    2099     LogFlowFuncLeaveRC(rc);
    2100     return rc;
    2101 }
    2102 
    2103 /**
    2104  * Host -> Guest: Event signalling that the host has finished sending drag'n drop
    2105  *                data to the guest for further processing.
    2106  *
    2107  * @returns IPRT status code.
    2108  * @param   pMeta               Pointer to meta data from host.
    2109  */
    2110 int DragInstance::hgDataReceive(PVBGLR3GUESTDNDMETADATA pMeta)
    2111 {
    2112     LogFlowThisFunc(("enmMode=%RU32, enmState=%RU32\n", m_enmMode, m_enmState));
    2113     LogFlowThisFunc(("enmMetaType=%RU32\n", pMeta->enmType));
    2114 
    2115     if (   m_enmMode  != HG
    2116         || m_enmState != Dropped)
    2117     {
    2118         return VERR_INVALID_STATE;
    2119     }
    2120 
    2121     void  *pvData = NULL;
    2122     size_t cbData = 0;
    2123 
    2124     int rc = VINF_SUCCESS; /* Shut up GCC. */
    2125 
    2126     switch (pMeta->enmType)
    2127     {
    2128         case VBGLR3GUESTDNDMETADATATYPE_RAW:
    2129         {
    2130             AssertBreakStmt(pMeta->u.Raw.pvMeta != NULL, rc = VERR_INVALID_POINTER);
    2131             pvData = pMeta->u.Raw.pvMeta;
    2132             AssertBreakStmt(pMeta->u.Raw.cbMeta, rc = VERR_INVALID_PARAMETER);
    2133             cbData = pMeta->u.Raw.cbMeta;
    2134 
    2135             rc = VINF_SUCCESS;
    2136             break;
    2137         }
    2138 
    2139         case VBGLR3GUESTDNDMETADATATYPE_URI_LIST:
    2140         {
    2141             const char *pcszRootPath = DnDTransferListGetRootPathAbs(&pMeta->u.URI.Transfer);
    2142             AssertPtrBreakStmt(pcszRootPath, VERR_INVALID_POINTER);
    2143 
    2144             VBClLogInfo("Transfer list root directory is '%s'\n", pcszRootPath);
    2145 
    2146             /* Note: Use the URI format here, as X' DnD spec says so. */
    2147             rc = DnDTransferListGetRootsEx(&pMeta->u.URI.Transfer, DNDTRANSFERLISTFMT_URI, pcszRootPath,
    2148                                            DND_PATH_SEPARATOR_STR, (char **)&pvData, &cbData);
    2149             break;
    2150         }
    2151 
    2152         default:
    2153             AssertFailedStmt(rc = VERR_NOT_IMPLEMENTED);
    2154             break;
    2155     }
    2156 
    2157     if (RT_FAILURE(rc))
    2158         return rc;
    2159 
    2160     /*
    2161      * At this point all data needed (including sent files/directories) should
    2162      * be on the guest, so proceed working on communicating with the target window.
    2163      */
    2164     VBClLogInfo("Received %RU32 bytes of meta data from host\n", cbData);
    2165 
    2166     /* Destroy any old data. */
    2167     if (m_pvSelReqData)
    2168     {
    2169         Assert(m_cbSelReqData);
    2170 
    2171         RTMemFree(m_pvSelReqData); /** @todo RTMemRealloc? */
    2172         m_cbSelReqData = 0;
    2173     }
    2174 
    2175     /** @todo Handle incremental transfers. */
    2176 
    2177     /* Make a copy of the data. This data later then will be used to fill into
    2178      * the selection request. */
    2179     if (cbData)
    2180     {
    2181         m_pvSelReqData = RTMemAlloc(cbData);
    2182         if (!m_pvSelReqData)
    2183             return VERR_NO_MEMORY;
    2184 
    2185         memcpy(m_pvSelReqData, pvData, cbData);
    2186         m_cbSelReqData = cbData;
    2187     }
    2188 
    2189     /*
    2190      * Send a drop event to the current window (target).
    2191      * This window in turn then will raise a SelectionRequest message to our proxy window,
    2192      * which we will handle in our onX11SelectionRequest handler.
    2193      *
    2194      * The SelectionRequest will tell us in which format the target wants the data from the host.
    2195      */
    2196     XClientMessageEvent m;
    2197     RT_ZERO(m);
    2198     m.type         = ClientMessage;
    2199     m.display      = m_pDisplay;
    2200     m.window       = m_wndCur;
    2201     m.message_type = xAtom(XA_XdndDrop);
    2202     m.format       = 32;
    2203     m.data.l[XdndDropWindow]    = m_wndProxy.hWnd;  /* Source window. */
    2204     m.data.l[XdndDropFlags]     = 0;                /* Reserved for future use. */
    2205     m.data.l[XdndDropTimeStamp] = CurrentTime;      /* Our DnD data does not rely on any timing, so just use the current time. */
    2206 
    2207     int xRc = XSendEvent(m_pDisplay, m_wndCur, False /* Propagate */, NoEventMask, reinterpret_cast<XEvent*>(&m));
    2208     if (xRc == 0)
    2209         VBClLogError("Error sending XA_XdndDrop event to window=%#x: %s\n", m_wndCur, gX11->xErrorToString(xRc).c_str());
    2210     XFlush(m_pDisplay);
    2211 
    2212     LogFlowFuncLeaveRC(rc);
    2213     return rc;
    2214 }
    2215 
    2216 /**
    2217  * Checks if the VM session has changed (can happen when restoring the VM from a saved state)
    2218  * and do a reconnect to the DnD HGCM service.
    2219  *
    2220  * @returns IPRT status code.
    2221  */
    2222 int DragInstance::checkForSessionChange(void)
    2223 {
    2224     uint64_t uSessionID;
    2225     int rc = VbglR3GetSessionId(&uSessionID);
    2226     if (   RT_SUCCESS(rc)
    2227         && uSessionID != m_dndCtx.uSessionID)
    2228     {
    2229         LogFlowThisFunc(("VM session has changed to %RU64\n", uSessionID));
    2230 
    2231         rc = VbglR3DnDDisconnect(&m_dndCtx);
    2232         AssertRC(rc);
    2233 
    2234         rc = VbglR3DnDConnect(&m_dndCtx);
    2235         AssertRC(rc);
    2236     }
    2237 
    2238     LogFlowFuncLeaveRC(rc);
    2239     return rc;
    2240 }
    2241 
    2242 #ifdef VBOX_WITH_DRAG_AND_DROP_GH
    2243 /**
    2244  * Guest -> Host: Event signalling that the host is asking whether there is a pending
    2245  *                drag event on the guest (to the host).
    2246  *
    2247  * @returns IPRT status code.
    2248  */
    2249 int DragInstance::ghIsDnDPending(void)
    2250 {
    2251     LogFlowThisFunc(("mode=%RU32, state=%RU32\n", m_enmMode, m_enmState));
    2252 
    2253     int rc;
    2254 
    2255     RTCString         strFormats       = DND_PATH_SEPARATOR_STR; /** @todo If empty, IOCTL fails with VERR_ACCESS_DENIED. */
    2256     VBOXDNDACTION     dndActionDefault = VBOX_DND_ACTION_IGNORE;
    2257     VBOXDNDACTIONLIST dndActionList    = VBOX_DND_ACTION_IGNORE;
    2258 
    2259     /* Currently in wrong mode? Bail out. */
    2260     if (m_enmMode == HG)
    2261     {
    2262         rc = VERR_INVALID_STATE;
    2263     }
    2264     /* Message already processed successfully? */
    2265     else if (   m_enmMode  == GH
    2266              && (   m_enmState == Dragging
    2267                  || m_enmState == Dropped)
    2268             )
    2269     {
    2270         /* No need to query for the source window again. */
    2271         rc = VINF_SUCCESS;
    2272     }
    2273     else
    2274     {
    2275         /* Check if the VM session has changed and reconnect to the HGCM service if necessary. */
    2276         rc = checkForSessionChange();
    2277 
    2278         /* Determine the current window which currently has the XdndSelection set. */
    2279         Window wndSel = XGetSelectionOwner(m_pDisplay, xAtom(XA_XdndSelection));
    2280         LogFlowThisFunc(("wndSel=%#x, wndProxy=%#x, wndCur=%#x\n", wndSel, m_wndProxy.hWnd, m_wndCur));
    2281 
    2282         /* Is this another window which has a Xdnd selection and not our proxy window? */
    2283         if (   RT_SUCCESS(rc)
    2284             && wndSel
    2285             && wndSel != m_wndCur)
    2286         {
    2287             char *pszWndSelName = wndX11GetNameA(wndSel);
    2288             AssertPtrReturn(pszWndSelName, VERR_NO_MEMORY);
    2289             VBClLogInfo("New guest source window %#x ('%s')\n", wndSel, pszWndSelName);
    2290 
    2291             /* Start over. */
    2292             reset();
    2293 
    2294             /* Map the window on the current cursor position, which should provoke
    2295              * an XdndEnter event. */
    2296             rc = proxyWinShow();
    2297             if (RT_SUCCESS(rc))
    2298             {
    2299                 rc = mouseCursorFakeMove();
    2300                 if (RT_SUCCESS(rc))
    2301                 {
    2302                     bool fWaitFailed = false; /* Waiting for status changed failed? */
    2303 
    2304                     /* Wait until we're in "Dragging" state. */
    2305                     rc = waitForStatusChange(Dragging, 100 /* 100ms timeout */);
    2306 
    2307                     /*
    2308                      * Note: Don't wait too long here, as this mostly will make
    2309                      *       the drag and drop experience on the host being laggy
    2310                      *       and unresponsive.
    2311                      *
    2312                      *       Instead, let the host query multiple times with 100ms
    2313                      *       timeout each (see above) and only report an error if
    2314                      *       the overall querying time has been exceeded.<
    2315                      */
    2316                     if (RT_SUCCESS(rc))
    2317                     {
    2318                         m_enmMode = GH;
    2319                     }
    2320                     else if (rc == VERR_TIMEOUT)
    2321                     {
    2322                         /** @todo Make m_cFailedPendingAttempts configurable. For slower window managers? */
    2323                         if (m_cFailedPendingAttempts++ > 50) /* Tolerate up to 5s total (100ms for each slot). */
    2324                             fWaitFailed = true;
    2325                         else
    2326                             rc = VINF_SUCCESS;
    2327                     }
    2328                     else if (RT_FAILURE(rc))
    2329                         fWaitFailed = true;
    2330 
    2331                     if (fWaitFailed)
    2332                     {
    2333                         VBClLogError("Error mapping proxy window to guest source window %#x ('%s'), rc=%Rrc\n",
    2334                                      wndSel, pszWndSelName, rc);
    2335 
    2336                         /* Reset the counter in any case. */
    2337                         m_cFailedPendingAttempts = 0;
    2338                     }
    2339                 }
    2340             }
    2341 
    2342             RTStrFree(pszWndSelName);
    2343         }
    2344         else
    2345             VBClLogInfo("No guest source window\n");
    2346     }
    2347 
    2348     /*
    2349      * Acknowledge to the host in any case, regardless
    2350      * if something failed here or not. Be responsive.
    2351      */
    2352 
    2353     int rc2 = RTCritSectEnter(&m_dataCS);
    2354     if (RT_SUCCESS(rc2))
    2355     {
    2356         /* Filter out the default X11-specific formats (required for Xdnd, 'TARGET' / 'MULTIPLE');
    2357          * those will not be supported by VirtualBox. */
    2358         VBoxDnDAtomList const lstAtomFormatsFiltered = gX11->xAtomListFiltered(m_lstAtomFormats, m_lstAtomFormatsX11);
    2359 
    2360         /* Anything left to report to the host? */
    2361         if (lstAtomFormatsFiltered.size())
    2362         {
    2363             strFormats       = gX11->xAtomListToString(lstAtomFormatsFiltered);
    2364             dndActionDefault = VBOX_DND_ACTION_COPY; /** @todo Handle default action! */
    2365             dndActionList    = VBOX_DND_ACTION_COPY; /** @todo Ditto. */
    2366             dndActionList   |= toHGCMActions(m_lstAtomActions);
    2367         }
    2368 
    2369         RTCritSectLeave(&m_dataCS);
    2370     }
    2371 
    2372     if (g_cVerbosity)
    2373     {
    2374         char *pszActions = DnDActionListToStrA(dndActionList);
    2375         AssertPtrReturn(pszActions, VERR_NO_MEMORY);
    2376         VBClLogVerbose(1, "Reporting formats '%s' (actions '%s' / %#x, default action is '%s' (%#x)\n",
    2377                        strFormats.c_str(), pszActions, dndActionList, DnDActionToStr(dndActionDefault), dndActionDefault);
    2378         RTStrFree(pszActions);
    2379     }
    2380 
    2381     rc2 = VbglR3DnDGHSendAckPending(&m_dndCtx, dndActionDefault, dndActionList,
    2382                                     strFormats.c_str(), strFormats.length() + 1 /* Include termination */);
    2383     LogFlowThisFunc(("uClientID=%RU32, dndActionDefault=0x%x, dndActionList=0x%x, strFormats=%s, rc=%Rrc\n",
    2384                      m_dndCtx.uClientID, dndActionDefault, dndActionList, strFormats.c_str(), rc2));
    2385     if (RT_FAILURE(rc2))
    2386     {
    2387         switch (rc2)
    2388         {
    2389             case VERR_ACCESS_DENIED:
    2390             {
    2391                 rc = VBClShowNotify(VBOX_DND_SHOWNOTIFY_HEADER,
    2392                                     "Drag and drop to the host either is not supported or disabled. "
    2393                                     "Please enable Guest to Host or Bidirectional drag and drop mode "
    2394                                     "or re-install the VirtualBox Guest Additions.");
    2395                 AssertRC(rc);
    2396                 break;
    2397             }
    2398 
    2399             default:
    2400                 break;
    2401         }
    2402 
    2403         VBClLogError("Error reporting pending drag and drop operation status to host: %Rrc\n", rc2);
    2404         if (RT_SUCCESS(rc))
    2405             rc = rc2;
    2406     }
    2407 
    2408     LogFlowFuncLeaveRC(rc);
    2409     return rc;
    2410 }
    2411 
    2412 /**
    2413  * Guest -> Host: Event signalling that the host has dropped the item(s) on the
    2414  *                host side.
    2415  *
    2416  * @returns IPRT status code.
    2417  * @param   strFormat               Requested format to send to the host.
    2418  * @param   dndActionRequested      Requested action to perform on the guest.
    2419  */
    2420 int DragInstance::ghDropped(const RTCString &strFormat, VBOXDNDACTION dndActionRequested)
    2421 {
    2422     LogFlowThisFunc(("mode=%RU32, state=%RU32, strFormat=%s, dndActionRequested=0x%x\n",
    2423                      m_enmMode, m_enmState, strFormat.c_str(), dndActionRequested));
    2424 
    2425     /* Currently in wrong mode? Bail out. */
    2426     if (   m_enmMode == Unknown
    2427         || m_enmMode == HG)
    2428     {
    2429         return VERR_INVALID_STATE;
    2430     }
    2431 
    2432     if (   m_enmMode  == GH
    2433         && m_enmState != Dragging)
    2434     {
    2435         return VERR_INVALID_STATE;
    2436     }
    2437 
    2438     int rc = VINF_SUCCESS;
    2439 
    2440     m_enmState = Dropped;
    2441 
    2442 #ifdef DEBUG
    2443     XWindowAttributes xwa;
    2444     XGetWindowAttributes(m_pDisplay, m_wndCur, &xwa);
    2445     LogFlowThisFunc(("wndProxy=%RU32, wndCur=%RU32, x=%d, y=%d, width=%d, height=%d\n",
    2446                      m_wndProxy.hWnd, m_wndCur, xwa.x, xwa.y, xwa.width, xwa.height));
    2447 
    2448     Window wndSelection = XGetSelectionOwner(m_pDisplay, xAtom(XA_XdndSelection));
    2449     LogFlowThisFunc(("wndSelection=%#x\n", wndSelection));
    2450 #endif
    2451 
    2452     /* We send a fake mouse move event to the current window, cause
    2453      * this should have the grab. */
    2454     mouseCursorFakeMove();
    2455 
    2456     /**
    2457      * The fake button release event above should lead to a XdndDrop event from the
    2458      * source window. Because of showing our proxy window, other Xdnd events can
    2459      * occur before, e.g. a XdndPosition event. We are not interested
    2460      * in those, so just try to get the right one.
    2461      */
    2462 
    2463     XClientMessageEvent evDnDDrop;
    2464     bool fDrop = waitForX11ClientMsg(evDnDDrop, xAtom(XA_XdndDrop), 5 * 1000 /* 5s timeout */);
    2465     if (fDrop)
    2466     {
    2467         LogFlowThisFunc(("XA_XdndDrop\n"));
    2468 
    2469         /* Request to convert the selection in the specific format and
    2470          * place it to our proxy window as property. */
    2471         Assert(evDnDDrop.message_type == xAtom(XA_XdndDrop));
    2472 
    2473         Window wndSource = evDnDDrop.data.l[XdndDropWindow]; /* Source window which has sent the message. */
    2474         Assert(wndSource == m_wndCur);
    2475 
    2476         Atom aFormat     = gX11->stringToxAtom(strFormat.c_str());
    2477 
    2478         Time tsDrop;
    2479         if (m_uXdndVer >= 1)
    2480             tsDrop = evDnDDrop.data.l[XdndDropTimeStamp];
    2481         else
    2482             tsDrop = CurrentTime;
    2483 
    2484         XConvertSelection(m_pDisplay, xAtom(XA_XdndSelection), aFormat, xAtom(XA_XdndSelection),
    2485                           m_wndProxy.hWnd, tsDrop);
    2486 
    2487         /* Wait for the selection notify event. */
    2488         XEvent evSelNotify;
    2489         RT_ZERO(evSelNotify);
    2490         if (waitForX11Msg(evSelNotify, SelectionNotify, 5 * 1000 /* 5s timeout */))
    2491         {
    2492             bool fCancel = false;
    2493 
    2494             /* Make some paranoid checks. */
    2495             if (   evSelNotify.xselection.type      == SelectionNotify
    2496                 && evSelNotify.xselection.display   == m_pDisplay
    2497                 && evSelNotify.xselection.selection == xAtom(XA_XdndSelection)
    2498                 && evSelNotify.xselection.requestor == m_wndProxy.hWnd
    2499                 && evSelNotify.xselection.target    == aFormat)
    2500             {
    2501                 LogFlowThisFunc(("Selection notfiy (from wnd=%#x)\n", m_wndCur));
    2502 
    2503                 Atom aPropType;
    2504                 int iPropFormat;
    2505                 unsigned long cItems, cbRemaining;
    2506                 unsigned char *pcData = NULL;
    2507                 int xRc = XGetWindowProperty(m_pDisplay, m_wndProxy.hWnd,
    2508                                              xAtom(XA_XdndSelection)  /* Property */,
    2509                                              0                        /* Offset */,
    2510                                              VBOX_MAX_XPROPERTIES     /* Length of 32-bit multiples */,
    2511                                              True                     /* Delete property? */,
    2512                                              AnyPropertyType,         /* Property type */
    2513                                              &aPropType, &iPropFormat, &cItems, &cbRemaining, &pcData);
    2514                 if (xRc != Success)
    2515                     VBClLogError("Error getting XA_XdndSelection property of proxy window=%#x: %s\n",
    2516                                  m_wndProxy.hWnd, gX11->xErrorToString(xRc).c_str());
    2517 
    2518                 LogFlowThisFunc(("strType=%s, iPropFormat=%d, cItems=%RU32, cbRemaining=%RU32\n",
    2519                                  gX11->xAtomToString(aPropType).c_str(), iPropFormat, cItems, cbRemaining));
    2520 
    2521                 if (   aPropType   != None
    2522                     && pcData      != NULL
    2523                     && iPropFormat >= 8
    2524                     && cItems      >  0
    2525                     && cbRemaining == 0)
    2526                 {
    2527                     size_t cbData = cItems * (iPropFormat / 8);
    2528                     LogFlowThisFunc(("cbData=%zu\n", cbData));
    2529 
    2530                     /* For whatever reason some of the string MIME types are not
    2531                      * zero terminated. Check that and correct it when necessary,
    2532                      * because the guest side wants this in any case. */
    2533                     if (   m_lstAllowedFormats.contains(strFormat)
    2534                         && pcData[cbData - 1] != '\0')
    2535                     {
    2536                         unsigned char *pvDataTmp = static_cast<unsigned char*>(RTMemAlloc(cbData + 1));
    2537                         if (pvDataTmp)
    2538                         {
    2539                             memcpy(pvDataTmp, pcData, cbData);
    2540                             pvDataTmp[cbData++] = '\0';
    2541 
    2542                             rc = VbglR3DnDGHSendData(&m_dndCtx, strFormat.c_str(), pvDataTmp, cbData);
    2543                             RTMemFree(pvDataTmp);
    2544                         }
    2545                         else
    2546                             rc = VERR_NO_MEMORY;
    2547                     }
    2548                     else
    2549                     {
    2550                         /* Send the raw data to the host. */
    2551                         rc = VbglR3DnDGHSendData(&m_dndCtx, strFormat.c_str(), pcData, cbData);
    2552                         LogFlowThisFunc(("Sent strFormat=%s, rc=%Rrc\n", strFormat.c_str(), rc));
    2553                     }
    2554 
    2555                     if (RT_SUCCESS(rc))
    2556                     {
    2557                         rc = m_wndProxy.sendFinished(wndSource, dndActionRequested);
    2558                     }
    2559                     else
    2560                         fCancel = true;
    2561                 }
    2562                 else
    2563                 {
    2564                     if (aPropType == xAtom(XA_INCR))
    2565                     {
    2566                         /** @todo Support incremental transfers. */
    2567                         AssertMsgFailed(("Incremental transfers are not supported yet\n"));
    2568 
    2569                         VBClLogError("Incremental transfers are not supported yet\n");
    2570                         rc = VERR_NOT_IMPLEMENTED;
    2571                     }
    2572                     else
    2573                     {
    2574                         VBClLogError("Not supported data type: %s\n", gX11->xAtomToString(aPropType).c_str());
    2575                         rc = VERR_NOT_SUPPORTED;
    2576                     }
    2577 
    2578                     fCancel = true;
    2579                 }
    2580 
    2581                 if (fCancel)
    2582                 {
    2583                     VBClLogInfo("Cancelling dropping to host\n");
    2584 
    2585                     /* Cancel the operation -- inform the source window by
    2586                      * sending a XdndFinished message so that the source can toss the required data. */
    2587                     rc = m_wndProxy.sendFinished(wndSource, VBOX_DND_ACTION_IGNORE);
    2588                 }
    2589 
    2590                 /* Cleanup. */
    2591                 if (pcData)
    2592                     XFree(pcData);
    2593             }
    2594             else
    2595                 rc = VERR_INVALID_PARAMETER;
    2596         }
    2597         else
    2598             rc = VERR_TIMEOUT;
    2599     }
    2600     else
    2601         rc = VERR_TIMEOUT;
    2602 
    2603     /* Inform the host on error. */
    2604     if (RT_FAILURE(rc))
    2605     {
    2606         int rc2 = VbglR3DnDSendError(&m_dndCtx, rc);
    2607         LogFlowThisFunc(("Sending error %Rrc to host resulted in %Rrc\n", rc, rc2)); RT_NOREF(rc2);
    2608         /* This is not fatal for us, just ignore. */
    2609     }
    2610 
    2611     /* At this point, we have either successfully transfered any data or not.
    2612      * So reset our internal state because we are done here for the current (ongoing)
    2613      * drag and drop operation. */
    2614     reset();
    2615 
    2616     LogFlowFuncLeaveRC(rc);
    2617     return rc;
    2618 }
    2619 #endif /* VBOX_WITH_DRAG_AND_DROP_GH */
    2620 
    2621 /*
    2622  * Helpers
    2623  */
    2624 
    2625 /**
    2626  * Fakes moving the mouse cursor to provoke various drag and drop
    2627  * events such as entering a target window or moving within a
    2628  * source window.
    2629  *
    2630  * Not the most elegant and probably correct function, but does
    2631  * the work for now.
    2632  *
    2633  * @returns IPRT status code.
    2634  */
    2635 int DragInstance::mouseCursorFakeMove(void)
    2636 {
    2637     int iScreenID = XDefaultScreen(m_pDisplay);
    2638     /** @todo What about multiple screens? Test this! */
    2639 
    2640     const int iScrX = XDisplayWidth(m_pDisplay, iScreenID);
    2641     const int iScrY = XDisplayHeight(m_pDisplay, iScreenID);
    2642 
    2643     int fx, fy, rx, ry;
    2644     Window wndTemp, wndChild;
    2645     int wx, wy; unsigned int mask;
    2646     XQueryPointer(m_pDisplay, m_wndRoot, &wndTemp, &wndChild, &rx, &ry, &wx, &wy, &mask);
    2647 
    2648     /*
    2649      * Apply some simple clipping and change the position slightly.
    2650      */
    2651 
    2652     /* FakeX */
    2653     if      (rx == 0)     fx = 1;
    2654     else if (rx == iScrX) fx = iScrX - 1;
    2655     else                  fx = rx + 1;
    2656 
    2657     /* FakeY */
    2658     if      (ry == 0)     fy = 1;
    2659     else if (ry == iScrY) fy = iScrY - 1;
    2660     else                  fy = ry + 1;
    2661 
    2662     /*
    2663      * Move the cursor to trigger the wanted events.
    2664      */
    2665     LogFlowThisFunc(("cursorRootX=%d, cursorRootY=%d\n", fx, fy));
    2666     int rc = mouseCursorMove(fx, fy);
    2667     if (RT_SUCCESS(rc))
    2668     {
    2669         /* Move the cursor back to its original position. */
    2670         rc = mouseCursorMove(rx, ry);
    2671     }
    2672 
    2673     return rc;
    2674 }
    2675 
    2676 /**
    2677  * Moves the mouse pointer to a specific position.
    2678  *
    2679  * @returns IPRT status code.
    2680  * @param   iPosX                   Absolute X coordinate.
    2681  * @param   iPosY                   Absolute Y coordinate.
    2682  */
    2683 int DragInstance::mouseCursorMove(int iPosX, int iPosY)
    2684 {
    2685     int const iScreenID = XDefaultScreen(m_pDisplay);
    2686     /** @todo What about multiple screens? Test this! */
    2687 
    2688     int const iScreenWidth  = XDisplayWidth (m_pDisplay, iScreenID);
    2689     int const iScreenHeight = XDisplayHeight(m_pDisplay, iScreenID);
    2690 
    2691     iPosX = RT_CLAMP(iPosX, 0, iScreenWidth);
    2692     iPosY = RT_CLAMP(iPosY, 0, iScreenHeight);
    2693 
    2694     /* Same mouse position as before? No need to do anything. */
    2695     if (   m_lastMouseX == iPosX
    2696         && m_lastMouseY == iPosY)
    2697     {
    2698         return VINF_SUCCESS;
    2699     }
    2700 
    2701     LogFlowThisFunc(("iPosX=%d, iPosY=%d, m_wndRoot=%#x\n", iPosX, iPosY, m_wndRoot));
    2702 
    2703     /* Move the guest pointer to the DnD position, so we can find the window
    2704      * below that position. */
    2705     int xRc = XWarpPointer(m_pDisplay, None, m_wndRoot, 0, 0, 0, 0, iPosX, iPosY);
    2706     if (xRc == Success)
    2707     {
    2708         XFlush(m_pDisplay);
    2709 
    2710         m_lastMouseX = iPosX;
    2711         m_lastMouseY = iPosY;
    2712     }
    2713     else
    2714         VBClLogError("Moving mouse cursor failed: %s", gX11->xErrorToString(xRc).c_str());
    2715 
    2716     return VINF_SUCCESS;
    2717 }
    2718 
    2719 /**
    2720  * Sends a mouse button event to a specific window.
    2721  *
    2722  * @param   wndDest                 Window to send the mouse button event to.
    2723  * @param   rx                      X coordinate relative to the root window's origin.
    2724  * @param   ry                      Y coordinate relative to the root window's origin.
    2725  * @param   iButton                 Mouse button to press/release.
    2726  * @param   fPress                  Whether to press or release the mouse button.
    2727  */
    2728 void DragInstance::mouseButtonSet(Window wndDest, int rx, int ry, int iButton, bool fPress)
    2729 {
    2730     LogFlowThisFunc(("wndDest=%#x, rx=%d, ry=%d, iBtn=%d, fPress=%RTbool\n",
    2731                      wndDest, rx, ry, iButton, fPress));
    2732 
    2733 #ifdef VBOX_DND_WITH_XTEST
    2734     /** @todo Make this check run only once. */
    2735     int ev, er, ma, mi;
    2736     if (XTestQueryExtension(m_pDisplay, &ev, &er, &ma, &mi))
    2737     {
    2738         LogFlowThisFunc(("XText extension available\n"));
    2739 
    2740         int xRc = XTestFakeButtonEvent(m_pDisplay, 1, fPress ? True : False, CurrentTime);
    2741         if (Rc == 0)
    2742             VBClLogError("Error sending XTestFakeButtonEvent event: %s\n", gX11->xErrorToString(xRc).c_str());
    2743         XFlush(m_pDisplay);
    2744     }
    2745     else
    2746     {
    2747 #endif
    2748         LogFlowThisFunc(("Note: XText extension not available or disabled\n"));
    2749 
    2750         unsigned int mask = 0;
    2751 
    2752         if (   rx == -1
    2753             && ry == -1)
    2754         {
    2755             Window wndRoot, wndChild;
    2756             int wx, wy;
    2757             XQueryPointer(m_pDisplay, m_wndRoot, &wndRoot, &wndChild, &rx, &ry, &wx, &wy, &mask);
    2758             LogFlowThisFunc(("Mouse pointer is at root x=%d, y=%d\n", rx, ry));
    2759         }
    2760 
    2761         XButtonEvent eBtn;
    2762         RT_ZERO(eBtn);
    2763 
    2764         eBtn.display      = m_pDisplay;
    2765         eBtn.root         = m_wndRoot;
    2766         eBtn.window       = wndDest;
    2767         eBtn.subwindow    = None;
    2768         eBtn.same_screen  = True;
    2769         eBtn.time         = CurrentTime;
    2770         eBtn.button       = iButton;
    2771         eBtn.state        = mask | (iButton == 1 ? Button1MotionMask :
    2772                                     iButton == 2 ? Button2MotionMask :
    2773                                     iButton == 3 ? Button3MotionMask :
    2774                                     iButton == 4 ? Button4MotionMask :
    2775                                     iButton == 5 ? Button5MotionMask : 0);
    2776         eBtn.type         = fPress ? ButtonPress : ButtonRelease;
    2777         eBtn.send_event   = False;
    2778         eBtn.x_root       = rx;
    2779         eBtn.y_root       = ry;
    2780 
    2781         XTranslateCoordinates(m_pDisplay, eBtn.root, eBtn.window, eBtn.x_root, eBtn.y_root, &eBtn.x, &eBtn.y, &eBtn.subwindow);
    2782         LogFlowThisFunc(("state=0x%x, x=%d, y=%d\n", eBtn.state, eBtn.x, eBtn.y));
    2783 
    2784         int xRc = XSendEvent(m_pDisplay, wndDest, True /* fPropagate */,
    2785                              ButtonPressMask,
    2786                              reinterpret_cast<XEvent*>(&eBtn));
    2787         if (xRc == 0)
    2788             VBClLogError("Error sending XButtonEvent event to window=%#x: %s\n", wndDest, gX11->xErrorToString(xRc).c_str());
    2789 
    2790         XFlush(m_pDisplay);
    2791 
    2792 #ifdef VBOX_DND_WITH_XTEST
    2793     }
    2794 #endif
    2795 }
    2796 
    2797 /**
    2798  * Shows the (invisible) proxy window. The proxy window is needed for intercepting
    2799  * drags from the host to the guest or from the guest to the host. It acts as a proxy
    2800  * between the host and the actual (UI) element on the guest OS.
    2801  *
    2802  * To not make it miss any actions this window gets spawned across the entire guest
    2803  * screen (think of an umbrella) to (hopefully) capture everything. A proxy window
    2804  * which follows the cursor would be far too slow here.
    2805  *
    2806  * @returns IPRT status code.
    2807  * @param   piRootX                 X coordinate relative to the root window's origin. Optional.
    2808  * @param   piRootY                 Y coordinate relative to the root window's origin. Optional.
    2809  */
    2810 int DragInstance::proxyWinShow(int *piRootX /* = NULL */, int *piRootY /* = NULL */) const
    2811 {
    2812     /* piRootX is optional. */
    2813     /* piRootY is optional. */
    2814 
    2815     LogFlowThisFuncEnter();
    2816 
    2817     int rc = VINF_SUCCESS;
    2818 
    2819 #if 0
    2820 # ifdef VBOX_DND_WITH_XTEST
    2821     XTestGrabControl(m_pDisplay, False);
    2822 # endif
    2823 #endif
    2824 
    2825     /* Get the mouse pointer position and determine if we're on the same screen as the root window
    2826      * and return the current child window beneath our mouse pointer, if any. */
    2827     int iRootX, iRootY;
    2828     int iChildX, iChildY;
    2829     unsigned int iMask;
    2830     Window wndRoot, wndChild;
    2831     Bool fInRootWnd = XQueryPointer(m_pDisplay, m_wndRoot, &wndRoot, &wndChild,
    2832                                     &iRootX, &iRootY, &iChildX, &iChildY, &iMask);
    2833 
    2834     LogFlowThisFunc(("fInRootWnd=%RTbool, wndRoot=%RU32, wndChild=%RU32, iRootX=%d, iRootY=%d\n",
    2835                      RT_BOOL(fInRootWnd), wndRoot, wndChild, iRootX, iRootY)); RT_NOREF(fInRootWnd);
    2836 
    2837     if (piRootX)
    2838         *piRootX = iRootX;
    2839     if (piRootY)
    2840         *piRootY = iRootY;
    2841 
    2842     XSynchronize(m_pDisplay, True /* Enable sync */);
    2843 
    2844     /* Bring our proxy window into foreground. */
    2845     XMapWindow(m_pDisplay, m_wndProxy.hWnd);
    2846     XRaiseWindow(m_pDisplay, m_wndProxy.hWnd);
    2847 
    2848     /* Spawn our proxy window over the entire screen, making it an easy drop target for the host's cursor. */
    2849     LogFlowThisFunc(("Proxy window x=%d, y=%d, width=%d, height=%d\n",
    2850                      m_wndProxy.iX, m_wndProxy.iY, m_wndProxy.iWidth, m_wndProxy.iHeight));
    2851     XMoveResizeWindow(m_pDisplay, m_wndProxy.hWnd, m_wndProxy.iX, m_wndProxy.iY, m_wndProxy.iWidth, m_wndProxy.iHeight);
    2852 
    2853     XFlush(m_pDisplay);
    2854 
    2855     XSynchronize(m_pDisplay, False /* Disable sync */);
    2856 
    2857 #if 0
    2858 # ifdef VBOX_DND_WITH_XTEST
    2859     XTestGrabControl(m_pDisplay, True);
    2860 # endif
    2861 #endif
    2862 
    2863     LogFlowFuncLeaveRC(rc);
    2864     return rc;
    2865 }
    2866 
    2867 /**
    2868  * Hides the (invisible) proxy window.
    2869  */
    2870 int DragInstance::proxyWinHide(void)
    2871 {
    2872     LogFlowFuncEnter();
    2873 
    2874     XUnmapWindow(m_pDisplay, m_wndProxy.hWnd);
    2875     XFlush(m_pDisplay);
    2876 
    2877     return VINF_SUCCESS; /** @todo Add error checking. */
    2878 }
    2879 
    2880 /**
    2881  * Allocates the name (title) of an X window.
    2882  * The returned pointer must be freed using RTStrFree().
    2883  *
    2884  * @returns Pointer to the allocated window name.
    2885  * @retval  NULL on allocation failure.
    2886  * @retval  "<No name>" if window name was not found / invalid window handle.
    2887  * @param   wndThis                 Window to retrieve name for.
    2888  */
    2889 char *DragInstance::wndX11GetNameA(Window wndThis) const
    2890 {
    2891     char *pszName = NULL;
    2892 
    2893     XTextProperty propName;
    2894     if (   wndThis != None
    2895         && XGetWMName(m_pDisplay, wndThis, &propName))
    2896     {
    2897         if (propName.value)
    2898             pszName = RTStrDup((char *)propName.value); /** @todo UTF8? */
    2899         XFree(propName.value);
    2900     }
    2901 
    2902     if (!pszName) /* No window name found? */
    2903         pszName = RTStrDup("<No name>");
    2904 
    2905     return pszName;
    2906 }
    2907 
    2908 /**
    2909  * Clear a window's supported/accepted actions list.
    2910  *
    2911  * @param   wndThis                 Window to clear the list for.
    2912  */
    2913 void DragInstance::wndXDnDClearActionList(Window wndThis) const
    2914 {
    2915     XDeleteProperty(m_pDisplay, wndThis, xAtom(XA_XdndActionList));
    2916 }
    2917 
    2918 /**
    2919  * Clear a window's supported/accepted formats list.
    2920  *
    2921  * @param   wndThis                 Window to clear the list for.
    2922  */
    2923 void DragInstance::wndXDnDClearFormatList(Window wndThis) const
    2924 {
    2925     XDeleteProperty(m_pDisplay, wndThis, xAtom(XA_XdndTypeList));
    2926 }
    2927 
    2928 /**
    2929  * Retrieves a window's supported/accepted XDnD actions.
    2930  *
    2931  * @returns IPRT status code.
    2932  * @param   wndThis                 Window to retrieve the XDnD actions for.
    2933  * @param   lstActions              Reference to VBoxDnDAtomList to store the action into.
    2934  */
    2935 int DragInstance::wndXDnDGetActionList(Window wndThis, VBoxDnDAtomList &lstActions) const
    2936 {
    2937     Atom iActType = None;
    2938     int iActFmt;
    2939     unsigned long cItems, cbData;
    2940     unsigned char *pcbData = NULL;
    2941 
    2942     /* Fetch the possible list of actions, if this property is set. */
    2943     int xRc = XGetWindowProperty(m_pDisplay, wndThis,
    2944                                  xAtom(XA_XdndActionList),
    2945                                  0, VBOX_MAX_XPROPERTIES,
    2946                                  False, XA_ATOM, &iActType, &iActFmt, &cItems, &cbData, &pcbData);
    2947     if (xRc != Success)
    2948     {
    2949         LogFlowThisFunc(("Error getting XA_XdndActionList atoms from window=%#x: %s\n",
    2950                          wndThis, gX11->xErrorToString(xRc).c_str()));
    2951         return VERR_NOT_FOUND;
    2952     }
    2953 
    2954     LogFlowThisFunc(("wndThis=%#x, cItems=%RU32, pcbData=%p\n", wndThis, cItems, pcbData));
    2955 
    2956     if (cItems > 0)
    2957     {
    2958         AssertPtr(pcbData);
    2959         Atom *paData = reinterpret_cast<Atom *>(pcbData);
    2960 
    2961         for (unsigned i = 0; i < RT_MIN(VBOX_MAX_XPROPERTIES, cItems); i++)
    2962         {
    2963             LogFlowThisFunc(("\t%s\n", gX11->xAtomToString(paData[i]).c_str()));
    2964             lstActions.append(paData[i]);
    2965         }
    2966 
    2967         XFree(pcbData);
    2968     }
    2969 
    2970     return VINF_SUCCESS;
    2971 }
    2972 
    2973 /**
    2974  * Retrieves a window's supported/accepted XDnD formats.
    2975  *
    2976  * @returns IPRT status code.
    2977  * @param   wndThis                 Window to retrieve the XDnD formats for.
    2978  * @param   lstTypes                Reference to VBoxDnDAtomList to store the formats into.
    2979  */
    2980 int DragInstance::wndXDnDGetFormatList(Window wndThis, VBoxDnDAtomList &lstTypes) const
    2981 {
    2982     Atom iActType = None;
    2983     int iActFmt;
    2984     unsigned long cItems, cbData;
    2985     unsigned char *pcbData = NULL;
    2986 
    2987     int xRc = XGetWindowProperty(m_pDisplay, wndThis,
    2988                              xAtom(XA_XdndTypeList),
    2989                              0, VBOX_MAX_XPROPERTIES,
    2990                              False, XA_ATOM, &iActType, &iActFmt, &cItems, &cbData, &pcbData);
    2991     if (xRc != Success)
    2992     {
    2993         LogFlowThisFunc(("Error getting XA_XdndTypeList atoms from window=%#x: %s\n",
    2994                          wndThis, gX11->xErrorToString(xRc).c_str()));
    2995         return VERR_NOT_FOUND;
    2996     }
    2997 
    2998     LogFlowThisFunc(("wndThis=%#x, cItems=%RU32, pcbData=%p\n", wndThis, cItems, pcbData));
    2999 
    3000     if (cItems > 0)
    3001     {
    3002         AssertPtr(pcbData);
    3003         Atom *paData = reinterpret_cast<Atom *>(pcbData);
    3004 
    3005         for (unsigned i = 0; i < RT_MIN(VBOX_MAX_XPROPERTIES, cItems); i++)
    3006         {
    3007             LogFlowThisFunc(("\t%s\n", gX11->xAtomToString(paData[i]).c_str()));
    3008             lstTypes.append(paData[i]);
    3009         }
    3010 
    3011         XFree(pcbData);
    3012     }
    3013 
    3014     return VINF_SUCCESS;
    3015 }
    3016 
    3017 /**
    3018  * Sets (replaces) a window's XDnD accepted/allowed actions.
    3019  *
    3020  * @returns IPRT status code.
    3021  * @param   wndThis                 Window to set the format list for.
    3022  * @param   lstActions              Reference to list of XDnD actions to set.
    3023  */
    3024 int DragInstance::wndXDnDSetActionList(Window wndThis, const VBoxDnDAtomList &lstActions) const
    3025 {
    3026     if (lstActions.isEmpty())
    3027         return VINF_SUCCESS;
    3028 
    3029     XChangeProperty(m_pDisplay, wndThis,
    3030                     xAtom(XA_XdndActionList),
    3031                     XA_ATOM, 32, PropModeReplace,
    3032                     reinterpret_cast<const unsigned char*>(lstActions.raw()),
    3033                     lstActions.size());
    3034 
    3035     return VINF_SUCCESS;
    3036 }
    3037 
    3038 /**
    3039  * Sets (replaces) a window's XDnD accepted format list.
    3040  *
    3041  * @returns IPRT status code.
    3042  * @param   wndThis                 Window to set the format list for.
    3043  * @param   atmProp                 Property to set.
    3044  * @param   lstFormats              Reference to list of XDnD formats to set.
    3045  */
    3046 int DragInstance::wndXDnDSetFormatList(Window wndThis, Atom atmProp, const VBoxDnDAtomList &lstFormats) const
    3047 {
    3048     if (lstFormats.isEmpty())
    3049         return VERR_INVALID_PARAMETER;
    3050 
    3051     /* Add the property with the property data to the window. */
    3052     XChangeProperty(m_pDisplay, wndThis, atmProp,
    3053                     XA_ATOM, 32, PropModeReplace,
    3054                     reinterpret_cast<const unsigned char*>(lstFormats.raw()),
    3055                     lstFormats.size());
    3056 
    3057     return VINF_SUCCESS;
    3058 }
    3059 
    3060 /**
    3061  * Appends a RTCString list to VBoxDnDAtomList list.
    3062  *
    3063  * @returns IPRT status code.
    3064  * @param   lstFormats              Reference to RTCString list to convert.
    3065  * @param   lstAtoms                Reference to VBoxDnDAtomList list to store results in.
    3066  */
    3067 int DragInstance::appendFormatsToList(const RTCList<RTCString> &lstFormats, VBoxDnDAtomList &lstAtoms) const
    3068 {
    3069     for (size_t i = 0; i < lstFormats.size(); ++i)
    3070         lstAtoms.append(XInternAtom(m_pDisplay, lstFormats.at(i).c_str(), False));
    3071 
    3072     return VINF_SUCCESS;
    3073 }
    3074 
    3075 /**
    3076  * Appends a raw-data string list to VBoxDnDAtomList list.
    3077  *
    3078  * @returns IPRT status code.
    3079  * @param   pvData                  Pointer to string data to convert.
    3080  * @param   cbData                  Size (in bytes) to convert.
    3081  * @param   lstAtoms                Reference to VBoxDnDAtomList list to store results in.
    3082  */
    3083 int DragInstance::appendDataToList(const void *pvData, uint32_t cbData, VBoxDnDAtomList &lstAtoms) const
    3084 {
    3085     RT_NOREF1(lstAtoms);
    3086     AssertPtrReturn(pvData, VERR_INVALID_POINTER);
    3087     AssertReturn(cbData, VERR_INVALID_PARAMETER);
    3088 
    3089     const char *pszStr = (char *)pvData;
    3090     uint32_t cbStr = cbData;
    3091 
    3092     int rc = VINF_SUCCESS;
    3093 
    3094     VBoxDnDAtomList lstAtom;
    3095     while (cbStr)
    3096     {
    3097         size_t cbSize = RTStrNLen(pszStr, cbStr);
    3098 
    3099         /* Create a copy with max N chars, so that we are on the save side,
    3100          * even if the data isn't zero terminated. */
    3101         char *pszTmp = RTStrDupN(pszStr, cbSize);
    3102         if (!pszTmp)
    3103         {
    3104             rc = VERR_NO_MEMORY;
    3105             break;
    3106         }
    3107 
    3108         lstAtom.append(XInternAtom(m_pDisplay, pszTmp, False));
    3109         RTStrFree(pszTmp);
    3110 
    3111         pszStr  += cbSize + 1;
    3112         cbStr   -= cbSize + 1;
    3113     }
    3114 
    3115     return rc;
    3116 }
    3117 
    3118 /**
    3119  * Converts a HGCM-based drag'n drop action to a Atom-based drag'n drop action.
    3120  *
    3121  * @returns Converted Atom-based drag'n drop action.
    3122  * @param   dndAction               HGCM drag'n drop actions to convert.
    3123  */
    3124 /* static */
    3125 Atom DragInstance::toAtomAction(VBOXDNDACTION dndAction)
    3126 {
    3127     /* Ignore is None. */
    3128     return (isDnDCopyAction(dndAction) ? xAtom(XA_XdndActionCopy) :
    3129             isDnDMoveAction(dndAction) ? xAtom(XA_XdndActionMove) :
    3130             isDnDLinkAction(dndAction) ? xAtom(XA_XdndActionLink) :
    3131             None);
    3132 }
    3133 
    3134 /**
    3135  * Converts HGCM-based drag'n drop actions to a VBoxDnDAtomList list.
    3136  *
    3137  * @returns IPRT status code.
    3138  * @param   dndActionList           HGCM drag'n drop actions to convert.
    3139  * @param   lstAtoms                Reference to VBoxDnDAtomList to store actions in.
    3140  */
    3141 /* static */
    3142 int DragInstance::toAtomActions(VBOXDNDACTIONLIST dndActionList, VBoxDnDAtomList &lstAtoms)
    3143 {
    3144     if (hasDnDCopyAction(dndActionList))
    3145         lstAtoms.append(xAtom(XA_XdndActionCopy));
    3146     if (hasDnDMoveAction(dndActionList))
    3147         lstAtoms.append(xAtom(XA_XdndActionMove));
    3148     if (hasDnDLinkAction(dndActionList))
    3149         lstAtoms.append(xAtom(XA_XdndActionLink));
    3150 
    3151     return VINF_SUCCESS;
    3152 }
    3153 
    3154 /**
    3155  * Converts an Atom-based drag'n drop action to a HGCM drag'n drop action.
    3156  *
    3157  * @returns HGCM drag'n drop action.
    3158  * @param   atom                    Atom-based drag'n drop action to convert.
    3159  */
    3160 /* static */
    3161 uint32_t DragInstance::toHGCMAction(Atom atom)
    3162 {
    3163     uint32_t uAction = VBOX_DND_ACTION_IGNORE;
    3164 
    3165     if (atom == xAtom(XA_XdndActionCopy))
    3166         uAction = VBOX_DND_ACTION_COPY;
    3167     else if (atom == xAtom(XA_XdndActionMove))
    3168         uAction = VBOX_DND_ACTION_MOVE;
    3169     else if (atom == xAtom(XA_XdndActionLink))
    3170         uAction = VBOX_DND_ACTION_LINK;
    3171 
    3172     return uAction;
    3173 }
    3174 
    3175 /**
    3176  * Converts an VBoxDnDAtomList list to an HGCM action list.
    3177  *
    3178  * @returns ORed HGCM action list.
    3179  * @param   lstActions              List of Atom-based actions to convert.
    3180  */
    3181 /* static */
    3182 uint32_t DragInstance::toHGCMActions(const VBoxDnDAtomList &lstActions)
    3183 {
    3184     uint32_t uActions = VBOX_DND_ACTION_IGNORE;
    3185 
    3186     for (size_t i = 0; i < lstActions.size(); i++)
    3187         uActions |= toHGCMAction(lstActions.at(i));
    3188 
    3189     return uActions;
    3190 }
    3191 
    3192 /*********************************************************************************************************************************
    3193  * VBoxDnDProxyWnd implementation.                                                                                               *
    3194  ********************************************************************************************************************************/
    3195 
    3196 VBoxDnDProxyWnd::VBoxDnDProxyWnd(void)
    3197     : pDisp(NULL)
    3198     , hWnd(0)
    3199     , iX(0)
    3200     , iY(0)
    3201     , iWidth(0)
    3202     , iHeight(0)
    3203 {
    3204 
    3205 }
    3206 
    3207 VBoxDnDProxyWnd::~VBoxDnDProxyWnd(void)
    3208 {
    3209     destroy();
    3210 }
    3211 
    3212 int VBoxDnDProxyWnd::init(Display *pDisplay)
    3213 {
    3214     /** @todo What about multiple screens? Test this! */
    3215     int iScreenID = XDefaultScreen(pDisplay);
    3216 
    3217     iWidth   = XDisplayWidth(pDisplay, iScreenID);
    3218     iHeight  = XDisplayHeight(pDisplay, iScreenID);
    3219     pDisp    = pDisplay;
    3220 
    3221     return VINF_SUCCESS;
    3222 }
    3223 
    3224 void VBoxDnDProxyWnd::destroy(void)
    3225 {
    3226 
    3227 }
    3228 
    3229 int VBoxDnDProxyWnd::sendFinished(Window hWndSource, VBOXDNDACTION dndAction)
    3230 {
    3231     /* Was the drop accepted by the host? That is, anything than ignoring. */
    3232     bool fDropAccepted = dndAction > VBOX_DND_ACTION_IGNORE;
    3233 
    3234     LogFlowFunc(("dndAction=0x%x\n", dndAction));
    3235 
    3236     /* Confirm the result of the transfer to the target window. */
    3237     XClientMessageEvent m;
    3238     RT_ZERO(m);
    3239     m.type         = ClientMessage;
    3240     m.display      = pDisp;
    3241     m.window       = hWnd;
    3242     m.message_type = xAtom(XA_XdndFinished);
    3243     m.format       = 32;
    3244     m.data.l[XdndFinishedWindow] = hWnd;                                                         /* Target window. */
    3245     m.data.l[XdndFinishedFlags]  = fDropAccepted ? RT_BIT(0) : 0;                                /* Was the drop accepted? */
    3246     m.data.l[XdndFinishedAction] = fDropAccepted ? DragInstance::toAtomAction(dndAction) : None; /* Action used on accept. */
    3247 
    3248     int xRc = XSendEvent(pDisp, hWndSource, True, NoEventMask, reinterpret_cast<XEvent*>(&m));
    3249     if (xRc == 0)
    3250     {
    3251         VBClLogError("Error sending finished event to source window=%#x: %s\n",
    3252                       hWndSource, gX11->xErrorToString(xRc).c_str());
    3253 
    3254         return VERR_GENERAL_FAILURE; /** @todo Fudge. */
    3255     }
    3256 
    3257     return VINF_SUCCESS;
    3258 }
    3259 
    3260 /*********************************************************************************************************************************
    3261  * DragAndDropService implementation.                                                                                            *
    3262  ********************************************************************************************************************************/
    3263 
    3264 /** @copydoc VBCLSERVICE::pfnInit */
    3265 int DragAndDropService::init(void)
    3266 {
    3267     LogFlowFuncEnter();
    3268 
    3269     /* Connect to the x11 server. */
    3270     m_pDisplay = XOpenDisplay(NULL);
    3271     if (!m_pDisplay)
    3272     {
    3273         VBClLogFatalError("Unable to connect to X server -- running in a terminal session?\n");
    3274         return VERR_NOT_FOUND;
    3275     }
    3276 
    3277     xHelpers *pHelpers = xHelpers::getInstance(m_pDisplay);
    3278     if (!pHelpers)
    3279         return VERR_NO_MEMORY;
    3280 
    3281     int rc;
    3282 
    3283     do
    3284     {
    3285         rc = RTSemEventCreate(&m_hEventSem);
    3286         AssertRCBreak(rc);
    3287 
    3288         rc = RTCritSectInit(&m_eventQueueCS);
    3289         AssertRCBreak(rc);
    3290 
    3291         rc = VbglR3DnDConnect(&m_dndCtx);
    3292         AssertRCBreak(rc);
    3293 
    3294         /* Event thread for events coming from the HGCM device. */
    3295         rc = RTThreadCreate(&m_hHGCMThread, hgcmEventThread, this,
    3296                             0, RTTHREADTYPE_MSG_PUMP, RTTHREADFLAGS_WAITABLE, "dndHGCM");
    3297         AssertRCBreak(rc);
    3298 
    3299         rc = RTThreadUserWait(m_hHGCMThread, RT_MS_30SEC);
    3300         AssertRCBreak(rc);
    3301 
    3302         if (ASMAtomicReadBool(&m_fStop))
    3303             break;
    3304 
    3305         /* Event thread for events coming from the x11 system. */
    3306         rc = RTThreadCreate(&m_hX11Thread, x11EventThread, this,
    3307                             0, RTTHREADTYPE_MSG_PUMP, RTTHREADFLAGS_WAITABLE, "dndX11");
    3308         AssertRCBreak(rc);
    3309 
    3310         rc = RTThreadUserWait(m_hX11Thread, RT_MS_30SEC);
    3311         AssertRCBreak(rc);
    3312 
    3313         if (ASMAtomicReadBool(&m_fStop))
    3314             break;
    3315 
    3316     } while (0);
    3317 
    3318     if (m_fStop)
    3319         rc = VERR_GENERAL_FAILURE; /** @todo Fudge! */
    3320 
    3321     if (RT_FAILURE(rc))
    3322         VBClLogError("Failed to initialize, rc=%Rrc\n", rc);
    3323 
    3324     LogFlowFuncLeaveRC(rc);
    3325     return rc;
    3326 }
    3327 
    3328 /** @copydoc VBCLSERVICE::pfnWorker */
    3329 int DragAndDropService::worker(bool volatile *pfShutdown)
    3330 {
    3331     int rc;
    3332     do
    3333     {
    3334         m_pCurDnD = new DragInstance(m_pDisplay, this);
    3335         if (!m_pCurDnD)
    3336         {
    3337             rc = VERR_NO_MEMORY;
    3338             break;
    3339         }
    3340 
    3341         /* Note: For multiple screen support in VBox it is not necessary to use
    3342          * another screen number than zero. Maybe in the future it will become
    3343          * necessary if VBox supports multiple X11 screens. */
    3344         rc = m_pCurDnD->init(0 /* uScreenID */);
    3345         /* Note: Can return VINF_PERMISSION_DENIED if HGCM host service is not available. */
    3346         if (rc != VINF_SUCCESS)
    3347         {
    3348             if (RT_FAILURE(rc))
    3349                 VBClLogError("Unable to connect to drag and drop service, rc=%Rrc\n", rc);
    3350             else if (rc == VINF_PERMISSION_DENIED) /* No error, DnD might be just disabled. */
    3351                 VBClLogInfo("Not available on host, terminating\n");
    3352             break;
    3353         }
    3354 
    3355         /* Let the main thread know that it can continue spawning services. */
    3356         RTThreadUserSignal(RTThreadSelf());
    3357 
    3358         /* Enter the main event processing loop. */
    3359         do
    3360         {
    3361             DNDEVENT e;
    3362             RT_ZERO(e);
    3363 
    3364             LogFlowFunc(("Waiting for new events ...\n"));
    3365             rc = RTSemEventWait(m_hEventSem, RT_INDEFINITE_WAIT);
    3366             if (RT_FAILURE(rc))
    3367                 break;
    3368 
    3369             size_t cEvents = 0;
    3370 
    3371             int rc2 = RTCritSectEnter(&m_eventQueueCS);
    3372             if (RT_SUCCESS(rc2))
    3373             {
    3374                 cEvents = m_eventQueue.size();
    3375 
    3376                 rc2 = RTCritSectLeave(&m_eventQueueCS);
    3377                 AssertRC(rc2);
    3378             }
    3379 
    3380             while (cEvents)
    3381             {
    3382                 rc2 = RTCritSectEnter(&m_eventQueueCS);
    3383                 if (RT_SUCCESS(rc2))
    3384                 {
    3385                     if (m_eventQueue.isEmpty())
    3386                     {
    3387                         rc2 = RTCritSectLeave(&m_eventQueueCS);
    3388                         AssertRC(rc2);
    3389                         break;
    3390                     }
    3391 
    3392                     e = m_eventQueue.first();
    3393                     m_eventQueue.removeFirst();
    3394 
    3395                     rc2 = RTCritSectLeave(&m_eventQueueCS);
    3396                     AssertRC(rc2);
    3397                 }
    3398 
    3399                 if (e.enmType == DNDEVENT::DnDEventType_HGCM)
    3400                 {
    3401                     PVBGLR3DNDEVENT pVbglR3Event = e.hgcm;
    3402                     AssertPtrBreak(pVbglR3Event);
    3403 
    3404                     LogFlowThisFunc(("HGCM event enmType=%RU32\n", pVbglR3Event->enmType));
    3405                     switch (pVbglR3Event->enmType)
    3406                     {
    3407                         case VBGLR3DNDEVENTTYPE_HG_ENTER:
    3408                         {
    3409                             if (pVbglR3Event->u.HG_Enter.cbFormats)
    3410                             {
    3411                                 RTCList<RTCString> lstFormats =
    3412                                     RTCString(pVbglR3Event->u.HG_Enter.pszFormats, pVbglR3Event->u.HG_Enter.cbFormats - 1).split(DND_PATH_SEPARATOR_STR);
    3413                                 rc = m_pCurDnD->hgEnter(lstFormats, pVbglR3Event->u.HG_Enter.dndLstActionsAllowed);
    3414                                 if (RT_FAILURE(rc))
    3415                                     break;
    3416                                 /* Enter is always followed by a move event. */
    3417                             }
    3418                             else
    3419                             {
    3420                                 AssertMsgFailed(("cbFormats is 0\n"));
    3421                                 rc = VERR_INVALID_PARAMETER;
    3422                                 break;
    3423                             }
    3424 
    3425                             /* Note: After HOST_DND_FN_HG_EVT_ENTER there immediately is a move
    3426                              *       event, so fall through is intentional here. */
    3427                             RT_FALL_THROUGH();
    3428                         }
    3429 
    3430                         case VBGLR3DNDEVENTTYPE_HG_MOVE:
    3431                         {
    3432                             rc = m_pCurDnD->hgMove(pVbglR3Event->u.HG_Move.uXpos, pVbglR3Event->u.HG_Move.uYpos,
    3433                                                    pVbglR3Event->u.HG_Move.dndActionDefault);
    3434                             break;
    3435                         }
    3436 
    3437                         case VBGLR3DNDEVENTTYPE_HG_LEAVE:
    3438                         {
    3439                             rc = m_pCurDnD->hgLeave();
    3440                             break;
    3441                         }
    3442 
    3443                         case VBGLR3DNDEVENTTYPE_HG_DROP:
    3444                         {
    3445                             rc = m_pCurDnD->hgDrop(pVbglR3Event->u.HG_Drop.uXpos, pVbglR3Event->u.HG_Drop.uYpos,
    3446                                                    pVbglR3Event->u.HG_Drop.dndActionDefault);
    3447                             break;
    3448                         }
    3449 
    3450                         /* Note: VbglR3DnDRecvNextMsg() will return HOST_DND_FN_HG_SND_DATA_HDR when
    3451                          *       the host has finished copying over all the data to the guest.
    3452                          *
    3453                          *       The actual data transfer (and message processing for it) will be done
    3454                          *       internally by VbglR3DnDRecvNextMsg() to not duplicate any code for different
    3455                          *       platforms.
    3456                          *
    3457                          *       The data header now will contain all the (meta) data the guest needs in
    3458                          *       order to complete the DnD operation. */
    3459                         case VBGLR3DNDEVENTTYPE_HG_RECEIVE:
    3460                         {
    3461                             rc = m_pCurDnD->hgDataReceive(&pVbglR3Event->u.HG_Received.Meta);
    3462                             break;
    3463                         }
    3464 
    3465                         case VBGLR3DNDEVENTTYPE_CANCEL:
    3466                         {
    3467                             m_pCurDnD->reset();
    3468                             break;
    3469                         }
    3470 
    3471 #ifdef VBOX_WITH_DRAG_AND_DROP_GH
    3472                         case VBGLR3DNDEVENTTYPE_GH_ERROR:
    3473                         {
    3474                             m_pCurDnD->reset();
    3475                             break;
    3476                         }
    3477 
    3478                         case VBGLR3DNDEVENTTYPE_GH_REQ_PENDING:
    3479                         {
    3480                             rc = m_pCurDnD->ghIsDnDPending();
    3481                             break;
    3482                         }
    3483 
    3484                         case VBGLR3DNDEVENTTYPE_GH_DROP:
    3485                         {
    3486                             rc = m_pCurDnD->ghDropped(pVbglR3Event->u.GH_Drop.pszFormat, pVbglR3Event->u.GH_Drop.dndActionRequested);
    3487                             break;
    3488                         }
    3489 #endif
    3490                         case VBGLR3DNDEVENTTYPE_QUIT:
    3491                         {
    3492                             rc = VINF_SUCCESS;
    3493                             break;
    3494                         }
    3495 
    3496                         default:
    3497                         {
    3498                             VBClLogError("Received unsupported message type %RU32\n", pVbglR3Event->enmType);
    3499                             rc = VERR_NOT_SUPPORTED;
    3500                             break;
    3501                         }
    3502                     }
    3503 
    3504                     LogFlowFunc(("Message %RU32 processed with %Rrc\n", pVbglR3Event->enmType, rc));
    3505                     if (RT_FAILURE(rc))
    3506                     {
    3507                         /* Tell the user. */
    3508                         VBClLogError("Processing message %RU32 failed with %Rrc\n", pVbglR3Event->enmType, rc);
    3509 
    3510                         /* If anything went wrong, do a reset and start over. */
    3511                         reset();
    3512                     }
    3513 
    3514                     const bool fQuit = pVbglR3Event->enmType == VBGLR3DNDEVENTTYPE_QUIT;
    3515 
    3516                     VbglR3DnDEventFree(e.hgcm);
    3517                     e.hgcm = NULL;
    3518 
    3519                     if (fQuit)
    3520                         break;
    3521                 }
    3522                 else if (e.enmType == DNDEVENT::DnDEventType_X11)
    3523                 {
    3524                     LogFlowThisFunc(("X11 event (type %#x)\n", e.x11.type));
    3525                     m_pCurDnD->onX11Event(e.x11);
    3526                 }
    3527                 else
    3528                     AssertMsgFailed(("Unknown event queue type %RU32\n", e.enmType));
    3529 
    3530                 --cEvents;
    3531 
    3532             } /* for */
    3533 
    3534             /*
    3535              * Make sure that any X11 requests have actually been sent to the
    3536              * server, since we are waiting for responses using poll() on
    3537              * another thread which will not automatically trigger flushing.
    3538              */
    3539             XFlush(m_pDisplay);
    3540 
    3541             if (m_fStop)
    3542                 break;
    3543 
    3544         } while (!ASMAtomicReadBool(pfShutdown));
    3545 
    3546     } while (0);
    3547 
    3548     if (m_pCurDnD)
    3549     {
    3550         delete m_pCurDnD;
    3551         m_pCurDnD = NULL;
    3552     }
    3553 
    3554     LogFlowFuncLeaveRC(rc);
    3555     return rc;
    3556 }
    3557 
    3558 /**
    3559  * Resets the DnD service' data.
    3560  */
    3561 void DragAndDropService::reset(void)
    3562 {
    3563     LogFlowFuncEnter();
    3564 
    3565     if (m_pCurDnD)
    3566         m_pCurDnD->reset();
    3567 
    3568     /*
    3569      * Clear the event queue.
    3570      */
    3571     int rc2 = RTCritSectEnter(&m_eventQueueCS);
    3572     if (RT_SUCCESS(rc2))
    3573     {
    3574         for (size_t i = 0; i < m_eventQueue.size(); i++)
    3575         {
    3576             switch (m_eventQueue[i].enmType)
    3577             {
    3578                 case DNDEVENT::DnDEventType_HGCM:
    3579                 {
    3580                     VbglR3DnDEventFree(m_eventQueue[i].hgcm);
    3581                     break;
    3582                 }
    3583 
    3584                 default:
    3585                     break;
    3586             }
    3587 
    3588         }
    3589 
    3590         m_eventQueue.clear();
    3591 
    3592         rc2 = RTCritSectLeave(&m_eventQueueCS);
    3593         AssertRC(rc2);
    3594     }
    3595 
    3596     LogFlowFuncLeave();
    3597 }
    3598 
    3599 /** @copydoc VBCLSERVICE::pfnStop */
    3600 void DragAndDropService::stop(void)
    3601 {
    3602     LogFlowFuncEnter();
    3603 
    3604     /* Set stop flag first. */
    3605     ASMAtomicXchgBool(&m_fStop, true);
    3606 
    3607     /* First, disconnect any instances. */
    3608     if (m_pCurDnD)
    3609         m_pCurDnD->stop();
    3610 
    3611     /* Second, disconnect the service's DnD connection. */
    3612     VbglR3DnDDisconnect(&m_dndCtx);
    3613 
    3614     LogFlowFuncLeave();
    3615 }
    3616 
    3617 /** @copydoc VBCLSERVICE::pfnTerm */
    3618 int DragAndDropService::term(void)
    3619 {
    3620     int rc = VINF_SUCCESS;
    3621 
    3622     /*
    3623      * Wait for threads to terminate.
    3624      */
    3625     int rcThread;
    3626 
    3627     if (m_hX11Thread != NIL_RTTHREAD)
    3628     {
    3629         VBClLogVerbose(2, "Terminating X11 thread ...\n");
    3630 
    3631         int rc2 = RTThreadWait(m_hX11Thread, RT_MS_30SEC, &rcThread);
    3632         if (RT_SUCCESS(rc2))
    3633             rc2 = rcThread;
    3634 
    3635         if (RT_FAILURE(rc2))
    3636             VBClLogError("Error waiting for X11 thread to terminate: %Rrc\n", rc2);
    3637 
    3638         if (RT_SUCCESS(rc))
    3639             rc = rc2;
    3640 
    3641         m_hX11Thread = NIL_RTTHREAD;
    3642 
    3643         VBClLogVerbose(2, "X11 thread terminated\n");
    3644     }
    3645 
    3646     if (m_hHGCMThread != NIL_RTTHREAD)
    3647     {
    3648         VBClLogVerbose(2, "Terminating HGCM thread ...\n");
    3649 
    3650         int rc2 = RTThreadWait(m_hHGCMThread, RT_MS_30SEC, &rcThread);
    3651         if (RT_SUCCESS(rc2))
    3652             rc2 = rcThread;
    3653 
    3654         if (RT_FAILURE(rc2))
    3655             VBClLogError("Error waiting for HGCM thread to terminate: %Rrc\n", rc2);
    3656 
    3657         if (RT_SUCCESS(rc))
    3658             rc = rc2;
    3659 
    3660         m_hHGCMThread = NIL_RTTHREAD;
    3661 
    3662         VBClLogVerbose(2, "HGCM thread terminated\n");
    3663     }
    3664 
    3665     reset();
    3666 
    3667     if (m_pCurDnD)
    3668     {
    3669         delete m_pCurDnD;
    3670         m_pCurDnD = NULL;
    3671     }
    3672 
    3673     xHelpers::destroyInstance();
    3674 
    3675     return rc;
    3676 }
    3677 
    3678 /**
    3679  * Static callback function for HGCM message processing thread. An internal
    3680  * message queue will be filled which then will be processed by the according
    3681  * drag'n drop instance.
    3682  *
    3683  * @returns IPRT status code.
    3684  * @param   hThread                 Thread handle to use.
    3685  * @param   pvUser                  Pointer to DragAndDropService instance to use.
    3686  */
    3687 /* static */
    3688 DECLCALLBACK(int) DragAndDropService::hgcmEventThread(RTTHREAD hThread, void *pvUser)
    3689 {
    3690     AssertPtrReturn(pvUser, VERR_INVALID_PARAMETER);
    3691     DragAndDropService *pThis = static_cast<DragAndDropService*>(pvUser);
    3692 
    3693     /* Let the service instance know in any case. */
    3694     int rc = RTThreadUserSignal(hThread);
    3695     AssertRCReturn(rc, rc);
    3696 
    3697     VBClLogVerbose(2, "HGCM thread started\n");
    3698 
    3699     /* Number of invalid messages skipped in a row. */
    3700     int cMsgSkippedInvalid = 0;
    3701     DNDEVENT e;
    3702 
    3703     do
    3704     {
    3705         RT_ZERO(e);
    3706         e.enmType = DNDEVENT::DnDEventType_HGCM;
    3707 
    3708         /* Wait for new events. */
    3709         rc = VbglR3DnDEventGetNext(&pThis->m_dndCtx, &e.hgcm);
    3710         if (RT_SUCCESS(rc))
    3711         {
    3712             cMsgSkippedInvalid = 0; /* Reset skipped messages count. */
    3713 
    3714             int rc2 = RTCritSectEnter(&pThis->m_eventQueueCS);
    3715             if (RT_SUCCESS(rc2))
    3716             {
    3717                 VBClLogVerbose(2, "Received new HGCM message (type %#x)\n", e.hgcm->enmType);
    3718 
    3719                 pThis->m_eventQueue.append(e);
    3720 
    3721                 rc2 = RTCritSectLeave(&pThis->m_eventQueueCS);
    3722                 AssertRC(rc2);
    3723             }
    3724 
    3725             rc = RTSemEventSignal(pThis->m_hEventSem);
    3726             if (RT_FAILURE(rc))
    3727                 break;
    3728         }
    3729         else
    3730         {
    3731             VBClLogError("Processing next message failed with rc=%Rrc\n", rc);
    3732 
    3733             /* Old(er) hosts either are broken regarding DnD support or otherwise
    3734              * don't support the stuff we do on the guest side, so make sure we
    3735              * don't process invalid messages forever. */
    3736 
    3737             if (cMsgSkippedInvalid++ > 32)
    3738             {
    3739                 VBClLogError("Too many invalid/skipped messages from host, exiting ...\n");
    3740                 break;
    3741             }
    3742         }
    3743 
    3744     } while (!ASMAtomicReadBool(&pThis->m_fStop));
    3745 
    3746     VBClLogVerbose(2, "HGCM thread ended\n");
    3747 
    3748     LogFlowFuncLeaveRC(rc);
    3749     return rc;
    3750 }
    3751 
    3752 /**
    3753  * Static callback function for X11 message processing thread. All X11 messages
    3754  * will be directly routed to the according drag'n drop instance.
    3755  *
    3756  * @returns IPRT status code.
    3757  * @param   hThread                 Thread handle to use.
    3758  * @param   pvUser                  Pointer to DragAndDropService instance to use.
    3759  */
    3760 /* static */
    3761 DECLCALLBACK(int) DragAndDropService::x11EventThread(RTTHREAD hThread, void *pvUser)
    3762 {
    3763     AssertPtrReturn(pvUser, VERR_INVALID_PARAMETER);
    3764     DragAndDropService *pThis = static_cast<DragAndDropService*>(pvUser);
    3765     AssertPtr(pThis);
    3766 
    3767     int rc = VINF_SUCCESS;
    3768 
    3769     /* Note: Nothing to initialize here (yet). */
    3770 
    3771     /* Let the service instance know in any case. */
    3772     int rc2 = RTThreadUserSignal(hThread);
    3773     AssertRC(rc2);
    3774 
    3775     VBClLogVerbose(2, "X11 thread started\n");
    3776 
    3777     DNDEVENT e;
    3778     RT_ZERO(e);
    3779     e.enmType = DNDEVENT::DnDEventType_X11;
    3780 
    3781     do
    3782     {
    3783         /*
    3784          * Wait for new events. We can't use XIfEvent here, cause this locks
    3785          * the window connection with a mutex and if no X11 events occurs this
    3786          * blocks any other calls we made to X11. So instead check for new
    3787          * events and if there are not any new one, sleep for a certain amount
    3788          * of time.
    3789          */
    3790         unsigned cNewEvents = 0;
    3791         unsigned cQueued    = XEventsQueued(pThis->m_pDisplay, QueuedAfterFlush);
    3792         while (cQueued)
    3793         {
    3794             /* XNextEvent will block until a new X event becomes available. */
    3795             XNextEvent(pThis->m_pDisplay, &e.x11);
    3796             {
    3797                 rc2 = RTCritSectEnter(&pThis->m_eventQueueCS);
    3798                 if (RT_SUCCESS(rc2))
    3799                 {
    3800                     LogFlowFunc(("Added new X11 event, type=%d\n", e.x11.type));
    3801 
    3802                     pThis->m_eventQueue.append(e);
    3803                     cNewEvents++;
    3804 
    3805                     rc2 = RTCritSectLeave(&pThis->m_eventQueueCS);
    3806                     AssertRC(rc2);
    3807                 }
    3808             }
    3809 
    3810             cQueued--;
    3811         }
    3812 
    3813         if (cNewEvents)
    3814         {
    3815             rc = RTSemEventSignal(pThis->m_hEventSem);
    3816             if (RT_FAILURE(rc))
    3817                 break;
    3818 
    3819             continue;
    3820         }
    3821 
    3822         /* No new events; wait a bit. */
    3823         RTThreadSleep(25 /* ms */);
    3824 
    3825     } while (!ASMAtomicReadBool(&pThis->m_fStop));
    3826 
    3827     VBClLogVerbose(2, "X11 thread ended\n");
    3828 
    3829     LogFlowFuncLeaveRC(rc);
    3830     return rc;
    3831 }
    383239/**
    383340 * @interface_method_impl{VBCLSERVICE,pfnInit}
     
    383542static DECLCALLBACK(int) vbclDnDInit(void)
    383643{
    3837     return g_Svc.init();
     44    switch (VBClGetSessionType())
     45    {
     46        case VBGHSESSIONTYPE_X11:
     47            g_pSvc = new VBClX11DnDSvc();
     48            break;
     49
     50        case VBGHSESSIONTYPE_WAYLAND:
     51            RT_FALL_THROUGH();
     52        default:
     53            return VERR_NOT_SUPPORTED;
     54    }
     55
     56    if (!g_pSvc)
     57        return VERR_NO_MEMORY;
     58
     59    return g_pSvc->init();
    383860}
    383961
     
    384365static DECLCALLBACK(int) vbclDnDWorker(bool volatile *pfShutdown)
    384466{
    3845     return g_Svc.worker(pfShutdown);
     67    return g_pSvc->worker(pfShutdown);
    384668}
    384769
     
    385173static DECLCALLBACK(void) vbclDnDStop(void)
    385274{
    3853     g_Svc.stop();
     75    g_pSvc->stop();
    385476}
    385577
     
    385981static DECLCALLBACK(int) vbclDnDTerm(void)
    386082{
    3861     return g_Svc.term();
     83    return g_pSvc->term();
    386284}
    386385
Note: See TracChangeset for help on using the changeset viewer.

© 2024 Oracle Support Privacy / Do Not Sell My Info Terms of Use Trademark Policy Automated Access Etiquette