x11-clipboard.cpp revision 289fdd35bf70c71ed32075f45c3af244431ffa71
/** @file
*
* Shared Clipboard:
* X11 backend code.
*/
/*
* Copyright (C) 2006-2007 Sun Microsystems, Inc.
*
* 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.
*
* Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa
* Clara, CA 95054 USA or visit http://www.sun.com if you need
* additional information or have any questions.
*/
#define LOG_GROUP LOG_GROUP_SHARED_CLIPBOARD
#include <errno.h>
#include <vector>
#include <unistd.h>
#ifdef RT_OS_SOLARIS
#endif
#include <X11/Intrinsic.h>
#include <X11/StringDefs.h>
#include <iprt/semaphore.h>
/** Do we want to test Utf8 by disabling other text formats? */
static bool g_testUtf8 = false;
/** Do we want to test compount text by disabling other text formats? */
static bool g_testCText = false;
/** Are we currently debugging the clipboard code? */
static bool g_debugClipboard = false;
/** The different clipboard formats which we support. */
enum g_eClipboardFormats
{
INVALID = 0,
};
/** The X11 clipboard uses several names for the same format. This
* structure maps an X11 name to a format. */
typedef struct {
unsigned guestFormat;
/** Global context information used by the X11 clipboard backend */
struct _VBOXCLIPBOARDCONTEXTX11
{
/** Opaque data structure describing the front-end. */
/** The X Toolkit application context structure */
/** We have a separate thread to wait for Window and Clipboard events */
/** The X Toolkit widget which we use as our clipboard client. It is never made visible. */
/** X11 atom refering to the clipboard: CLIPBOARD */
/** X11 atom refering to the selection: PRIMARY */
/** X11 atom refering to the clipboard targets: TARGETS */
/** X11 atom refering to the clipboard multiple target: MULTIPLE */
/** X11 atom refering to the clipboard timestamp target: TIMESTAMP */
/** X11 atom refering to the clipboard utf8 text format: UTF8_STRING */
/** X11 atom refering to the clipboard compound text format: COMPOUND_TEXT */
/** A list of the X11 formats which we support, mapped to our identifier for them, in the
order we prefer to have them in. */
/** Does VBox currently own the clipboard? If so, we don't need to poll
* X11 for supported formats. */
bool fOwnsClipboard;
/** What is the best text format X11 has to offer? INVALID for none. */
/** Atom corresponding to the X11 text format */
/** What is the best bitmap format X11 has to offer? INVALID for none.
*/
/** Atom corresponding to the X11 Bitmap format */
/** What formats does VBox have on offer? */
/** Windows hosts and guests cache the clipboard data they receive.
* Since we have no way of knowing whether their cache is still valid,
* we always send a "data changed" message after a successful transfer
* to invalidate it. */
bool notifyVBox;
/** Cache of the last unicode data that we received */
void *pvUnicodeCache;
/** Size of the unicode data in the cache */
/** When we wish the clipboard to exit, we have to wake up the event
* loop. We do this by writing into a pipe. This end of the pipe is
* the end that another thread can write to. */
int wakeupPipeWrite;
/** The reader end of the pipe */
int wakeupPipeRead;
};
/** The number of simultaneous instances we support. For all normal purposes
* we should never need more than one. For the testcase it is convenient to
* have a second instance that the first can interact with in order to have
* a more controlled environment. */
enum { CLIPBOARD_NUM_CONTEXTS = 20 };
/** Array of structures for mapping Xt widgets to context pointers. We
* need this because the widget clipboard callbacks do not pass user data. */
static struct {
/** The widget we want to associate the context with */
/** The context associated with the widget */
/** Register a new X11 clipboard context. */
{
bool found = false;
for (unsigned i = 0; i < RT_ELEMENTS(g_contexts); ++i)
{
{
found = true;
}
}
}
/** Unregister an X11 clipboard context. */
{
bool found = false;
for (unsigned i = 0; i < RT_ELEMENTS(g_contexts); ++i)
{
{
found = true;
}
}
}
/** Find an X11 clipboard context. */
{
for (unsigned i = 0; i < RT_ELEMENTS(g_contexts); ++i)
{
{
return g_contexts[i].pCtx;
}
}
return NULL;
}
/* Are we actually connected to the X server? */
static bool g_fHaveX11;
{
int rc = VINF_SUCCESS;
/* Check how much longer will the converted text will be. */
{
/* Not enough buffer space provided - report the amount needed. */
LogFlowFunc (("guest buffer too small: size %d bytes, needed %d. Returning.\n",
}
/* Convert the text. */
if (RT_SUCCESS(rc))
if (RT_SUCCESS(rc))
{
}
return rc;
}
/**
* Convert the UTF-8 text obtained from the X11 clipboard to UTF-16LE with
* Windows EOLs, place it in the buffer supplied and signal that data has
* arrived.
*
* @param pValue Source UTF-8 text
* @param cbSourceLen Length in 8-bit bytes of the source text
* @param pv Where to store the converted data
* @param cb Length in bytes of the buffer pointed to by pv
* @param pcbActual Where to store the size of the converted data
* @param pClient Pointer to the client context structure
* @note X11 backend code, called from the Xt callback when we wish to read
* the X11 clipboard.
*/
{
char *pu8SrcText = reinterpret_cast<char *>(pValue);
LogFlowFunc (("converting Utf-8 to Utf-16LE. cbSrcLen=%d, cb=%d, pu8SrcText=%.*s\n",
*pcbActual = 0; /* Only set this to the right value on success. */
/* First convert the UTF8 to UTF16 */
if (RT_SUCCESS(rc))
return rc;
}
/**
* Convert the COMPOUND_TEXT obtained from the X11 clipboard to UTF-16LE with
* Windows EOLs, place it in the buffer supplied and signal that data has
* arrived.
*
* @param pValue Source COMPOUND_TEXT
* @param cbSourceLen Length in 8-bit bytes of the source text
* @param pv Where to store the converted data
* @param cb Length in bytes of the buffer pointed to by pv
* @param pcbActual Where to store the size of the converted data
* @param pClient Pointer to the client context structure
* @note X11 backend code, called from the Xt callback when we wish to read
* the X11 clipboard.
*/
{
char **ppu8SrcText = NULL;
int rc = VINF_SUCCESS;
int cProps;
LogFlowFunc (("converting COMPOUND TEXT to Utf-16LE. cbSrcLen=%d, cb=%d, pu8SrcText=%.*s\n",
*pcbActual = 0; /* Only set this to the right value on success. */
/* First convert the compound text to Utf8 */
#ifdef RT_OS_SOLARIS
&ppu8SrcText, &cProps);
#else
#endif
if (xrc < 0)
switch(xrc)
{
case XNoMemory:
rc = VERR_NO_MEMORY;
break;
case XLocaleNotSupported:
case XConverterNotFound:
break;
default:
}
/* Now convert the UTF8 to UTF16 */
if (RT_SUCCESS(rc))
if (RT_SUCCESS(rc))
if (ppu8SrcText != NULL)
return rc;
}
/**
* Convert the Latin1 text obtained from the X11 clipboard to UTF-16LE with
* Windows EOLs, place it in the buffer supplied and signal that data has
* arrived.
*
* @param pValue Source Latin1 text
* @param cbSourceLen Length in 8-bit bytes of the source text
* @param pv Where to store the converted data
* @param cb Length in bytes of the buffer pointed to by cb
* @param pcbActual Where to store the size of the converted data
* @param pClient Pointer to the client context structure
* @note X11 backend code, called from the Xt callback when we wish to read
* the X11 clipboard.
*/
unsigned cbSourceLen, void *pv,
{
char *pu8SourceText = reinterpret_cast<char *>(pValue);
int rc = VINF_SUCCESS;
LogFlowFunc (("converting Latin1 to Utf-16LE. Original is %.*s\n",
*pcbActual = 0; /* Only set this to the right value on success. */
for (unsigned i = 0; i < cbSourceLen; i++)
if (pu8SourceText[i] == LINEFEED)
++cwDestLen;
{
/* Not enough buffer space provided - report the amount needed. */
}
if (RT_SUCCESS(rc))
{
for (unsigned i = 0, j = 0; i < cbSourceLen; ++i, ++j)
if (pu8SourceText[i] != LINEFEED)
else
{
pu16DestText[j] = CARRIAGERETURN;
++j;
pu16DestText[j] = LINEFEED;
}
}
return rc;
}
/**
* Convert the text obtained from the X11 clipboard to UTF-16LE with Windows
* EOLs, place it in the buffer supplied and signal that data has arrived.
* @note X11 backend code, callback for XtGetSelectionValue, for use when
* the X11 clipboard contains a text format we understand.
*/
Atom * /* selection */,
long unsigned int *pcLen,
int *piFormat)
{
= reinterpret_cast<VBOXCLIPBOARDREQUEST *>(pClientData);
if (pCtx->fOwnsClipboard == true)
{
/* We don't want to request data from ourselves! */
return;
}
LogFlowFunc(("pCtx->X11TextFormat=%d, pRequest->cb=%d\n",
/* The X Toolkit may have failed to get the clipboard selection for us. */
{
return;
}
/* The clipboard selection may have changed before we could get it. */
{
return;
}
/* In which format is the clipboard data? */
switch (pCtx->X11TextFormat)
{
case CTEXT:
break;
case UTF8:
{
/* If we are given broken Utf-8, we treat it as Latin1. Is this acceptable? */
char *pu8SourceText = reinterpret_cast<char *>(pValue);
{
break;
}
else
{
break;
}
}
default:
LogFunc (("bad target format\n"));
return;
}
pCtx->notifyVBox = true;
}
/**
* Notify the host clipboard about the data formats we support, based on the
* "targets" (available data formats) information obtained from the X11
* clipboard.
* @note X11 backend code, callback for XtGetSelectionValue, called when we
* poll for available targets.
*/
static void vboxClipboardGetTargetsFromX11(Widget,
Atom * /* selection */,
long unsigned int *pcLen,
int *piFormat)
{
reinterpret_cast<VBOXCLIPBOARDCONTEXTX11 *>(pClientData);
* clipboard */
)
{
return;
}
for (unsigned i = 0; i < cAtoms; ++i)
{
{
{
}
break;
}
if (g_debugClipboard)
{
atomTargets[i]);
if (szAtomName != 0)
{
szAtomName));
}
}
}
{
uint32_t u32Formats = 0;
if (g_debugClipboard)
{
if (atomBestTarget != None)
{
Log2 (("%s: switching to host text target %s. Available targets are:\n",
}
else
Log2(("%s: no supported host text target found. Available targets are:\n",
for (unsigned i = 0; i < cAtoms; ++i)
{
atomTargets[i]);
if (szAtomName != 0)
{
}
}
}
if (eBestTarget != INVALID)
pCtx->notifyVBox = false;
}
}
/**
* This timer callback is called every 200ms to check the contents of the X11
* clipboard.
* @note X11 backend code, callback for XtAppAddTimeOut, recursively
* re-armed.
* @todo Use the XFIXES extension to check for new clipboard data when
* available.
*/
XtIntervalId * /* hTimerId */)
{
reinterpret_cast<VBOXCLIPBOARDCONTEXTX11 *>(pUserData);
/* Get the current clipboard contents if we don't own it ourselves */
if (!pCtx->fOwnsClipboard)
{
Log3 (("%s: requesting the targets that the host clipboard offers\n",
}
/* Re-arm our timer */
}
/** We store information about the target formats we can handle in a global
* vector for internal use.
* @note X11 backend code.
*/
const char *pszName,
unsigned guestFormat)
{
/* Get an atom from the X server for that target format */
}
/**
* The main loop of our clipboard reader.
* @note X11 backend code.
*/
{
LogRel(("Shared clipboard: starting host clipboard thread\n"));
reinterpret_cast<VBOXCLIPBOARDCONTEXTX11 *>(pvUser);
/* Set up a timer to poll the host clipboard */
LogRel(("Shared clipboard: host clipboard thread terminated successfully\n"));
return VINF_SUCCESS;
}
/** X11 specific uninitialisation for the shared clipboard.
* @note X11 backend code.
*/
{
{
/* Valid widget + invalid appcontext = bug. But don't return yet. */
}
if (pCtx->appContext)
if (pCtx->wakeupPipeRead != 0)
if (pCtx->wakeupPipeWrite != 0)
pCtx->wakeupPipeRead = 0;
pCtx->wakeupPipeWrite = 0;
}
/** Worker function for stopping the clipboard which runs on the event
* thread. */
XtInputId * /* id */)
{
/* This might mean that we are getting stopped twice. */
/* Set the termination flag to tell the Xt event loop to exit. We
* reiterate that any outstanding requests from the X11 event loop to
* the VBox part *must* have returned before we do this. */
pCtx->fOwnsClipboard = false;
}
/** X11 specific initialisation for the shared clipboard.
* @note X11 backend code.
*/
{
/* Create a window and make it a clipboard viewer. */
int cArgc = 0;
char *pcArgv = 0;
int rc = VINF_SUCCESS;
/* Make sure we are thread safe */
/* Set up the Clipbard application context and main window. We call all these functions
directly instead of calling XtOpenApplication() so that we can fail gracefully if we
can't get an X11 display. */
{
LogRel(("Shared clipboard: failed to connect to the host clipboard - the window system may not be running.\n"));
}
if (RT_SUCCESS(rc))
{
{
LogRel(("Shared clipboard: failed to construct the X11 window for the host clipboard manager.\n"));
rc = VERR_NO_MEMORY;
}
else
}
if (RT_SUCCESS(rc))
{
/* Get hold of the atoms which we need */
pCtx->atomClipboard = XInternAtom(XtDisplay(pCtx->widget), "CLIPBOARD", false /* only_if_exists */);
/* And build up the vector of supported formats */
/* And build up the vector of supported formats */
if (!g_testCText)
{
}
if (!g_testUtf8)
}
/* Create the pipes */
int pipes[2];
{
}
else
if (RT_FAILURE(rc))
return rc;
}
/**
* Construct the X11 backend of the shared clipboard.
* @note X11 backend code
*/
{
int rc;
RTMemAllocZ(sizeof(VBOXCLIPBOARDCONTEXTX11));
{
/*
* If we don't find the DISPLAY environment variable we assume that
* we are not connected to an X11 server. Don't actually try to do
* this then, just fail silently and report success on every call.
* This is important for VBoxHeadless.
*/
LogRelFunc(("X11 DISPLAY variable not set -- disabling shared clipboard\n"));
g_fHaveX11 = false;
return pCtx;
}
if (RTEnvGet("VBOX_CBTEST_UTF8"))
{
g_testUtf8 = true;
LogRel(("Host clipboard: testing Utf8\n"));
}
else if (RTEnvGet("VBOX_CBTEST_CTEXT"))
{
g_testCText = true;
LogRel(("Host clipboard: testing compound text\n"));
}
else if (RTEnvGet("VBOX_CBDEBUG"))
{
g_debugClipboard = true;
LogRel(("Host clipboard: enabling additional debugging output\n"));
}
g_fHaveX11 = true;
LogRel(("Initializing X11 clipboard backend\n"));
if (pCtx)
return pCtx;
}
/**
* Destruct the shared clipboard X11 backend.
* @note X11 backend code
*/
{
/*
* Immediately return if we are not connected to the host X server.
*/
if (!g_fHaveX11)
return;
/* We set this to NULL when the event thread exits. It really should
* have exited at this point, when we are about to unload the code from
* memory. */
}
/**
* Announce to the X11 backend that we are ready to start.
*/
{
int rc = VINF_SUCCESS;
LogFlowFunc(("\n"));
/*
* Immediately return if we are not connected to the host X server.
*/
if (!g_fHaveX11)
return VINF_SUCCESS;
if (RT_SUCCESS(rc))
{
if (RT_FAILURE(rc))
LogRel(("Failed to initialise the shared clipboard X11 backend.\n"));
}
if (RT_SUCCESS(rc))
{
pCtx->fOwnsClipboard = false;
pCtx->notifyVBox = true;
}
return rc;
}
/** String written to the wakeup pipe. */
#define WAKE_UP_STRING "WakeUp!"
/** Length of the string written. */
/**
* Shut down the shared clipboard X11 backend.
* @note X11 backend code
* @note Any requests from this object to get clipboard data from VBox
* *must* have completed or aborted before we are called, as
* otherwise the X11 event loop will still be waiting for the request
* to return and will not be able to terminate.
*/
{
unsigned count = 0;
/*
* Immediately return if we are not connected to the host X server.
*/
if (!g_fHaveX11)
return VINF_SUCCESS;
LogRelFunc(("stopping the shared clipboard X11 backend\n"));
/* Write to the "stop" pipe */
do
{
++count;
if (RT_SUCCESS(rc))
else
return rc;
}
/**
* Satisfy a request from X11 for clipboard targets supported by VBox.
*
* @returns true if we successfully convert the data to the format
* requested, false otherwise.
*
* @param atomTypeReturn The type of the data we are returning
* @param pValReturn A pointer to the data we are returning. This
* should be set to memory allocated by XtMalloc,
* which will be freed later by the Xt toolkit.
* @param pcLenReturn The length of the data we are returning
* @param piFormatReturn The format (8bit, 16bit, 32bit) of the data we are
* returning
* @note X11 backend code, called by the XtOwnSelection callback.
*/
*pCtx,
unsigned long *pcLenReturn,
int *piFormatReturn)
{
unsigned cTargets = 0;
LogFlowFunc (("called\n"));
for (unsigned i = 0; i < uListSize; ++i)
{
{
++cTargets;
}
}
if (g_debugClipboard)
{
for (unsigned i = 0; i < cTargets + 3; i++)
{
if (szAtomName != 0)
{
szAtomName));
}
else
{
atomTargets[i]));
}
}
}
*piFormatReturn = 32;
return true;
}
/** This is a wrapper around VBoxX11ClipboardReadVBoxData that will cache the
* data returned. This is unfortunately necessary, because if the other side
* of the shared clipboard is also an X11 system, it may send a format
* announcement message every time its clipboard is read, for reasons that
* are explained elsewhere. Unfortunately, some applications on our side
* like to read the clipboard several times in short succession in different
* formats. This can fail if it collides with a format announcement message.
* @todo any ideas about how to do this better are welcome.
*/
{
int rc = VINF_SUCCESS;
{
&pCtx->cbUnicodeCache);
if (RT_SUCCESS(rc))
{
rc = VERR_NO_MEMORY;
}
}
else
if (RT_SUCCESS(rc))
return rc;
}
/**
* Satisfy a request from X11 to convert the clipboard text to Utf8. We
* return non-zero terminated text.
* @todo that works, but it is bad. Change it to return zero-terminated
* text.
*
* @returns true if we successfully convert the data to the format
* requested, false otherwise.
*
* @param atomTypeReturn Where to store the atom for the type of the data
* we are returning
* @param pValReturn Where to store the pointer to the data we are
* returning. This should be to memory allocated by
* XtMalloc, which will be freed by the Xt toolkit
* later.
* @param pcLenReturn Where to store the length of the data we are
* returning
* @param piFormatReturn Where to store the bit width (8, 16, 32) of the
* data we are returning
* @note X11 backend code, called by the callback for XtOwnSelection.
*/
*pCtx,
unsigned long *pcLenReturn,
int *piFormatReturn)
{
char *pu8DestText;
int rc;
LogFlowFunc (("called\n"));
/* Read the clipboard data from the guest. */
{
/* If vboxClipboardReadVBoxData fails then we may be terminating */
return false;
}
/* How long will the converted text be? */
if (RT_FAILURE(rc))
{
LogRelFunc (("clipboard conversion failed. vboxClipboardUtf16GetLinSize returned %Rrc. Abandoning.\n", rc));
AssertRCReturn(rc, false);
}
if (cwDestLen == 0)
{
LogFlowFunc(("received empty clipboard data from the guest, returning false.\n"));
return false;
}
if (pu16DestText == 0)
{
return false;
}
/* Convert the text. */
if (RT_FAILURE(rc))
{
LogRelFunc (("clipboard conversion failed. vboxClipboardUtf16WinToLin() returned %Rrc. Abandoning.\n", rc));
RTMemFree(reinterpret_cast<void *>(pu16DestText));
return false;
}
/* Allocate enough space, as RTUtf16ToUtf8Ex may fail if the
space is too tightly calculated. */
if (pu8DestText == 0)
{
RTMemFree(reinterpret_cast<void *>(pu16DestText));
return false;
}
/* Convert the Utf16 string to Utf8. */
&cbDestLen);
RTMemFree(reinterpret_cast<void *>(pu16DestText));
if (RT_FAILURE(rc))
{
return false;
}
*pcLenReturn = cbDestLen;
*piFormatReturn = 8;
return true;
}
/**
* Satisfy a request from X11 to convert the clipboard text to
* COMPOUND_TEXT. We return non-zero terminated text.
* @todo that works, but it is bad. Change it to return zero-terminated
* text.
*
* @returns true if we successfully convert the data to the format
* requested, false otherwise.
*
* @param atomTypeReturn Where to store the atom for the type of the data
* we are returning
* @param pValReturn Where to store the pointer to the data we are
* returning. This should be to memory allocated by
* XtMalloc, which will be freed by the Xt toolkit
* later.
* @param pcLenReturn Where to store the length of the data we are
* returning
* @param piFormatReturn Where to store the bit width (8, 16, 32) of the
* data we are returning
* @note X11 backend code, called by the callback for XtOwnSelection.
*/
*pCtx,
unsigned long *pcLenReturn,
int *piFormatReturn)
{
char *pu8DestText = 0;
int rc;
LogFlowFunc (("called\n"));
/* Read the clipboard data from the guest. */
{
/* If vboxClipboardReadVBoxData fails then we may be terminating */
return false;
}
/* How long will the converted text be? */
if (RT_FAILURE(rc))
{
LogRelFunc (("clipboard conversion failed. vboxClipboardUtf16GetLinSize returned %Rrc. Abandoning.\n", rc));
AssertRCReturn(rc, false);
}
if (cwDestLen == 0)
{
LogFlowFunc(("received empty clipboard data from the guest, returning false.\n"));
return false;
}
if (pu16DestText == 0)
{
return false;
}
/* Convert the text. */
if (RT_FAILURE(rc))
{
LogRelFunc (("clipboard conversion failed. vboxClipboardUtf16WinToLin() returned %Rrc. Abandoning.\n", rc));
RTMemFree(reinterpret_cast<void *>(pu16DestText));
return false;
}
/* Convert the Utf16 string to Utf8. */
RTMemFree(reinterpret_cast<void *>(pu16DestText));
if (RT_FAILURE(rc))
{
return false;
}
/* And finally (!) convert the Utf8 text to compound text. */
#ifdef RT_OS_SOLARIS
#else
#endif
if (rc < 0)
{
const char *pcReason;
switch(rc)
{
case XNoMemory:
pcReason = "out of memory";
break;
case XLocaleNotSupported:
pcReason = "locale (Utf8) not supported";
break;
case XConverterNotFound:
pcReason = "converter not found";
break;
default:
pcReason = "unknown error";
}
LogRelFunc (("Xutf8TextListToTextProperty failed. Reason: %s\n",
pcReason));
return false;
}
return true;
}
/**
* Return VBox's clipboard data for an X11 client.
* @note X11 backend code, callback for XtOwnSelection
*/
unsigned long *pcLenReturn,
int *piFormatReturn)
{
LogFlowFunc(("\n"));
/* Drop requests that we receive too late. */
if (!pCtx->fOwnsClipboard)
return false;
)
{
LogFlowFunc(("rc = false\n"));
return false;
}
if (g_debugClipboard)
{
if (szAtomName != 0)
{
}
else
{
}
}
{
}
else
{
{
{
break;
}
}
}
switch (eFormat)
{
case TARGETS:
case UTF8:
case CTEXT:
default:
LogFunc (("bad format\n"));
return false;
}
}
/**
* This is called by the X toolkit intrinsics to let us know that another
* X11 client has taken the clipboard. In this case we notify VBox that
* we want ownership of the clipboard.
* @note X11 backend code, callback for XtOwnSelection
*/
{
LogFlowFunc (("called, giving X11 clipboard ownership\n"));
/* These should be set to the right values as soon as we start polling */
pCtx->fOwnsClipboard = false;
pCtx->notifyVBox = true;
}
/** Structure used to pass information about formats that VBox supports */
typedef struct _VBOXCLIPBOARDFORMATS
{
/** Context information for the X11 clipboard */
/** Formats supported by VBox */
/** Worker function for VBoxX11ClipboardAnnounceVBoxFormat which runs on the
* event thread. */
XtIntervalId * /* interval */)
{
/* Extract and free the user data */
/* Invalidate the clipboard cache */
{
}
if (u32Formats == 0)
{
/* This is just an automatism, not a genuine anouncement */
pCtx->fOwnsClipboard = false;
LogFlowFunc(("returning\n"));
return;
}
0) == True)
{
pCtx->fOwnsClipboard = true;
/* Grab the middle-button paste selection too. */
}
else
{
/* Another X11 client claimed the clipboard just after us, so let it
* go again. */
Log2 (("%s: returning clipboard ownership to the X11\n",
/* VBox thinks it currently owns the clipboard, so we must notify it
* as soon as we know what formats X11 has to offer. */
pCtx->notifyVBox = true;
pCtx->fOwnsClipboard = false;
}
LogFlowFunc(("returning\n"));
}
/**
* VBox is taking possession of the shared clipboard.
*
* @param u32Formats Clipboard formats the guest is offering
* @note X11 backend code
*/
{
/*
* Immediately return if we are not connected to the host X server.
*/
if (!g_fHaveX11)
return;
/* This must be freed by the worker callback */
{
}
}
/** Worker function for VBoxX11ClipboardReadX11Data which runs on the event
* thread. */
XtIntervalId * /* interval */)
{
int rc = VINF_SUCCESS;
/* Set this to start with, just in case */
/* Do not continue if we already own the clipboard */
if (pCtx->fOwnsClipboard == true)
else
{
/*
* VBox wants to read data in the given format.
*/
{
/* VBox thinks we have data and we don't */
rc = VERR_NO_DATA;
else
/* Send out a request for the data to the current clipboard
* owner */
}
else
}
if (RT_FAILURE(rc))
{
/* The clipboard callback was never scheduled, so we must signal
* that the request processing is finished ourselves. */
}
}
/**
* Called when VBox wants to read the X11 clipboard.
*
* @param pClient Context information about the guest VM
* @param u32Format The format that the guest would like to receive the data in
* @param pv Where to write the data to
* @param cb The size of the buffer to write the data to
* @param pcbActual Where to write the actual size of the written data
* @note X11 backend code
*/
{
/* Initially set the size of the data read to zero in case we fail
* somewhere or X11 is not available. */
*pcbActual = 0;
/*
* Immediately return if we are not connected to the host X server.
*/
if (!g_fHaveX11)
return VINF_SUCCESS;
/* The worker function will signal this when it has finished. */
if (RT_SUCCESS(rc))
{
/* We use this to schedule a worker function on the event thread. */
if (RT_SUCCESS(rc))
}
return rc;
}
#ifdef TESTCASE
#include <iprt/initterm.h>
enum { MAX_ATTEMPTS = 10 };
static VBOXCLIPBOARDCONTEXTX11 *g_pCtxTestX11;
static VBOXCLIPBOARDCONTEXTX11 *g_pCtxTestVBox;
/** Quick Utf16 string class, initialised from a Utf8 string */
class testUtf16String
{
/** Stores the Utf16 data. NULL if something went wrong. */
/** Stores the size in bytes of the data. 0 if something went wrong. */
public:
/** Constructor
* @param aString the Utf8 representation of the string data
*/
testUtf16String(const char *aString)
{
if (RT_FAILURE(rc))
{
mSize = 0;
return;
}
}
/** Destructor */
{
}
/** Getter for the data */
/** Getter for the data size */
};
/** Parameters for a test run. */
static struct testData
{
/** The string data we are offering. */
/** The size of the buffer to receive the request data */
/** The format we request the data in */
/** Getter for the Utf16 string data size in bytes */
/** Is the test expected to produce a buffer overflow? */
} g_sTestData[] =
{
{ "Hello world\r\n", 256 },
{ "Goodbye world", 28 },
{ "", 2 },
/* This should produce a buffer overflow */
{ "Goodbye world", 27 },
};
/** Which line of the table above are we currently testing? */
/** Are we doing a timeout test? Ugly, but whatever. */
static bool g_testTimeout = false;
/** @todo the current code can't test the following conditions:
* * the X11 clipboard offers non-Utf8 text
* * the X11 clipboard offers invalid Utf8 text
*/
/** @todo stress test our context store code? Probably not important... */
{
/* This should only ever be called for the second "test" clipboard. */
/* Magic cookie */, VERR_WRONG_ORDER);
/* Timeout test hack */
if (g_testTimeout)
{
RTThreadSleep(200);
/* Try to return data after we have been timed out. */
*pcb = sizeof("Scribblings");
LogFlowFunc(("sleep finished, returning\n"));
return VINF_SUCCESS;
}
/* Sanity. */
LogFlowFunc(("returning\n"));
return VINF_SUCCESS;
}
/** As long as our test is running, we grab back the clipboard if X11 ever
* tries to take it away. I hope we don't end up with a tug of war... */
{
}
/** Initial tests that can be done while the clipboard contents are still
* invalid.
* @returns boolean success value
* @note prints status information to stdout
*/
bool testInvalid(void)
{
bool fSuccess = true;
char pc[256];
RTPrintf("tstClipboardX11: TESTING a request for an invalid data format\n");
if (rc != VERR_NOT_IMPLEMENTED)
fSuccess = false;
RTPrintf("tstClipboardX11: TESTING a request for data from an empty clipboard\n");
0);
if (rc != VERR_NO_DATA)
fSuccess = false;
return fSuccess;
}
/** Tests an entry in the table above.
* @returns boolean success value
* @note prints status information to stdout
*/
{
bool fSuccess = false;
char pc[256];
for (int i = 0; (i < MAX_ATTEMPTS) && !fSuccess; ++i)
{
&cbActual);
/* Did we expect and get an overflow? */
if ( RT_SUCCESS(rc)
fSuccess = true;
/* Did we expect a string and get it? */
else if ( RT_SUCCESS(rc)
fSuccess = true;
else
RTThreadSleep(50);
if (fSuccess)
RTPrintf("text %ls, %sretval %Rrc - SUCCESS\n",
: "", rc);
else
RTPrintf("text %ls, retval %Rrc, attempt %d of %d\n",
}
if (!fSuccess)
RTPrintf("FAILURE. Last string obtained was %.*lS\n",
return fSuccess;
}
int main()
{
int rc = VINF_SUCCESS;
int status = 0;
/* We can't test anything without an X session, so just return success
* in that case. */
if (!RTEnvGet("DISPLAY"))
return 0;
RTR3Init();
bool fSuccess = false;
/* Test a request for an invalid data format and data from an empty
* clipboard */
for (unsigned i = 0; (i < MAX_ATTEMPTS) && !fSuccess; ++i)
{
fSuccess = testInvalid();
if (!fSuccess)
{
RTThreadSleep(50);
}
}
if (!fSuccess)
status = 1;
/* Claim the clipboard and make sure we get it */
for (unsigned i = 0;
i < 255
++i)
RTThreadSleep(50);
/* Do general purpose clipboard tests */
for (int i = 0; i < 2; ++i)
{
g_testUtf8 = (i == 0);
g_testCText = (i == 1);
RTPrintf("tstClipboardX11: TESTING sending and receiving of %s\n",
i == 0 ? "Utf-8" : "COMPOUND TEXT");
{
status = 1;
}
}
/* Finally test timeouts. */
RTPrintf("tstClipboardX11: TESTING the clipboard timeout\n");
rc = VINF_SUCCESS;
g_testTimeout = true;
{
char pc[256];
|| (rc == VERR_TIMEOUT)
|| (rc == VERR_NO_DATA),
("rc = %Rrc\n", rc));
if (rc == VERR_TIMEOUT)
RTPrintf("SUCCESS\n");
else
{
RTThreadSleep(50);
}
}
if (rc != VERR_TIMEOUT)
status = 1;
return status;
}
#endif /* TESTCASE defined */