VirtualBox

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

Last change on this file since 54008 was 54008, checked in by vboxsync, 10 years ago

Additions/x11/VBoxClient: initialise the guest library from the services, as preparation for running the display service without using the VMM device.

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 82.1 KB
Line 
1/** @file
2 * X11 guest client - Drag and Drop.
3 */
4
5/*
6 * Copyright (C) 2011-2014 Oracle Corporation
7 *
8 * This file is part of VirtualBox Open Source Edition (OSE), as
9 * available from http://www.virtualbox.org. This file is free software;
10 * you can redistribute it and/or modify it under the terms of the GNU
11 * General Public License (GPL) as published by the Free Software
12 * Foundation, in version 2 as it comes in the "COPYING" file of the
13 * VirtualBox OSE distribution. VirtualBox OSE is distributed in the
14 * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
15 */
16
17#include <X11/Xlib.h>
18#include <X11/Xatom.h>
19#ifdef VBOX_DND_WITH_XTEST
20# include <X11/extensions/XTest.h>
21#endif
22
23#include <iprt/asm.h>
24#include <iprt/critsect.h>
25#include <iprt/thread.h>
26#include <iprt/time.h>
27
28#include <iprt/cpp/mtlist.h>
29#include <iprt/cpp/ministring.h>
30
31#include <limits.h>
32
33# ifdef LOG_GROUP
34 # undef LOG_GROUP
35# endif
36#define LOG_GROUP LOG_GROUP_GUEST_DND
37#include <VBox/log.h>
38#include <VBox/VBoxGuestLib.h>
39
40#include "VBox/HostServices/DragAndDropSvc.h"
41
42#include "VBoxClient.h"
43
44/* Enable this define to see the proxy window(s) when debugging
45 * their behavior. Don't have this enabled in release builds! */
46#ifdef DEBUG
47//# define VBOX_DND_DEBUG_WND
48#endif
49
50/* For X11 guest xDnD is used. See http://www.acc.umu.se/~vatten/XDND.html for
51 * a walk trough.
52 *
53 * H->G:
54 * For X11 this means mainly forwarding all the events from HGCM to the
55 * appropriate X11 events. There exists a proxy window, which is invisible and
56 * used for all the X11 communication. On a HGCM Enter event, we set our proxy
57 * window as XdndSelection owner with the given mime-types. On every HGCM move
58 * event, we move the X11 mouse cursor to the new position and query for the
59 * window below that position. Depending on if it is XdndAware, a new window or
60 * a known window, we send the appropriate X11 messages to it. On HGCM drop, we
61 * send a XdndDrop message to the current window and wait for a X11
62 * SelectionMessage from the target window. Because we didn't have the data in
63 * the requested mime-type, yet, we save that message and ask the host for the
64 * data. When the data is successfully received from the host, we put the data
65 * as a property to the window and send a X11 SelectionNotify event to the
66 * target window.
67 *
68 * G->H:
69 * This is a lot more trickery than H->G. When a pending event from HGCM
70 * arrives, we ask if there currently is an owner of the XdndSelection
71 * property. If so, our proxy window is shown (1x1, but without backing store)
72 * and some mouse event is triggered. This should be followed by an XdndEnter
73 * event send to the proxy window. From this event we can fetch the necessary
74 * info of the MIME types and allowed actions and send this back to the host.
75 * On a drop request from the host, we query for the selection and should get
76 * the data in the specified mime-type. This data is send back to the host.
77 * After that we send a XdndLeave event to the source window.
78 *
79 * To-do:
80 * - Cancelling (e.g. with ESC key) doesn't work.
81 *
82 * To-do:
83 * - INCR (incremental transfers) support.
84 * - Make this much more robust for crashes of the other party.
85 * - Really check for the Xdnd version and the supported features.
86 */
87
88#define VBOX_XDND_VERSION (4)
89#define VBOX_MAX_XPROPERTIES (LONG_MAX-1)
90
91/* Shared struct used for adding new X11 events and HGCM messages to a single
92 * event queue. */
93struct DnDEvent
94{
95 enum DnDEventType
96 {
97 HGCM_Type = 1,
98 X11_Type
99 };
100 DnDEventType type;
101 union
102 {
103 VBGLR3DNDHGCMEVENT hgcm;
104 XEvent x11;
105 };
106};
107
108enum XA_Type
109{
110 /* States */
111 XA_WM_STATE = 0,
112 /* Properties */
113 XA_TARGETS,
114 XA_MULTIPLE,
115 XA_INCR,
116 /* Mime Types */
117 XA_image_bmp,
118 XA_image_jpg,
119 XA_image_tiff,
120 XA_image_png,
121 XA_text_uri_list,
122 XA_text_uri,
123 XA_text_plain,
124 XA_TEXT,
125 /* xDnD */
126 XA_XdndSelection,
127 XA_XdndAware,
128 XA_XdndEnter,
129 XA_XdndLeave,
130 XA_XdndTypeList,
131 XA_XdndActionList,
132 XA_XdndPosition,
133 XA_XdndActionCopy,
134 XA_XdndActionMove,
135 XA_XdndActionLink,
136 XA_XdndStatus,
137 XA_XdndDrop,
138 XA_XdndFinished,
139 /* Our own stop marker */
140 XA_dndstop,
141 /* End marker */
142 XA_End
143};
144
145class DragAndDropService;
146
147/*******************************************************************************
148 *
149 * xHelpers Declaration
150 *
151 ******************************************************************************/
152
153class xHelpers
154{
155public:
156
157 static xHelpers *getInstance(Display *pDisplay = 0)
158 {
159 if (!m_pInstance)
160 {
161 AssertPtrReturn(pDisplay, NULL);
162 m_pInstance = new xHelpers(pDisplay);
163 }
164
165 return m_pInstance;
166 }
167
168 inline Display *display() const { return m_pDisplay; }
169 inline Atom xAtom(XA_Type e) const { return m_xAtoms[e]; }
170
171 inline Atom stringToxAtom(const char *pcszString) const
172 {
173 return XInternAtom(m_pDisplay, pcszString, False);
174 }
175 inline RTCString xAtomToString(Atom atom) const
176 {
177 if (atom == None) return "None";
178
179 char* pcsAtom = XGetAtomName(m_pDisplay, atom);
180 RTCString strAtom(pcsAtom);
181 XFree(pcsAtom);
182
183 return strAtom;
184 }
185
186 inline RTCString xAtomListToString(const RTCList<Atom> &formatList)
187 {
188 RTCString format;
189 for (size_t i = 0; i < formatList.size(); ++i)
190 format += xAtomToString(formatList.at(i)) + "\r\n";
191 return format;
192 }
193
194 RTCString xErrorToString(int xrc) const;
195 Window applicationWindowBelowCursor(Window parentWin) const;
196
197private:
198 xHelpers(Display *pDisplay)
199 : m_pDisplay(pDisplay)
200 {
201 /* Not all x11 atoms we use are defined in the headers. Create the
202 * additional one we need here. */
203 for (int i = 0; i < XA_End; ++i)
204 m_xAtoms[i] = XInternAtom(m_pDisplay, m_xAtomNames[i], False);
205 };
206
207 /* Private member vars */
208 static xHelpers *m_pInstance;
209 Display *m_pDisplay;
210 Atom m_xAtoms[XA_End];
211 static const char *m_xAtomNames[XA_End];
212};
213
214/* Some xHelpers convenience defines. */
215#define gX11 xHelpers::getInstance()
216#define xAtom(xa) gX11->xAtom((xa))
217#define xAtomToString(xa) gX11->xAtomToString((xa))
218
219/*******************************************************************************
220 *
221 * xHelpers Implementation
222 *
223 ******************************************************************************/
224
225xHelpers *xHelpers::m_pInstance = 0;
226/* Has to be in sync with the XA_Type enum. */
227const char *xHelpers::m_xAtomNames[] =
228{
229 /* States */
230 "WM_STATE",
231 /* Properties */
232 "TARGETS",
233 "MULTIPLE",
234 "INCR",
235 /* Mime Types */
236 "image/bmp",
237 "image/jpg",
238 "image/tiff",
239 "image/png",
240 "text/uri-list",
241 "text/uri",
242 "text/plain",
243 "TEXT",
244 /* xDnD */
245 "XdndSelection",
246 "XdndAware",
247 "XdndEnter",
248 "XdndLeave",
249 "XdndTypeList",
250 "XdndActionList",
251 "XdndPosition",
252 "XdndActionCopy",
253 "XdndActionMove",
254 "XdndActionLink",
255 "XdndStatus",
256 "XdndDrop",
257 "XdndFinished",
258 /* Our own stop marker */
259 "dndstop"
260};
261
262RTCString xHelpers::xErrorToString(int xrc) const
263{
264 switch (xrc)
265 {
266 case Success: return RTCStringFmt("%d (Success)", xrc); break;
267 case BadRequest: return RTCStringFmt("%d (BadRequest)", xrc); break;
268 case BadValue: return RTCStringFmt("%d (BadValue)", xrc); break;
269 case BadWindow: return RTCStringFmt("%d (BadWindow)", xrc); break;
270 case BadPixmap: return RTCStringFmt("%d (BadPixmap)", xrc); break;
271 case BadAtom: return RTCStringFmt("%d (BadAtom)", xrc); break;
272 case BadCursor: return RTCStringFmt("%d (BadCursor)", xrc); break;
273 case BadFont: return RTCStringFmt("%d (BadFont)", xrc); break;
274 case BadMatch: return RTCStringFmt("%d (BadMatch)", xrc); break;
275 case BadDrawable: return RTCStringFmt("%d (BadDrawable)", xrc); break;
276 case BadAccess: return RTCStringFmt("%d (BadAccess)", xrc); break;
277 case BadAlloc: return RTCStringFmt("%d (BadAlloc)", xrc); break;
278 case BadColor: return RTCStringFmt("%d (BadColor)", xrc); break;
279 case BadGC: return RTCStringFmt("%d (BadGC)", xrc); break;
280 case BadIDChoice: return RTCStringFmt("%d (BadIDChoice)", xrc); break;
281 case BadName: return RTCStringFmt("%d (BadName)", xrc); break;
282 case BadLength: return RTCStringFmt("%d (BadLength)", xrc); break;
283 case BadImplementation: return RTCStringFmt("%d (BadImplementation)", xrc); break;
284 }
285 return RTCStringFmt("%d (unknown)", xrc);
286}
287
288/* Todo: make this iterative */
289Window xHelpers::applicationWindowBelowCursor(Window wndParent) const
290{
291 /* No parent, nothing to do. */
292 if(wndParent == 0)
293 return 0;
294
295 Window wndApp = 0;
296 int cProps = -1;
297 /* Fetch all x11 window properties of the parent window. */
298 Atom *pProps = XListProperties(m_pDisplay, wndParent, &cProps);
299 if (cProps > 0)
300 {
301 /* We check the window for the WM_STATE property. */
302 for (int i = 0; i < cProps; ++i)
303 if (pProps[i] == xAtom(XA_WM_STATE))
304 {
305 /* Found it. */
306 wndApp = wndParent;
307 break;
308 }
309 /* Cleanup */
310 XFree(pProps);
311 }
312
313 if (!wndApp)
314 {
315 Window wndChild, wndTemp;
316 int tmp;
317 unsigned int utmp;
318
319 /* Query the next child window of the parent window at the current
320 * mouse position. */
321 XQueryPointer(m_pDisplay, wndParent, &wndTemp, &wndChild, &tmp, &tmp, &tmp, &tmp, &utmp);
322
323 /* Recursive call our self to dive into the child tree. */
324 wndApp = applicationWindowBelowCursor(wndChild);
325 }
326
327 return wndApp;
328}
329
330/*******************************************************************************
331 *
332 * DragInstance Declaration
333 *
334 ******************************************************************************/
335
336/* For now only one DragInstance will exits when the app is running. In the
337 * future the support for having more than one D&D operation supported at the
338 * time will be necessary. */
339class DragInstance
340{
341public:
342
343 enum State
344 {
345 Uninitialized = 0,
346 Initialized,
347 Dragging,
348 Dropped
349 };
350
351 enum Mode
352 {
353 Unknown = 0,
354 HG,
355 GH
356 };
357
358 DragInstance(Display *pDisplay, DragAndDropService *pParent);
359
360public:
361
362 int init(uint32_t u32ScreenId);
363 void uninit(void);
364 void reset(void);
365
366 /* X11 message processing. */
367 int onX11ClientMessage(const XEvent &e);
368 int onX11SelectionNotify(const XEvent &e);
369 int onX11SelectionRequest(const XEvent &e);
370 int onX11Event(const XEvent &e);
371 bool waitForX11Msg(XEvent &evX, int iType, RTMSINTERVAL uTimeoutMS = 100);
372 bool waitForX11ClientMsg(XClientMessageEvent &evMsg, Atom aType, RTMSINTERVAL uTimeoutMS = 100);
373
374 /* H->G */
375 int hgEnter(const RTCList<RTCString> &formats, uint32_t actions);
376 int hgMove(uint32_t u32xPos, uint32_t u32yPos, uint32_t action);
377 int hgDrop();
378 int hgDataReceived(void *pvData, uint32_t cData);
379
380#ifdef VBOX_WITH_DRAG_AND_DROP_GH
381 /* G->H */
382 int ghIsDnDPending();
383 int ghDropped(const RTCString &strFormat, uint32_t action);
384#endif
385
386 /* X11 helpers. */
387 int mouseCursorMove(int iPosX, int iPosY) const;
388 void mouseButtonSet(Window wndDest, int rx, int ry, int iButton, bool fPress) const;
389 int proxyWinShow(int *piRootX = NULL, int *piRootY = NULL, bool fMouseMove = false) const;
390 int proxyWinHide(void);
391 void registerForEvents(Window w) const;
392
393 void setActionsWindowProperty(Window wndThis, const RTCList<Atom> &lstActions) const;
394 void clearActionsWindowProperty(Window wndThis) const;
395 void setFormatsWindowProperty(Window wndThis, Atom property) const;
396 void clearFormatsWindowProperty(Window wndThis) const;
397
398 RTCList<Atom> toAtomList(const RTCList<RTCString> &lstFormats) const;
399 RTCList<Atom> toAtomList(void *pvData, uint32_t cbData) const;
400 static Atom toX11Action(uint32_t uAction);
401 static RTCList<Atom> toX11Actions(uint32_t uActions);
402 static uint32_t toHGCMAction(Atom atom);
403 static uint32_t toHGCMActions(const RTCList<Atom> &actionsList);
404
405protected:
406
407 uint32_t m_uClientID;
408 DragAndDropService *m_pParent;
409 Display *m_pDisplay;
410 int m_screenId;
411 Screen *m_pScreen;
412 Window m_wndRoot;
413 Window m_wndProxy;
414 Window m_wndCur;
415 long m_curVer;
416 RTCList<Atom> m_formats;
417 RTCList<Atom> m_actions;
418 /** Deferred host to guest selection event for sending to the
419 * target window as soon as data from the host arrived. */
420 XEvent m_eventHgSelection;
421 /** Current operation mode. */
422 Mode m_mode;
423 /** Current state of operation mode. */
424 State m_state;
425 /** The instance's own X event queue. */
426 RTCMTList<XEvent> m_eventQueue;
427 /** Critical section for providing serialized access to list
428 * event queue's contents. */
429 RTCRITSECT m_eventQueueCS;
430 /** Event for notifying this instance in case of a new
431 * event. */
432 RTSEMEVENT m_hEventSem;
433 /** List of allowed formats. */
434 RTCList<RTCString> m_lstAllowedFormats;
435};
436
437/*******************************************************************************
438 *
439 * DragAndDropService Declaration
440 *
441 ******************************************************************************/
442
443class DragAndDropService
444{
445public:
446 DragAndDropService(void)
447 : m_pDisplay(0)
448 , m_hHGCMThread(NIL_RTTHREAD)
449 , m_hX11Thread(NIL_RTTHREAD)
450 , m_hEventSem(NIL_RTSEMEVENT)
451 , m_pCurDnD(0)
452 , m_fSrvStopping(false)
453 {}
454
455 int run(bool fDaemonised = false);
456
457private:
458 int x11DragAndDropInit(void);
459 static int hgcmEventThread(RTTHREAD hThread, void *pvUser);
460 static int x11EventThread(RTTHREAD hThread, void *pvUser);
461
462 void clearEventQueue();
463
464 /* Usually XCheckMaskEvent could be used for querying selected x11 events.
465 * Unfortunately this doesn't work exactly with the events we need. So we
466 * use this predicate method below and XCheckIfEvent. */
467 static bool isDnDRespondEvent(Display * /* pDisplay */, XEvent *pEvent, char *pUser)
468 {
469 if (!pEvent)
470 return false;
471 if ( pEvent->type == SelectionClear
472 || pEvent->type == ClientMessage
473 || pEvent->type == MotionNotify
474 || pEvent->type == SelectionRequest)
475// || ( pEvent->type == ClientMessage
476// && reinterpret_cast<XClientMessageEvent*>(pEvent)->window == reinterpret_cast<Window>(pUser))
477// || ( pEvent->type == SelectionRequest
478// && reinterpret_cast<XSelectionRequestEvent*>(pEvent)->requestor == reinterpret_cast<Window>(pUser)))
479 return true;
480 return false;
481 }
482
483 /* Private member vars */
484 Display *m_pDisplay;
485
486 /** Our (thread-safe) event queue with
487 * mixed events (DnD HGCM / X11). */
488 RTCMTList<DnDEvent> m_eventQueue;
489 /** Critical section for providing serialized access to list
490 * event queue's contents. */
491 RTCRITSECT m_eventQueueCS;
492 RTTHREAD m_hHGCMThread;
493 RTTHREAD m_hX11Thread;
494 RTSEMEVENT m_hEventSem;
495 DragInstance *m_pCurDnD;
496 bool m_fSrvStopping;
497
498 friend class DragInstance;
499};
500
501/*******************************************************************************
502 *
503 * DragInstanc Implementation
504 *
505 ******************************************************************************/
506
507DragInstance::DragInstance(Display *pDisplay, DragAndDropService *pParent)
508 : m_uClientID(0)
509 , m_pParent(pParent)
510 , m_pDisplay(pDisplay)
511 , m_pScreen(0)
512 , m_wndRoot(0)
513 , m_wndProxy(0)
514 , m_wndCur(0)
515 , m_curVer(-1)
516 , m_mode(Unknown)
517 , m_state(Uninitialized)
518{
519 uninit();
520}
521
522void DragInstance::uninit(void)
523{
524 reset();
525 if (m_wndProxy != 0)
526 XDestroyWindow(m_pDisplay, m_wndProxy);
527
528 if (m_uClientID)
529 {
530 VbglR3DnDDisconnect(m_uClientID);
531 m_uClientID = 0;
532 }
533
534 m_state = Uninitialized;
535 m_screenId = -1;
536 m_pScreen = 0;
537 m_wndRoot = 0;
538 m_wndProxy = 0;
539}
540
541void DragInstance::reset(void)
542{
543 /* Hide the proxy win. */
544 proxyWinHide();
545
546 /* If we are currently the Xdnd selection owner, clear that. */
547 Window w = XGetSelectionOwner(m_pDisplay, xAtom(XA_XdndSelection));
548 if (w == m_wndProxy)
549 XSetSelectionOwner(m_pDisplay, xAtom(XA_XdndSelection), None, CurrentTime);
550
551 /* Clear any other DnD specific data on the proxy win. */
552 clearFormatsWindowProperty(m_wndProxy);
553 clearActionsWindowProperty(m_wndProxy);
554
555 /* Reset the internal state. */
556 m_actions.clear();
557 m_formats.clear();
558 m_wndCur = 0;
559 m_curVer = -1;
560 m_state = Initialized;
561 m_eventQueue.clear();
562}
563
564int DragInstance::init(uint32_t u32ScreenId)
565{
566 int rc;
567
568 do
569 {
570 uninit();
571
572 rc = VbglR3DnDConnect(&m_uClientID);
573 if (RT_FAILURE(rc))
574 break;
575
576 rc = RTSemEventCreate(&m_hEventSem);
577 if (RT_FAILURE(rc))
578 break;
579
580 rc = RTCritSectInit(&m_eventQueueCS);
581 if (RT_FAILURE(rc))
582 break;
583
584 /*
585 * Enough screens configured in the x11 server?
586 */
587 if ((int)u32ScreenId > ScreenCount(m_pDisplay))
588 {
589 rc = VERR_INVALID_PARAMETER;
590 break;
591 }
592 /* Get the screen number from the x11 server. */
593// pDrag->screen = ScreenOfDisplay(m_pDisplay, u32ScreenId);
594// if (!pDrag->screen)
595// {
596// rc = VERR_GENERAL_FAILURE;
597// break;
598// }
599 m_screenId = u32ScreenId;
600 /* Now query the corresponding root window of this screen. */
601 m_wndRoot = RootWindow(m_pDisplay, m_screenId);
602 if (!m_wndRoot)
603 {
604 rc = VERR_GENERAL_FAILURE;
605 break;
606 }
607
608 /*
609 * Create an invisible window which will act as proxy for the DnD
610 * operation. This window will be used for both the GH and HG
611 * direction.
612 */
613 XSetWindowAttributes attr;
614 RT_ZERO(attr);
615 attr.event_mask = EnterWindowMask | LeaveWindowMask
616 | ButtonMotionMask | ButtonPressMask | ButtonReleaseMask;
617 attr.do_not_propagate_mask = NoEventMask;
618 attr.override_redirect = True;
619#ifdef VBOX_DND_DEBUG_WND
620 attr.background_pixel = WhitePixel(m_pDisplay, m_screenId);
621#endif
622 m_wndProxy = XCreateWindow(m_pDisplay, m_wndRoot /* Parent */,
623 0, 0, /* Position */
624 1, 1, /* Width + height */
625 0, /* Border width */
626 CopyFromParent, /* Depth */
627 InputOnly, /* Class */
628 CopyFromParent, /* Visual */
629 CWOverrideRedirect | CWDontPropagate, /* Value mask */
630 &attr /* Attributes for value mask */);
631 if (!m_wndProxy)
632 {
633 rc = VERR_GENERAL_FAILURE;
634 break;
635 }
636
637 LogFlowThisFunc(("Created proxy window 0x%x at m_wndRoot=0x%x ...\n",
638 m_wndProxy, m_wndRoot));
639
640 /* Set the window's name for easier lookup. */
641 XStoreName(m_pDisplay, m_wndProxy, "VBoxClientWndDnD");
642
643 /* Make the new window Xdnd aware. */
644 Atom ver = VBOX_XDND_VERSION;
645 XChangeProperty(m_pDisplay, m_wndProxy, xAtom(XA_XdndAware), XA_ATOM, 32, PropModeReplace,
646 reinterpret_cast<unsigned char*>(&ver), 1);
647 } while (0);
648
649 if (RT_SUCCESS(rc))
650 {
651 m_state = Initialized;
652 }
653 else
654 LogRel(("DnD: Initializing drag instance for screen %RU32 failed with rc=%Rrc\n",
655 u32ScreenId, rc));
656
657 LogFlowFuncLeaveRC(rc);
658 return rc;
659}
660
661int DragInstance::onX11ClientMessage(const XEvent &e)
662{
663 AssertReturn(e.type == ClientMessage, VERR_INVALID_PARAMETER);
664
665 LogFlowThisFunc(("m_mode=%d, m_state=%d\n", m_mode, m_state));
666 LogFlowThisFunc(("Event wnd=%#x, msg=%s\n",
667 e.xclient.window,
668 xAtomToString(e.xclient.message_type).c_str()));
669 int rc;
670
671 switch (m_mode)
672 {
673 case HG:
674 {
675 /* Client messages are used to inform us about the status of a XdndAware
676 * window, in response of some events we send to them. */
677 if ( e.xclient.message_type == xAtom(XA_XdndStatus)
678 && m_wndCur == static_cast<Window>(e.xclient.data.l[0]))
679 {
680 /* The XdndStatus message tell us if the window will accept the DnD
681 * event and with which action. We immediately send this info down to
682 * the host as a response of a previous DnD message. */
683 LogFlowThisFunc(("XA_XdndStatus wnd=%#x, accept=%RTbool, action=%s\n",
684 e.xclient.data.l[0],
685 ASMBitTest(&e.xclient.data.l[1], 0),
686 xAtomToString(e.xclient.data.l[4]).c_str()));
687
688 uint32_t uAction = DND_IGNORE_ACTION;
689 /** @todo Compare this with the allowed actions. */
690 if (ASMBitTest(&e.xclient.data.l[1], 0))
691 uAction = toHGCMAction(static_cast<Atom>(e.xclient.data.l[4]));
692
693 rc = VbglR3DnDHGAcknowledgeOperation(m_uClientID, uAction);
694 }
695 else if (e.xclient.message_type == xAtom(XA_XdndFinished))
696 {
697 rc = VINF_SUCCESS;
698
699 /* This message is send on a un/successful DnD drop request. */
700 LogFlowThisFunc(("XA_XdndFinished: wnd=%#x, success=%RTbool, action=%s\n",
701 e.xclient.data.l[0],
702 ASMBitTest(&e.xclient.data.l[1], 0),
703 xAtomToString(e.xclient.data.l[2]).c_str()));
704 reset();
705 }
706 else
707 {
708 LogFlowThisFunc(("Unhandled: wnd=%#x, msg=%s\n",
709 e.xclient.data.l[0], xAtomToString(e.xclient.message_type).c_str()));
710 rc = VERR_NOT_SUPPORTED;
711 }
712
713 break;
714 }
715
716 case GH:
717 {
718 LogFlowThisFunc(("Enqueuing ClientMessage\n"));
719
720 m_eventQueue.append(e);
721 rc = RTSemEventSignal(m_hEventSem);
722 break;
723 }
724
725 default:
726 {
727 LogFlowThisFunc(("Unhandled: wnd=%#x, msg=%s\n",
728 e.xclient.data.l[0], xAtomToString(e.xclient.message_type).c_str()));
729 rc = VERR_INVALID_STATE;
730 break;
731 }
732 }
733
734 LogFlowThisFunc(("Returning rc=%Rrc\n", rc));
735 return rc;
736}
737
738int DragInstance::onX11SelectionNotify(const XEvent &e)
739{
740 AssertReturn(e.type == SelectionNotify, VERR_INVALID_PARAMETER);
741
742 LogFlowThisFunc(("m_mode=%d, m_state=%d\n", m_mode, m_state));
743
744 int rc = VINF_SUCCESS;
745
746 switch (m_mode)
747 {
748 case GH:
749 {
750 if (m_state == Dropped)
751 {
752 LogFlowThisFunc(("Enqueuing SelectionNotify\n"));
753
754 m_eventQueue.append(e);
755 rc = RTSemEventSignal(m_hEventSem);
756 }
757 break;
758 }
759
760 default:
761 {
762 LogFlowThisFunc(("Unhandled: wnd=%#x, msg=%s\n",
763 e.xclient.data.l[0], xAtomToString(e.xclient.message_type).c_str()));
764 rc = VERR_INVALID_STATE;
765 break;
766 }
767 }
768
769 LogFlowThisFunc(("Returning rc=%Rrc\n", rc));
770 return rc;
771}
772
773
774int DragInstance::onX11SelectionRequest(const XEvent &e)
775{
776 AssertReturn(e.type == SelectionRequest, VERR_INVALID_PARAMETER);
777
778 LogFlowThisFunc(("m_mode=%d, m_state=%d\n", m_mode, m_state));
779 LogFlowThisFunc(("Event owner=%#x, requestor=%#x, selection=%s, target=%s, prop=%s, time=%u\n",
780 e.xselectionrequest.owner,
781 e.xselectionrequest.requestor,
782 xAtomToString(e.xselectionrequest.selection).c_str(),
783 xAtomToString(e.xselectionrequest.target).c_str(),
784 xAtomToString(e.xselectionrequest.property).c_str(),
785 e.xselectionrequest.time));
786 int rc;
787
788 switch (m_mode)
789 {
790 case HG:
791 {
792 rc = VINF_SUCCESS;
793
794 /*
795 * A window is asking for some data. Normally here the data would be copied
796 * into the selection buffer and send to the requestor. Obviously we can't
797 * do that, because we first need to ask the host for the data of the
798 * requested MIME type. This is done and later answered with the correct
799 * data -- see DragInstance::hgDataReceived().
800 */
801
802 /* Is the requestor asking for the possible MIME types? */
803 if (e.xselectionrequest.target == xAtom(XA_TARGETS))
804 {
805 LogFlowThisFunc(("wnd=%#x asking for target list\n", e.xselectionrequest.requestor));
806
807 /* If so, set the window property with the formats on the requestor
808 * window. */
809 setFormatsWindowProperty(e.xselectionrequest.requestor, e.xselectionrequest.property);
810
811 XEvent s;
812 RT_ZERO(s);
813 s.xselection.type = SelectionNotify;
814 s.xselection.display = e.xselection.display;
815 s.xselection.time = e.xselectionrequest.time;
816 s.xselection.selection = e.xselectionrequest.selection;
817 s.xselection.requestor = e.xselectionrequest.requestor;
818 s.xselection.target = e.xselectionrequest.target;
819 s.xselection.property = e.xselectionrequest.property;
820
821 int xrc = XSendEvent(e.xselection.display, e.xselectionrequest.requestor, False, 0, &s);
822 if (RT_UNLIKELY(xrc == 0))
823 LogFlowThisFunc(("Error sending SelectionNotify event to wnd=%#x\n", e.xselectionrequest.requestor));
824 }
825 /* Is the requestor asking for a specific MIME type (we support)? */
826 else if (m_formats.contains(e.xselectionrequest.target))
827 {
828 LogFlowThisFunc(("wnd=%#x asking for data, format=%s\n",
829 e.xselectionrequest.requestor, xAtomToString(e.xselectionrequest.target).c_str()));
830
831 /* If so, we need to inform the host about this request. Save the
832 * selection request event for later use. */
833 if ( m_state != Dropped)
834 // || m_curWin != e.xselectionrequest.requestor)
835 {
836 LogFlowThisFunc(("Wrong state, refusing request\n"));
837
838 XEvent s;
839 RT_ZERO(s);
840 s.xselection.type = SelectionNotify;
841 s.xselection.display = e.xselection.display;
842 s.xselection.time = e.xselectionrequest.time;
843 s.xselection.selection = e.xselectionrequest.selection;
844 s.xselection.requestor = e.xselectionrequest.requestor;
845 s.xselection.target = None;
846 s.xselection.property = e.xselectionrequest.property;
847
848 int xrc = XSendEvent(e.xselection.display, e.xselectionrequest.requestor, False, 0, &s);
849 if (RT_UNLIKELY(xrc == 0))
850 LogFlowThisFunc(("Error sending SelectionNotify event to wnd=%#x\n", e.xselectionrequest.requestor));
851 }
852 else
853 {
854 LogFlowThisFunc(("Saving selection notify message\n"));
855
856 memcpy(&m_eventHgSelection, &e, sizeof(XEvent));
857
858 RTCString strFormat = xAtomToString(e.xselectionrequest.target);
859 Assert(strFormat.isNotEmpty());
860 rc = VbglR3DnDHGRequestData(m_uClientID, strFormat.c_str());
861 LogFlowThisFunc(("Requesting data from host as \"%s\", rc=%Rrc\n",
862 strFormat.c_str(), rc));
863 }
864 }
865 /* Anything else. */
866 else
867 {
868 LogFlowThisFunc(("Refusing unknown command\n"));
869
870 /* We don't understand this request message and therefore answer with an
871 * refusal messages. */
872 XEvent s;
873 RT_ZERO(s);
874 s.xselection.type = SelectionNotify;
875 s.xselection.display = e.xselection.display;
876 s.xselection.time = e.xselectionrequest.time;
877 s.xselection.selection = e.xselectionrequest.selection;
878 s.xselection.requestor = e.xselectionrequest.requestor;
879 s.xselection.target = None; /* default is refusing */
880 s.xselection.property = None; /* default is refusing */
881 int xrc = XSendEvent(e.xselection.display, e.xselectionrequest.requestor, False, 0, &s);
882 if (RT_UNLIKELY(xrc == 0))
883 LogFlowThisFunc(("Error sending SelectionNotify event to wnd=%#x\n", e.xselectionrequest.requestor));
884 }
885
886 break;
887 }
888
889 default:
890 {
891 LogFlowThisFunc(("Unhandled: wnd=%#x, msg=%s\n",
892 e.xclient.data.l[0], xAtomToString(e.xclient.message_type).c_str()));
893 rc = VERR_INVALID_STATE;
894 break;
895 }
896 }
897
898 LogFlowThisFunc(("Returning rc=%Rrc\n", rc));
899 return rc;
900}
901
902int DragInstance::onX11Event(const XEvent &e)
903{
904 int rc;
905
906 LogFlowThisFunc(("X11 event, type=%d\n", e.type));
907 switch (e.type)
908 {
909 case ButtonPress:
910 LogFlowThisFunc(("ButtonPress\n"));
911 rc = VINF_SUCCESS;
912 break;
913
914 case ButtonRelease:
915 LogFlowThisFunc(("ButtonRelease\n"));
916 rc = VINF_SUCCESS;
917 break;
918
919 case ClientMessage:
920 rc = onX11ClientMessage(e);
921 break;
922
923 case SelectionClear:
924 LogFlowThisFunc(("SelectionClear\n"));
925 reset();
926 rc = VINF_SUCCESS;
927 break;
928
929 case SelectionNotify:
930 rc = onX11SelectionNotify(e);
931 break;
932
933 case SelectionRequest:
934 rc = onX11SelectionRequest(e);
935 break;
936
937 /*case MotionNotify:
938 hide();
939 break;*/
940
941 default:
942 rc = VERR_NOT_IMPLEMENTED;
943 break;
944 }
945
946 LogFlowThisFunc(("rc=%Rrc\n", rc));
947 return rc;
948}
949
950#ifdef VBOX_WITH_DRAG_AND_DROP_GH
951bool DragInstance::waitForX11Msg(XEvent &evX, int iType,
952 RTMSINTERVAL uTimeoutMS /* = 100 */)
953{
954 LogFlowThisFunc(("iType=%d, uTimeoutMS=%RU32, cEventQueue=%zu\n",
955 iType, uTimeoutMS, m_eventQueue.size()));
956
957 bool fFound = false;
958 const uint64_t uiStart = RTTimeMilliTS();
959 do
960 {
961 /* Check if there is a client message in the queue. */
962 for (size_t i = 0; i < m_eventQueue.size(); i++)
963 {
964 int rc2 = RTCritSectEnter(&m_eventQueueCS);
965 if (RT_SUCCESS(rc2))
966 {
967 XEvent e = m_eventQueue.at(i);
968
969 fFound = e.type == iType;
970 if (fFound)
971 {
972 m_eventQueue.removeAt(i);
973 evX = e;
974 }
975
976 rc2 = RTCritSectLeave(&m_eventQueueCS);
977 AssertRC(rc2);
978
979 if (fFound)
980 break;
981 }
982 }
983
984 if (fFound)
985 break;
986
987 int rc2 = RTSemEventWait(m_hEventSem, 25 /* ms */);
988 if ( RT_FAILURE(rc2)
989 && rc2 != VERR_TIMEOUT)
990 {
991 LogFlowFunc(("Waiting failed with rc=%Rrc\n", rc2));
992 break;
993 }
994 }
995 while (RTTimeMilliTS() - uiStart < uTimeoutMS);
996
997 LogFlowThisFunc(("Returning fFound=%RTbool, msRuntime=%RU64\n",
998 fFound, RTTimeMilliTS() - uiStart));
999 return fFound;
1000}
1001
1002bool DragInstance::waitForX11ClientMsg(XClientMessageEvent &evMsg, Atom aType,
1003 RTMSINTERVAL uTimeoutMS /*= 100 */)
1004{
1005 LogFlowThisFunc(("aType=%s, uTimeoutMS=%RU32, cEventQueue=%zu\n",
1006 xAtomToString(aType).c_str(), uTimeoutMS, m_eventQueue.size()));
1007
1008 bool fFound = false;
1009 const uint64_t uiStart = RTTimeMilliTS();
1010 do
1011 {
1012 /* Check if there is a client message in the queue. */
1013 for (size_t i = 0; i < m_eventQueue.size(); i++)
1014 {
1015 int rc2 = RTCritSectEnter(&m_eventQueueCS);
1016 if (RT_SUCCESS(rc2))
1017 {
1018 XEvent e = m_eventQueue.at(i);
1019 if (e.type == ClientMessage)
1020 {
1021 m_eventQueue.removeAt(i);
1022 evMsg = e.xclient;
1023
1024 fFound = true;
1025 }
1026
1027 rc2 = RTCritSectLeave(&m_eventQueueCS);
1028 AssertRC(rc2);
1029
1030 if (fFound)
1031 break;
1032 }
1033 }
1034
1035 if (fFound)
1036 break;
1037
1038 int rc2 = RTSemEventWait(m_hEventSem, 25 /* ms */);
1039 if ( RT_FAILURE(rc2)
1040 && rc2 != VERR_TIMEOUT)
1041 {
1042 LogFlowFunc(("Waiting failed with rc=%Rrc\n", rc2));
1043 break;
1044 }
1045 }
1046 while (RTTimeMilliTS() - uiStart < uTimeoutMS);
1047
1048 LogFlowThisFunc(("Returning fFound=%RTbool, msRuntime=%RU64\n",
1049 fFound, RTTimeMilliTS() - uiStart));
1050 return fFound;
1051}
1052#endif /* VBOX_WITH_DRAG_AND_DROP_GH */
1053
1054/*
1055 * Host -> Guest
1056 */
1057
1058int DragInstance::hgEnter(const RTCList<RTCString> &formats, uint32_t uActions)
1059{
1060 reset();
1061
1062#ifdef DEBUG
1063 LogFlowThisFunc(("uActions=0x%x, lstFormats=%zu: ", uActions, formats.size()));
1064 for (size_t i = 0; i < formats.size(); ++i)
1065 LogFlow(("'%s' ", formats.at(i).c_str()));
1066 LogFlow(("\n"));
1067#endif
1068
1069 m_formats = toAtomList(formats);
1070
1071 /* If we have more than 3 formats we have to use the type list extension. */
1072 if (m_formats.size() > 3)
1073 setFormatsWindowProperty(m_wndProxy, xAtom(XA_XdndTypeList));
1074
1075 /* Announce the possible actions */
1076 setActionsWindowProperty(m_wndProxy, toX11Actions(uActions));
1077
1078 /* Set the DnD selection owner to our window. */
1079 XSetSelectionOwner(m_pDisplay, xAtom(XA_XdndSelection), m_wndProxy, CurrentTime);
1080
1081 m_mode = HG;
1082 m_state = Dragging;
1083
1084 return VINF_SUCCESS;
1085}
1086
1087int DragInstance::hgMove(uint32_t u32xPos, uint32_t u32yPos, uint32_t uAction)
1088{
1089 LogFlowThisFunc(("u32xPos=%RU32, u32yPos=%RU32, uAction=%RU32\n",
1090 u32xPos, u32yPos, uAction));
1091
1092 if ( m_mode != HG
1093 || m_state != Dragging)
1094 return VERR_INVALID_STATE;
1095
1096 int rc = VINF_SUCCESS;
1097 int xrc = Success;
1098
1099 /* Move the mouse cursor within the guest. */
1100 mouseCursorMove(u32xPos, u32yPos);
1101
1102 long newVer = -1; /* This means the current window is _not_ XdndAware. */
1103
1104 /* Search for the application window below the cursor. */
1105 Window wndCursor = gX11->applicationWindowBelowCursor(m_wndRoot);
1106 if (wndCursor != None)
1107 {
1108 /* Temp stuff for the XGetWindowProperty call. */
1109 Atom atmp;
1110 int fmt;
1111 unsigned long cItems, cbRemaining;
1112 unsigned char *pcData = NULL;
1113
1114 /* Query the XdndAware property from the window. We are interested in
1115 * the version and if it is XdndAware at all. */
1116 xrc = XGetWindowProperty(m_pDisplay, wndCursor, xAtom(XA_XdndAware),
1117 0, 2, False, AnyPropertyType,
1118 &atmp, &fmt, &cItems, &cbRemaining, &pcData);
1119
1120 if (RT_UNLIKELY(xrc != Success))
1121 LogFlowThisFunc(("Error in getting the window property: %s\n", gX11->xErrorToString(xrc).c_str()));
1122 else
1123 {
1124 if (RT_UNLIKELY(pcData == NULL || fmt != 32 || cItems != 1))
1125 LogFlowThisFunc(("Wrong properties pcData=%#x, iFmt=%u, cItems=%u\n", pcData, fmt, cItems));
1126 else
1127 {
1128 newVer = reinterpret_cast<long*>(pcData)[0];
1129 LogFlowThisFunc(("wndCursor=%#x, XdndAware=%u\n", newVer));
1130 }
1131
1132 XFree(pcData);
1133 }
1134 }
1135
1136 /*
1137 * Is the window under the cursor another one than our current one?
1138 */
1139 if ( wndCursor != m_wndCur
1140 && m_curVer != -1)
1141 {
1142 LogFlowThisFunc(("Leaving window=%#x\n", m_wndCur));
1143
1144 /* We left the current XdndAware window. Announce this to the window. */
1145 XClientMessageEvent m;
1146 RT_ZERO(m);
1147 m.type = ClientMessage;
1148 m.display = m_pDisplay;
1149 m.window = m_wndCur;
1150 m.message_type = xAtom(XA_XdndLeave);
1151 m.format = 32;
1152 m.data.l[0] = m_wndProxy;
1153
1154 xrc = XSendEvent(m_pDisplay, m_wndCur, False, NoEventMask, reinterpret_cast<XEvent*>(&m));
1155 if (RT_UNLIKELY(xrc == 0))
1156 LogFlowThisFunc(("Error sending XA_XdndLeave to old window=%#x\n", m_wndCur));
1157 }
1158
1159 /*
1160 * Do we have a new window which now is under the cursor?
1161 */
1162 if (wndCursor != m_wndCur && newVer != -1)
1163 {
1164 LogFlowThisFunc(("Entering window=%#x\n", wndCursor));
1165
1166 /*
1167 * We enter a new window. Announce the XdndEnter event to the new
1168 * window. The first three mime types are attached to the event (the
1169 * others could be requested by the XdndTypeList property from the
1170 * window itself).
1171 */
1172 XClientMessageEvent m;
1173 RT_ZERO(m);
1174 m.type = ClientMessage;
1175 m.display = m_pDisplay;
1176 m.window = wndCursor;
1177 m.message_type = xAtom(XA_XdndEnter);
1178 m.format = 32;
1179 m.data.l[0] = m_wndProxy;
1180 m.data.l[1] = RT_MAKE_U32_FROM_U8(m_formats.size() > 3 ? 1 : 0, 0, 0, RT_MIN(VBOX_XDND_VERSION, newVer));
1181 m.data.l[2] = m_formats.value(0, None);
1182 m.data.l[3] = m_formats.value(1, None);
1183 m.data.l[4] = m_formats.value(2, None);
1184
1185 xrc = XSendEvent(m_pDisplay, wndCursor, False, NoEventMask, reinterpret_cast<XEvent*>(&m));
1186 if (RT_UNLIKELY(xrc == 0))
1187 LogFlowThisFunc(("Error sending XA_XdndEnter to new window=%#x\n", wndCursor));
1188 }
1189
1190 if (newVer != -1)
1191 {
1192 LogFlowThisFunc(("Moving window=%#x, xPos=%RU32, yPos=%RU32\n",
1193 wndCursor, u32xPos, u32yPos));
1194
1195 /*
1196 * Send a XdndPosition event with the proposed action to the guest.
1197 */
1198 Atom pa = toX11Action(uAction);
1199 LogFlowThisFunc(("strAction=%s\n", xAtomToString(pa).c_str()));
1200
1201 XClientMessageEvent m;
1202 RT_ZERO(m);
1203 m.type = ClientMessage;
1204 m.display = m_pDisplay;
1205 m.window = wndCursor;
1206 m.message_type = xAtom(XA_XdndPosition);
1207 m.format = 32;
1208 m.data.l[0] = m_wndProxy;
1209 m.data.l[2] = RT_MAKE_U32(u32yPos, u32xPos);
1210 m.data.l[3] = CurrentTime;
1211 m.data.l[4] = pa;
1212
1213 xrc = XSendEvent(m_pDisplay, wndCursor, False, NoEventMask, reinterpret_cast<XEvent*>(&m));
1214 if (RT_UNLIKELY(xrc == 0))
1215 LogFlowThisFunc(("Error sending XA_XdndPosition to window=%#x\n", wndCursor));
1216 }
1217
1218 if ( wndCursor == None
1219 && newVer == -1)
1220 {
1221 /* No window to process, so send a ignore ack event to the host. */
1222 rc = VbglR3DnDHGAcknowledgeOperation(m_uClientID, DND_IGNORE_ACTION);
1223 }
1224
1225 m_wndCur = wndCursor;
1226 m_curVer = RT_MIN(VBOX_XDND_VERSION, newVer);
1227
1228 LogFlowFuncLeaveRC(rc);
1229 return rc;
1230}
1231
1232int DragInstance::hgDrop(void)
1233{
1234 LogFlowThisFunc(("wndCur=%#x, mMode=%RU32, mState=%RU32\n",
1235 m_wndCur, m_mode, m_state));
1236
1237 if ( m_mode != HG
1238 || m_state != Dragging)
1239 return VERR_INVALID_STATE;
1240
1241 int rc = VINF_SUCCESS;
1242
1243 /* Send a drop event to the current window and reset our DnD status. */
1244 XClientMessageEvent m;
1245 RT_ZERO(m);
1246 m.type = ClientMessage;
1247 m.display = m_pDisplay;
1248 m.window = m_wndCur;
1249 m.message_type = xAtom(XA_XdndDrop);
1250 m.format = 32;
1251 m.data.l[0] = m_wndProxy;
1252 m.data.l[2] = CurrentTime;
1253
1254 int xrc = XSendEvent(m_pDisplay, m_wndCur, False, NoEventMask, reinterpret_cast<XEvent*>(&m));
1255 if (RT_UNLIKELY(xrc == 0))
1256 LogFlowThisFunc(("Error sending XA_XdndDrop to window=%#x\n", m_wndCur));
1257
1258 m_wndCur = None;
1259 m_curVer = -1;
1260
1261 m_state = Dropped;
1262
1263 LogFlowFuncLeaveRC(rc);
1264 return rc;
1265}
1266
1267int DragInstance::hgDataReceived(void *pvData, uint32_t cData)
1268{
1269 if ( m_mode != HG
1270 || m_state != Dropped)
1271 return VERR_INVALID_STATE;
1272
1273 if (RT_UNLIKELY( pvData == NULL
1274 || cData == 0))
1275 return VERR_INVALID_PARAMETER;
1276
1277 if (RT_UNLIKELY(m_state != Dropped))
1278 return VERR_INVALID_STATE;
1279
1280 /* Make a copy of the data. The xserver will become the new owner. */
1281 void *pvNewData = RTMemAlloc(cData);
1282 if (RT_UNLIKELY(!pvNewData))
1283 return VERR_NO_MEMORY;
1284 memcpy(pvNewData, pvData, cData);
1285
1286 /*
1287 * The host send us the DnD data in the requested mime type. This allows us
1288 * to fill the XdndSelection property of the requestor window with the data
1289 * and afterwards inform the host about the new status.
1290 */
1291 XEvent s;
1292 RT_ZERO(s);
1293 s.xselection.type = SelectionNotify;
1294 s.xselection.display = m_eventHgSelection.xselection.display;
1295// s.xselection.owner = m_selEvent.xselectionrequest.owner;
1296 s.xselection.time = m_eventHgSelection.xselectionrequest.time;
1297 s.xselection.selection = m_eventHgSelection.xselectionrequest.selection;
1298 s.xselection.requestor = m_eventHgSelection.xselectionrequest.requestor;
1299 s.xselection.target = m_eventHgSelection.xselectionrequest.target;
1300 s.xselection.property = m_eventHgSelection.xselectionrequest.property;
1301
1302 LogFlowThisFunc(("owner=%#x, requestor=%#x, sel_atom=%s, target_atom=%s, prop_atom=%s, time=%u\n",
1303 m_eventHgSelection.xselectionrequest.owner,
1304 s.xselection.requestor,
1305 xAtomToString(s.xselection.selection).c_str(),
1306 xAtomToString(s.xselection.target).c_str(),
1307 xAtomToString(s.xselection.property).c_str(),
1308 s.xselection.time));
1309
1310 /* Fill up the property with the data. */
1311 XChangeProperty(s.xselection.display, s.xselection.requestor, s.xselection.property, s.xselection.target, 8, PropModeReplace,
1312 reinterpret_cast<const unsigned char*>(pvNewData), cData);
1313 int xrc = XSendEvent(s.xselection.display, s.xselection.requestor, True, 0, &s);
1314 if (RT_UNLIKELY(xrc == 0))
1315 LogFlowThisFunc(("Error sending SelectionNotify event to wnd=%#x\n", s.xselection.requestor));
1316
1317 return VINF_SUCCESS;
1318}
1319
1320#ifdef VBOX_WITH_DRAG_AND_DROP_GH
1321int DragInstance::ghIsDnDPending(void)
1322{
1323 int rc = VINF_SUCCESS;
1324 Window wndSelection = XGetSelectionOwner(m_pDisplay, xAtom(XA_XdndSelection));
1325 LogFlowThisFunc(("Checking pending wndSelection=%#x, wndProxy=%#x\n", wndSelection, m_wndProxy));
1326
1327 /* Is this another window which has a Xdnd selection than our current one? */
1328 if ( wndSelection
1329 && wndSelection != m_wndProxy)
1330 {
1331 m_mode = GH;
1332
1333 /* Map the window on the current cursor position, which should provoke
1334 * an XdndEnter event. */
1335 proxyWinShow();
1336
1337 XEvent e;
1338 if (waitForX11Msg(e, ClientMessage))
1339 {
1340 int xRc;
1341 XClientMessageEvent *pEventClient = reinterpret_cast<XClientMessageEvent*>(&e);
1342 AssertPtr(pEventClient);
1343
1344 LogFlowThisFunc(("Received event=%s\n",
1345 gX11->xAtomToString(pEventClient->message_type).c_str()));
1346
1347 if (pEventClient->message_type == xAtom(XA_XdndEnter))
1348 {
1349 Atom type = None;
1350 int f;
1351 unsigned long n, a;
1352 unsigned char *ret = 0;
1353
1354 reset();
1355
1356 m_state = Dragging;
1357 m_wndCur = wndSelection;
1358 Assert(m_wndCur == (Window)pEventClient->data.l[0]);
1359
1360 LogFlowThisFunc(("XA_XdndEnter\n"));
1361#ifdef DEBUG
1362 XWindowAttributes xwa;
1363 XGetWindowAttributes(m_pDisplay, m_wndCur, &xwa);
1364 LogFlowThisFunc(("m_wndCur=%#x, x=%d, y=%d, width=%d, height=%d\n",
1365 m_wndCur, xwa.x, xwa.y, xwa.width, xwa.height));
1366#endif
1367 /* Check if the MIME types are in the message itself or if we need
1368 * to fetch the XdndTypeList property from the window. */
1369 if (!ASMBitTest(&pEventClient->data.l[1], 0))
1370 {
1371 for (int i = 2; i < 5; ++i)
1372 {
1373 LogFlowThisFunc(("Received format via message: %s\n",
1374 gX11->xAtomToString(pEventClient->data.l[i]).c_str()));
1375
1376 m_formats.append(pEventClient->data.l[i]);
1377 }
1378 }
1379 else
1380 {
1381 xRc = XGetWindowProperty(m_pDisplay, wndSelection,
1382 xAtom(XA_XdndTypeList),
1383 0, VBOX_MAX_XPROPERTIES,
1384 False, XA_ATOM, &type, &f, &n, &a, &ret);
1385 if ( xRc == Success
1386 && n > 0
1387 && ret)
1388 {
1389 Atom *data = reinterpret_cast<Atom*>(ret);
1390 for (unsigned i = 0; i < RT_MIN(VBOX_MAX_XPROPERTIES, n); ++i)
1391 {
1392 LogFlowThisFunc(("Received format via XdndTypeList: %s\n",
1393 gX11->xAtomToString(data[i]).c_str()));
1394
1395 m_formats.append(data[i]);
1396 }
1397
1398 XFree(ret);
1399 }
1400 }
1401
1402 /* Fetch the possible list of actions, if this property is set. */
1403 xRc = XGetWindowProperty(m_pDisplay, wndSelection,
1404 xAtom(XA_XdndActionList),
1405 0, VBOX_MAX_XPROPERTIES,
1406 False, XA_ATOM, &type, &f, &n, &a, &ret);
1407 if ( xRc == Success
1408 && n > 0
1409 && ret)
1410 {
1411 Atom *data = reinterpret_cast<Atom*>(ret);
1412 for (unsigned i = 0; i < RT_MIN(VBOX_MAX_XPROPERTIES, n); ++i)
1413 {
1414 LogFlowThisFunc(("Received action: %s\n",
1415 gX11->xAtomToString(data[i]).c_str()));
1416
1417 m_actions.append(data[i]);
1418 }
1419
1420 XFree(ret);
1421 }
1422
1423 /* Acknowledge the event by sending a Status msg back to the
1424 * window. */
1425 XClientMessageEvent m;
1426 RT_ZERO(m);
1427 m.type = ClientMessage;
1428 m.display = m_pDisplay;
1429 m.window = m_wndCur;
1430 m.message_type = xAtom(XA_XdndStatus);
1431 m.format = 32;
1432 m.data.l[0] = m_wndProxy;
1433 m.data.l[1] = RT_BIT(0); /* Accept the drop. */
1434 m.data.l[4] = xAtom(XA_XdndActionCopy); /** @todo Make the accepted action configurable. */
1435
1436 xRc = XSendEvent(m_pDisplay, m_wndCur,
1437 False, 0, reinterpret_cast<XEvent*>(&m));
1438 if (RT_UNLIKELY(xRc == 0))
1439 LogFlowThisFunc(("Error sending xEvent\n"));
1440 }
1441 /* Did the source tell us where the cursor currently is? */
1442 else if (pEventClient->message_type == xAtom(XA_XdndPosition))
1443 {
1444 LogFlowThisFunc(("XA_XdndPosition\n"));
1445
1446 /* Reply with a XdndStatus message to tell the source whether
1447 * the data can be dropped or not. */
1448 XClientMessageEvent m;
1449 RT_ZERO(m);
1450 m.type = ClientMessage;
1451 m.display = m_pDisplay;
1452 m.window = m_wndCur;
1453 m.message_type = xAtom(XA_XdndStatus);
1454 m.format = 32;
1455 m.data.l[0] = m_wndProxy;
1456 m.data.l[1] = RT_BIT(0); /* Accept the drop. */
1457 m.data.l[4] = xAtom(XA_XdndActionCopy); /** @todo Make the accepted action configurable. */
1458
1459 xRc = XSendEvent(m_pDisplay, m_wndCur,
1460 False, 0, reinterpret_cast<XEvent*>(&m));
1461 if (RT_UNLIKELY(xRc == 0))
1462 LogFlowThisFunc(("Error sending xEvent\n"));
1463 }
1464 else if (pEventClient->message_type == xAtom(XA_XdndLeave))
1465 {
1466 LogFlowThisFunc(("XA_XdndLeave\n"));
1467 }
1468 }
1469
1470 /* Do we need to acknowledge at least one format to the host? */
1471 if (!m_formats.isEmpty())
1472 {
1473 RTCString strFormats = gX11->xAtomListToString(m_formats);
1474 uint32_t uDefAction = DND_COPY_ACTION; /** @todo Handle default action! */
1475 uint32_t uAllActions = toHGCMActions(m_actions);
1476
1477 rc = VbglR3DnDGHAcknowledgePending(m_uClientID, uDefAction, uAllActions, strFormats.c_str());
1478 LogFlowThisFunc(("Acknowledging m_uClientID=%RU32, allActions=0x%x, strFormats=%s, rc=%Rrc\n",
1479 m_uClientID, uAllActions, strFormats.c_str(), rc));
1480 }
1481 }
1482
1483 LogFlowFuncLeaveRC(rc);
1484 return rc;
1485}
1486
1487int DragInstance::ghDropped(const RTCString &strFormat, uint32_t uAction)
1488{
1489 LogFlowThisFunc(("strFormat=%s, uAction=%RU32\n",
1490 strFormat.c_str(), uAction));
1491
1492 int rc = VINF_SUCCESS;
1493
1494 m_state = Dropped;
1495
1496 /* Show the proxy window, so that the source will find it. */
1497 int iRootX, iRootY;
1498 proxyWinShow(&iRootX, &iRootY);
1499 XFlush(m_pDisplay);
1500
1501#ifdef DEBUG
1502 XWindowAttributes xwa;
1503 XGetWindowAttributes(m_pDisplay, m_wndCur, &xwa);
1504 LogFlowThisFunc(("m_wndCur=%#x, x=%d, y=%d, width=%d, height=%d\n",
1505 m_wndCur, xwa.x, xwa.y, xwa.width, xwa.height));
1506
1507 iRootX = xwa.x;
1508 iRootY = xwa.y;
1509#endif
1510
1511 /* We send a fake release event to the current window, cause
1512 * this should have the grab. */
1513 mouseButtonSet(m_wndCur /* Source window */, iRootX, iRootY,
1514 1 /* Button */, false /* Release button */);
1515
1516 /* The fake button release event should lead to a XdndDrop event from the
1517 * source. Because of showing our proxy window, other Xdnd events can
1518 * occur before, e.g. a XdndPosition event. We are not interested
1519 * in those, so just try to get the right one. */
1520
1521 XClientMessageEvent evDnDDrop;
1522 bool fDrop = waitForX11ClientMsg(evDnDDrop, xAtom(XA_XdndDrop),
1523 5 * 1000 /* Timeout in ms */);
1524 if (fDrop)
1525 {
1526 /* Request to convert the selection in the specific format and
1527 * place it to our proxy window as property. */
1528 Window wndSource = evDnDDrop.data.l[0]; /* Source window which sent the message. */
1529 Assert(wndSource == m_wndCur);
1530 Atom aFormat = gX11->stringToxAtom(strFormat.c_str());
1531
1532 XConvertSelection(m_pDisplay, xAtom(XA_XdndSelection),
1533 aFormat, xAtom(XA_XdndSelection),
1534 m_wndProxy, evDnDDrop.data.l[2]);
1535
1536 /* Wait for the selection notify event. */
1537 XEvent evSelNotify;
1538 RT_ZERO(evSelNotify);
1539 if (waitForX11Msg(evSelNotify, SelectionNotify))
1540 {
1541 bool fCancel = false;
1542
1543 /* Make some paranoid checks. */
1544 if ( evSelNotify.xselection.type == SelectionNotify
1545 && evSelNotify.xselection.display == m_pDisplay
1546 && evSelNotify.xselection.selection == xAtom(XA_XdndSelection)
1547 && evSelNotify.xselection.requestor == m_wndProxy
1548 && evSelNotify.xselection.target == aFormat)
1549 {
1550 LogFlowThisFunc(("Selection notfiy (from wnd=%#x)\n", m_wndCur));
1551
1552 Atom aPropType;
1553 int iPropFormat;
1554 unsigned long cItems, cbRemaining;
1555 unsigned char *pcData = NULL;
1556 XGetWindowProperty(m_pDisplay, m_wndProxy,
1557 xAtom(XA_XdndSelection) /* Property */,
1558 0 /* Offset */,
1559 VBOX_MAX_XPROPERTIES /* Length of 32-bit multiples */,
1560 True /* Delete property? */,
1561 AnyPropertyType, /* Property type */
1562 &aPropType, &iPropFormat, &cItems, &cbRemaining, &pcData);
1563
1564 LogFlowThisFunc(("strType=%s, iPropFormat=%d, cItems=%RU32, cbRemaining=%RU32\n",
1565 gX11->xAtomToString(aPropType).c_str(), iPropFormat, cItems, cbRemaining));
1566
1567 if ( aPropType != None
1568 && pcData != NULL
1569 && iPropFormat >= 8
1570 && cItems > 0
1571 && cbRemaining == 0)
1572 {
1573 size_t cbData = cItems * (iPropFormat / 8);
1574 LogFlowThisFunc(("cbData=%zu\n", cbData));
1575
1576 /* For whatever reason some of the string MIME types are not
1577 * zero terminated. Check that and correct it when necessary,
1578 * because the guest side wants this in any case. */
1579 if ( m_lstAllowedFormats.contains(strFormat)
1580 && pcData[cbData - 1] != '\0')
1581 {
1582 unsigned char *pvDataTmp = static_cast<unsigned char*>(RTMemAlloc(cbData + 1));
1583 if (pvDataTmp)
1584 {
1585 memcpy(pvDataTmp, pcData, cbData);
1586 pvDataTmp[cbData++] = '\0';
1587
1588 rc = VbglR3DnDGHSendData(m_uClientID, strFormat.c_str(),
1589 pvDataTmp, cbData);
1590 RTMemFree(pvDataTmp);
1591 }
1592 else
1593 rc = VERR_NO_MEMORY;
1594 }
1595 else
1596 {
1597 /* Send the raw data to the host. */
1598 rc = VbglR3DnDGHSendData(m_uClientID, strFormat.c_str(),
1599 pcData, cbData);
1600 }
1601
1602 LogFlowThisFunc(("Sent strFormat=%s with rc=%Rrc\n",
1603 strFormat.c_str(), rc));
1604
1605 if (RT_SUCCESS(rc))
1606 {
1607 /* Confirm the result of the transfer to the source window. */
1608 XClientMessageEvent m;
1609 RT_ZERO(m);
1610 m.type = ClientMessage;
1611 m.display = m_pDisplay;
1612 m.window = m_wndProxy;
1613 m.message_type = xAtom(XA_XdndFinished);
1614 m.format = 32;
1615 m.data.l[0] = m_wndProxy; /* Target window. */
1616 m.data.l[1] = 0; /* Don't accept the drop to not make the guest stuck. */
1617 m.data.l[2] = RT_SUCCESS(rc) ? toX11Action(uAction) : None; /* Action used on success */
1618
1619 int xrc = XSendEvent(m_pDisplay, wndSource, True, NoEventMask, reinterpret_cast<XEvent*>(&m));
1620 if (RT_UNLIKELY(xrc == 0))
1621 LogFlowThisFunc(("Error sending xEvent\n"));
1622 }
1623 else
1624 fCancel = true;
1625 }
1626 else
1627 {
1628 if (aPropType == xAtom(XA_INCR))
1629 {
1630 /** @todo Support incremental transfers. */
1631 AssertMsgFailed(("Incrementally transfers are not supported yet\n"));
1632 rc = VERR_NOT_IMPLEMENTED;
1633 }
1634 else
1635 {
1636 AssertMsgFailed(("Not supported data type (%s)\n",
1637 gX11->xAtomToString(aPropType).c_str()));
1638 rc = VERR_INVALID_PARAMETER;
1639 }
1640
1641 fCancel = true;
1642 }
1643
1644 if (fCancel)
1645 {
1646 LogFlowFunc(("Cancelling drop ...\n"));
1647
1648 /* Cancel this. */
1649 XClientMessageEvent m;
1650 RT_ZERO(m);
1651 m.type = ClientMessage;
1652 m.display = m_pDisplay;
1653 m.window = m_wndProxy;
1654 m.message_type = xAtom(XA_XdndFinished);
1655 m.format = 32;
1656 m.data.l[0] = m_wndProxy; /* Target window. */
1657 m.data.l[1] = 0; /* Don't accept the drop to not make the guest stuck. */
1658
1659 int xrc = XSendEvent(m_pDisplay, wndSource, False, NoEventMask, reinterpret_cast<XEvent*>(&m));
1660 if (RT_UNLIKELY(xrc == 0))
1661 LogFlowThisFunc(("Error sending cancel event\n"));
1662 }
1663
1664 /* Cleanup. */
1665 if (pcData)
1666 XFree(pcData);
1667 }
1668 else
1669 rc = VERR_INVALID_PARAMETER;
1670 }
1671 else
1672 rc = VERR_TIMEOUT;
1673 }
1674 else
1675 rc = VERR_TIMEOUT;
1676
1677 /* Inform the host on error. */
1678 if (RT_FAILURE(rc))
1679 {
1680 int rc2 = VbglR3DnDGHSendError(m_uClientID, rc);
1681 AssertRC(rc2);
1682 }
1683
1684 /* At this point, we have either successfully transfered any data or not.
1685 * So reset our internal state because we are done here for this transaction. */
1686 reset();
1687
1688 LogFlowFuncLeaveRC(rc);
1689 return rc;
1690}
1691#endif /* VBOX_WITH_DRAG_AND_DROP_GH */
1692
1693/*
1694 * Helpers
1695 */
1696
1697int DragInstance::mouseCursorMove(int iPosX, int iPosY) const
1698{
1699 LogFlowThisFunc(("iPosX=%d, iPosY=%d\n", iPosX, iPosY));
1700
1701 /* Move the guest pointer to the DnD position, so we can find the window
1702 * below that position. */
1703 XWarpPointer(m_pDisplay, None, m_wndRoot, 0, 0, 0, 0, iPosX, iPosY);
1704 return VINF_SUCCESS;
1705}
1706
1707void DragInstance::mouseButtonSet(Window wndDest, int rx, int ry, int iButton, bool fPress) const
1708{
1709 LogFlowThisFunc(("wndDest=%#x, rx=%d, ry=%d, iBtn=%d, fPress=%RTbool\n",
1710 wndDest, rx, ry, iButton, fPress));
1711#if 0
1712 // Create and setting up the event
1713 XEvent event;
1714 memset (&event, 0, sizeof (event));
1715 event.xbutton.button = iButton;
1716 event.xbutton.same_screen = True;
1717 event.xbutton.subwindow = DefaultRootWindow (m_pDisplay);
1718
1719 while (event.xbutton.subwindow)
1720 {
1721 event.xbutton.window = event.xbutton.subwindow;
1722 XQueryPointer (m_pDisplay, event.xbutton.window,
1723 &event.xbutton.root, &event.xbutton.subwindow,
1724 &event.xbutton.x_root, &event.xbutton.y_root,
1725 &event.xbutton.x, &event.xbutton.y,
1726 &event.xbutton.state);
1727 }
1728 // Press
1729 event.type = ButtonPress;
1730 if (XSendEvent (m_pDisplay, PointerWindow, True, ButtonPressMask, &event) == 0)
1731 LogFlowThisFunc(("Error sending XTestFakeButtonEvent event\n"));
1732 XFlush (m_pDisplay);
1733
1734 // Release
1735 event.type = ButtonRelease;
1736 if (XSendEvent (m_pDisplay, PointerWindow, True, ButtonReleaseMask, &event) == 0)
1737 LogFlowThisFunc(("Error sending XTestFakeButtonEvent event\n"));
1738 XFlush (m_pDisplay);
1739
1740#else
1741#ifdef VBOX_DND_WITH_XTEST
1742 /** @todo Make this check run only once. */
1743 int ev, er, ma, mi;
1744 if (XTestQueryExtension(m_pDisplay, &ev, &er, &ma, &mi))
1745 {
1746 LogFlowThisFunc(("XText extension available\n"));
1747
1748 int xrc = XTestFakeButtonEvent(m_pDisplay, 1, fPress ? True : False, CurrentTime);
1749 if (RT_UNLIKELY(xrc == 0))
1750 LogFlowThisFunc(("Error sending XTestFakeButtonEvent event\n"));
1751 XFlush(m_pDisplay);
1752 }
1753 else
1754 {
1755#endif
1756 LogFlowThisFunc(("XText extension not available or disabled\n"));
1757
1758 XButtonEvent eBtn;
1759 RT_ZERO(eBtn);
1760
1761 eBtn.display = m_pDisplay;
1762 eBtn.root = m_wndRoot;
1763 eBtn.window = wndDest;
1764 eBtn.subwindow = None;
1765 eBtn.same_screen = True;
1766 eBtn.time = CurrentTime;
1767 eBtn.button = iButton;
1768 eBtn.state |= iButton == 1 ? Button1Mask /*:
1769 iButton == 2 ? Button2MotionMask :
1770 iButton == 3 ? Button3MotionMask :
1771 iButton == 4 ? Button4MotionMask :
1772 iButton == 5 ? Button5MotionMask*/ : 0;
1773 eBtn.type = fPress ? ButtonPress : ButtonRelease;
1774 eBtn.send_event = False;
1775 eBtn.x_root = rx;
1776 eBtn.y_root = ry;
1777
1778 //XTranslateCoordinates(m_pDisplay, eBtn.root, eBtn.window, eBtn.x_root, eBtn.y_root, &eBtn.x, &eBtn.y, &eBtn.subwindow);
1779#if 0
1780 int xrc = XSendEvent(m_pDisplay, eBtn.window, True /* fPropagate */,
1781 fPress
1782 ? ButtonPressMask : ButtonReleaseMask,
1783 reinterpret_cast<XEvent*>(&eBtn));
1784#else
1785 int xrc = XSendEvent(m_pDisplay, eBtn.window, False /* fPropagate */,
1786 0 /* Mask */,
1787 reinterpret_cast<XEvent*>(&eBtn));
1788#endif
1789 if (RT_UNLIKELY(xrc == 0))
1790 LogFlowThisFunc(("Error sending XSendEvent\n"));
1791
1792#ifdef DEBUG
1793 Window wndTemp, wndChild;
1794 int wx, wy; unsigned int mask;
1795 XQueryPointer(m_pDisplay, m_wndRoot, &wndTemp, &wndChild, &rx, &ry, &wx, &wy, &mask);
1796 LogFlowFunc(("cursorRootX=%d, cursorRootY=%d\n", rx, ry));
1797#endif
1798
1799#ifdef VBOX_DND_WITH_XTEST
1800 }
1801#endif
1802#endif
1803}
1804
1805int DragInstance::proxyWinShow(int *piRootX /*= NULL*/,
1806 int *piRootY /*= NULL*/,
1807 bool fMouseMove /*= false */) const
1808{
1809 /* piRootX is optional. */
1810 /* piRootY is optional. */
1811
1812 LogFlowThisFuncEnter();
1813
1814 int iRootX, iRootY;
1815 int iChildX, iChildY;
1816 unsigned int iMask;
1817 Window wndRoot, wndChild;
1818
1819// XTestGrabControl(m_pDisplay, False);
1820 Bool fInRootWnd = XQueryPointer(m_pDisplay, m_wndRoot, &wndRoot, &wndChild,
1821 &iRootX, &iRootY,
1822 &iChildX, &iChildY, &iMask);
1823#ifdef DEBUG_andy
1824 LogFlowThisFunc(("fInRootWnd=%RTbool, wndRoot=0x%x, wndChild=0x%x, iRootX=%d, iRootY=%d\n",
1825 RT_BOOL(fInRootWnd), wndRoot, wndChild, iRootX, iRootY));
1826#endif
1827
1828 if (piRootX)
1829 *piRootX = iRootX;
1830 if (piRootY)
1831 *piRootY = iRootY;
1832
1833 XSynchronize(m_pDisplay, True /* Enable sync */);
1834
1835 XMapWindow(m_pDisplay, m_wndProxy);
1836 XRaiseWindow(m_pDisplay, m_wndProxy);
1837 XMoveResizeWindow(m_pDisplay, m_wndProxy, iRootX, iRootY, 100, 100);
1838
1839 if (fMouseMove)
1840 XWarpPointer(m_pDisplay, None, m_wndRoot, 0, 0, 0, 0, iRootX, iRootY);
1841
1842 XSynchronize(m_pDisplay, False /* Disable sync */);
1843// XTestGrabControl(m_pDisplay, True);
1844
1845 return VINF_SUCCESS; /** @todo Add error checking. */
1846}
1847
1848int DragInstance::proxyWinHide(void)
1849{
1850 LogFlowFuncEnter();
1851
1852 XUnmapWindow(m_pDisplay, m_wndProxy);
1853 m_eventQueue.clear();
1854
1855 return VINF_SUCCESS; /** @todo Add error checking. */
1856}
1857
1858/* Currently not used. */
1859/** @todo Is this function still needed? */
1860void DragInstance::registerForEvents(Window wndThis) const
1861{
1862 if (wndThis == m_wndProxy)
1863 return;
1864
1865 LogFlowThisFunc(("%x\n", wndThis));
1866// XSelectInput(m_pDisplay, w, Button1MotionMask | Button2MotionMask | Button3MotionMask | Button4MotionMask | Button5MotionMask);//| SubstructureNotifyMask);
1867// XSelectInput(m_pDisplay, w, ButtonMotionMask); //PointerMotionMask);
1868 XSelectInput(m_pDisplay, wndThis, PointerMotionMask); //PointerMotionMask);
1869 Window hRealRoot, hParent;
1870 Window *phChildrenRaw = NULL;
1871 unsigned cChildren;
1872 if (XQueryTree(m_pDisplay, wndThis, &hRealRoot, &hParent, &phChildrenRaw, &cChildren))
1873 {
1874 for (unsigned i = 0; i < cChildren; ++i)
1875 registerForEvents(phChildrenRaw[i]);
1876 XFree(phChildrenRaw);
1877 }
1878}
1879
1880void DragInstance::setActionsWindowProperty(Window wndThis,
1881 const RTCList<Atom> &lstActions) const
1882{
1883 if (lstActions.isEmpty())
1884 return;
1885
1886 XChangeProperty(m_pDisplay, wndThis,
1887 xAtom(XA_XdndActionList),
1888 XA_ATOM, 32, PropModeReplace,
1889 reinterpret_cast<const unsigned char*>(lstActions.raw()),
1890 lstActions.size());
1891}
1892
1893void DragInstance::clearActionsWindowProperty(Window wndThis) const
1894{
1895 XDeleteProperty(m_pDisplay, wndThis, xAtom(XA_XdndActionList));
1896}
1897
1898void DragInstance::setFormatsWindowProperty(Window wndThis,
1899 Atom property) const
1900{
1901 if (m_formats.isEmpty())
1902 return;
1903
1904 /* We support TARGETS and the data types. */
1905 RTCList<Atom> targets(m_formats.size() + 1);
1906 targets.append(xAtom(XA_TARGETS));
1907 targets.append(m_formats);
1908
1909 /* Add the property with the property data to the window. */
1910 XChangeProperty(m_pDisplay, wndThis, property,
1911 XA_ATOM, 32, PropModeReplace,
1912 reinterpret_cast<const unsigned char*>(targets.raw()),
1913 targets.size());
1914}
1915
1916void DragInstance::clearFormatsWindowProperty(Window wndThis) const
1917{
1918 XDeleteProperty(m_pDisplay, wndThis,
1919 xAtom(XA_XdndTypeList));
1920}
1921
1922RTCList<Atom> DragInstance::toAtomList(const RTCList<RTCString> &lstFormats) const
1923{
1924 RTCList<Atom> atomList;
1925 for (size_t i = 0; i < lstFormats.size(); ++i)
1926 atomList.append(XInternAtom(m_pDisplay, lstFormats.at(i).c_str(), False));
1927
1928 return atomList;
1929}
1930
1931RTCList<Atom> DragInstance::toAtomList(void *pvData, uint32_t cbData) const
1932{
1933 if ( !pvData
1934 || !cbData)
1935 return RTCList<Atom>();
1936
1937 char *pszStr = (char*)pvData;
1938 uint32_t cbStr = cbData;
1939
1940 RTCList<Atom> lstAtom;
1941 while (cbStr)
1942 {
1943 size_t cbSize = RTStrNLen(pszStr, cbStr);
1944
1945 /* Create a copy with max N chars, so that we are on the save side,
1946 * even if the data isn't zero terminated. */
1947 char *pszTmp = RTStrDupN(pszStr, cbSize);
1948 LogFlowThisFunc(("f: %s\n", pszTmp));
1949 lstAtom.append(XInternAtom(m_pDisplay, pszTmp, False));
1950 RTStrFree(pszTmp);
1951 pszStr += cbSize + 1;
1952 cbStr -= cbSize + 1;
1953 }
1954
1955 return lstAtom;
1956}
1957
1958/* static */
1959Atom DragInstance::toX11Action(uint32_t uAction)
1960{
1961 /* Ignore is None. */
1962 return (isDnDCopyAction(uAction) ? xAtom(XA_XdndActionCopy) :
1963 isDnDMoveAction(uAction) ? xAtom(XA_XdndActionMove) :
1964 isDnDLinkAction(uAction) ? xAtom(XA_XdndActionLink) :
1965 None);
1966}
1967
1968/* static */
1969RTCList<Atom> DragInstance::toX11Actions(uint32_t uActions)
1970{
1971 RTCList<Atom> actionList;
1972 if (hasDnDCopyAction(uActions))
1973 actionList.append(xAtom(XA_XdndActionCopy));
1974 if (hasDnDMoveAction(uActions))
1975 actionList.append(xAtom(XA_XdndActionMove));
1976 if (hasDnDLinkAction(uActions))
1977 actionList.append(xAtom(XA_XdndActionLink));
1978
1979 return actionList;
1980}
1981
1982/* static */
1983uint32_t DragInstance::toHGCMAction(Atom atom)
1984{
1985 uint32_t uAction = DND_IGNORE_ACTION;
1986
1987 if (atom == xAtom(XA_XdndActionCopy))
1988 uAction = DND_COPY_ACTION;
1989 else if (atom == xAtom(XA_XdndActionMove))
1990 uAction = DND_MOVE_ACTION;
1991 else if (atom == xAtom(XA_XdndActionLink))
1992 uAction = DND_LINK_ACTION;
1993
1994 return uAction;
1995}
1996
1997/* static */
1998uint32_t DragInstance::toHGCMActions(const RTCList<Atom> &actionsList)
1999{
2000 uint32_t uActions = DND_IGNORE_ACTION;
2001
2002 for (size_t i = 0; i < actionsList.size(); ++i)
2003 uActions |= toHGCMAction(actionsList.at(i));
2004
2005 return uActions;
2006}
2007
2008/** @todo Replace by DnDURIList? */
2009RTCList<RTCString> toStringList(const void *pvData, uint32_t cbData)
2010{
2011 if ( !pvData
2012 || !cbData)
2013 return RTCList<RTCString>();
2014
2015 const char *pszStr = (char*)pvData;
2016 uint32_t cbStr = cbData;
2017
2018 RTCList<RTCString> lstString;
2019 while (cbStr > 0)
2020 {
2021 size_t cbSize = RTStrNLen(pszStr, cbStr);
2022
2023 /* Create a copy with max N chars, so that we are on the save side,
2024 * even if the data isn't zero terminated. */
2025 char *pszTmp = RTStrDupN(pszStr, cbSize);
2026 lstString.append(pszTmp);
2027 RTStrFree(pszTmp);
2028 pszStr += cbSize + 1;
2029 cbStr -= cbSize + 1;
2030 }
2031
2032 return lstString;
2033}
2034
2035/** @todo Is this function really needed? */
2036void DragAndDropService::clearEventQueue(void)
2037{
2038 LogFlowThisFuncEnter();
2039 m_eventQueue.clear();
2040}
2041
2042/*******************************************************************************
2043 * DragAndDropService implementation.
2044 ******************************************************************************/
2045
2046int DragAndDropService::run(bool fDaemonised /* = false */)
2047{
2048 LogFlowThisFunc(("fDaemonised=%RTbool\n", fDaemonised));
2049
2050 int rc;
2051 do
2052 {
2053 /* Initialize X11 DnD. */
2054 rc = x11DragAndDropInit();
2055 if (RT_FAILURE(rc))
2056 break;
2057
2058 m_pCurDnD = new DragInstance(m_pDisplay, this);
2059 if (!m_pCurDnD)
2060 {
2061 rc = VERR_NO_MEMORY;
2062 break;
2063 }
2064
2065 /* Note: For multiple screen support in VBox it is not necessary to use
2066 * another screen number than zero. Maybe in the future it will become
2067 * necessary if VBox supports multiple X11 screens. */
2068 rc = m_pCurDnD->init(0);
2069 if (RT_FAILURE(rc))
2070 break;
2071
2072 LogRel(("DnD: Started\n"));
2073
2074 /* Enter the main event processing loop. */
2075 do
2076 {
2077 DnDEvent e;
2078 RT_ZERO(e);
2079
2080 LogFlowFunc(("Waiting for new event ...\n"));
2081 rc = RTSemEventWait(m_hEventSem, RT_INDEFINITE_WAIT);
2082 if (RT_FAILURE(rc))
2083 break;
2084
2085 AssertMsg(m_eventQueue.size(),
2086 ("Event queue is empty when it shouldn't\n"));
2087
2088 e = m_eventQueue.first();
2089 m_eventQueue.removeFirst();
2090
2091 if (e.type == DnDEvent::HGCM_Type)
2092 {
2093 LogFlowThisFunc(("HGCM event, type=%RU32\n", e.hgcm.uType));
2094 switch (e.hgcm.uType)
2095 {
2096 case DragAndDropSvc::HOST_DND_HG_EVT_ENTER:
2097 {
2098 if (e.hgcm.cbFormats)
2099 {
2100 RTCList<RTCString> lstFormats
2101 = RTCString(e.hgcm.pszFormats, e.hgcm.cbFormats - 1).split("\r\n");
2102 m_pCurDnD->hgEnter(lstFormats, e.hgcm.u.a.uAllActions);
2103
2104 /* Enter is always followed by a move event. */
2105 }
2106 else /* Invalid parameter, skip. */
2107 break;
2108 /* Not breaking unconditionally is intentional. See comment above. */
2109 }
2110 case DragAndDropSvc::HOST_DND_HG_EVT_MOVE:
2111 {
2112 m_pCurDnD->hgMove(e.hgcm.u.a.uXpos, e.hgcm.u.a.uYpos,
2113 e.hgcm.u.a.uDefAction);
2114 break;
2115 }
2116 case DragAndDropSvc::HOST_DND_HG_EVT_LEAVE:
2117 {
2118 m_pCurDnD->reset();
2119 break;
2120 }
2121 case DragAndDropSvc::HOST_DND_HG_EVT_DROPPED:
2122 {
2123 m_pCurDnD->hgDrop();
2124 break;
2125 }
2126 case DragAndDropSvc::HOST_DND_HG_SND_DATA:
2127 {
2128 m_pCurDnD->hgDataReceived(e.hgcm.u.b.pvData, e.hgcm.u.b.cbData);
2129 break;
2130 }
2131 case DragAndDropSvc::HOST_DND_GH_REQ_PENDING:
2132 {
2133#ifdef VBOX_WITH_DRAG_AND_DROP_GH
2134 m_pCurDnD->ghIsDnDPending();
2135#endif
2136 break;
2137 }
2138 case DragAndDropSvc::HOST_DND_GH_EVT_DROPPED:
2139 {
2140#ifdef VBOX_WITH_DRAG_AND_DROP_GH
2141 m_pCurDnD->ghDropped(e.hgcm.pszFormats,
2142 e.hgcm.u.a.uDefAction);
2143#endif
2144 break;
2145 }
2146
2147 default:
2148 LogFlowThisFunc(("Unknown message: %RU32\n", e.hgcm.uType));
2149 break;
2150 }
2151
2152 /* Some messages require cleanup. */
2153 switch (e.hgcm.uType)
2154 {
2155 case DragAndDropSvc::HOST_DND_HG_EVT_ENTER:
2156 case DragAndDropSvc::HOST_DND_HG_EVT_MOVE:
2157 case DragAndDropSvc::HOST_DND_HG_EVT_DROPPED:
2158#ifdef VBOX_WITH_DRAG_AND_DROP_GH
2159 case DragAndDropSvc::HOST_DND_GH_EVT_DROPPED:
2160#endif
2161 {
2162 if (e.hgcm.pszFormats)
2163 RTMemFree(e.hgcm.pszFormats);
2164 break;
2165 }
2166
2167 case DragAndDropSvc::HOST_DND_HG_SND_DATA:
2168 {
2169 if (e.hgcm.pszFormats)
2170 RTMemFree(e.hgcm.pszFormats);
2171 if (e.hgcm.u.b.pvData)
2172 RTMemFree(e.hgcm.u.b.pvData);
2173 break;
2174 }
2175
2176 default:
2177 break;
2178 }
2179 }
2180 else if (e.type == DnDEvent::X11_Type)
2181 {
2182 m_pCurDnD->onX11Event(e.x11);
2183 }
2184 else
2185 AssertMsgFailed(("Unknown event queue type %d\n", e.type));
2186
2187 /* Make sure that any X11 requests have actually been sent to the
2188 * server, since we are waiting for responses using poll() on
2189 * another thread which will not automatically trigger flushing. */
2190 XFlush(m_pDisplay);
2191
2192 } while (!ASMAtomicReadBool(&m_fSrvStopping));
2193
2194 } while (0);
2195
2196 LogRel(("DnD: Stopped with rc=%Rrc\n", rc));
2197 return rc;
2198}
2199
2200int DragAndDropService::x11DragAndDropInit(void)
2201{
2202 /* Initialise the guest library. */
2203 int rc = VbglR3InitUser();
2204 if (RT_FAILURE(rc))
2205 VBClFatalError(("Failed to connect to the VirtualBox kernel service, rc=%Rrc\n", rc));
2206 /* Connect to the x11 server. */
2207 m_pDisplay = XOpenDisplay(NULL);
2208 if (!m_pDisplay)
2209 /** @todo Correct errors. */
2210 return VERR_NOT_FOUND;
2211
2212 xHelpers *pHelpers = xHelpers::getInstance(m_pDisplay);
2213 if (!pHelpers)
2214 return VERR_NO_MEMORY;
2215
2216 do
2217 {
2218 rc = RTSemEventCreate(&m_hEventSem);
2219 if (RT_FAILURE(rc))
2220 break;
2221
2222 rc = RTCritSectInit(&m_eventQueueCS);
2223 if (RT_FAILURE(rc))
2224 break;
2225
2226 /* Event thread for events coming from the HGCM device. */
2227 rc = RTThreadCreate(&m_hHGCMThread, hgcmEventThread, this,
2228 0, RTTHREADTYPE_MSG_PUMP, RTTHREADFLAGS_WAITABLE,
2229 "HGCM-NOTIFY");
2230 if (RT_FAILURE(rc))
2231 break;
2232
2233 /* Event thread for events coming from the x11 system. */
2234 rc = RTThreadCreate(&m_hX11Thread, x11EventThread, this,
2235 0, RTTHREADTYPE_MSG_PUMP, RTTHREADFLAGS_WAITABLE,
2236 "X11-NOTIFY");
2237 } while (0);
2238
2239 /* No clean-up code for now, as we have no good way of testing it and things
2240 * should get cleaned up when the user process/X11 client exits. */
2241 if (RT_FAILURE(rc))
2242 LogRel(("DnD: Failed to start, rc=%Rrc\n", rc));
2243
2244 return rc;
2245}
2246
2247/* static */
2248int DragAndDropService::hgcmEventThread(RTTHREAD hThread, void *pvUser)
2249{
2250 AssertPtrReturn(pvUser, VERR_INVALID_PARAMETER);
2251 DragAndDropService *pThis = static_cast<DragAndDropService*>(pvUser);
2252 AssertPtr(pThis);
2253
2254 uint32_t uClientID;
2255 int rc = VbglR3DnDConnect(&uClientID);
2256 if (RT_FAILURE(rc))
2257 {
2258 LogRel(("DnD: Unable to connect to drag'n drop service, rc=%Rrc\n", rc));
2259 return rc;
2260 }
2261
2262 /* Number of invalid messages skipped in a row. */
2263 int cMsgSkippedInvalid = 0;
2264 DnDEvent e;
2265
2266 do
2267 {
2268 RT_ZERO(e);
2269 e.type = DnDEvent::HGCM_Type;
2270
2271 /* Wait for new events. */
2272 rc = VbglR3DnDProcessNextMessage(uClientID, &e.hgcm);
2273 if (RT_SUCCESS(rc))
2274 {
2275 cMsgSkippedInvalid = 0; /* Reset skipped messages count. */
2276
2277 LogFlowFunc(("Adding new HGCM event ...\n"));
2278 pThis->m_eventQueue.append(e);
2279
2280 rc = RTSemEventSignal(pThis->m_hEventSem);
2281 if (RT_FAILURE(rc))
2282 break;
2283 }
2284 else
2285 {
2286 /* Old(er) hosts either are broken regarding DnD support or otherwise
2287 * don't support the stuff we do on the guest side, so make sure we
2288 * don't process invalid messages forever. */
2289 if (rc == VERR_INVALID_PARAMETER)
2290 cMsgSkippedInvalid++;
2291 if (cMsgSkippedInvalid > 3)
2292 {
2293 LogRel(("DnD: Too many invalid/skipped messages from host, exiting ...\n"));
2294 break;
2295 }
2296 }
2297
2298 } while (!ASMAtomicReadBool(&pThis->m_fSrvStopping));
2299
2300 VbglR3DnDDisconnect(uClientID);
2301
2302 LogFlowFuncLeaveRC(rc);
2303 return rc;
2304}
2305
2306/* static */
2307int DragAndDropService::x11EventThread(RTTHREAD hThread, void *pvUser)
2308{
2309 AssertPtrReturn(pvUser, VERR_INVALID_PARAMETER);
2310 DragAndDropService *pThis = static_cast<DragAndDropService*>(pvUser);
2311 AssertPtr(pThis);
2312
2313 int rc = VINF_SUCCESS;
2314
2315 DnDEvent e;
2316 do
2317 {
2318 /*
2319 * Wait for new events. We can't use XIfEvent here, cause this locks
2320 * the window connection with a mutex and if no X11 events occurs this
2321 * blocks any other calls we made to X11. So instead check for new
2322 * events and if there are not any new one, sleep for a certain amount
2323 * of time.
2324 */
2325 if (XEventsQueued(pThis->m_pDisplay, QueuedAfterFlush) > 0)
2326 {
2327 RT_ZERO(e);
2328 e.type = DnDEvent::X11_Type;
2329
2330 /* XNextEvent will block until a new X event becomes available. */
2331 XNextEvent(pThis->m_pDisplay, &e.x11);
2332 {
2333#ifdef DEBUG
2334 switch (e.x11.type)
2335 {
2336 case ClientMessage:
2337 {
2338 XClientMessageEvent *pEvent = reinterpret_cast<XClientMessageEvent*>(&e);
2339 AssertPtr(pEvent);
2340
2341 RTCString strType = xAtomToString(pEvent->message_type);
2342 LogFlowFunc(("ClientMessage: %s (%RU32), serial=%RU32, wnd=%#x\n", strType.c_str(),
2343 pEvent->message_type, pEvent->serial, pEvent->window));
2344
2345 if (pEvent->message_type == xAtom(XA_XdndPosition))
2346 {
2347 int32_t dwPos = pEvent->data.l[2];
2348 int32_t dwAction = pEvent->data.l[4];
2349
2350 LogFlowFunc(("XA_XdndPosition x=%RI32, y=%RI32, dwAction=%RI32\n",
2351 RT_HIWORD(dwPos), RT_LOWORD(dwPos), dwAction));
2352 }
2353 else if (pEvent->message_type == xAtom(XA_XdndDrop))
2354 {
2355 LogFlowFunc(("XA_XdndDrop\n"));
2356 }
2357
2358 break;
2359 }
2360
2361 default:
2362 LogFlowFunc(("Received X event type=%d\n", e.x11.type));
2363 break;
2364 }
2365#endif
2366 LogFlowFunc(("Adding new X11 event ...\n"));
2367
2368 /* At the moment we only have one drag instance. */
2369 DragInstance *pInstance = pThis->m_pCurDnD;
2370
2371 AssertPtr(pInstance);
2372 pInstance->onX11Event(e.x11);
2373 }
2374 }
2375 else
2376 RTThreadSleep(25 /* ms */);
2377
2378 } while (!ASMAtomicReadBool(&pThis->m_fSrvStopping));
2379
2380 LogFlowFuncLeaveRC(rc);
2381 return rc;
2382}
2383
2384/** Drag and drop magic number, start of a UUID. */
2385#define DRAGANDDROPSERVICE_MAGIC 0x67c97173
2386
2387/** VBoxClient service class wrapping the logic for the service while
2388 * the main VBoxClient code provides the daemon logic needed by all services.
2389 */
2390struct DRAGANDDROPSERVICE
2391{
2392 /** The service interface. */
2393 struct VBCLSERVICE *pInterface;
2394 /** Magic number for sanity checks. */
2395 uint32_t magic;
2396 /** Service object. */
2397 DragAndDropService mDragAndDrop;
2398};
2399
2400static const char *getPidFilePath()
2401{
2402 return ".vboxclient-draganddrop.pid";
2403}
2404
2405static int run(struct VBCLSERVICE **ppInterface, bool fDaemonised)
2406{
2407 struct DRAGANDDROPSERVICE *pSelf = (struct DRAGANDDROPSERVICE *)ppInterface;
2408
2409 if (pSelf->magic != DRAGANDDROPSERVICE_MAGIC)
2410 VBClFatalError(("Bad display service object!\n"));
2411 return pSelf->mDragAndDrop.run(fDaemonised);
2412}
2413
2414static void cleanup(struct VBCLSERVICE **ppInterface)
2415{
2416 NOREF(ppInterface);
2417 VbglR3Term();
2418}
2419
2420struct VBCLSERVICE vbclDragAndDropInterface =
2421{
2422 getPidFilePath,
2423 VBClServiceDefaultHandler, /* init */
2424 run,
2425 VBClServiceDefaultHandler, /* pause */
2426 VBClServiceDefaultHandler, /* resume */
2427 cleanup
2428};
2429
2430/* Static factory */
2431struct VBCLSERVICE **VBClGetDragAndDropService(void)
2432{
2433 struct DRAGANDDROPSERVICE *pService =
2434 (struct DRAGANDDROPSERVICE *)RTMemAlloc(sizeof(*pService));
2435
2436 if (!pService)
2437 VBClFatalError(("Out of memory\n"));
2438 pService->pInterface = &vbclDragAndDropInterface;
2439 pService->magic = DRAGANDDROPSERVICE_MAGIC;
2440 new(&pService->mDragAndDrop) DragAndDropService();
2441 return &pService->pInterface;
2442}
2443
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