1 | /** @file
|
---|
2 | * X11 guest client - Drag and Drop.
|
---|
3 | */
|
---|
4 |
|
---|
5 | /*
|
---|
6 | * Copyright (C) 2011-2013 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 | //#include <X11/extensions/XTest.h>
|
---|
20 |
|
---|
21 | #include <iprt/thread.h>
|
---|
22 | #include <iprt/asm.h>
|
---|
23 | #include <iprt/time.h>
|
---|
24 |
|
---|
25 | #include <iprt/cpp/mtlist.h>
|
---|
26 | #include <iprt/cpp/ministring.h>
|
---|
27 |
|
---|
28 | #include <limits.h>
|
---|
29 |
|
---|
30 | #include <VBox/log.h>
|
---|
31 | #include <VBox/VBoxGuestLib.h>
|
---|
32 |
|
---|
33 | #include "VBox/HostServices/DragAndDropSvc.h"
|
---|
34 |
|
---|
35 | #include "VBoxClient.h"
|
---|
36 |
|
---|
37 | /* For X11 guest xDnD is used. See http://www.acc.umu.se/~vatten/XDND.html for
|
---|
38 | * a walk trough.
|
---|
39 | *
|
---|
40 | * H->G:
|
---|
41 | * For X11 this means mainly forwarding all the events from HGCM to the
|
---|
42 | * appropriate X11 events. There exists a proxy window, which is invisible and
|
---|
43 | * used for all the X11 communication. On a HGCM Enter event, we set our proxy
|
---|
44 | * window as XdndSelection owner with the given mime-types. On every HGCM move
|
---|
45 | * event, we move the X11 mouse cursor to the new position and query for the
|
---|
46 | * window below that position. Depending on if it is XdndAware, a new window or
|
---|
47 | * a known window, we send the appropriate X11 messages to it. On HGCM drop, we
|
---|
48 | * send a XdndDrop message to the current window and wait for a X11
|
---|
49 | * SelectionMessage from the target window. Because we didn't have the data in
|
---|
50 | * the requested mime-type, yet, we save that message and ask the host for the
|
---|
51 | * data. When the data is successfully received from the host, we put the data
|
---|
52 | * as a property to the window and send a X11 SelectionNotify event to the
|
---|
53 | * target window.
|
---|
54 | *
|
---|
55 | * G->H:
|
---|
56 | * This is a lot more trickery than H->G. When a pending event from HGCM
|
---|
57 | * arrives, we ask if there currently is an owner of the XdndSelection
|
---|
58 | * property. If so, our proxy window is shown (1x1, but without backing store)
|
---|
59 | * and some mouse event is triggered. This should be followed by an XdndEnter
|
---|
60 | * event send to the proxy window. From this event we can fetch the necessary
|
---|
61 | * info of the mime-types and allowed actions and send this back to the host.
|
---|
62 | * On a drop request from the host, we query for the selection and should get
|
---|
63 | * the data in the specified mime-type. This data is send back to the host.
|
---|
64 | * After that we send a XdndLeave event to the source window.
|
---|
65 | * Todo:
|
---|
66 | * - this isn't finished, yet. Currently the mouse isn't correctly released
|
---|
67 | * in the guest (both, when the drop was successfully or canceled).
|
---|
68 | * - cancel (e.g. with the ESC key) doesn't work
|
---|
69 | *
|
---|
70 | * Todo:
|
---|
71 | * - XdndProxy window support
|
---|
72 | * - INCR support
|
---|
73 | * - make this much more robust for crashes of the other party
|
---|
74 | * - really check for the Xdnd version and the supported features
|
---|
75 | */
|
---|
76 |
|
---|
77 | #define VBOX_XDND_VERSION (4)
|
---|
78 | #define VBOX_MAX_XPROPERTIES (LONG_MAX-1)
|
---|
79 |
|
---|
80 | /* Shared struct used for adding new X11 events and HGCM messages to a single
|
---|
81 | * event queue. */
|
---|
82 | struct DnDEvent
|
---|
83 | {
|
---|
84 | enum DnDEventType
|
---|
85 | {
|
---|
86 | HGCM_Type = 1,
|
---|
87 | X11_Type
|
---|
88 | };
|
---|
89 | DnDEventType type;
|
---|
90 | union
|
---|
91 | {
|
---|
92 | VBGLR3DNDHGCMEVENT hgcm;
|
---|
93 | XEvent x11;
|
---|
94 | };
|
---|
95 | };
|
---|
96 |
|
---|
97 | enum XA_Type
|
---|
98 | {
|
---|
99 | /* States */
|
---|
100 | XA_WM_STATE = 0,
|
---|
101 | /* Properties */
|
---|
102 | XA_TARGETS,
|
---|
103 | XA_MULTIPLE,
|
---|
104 | XA_INCR,
|
---|
105 | /* Mime Types */
|
---|
106 | XA_image_bmp,
|
---|
107 | XA_image_jpg,
|
---|
108 | XA_image_tiff,
|
---|
109 | XA_image_png,
|
---|
110 | XA_text_uri_list,
|
---|
111 | XA_text_uri,
|
---|
112 | XA_text_plain,
|
---|
113 | XA_TEXT,
|
---|
114 | /* xDnD */
|
---|
115 | XA_XdndSelection,
|
---|
116 | XA_XdndAware,
|
---|
117 | XA_XdndEnter,
|
---|
118 | XA_XdndLeave,
|
---|
119 | XA_XdndTypeList,
|
---|
120 | XA_XdndActionList,
|
---|
121 | XA_XdndPosition,
|
---|
122 | XA_XdndActionCopy,
|
---|
123 | XA_XdndActionMove,
|
---|
124 | XA_XdndActionLink,
|
---|
125 | XA_XdndStatus,
|
---|
126 | XA_XdndDrop,
|
---|
127 | XA_XdndFinished,
|
---|
128 | /* Our own stop marker */
|
---|
129 | XA_dndstop,
|
---|
130 | /* End marker */
|
---|
131 | XA_End
|
---|
132 | };
|
---|
133 |
|
---|
134 | class DragAndDropService;
|
---|
135 |
|
---|
136 | /*******************************************************************************
|
---|
137 | *
|
---|
138 | * xHelpers Declaration
|
---|
139 | *
|
---|
140 | ******************************************************************************/
|
---|
141 |
|
---|
142 | class xHelpers
|
---|
143 | {
|
---|
144 | public:
|
---|
145 |
|
---|
146 | static xHelpers *getInstance(Display *pDisplay = 0)
|
---|
147 | {
|
---|
148 | if (!m_pInstance)
|
---|
149 | {
|
---|
150 | AssertPtrReturn(pDisplay, NULL);
|
---|
151 | m_pInstance = new xHelpers(pDisplay);
|
---|
152 | }
|
---|
153 |
|
---|
154 | return m_pInstance;
|
---|
155 | }
|
---|
156 |
|
---|
157 | inline Display *display() const { return m_pDisplay; }
|
---|
158 | inline Atom xAtom(XA_Type e) const { return m_xAtoms[e]; }
|
---|
159 |
|
---|
160 | inline Atom stringToxAtom(const char *pcszString) const
|
---|
161 | {
|
---|
162 | return XInternAtom(m_pDisplay, pcszString, False);
|
---|
163 | }
|
---|
164 | inline RTCString xAtomToString(Atom atom) const
|
---|
165 | {
|
---|
166 | if (atom == None) return "None";
|
---|
167 |
|
---|
168 | char* pcsAtom = XGetAtomName(m_pDisplay, atom);
|
---|
169 | RTCString strAtom(pcsAtom);
|
---|
170 | XFree(pcsAtom);
|
---|
171 |
|
---|
172 | return strAtom;
|
---|
173 | }
|
---|
174 |
|
---|
175 | inline RTCString xAtomListToString(const RTCList<Atom> &formatList)
|
---|
176 | {
|
---|
177 | RTCString format;
|
---|
178 | for (size_t i = 0; i < formatList.size(); ++i)
|
---|
179 | format += xAtomToString(formatList.at(i)) + "\r\n";
|
---|
180 | return format;
|
---|
181 | }
|
---|
182 |
|
---|
183 | RTCString xErrorToString(int xrc) const;
|
---|
184 | Window applicationWindowBelowCursor(Window parentWin) const;
|
---|
185 |
|
---|
186 | private:
|
---|
187 | xHelpers(Display *pDisplay)
|
---|
188 | : m_pDisplay(pDisplay)
|
---|
189 | {
|
---|
190 | /* Not all x11 atoms we use are defined in the headers. Create the
|
---|
191 | * additional one we need here. */
|
---|
192 | for (int i = 0; i < XA_End; ++i)
|
---|
193 | m_xAtoms[i] = XInternAtom(m_pDisplay, m_xAtomNames[i], False);
|
---|
194 | };
|
---|
195 |
|
---|
196 | /* Private member vars */
|
---|
197 | static xHelpers *m_pInstance;
|
---|
198 | Display *m_pDisplay;
|
---|
199 | Atom m_xAtoms[XA_End];
|
---|
200 | static const char *m_xAtomNames[XA_End];
|
---|
201 | };
|
---|
202 |
|
---|
203 | /* Some xHelpers convenience defines. */
|
---|
204 | #define gX11 xHelpers::getInstance()
|
---|
205 | #define xAtom(xa) gX11->xAtom((xa))
|
---|
206 | #define xAtomToString(xa) gX11->xAtomToString((xa))
|
---|
207 |
|
---|
208 | /*******************************************************************************
|
---|
209 | *
|
---|
210 | * xHelpers Implementation
|
---|
211 | *
|
---|
212 | ******************************************************************************/
|
---|
213 |
|
---|
214 | xHelpers *xHelpers::m_pInstance = 0;
|
---|
215 | /* Has to be in sync with the XA_Type enum. */
|
---|
216 | const char *xHelpers::m_xAtomNames[] =
|
---|
217 | {
|
---|
218 | /* States */
|
---|
219 | "WM_STATE",
|
---|
220 | /* Properties */
|
---|
221 | "TARGETS",
|
---|
222 | "MULTIPLE",
|
---|
223 | "INCR",
|
---|
224 | /* Mime Types */
|
---|
225 | "image/bmp",
|
---|
226 | "image/jpg",
|
---|
227 | "image/tiff",
|
---|
228 | "image/png",
|
---|
229 | "text/uri-list",
|
---|
230 | "text/uri",
|
---|
231 | "text/plain",
|
---|
232 | "TEXT",
|
---|
233 | /* xDnD */
|
---|
234 | "XdndSelection",
|
---|
235 | "XdndAware",
|
---|
236 | "XdndEnter",
|
---|
237 | "XdndLeave",
|
---|
238 | "XdndTypeList",
|
---|
239 | "XdndActionList",
|
---|
240 | "XdndPosition",
|
---|
241 | "XdndActionCopy",
|
---|
242 | "XdndActionMove",
|
---|
243 | "XdndActionLink",
|
---|
244 | "XdndStatus",
|
---|
245 | "XdndDrop",
|
---|
246 | "XdndFinished",
|
---|
247 | /* Our own stop marker */
|
---|
248 | "dndstop"
|
---|
249 | };
|
---|
250 |
|
---|
251 | RTCString xHelpers::xErrorToString(int xrc) const
|
---|
252 | {
|
---|
253 | switch (xrc)
|
---|
254 | {
|
---|
255 | case Success: return RTCStringFmt("%d (Success)", xrc); break;
|
---|
256 | case BadRequest: return RTCStringFmt("%d (BadRequest)", xrc); break;
|
---|
257 | case BadValue: return RTCStringFmt("%d (BadValue)", xrc); break;
|
---|
258 | case BadWindow: return RTCStringFmt("%d (BadWindow)", xrc); break;
|
---|
259 | case BadPixmap: return RTCStringFmt("%d (BadPixmap)", xrc); break;
|
---|
260 | case BadAtom: return RTCStringFmt("%d (BadAtom)", xrc); break;
|
---|
261 | case BadCursor: return RTCStringFmt("%d (BadCursor)", xrc); break;
|
---|
262 | case BadFont: return RTCStringFmt("%d (BadFont)", xrc); break;
|
---|
263 | case BadMatch: return RTCStringFmt("%d (BadMatch)", xrc); break;
|
---|
264 | case BadDrawable: return RTCStringFmt("%d (BadDrawable)", xrc); break;
|
---|
265 | case BadAccess: return RTCStringFmt("%d (BadAccess)", xrc); break;
|
---|
266 | case BadAlloc: return RTCStringFmt("%d (BadAlloc)", xrc); break;
|
---|
267 | case BadColor: return RTCStringFmt("%d (BadColor)", xrc); break;
|
---|
268 | case BadGC: return RTCStringFmt("%d (BadGC)", xrc); break;
|
---|
269 | case BadIDChoice: return RTCStringFmt("%d (BadIDChoice)", xrc); break;
|
---|
270 | case BadName: return RTCStringFmt("%d (BadName)", xrc); break;
|
---|
271 | case BadLength: return RTCStringFmt("%d (BadLength)", xrc); break;
|
---|
272 | case BadImplementation: return RTCStringFmt("%d (BadImplementation)", xrc); break;
|
---|
273 | }
|
---|
274 | return RTCStringFmt("%d (unknown)", xrc);
|
---|
275 | }
|
---|
276 |
|
---|
277 | /* Todo: make this iterative */
|
---|
278 | Window xHelpers::applicationWindowBelowCursor(Window wndParent) const
|
---|
279 | {
|
---|
280 | /* No parent, nothing to do. */
|
---|
281 | if(wndParent == 0)
|
---|
282 | return 0;
|
---|
283 |
|
---|
284 | Window wndApp = 0;
|
---|
285 | int cProps = -1;
|
---|
286 | /* Fetch all x11 window properties of the parent window. */
|
---|
287 | Atom *pProps = XListProperties(m_pDisplay, wndParent, &cProps);
|
---|
288 | if (cProps > 0)
|
---|
289 | {
|
---|
290 | /* We check the window for the WM_STATE property. */
|
---|
291 | for (int i = 0; i < cProps; ++i)
|
---|
292 | if (pProps[i] == xAtom(XA_WM_STATE))
|
---|
293 | {
|
---|
294 | /* Found it. */
|
---|
295 | wndApp = wndParent;
|
---|
296 | break;
|
---|
297 | }
|
---|
298 | /* Cleanup */
|
---|
299 | XFree(pProps);
|
---|
300 | }
|
---|
301 |
|
---|
302 | if (!wndApp)
|
---|
303 | {
|
---|
304 | Window wndChild, wndTemp;
|
---|
305 | int tmp;
|
---|
306 | unsigned int utmp;
|
---|
307 | /* Query the next child window of the parent window at the current
|
---|
308 | * mouse position. */
|
---|
309 | XQueryPointer(m_pDisplay, wndParent, &wndTemp, &wndChild, &tmp, &tmp, &tmp, &tmp, &utmp);
|
---|
310 | /* Recursive call our self to dive into the child tree. */
|
---|
311 | wndApp = applicationWindowBelowCursor(wndChild);
|
---|
312 | }
|
---|
313 |
|
---|
314 | return wndApp;
|
---|
315 | }
|
---|
316 |
|
---|
317 | /*******************************************************************************
|
---|
318 | *
|
---|
319 | * DragInstance Declaration
|
---|
320 | *
|
---|
321 | ******************************************************************************/
|
---|
322 |
|
---|
323 | /* For now only one DragInstance will exits when the app is running. In the
|
---|
324 | * future the support for having more than one D&D operation supported at the
|
---|
325 | * time will be necessary. */
|
---|
326 | class DragInstance
|
---|
327 | {
|
---|
328 | public:
|
---|
329 | enum State
|
---|
330 | {
|
---|
331 | Uninitialized,
|
---|
332 | Initialized,
|
---|
333 | Dragging,
|
---|
334 | Dropped
|
---|
335 | };
|
---|
336 |
|
---|
337 | enum Mode
|
---|
338 | {
|
---|
339 | Unknown,
|
---|
340 | HG,
|
---|
341 | GH
|
---|
342 | };
|
---|
343 |
|
---|
344 | DragInstance(Display *pDisplay, DragAndDropService *pParent);
|
---|
345 | int init(uint32_t u32ScreenId);
|
---|
346 | void uninit();
|
---|
347 | void reset();
|
---|
348 |
|
---|
349 | /* H->G */
|
---|
350 | int hgEnter(const RTCList<RTCString> &formats, uint32_t actions);
|
---|
351 | int hgMove(uint32_t u32xPos, uint32_t u32yPos, uint32_t action);
|
---|
352 | int hgX11ClientMessage(const XEvent& e);
|
---|
353 | int hgDrop();
|
---|
354 | int hgX11SelectionRequest(const XEvent& e);
|
---|
355 | int hgDataReceived(void *pvData, uint32_t cData);
|
---|
356 |
|
---|
357 | #ifdef VBOX_WITH_DRAG_AND_DROP_GH
|
---|
358 | /* G->H */
|
---|
359 | int ghIsDnDPending();
|
---|
360 | int ghDropped(const RTCString &strFormat, uint32_t action);
|
---|
361 | #endif
|
---|
362 |
|
---|
363 | /* X11 helpers */
|
---|
364 | int moveCursor(uint32_t u32xPos, uint32_t u32yPos);
|
---|
365 | void sendButtonEvent(Window w, int rx, int ry, int button, bool fPress) const;
|
---|
366 | void showProxyWin(int &rx, int &ry) const;
|
---|
367 | void hideProxyWin() const;
|
---|
368 | void registerForEvents(Window w) const;
|
---|
369 |
|
---|
370 | void setActionsWindowProperty(Window win, const RTCList<Atom> &actionList) const;
|
---|
371 | void clearActionsWindowProperty(Window win) const;
|
---|
372 | void setFormatsWindowProperty(Window win, Atom property) const;
|
---|
373 | void clearFormatsWindowProperty(Window win) const;
|
---|
374 |
|
---|
375 | RTCList<Atom> toAtomList(const RTCList<RTCString> &formatList) const;
|
---|
376 | RTCList<Atom> toAtomList(void *pvData, uint32_t cData) const;
|
---|
377 | static Atom toX11Action(uint32_t uAction);
|
---|
378 | static RTCList<Atom> toX11Actions(uint32_t uActions);
|
---|
379 | static uint32_t toHGCMAction(Atom atom);
|
---|
380 | static uint32_t toHGCMActions(const RTCList<Atom> &actionsList);
|
---|
381 |
|
---|
382 | /* Member vars */
|
---|
383 | uint32_t m_uClientID;
|
---|
384 | DragAndDropService *m_pParent;
|
---|
385 | Display *m_pDisplay;
|
---|
386 | int m_screenId;
|
---|
387 | Screen *m_pScreen;
|
---|
388 | Window m_wndRoot;
|
---|
389 | Window m_wndProxy;
|
---|
390 | Window m_wndCur;
|
---|
391 | long m_curVer;
|
---|
392 | RTCList<Atom> m_formats;
|
---|
393 | RTCList<Atom> m_actions;
|
---|
394 |
|
---|
395 | XEvent m_selEvent;
|
---|
396 |
|
---|
397 | Mode m_mode;
|
---|
398 | State m_state;
|
---|
399 |
|
---|
400 | static const RTCList<RTCString> m_sstrStringMimeTypes;
|
---|
401 | };
|
---|
402 |
|
---|
403 | /*******************************************************************************
|
---|
404 | *
|
---|
405 | * DragAndDropService Declaration
|
---|
406 | *
|
---|
407 | ******************************************************************************/
|
---|
408 |
|
---|
409 | class DragAndDropService : public VBoxClient::Service
|
---|
410 | {
|
---|
411 | public:
|
---|
412 | DragAndDropService(void)
|
---|
413 | : m_pDisplay(0)
|
---|
414 | , m_hHGCMThread(NIL_RTTHREAD)
|
---|
415 | , m_hX11Thread(NIL_RTTHREAD)
|
---|
416 | , m_hEventSem(NIL_RTSEMEVENT)
|
---|
417 | , m_pCurDnD(0)
|
---|
418 | , m_fSrvStopping(false)
|
---|
419 | {}
|
---|
420 |
|
---|
421 | virtual const char *getPidFilePath() { return ".vboxclient-draganddrop.pid"; }
|
---|
422 |
|
---|
423 | /** @todo Move this part in VbglR3 and just provide a callback for the platform-specific
|
---|
424 | notification stuff, since this is very similar to the VBoxTray code. */
|
---|
425 | virtual int run(bool fDaemonised = false);
|
---|
426 |
|
---|
427 | virtual void cleanup(void)
|
---|
428 | {
|
---|
429 | /* Nothing to do, everything should be cleaned up automatically when the
|
---|
430 | * user process/X11 client exits. */
|
---|
431 | };
|
---|
432 |
|
---|
433 | private:
|
---|
434 | int x11DragAndDropInit(void);
|
---|
435 | static int hgcmEventThread(RTTHREAD hThread, void *pvUser);
|
---|
436 | static int x11EventThread(RTTHREAD hThread, void *pvUser);
|
---|
437 |
|
---|
438 | bool waitForXMsg(XEvent &ecm, int type, uint32_t uiMaxMS = 100);
|
---|
439 | void clearEventQueue();
|
---|
440 | /* Usually XCheckMaskEvent could be used for queering selected x11 events.
|
---|
441 | * Unfortunately this doesn't work exactly with the events we need. So we
|
---|
442 | * use this predicate method below and XCheckIfEvent. */
|
---|
443 | static bool isDnDRespondEvent(Display * /* pDisplay */, XEvent *pEvent, char *pUser)
|
---|
444 | {
|
---|
445 | if (!pEvent)
|
---|
446 | return false;
|
---|
447 | if ( pEvent->type == SelectionClear
|
---|
448 | || pEvent->type == ClientMessage
|
---|
449 | || pEvent->type == MotionNotify
|
---|
450 | || pEvent->type == SelectionRequest)
|
---|
451 | // || ( pEvent->type == ClientMessage
|
---|
452 | // && reinterpret_cast<XClientMessageEvent*>(pEvent)->window == reinterpret_cast<Window>(pUser))
|
---|
453 | // || ( pEvent->type == SelectionRequest
|
---|
454 | // && reinterpret_cast<XSelectionRequestEvent*>(pEvent)->requestor == reinterpret_cast<Window>(pUser)))
|
---|
455 | return true;
|
---|
456 | return false;
|
---|
457 | }
|
---|
458 |
|
---|
459 | /* Private member vars */
|
---|
460 | Display *m_pDisplay;
|
---|
461 |
|
---|
462 | RTCMTList<DnDEvent> m_eventQueue;
|
---|
463 | RTTHREAD m_hHGCMThread;
|
---|
464 | RTTHREAD m_hX11Thread;
|
---|
465 | RTSEMEVENT m_hEventSem;
|
---|
466 | DragInstance *m_pCurDnD;
|
---|
467 | bool m_fSrvStopping;
|
---|
468 |
|
---|
469 | friend class DragInstance;
|
---|
470 | };
|
---|
471 |
|
---|
472 | /*******************************************************************************
|
---|
473 | *
|
---|
474 | * DragInstanc Implementation
|
---|
475 | *
|
---|
476 | ******************************************************************************/
|
---|
477 |
|
---|
478 | DragInstance::DragInstance(Display *pDisplay, DragAndDropService *pParent)
|
---|
479 | : m_uClientID(0)
|
---|
480 | , m_pParent(pParent)
|
---|
481 | , m_pDisplay(pDisplay)
|
---|
482 | , m_pScreen(0)
|
---|
483 | , m_wndRoot(0)
|
---|
484 | , m_wndProxy(0)
|
---|
485 | , m_wndCur(0)
|
---|
486 | , m_curVer(-1)
|
---|
487 | , m_mode(Unknown)
|
---|
488 | , m_state(Uninitialized)
|
---|
489 | {
|
---|
490 | uninit();
|
---|
491 | }
|
---|
492 |
|
---|
493 | void DragInstance::uninit(void)
|
---|
494 | {
|
---|
495 | reset();
|
---|
496 | if (m_wndProxy != 0)
|
---|
497 | XDestroyWindow(m_pDisplay, m_wndProxy);
|
---|
498 |
|
---|
499 | if (m_uClientID)
|
---|
500 | {
|
---|
501 | VbglR3DnDDisconnect(m_uClientID);
|
---|
502 | m_uClientID = 0;
|
---|
503 | }
|
---|
504 |
|
---|
505 | m_state = Uninitialized;
|
---|
506 | m_screenId = -1;
|
---|
507 | m_pScreen = 0;
|
---|
508 | m_wndRoot = 0;
|
---|
509 | m_wndProxy = 0;
|
---|
510 | }
|
---|
511 |
|
---|
512 | void DragInstance::reset(void)
|
---|
513 | {
|
---|
514 | /* Hide the proxy win. */
|
---|
515 | hideProxyWin();
|
---|
516 | /* If we are currently the Xdnd selection owner, clear that. */
|
---|
517 | Window w = XGetSelectionOwner(m_pDisplay, xAtom(XA_XdndSelection));
|
---|
518 | if (w == m_wndProxy)
|
---|
519 | XSetSelectionOwner(m_pDisplay, xAtom(XA_XdndSelection), None, CurrentTime);
|
---|
520 | /* Clear any other DnD specific data on the proxy win. */
|
---|
521 | clearFormatsWindowProperty(m_wndProxy);
|
---|
522 | clearActionsWindowProperty(m_wndProxy);
|
---|
523 | /* Reset the internal state. */
|
---|
524 | m_formats.clear();
|
---|
525 | m_wndCur = 0;
|
---|
526 | m_curVer = -1;
|
---|
527 | m_state = Initialized;
|
---|
528 | }
|
---|
529 |
|
---|
530 | const RTCList<RTCString> DragInstance::m_sstrStringMimeTypes = RTCList<RTCString>()
|
---|
531 | /* Uri's */
|
---|
532 | << "text/uri-list"
|
---|
533 | /* Text */
|
---|
534 | << "text/plain;charset=utf-8"
|
---|
535 | << "UTF8_STRING"
|
---|
536 | << "text/plain"
|
---|
537 | << "COMPOUND_TEXT"
|
---|
538 | << "TEXT"
|
---|
539 | << "STRING"
|
---|
540 | /* OpenOffice formates */
|
---|
541 | << "application/x-openoffice-embed-source-xml;windows_formatname=\"Star Embed Source (XML)\""
|
---|
542 | << "application/x-openoffice-drawing;windows_formatname=\"Drawing Format\"";
|
---|
543 |
|
---|
544 | int DragInstance::init(uint32_t u32ScreenId)
|
---|
545 | {
|
---|
546 | int rc;
|
---|
547 |
|
---|
548 | do
|
---|
549 | {
|
---|
550 | uninit();
|
---|
551 |
|
---|
552 | rc = VbglR3DnDConnect(&m_uClientID);
|
---|
553 | if (RT_FAILURE(rc))
|
---|
554 | break;
|
---|
555 |
|
---|
556 | /*
|
---|
557 | * Enough screens configured in the x11 server?
|
---|
558 | */
|
---|
559 | if ((int)u32ScreenId > ScreenCount(m_pDisplay))
|
---|
560 | {
|
---|
561 | rc = VERR_INVALID_PARAMETER;
|
---|
562 | break;
|
---|
563 | }
|
---|
564 | /* Get the screen number from the x11 server. */
|
---|
565 | // pDrag->screen = ScreenOfDisplay(m_pDisplay, u32ScreenId);
|
---|
566 | // if (!pDrag->screen)
|
---|
567 | // {
|
---|
568 | // rc = VERR_GENERAL_FAILURE;
|
---|
569 | // break;
|
---|
570 | // }
|
---|
571 | m_screenId = u32ScreenId;
|
---|
572 | /* Now query the corresponding root window of this screen. */
|
---|
573 | m_wndRoot = RootWindow(m_pDisplay, m_screenId);
|
---|
574 | if (!m_wndRoot)
|
---|
575 | {
|
---|
576 | rc = VERR_GENERAL_FAILURE;
|
---|
577 | break;
|
---|
578 | }
|
---|
579 |
|
---|
580 | /*
|
---|
581 | * Create an invisible window which will act as proxy for the DnD
|
---|
582 | * operation. This window will be used for both the GH and HG
|
---|
583 | * direction.
|
---|
584 | */
|
---|
585 | XSetWindowAttributes attr;
|
---|
586 | RT_ZERO(attr);
|
---|
587 | attr.do_not_propagate_mask = 0;
|
---|
588 | attr.override_redirect = True;
|
---|
589 | #if 0
|
---|
590 | attr.background_pixel = WhitePixel(m_pDisplay, m_screenId);
|
---|
591 | #endif
|
---|
592 | m_wndProxy = XCreateWindow(m_pDisplay, m_wndRoot, 0, 0, 1, 1, 0,
|
---|
593 | CopyFromParent, InputOnly, CopyFromParent,
|
---|
594 | CWOverrideRedirect | CWDontPropagate,
|
---|
595 | &attr);
|
---|
596 | #ifdef DEBUG_andy
|
---|
597 | m_wndProxy = XCreateSimpleWindow(m_pDisplay, m_wndRoot, 0, 0, 50, 50, 0,
|
---|
598 | WhitePixel(m_pDisplay, m_screenId),
|
---|
599 | WhitePixel(m_pDisplay, m_screenId));
|
---|
600 | #endif
|
---|
601 | if (!m_wndProxy)
|
---|
602 | {
|
---|
603 | rc = VERR_GENERAL_FAILURE;
|
---|
604 | break;
|
---|
605 | }
|
---|
606 |
|
---|
607 | /* Make the new window Xdnd aware. */
|
---|
608 | Atom ver = VBOX_XDND_VERSION;
|
---|
609 | XChangeProperty(m_pDisplay, m_wndProxy, xAtom(XA_XdndAware), XA_ATOM, 32, PropModeReplace,
|
---|
610 | reinterpret_cast<unsigned char*>(&ver), 1);
|
---|
611 | } while (0);
|
---|
612 |
|
---|
613 | if (RT_SUCCESS(rc))
|
---|
614 | m_state = Initialized;
|
---|
615 |
|
---|
616 | LogFlowFuncLeaveRC(rc);
|
---|
617 | return rc;
|
---|
618 | }
|
---|
619 |
|
---|
620 | /*
|
---|
621 | * Host -> Guest
|
---|
622 | */
|
---|
623 |
|
---|
624 | int DragInstance::hgEnter(const RTCList<RTCString> &formats, uint32_t uActions)
|
---|
625 | {
|
---|
626 | reset();
|
---|
627 |
|
---|
628 | #ifdef DEBUG
|
---|
629 | LogFlowThisFunc(("uActions=0x%x, lstFormats=%zu: ", uActions, formats.size()));
|
---|
630 | for (size_t i = 0; i < formats.size(); ++i)
|
---|
631 | LogFlow(("'%s' ", formats.at(i).c_str()));
|
---|
632 | LogFlow(("\n"));
|
---|
633 | #endif
|
---|
634 |
|
---|
635 | m_formats = toAtomList(formats);
|
---|
636 |
|
---|
637 | /* If we have more than 3 formats we have to use the type list extension. */
|
---|
638 | if (m_formats.size() > 3)
|
---|
639 | setFormatsWindowProperty(m_wndProxy, xAtom(XA_XdndTypeList));
|
---|
640 |
|
---|
641 | /* Announce the possible actions */
|
---|
642 | setActionsWindowProperty(m_wndProxy, toX11Actions(uActions));
|
---|
643 |
|
---|
644 | /* Set the DnD selection owner to our window. */
|
---|
645 | XSetSelectionOwner(m_pDisplay, xAtom(XA_XdndSelection), m_wndProxy, CurrentTime);
|
---|
646 |
|
---|
647 | m_mode = HG;
|
---|
648 | m_state = Dragging;
|
---|
649 |
|
---|
650 | return VINF_SUCCESS;
|
---|
651 | }
|
---|
652 |
|
---|
653 | int DragInstance::hgMove(uint32_t u32xPos, uint32_t u32yPos, uint32_t uAction)
|
---|
654 | {
|
---|
655 | LogFlowThisFunc(("u32xPos=%RU32, u32yPos=%RU32, uAction=%RU32\n",
|
---|
656 | u32xPos, u32yPos, uAction));
|
---|
657 |
|
---|
658 | if ( m_mode != HG
|
---|
659 | || m_state != Dragging)
|
---|
660 | return VERR_INVALID_STATE;
|
---|
661 |
|
---|
662 | int rc = VINF_SUCCESS;
|
---|
663 | int xrc = Success;
|
---|
664 |
|
---|
665 | /* Move the mouse cursor within the guest. */
|
---|
666 | moveCursor(u32xPos, u32yPos);
|
---|
667 |
|
---|
668 | long newVer = -1; /* This means the current window is _not_ XdndAware. */
|
---|
669 |
|
---|
670 | /* Search for the application window below the cursor. */
|
---|
671 | Window wndCursor = gX11->applicationWindowBelowCursor(m_wndRoot);
|
---|
672 | if (wndCursor != None)
|
---|
673 | {
|
---|
674 | /* Temp stuff for the XGetWindowProperty call. */
|
---|
675 | Atom atmp;
|
---|
676 | int fmt;
|
---|
677 | unsigned long cItems, cbRemaining;
|
---|
678 | unsigned char *pcData = NULL;
|
---|
679 |
|
---|
680 | /* Query the XdndAware property from the window. We are interested in
|
---|
681 | * the version and if it is XdndAware at all. */
|
---|
682 | xrc = XGetWindowProperty(m_pDisplay, wndCursor, xAtom(XA_XdndAware),
|
---|
683 | 0, 2, False, AnyPropertyType,
|
---|
684 | &atmp, &fmt, &cItems, &cbRemaining, &pcData);
|
---|
685 | if (RT_UNLIKELY(xrc != Success))
|
---|
686 | LogFlowThisFunc(("Error in getting the window property: %s\n", gX11->xErrorToString(xrc).c_str()));
|
---|
687 | else
|
---|
688 | {
|
---|
689 | if (RT_UNLIKELY(pcData == NULL || fmt != 32 || cItems != 1))
|
---|
690 | LogFlowThisFunc(("Wrong properties pcData=%#x, iFmt=%u, cItems=%u\n", pcData, fmt, cItems));
|
---|
691 | else
|
---|
692 | {
|
---|
693 | newVer = reinterpret_cast<long*>(pcData)[0];
|
---|
694 | LogFlowThisFunc(("wndCursor=%#x, XdndAware=%u\n", newVer));
|
---|
695 | }
|
---|
696 | XFree(pcData);
|
---|
697 | }
|
---|
698 | }
|
---|
699 |
|
---|
700 | /*
|
---|
701 | * Is the window under the cursor another one than our current one?
|
---|
702 | */
|
---|
703 | if (wndCursor != m_wndCur && m_curVer != -1)
|
---|
704 | {
|
---|
705 | LogFlowThisFunc(("Leaving window=%#x\n", m_wndCur));
|
---|
706 |
|
---|
707 | /* We left the current XdndAware window. Announce this to the window. */
|
---|
708 | XClientMessageEvent m;
|
---|
709 | RT_ZERO(m);
|
---|
710 | m.type = ClientMessage;
|
---|
711 | m.display = m_pDisplay;
|
---|
712 | m.window = m_wndCur;
|
---|
713 | m.message_type = xAtom(XA_XdndLeave);
|
---|
714 | m.format = 32;
|
---|
715 | m.data.l[0] = m_wndProxy;
|
---|
716 |
|
---|
717 | xrc = XSendEvent(m_pDisplay, m_wndCur, False, NoEventMask, reinterpret_cast<XEvent*>(&m));
|
---|
718 | if (RT_UNLIKELY(xrc == 0))
|
---|
719 | LogFlowThisFunc(("Error sending XA_XdndLeave to old window=%#x\n", m_wndCur));
|
---|
720 | }
|
---|
721 |
|
---|
722 | /*
|
---|
723 | * Do we have a new window which now is under the cursor?
|
---|
724 | */
|
---|
725 | if (wndCursor != m_wndCur && newVer != -1)
|
---|
726 | {
|
---|
727 | LogFlowThisFunc(("Entering window=%#x\n", wndCursor));
|
---|
728 |
|
---|
729 | /*
|
---|
730 | * We enter a new window. Announce the XdndEnter event to the new
|
---|
731 | * window. The first three mime types are attached to the event (the
|
---|
732 | * others could be requested by the XdndTypeList property from the
|
---|
733 | * window itself).
|
---|
734 | */
|
---|
735 | XClientMessageEvent m;
|
---|
736 | RT_ZERO(m);
|
---|
737 | m.type = ClientMessage;
|
---|
738 | m.display = m_pDisplay;
|
---|
739 | m.window = wndCursor;
|
---|
740 | m.message_type = xAtom(XA_XdndEnter);
|
---|
741 | m.format = 32;
|
---|
742 | m.data.l[0] = m_wndProxy;
|
---|
743 | m.data.l[1] = RT_MAKE_U32_FROM_U8(m_formats.size() > 3 ? 1 : 0, 0, 0, RT_MIN(VBOX_XDND_VERSION, newVer));
|
---|
744 | m.data.l[2] = m_formats.value(0, None);
|
---|
745 | m.data.l[3] = m_formats.value(1, None);
|
---|
746 | m.data.l[4] = m_formats.value(2, None);
|
---|
747 |
|
---|
748 | xrc = XSendEvent(m_pDisplay, wndCursor, False, NoEventMask, reinterpret_cast<XEvent*>(&m));
|
---|
749 | if (RT_UNLIKELY(xrc == 0))
|
---|
750 | LogFlowThisFunc(("Error sending XA_XdndEnter to new window=%#x\n", wndCursor));
|
---|
751 | }
|
---|
752 |
|
---|
753 | if (newVer != -1)
|
---|
754 | {
|
---|
755 | LogFlowThisFunc(("Moving window=%#x, xPos=%RU32, yPos=%RU32\n",
|
---|
756 | wndCursor, u32xPos, u32yPos));
|
---|
757 |
|
---|
758 | /*
|
---|
759 | * Send a XdndPosition event with the proposed action to the guest.
|
---|
760 | */
|
---|
761 | Atom pa = toX11Action(uAction);
|
---|
762 | LogFlowThisFunc(("strAction='%s' ", xAtomToString(pa).c_str()));
|
---|
763 |
|
---|
764 | XClientMessageEvent m;
|
---|
765 | RT_ZERO(m);
|
---|
766 | m.type = ClientMessage;
|
---|
767 | m.display = m_pDisplay;
|
---|
768 | m.window = wndCursor;
|
---|
769 | m.message_type = xAtom(XA_XdndPosition);
|
---|
770 | m.format = 32;
|
---|
771 | m.data.l[0] = m_wndProxy;
|
---|
772 | m.data.l[2] = RT_MAKE_U32(u32yPos, u32xPos);
|
---|
773 | m.data.l[3] = CurrentTime;
|
---|
774 | m.data.l[4] = pa;
|
---|
775 |
|
---|
776 | xrc = XSendEvent(m_pDisplay, wndCursor, False, NoEventMask, reinterpret_cast<XEvent*>(&m));
|
---|
777 | if (RT_UNLIKELY(xrc == 0))
|
---|
778 | LogFlowThisFunc(("Error sending XA_XdndPosition to window=%#x\n", wndCursor));
|
---|
779 | }
|
---|
780 |
|
---|
781 | if (wndCursor == None && newVer == -1)
|
---|
782 | {
|
---|
783 | /* No window to process, so send a ignore ack event to the host. */
|
---|
784 | rc = VbglR3DnDHGAcknowledgeOperation(m_uClientID, DND_IGNORE_ACTION);
|
---|
785 | }
|
---|
786 |
|
---|
787 | m_wndCur = wndCursor;
|
---|
788 | m_curVer = RT_MIN(VBOX_XDND_VERSION, newVer);
|
---|
789 |
|
---|
790 | LogFlowFuncLeaveRC(rc);
|
---|
791 | return rc;
|
---|
792 | }
|
---|
793 |
|
---|
794 | int DragInstance::hgX11ClientMessage(const XEvent& e)
|
---|
795 | {
|
---|
796 | if ( m_mode != HG)
|
---|
797 | // || m_state != Dragging)
|
---|
798 | return VERR_INVALID_STATE;
|
---|
799 |
|
---|
800 | /* Client messages are used to inform us about the status of a XdndAware
|
---|
801 | * window, in response of some events we send to them. */
|
---|
802 | int rc = VINF_SUCCESS;
|
---|
803 | if ( e.xclient.message_type == xAtom(XA_XdndStatus)
|
---|
804 | && m_wndCur == static_cast<Window>(e.xclient.data.l[0]))
|
---|
805 | {
|
---|
806 | /* The XdndStatus message tell us if the window will accept the DnD
|
---|
807 | * event and with which action. We immediately send this info down to
|
---|
808 | * the host as a response of a previous DnD message. */
|
---|
809 | LogFlowThisFunc(("XA_XdndStatus wnd=%#x, accept=%RTbool, action='%s'\n",
|
---|
810 | e.xclient.data.l[0],
|
---|
811 | ASMBitTest(&e.xclient.data.l[1], 0),
|
---|
812 | xAtomToString(e.xclient.data.l[4]).c_str()));
|
---|
813 |
|
---|
814 | uint32_t uAction = DND_IGNORE_ACTION;
|
---|
815 | /** @todo Compare this with the allowed actions. */
|
---|
816 | if (ASMBitTest(&e.xclient.data.l[1], 0))
|
---|
817 | uAction = toHGCMAction(static_cast<Atom>(e.xclient.data.l[4]));
|
---|
818 |
|
---|
819 | rc = VbglR3DnDHGAcknowledgeOperation(m_uClientID, uAction);
|
---|
820 | }
|
---|
821 | else if (e.xclient.message_type == xAtom(XA_XdndFinished))
|
---|
822 | {
|
---|
823 | /* This message is send on a un/successful DnD drop request. */
|
---|
824 | LogFlowThisFunc(("XA_XdndFinished: wnd=%#x, success=%RTbool, action='%s'\n",
|
---|
825 | e.xclient.data.l[0],
|
---|
826 | ASMBitTest(&e.xclient.data.l[1], 0),
|
---|
827 | xAtomToString(e.xclient.data.l[2]).c_str()));
|
---|
828 |
|
---|
829 | reset();
|
---|
830 | }
|
---|
831 | else
|
---|
832 | LogFlowThisFunc(("Unhandled: wnd=%#x, msg='%s'\n",
|
---|
833 | e.xclient.data.l[0], xAtomToString(e.xclient.message_type).c_str()));
|
---|
834 |
|
---|
835 | LogFlowFuncLeaveRC(rc);
|
---|
836 | return rc;
|
---|
837 | }
|
---|
838 |
|
---|
839 | int DragInstance::hgDrop(void)
|
---|
840 | {
|
---|
841 | LogFlowThisFunc(("wndCur=%#x, mMode=%RU32, mState=%RU32\n",
|
---|
842 | m_wndCur, m_mode, m_state));
|
---|
843 |
|
---|
844 | if ( m_mode != HG
|
---|
845 | || m_state != Dragging)
|
---|
846 | return VERR_INVALID_STATE;
|
---|
847 |
|
---|
848 | int rc = VINF_SUCCESS;
|
---|
849 |
|
---|
850 | /* Send a drop event to the current window and reset our DnD status. */
|
---|
851 | XClientMessageEvent m;
|
---|
852 | RT_ZERO(m);
|
---|
853 | m.type = ClientMessage;
|
---|
854 | m.display = m_pDisplay;
|
---|
855 | m.window = m_wndCur;
|
---|
856 | m.message_type = xAtom(XA_XdndDrop);
|
---|
857 | m.format = 32;
|
---|
858 | m.data.l[0] = m_wndProxy;
|
---|
859 | m.data.l[2] = CurrentTime;
|
---|
860 |
|
---|
861 | int xrc = XSendEvent(m_pDisplay, m_wndCur, False, NoEventMask, reinterpret_cast<XEvent*>(&m));
|
---|
862 | if (RT_UNLIKELY(xrc == 0))
|
---|
863 | LogFlowThisFunc(("Error sending XA_XdndDrop to window=%#x\n", m_wndCur));
|
---|
864 |
|
---|
865 | m_wndCur = None;
|
---|
866 | m_curVer = -1;
|
---|
867 |
|
---|
868 | m_state = Dropped;
|
---|
869 |
|
---|
870 | LogFlowFuncLeaveRC(rc);
|
---|
871 | return rc;
|
---|
872 | }
|
---|
873 |
|
---|
874 | int DragInstance::hgX11SelectionRequest(const XEvent& e)
|
---|
875 | {
|
---|
876 | AssertReturn(e.type == SelectionRequest, VERR_INVALID_PARAMETER);
|
---|
877 |
|
---|
878 | if ( m_mode != HG)
|
---|
879 | // || m_state != D)
|
---|
880 | return VERR_INVALID_STATE;
|
---|
881 |
|
---|
882 | LogFlowThisFunc(("owner=%#x, requestor=%#x, sel_atom='%s', tar_atom='%s', prop_atom='%s', time=%u\n",
|
---|
883 | e.xselectionrequest.owner,
|
---|
884 | e.xselectionrequest.requestor,
|
---|
885 | xAtomToString(e.xselectionrequest.selection).c_str(),
|
---|
886 | xAtomToString(e.xselectionrequest.target).c_str(),
|
---|
887 | xAtomToString(e.xselectionrequest.property).c_str(),
|
---|
888 | e.xselectionrequest.time));
|
---|
889 |
|
---|
890 | int rc = VINF_SUCCESS;
|
---|
891 |
|
---|
892 | /*
|
---|
893 | * A window is asking for some data. Normally here the data would be copied
|
---|
894 | * into the selection buffer and send to the requestor. Obviously we can't
|
---|
895 | * do that, cause we first need to ask the host for the data of the
|
---|
896 | * requested mime type. This is done and later answered with the correct
|
---|
897 | * data (s. dataReceived).
|
---|
898 | */
|
---|
899 |
|
---|
900 | /* Is the requestor asking for the possible mime types? */
|
---|
901 | if(e.xselectionrequest.target == xAtom(XA_TARGETS))
|
---|
902 | {
|
---|
903 | LogFlowThisFunc(("wnd=%#x asking for target list\n", e.xselectionrequest.requestor));
|
---|
904 |
|
---|
905 | /* If so, set the window property with the formats on the requestor
|
---|
906 | * window. */
|
---|
907 | setFormatsWindowProperty(e.xselectionrequest.requestor, e.xselectionrequest.property);
|
---|
908 |
|
---|
909 | XEvent s;
|
---|
910 | RT_ZERO(s);
|
---|
911 | s.xselection.type = SelectionNotify;
|
---|
912 | s.xselection.display = e.xselection.display;
|
---|
913 | s.xselection.time = e.xselectionrequest.time;
|
---|
914 | s.xselection.selection = e.xselectionrequest.selection;
|
---|
915 | s.xselection.requestor = e.xselectionrequest.requestor;
|
---|
916 | s.xselection.target = e.xselectionrequest.target;
|
---|
917 | s.xselection.property = e.xselectionrequest.property;
|
---|
918 |
|
---|
919 | int xrc = XSendEvent(e.xselection.display, e.xselectionrequest.requestor, False, 0, &s);
|
---|
920 | if (RT_UNLIKELY(xrc == 0))
|
---|
921 | LogFlowThisFunc(("Error sending SelectionNotify event to wnd=%#x\n", e.xselectionrequest.requestor));
|
---|
922 | }
|
---|
923 | /* Is the requestor asking for a specific mime type (we support)? */
|
---|
924 | else if(m_formats.contains(e.xselectionrequest.target))
|
---|
925 | {
|
---|
926 | LogFlowThisFunc(("wnd=%#x asking for data (format='%s')\n",
|
---|
927 | e.xselectionrequest.requestor, xAtomToString(e.xselectionrequest.target).c_str()));
|
---|
928 |
|
---|
929 | /* If so, we need to inform the host about this request. Save the
|
---|
930 | * selection request event for later use. */
|
---|
931 | if ( m_state != Dropped)
|
---|
932 | // || m_curWin != e.xselectionrequest.requestor)
|
---|
933 | {
|
---|
934 | LogFlowThisFunc(("Refusing ...\n"));
|
---|
935 |
|
---|
936 | XEvent s;
|
---|
937 | RT_ZERO(s);
|
---|
938 | s.xselection.type = SelectionNotify;
|
---|
939 | s.xselection.display = e.xselection.display;
|
---|
940 | s.xselection.time = e.xselectionrequest.time;
|
---|
941 | s.xselection.selection = e.xselectionrequest.selection;
|
---|
942 | s.xselection.requestor = e.xselectionrequest.requestor;
|
---|
943 | s.xselection.target = None;
|
---|
944 | s.xselection.property = e.xselectionrequest.property;
|
---|
945 |
|
---|
946 | int xrc = XSendEvent(e.xselection.display, e.xselectionrequest.requestor, False, 0, &s);
|
---|
947 | if (RT_UNLIKELY(xrc == 0))
|
---|
948 | LogFlowThisFunc(("Error sending SelectionNotify event to wnd=%#x\n", e.xselectionrequest.requestor));
|
---|
949 | }
|
---|
950 | else
|
---|
951 | {
|
---|
952 | LogFlowThisFunc(("Copying data from host ...\n"));
|
---|
953 |
|
---|
954 | memcpy(&m_selEvent, &e, sizeof(XEvent));
|
---|
955 | rc = VbglR3DnDHGRequestData(m_uClientID, xAtomToString(e.xselectionrequest.target).c_str());
|
---|
956 | }
|
---|
957 | }
|
---|
958 | /* Anything else. */
|
---|
959 | else
|
---|
960 | {
|
---|
961 | LogFlowThisFunc(("Refusing unknown command\n"));
|
---|
962 |
|
---|
963 | /* We don't understand this request message and therefore answer with an
|
---|
964 | * refusal messages. */
|
---|
965 | XEvent s;
|
---|
966 | RT_ZERO(s);
|
---|
967 | s.xselection.type = SelectionNotify;
|
---|
968 | s.xselection.display = e.xselection.display;
|
---|
969 | s.xselection.time = e.xselectionrequest.time;
|
---|
970 | s.xselection.selection = e.xselectionrequest.selection;
|
---|
971 | s.xselection.requestor = e.xselectionrequest.requestor;
|
---|
972 | s.xselection.target = None; /* default is refusing */
|
---|
973 | s.xselection.property = None; /* default is refusing */
|
---|
974 | int xrc = XSendEvent(e.xselection.display, e.xselectionrequest.requestor, False, 0, &s);
|
---|
975 | if (RT_UNLIKELY(xrc == 0))
|
---|
976 | LogFlowThisFunc(("Error sending SelectionNotify event to wnd=%#x\n", e.xselectionrequest.requestor));
|
---|
977 | }
|
---|
978 |
|
---|
979 | LogFlowFuncLeaveRC(rc);
|
---|
980 | return rc;
|
---|
981 | }
|
---|
982 |
|
---|
983 | int DragInstance::hgDataReceived(void *pvData, uint32_t cData)
|
---|
984 | {
|
---|
985 | if ( m_mode != HG
|
---|
986 | || m_state != Dropped)
|
---|
987 | return VERR_INVALID_STATE;
|
---|
988 |
|
---|
989 | if (RT_UNLIKELY( pvData == NULL
|
---|
990 | || cData == 0))
|
---|
991 | return VERR_INVALID_PARAMETER;
|
---|
992 |
|
---|
993 | if (RT_UNLIKELY(m_state != Dropped))
|
---|
994 | return VERR_INVALID_STATE;
|
---|
995 |
|
---|
996 | /* Make a copy of the data. The xserver will become the new owner. */
|
---|
997 | void *pvNewData = RTMemAlloc(cData);
|
---|
998 | if (RT_UNLIKELY(!pvNewData))
|
---|
999 | return VERR_NO_MEMORY;
|
---|
1000 | memcpy(pvNewData, pvData, cData);
|
---|
1001 |
|
---|
1002 | /*
|
---|
1003 | * The host send us the DnD data in the requested mime type. This allows us
|
---|
1004 | * to fill the XdndSelection property of the requestor window with the data
|
---|
1005 | * and afterwards inform him about the new status.
|
---|
1006 | */
|
---|
1007 | XEvent s;
|
---|
1008 | RT_ZERO(s);
|
---|
1009 | s.xselection.type = SelectionNotify;
|
---|
1010 | s.xselection.display = m_selEvent.xselection.display;
|
---|
1011 | // s.xselection.owner = m_selEvent.xselectionrequest.owner;
|
---|
1012 | s.xselection.time = m_selEvent.xselectionrequest.time;
|
---|
1013 | s.xselection.selection = m_selEvent.xselectionrequest.selection;
|
---|
1014 | s.xselection.requestor = m_selEvent.xselectionrequest.requestor;
|
---|
1015 | s.xselection.target = m_selEvent.xselectionrequest.target;
|
---|
1016 | s.xselection.property = m_selEvent.xselectionrequest.property;
|
---|
1017 |
|
---|
1018 | LogFlowThisFunc(("owner=%#x,requestor=%#x,sel_atom='%s',tar_atom='%s',prop_atom='%s',time=%u\n",
|
---|
1019 | m_selEvent.xselectionrequest.owner,
|
---|
1020 | s.xselection.requestor,
|
---|
1021 | xAtomToString(s.xselection.selection).c_str(),
|
---|
1022 | xAtomToString(s.xselection.target).c_str(),
|
---|
1023 | xAtomToString(s.xselection.property).c_str(),
|
---|
1024 | s.xselection.time));
|
---|
1025 |
|
---|
1026 | /* Fill up the property with the data. */
|
---|
1027 | XChangeProperty(s.xselection.display, s.xselection.requestor, s.xselection.property, s.xselection.target, 8, PropModeReplace,
|
---|
1028 | reinterpret_cast<const unsigned char*>(pvNewData), cData);
|
---|
1029 | int xrc = XSendEvent(s.xselection.display, s.xselection.requestor, True, 0, &s);
|
---|
1030 | if (RT_UNLIKELY(xrc == 0))
|
---|
1031 | LogFlowThisFunc(("Error sending SelectionNotify event to wnd=%#x\n", s.xselection.requestor));
|
---|
1032 |
|
---|
1033 | return VINF_SUCCESS;
|
---|
1034 | }
|
---|
1035 |
|
---|
1036 | #ifdef VBOX_WITH_DRAG_AND_DROP_GH
|
---|
1037 | int DragInstance::ghIsDnDPending(void)
|
---|
1038 | {
|
---|
1039 | int rc = VINF_SUCCESS;
|
---|
1040 | Window wndOwner = XGetSelectionOwner(m_pDisplay, xAtom(XA_XdndSelection));
|
---|
1041 | LogFlowThisFunc(("Checking pending wndOwner=%#x wndProxy=%#x\n", wndOwner, m_wndProxy));
|
---|
1042 |
|
---|
1043 | /* Is there someone own the Xdnd selection which aren't we. */
|
---|
1044 | if ( wndOwner
|
---|
1045 | && wndOwner != m_wndProxy)
|
---|
1046 | {
|
---|
1047 | /* Map the window on the current cursor position, which should provoke
|
---|
1048 | * an XdndEnter event. */
|
---|
1049 | int rx, ry;
|
---|
1050 | showProxyWin(rx, ry);
|
---|
1051 | XEvent e;
|
---|
1052 | if (m_pParent->waitForXMsg(e, ClientMessage))
|
---|
1053 | {
|
---|
1054 | int xrc = Success;
|
---|
1055 | XClientMessageEvent *clme = reinterpret_cast<XClientMessageEvent*>(&e);
|
---|
1056 | LogFlowThisFunc(("Next X event %s\n", gX11->xAtomToString(clme->message_type).c_str()));
|
---|
1057 | if (clme->message_type == xAtom(XA_XdndEnter))
|
---|
1058 | {
|
---|
1059 | Atom type = None;
|
---|
1060 | int f;
|
---|
1061 | unsigned long n, a;
|
---|
1062 | unsigned char *ret = 0;
|
---|
1063 | reset();
|
---|
1064 |
|
---|
1065 | m_formats.clear();
|
---|
1066 | m_actions.clear();
|
---|
1067 | m_wndCur = wndOwner;
|
---|
1068 | LogFlowThisFunc(("XA_XdndEnter\n"));
|
---|
1069 | /* Check if the mime types are in the msg itself or if we need
|
---|
1070 | * to fetch the XdndTypeList property from the window. */
|
---|
1071 | if (!ASMBitTest(&clme->data.l[1], 0))
|
---|
1072 | {
|
---|
1073 | for (int i = 2; i < 5; ++i)
|
---|
1074 | {
|
---|
1075 | LogFlowThisFunc(("Receive list msg: %s\n", gX11->xAtomToString(clme->data.l[i]).c_str()));
|
---|
1076 | m_formats.append(clme->data.l[i]);
|
---|
1077 | }
|
---|
1078 | }
|
---|
1079 | else
|
---|
1080 | {
|
---|
1081 | xrc = XGetWindowProperty(m_pDisplay, wndOwner,
|
---|
1082 | xAtom(XA_XdndTypeList),
|
---|
1083 | 0, VBOX_MAX_XPROPERTIES,
|
---|
1084 | False, XA_ATOM, &type, &f, &n, &a, &ret);
|
---|
1085 | if ( xrc == Success
|
---|
1086 | && n > 0
|
---|
1087 | && ret)
|
---|
1088 | {
|
---|
1089 | Atom *data = reinterpret_cast<Atom*>(ret);
|
---|
1090 | for (int i = 0; i < RT_MIN(VBOX_MAX_XPROPERTIES, n); ++i)
|
---|
1091 | {
|
---|
1092 | LogFlowThisFunc(("Receive list: %s\n", gX11->xAtomToString(data[i]).c_str()));
|
---|
1093 | m_formats.append(data[i]);
|
---|
1094 | }
|
---|
1095 | XFree(ret);
|
---|
1096 | }
|
---|
1097 | }
|
---|
1098 |
|
---|
1099 | /* Fetch the possible list of actions, if this property is set. */
|
---|
1100 | xrc = XGetWindowProperty(m_pDisplay, wndOwner,
|
---|
1101 | xAtom(XA_XdndActionList),
|
---|
1102 | 0, VBOX_MAX_XPROPERTIES,
|
---|
1103 | False, XA_ATOM, &type, &f, &n, &a, &ret);
|
---|
1104 | if ( xrc == Success
|
---|
1105 | && n > 0
|
---|
1106 | && ret)
|
---|
1107 | {
|
---|
1108 | Atom *data = reinterpret_cast<Atom*>(ret);
|
---|
1109 | for (int i = 0; i < RT_MIN(VBOX_MAX_XPROPERTIES, n); ++i)
|
---|
1110 | {
|
---|
1111 | LogFlowThisFunc(("Receive actions: %s\n", gX11->xAtomToString(data[i]).c_str()));
|
---|
1112 | m_actions.append(data[i]);
|
---|
1113 | }
|
---|
1114 |
|
---|
1115 | XFree(ret);
|
---|
1116 | }
|
---|
1117 |
|
---|
1118 | m_state = Dragging;
|
---|
1119 | m_mode = GH;
|
---|
1120 |
|
---|
1121 | /* Acknowledge the event by sending a Status msg back to the
|
---|
1122 | * window. */
|
---|
1123 | XClientMessageEvent m;
|
---|
1124 | RT_ZERO(m);
|
---|
1125 | m.type = ClientMessage;
|
---|
1126 | m.display = m_pDisplay;
|
---|
1127 | m.window = clme->data.l[0];
|
---|
1128 | m.message_type = xAtom(XA_XdndStatus);
|
---|
1129 | m.format = 32;
|
---|
1130 | m.data.l[0] = m_wndProxy;
|
---|
1131 | m.data.l[1] = 1;
|
---|
1132 | m.data.l[4] = xAtom(XA_XdndActionCopy);
|
---|
1133 | xrc = XSendEvent(m_pDisplay, clme->data.l[0], False, 0, reinterpret_cast<XEvent*>(&m));
|
---|
1134 | if (RT_UNLIKELY(xrc == 0))
|
---|
1135 | LogFlowThisFunc(("Error sending xevent\n"));
|
---|
1136 | }
|
---|
1137 | else if (clme->message_type == xAtom(XA_XdndPosition))
|
---|
1138 | {
|
---|
1139 | LogFlowThisFunc(("XA_XdndPosition\n"));
|
---|
1140 | XClientMessageEvent m;
|
---|
1141 | RT_ZERO(m);
|
---|
1142 | m.type = ClientMessage;
|
---|
1143 | m.display = m_pDisplay;
|
---|
1144 | m.window = clme->data.l[0];
|
---|
1145 | m.message_type = xAtom(XA_XdndStatus);
|
---|
1146 | m.format = 32;
|
---|
1147 | m.data.l[0] = m_wndProxy;
|
---|
1148 | m.data.l[1] = 1;
|
---|
1149 | m.data.l[4] = clme->data.l[4];
|
---|
1150 | xrc = XSendEvent(m_pDisplay, clme->data.l[0], False, 0, reinterpret_cast<XEvent*>(&m));
|
---|
1151 | if (RT_UNLIKELY(xrc == 0))
|
---|
1152 | LogFlowThisFunc(("Error sending xevent\n"));
|
---|
1153 | }
|
---|
1154 | else if (clme->message_type == xAtom(XA_XdndLeave))
|
---|
1155 | {
|
---|
1156 | }
|
---|
1157 | }
|
---|
1158 |
|
---|
1159 | hideProxyWin();
|
---|
1160 |
|
---|
1161 | rc = VbglR3DnDGHAcknowledgePending(m_uClientID, DND_COPY_ACTION, toHGCMActions(m_actions),
|
---|
1162 | gX11->xAtomListToString(m_formats).c_str());
|
---|
1163 | }
|
---|
1164 |
|
---|
1165 | LogFlowFuncLeaveRC(rc);
|
---|
1166 | return rc;
|
---|
1167 | }
|
---|
1168 |
|
---|
1169 | int DragInstance::ghDropped(const RTCString &strFormat, uint32_t uAction)
|
---|
1170 | {
|
---|
1171 | LogFlowThisFunc(("strFormat=%s, uAction=%RU32\n",
|
---|
1172 | strFormat.c_str(), uAction));
|
---|
1173 |
|
---|
1174 | int rc = VINF_SUCCESS;
|
---|
1175 |
|
---|
1176 | /* Show the proxy window, so that the source will find it. */
|
---|
1177 | int rx, ry;
|
---|
1178 | showProxyWin(rx, ry);
|
---|
1179 | XFlush(m_pDisplay);
|
---|
1180 |
|
---|
1181 | /* We send a fake release event to the current window, cause
|
---|
1182 | * this should have the grab. */
|
---|
1183 | sendButtonEvent(m_wndCur, rx, ry, 1, false);
|
---|
1184 |
|
---|
1185 | /* The fake button release event, should lead to an XdndDrop event from the
|
---|
1186 | * source. Because of the showing of the proxy window, sometimes other Xdnd
|
---|
1187 | * events occurs before, like a XdndPosition event. We are not interested
|
---|
1188 | * in those, so try to get the right one. */
|
---|
1189 | XEvent e;
|
---|
1190 | XClientMessageEvent *clme = 0;
|
---|
1191 | RT_ZERO(e);
|
---|
1192 |
|
---|
1193 | int cRetries = 3;
|
---|
1194 | for (int i = 0; i < cRetries; i++)
|
---|
1195 | {
|
---|
1196 | if (m_pParent->waitForXMsg(e, ClientMessage))
|
---|
1197 | {
|
---|
1198 | if (reinterpret_cast<XClientMessageEvent*>(&e)->message_type == xAtom(XA_XdndDrop))
|
---|
1199 | {
|
---|
1200 | clme = reinterpret_cast<XClientMessageEvent*>(&e);
|
---|
1201 | break;
|
---|
1202 | }
|
---|
1203 | }
|
---|
1204 | }
|
---|
1205 |
|
---|
1206 | if (clme)
|
---|
1207 | {
|
---|
1208 | /* Make some paranoid checks. */
|
---|
1209 | if (clme->message_type == xAtom(XA_XdndDrop))
|
---|
1210 | {
|
---|
1211 | /* Request to convert the selection in the specific format and
|
---|
1212 | * place it to our proxy window as property. */
|
---|
1213 | Window srcWin = m_wndCur;//clme->data.l[0];
|
---|
1214 | Atom aFormat = gX11->stringToxAtom(strFormat.c_str());
|
---|
1215 |
|
---|
1216 | XConvertSelection(m_pDisplay, xAtom(XA_XdndSelection),
|
---|
1217 | aFormat, xAtom(XA_XdndSelection),
|
---|
1218 | m_wndProxy, clme->data.l[2]);
|
---|
1219 |
|
---|
1220 | /* Wait for the selection notify event. */
|
---|
1221 | RT_ZERO(e);
|
---|
1222 | if (m_pParent->waitForXMsg(e, SelectionNotify))
|
---|
1223 | {
|
---|
1224 | /* Make some paranoid checks. */
|
---|
1225 | if ( e.xselection.type == SelectionNotify
|
---|
1226 | && e.xselection.display == m_pDisplay
|
---|
1227 | && e.xselection.selection == xAtom(XA_XdndSelection)
|
---|
1228 | && e.xselection.requestor == m_wndProxy
|
---|
1229 | && e.xselection.target == aFormat)
|
---|
1230 | {
|
---|
1231 | LogFlowThisFunc(("Selection notfiy (from: %x)\n", m_wndCur));
|
---|
1232 |
|
---|
1233 | Atom type;
|
---|
1234 | int format;
|
---|
1235 | unsigned long cItems, cbRemaining;
|
---|
1236 | unsigned char *ucData = 0;
|
---|
1237 | XGetWindowProperty(m_pDisplay, m_wndProxy, xAtom(XA_XdndSelection),
|
---|
1238 | 0, VBOX_MAX_XPROPERTIES, True, AnyPropertyType,
|
---|
1239 | &type, &format, &cItems, &cbRemaining, &ucData);
|
---|
1240 | LogFlowThisFunc(("%s %d %d %s\n", gX11->xAtomToString(type).c_str(), cItems, format, ucData));
|
---|
1241 | if ( type != None
|
---|
1242 | && ucData != NULL
|
---|
1243 | && format >= 8
|
---|
1244 | && cItems > 0
|
---|
1245 | && cbRemaining == 0)
|
---|
1246 | {
|
---|
1247 | size_t cbData = cItems * (format / 8);
|
---|
1248 | /* For whatever reason some of the string mime-types are not
|
---|
1249 | * zero terminated. Check that and correct it when necessary,
|
---|
1250 | * cause the guest side wants this always. */
|
---|
1251 | if ( m_sstrStringMimeTypes.contains(strFormat)
|
---|
1252 | && ucData[cbData - 1] != '\0')
|
---|
1253 | {
|
---|
1254 | LogFlowThisFunc(("Rebuild %u\n", cbData));
|
---|
1255 | unsigned char *ucData1 = static_cast<unsigned char*>(RTMemAlloc(cbData + 1));
|
---|
1256 | if (ucData1)
|
---|
1257 | {
|
---|
1258 | memcpy(ucData1, ucData, cbData);
|
---|
1259 | ucData1[cbData++] = '\0';
|
---|
1260 |
|
---|
1261 | /* Got the data and its fully transfered. */
|
---|
1262 | rc = VbglR3DnDGHSendData(m_uClientID, strFormat.c_str(),
|
---|
1263 | ucData1, cbData);
|
---|
1264 | RTMemFree(ucData1);
|
---|
1265 | }
|
---|
1266 | else
|
---|
1267 | rc = VERR_NO_MEMORY;
|
---|
1268 | }
|
---|
1269 | else
|
---|
1270 | {
|
---|
1271 | /* Just send the data to the host. */
|
---|
1272 | rc = VbglR3DnDGHSendData(m_uClientID, strFormat.c_str(),
|
---|
1273 | ucData, cbData);
|
---|
1274 | }
|
---|
1275 |
|
---|
1276 | LogFlowThisFunc(("send responce\n"));
|
---|
1277 | /* Confirm the result of the transfer to the source window. */
|
---|
1278 | XClientMessageEvent m;
|
---|
1279 | RT_ZERO(m);
|
---|
1280 | m.type = ClientMessage;
|
---|
1281 | m.display = m_pDisplay;
|
---|
1282 | m.window = srcWin;
|
---|
1283 | m.message_type = xAtom(XA_XdndFinished);
|
---|
1284 | m.format = 32;
|
---|
1285 | m.data.l[0] = m_wndProxy;
|
---|
1286 | m.data.l[1] = RT_SUCCESS(rc) ? 1 : 0; /* Confirm or deny success */
|
---|
1287 | m.data.l[2] = RT_SUCCESS(rc) ? toX11Action(uAction) : None; /* Action used on success */
|
---|
1288 |
|
---|
1289 | int xrc = XSendEvent(m_pDisplay, srcWin, True, NoEventMask, reinterpret_cast<XEvent*>(&m));
|
---|
1290 | if (RT_UNLIKELY(xrc == 0))
|
---|
1291 | LogFlowThisFunc(("Error sending xevent\n"));
|
---|
1292 | }
|
---|
1293 | else
|
---|
1294 | {
|
---|
1295 | if (type == xAtom(XA_INCR))
|
---|
1296 | {
|
---|
1297 | /** @todo Support incremental transfers. */
|
---|
1298 | AssertMsgFailed(("Incrementally transfers are not supported, yet\n"));
|
---|
1299 | rc = VERR_NOT_IMPLEMENTED;
|
---|
1300 | }
|
---|
1301 | else
|
---|
1302 | {
|
---|
1303 | AssertMsgFailed(("Not supported data type\n"));
|
---|
1304 | rc = VERR_INVALID_PARAMETER;
|
---|
1305 | }
|
---|
1306 |
|
---|
1307 | /* Cancel this. */
|
---|
1308 | XClientMessageEvent m;
|
---|
1309 | RT_ZERO(m);
|
---|
1310 | m.type = ClientMessage;
|
---|
1311 | m.display = m_pDisplay;
|
---|
1312 | m.window = srcWin;
|
---|
1313 | m.message_type = xAtom(XA_XdndFinished);
|
---|
1314 | m.format = 32;
|
---|
1315 | m.data.l[0] = m_wndProxy;
|
---|
1316 | m.data.l[1] = 0;
|
---|
1317 | m.data.l[2] = None;
|
---|
1318 |
|
---|
1319 | int xrc = XSendEvent(m_pDisplay, srcWin, False, NoEventMask, reinterpret_cast<XEvent*>(&m));
|
---|
1320 | if (RT_UNLIKELY(xrc == 0))
|
---|
1321 | LogFlowThisFunc(("Error sending xevent\n"));
|
---|
1322 | m_wndCur = 0;
|
---|
1323 | }
|
---|
1324 |
|
---|
1325 | /* Cleanup */
|
---|
1326 | if (ucData)
|
---|
1327 | XFree(ucData);
|
---|
1328 | }
|
---|
1329 | else
|
---|
1330 | rc = VERR_INVALID_PARAMETER;
|
---|
1331 | }
|
---|
1332 | else
|
---|
1333 | rc = VERR_TIMEOUT;
|
---|
1334 | }
|
---|
1335 | else
|
---|
1336 | rc = VERR_WRONG_ORDER;
|
---|
1337 | }
|
---|
1338 | else
|
---|
1339 | rc = VERR_TIMEOUT;
|
---|
1340 |
|
---|
1341 | /* Inform the host on error. */
|
---|
1342 | if (RT_FAILURE(rc))
|
---|
1343 | VbglR3DnDGHSendError(m_uClientID, rc);
|
---|
1344 |
|
---|
1345 | /* At this point, we have either successfully transfered any data or not.
|
---|
1346 | * So reset our internal state, cause we are done. */
|
---|
1347 | reset();
|
---|
1348 |
|
---|
1349 | LogFlowFuncLeaveRC(rc);
|
---|
1350 | return rc;
|
---|
1351 | }
|
---|
1352 | #endif /* VBOX_WITH_DRAG_AND_DROP_GH */
|
---|
1353 |
|
---|
1354 | /*
|
---|
1355 | * Helpers
|
---|
1356 | */
|
---|
1357 |
|
---|
1358 | int DragInstance::moveCursor(uint32_t u32xPos, uint32_t u32yPos)
|
---|
1359 | {
|
---|
1360 | /* Move the guest pointer to the DnD position, so we can find the window
|
---|
1361 | * below that position. */
|
---|
1362 | XWarpPointer(m_pDisplay, None, m_wndRoot, 0, 0, 0, 0, u32xPos, u32yPos);
|
---|
1363 | return VINF_SUCCESS;
|
---|
1364 | }
|
---|
1365 |
|
---|
1366 | void DragInstance::sendButtonEvent(Window w, int rx, int ry, int button, bool fPress) const
|
---|
1367 | {
|
---|
1368 | // XTestFakeMotionEvent(m_pDisplay, -1, rx, ry, CurrentTime);
|
---|
1369 | // XTestFakeMotionEvent(m_pDisplay, -1, rx + 1, ry + 1, CurrentTime);
|
---|
1370 | // int rc = XTestFakeButtonEvent(m_pDisplay, 1, False, CurrentTime);
|
---|
1371 | // if (rc != 0)
|
---|
1372 | {
|
---|
1373 | XButtonEvent be;
|
---|
1374 | RT_ZERO(be);
|
---|
1375 | be.display = m_pDisplay;
|
---|
1376 | be.root = m_wndRoot;
|
---|
1377 | be.window = w;
|
---|
1378 | be.subwindow = None;
|
---|
1379 | be.same_screen = True;
|
---|
1380 | be.time = CurrentTime;
|
---|
1381 | be.button = button;
|
---|
1382 | be.state |= button == 1 ? Button1MotionMask :
|
---|
1383 | button == 2 ? Button2MotionMask :
|
---|
1384 | button == 3 ? Button3MotionMask :
|
---|
1385 | button == 4 ? Button4MotionMask :
|
---|
1386 | button == 5 ? Button5MotionMask : 0;
|
---|
1387 | be.type = fPress ? ButtonPress : ButtonRelease;
|
---|
1388 | be.x_root = rx;
|
---|
1389 | be.y_root = ry;
|
---|
1390 | XTranslateCoordinates(m_pDisplay, be.root, be.window, be.x_root, be.y_root, &be.x, &be.y, &be.subwindow);
|
---|
1391 | int xrc = XSendEvent(m_pDisplay, be.window, True, ButtonPressMask, reinterpret_cast<XEvent*>(&be));
|
---|
1392 | if (RT_UNLIKELY(xrc == 0))
|
---|
1393 | LogFlowThisFunc(("Error sending xevent\n"));
|
---|
1394 | }
|
---|
1395 | }
|
---|
1396 |
|
---|
1397 | void DragInstance::showProxyWin(int &rx, int &ry) const
|
---|
1398 | {
|
---|
1399 | int cx, cy;
|
---|
1400 | unsigned int m;
|
---|
1401 | Window r, c;
|
---|
1402 | // XTestGrabControl(m_pDisplay, False);
|
---|
1403 | XQueryPointer(m_pDisplay, m_wndRoot, &r, &c, &rx, &ry, &cx, &cy, &m);
|
---|
1404 | XSynchronize(m_pDisplay, True);
|
---|
1405 | XMapWindow(m_pDisplay, m_wndProxy);
|
---|
1406 | XRaiseWindow(m_pDisplay, m_wndProxy);
|
---|
1407 | XMoveResizeWindow(m_pDisplay, m_wndProxy, rx, ry, 1, 1);
|
---|
1408 | XWarpPointer(m_pDisplay, None, m_wndRoot, 0, 0, 0, 0, rx , ry);
|
---|
1409 | XSynchronize(m_pDisplay, False);
|
---|
1410 | // XTestGrabControl(m_pDisplay, True);
|
---|
1411 | }
|
---|
1412 |
|
---|
1413 | void DragInstance::hideProxyWin(void) const
|
---|
1414 | {
|
---|
1415 | XUnmapWindow(m_pDisplay, m_wndProxy);
|
---|
1416 | }
|
---|
1417 |
|
---|
1418 | /* Currently, not used */
|
---|
1419 | void DragInstance::registerForEvents(Window wndThis) const
|
---|
1420 | {
|
---|
1421 | // if (w == m_proxyWin)
|
---|
1422 | // return;
|
---|
1423 |
|
---|
1424 | LogFlowThisFunc(("%x\n", wndThis));
|
---|
1425 | // XSelectInput(m_pDisplay, w, Button1MotionMask | Button2MotionMask | Button3MotionMask | Button4MotionMask | Button5MotionMask);//| SubstructureNotifyMask);
|
---|
1426 | // XSelectInput(m_pDisplay, w, ButtonMotionMask); //PointerMotionMask);
|
---|
1427 | XSelectInput(m_pDisplay, wndThis, PointerMotionMask); //PointerMotionMask);
|
---|
1428 | Window hRealRoot, hParent;
|
---|
1429 | Window *phChildrenRaw = NULL;
|
---|
1430 | unsigned cChildren;
|
---|
1431 | if (XQueryTree(m_pDisplay, wndThis, &hRealRoot, &hParent, &phChildrenRaw, &cChildren))
|
---|
1432 | {
|
---|
1433 | for (unsigned i = 0; i < cChildren; ++i)
|
---|
1434 | registerForEvents(phChildrenRaw[i]);
|
---|
1435 | XFree(phChildrenRaw);
|
---|
1436 | }
|
---|
1437 | }
|
---|
1438 |
|
---|
1439 | void DragInstance::setActionsWindowProperty(Window wndThis, const RTCList<Atom> &actionList) const
|
---|
1440 | {
|
---|
1441 | if (actionList.isEmpty())
|
---|
1442 | return;
|
---|
1443 |
|
---|
1444 | XChangeProperty(m_pDisplay, wndThis, xAtom(XA_XdndActionList), XA_ATOM, 32, PropModeReplace,
|
---|
1445 | reinterpret_cast<const unsigned char*>(actionList.raw()), actionList.size());
|
---|
1446 | }
|
---|
1447 |
|
---|
1448 | void DragInstance::clearActionsWindowProperty(Window wndThis) const
|
---|
1449 | {
|
---|
1450 | XDeleteProperty(m_pDisplay, wndThis, xAtom(XA_XdndActionList));
|
---|
1451 | }
|
---|
1452 |
|
---|
1453 | void DragInstance::setFormatsWindowProperty(Window wndThis, Atom property) const
|
---|
1454 | {
|
---|
1455 | if (m_formats.isEmpty())
|
---|
1456 | return;
|
---|
1457 |
|
---|
1458 | /* We support TARGETS and the data types. */
|
---|
1459 | RTCList<Atom> targets(m_formats.size() + 1);
|
---|
1460 | targets.append(xAtom(XA_TARGETS));
|
---|
1461 | targets.append(m_formats);
|
---|
1462 |
|
---|
1463 | /* Add the property with the property data to the window. */
|
---|
1464 | XChangeProperty(m_pDisplay, wndThis, property, XA_ATOM, 32, PropModeReplace,
|
---|
1465 | reinterpret_cast<const unsigned char*>(targets.raw()), targets.size());
|
---|
1466 | }
|
---|
1467 |
|
---|
1468 | void DragInstance::clearFormatsWindowProperty(Window wndThis) const
|
---|
1469 | {
|
---|
1470 | XDeleteProperty(m_pDisplay, wndThis, xAtom(XA_XdndTypeList));
|
---|
1471 | }
|
---|
1472 |
|
---|
1473 | RTCList<Atom> DragInstance::toAtomList(const RTCList<RTCString> &formatList) const
|
---|
1474 | {
|
---|
1475 | RTCList<Atom> atomList;
|
---|
1476 | for (size_t i = 0; i < formatList.size(); ++i)
|
---|
1477 | atomList.append(XInternAtom(m_pDisplay, formatList.at(i).c_str(), False));
|
---|
1478 |
|
---|
1479 | return atomList;
|
---|
1480 | }
|
---|
1481 |
|
---|
1482 | RTCList<Atom> DragInstance::toAtomList(void *pvData, uint32_t cData) const
|
---|
1483 | {
|
---|
1484 | if ( !pvData
|
---|
1485 | || !cData)
|
---|
1486 | return RTCList<Atom>();
|
---|
1487 | char *pszStr = (char*)pvData;
|
---|
1488 | uint32_t cStr = cData;
|
---|
1489 |
|
---|
1490 | RTCList<Atom> atomList;
|
---|
1491 | while (cStr > 0)
|
---|
1492 | {
|
---|
1493 | size_t cSize = RTStrNLen(pszStr, cStr);
|
---|
1494 | /* Create a copy with max N chars, so that we are on the save side,
|
---|
1495 | * even if the data isn't zero terminated. */
|
---|
1496 | char *pszTmp = RTStrDupN(pszStr, cSize);
|
---|
1497 | LogFlowThisFunc(("f: %s\n", pszTmp));
|
---|
1498 | atomList.append(XInternAtom(m_pDisplay, pszTmp, False));
|
---|
1499 | RTStrFree(pszTmp);
|
---|
1500 | pszStr += cSize + 1;
|
---|
1501 | cStr -= cSize + 1;
|
---|
1502 | }
|
---|
1503 |
|
---|
1504 | return atomList;
|
---|
1505 | }
|
---|
1506 |
|
---|
1507 | /* static */
|
---|
1508 | Atom DragInstance::toX11Action(uint32_t uAction)
|
---|
1509 | {
|
---|
1510 | /* Ignore is None */
|
---|
1511 | return (isDnDCopyAction(uAction) ? xAtom(XA_XdndActionCopy) :
|
---|
1512 | isDnDMoveAction(uAction) ? xAtom(XA_XdndActionMove) :
|
---|
1513 | isDnDLinkAction(uAction) ? xAtom(XA_XdndActionLink) :
|
---|
1514 | None);
|
---|
1515 | }
|
---|
1516 |
|
---|
1517 | /* static */
|
---|
1518 | RTCList<Atom> DragInstance::toX11Actions(uint32_t uActions)
|
---|
1519 | {
|
---|
1520 | RTCList<Atom> actionList;
|
---|
1521 | if (hasDnDCopyAction(uActions))
|
---|
1522 | actionList.append(xAtom(XA_XdndActionCopy));
|
---|
1523 | if (hasDnDMoveAction(uActions))
|
---|
1524 | actionList.append(xAtom(XA_XdndActionMove));
|
---|
1525 | if (hasDnDLinkAction(uActions))
|
---|
1526 | actionList.append(xAtom(XA_XdndActionLink));
|
---|
1527 |
|
---|
1528 | return actionList;
|
---|
1529 | }
|
---|
1530 |
|
---|
1531 | /* static */
|
---|
1532 | uint32_t DragInstance::toHGCMAction(Atom atom)
|
---|
1533 | {
|
---|
1534 | uint32_t uAction = DND_IGNORE_ACTION;
|
---|
1535 | if (atom == xAtom(XA_XdndActionCopy))
|
---|
1536 | uAction = DND_COPY_ACTION;
|
---|
1537 | else if (atom == xAtom(XA_XdndActionMove))
|
---|
1538 | uAction = DND_MOVE_ACTION;
|
---|
1539 | else if (atom == xAtom(XA_XdndActionLink))
|
---|
1540 | uAction = DND_LINK_ACTION;
|
---|
1541 | return uAction;
|
---|
1542 | }
|
---|
1543 |
|
---|
1544 | /* static */
|
---|
1545 | uint32_t DragInstance::toHGCMActions(const RTCList<Atom> &actionsList)
|
---|
1546 | {
|
---|
1547 | uint32_t uActions = DND_IGNORE_ACTION;
|
---|
1548 | for (size_t i = 0; i < actionsList.size(); ++i)
|
---|
1549 | uActions |= toHGCMAction(actionsList.at(i));
|
---|
1550 | return uActions;
|
---|
1551 | }
|
---|
1552 |
|
---|
1553 | /*******************************************************************************
|
---|
1554 | *
|
---|
1555 | * DragAndDropService Implementation
|
---|
1556 | *
|
---|
1557 | ******************************************************************************/
|
---|
1558 |
|
---|
1559 | RTCList<RTCString> toStringList(void *pvData, uint32_t cData)
|
---|
1560 | {
|
---|
1561 | if ( !pvData
|
---|
1562 | || !cData)
|
---|
1563 | return RTCList<RTCString>();
|
---|
1564 | char *pszStr = (char*)pvData;
|
---|
1565 | uint32_t cStr = cData;
|
---|
1566 |
|
---|
1567 | RTCList<RTCString> strList;
|
---|
1568 | while (cStr > 0)
|
---|
1569 | {
|
---|
1570 | size_t cSize = RTStrNLen(pszStr, cStr);
|
---|
1571 | /* Create a copy with max N chars, so that we are on the save side,
|
---|
1572 | * even if the data isn't zero terminated. */
|
---|
1573 | char *pszTmp = RTStrDupN(pszStr, cSize);
|
---|
1574 | strList.append(pszTmp);
|
---|
1575 | RTStrFree(pszTmp);
|
---|
1576 | pszStr += cSize + 1;
|
---|
1577 | cStr -= cSize + 1;
|
---|
1578 | }
|
---|
1579 |
|
---|
1580 | return strList;
|
---|
1581 | }
|
---|
1582 |
|
---|
1583 | #ifdef VBOX_WITH_DRAG_AND_DROP_GH
|
---|
1584 |
|
---|
1585 | bool DragAndDropService::waitForXMsg(XEvent &ecm, int type, uint32_t uiMaxMS /* = 100 */)
|
---|
1586 | {
|
---|
1587 | const uint64_t uiStart = RTTimeProgramMilliTS();
|
---|
1588 | do
|
---|
1589 | {
|
---|
1590 | if (!m_eventQueue.isEmpty())
|
---|
1591 | {
|
---|
1592 | LogFlowThisFunc(("new msg size %d\n", m_eventQueue.size()));
|
---|
1593 | /* Check if there is a client message in the queue. */
|
---|
1594 | for (size_t i = 0; i < m_eventQueue.size(); ++i)
|
---|
1595 | {
|
---|
1596 | DnDEvent e = m_eventQueue.at(i);
|
---|
1597 | if( e.type == DnDEvent::X11_Type)
|
---|
1598 | LogFlowThisFunc(("new msg\n"));
|
---|
1599 | if( e.type == DnDEvent::X11_Type
|
---|
1600 | && e.x11.type == type)
|
---|
1601 | {
|
---|
1602 | m_eventQueue.removeAt(i);
|
---|
1603 | ecm = e.x11;
|
---|
1604 | return true;
|
---|
1605 | }
|
---|
1606 | }
|
---|
1607 | }
|
---|
1608 | int rc = RTSemEventWait(m_hEventSem, 25);
|
---|
1609 | // if (RT_FAILURE(rc))
|
---|
1610 | // return false;
|
---|
1611 | }
|
---|
1612 | while (RTTimeProgramMilliTS() - uiStart < uiMaxMS);
|
---|
1613 |
|
---|
1614 | return false;
|
---|
1615 | }
|
---|
1616 |
|
---|
1617 | #endif
|
---|
1618 |
|
---|
1619 | void DragAndDropService::clearEventQueue()
|
---|
1620 | {
|
---|
1621 | m_eventQueue.clear();
|
---|
1622 | }
|
---|
1623 |
|
---|
1624 | int DragAndDropService::run(bool fDaemonised /* = false */)
|
---|
1625 | {
|
---|
1626 | int rc = VINF_SUCCESS;
|
---|
1627 | LogRelFlowFunc(("\n"));
|
---|
1628 |
|
---|
1629 | do
|
---|
1630 | {
|
---|
1631 | /* Initialize X11 DND */
|
---|
1632 | rc = x11DragAndDropInit();
|
---|
1633 | if (RT_FAILURE(rc))
|
---|
1634 | break;
|
---|
1635 |
|
---|
1636 | m_pCurDnD = new DragInstance(m_pDisplay, this);
|
---|
1637 | if (!m_pCurDnD)
|
---|
1638 | {
|
---|
1639 | rc = VERR_NO_MEMORY;
|
---|
1640 | break;
|
---|
1641 | }
|
---|
1642 | /* Note: For multiple screen support in VBox it is not necessary to use
|
---|
1643 | * another screen number than zero. Maybe in the future it will become
|
---|
1644 | * necessary if VBox supports multiple X11 screens. */
|
---|
1645 | rc = m_pCurDnD->init(0);
|
---|
1646 | if (RT_FAILURE(rc))
|
---|
1647 | break;
|
---|
1648 |
|
---|
1649 | /* Loop over new events */
|
---|
1650 | do
|
---|
1651 | {
|
---|
1652 | DnDEvent e;
|
---|
1653 | RT_ZERO(e);
|
---|
1654 | if (m_eventQueue.isEmpty())
|
---|
1655 | rc = RTSemEventWait(m_hEventSem, RT_INDEFINITE_WAIT);
|
---|
1656 | if (!m_eventQueue.isEmpty())
|
---|
1657 | {
|
---|
1658 | e = m_eventQueue.first();
|
---|
1659 | m_eventQueue.removeFirst();
|
---|
1660 | LogFlowThisFunc(("new msg %d\n", e.type));
|
---|
1661 | if (e.type == DnDEvent::HGCM_Type)
|
---|
1662 | {
|
---|
1663 | switch (e.hgcm.uType)
|
---|
1664 | {
|
---|
1665 | case DragAndDropSvc::HOST_DND_HG_EVT_ENTER:
|
---|
1666 | {
|
---|
1667 | RTCList<RTCString> formats = RTCString(e.hgcm.pszFormats, e.hgcm.cbFormats - 1).split("\r\n");
|
---|
1668 | m_pCurDnD->hgEnter(formats, e.hgcm.u.a.uAllActions);
|
---|
1669 | /* Enter is always followed by a move event. */
|
---|
1670 | }
|
---|
1671 | case DragAndDropSvc::HOST_DND_HG_EVT_MOVE:
|
---|
1672 | {
|
---|
1673 | m_pCurDnD->hgMove(e.hgcm.u.a.uXpos, e.hgcm.u.a.uYpos, e.hgcm.u.a.uDefAction);
|
---|
1674 | break;
|
---|
1675 | }
|
---|
1676 | case DragAndDropSvc::HOST_DND_HG_EVT_LEAVE:
|
---|
1677 | {
|
---|
1678 | m_pCurDnD->reset();
|
---|
1679 | /* Not sure if this is really right! */
|
---|
1680 | clearEventQueue();
|
---|
1681 | break;
|
---|
1682 | }
|
---|
1683 | case DragAndDropSvc::HOST_DND_HG_EVT_DROPPED:
|
---|
1684 | {
|
---|
1685 | m_pCurDnD->hgDrop();
|
---|
1686 | break;
|
---|
1687 | }
|
---|
1688 | case DragAndDropSvc::HOST_DND_HG_SND_DATA:
|
---|
1689 | {
|
---|
1690 | m_pCurDnD->hgDataReceived(e.hgcm.u.b.pvData, e.hgcm.u.b.cbData);
|
---|
1691 | break;
|
---|
1692 | }
|
---|
1693 | #ifdef VBOX_WITH_DRAG_AND_DROP_GH
|
---|
1694 | case DragAndDropSvc::HOST_DND_GH_REQ_PENDING:
|
---|
1695 | {
|
---|
1696 | m_pCurDnD->ghIsDnDPending();
|
---|
1697 | break;
|
---|
1698 | }
|
---|
1699 | case DragAndDropSvc::HOST_DND_GH_EVT_DROPPED:
|
---|
1700 | {
|
---|
1701 | m_pCurDnD->ghDropped(e.hgcm.pszFormats, e.hgcm.u.a.uDefAction);
|
---|
1702 | /* Not sure if this is really right! */
|
---|
1703 | clearEventQueue();
|
---|
1704 | break;
|
---|
1705 | }
|
---|
1706 | #endif
|
---|
1707 | default:
|
---|
1708 | LogFlowThisFunc(("Unknown message: %RU32\n", e.hgcm.uType));
|
---|
1709 | break;
|
---|
1710 | }
|
---|
1711 |
|
---|
1712 | /* Some messages require cleanup. */
|
---|
1713 | switch (e.hgcm.uType)
|
---|
1714 | {
|
---|
1715 | case DragAndDropSvc::HOST_DND_HG_EVT_ENTER:
|
---|
1716 | case DragAndDropSvc::HOST_DND_HG_EVT_MOVE:
|
---|
1717 | case DragAndDropSvc::HOST_DND_HG_EVT_DROPPED:
|
---|
1718 | #ifdef VBOX_WITH_DRAG_AND_DROP_GH
|
---|
1719 | case DragAndDropSvc::HOST_DND_GH_EVT_DROPPED:
|
---|
1720 | #endif
|
---|
1721 | {
|
---|
1722 | if (e.hgcm.pszFormats)
|
---|
1723 | RTMemFree(e.hgcm.pszFormats);
|
---|
1724 | break;
|
---|
1725 | }
|
---|
1726 | case DragAndDropSvc::HOST_DND_HG_SND_DATA:
|
---|
1727 | {
|
---|
1728 | if (e.hgcm.pszFormats)
|
---|
1729 | RTMemFree(e.hgcm.pszFormats);
|
---|
1730 | if (e.hgcm.u.b.pvData)
|
---|
1731 | RTMemFree(e.hgcm.u.b.pvData);
|
---|
1732 | break;
|
---|
1733 | }
|
---|
1734 | default:
|
---|
1735 | break;
|
---|
1736 | }
|
---|
1737 | }
|
---|
1738 | else if(e.type == DnDEvent::X11_Type)
|
---|
1739 | {
|
---|
1740 | LogFlowThisFunc(("X11 type: %u\n", e.x11.type));
|
---|
1741 | /* Now the X11 event stuff */
|
---|
1742 | switch (e.x11.type)
|
---|
1743 | {
|
---|
1744 | case SelectionRequest: m_pCurDnD->hgX11SelectionRequest(e.x11); break;
|
---|
1745 | case ClientMessage: m_pCurDnD->hgX11ClientMessage(e.x11); break;
|
---|
1746 | case SelectionClear: LogFlowThisFunc(("DnD_CLER\n")); break;
|
---|
1747 | // case MotionNotify: m_pCurDnD->hide(); break;
|
---|
1748 | }
|
---|
1749 | }
|
---|
1750 | }
|
---|
1751 | } while (!ASMAtomicReadBool(&m_fSrvStopping));
|
---|
1752 | } while (0);
|
---|
1753 |
|
---|
1754 | LogFlowFuncLeaveRC(rc);
|
---|
1755 | return rc;
|
---|
1756 | }
|
---|
1757 |
|
---|
1758 | int DragAndDropService::x11DragAndDropInit(void)
|
---|
1759 | {
|
---|
1760 | /* Connect to the x11 server. */
|
---|
1761 | m_pDisplay = XOpenDisplay(NULL);
|
---|
1762 | if (!m_pDisplay)
|
---|
1763 | /* todo: correct errors */
|
---|
1764 | return VERR_NOT_FOUND;
|
---|
1765 |
|
---|
1766 | xHelpers *pHelpers = xHelpers::getInstance(m_pDisplay);
|
---|
1767 | if (!pHelpers)
|
---|
1768 | return VERR_NO_MEMORY;
|
---|
1769 |
|
---|
1770 | int rc = VINF_SUCCESS;
|
---|
1771 | do
|
---|
1772 | {
|
---|
1773 | /* Signal a new event to our main loop. */
|
---|
1774 | rc = RTSemEventCreate(&m_hEventSem);
|
---|
1775 | if (RT_FAILURE(rc))
|
---|
1776 | break;
|
---|
1777 | /* Event thread for events coming from the HGCM device. */
|
---|
1778 | rc = RTThreadCreate(&m_hHGCMThread, hgcmEventThread, this,
|
---|
1779 | 0, RTTHREADTYPE_MSG_PUMP, RTTHREADFLAGS_WAITABLE,
|
---|
1780 | "HGCM-NOTIFY");
|
---|
1781 | if (RT_FAILURE(rc))
|
---|
1782 | break;
|
---|
1783 | /* Event thread for events coming from the x11 system. */
|
---|
1784 | rc = RTThreadCreate(&m_hX11Thread, x11EventThread, this,
|
---|
1785 | 0, RTTHREADTYPE_MSG_PUMP, RTTHREADFLAGS_WAITABLE,
|
---|
1786 | "X11-NOTIFY");
|
---|
1787 | } while (0);
|
---|
1788 |
|
---|
1789 | /* No clean-up code for now, as we have no good way of testing it and things
|
---|
1790 | * should get cleaned up when the user process/X11 client exits. */
|
---|
1791 |
|
---|
1792 | return rc;
|
---|
1793 | }
|
---|
1794 |
|
---|
1795 | /* static */
|
---|
1796 | int DragAndDropService::hgcmEventThread(RTTHREAD hThread, void *pvUser)
|
---|
1797 | {
|
---|
1798 | AssertPtrReturn(pvUser, VERR_INVALID_PARAMETER);
|
---|
1799 | DragAndDropService *pThis = static_cast<DragAndDropService*>(pvUser);
|
---|
1800 | DnDEvent e;
|
---|
1801 |
|
---|
1802 | uint32_t uClientID;
|
---|
1803 | int rc = VbglR3DnDConnect(&uClientID);
|
---|
1804 | if (RT_FAILURE(rc)) {
|
---|
1805 | LogFlowFunc(("Unable to connect to HGCM service, rc=%Rrc\n", rc));
|
---|
1806 | return rc;
|
---|
1807 | }
|
---|
1808 |
|
---|
1809 | /* Number of invalid messages skipped in a row. */
|
---|
1810 | int cMsgSkippedInvalid = 0;
|
---|
1811 |
|
---|
1812 | do
|
---|
1813 | {
|
---|
1814 | RT_ZERO(e);
|
---|
1815 | e.type = DnDEvent::HGCM_Type;
|
---|
1816 | /* Wait for new events */
|
---|
1817 | rc = VbglR3DnDProcessNextMessage(uClientID, &e.hgcm);
|
---|
1818 | if (RT_SUCCESS(rc))
|
---|
1819 | {
|
---|
1820 | cMsgSkippedInvalid = 0; /* Reset skipped messages count. */
|
---|
1821 |
|
---|
1822 | pThis->m_eventQueue.append(e);
|
---|
1823 | rc = RTSemEventSignal(pThis->m_hEventSem);
|
---|
1824 | if (RT_FAILURE(rc))
|
---|
1825 | return rc;
|
---|
1826 | }
|
---|
1827 | else
|
---|
1828 | {
|
---|
1829 | /* Old(er) hosts either are broken regarding DnD support or otherwise
|
---|
1830 | * don't support the stuff we do on the guest side, so make sure we
|
---|
1831 | * don't process invalid messages forever. */
|
---|
1832 | if (rc == VERR_INVALID_PARAMETER)
|
---|
1833 | cMsgSkippedInvalid++;
|
---|
1834 | if (cMsgSkippedInvalid > 3)
|
---|
1835 | {
|
---|
1836 | LogFlowFunc(("Too many invalid/skipped messages from host, exiting ...\n"));
|
---|
1837 | break;
|
---|
1838 | }
|
---|
1839 | }
|
---|
1840 |
|
---|
1841 | } while (!ASMAtomicReadBool(&pThis->m_fSrvStopping));
|
---|
1842 |
|
---|
1843 | VbglR3DnDDisconnect(uClientID);
|
---|
1844 |
|
---|
1845 | return VINF_SUCCESS;
|
---|
1846 | }
|
---|
1847 |
|
---|
1848 | /* static */
|
---|
1849 | int DragAndDropService::x11EventThread(RTTHREAD hThread, void *pvUser)
|
---|
1850 | {
|
---|
1851 | AssertPtrReturn(pvUser, VERR_INVALID_PARAMETER);
|
---|
1852 | DragAndDropService *pThis = static_cast<DragAndDropService*>(pvUser);
|
---|
1853 | DnDEvent e;
|
---|
1854 | do
|
---|
1855 | {
|
---|
1856 | /* Wait for new events. We can't use XIfEvent here, cause this locks
|
---|
1857 | * the window connection with a mutex and if no X11 events occurs this
|
---|
1858 | * blocks any other calls we made to X11. So instead check for new
|
---|
1859 | * events and if there are not any new one, sleep for a certain amount
|
---|
1860 | * of time. */
|
---|
1861 | if (XEventsQueued(pThis->m_pDisplay, QueuedAfterFlush) > 0)
|
---|
1862 | {
|
---|
1863 | RT_ZERO(e);
|
---|
1864 | e.type = DnDEvent::X11_Type;
|
---|
1865 | XNextEvent(pThis->m_pDisplay, &e.x11);
|
---|
1866 | {
|
---|
1867 | /* Appending makes a copy of the event structure. */
|
---|
1868 | pThis->m_eventQueue.append(e);
|
---|
1869 | int rc = RTSemEventSignal(pThis->m_hEventSem);
|
---|
1870 | if (RT_FAILURE(rc))
|
---|
1871 | return rc;
|
---|
1872 | }
|
---|
1873 | }
|
---|
1874 | else
|
---|
1875 | RTThreadSleep(25);
|
---|
1876 | } while (!ASMAtomicReadBool(&pThis->m_fSrvStopping));
|
---|
1877 |
|
---|
1878 | return VINF_SUCCESS;
|
---|
1879 | }
|
---|
1880 |
|
---|
1881 | /* Static factory */
|
---|
1882 | VBoxClient::Service *VBoxClient::GetDragAndDropService(void)
|
---|
1883 | {
|
---|
1884 | DragAndDropService *pService = new DragAndDropService();
|
---|
1885 | return pService;
|
---|
1886 | }
|
---|
1887 |
|
---|