VirtualBox

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

Last change on this file since 50593 was 50593, checked in by vboxsync, 11 years ago

DnD: Bugfixes for Linux hosts.

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 65.3 KB
Line 
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. */
82struct 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
97enum 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
134class DragAndDropService;
135
136/*******************************************************************************
137 *
138 * xHelpers Declaration
139 *
140 ******************************************************************************/
141
142class xHelpers
143{
144public:
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
186private:
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
214xHelpers *xHelpers::m_pInstance = 0;
215/* Has to be in sync with the XA_Type enum. */
216const 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
251RTCString 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 */
278Window 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. */
326class DragInstance
327{
328public:
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
409class DragAndDropService : public VBoxClient::Service
410{
411public:
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
433private:
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
478DragInstance::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
493void 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
512void 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
530const 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
544int 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
624int 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
653int 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
794int 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
839int 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
874int 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
983int 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
1037int 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
1169int 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
1358int 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
1366void 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
1397void 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
1413void DragInstance::hideProxyWin(void) const
1414{
1415 XUnmapWindow(m_pDisplay, m_wndProxy);
1416}
1417
1418/* Currently, not used */
1419void 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
1439void 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
1448void DragInstance::clearActionsWindowProperty(Window wndThis) const
1449{
1450 XDeleteProperty(m_pDisplay, wndThis, xAtom(XA_XdndActionList));
1451}
1452
1453void 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
1468void DragInstance::clearFormatsWindowProperty(Window wndThis) const
1469{
1470 XDeleteProperty(m_pDisplay, wndThis, xAtom(XA_XdndTypeList));
1471}
1472
1473RTCList<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
1482RTCList<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 */
1508Atom 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 */
1518RTCList<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 */
1532uint32_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 */
1545uint32_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
1559RTCList<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
1585bool 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
1619void DragAndDropService::clearEventQueue()
1620{
1621 m_eventQueue.clear();
1622}
1623
1624int 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
1758int 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 */
1796int 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 */
1849int 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 */
1882VBoxClient::Service *VBoxClient::GetDragAndDropService(void)
1883{
1884 DragAndDropService *pService = new DragAndDropService();
1885 return pService;
1886}
1887
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