draganddrop.cpp revision 970606b6e6e9effb34de39a91ebacba8cdaebfdc
/** @file
* X11 guest client - Drag and Drop.
*/
/*
* Copyright (C) 2011-2012 Oracle Corporation
*
* This file is part of VirtualBox Open Source Edition (OSE), as
* available from http://www.virtualbox.org. This file is free software;
* General Public License (GPL) as published by the Free Software
* Foundation, in version 2 as it comes in the "COPYING" file of the
* VirtualBox OSE distribution. VirtualBox OSE is distributed in the
* hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
*/
//#include <X11/extensions/XTest.h>
#include <limits.h>
#include <VBox/VBoxGuestLib.h>
#include "VBox/HostServices/DragAndDropSvc.h"
#include "VBoxClient.h"
/* For X11 guest xDnD is used. See http://www.acc.umu.se/~vatten/XDND.html for
* a walk trough.
*
* H->G:
* For X11 this means mainly forwarding all the events from HGCM to the
* appropriate X11 events. There exists a proxy window, which is invisible and
* used for all the X11 communication. On a HGCM Enter event, we set our proxy
* window as XdndSelection owner with the given mime-types. On every HGCM move
* event, we move the X11 mouse cursor to the new position and query for the
* window below that position. Depending on if it is XdndAware, a new window or
* a known window, we send the appropriate X11 messages to it. On HGCM drop, we
* send a XdndDrop message to the current window and wait for a X11
* SelectionMessage from the target window. Because we didn't have the data in
* the requested mime-type, yet, we save that message and ask the host for the
* data. When the data is successfully received from the host, we put the data
* as a property to the window and send a X11 SelectionNotify event to the
* target window.
*
* G->H:
* This is a lot more trickery than H->G. When a pending event from HGCM
* arrives, we asks if there is currently an owner of the XdndSelection
* property. If so, our proxy window is shown (1x1, but without backing store)
* and some mouse event is triggered. This should be followed by an XdndEnter
* event send to the proxy window. From this event we can fetch the necessary
* info of the mime-types and allowed actions and send this back to the host.
* On a drop request from the host, we query for the selection and should get
* the data in the specified mime-type. This data is send back to the host.
* After that we send a XdndLeave event to the source window.
* Todo:
* - this isn't finished, yet. Currently the mouse isn't correctly released
* in the guest (both, when the drop was successfully or canceled).
* - cancel (e.g. with the ESC key) doesn't work
*
* Todo:
* - XdndProxy window support
* - INCR support
* - make this much more robust for crashes of the other party
* - really check for the Xdnd version and the supported features
*/
#define VERBOSE 1
#if defined(VERBOSE) && defined(DEBUG_poetzsch)
#else
# define DO(s) do {} while (0)
//# define DO(s) Log s
#endif
#define VBOX_XDND_VERSION (4)
/* Shared struct used for adding new X11 events and HGCM messages to a single
* event queue. */
struct DnDEvent
{
enum DnDEventType
{
HGCM_Type = 1,
};
union
{
};
};
enum XA_Type
{
/* States */
XA_WM_STATE = 0,
/* Properties */
/* Mime Types */
/* xDnD */
/* Our own stop marker */
/* End marker */
};
class DragAndDropService;
/*******************************************************************************
*
* xHelpers Declaration
*
******************************************************************************/
class xHelpers
{
public:
{
if (!m_pInstance)
{
AssertPtrReturn(pDisplay, 0);
}
return m_pInstance;
}
{
}
{
return strAtom;
}
{
return format;
}
private:
{
/* Not all x11 atoms we use are defined in the headers. Create the
* additional one we need here. */
for (int i = 0; i < XA_End; ++i)
};
/* Private member vars */
static xHelpers *m_pInstance;
static const char *m_xAtomNames[XA_End];
};
/* Some xHelpers convenience defines. */
/*******************************************************************************
*
* xHelpers Implementation
*
******************************************************************************/
/* Has to be in sync with the XA_Type enum. */
const char *xHelpers::m_xAtomNames[] =
{
/* States */
"WM_STATE",
/* Properties */
"TARGETS",
"MULTIPLE",
"INCR",
/* Mime Types */
"TEXT",
/* xDnD */
"XdndSelection",
"XdndAware",
"XdndEnter",
"XdndLeave",
"XdndTypeList",
"XdndActionList",
"XdndPosition",
"XdndActionCopy",
"XdndActionMove",
"XdndActionLink",
"XdndStatus",
"XdndDrop",
"XdndFinished",
/* Our own stop marker */
"dndstop"
};
{
switch (xrc)
{
}
}
/* Todo: make this iterative */
{
/* No parent, nothing to do. */
if(parentWin == 0)
return 0;
int cProps = -1;
/* Fetch all x11 window properties of the parent window. */
if (cProps > 0)
{
/* We check the window for the WM_STATE property. */
for(int i = 0; i < cProps; ++i)
{
/* Found it. */
break;
}
/* Cleanup */
}
if (!appWin)
{
int tmp;
unsigned int utmp;
/* Query the next child window of the parent window at the current
* mouse position. */
/* Recursive call our self to dive into the child tree. */
}
return appWin;
}
/*******************************************************************************
*
* DragInstance Declaration
*
******************************************************************************/
/* For now only one DragInstance will exits when the app is running. In the
* future the support for having more than one D&D operation supported at the
* time will be necessary. */
class DragInstance
{
public:
enum State
{
};
enum Mode
{
HG,
};
void uninit();
void reset();
/* H->G */
int hgX11ClientMessage(const XEvent& e);
int hgDrop();
int hgX11SelectionRequest(const XEvent& e);
#ifdef VBOX_WITH_DRAG_AND_DROP_GH
/* G->H */
int ghIsDnDPending();
#endif
/* X11 helpers */
void hideProxyWin() const;
void registerForEvents(Window w) const;
/* Member vars */
int m_screenId;
long m_curVer;
};
/*******************************************************************************
*
* DragAndDropService Declaration
*
******************************************************************************/
{
public:
: m_pDisplay(0)
, m_pCurDnD(0)
, m_fSrvStopping(false)
{}
virtual const char *getPidFilePath() { return ".vboxclient-draganddrop.pid"; }
/** @todo Move this part in VbglR3 and just provide a callback for the platform-specific
notification stuff, since this is very similar to the VBoxTray code. */
virtual int run(bool fDaemonised = false);
virtual void cleanup()
{
/* Cleanup */
};
private:
int x11DragAndDropInit();
int x11DragAndDropTerm();
void clearEventQueue();
/* Usually XCheckMaskEvent could be used for queering selected x11 events.
* Unfortunately this doesn't work exactly with the events we need. So we
* use this predicate method below and XCheckIfEvent. */
{
if (!pEvent)
return False;
// || ( pEvent->type == ClientMessage
// && reinterpret_cast<XClientMessageEvent*>(pEvent)->window == reinterpret_cast<Window>(pUser))
// || ( pEvent->type == SelectionRequest
// && reinterpret_cast<XSelectionRequestEvent*>(pEvent)->requestor == reinterpret_cast<Window>(pUser)))
return True;
return False;
}
/* Private member vars */
bool m_fSrvStopping;
friend class DragInstance;
};
/*******************************************************************************
*
* DragInstanc Implementation
*
******************************************************************************/
, m_pScreen(0)
, m_rootWin(0)
, m_proxyWin(0)
, m_curWin(0)
, m_curVer(-1)
{
uninit();
}
void DragInstance::uninit()
{
reset();
if (m_proxyWin != 0)
m_screenId = -1;
m_pScreen = 0;
m_rootWin = 0;
m_proxyWin = 0;
}
void DragInstance::reset()
{
/* Hide the proxy win. */
hideProxyWin();
/* If we are currently the Xdnd selection owner, clear that. */
if (w == m_proxyWin)
/* Clear any other DnD specific data on the proxy win. */
/* Reset the internal state. */
m_curWin = 0;
m_curVer = -1;
}
/* Uri's */
/* Text */
<< "UTF8_STRING"
<< "COMPOUND_TEXT"
<< "TEXT"
<< "STRING"
/* OpenOffice formates */
<< "application/x-openoffice-embed-source-xml;windows_formatname=\"Star Embed Source (XML)\""
<< "application/x-openoffice-drawing;windows_formatname=\"Drawing Format\"";
{
int rc = VINF_SUCCESS;
do
{
uninit();
/* Enough screens configured in the x11 server? */
{
break;
}
/* Get the screen number from the x11 server. */
// pDrag->screen = ScreenOfDisplay(m_pDisplay, u32ScreenId);
// if (!pDrag->screen)
// {
// rc = VERR_GENERAL_FAILURE;
// break;
// }
/* Now query the corresponding root window of this screen. */
if (!m_rootWin)
{
break;
}
/* Create an invisible window which will act as proxy for the DnD
* operation. This window will be used for both the GH and HG
* direction. */
// attr.background_pixel = WhitePixel(m_pDisplay, m_screenId);
&attr);
// m_proxyWin = XCreateSimpleWindow(m_pDisplay, m_rootWin, 0, 0, 50, 50, 0, WhitePixel(m_pDisplay, m_screenId), WhitePixel(m_pDisplay, m_screenId));
if (!m_proxyWin)
{
break;
}
/* Make the new window Xdnd aware. */
reinterpret_cast<unsigned char*>(&ver), 1);
} while (0);
return rc;
}
/*
* Host -> Guest
*/
{
int rc = VINF_SUCCESS;
reset();
#if defined(VERBOSE) && defined(DEBUG_poetzsch)
#endif /* DEBUG */
DO(("\n"));
/* If we have more than 3 formats we have to use the type list extension. */
/* Announce the possible actions */
/* Set the DnD selection owner to our window. */
return rc;
}
{
DO(("DnD_MOVE: "));
return VERR_INVALID_STATE;
int rc = VINF_SUCCESS;
/* Move the mouse cursor within the guest. */
/* Search for the application window below the cursor. */
{
/* Temp stuff for the XGetWindowProperty call. */
int fmt;
unsigned long cItems, cbRemaining;
/* Query the XdndAware property from the window. We are interested in
* the version and if it is XdndAware at all. */
xrc = XGetWindowProperty(m_pDisplay, newWin, xAtom(XA_XdndAware), 0, 2, False, AnyPropertyType, &atmp, &fmt, &cItems, &cbRemaining, &pcData);
else
{
else
{
}
}
}
{
/* We left the current XdndAware window. Announce this to the window. */
RT_ZERO(m);
m.type = ClientMessage;
m.display = m_pDisplay;
m.format = 32;
m.data.l[0] = m_proxyWin;
if (RT_UNLIKELY(xrc == 0))
DO(("DnD_MOVE: error sending xevent\n"));
}
{
/* We enter a new window. Announce the XdndEnter event to the new
* window. The first three mime types are attached to the event (the
* others could be requested by the XdndTypeList property from the
* window itself). */
RT_ZERO(m);
m.type = ClientMessage;
m.display = m_pDisplay;
m.format = 32;
m.data.l[0] = m_proxyWin;
m.data.l[1] = RT_MAKE_U32_FROM_U8(m_formats.size() > 3 ? 1 : 0, 0, 0, RT_MIN(VBOX_XDND_VERSION, newVer));
if (RT_UNLIKELY(xrc == 0))
DO(("DnD_MOVE: error sending xevent\n"));
}
if (newVer != -1)
{
/* Send a XdndPosition event with the proposed action to the guest. */
RT_ZERO(m);
m.type = ClientMessage;
m.display = m_pDisplay;
m.format = 32;
m.data.l[0] = m_proxyWin;
if (RT_UNLIKELY(xrc == 0))
DO(("DnD_MOVE: error sending xevent\n"));
}
/* No window to process, so send a ignore ack event to the host. */
DO(("\n"));
return rc;
}
{
// || m_state != Dragging)
return VERR_INVALID_STATE;
/* Client messages are used to inform us about the status of a XdndAware
* window, in response of some events we send to them. */
int rc = VINF_SUCCESS;
{
/* The XdndStatus message tell us if the window will accept the DnD
* event and with which action. We immediately send this info down to
* the host as a response of a previous DnD message. */
DO(("DnD_STAT: win=%#x,accept=%RTbool,action='%s'\n",
/* Todo: compare this with the allowed actions. */
}
{
/* This message is send on a un/successful DnD drop request. */
DO(("DnD_FINI: win=%#x,success=%RTbool,action='%s'\n",
reset();
}
else
DO(("DnD_CLI: win=%#x,msg='%s'\n", e.xclient.data.l[0], xAtomToString(e.xclient.message_type).c_str()));
return rc;
}
int DragInstance::hgDrop()
{
return VERR_INVALID_STATE;
int rc = VINF_SUCCESS;
/* Send a drop event to the current window and reset our DnD status. */
RT_ZERO(m);
m.type = ClientMessage;
m.display = m_pDisplay;
m.format = 32;
m.data.l[0] = m_proxyWin;
if (RT_UNLIKELY(xrc == 0))
DO(("DnD_DROP: error sending xevent\n"));
m_curVer = -1;
return rc;
}
{
// || m_state != D)
return VERR_INVALID_STATE;
DO(("DnD_SELR: owner=%#x,requestor=%#x,sel_atom='%s',tar_atom='%s',prop_atom='%s',time=%u\n",
e.xselectionrequest.time));
int rc = VINF_SUCCESS;
/* A window is asking for some data. Normally here the data would be copied
* into the selection buffer and send to the requestor. Obviously we can't
* do that, cause we first need to ask the host for the data of the
* requested mime type. This is done and later answered with the correct
* data (s. dataReceived). */
/* Is the requestor asking for the possible mime types? */
{
DO(("DnD_SELR: ask for target list\n"));
/* If so, set the window property with the formats on the requestor
* window. */
XEvent s;
RT_ZERO(s);
if (RT_UNLIKELY(xrc == 0))
DO(("DnD_SELR: error sending xevent\n"));
}
/* Is the requestor asking for a specific mime type (we support)? */
{
/* If so, we need to inform the host about this request. Save the
* selection request event for later use. */
// || m_curWin != e.xselectionrequest.requestor)
{
DO(("DnD_SELR: refuse\n"));
XEvent s;
RT_ZERO(s);
if (RT_UNLIKELY(xrc == 0))
DO(("DnD_SELR: error sending xevent\n"));
}
else
{
}
}
/* Anything else. */
else
{
DO(("DnD_SELR: refuse\n"));
/* We don't understand this request message and therefore answer with an
* refusal messages. */
XEvent s;
RT_ZERO(s);
if (RT_UNLIKELY(xrc == 0))
DO(("DnD_SELR: error sending xevent\n"));
}
return rc;
}
{
return VERR_INVALID_STATE;
|| cData == 0))
return VERR_INVALID_PARAMETER;
return VERR_INVALID_STATE;
/* Make a copy of the data. The xserver will become the new owner. */
if (RT_UNLIKELY(!pvNewData))
return VERR_NO_MEMORY;
/* The host send us the DnD data in the requested mime type. This allows us
* to fill the XdndSelection property of the requestor window with the data
* and afterwards inform him about the new status. */
XEvent s;
RT_ZERO(s);
// s.xselection.owner = m_selEvent.xselectionrequest.owner;
DO(("DnD_SEND: owner=%#x,requestor=%#x,sel_atom='%s',tar_atom='%s',prop_atom='%s',time=%u\n",
s.xselection.time));
/* Fill up the property with the data. */
XChangeProperty(s.xselection.display, s.xselection.requestor, s.xselection.property, s.xselection.target, 8, PropModeReplace,
if (RT_UNLIKELY(xrc == 0))
DO(("DnD_SEND: error sending xevent\n"));
return VINF_SUCCESS;
}
#ifdef VBOX_WITH_DRAG_AND_DROP_GH
/*
* Guest -> Host
*/
int DragInstance::ghIsDnDPending()
{
int rc = VINF_SUCCESS;
/* Is there someone own the Xdnd selection which aren't we. */
if ( w
&& w != m_proxyWin)
{
/* Map the window on the current cursor position, which should provoke
* an XdndEnter event. */
XEvent e;
{
{
int f;
unsigned long n, a;
unsigned char *ret = 0;
reset();
m_curWin = w;
DO(("XA_XdndEnter\n"));
/* Check if the mime types are in the msg itself or if we need
* to fetch the XdndTypeList property from the window. */
{
for (int i = 2; i < 5; ++i)
{
}
}
else
{
xrc = XGetWindowProperty(m_pDisplay, w, xAtom(XA_XdndTypeList), 0, VBOX_MAX_XPROPERTIES, False, XA_ATOM, &type, &f, &n, &a, &ret);
&& n > 0
&& ret)
{
for (int i = 0; i < RT_MIN(VBOX_MAX_XPROPERTIES, n); ++i)
{
}
}
}
/* Fetch the possible list of actions, if this property is set. */
xrc = XGetWindowProperty(m_pDisplay, w, xAtom(XA_XdndActionList), 0, VBOX_MAX_XPROPERTIES, False, XA_ATOM, &type, &f, &n, &a, &ret);
&& n > 0
&& ret)
{
for (int i = 0; i < RT_MIN(VBOX_MAX_XPROPERTIES, n); ++i)
{
}
}
/* Acknowledge the event by sending a Status msg back to the
* window. */
RT_ZERO(m);
m.type = ClientMessage;
m.display = m_pDisplay;
m.format = 32;
m.data.l[0] = m_proxyWin;
if (RT_UNLIKELY(xrc == 0))
DO(("DnD_PNDG: error sending xevent\n"));
}
{
DO(("XA_XdndPosition\n"));
RT_ZERO(m);
m.type = ClientMessage;
m.display = m_pDisplay;
m.format = 32;
m.data.l[0] = m_proxyWin;
if (RT_UNLIKELY(xrc == 0))
DO(("DnD_PNDG: error sending xevent\n"));
}
{
}
}
hideProxyWin();
rc = VbglR3DnDGHAcknowledgePending(DND_COPY_ACTION, toHGCMActions(m_actions), gX11->xAtomListToString(m_formats).c_str());
}
return rc;
}
{
int rc = VINF_SUCCESS;
/* Show the proxy window, so that the source will find it. */
/* We send a fake release event to the current window, cause
* this should have the grab. */
/* The fake button release event, should lead to an XdndDrop event from the
* source. Because of the showing of the proxy window, sometimes other Xdnd
* events occurs before, like a XdndPosition event. We are not interested
* in those, so try to get the right one. */
XEvent e;
XClientMessageEvent *clme = 0;
RT_ZERO(e);
int tries = 3;
do
{
{
{
clme = reinterpret_cast<XClientMessageEvent*>(&e);
break;
}
}
} while (tries--);
if (clme)
{
/* Make some paranoid checks. */
{
/* Request to convert the selection in the specific format and
* place it to our proxy window as property. */
XConvertSelection(m_pDisplay, xAtom(XA_XdndSelection), aFormat, xAtom(XA_XdndSelection), m_proxyWin, clme->data.l[2]);
/* Wait for the selection notify event. */
RT_ZERO(e);
{
/* Make some paranoid checks. */
{
int format;
unsigned long cItems, cbRemaining;
unsigned char *ucData = 0;
&& format >= 8
&& cItems > 0
&& cbRemaining == 0)
{
/* For whatever reason some of the string mime-types are not
* zero terminated. Check that and correct it when necessary,
* cause the guest side wants this always. */
{
if (ucData1)
{
/* Got the data and its fully transfered. */
}
else
rc = VERR_NO_MEMORY;
}
else
/* Just send the data to the host. */
DO(("send responce\n"));
/* Confirm the result of the transfer to the source window. */
RT_ZERO(m);
m.type = ClientMessage;
m.display = m_pDisplay;
m.format = 32;
m.data.l[0] = m_proxyWin;
if (RT_UNLIKELY(xrc == 0))
DO(("DnD_DRO: error sending xevent\n"));
}
else
{
{
/* Todo: */
AssertMsgFailed(("Incrementally transfers are not supported, yet\n"));
}
else
{
AssertMsgFailed(("Not supported data type\n"));
}
/* Cancel this. */
RT_ZERO(m);
m.type = ClientMessage;
m.display = m_pDisplay;
m.format = 32;
m.data.l[0] = m_proxyWin;
m.data.l[1] = 0;
if (RT_UNLIKELY(xrc == 0))
DO(("DnD_DRO: error sending xevent\n"));
m_curWin = 0;
}
/* Cleanup */
if (ucData)
}
else
}
else
rc = VERR_TIMEOUT;
}
else
}
else
rc = VERR_TIMEOUT;
/* Inform the host on error */
if (RT_FAILURE(rc))
/* At this point, we have either successfully transfered any data or not.
* So reset our internal state, cause we are done. */
reset();
return rc;
}
#endif /* VBOX_WITH_DRAG_AND_DROP_GH */
/*
* Helpers
*/
{
/* Move the guest pointer to the DnD position, so we can find the window
* below that position. */
return VINF_SUCCESS;
}
{
// XTestFakeMotionEvent(m_pDisplay, -1, rx, ry, CurrentTime);
// XTestFakeMotionEvent(m_pDisplay, -1, rx + 1, ry + 1, CurrentTime);
// int rc = XTestFakeButtonEvent(m_pDisplay, 1, False, CurrentTime);
// if (rc != 0)
{
XTranslateCoordinates(m_pDisplay, be.root, be.window, be.x_root, be.y_root, &be.x, &be.y, &be.subwindow);
if (RT_UNLIKELY(xrc == 0))
DO(("DnD_BTN: error sending xevent\n"));
}
}
{
unsigned int m;
Window r, c;
// XTestGrabControl(m_pDisplay, False);
// XTestGrabControl(m_pDisplay, True);
}
void DragInstance::hideProxyWin() const
{
}
/* Currently, not used */
{
// if (w == m_proxyWin)
// return;
DO(("%x\n", w));
// XSelectInput(m_pDisplay, w, Button1MotionMask | Button2MotionMask | Button3MotionMask | Button4MotionMask | Button5MotionMask);//| SubstructureNotifyMask);
// XSelectInput(m_pDisplay, w, ButtonMotionMask); //PointerMotionMask);
unsigned cChildren;
{
for (unsigned i = 0; i < cChildren; ++i)
}
}
{
if (actionList.isEmpty())
return;
}
{
}
{
return;
/* We support TARGETS and the data types. */
/* Add the property with the property data to the window. */
}
{
}
{
return atomList;
}
{
if ( !pvData
|| !cData)
while (cStr > 0)
{
/* Create a copy with max N chars, so that we are on the save side,
* even if the data isn't zero terminated. */
}
return atomList;
}
/* static */
{
/* Ignore is None */
None);
}
/* static */
{
if (hasDnDCopyAction(uActions))
if (hasDnDMoveAction(uActions))
if (hasDnDLinkAction(uActions))
return actionList;
}
/* static */
{
return uAction;
}
/* static */
{
return uActions;
}
/*******************************************************************************
*
* DragAndDropService Implementation
*
******************************************************************************/
{
if ( !pvData
|| !cData)
while (cStr > 0)
{
/* Create a copy with max N chars, so that we are on the save side,
* even if the data isn't zero terminated. */
}
return strList;
}
#ifdef VBOX_WITH_DRAG_AND_DROP_GH
{
do
{
if (!m_eventQueue.isEmpty())
{
/* Check if there is a client message in the queue. */
{
DO(("new msg\n"));
{
m_eventQueue.removeAt(i);
return true;
}
}
}
// if (RT_FAILURE(rc))
// return false;
}
return false;
}
#endif
void DragAndDropService::clearEventQueue()
{
}
{
int rc = VINF_SUCCESS;
LogRelFlowFunc(("\n"));
do
{
/* Initialize our service */
rc = VbglR3DnDInit();
if (RT_FAILURE(rc))
break;
/* Initialize X11 DND */
rc = x11DragAndDropInit();
if (RT_FAILURE(rc))
break;
/* Note: For multiple screen support in VBox it is not necessary to use
* another screen number than zero. Maybe in the future it will become
* necessary if VBox supports multiple X11 screens. */
/* Loop over new events */
do
{
DnDEvent e;
RT_ZERO(e);
if (m_eventQueue.isEmpty())
if (!m_eventQueue.isEmpty())
{
e = m_eventQueue.first();
{
{
{
/* Enter is always followed by a move event. */
}
{
break;
}
{
/* Not sure if this is really right! */
break;
}
{
break;
}
{
break;
}
#ifdef VBOX_WITH_DRAG_AND_DROP_GH
{
break;
}
{
/* Not sure if this is really right! */
break;
}
#endif
}
/* Some messages require cleanup. */
{
#ifdef VBOX_WITH_DRAG_AND_DROP_GH
#endif
{
if (e.hgcm.pszFormats)
break;
}
{
if (e.hgcm.pszFormats)
break;
}
}
}
{
/* Now the X11 event stuff */
{
// case MotionNotify: m_pCurDnD->hide(); break;
}
}
}
} while (!ASMAtomicReadBool(&m_fSrvStopping));
} while (0);
return rc;
}
{
/* Connect to the x11 server. */
if (!m_pDisplay)
/* todo: correct errors */
return VERR_NOT_FOUND;
int rc = VINF_SUCCESS;
do
{
/* Signal a new event to our main loop. */
if (RT_FAILURE(rc))
break;
/* Event thread for events coming from the HGCM device. */
"HGCM-NOTIFY");
if (RT_FAILURE(rc))
break;
/* Event thread for events coming from the x11 system. */
"X11-NOTIFY");
} while (0);
/* Cleanup on failure */
if (RT_FAILURE(rc))
return rc;
}
{
/* Mark that we are stopping. */
ASMAtomicWriteBool(&m_fSrvStopping, true);
if (m_pDisplay)
{
/* Send a x11 client messages to the x11 event loop. */
RT_ZERO(m);
m.type = ClientMessage;
m.display = m_pDisplay;
m.format = 32;
if (RT_UNLIKELY(xrc == 0))
DO(("DnD_TERM: error sending xevent\n"));
}
/* We cannot signal the m_hHGCMThread as it is most likely waiting in vbglR3DoIOCtl() */
/** @todo r=michael Don't we have a mechanism for cancelling HGCM calls
* though? */
/* Wait for our event threads to stop. */
/** @todo r=michael This routine is generally called on the X11 thread,
* protected by a mutex, so the following thread wait
* makes us hang forever. */
if (m_hX11Thread)
/* Cleanup */
/* todo: This doesn't work. The semaphore was interrupted by the user
* signal. It is not possible to destroy a semaphore while it is in interrupted state.
* According to Frank, the cleanup stuff done here is done _wrong_. We just
* should signal the main loop to stop and do the cleanup there. Needs
* adoption in all VBoxClient::Service's. */
// if (m_hEventSem)
// RTSemEventDestroy(m_hEventSem);
if (m_pDisplay)
return VINF_SUCCESS;
}
/* static */
{
DnDEvent e;
do
{
RT_ZERO(e);
/* Wait for new events */
if (RT_SUCCESS(rc))
{
if (RT_FAILURE(rc))
return rc;
}
return VINF_SUCCESS;
}
/* static */
{
DnDEvent e;
do
{
/* Wait for new events. We can't use XIfEvent here, cause this locks
* the window connection with a mutex and if no X11 events occurs this
* blocks any other calls we made to X11. So instead check for new
* events and if there are not any new one, sleep for a certain amount
* of time. */
{
RT_ZERO(e);
#if 0
/* We never detect the stop event here for some reason */
/* Check for a stop message. */
break;
#endif
// if (isDnDRespondEvent(pThis->m_pDisplay, &e.x11, 0))
{
/* Appending makes a copy of the event structure. */
if (RT_FAILURE(rc))
return rc;
}
}
else
RTThreadSleep(25);
return VINF_SUCCESS;
}
/* Static factory */
{
return new(DragAndDropService);
}