VirtualBox

Changeset 20552 in vbox for trunk/src/VBox


Ignore:
Timestamp:
Jun 14, 2009 4:34:30 PM (16 years ago)
Author:
vboxsync
Message:

Additions/x11/VBoxClient: switch the shared clipboard code to using the common code in GuestHost

Location:
trunk/src/VBox/Additions/x11/VBoxClient
Files:
2 edited

Legend:

Unmodified
Added
Removed
  • trunk/src/VBox/Additions/x11/VBoxClient/Makefile.kmk

    r18360 r20552  
    3434        main.cpp
    3535VBoxClient_SOURCES += \
     36        $(PATH_ROOT)/src/VBox/GuestHost/SharedClipboard/clipboard-helper.cpp \
     37        $(PATH_ROOT)/src/VBox/GuestHost/SharedClipboard/x11-clipboard.cpp \
    3638        clipboard.cpp
    3739VBoxClient_LIBPATH = \
  • trunk/src/VBox/Additions/x11/VBoxClient/clipboard.cpp

    r19800 r20552  
    2323*   Header Files                                                            *
    2424****************************************************************************/
    25 #include <VBox/HostServices/VBoxClipboardSvc.h>
    26 #include <VBox/log.h>
    2725#include <iprt/alloc.h>
    2826#include <iprt/asm.h>
     
    3129#include <iprt/mem.h>
    3230#include <iprt/string.h>
    33 #include <iprt/thread.h>
    3431#include <iprt/process.h>
    3532#include <iprt/semaphore.h>
    3633
    37 #include <X11/Xlib.h>
    38 #include <X11/Xatom.h>
    39 #include <X11/Intrinsic.h>
    40 #include <X11/Shell.h>
    41 #include <X11/X.h>
    42 #include <vector>
     34#include <VBox/log.h>
     35#include <VBox/HostServices/VBoxClipboardSvc.h>
     36#include <VBox/GuestHost/SharedClipboard.h>
    4337
    4438#include "VBoxClient.h"
    4539#include "clipboard.h"
    46 
    47 /** The formats which we support in the guest. These can be deactivated in order to test specific code paths. */
    48 #define USE_UTF16
    49 #define USE_UTF8
    50 #define USE_CTEXT
    5140
    5241/****************************************************************************
    5342*   Global Variables                                                        *
    5443****************************************************************************/
    55 /** The different clipboard formats which we support. */
    56 enum g_eClipboardFormat
    57 {
    58     INVALID = 0,
    59     TARGETS,
    60     CTEXT,
    61     UTF8,
    62     UTF16
    63 };
    64 
    65 /** The X11 clipboard uses several names for the same format. This structure maps an X11 name to a format. */
    66 typedef struct
    67 {
    68     Atom atom;
    69     g_eClipboardFormat format;
    70     unsigned hostFormat;
    71 } VBOXCLIPBOARDFORMAT;
    72 
    73 /** Does the host or the guest currently own the clipboard? */
    74 enum g_eClipboardOwner
    75 {
    76     NONE = 0,
    77     HOST,
    78     GUEST
    79 };
    8044
    8145/**
    8246 * Global clipboard context information.
    8347 */
    84 typedef struct
    85 {
    86     /** The Xt application context structure */
    87     XtAppContext appContext;
    88 
    89     /** We have a separate thread to wait for Window and Clipboard events */
    90     RTTHREAD thread;
    91     /** The Xt widget which we use as our clipboard client.  It is never made visible. */
    92     Widget widget;
    93 
    94     /** X11 atom refering to the clipboard: CLIPBOARD */
    95     Atom atomClipboard;
    96     /** X11 atom refering to the selection: PRIMARY */
    97     Atom atomPrimary;
    98     /** X11 atom refering to the clipboard: TARGETS */
    99     Atom atomTargets;
    100     /** X11 atom refering to the clipboard: MULTIPLE */
    101     Atom atomMultiple;
    102     /** X11 atom refering to the clipboard: TIMESTAMP */
    103     Atom atomTimestamp;
    104     /** X11 atom refering to the clipboard utf16 text format: text/plain;charset=ISO-10646-UCS-2 */
    105     Atom atomUtf16;
    106     /** X11 atom refering to the clipboard utf8 text format: UTF8_STRING */
    107     Atom atomUtf8;
    108     /** X11 atom refering to the native X11 clipboard text format: COMPOUND_TEXT */
    109     Atom atomCText;
    110 
    111     /** A list of the X11 formats which we support, mapped to our identifier for them, in the order
    112         we prefer to have them in. */
    113     std::vector<VBOXCLIPBOARDFORMAT> formatList;
    114 
    115     /** Does the host or the guest currently own the clipboard? */
    116     volatile enum g_eClipboardOwner eOwner;
    117 
    118     /** What is the best text format the guest has to offer?  INVALID for none. */
    119     g_eClipboardFormat guestTextFormat;
    120     /** Atom corresponding to the guest text format */
    121     Atom atomGuestTextFormat;
    122     /** What is the best bitmap format the guest has to offer?  INVALID for none. */
    123     g_eClipboardFormat guestBitmapFormat;
    124     /** Atom corresponding to the guest Bitmap format */
    125     Atom atomGuestBitmapFormat;
    126     /** What formats does the host have on offer? */
    127     int hostFormats;
    128     /** Windows caches the clipboard data it receives.  Since we have no way of knowing whether
    129         that data is still valid, we always send a "data changed" message after a successful
    130         transfer to invalidate the cache. */
    131     bool notifyHost;
    132 
    133     /** Since the clipboard data moves asynchronously, we use an event semaphore to wait for it. */
    134     RTSEMEVENT terminating;
    135 
    136     /** Format which we are reading from the guest clipboard (valid during a request for the
    137         guest clipboard) */
    138     g_eClipboardFormat requestGuestFormat;
    139     /** The guest buffer to write guest clipboard data to (valid during a request for the host
    140         clipboard) */
    141     void *requestBuffer;
    142     /** The size of the host buffer to write guest clipboard data to (valid during a request for
    143         the guest clipboard) */
    144     unsigned requestBufferSize;
    145     /** The size of the guest clipboard data written to the host buffer (valid during a request
    146         for the guest clipboard) */
    147     uint32_t *requestActualSize;
    148 
     48struct _VBOXCLIPBOARDCONTEXT
     49{
    14950    /** Client ID for the clipboard subsystem */
    15051    uint32_t client;
    151 } VBOXCLIPBOARDCONTEXT;
     52
     53    /** Pointer to the X11 clipboard backend */
     54    CLIPBACKEND *pBackend;
     55};
    15256
    15357/** Only one client is supported. There seems to be no need for more clients. */
     
    18387 *                    returned
    18488 */
    185 static int vboxClipboardReadHostData(uint32_t u32Format, void **ppv, uint32_t *pcb)
     89int ClipRequestDataForX11(VBOXCLIPBOARDCONTEXT *pCtx, uint32_t u32Format,
     90                          void **ppv, uint32_t *pcb)
    18691{
    18792    int rc = VINF_SUCCESS;
     
    229134}
    230135
    231 
    232 /**
    233  * Convert a Utf16 text with Linux EOLs to zero-terminated Utf16-LE with Windows EOLs, allocating
    234  * memory for the converted text.  Does no checking for validity.
    235  *
    236  * @returns VBox status code
    237  *
    238  * @param   pu16Src   Source Utf16 text to convert
    239  * @param   cwSrc     Size of the source text in 16 bit words
    240  * @retval  ppu16Dest Where to store the pointer to the converted text.  Only valid on success
    241  *                    and if pcwDest is greater than 0.
    242  * @retval  pcwDest   Size of the converted text in 16 bit words, including the trailing null
    243  *                    if present
    244  */
    245 static int vboxClipboardUtf16LinToWin(PRTUTF16 pu16Src, size_t cwSrc, PRTUTF16 *ppu16Dest,
    246                                       size_t *pcwDest)
    247 {
    248     enum { LINEFEED = 0xa, CARRIAGERETURN = 0xd };
    249     PRTUTF16 pu16Dest;
    250     size_t cwDest, i, j;
    251     LogFlowFunc(("pu16Src=%.*ls, cwSrc=%u\n", cwSrc, pu16Src, cwSrc));
    252     if (cwSrc == 0)
    253     {
    254         *ppu16Dest = 0;
    255         *pcwDest = 0;
    256         LogFlowFunc(("*ppu16Dest=0, *pcwDest=0, rc=VINF_SUCCESS\n"));
    257         return VINF_SUCCESS;
    258     }
    259     AssertReturn(VALID_PTR(pu16Src), VERR_INVALID_PARAMETER);
    260     cwDest = 0;
    261     for (i = 0; i < cwSrc; ++i, ++cwDest)
    262     {
    263         if (pu16Src[i] == LINEFEED)
    264             ++cwDest;
    265         if (pu16Src[i] == 0)
    266             break;
    267     }
    268     /* Leave space for a trailing null */
    269     ++cwDest;
    270     pu16Dest = reinterpret_cast<PRTUTF16>(RTMemAlloc(cwDest * 2));
    271     if (pu16Dest == 0)
    272     {
    273         LogFlowFunc(("rc=VERR_NO_MEMORY\n"));
    274         return VERR_NO_MEMORY;
    275     }
    276     for (i = (pu16Src[0] == 0xfeff ? 1 : 0), j = 0; i < cwSrc; ++i, ++j)
    277     {
    278         if (pu16Src[i] == LINEFEED)
    279         {
    280             pu16Dest[j] = CARRIAGERETURN;
    281             ++j;
    282         }
    283         if (pu16Src[i] == 0)
    284             break;
    285         pu16Dest[j] = pu16Src[i];
    286     }
    287     /* The trailing null */
    288     pu16Dest[j] = 0;
    289     *ppu16Dest = pu16Dest;
    290     *pcwDest = cwDest;
    291     LogFlowFunc(("*ppu16Dest=%p, *pcwDest=%d, rc=VINF_SUCCESS\n", pu16Dest, cwDest));
    292     return VINF_SUCCESS;
    293 }
    294 
    295 
    296 /**
    297  * Convert the UTF-16 text returned from the guest X11 clipboard to UTF-16LE with Windows EOLs
    298  * and send it to the host.
    299  *
    300  * @param pValue      Source UTF-16 text
    301  * @param cwSourceLen Length in 16-bit words of the source text
    302  */
    303 static void vboxClipboardGetUtf16(XtPointer pValue, size_t cwSourceLen)
    304 {
    305     size_t cwDestLen;
    306     PRTUTF16 pu16DestText;
    307     PRTUTF16 pu16SourceText = reinterpret_cast<PRTUTF16>(pValue);
    308     int rc;
    309 
    310     LogFlowFunc(("converting Utf-16 to Utf-16LE.  Original is %.*ls\n",
    311               cwSourceLen - 1, pu16SourceText + 1));
    312     rc = vboxClipboardUtf16LinToWin(pu16SourceText, cwSourceLen, &pu16DestText, &cwDestLen);
    313     if (rc != VINF_SUCCESS)
    314     {
    315         XtFree(reinterpret_cast<char *>(pValue));
    316         vboxClipboardSendData(0, NULL, 0);
    317         LogFlowFunc(("sending empty data and returning\n"));
    318         return;
    319     }
    320     LogFlow(("vboxClipboardGetUtf16: converted string is %.*ls\n", cwDestLen, pu16DestText));
    321     vboxClipboardSendData(VBOX_SHARED_CLIPBOARD_FMT_UNICODETEXT,
    322                             reinterpret_cast<void *>(pu16DestText), cwDestLen * 2);
    323     RTMemFree(reinterpret_cast<void *>(pu16DestText));
    324     XtFree(reinterpret_cast<char *>(pValue));
    325     LogFlowFunc(("returning\n"));
    326 }
    327 
    328 
    329 /**
    330  * Convert the UTF-8 text returned from the guest X11 clipboard to UTF-16LE with Windows EOLs
    331  * and send it to the host.
    332  *
    333  * @param pValue      Source UTF-8 text
    334  * @param cbSourceLen Length in 8-bit bytes of the source text
    335  */
    336 static void vboxClipboardGetUtf8(XtPointer pValue, size_t cbSourceLen)
    337 {
    338     size_t cwSourceLen, cwDestLen;
    339     char *pu8SourceText = reinterpret_cast<char *>(pValue);
    340     PRTUTF16 pu16SourceText = 0, pu16DestText;
    341     int rc;
    342 
    343     LogFlowFunc(("\n"));
    344     LogFlow(("vboxClipboardGetUtf8: converting Utf-8 to Utf-16LE. Original is %.*s\n", cbSourceLen,
    345            pu8SourceText));
    346     /* First convert the UTF8 to UTF16 */
    347     rc = RTStrToUtf16Ex(pu8SourceText, cbSourceLen, &pu16SourceText, 0, &cwSourceLen);
    348     if (rc != VINF_SUCCESS)
    349     {
    350         XtFree(reinterpret_cast<char *>(pValue));
    351         vboxClipboardSendData(0, NULL, 0);
    352         LogFlowFunc(("sending empty data and returning\n"));
    353         return;
    354     }
    355     rc = vboxClipboardUtf16LinToWin(pu16SourceText, cwSourceLen, &pu16DestText, &cwDestLen);
    356     if (rc != VINF_SUCCESS)
    357     {
    358         RTMemFree(reinterpret_cast<void *>(pu16SourceText));
    359         XtFree(reinterpret_cast<char *>(pValue));
    360         vboxClipboardSendData(0, NULL, 0);
    361         LogFlowFunc(("sending empty data and returning\n"));
    362         return;
    363     }
    364     LogFlow(("vboxClipboardGetUtf8: converted string is %.*ls\n", cwDestLen, pu16DestText));
    365     vboxClipboardSendData(VBOX_SHARED_CLIPBOARD_FMT_UNICODETEXT,
    366                             reinterpret_cast<void *>(pu16DestText), cwDestLen * 2);
    367     RTMemFree(reinterpret_cast<void *>(pu16SourceText));
    368     RTMemFree(reinterpret_cast<void *>(pu16DestText));
    369     XtFree(reinterpret_cast<char *>(pValue));
    370     LogFlowFunc(("returning\n"));
    371 }
    372 
    373 
    374 /**
    375  * Convert the compound text returned from the guest X11 clipboard to UTF-16LE with Windows EOLs
    376  * and send it to the host.
    377  *
    378  * @param pValue      Source compound text
    379  * @param cbSourceLen Length in 8-bit bytes of the source text
    380  */
    381 static void vboxClipboardGetCText(XtPointer pValue, size_t cbSourceLen)
    382 {
    383     size_t cwSourceLen, cwDestLen;
    384     char **ppu8SourceText = 0;
    385     PRTUTF16 pu16SourceText = 0, pu16DestText;
    386     XTextProperty property;
    387     int rc, cProps;
    388 
    389     LogFlowFunc(("\n"));
    390     LogFlow(("vboxClipboardGetCText: converting compound text to Utf-16LE. Original is %.*s\n",
    391            cbSourceLen, pValue));
    392     /* Quick fix for 2.2. */
    393     if (cbSourceLen == 0)
    394     {
    395         XtFree(reinterpret_cast<char *>(pValue));
    396         vboxClipboardSendData(VBOX_SHARED_CLIPBOARD_FMT_UNICODETEXT,
    397                               NULL, 0);
    398         return;
    399     }
    400     /* First convert the compound text to Utf8 */
    401     property.value = reinterpret_cast<unsigned char *>(pValue);
    402     property.encoding = g_ctx.atomCText;
    403     property.format = 8;
    404     property.nitems = cbSourceLen;
    405 #ifdef RT_OS_SOLARIS
    406     rc = XmbTextPropertyToTextList(XtDisplay(g_ctx.widget), &property, &ppu8SourceText, &cProps);
    407 #else
    408     rc = Xutf8TextPropertyToTextList(XtDisplay(g_ctx.widget), &property, &ppu8SourceText, &cProps);
    409 #endif
    410     XtFree(reinterpret_cast<char *>(pValue));
    411     if (rc < 0)
    412     {
    413         const char *pcReason;
    414         switch(rc)
    415         {
    416             case XNoMemory:
    417                 pcReason = "out of memory";
    418                 break;
    419             case XLocaleNotSupported:
    420                 pcReason = "locale (Utf8) not supported";
    421                 break;
    422             case XConverterNotFound:
    423                 pcReason = "converter not found";
    424                 break;
    425             default:
    426                 pcReason = "unknown error";
    427         }
    428         XFreeStringList(ppu8SourceText);
    429         LogRel(("vboxClipboardGetCText: Xutf8TextPropertyToTextList failed.  Reason: %s\n", pcReason));
    430         vboxClipboardSendData(0, NULL, 0);
    431         LogFlowFunc(("sending empty data and returning\n"));
    432         return;
    433     }
    434     /* Next convert the UTF8 to UTF16 */
    435     rc = RTStrToUtf16Ex(*ppu8SourceText, cbSourceLen, &pu16SourceText, 0, &cwSourceLen);
    436     XFreeStringList(ppu8SourceText);
    437     if (rc != VINF_SUCCESS)
    438     {
    439         vboxClipboardSendData(0, NULL, 0);
    440         LogFlowFunc(("sending empty data and returning\n"));
    441         return;
    442     }
    443     rc = vboxClipboardUtf16LinToWin(pu16SourceText, cwSourceLen, &pu16DestText, &cwDestLen);
    444     RTMemFree(reinterpret_cast<void *>(pu16SourceText));
    445     if (rc != VINF_SUCCESS)
    446     {
    447         vboxClipboardSendData(0, NULL, 0);
    448         LogFlowFunc(("sending empty data and returning\n"));
    449         return;
    450     }
    451     LogFlow(("vboxClipboardGetCText: converted string is %.*ls\n", cwDestLen, pu16DestText));
    452     vboxClipboardSendData(VBOX_SHARED_CLIPBOARD_FMT_UNICODETEXT,
    453                             reinterpret_cast<void *>(pu16DestText), cwDestLen * 2);
    454     RTMemFree(reinterpret_cast<void *>(pu16DestText));
    455     LogFlowFunc(("returning\n"));
    456 }
    457 
    458 
    459 /**
    460  * Convert the Latin1 text returned from the guest X11 clipboard to UTF-16LE with Windows EOLs
    461  * and send it to the host.
    462  *
    463  * @param pValue      Source Latin1 text
    464  * @param cbSourceLen Length in 8-bit bytes of the source text
    465  */
    466 static void vboxClipboardGetLatin1(XtPointer pValue, size_t cbSourceLen)
    467 {
    468     enum { LINEFEED = 0xa, CARRIAGERETURN = 0xd };
    469     /* Leave space for an additional null character at the end of the destination text. */
    470     size_t cwDestLen = cbSourceLen + 1, cwDestPos;
    471     char *pu8SourceText = reinterpret_cast<char *>(pValue);
    472     PRTUTF16 pu16DestText;
    473 
    474     LogFlowFunc(("converting Latin1 to Utf-16LE.  Original is %.*s\n", cbSourceLen,
    475                  pu8SourceText));
    476     /* Find the size of the destination text */
    477     for (size_t i = 0; i < cbSourceLen; i++)
    478     {
    479         if (pu8SourceText[i] == LINEFEED)
    480             ++cwDestLen;
    481     }
    482     pu16DestText = reinterpret_cast<PRTUTF16>(RTMemAlloc(cwDestLen * 2));
    483     if (pu16DestText == 0)
    484     {
    485         XtFree(reinterpret_cast<char *>(pValue));
    486         LogFlow(("vboxClipboardGetLatin1: failed to allocate %d bytes!\n", cwDestLen * 2));
    487         vboxClipboardSendData(0, NULL, 0);
    488         LogFlowFunc(("sending empty data and returning\n"));
    489         return;
    490     }
    491     /* Copy the original X clipboard string to the destination, replacing Linux EOLs with
    492        Windows ones */
    493     cwDestPos = 0;
    494     for (size_t i = 0; i < cbSourceLen; ++i, ++cwDestPos)
    495     {
    496         if (pu8SourceText[i] == LINEFEED)
    497         {
    498             pu16DestText[cwDestPos] = CARRIAGERETURN;
    499             ++cwDestPos;
    500         }
    501         /* latin1 < utf-16LE */
    502         pu16DestText[cwDestPos] = pu8SourceText[i];
    503         if (pu8SourceText[i] == 0)
    504             break;
    505     }
    506     pu16DestText[cwDestPos] = 0;
    507     LogFlow(("vboxClipboardGetLatin1: converted text is %.*ls\n", cwDestPos, pu16DestText));
    508     vboxClipboardSendData(VBOX_SHARED_CLIPBOARD_FMT_UNICODETEXT,
    509                             reinterpret_cast<void *>(pu16DestText), cwDestPos * 2);
    510     RTMemFree(reinterpret_cast<void *>(pu16DestText));
    511     XtFree(reinterpret_cast<char *>(pValue));
    512     LogFlowFunc(("returning\n"));
    513 }
    514 
    515 
    516 /** Convert the clipboard text from the current format to Utf-16 with Windows line breaks.
    517     We are reading the guest clipboard to make it available to the host. */
    518 static void vboxClipboardGetProc(Widget, XtPointer /* pClientData */, Atom * /* selection */,
    519                                  Atom *atomType, XtPointer pValue, long unsigned int *pcLen,
    520                                  int *piFormat)
    521 {
    522     LogFlowFunc(("*pcLen=%lu, *piFormat=%d, requested target format: %d, g_ctx.requestBufferSize=%d\n",
    523                  *pcLen, *piFormat, g_ctx.requestGuestFormat, g_ctx.requestBufferSize));
    524     /* The X Toolkit may have failed to get the clipboard selection for us. */
    525     if (*atomType == XT_CONVERT_FAIL)
    526     {
    527         vboxClipboardSendData(0, NULL, 0);
    528         LogFlowFunc(("Xt failed to convert the data.  Sending empty data and returning\n"));
    529         return;
    530     }
    531     if (NULL == pValue)
    532     {
    533         vboxClipboardSendData(0, NULL, 0);
    534         LogFlowFunc(("The clipboard contents changed while we were requesting them.  Sending empty data and returning\n"));
    535         return;
    536     }
    537     /* In which format did we request the clipboard data? */
    538     unsigned cTextLen = (*pcLen) * (*piFormat) / 8;
    539     switch (g_ctx.requestGuestFormat)
    540     {
    541         case UTF16:
    542             vboxClipboardGetUtf16(pValue, cTextLen / 2);
    543             break;
    544         case CTEXT:
    545             vboxClipboardGetCText(pValue, cTextLen);
    546             break;
    547         case UTF8:
    548         {
    549             /* If we are given broken Utf-8, we treat it as Latin1.  Is this acceptable? */
    550             size_t cStringLen;
    551             char *pu8SourceText = reinterpret_cast<char *>(pValue);
    552 
    553             if (RTStrUniLenEx(pu8SourceText, *pcLen, &cStringLen) == VINF_SUCCESS)
    554                 vboxClipboardGetUtf8(pValue, cTextLen);
    555             else
    556                 vboxClipboardGetLatin1(pValue, cTextLen);
    557             break;
    558         }
    559         default:
    560         {
    561             XtFree(reinterpret_cast<char *>(pValue));
    562             Log(("vboxClipboardGetProc: bad target format\n"));
    563             vboxClipboardSendData(0, NULL, 0);
    564             LogFlowFunc(("sending empty data and returning\n"));
    565             return;
    566         }
    567     }
    568     g_ctx.notifyHost = true;
    569     LogFlowFunc(("returning\n"));
    570 }
    571 
     136/** Opaque data structure describing a request from the host for clipboard
     137 * data, passed in when the request is forwarded to the X11 backend so that
     138 * it can be completed correctly. */
     139struct _CLIPREADCBREQ
     140{
     141    /** The data format that was requested. */
     142    uint32_t u32Format;
     143};
    572144
    573145/**
    574146 * Tell the host that new clipboard formats are available.
    575147 *
    576  * @returns VBox status code.
    577148 * @param u32Formats      The formats to advertise
    578149 */
    579 static int vboxClipboardReportFormats(uint32_t u32Formats)
     150void ClipReportX11Formats(VBOXCLIPBOARDCONTEXT *pCtx, uint32_t u32Formats)
    580151{
    581152    int rc;
     
    583154    rc = VbglR3ClipboardReportFormats(g_ctx.client, u32Formats);
    584155    LogFlowFunc(("rc=%Rrc\n", rc));
     156}
     157
     158/** This is called by the backend to tell us that a request for data from
     159 * X11 has completed.
     160 * @param  pCtx      Our context information
     161 * @param  rc        the iprt result code of the request
     162 * @param  pReq      the request structure that we passed in when we started
     163 *                   the request.  We RTMemFree() this in this function.
     164 * @param  pv        the clipboard data returned from X11 if the request
     165 *                   succeeded (see @a rc)
     166 * @param  cb        the size of the data in @a pv
     167 */
     168void ClipCompleteDataRequestFromX11(VBOXCLIPBOARDCONTEXT *pCtx, int rc,
     169                                    CLIPREADCBREQ *pReq, void *pv,
     170                                    uint32_t cb)
     171{
     172    if (RT_SUCCESS(rc))
     173        vboxClipboardSendData(pReq->u32Format, pv, cb);
     174    else
     175        vboxClipboardSendData(0, NULL, 0);
     176    RTMemFree(pReq);
     177}
     178
     179/**
     180 * Connect the guest clipboard to the host.
     181 *
     182 * @returns VBox status code
     183 */
     184int vboxClipboardConnect(void)
     185{
     186    int rc;
     187    LogFlowFunc(("\n"));
     188
     189    /* Sanity */
     190    AssertReturn(g_ctx.client == 0, VERR_WRONG_ORDER);
     191    g_ctx.pBackend = ClipConstructX11(&g_ctx);
     192    if (!g_ctx.pBackend)
     193        rc = VERR_NO_MEMORY;
     194    if (RT_SUCCESS(rc))
     195        rc = ClipStartX11(g_ctx.pBackend);
     196    if (RT_SUCCESS(rc))
     197    {
     198        rc = VbglR3ClipboardConnect(&g_ctx.client);
     199        if (RT_FAILURE(rc))
     200            LogRel(("Error connecting to host. rc=%Rrc\n", rc));
     201        else if (!g_ctx.client)
     202        {
     203            LogRel(("Invalid client ID of 0\n"));
     204            rc = VERR_NOT_SUPPORTED;
     205        }
     206    }
     207
     208    if (RT_FAILURE(rc) && g_ctx.pBackend)
     209        ClipDestructX11(g_ctx.pBackend);
     210    LogFlowFunc(("g_ctx.client=%u rc=%Rrc\n", g_ctx.client, rc));
    585211    return rc;
    586212}
    587213
    588 
    589 /** Callback to handle a reply to a request for the targets the current clipboard holder can
    590     handle.  We are reading the guest clipboard to make it available to the host. */
    591 static void vboxClipboardTargetsProc(Widget, XtPointer, Atom * /* selection */, Atom *atomType,
    592                                      XtPointer pValue, long unsigned int *pcLen, int *piFormat)
    593 {
    594     static int cCalls = 0;
    595     Atom *atomTargets = reinterpret_cast<Atom *>(pValue);
    596     /* The number of format atoms the clipboard holder is offering us */
    597     unsigned cAtoms = *pcLen;
    598     /* The best clipboard format we have found so far */
    599     g_eClipboardFormat eBestTarget = INVALID;
    600     /* The atom corresponding to our best clipboard format found */
    601     Atom atomBestTarget = None;
    602 
    603     if ((cCalls % 10) == 0)
    604         Log3(("vboxClipboardTargetsProc called, cAtoms=%d\n", cAtoms));
    605     if (*atomType == XT_CONVERT_FAIL)
    606     {
    607         Log(("vboxClipboardTargetsProc: reading clipboard from guest, X toolkit failed to convert the selection\n"));
    608         LogFlowFunc(("returning\n"));
    609         return;
    610     }
    611     /* Run through the atoms offered to us to see if we recognise them.  If we find the atom for
    612        a "better" format than the best we have found so far, we remember it as our new "best"
    613        format. */
    614     for (unsigned i = 0; i < cAtoms; ++i)
    615     {
    616         for (unsigned j = 0; j != g_ctx.formatList.size(); ++j)
    617             if (g_ctx.formatList[j].atom == atomTargets[i])
    618             {
    619                 if (eBestTarget < g_ctx.formatList[j].format)
    620                 {
    621                     eBestTarget = g_ctx.formatList[j].format;
    622                     atomBestTarget = g_ctx.formatList[j].atom;
    623                 }
    624                 break;
    625             }
    626 #ifdef DEBUG
    627         char *szAtomName = XGetAtomName(XtDisplay(g_ctx.widget), atomTargets[i]);
    628         if (szAtomName != 0)
    629         {
    630             if ((cCalls % 10) == 0)
    631                 Log3(("vboxClipboardTargetsProc: the guest offers target %s\n", szAtomName));
    632             XFree(szAtomName);
    633         }
    634         else
    635         {
    636             if ((cCalls % 10) == 0)
    637                 Log3(("vboxClipboardTargetsProc: the guest returned a bad atom: %d\n",
    638                        atomTargets[i]));
    639         }
    640 #endif
    641     }
    642     g_ctx.atomGuestTextFormat = atomBestTarget;
    643     /* If the available formats as seen by the host have changed, or if we suspect that
    644        the host has cached the clipboard data (which can change without our noticing it),
    645        then tell the host that new clipboard contents are available. */
    646     if ((eBestTarget != g_ctx.guestTextFormat) || (g_ctx.notifyHost == true))
    647     {
    648         uint32_t u32Formats = 0;
    649 #ifdef DEBUG
    650         if (atomBestTarget != None)
    651         {
    652             char *szAtomName = XGetAtomName(XtDisplay(g_ctx.widget), atomBestTarget);
    653             LogFlow(("vboxClipboardTargetsProc: switching to guest text target %s.  Available targets are:\n", szAtomName));
    654             XFree(szAtomName);
    655         }
    656         else
    657             LogFlow(("vboxClipboardTargetsProc: no supported host text target found.  Available targets are:\n"));
    658 
    659         for (unsigned i = 0; i < cAtoms; ++i)
    660         {
    661             char *szAtomName = XGetAtomName(XtDisplay(g_ctx.widget), atomTargets[i]);
    662             if (szAtomName != 0)
    663             {
    664                 LogFlow(("vboxClipboardTargetsProc:     %s\n", szAtomName));
    665                 XFree(szAtomName);
    666             }
    667         }
    668 #endif
    669         g_ctx.guestTextFormat = eBestTarget;
    670         if (eBestTarget != INVALID)
    671             u32Formats |= VBOX_SHARED_CLIPBOARD_FMT_UNICODETEXT;
    672         vboxClipboardReportFormats(u32Formats);
    673         g_ctx.notifyHost = false;
    674     }
    675     XtFree(reinterpret_cast<char *>(pValue));
    676     ++cCalls;
    677 }
    678 
    679 
    680 /**
    681  * This callback is called every 200ms to check the contents of the guest clipboard.
    682  */
    683 static void vboxClipboardTimerProc(XtPointer /* pUserData */, XtIntervalId * /* hTimerId */)
    684 {
    685     static int cCalls = 0;
    686     /* Get the current clipboard contents */
    687     if (g_ctx.eOwner == GUEST)
    688     {
    689         if ((cCalls % 10) == 0)
    690             Log3(("vboxClipboardTimerProc: requesting the targets that the guest clipboard offers\n"));
    691         XtGetSelectionValue(g_ctx.widget, g_ctx.atomClipboard, g_ctx.atomTargets,
    692                             vboxClipboardTargetsProc, 0, CurrentTime);
    693     }
    694     /* Re-arm our timer */
    695     XtAppAddTimeOut(g_ctx.appContext, 200 /* ms */, vboxClipboardTimerProc, 0);
    696     ++cCalls;
    697 }
    698 
    699 
    700 /**
    701  * Satisfy a request from the guest for available clipboard targets.
    702  *
    703  * @returns true if we successfully convert the data to the format requested, false otherwise.
    704  *
    705  * @param atomTypeReturn The type of the data we are returning
    706  * @param pValReturn     A pointer to the data we are returning.  This should be to memory
    707  *                       allocated by XtMalloc, which will be freed by the toolkit later
    708  * @param pcLenReturn    The length of the data we are returning
    709  * @param piFormatReturn The format (8bit, 16bit, 32bit) of the data we are returning
    710  */
    711 static Boolean vboxClipboardConvertTargets(Atom *atomTypeReturn, XtPointer *pValReturn,
    712                                            unsigned long *pcLenReturn, int *piFormatReturn)
    713 {
    714     unsigned uListSize = g_ctx.formatList.size();
    715     Atom *atomTargets = reinterpret_cast<Atom *>(XtMalloc((uListSize + 3) * sizeof(Atom)));
    716     unsigned cTargets = 0;
    717 
    718     LogFlowFunc(("\n"));
    719     LogFlow(("vboxClipboardConvertTargets: uListSize=%u\n", uListSize));
    720     for (unsigned i = 0; i < uListSize; ++i)
    721     {
    722         if (   ((g_ctx.hostFormats & VBOX_SHARED_CLIPBOARD_FMT_UNICODETEXT) != 0)
    723             && (g_ctx.formatList[i].hostFormat == VBOX_SHARED_CLIPBOARD_FMT_UNICODETEXT))
    724         {
    725             atomTargets[cTargets] = g_ctx.formatList[i].atom;
    726             ++cTargets;
    727         }
    728     }
    729     atomTargets[cTargets] = g_ctx.atomTargets;
    730     atomTargets[cTargets + 1] = g_ctx.atomMultiple;
    731     atomTargets[cTargets + 2] = g_ctx.atomTimestamp;
    732 #ifdef DEBUG
    733     for (unsigned i = 0; i < cTargets + 3; i++)
    734     {
    735         char *szAtomName = XGetAtomName(XtDisplay(g_ctx.widget), atomTargets[i]);
    736         if (szAtomName != 0)
    737         {
    738             LogFlow(("vboxClipboardConvertTargets: returning target %s\n", szAtomName));
    739             XFree(szAtomName);
    740         }
    741         else
    742             Log(("vboxClipboardConvertTargets: invalid atom %d in the list!\n", atomTargets[i]));
    743     }
    744 #endif
    745     *atomTypeReturn = XA_ATOM;
    746     *pValReturn = reinterpret_cast<XtPointer>(atomTargets);
    747     *pcLenReturn = cTargets + 3;
    748     *piFormatReturn = 32;
    749     LogFlowFunc(("returning true\n"));
    750     return true;
    751 }
    752 
    753 
    754 /**
    755  * Get the size of the buffer needed to hold a zero-terminated Utf16 string with Linux EOLs
    756  * converted from a Utf16 string with Windows EOLs.
    757  *
    758  * @returns The size of the buffer needed in bytes
    759  *
    760  * @param   pu16Src  The source Utf16 string
    761  * @param   cwSrc    The length in 16 bit words of the source string
    762  */
    763 static int vboxClipboardUtf16GetLinSize(PRTUTF16 pu16Src, size_t cwSrc)
    764 {
    765     enum { LINEFEED = 0xa, CARRIAGERETURN = 0xd };
    766     size_t cwDest;
    767 
    768     LogFlowFunc(("pu16Src=%.*ls, cwSrc=%u\n", cwSrc, pu16Src, cwSrc));
    769     AssertReturn(pu16Src != 0, VERR_INVALID_PARAMETER);
    770     /* We only take little endian Utf16 */
    771     AssertReturn(pu16Src[0] != 0xfffe, VERR_INVALID_PARAMETER);
    772     if (cwSrc == 0)
    773     {
    774         LogFlowFunc(("returning 0\n"));
    775         return 0;
    776     }
    777     /* Calculate the size of the destination text string. */
    778     /* Is this Utf16 or Utf16-LE? */
    779     if (pu16Src[0] == 0xfeff)
    780         cwDest = 0;
    781     else
    782         cwDest = 1;
    783     for (size_t i = 0; i < cwSrc; ++i, ++cwDest)
    784     {
    785         if (   (i + 1 < cwSrc)
    786             && (pu16Src[i] == CARRIAGERETURN)
    787             && (pu16Src[i + 1] == LINEFEED))
    788             ++i;
    789         if (pu16Src[i] == 0)
    790             break;
    791     }
    792     /* The terminating null */
    793     ++cwDest;
    794     LogFlowFunc(("returning %d\n", cwDest * 2));
    795     return cwDest * 2;
    796 }
    797 
    798 
    799 /**
    800  * Convert Utf16-LE text with Windows EOLs to Utf16 with Linux EOLs.  This function does not
    801  * verify that the Utf16 is valid.
    802  *
    803  * @returns VBox status code
    804  *
    805  * @param   pu16Src       Text to convert
    806  * @param   cwSrc         Size of the source text in 16 bit words
    807  * @param   pu16Dest      The buffer to store the converted text to
    808  * @param   cwDest        The size of the buffer for the destination text
    809  */
    810 static int vboxClipboardUtf16WinToLin(PRTUTF16 pu16Src, size_t cwSrc, PRTUTF16 pu16Dest,
    811                                       size_t cwDest)
    812 {
    813     enum { LINEFEED = 0xa, CARRIAGERETURN = 0xd };
    814     size_t cwDestPos;
    815 
    816     LogFlowFunc(("pu16Src=%.*ls, cwSrc=%u, pu16Dest=%p, cwDest=%u\n",
    817                   cwSrc, pu16Src, cwSrc, pu16Dest, cwDest));
    818     /* A buffer of size 0 may not be an error, but it is not a good idea either. */
    819     Assert(cwDest > 0);
    820     AssertReturn(VALID_PTR(pu16Src), VERR_INVALID_PARAMETER);
    821     /* We only take little endian Utf16 */
    822     AssertReturn(pu16Src[0] != 0xfffe, VERR_INVALID_PARAMETER);
    823     if (cwSrc == 0)
    824     {
    825         if (cwDest != 0)
    826             pu16Dest[0] = 0;
    827         LogFlowFunc(("set empty string.  Returning VINF_SUCCESS\n"));
    828         return VINF_SUCCESS;
    829     }
    830     /* Prepend the Utf16 byte order marker if it is missing. */
    831     if (pu16Src[0] == 0xfeff)
    832         cwDestPos = 0;
    833     else
    834     {
    835         if (cwDest == 0)
    836         {
    837             LogFlowFunc(("returning VERR_BUFFER_OVERFLOW\n"));
    838             return VERR_BUFFER_OVERFLOW;
    839         }
    840         pu16Dest[0] = 0xfeff;
    841         cwDestPos = 1;
    842     }
    843     for (size_t i = 0; i < cwSrc; ++i, ++cwDestPos)
    844     {
    845         if (cwDestPos == cwDest)
    846         {
    847             LogFlowFunc(("returning VERR_BUFFER_OVERFLOW\n"));
    848             return VERR_BUFFER_OVERFLOW;
    849         }
    850         if (   (i + 1 < cwSrc)
    851             && (pu16Src[i] == CARRIAGERETURN)
    852             && (pu16Src[i + 1] == LINEFEED))
    853             ++i;
    854 
    855         pu16Dest[cwDestPos] = pu16Src[i];
    856         if (pu16Src[i] == 0)
    857             break;
    858     }
    859     if (cwDestPos == cwDest)
    860     {
    861         LogFlowFunc(("returning VERR_BUFFER_OVERFLOW\n"));
    862         return VERR_BUFFER_OVERFLOW;
    863     }
    864     pu16Dest[cwDestPos] = 0;
    865     LogFlowFunc(("set string %ls.  Returning\n", pu16Dest + 1));
    866     return VINF_SUCCESS;
    867 }
    868 
    869 
    870 /**
    871  * Satisfy a request from the guest to convert the clipboard text to Utf16.
    872  *
    873  * @returns true if we successfully convert the data to the format requested, false otherwise.
    874  *
    875  * @param atomTypeReturn The type of the data we are returning
    876  * @param pValReturn     A pointer to the data we are returning.  This should be to memory
    877  *                       allocated by XtMalloc, which will be freed by the toolkit later
    878  * @param pcLenReturn    The length of the data we are returning
    879  * @param piFormatReturn The format (8bit, 16bit, 32bit) of the data we are returning
    880  */
    881 static Boolean vboxClipboardConvertUtf16(Atom *atomTypeReturn, XtPointer *pValReturn,
    882                                          unsigned long *pcLenReturn, int *piFormatReturn)
    883 {
    884     PRTUTF16 pu16GuestText, pu16HostText;
    885     unsigned cbHostText, cwHostText, cwGuestText;
     214/**
     215 * The main loop of our clipboard reader.
     216 */
     217int vboxClipboardMain(void)
     218{
    886219    int rc;
    887 
    888     LogFlowFunc(("\n"));
    889     rc = vboxClipboardReadHostData(VBOX_SHARED_CLIPBOARD_FMT_UNICODETEXT,
    890                                    reinterpret_cast<void **>(&pu16HostText), &cbHostText);
    891     if ((rc != VINF_SUCCESS) || cbHostText == 0)
    892     {
    893         LogFlow(("vboxClipboardConvertUtf16: vboxClipboardReadHostData returned %Rrc, %d bytes of data\n", rc, cbHostText));
    894         g_ctx.hostFormats = 0;
    895         LogFlowFunc(("rc = false\n"));
    896         return false;
    897     }
    898     cwHostText = cbHostText / 2;
    899     cwGuestText = vboxClipboardUtf16GetLinSize(pu16HostText, cwHostText);
    900     pu16GuestText = reinterpret_cast<PRTUTF16>(XtMalloc(cwGuestText * 2));
    901     if (pu16GuestText == 0)
    902     {
    903         RTMemFree(reinterpret_cast<void *>(pu16HostText));
    904         LogFlowFunc(("rc = false\n"));
    905         return false;
    906     }
    907     rc = vboxClipboardUtf16WinToLin(pu16HostText, cwHostText, pu16GuestText, cwGuestText);
    908     if (rc != VINF_SUCCESS)
    909     {
    910         LogFlow(("vboxClipboardConvertUtf16: vboxClipboardUtf16WinToLin returned %Rrc\n", rc));
    911         RTMemFree(reinterpret_cast<void *>(pu16HostText));
    912         XtFree(reinterpret_cast<char *>(pu16GuestText));
    913         LogFlowFunc(("rc = false\n"));
    914         return false;
    915     }
    916     LogFlow(("vboxClipboardConvertUtf16: returning Unicode, original text is %.*ls\n", cwHostText, pu16HostText));
    917     LogFlow(("vboxClipboardConvertUtf16: converted text is %.*ls\n", cwGuestText - 1, pu16GuestText + 1));
    918     RTMemFree(reinterpret_cast<void *>(pu16HostText));
    919     *atomTypeReturn = g_ctx.atomUtf16;
    920     *pValReturn = reinterpret_cast<XtPointer>(pu16GuestText);
    921     *pcLenReturn = cwGuestText * 2;
    922     *piFormatReturn = 8;
    923     LogFlowFunc(("rc = true\n"));
    924     return true;
    925 }
    926 
    927 
    928 /**
    929  * Satisfy a request from the guest to convert the clipboard text to Utf8.
    930  *
    931  * @returns true if we successfully convert the data to the format requested, false otherwise.
    932  *
    933  * @param atomTypeReturn The type of the data we are returning
    934  * @param pValReturn     A pointer to the data we are returning.  This should be to memory
    935  *                       allocated by XtMalloc, which will be freed by the toolkit later
    936  * @param pcLenReturn    The length of the data we are returning
    937  * @param piFormatReturn The format (8bit, 16bit, 32bit) of the data we are returning
    938  */
    939 static Boolean vboxClipboardConvertUtf8(Atom *atomTypeReturn, XtPointer *pValReturn,
    940                                         unsigned long *pcLenReturn, int *piFormatReturn)
    941 {
    942     PRTUTF16 pu16GuestText, pu16HostText;
    943     char *pcGuestText;
    944     unsigned cbHostText, cwHostText, cwGuestText, cbGuestTextBuffer;
    945     size_t cbGuestTextActual;
    946     int rc;
    947 
    948     LogFlowFunc(("\n"));
    949     /* Get the host Utf16 data and convert it to Linux Utf16. */
    950     rc = vboxClipboardReadHostData(VBOX_SHARED_CLIPBOARD_FMT_UNICODETEXT,
    951                                    reinterpret_cast<void **>(&pu16HostText), &cbHostText);
    952     if ((rc != VINF_SUCCESS) || cbHostText == 0)
    953     {
    954         LogFlow(("vboxClipboardConvertUtf16: vboxClipboardReadHostData returned %Rrc, %d bytes of data\n", rc, cbHostText));
    955         g_ctx.hostFormats = 0;
    956         LogFlowFunc(("rc = false\n"));
    957         return false;
    958     }
    959     cwHostText = cbHostText / 2;
    960     LogFlow(("vboxClipboardConvertUtf8: original text is %.*ls\n", cwHostText, pu16HostText));
    961     cwGuestText = vboxClipboardUtf16GetLinSize(pu16HostText, cwHostText);
    962     pu16GuestText = reinterpret_cast<PRTUTF16>(RTMemAlloc(cwGuestText * 2));
    963     if (pu16GuestText == 0)
    964     {
    965         RTMemFree(reinterpret_cast<char *>(pu16HostText));
    966         LogFlowFunc(("rc = false\n"));
    967         return false;
    968     }
    969     rc = vboxClipboardUtf16WinToLin(pu16HostText, cwHostText, pu16GuestText, cwGuestText);
    970     RTMemFree(reinterpret_cast<char *>(pu16HostText));
    971     if (rc != VINF_SUCCESS)
    972     {
    973         LogFlow(("vboxClipboardConvertUtf16: vboxClipboardUtf16WinToLin returned %Rrc\n", rc));
    974         RTMemFree(reinterpret_cast<char *>(pu16GuestText));
    975         LogFlowFunc(("rc = false\n"));
    976         return false;
    977     }
    978     /* Now convert the Utf16 Linux text to Utf8 */
    979     cbGuestTextBuffer = cwGuestText * 3;  /* Should always be enough. */
    980     pcGuestText = XtMalloc(cbGuestTextBuffer);
    981     if (pcGuestText == 0)
    982     {
    983         RTMemFree(reinterpret_cast<char *>(pu16GuestText));
    984         LogFlowFunc(("rc = false\n"));
    985         return false;
    986     }
    987     /* Our runtime can't cope with endian markers. */
    988     cbGuestTextActual = 0;
    989     rc = RTUtf16ToUtf8Ex(pu16GuestText + 1, cwGuestText - 1, &pcGuestText, cbGuestTextBuffer, &cbGuestTextActual);
    990     RTMemFree(reinterpret_cast<char *>(pu16GuestText));
    991     if (rc != VINF_SUCCESS)
    992     {
    993         XtFree(pcGuestText);
    994         LogFlowFunc(("rc = false\n"));
    995         return false;
    996     }
    997     LogFlow(("vboxClipboardConvertUtf8: converted text is %.*s\n", cbGuestTextActual, pcGuestText));
    998     *atomTypeReturn = g_ctx.atomUtf8;
    999     *pValReturn = reinterpret_cast<XtPointer>(pcGuestText);
    1000     *pcLenReturn = (unsigned long)cbGuestTextActual;
    1001     *piFormatReturn = 8;
    1002     LogFlowFunc(("rc = true\n"));
    1003     return true;
    1004 }
    1005 
    1006 
    1007 /**
    1008  * Satisfy a request from the guest to convert the clipboard text to compound text.
    1009  *
    1010  * @returns true if we successfully convert the data to the format requested, false otherwise.
    1011  *
    1012  * @param atomTypeReturn The type of the data we are returning
    1013  * @param pValReturn     A pointer to the data we are returning.  This should be to memory
    1014  *                       allocated by XtMalloc, which will be freed by the toolkit later
    1015  * @param pcLenReturn    The length of the data we are returning
    1016  * @param piFormatReturn The format (8bit, 16bit, 32bit) of the data we are returning
    1017  */
    1018 static Boolean vboxClipboardConvertCText(Atom *atomTypeReturn, XtPointer *pValReturn,
    1019                                          unsigned long *pcLenReturn, int *piFormatReturn)
    1020 {
    1021     PRTUTF16 pu16GuestText, pu16HostText;
    1022     char *pcUtf8Text;
    1023     unsigned cbHostText, cwHostText, cwGuestText, cbUtf8Text;
    1024     XTextProperty property;
    1025     int rc;
    1026 
    1027     LogFlowFunc(("\n"));
    1028     /* Get the host Utf16 data and convert it to Linux Utf16. */
    1029     rc = vboxClipboardReadHostData(VBOX_SHARED_CLIPBOARD_FMT_UNICODETEXT,
    1030                                    reinterpret_cast<void **>(&pu16HostText), &cbHostText);
    1031     if ((rc != VINF_SUCCESS) || cbHostText == 0)
    1032     {
    1033         LogFlow(("vboxClipboardConvertCText: vboxClipboardReadHostData returned %Rrc, %d bytes of data\n", rc, cbHostText));
    1034         g_ctx.hostFormats = 0;
    1035         LogFlowFunc(("rc = false\n"));
    1036         return false;
    1037     }
    1038     cwHostText = cbHostText / 2;
    1039     LogFlow(("vboxClipboardConvertCText: original text is %.*ls\n", cwHostText, pu16HostText));
    1040     cwGuestText = vboxClipboardUtf16GetLinSize(pu16HostText, cwHostText);
    1041     pu16GuestText = reinterpret_cast<PRTUTF16>(RTMemAlloc(cwGuestText * 2));
    1042     if (pu16GuestText == 0)
    1043     {
    1044         Log(("vboxClipboardConvertCText: out of memory\n"));
    1045         RTMemFree(reinterpret_cast<char *>(pu16HostText));
    1046         LogFlowFunc(("rc = false\n"));
    1047         return false;
    1048     }
    1049     rc = vboxClipboardUtf16WinToLin(pu16HostText, cwHostText, pu16GuestText, cwGuestText);
    1050     RTMemFree(reinterpret_cast<char *>(pu16HostText));
    1051     if (rc != VINF_SUCCESS)
    1052     {
    1053         LogFlow(("vboxClipboardConvertUtf16: vboxClipboardUtf16WinToLin returned %Rrc\n", rc));
    1054         RTMemFree(reinterpret_cast<char *>(pu16GuestText));
    1055         LogFlowFunc(("rc = false\n"));
    1056         return false;
    1057     }
    1058     /* Now convert the Utf16 Linux text to Utf8 */
    1059     cbUtf8Text = cwGuestText * 3;  /* Should always be enough. */
    1060     pcUtf8Text = reinterpret_cast<char *>(RTMemAlloc(cbUtf8Text));
    1061     if (pcUtf8Text == 0)
    1062     {
    1063         Log(("vboxClipboardConvertCText: out of memory\n"));
    1064         RTMemFree(reinterpret_cast<char *>(pu16GuestText));
    1065         LogFlowFunc(("rc = false\n"));
    1066         return false;
    1067     }
    1068     /* Our runtime can't cope with endian markers. */
    1069     rc = RTUtf16ToUtf8Ex(pu16GuestText + 1, cwGuestText - 1, &pcUtf8Text, cbUtf8Text, 0);
    1070     RTMemFree(reinterpret_cast<char *>(pu16GuestText));
    1071     if (rc != VINF_SUCCESS)
    1072     {
    1073         Log(("vboxClipboardConvertCText: RTUtf16ToUtf8Ex failed: rc=%Rrc\n", rc));
    1074         XtFree(pcUtf8Text);
    1075         LogFlowFunc(("rc = false\n"));
    1076         return false;
    1077     }
    1078     /* And finally (!) convert the Utf8 text to compound text. */
    1079 #ifdef RT_OS_SOLARIS
    1080     rc = XmbTextListToTextProperty(XtDisplay(g_ctx.widget), &pcUtf8Text, 1,
    1081                                      XCompoundTextStyle, &property);
    1082 #else
    1083     rc = Xutf8TextListToTextProperty(XtDisplay(g_ctx.widget), &pcUtf8Text, 1,
    1084                                      XCompoundTextStyle, &property);
    1085 #endif
    1086     RTMemFree(pcUtf8Text);
    1087     if (rc < 0)
    1088     {
    1089         const char *pcReason;
    1090         switch(rc)
    1091         {
    1092             case XNoMemory:
    1093                 pcReason = "out of memory";
    1094                 break;
    1095             case XLocaleNotSupported:
    1096                 pcReason = "locale (Utf8) not supported";
    1097                 break;
    1098             case XConverterNotFound:
    1099                 pcReason = "converter not found";
    1100                 break;
    1101             default:
    1102                 pcReason = "unknown error";
    1103         }
    1104         LogRel(("vboxClipboardConvertCText: Xutf8TextListToTextProperty failed.  Reason: %s\n",
    1105                 pcReason));
    1106         LogFlowFunc(("rc = false\n"));
    1107         return false;
    1108     }
    1109     LogFlow(("vboxClipboardConvertCText: converted text is %s\n", property.value));
    1110     *atomTypeReturn = property.encoding;
    1111     *pValReturn = reinterpret_cast<XtPointer>(property.value);
    1112     *pcLenReturn = property.nitems;
    1113     *piFormatReturn = property.format;
    1114     LogFlowFunc(("rc = true\n"));
    1115     return true;
    1116 }
    1117 
    1118 
    1119 /**
    1120  * Callback to convert the hosts clipboard data for an application on the guest.  Called by the
    1121  * X Toolkit.
    1122  * @returns true if we successfully convert the data to the format requested, false otherwise.
    1123  *
    1124  * @param atomSelection  The selection which is being requested.  We only handle the clipboard.
    1125  * @param atomTarget     The format we should convert the data to
    1126  * @param atomTypeReturn The type of the data we are returning
    1127  * @param pValReturn     A pointer to the data we are returning.  This should be to memory
    1128  *                       allocated by XtMalloc, which will be freed by the toolkit later
    1129  * @param pcLenReturn    The length of the data we are returning
    1130  * @param piFormatReturn The format (8bit, 16bit, 32bit) of the data we are returning
    1131  */
    1132 static Boolean vboxClipboardConvertProc(Widget, Atom *atomSelection, Atom *atomTarget,
    1133                                         Atom *atomTypeReturn, XtPointer *pValReturn,
    1134                                         unsigned long *pcLenReturn, int *piFormatReturn)
    1135 {
    1136     g_eClipboardFormat eFormat = INVALID;
    1137     int rc;
    1138 
    1139     LogFlowFunc(("\n"));
    1140     if (   (*atomSelection != g_ctx.atomClipboard)
    1141         && (*atomSelection != g_ctx.atomPrimary)
    1142        )
    1143     {
    1144         LogFlowFunc(("rc = false\n"));
    1145         return false;
    1146     }
    1147 #ifdef DEBUG
    1148     char *szAtomName = XGetAtomName(XtDisplay(g_ctx.widget), *atomTarget);
    1149     if (szAtomName != 0)
    1150     {
    1151         LogFlow(("vboxClipboardConvertProc: request for format %s\n", szAtomName));
    1152         XFree(szAtomName);
    1153     }
    1154     else
    1155         Log(("vboxClipboardConvertProc: request for invalid target atom %d!\n", *atomTarget));
    1156 #endif
    1157     if (*atomTarget == g_ctx.atomTargets)
    1158         eFormat = TARGETS;
    1159     else
    1160     {
    1161         for (unsigned i = 0; i != g_ctx.formatList.size(); ++i)
    1162         {
    1163             if (g_ctx.formatList[i].atom == *atomTarget)
    1164             {
    1165                 eFormat = g_ctx.formatList[i].format;
    1166                 break;
    1167             }
    1168         }
    1169     }
    1170     switch (eFormat)
    1171     {
    1172         case TARGETS:
    1173             rc = vboxClipboardConvertTargets(atomTypeReturn, pValReturn, pcLenReturn, piFormatReturn);
    1174             LogFlowFunc(("TARGETS rc=%d\n", rc));
    1175             return rc;
    1176         case UTF16:
    1177             rc = vboxClipboardConvertUtf16(atomTypeReturn, pValReturn, pcLenReturn, piFormatReturn);
    1178             LogFlowFunc(("UTF16 rc=%d\n", rc));
    1179             return rc;
    1180         case UTF8:
    1181             rc = vboxClipboardConvertUtf8(atomTypeReturn, pValReturn, pcLenReturn, piFormatReturn);
    1182             LogFlowFunc(("UTF8 rc=%d\n", rc));
    1183             return rc;
    1184         case CTEXT:
    1185             rc = vboxClipboardConvertCText(atomTypeReturn, pValReturn, pcLenReturn, piFormatReturn);
    1186             LogFlowFunc(("CTEXT rc=%d\n", rc));
    1187             return rc;
    1188         default:
    1189             Log(("vboxClipboardConvertProc: bad format\n"));
    1190             LogFlowFunc(("rc = false\n"));
    1191             return false;
    1192     }
    1193 }
    1194 
    1195 
    1196 static void vboxClipboardLoseProc(Widget, Atom *)
    1197 {
    1198     LogFlowFunc(("giving the guest clipboard ownership\n"));
    1199     g_ctx.eOwner = GUEST;
    1200     g_ctx.notifyHost = true;
    1201     LogFlowFunc(("returning\n"));
    1202 }
    1203 
    1204 
    1205 /**
    1206  * The host is taking possession of the shared clipboard.  Called by the HGCM clipboard
    1207  * subsystem.
    1208  *
    1209  * @param u32Formats Clipboard formats the guest is offering
    1210  */
    1211 void vboxClipboardFormatAnnounce (uint32_t u32Formats)
    1212 {
    1213     g_ctx.hostFormats = u32Formats;
    1214     LogFlowFunc(("u32Formats = %d\n", u32Formats));
    1215     if (u32Formats == 0)
    1216     {
    1217         /* This is just an automatism, not a genuine announcement */
    1218         LogFlowFunc(("returning\n"));
    1219         return;
    1220     }
    1221     LogFlow(("vboxClipboardFormatAnnounce: giving the host clipboard ownership\n"));
    1222     g_ctx.eOwner = HOST;
    1223     g_ctx.guestTextFormat = INVALID;
    1224     g_ctx.guestBitmapFormat = INVALID;
    1225     if (XtOwnSelection(g_ctx.widget, g_ctx.atomClipboard, CurrentTime,
    1226                        vboxClipboardConvertProc, vboxClipboardLoseProc, 0)
    1227                        == True)
    1228         XtOwnSelection(g_ctx.widget, g_ctx.atomPrimary, CurrentTime,
    1229                        vboxClipboardConvertProc, NULL, 0);
    1230     else
    1231     {
    1232         LogFlow(("vboxClipboardFormatAnnounce: returning clipboard ownership to the guest\n"));
    1233         g_ctx.notifyHost = true;
    1234         g_ctx.eOwner = GUEST;
    1235     }
    1236     LogFlowFunc(("returning\n"));
    1237 }
    1238 
    1239 
    1240 /**
    1241  * Called when the host wants to read the guest clipboard.
    1242  *
    1243  * @param u32Format The format that the host would like to receive the data in
    1244  */
    1245 int vboxClipboardReadGuestData (uint32_t u32Format)
    1246 {
    1247     LogFlowFunc(("u32Format = %d\n", u32Format));
    1248 
    1249     /*
    1250      * The guest wants to read data in the given format.
    1251      */
    1252     if (u32Format & VBOX_SHARED_CLIPBOARD_FMT_UNICODETEXT)
    1253     {
    1254         if (g_ctx.guestTextFormat == INVALID)
    1255         {
    1256             /* No data available. */
    1257             vboxClipboardSendData(0, NULL, 0);
    1258             LogFlowFunc(("sent empty data, returned VINF_SUCCESS\n"));
    1259             return VINF_SUCCESS;
    1260         }
    1261         g_ctx.requestGuestFormat = g_ctx.guestTextFormat;
    1262         /* Send out a request for the data to the current clipboard owner */
    1263         XtGetSelectionValue(g_ctx.widget, g_ctx.atomClipboard, g_ctx.atomGuestTextFormat,
    1264                             vboxClipboardGetProc, 0, CurrentTime);
    1265         /* When the data arrives, the vboxClipboardGetProc callback will be called.  The
    1266            callback will signal the event semaphore when it has processed the data for us. */
    1267     }
    1268     else
    1269     {
    1270         vboxClipboardSendData(0, NULL, 0);
    1271         LogFlowFunc(("sent empty data, returned VERR_NOT_IMPLEMENTED\n"));
    1272         return VERR_NOT_IMPLEMENTED;
    1273     }
    1274     LogFlowFunc(("returned VINF_SUCCESS\n"));
    1275     return VINF_SUCCESS;
    1276 }
    1277 
    1278 
    1279 /** Terminate the guest side of the shared clipboard. */
    1280 void vboxClipboardDestroy (void)
    1281 {
    1282     LogFlowFunc(("\n"));
    1283 
    1284     /* Set the termination flag. */
    1285     XtAppSetExitFlag(g_ctx.appContext);
    1286     LogFlowFunc(("returning\n"));
    1287 }
    1288 
    1289 
    1290 /**
    1291  * The main loop of our clipboard reader.
    1292  */
    1293 static int vboxClipboardThread(RTTHREAD /* ThreadSelf */, void * /* pvUser */)
    1294 {
    1295     int rc;
    1296     LogFlowFunc(("Starting clipboard thread\n"));
    1297 
    1298     for (;;)
     220    LogFlowFunc(("Starting guest clipboard service\n"));
     221    bool fExiting = false;
     222
     223    while (!fExiting)
    1299224    {
    1300225        uint32_t Msg;
     
    1312237                     */
    1313238                    LogFlowFunc(("VBOX_SHARED_CLIPBOARD_HOST_MSG_FORMATS fFormats=%x\n", fFormats));
    1314                     vboxClipboardFormatAnnounce(fFormats);
     239                    ClipAnnounceFormatToX11(g_ctx.pBackend, fFormats);
    1315240                    break;
    1316241                }
     
    1320245                    /* The host needs data in the specified format. */
    1321246                    LogFlowFunc(("VBOX_SHARED_CLIPBOARD_HOST_MSG_READ_DATA fFormats=%x\n", fFormats));
    1322                     vboxClipboardReadGuestData(fFormats);
     247                    CLIPREADCBREQ *pReq;
     248                    pReq = (CLIPREADCBREQ *)RTMemAllocZ(sizeof(*pReq));
     249                    if (!pReq)
     250                    {
     251                        rc = VERR_NO_MEMORY;
     252                        fExiting = true;
     253                    }
     254                    else
     255                    {
     256                        pReq->u32Format = fFormats;
     257                        ClipRequestDataFromX11(g_ctx.pBackend, fFormats,
     258                                               pReq);
     259                    }
    1323260                    break;
    1324261                }
     
    1328265                    /* The host is terminating. */
    1329266                    LogFlowFunc(("VBOX_SHARED_CLIPBOARD_HOST_MSG_QUIT\n"));
    1330                     vboxClipboardDestroy();
    1331                     rc = VERR_INTERRUPTED;
     267                    if (RT_SUCCESS(ClipStopX11(g_ctx.pBackend)))
     268                        ClipDestructX11(g_ctx.pBackend);
     269                    fExiting = true;
    1332270                    break;
    1333271                }
     
    1337275            }
    1338276        }
    1339         if (rc == VERR_INTERRUPTED)
    1340         {
    1341             /* Wait for termination event. */
    1342             RTSemEventWait(g_ctx.terminating, RT_INDEFINITE_WAIT);
    1343             break;
    1344         }
    1345         if (RT_FAILURE(rc))
    1346         {
    1347             /* Wait a bit before retrying. */
    1348             if (RTSemEventWait(g_ctx.terminating, 1000) == VINF_SUCCESS)
    1349                 break;
    1350             continue;
    1351         }
    1352277
    1353278        LogFlow(("processed host event rc = %d\n", rc));
    1354279    }
    1355280    LogFlowFunc(("rc=%d\n", rc));
    1356     return rc;
    1357 }
    1358 
    1359 
    1360 /**
    1361  * Disconnect the guest clipboard from the host.
    1362  */
    1363 void vboxClipboardDisconnect(void)
    1364 {
    1365 #if 0
    1366     VMMDevHGCMDisconnect request;
    1367 #endif
    1368     LogFlowFunc(("\n"));
    1369 
    1370     AssertReturn(g_ctx.client != 0, (void) 0);
    1371     VbglR3ClipboardDisconnect(g_ctx.client);
    1372     LogFlowFunc(("returning\n"));
    1373 }
    1374 
    1375 /**
    1376  * Connect the guest clipboard to the host.
    1377  *
    1378  * @returns VBox status code
    1379  */
    1380 int vboxClipboardConnect(void)
    1381 {
    1382     int rc;
    1383     LogFlowFunc(("\n"));
    1384 
    1385     /* Only one client is supported for now */
    1386     AssertReturn(g_ctx.client == 0, VERR_NOT_SUPPORTED);
    1387 
    1388     /* Initialise the termination semaphore. */
    1389     RTSemEventCreate(&g_ctx.terminating);
    1390 
    1391     /* Initialise threading in X11 and in Xt. */
    1392     if (!XInitThreads() || !XtToolkitThreadInitialize())
    1393     {
    1394         LogRel(("VBoxClient: error initialising threads in X11, exiting.\n"));
    1395         return VERR_NOT_SUPPORTED;
    1396     }
    1397 
    1398     rc = VbglR3ClipboardConnect(&g_ctx.client);
    1399     if (RT_FAILURE(rc))
    1400     {
    1401         LogRel(("Error connecting to host. rc=%Rrc\n", rc));
    1402         return rc;
    1403     }
    1404     if (!g_ctx.client)
    1405     {
    1406         LogRel(("Invalid client ID of 0\n"));
    1407         return VERR_NOT_SUPPORTED;
    1408     }
    1409     /* Assume that if the guest clipboard already contains data then the
    1410      * user put it there for a reason. */
    1411     g_ctx.eOwner = GUEST;
    1412     LogFlowFunc(("g_ctx.client=%u rc=%Rrc\n", g_ctx.client, rc));
    1413     return rc;
    1414 }
    1415 
    1416 
    1417 /** We store information about the target formats we can handle in a global vector for internal
    1418     use. */
    1419 static void vboxClipboardAddFormat(const char *pszName, g_eClipboardFormat eFormat, unsigned hostFormat)
    1420 {
    1421     VBOXCLIPBOARDFORMAT sFormat;
    1422     LogFlowFunc(("pszName=%s, eFormat=%d, hostFormat=%u\n", pszName, eFormat, hostFormat));
    1423     /* Get an atom from the X server for that target format */
    1424     Atom atomFormat = XInternAtom(XtDisplay(g_ctx.widget), pszName, false);
    1425     LogFlow(("vboxClipboardAddFormat: atomFormat=%d\n", atomFormat));
    1426     if (atomFormat == 0)
    1427     {
    1428         LogFlowFunc(("atomFormat=0!  returning...\n"));
    1429         return;
    1430     }
    1431     sFormat.atom   = atomFormat;
    1432     sFormat.format = eFormat;
    1433     sFormat.hostFormat = hostFormat;
    1434     g_ctx.formatList.push_back(sFormat);
    1435     LogFlowFunc(("returning\n"));
    1436 }
    1437 
    1438 
    1439 /** Create the X11 window which we use to interact with the guest clipboard */
    1440 static int vboxClipboardCreateWindow(void)
    1441 {
    1442     /* Create a window and make it a clipboard viewer. */
    1443     int cArgc = 0;
    1444     char *pcArgv = 0;
    1445     String szFallbackResources[] = { (char*)"*.width: 1", (char*)"*.height: 1", 0 };
    1446 
    1447     /* Set up the Clipbard application context and main window. */
    1448     g_ctx.widget = XtOpenApplication(&g_ctx.appContext, "VBoxClipboard", 0, 0, &cArgc, &pcArgv,
    1449                                   szFallbackResources, applicationShellWidgetClass, 0, 0);
    1450     AssertMsgReturn(g_ctx.widget != 0, ("Failed to construct the X clipboard window\n"),
    1451                     VERR_ACCESS_DENIED);
    1452     XtSetMappedWhenManaged(g_ctx.widget, false);
    1453     XtRealizeWidget(g_ctx.widget);
    1454 
    1455     /* Get hold of the atoms which we need */
    1456     g_ctx.atomClipboard = XInternAtom(XtDisplay(g_ctx.widget), "CLIPBOARD", false /* only_if_exists */);
    1457     g_ctx.atomPrimary   = XInternAtom(XtDisplay(g_ctx.widget), "PRIMARY",   false);
    1458     g_ctx.atomTargets   = XInternAtom(XtDisplay(g_ctx.widget), "TARGETS",   false);
    1459     g_ctx.atomMultiple  = XInternAtom(XtDisplay(g_ctx.widget), "MULTIPLE",  false);
    1460     g_ctx.atomTimestamp = XInternAtom(XtDisplay(g_ctx.widget), "TIMESTAMP", false);
    1461     g_ctx.atomUtf16     = XInternAtom(XtDisplay(g_ctx.widget),
    1462                                       "text/plain;charset=ISO-10646-UCS-2", false);
    1463     g_ctx.atomUtf8      = XInternAtom(XtDisplay(g_ctx.widget), "UTF_STRING", false);
    1464     g_ctx.atomCText     = XInternAtom(XtDisplay(g_ctx.widget), "COMPOUND_TEXT", false);
    1465     /* And build up the vector of supported formats */
    1466 #ifdef USE_UTF16
    1467     vboxClipboardAddFormat("text/plain;charset=ISO-10646-UCS-2", UTF16, VBOX_SHARED_CLIPBOARD_FMT_UNICODETEXT);
    1468 #endif
    1469 #ifdef USE_UTF8
    1470     vboxClipboardAddFormat("UTF8_STRING", UTF8, VBOX_SHARED_CLIPBOARD_FMT_UNICODETEXT);
    1471     vboxClipboardAddFormat("text/plain;charset=UTF-8", UTF8, VBOX_SHARED_CLIPBOARD_FMT_UNICODETEXT);
    1472     vboxClipboardAddFormat("text/plain;charset=utf-8", UTF8, VBOX_SHARED_CLIPBOARD_FMT_UNICODETEXT);
    1473     vboxClipboardAddFormat("STRING", UTF8, VBOX_SHARED_CLIPBOARD_FMT_UNICODETEXT);
    1474     vboxClipboardAddFormat("TEXT", UTF8, VBOX_SHARED_CLIPBOARD_FMT_UNICODETEXT);
    1475     vboxClipboardAddFormat("text/plain", UTF8, VBOX_SHARED_CLIPBOARD_FMT_UNICODETEXT);
    1476 #endif
    1477 #ifdef USE_CTEXT
    1478     vboxClipboardAddFormat("COMPOUND_TEXT", CTEXT, VBOX_SHARED_CLIPBOARD_FMT_UNICODETEXT);
    1479 #endif
    1480     return VINF_SUCCESS;
    1481 }
    1482 
    1483 /** Initialise the guest side of the shared clipboard. */
    1484 int vboxClipboardMain(void)
    1485 {
    1486     int rc;
    1487     LogFlowFunc(("\n"));
    1488 
    1489     rc = vboxClipboardCreateWindow();
    1490     if (RT_FAILURE(rc))
    1491         return rc;
    1492 
    1493     rc = RTThreadCreate(&g_ctx.thread, vboxClipboardThread, 0, 0, RTTHREADTYPE_IO,
    1494                         RTTHREADFLAGS_WAITABLE, "SHCLIP");
    1495     AssertRCReturn(rc, rc);
    1496     /* Set up a timer to poll the host clipboard */
    1497     XtAppAddTimeOut(g_ctx.appContext, 200 /* ms */, vboxClipboardTimerProc, 0);
    1498 
    1499     XtAppMainLoop(g_ctx.appContext);
    1500     g_ctx.formatList.clear();
    1501     XtDestroyApplicationContext(g_ctx.appContext);
    1502     /* Set the termination signal. */
    1503     RTSemEventSignal(g_ctx.terminating);
    1504     LogFlowFunc(("returning %d\n", rc));
    1505281    return rc;
    1506282}
     
    1518294        if (RT_SUCCESS(rc))
    1519295            rc = vboxClipboardMain();
     296        if (RT_FAILURE(rc))
     297            LogFunc(("guest clipboard service terminated annormally: return code %Rrc\n", rc));
    1520298        return rc;
    1521299    }
Note: See TracChangeset for help on using the changeset viewer.

© 2024 Oracle Support Privacy / Do Not Sell My Info Terms of Use Trademark Policy Automated Access Etiquette