VirtualBox

source: vbox/trunk/src/VBox/Additions/x11/VBoxClient/draganddrop.cpp@ 56671

Last change on this file since 56671 was 56671, checked in by vboxsync, 9 years ago

DnD/VBoxClient: Initialize empty format string in ghIsDnDPending(), logging.

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 109.8 KB
Line 
1/* $Id: draganddrop.cpp 56671 2015-06-29 11:14:58Z vboxsync $ */
2/** @file
3 * X11 guest client - Drag and drop implementation.
4 */
5
6/*
7 * Copyright (C) 2011-2015 Oracle Corporation
8 *
9 * This file is part of VirtualBox Open Source Edition (OSE), as
10 * available from http://www.virtualbox.org. This file is free software;
11 * you can redistribute it and/or modify it under the terms of the GNU
12 * General Public License (GPL) as published by the Free Software
13 * Foundation, in version 2 as it comes in the "COPYING" file of the
14 * VirtualBox OSE distribution. VirtualBox OSE is distributed in the
15 * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
16 */
17
18#include <X11/Xlib.h>
19#include <X11/Xutil.h>
20#include <X11/Xatom.h>
21#ifdef VBOX_DND_WITH_XTEST
22# include <X11/extensions/XTest.h>
23#endif
24
25#include <iprt/asm.h>
26#include <iprt/buildconfig.h>
27#include <iprt/critsect.h>
28#include <iprt/thread.h>
29#include <iprt/time.h>
30
31#include <iprt/cpp/mtlist.h>
32#include <iprt/cpp/ministring.h>
33
34#include <limits.h>
35
36# ifdef LOG_GROUP
37 # undef LOG_GROUP
38# endif
39#define LOG_GROUP LOG_GROUP_GUEST_DND
40#include <VBox/log.h>
41#include <VBox/VBoxGuestLib.h>
42
43#include "VBox/HostServices/DragAndDropSvc.h"
44
45#include "VBoxClient.h"
46
47/* Enable this define to see the proxy window(s) when debugging
48 * their behavior. Don't have this enabled in release builds! */
49#ifdef DEBUG
50//# define VBOX_DND_DEBUG_WND
51#endif
52
53/**
54 * For X11 guest Xdnd is used. See http://www.acc.umu.se/~vatten/XDND.html for
55 * a walk trough.
56 *
57 * Host -> Guest:
58 * For X11 this means mainly forwarding all the events from HGCM to the
59 * appropriate X11 events. There exists a proxy window, which is invisible and
60 * used for all the X11 communication. On a HGCM Enter event, we set our proxy
61 * window as XdndSelection owner with the given mime-types. On every HGCM move
62 * event, we move the X11 mouse cursor to the new position and query for the
63 * window below that position. Depending on if it is XdndAware, a new window or
64 * a known window, we send the appropriate X11 messages to it. On HGCM drop, we
65 * send a XdndDrop message to the current window and wait for a X11
66 * SelectionMessage from the target window. Because we didn't have the data in
67 * the requested mime-type, yet, we save that message and ask the host for the
68 * data. When the data is successfully received from the host, we put the data
69 * as a property to the window and send a X11 SelectionNotify event to the
70 * target window.
71 *
72 * Guest -> Host:
73 * This is a lot more trickery than H->G. When a pending event from HGCM
74 * arrives, we ask if there currently is an owner of the XdndSelection
75 * property. If so, our proxy window is shown (1x1, but without backing store)
76 * and some mouse event is triggered. This should be followed by an XdndEnter
77 * event send to the proxy window. From this event we can fetch the necessary
78 * info of the MIME types and allowed actions and send this back to the host.
79 * On a drop request from the host, we query for the selection and should get
80 * the data in the specified mime-type. This data is send back to the host.
81 * After that we send a XdndLeave event to the source window.
82 *
83 ** @todo Cancelling (e.g. with ESC key) doesn't work.
84 ** @todo INCR (incremental transfers) support.
85 ** @todo Really check for the Xdnd version and the supported features.
86 ** @todo Either get rid of the xHelpers class or properly unify the code with the drag instance class.
87 */
88
89#define VBOX_XDND_VERSION (4)
90#define VBOX_MAX_XPROPERTIES (LONG_MAX-1)
91
92/**
93 * Structure for storing new X11 events and HGCM messages
94 * into a single vent queue.
95 */
96struct DnDEvent
97{
98 enum DnDEventType
99 {
100 HGCM_Type = 1,
101 X11_Type
102 };
103 DnDEventType type;
104 union
105 {
106 VBGLR3DNDHGCMEVENT hgcm;
107 XEvent x11;
108 };
109};
110
111enum XA_Type
112{
113 /* States */
114 XA_WM_STATE = 0,
115 /* Properties */
116 XA_TARGETS,
117 XA_MULTIPLE,
118 XA_INCR,
119 /* Mime Types */
120 XA_image_bmp,
121 XA_image_jpg,
122 XA_image_tiff,
123 XA_image_png,
124 XA_text_uri_list,
125 XA_text_uri,
126 XA_text_plain,
127 XA_TEXT,
128 /* Xdnd */
129 XA_XdndSelection,
130 XA_XdndAware,
131 XA_XdndEnter,
132 XA_XdndLeave,
133 XA_XdndTypeList,
134 XA_XdndActionList,
135 XA_XdndPosition,
136 XA_XdndActionCopy,
137 XA_XdndActionMove,
138 XA_XdndActionLink,
139 XA_XdndStatus,
140 XA_XdndDrop,
141 XA_XdndFinished,
142 /* Our own stop marker */
143 XA_dndstop,
144 /* End marker */
145 XA_End
146};
147
148/**
149 * Xdnd message value indexes, sorted by message type.
150 */
151typedef enum XdndMsg
152{
153 /** XdndEnter. */
154 XdndEnterTypeCount = 3, /* Maximum number of types in XdndEnter message. */
155
156 XdndEnterWindow = 0, /* Source window (sender). */
157 XdndEnterFlags, /* Version in high byte, bit 0 => more data types. */
158 XdndEnterType1, /* First available data type. */
159 XdndEnterType2, /* Second available data type. */
160 XdndEnterType3, /* Third available data type. */
161
162 XdndEnterMoreTypesFlag = 1, /* Set if there are more than XdndEnterTypeCount. */
163 XdndEnterVersionRShift = 24, /* Right shift to position version number. */
164 XdndEnterVersionMask = 0xFF, /* Mask to get version after shifting. */
165
166 /** XdndHere. */
167 XdndHereWindow = 0, /* Source window (sender). */
168 XdndHereFlags, /* Reserved. */
169 XdndHerePt, /* X + Y coordinates of mouse (root window coords). */
170 XdndHereTimeStamp, /* Timestamp for requesting data. */
171 XdndHereAction, /* Action requested by user. */
172
173 /** XdndPosition. */
174 XdndPositionWindow = 0, /* Source window (sender). */
175 XdndPositionFlags, /* Flags. */
176 XdndPositionCoords, /* X/Y coordinates of the mouse position relative to the root window. */
177 XdndPositionTimeStamp, /* Time stamp for retrieving the data. */
178 XdndPositionAction, /* Action requested by the user. */
179
180 /** XdndStatus. */
181 XdndStatusWindow = 0, /* Target window (sender).*/
182 XdndStatusFlags, /* Flags returned by target. */
183 XdndStatusPt, /* X + Y of "no msg" rectangle (root window coords). */
184 XdndStatusArea, /* Width + height of "no msg" rectangle. */
185 XdndStatusAction, /* Action accepted by target. */
186
187 XdndStatusAcceptDropFlag = 1, /* Set if target will accept the drop. */
188 XdndStatusSendHereFlag = 2, /* Set if target wants a stream of XdndPosition. */
189
190 /** XdndLeave. */
191 XdndLeaveWindow = 0, /* Source window (sender). */
192 XdndLeaveFlags, /* Reserved. */
193
194 /** XdndDrop. */
195 XdndDropWindow = 0, /* Source window (sender). */
196 XdndDropFlags, /* Reserved. */
197 XdndDropTimeStamp, /* Timestamp for requesting data. */
198
199 /** XdndFinished. */
200 XdndFinishedWindow = 0, /* Target window (sender). */
201 XdndFinishedFlags, /* Version 5: Bit 0 is set if the current target accepted the drop. */
202 XdndFinishedAction /* Version 5: Contains the action performed by the target. */
203
204} XdndMsg;
205
206class DragAndDropService;
207
208/** List of Atoms. */
209#define VBoxDnDAtomList RTCList<Atom>
210
211/*******************************************************************************
212 *
213 * xHelpers Declaration
214 *
215 ******************************************************************************/
216
217class xHelpers
218{
219public:
220
221 static xHelpers *getInstance(Display *pDisplay = 0)
222 {
223 if (!m_pInstance)
224 {
225 AssertPtrReturn(pDisplay, NULL);
226 m_pInstance = new xHelpers(pDisplay);
227 }
228
229 return m_pInstance;
230 }
231
232 inline Display *display() const { return m_pDisplay; }
233 inline Atom xAtom(XA_Type e) const { return m_xAtoms[e]; }
234
235 inline Atom stringToxAtom(const char *pcszString) const
236 {
237 return XInternAtom(m_pDisplay, pcszString, False);
238 }
239 inline RTCString xAtomToString(Atom atom) const
240 {
241 if (atom == None) return "None";
242
243 char* pcsAtom = XGetAtomName(m_pDisplay, atom);
244 RTCString strAtom(pcsAtom);
245 XFree(pcsAtom);
246
247 return strAtom;
248 }
249
250 inline RTCString xAtomListToString(const VBoxDnDAtomList &formatList)
251 {
252 RTCString format;
253 for (size_t i = 0; i < formatList.size(); ++i)
254 format += xAtomToString(formatList.at(i)) + "\r\n";
255 return format;
256 }
257
258 RTCString xErrorToString(int xRc) const;
259 Window applicationWindowBelowCursor(Window parentWin) const;
260
261private:
262
263 xHelpers(Display *pDisplay)
264 : m_pDisplay(pDisplay)
265 {
266 /* Not all x11 atoms we use are defined in the headers. Create the
267 * additional one we need here. */
268 for (int i = 0; i < XA_End; ++i)
269 m_xAtoms[i] = XInternAtom(m_pDisplay, m_xAtomNames[i], False);
270 };
271
272 /* Private member vars */
273 static xHelpers *m_pInstance;
274 Display *m_pDisplay;
275 Atom m_xAtoms[XA_End];
276 static const char *m_xAtomNames[XA_End];
277};
278
279/* Some xHelpers convenience defines. */
280#define gX11 xHelpers::getInstance()
281#define xAtom(xa) gX11->xAtom((xa))
282#define xAtomToString(xa) gX11->xAtomToString((xa))
283
284/*******************************************************************************
285 *
286 * xHelpers Implementation
287 *
288 ******************************************************************************/
289
290xHelpers *xHelpers::m_pInstance = NULL;
291
292/* Has to be in sync with the XA_Type enum. */
293const char *xHelpers::m_xAtomNames[] =
294{
295 /* States */
296 "WM_STATE",
297 /* Properties */
298 "TARGETS",
299 "MULTIPLE",
300 "INCR",
301 /* Mime Types */
302 "image/bmp",
303 "image/jpg",
304 "image/tiff",
305 "image/png",
306 "text/uri-list",
307 "text/uri",
308 "text/plain",
309 "TEXT",
310 /* Xdnd */
311 "XdndSelection",
312 "XdndAware",
313 "XdndEnter",
314 "XdndLeave",
315 "XdndTypeList",
316 "XdndActionList",
317 "XdndPosition",
318 "XdndActionCopy",
319 "XdndActionMove",
320 "XdndActionLink",
321 "XdndStatus",
322 "XdndDrop",
323 "XdndFinished",
324 /* Our own stop marker */
325 "dndstop"
326};
327
328RTCString xHelpers::xErrorToString(int xRc) const
329{
330 switch (xRc)
331 {
332 case Success: return RTCStringFmt("%d (Success)", xRc); break;
333 case BadRequest: return RTCStringFmt("%d (BadRequest)", xRc); break;
334 case BadValue: return RTCStringFmt("%d (BadValue)", xRc); break;
335 case BadWindow: return RTCStringFmt("%d (BadWindow)", xRc); break;
336 case BadPixmap: return RTCStringFmt("%d (BadPixmap)", xRc); break;
337 case BadAtom: return RTCStringFmt("%d (BadAtom)", xRc); break;
338 case BadCursor: return RTCStringFmt("%d (BadCursor)", xRc); break;
339 case BadFont: return RTCStringFmt("%d (BadFont)", xRc); break;
340 case BadMatch: return RTCStringFmt("%d (BadMatch)", xRc); break;
341 case BadDrawable: return RTCStringFmt("%d (BadDrawable)", xRc); break;
342 case BadAccess: return RTCStringFmt("%d (BadAccess)", xRc); break;
343 case BadAlloc: return RTCStringFmt("%d (BadAlloc)", xRc); break;
344 case BadColor: return RTCStringFmt("%d (BadColor)", xRc); break;
345 case BadGC: return RTCStringFmt("%d (BadGC)", xRc); break;
346 case BadIDChoice: return RTCStringFmt("%d (BadIDChoice)", xRc); break;
347 case BadName: return RTCStringFmt("%d (BadName)", xRc); break;
348 case BadLength: return RTCStringFmt("%d (BadLength)", xRc); break;
349 case BadImplementation: return RTCStringFmt("%d (BadImplementation)", xRc); break;
350 }
351 return RTCStringFmt("%d (unknown)", xRc);
352}
353
354/** todo Make this iterative. */
355Window xHelpers::applicationWindowBelowCursor(Window wndParent) const
356{
357 /* No parent, nothing to do. */
358 if(wndParent == 0)
359 return 0;
360
361 Window wndApp = 0;
362 int cProps = -1;
363
364 /* Fetch all x11 window properties of the parent window. */
365 Atom *pProps = XListProperties(m_pDisplay, wndParent, &cProps);
366 if (cProps > 0)
367 {
368 /* We check the window for the WM_STATE property. */
369 for (int i = 0; i < cProps; ++i)
370 {
371 if (pProps[i] == xAtom(XA_WM_STATE))
372 {
373 /* Found it. */
374 wndApp = wndParent;
375 break;
376 }
377 }
378
379 /* Cleanup */
380 XFree(pProps);
381 }
382
383 if (!wndApp)
384 {
385 Window wndChild, wndTemp;
386 int tmp;
387 unsigned int utmp;
388
389 /* Query the next child window of the parent window at the current
390 * mouse position. */
391 XQueryPointer(m_pDisplay, wndParent, &wndTemp, &wndChild, &tmp, &tmp, &tmp, &tmp, &utmp);
392
393 /* Recursive call our self to dive into the child tree. */
394 wndApp = applicationWindowBelowCursor(wndChild);
395 }
396
397 return wndApp;
398}
399
400/*******************************************************************************
401 *
402 * DragInstance Declaration
403 *
404 ******************************************************************************/
405
406#ifdef DEBUG
407# define VBOX_DND_FN_DECL_LOG(x) inline x /* For LogFlowXXX logging. */
408#else
409# define VBOX_DND_FN_DECL_LOG(x) x
410#endif
411
412/**
413 * Class for handling a single drag and drop operation, that is,
414 * one source and one target at a time.
415 *
416 * For now only one DragInstance will exits when the app is running.
417 */
418class DragInstance
419{
420public:
421
422 enum State
423 {
424 Uninitialized = 0,
425 Initialized,
426 Dragging,
427 Dropped,
428 State_32BIT_Hack = 0x7fffffff
429 };
430
431 enum Mode
432 {
433 Unknown = 0,
434 HG,
435 GH,
436 Mode_32Bit_Hack = 0x7fffffff
437 };
438
439 DragInstance(Display *pDisplay, DragAndDropService *pParent);
440
441public:
442
443 int init(uint32_t u32ScreenId);
444 void uninit(void);
445 void reset(void);
446
447 /* Logging. */
448 VBOX_DND_FN_DECL_LOG(void) logInfo(const char *pszFormat, ...);
449 VBOX_DND_FN_DECL_LOG(void) logError(const char *pszFormat, ...);
450
451 /* X11 message processing. */
452 int onX11ClientMessage(const XEvent &e);
453 int onX11MotionNotify(const XEvent &e);
454 int onX11SelectionClear(const XEvent &e);
455 int onX11SelectionNotify(const XEvent &e);
456 int onX11SelectionRequest(const XEvent &e);
457 int onX11Event(const XEvent &e);
458 int waitForStatusChange(uint32_t enmState, RTMSINTERVAL uTimeoutMS = 30000);
459 bool waitForX11Msg(XEvent &evX, int iType, RTMSINTERVAL uTimeoutMS = 100);
460 bool waitForX11ClientMsg(XClientMessageEvent &evMsg, Atom aType, RTMSINTERVAL uTimeoutMS = 100);
461
462 /* Host -> Guest handling. */
463 int hgEnter(const RTCList<RTCString> &formats, uint32_t actions);
464 int hgLeave(void);
465 int hgMove(uint32_t u32xPos, uint32_t u32yPos, uint32_t uDefaultAction);
466 int hgDrop(uint32_t u32xPos, uint32_t u32yPos, uint32_t uDefaultAction);
467 int hgDataReceived(const void *pvData, uint32_t cData);
468
469#ifdef VBOX_WITH_DRAG_AND_DROP_GH
470 /* Guest -> Host handling. */
471 int ghIsDnDPending(void);
472 int ghDropped(const RTCString &strFormat, uint32_t action);
473#endif
474
475 /* X11 helpers. */
476 int mouseCursorMove(int iPosX, int iPosY) const;
477 void mouseButtonSet(Window wndDest, int rx, int ry, int iButton, bool fPress);
478 int proxyWinShow(int *piRootX = NULL, int *piRootY = NULL) const;
479 int proxyWinHide(void);
480
481 /* X11 window helpers. */
482 char *wndX11GetNameA(Window wndThis) const;
483
484 /* Xdnd protocol helpers. */
485 void wndXDnDClearActionList(Window wndThis) const;
486 void wndXDnDClearFormatList(Window wndThis) const;
487 int wndXDnDGetActionList(Window wndThis, VBoxDnDAtomList &lstActions) const;
488 int wndXDnDGetFormatList(Window wndThis, VBoxDnDAtomList &lstTypes) const;
489 int wndXDnDSetActionList(Window wndThis, const VBoxDnDAtomList &lstActions) const;
490 int wndXDnDSetFormatList(Window wndThis, Atom atmProp, const VBoxDnDAtomList &lstFormats) const;
491
492 /* Atom / HGCM formatting helpers. */
493 int toAtomList(const RTCList<RTCString> &lstFormats, VBoxDnDAtomList &lstAtoms) const;
494 int toAtomList(const void *pvData, uint32_t cbData, VBoxDnDAtomList &lstAtoms) const;
495 static Atom toAtomAction(uint32_t uAction);
496 static int toAtomActions(uint32_t uActions, VBoxDnDAtomList &lstAtoms);
497 static uint32_t toHGCMAction(Atom atom);
498 static uint32_t toHGCMActions(const VBoxDnDAtomList &actionsList);
499
500protected:
501
502 /** The instance's own DnD context. */
503 VBGLR3GUESTDNDCMDCTX m_dndCtx;
504 /** Pointer to service instance. */
505 DragAndDropService *m_pParent;
506 /** Pointer to X display operating on. */
507 Display *m_pDisplay;
508 /** X screen ID to operate on. */
509 int m_screenId;
510 /** Pointer to X screen operating on. */
511 Screen *m_pScreen;
512 /** Root window handle. */
513 Window m_wndRoot;
514 /** Proxy window handle. */
515 Window m_wndProxy;
516 /** Current source/target window handle. */
517 Window m_wndCur;
518 /** The XDnD protocol version the current
519 * source/target window is using. */
520 long m_curVer;
521 /** List of (Atom) formats the source window supports. */
522 VBoxDnDAtomList m_lstFormats;
523 /** List of (Atom) actions the source window supports. */
524 VBoxDnDAtomList m_lstActions;
525 /** Buffer for answering the target window's selection request. */
526 void *m_pvSelReqData;
527 /** Size (in bytes) of selection request data buffer. */
528 uint32_t m_cbSelReqData;
529 /** Current operation mode. */
530 volatile uint32_t m_enmMode;
531 /** Current state of operation mode. */
532 volatile uint32_t m_enmState;
533 /** The instance's own X event queue. */
534 RTCMTList<XEvent> m_eventQueueList;
535 /** Critical section for providing serialized access to list
536 * event queue's contents. */
537 RTCRITSECT m_eventQueueCS;
538 /** Event for notifying this instance in case of a new
539 * event. */
540 RTSEMEVENT m_eventQueueEvent;
541 /** Critical section for data access. */
542 RTCRITSECT m_dataCS;
543 /** List of allowed formats. */
544 RTCList<RTCString> m_lstAllowedFormats;
545};
546
547/*******************************************************************************
548 *
549 * DragAndDropService Declaration
550 *
551 ******************************************************************************/
552
553class DragAndDropService
554{
555public:
556 DragAndDropService(void)
557 : m_pDisplay(0)
558 , m_hHGCMThread(NIL_RTTHREAD)
559 , m_hX11Thread(NIL_RTTHREAD)
560 , m_hEventSem(NIL_RTSEMEVENT)
561 , m_pCurDnD(0)
562 , m_fSrvStopping(false)
563 {}
564
565 int run(bool fDaemonised = false);
566
567private:
568
569 int dragAndDropInit(void);
570 static int hgcmEventThread(RTTHREAD hThread, void *pvUser);
571 static int x11EventThread(RTTHREAD hThread, void *pvUser);
572
573 /* Private member vars */
574 Display *m_pDisplay;
575
576 /** Our (thread-safe) event queue with
577 * mixed events (DnD HGCM / X11). */
578 RTCMTList<DnDEvent> m_eventQueue;
579 /** Critical section for providing serialized access to list
580 * event queue's contents. */
581 RTCRITSECT m_eventQueueCS;
582 RTTHREAD m_hHGCMThread;
583 RTTHREAD m_hX11Thread;
584 RTSEMEVENT m_hEventSem;
585 DragInstance *m_pCurDnD;
586 bool m_fSrvStopping;
587
588 friend class DragInstance;
589};
590
591/*******************************************************************************
592 *
593 * DragInstanc Implementation
594 *
595 ******************************************************************************/
596
597DragInstance::DragInstance(Display *pDisplay, DragAndDropService *pParent)
598 : m_pParent(pParent)
599 , m_pDisplay(pDisplay)
600 , m_pScreen(0)
601 , m_wndRoot(0)
602 , m_wndProxy(0)
603 , m_wndCur(0)
604 , m_curVer(-1)
605 , m_pvSelReqData(NULL)
606 , m_cbSelReqData(0)
607 , m_enmMode(Unknown)
608 , m_enmState(Uninitialized)
609{
610}
611
612/**
613 * Unitializes (destroys) this drag instance.
614 */
615void DragInstance::uninit(void)
616{
617 LogFlowFuncEnter();
618
619 if (m_wndProxy != 0)
620 XDestroyWindow(m_pDisplay, m_wndProxy);
621
622 int rc2 = VbglR3DnDDisconnect(&m_dndCtx);
623
624 if (m_pvSelReqData)
625 RTMemFree(m_pvSelReqData);
626
627 rc2 = RTSemEventDestroy(m_eventQueueEvent);
628 AssertRC(rc2);
629
630 rc2 = RTCritSectDelete(&m_eventQueueCS);
631 AssertRC(rc2);
632
633 rc2 = RTCritSectDelete(&m_dataCS);
634 AssertRC(rc2);
635}
636
637/**
638 * Resets this drag instance.
639 */
640void DragInstance::reset(void)
641{
642 LogFlowFuncEnter();
643
644 /* Hide the proxy win. */
645 proxyWinHide();
646
647 int rc2 = RTCritSectEnter(&m_dataCS);
648 if (RT_SUCCESS(rc2))
649 {
650 /* If we are currently the Xdnd selection owner, clear that. */
651 Window pWnd = XGetSelectionOwner(m_pDisplay, xAtom(XA_XdndSelection));
652 if (pWnd == m_wndProxy)
653 XSetSelectionOwner(m_pDisplay, xAtom(XA_XdndSelection), None, CurrentTime);
654
655 /* Clear any other DnD specific data on the proxy window. */
656 wndXDnDClearFormatList(m_wndProxy);
657 wndXDnDClearActionList(m_wndProxy);
658
659 /* Reset the internal state. */
660 m_lstActions.clear();
661 m_lstFormats.clear();
662 m_wndCur = 0;
663 m_curVer = -1;
664 m_enmState = Initialized;
665 m_enmMode = Unknown;
666 m_eventQueueList.clear();
667
668 /* Reset the selection request buffer. */
669 if (m_pvSelReqData)
670 {
671 RTMemFree(m_pvSelReqData);
672 m_pvSelReqData = NULL;
673
674 Assert(m_cbSelReqData);
675 m_cbSelReqData = 0;
676 }
677
678 RTCritSectLeave(&m_dataCS);
679 }
680}
681
682/**
683 * Initializes this drag instance.
684 *
685 * @return IPRT status code.
686 * @param u32ScreenID X' screen ID to use.
687 */
688int DragInstance::init(uint32_t u32ScreenID)
689{
690 int rc;
691
692 do
693 {
694 rc = VbglR3DnDConnect(&m_dndCtx);
695 if (RT_FAILURE(rc))
696 break;
697
698 rc = RTSemEventCreate(&m_eventQueueEvent);
699 if (RT_FAILURE(rc))
700 break;
701
702 rc = RTCritSectInit(&m_eventQueueCS);
703 if (RT_FAILURE(rc))
704 break;
705
706 rc = RTCritSectInit(&m_dataCS);
707 if (RT_FAILURE(rc))
708 break;
709
710 /*
711 * Enough screens configured in the x11 server?
712 */
713 if ((int)u32ScreenID > ScreenCount(m_pDisplay))
714 {
715 rc = VERR_INVALID_PARAMETER;
716 break;
717 }
718#if 0
719 /* Get the screen number from the x11 server. */
720 pDrag->screen = ScreenOfDisplay(m_pDisplay, u32ScreenId);
721 if (!pDrag->screen)
722 {
723 rc = VERR_GENERAL_FAILURE;
724 break;
725 }
726#endif
727 m_screenId = u32ScreenID;
728
729 /* Now query the corresponding root window of this screen. */
730 m_wndRoot = RootWindow(m_pDisplay, m_screenId);
731 if (!m_wndRoot)
732 {
733 rc = VERR_GENERAL_FAILURE;
734 break;
735 }
736
737 /*
738 * Create an invisible window which will act as proxy for the DnD
739 * operation. This window will be used for both the GH and HG
740 * direction.
741 */
742 XSetWindowAttributes attr;
743 RT_ZERO(attr);
744 attr.event_mask = EnterWindowMask | LeaveWindowMask
745 | ButtonMotionMask | ButtonPressMask | ButtonReleaseMask;
746 attr.override_redirect = True;
747 attr.do_not_propagate_mask = NoEventMask;
748#ifdef VBOX_DND_DEBUG_WND
749 attr.background_pixel = XWhitePixel(m_pDisplay, m_screenId);
750 attr.border_pixel = XBlackPixel(m_pDisplay, m_screenId);
751 m_wndProxy = XCreateWindow(m_pDisplay, m_wndRoot /* Parent */,
752 100, 100, /* Position */
753 100, 100, /* Width + height */
754 2, /* Border width */
755 CopyFromParent, /* Depth */
756 InputOutput, /* Class */
757 CopyFromParent, /* Visual */
758 CWBackPixel
759 | CWBorderPixel
760 | CWOverrideRedirect
761 | CWDontPropagate, /* Value mask */
762 &attr); /* Attributes for value mask */
763#else
764 m_wndProxy = XCreateWindow(m_pDisplay, m_wndRoot /* Parent */,
765 0, 0, /* Position */
766 1, 1, /* Width + height */
767 0, /* Border width */
768 CopyFromParent, /* Depth */
769 InputOnly, /* Class */
770 CopyFromParent, /* Visual */
771 CWOverrideRedirect | CWDontPropagate, /* Value mask */
772 &attr); /* Attributes for value mask */
773#endif
774 if (!m_wndProxy)
775 {
776 LogRel(("DnD: Error creating proxy window\n"));
777 rc = VERR_GENERAL_FAILURE;
778 break;
779 }
780
781#ifdef VBOX_DND_DEBUG_WND
782 XFlush(m_pDisplay);
783 XMapWindow(m_pDisplay, m_wndProxy);
784 XRaiseWindow(m_pDisplay, m_wndProxy);
785 XFlush(m_pDisplay);
786#endif
787 logInfo("Proxy window=0x%x, root window=0x%x ...\n", m_wndProxy, m_wndRoot);
788
789 /* Set the window's name for easier lookup. */
790 XStoreName(m_pDisplay, m_wndProxy, "VBoxClientWndDnD");
791
792 /* Make the new window Xdnd aware. */
793 Atom ver = VBOX_XDND_VERSION;
794 XChangeProperty(m_pDisplay, m_wndProxy, xAtom(XA_XdndAware), XA_ATOM, 32, PropModeReplace,
795 reinterpret_cast<unsigned char*>(&ver), 1);
796 } while (0);
797
798 if (RT_SUCCESS(rc))
799 {
800 reset();
801 }
802 else
803 logError("Initializing drag instance for screen %RU32 failed with rc=%Rrc\n", u32ScreenID, rc);
804
805 LogFlowFuncLeaveRC(rc);
806 return rc;
807}
808
809/**
810 * Logs an error message to the (release) logging instance.
811 *
812 * @param pszFormat Format string to log.
813 */
814VBOX_DND_FN_DECL_LOG(void) DragInstance::logError(const char *pszFormat, ...)
815{
816 va_list args;
817 va_start(args, pszFormat);
818 char *psz = NULL;
819 RTStrAPrintfV(&psz, pszFormat, args);
820 va_end(args);
821
822 AssertPtr(psz);
823 LogFlowFunc(("%s", psz));
824 LogRel(("DnD: %s", psz));
825
826 RTStrFree(psz);
827}
828
829/**
830 * Logs an info message to the (release) logging instance.
831 *
832 * @param pszFormat Format string to log.
833 */
834VBOX_DND_FN_DECL_LOG(void) DragInstance::logInfo(const char *pszFormat, ...)
835{
836 va_list args;
837 va_start(args, pszFormat);
838 char *psz = NULL;
839 RTStrAPrintfV(&psz, pszFormat, args);
840 va_end(args);
841
842 AssertPtr(psz);
843 LogFlowFunc(("%s", psz));
844 LogRel2(("DnD: %s", psz));
845
846 RTStrFree(psz);
847}
848
849/**
850 * Callback handler for a generic client message from a window.
851 *
852 * @return IPRT status code.
853 * @param e X11 event to handle.
854 */
855int DragInstance::onX11ClientMessage(const XEvent &e)
856{
857 AssertReturn(e.type == ClientMessage, VERR_INVALID_PARAMETER);
858
859 LogFlowThisFunc(("mode=%RU32, state=%RU32\n", m_enmMode, m_enmState));
860 LogFlowThisFunc(("Event wnd=%#x, msg=%s\n", e.xclient.window, xAtomToString(e.xclient.message_type).c_str()));
861
862 int rc = VINF_SUCCESS;
863
864 switch (m_enmMode)
865 {
866 case HG:
867 {
868 /*
869 * Client messages are used to inform us about the status of a XdndAware
870 * window, in response of some events we send to them.
871 */
872 if ( e.xclient.message_type == xAtom(XA_XdndStatus)
873 && m_wndCur == static_cast<Window>(e.xclient.data.l[XdndStatusWindow]))
874 {
875 bool fAcceptDrop = ASMBitTest (&e.xclient.data.l[XdndStatusFlags], 0); /* Does the target accept the drop? */
876 bool fWantsPosition = ASMBitTest (&e.xclient.data.l[XdndStatusFlags], 1); /* Does the target want XdndPosition messages? */
877 RTCString strActions = xAtomToString( e.xclient.data.l[XdndStatusAction]);
878
879 char *pszWndName = wndX11GetNameA(e.xclient.data.l[XdndStatusWindow]);
880 AssertPtr(pszWndName);
881
882 /*
883 * The XdndStatus message tell us if the window will accept the DnD
884 * event and with which action. We immediately send this info down to
885 * the host as a response of a previous DnD message.
886 */
887 LogFlowThisFunc(("XA_XdndStatus: wnd=%#x ('%s'), fAcceptDrop=%RTbool, fWantsPosition=%RTbool, strActions=%s\n",
888 e.xclient.data.l[XdndStatusWindow], pszWndName, fAcceptDrop, fWantsPosition, strActions.c_str()));
889
890 RTStrFree(pszWndName);
891
892 uint16_t x = RT_HI_U16((uint32_t)e.xclient.data.l[XdndStatusPt]);
893 uint16_t y = RT_LO_U16((uint32_t)e.xclient.data.l[XdndStatusPt]);
894 uint16_t w = RT_HI_U16((uint32_t)e.xclient.data.l[XdndStatusArea]);
895 uint16_t h = RT_LO_U16((uint32_t)e.xclient.data.l[XdndStatusArea]);
896 LogFlowThisFunc(("\tReported dead area: x=%RU16, y=%RU16, w=%RU16, h=%RU16\n", x, y, w, h));
897
898 uint32_t uAction = DND_IGNORE_ACTION; /* Default is ignoring. */
899 /** @todo Compare this with the allowed actions. */
900 if (fAcceptDrop)
901 uAction = toHGCMAction(static_cast<Atom>(e.xclient.data.l[XdndStatusAction]));
902
903 rc = VbglR3DnDHGAcknowledgeOperation(&m_dndCtx, uAction);
904 }
905 else if (e.xclient.message_type == xAtom(XA_XdndFinished))
906 {
907 bool fSucceeded = ASMBitTest(&e.xclient.data.l[XdndFinishedFlags], 0);
908
909 char *pszWndName = wndX11GetNameA(e.xclient.data.l[XdndFinishedWindow]);
910 AssertPtr(pszWndName);
911
912 /* This message is sent on an un/successful DnD drop request. */
913 LogFlowThisFunc(("XA_XdndFinished: wnd=%#x ('%s'), success=%RTbool, action=%s\n",
914 e.xclient.data.l[XdndFinishedWindow], pszWndName, fSucceeded,
915 xAtomToString(e.xclient.data.l[XdndFinishedAction]).c_str()));
916
917 RTStrFree(pszWndName);
918
919 reset();
920 }
921 else
922 {
923 char *pszWndName = wndX11GetNameA(e.xclient.data.l[0]);
924 AssertPtr(pszWndName);
925 LogFlowThisFunc(("Unhandled: wnd=%#x ('%s'), msg=%s\n",
926 e.xclient.data.l[0], pszWndName, xAtomToString(e.xclient.message_type).c_str()));
927 RTStrFree(pszWndName);
928
929 rc = VERR_NOT_SUPPORTED;
930 }
931
932 break;
933 }
934
935 case Unknown: /* Mode not set (yet). */
936 case GH:
937 {
938 /*
939 * This message marks the beginning of a new drag and drop
940 * operation on the guest.
941 */
942 if (e.xclient.message_type == xAtom(XA_XdndEnter))
943 {
944 LogFlowFunc(("XA_XdndEnter\n"));
945
946 /*
947 * Get the window which currently has the XA_XdndSelection
948 * bit set.
949 */
950 Window wndSelection = XGetSelectionOwner(m_pDisplay, xAtom(XA_XdndSelection));
951
952 char *pszWndName = wndX11GetNameA(wndSelection);
953 AssertPtr(pszWndName);
954
955 LogFlowThisFunc(("wndSelection=%#x ('%s'), wndProxy=%#x\n", wndSelection, pszWndName, m_wndProxy));
956
957 RTStrFree(pszWndName);
958
959 mouseButtonSet(m_wndProxy, -1, -1, 1, true /* fPress */);
960
961 /*
962 * Update our state and the window handle to process.
963 */
964 int rc2 = RTCritSectEnter(&m_dataCS);
965 if (RT_SUCCESS(rc2))
966 {
967 m_wndCur = wndSelection;
968 m_curVer = e.xclient.data.l[XdndEnterFlags] >> XdndEnterVersionRShift;
969 Assert(m_wndCur == (Window)e.xclient.data.l[XdndEnterWindow]); /* Source window. */
970#ifdef DEBUG
971 XWindowAttributes xwa;
972 XGetWindowAttributes(m_pDisplay, m_wndCur, &xwa);
973 LogFlowThisFunc(("wndCur=%#x, x=%d, y=%d, width=%d, height=%d\n", m_wndCur, xwa.x, xwa.y, xwa.width, xwa.height));
974#endif
975 /*
976 * Retrieve supported formats.
977 */
978
979 /* Check if the MIME types are in the message itself or if we need
980 * to fetch the XdndTypeList property from the window. */
981 bool fMoreTypes = e.xclient.data.l[XdndEnterFlags] & XdndEnterMoreTypesFlag;
982 LogFlowThisFunc(("XdndVer=%d, fMoreTypes=%RTbool\n", m_curVer, fMoreTypes));
983 if (!fMoreTypes)
984 {
985 /* Only up to 3 format types supported. */
986 /* Start with index 2 (first item). */
987 for (int i = 2; i < 5; i++)
988 {
989 LogFlowThisFunc(("\t%s\n", gX11->xAtomToString(e.xclient.data.l[i]).c_str()));
990 m_lstFormats.append(e.xclient.data.l[i]);
991 }
992 }
993 else
994 {
995 /* More than 3 format types supported. */
996 rc = wndXDnDGetFormatList(wndSelection, m_lstFormats);
997 }
998
999 /*
1000 * Retrieve supported actions.
1001 */
1002 if (RT_SUCCESS(rc))
1003 {
1004 if (m_curVer >= 2) /* More than one action allowed since protocol version 2. */
1005 {
1006 rc = wndXDnDGetActionList(wndSelection, m_lstActions);
1007 }
1008 else /* Only "copy" action allowed on legacy applications. */
1009 m_lstActions.append(XA_XdndActionCopy);
1010 }
1011
1012 if (RT_SUCCESS(rc))
1013 {
1014 m_enmMode = GH;
1015 m_enmState = Dragging;
1016 }
1017
1018 RTCritSectLeave(&m_dataCS);
1019 }
1020 }
1021 else if ( e.xclient.message_type == xAtom(XA_XdndPosition)
1022 && m_wndCur == static_cast<Window>(e.xclient.data.l[XdndPositionWindow]))
1023 {
1024 int32_t lPos = e.xclient.data.l[XdndPositionCoords];
1025 Atom atmAction = m_curVer >= 2 /* Actions other than "copy" or only supported since protocol version 2. */
1026 ? e.xclient.data.l[XdndPositionAction] : xAtom(XA_XdndActionCopy);
1027
1028 LogFlowThisFunc(("XA_XdndPosition: wndProxy=%#x, wndCur=%#x, x=%RI32, y=%RI32, strAction=%s\n",
1029 m_wndProxy, m_wndCur, RT_HIWORD(lPos), RT_LOWORD(lPos),
1030 xAtomToString(atmAction).c_str()));
1031
1032 bool fAcceptDrop = true;
1033
1034 /* Reply with a XdndStatus message to tell the source whether
1035 * the data can be dropped or not. */
1036 XClientMessageEvent m;
1037 RT_ZERO(m);
1038 m.type = ClientMessage;
1039 m.display = m_pDisplay;
1040 m.window = e.xclient.data.l[XdndPositionWindow];
1041 m.message_type = xAtom(XA_XdndStatus);
1042 m.format = 32;
1043 m.data.l[XdndStatusWindow] = m_wndProxy;
1044 m.data.l[XdndStatusFlags] = fAcceptDrop ? RT_BIT(0) : 0; /* Whether to accept the drop or not. */
1045 m.data.l[XdndStatusAction] = fAcceptDrop ? toAtomAction(DND_COPY_ACTION) : None; /** @todo Handle default action! */
1046
1047 int xRc = XSendEvent(m_pDisplay, e.xclient.data.l[XdndPositionWindow],
1048 False /* Propagate */, NoEventMask, reinterpret_cast<XEvent *>(&m));
1049 if (xRc == 0)
1050 logError("Error sending position XA_XdndStatus event to current window=%#x: %s\n",
1051 m_wndCur, gX11->xErrorToString(xRc).c_str());
1052 }
1053 else if ( e.xclient.message_type == xAtom(XA_XdndLeave)
1054 && m_wndCur == static_cast<Window>(e.xclient.data.l[XdndLeaveWindow]))
1055 {
1056 LogFlowThisFunc(("XA_XdndLeave\n"));
1057 logInfo("Guest to host transfer canceled by the guest source window\n");
1058
1059 /* Start over. */
1060 reset();
1061 }
1062 else if ( e.xclient.message_type == xAtom(XA_XdndDrop)
1063 && m_wndCur == static_cast<Window>(e.xclient.data.l[XdndDropWindow]))
1064 {
1065 LogFlowThisFunc(("XA_XdndDrop\n"));
1066 logInfo("Notified guest target window that drop operation is finished\n");
1067
1068 m_eventQueueList.append(e);
1069 rc = RTSemEventSignal(m_eventQueueEvent);
1070 }
1071 else if ( e.xclient.message_type == xAtom(XA_XdndFinished)
1072 && m_wndCur == static_cast<Window>(e.xclient.data.l[XdndFinishedWindow]))
1073 {
1074 LogFlowThisFunc(("XA_XdndFinished\n"));
1075 logInfo("Guest target window finished drag and drop operation\n");
1076
1077 m_eventQueueList.append(e);
1078 rc = RTSemEventSignal(m_eventQueueEvent);
1079 }
1080 break;
1081 }
1082
1083 default:
1084 {
1085 AssertMsgFailed(("Drag and drop mode not implemented: %RU32\n", m_enmMode));
1086 rc = VERR_NOT_IMPLEMENTED;
1087 break;
1088 }
1089 }
1090
1091 LogFlowThisFunc(("Returning rc=%Rrc\n", rc));
1092 return rc;
1093}
1094
1095int DragInstance::onX11MotionNotify(const XEvent &e)
1096{
1097 LogFlowThisFunc(("mode=%RU32, state=%RU32\n", m_enmMode, m_enmState));
1098
1099 return VINF_SUCCESS;
1100}
1101
1102/**
1103 * Callback handler for being notified if some other window now
1104 * is the owner of the current selection.
1105 *
1106 * @return IPRT status code.
1107 * @param e X11 event to handle.
1108 *
1109 * @remark
1110 */
1111int DragInstance::onX11SelectionClear(const XEvent &e)
1112{
1113 LogFlowThisFunc(("mode=%RU32, state=%RU32\n", m_enmMode, m_enmState));
1114
1115 return VINF_SUCCESS;
1116}
1117
1118/**
1119 * Callback handler for a XDnD selection notify from a window. This is needed
1120 * to let the us know if a certain window has drag'n drop data to share with us,
1121 * e.g. our proxy window.
1122 *
1123 * @return IPRT status code.
1124 * @param e X11 event to handle.
1125 */
1126int DragInstance::onX11SelectionNotify(const XEvent &e)
1127{
1128 AssertReturn(e.type == SelectionNotify, VERR_INVALID_PARAMETER);
1129
1130 LogFlowThisFunc(("mode=%RU32, state=%RU32\n", m_enmMode, m_enmState));
1131
1132 int rc;
1133
1134 switch (m_enmMode)
1135 {
1136 case GH:
1137 {
1138 if (m_enmState == Dropped)
1139 {
1140 m_eventQueueList.append(e);
1141 rc = RTSemEventSignal(m_eventQueueEvent);
1142 }
1143 else
1144 rc = VERR_WRONG_ORDER;
1145 break;
1146 }
1147
1148 default:
1149 {
1150 LogFlowThisFunc(("Unhandled: wnd=%#x, msg=%s\n",
1151 e.xclient.data.l[0], xAtomToString(e.xclient.message_type).c_str()));
1152 rc = VERR_INVALID_STATE;
1153 break;
1154 }
1155 }
1156
1157 LogFlowThisFunc(("Returning rc=%Rrc\n", rc));
1158 return rc;
1159}
1160
1161/**
1162 * Callback handler for a XDnD selection request from a window. This is needed
1163 * to retrieve the data required to complete the actual drag'n drop operation.
1164 *
1165 * @returns IPRT status code.
1166 * @param e X11 event to handle.
1167 */
1168int DragInstance::onX11SelectionRequest(const XEvent &e)
1169{
1170 AssertReturn(e.type == SelectionRequest, VERR_INVALID_PARAMETER);
1171
1172 LogFlowThisFunc(("mode=%RU32, state=%RU32\n", m_enmMode, m_enmState));
1173 LogFlowThisFunc(("Event owner=%#x, requestor=%#x, selection=%s, target=%s, prop=%s, time=%u\n",
1174 e.xselectionrequest.owner,
1175 e.xselectionrequest.requestor,
1176 xAtomToString(e.xselectionrequest.selection).c_str(),
1177 xAtomToString(e.xselectionrequest.target).c_str(),
1178 xAtomToString(e.xselectionrequest.property).c_str(),
1179 e.xselectionrequest.time));
1180 int rc;
1181
1182 switch (m_enmMode)
1183 {
1184 case HG:
1185 {
1186 rc = VINF_SUCCESS;
1187
1188 char *pszWndName = wndX11GetNameA(e.xselectionrequest.requestor);
1189 AssertPtr(pszWndName);
1190
1191 /*
1192 * Start by creating a refusal selection notify message.
1193 * That way we only need to care for the success case.
1194 */
1195
1196 XEvent s;
1197 RT_ZERO(s);
1198 s.xselection.type = SelectionNotify;
1199 s.xselection.display = e.xselectionrequest.display;
1200 s.xselection.requestor = e.xselectionrequest.requestor;
1201 s.xselection.selection = e.xselectionrequest.selection;
1202 s.xselection.target = e.xselectionrequest.target;
1203 s.xselection.property = None; /* "None" means refusal. */
1204 s.xselection.time = e.xselectionrequest.time;
1205
1206 const XSelectionRequestEvent *pReq = &e.xselectionrequest;
1207
1208#ifdef DEBUG
1209 LogFlowFunc(("Supported formats:\n"));
1210 for (size_t i = 0; i < m_lstFormats.size(); i++)
1211 LogFlowFunc(("\t%s\n", xAtomToString(m_lstFormats.at(i)).c_str()));
1212#endif
1213 /* Is the requestor asking for the possible MIME types? */
1214 if (pReq->target == xAtom(XA_TARGETS))
1215 {
1216 logInfo("Target window %#x ('%s') asking for target list\n", e.xselectionrequest.requestor, pszWndName);
1217
1218 /* If so, set the window property with the formats on the requestor
1219 * window. */
1220 rc = wndXDnDSetFormatList(pReq->requestor, pReq->property, m_lstFormats);
1221 if (RT_SUCCESS(rc))
1222 s.xselection.property = pReq->property;
1223 }
1224 /* Is the requestor asking for a specific MIME type (we support)? */
1225 else if (m_lstFormats.contains(pReq->target))
1226 {
1227 logInfo("Target window %#x ('%s') is asking for data as '%s'\n",
1228 pReq->requestor, pszWndName, xAtomToString(pReq->target).c_str());
1229
1230 /* Did we not drop our stuff to the guest yet? Bail out. */
1231 if (m_enmState != Dropped)
1232 {
1233 LogFlowThisFunc(("Wrong state (%RU32), refusing request\n", m_enmState));
1234 }
1235 /* Did we not store the requestor's initial selection request yet? Then do so now. */
1236 else
1237 {
1238 /* Get the data format the requestor wants from us. */
1239 RTCString strFormat = xAtomToString(pReq->target);
1240 Assert(strFormat.isNotEmpty());
1241 logInfo("Target window=%#x requested data from host as '%s', rc=%Rrc\n",
1242 pReq->requestor, strFormat.c_str(), rc);
1243
1244 /* Make a copy of the MIME data to be passed back. The X server will be become
1245 * the new owner of that data, so no deletion needed. */
1246 /** @todo Do we need to do some more conversion here? XConvertSelection? */
1247 void *pvData = RTMemDup(m_pvSelReqData, m_cbSelReqData);
1248 uint32_t cbData = m_cbSelReqData;
1249
1250 /* Always return the requested property. */
1251 s.xselection.property = pReq->property;
1252
1253 /* Note: Always seems to return BadRequest. Seems fine. */
1254 int xRc = XChangeProperty(s.xselection.display, s.xselection.requestor, s.xselection.property,
1255 s.xselection.target, 8, PropModeReplace,
1256 reinterpret_cast<const unsigned char*>(pvData), cbData);
1257
1258 LogFlowFunc(("Changing property '%s' (target '%s') of window=0x%x: %s\n",
1259 xAtomToString(pReq->property).c_str(),
1260 xAtomToString(pReq->target).c_str(),
1261 pReq->requestor,
1262 gX11->xErrorToString(xRc).c_str()));
1263 }
1264 }
1265 /* Anything else. */
1266 else
1267 {
1268 logError("Refusing unknown command/format '%s' of wnd=%#x ('%s')\n",
1269 xAtomToString(e.xselectionrequest.target).c_str(), pReq->requestor, pszWndName);
1270 rc = VERR_NOT_SUPPORTED;
1271 }
1272
1273 LogFlowThisFunc(("Offering type '%s', property '%s' to wnd=%#x ...\n",
1274 xAtomToString(pReq->target).c_str(),
1275 xAtomToString(pReq->property).c_str(), pReq->requestor));
1276
1277 int xRc = XSendEvent(pReq->display, pReq->requestor, True /* Propagate */, 0, &s);
1278 if (xRc == 0)
1279 logError("Error sending SelectionNotify(1) event to wnd=%#x: %s\n", pReq->requestor,
1280 gX11->xErrorToString(xRc).c_str());
1281 XFlush(pReq->display);
1282
1283 if (pszWndName)
1284 RTStrFree(pszWndName);
1285 break;
1286 }
1287
1288 default:
1289 rc = VERR_INVALID_STATE;
1290 break;
1291 }
1292
1293 LogFlowThisFunc(("Returning rc=%Rrc\n", rc));
1294 return rc;
1295}
1296
1297/**
1298 * Handles X11 events, called by x11EventThread.
1299 *
1300 * @returns IPRT status code.
1301 * @param e X11 event to handle.
1302 */
1303int DragInstance::onX11Event(const XEvent &e)
1304{
1305 int rc;
1306
1307 LogFlowThisFunc(("X11 event, type=%d\n", e.type));
1308 switch (e.type)
1309 {
1310 case ButtonPress:
1311 LogFlowThisFunc(("ButtonPress\n"));
1312 rc = VINF_SUCCESS;
1313 break;
1314
1315 case ButtonRelease:
1316 LogFlowThisFunc(("ButtonRelease\n"));
1317 rc = VINF_SUCCESS;
1318 break;
1319
1320 case ClientMessage:
1321 rc = onX11ClientMessage(e);
1322 break;
1323
1324 case SelectionClear:
1325 rc = onX11SelectionClear(e);
1326 break;
1327
1328 case SelectionNotify:
1329 rc = onX11SelectionNotify(e);
1330 break;
1331
1332 case SelectionRequest:
1333 rc = onX11SelectionRequest(e);
1334 break;
1335
1336 case MotionNotify:
1337 rc = onX11MotionNotify(e);
1338 break;
1339
1340 default:
1341 rc = VERR_NOT_IMPLEMENTED;
1342 break;
1343 }
1344
1345 LogFlowThisFunc(("rc=%Rrc\n", rc));
1346 return rc;
1347}
1348
1349int DragInstance::waitForStatusChange(uint32_t enmState, RTMSINTERVAL uTimeoutMS /* = 30000 */)
1350{
1351 const uint64_t uiStart = RTTimeMilliTS();
1352 volatile uint32_t enmCurState;
1353
1354 int rc = VERR_TIMEOUT;
1355
1356 LogFlowFunc(("enmState=%RU32, uTimeoutMS=%RU32\n", enmState, uTimeoutMS));
1357
1358 do
1359 {
1360 enmCurState = ASMAtomicReadU32(&m_enmState);
1361 if (enmCurState == enmState)
1362 {
1363 rc = VINF_SUCCESS;
1364 break;
1365 }
1366 }
1367 while (RTTimeMilliTS() - uiStart < uTimeoutMS);
1368
1369 LogFlowThisFunc(("Returning %Rrc\n", rc));
1370 return rc;
1371}
1372
1373#ifdef VBOX_WITH_DRAG_AND_DROP_GH
1374/**
1375 * Waits for an X11 event of a specific type.
1376 *
1377 * @returns IPRT status code.
1378 * @param evX Reference where to store the event into.
1379 * @param iType Event type to wait for.
1380 * @param uTimeoutMS Timeout (in ms) to wait for the event.
1381 */
1382bool DragInstance::waitForX11Msg(XEvent &evX, int iType, RTMSINTERVAL uTimeoutMS /* = 100 */)
1383{
1384 LogFlowThisFunc(("iType=%d, uTimeoutMS=%RU32, cEventQueue=%zu\n", iType, uTimeoutMS, m_eventQueueList.size()));
1385
1386 bool fFound = false;
1387 const uint64_t uiStart = RTTimeMilliTS();
1388
1389 do
1390 {
1391 /* Check if there is a client message in the queue. */
1392 for (size_t i = 0; i < m_eventQueueList.size(); i++)
1393 {
1394 int rc2 = RTCritSectEnter(&m_eventQueueCS);
1395 if (RT_SUCCESS(rc2))
1396 {
1397 XEvent e = m_eventQueueList.at(i);
1398
1399 fFound = e.type == iType;
1400 if (fFound)
1401 {
1402 m_eventQueueList.removeAt(i);
1403 evX = e;
1404 }
1405
1406 rc2 = RTCritSectLeave(&m_eventQueueCS);
1407 AssertRC(rc2);
1408
1409 if (fFound)
1410 break;
1411 }
1412 }
1413
1414 if (fFound)
1415 break;
1416
1417 int rc2 = RTSemEventWait(m_eventQueueEvent, 25 /* ms */);
1418 if ( RT_FAILURE(rc2)
1419 && rc2 != VERR_TIMEOUT)
1420 {
1421 LogFlowFunc(("Waiting failed with rc=%Rrc\n", rc2));
1422 break;
1423 }
1424 }
1425 while (RTTimeMilliTS() - uiStart < uTimeoutMS);
1426
1427 LogFlowThisFunc(("Returning fFound=%RTbool, msRuntime=%RU64\n", fFound, RTTimeMilliTS() - uiStart));
1428 return fFound;
1429}
1430
1431/**
1432 * Waits for an X11 client message of a specific type.
1433 *
1434 * @returns IPRT status code.
1435 * @param evMsg Reference where to store the event into.
1436 * @param aType Event type to wait for.
1437 * @param uTimeoutMS Timeout (in ms) to wait for the event.
1438 */
1439bool DragInstance::waitForX11ClientMsg(XClientMessageEvent &evMsg, Atom aType,
1440 RTMSINTERVAL uTimeoutMS /* = 100 */)
1441{
1442 LogFlowThisFunc(("aType=%s, uTimeoutMS=%RU32, cEventQueue=%zu\n",
1443 xAtomToString(aType).c_str(), uTimeoutMS, m_eventQueueList.size()));
1444
1445 bool fFound = false;
1446 const uint64_t uiStart = RTTimeMilliTS();
1447 do
1448 {
1449 /* Check if there is a client message in the queue. */
1450 for (size_t i = 0; i < m_eventQueueList.size(); i++)
1451 {
1452 int rc2 = RTCritSectEnter(&m_eventQueueCS);
1453 if (RT_SUCCESS(rc2))
1454 {
1455 XEvent e = m_eventQueueList.at(i);
1456 if ( e.type == ClientMessage
1457 && e.xclient.message_type == aType)
1458 {
1459 m_eventQueueList.removeAt(i);
1460 evMsg = e.xclient;
1461
1462 fFound = true;
1463 }
1464
1465 if (e.type == ClientMessage)
1466 {
1467 LogFlowThisFunc(("Client message: Type=%ld (%s)\n",
1468 e.xclient.message_type, xAtomToString(e.xclient.message_type).c_str()));
1469 }
1470 else
1471 LogFlowThisFunc(("X message: Type=%d\n", e.type));
1472
1473 rc2 = RTCritSectLeave(&m_eventQueueCS);
1474 AssertRC(rc2);
1475
1476 if (fFound)
1477 break;
1478 }
1479 }
1480
1481 if (fFound)
1482 break;
1483
1484 int rc2 = RTSemEventWait(m_eventQueueEvent, 25 /* ms */);
1485 if ( RT_FAILURE(rc2)
1486 && rc2 != VERR_TIMEOUT)
1487 {
1488 LogFlowFunc(("Waiting failed with rc=%Rrc\n", rc2));
1489 break;
1490 }
1491 }
1492 while (RTTimeMilliTS() - uiStart < uTimeoutMS);
1493
1494 LogFlowThisFunc(("Returning fFound=%RTbool, msRuntime=%RU64\n", fFound, RTTimeMilliTS() - uiStart));
1495 return fFound;
1496}
1497#endif /* VBOX_WITH_DRAG_AND_DROP_GH */
1498
1499/*
1500 * Host -> Guest
1501 */
1502
1503/**
1504 * Host -> Guest: Event signalling that the host's (mouse) cursor just entered the VM's (guest's) display
1505 * area.
1506 *
1507 * @returns IPRT status code.
1508 * @param lstFormats List of supported formats from the host.
1509 * @param uActions (ORed) List of supported actions from the host.
1510 */
1511int DragInstance::hgEnter(const RTCList<RTCString> &lstFormats, uint32_t uActions)
1512{
1513 LogFlowThisFunc(("mode=%RU32, state=%RU32\n", m_enmMode, m_enmState));
1514
1515 if (m_enmMode != Unknown)
1516 return VERR_INVALID_STATE;
1517
1518 reset();
1519
1520#ifdef DEBUG
1521 LogFlowThisFunc(("uActions=0x%x, lstFormats=%zu: ", uActions, lstFormats.size()));
1522 for (size_t i = 0; i < lstFormats.size(); ++i)
1523 LogFlow(("'%s' ", lstFormats.at(i).c_str()));
1524 LogFlow(("\n"));
1525#endif
1526
1527 int rc;
1528
1529 do
1530 {
1531 rc = toAtomList(lstFormats, m_lstFormats);
1532 if (RT_FAILURE(rc))
1533 break;
1534
1535 /* If we have more than 3 formats we have to use the type list extension. */
1536 if (m_lstFormats.size() > 3)
1537 {
1538 rc = wndXDnDSetFormatList(m_wndProxy, xAtom(XA_XdndTypeList), m_lstFormats);
1539 if (RT_FAILURE(rc))
1540 break;
1541 }
1542
1543 /* Announce the possible actions. */
1544 VBoxDnDAtomList lstActions;
1545 rc = toAtomActions(uActions, lstActions);
1546 if (RT_FAILURE(rc))
1547 break;
1548 rc = wndXDnDSetActionList(m_wndProxy, lstActions);
1549
1550 /* Set the DnD selection owner to our window. */
1551 /** @todo Don't use CurrentTime -- according to ICCCM section 2.1. */
1552 XSetSelectionOwner(m_pDisplay, xAtom(XA_XdndSelection), m_wndProxy, CurrentTime);
1553
1554 m_enmMode = HG;
1555 m_enmState = Dragging;
1556
1557 } while (0);
1558
1559 LogFlowFuncLeaveRC(rc);
1560 return rc;
1561}
1562
1563/**
1564 * Host -> Guest: Event signalling that the host's (mouse) cursor has left the VM's (guest's)
1565 * display area.
1566 */
1567int DragInstance::hgLeave(void)
1568{
1569 if (m_enmMode == HG) /* Only reset if in the right operation mode. */
1570 reset();
1571
1572 return VINF_SUCCESS;
1573}
1574
1575/**
1576 * Host -> Guest: Event signalling that the host's (mouse) cursor has been moved within the VM's
1577 * (guest's) display area.
1578 *
1579 * @returns IPRT status code.
1580 * @param u32xPos Relative X position within the guest's display area.
1581 * @param u32yPos Relative Y position within the guest's display area.
1582 * @param uDefaultAction Default action the host wants to perform on the guest
1583 * as soon as the operation successfully finishes.
1584 */
1585int DragInstance::hgMove(uint32_t u32xPos, uint32_t u32yPos, uint32_t uDefaultAction)
1586{
1587 LogFlowThisFunc(("mode=%RU32, state=%RU32\n", m_enmMode, m_enmState));
1588 LogFlowThisFunc(("u32xPos=%RU32, u32yPos=%RU32, uAction=%RU32\n", u32xPos, u32yPos, uDefaultAction));
1589
1590 if ( m_enmMode != HG
1591 || m_enmState != Dragging)
1592 {
1593 return VERR_INVALID_STATE;
1594 }
1595
1596 int rc = VINF_SUCCESS;
1597 int xRc = Success;
1598
1599 /* Move the mouse cursor within the guest. */
1600 mouseCursorMove(u32xPos, u32yPos);
1601
1602 long newVer = -1; /* This means the current window is _not_ XdndAware. */
1603
1604 /* Search for the application window below the cursor. */
1605 Window wndCursor = gX11->applicationWindowBelowCursor(m_wndRoot);
1606 if (wndCursor != None)
1607 {
1608 /* Temp stuff for the XGetWindowProperty call. */
1609 Atom atmp;
1610 int fmt;
1611 unsigned long cItems, cbRemaining;
1612 unsigned char *pcData = NULL;
1613
1614 /* Query the XdndAware property from the window. We are interested in
1615 * the version and if it is XdndAware at all. */
1616 xRc = XGetWindowProperty(m_pDisplay, wndCursor, xAtom(XA_XdndAware),
1617 0, 2, False, AnyPropertyType,
1618 &atmp, &fmt, &cItems, &cbRemaining, &pcData);
1619 if (xRc != Success)
1620 {
1621 logError("Error getting properties of cursor window=%#x: %s\n", wndCursor, gX11->xErrorToString(xRc).c_str());
1622 }
1623 else
1624 {
1625 if (pcData == NULL || fmt != 32 || cItems != 1)
1626 {
1627 /** @todo Do we need to deal with this? */
1628 logError("Wrong window properties for window %#x: pcData=%#x, iFmt=%d, cItems=%ul\n",
1629 wndCursor, pcData, fmt, cItems);
1630 }
1631 else
1632 {
1633 /* Get the current window's Xdnd version. */
1634 newVer = reinterpret_cast<long *>(pcData)[0];
1635 }
1636
1637 XFree(pcData);
1638 }
1639 }
1640
1641#ifdef DEBUG
1642 char *pszNameCursor = wndX11GetNameA(wndCursor);
1643 AssertPtr(pszNameCursor);
1644 char *pszNameCur = wndX11GetNameA(m_wndCur);
1645 AssertPtr(pszNameCur);
1646
1647 LogFlowThisFunc(("wndCursor=%x ('%s', Xdnd version %ld), wndCur=%x ('%s', Xdnd version %ld)\n",
1648 wndCursor, pszNameCursor, newVer, m_wndCur, pszNameCur, m_curVer));
1649
1650 RTStrFree(pszNameCursor);
1651 RTStrFree(pszNameCur);
1652#endif
1653
1654 if ( wndCursor != m_wndCur
1655 && m_curVer != -1)
1656 {
1657 LogFlowThisFunc(("XA_XdndLeave: window=%#x\n", m_wndCur));
1658
1659 char *pszWndName = wndX11GetNameA(m_wndCur);
1660 AssertPtr(pszWndName);
1661 logInfo("Left old window %#x ('%s'), Xdnd version=%ld\n", m_wndCur, pszWndName, newVer);
1662 RTStrFree(pszWndName);
1663
1664 /* We left the current XdndAware window. Announce this to the current indow. */
1665 XClientMessageEvent m;
1666 RT_ZERO(m);
1667 m.type = ClientMessage;
1668 m.display = m_pDisplay;
1669 m.window = m_wndCur;
1670 m.message_type = xAtom(XA_XdndLeave);
1671 m.format = 32;
1672 m.data.l[XdndLeaveWindow] = m_wndProxy;
1673
1674 xRc = XSendEvent(m_pDisplay, m_wndCur, False, NoEventMask, reinterpret_cast<XEvent*>(&m));
1675 if (xRc == 0)
1676 logError("Error sending XA_XdndLeave event to old window=%#x: %s\n", m_wndCur, gX11->xErrorToString(xRc).c_str());
1677
1678 /* Reset our current window. */
1679 m_wndCur = 0;
1680 m_curVer = -1;
1681 }
1682
1683 /*
1684 * Do we have a new Xdnd-aware window which now is under the cursor?
1685 */
1686 if ( wndCursor != m_wndCur
1687 && newVer != -1)
1688 {
1689 LogFlowThisFunc(("XA_XdndEnter: window=%#x\n", wndCursor));
1690
1691 char *pszWndName = wndX11GetNameA(wndCursor);
1692 AssertPtr(pszWndName);
1693 logInfo("Entered new window %#x ('%s'), supports Xdnd version=%ld\n", wndCursor, pszWndName, newVer);
1694 RTStrFree(pszWndName);
1695
1696 /*
1697 * We enter a new window. Announce the XdndEnter event to the new
1698 * window. The first three mime types are attached to the event (the
1699 * others could be requested by the XdndTypeList property from the
1700 * window itself).
1701 */
1702 XClientMessageEvent m;
1703 RT_ZERO(m);
1704 m.type = ClientMessage;
1705 m.display = m_pDisplay;
1706 m.window = wndCursor;
1707 m.message_type = xAtom(XA_XdndEnter);
1708 m.format = 32;
1709 m.data.l[XdndEnterWindow] = m_wndProxy;
1710 m.data.l[XdndEnterFlags] = RT_MAKE_U32_FROM_U8(
1711 /* Bit 0 is set if the source supports more than three data types. */
1712 m_lstFormats.size() > 3 ? RT_BIT(0) : 0,
1713 /* Reserved for future use. */
1714 0, 0,
1715 /* Protocol version to use. */
1716 RT_MIN(VBOX_XDND_VERSION, newVer));
1717 m.data.l[XdndEnterType1] = m_lstFormats.value(0, None); /* First data type to use. */
1718 m.data.l[XdndEnterType2] = m_lstFormats.value(1, None); /* Second data type to use. */
1719 m.data.l[XdndEnterType3] = m_lstFormats.value(2, None); /* Third data type to use. */
1720
1721 xRc = XSendEvent(m_pDisplay, wndCursor, False, NoEventMask, reinterpret_cast<XEvent*>(&m));
1722 if (xRc == 0)
1723 logError("Error sending XA_XdndEnter event to window=%#x: %s\n", wndCursor, gX11->xErrorToString(xRc).c_str());
1724 }
1725
1726 if (newVer != -1)
1727 {
1728 Assert(wndCursor != None);
1729
1730 LogFlowThisFunc(("XA_XdndPosition: xPos=%RU32, yPos=%RU32 to window=%#x\n", u32xPos, u32yPos, wndCursor));
1731
1732 /*
1733 * Send a XdndPosition event with the proposed action to the guest.
1734 */
1735 Atom pa = toAtomAction(uDefaultAction);
1736 LogFlowThisFunc(("strAction=%s\n", xAtomToString(pa).c_str()));
1737
1738 XClientMessageEvent m;
1739 RT_ZERO(m);
1740 m.type = ClientMessage;
1741 m.display = m_pDisplay;
1742 m.window = wndCursor;
1743 m.message_type = xAtom(XA_XdndPosition);
1744 m.format = 32;
1745 m.data.l[XdndPositionWindow] = m_wndProxy; /* X window ID of source window. */
1746 m.data.l[XdndPositionCoords] = RT_MAKE_U32(u32yPos, u32xPos); /* Cursor coordinates relative to the root window. */
1747 m.data.l[XdndPositionTimeStamp] = CurrentTime; /* Timestamp for retrieving data. */
1748 m.data.l[XdndPositionAction] = pa; /* Actions requested by the user. */
1749
1750 xRc = XSendEvent(m_pDisplay, wndCursor, False, NoEventMask, reinterpret_cast<XEvent*>(&m));
1751 if (xRc == 0)
1752 logError("Error sending XA_XdndPosition event to current window=%#x: %s\n", wndCursor, gX11->xErrorToString(xRc).c_str());
1753 }
1754
1755 if (newVer == -1)
1756 {
1757 /* No window to process, so send a ignore ack event to the host. */
1758 rc = VbglR3DnDHGAcknowledgeOperation(&m_dndCtx, DND_IGNORE_ACTION);
1759 }
1760 else
1761 {
1762 Assert(wndCursor != None);
1763
1764 m_wndCur = wndCursor;
1765 m_curVer = newVer;
1766 }
1767
1768 LogFlowFuncLeaveRC(rc);
1769 return rc;
1770}
1771
1772/**
1773 * Host -> Guest: Event signalling that the host has dropped the data over the VM (guest) window.
1774 *
1775 * @returns IPRT status code.
1776 * @param u32xPos Relative X position within the guest's display area.
1777 * @param u32yPos Relative Y position within the guest's display area.
1778 * @param uDefaultAction Default action the host wants to perform on the guest
1779 * as soon as the operation successfully finishes.
1780 */
1781int DragInstance::hgDrop(uint32_t u32xPos, uint32_t u32yPos, uint32_t uDefaultAction)
1782{
1783 LogFlowThisFunc(("wndCur=%#x, wndProxy=%#x, mode=%RU32, state=%RU32\n", m_wndCur, m_wndProxy, m_enmMode, m_enmState));
1784 LogFlowThisFunc(("u32xPos=%RU32, u32yPos=%RU32, uAction=%RU32\n", u32xPos, u32yPos, uDefaultAction));
1785
1786 if ( m_enmMode != HG
1787 || m_enmState != Dragging)
1788 {
1789 return VERR_INVALID_STATE;
1790 }
1791
1792 /* Set the state accordingly. */
1793 m_enmState = Dropped;
1794
1795 /*
1796 * Ask the host to send the raw data, as we don't (yet) know which format
1797 * the guest exactly expects. As blocking in a SelectionRequest message turned
1798 * out to be very unreliable (e.g. with KDE apps) we request to start transferring
1799 * file/directory data (if any) here.
1800 */
1801 char szFormat[] = { "text/uri-list" };
1802
1803 int rc = VbglR3DnDHGRequestData(&m_dndCtx, szFormat);
1804 logInfo("Drop event from host resuled in: %Rrc\n", rc);
1805
1806 LogFlowFuncLeaveRC(rc);
1807 return rc;
1808}
1809
1810/**
1811 * Host -> Guest: Event signalling that the host has finished sending drag'n drop
1812 * data to the guest for further processing.
1813 *
1814 * @returns IPRT status code.
1815 * @param pvData Pointer to (MIME) data from host.
1816 * @param cbData Size (in bytes) of data from host.
1817 */
1818int DragInstance::hgDataReceived(const void *pvData, uint32_t cbData)
1819{
1820 LogFlowThisFunc(("mode=%RU32, state=%RU32\n", m_enmMode, m_enmState));
1821 LogFlowThisFunc(("pvData=%p, cbData=%RU32\n", pvData, cbData));
1822
1823 if ( m_enmMode != HG
1824 || m_enmState != Dropped)
1825 {
1826 return VERR_INVALID_STATE;
1827 }
1828
1829 if ( pvData == NULL
1830 || cbData == 0)
1831 {
1832 return VERR_INVALID_PARAMETER;
1833 }
1834
1835 int rc = VINF_SUCCESS;
1836
1837 /*
1838 * At this point all data needed (including sent files/directories) should
1839 * be on the guest, so proceed working on communicating with the target window.
1840 */
1841 logInfo("Received %RU32 bytes MIME data from host\n", cbData);
1842
1843 /* Destroy any old data. */
1844 if (m_pvSelReqData)
1845 {
1846 Assert(m_cbSelReqData);
1847
1848 RTMemFree(m_pvSelReqData); /** @todo RTMemRealloc? */
1849 m_cbSelReqData = 0;
1850 }
1851
1852 /** @todo Handle incremental transfers. */
1853
1854 /* Make a copy of the data. This data later then will be used to fill into
1855 * the selection request. */
1856 if (cbData)
1857 {
1858 m_pvSelReqData = RTMemAlloc(cbData);
1859 if (!m_pvSelReqData)
1860 return VERR_NO_MEMORY;
1861
1862 memcpy(m_pvSelReqData, pvData, cbData);
1863 m_cbSelReqData = cbData;
1864 }
1865
1866 /*
1867 * Send a drop event to the current window (target).
1868 * This window in turn then will raise a SelectionRequest message to our proxy window,
1869 * which we will handle in our onX11SelectionRequest handler.
1870 *
1871 * The SelectionRequest will tell us in which format the target wants the data from the host.
1872 */
1873 XClientMessageEvent m;
1874 RT_ZERO(m);
1875 m.type = ClientMessage;
1876 m.display = m_pDisplay;
1877 m.window = m_wndCur;
1878 m.message_type = xAtom(XA_XdndDrop);
1879 m.format = 32;
1880 m.data.l[XdndDropWindow] = m_wndProxy; /* Source window. */
1881 m.data.l[XdndDropFlags] = 0; /* Reserved for future use. */
1882 m.data.l[XdndDropTimeStamp] = CurrentTime; /* Our DnD data does not rely on any timing, so just use the current time. */
1883
1884 int xRc = XSendEvent(m_pDisplay, m_wndCur, False /* Propagate */, NoEventMask, reinterpret_cast<XEvent*>(&m));
1885 if (xRc == 0)
1886 logError("Error sending XA_XdndDrop event to window=%#x: %s\n", m_wndCur, gX11->xErrorToString(xRc).c_str());
1887 XFlush(m_pDisplay);
1888
1889 LogFlowFuncLeaveRC(rc);
1890 return rc;
1891}
1892
1893#ifdef VBOX_WITH_DRAG_AND_DROP_GH
1894/**
1895 * Guest -> Host: Event signalling that the host is asking whether there is a pending
1896 * drag event on the guest (to the host).
1897 *
1898 * @returns IPRT status code.
1899 */
1900int DragInstance::ghIsDnDPending(void)
1901{
1902 LogFlowThisFunc(("mode=%RU32, state=%RU32\n", m_enmMode, m_enmState));
1903
1904 /* Currently in wrong mode? Bail out. */
1905 if (m_enmMode == HG)
1906 return VERR_INVALID_STATE;
1907
1908 /* Message already processed successfully? */
1909 if ( m_enmMode == GH
1910 && ( m_enmState == Dragging
1911 || m_enmState == Dropped)
1912 )
1913 {
1914 return VERR_INVALID_STATE;
1915 }
1916
1917 int rc = VINF_SUCCESS;
1918
1919 /* Determine the current window which currently has the XdndSelection set. */
1920 Window wndSelection = XGetSelectionOwner(m_pDisplay, xAtom(XA_XdndSelection));
1921 LogFlowThisFunc(("wndSelection=%#x, wndProxy=%#x, wndCur=%#x\n", wndSelection, m_wndProxy, m_wndCur));
1922
1923 RTCString strFormats = "None"; /** @todo If empty, IOCTL fails with VERR_ACCESS_DENIED. */
1924 uint32_t uDefAction = DND_IGNORE_ACTION;
1925 uint32_t uAllActions = DND_IGNORE_ACTION;
1926
1927 /* Is this another window which has a Xdnd selection and not our proxy window? */
1928 if ( wndSelection
1929 && wndSelection != m_wndCur)
1930 {
1931#ifdef DEBUG
1932 char *pszWndName = wndX11GetNameA(wndSelection);
1933 AssertPtr(pszWndName);
1934 LogFlowThisFunc(("*** New source window ('%s') ***\n", pszWndName));
1935 RTStrFree(pszWndName);
1936#endif
1937 /* Start over. */
1938 reset();
1939
1940 /* Map the window on the current cursor position, which should provoke
1941 * an XdndEnter event. */
1942 rc = proxyWinShow(NULL, NULL);
1943 if (RT_SUCCESS(rc))
1944 {
1945 /* Wait until we're in "Dragging" state. */
1946 rc = waitForStatusChange(Dragging, 5 * 1000);
1947 if (RT_SUCCESS(rc))
1948 m_enmMode = GH;
1949 }
1950 }
1951 else
1952 RTThreadSleep(3000);
1953
1954 /*
1955 * Acknowledge to the host in any case, regardless
1956 * if something failed here or not. Be responsive.
1957 */
1958
1959 int rc2 = RTCritSectEnter(&m_dataCS);
1960 if (RT_SUCCESS(rc2))
1961 {
1962 strFormats = gX11->xAtomListToString(m_lstFormats);
1963 if (strFormats.isEmpty())
1964 strFormats = "\r\n"; /** @todo If empty, IOCTL fails with VERR_ACCESS_DENIED. */
1965 uDefAction = DND_COPY_ACTION; /** @todo Handle default action! */
1966 uAllActions = DND_COPY_ACTION; /** @todo Ditto. */
1967 uAllActions |= toHGCMActions(m_lstActions);
1968
1969 RTCritSectLeave(&m_dataCS);
1970 }
1971
1972 rc2 = VbglR3DnDGHAcknowledgePending(&m_dndCtx, uDefAction, uAllActions, strFormats.c_str());
1973 LogFlowThisFunc(("uClientID=%RU32, uDefAction=0x%x, allActions=0x%x, strFormats=%s, rc=%Rrc\n",
1974 m_dndCtx.uClientID, uDefAction, uAllActions, strFormats.c_str(), rc2));
1975 if (RT_FAILURE(rc2))
1976 {
1977 logError("Error reporting pending drag and drop operation status to host: %Rrc\n", rc2);
1978 if (RT_SUCCESS(rc))
1979 rc = rc2;
1980 }
1981
1982 if (RT_FAILURE(rc)) /* Start over on failure. */
1983 reset();
1984
1985 LogFlowFuncLeaveRC(rc);
1986 return rc;
1987}
1988
1989/**
1990 * Guest -> Host: Event signalling that the host has dropped the item(s) on the
1991 * host side.
1992 *
1993 * @returns IPRT status code.
1994 * @param strFormat Requested format to send to the host.
1995 * @param uAction Requested action to perform on the guest.
1996 */
1997int DragInstance::ghDropped(const RTCString &strFormat, uint32_t uAction)
1998{
1999 LogFlowThisFunc(("mode=%RU32, state=%RU32, strFormat=%s, uAction=%RU32\n",
2000 m_enmMode, m_enmState, strFormat.c_str(), uAction));
2001
2002 /* Currently in wrong mode? Bail out. */
2003 if ( m_enmMode == Unknown
2004 || m_enmMode == HG)
2005 {
2006 return VERR_INVALID_STATE;
2007 }
2008
2009 if ( m_enmMode == GH
2010 && m_enmState != Dragging)
2011 {
2012 return VERR_INVALID_STATE;
2013 }
2014
2015 int rc = VINF_SUCCESS;
2016
2017 m_enmState = Dropped;
2018
2019 /* Show the proxy window, so that the current source window will find it. */
2020 int iRootX, iRootY;
2021 proxyWinShow(&iRootX, &iRootY);
2022
2023#ifdef DEBUG
2024 XWindowAttributes xwa;
2025 XGetWindowAttributes(m_pDisplay, m_wndCur, &xwa);
2026 LogFlowThisFunc(("wndProxy=%#x, wndCur=%#x, x=%d, y=%d, width=%d, height=%d\n",
2027 m_wndProxy, m_wndCur, xwa.x, xwa.y, xwa.width, xwa.height));
2028#endif
2029
2030 /* We send a fake release event to the current window, cause
2031 * this should have the grab. */
2032#if 0
2033 //mouseButtonSet(m_wndCur /* Destination window */, xwa.x + (xwa.width / 2), xwa.y + (xwa.height / 2), 1 /* Button */, false /* fPress */);
2034#else
2035 mouseButtonSet(m_wndCur /* Destination window */, -1 /* Root X */, -1 /* Root Y */, 1 /* Button */, false /* fPress */);
2036#endif
2037
2038 /**
2039 * The fake button release event above should lead to a XdndDrop event from the
2040 * source window. Because of showing our proxy window, other Xdnd events can
2041 * occur before, e.g. a XdndPosition event. We are not interested
2042 * in those, so just try to get the right one.
2043 */
2044
2045 XClientMessageEvent evDnDDrop;
2046 bool fDrop = waitForX11ClientMsg(evDnDDrop, xAtom(XA_XdndDrop), 5 * 1000 /* 5s timeout */);
2047 if (fDrop)
2048 {
2049 LogFlowThisFunc(("XA_XdndDrop\n"));
2050
2051 /* Request to convert the selection in the specific format and
2052 * place it to our proxy window as property. */
2053 Assert(evDnDDrop.message_type == xAtom(XA_XdndDrop));
2054
2055 Window wndSource = evDnDDrop.data.l[XdndDropWindow]; /* Source window which has sent the message. */
2056 Assert(wndSource == m_wndCur);
2057
2058 Atom aFormat = gX11->stringToxAtom(strFormat.c_str());
2059
2060 Time tsDrop;
2061 if (m_curVer >= 1)
2062 tsDrop = evDnDDrop.data.l[XdndDropTimeStamp];
2063 else
2064 tsDrop = CurrentTime;
2065
2066 XConvertSelection(m_pDisplay, xAtom(XA_XdndSelection), aFormat, xAtom(XA_XdndSelection),
2067 m_wndProxy, tsDrop);
2068
2069 /* Wait for the selection notify event. */
2070 XEvent evSelNotify;
2071 RT_ZERO(evSelNotify);
2072 if (waitForX11Msg(evSelNotify, SelectionNotify, 5 * 1000 /* 5s timeout */))
2073 {
2074 bool fCancel = false;
2075
2076 /* Make some paranoid checks. */
2077 if ( evSelNotify.xselection.type == SelectionNotify
2078 && evSelNotify.xselection.display == m_pDisplay
2079 && evSelNotify.xselection.selection == xAtom(XA_XdndSelection)
2080 && evSelNotify.xselection.requestor == m_wndProxy
2081 && evSelNotify.xselection.target == aFormat)
2082 {
2083 LogFlowThisFunc(("Selection notfiy (from wnd=%#x)\n", m_wndCur));
2084
2085 Atom aPropType;
2086 int iPropFormat;
2087 unsigned long cItems, cbRemaining;
2088 unsigned char *pcData = NULL;
2089 int xRc = XGetWindowProperty(m_pDisplay, m_wndProxy,
2090 xAtom(XA_XdndSelection) /* Property */,
2091 0 /* Offset */,
2092 VBOX_MAX_XPROPERTIES /* Length of 32-bit multiples */,
2093 True /* Delete property? */,
2094 AnyPropertyType, /* Property type */
2095 &aPropType, &iPropFormat, &cItems, &cbRemaining, &pcData);
2096 if (xRc != Success)
2097 logError("Error getting XA_XdndSelection property of proxy window=%#x: %s\n",
2098 m_wndProxy, gX11->xErrorToString(xRc).c_str());
2099
2100 LogFlowThisFunc(("strType=%s, iPropFormat=%d, cItems=%RU32, cbRemaining=%RU32\n",
2101 gX11->xAtomToString(aPropType).c_str(), iPropFormat, cItems, cbRemaining));
2102
2103 if ( aPropType != None
2104 && pcData != NULL
2105 && iPropFormat >= 8
2106 && cItems > 0
2107 && cbRemaining == 0)
2108 {
2109 size_t cbData = cItems * (iPropFormat / 8);
2110 LogFlowThisFunc(("cbData=%zu\n", cbData));
2111
2112 /* For whatever reason some of the string MIME types are not
2113 * zero terminated. Check that and correct it when necessary,
2114 * because the guest side wants this in any case. */
2115 if ( m_lstAllowedFormats.contains(strFormat)
2116 && pcData[cbData - 1] != '\0')
2117 {
2118 unsigned char *pvDataTmp = static_cast<unsigned char*>(RTMemAlloc(cbData + 1));
2119 if (pvDataTmp)
2120 {
2121 memcpy(pvDataTmp, pcData, cbData);
2122 pvDataTmp[cbData++] = '\0';
2123
2124 rc = VbglR3DnDGHSendData(&m_dndCtx, strFormat.c_str(), pvDataTmp, cbData);
2125 RTMemFree(pvDataTmp);
2126 }
2127 else
2128 rc = VERR_NO_MEMORY;
2129 }
2130 else
2131 {
2132 /* Send the raw data to the host. */
2133 rc = VbglR3DnDGHSendData(&m_dndCtx, strFormat.c_str(), pcData, cbData);
2134 }
2135
2136 LogFlowThisFunc(("Sent strFormat=%s, rc=%Rrc\n", strFormat.c_str(), rc));
2137
2138 if (RT_SUCCESS(rc))
2139 {
2140 /* Was the drop accepted by the host? That is, anything than ignoring. */
2141 bool fDropAccepted = uAction > DND_IGNORE_ACTION;
2142
2143 /* Confirm the result of the transfer to the target window. */
2144 XClientMessageEvent m;
2145 RT_ZERO(m);
2146 m.type = ClientMessage;
2147 m.display = m_pDisplay;
2148 m.window = wndSource;
2149 m.message_type = xAtom(XA_XdndFinished);
2150 m.format = 32;
2151 m.data.l[XdndFinishedWindow] = m_wndProxy; /* Target window. */
2152 m.data.l[XdndFinishedFlags] = fDropAccepted ? RT_BIT(0) : 0; /* Was the drop accepted? */
2153 m.data.l[XdndFinishedAction] = fDropAccepted ? toAtomAction(uAction) : None; /* Action used on accept. */
2154
2155 xRc = XSendEvent(m_pDisplay, wndSource, True, NoEventMask, reinterpret_cast<XEvent*>(&m));
2156 if (xRc == 0)
2157 logError("Error sending XA_XdndFinished event to source window=%#x: %s\n",
2158 wndSource, gX11->xErrorToString(xRc).c_str());
2159 }
2160 else
2161 fCancel = true;
2162 }
2163 else
2164 {
2165 if (aPropType == xAtom(XA_INCR))
2166 {
2167 /** @todo Support incremental transfers. */
2168 AssertMsgFailed(("Incremental transfers are not supported yet\n"));
2169
2170 logError("Incremental transfers are not supported yet\n");
2171 rc = VERR_NOT_IMPLEMENTED;
2172 }
2173 else
2174 {
2175 logError("Not supported data type: %s\n", gX11->xAtomToString(aPropType).c_str());
2176 rc = VERR_NOT_SUPPORTED;
2177 }
2178
2179 fCancel = true;
2180 }
2181
2182 if (fCancel)
2183 {
2184 LogFlowFunc(("Cancelling drop ...\n"));
2185
2186 /* Cancel the operation -- inform the source window by
2187 * sending a XdndFinished message so that the source can toss the required data. */
2188 XClientMessageEvent m;
2189 RT_ZERO(m);
2190 m.type = ClientMessage;
2191 m.display = m_pDisplay;
2192 m.window = m_wndProxy;
2193 m.message_type = xAtom(XA_XdndFinished);
2194 m.format = 32;
2195 m.data.l[XdndFinishedWindow] = m_wndProxy; /* Target window. */
2196 m.data.l[XdndFinishedFlags] = 0; /* Did not accept the drop. */
2197 m.data.l[XdndFinishedAction] = None; /* Action used on accept. */
2198
2199 xRc = XSendEvent(m_pDisplay, wndSource, False, NoEventMask, reinterpret_cast<XEvent*>(&m));
2200 if (xRc == 0)
2201 logError("Error sending XA_XdndFinished event to source window=%#x: %s\n",
2202 wndSource, gX11->xErrorToString(xRc).c_str());
2203 }
2204
2205 /* Cleanup. */
2206 if (pcData)
2207 XFree(pcData);
2208 }
2209 else
2210 rc = VERR_INVALID_PARAMETER;
2211 }
2212 else
2213 rc = VERR_TIMEOUT;
2214 }
2215 else
2216 rc = VERR_TIMEOUT;
2217
2218 /* Inform the host on error. */
2219 if (RT_FAILURE(rc))
2220 {
2221 int rc2 = VbglR3DnDGHSendError(&m_dndCtx, rc);
2222 LogFlowThisFunc(("Sending error to host resulted in %Rrc\n", rc2));
2223 /* This is not fatal for us, just ignore. */
2224 }
2225
2226 /* At this point, we have either successfully transfered any data or not.
2227 * So reset our internal state because we are done here for the current (ongoing)
2228 * drag and drop operation. */
2229 reset();
2230
2231 LogFlowFuncLeaveRC(rc);
2232 return rc;
2233}
2234#endif /* VBOX_WITH_DRAG_AND_DROP_GH */
2235
2236/*
2237 * Helpers
2238 */
2239
2240/**
2241 * Moves the mouse pointer to a specific position.
2242 *
2243 * @returns IPRT status code.
2244 * @param iPosX Absolute X coordinate.
2245 * @param iPosY Absolute Y coordinate.
2246 */
2247int DragInstance::mouseCursorMove(int iPosX, int iPosY) const
2248{
2249 int iScreenID = XDefaultScreen(m_pDisplay);
2250 /** @todo What about multiple screens? Test this! */
2251
2252 const int iScrX = DisplayWidth(m_pDisplay, iScreenID);
2253 const int iScrY = XDisplayHeight(m_pDisplay, iScreenID);
2254
2255 iPosX = RT_CLAMP(iPosX, 0, iScrX);
2256 iPosY = RT_CLAMP(iPosY, 0, iScrY);
2257
2258 LogFlowThisFunc(("iPosX=%d, iPosY=%d\n", iPosX, iPosY));
2259
2260 /* Move the guest pointer to the DnD position, so we can find the window
2261 * below that position. */
2262 XWarpPointer(m_pDisplay, None, m_wndRoot, 0, 0, 0, 0, iPosX, iPosY);
2263 return VINF_SUCCESS;
2264}
2265
2266/**
2267 * Sends a mouse button event to a specific window.
2268 *
2269 * @param wndDest Window to send the mouse button event to.
2270 * @param rx X coordinate relative to the root window's origin.
2271 * @param ry Y coordinate relative to the root window's origin.
2272 * @param iButton Mouse button to press/release.
2273 * @param fPress Whether to press or release the mouse button.
2274 */
2275void DragInstance::mouseButtonSet(Window wndDest, int rx, int ry, int iButton, bool fPress)
2276{
2277 LogFlowThisFunc(("wndDest=%#x, rx=%d, ry=%d, iBtn=%d, fPress=%RTbool\n",
2278 wndDest, rx, ry, iButton, fPress));
2279
2280#ifdef VBOX_DND_WITH_XTEST
2281 /** @todo Make this check run only once. */
2282 int ev, er, ma, mi;
2283 if (XTestQueryExtension(m_pDisplay, &ev, &er, &ma, &mi))
2284 {
2285 LogFlowThisFunc(("XText extension available\n"));
2286
2287 int xRc = XTestFakeButtonEvent(m_pDisplay, 1, fPress ? True : False, CurrentTime);
2288 if (Rc == 0)
2289 logError("Error sending XTestFakeButtonEvent event: %s\n", gX11->xErrorToString(xRc).c_str());
2290 XFlush(m_pDisplay);
2291 }
2292 else
2293 {
2294#endif
2295 LogFlowThisFunc(("Note: XText extension not available or disabled\n"));
2296
2297 if ( rx == -1
2298 && ry == -1)
2299 {
2300 Window wndTemp, wndChild;
2301 int wx, wy; unsigned int mask;
2302 XQueryPointer(m_pDisplay, m_wndRoot, &wndTemp, &wndChild, &rx, &ry, &wx, &wy, &mask);
2303 LogFlowThisFunc(("cursorRootX=%d, cursorRootY=%d\n", rx, ry));
2304 }
2305
2306 XButtonEvent eBtn;
2307 RT_ZERO(eBtn);
2308
2309 eBtn.display = m_pDisplay;
2310 eBtn.root = m_wndRoot;
2311 eBtn.window = wndDest;
2312 eBtn.subwindow = None;
2313 eBtn.same_screen = True;
2314 eBtn.time = CurrentTime;
2315 eBtn.button = iButton;
2316 eBtn.state |= iButton == 1 ? Button1Mask /*:
2317 iButton == 2 ? Button2MotionMask :
2318 iButton == 3 ? Button3MotionMask :
2319 iButton == 4 ? Button4MotionMask :
2320 iButton == 5 ? Button5MotionMask*/ : 0;
2321 eBtn.type = fPress ? ButtonPress : ButtonRelease;
2322 eBtn.send_event = False;
2323 eBtn.x_root = rx;
2324 eBtn.y_root = ry;
2325
2326 XTranslateCoordinates(m_pDisplay, eBtn.root, eBtn.window, eBtn.x_root, eBtn.y_root, &eBtn.x, &eBtn.y, &eBtn.subwindow);
2327 LogFlowThisFunc(("x=%d, y=%d\n", eBtn.x, eBtn.y));
2328#if 1
2329 int xRc = XSendEvent(m_pDisplay, wndDest, True /* fPropagate */,
2330 fPress
2331 ? ButtonPressMask : ButtonReleaseMask,
2332 reinterpret_cast<XEvent*>(&eBtn));
2333 if (xRc == 0)
2334 logError("Error sending XButtonEvent event to window=%#x: %s\n", wndDest, gX11->xErrorToString(xRc).c_str());
2335#else
2336 int xRc = XSendEvent(m_pDisplay, eBtn.window, False /* fPropagate */,
2337 0 /* Mask */, reinterpret_cast<XEvent*>(&eBtn));
2338 if (xRc == 0)
2339 logError("Error sending XButtonEvent event to window=%#x: %s\n", wndDest, gX11->xErrorToString(xRc).c_str());
2340#endif
2341
2342#ifdef VBOX_DND_WITH_XTEST
2343 }
2344#endif
2345}
2346
2347/**
2348 * Shows the (invisible) proxy window. The proxy window is needed for intercepting
2349 * drags from the host to the guest or from the guest to the host. It acts as a proxy
2350 * between the host and the actual (UI) element on the guest OS.
2351 *
2352 * To not make it miss any actions this window gets spawned across the entire guest
2353 * screen (think of an umbrella) to (hopefully) capture everything. A proxy window
2354 * which follows the cursor would be far too slow here.
2355 *
2356 * @returns IPRT status code.
2357 * @param piRootX X coordinate relative to the root window's origin. Optional.
2358 * @param piRootY Y coordinate relative to the root window's origin. Optional.
2359 */
2360int DragInstance::proxyWinShow(int *piRootX /* = NULL */, int *piRootY /* = NULL */) const
2361{
2362 /* piRootX is optional. */
2363 /* piRootY is optional. */
2364
2365 LogFlowThisFuncEnter();
2366
2367 int rc = VINF_SUCCESS;
2368
2369#if 0
2370# ifdef VBOX_DND_WITH_XTEST
2371 XTestGrabControl(m_pDisplay, False);
2372# endif
2373#endif
2374
2375 /* Get the mouse pointer position and determine if we're on the same screen as the root window
2376 * and return the current child window beneath our mouse pointer, if any. */
2377 int iRootX, iRootY;
2378 int iChildX, iChildY;
2379 unsigned int iMask;
2380 Window wndRoot, wndChild;
2381 Bool fInRootWnd = XQueryPointer(m_pDisplay, m_wndRoot, &wndRoot, &wndChild,
2382 &iRootX, &iRootY, &iChildX, &iChildY, &iMask);
2383
2384 LogFlowThisFunc(("fInRootWnd=%RTbool, wndRoot=0x%x, wndChild=0x%x, iRootX=%d, iRootY=%d\n",
2385 RT_BOOL(fInRootWnd), wndRoot, wndChild, iRootX, iRootY));
2386
2387 if (piRootX)
2388 *piRootX = iRootX;
2389 if (piRootY)
2390 *piRootY = iRootY;
2391
2392 XSynchronize(m_pDisplay, True /* Enable sync */);
2393
2394 /* Bring our proxy window into foreground. */
2395 XMapWindow(m_pDisplay, m_wndProxy);
2396 XRaiseWindow(m_pDisplay, m_wndProxy);
2397
2398 /* Spawn our proxy window over the entire screen, making it an easy drop target for the host's cursor. */
2399 int iScreenID = XDefaultScreen(m_pDisplay);
2400 LogFlowThisFunc(("Proxy window screenID=%d, x=%d, y=%d, width=%d, height=%d\n",
2401 iScreenID, 0, 0, XDisplayWidth(m_pDisplay, iScreenID), XDisplayHeight(m_pDisplay, iScreenID)));
2402 XMoveResizeWindow(m_pDisplay, m_wndProxy, 0, 0, XDisplayWidth(m_pDisplay, iScreenID), XDisplayHeight(m_pDisplay, iScreenID));
2403 /** @todo What about multiple screens? Test this! */
2404
2405 XFlush(m_pDisplay);
2406
2407 XSynchronize(m_pDisplay, False /* Disable sync */);
2408
2409#if 0
2410# ifdef VBOX_DND_WITH_XTEST
2411 XTestGrabControl(m_pDisplay, True);
2412# endif
2413#endif
2414
2415 LogFlowFuncLeaveRC(rc);
2416 return rc;
2417}
2418
2419/**
2420 * Hides the (invisible) proxy window.
2421 */
2422int DragInstance::proxyWinHide(void)
2423{
2424 LogFlowFuncEnter();
2425
2426 XUnmapWindow(m_pDisplay, m_wndProxy);
2427 XFlush(m_pDisplay);
2428
2429 m_eventQueueList.clear();
2430
2431 return VINF_SUCCESS; /** @todo Add error checking. */
2432}
2433
2434/**
2435 * Allocates the name (title) of an X window.
2436 * The returned pointer must be freed using RTStrFree().
2437 *
2438 * @returns Pointer to the allocated window name.
2439 * @param wndThis Window to retrieve name for.
2440 *
2441 * @remark If the window title is not available, the text
2442 * "<No name>" will be returned.
2443 */
2444char *DragInstance::wndX11GetNameA(Window wndThis) const
2445{
2446 char *pszName = NULL;
2447
2448 XTextProperty propName;
2449 if (XGetWMName(m_pDisplay, wndThis, &propName))
2450 {
2451 if (propName.value)
2452 pszName = RTStrDup((char *)propName.value); /** @todo UTF8? */
2453 XFree(propName.value);
2454 }
2455
2456 if (!pszName) /* No window name found? */
2457 pszName = RTStrDup("<No name>");
2458
2459 return pszName;
2460}
2461
2462/**
2463 * Clear a window's supported/accepted actions list.
2464 *
2465 * @param wndThis Window to clear the list for.
2466 */
2467void DragInstance::wndXDnDClearActionList(Window wndThis) const
2468{
2469 XDeleteProperty(m_pDisplay, wndThis, xAtom(XA_XdndActionList));
2470}
2471
2472/**
2473 * Clear a window's supported/accepted formats list.
2474 *
2475 * @param wndThis Window to clear the list for.
2476 */
2477void DragInstance::wndXDnDClearFormatList(Window wndThis) const
2478{
2479 XDeleteProperty(m_pDisplay, wndThis, xAtom(XA_XdndTypeList));
2480}
2481
2482/**
2483 * Retrieves a window's supported/accepted XDnD actions.
2484 *
2485 * @returns IPRT status code.
2486 * @param wndThis Window to retrieve the XDnD actions for.
2487 * @param lstActions Reference to VBoxDnDAtomList to store the action into.
2488 */
2489int DragInstance::wndXDnDGetActionList(Window wndThis, VBoxDnDAtomList &lstActions) const
2490{
2491 Atom iActType = None;
2492 int iActFmt;
2493 unsigned long cItems, cbData;
2494 unsigned char *pcbData = NULL;
2495
2496 /* Fetch the possible list of actions, if this property is set. */
2497 int xRc = XGetWindowProperty(m_pDisplay, wndThis,
2498 xAtom(XA_XdndActionList),
2499 0, VBOX_MAX_XPROPERTIES,
2500 False, XA_ATOM, &iActType, &iActFmt, &cItems, &cbData, &pcbData);
2501 if (xRc != Success)
2502 {
2503 LogFlowThisFunc(("Error getting XA_XdndActionList atoms from window=%#x: %s\n",
2504 wndThis, gX11->xErrorToString(xRc).c_str()));
2505 return VERR_NOT_FOUND;
2506 }
2507
2508 LogFlowThisFunc(("wndThis=%#x, cItems=%RU32, pcbData=%p\n", wndThis, cItems, pcbData));
2509
2510 if (cItems > 0)
2511 {
2512 AssertPtr(pcbData);
2513 Atom *paData = reinterpret_cast<Atom *>(pcbData);
2514
2515 for (unsigned i = 0; i < RT_MIN(VBOX_MAX_XPROPERTIES, cItems); i++)
2516 {
2517 LogFlowThisFunc(("\t%s\n", gX11->xAtomToString(paData[i]).c_str()));
2518 lstActions.append(paData[i]);
2519 }
2520
2521 XFree(pcbData);
2522 }
2523
2524 return VINF_SUCCESS;
2525}
2526
2527/**
2528 * Retrieves a window's supported/accepted XDnD formats.
2529 *
2530 * @returns IPRT status code.
2531 * @param wndThis Window to retrieve the XDnD formats for.
2532 * @param lstTypes Reference to VBoxDnDAtomList to store the formats into.
2533 */
2534int DragInstance::wndXDnDGetFormatList(Window wndThis, VBoxDnDAtomList &lstTypes) const
2535{
2536 Atom iActType = None;
2537 int iActFmt;
2538 unsigned long cItems, cbData;
2539 unsigned char *pcbData = NULL;
2540
2541 int xRc = XGetWindowProperty(m_pDisplay, wndThis,
2542 xAtom(XA_XdndTypeList),
2543 0, VBOX_MAX_XPROPERTIES,
2544 False, XA_ATOM, &iActType, &iActFmt, &cItems, &cbData, &pcbData);
2545 if (xRc != Success)
2546 {
2547 LogFlowThisFunc(("Error getting XA_XdndTypeList atoms from window=%#x: %s\n",
2548 wndThis, gX11->xErrorToString(xRc).c_str()));
2549 return VERR_NOT_FOUND;
2550 }
2551
2552 LogFlowThisFunc(("wndThis=%#x, cItems=%RU32, pcbData=%p\n", wndThis, cItems, pcbData));
2553
2554 if (cItems > 0)
2555 {
2556 AssertPtr(pcbData);
2557 Atom *paData = reinterpret_cast<Atom *>(pcbData);
2558
2559 for (unsigned i = 0; i < RT_MIN(VBOX_MAX_XPROPERTIES, cItems); i++)
2560 {
2561 LogFlowThisFunc(("\t%s\n", gX11->xAtomToString(paData[i]).c_str()));
2562 lstTypes.append(paData[i]);
2563 }
2564
2565 XFree(pcbData);
2566 }
2567
2568 return VINF_SUCCESS;
2569}
2570
2571/**
2572 * Sets (replaces) a window's XDnD accepted/allowed actions.
2573 *
2574 * @returns IPRT status code.
2575 * @param wndThis Window to set the format list for.
2576 * @param lstActions Reference to list of XDnD actions to set.
2577 *
2578 * @remark
2579 */
2580int DragInstance::wndXDnDSetActionList(Window wndThis, const VBoxDnDAtomList &lstActions) const
2581{
2582 if (lstActions.isEmpty())
2583 return VINF_SUCCESS;
2584
2585 XChangeProperty(m_pDisplay, wndThis,
2586 xAtom(XA_XdndActionList),
2587 XA_ATOM, 32, PropModeReplace,
2588 reinterpret_cast<const unsigned char*>(lstActions.raw()),
2589 lstActions.size());
2590
2591 return VINF_SUCCESS;
2592}
2593
2594/**
2595 * Sets (replaces) a window's XDnD accepted format list.
2596 *
2597 * @returns IPRT status code.
2598 * @param wndThis Window to set the format list for.
2599 * @param atmProp Property to set.
2600 * @param lstFormats Reference to list of XDnD formats to set.
2601 */
2602int DragInstance::wndXDnDSetFormatList(Window wndThis, Atom atmProp, const VBoxDnDAtomList &lstFormats) const
2603{
2604 if (lstFormats.isEmpty())
2605 return VERR_INVALID_PARAMETER;
2606
2607 /* We support TARGETS and the data types. */
2608 VBoxDnDAtomList lstFormatsExt(lstFormats.size() + 1);
2609 lstFormatsExt.append(xAtom(XA_TARGETS));
2610 lstFormatsExt.append(lstFormats);
2611
2612 /* Add the property with the property data to the window. */
2613 XChangeProperty(m_pDisplay, wndThis, atmProp,
2614 XA_ATOM, 32, PropModeReplace,
2615 reinterpret_cast<const unsigned char*>(lstFormatsExt.raw()),
2616 lstFormatsExt.size());
2617
2618 return VINF_SUCCESS;
2619}
2620
2621/**
2622 * Converts a RTCString list to VBoxDnDAtomList list.
2623 *
2624 * @returns IPRT status code.
2625 * @param lstFormats Reference to RTCString list to convert.
2626 * @param lstAtoms Reference to VBoxDnDAtomList list to store results in.
2627 */
2628int DragInstance::toAtomList(const RTCList<RTCString> &lstFormats, VBoxDnDAtomList &lstAtoms) const
2629{
2630 for (size_t i = 0; i < lstFormats.size(); ++i)
2631 lstAtoms.append(XInternAtom(m_pDisplay, lstFormats.at(i).c_str(), False));
2632
2633 return VINF_SUCCESS;
2634}
2635
2636/**
2637 * Converts a raw-data string list to VBoxDnDAtomList list.
2638 *
2639 * @returns IPRT status code.
2640 * @param pvData Pointer to string data to convert.
2641 * @param cbData Size (in bytes) to convert.
2642 * @param lstAtoms Reference to VBoxDnDAtomList list to store results in.
2643 */
2644int DragInstance::toAtomList(const void *pvData, uint32_t cbData, VBoxDnDAtomList &lstAtoms) const
2645{
2646 AssertPtrReturn(pvData, VERR_INVALID_POINTER);
2647 AssertReturn(cbData, VERR_INVALID_PARAMETER);
2648
2649 const char *pszStr = (char *)pvData;
2650 uint32_t cbStr = cbData;
2651
2652 int rc = VINF_SUCCESS;
2653
2654 VBoxDnDAtomList lstAtom;
2655 while (cbStr)
2656 {
2657 size_t cbSize = RTStrNLen(pszStr, cbStr);
2658
2659 /* Create a copy with max N chars, so that we are on the save side,
2660 * even if the data isn't zero terminated. */
2661 char *pszTmp = RTStrDupN(pszStr, cbSize);
2662 if (!pszTmp)
2663 {
2664 rc = VERR_NO_MEMORY;
2665 break;
2666 }
2667
2668 lstAtom.append(XInternAtom(m_pDisplay, pszTmp, False));
2669 RTStrFree(pszTmp);
2670
2671 pszStr += cbSize + 1;
2672 cbStr -= cbSize + 1;
2673 }
2674
2675 return rc;
2676}
2677
2678/**
2679 * Converts a HGCM-based drag'n drop action to a Atom-based drag'n drop action.
2680 *
2681 * @returns Converted Atom-based drag'n drop action.
2682 * @param uActions HGCM drag'n drop actions to convert.
2683 */
2684/* static */
2685Atom DragInstance::toAtomAction(uint32_t uAction)
2686{
2687 /* Ignore is None. */
2688 return (isDnDCopyAction(uAction) ? xAtom(XA_XdndActionCopy) :
2689 isDnDMoveAction(uAction) ? xAtom(XA_XdndActionMove) :
2690 isDnDLinkAction(uAction) ? xAtom(XA_XdndActionLink) :
2691 None);
2692}
2693
2694/**
2695 * Converts HGCM-based drag'n drop actions to a VBoxDnDAtomList list.
2696 *
2697 * @returns IPRT status code.
2698 * @param uActions HGCM drag'n drop actions to convert.
2699 * @param lstAtoms Reference to VBoxDnDAtomList to store actions in.
2700 */
2701/* static */
2702int DragInstance::toAtomActions(uint32_t uActions, VBoxDnDAtomList &lstAtoms)
2703{
2704 if (hasDnDCopyAction(uActions))
2705 lstAtoms.append(xAtom(XA_XdndActionCopy));
2706 if (hasDnDMoveAction(uActions))
2707 lstAtoms.append(xAtom(XA_XdndActionMove));
2708 if (hasDnDLinkAction(uActions))
2709 lstAtoms.append(xAtom(XA_XdndActionLink));
2710
2711 return VINF_SUCCESS;
2712}
2713
2714/**
2715 * Converts an Atom-based drag'n drop action to a HGCM drag'n drop action.
2716 *
2717 * @returns HGCM drag'n drop action.
2718 * @param atom Atom-based drag'n drop action to convert.
2719 */
2720/* static */
2721uint32_t DragInstance::toHGCMAction(Atom atom)
2722{
2723 uint32_t uAction = DND_IGNORE_ACTION;
2724
2725 if (atom == xAtom(XA_XdndActionCopy))
2726 uAction = DND_COPY_ACTION;
2727 else if (atom == xAtom(XA_XdndActionMove))
2728 uAction = DND_MOVE_ACTION;
2729 else if (atom == xAtom(XA_XdndActionLink))
2730 uAction = DND_LINK_ACTION;
2731
2732 return uAction;
2733}
2734
2735/**
2736 * Converts an VBoxDnDAtomList list to an HGCM action list.
2737 *
2738 * @returns ORed HGCM action list.
2739 * @param actionsList List of Atom-based actions to convert.
2740 */
2741/* static */
2742uint32_t DragInstance::toHGCMActions(const VBoxDnDAtomList &lstActions)
2743{
2744 uint32_t uActions = DND_IGNORE_ACTION;
2745
2746 for (size_t i = 0; i < lstActions.size(); i++)
2747 uActions |= toHGCMAction(lstActions.at(i));
2748
2749 return uActions;
2750}
2751
2752/*******************************************************************************
2753 * DragAndDropService implementation.
2754 ******************************************************************************/
2755
2756/**
2757 * Main loop for the drag and drop service which does the HGCM message
2758 * processing and routing to the according drag and drop instance(s).
2759 *
2760 * @returns IPRT status code.
2761 * @param fDaemonised Whether to run in daemonized or not. Does not
2762 * apply for this service.
2763 */
2764int DragAndDropService::run(bool fDaemonised /* = false */)
2765{
2766 LogFlowThisFunc(("fDaemonised=%RTbool\n", fDaemonised));
2767
2768 int rc;
2769 do
2770 {
2771 /* Initialize drag and drop. */
2772 rc = dragAndDropInit();
2773 if (RT_FAILURE(rc))
2774 break;
2775
2776 m_pCurDnD = new DragInstance(m_pDisplay, this);
2777 if (!m_pCurDnD)
2778 {
2779 rc = VERR_NO_MEMORY;
2780 break;
2781 }
2782
2783 /* Note: For multiple screen support in VBox it is not necessary to use
2784 * another screen number than zero. Maybe in the future it will become
2785 * necessary if VBox supports multiple X11 screens. */
2786 rc = m_pCurDnD->init(0);
2787 if (RT_FAILURE(rc))
2788 break;
2789
2790 LogRel(("DnD: Started\n"));
2791 LogRel2(("DnD: %sr%s\n", RTBldCfgVersion(), RTBldCfgRevisionStr()));
2792
2793 /* Enter the main event processing loop. */
2794 do
2795 {
2796 DnDEvent e;
2797 RT_ZERO(e);
2798
2799 LogFlowFunc(("Waiting for new event ...\n"));
2800 rc = RTSemEventWait(m_hEventSem, RT_INDEFINITE_WAIT);
2801 if (RT_FAILURE(rc))
2802 break;
2803
2804 AssertMsg(m_eventQueue.size(),
2805 ("Event queue is empty when it shouldn't\n"));
2806
2807 e = m_eventQueue.first();
2808 m_eventQueue.removeFirst();
2809
2810 if (e.type == DnDEvent::HGCM_Type)
2811 {
2812 LogFlowThisFunc(("HGCM event, type=%RU32\n", e.hgcm.uType));
2813 switch (e.hgcm.uType)
2814 {
2815 case DragAndDropSvc::HOST_DND_HG_EVT_ENTER:
2816 {
2817 if (e.hgcm.cbFormats)
2818 {
2819 RTCList<RTCString> lstFormats = RTCString(e.hgcm.pszFormats, e.hgcm.cbFormats - 1).split("\r\n");
2820 rc = m_pCurDnD->hgEnter(lstFormats, e.hgcm.u.a.uAllActions);
2821 /* Enter is always followed by a move event. */
2822 }
2823 else
2824 {
2825 rc = VERR_INVALID_PARAMETER;
2826 break;
2827 }
2828 /* Not breaking unconditionally is intentional. See comment above. */
2829 }
2830 case DragAndDropSvc::HOST_DND_HG_EVT_MOVE:
2831 {
2832 rc = m_pCurDnD->hgMove(e.hgcm.u.a.uXpos, e.hgcm.u.a.uYpos, e.hgcm.u.a.uDefAction);
2833 break;
2834 }
2835 case DragAndDropSvc::HOST_DND_HG_EVT_LEAVE:
2836 {
2837 rc = m_pCurDnD->hgLeave();
2838 break;
2839 }
2840 case DragAndDropSvc::HOST_DND_HG_EVT_DROPPED:
2841 {
2842 rc = m_pCurDnD->hgDrop(e.hgcm.u.a.uXpos, e.hgcm.u.a.uYpos, e.hgcm.u.a.uDefAction);
2843 break;
2844 }
2845 case DragAndDropSvc::HOST_DND_HG_SND_DATA:
2846 {
2847 rc = m_pCurDnD->hgDataReceived(e.hgcm.u.b.pvData, e.hgcm.u.b.cbData);
2848 break;
2849 }
2850#ifdef VBOX_WITH_DRAG_AND_DROP_GH
2851 case DragAndDropSvc::HOST_DND_GH_REQ_PENDING:
2852 {
2853 rc = m_pCurDnD->ghIsDnDPending();
2854 break;
2855 }
2856 case DragAndDropSvc::HOST_DND_GH_EVT_DROPPED:
2857 {
2858 rc = m_pCurDnD->ghDropped(e.hgcm.pszFormats, e.hgcm.u.a.uDefAction);
2859 break;
2860 }
2861#endif
2862 default:
2863 {
2864 m_pCurDnD->logError("Received unsupported message: %RU32\n", e.hgcm.uType);
2865 rc = VERR_NOT_SUPPORTED;
2866 break;
2867 }
2868 }
2869
2870 LogFlowFunc(("Message %RU32 processed with %Rrc\n", e.hgcm.uType, rc));
2871 if (RT_FAILURE(rc))
2872 {
2873 /* Tell the user. */
2874 m_pCurDnD->logError("Error processing message %RU32, failed with %Rrc, resetting all\n", e.hgcm.uType, rc);
2875
2876 /* If anything went wrong, do a reset and start over. */
2877 m_pCurDnD->reset();
2878 }
2879
2880 /* Some messages require cleanup. */
2881 switch (e.hgcm.uType)
2882 {
2883 case DragAndDropSvc::HOST_DND_HG_EVT_ENTER:
2884 case DragAndDropSvc::HOST_DND_HG_EVT_MOVE:
2885 case DragAndDropSvc::HOST_DND_HG_EVT_DROPPED:
2886#ifdef VBOX_WITH_DRAG_AND_DROP_GH
2887 case DragAndDropSvc::HOST_DND_GH_EVT_DROPPED:
2888#endif
2889 {
2890 if (e.hgcm.pszFormats)
2891 RTMemFree(e.hgcm.pszFormats);
2892 break;
2893 }
2894
2895 case DragAndDropSvc::HOST_DND_HG_SND_DATA:
2896 {
2897 if (e.hgcm.pszFormats)
2898 RTMemFree(e.hgcm.pszFormats);
2899 if (e.hgcm.u.b.pvData)
2900 RTMemFree(e.hgcm.u.b.pvData);
2901 break;
2902 }
2903
2904 default:
2905 break;
2906 }
2907 }
2908 else if (e.type == DnDEvent::X11_Type)
2909 {
2910 m_pCurDnD->onX11Event(e.x11);
2911 }
2912 else
2913 AssertMsgFailed(("Unknown event queue type %d\n", e.type));
2914
2915 /*
2916 * Make sure that any X11 requests have actually been sent to the
2917 * server, since we are waiting for responses using poll() on
2918 * another thread which will not automatically trigger flushing.
2919 */
2920 XFlush(m_pDisplay);
2921
2922 } while (!ASMAtomicReadBool(&m_fSrvStopping));
2923
2924 } while (0);
2925
2926 LogRel(("DnD: Stopped with rc=%Rrc\n", rc));
2927 return rc;
2928}
2929
2930/**
2931 * Initializes the drag and drop instance.
2932 *
2933 * @returns IPRT status code.
2934 */
2935int DragAndDropService::dragAndDropInit(void)
2936{
2937 /* Initialise the guest library. */
2938 int rc = VbglR3InitUser();
2939 if (RT_FAILURE(rc))
2940 VBClFatalError(("DnD: Failed to connect to the VirtualBox kernel service, rc=%Rrc\n", rc));
2941
2942 /* Connect to the x11 server. */
2943 m_pDisplay = XOpenDisplay(NULL);
2944 if (!m_pDisplay)
2945 /** @todo Correct errors. */
2946 return VERR_NOT_FOUND;
2947
2948 xHelpers *pHelpers = xHelpers::getInstance(m_pDisplay);
2949 if (!pHelpers)
2950 return VERR_NO_MEMORY;
2951
2952 do
2953 {
2954 rc = RTSemEventCreate(&m_hEventSem);
2955 if (RT_FAILURE(rc))
2956 break;
2957
2958 rc = RTCritSectInit(&m_eventQueueCS);
2959 if (RT_FAILURE(rc))
2960 break;
2961
2962 /* Event thread for events coming from the HGCM device. */
2963 rc = RTThreadCreate(&m_hHGCMThread, hgcmEventThread, this,
2964 0, RTTHREADTYPE_MSG_PUMP, RTTHREADFLAGS_WAITABLE,
2965 "dndHGCM");
2966 if (RT_FAILURE(rc))
2967 break;
2968
2969 /* Event thread for events coming from the x11 system. */
2970 rc = RTThreadCreate(&m_hX11Thread, x11EventThread, this,
2971 0, RTTHREADTYPE_MSG_PUMP, RTTHREADFLAGS_WAITABLE,
2972 "dndX11");
2973 } while (0);
2974
2975 /* No clean-up code for now, as we have no good way of testing it and things
2976 * should get cleaned up when the user process/X11 client exits. */
2977 if (RT_FAILURE(rc))
2978 LogRel(("DnD: Failed to start, rc=%Rrc\n", rc));
2979
2980 return rc;
2981}
2982
2983/**
2984 * Static callback function for HGCM message processing thread. An internal
2985 * message queue will be filled which then will be processed by the according
2986 * drag'n drop instance.
2987 *
2988 * @returns IPRT status code.
2989 * @param hThread Thread handle to use.
2990 * @param pvUser Pointer to DragAndDropService instance to use.
2991 */
2992/* static */
2993int DragAndDropService::hgcmEventThread(RTTHREAD hThread, void *pvUser)
2994{
2995 AssertPtrReturn(pvUser, VERR_INVALID_PARAMETER);
2996 DragAndDropService *pThis = static_cast<DragAndDropService*>(pvUser);
2997 AssertPtr(pThis);
2998
2999 /* This thread has an own DnD context, e.g. an own client ID. */
3000 VBGLR3GUESTDNDCMDCTX dndCtx;
3001
3002 int rc = VbglR3DnDConnect(&dndCtx);
3003 if (RT_FAILURE(rc))
3004 LogRel(("DnD: Unable to connect to drag and drop service, rc=%Rrc\n", rc));
3005 /* Not RT_FAILURE: VINF_PERMISSION_DENIED is host service not present. */
3006 if (rc != VINF_SUCCESS)
3007 return rc;
3008
3009 /* Number of invalid messages skipped in a row. */
3010 int cMsgSkippedInvalid = 0;
3011 DnDEvent e;
3012
3013 do
3014 {
3015 RT_ZERO(e);
3016 e.type = DnDEvent::HGCM_Type;
3017
3018 /* Wait for new events. */
3019 rc = VbglR3DnDProcessNextMessage(&dndCtx, &e.hgcm);
3020 if ( RT_SUCCESS(rc)
3021 || rc == VERR_CANCELLED)
3022 {
3023 cMsgSkippedInvalid = 0; /* Reset skipped messages count. */
3024 pThis->m_eventQueue.append(e);
3025
3026 rc = RTSemEventSignal(pThis->m_hEventSem);
3027 if (RT_FAILURE(rc))
3028 break;
3029 }
3030 else
3031 {
3032 LogRel(("DnD: Processing next message failed with rc=%Rrc\n", rc));
3033
3034 /* Old(er) hosts either are broken regarding DnD support or otherwise
3035 * don't support the stuff we do on the guest side, so make sure we
3036 * don't process invalid messages forever. */
3037 if (rc == VERR_INVALID_PARAMETER)
3038 cMsgSkippedInvalid++;
3039 if (cMsgSkippedInvalid > 32)
3040 {
3041 LogRel(("DnD: Too many invalid/skipped messages from host, exiting ...\n"));
3042 break;
3043 }
3044 }
3045
3046 } while (!ASMAtomicReadBool(&pThis->m_fSrvStopping));
3047
3048 VbglR3DnDDisconnect(&dndCtx);
3049
3050 LogFlowFuncLeaveRC(rc);
3051 return rc;
3052}
3053
3054/**
3055 * Static callback function for X11 message processing thread. All X11 messages
3056 * will be directly routed to the according drag'n drop instance.
3057 *
3058 * @returns IPRT status code.
3059 * @param hThread Thread handle to use.
3060 * @param pvUser Pointer to DragAndDropService instance to use.
3061 */
3062/* static */
3063int DragAndDropService::x11EventThread(RTTHREAD hThread, void *pvUser)
3064{
3065 AssertPtrReturn(pvUser, VERR_INVALID_PARAMETER);
3066 DragAndDropService *pThis = static_cast<DragAndDropService*>(pvUser);
3067 AssertPtr(pThis);
3068
3069 int rc = VINF_SUCCESS;
3070
3071 DnDEvent e;
3072 do
3073 {
3074 /*
3075 * Wait for new events. We can't use XIfEvent here, cause this locks
3076 * the window connection with a mutex and if no X11 events occurs this
3077 * blocks any other calls we made to X11. So instead check for new
3078 * events and if there are not any new one, sleep for a certain amount
3079 * of time.
3080 */
3081 if (XEventsQueued(pThis->m_pDisplay, QueuedAfterFlush) > 0)
3082 {
3083 RT_ZERO(e);
3084 e.type = DnDEvent::X11_Type;
3085
3086 /* XNextEvent will block until a new X event becomes available. */
3087 XNextEvent(pThis->m_pDisplay, &e.x11);
3088 {
3089#ifdef DEBUG
3090 switch (e.x11.type)
3091 {
3092 case ClientMessage:
3093 {
3094 XClientMessageEvent *pEvent = reinterpret_cast<XClientMessageEvent*>(&e);
3095 AssertPtr(pEvent);
3096
3097 RTCString strType = xAtomToString(pEvent->message_type);
3098 LogFlowFunc(("ClientMessage: %s from wnd=%#x\n", strType.c_str(), pEvent->window));
3099 break;
3100 }
3101
3102 default:
3103 LogFlowFunc(("Received X event type=%d\n", e.x11.type));
3104 break;
3105 }
3106#endif
3107 /* At the moment we only have one drag instance. */
3108 DragInstance *pInstance = pThis->m_pCurDnD;
3109 AssertPtr(pInstance);
3110
3111 pInstance->onX11Event(e.x11);
3112 }
3113 }
3114 else
3115 RTThreadSleep(25 /* ms */);
3116
3117 } while (!ASMAtomicReadBool(&pThis->m_fSrvStopping));
3118
3119 LogFlowFuncLeaveRC(rc);
3120 return rc;
3121}
3122
3123/** Drag and drop magic number, start of a UUID. */
3124#define DRAGANDDROPSERVICE_MAGIC 0x67c97173
3125
3126/** VBoxClient service class wrapping the logic for the service while
3127 * the main VBoxClient code provides the daemon logic needed by all services.
3128 */
3129struct DRAGANDDROPSERVICE
3130{
3131 /** The service interface. */
3132 struct VBCLSERVICE *pInterface;
3133 /** Magic number for sanity checks. */
3134 uint32_t uMagic;
3135 /** Service object. */
3136 DragAndDropService mDragAndDrop;
3137};
3138
3139static const char *getPidFilePath()
3140{
3141 return ".vboxclient-draganddrop.pid";
3142}
3143
3144static int run(struct VBCLSERVICE **ppInterface, bool fDaemonised)
3145{
3146 struct DRAGANDDROPSERVICE *pSelf = (struct DRAGANDDROPSERVICE *)ppInterface;
3147
3148 if (pSelf->uMagic != DRAGANDDROPSERVICE_MAGIC)
3149 VBClFatalError(("Bad display service object!\n"));
3150 return pSelf->mDragAndDrop.run(fDaemonised);
3151}
3152
3153static void cleanup(struct VBCLSERVICE **ppInterface)
3154{
3155 NOREF(ppInterface);
3156 VbglR3Term();
3157}
3158
3159struct VBCLSERVICE vbclDragAndDropInterface =
3160{
3161 getPidFilePath,
3162 VBClServiceDefaultHandler, /* init */
3163 run,
3164 VBClServiceDefaultHandler, /* pause */
3165 VBClServiceDefaultHandler, /* resume */
3166 cleanup
3167};
3168
3169/* Static factory. */
3170struct VBCLSERVICE **VBClGetDragAndDropService(void)
3171{
3172 struct DRAGANDDROPSERVICE *pService =
3173 (struct DRAGANDDROPSERVICE *)RTMemAlloc(sizeof(*pService));
3174
3175 if (!pService)
3176 VBClFatalError(("Out of memory\n"));
3177 pService->pInterface = &vbclDragAndDropInterface;
3178 pService->uMagic = DRAGANDDROPSERVICE_MAGIC;
3179 new(&pService->mDragAndDrop) DragAndDropService();
3180 return &pService->pInterface;
3181}
3182
Note: See TracBrowser for help on using the repository browser.

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