Changeset 99595 in vbox
- Timestamp:
- May 4, 2023 8:58:54 AM (19 months ago)
- 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 142 142 $(if $(VBOX_WITH_DRAG_AND_DROP_GH),VBOX_WITH_DRAG_AND_DROP_GH,) 143 143 VBoxClient_SOURCES += \ 144 draganddrop.cpp 144 draganddrop.cpp \ 145 draganddrop-x11.cpp 145 146 VBoxClient_LIBS += \ 146 147 $(VBOX_LIB_VBGL_R3) \ -
trunk/src/VBox/Additions/x11/VBoxClient/draganddrop-x11.cpp
r99557 r99595 1 1 /* $Id$ */ 2 2 /** @file 3 * X11 guest client - Drag anddrop implementation.3 * Guest Additions - VBoxClient X11 drag'n drop implementation. 4 4 */ 5 5 … … 54 54 #include "VBox/HostServices/DragAndDropSvc.h" 55 55 #include "VBoxClient.h" 56 #include "draganddrop.h" 56 57 57 58 … … 132 133 /** The notification header text for VBClShowNotify(). */ 133 134 #define VBOX_DND_SHOWNOTIFY_HEADER VBOX_PRODUCT " Drag'n Drop" 134 135 /**136 * Structure for storing new X11 events and HGCM messages137 * into a single event queue.138 */139 typedef struct DNDEVENT140 {141 enum DnDEventType142 {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 = 0x7fffffff151 };152 /** Event type. */153 DnDEventType enmType;154 union155 {156 PVBGLR3DNDEVENT hgcm;157 XEvent x11;158 };159 #ifdef IN_GUEST160 RTMEM_IMPLEMENT_NEW_AND_DELETE();161 #endif162 } DNDEVENT;163 /** Pointer to a DnD event. */164 typedef DNDEVENT *PDNDEVENT;165 135 166 136 enum XA_Type … … 259 229 } XdndMsg; 260 230 261 class DragAndDropService;231 class VBClX11DnDSvc; 262 232 263 233 /** List of Atoms. */ 264 234 #define VBoxDnDAtomList RTCList<Atom> 265 235 266 class xHelpers236 class VBClX11DnDXHelpers 267 237 { 268 238 public: 269 239 270 static xHelpers *getInstance(Display *pDisplay = 0)240 static VBClX11DnDXHelpers *getInstance(Display *pDisplay = 0) 271 241 { 272 242 if (!m_pInstance) 273 243 { 274 244 AssertPtrReturn(pDisplay, NULL); 275 m_pInstance = new xHelpers(pDisplay);245 m_pInstance = new VBClX11DnDXHelpers(pDisplay); 276 246 } 277 247 … … 335 305 RTMEM_IMPLEMENT_NEW_AND_DELETE(); 336 306 #endif 337 xHelpers(Display *pDisplay)307 VBClX11DnDXHelpers(Display *pDisplay) 338 308 : m_pDisplay(pDisplay) 339 309 { … … 345 315 346 316 /* Private member vars */ 347 static xHelpers *m_pInstance;317 static VBClX11DnDXHelpers *m_pInstance; 348 318 Display *m_pDisplay; 349 319 Atom m_xAtoms[XA_End]; … … 352 322 353 323 /* 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)) 357 327 358 328 /********************************************************************************************************************************* … … 360 330 ********************************************************************************************************************************/ 361 331 362 xHelpers *xHelpers::m_pInstance = NULL;332 VBClX11DnDXHelpers *VBClX11DnDXHelpers::m_pInstance = NULL; 363 333 364 334 /* Has to be in sync with the XA_Type enum. */ 365 const char * xHelpers::m_xAtomNames[] =335 const char *VBClX11DnDXHelpers::m_xAtomNames[] = 366 336 { 367 337 /* States */ … … 398 368 }; 399 369 400 RTCString xHelpers::xErrorToString(int xRc) const370 RTCString VBClX11DnDXHelpers::xErrorToString(int xRc) const 401 371 { 402 372 switch (xRc) … … 425 395 426 396 /** @todo Make this iterative. */ 427 Window xHelpers::applicationWindowBelowCursor(Window wndParent) const397 Window VBClX11DnDXHelpers::applicationWindowBelowCursor(Window wndParent) const 428 398 { 429 399 /* No parent, nothing to do. */ … … 480 450 ** @todo Move all proxy window-related stuff into this class! Clean up this mess. 481 451 */ 482 class VB oxDnDProxyWnd452 class VBClX11DnDProxyWnd 483 453 { 484 454 … … 487 457 RTMEM_IMPLEMENT_NEW_AND_DELETE(); 488 458 #endif 489 VB oxDnDProxyWnd(void);490 virtual ~VB oxDnDProxyWnd(void);459 VBClX11DnDProxyWnd(void); 460 virtual ~VBClX11DnDProxyWnd(void); 491 461 492 462 public: … … 509 479 510 480 /** This class only serve to avoid dragging in generic new() and delete(). */ 511 class WrappedXEvent481 class VBClX11DnDWrappedXEvent 512 482 { 513 483 public: … … 518 488 RTMEM_IMPLEMENT_NEW_AND_DELETE(); 519 489 #endif 520 WrappedXEvent(const XEvent &a_rSrcEvent)490 VBClX11DnDWrappedXEvent(const XEvent &a_rSrcEvent) 521 491 { 522 492 m_Event = a_rSrcEvent; 523 493 } 524 494 525 WrappedXEvent()495 VBClX11DnDWrappedXEvent() 526 496 { 527 497 RT_ZERO(m_Event); 528 498 } 529 499 530 WrappedXEvent &operator=(const XEvent &a_rSrcEvent)500 VBClX11DnDWrappedXEvent &operator=(const XEvent &a_rSrcEvent) 531 501 { 532 502 m_Event = a_rSrcEvent; … … 541 511 * For now only one DragInstance will exits when the app is running. 542 512 */ 543 class DragInstance513 class VBClX11DnDInst 544 514 { 545 515 public: … … 565 535 RTMEM_IMPLEMENT_NEW_AND_DELETE(); 566 536 #endif 567 DragInstance(Display *pDisplay, DragAndDropService*pParent);537 VBClX11DnDInst(Display *pDisplay, VBClX11DnDSvc *pParent); 568 538 569 539 public: … … 632 602 VBGLR3GUESTDNDCMDCTX m_dndCtx; 633 603 /** Pointer to service instance. */ 634 DragAndDropService*m_pParent;604 VBClX11DnDSvc *m_pParent; 635 605 /** Pointer to X display operating on. */ 636 606 Display *m_pDisplay; … … 642 612 Window m_wndRoot; 643 613 /** Proxy window. */ 644 VB oxDnDProxyWndm_wndProxy;614 VBClX11DnDProxyWnd m_wndProxy; 645 615 /** Current source/target window handle. */ 646 616 Window m_wndCur; … … 670 640 volatile uint32_t m_enmState; 671 641 /** The instance's own X event queue. */ 672 RTCMTList< WrappedXEvent> m_eventQueueList;642 RTCMTList<VBClX11DnDWrappedXEvent> m_eventQueueList; 673 643 /** Critical section for providing serialized access to list event queue's contents. */ 674 644 RTCRITSECT m_eventQueueCS; … … 684 654 }; 685 655 686 /**687 * Service class which implements drag'n drop.688 */689 class DragAndDropService690 {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 list719 * 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 656 738 657 /********************************************************************************************************************************* … … 740 659 ********************************************************************************************************************************/ 741 660 742 DragInstance::DragInstance(Display *pDisplay, DragAndDropService*pParent)661 VBClX11DnDInst::VBClX11DnDInst(Display *pDisplay, VBClX11DnDSvc *pParent) 743 662 : m_pParent(pParent) 744 663 , m_pDisplay(pDisplay) … … 762 681 * Stops this drag instance. 763 682 */ 764 void DragInstance::stop(void)683 void VBClX11DnDInst::stop(void) 765 684 { 766 685 LogFlowFuncEnter(); … … 777 696 * @return VBox status code. 778 697 */ 779 int DragInstance::term(void)698 int VBClX11DnDInst::term(void) 780 699 { 781 700 LogFlowFuncEnter(); … … 806 725 * Resets this drag instance. 807 726 */ 808 void DragInstance::reset(void)727 void VBClX11DnDInst::reset(void) 809 728 { 810 729 LogFlowFuncEnter(); … … 871 790 * @param uScreenID X' screen ID to use. 872 791 */ 873 int DragInstance::init(uint32_t uScreenID)792 int VBClX11DnDInst::init(uint32_t uScreenID) 874 793 { 875 794 int rc = VbglR3DnDConnect(&m_dndCtx); … … 1016 935 * @param e X11 event to handle. 1017 936 */ 1018 int DragInstance::onX11ClientMessage(const XEvent &e)937 int VBClX11DnDInst::onX11ClientMessage(const XEvent &e) 1019 938 { 1020 939 AssertReturn(e.type == ClientMessage, VERR_INVALID_PARAMETER); … … 1331 1250 } 1332 1251 1333 int DragInstance::onX11MotionNotify(const XEvent &e)1252 int VBClX11DnDInst::onX11MotionNotify(const XEvent &e) 1334 1253 { 1335 1254 RT_NOREF1(e); … … 1348 1267 * @remark 1349 1268 */ 1350 int DragInstance::onX11SelectionClear(const XEvent &e)1269 int VBClX11DnDInst::onX11SelectionClear(const XEvent &e) 1351 1270 { 1352 1271 RT_NOREF1(e); … … 1364 1283 * @param e X11 event to handle. 1365 1284 */ 1366 int DragInstance::onX11SelectionNotify(const XEvent &e)1285 int VBClX11DnDInst::onX11SelectionNotify(const XEvent &e) 1367 1286 { 1368 1287 AssertReturn(e.type == SelectionNotify, VERR_INVALID_PARAMETER); … … 1406 1325 * @param evReq X11 event to handle. 1407 1326 */ 1408 int DragInstance::onX11SelectionRequest(const XEvent &evReq)1327 int VBClX11DnDInst::onX11SelectionRequest(const XEvent &evReq) 1409 1328 { 1410 1329 AssertReturn(evReq.type == SelectionRequest, VERR_INVALID_PARAMETER); … … 1566 1485 * @param e X11 event to handle. 1567 1486 */ 1568 int DragInstance::onX11Event(const XEvent &e)1487 int VBClX11DnDInst::onX11Event(const XEvent &e) 1569 1488 { 1570 1489 int rc; … … 1619 1538 } 1620 1539 1621 int DragInstance::waitForStatusChange(uint32_t enmState, RTMSINTERVAL uTimeoutMS /* = 30000 */)1540 int VBClX11DnDInst::waitForStatusChange(uint32_t enmState, RTMSINTERVAL uTimeoutMS /* = 30000 */) 1622 1541 { 1623 1542 const uint64_t uiStart = RTTimeMilliTS(); … … 1652 1571 * @param uTimeoutMS Timeout (in ms) to wait for the event. 1653 1572 */ 1654 bool DragInstance::waitForX11Msg(XEvent &evX, int iType, RTMSINTERVAL uTimeoutMS /* = 100 */)1573 bool VBClX11DnDInst::waitForX11Msg(XEvent &evX, int iType, RTMSINTERVAL uTimeoutMS /* = 100 */) 1655 1574 { 1656 1575 LogFlowThisFunc(("iType=%d, uTimeoutMS=%RU32, cEventQueue=%zu\n", iType, uTimeoutMS, m_eventQueueList.size())); … … 1709 1628 * @param uTimeoutMS Timeout (in ms) to wait for the event. 1710 1629 */ 1711 bool DragInstance::waitForX11ClientMsg(XClientMessageEvent &evMsg, Atom aType,1630 bool VBClX11DnDInst::waitForX11ClientMsg(XClientMessageEvent &evMsg, Atom aType, 1712 1631 RTMSINTERVAL uTimeoutMS /* = 100 */) 1713 1632 { … … 1781 1700 * @param dndListActionsAllowed (ORed) List of supported actions from the host. 1782 1701 */ 1783 int DragInstance::hgEnter(const RTCList<RTCString> &lstFormats, uint32_t dndListActionsAllowed)1702 int VBClX11DnDInst::hgEnter(const RTCList<RTCString> &lstFormats, uint32_t dndListActionsAllowed) 1784 1703 { 1785 1704 LogFlowThisFunc(("mode=%RU32, state=%RU32\n", m_enmMode, m_enmState)); … … 1859 1778 * display area. 1860 1779 */ 1861 int DragInstance::hgLeave(void)1780 int VBClX11DnDInst::hgLeave(void) 1862 1781 { 1863 1782 if (g_cVerbosity) … … 1880 1799 * as soon as the operation successfully finishes. 1881 1800 */ 1882 int DragInstance::hgMove(uint32_t uPosX, uint32_t uPosY, VBOXDNDACTION dndActionDefault)1801 int VBClX11DnDInst::hgMove(uint32_t uPosX, uint32_t uPosY, VBOXDNDACTION dndActionDefault) 1883 1802 { 1884 1803 LogFlowThisFunc(("mode=%RU32, state=%RU32\n", m_enmMode, m_enmState)); … … 2068 1987 * as soon as the operation successfully finishes. 2069 1988 */ 2070 int DragInstance::hgDrop(uint32_t uPosX, uint32_t uPosY, VBOXDNDACTION dndActionDefault)1989 int VBClX11DnDInst::hgDrop(uint32_t uPosX, uint32_t uPosY, VBOXDNDACTION dndActionDefault) 2071 1990 { 2072 1991 RT_NOREF3(uPosX, uPosY, dndActionDefault); … … 2108 2027 * @param pMeta Pointer to meta data from host. 2109 2028 */ 2110 int DragInstance::hgDataReceive(PVBGLR3GUESTDNDMETADATA pMeta)2029 int VBClX11DnDInst::hgDataReceive(PVBGLR3GUESTDNDMETADATA pMeta) 2111 2030 { 2112 2031 LogFlowThisFunc(("enmMode=%RU32, enmState=%RU32\n", m_enmMode, m_enmState)); … … 2220 2139 * @returns IPRT status code. 2221 2140 */ 2222 int DragInstance::checkForSessionChange(void)2141 int VBClX11DnDInst::checkForSessionChange(void) 2223 2142 { 2224 2143 uint64_t uSessionID; … … 2247 2166 * @returns IPRT status code. 2248 2167 */ 2249 int DragInstance::ghIsDnDPending(void)2168 int VBClX11DnDInst::ghIsDnDPending(void) 2250 2169 { 2251 2170 LogFlowThisFunc(("mode=%RU32, state=%RU32\n", m_enmMode, m_enmState)); … … 2418 2337 * @param dndActionRequested Requested action to perform on the guest. 2419 2338 */ 2420 int DragInstance::ghDropped(const RTCString &strFormat, VBOXDNDACTION dndActionRequested)2339 int VBClX11DnDInst::ghDropped(const RTCString &strFormat, VBOXDNDACTION dndActionRequested) 2421 2340 { 2422 2341 LogFlowThisFunc(("mode=%RU32, state=%RU32, strFormat=%s, dndActionRequested=0x%x\n", … … 2633 2552 * @returns IPRT status code. 2634 2553 */ 2635 int DragInstance::mouseCursorFakeMove(void)2554 int VBClX11DnDInst::mouseCursorFakeMove(void) 2636 2555 { 2637 2556 int iScreenID = XDefaultScreen(m_pDisplay); … … 2681 2600 * @param iPosY Absolute Y coordinate. 2682 2601 */ 2683 int DragInstance::mouseCursorMove(int iPosX, int iPosY)2602 int VBClX11DnDInst::mouseCursorMove(int iPosX, int iPosY) 2684 2603 { 2685 2604 int const iScreenID = XDefaultScreen(m_pDisplay); … … 2726 2645 * @param fPress Whether to press or release the mouse button. 2727 2646 */ 2728 void DragInstance::mouseButtonSet(Window wndDest, int rx, int ry, int iButton, bool fPress)2647 void VBClX11DnDInst::mouseButtonSet(Window wndDest, int rx, int ry, int iButton, bool fPress) 2729 2648 { 2730 2649 LogFlowThisFunc(("wndDest=%#x, rx=%d, ry=%d, iBtn=%d, fPress=%RTbool\n", … … 2808 2727 * @param piRootY Y coordinate relative to the root window's origin. Optional. 2809 2728 */ 2810 int DragInstance::proxyWinShow(int *piRootX /* = NULL */, int *piRootY /* = NULL */) const2729 int VBClX11DnDInst::proxyWinShow(int *piRootX /* = NULL */, int *piRootY /* = NULL */) const 2811 2730 { 2812 2731 /* piRootX is optional. */ … … 2868 2787 * Hides the (invisible) proxy window. 2869 2788 */ 2870 int DragInstance::proxyWinHide(void)2789 int VBClX11DnDInst::proxyWinHide(void) 2871 2790 { 2872 2791 LogFlowFuncEnter(); … … 2887 2806 * @param wndThis Window to retrieve name for. 2888 2807 */ 2889 char * DragInstance::wndX11GetNameA(Window wndThis) const2808 char *VBClX11DnDInst::wndX11GetNameA(Window wndThis) const 2890 2809 { 2891 2810 char *pszName = NULL; … … 2911 2830 * @param wndThis Window to clear the list for. 2912 2831 */ 2913 void DragInstance::wndXDnDClearActionList(Window wndThis) const2832 void VBClX11DnDInst::wndXDnDClearActionList(Window wndThis) const 2914 2833 { 2915 2834 XDeleteProperty(m_pDisplay, wndThis, xAtom(XA_XdndActionList)); … … 2921 2840 * @param wndThis Window to clear the list for. 2922 2841 */ 2923 void DragInstance::wndXDnDClearFormatList(Window wndThis) const2842 void VBClX11DnDInst::wndXDnDClearFormatList(Window wndThis) const 2924 2843 { 2925 2844 XDeleteProperty(m_pDisplay, wndThis, xAtom(XA_XdndTypeList)); … … 2933 2852 * @param lstActions Reference to VBoxDnDAtomList to store the action into. 2934 2853 */ 2935 int DragInstance::wndXDnDGetActionList(Window wndThis, VBoxDnDAtomList &lstActions) const2854 int VBClX11DnDInst::wndXDnDGetActionList(Window wndThis, VBoxDnDAtomList &lstActions) const 2936 2855 { 2937 2856 Atom iActType = None; … … 2978 2897 * @param lstTypes Reference to VBoxDnDAtomList to store the formats into. 2979 2898 */ 2980 int DragInstance::wndXDnDGetFormatList(Window wndThis, VBoxDnDAtomList &lstTypes) const2899 int VBClX11DnDInst::wndXDnDGetFormatList(Window wndThis, VBoxDnDAtomList &lstTypes) const 2981 2900 { 2982 2901 Atom iActType = None; … … 3022 2941 * @param lstActions Reference to list of XDnD actions to set. 3023 2942 */ 3024 int DragInstance::wndXDnDSetActionList(Window wndThis, const VBoxDnDAtomList &lstActions) const2943 int VBClX11DnDInst::wndXDnDSetActionList(Window wndThis, const VBoxDnDAtomList &lstActions) const 3025 2944 { 3026 2945 if (lstActions.isEmpty()) … … 3044 2963 * @param lstFormats Reference to list of XDnD formats to set. 3045 2964 */ 3046 int DragInstance::wndXDnDSetFormatList(Window wndThis, Atom atmProp, const VBoxDnDAtomList &lstFormats) const2965 int VBClX11DnDInst::wndXDnDSetFormatList(Window wndThis, Atom atmProp, const VBoxDnDAtomList &lstFormats) const 3047 2966 { 3048 2967 if (lstFormats.isEmpty()) … … 3065 2984 * @param lstAtoms Reference to VBoxDnDAtomList list to store results in. 3066 2985 */ 3067 int DragInstance::appendFormatsToList(const RTCList<RTCString> &lstFormats, VBoxDnDAtomList &lstAtoms) const2986 int VBClX11DnDInst::appendFormatsToList(const RTCList<RTCString> &lstFormats, VBoxDnDAtomList &lstAtoms) const 3068 2987 { 3069 2988 for (size_t i = 0; i < lstFormats.size(); ++i) … … 3081 3000 * @param lstAtoms Reference to VBoxDnDAtomList list to store results in. 3082 3001 */ 3083 int DragInstance::appendDataToList(const void *pvData, uint32_t cbData, VBoxDnDAtomList &lstAtoms) const3002 int VBClX11DnDInst::appendDataToList(const void *pvData, uint32_t cbData, VBoxDnDAtomList &lstAtoms) const 3084 3003 { 3085 3004 RT_NOREF1(lstAtoms); … … 3123 3042 */ 3124 3043 /* static */ 3125 Atom DragInstance::toAtomAction(VBOXDNDACTION dndAction)3044 Atom VBClX11DnDInst::toAtomAction(VBOXDNDACTION dndAction) 3126 3045 { 3127 3046 /* Ignore is None. */ … … 3140 3059 */ 3141 3060 /* static */ 3142 int DragInstance::toAtomActions(VBOXDNDACTIONLIST dndActionList, VBoxDnDAtomList &lstAtoms)3061 int VBClX11DnDInst::toAtomActions(VBOXDNDACTIONLIST dndActionList, VBoxDnDAtomList &lstAtoms) 3143 3062 { 3144 3063 if (hasDnDCopyAction(dndActionList)) … … 3159 3078 */ 3160 3079 /* static */ 3161 uint32_t DragInstance::toHGCMAction(Atom atom)3080 uint32_t VBClX11DnDInst::toHGCMAction(Atom atom) 3162 3081 { 3163 3082 uint32_t uAction = VBOX_DND_ACTION_IGNORE; … … 3180 3099 */ 3181 3100 /* static */ 3182 uint32_t DragInstance::toHGCMActions(const VBoxDnDAtomList &lstActions)3101 uint32_t VBClX11DnDInst::toHGCMActions(const VBoxDnDAtomList &lstActions) 3183 3102 { 3184 3103 uint32_t uActions = VBOX_DND_ACTION_IGNORE; … … 3194 3113 ********************************************************************************************************************************/ 3195 3114 3196 VB oxDnDProxyWnd::VBoxDnDProxyWnd(void)3115 VBClX11DnDProxyWnd::VBClX11DnDProxyWnd(void) 3197 3116 : pDisp(NULL) 3198 3117 , hWnd(0) … … 3205 3124 } 3206 3125 3207 VB oxDnDProxyWnd::~VBoxDnDProxyWnd(void)3126 VBClX11DnDProxyWnd::~VBClX11DnDProxyWnd(void) 3208 3127 { 3209 3128 destroy(); 3210 3129 } 3211 3130 3212 int VB oxDnDProxyWnd::init(Display *pDisplay)3131 int VBClX11DnDProxyWnd::init(Display *pDisplay) 3213 3132 { 3214 3133 /** @todo What about multiple screens? Test this! */ … … 3222 3141 } 3223 3142 3224 void VB oxDnDProxyWnd::destroy(void)3225 { 3226 3227 } 3228 3229 int VB oxDnDProxyWnd::sendFinished(Window hWndSource, VBOXDNDACTION dndAction)3143 void VBClX11DnDProxyWnd::destroy(void) 3144 { 3145 3146 } 3147 3148 int VBClX11DnDProxyWnd::sendFinished(Window hWndSource, VBOXDNDACTION dndAction) 3230 3149 { 3231 3150 /* Was the drop accepted by the host? That is, anything than ignoring. */ … … 3244 3163 m.data.l[XdndFinishedWindow] = hWnd; /* Target window. */ 3245 3164 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. */ 3247 3166 3248 3167 int xRc = XSendEvent(pDisp, hWndSource, True, NoEventMask, reinterpret_cast<XEvent*>(&m)); … … 3263 3182 3264 3183 /** @copydoc VBCLSERVICE::pfnInit */ 3265 int DragAndDropService::init(void)3184 int VBClX11DnDSvc::init(void) 3266 3185 { 3267 3186 LogFlowFuncEnter(); … … 3275 3194 } 3276 3195 3277 xHelpers *pHelpers = xHelpers::getInstance(m_pDisplay);3196 VBClX11DnDXHelpers *pHelpers = VBClX11DnDXHelpers::getInstance(m_pDisplay); 3278 3197 if (!pHelpers) 3279 3198 return VERR_NO_MEMORY; … … 3327 3246 3328 3247 /** @copydoc VBCLSERVICE::pfnWorker */ 3329 int DragAndDropService::worker(bool volatile *pfShutdown)3248 int VBClX11DnDSvc::worker(bool volatile *pfShutdown) 3330 3249 { 3331 3250 int rc; 3332 3251 do 3333 3252 { 3334 m_pCurDnD = new DragInstance(m_pDisplay, this);3253 m_pCurDnD = new VBClX11DnDInst(m_pDisplay, this); 3335 3254 if (!m_pCurDnD) 3336 3255 { … … 3359 3278 do 3360 3279 { 3361 DNDEVENT e;3280 VBCLDNDEVENT e; 3362 3281 RT_ZERO(e); 3363 3282 … … 3397 3316 } 3398 3317 3399 if (e.enmType == DNDEVENT::DnDEventType_HGCM)3318 if (e.enmType == VBCLDNDEVENT::DnDEventType_HGCM) 3400 3319 { 3401 3320 PVBGLR3DNDEVENT pVbglR3Event = e.hgcm; … … 3520 3439 break; 3521 3440 } 3522 else if (e.enmType == DNDEVENT::DnDEventType_X11)3441 else if (e.enmType == VBCLDNDEVENT::DnDEventType_X11) 3523 3442 { 3524 3443 LogFlowThisFunc(("X11 event (type %#x)\n", e.x11.type)); … … 3559 3478 * Resets the DnD service' data. 3560 3479 */ 3561 void DragAndDropService::reset(void)3480 void VBClX11DnDSvc::reset(void) 3562 3481 { 3563 3482 LogFlowFuncEnter(); … … 3576 3495 switch (m_eventQueue[i].enmType) 3577 3496 { 3578 case DNDEVENT::DnDEventType_HGCM:3497 case VBCLDNDEVENT::DnDEventType_HGCM: 3579 3498 { 3580 3499 VbglR3DnDEventFree(m_eventQueue[i].hgcm); … … 3598 3517 3599 3518 /** @copydoc VBCLSERVICE::pfnStop */ 3600 void DragAndDropService::stop(void)3519 void VBClX11DnDSvc::stop(void) 3601 3520 { 3602 3521 LogFlowFuncEnter(); … … 3616 3535 3617 3536 /** @copydoc VBCLSERVICE::pfnTerm */ 3618 int DragAndDropService::term(void)3537 int VBClX11DnDSvc::term(void) 3619 3538 { 3620 3539 int rc = VINF_SUCCESS; … … 3671 3590 } 3672 3591 3673 xHelpers::destroyInstance();3592 VBClX11DnDXHelpers::destroyInstance(); 3674 3593 3675 3594 return rc; … … 3686 3605 */ 3687 3606 /* static */ 3688 DECLCALLBACK(int) DragAndDropService::hgcmEventThread(RTTHREAD hThread, void *pvUser)3607 DECLCALLBACK(int) VBClX11DnDSvc::hgcmEventThread(RTTHREAD hThread, void *pvUser) 3689 3608 { 3690 3609 AssertPtrReturn(pvUser, VERR_INVALID_PARAMETER); 3691 DragAndDropService *pThis = static_cast<DragAndDropService*>(pvUser);3610 VBClX11DnDSvc *pThis = static_cast<VBClX11DnDSvc*>(pvUser); 3692 3611 3693 3612 /* Let the service instance know in any case. */ … … 3699 3618 /* Number of invalid messages skipped in a row. */ 3700 3619 int cMsgSkippedInvalid = 0; 3701 DNDEVENT e;3620 VBCLDNDEVENT e; 3702 3621 3703 3622 do 3704 3623 { 3705 3624 RT_ZERO(e); 3706 e.enmType = DNDEVENT::DnDEventType_HGCM;3625 e.enmType = VBCLDNDEVENT::DnDEventType_HGCM; 3707 3626 3708 3627 /* Wait for new events. */ … … 3759 3678 */ 3760 3679 /* static */ 3761 DECLCALLBACK(int) DragAndDropService::x11EventThread(RTTHREAD hThread, void *pvUser)3680 DECLCALLBACK(int) VBClX11DnDSvc::x11EventThread(RTTHREAD hThread, void *pvUser) 3762 3681 { 3763 3682 AssertPtrReturn(pvUser, VERR_INVALID_PARAMETER); 3764 DragAndDropService *pThis = static_cast<DragAndDropService*>(pvUser);3683 VBClX11DnDSvc *pThis = static_cast<VBClX11DnDSvc*>(pvUser); 3765 3684 AssertPtr(pThis); 3766 3685 … … 3775 3694 VBClLogVerbose(2, "X11 thread started\n"); 3776 3695 3777 DNDEVENT e;3696 VBCLDNDEVENT e; 3778 3697 RT_ZERO(e); 3779 e.enmType = DNDEVENT::DnDEventType_X11;3698 e.enmType = VBCLDNDEVENT::DnDEventType_X11; 3780 3699 3781 3700 do … … 3830 3749 return rc; 3831 3750 } 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 1 1 /* $Id$ */ 2 2 /** @file 3 * X11 guest client - Drag and drop implementation.3 * Guest Additions - Common drag'n drop wrapper service. 4 4 */ 5 5 6 6 /* 7 * Copyright (C) 20 11-2023 Oracle and/or its affiliates.7 * Copyright (C) 2023 Oracle and/or its affiliates. 8 8 * 9 9 * This file is part of VirtualBox base platform packages, as … … 26 26 */ 27 27 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> 53 29 54 30 #include "VBox/HostServices/DragAndDropSvc.h" 55 31 #include "VBoxClient.h" 32 #include "draganddrop.h" 56 33 57 34 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. */ 36 static VBClDnDSvc *g_pSvc = NULL; 71 37 72 /**73 * For X11 guest Xdnd is used. See http://www.acc.umu.se/~vatten/XDND.html for74 * 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 the82 * appropriate X11 events. There exists a proxy window, which is invisible and83 * used for all the X11 communication. On a HGCM Enter event, we set our proxy84 * window as XdndSelection owner with the given mime-types. On every HGCM move85 * event, we move the X11 mouse cursor to the new position and query for the86 * window below that position. Depending on if it is XdndAware, a new window or87 * a known window, we send the appropriate X11 messages to it. On HGCM drop, we88 * send a XdndDrop message to the current window and wait for a X1189 * SelectionMessage from the target window. Because we didn't have the data in90 * the requested mime-type, yet, we save that message and ask the host for the91 * data. When the data is successfully received from the host, we put the data92 * as a property to the window and send a X11 SelectionNotify event to the93 * target window.94 *95 * Guest -> Host:96 * This is a lot more trickery than H->G. When a pending event from HGCM97 * arrives, we ask if there currently is an owner of the XdndSelection98 * 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 XdndEnter100 * event send to the proxy window. From this event we can fetch the necessary101 * 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 get103 * 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 */111 38 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 0121 /** 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 messages137 * into a single event queue.138 */139 typedef struct DNDEVENT140 {141 enum DnDEventType142 {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 = 0x7fffffff151 };152 /** Event type. */153 DnDEventType enmType;154 union155 {156 PVBGLR3DNDEVENT hgcm;157 XEvent x11;158 };159 #ifdef IN_GUEST160 RTMEM_IMPLEMENT_NEW_AND_DELETE();161 #endif162 } DNDEVENT;163 /** Pointer to a DnD event. */164 typedef DNDEVENT *PDNDEVENT;165 166 enum XA_Type167 {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_End201 };202 203 /**204 * Xdnd message value indices, sorted by message type.205 */206 typedef enum XdndMsg207 {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 xHelpers267 {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) const294 {295 return XInternAtom(m_pDisplay, pcszString, False);296 }297 inline RTCString xAtomToString(Atom atom) const298 {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_DELETE335 RTMEM_IMPLEMENT_NEW_AND_DELETE();336 #endif337 xHelpers(Display *pDisplay)338 : m_pDisplay(pDisplay)339 {340 /* Not all x11 atoms we use are defined in the headers. Create the341 * 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) const401 {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) const428 {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 current462 * 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 DEBUG473 # define VBOX_DND_FN_DECL_LOG(x) inline x /* For LogFlowXXX logging. */474 #else475 # define VBOX_DND_FN_DECL_LOG(x) x476 #endif477 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 VBoxDnDProxyWnd483 {484 485 public:486 #ifdef RT_NEED_NEW_AND_DELETE487 RTMEM_IMPLEMENT_NEW_AND_DELETE();488 #endif489 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 WrappedXEvent512 {513 public:514 XEvent m_Event;515 516 public:517 #ifdef RT_NEED_NEW_AND_DELETE518 RTMEM_IMPLEMENT_NEW_AND_DELETE();519 #endif520 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 DragInstance544 {545 public:546 547 enum State548 {549 Uninitialized = 0,550 Initialized,551 Dragging,552 Dropped,553 State_32BIT_Hack = 0x7fffffff554 };555 556 enum Mode557 {558 Unknown = 0,559 HG,560 GH,561 Mode_32Bit_Hack = 0x7fffffff562 };563 564 #ifdef RT_NEED_NEW_AND_DELETE565 RTMEM_IMPLEMENT_NEW_AND_DELETE();566 #endif567 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_GH591 /* Guest -> Host handling. */592 int ghIsDnDPending(void);593 int ghDropped(const RTCString &strFormat, VBOXDNDACTION dndActionRequested);594 #endif595 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 host682 * 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 DragAndDropService690 {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 list719 * 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 do887 {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 0909 /* 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 #endif917 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 DnD929 * operation. This window will be used for both the GH and HG930 * direction.931 */932 XSetWindowAttributes attr;933 RT_ZERO(attr);934 attr.event_mask = EnterWindowMask | LeaveWindowMask935 | 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 CWBackPixel951 | CWBorderPixel952 | CWOverrideRedirect953 | 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 else1006 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 XdndAware1036 * 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 DnD1054 * event and with which action. We immediately send this info down to1055 * 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 else1085 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 #endif1103 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 else1114 {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 drop1128 * 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_XdndSelection1134 * 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 DEBUG1152 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 #endif1156 /*1157 * Retrieve supported formats.1158 */1159 1160 /* Check if the MIME types are in the message itself or if we need1161 * 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 else1174 {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_ENABLED1241 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 #endif1248 bool fAcceptDrop = true;1249 1250 /* Reply with a XdndStatus message to tell the source whether1251 * 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 being1263 * 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 now1343 * is the owner of the current selection.1344 *1345 * @return IPRT status code.1346 * @param e X11 event to handle.1347 *1348 * @remark1349 */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 needed1360 * 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 else1384 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 needed1403 * 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 requestor1473 * 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_PROMISES1485 # error "Implement me!"1486 #else1487 /* 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 else1495 {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 become1502 * 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_PROMISES1526 }1527 #endif1528 }1529 /* Anything else. */1530 else1531 {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 operation1577 * goes back from the host to the guest. This is not what1578 * 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 do1631 {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_GH1646 /**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 do1662 {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 do1720 {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 == ClientMessage1729 && 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 else1743 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 -> Guest1773 */1774 1775 /**1776 * Host -> Guest: Event signalling that the host's (mouse) cursor just entered the VM's (guest's) display1777 * 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 DEBUG1793 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 #endif1798 1799 int rc;1800 1801 do1802 {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's1874 * (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 guest1880 * 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 != HG1888 || 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 in1915 * 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 else1924 {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 else1932 {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_wndCur1948 && 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_wndCur1975 && 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 new1982 * window. The first three mime types are attached to the event (the1983 * others could be requested by the XdndTypeList property from the1984 * 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 else2047 {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 guest2068 * 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 != HG2077 || 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 format2087 * the guest exactly expects. As blocking in a SelectionRequest message turned2088 * out to be very unreliable (e.g. with KDE apps) we request to start transferring2089 * 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 drop2105 * 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 != HG2116 || 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) should2162 * 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 into2178 * 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_GH2243 /**2244 * Guest -> Host: Event signalling that the host is asking whether there is a pending2245 * 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 == GH2266 && ( m_enmState == Dragging2267 || m_enmState == Dropped)2268 )2269 {2270 /* No need to query for the source window again. */2271 rc = VINF_SUCCESS;2272 }2273 else2274 {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 && wndSel2285 && 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 provoke2295 * 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 make2309 * the drag and drop experience on the host being laggy2310 * and unresponsive.2311 *2312 * Instead, let the host query multiple times with 100ms2313 * timeout each (see above) and only report an error if2314 * 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 else2326 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 else2345 VBClLogInfo("No guest source window\n");2346 }2347 2348 /*2349 * Acknowledge to the host in any case, regardless2350 * 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 the2414 * 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 == Unknown2427 || m_enmMode == HG)2428 {2429 return VERR_INVALID_STATE;2430 }2431 2432 if ( m_enmMode == GH2433 && m_enmState != Dragging)2434 {2435 return VERR_INVALID_STATE;2436 }2437 2438 int rc = VINF_SUCCESS;2439 2440 m_enmState = Dropped;2441 2442 #ifdef DEBUG2443 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 #endif2451 2452 /* We send a fake mouse move event to the current window, cause2453 * this should have the grab. */2454 mouseCursorFakeMove();2455 2456 /**2457 * The fake button release event above should lead to a XdndDrop event from the2458 * source window. Because of showing our proxy window, other Xdnd events can2459 * occur before, e.g. a XdndPosition event. We are not interested2460 * 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 and2470 * 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 else2482 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 == SelectionNotify2496 && evSelNotify.xselection.display == m_pDisplay2497 && evSelNotify.xselection.selection == xAtom(XA_XdndSelection)2498 && evSelNotify.xselection.requestor == m_wndProxy.hWnd2499 && 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 != None2522 && pcData != NULL2523 && iPropFormat >= 82524 && cItems > 02525 && 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 not2531 * 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 else2546 rc = VERR_NO_MEMORY;2547 }2548 else2549 {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 else2560 fCancel = true;2561 }2562 else2563 {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 else2573 {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 by2586 * 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 else2595 rc = VERR_INVALID_PARAMETER;2596 }2597 else2598 rc = VERR_TIMEOUT;2599 }2600 else2601 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 * Helpers2623 */2624 2625 /**2626 * Fakes moving the mouse cursor to provoke various drag and drop2627 * events such as entering a target window or moving within a2628 * source window.2629 *2630 * Not the most elegant and probably correct function, but does2631 * 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 == iPosX2696 && 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 window2704 * 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 else2714 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_XTEST2734 /** @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 else2746 {2747 #endif2748 LogFlowThisFunc(("Note: XText extension not available or disabled\n"));2749 2750 unsigned int mask = 0;2751 2752 if ( rx == -12753 && 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_XTEST2793 }2794 #endif2795 }2796 2797 /**2798 * Shows the (invisible) proxy window. The proxy window is needed for intercepting2799 * drags from the host to the guest or from the guest to the host. It acts as a proxy2800 * 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 guest2803 * screen (think of an umbrella) to (hopefully) capture everything. A proxy window2804 * 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 */) const2811 {2812 /* piRootX is optional. */2813 /* piRootY is optional. */2814 2815 LogFlowThisFuncEnter();2816 2817 int rc = VINF_SUCCESS;2818 2819 #if 02820 # ifdef VBOX_DND_WITH_XTEST2821 XTestGrabControl(m_pDisplay, False);2822 # endif2823 #endif2824 2825 /* Get the mouse pointer position and determine if we're on the same screen as the root window2826 * 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 02858 # ifdef VBOX_DND_WITH_XTEST2859 XTestGrabControl(m_pDisplay, True);2860 # endif2861 #endif2862 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) const2890 {2891 char *pszName = NULL;2892 2893 XTextProperty propName;2894 if ( wndThis != None2895 && 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) const2914 {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) const2924 {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) const2936 {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) const2981 {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) const3025 {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) const3047 {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) const3068 {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) const3084 {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 do3284 {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 do3333 {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 use3342 * another screen number than zero. Maybe in the future it will become3343 * 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 do3360 {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 else3419 {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 move3426 * 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 when3451 * 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 done3454 * internally by VbglR3DnDRecvNextMsg() to not duplicate any code for different3455 * platforms.3456 *3457 * The data header now will contain all the (meta) data the guest needs in3458 * 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_GH3472 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 #endif3490 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 else3528 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 the3536 * server, since we are waiting for responses using poll() on3537 * 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 internal3680 * message queue will be filled which then will be processed by the according3681 * 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 do3704 {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 else3730 {3731 VBClLogError("Processing next message failed with rc=%Rrc\n", rc);3732 3733 /* Old(er) hosts either are broken regarding DnD support or otherwise3734 * don't support the stuff we do on the guest side, so make sure we3735 * 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 messages3754 * 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 do3782 {3783 /*3784 * Wait for new events. We can't use XIfEvent here, cause this locks3785 * the window connection with a mutex and if no X11 events occurs this3786 * blocks any other calls we made to X11. So instead check for new3787 * events and if there are not any new one, sleep for a certain amount3788 * 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 }3832 39 /** 3833 40 * @interface_method_impl{VBCLSERVICE,pfnInit} … … 3835 42 static DECLCALLBACK(int) vbclDnDInit(void) 3836 43 { 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(); 3838 60 } 3839 61 … … 3843 65 static DECLCALLBACK(int) vbclDnDWorker(bool volatile *pfShutdown) 3844 66 { 3845 return g_ Svc.worker(pfShutdown);67 return g_pSvc->worker(pfShutdown); 3846 68 } 3847 69 … … 3851 73 static DECLCALLBACK(void) vbclDnDStop(void) 3852 74 { 3853 g_ Svc.stop();75 g_pSvc->stop(); 3854 76 } 3855 77 … … 3859 81 static DECLCALLBACK(int) vbclDnDTerm(void) 3860 82 { 3861 return g_ Svc.term();83 return g_pSvc->term(); 3862 84 } 3863 85
Note:
See TracChangeset
for help on using the changeset viewer.