Index: /trunk/include/VBox/GuestHost/SharedClipboard.h
===================================================================
--- /trunk/include/VBox/GuestHost/SharedClipboard.h	(revision 19841)
+++ /trunk/include/VBox/GuestHost/SharedClipboard.h	(revision 19842)
@@ -37,5 +37,9 @@
 enum {
     /** The number of milliseconds before the clipboard times out. */
+#ifndef TESTCASE
     CLIPBOARD_TIMEOUT = 5000
+#else
+    CLIPBOARD_TIMEOUT = 1
+#endif
 };
 
@@ -48,22 +52,27 @@
 typedef struct _CLIPBACKEND CLIPBACKEND;
 
+/** Opaque data structure used for asynchronously completing requests by VBox
+ * to read the X11 clipboard data. */
+struct _CLIPREADX11CBCONTEXT;
+typedef struct _CLIPREADX11CBCONTEXT CLIPREADX11CBCONTEXT;
+
 /* APIs exported by the X11 backend */
-extern CLIPBACKEND *VBoxX11ClipboardConstructX11
-                                        (VBOXCLIPBOARDCONTEXT *pFrontend);
-extern void VBoxX11ClipboardDestructX11(CLIPBACKEND *pBackend);
-extern int VBoxX11ClipboardStartX11(CLIPBACKEND *pBackend);
-extern int VBoxX11ClipboardStopX11(CLIPBACKEND *pBackend);
-extern void VBoxX11ClipboardAnnounceVBoxFormat(CLIPBACKEND
-                                               *pBackend, uint32_t u32Formats);
-extern int VBoxX11ClipboardReadX11Data(CLIPBACKEND *pBackend,
-                                       uint32_t u32Format,
-                                       void *pv, uint32_t cb,
-                                       uint32_t *pcbActual);
+extern CLIPBACKEND *ClipConstructX11(VBOXCLIPBOARDCONTEXT *pFrontend);
+extern void ClipDestructX11(CLIPBACKEND *pBackend);
+extern int ClipStartX11(CLIPBACKEND *pBackend);
+extern int ClipStopX11(CLIPBACKEND *pBackend);
+extern void ClipAnnounceFormatToX11(CLIPBACKEND *pBackend,
+                                    uint32_t u32Formats);
+extern int ClipRequestDataFromX11(CLIPBACKEND *pBackend, uint32_t u32Format,
+                                  void *pv, uint32_t cb,
+                                  CLIPREADX11CBCONTEXT *pCtx);
 
 /* APIs exported by the X11/VBox frontend */
-extern int VBoxX11ClipboardReadVBoxData(VBOXCLIPBOARDCONTEXT *pCtx,
+extern int ClipRequestDataForX11(VBOXCLIPBOARDCONTEXT *pCtx,
                                         uint32_t u32Format, void **ppv,
                                         uint32_t *pcb);
-extern void VBoxX11ClipboardReportX11Formats(VBOXCLIPBOARDCONTEXT *pCtx,
+extern void ClipReportX11Formats(VBOXCLIPBOARDCONTEXT *pCtx,
                                              uint32_t u32Formats);
+extern void ClipCompleteDataRequestFromX11(CLIPREADX11CBCONTEXT *pCtx, int rc,
+                                           uint32_t cbActual);
 #endif  /* ___GUESTHOST_VBOXCLIPBOARD__H */
Index: /trunk/src/VBox/GuestHost/SharedClipboard/x11-clipboard.cpp
===================================================================
--- /trunk/src/VBox/GuestHost/SharedClipboard/x11-clipboard.cpp	(revision 19841)
+++ /trunk/src/VBox/GuestHost/SharedClipboard/x11-clipboard.cpp	(revision 19842)
@@ -297,5 +297,5 @@
 {
     uint32_t u32VBoxFormats = clipVBoxFormatForX11Format(pCtx->X11TextFormat);
-    VBoxX11ClipboardReportX11Formats(pCtx->pFrontend, u32VBoxFormats);
+    ClipReportX11Formats(pCtx->pFrontend, u32VBoxFormats);
 }
 
@@ -381,5 +381,6 @@
         /* VBox raced us and we lost.  So we don't want to report anything. */
         changed = false;
-    else if (*atomType == XT_CONVERT_FAIL)  /* timeout */
+    else if (   (*atomType == XT_CONVERT_FAIL)  /* timeout */
+             || !pTargets  /* Conversion failed */)
         clipResetX11Formats(pCtx);
     else
@@ -558,5 +559,5 @@
  * @note  X11 backend code
  */
-CLIPBACKEND *VBoxX11ClipboardConstructX11(VBOXCLIPBOARDCONTEXT *pFrontend)
+CLIPBACKEND *ClipConstructX11(VBOXCLIPBOARDCONTEXT *pFrontend)
 {
     int rc;
@@ -589,5 +590,5 @@
  * @note  X11 backend code
  */
-void VBoxX11ClipboardDestructX11(CLIPBACKEND *pCtx)
+void ClipDestructX11(CLIPBACKEND *pCtx)
 {
     /*
@@ -606,5 +607,5 @@
  * Announce to the X11 backend that we are ready to start.
  */
-int VBoxX11ClipboardStartX11(CLIPBACKEND *pCtx)
+int ClipStartX11(CLIPBACKEND *pCtx)
 {
     int rc = VINF_SUCCESS;
@@ -647,5 +648,5 @@
  *        to return and will not be able to terminate.
  */
-int VBoxX11ClipboardStopX11(CLIPBACKEND *pCtx)
+int ClipStopX11(CLIPBACKEND *pCtx)
 {
     int rc, rcThread;
@@ -719,12 +720,6 @@
 }
 
-/** 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.
+/** This is a wrapper around ClipRequestDataForX11 that will cache the
+ * data returned.
  */
 static int clipReadVBoxClipboard(CLIPBACKEND *pCtx, uint32_t u32Format,
@@ -737,5 +732,5 @@
     {
         if (pCtx->pvUnicodeCache == NULL)
-            rc = VBoxX11ClipboardReadVBoxData(pCtx->pFrontend, u32Format,
+            rc = ClipRequestDataForX11(pCtx->pFrontend, u32Format,
                                               &pCtx->pvUnicodeCache,
                                               &pCtx->cbUnicodeCache);
@@ -749,5 +744,5 @@
     }
     else
-        rc = VBoxX11ClipboardReadVBoxData(pCtx->pFrontend, u32Format,
+        rc = ClipRequestDataForX11(pCtx->pFrontend, u32Format,
                                           ppv, pcb);
     LogFlowFunc(("returning %Rrc\n", rc));
@@ -964,5 +959,6 @@
                                                       *atomTarget);
     CLIPFORMAT format = clipRealFormatForX11Format(x11Format);
-    if ((format == UTF8) || (format == CTEXT))
+    if (   ((format == UTF8) || (format == CTEXT))
+        && (pCtx->vboxFormats & VBOX_SHARED_CLIPBOARD_FMT_UNICODETEXT))
     {
         void *pv = NULL;
@@ -1084,5 +1080,5 @@
 
 /**
- * Worker function for VBoxX11ClipboardAnnounceVBoxFormat which runs on the
+ * Worker function for ClipAnnounceFormatToX11 which runs on the
  * event thread.
  * @param pUserData  Pointer to a CLIPNEWVBOXFORMATS structure containing
@@ -1113,5 +1109,5 @@
  * @note  X11 backend code
  */
-void VBoxX11ClipboardAnnounceVBoxFormat(CLIPBACKEND *pCtx,
+void ClipAnnounceFormatToX11(CLIPBACKEND *pCtx,
                                         uint32_t u32Formats)
 {
@@ -1357,15 +1353,10 @@
      * a request for the clipboard contents) */
     unsigned mSize;
-    /** The size of the X11 clipboard data written to the buffer (valid
-     * during a request for the clipboard contents) */
-    uint32_t *mActual;
+    /** The opaque context structure for completing the request */
+    CLIPREADX11CBCONTEXT *mReadCtx;
     /** The format VBox would like the data in */
     uint32_t mFormat;
     /** The text format we requested from X11 if we requested text */
     CLIPX11FORMAT mTextFormat;
-    /** Return code for the request processing code */
-    int mRC;
-    /** Semaphore which is signalled when the request is completed */
-    RTSEMEVENT mSem;
     /** The clipboard context this request is associated with */
     CLIPBACKEND *mCtx;
@@ -1395,4 +1386,5 @@
     CLIPBACKEND *pCtx = pReq->mCtx;
     unsigned cbSrc = (*pcLen) * (*piFormat) / 8;
+    uint32_t cbActual = 0;
 
     if (pvSrc == NULL)
@@ -1409,5 +1401,5 @@
                 rc = clipCTextToWinTxt(widget, (unsigned char *)pvSrc, cbSrc,
                                        pReq->mBuffer, pReq->mSize,
-                                       pReq->mActual);
+                                       &cbActual);
                 break;
             case UTF8:
@@ -1419,9 +1411,9 @@
                     rc = clipUtf8ToWinTxt((const char *)pvSrc, cbSrc,
                                           pReq->mBuffer, pReq->mSize,
-                                          pReq->mActual);
+                                          &cbActual);
                 else
                     rc = clipLatin1ToWinTxt((char *) pvSrc, cbSrc,
                                             pReq->mBuffer, pReq->mSize,
-                                            pReq->mActual);
+                                            &cbActual);
                 break;
             }
@@ -1433,9 +1425,6 @@
         rc = VERR_NOT_IMPLEMENTED;
     XtFree((char *)pvSrc);
-    pReq->mRC = rc;
-    RTSemEventSignal(pReq->mSem);
-    /* Don't try to call VBox until after we have released the inbound call
-     * from VBox! */
-    /** @todo make this safer */
+    ClipCompleteDataRequestFromX11(pReq->mReadCtx, rc, cbActual);
+    RTMemFree(pReq);
     if (RT_SUCCESS(rc))
         /* The other end may want to cache the data, so pretend we have new
@@ -1448,5 +1437,5 @@
 }
 
-/** Worker function for VBoxX11ClipboardReadX11Data which runs on the event
+/** Worker function for ClipRequestDataFromX11 which runs on the event
  * thread. */
 static void vboxClipboardReadX11Worker(XtPointer pUserData,
@@ -1459,9 +1448,7 @@
 
     int rc = VINF_SUCCESS;
-    /* Set this to start with, just in case */
-    *pReq->mActual = 0;
     /* Do not continue if we already own the clipboard */
     if (pCtx->fOwnsClipboard == true)
-        rc = VERR_NO_DATA;  /** @todo should this be VINF? */
+        rc = VERR_TIMEOUT;
     else
     {
@@ -1490,8 +1477,8 @@
     if (RT_FAILURE(rc))
     {
-        pReq->mRC = rc;
         /* The clipboard callback was never scheduled, so we must signal
-         * that the request processing is finished ourselves. */
-        RTSemEventSignal(pReq->mSem);
+         * that the request processing is finished and clean up ourselves. */
+        ClipCompleteDataRequestFromX11(pReq->mReadCtx, rc, 0);
+        RTMemFree(pReq);
     }
     LogFlowFunc(("status %Rrc\n", rc));
@@ -1501,4 +1488,5 @@
  * Called when VBox wants to read the X11 clipboard.
  *
+ * @returns iprt status code
  * @param  pCtx      Context data for the clipboard backend
  * @param  u32Format The format that the VBox would like to receive the data
@@ -1507,38 +1495,28 @@
  * @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
- */
-int VBoxX11ClipboardReadX11Data(CLIPBACKEND *pCtx,
-                                uint32_t u32Format, void *pv, uint32_t cb,
-                                uint32_t *pcbActual)
-{
-    /* Initially set the size of the data read to zero in case we fail
-     * somewhere or X11 is not available. */
-    *pcbActual = 0;
-
+ * @note   We allocate a request structure which must be freed by the worker
+ */
+int ClipRequestDataFromX11(CLIPBACKEND *pCtx, uint32_t u32Format, void *pv,
+                           uint32_t cb, CLIPREADX11CBCONTEXT *pReadCtx)
+{
     /*
      * Immediately return if we are not connected to the X server.
      */
     if (!pCtx->fHaveX11)
-        return VINF_SUCCESS;
-
-    CLIPREADX11CBREQ req = { 0 };
-    req.mRC = VERR_WRONG_ORDER;
-    req.mBuffer = pv;
-    req.mSize = cb;
-    req.mActual = pcbActual;
-    req.mFormat = u32Format;
-    req.mCtx = pCtx;
-    /* The worker function will signal this when it has finished. */
-    int rc = RTSemEventCreate(&req.mSem);
-    if (RT_SUCCESS(rc))
-    {
+        return VERR_NO_DATA;
+    int rc = VINF_SUCCESS;
+    CLIPREADX11CBREQ *pReq = (CLIPREADX11CBREQ *)RTMemAllocZ(sizeof(*pReq));
+    if (!pReq)
+        rc = VERR_NO_MEMORY;
+    else
+    {
+        pReq->mBuffer = pv;
+        pReq->mSize = cb;
+        pReq->mReadCtx = pReadCtx;
+        pReq->mFormat = u32Format;
+        pReq->mCtx = pCtx;
         /* We use this to schedule a worker function on the event thread. */
         clipQueueToEventThread(pCtx->appContext, vboxClipboardReadX11Worker,
-                               (XtPointer) &req);
-        rc = RTSemEventWait(req.mSem, RT_INDEFINITE_WAIT);
-        if (RT_SUCCESS(rc))
-            rc = req.mRC;
-        RTSemEventDestroy(req.mSem);
+                               (XtPointer) pReq);
     }
     return rc;
@@ -1600,5 +1578,5 @@
     g_vboxDatapv = NULL;
     g_vboxDatacb = 0;
-    VBoxX11ClipboardAnnounceVBoxFormat(pCtx, 0);
+    ClipAnnounceFormatToX11(pCtx, 0);
 }
 
@@ -1622,5 +1600,5 @@
     g_vboxDatapv = pv;
     g_vboxDatacb = cb;
-    VBoxX11ClipboardAnnounceVBoxFormat(pCtx,
+    ClipAnnounceFormatToX11(pCtx,
                                        VBOX_SHARED_CLIPBOARD_FMT_UNICODETEXT);
     return VINF_SUCCESS;
@@ -1628,5 +1606,5 @@
 
 /* Return the data in the simulated VBox clipboard. */
-int VBoxX11ClipboardReadVBoxData(VBOXCLIPBOARDCONTEXT *pCtx,
+int ClipRequestDataForX11(VBOXCLIPBOARDCONTEXT *pCtx,
                                  uint32_t u32Format, void **ppv,
                                  uint32_t *pcb)
@@ -1792,4 +1770,6 @@
 static unsigned long g_cSelData = 0;
 static int g_selFormat = 0;
+static bool g_fTargetsTimeout = false;
+static bool g_fTargetsFailure = false;
 
 void XtGetSelectionValue(Widget widget, Atom selection, Atom target,
@@ -1815,7 +1795,10 @@
     if (target == clipGetAtom(NULL, "TARGETS"))
     {
-        pValue = (XtPointer) RTMemDup(&g_selTarget, sizeof(g_selTarget));
-        type = XA_ATOM;
-        count = 1;
+        if (g_fTargetsFailure)
+            pValue = NULL;
+        else
+            pValue = (XtPointer) RTMemDup(&g_selTarget, sizeof(g_selTarget));
+        type = g_fTargetsTimeout ? XT_CONVERT_FAIL : XA_ATOM;
+        count = g_fTargetsFailure ? 0 : 1;
         format = 32;
     }
@@ -1840,5 +1823,5 @@
 static uint32_t g_fX11Formats = 0;
 
-void VBoxX11ClipboardReportX11Formats(VBOXCLIPBOARDCONTEXT* pCtx,
+void ClipReportX11Formats(VBOXCLIPBOARDCONTEXT* pCtx,
                                       uint32_t u32Formats)
 {
@@ -1923,4 +1906,13 @@
         g_pfnSelLose(TEST_WIDGET, &clipAtom);
     g_ownsSel = false;
+    g_fTargetsTimeout = false;
+    g_fTargetsFailure = false;
+}
+
+/* Configure if and how the X11 TARGETS clipboard target will fail */
+static void clipSetTargetsFailure(bool fTimeout, bool fFailure)
+{
+    g_fTargetsTimeout = fTimeout;
+    g_fTargetsFailure = fFailure;
 }
 
@@ -1959,4 +1951,17 @@
         RTMemFree(*list);
     RTMemFree(list);
+}
+
+struct _CLIPREADX11CBCONTEXT
+{
+    int rc;
+    uint32_t cbActual;
+};
+
+void ClipCompleteDataRequestFromX11(CLIPREADX11CBCONTEXT *pCtx, int rc,
+                                    uint32_t cbActual)
+{
+    pCtx->rc = rc;
+    pCtx->cbActual = cbActual;
 }
 
@@ -1979,8 +1984,10 @@
     {
         char pc[MAX_BUF_SIZE];
-        uint32_t cbActual;
-        int rc = VBoxX11ClipboardReadX11Data(pCtx,
-                                     VBOX_SHARED_CLIPBOARD_FMT_UNICODETEXT,
-                                     (void *) pc, cbBuf, &cbActual);
+        CLIPREADX11CBCONTEXT readCtx;
+        uint32_t cbActual = 0;
+        ClipRequestDataFromX11(pCtx, VBOX_SHARED_CLIPBOARD_FMT_UNICODETEXT,
+                               (void *) pc, cbBuf, &readCtx);
+        int rc = readCtx.rc;
+        cbActual = readCtx.cbActual;
         if (rc != rcExp)
             RTPrintf("Wrong return code, expected %Rrc, got %Rrc\n", rcExp,
@@ -2035,8 +2042,10 @@
     {
         char pc[MAX_BUF_SIZE];
-        uint32_t cbActual;
-        int rc = VBoxX11ClipboardReadX11Data(pCtx,
-                                     VBOX_SHARED_CLIPBOARD_FMT_UNICODETEXT,
-                                     (void *) pc, cbBuf, &cbActual);
+        CLIPREADX11CBCONTEXT readCtx;
+        uint32_t cbActual = 0;
+        ClipRequestDataFromX11(pCtx, VBOX_SHARED_CLIPBOARD_FMT_UNICODETEXT,
+                               (void *) pc, cbBuf, &readCtx);
+        int rc = readCtx.rc;
+        cbActual = readCtx.cbActual;
         if (rc != rcExp)
             RTPrintf("Wrong return code, expected %Rrc, got %Rrc\n", rcExp,
@@ -2133,9 +2142,9 @@
 {
     RTR3Init();
-    CLIPBACKEND *pCtx = VBoxX11ClipboardConstructX11(NULL);
+    CLIPBACKEND *pCtx = ClipConstructX11(NULL);
     unsigned cErrs = 0;
     char pc[MAX_BUF_SIZE];
     uint32_t cbActual;
-    int rc = VBoxX11ClipboardStartX11(pCtx);
+    int rc = ClipStartX11(pCtx);
     AssertRCReturn(rc, 1);
 
@@ -2263,7 +2272,9 @@
     clipSetSelectionValues("UTF8_STRING", XA_STRING, NULL,
                            0, 8);
-    rc = VBoxX11ClipboardReadX11Data(pCtx,
-                                     VBOX_SHARED_CLIPBOARD_FMT_UNICODETEXT,
-                                     (void *) pc, sizeof(pc), &cbActual);
+    CLIPREADX11CBCONTEXT readCtx;
+    ClipRequestDataFromX11(pCtx, VBOX_SHARED_CLIPBOARD_FMT_UNICODETEXT,
+                           (void *) pc, sizeof(pc), &readCtx);
+    rc = readCtx.rc;
+    cbActual = readCtx.cbActual;
     if (rc != VERR_NO_DATA)
     {
@@ -2274,9 +2285,42 @@
     /*** request for an invalid VBox format from X11 ***/
     RTPrintf(TEST_NAME ": TESTING a request for an invalid VBox format from X11\n");
-    rc = VBoxX11ClipboardReadX11Data(pCtx, 0xffff, (void *) pc,
-                                     sizeof(pc), &cbActual);
+    ClipRequestDataFromX11(pCtx, 0xffff, (void *) pc, sizeof(pc), &readCtx);
+    rc = readCtx.rc;
+    cbActual = readCtx.cbActual;
     if (rc != VERR_NOT_IMPLEMENTED)
     {
         RTPrintf("Returned %Rrc instead of VERR_NOT_IMPLEMENTED\n", rc);
+        ++cErrs;
+    }
+
+    /*** Targets timeout from X11 ***/
+    RTPrintf(TEST_NAME ": TESTING X11 targets timeout\n");
+    clipSetSelectionValues("UTF8_STRING", XA_STRING, "hello world",
+                           sizeof("hello world"), 8);
+    clipSetTargetsFailure(true, false);
+    if (!clipPollTargets())
+    {
+        RTPrintf("Failed to poll for targets\n");
+        ++cErrs;
+    }
+    else if (clipQueryFormats() != 0)
+    {
+        RTPrintf("Wrong targets reported: %02X\n", clipQueryFormats());
+        ++cErrs;
+    }
+
+    /*** Targets failure from X11 ***/
+    RTPrintf(TEST_NAME ": TESTING X11 targets conversion failure\n");
+    clipSetSelectionValues("UTF8_STRING", XA_STRING, "hello world",
+                           sizeof("hello world"), 8);
+    clipSetTargetsFailure(false, true);
+    if (!clipPollTargets())
+    {
+        RTPrintf("Failed to poll for targets\n");
+        ++cErrs;
+    }
+    else if (clipQueryFormats() != 0)
+    {
+        RTPrintf("Wrong targets reported: %02X\n", clipQueryFormats());
         ++cErrs;
     }
@@ -2360,4 +2404,12 @@
     if (!testStringFromVBoxFailed(pCtx, "UTF8_STRING"))
         ++cErrs;
+
+    /*** An unknown VBox format ***/
+    RTPrintf(TEST_NAME ": TESTING reading an unknown VBox format\n");
+    clipSetVBoxUtf16(pCtx, VINF_SUCCESS, "", 2);
+    ClipAnnounceFormatToX11(pCtx, 0xa0000);
+    if (!testStringFromVBoxFailed(pCtx, "UTF8_STRING"))
+        ++cErrs;
+
     if (cErrs > 0)
         RTPrintf("Failed with %u error(s)\n", cErrs);
@@ -2379,5 +2431,5 @@
 #define TEST_NAME "tstClipboardX11Smoke"
 
-int VBoxX11ClipboardReadVBoxData(VBOXCLIPBOARDCONTEXT *pCtx,
+int ClipRequestDataForX11(VBOXCLIPBOARDCONTEXT *pCtx,
                                  uint32_t u32Format, void **ppv,
                                  uint32_t *pcb)
@@ -2386,6 +2438,10 @@
 }
 
-void VBoxX11ClipboardReportX11Formats(VBOXCLIPBOARDCONTEXT *pCtx,
+void ClipReportX11Formats(VBOXCLIPBOARDCONTEXT *pCtx,
                                       uint32_t u32Formats)
+{}
+
+void ClipCompleteDataRequestFromX11(CLIPREADX11CBCONTEXT *pCtx, int rc,
+                                    uint32_t cbActual)
 {}
 
@@ -2402,13 +2458,13 @@
     }
     RTPrintf(TEST_NAME ": TESTING\n");
-    CLIPBACKEND *pCtx = VBoxX11ClipboardConstructX11(NULL);
+    CLIPBACKEND *pCtx = ClipConstructX11(NULL);
     AssertReturn(pCtx, 1);
-    rc = VBoxX11ClipboardStartX11(pCtx);
+    rc = ClipStartX11(pCtx);
     AssertRCReturn(rc, 1);
     /* Give the clipboard time to synchronise. */
     RTThreadSleep(500);
-    rc = VBoxX11ClipboardStopX11(pCtx);
+    rc = ClipStopX11(pCtx);
     AssertRCReturn(rc, 1);
-    VBoxX11ClipboardDestructX11(pCtx);
+    ClipDestructX11(pCtx);
     return 0;
 }
Index: /trunk/src/VBox/HostServices/SharedClipboard/Makefile.kmk
===================================================================
--- /trunk/src/VBox/HostServices/SharedClipboard/Makefile.kmk	(revision 19841)
+++ /trunk/src/VBox/HostServices/SharedClipboard/Makefile.kmk	(revision 19842)
@@ -69,4 +69,32 @@
 	-framework ApplicationServices -install_name $(VBOX_DYLD_EXECUTABLE_PATH)/VBoxSharedClipboard.dylib
 
+ifdef VBOX_WITH_TESTCASES
+ ifndef VBOX_ONLY_ADDITIONS
+#
+# Case conversion testcase.
+#
+  if1of ($(KBUILD_TARGET),freebsd linux netbsd openbsd solaris)
+   PROGRAMS += tstClipboardX11-2
+   tstClipboardX11-2_TEMPLATE = VBOXR3TSTEXE
+   tstClipboardX11-2_DEFS     = VBOX_WITH_HGCM TESTCASE
+   tstClipboardX11-2_SOURCES  = x11-clipboard.cpp
+   tstClipboardX11-2_LIBS     = $(LIB_RUNTIME)
+
+# Set this in LocalConfig.kmk if you are working on the X11 clipboard service
+# to automatically run the testcase at build time.
+   ifdef VBOX_RUN_X11_CLIPBOARD_TEST
+    ifndef VBOX_ONLY_SDK
+     TESTING  += $(PATH_tstClipboardX11-2)/tstClipboardX11-2.run
+     OTHERS += $(PATH_tstClipboardX11-2)/tstClipboardX11-2.run
+$$(PATH_tstClipboardX11-2)/tstClipboardX11-2.run: \
+        $$(INSTARGET_tstClipboardX11-2)
+	export VBOX_LOG_DEST=nofile; $(INSTARGET_tstClipboardX11-2) quiet
+	$(QUIET)$(APPEND) -t "$@" "done"
+    endif
+   endif
+
+  endif # 1of ($(KBUILD_TARGET),freebsd linux netbsd openbsd solaris)
+ endif # ! VBOX_ONLY_ADDITIONS
+endif # VBOX_WITH_TESTCASES
+
 include $(KBUILD_PATH)/subfooter.kmk
-
Index: /trunk/src/VBox/HostServices/SharedClipboard/x11-clipboard.cpp
===================================================================
--- /trunk/src/VBox/HostServices/SharedClipboard/x11-clipboard.cpp	(revision 19841)
+++ /trunk/src/VBox/HostServices/SharedClipboard/x11-clipboard.cpp	(revision 19842)
@@ -35,16 +35,6 @@
 #include "VBoxClipboard.h"
 
-/** A request for clipboard data from VBox */
-typedef struct _VBOXCLIPBOARDREQFROMVBOX
-{
-    /** Data received */
-    void *pv;
-    /** The size of the data */
-    uint32_t cb;
-    /** Format of the data */
-    uint32_t format;
-    /** A semaphore for waiting for the data */
-    RTSEMEVENT finished;
-} VBOXCLIPBOARDREQFROMVBOX;
+struct _VBOXCLIPBOARDREQFROMVBOX;
+typedef struct _VBOXCLIPBOARDREQFROMVBOX VBOXCLIPBOARDREQFROMVBOX;
 
 /** Global context information used by the host glue for the X11 clipboard
@@ -73,5 +63,223 @@
 };
 
-static int vboxClipboardWaitForReq(VBOXCLIPBOARDCONTEXT *pCtx,
+/**
+ * Report formats available in the X11 clipboard to VBox.
+ * @param  pCtx        Opaque context pointer for the glue code
+ * @param  u32Formats  The formats available
+ * @note  Host glue code
+ */
+void ClipReportX11Formats(VBOXCLIPBOARDCONTEXT *pCtx,
+                                      uint32_t u32Formats)
+{
+    LogFlowFunc(("called.  pCtx=%p, u32Formats=%02X\n", pCtx, u32Formats));
+    vboxSvcClipboardReportMsg(pCtx->pClient,
+                              VBOX_SHARED_CLIPBOARD_HOST_MSG_FORMATS,
+                              u32Formats);
+}
+
+/**
+ * Initialise the host side of the shared clipboard.
+ * @note  Host glue code
+ */
+int vboxClipboardInit (void)
+{
+    return VINF_SUCCESS;
+}
+
+/**
+ * Terminate the host side of the shared clipboard.
+ * @note  host glue code
+ */
+void vboxClipboardDestroy (void)
+{
+
+}
+
+/**
+ * Connect a guest to the shared clipboard.
+ * @note  host glue code
+ * @note  on the host, we assume that some other application already owns
+ *        the clipboard and leave ownership to X11.
+ */
+int vboxClipboardConnect (VBOXCLIPBOARDCLIENTDATA *pClient)
+{
+    int rc = VINF_SUCCESS;
+    CLIPBACKEND *pBackend = NULL;
+
+    LogRel(("Starting host clipboard service\n"));
+    VBOXCLIPBOARDCONTEXT *pCtx =
+        (VBOXCLIPBOARDCONTEXT *) RTMemAllocZ(sizeof(VBOXCLIPBOARDCONTEXT));
+    if (!pCtx)
+        rc = VERR_NO_MEMORY;
+    else
+    {
+        RTCritSectInit(&pCtx->clipboardMutex);
+        pBackend = ClipConstructX11(pCtx);
+        if (pBackend == NULL)
+            rc = VERR_NO_MEMORY;
+        else
+        {
+            pCtx->pBackend = pBackend;
+            pClient->pCtx = pCtx;
+            pCtx->pClient = pClient;
+            rc = ClipStartX11(pBackend);
+        }
+        if (RT_FAILURE(rc) && pBackend)
+            ClipStopX11(pCtx->pBackend);
+        if (RT_FAILURE(rc))
+            RTCritSectDelete(&pCtx->clipboardMutex);
+    }
+    if (RT_FAILURE(rc))
+    {
+        RTMemFree(pCtx);
+        LogRel(("Failed to initialise the shared clipboard\n"));
+    }
+    LogFlowFunc(("returning %Rrc\n", rc));
+    return rc;
+}
+
+/**
+ * Synchronise the contents of the host clipboard with the guest, called
+ * after a save and restore of the guest.
+ * @note  Host glue code
+ */
+int vboxClipboardSync (VBOXCLIPBOARDCLIENTDATA *pClient)
+{
+    /* Tell the guest we have no data in case X11 is not available.  If
+     * there is data in the host clipboard it will automatically be sent to
+     * the guest when the clipboard starts up. */
+    vboxSvcClipboardReportMsg (pClient,
+                               VBOX_SHARED_CLIPBOARD_HOST_MSG_FORMATS, 0);
+    return VINF_SUCCESS;
+}
+
+/**
+ * Shut down the shared clipboard service and "disconnect" the guest.
+ * @note  Host glue code
+ */
+void vboxClipboardDisconnect (VBOXCLIPBOARDCLIENTDATA *pClient)
+{
+    LogFlow(("vboxClipboardDisconnect\n"));
+
+    LogRel(("Stopping the host clipboard service\n"));
+    VBOXCLIPBOARDCONTEXT *pCtx = pClient->pCtx;
+    /* Drop the reference to the client, in case it is still there.  This
+     * will cause any outstanding clipboard data requests from X11 to fail
+     * immediately. */
+    pCtx->fShuttingDown = true;
+    /* If there is a currently pending request, release it immediately. */
+    vboxClipboardWriteData(pClient, NULL, 0, 0);
+    int rc = ClipStopX11(pCtx->pBackend);
+    /** @todo handle this slightly more reasonably, or be really sure
+     *        it won't go wrong. */
+    AssertRC(rc);
+    if (RT_SUCCESS(rc))  /* And if not? */
+    {
+        ClipDestructX11(pCtx->pBackend);
+        RTCritSectDelete(&pCtx->clipboardMutex);
+        RTMemFree(pCtx);
+    }
+}
+
+/**
+ * VBox is taking possession of the shared clipboard.
+ *
+ * @param pClient    Context data for the guest system
+ * @param u32Formats Clipboard formats the guest is offering
+ * @note  Host glue code
+ */
+void vboxClipboardFormatAnnounce (VBOXCLIPBOARDCLIENTDATA *pClient,
+                                  uint32_t u32Formats)
+{
+    LogFlowFunc(("called.  pClient=%p, u32Formats=%02X\n", pClient,
+                 u32Formats));
+    ClipAnnounceFormatToX11 (pClient->pCtx->pBackend, u32Formats);
+}
+
+struct _CLIPREADX11CBCONTEXT
+{
+    /** HGCM call data */
+    VBOXHGCMCALLHANDLE callHandle;
+    /** HGCM call parameters */
+    VBOXHGCMSVCPARM *paParms;
+};
+
+/**
+ * Called when VBox wants to read the X11 clipboard.
+ *
+ * @returns VINF_SUCCESS on successful completion
+ * @returns VINF_HGCM_ASYNC_EXECUTE if the operation will complete
+ *          asynchronously
+ * @returns iprt status code on failure
+ * @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   We always fail or complete asynchronously
+ * @note   On success allocates a CLIPREADX11CBCONTEXT structure which must be
+ *         freed in ClipCompleteDataRequestFromX11 when it is called back from
+ *         the backend code.
+ *         
+ */
+int vboxClipboardReadData (VBOXCLIPBOARDCLIENTDATA *pClient,
+                           uint32_t u32Format, void *pv, uint32_t cb,
+                           uint32_t *pcbActual)
+{
+    LogFlowFunc(("pClient=%p, u32Format=%02X, pv=%p, cb=%u, pcbActual=%p",
+                 pClient, u32Format, pv, cb, pcbActual));
+    
+    int rc = VINF_SUCCESS;
+    CLIPREADX11CBCONTEXT *pCtx
+            = (CLIPREADX11CBCONTEXT *)RTMemAlloc(sizeof(*pCtx));
+    if (!pCtx)
+        rc = VERR_NO_MEMORY;
+    if (RT_SUCCESS(rc))
+    {
+        pCtx->callHandle = pClient->asyncRead.callHandle;
+        pCtx->paParms = pClient->asyncRead.paParms;
+        rc = ClipRequestDataFromX11(pClient->pCtx->pBackend, u32Format, pv,
+                                    cb, pCtx);
+        if (RT_SUCCESS(rc))
+            rc = VINF_HGCM_ASYNC_EXECUTE;
+    }
+    LogFlowFunc(("returning %Rrc\n", rc));
+    return rc;
+}
+
+/**
+ * Complete a request from VBox for the X11 clipboard data.  The data should
+ * be written to the buffer provided in the initial request.
+ * @param  pCtx  request context information
+ * @param  rc    the completion status of the request
+ * @param  cbActual  on successful completion, the number of bytes of data
+ *                   actually written, on buffer overflow the size of the
+ *                   buffer needed, ignored otherwise
+ * @todo   change this to deal with the buffer issues rather than offloading
+ *         them onto the caller
+ */
+void ClipCompleteDataRequestFromX11(CLIPREADX11CBCONTEXT *pCtx, int rc,
+                                    uint32_t cbActual)
+{
+    vboxSvcClipboardCompleteReadData(pCtx->callHandle, pCtx->paParms, rc,
+                                 cbActual);
+    RTMemFree(pCtx);
+}
+
+/** A request for clipboard data from VBox */
+struct _VBOXCLIPBOARDREQFROMVBOX
+{
+    /** Data received */
+    void *pv;
+    /** The size of the data */
+    uint32_t cb;
+    /** Format of the data */
+    uint32_t format;
+    /** A semaphore for waiting for the data */
+    RTSEMEVENT finished;
+};
+
+/** Wait for clipboard data requested from VBox to arrive. */
+static int clipWaitForDataFromVBox(VBOXCLIPBOARDCONTEXT *pCtx,
                                    VBOXCLIPBOARDREQFROMVBOX *pReq,
                                    uint32_t u32Format)
@@ -104,5 +312,5 @@
 /** Post a request for clipboard data to VBox/the guest and wait for it to be
  * completed. */
-static int vboxClipboardPostAndWaitForReq(VBOXCLIPBOARDCONTEXT *pCtx,
+static int clipRequestDataFromVBox(VBOXCLIPBOARDCONTEXT *pCtx,
                                           VBOXCLIPBOARDREQFROMVBOX *pReq,
                                           uint32_t u32Format)
@@ -125,5 +333,5 @@
     RTCritSectLeave(&pCtx->clipboardMutex);
     if (RT_SUCCESS(rc))
-        rc = vboxClipboardWaitForReq(pCtx, pReq, u32Format);
+        rc = clipWaitForDataFromVBox(pCtx, pReq, u32Format);
     LogFlowFunc(("returning %Rrc\n", rc));
     return rc;
@@ -141,5 +349,5 @@
  * @note   Host glue code.
  */
-int VBoxX11ClipboardReadVBoxData (VBOXCLIPBOARDCONTEXT *pCtx,
+int ClipRequestDataForX11 (VBOXCLIPBOARDCONTEXT *pCtx,
                                    uint32_t u32Format, void **ppv,
                                    uint32_t *pcb)
@@ -158,6 +366,5 @@
     if (RT_SUCCESS(rc))
     {
-        rc = vboxClipboardPostAndWaitForReq(pCtx, &request,
-                                            u32Format);
+        rc = clipRequestDataFromVBox(pCtx, &request, u32Format);
         RTSemEventDestroy(request.finished);
     }
@@ -170,163 +377,4 @@
     if (RT_SUCCESS(rc))
         LogFlowFunc(("*ppv=%.*ls, *pcb=%u\n", *pcb / 2, *ppv, *pcb));
-    return rc;
-}
-
-/**
- * Report formats available in the X11 clipboard to VBox.
- * @param  pCtx        Opaque context pointer for the glue code
- * @param  u32Formats  The formats available
- * @note  Host glue code
- */
-void VBoxX11ClipboardReportX11Formats(VBOXCLIPBOARDCONTEXT *pCtx,
-                                      uint32_t u32Formats)
-{
-    LogFlowFunc(("called.  pCtx=%p, u32Formats=%02X\n", pCtx, u32Formats));
-    vboxSvcClipboardReportMsg(pCtx->pClient,
-                              VBOX_SHARED_CLIPBOARD_HOST_MSG_FORMATS,
-                              u32Formats);
-}
-
-/**
- * Initialise the host side of the shared clipboard.
- * @note  Host glue code
- */
-int vboxClipboardInit (void)
-{
-    return VINF_SUCCESS;
-}
-
-/**
- * Terminate the host side of the shared clipboard.
- * @note  host glue code
- */
-void vboxClipboardDestroy (void)
-{
-
-}
-
-/**
- * Connect a guest to the shared clipboard.
- * @note  host glue code
- * @note  on the host, we assume that some other application already owns
- *        the clipboard and leave ownership to X11.
- */
-int vboxClipboardConnect (VBOXCLIPBOARDCLIENTDATA *pClient)
-{
-    int rc = VINF_SUCCESS;
-    CLIPBACKEND *pBackend = NULL;
-
-    LogRel(("Starting host clipboard service\n"));
-    VBOXCLIPBOARDCONTEXT *pCtx =
-        (VBOXCLIPBOARDCONTEXT *) RTMemAllocZ(sizeof(VBOXCLIPBOARDCONTEXT));
-    if (!pCtx)
-        rc = VERR_NO_MEMORY;
-    else
-    {
-        RTCritSectInit(&pCtx->clipboardMutex);
-        pBackend = VBoxX11ClipboardConstructX11(pCtx);
-        if (pBackend == NULL)
-            rc = VERR_NO_MEMORY;
-        else
-        {
-            pCtx->pBackend = pBackend;
-            pClient->pCtx = pCtx;
-            pCtx->pClient = pClient;
-            rc = VBoxX11ClipboardStartX11(pBackend);
-        }
-        if (RT_FAILURE(rc) && pBackend)
-            VBoxX11ClipboardStopX11(pCtx->pBackend);
-        if (RT_FAILURE(rc))
-            RTCritSectDelete(&pCtx->clipboardMutex);
-    }
-    if (RT_FAILURE(rc))
-    {
-        RTMemFree(pCtx);
-        LogRel(("Failed to initialise the shared clipboard\n"));
-    }
-    LogFlowFunc(("returning %Rrc\n", rc));
-    return rc;
-}
-
-/**
- * Synchronise the contents of the host clipboard with the guest, called
- * after a save and restore of the guest.
- * @note  Host glue code
- */
-int vboxClipboardSync (VBOXCLIPBOARDCLIENTDATA *pClient)
-{
-    /* Tell the guest we have no data in case X11 is not available.  If
-     * there is data in the host clipboard it will automatically be sent to
-     * the guest when the clipboard starts up. */
-    vboxSvcClipboardReportMsg (pClient,
-                               VBOX_SHARED_CLIPBOARD_HOST_MSG_FORMATS, 0);
-    return VINF_SUCCESS;
-}
-
-/**
- * Shut down the shared clipboard service and "disconnect" the guest.
- * @note  Host glue code
- */
-void vboxClipboardDisconnect (VBOXCLIPBOARDCLIENTDATA *pClient)
-{
-    LogFlow(("vboxClipboardDisconnect\n"));
-
-    LogRel(("Stopping the host clipboard service\n"));
-    VBOXCLIPBOARDCONTEXT *pCtx = pClient->pCtx;
-    /* Drop the reference to the client, in case it is still there.  This
-     * will cause any outstanding clipboard data requests from X11 to fail
-     * immediately. */
-    pCtx->fShuttingDown = true;
-    /* If there is a currently pending request, release it immediately. */
-    vboxClipboardWriteData(pClient, NULL, 0, 0);
-    int rc = VBoxX11ClipboardStopX11(pCtx->pBackend);
-    /** @todo handle this slightly more reasonably, or be really sure
-     *        it won't go wrong. */
-    AssertRC(rc);
-    if (RT_SUCCESS(rc))  /* And if not? */
-    {
-        VBoxX11ClipboardDestructX11(pCtx->pBackend);
-        RTCritSectDelete(&pCtx->clipboardMutex);
-        RTMemFree(pCtx);
-    }
-}
-
-/**
- * VBox is taking possession of the shared clipboard.
- *
- * @param pClient    Context data for the guest system
- * @param u32Formats Clipboard formats the guest is offering
- * @note  Host glue code
- */
-void vboxClipboardFormatAnnounce (VBOXCLIPBOARDCLIENTDATA *pClient,
-                                  uint32_t u32Formats)
-{
-    LogFlowFunc(("called.  pClient=%p, u32Formats=%02X\n", pClient,
-                 u32Formats));
-    VBoxX11ClipboardAnnounceVBoxFormat (pClient->pCtx->pBackend, u32Formats);
-}
-
-/**
- * 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   Host glue code
- */
-int vboxClipboardReadData (VBOXCLIPBOARDCLIENTDATA *pClient,
-                           uint32_t u32Format, void *pv, uint32_t cb,
-                           uint32_t *pcbActual)
-{
-    LogFlowFunc(("pClient=%p, u32Format=%02X, pv=%p, cb=%u, pcbActual=%p",
-                 pClient, u32Format, pv, cb, pcbActual));
-    int rc = VBoxX11ClipboardReadX11Data(pClient->pCtx->pBackend, u32Format,
-                                         pv, cb, pcbActual);
-    LogFlowFunc(("returning %Rrc\n", rc));
-    if (RT_SUCCESS(rc))
-        LogFlowFunc(("*pv=%.*ls, *pcbActual=%u\n", *pcbActual / 2, pv,
-                     *pcbActual));
     return rc;
 }
@@ -365,6 +413,184 @@
         /* Signal that the request has been completed. */
         RTSemEventSignal(pReq->finished);
-    }
-    pCtx->pReq = NULL;
+        pCtx->pReq = NULL;
+    }
     RTCritSectLeave(&pCtx->clipboardMutex);
 }
+
+#ifdef TESTCASE
+#include <iprt/initterm.h>
+#include <iprt/stream.h>
+
+#define TEST_NAME "tstClipboardX11-2"
+
+struct _CLIPBACKEND
+{
+    uint32_t formats;
+    struct _READDATA
+    {
+        uint32_t format;
+        void *pv;
+        uint32_t cb;
+        CLIPREADX11CBCONTEXT *pCtx;
+        int rc;
+    } readData;
+    struct _COMPLETEREAD
+    {
+        int rc;
+        uint32_t cbActual;
+    } completeRead;
+    struct _WRITEDATA
+    {
+        void *pv;
+        uint32_t cb;
+        uint32_t format;
+        bool timeout;
+    } writeData;
+    struct _REPORTDATA
+    {
+        uint32_t format;
+    } reportData;
+};
+
+void vboxSvcClipboardReportMsg (VBOXCLIPBOARDCLIENTDATA *pClient, uint32_t u32Msg, uint32_t u32Formats)
+{
+    CLIPBACKEND *pBackend = pClient->pCtx->pBackend;
+    if (   (u32Msg == VBOX_SHARED_CLIPBOARD_HOST_MSG_READ_DATA)
+        && !pBackend->writeData.timeout)
+        vboxClipboardWriteData(pClient, pBackend->writeData.pv,
+                               pBackend->writeData.cb,
+                               pBackend->writeData.format);
+    else
+        return;
+}
+
+void vboxSvcClipboardCompleteReadData(VBOXHGCMCALLHANDLE callHandle, VBOXHGCMSVCPARM *paParms, int rc, uint32_t cbActual)
+{
+    CLIPBACKEND *pBackend = (CLIPBACKEND *)callHandle;
+    pBackend->completeRead.rc = rc;
+    pBackend->completeRead.cbActual = cbActual;
+}
+
+CLIPBACKEND *ClipConstructX11(VBOXCLIPBOARDCONTEXT *pFrontend)
+{
+    return (CLIPBACKEND *)RTMemAllocZ(sizeof(CLIPBACKEND));
+}
+
+void ClipDestructX11(CLIPBACKEND *pBackend)
+{
+    RTMemFree(pBackend);
+}
+
+int ClipStartX11(CLIPBACKEND *pBackend)
+{
+    return VINF_SUCCESS;
+}
+
+int ClipStopX11(CLIPBACKEND *pBackend)
+{
+    return VINF_SUCCESS;
+}
+
+void ClipAnnounceFormatToX11(CLIPBACKEND *pBackend,
+                                    uint32_t u32Formats)
+{
+    pBackend->formats = u32Formats;
+}
+
+extern int ClipRequestDataFromX11(CLIPBACKEND *pBackend, uint32_t u32Format,
+                                  void *pv, uint32_t cb,
+                                  CLIPREADX11CBCONTEXT *pCtx)
+{
+    pBackend->readData.format = u32Format;
+    pBackend->readData.pv = pv;
+    pBackend->readData.cb = cb;
+    pBackend->readData.pCtx = pCtx;
+    return pBackend->readData.rc;
+}
+
+int main()
+{
+    VBOXCLIPBOARDCLIENTDATA client;
+    unsigned cErrors = 0;
+    int rc = RTR3Init();
+    RTPrintf(TEST_NAME ": TESTING\n");
+    AssertRCReturn(rc, 1);
+    rc = vboxClipboardConnect(&client);
+    CLIPBACKEND *pBackend = client.pCtx->pBackend;
+    AssertRCReturn(rc, 1);
+    vboxClipboardFormatAnnounce(&client,
+                                VBOX_SHARED_CLIPBOARD_FMT_UNICODETEXT);
+    if (pBackend->formats != VBOX_SHARED_CLIPBOARD_FMT_UNICODETEXT)
+    {
+        RTPrintf(TEST_NAME ": vboxClipboardFormatAnnounce failed with VBOX_SHARED_CLIPBOARD_FMT_UNICODETEXT\n");
+        ++cErrors;
+    }
+    pBackend->readData.rc = VINF_SUCCESS;
+    client.asyncRead.callHandle = (VBOXHGCMCALLHANDLE)pBackend;
+    client.asyncRead.paParms = (VBOXHGCMSVCPARM *)&client;
+    uint32_t u32Dummy;
+    rc = vboxClipboardReadData(&client, VBOX_SHARED_CLIPBOARD_FMT_UNICODETEXT,
+                               &u32Dummy, 42, &u32Dummy);
+    if (rc != VINF_HGCM_ASYNC_EXECUTE)
+    {
+        RTPrintf(TEST_NAME ": vboxClipboardReadData returned %Rrc\n", rc);
+        ++cErrors;
+    }
+    else
+    {
+        if (   pBackend->readData.format !=
+                       VBOX_SHARED_CLIPBOARD_FMT_UNICODETEXT
+            || pBackend->readData.pv != &u32Dummy
+            || pBackend->readData.cb != 42)
+        {
+            RTPrintf(TEST_NAME ": format=%u, pv=%p, cb=%u\n",
+                     pBackend->readData.format, pBackend->readData.pv,
+                     pBackend->readData.cb);
+            ++cErrors;
+        }
+        else
+        {
+            ClipCompleteDataRequestFromX11(pBackend->readData.pCtx,
+                                           VERR_NO_DATA, 43);
+            if (   pBackend->completeRead.rc != VERR_NO_DATA
+                || pBackend->completeRead.cbActual != 43)
+            {
+                RTPrintf(TEST_NAME ": rc=%Rrc, cbActual=%u\n",
+                         pBackend->completeRead.rc,
+                         pBackend->completeRead.cbActual);
+                ++cErrors;
+            }
+        }
+    }
+    void *pv;
+    uint32_t cb;
+    pBackend->writeData.pv = (void *)"testing";
+    pBackend->writeData.cb = sizeof("testing");
+    pBackend->writeData.format = 1234;
+    pBackend->reportData.format = 4321;  /* XX this should be handled! */
+    rc = ClipRequestDataForX11(client.pCtx, 23, &pv, &cb);
+    if (   rc != VINF_SUCCESS
+        || strcmp((const char *)pv, "testing") != 0
+        || cb != sizeof("testing"))
+    {
+        RTPrintf("rc=%Rrc, pv=%p, cb=%u\n", rc, pv, cb);
+        ++cErrors;
+    }
+    else
+        RTMemFree(pv);
+    pBackend->writeData.timeout = true;
+    rc = ClipRequestDataForX11(client.pCtx, 23, &pv, &cb);
+    if (rc != VERR_TIMEOUT)
+    {
+        RTPrintf("rc=%Rrc\n", rc);
+        ++cErrors;
+    }
+    /* Data arriving after a timeout should *not* cause any segfaults or
+     * memory leaks.  Check with Valgrind! */
+    vboxClipboardWriteData(&client, (void *)"tested", sizeof("tested"), 999);
+    vboxClipboardDisconnect(&client);
+    if (cErrors > 0)
+        RTPrintf(TEST_NAME ": errors: %u\n", cErrors);
+    return cErrors > 0 ? 1 : 0;
+}
+#endif  /* TESTCASE */
