VirtualBox

Ignore:
Timestamp:
Mar 31, 2009 7:58:06 PM (16 years ago)
Author:
vboxsync
svn:sync-xref-src-repo-rev:
45483
Message:

HostServices/SharedClipboard: split up the X11 host clipboard code into separate files for the X11-specific and host-specific parts

Location:
trunk/src/VBox/HostServices/SharedClipboard
Files:
3 edited

Legend:

Unmodified
Added
Removed
  • trunk/src/VBox/HostServices/SharedClipboard/Makefile.kmk

    r18398 r18585  
    4444  VBoxSharedClipboard_SOURCES += \
    4545        $(PATH_ROOT)/src/VBox/GuestHost/SharedClipboard/clipboard-helper.cpp \
     46        $(PATH_ROOT)/src/VBox/GuestHost/SharedClipboard/x11-clipboard.cpp \
    4647        x11-clipboard.cpp
    4748 else
  • trunk/src/VBox/HostServices/SharedClipboard/VBoxClipboard.h

    r15928 r18585  
    2525#define LOG_GROUP LOG_GROUP_HGCM
    2626#include <VBox/log.h>
    27 
    28 enum {
    29     /** The number of milliseconds before the clipboard times out. */
    30     CLIPBOARDTIMEOUT = 5000
    31 };
    3227
    3328struct _VBOXCLIPBOARDCONTEXT;
  • trunk/src/VBox/HostServices/SharedClipboard/x11-clipboard.cpp

    r18582 r18585  
    2121 */
    2222
     23/** @todo create a clipboard log group */
     24#define LOG_GROUP LOG_GROUP_HGCM
     25
    2326#include <string.h>
    24 #include <stdio.h>
    25 #include <stdint.h>
    26 
    27 #ifdef RT_OS_SOLARIS
    28 #include <tsol/label.h>
    29 #endif
    30 
    31 #include <vector>
    32 
    33 #include <X11/Xlib.h>
    34 #include <X11/Xatom.h>
    35 #include <X11/Intrinsic.h>
    36 #include <X11/Shell.h>
    37 #include <X11/Xproto.h>
    38 #include <X11/StringDefs.h>
    39 
    40 #include <iprt/alloc.h>
     27
    4128#include <iprt/asm.h>        /* For atomic operations */
    4229#include <iprt/assert.h>
    43 #include <iprt/env.h>
    4430#include <iprt/mem.h>
    45 #include <iprt/string.h>
    46 #include <iprt/thread.h>
    47 #include <iprt/process.h>
    4831#include <iprt/semaphore.h>
    4932
    50 #include <VBox/GuestHost/clipboard-helper.h>
     33#include <VBox/log.h>
     34
     35#include <VBox/GuestHost/SharedClipboard.h>
    5136#include <VBox/HostServices/VBoxClipboardSvc.h>
    5237
    5338#include "VBoxClipboard.h"
    54 
    55 /** Do we want to test Utf16 by disabling other text formats? */
    56 static bool g_testUtf16 = false;
    57 /** Do we want to test Utf8 by disabling other text formats? */
    58 static bool g_testUtf8 = false;
    59 /** Do we want to test compount text by disabling other text formats? */
    60 static bool g_testCText = false;
    61 /** Are we currently debugging the clipboard code? */
    62 static bool g_debugClipboard = false;
    63 
    64 /** The different clipboard formats which we support. */
    65 enum g_eClipboardFormats
    66 {
    67     INVALID = 0,
    68     TARGETS,
    69     CTEXT,
    70     UTF8,
    71     UTF16
    72 };
    73 
    74 /** The X11 clipboard uses several names for the same format.  This structure maps an X11
    75     name to a format. */
    76 typedef struct {
    77     Atom atom;
    78     g_eClipboardFormats format;
    79     unsigned guestFormat;
    80 } VBOXCLIPBOARDFORMAT;
    81 
    82 /** Does X11 or VBox currently own the clipboard? */
    83 enum g_eOwner { NONE = 0, X11, VB };
    84 
    85 typedef struct {
    86     /** BMP file type marker - must always contain 'BM' */
    87     uint16_t bfType;
    88     /** The size of the BMP file in bytes (the MS docs say that this is not reliable) */
    89     uint32_t bfSize;
    90     /** Reserved, must always be zero */
    91     uint16_t bfReserved1;
    92     /** Reserved, must always be zero */
    93     uint16_t bfReserved2;
    94     /** Offset from the beginning of this header to the actual image bits */
    95 } VBOXBITMAPFILEHEADER;
    9639
    9740/** Global context information used by the host glue for the X11 clipboard
     
    11558};
    11659
    117 /** Global context information used by the X11 clipboard backend */
    118 struct _VBOXCLIPBOARDCONTEXTX11
    119 {
    120     /** Opaque data structure describing the front-end. */
    121     VBOXCLIPBOARDCONTEXT *pFrontend;
    122     /** The X Toolkit application context structure */
    123     XtAppContext appContext;
    124 
    125     /** We have a separate thread to wait for Window and Clipboard events */
    126     RTTHREAD thread;
    127     /** The X Toolkit widget which we use as our clipboard client.  It is never made visible. */
    128     Widget widget;
    129 
    130     /** X11 atom refering to the clipboard: CLIPBOARD */
    131     Atom atomClipboard;
    132     /** X11 atom refering to the selection: PRIMARY */
    133     Atom atomPrimary;
    134     /** X11 atom refering to the clipboard targets: TARGETS */
    135     Atom atomTargets;
    136     /** X11 atom refering to the clipboard multiple target: MULTIPLE */
    137     Atom atomMultiple;
    138     /** X11 atom refering to the clipboard timestamp target: TIMESTAMP */
    139     Atom atomTimestamp;
    140     /** X11 atom refering to the clipboard utf16 text format: text/plain;charset=ISO-10646-UCS-2 */
    141     Atom atomUtf16;
    142     /** X11 atom refering to the clipboard utf8 text format: UTF8_STRING */
    143     Atom atomUtf8;
    144     /** X11 atom refering to the clipboard compound text format: COMPOUND_TEXT */
    145     Atom atomCText;
    146 
    147     /** A list of the X11 formats which we support, mapped to our identifier for them, in the
    148         order we prefer to have them in. */
    149     std::vector<VBOXCLIPBOARDFORMAT> formatList;
    150 
    151     /** Does VBox or X11 currently own the clipboard? */
    152     volatile enum g_eOwner eOwner;
    153 
    154     /** What is the best text format X11 has to offer?  INVALID for none. */
    155     g_eClipboardFormats X11TextFormat;
    156     /** Atom corresponding to the X11 text format */
    157     Atom atomX11TextFormat;
    158     /** What is the best bitmap format X11 has to offer?  INVALID for none. */
    159     g_eClipboardFormats X11BitmapFormat;
    160     /** Atom corresponding to the X11 Bitmap format */
    161     Atom atomX11BitmapFormat;
    162     /** What formats does VBox have on offer? */
    163     int vboxFormats;
    164     /** Windows hosts and guests cache the clipboard data they receive.
    165      * Since we have no way of knowing whether their cache is still valid,
    166      * we always send a "data changed" message after a successful transfer
    167      * to invalidate it. */
    168     bool notifyVBox;
    169 
    170     /** Since the clipboard data moves asynchronously, we use an event
    171      * semaphore to wait for it.  When a function issues a request for
    172      * clipboard data it must wait for this semaphore, which is triggered
    173      * when the data arrives. */
    174     RTSEMEVENT waitForData;
    175 };
    176 
    177 typedef struct _VBOXCLIPBOARDCONTEXTX11 VBOXCLIPBOARDCONTEXTX11;
    178 
    179 /** A structure containing information about where to store a request
    180  * for the X11 clipboard contents. */
    181 struct _VBOXCLIPBOARDREQUEST
    182 {
    183     /** The buffer to write X11 clipboard data to (valid during a request
    184      * for the clipboard contents) */
    185     void *pv;
    186     /** The size of the buffer to write X11 clipboard data to (valid during
    187      * a request for the clipboard contents) */
    188     unsigned cb;
    189     /** The size of the X11 clipboard data written to the buffer (valid
    190      * during a request for the clipboard contents) */
    191     uint32_t *pcbActual;
    192 };
    193 
    194 typedef struct _VBOXCLIPBOARDREQUEST VBOXCLIPBOARDREQUEST;
    195 
    19660/* Only one client is supported. There seems to be no need for more clients.
    19761 */
    19862static VBOXCLIPBOARDCONTEXT g_ctxHost;
    199 static VBOXCLIPBOARDCONTEXTX11 g_ctxX11;
    200 
    201 /* Are we actually connected to the X server? */
    202 static bool g_fHaveX11;
    203 
    204 /** @todo this is a temporary declaration. */
    205 static void vboxClipboardFormatAnnounceBackend (uint32_t u32Formats);
    20663
    20764/**
     
    21673 * @note   Host glue code.
    21774 */
    218 static int vboxClipboardReadDataFromVBox (VBOXCLIPBOARDCONTEXT *pCtx, uint32_t u32Format, void **ppv, uint32_t *pcb)
     75int VBoxX11ClipboardReadVBoxData (VBOXCLIPBOARDCONTEXT *pCtx,
     76                                   uint32_t u32Format, void **ppv,
     77                                   uint32_t *pcb)
    21978{
    22079    volatile VBOXCLIPBOARDCLIENTDATA *pClient = pCtx->pClient;
     
    22685         * data from X11. */
    22786        LogFunc(("host requested guest clipboard data after guest had disconnected.\n"));
    228         vboxClipboardFormatAnnounceBackend(0);
     87        VBoxX11ClipboardAnnounceVBoxFormat(0);
    22988        pCtx->waiter = NONE;
    23089        return VERR_TIMEOUT;
     
    283142
    284143/**
    285  * Convert the UTF-16 text obtained from the X11 clipboard to UTF-16LE with
    286  * Windows EOLs, place it in the buffer supplied and signal that data has
    287  * arrived.
    288  *
    289  * @param pValue      Source UTF-16 text
    290  * @param cwSourceLen Length in 16-bit words of the source text
    291  * @param pv          Where to store the converted data
    292  * @param cb          Length in bytes of the buffer pointed to by cb
    293  * @param pcbActual   Where to store the size of the converted data
    294  * @param pClient     Pointer to the client context structure
    295  * @note  X11 backend code, called from the Xt callback when we wish to read
    296  *        the X11 clipboard.
    297  */
    298 static void vboxClipboardGetUtf16(XtPointer pValue, unsigned cwSrcLen,
    299                                   void *pv, unsigned cb,
    300                                   uint32_t *pcbActual)
    301 {
    302     size_t cwDestLen;
    303     PRTUTF16 pu16SrcText = reinterpret_cast<PRTUTF16>(pValue);
    304     PRTUTF16 pu16DestText = reinterpret_cast<PRTUTF16>(pv);
    305 
    306     LogFlowFunc (("converting Utf-16 to Utf-16LE.  cwSrcLen=%d, cb=%d, pu16SrcText+1=%.*ls\n",
    307                    cwSrcLen, cb, cwSrcLen - 1, pu16SrcText + 1));
    308     *pcbActual = 0;  /* Only set this to the right value on success. */
    309     /* How long will the converted text be? */
    310     int rc = vboxClipboardUtf16GetWinSize(pu16SrcText, cwSrcLen, &cwDestLen);
    311     if (RT_SUCCESS(rc) && (cb < cwDestLen * 2))
    312     {
    313         /* Not enough buffer space provided - report the amount needed. */
    314         LogFlowFunc (("guest buffer too small: size %d bytes, needed %d.  Returning.\n",
    315                        cb, cwDestLen * 2));
    316         *pcbActual = cwDestLen * 2;
    317         rc = VERR_BUFFER_OVERFLOW;
    318     }
    319     /* Convert the text. */
    320     if (RT_SUCCESS(rc))
    321         rc = vboxClipboardUtf16LinToWin(pu16SrcText, cwSrcLen, pu16DestText, cb / 2);
    322     if (RT_SUCCESS(rc))
    323     {
    324         LogFlowFunc (("converted string is %.*ls\n", cwDestLen, pu16DestText));
    325         *pcbActual = cwDestLen * 2;
    326     }
    327     /* We need to do this whether we succeed or fail. */
    328     XtFree(reinterpret_cast<char *>(pValue));
    329     RTSemEventSignal(g_ctxX11.waitForData);
    330     LogFlowFunc(("Returning.  Status is %Rrc\n", rc));
    331 }
    332 
    333 /**
    334  * Convert the UTF-8 text obtained from the X11 clipboard to UTF-16LE with
    335  * Windows EOLs, place it in the buffer supplied and signal that data has
    336  * arrived.
    337  *
    338  * @param pValue      Source UTF-8 text
    339  * @param cbSourceLen Length in 8-bit bytes of the source text
    340  * @param pv          Where to store the converted data
    341  * @param cb          Length in bytes of the buffer pointed to by pv
    342  * @param pcbActual   Where to store the size of the converted data
    343  * @param pClient     Pointer to the client context structure
    344  * @note  X11 backend code, called from the Xt callback when we wish to read
    345  *        the X11 clipboard.
    346  */
    347 static void vboxClipboardGetUtf8FromX11(XtPointer pValue, unsigned cbSrcLen,
    348                                         void *pv, unsigned cb,
    349                                         uint32_t *pcbActual)
    350 {
    351     size_t cwSrcLen, cwDestLen;
    352     char *pu8SrcText = reinterpret_cast<char *>(pValue);
    353     PRTUTF16 pu16SrcText = NULL;
    354     PRTUTF16 pu16DestText = reinterpret_cast<PRTUTF16>(pv);
    355 
    356     LogFlowFunc (("converting Utf-8 to Utf-16LE.  cbSrcLen=%d, cb=%d, pu8SrcText=%.*s\n",
    357                    cbSrcLen, cb, cbSrcLen, pu8SrcText));
    358     *pcbActual = 0;  /* Only set this to the right value on success. */
    359     /* First convert the UTF8 to UTF16 */
    360     int rc = RTStrToUtf16Ex(pu8SrcText, cbSrcLen, &pu16SrcText, 0, &cwSrcLen);
    361     /* Check how much longer will the converted text will be. */
    362     if (RT_SUCCESS(rc))
    363         rc = vboxClipboardUtf16GetWinSize(pu16SrcText, cwSrcLen, &cwDestLen);
    364     if (RT_SUCCESS(rc) && (cb < cwDestLen * 2))
    365     {
    366         /* Not enough buffer space provided - report the amount needed. */
    367         LogFlowFunc (("guest buffer too small: size %d bytes, needed %d.  Returning.\n",
    368                        cb, cwDestLen * 2));
    369         *pcbActual = cwDestLen * 2;
    370         rc = VERR_BUFFER_OVERFLOW;
    371     }
    372     /* Convert the text. */
    373     if (RT_SUCCESS(rc))
    374         rc = vboxClipboardUtf16LinToWin(pu16SrcText, cwSrcLen, pu16DestText, cb / 2);
    375     if (RT_SUCCESS(rc))
    376     {
    377         LogFlowFunc (("converted string is %.*ls.\n", cwDestLen, pu16DestText));
    378         *pcbActual = cwDestLen * 2;
    379     }
    380     XtFree(reinterpret_cast<char *>(pValue));
    381     RTUtf16Free(pu16SrcText);
    382     RTSemEventSignal(g_ctxX11.waitForData);
    383     LogFlowFunc(("Returning.  Status is %Rrc", rc));
    384 }
    385 
    386 /**
    387  * Convert the COMPOUND_TEXT obtained from the X11 clipboard to UTF-16LE with
    388  * Windows EOLs, place it in the buffer supplied and signal that data has
    389  * arrived.
    390  *
    391  * @param pValue      Source COMPOUND_TEXT
    392  * @param cbSourceLen Length in 8-bit bytes of the source text
    393  * @param pv          Where to store the converted data
    394  * @param cb          Length in bytes of the buffer pointed to by pv
    395  * @param pcbActual   Where to store the size of the converted data
    396  * @param pClient     Pointer to the client context structure
    397  * @note  X11 backend code, called from the Xt callback when we wish to read
    398  *        the X11 clipboard.
    399  */
    400 static void vboxClipboardGetCTextFromX11(XtPointer pValue, unsigned cbSrcLen,
    401                                          void *pv, unsigned cb,
    402                                          uint32_t *pcbActual)
    403 {
    404     size_t cwSrcLen, cwDestLen;
    405     char **ppu8SrcText = NULL;
    406     PRTUTF16 pu16SrcText = NULL;
    407     PRTUTF16 pu16DestText = reinterpret_cast<PRTUTF16>(pv);
    408     XTextProperty property;
    409     int rc = VINF_SUCCESS;
    410     int cProps;
    411 
    412     LogFlowFunc (("converting COMPOUND TEXT to Utf-16LE.  cbSrcLen=%d, cb=%d, pu8SrcText=%.*s\n",
    413                    cbSrcLen, cb, cbSrcLen, reinterpret_cast<char *>(pValue)));
    414     *pcbActual = 0;  /* Only set this to the right value on success. */
    415     /* First convert the compound text to Utf8 */
    416     property.value = reinterpret_cast<unsigned char *>(pValue);
    417     property.encoding = g_ctxX11.atomCText;
    418     property.format = 8;
    419     property.nitems = cbSrcLen;
    420 #ifdef RT_OS_SOLARIS
    421     int xrc = XmbTextPropertyToTextList(XtDisplay(g_ctxX11.widget), &property, &ppu8SrcText, &cProps);
    422 #else
    423     int xrc = Xutf8TextPropertyToTextList(XtDisplay(g_ctxX11.widget), &property, &ppu8SrcText, &cProps);
    424 #endif
    425     XtFree(reinterpret_cast<char *>(pValue));
    426     if (xrc < 0)
    427         switch(xrc)
    428         {
    429         case XNoMemory:
    430             rc = VERR_NO_MEMORY;
    431             break;
    432         case XLocaleNotSupported:
    433         case XConverterNotFound:
    434             rc = VERR_NOT_SUPPORTED;
    435             break;
    436         default:
    437             rc = VERR_UNRESOLVED_ERROR;
    438         }
    439     /* Now convert the UTF8 to UTF16 */
    440     if (RT_SUCCESS(rc))
    441         rc = RTStrToUtf16Ex(*ppu8SrcText, cbSrcLen, &pu16SrcText, 0, &cwSrcLen);
    442     /* Check how much longer will the converted text will be. */
    443     if (RT_SUCCESS(rc))
    444         rc = vboxClipboardUtf16GetWinSize(pu16SrcText, cwSrcLen, &cwDestLen);
    445     if (RT_SUCCESS(rc) && (cb < cwDestLen * 2))
    446     {
    447         /* Not enough buffer space provided - report the amount needed. */
    448         LogFlowFunc (("guest buffer too small: size %d bytes, needed %d.  Returning.\n",
    449                        cb, cwDestLen * 2));
    450         *pcbActual = cwDestLen * 2;
    451         rc = VERR_BUFFER_OVERFLOW;
    452     }
    453     /* Convert the text. */
    454     if (RT_SUCCESS(rc))
    455         rc = vboxClipboardUtf16LinToWin(pu16SrcText, cwSrcLen, pu16DestText, cb / 2);
    456     if (RT_SUCCESS(rc))
    457     {
    458         LogFlowFunc (("converted string is %.*ls\n", cwDestLen, pu16DestText));
    459         *pcbActual = cwDestLen * 2;
    460     }
    461     if (ppu8SrcText != NULL)
    462         XFreeStringList(ppu8SrcText);
    463     RTUtf16Free(pu16SrcText);
    464     LogFlowFunc(("Returning.  Status is %Rrc\n", rc));
    465     RTSemEventSignal(g_ctxX11.waitForData);
    466 }
    467 
    468 /**
    469  * Convert the Latin1 text obtained from the X11 clipboard to UTF-16LE with
    470  * Windows EOLs, place it in the buffer supplied and signal that data has
    471  * arrived.
    472  *
    473  * @param pValue      Source Latin1 text
    474  * @param cbSourceLen Length in 8-bit bytes of the source text
    475  * @param pv          Where to store the converted data
    476  * @param cb          Length in bytes of the buffer pointed to by cb
    477  * @param pcbActual   Where to store the size of the converted data
    478  * @param pClient     Pointer to the client context structure
    479  * @note  X11 backend code, called from the Xt callback when we wish to read
    480  *        the X11 clipboard.
    481  */
    482 static void vboxClipboardGetLatin1FromX11(XtPointer pValue,
    483                                           unsigned cbSourceLen, void *pv,
    484                                           unsigned cb, uint32_t *pcbActual)
    485 {
    486     unsigned cwDestLen = cbSourceLen + 1;
    487     char *pu8SourceText = reinterpret_cast<char *>(pValue);
    488     PRTUTF16 pu16DestText = reinterpret_cast<PRTUTF16>(pv);
    489     int rc = VINF_SUCCESS;
    490 
    491     LogFlowFunc (("converting Latin1 to Utf-16LE.  Original is %.*s\n",
    492                   cbSourceLen, pu8SourceText));
    493     *pcbActual = 0;  /* Only set this to the right value on success. */
    494     for (unsigned i = 0; i < cbSourceLen; i++)
    495         if (pu8SourceText[i] == LINEFEED)
    496             ++cwDestLen;
    497     if (cb < cwDestLen * 2)
    498     {
    499         /* Not enough buffer space provided - report the amount needed. */
    500         LogFlowFunc (("guest buffer too small: size %d bytes\n", cb));
    501         *pcbActual = cwDestLen * 2;
    502         rc = VERR_BUFFER_OVERFLOW;
    503     }
    504     if (RT_SUCCESS(rc))
    505     {
    506         for (unsigned i = 0, j = 0; i < cbSourceLen; ++i, ++j)
    507             if (pu8SourceText[i] != LINEFEED)
    508                 pu16DestText[j] = pu8SourceText[i];  /* latin1 < utf-16LE */
    509             else
    510             {
    511                 pu16DestText[j] = CARRIAGERETURN;
    512                 ++j;
    513                 pu16DestText[j] = LINEFEED;
    514             }
    515         pu16DestText[cwDestLen - 1] = 0;
    516         *pcbActual = cwDestLen * 2;
    517         LogFlowFunc (("converted text is %.*ls\n", cwDestLen, pu16DestText));
    518     }
    519     XtFree(reinterpret_cast<char *>(pValue));
    520     RTSemEventSignal(g_ctxX11.waitForData);
    521     LogFlowFunc(("Returning.  Status is %Rrc\n", rc));
    522 }
    523 
    524 /**
    525  * Convert the text obtained from the X11 clipboard to UTF-16LE with Windows
    526  * EOLs, place it in the buffer supplied and signal that data has arrived.
    527  * @note  X11 backend code, callback for XtGetSelectionValue, for use when
    528  *        the X11 clipboard contains a text format we understand.
    529  */
    530 static void vboxClipboardGetDataFromX11(Widget, XtPointer pClientData,
    531                                         Atom * /* selection */,
    532                                         Atom *atomType,
    533                                         XtPointer pValue,
    534                                         long unsigned int *pcLen,
    535                                         int *piFormat)
    536 {
    537     VBOXCLIPBOARDREQUEST *pRequest
    538         = reinterpret_cast<VBOXCLIPBOARDREQUEST *>(pClientData);
    539     LogFlowFunc(("pClientData=%p, *pcLen=%lu, *piFormat=%d\n", pClientData,
    540                  *pcLen, *piFormat));
    541     LogFlowFunc(("g_ctxX11.X11TextFormat=%d, pRequest->cb=%d\n",
    542                  g_ctxX11.X11TextFormat, pRequest->cb));
    543     unsigned cTextLen = (*pcLen) * (*piFormat) / 8;
    544     /* The X Toolkit may have failed to get the clipboard selection for us. */
    545     if (*atomType == XT_CONVERT_FAIL)
    546         return;
    547     /* The clipboard selection may have changed before we could get it. */
    548     if (NULL == pValue)
    549         return;
    550     /* In which format is the clipboard data? */
    551     switch (g_ctxX11.X11TextFormat)
    552     {
    553     case UTF16:
    554         vboxClipboardGetUtf16(pValue, cTextLen / 2, pRequest->pv,
    555                               pRequest->cb, pRequest->pcbActual);
    556         break;
    557     case CTEXT:
    558         vboxClipboardGetCTextFromX11(pValue, cTextLen, pRequest->pv,
    559                                      pRequest->cb, pRequest->pcbActual);
    560         break;
    561     case UTF8:
    562     {
    563         /* If we are given broken Utf-8, we treat it as Latin1.  Is this acceptable? */
    564         size_t cStringLen;
    565         char *pu8SourceText = reinterpret_cast<char *>(pValue);
    566 
    567         if ((g_ctxX11.X11TextFormat == UTF8)
    568             && (RTStrUniLenEx(pu8SourceText, *pcLen, &cStringLen) == VINF_SUCCESS))
    569         {
    570             vboxClipboardGetUtf8FromX11(pValue, cTextLen, pRequest->pv,
    571                                      pRequest->cb, pRequest->pcbActual);
    572             break;
    573         }
    574         else
    575         {
    576             vboxClipboardGetLatin1FromX11(pValue, cTextLen, pRequest->pv,
    577                                      pRequest->cb, pRequest->pcbActual);
    578             break;
    579         }
    580     }
    581     default:
    582         LogFunc (("bad target format\n"));
    583         XtFree(reinterpret_cast<char *>(pValue));
    584         return;
    585     }
    586     g_ctxX11.notifyVBox = true;
    587 }
    588 
    589 /**
    590144 * Report formats available in the X11 clipboard to VBox.
    591145 * @param  pCtx        Opaque context pointer for the glue code
     
    599153                              VBOX_SHARED_CLIPBOARD_HOST_MSG_FORMATS,
    600154                              u32Formats);
    601 }
    602 
    603 
    604 /**
    605  * Notify the host clipboard about the data formats we support, based on the
    606  * "targets" (available data formats) information obtained from the X11
    607  * clipboard.
    608  * @note  X11 backend code, callback for XtGetSelectionValue, called when we
    609  *        poll for available targets.
    610  */
    611 static void vboxClipboardGetTargetsFromX11(Widget,
    612                                            XtPointer /* pClientData */,
    613                                            Atom * /* selection */,
    614                                            Atom *atomType,
    615                                            XtPointer pValue,
    616                                            long unsigned int *pcLen,
    617                                            int *piFormat)
    618 {
    619     Atom *atomTargets = reinterpret_cast<Atom *>(pValue);
    620     unsigned cAtoms = *pcLen;
    621     g_eClipboardFormats eBestTarget = INVALID;
    622     Atom atomBestTarget = None;
    623 
    624     Log3 (("%s: called\n", __PRETTY_FUNCTION__));
    625     if (*atomType == XT_CONVERT_FAIL)
    626     {
    627         LogFunc (("reading clipboard from host, X toolkit failed to convert the selection\n"));
    628         return;
    629     }
    630 
    631     for (unsigned i = 0; i < cAtoms; ++i)
    632     {
    633         for (unsigned j = 0; j != g_ctxX11.formatList.size(); ++j)
    634             if (g_ctxX11.formatList[j].atom == atomTargets[i])
    635             {
    636                 if (eBestTarget < g_ctxX11.formatList[j].format)
    637                 {
    638                     eBestTarget = g_ctxX11.formatList[j].format;
    639                     atomBestTarget = g_ctxX11.formatList[j].atom;
    640                 }
    641                 break;
    642             }
    643         if (g_debugClipboard)
    644         {
    645             char *szAtomName = XGetAtomName(XtDisplay(g_ctxX11.widget), atomTargets[i]);
    646             if (szAtomName != 0)
    647             {
    648                 Log2 (("%s: the host offers target %s\n", __PRETTY_FUNCTION__,
    649                        szAtomName));
    650                 XFree(szAtomName);
    651             }
    652         }
    653     }
    654     g_ctxX11.atomX11TextFormat = atomBestTarget;
    655     if ((eBestTarget != g_ctxX11.X11TextFormat) || (g_ctxX11.notifyVBox == true))
    656     {
    657         uint32_t u32Formats = 0;
    658         if (g_debugClipboard)
    659         {
    660             if (atomBestTarget != None)
    661             {
    662                 char *szAtomName = XGetAtomName(XtDisplay(g_ctxX11.widget), atomBestTarget);
    663                 Log2 (("%s: switching to host text target %s.  Available targets are:\n",
    664                        __PRETTY_FUNCTION__, szAtomName));
    665                 XFree(szAtomName);
    666             }
    667             else
    668                 Log2(("%s: no supported host text target found.  Available targets are:\n",
    669                       __PRETTY_FUNCTION__));
    670             for (unsigned i = 0; i < cAtoms; ++i)
    671             {
    672                 char *szAtomName = XGetAtomName(XtDisplay(g_ctxX11.widget), atomTargets[i]);
    673                 if (szAtomName != 0)
    674                 {
    675                     Log2 (("%s:     %s\n", __PRETTY_FUNCTION__, szAtomName));
    676                     XFree(szAtomName);
    677                 }
    678             }
    679         }
    680         g_ctxX11.X11TextFormat = eBestTarget;
    681         if (eBestTarget != INVALID)
    682             u32Formats |= VBOX_SHARED_CLIPBOARD_FMT_UNICODETEXT;
    683         VBoxX11ClipboardReportX11Formats(g_ctxX11.pFrontend, u32Formats);
    684         g_ctxX11.notifyVBox = false;
    685     }
    686     XtFree(reinterpret_cast<char *>(pValue));
    687 }
    688 
    689 /**
    690  * This timer callback is called every 200ms to check the contents of the X11
    691  * clipboard.
    692  * @note  X11 backend code, callback for XtAppAddTimeOut, recursively
    693  *        re-armed.
    694  * @todo  Use the XFIXES extension to check for new clipboard data when
    695  *        available.
    696  */
    697 static void vboxClipboardPollX11ForTargets(XtPointer /* pUserData */, XtIntervalId * /* hTimerId */)
    698 {
    699     Log3 (("%s: called\n", __PRETTY_FUNCTION__));
    700     /* Get the current clipboard contents */
    701     if (g_ctxX11.eOwner == X11)
    702     {
    703         Log3 (("%s: requesting the targets that the host clipboard offers\n",
    704                __PRETTY_FUNCTION__));
    705         XtGetSelectionValue(g_ctxX11.widget, g_ctxX11.atomClipboard,
    706                             g_ctxX11.atomTargets,
    707                             vboxClipboardGetTargetsFromX11, NULL,
    708                             CurrentTime);
    709     }
    710     /* Re-arm our timer */
    711     XtAppAddTimeOut(g_ctxX11.appContext, 200 /* ms */,
    712                     vboxClipboardPollX11ForTargets, 0);
    713 }
    714 
    715 /** We store information about the target formats we can handle in a global
    716  * vector for internal use.
    717  * @note  X11 backend code.
    718  */
    719 static void vboxClipboardAddFormat(const char *pszName, g_eClipboardFormats eFormat,
    720                                    unsigned guestFormat)
    721 {
    722     VBOXCLIPBOARDFORMAT sFormat;
    723     /* Get an atom from the X server for that target format */
    724     Atom atomFormat = XInternAtom(XtDisplay(g_ctxX11.widget), pszName, false);
    725     sFormat.atom   = atomFormat;
    726     sFormat.format = eFormat;
    727     sFormat.guestFormat = guestFormat;
    728     g_ctxX11.formatList.push_back(sFormat);
    729     LogFlow (("vboxClipboardAddFormat: added format %s (%d)\n", pszName, eFormat));
    730 }
    731 
    732 /**
    733  * The main loop of our clipboard reader.
    734  * @note  X11 backend code.
    735  */
    736 static int vboxClipboardThread(RTTHREAD self, void * /* pvUser */)
    737 {
    738     LogRel(("Shared clipboard: starting host clipboard thread\n"));
    739 
    740     /* Set up a timer to poll the host clipboard */
    741     XtAppAddTimeOut(g_ctxX11.appContext, 200 /* ms */, vboxClipboardPollX11ForTargets, 0);
    742 
    743     XtAppMainLoop(g_ctxX11.appContext);
    744     g_ctxX11.formatList.clear();
    745     LogRel(("Shared clipboard: host clipboard thread terminated successfully\n"));
    746     return VINF_SUCCESS;
    747 }
    748 
    749 /** X11 specific initialisation for the shared clipboard.
    750  * @note  X11 backend code.
    751  */
    752 int vboxClipboardInitX11 (void)
    753 {
    754     /* Create a window and make it a clipboard viewer. */
    755     int cArgc = 0;
    756     char *pcArgv = 0;
    757     int rc = VINF_SUCCESS;
    758     // static String szFallbackResources[] = { (char*)"*.width: 1", (char*)"*.height: 1", NULL };
    759     Display *pDisplay;
    760 
    761     /* Make sure we are thread safe */
    762     XtToolkitThreadInitialize();
    763     /* Set up the Clipbard application context and main window.  We call all these functions
    764        directly instead of calling XtOpenApplication() so that we can fail gracefully if we
    765        can't get an X11 display. */
    766     XtToolkitInitialize();
    767     g_ctxX11.appContext = XtCreateApplicationContext();
    768     // XtAppSetFallbackResources(g_ctxX11.appContext, szFallbackResources);
    769     pDisplay = XtOpenDisplay(g_ctxX11.appContext, 0, 0, "VBoxClipboard", 0, 0, &cArgc, &pcArgv);
    770     if (NULL == pDisplay)
    771     {
    772         LogRel(("Shared clipboard: failed to connect to the host clipboard - the window system may not be running.\n"));
    773         rc = VERR_NOT_SUPPORTED;
    774     }
    775     if (RT_SUCCESS(rc))
    776     {
    777         g_ctxX11.widget = XtVaAppCreateShell(0, "VBoxClipboard", applicationShellWidgetClass, pDisplay,
    778                                           XtNwidth, 1, XtNheight, 1, NULL);
    779         if (NULL == g_ctxX11.widget)
    780         {
    781             LogRel(("Shared clipboard: failed to construct the X11 window for the host clipboard manager.\n"));
    782             rc = VERR_NO_MEMORY;
    783         }
    784     }
    785     if (RT_SUCCESS(rc))
    786     {
    787         XtSetMappedWhenManaged(g_ctxX11.widget, false);
    788         XtRealizeWidget(g_ctxX11.widget);
    789 
    790         /* Get hold of the atoms which we need */
    791         g_ctxX11.atomClipboard = XInternAtom(XtDisplay(g_ctxX11.widget), "CLIPBOARD", false /* only_if_exists */);
    792         g_ctxX11.atomPrimary   = XInternAtom(XtDisplay(g_ctxX11.widget), "PRIMARY",   false);
    793         g_ctxX11.atomTargets   = XInternAtom(XtDisplay(g_ctxX11.widget), "TARGETS",   false);
    794         g_ctxX11.atomMultiple  = XInternAtom(XtDisplay(g_ctxX11.widget), "MULTIPLE",  false);
    795         g_ctxX11.atomTimestamp = XInternAtom(XtDisplay(g_ctxX11.widget), "TIMESTAMP", false);
    796         g_ctxX11.atomUtf16     = XInternAtom(XtDisplay(g_ctxX11.widget),
    797                                           "text/plain;charset=ISO-10646-UCS-2", false);
    798         g_ctxX11.atomUtf8      = XInternAtom(XtDisplay(g_ctxX11.widget), "UTF_STRING", false);
    799         /* And build up the vector of supported formats */
    800         g_ctxX11.atomCText     = XInternAtom(XtDisplay(g_ctxX11.widget), "COMPOUND_TEXT", false);
    801         /* And build up the vector of supported formats */
    802         if (!g_testUtf8 && !g_testCText)
    803             vboxClipboardAddFormat("text/plain;charset=ISO-10646-UCS-2", UTF16,
    804                                    VBOX_SHARED_CLIPBOARD_FMT_UNICODETEXT);
    805         if (!g_testUtf16 && !g_testCText)
    806         {
    807             vboxClipboardAddFormat("UTF8_STRING", UTF8,
    808                                    VBOX_SHARED_CLIPBOARD_FMT_UNICODETEXT);
    809             vboxClipboardAddFormat("text/plain;charset=UTF-8", UTF8,
    810                                    VBOX_SHARED_CLIPBOARD_FMT_UNICODETEXT);
    811             vboxClipboardAddFormat("text/plain;charset=utf-8", UTF8,
    812                                    VBOX_SHARED_CLIPBOARD_FMT_UNICODETEXT);
    813             vboxClipboardAddFormat("STRING", UTF8,
    814                                    VBOX_SHARED_CLIPBOARD_FMT_UNICODETEXT);
    815             vboxClipboardAddFormat("TEXT", UTF8,
    816                                    VBOX_SHARED_CLIPBOARD_FMT_UNICODETEXT);
    817             vboxClipboardAddFormat("text/plain", UTF8,
    818                                    VBOX_SHARED_CLIPBOARD_FMT_UNICODETEXT);
    819 }
    820         if (!g_testUtf16 && !g_testUtf8)
    821             vboxClipboardAddFormat("COMPOUND_TEXT", CTEXT,
    822                                    VBOX_SHARED_CLIPBOARD_FMT_UNICODETEXT);
    823     }
    824     return rc;
    825 }
    826 
    827 /**
    828  * Initialise the X11 backend of the shared clipboard.
    829  * @note  X11 backend code
    830  */
    831 int vboxClipboardInitBackend (VBOXCLIPBOARDCONTEXT *pFrontend)
    832 {
    833     int rc;
    834 
    835     if (!RTEnvGet("DISPLAY"))
    836     {
    837         /*
    838          * If we don't find the DISPLAY environment variable we assume that we are not
    839          * connected to an X11 server. Don't actually try to do this then, just fail
    840          * silently and report success on every call. This is important for VBoxHeadless.
    841          */
    842         LogRelFunc(("X11 DISPLAY variable not set -- disabling shared clipboard\n"));
    843         g_fHaveX11 = false;
    844         return VINF_SUCCESS;
    845     }
    846 
    847     if (RTEnvGet("VBOX_CBTEST_UTF16"))
    848     {
    849         g_testUtf16 = true;
    850         LogRel(("Host clipboard: testing Utf16\n"));
    851     }
    852     else if (RTEnvGet("VBOX_CBTEST_UTF8"))
    853     {
    854         g_testUtf8 = true;
    855         LogRel(("Host clipboard: testing Utf8\n"));
    856     }
    857     else if (RTEnvGet("VBOX_CBTEST_CTEXT"))
    858     {
    859         g_testCText = true;
    860         LogRel(("Host clipboard: testing compound text\n"));
    861     }
    862     else if (RTEnvGet("VBOX_CBDEBUG"))
    863     {
    864         g_debugClipboard = true;
    865         LogRel(("Host clipboard: enabling additional debugging output\n"));
    866     }
    867 
    868     g_fHaveX11 = true;
    869 
    870     LogRel(("Initializing X11 clipboard backend\n"));
    871     g_ctxX11.pFrontend = pFrontend;
    872     RTSemEventCreate(&g_ctxX11.waitForData);
    873     rc = vboxClipboardInitX11();
    874     if (RT_SUCCESS(rc))
    875     {
    876         rc = RTThreadCreate(&g_ctxX11.thread, vboxClipboardThread, 0, 0,
    877                             RTTHREADTYPE_IO, RTTHREADFLAGS_WAITABLE, "SHCLIP");
    878         if (RT_FAILURE(rc))
    879             LogRel(("Failed to initialise the shared clipboard X11 backend.\n"));
    880     }
    881     if (RT_FAILURE(rc))
    882         RTSemEventDestroy(g_ctxX11.waitForData);
    883     return rc;
    884155}
    885156
     
    894165    RTSemEventCreate(&g_ctxHost.waitForData);
    895166    RTSemMutexCreate(&g_ctxHost.clipboardMutex);
    896     rc = vboxClipboardInitBackend(&g_ctxHost);
     167    rc = VBoxX11ClipboardInitX11(&g_ctxHost);
    897168    if (RT_FAILURE(rc))
    898169    {
     
    901172        LogRel(("Failed to start the host shared clipboard service.\n"));
    902173    }
    903     return rc;
    904 }
    905 
    906 /**
    907  * Terminate the shared clipboard X11 backend.
    908  * @note  X11 backend code
    909  */
    910 int vboxClipboardDestroyBackend (void)
    911 {
    912     int rc, rcThread;
    913     unsigned count = 0;
    914     XEvent ev;
    915 
    916     /*
    917      * Immediately return if we are not connected to the host X server.
    918      */
    919     if (!g_fHaveX11)
    920         return VINF_SUCCESS;
    921 
    922     LogRelFunc(("shutting down the shared clipboard X11 backend\n"));
    923 
    924     /* Set the termination flag.  This has been observed to block if it was set
    925      * during a request for clipboard data coming from X11, so only we do it
    926      * after releasing any such requests. */
    927     XtAppSetExitFlag(g_ctxX11.appContext);
    928     /* Wake up the event loop */
    929     memset(&ev, 0, sizeof(ev));
    930     ev.xclient.type = ClientMessage;
    931     ev.xclient.format = 8;
    932     XSendEvent(XtDisplay(g_ctxX11.widget), XtWindow(g_ctxX11.widget), false, 0, &ev);
    933     XFlush(XtDisplay(g_ctxX11.widget));
    934     do
    935     {
    936         rc = RTThreadWait(g_ctxX11.thread, 1000, &rcThread);
    937         ++count;
    938         Assert(RT_SUCCESS(rc) || ((VERR_TIMEOUT == rc) && (count != 5)));
    939     } while ((VERR_TIMEOUT == rc) && (count < 300));
    940     if (RT_SUCCESS(rc))
    941     {
    942         /* We can safely destroy this now, as only this thread ever waits
    943          * for it. */
    944         RTSemEventDestroy(g_ctxX11.waitForData);
    945         AssertRC(rcThread);
    946     }
    947     else
    948         LogRel(("vboxClipboardDestroy: rc=%Rrc\n", rc));
    949     XtCloseDisplay(XtDisplay(g_ctxX11.widget));
    950     LogFlowFunc(("returning %Rrc.\n", rc));
    951174    return rc;
    952175}
     
    974197     *         even if we are not waiting. */
    975198    RTSemEventSignal(g_ctxHost.waitForData);
    976     rc = vboxClipboardDestroyBackend();
     199    rc = VBoxX11ClipboardTermX11();
    977200    if (RT_SUCCESS(rc))
    978201    {
     
    984207        RTSemMutexDestroy(g_ctxHost.clipboardMutex);
    985208    }
    986 }
    987 
    988 /**
    989  * Announce to the X11 backend that we are ready to start.
    990  * @param  owner who is the initial clipboard owner
    991  */
    992 int vboxClipboardConnectBackend (enum g_eOwner owner)
    993 {
    994     LogFlowFunc(("\n"));
    995     /*
    996      * Immediately return if we are not connected to the host X server.
    997      */
    998     if (!g_fHaveX11)
    999         return VINF_SUCCESS;
    1000 
    1001     g_ctxX11.eOwner = owner;
    1002     if (owner == X11)
    1003         g_ctxX11.notifyVBox = true;
    1004     else
    1005     {
    1006         /** @todo Check whether the guest gets a format announcement at
    1007           *       startup. */
    1008         vboxClipboardFormatAnnounceBackend(0);
    1009     }
    1010     return VINF_SUCCESS;
    1011209}
    1012210
     
    1027225    /** The pClient pointer is a dummy anyway, as we only support a single
    1028226     * client at a time. */
    1029     rc = vboxClipboardConnectBackend(X11 /* initial owner */);
     227    rc = VBoxX11ClipboardStartX11(X11 /* initial owner */);
    1030228    return rc;
    1031 }
    1032 
    1033 /**
    1034  * Called when the VBox may have fallen out of sync with the backend.
    1035  * @note  X11 backend code
    1036  */
    1037 void vboxClipboardRequestSync (void)
    1038 {
    1039     /*
    1040      * Immediately return if we are not connected to the host X server.
    1041      */
    1042     if (!g_fHaveX11)
    1043         return;
    1044     g_ctxX11.notifyVBox = true;
    1045229}
    1046230
     
    1062246    vboxSvcClipboardReportMsg (g_ctxHost.pClient,
    1063247                               VBOX_SHARED_CLIPBOARD_HOST_MSG_FORMATS, 0);
    1064     vboxClipboardRequestSync();
     248    VBoxX11ClipboardRequestSyncX11();
    1065249
    1066250    return VINF_SUCCESS;
    1067 }
    1068 
    1069 /**
    1070  * Shut down the shared clipboard X11 backend.
    1071  * @note  X11 backend code
    1072  */
    1073 void vboxClipboardDisconnectBackend (void)
    1074 {
    1075     /*
    1076      * Immediately return if we are not connected to the host X server.
    1077      */
    1078     if (!g_fHaveX11)
    1079         return;
    1080 
    1081     g_ctxX11.eOwner = NONE;
    1082     g_ctxX11.X11TextFormat = INVALID;
    1083     g_ctxX11.X11BitmapFormat = INVALID;
    1084251}
    1085252
     
    1094261    RTSemMutexRequest(g_ctxHost.clipboardMutex, RT_INDEFINITE_WAIT);
    1095262    g_ctxHost.pClient = NULL;
    1096     vboxClipboardDisconnectBackend();
     263    VBoxX11ClipboardStopX11();
    1097264    RTSemMutexRelease(g_ctxHost.clipboardMutex);
    1098 }
    1099 
    1100 /**
    1101  * Satisfy a request from X11 for clipboard targets supported by VBox.
    1102  *
    1103  * @returns true if we successfully convert the data to the format
    1104  *          requested, false otherwise.
    1105  *
    1106  * @param  atomTypeReturn The type of the data we are returning
    1107  * @param  pValReturn     A pointer to the data we are returning.  This
    1108  *                        should be set to memory allocated by XtMalloc,
    1109  *                        which will be freed later by the Xt toolkit.
    1110  * @param  pcLenReturn    The length of the data we are returning
    1111  * @param  piFormatReturn The format (8bit, 16bit, 32bit) of the data we are
    1112  *                        returning
    1113  * @note  X11 backend code, called by the XtOwnSelection callback.
    1114  */
    1115 static Boolean vboxClipboardConvertTargetsForX11(Atom *atomTypeReturn,
    1116                                                  XtPointer *pValReturn,
    1117                                                  unsigned long *pcLenReturn,
    1118                                                  int *piFormatReturn)
    1119 {
    1120     unsigned uListSize = g_ctxX11.formatList.size();
    1121     Atom *atomTargets = reinterpret_cast<Atom *>(XtMalloc((uListSize + 3) * sizeof(Atom)));
    1122     unsigned cTargets = 0;
    1123 
    1124     LogFlowFunc (("called\n"));
    1125     for (unsigned i = 0; i < uListSize; ++i)
    1126     {
    1127         if (   ((g_ctxX11.vboxFormats & VBOX_SHARED_CLIPBOARD_FMT_UNICODETEXT) != 0)
    1128             && (g_ctxX11.formatList[i].guestFormat == VBOX_SHARED_CLIPBOARD_FMT_UNICODETEXT))
    1129         {
    1130             atomTargets[cTargets] = g_ctxX11.formatList[i].atom;
    1131             ++cTargets;
    1132         }
    1133     }
    1134     atomTargets[cTargets] = g_ctxX11.atomTargets;
    1135     atomTargets[cTargets + 1] = g_ctxX11.atomMultiple;
    1136     atomTargets[cTargets + 2] = g_ctxX11.atomTimestamp;
    1137     if (g_debugClipboard)
    1138     {
    1139         for (unsigned i = 0; i < cTargets + 3; i++)
    1140         {
    1141             char *szAtomName = XGetAtomName(XtDisplay(g_ctxX11.widget), atomTargets[i]);
    1142             if (szAtomName != 0)
    1143             {
    1144                 Log2 (("%s: returning target %s\n", __PRETTY_FUNCTION__,
    1145                        szAtomName));
    1146                 XFree(szAtomName);
    1147             }
    1148             else
    1149             {
    1150                 Log(("%s: invalid atom %d in the list!\n", __PRETTY_FUNCTION__,
    1151                      atomTargets[i]));
    1152             }
    1153         }
    1154     }
    1155     *atomTypeReturn = XA_ATOM;
    1156     *pValReturn = reinterpret_cast<XtPointer>(atomTargets);
    1157     *pcLenReturn = cTargets + 3;
    1158     *piFormatReturn = 32;
    1159     return true;
    1160 }
    1161 
    1162 /**
    1163  * Satisfy a request from X11 to convert the clipboard text to Utf16.  We
    1164  * return non-zero terminated text.
    1165  * @todo that works, but it is bad.  Change it to return zero-terminated
    1166  *       text.
    1167  *
    1168  * @returns true if we successfully convert the data to the format
    1169  * requested, false otherwise.
    1170  *
    1171  * @param  atomTypeReturn  Where to store the atom for the type of the data
    1172  *                         we are returning
    1173  * @param  pValReturn      Where to store the pointer to the data we are
    1174  *                         returning.  This should be to memory allocated by
    1175  *                         XtMalloc, which will be freed by the Xt toolkit
    1176  *                         later.
    1177  * @param  pcLenReturn     Where to store the length of the data we are
    1178  *                         returning
    1179  * @param  piFormatReturn  Where to store the bit width (8, 16, 32) of the
    1180  *                         data we are returning
    1181  * @note  X11 backend code, called by the callback for XtOwnSelection.
    1182  */
    1183 static Boolean vboxClipboardConvertUtf16(Atom *atomTypeReturn,
    1184                                          XtPointer *pValReturn,
    1185                                          unsigned long *pcLenReturn,
    1186                                          int *piFormatReturn)
    1187 {
    1188     PRTUTF16 pu16SrcText, pu16DestText;
    1189     void *pvVBox;
    1190     uint32_t cbVBox;
    1191     size_t cwSrcLen, cwDestLen;
    1192     int rc;
    1193 
    1194     LogFlowFunc (("called\n"));
    1195     rc = vboxClipboardReadDataFromVBox(g_ctxX11.pFrontend, VBOX_SHARED_CLIPBOARD_FMT_UNICODETEXT, &pvVBox, &cbVBox);
    1196     if ((RT_FAILURE(rc)) || (cbVBox == 0))
    1197     {
    1198         /* If vboxClipboardReadDataFromVBox fails then pClient may be invalid */
    1199         LogRelFunc (("vboxClipboardReadDataFromVBox returned %Rrc%s\n", rc,
    1200                     RT_SUCCESS(rc) ? ", cbVBox == 0" :  ""));
    1201         RTMemFree(pvVBox);
    1202         return false;
    1203     }
    1204     pu16SrcText = reinterpret_cast<PRTUTF16>(pvVBox);
    1205     cwSrcLen = cbVBox / 2;
    1206     /* How long will the converted text be? */
    1207     rc = vboxClipboardUtf16GetLinSize(pu16SrcText, cwSrcLen, &cwDestLen);
    1208     if (RT_FAILURE(rc))
    1209     {
    1210         LogRel(("vboxClipboardConvertUtf16: clipboard conversion failed.  vboxClipboardUtf16GetLinSize returned %Rrc.  Abandoning.\n", rc));
    1211         RTMemFree(pvVBox);
    1212         AssertRCReturn(rc, false);
    1213     }
    1214     if (cwDestLen == 0)
    1215     {
    1216         LogFlowFunc(("received empty clipboard data from the guest, returning false.\n"));
    1217         RTMemFree(pvVBox);
    1218         return false;
    1219     }
    1220     pu16DestText = reinterpret_cast<PRTUTF16>(XtMalloc(cwDestLen * 2));
    1221     if (pu16DestText == 0)
    1222     {
    1223         LogRel(("vboxClipboardConvertUtf16: failed to allocate %d bytes\n", cwDestLen * 2));
    1224         RTMemFree(pvVBox);
    1225         return false;
    1226     }
    1227     /* Convert the text. */
    1228     rc = vboxClipboardUtf16WinToLin(pu16SrcText, cwSrcLen, pu16DestText, cwDestLen);
    1229     if (RT_FAILURE(rc))
    1230     {
    1231         LogRel(("vboxClipboardConvertUtf16: clipboard conversion failed.  vboxClipboardUtf16WinToLin returned %Rrc.  Abandoning.\n", rc));
    1232         XtFree(reinterpret_cast<char *>(pu16DestText));
    1233         RTMemFree(pvVBox);
    1234         return false;
    1235     }
    1236     LogFlowFunc (("converted string is %.*ls. Returning.\n", cwDestLen, pu16DestText));
    1237     RTMemFree(pvVBox);
    1238     *atomTypeReturn = g_ctxX11.atomUtf16;
    1239     *pValReturn = reinterpret_cast<XtPointer>(pu16DestText);
    1240     *pcLenReturn = cwDestLen;
    1241     *piFormatReturn = 16;
    1242     return true;
    1243 }
    1244 
    1245 /**
    1246  * Satisfy a request from X11 to convert the clipboard text to Utf8.  We
    1247  * return non-zero terminated text.
    1248  * @todo that works, but it is bad.  Change it to return zero-terminated
    1249  *       text.
    1250  *
    1251  * @returns true if we successfully convert the data to the format
    1252  * requested, false otherwise.
    1253  *
    1254  * @param  atomTypeReturn  Where to store the atom for the type of the data
    1255  *                         we are returning
    1256  * @param  pValReturn      Where to store the pointer to the data we are
    1257  *                         returning.  This should be to memory allocated by
    1258  *                         XtMalloc, which will be freed by the Xt toolkit
    1259  *                         later.
    1260  * @param  pcLenReturn     Where to store the length of the data we are
    1261  *                         returning
    1262  * @param  piFormatReturn  Where to store the bit width (8, 16, 32) of the
    1263  *                         data we are returning
    1264  * @note  X11 backend code, called by the callback for XtOwnSelection.
    1265  */
    1266 static Boolean vboxClipboardConvertToUtf8ForX11(Atom *atomTypeReturn,
    1267                                                 XtPointer *pValReturn,
    1268                                                 unsigned long *pcLenReturn,
    1269                                                 int *piFormatReturn)
    1270 {
    1271     PRTUTF16 pu16SrcText, pu16DestText;
    1272     char *pu8DestText;
    1273     void *pvVBox;
    1274     uint32_t cbVBox;
    1275     size_t cwSrcLen, cwDestLen, cbDestLen;
    1276     int rc;
    1277 
    1278     LogFlowFunc (("called\n"));
    1279     /* Read the clipboard data from the guest. */
    1280     rc = vboxClipboardReadDataFromVBox(g_ctxX11.pFrontend, VBOX_SHARED_CLIPBOARD_FMT_UNICODETEXT, &pvVBox, &cbVBox);
    1281     if ((rc != VINF_SUCCESS) || (cbVBox == 0))
    1282     {
    1283         /* If vboxClipboardReadDataFromVBox fails then pClient may be invalid */
    1284         LogRelFunc (("vboxClipboardReadDataFromVBox returned %Rrc%s\n", rc,
    1285                      RT_SUCCESS(rc) ? ", cbVBox == 0" :  ""));
    1286         RTMemFree(pvVBox);
    1287         return false;
    1288     }
    1289     pu16SrcText = reinterpret_cast<PRTUTF16>(pvVBox);
    1290     cwSrcLen = cbVBox / 2;
    1291     /* How long will the converted text be? */
    1292     rc = vboxClipboardUtf16GetLinSize(pu16SrcText, cwSrcLen, &cwDestLen);
    1293     if (RT_FAILURE(rc))
    1294     {
    1295         LogRelFunc (("clipboard conversion failed.  vboxClipboardUtf16GetLinSize returned %Rrc.  Abandoning.\n", rc));
    1296         RTMemFree(pvVBox);
    1297         AssertRCReturn(rc, false);
    1298     }
    1299     if (cwDestLen == 0)
    1300     {
    1301         LogFlowFunc(("received empty clipboard data from the guest, returning false.\n"));
    1302         RTMemFree(pvVBox);
    1303         return false;
    1304     }
    1305     pu16DestText = reinterpret_cast<PRTUTF16>(RTMemAlloc(cwDestLen * 2));
    1306     if (pu16DestText == 0)
    1307     {
    1308         LogRelFunc (("failed to allocate %d bytes\n", cwDestLen * 2));
    1309         RTMemFree(pvVBox);
    1310         return false;
    1311     }
    1312     /* Convert the text. */
    1313     rc = vboxClipboardUtf16WinToLin(pu16SrcText, cwSrcLen, pu16DestText, cwDestLen);
    1314     if (RT_FAILURE(rc))
    1315     {
    1316         LogRelFunc (("clipboard conversion failed.  vboxClipboardUtf16WinToLin() returned %Rrc.  Abandoning.\n", rc));
    1317         RTMemFree(reinterpret_cast<void *>(pu16DestText));
    1318         RTMemFree(pvVBox);
    1319         return false;
    1320     }
    1321     /* Allocate enough space, as RTUtf16ToUtf8Ex may fail if the
    1322        space is too tightly calculated. */
    1323     pu8DestText = XtMalloc(cwDestLen * 4);
    1324     if (pu8DestText == 0)
    1325     {
    1326         LogRelFunc (("failed to allocate %d bytes\n", cwDestLen * 4));
    1327         RTMemFree(reinterpret_cast<void *>(pu16DestText));
    1328         RTMemFree(pvVBox);
    1329         return false;
    1330     }
    1331     /* Convert the Utf16 string to Utf8. */
    1332     rc = RTUtf16ToUtf8Ex(pu16DestText + 1, cwDestLen - 1, &pu8DestText, cwDestLen * 4,
    1333                          &cbDestLen);
    1334     RTMemFree(reinterpret_cast<void *>(pu16DestText));
    1335     if (RT_FAILURE(rc))
    1336     {
    1337         LogRelFunc (("clipboard conversion failed.  RTUtf16ToUtf8Ex() returned %Rrc.  Abandoning.\n", rc));
    1338         XtFree(pu8DestText);
    1339         RTMemFree(pvVBox);
    1340         return false;
    1341     }
    1342     LogFlowFunc (("converted string is %.*s. Returning.\n", cbDestLen, pu8DestText));
    1343     RTMemFree(pvVBox);
    1344     *atomTypeReturn = g_ctxX11.atomUtf8;
    1345     *pValReturn = reinterpret_cast<XtPointer>(pu8DestText);
    1346     *pcLenReturn = cbDestLen;
    1347     *piFormatReturn = 8;
    1348     return true;
    1349 }
    1350 
    1351 /**
    1352  * Satisfy a request from X11 to convert the clipboard text to
    1353  * COMPOUND_TEXT.  We return non-zero terminated text.
    1354  * @todo that works, but it is bad.  Change it to return zero-terminated
    1355  *       text.
    1356  *
    1357  * @returns true if we successfully convert the data to the format
    1358  * requested, false otherwise.
    1359  *
    1360  * @param  atomTypeReturn  Where to store the atom for the type of the data
    1361  *                         we are returning
    1362  * @param  pValReturn      Where to store the pointer to the data we are
    1363  *                         returning.  This should be to memory allocated by
    1364  *                         XtMalloc, which will be freed by the Xt toolkit
    1365  *                         later.
    1366  * @param  pcLenReturn     Where to store the length of the data we are
    1367  *                         returning
    1368  * @param  piFormatReturn  Where to store the bit width (8, 16, 32) of the
    1369  *                         data we are returning
    1370  * @note  X11 backend code, called by the callback for XtOwnSelection.
    1371  */
    1372 static Boolean vboxClipboardConvertToCTextForX11(Atom *atomTypeReturn,
    1373                                                  XtPointer *pValReturn,
    1374                                                  unsigned long *pcLenReturn,
    1375                                                  int *piFormatReturn)
    1376 {
    1377     PRTUTF16 pu16SrcText, pu16DestText;
    1378     void *pvVBox;
    1379     uint32_t cbVBox;
    1380     char *pu8DestText = 0;
    1381     size_t cwSrcLen, cwDestLen, cbDestLen;
    1382     XTextProperty property;
    1383     int rc;
    1384 
    1385     LogFlowFunc (("called\n"));
    1386     /* Read the clipboard data from the guest. */
    1387     rc = vboxClipboardReadDataFromVBox(g_ctxX11.pFrontend, VBOX_SHARED_CLIPBOARD_FMT_UNICODETEXT, &pvVBox, &cbVBox);
    1388     if ((rc != VINF_SUCCESS) || (cbVBox == 0))
    1389     {
    1390         /* If vboxClipboardReadDataFromVBox fails then pClient may be invalid */
    1391         LogRelFunc (("vboxClipboardReadDataFromVBox returned %Rrc%s\n", rc,
    1392                       RT_SUCCESS(rc) ? ", cbVBox == 0" :  ""));
    1393         RTMemFree(pvVBox);
    1394         return false;
    1395     }
    1396     pu16SrcText = reinterpret_cast<PRTUTF16>(pvVBox);
    1397     cwSrcLen = cbVBox / 2;
    1398     /* How long will the converted text be? */
    1399     rc = vboxClipboardUtf16GetLinSize(pu16SrcText, cwSrcLen, &cwDestLen);
    1400     if (RT_FAILURE(rc))
    1401     {
    1402         LogRelFunc (("clipboard conversion failed.  vboxClipboardUtf16GetLinSize returned %Rrc.  Abandoning.\n", rc));
    1403         RTMemFree(pvVBox);
    1404         AssertRCReturn(rc, false);
    1405     }
    1406     if (cwDestLen == 0)
    1407     {
    1408         LogFlowFunc(("received empty clipboard data from the guest, returning false.\n"));
    1409         RTMemFree(pvVBox);
    1410         return false;
    1411     }
    1412     pu16DestText = reinterpret_cast<PRTUTF16>(RTMemAlloc(cwDestLen * 2));
    1413     if (pu16DestText == 0)
    1414     {
    1415         LogRelFunc (("failed to allocate %d bytes\n", cwDestLen * 2));
    1416         RTMemFree(pvVBox);
    1417         return false;
    1418     }
    1419     /* Convert the text. */
    1420     rc = vboxClipboardUtf16WinToLin(pu16SrcText, cwSrcLen, pu16DestText, cwDestLen);
    1421     if (RT_FAILURE(rc))
    1422     {
    1423         LogRelFunc (("clipboard conversion failed.  vboxClipboardUtf16WinToLin() returned %Rrc.  Abandoning.\n", rc));
    1424         RTMemFree(reinterpret_cast<void *>(pu16DestText));
    1425         RTMemFree(pvVBox);
    1426         return false;
    1427     }
    1428     /* Convert the Utf16 string to Utf8. */
    1429     rc = RTUtf16ToUtf8Ex(pu16DestText + 1, cwDestLen - 1, &pu8DestText, 0, &cbDestLen);
    1430     RTMemFree(reinterpret_cast<void *>(pu16DestText));
    1431     if (RT_FAILURE(rc))
    1432     {
    1433         LogRelFunc (("clipboard conversion failed.  RTUtf16ToUtf8Ex() returned %Rrc.  Abandoning.\n", rc));
    1434         RTMemFree(pvVBox);
    1435         return false;
    1436     }
    1437     /* And finally (!) convert the Utf8 text to compound text. */
    1438 #ifdef RT_OS_SOLARIS
    1439     rc = XmbTextListToTextProperty(XtDisplay(g_ctxX11.widget), &pu8DestText, 1,
    1440                                      XCompoundTextStyle, &property);
    1441 #else
    1442     rc = Xutf8TextListToTextProperty(XtDisplay(g_ctxX11.widget), &pu8DestText, 1,
    1443                                      XCompoundTextStyle, &property);
    1444 #endif
    1445     RTMemFree(pu8DestText);
    1446     if (rc < 0)
    1447     {
    1448         const char *pcReason;
    1449         switch(rc)
    1450         {
    1451         case XNoMemory:
    1452             pcReason = "out of memory";
    1453             break;
    1454         case XLocaleNotSupported:
    1455             pcReason = "locale (Utf8) not supported";
    1456             break;
    1457         case XConverterNotFound:
    1458             pcReason = "converter not found";
    1459             break;
    1460         default:
    1461             pcReason = "unknown error";
    1462         }
    1463         LogRelFunc (("Xutf8TextListToTextProperty failed.  Reason: %s\n",
    1464                 pcReason));
    1465         RTMemFree(pvVBox);
    1466         return false;
    1467     }
    1468     LogFlowFunc (("converted string is %s. Returning.\n", property.value));
    1469     RTMemFree(pvVBox);
    1470     *atomTypeReturn = property.encoding;
    1471     *pValReturn = reinterpret_cast<XtPointer>(property.value);
    1472     *pcLenReturn = property.nitems;
    1473     *piFormatReturn = property.format;
    1474     return true;
    1475 }
    1476 
    1477 /**
    1478  * Return VBox's clipboard data for an X11 client.
    1479  * @note  X11 backend code, callback for XtOwnSelection
    1480  */
    1481 static Boolean vboxClipboardConvertForX11(Widget, Atom *atomSelection,
    1482                                           Atom *atomTarget,
    1483                                           Atom *atomTypeReturn,
    1484                                           XtPointer *pValReturn,
    1485                                           unsigned long *pcLenReturn,
    1486                                           int *piFormatReturn)
    1487 {
    1488     g_eClipboardFormats eFormat = INVALID;
    1489 
    1490     LogFlowFunc(("\n"));
    1491     /* Drop requests that we receive too late. */
    1492     if (g_ctxX11.eOwner != VB)
    1493         return false;
    1494     if (   (*atomSelection != g_ctxX11.atomClipboard)
    1495         && (*atomSelection != g_ctxX11.atomPrimary)
    1496        )
    1497     {
    1498         LogFlowFunc(("rc = false\n"));
    1499         return false;
    1500     }
    1501     if (g_debugClipboard)
    1502     {
    1503         char *szAtomName = XGetAtomName(XtDisplay(g_ctxX11.widget), *atomTarget);
    1504         if (szAtomName != 0)
    1505         {
    1506             Log2 (("%s: request for format %s\n", __PRETTY_FUNCTION__, szAtomName));
    1507             XFree(szAtomName);
    1508         }
    1509         else
    1510         {
    1511             LogFunc (("request for invalid target atom %d!\n", *atomTarget));
    1512         }
    1513     }
    1514     if (*atomTarget == g_ctxX11.atomTargets)
    1515     {
    1516         eFormat = TARGETS;
    1517     }
    1518     else
    1519     {
    1520         for (unsigned i = 0; i != g_ctxX11.formatList.size(); ++i)
    1521         {
    1522             if (g_ctxX11.formatList[i].atom == *atomTarget)
    1523             {
    1524                 eFormat = g_ctxX11.formatList[i].format;
    1525                 break;
    1526             }
    1527         }
    1528     }
    1529     switch (eFormat)
    1530     {
    1531     case TARGETS:
    1532         return vboxClipboardConvertTargetsForX11(atomTypeReturn, pValReturn,
    1533                                                  pcLenReturn, piFormatReturn);
    1534     case UTF16:
    1535         return vboxClipboardConvertUtf16(atomTypeReturn, pValReturn, pcLenReturn,
    1536                                          piFormatReturn);
    1537     case UTF8:
    1538         return vboxClipboardConvertToUtf8ForX11(atomTypeReturn, pValReturn,
    1539                                                 pcLenReturn, piFormatReturn);
    1540     case CTEXT:
    1541         return vboxClipboardConvertToCTextForX11(atomTypeReturn, pValReturn,
    1542                                                  pcLenReturn, piFormatReturn);
    1543     default:
    1544         LogFunc (("bad format\n"));
    1545         return false;
    1546     }
    1547 }
    1548 
    1549 /**
    1550  * This is called by the X toolkit intrinsics to let us know that another
    1551  * X11 client has taken the clipboard.  In this case we notify VBox that
    1552  * we want ownership of the clipboard.
    1553  * @note  X11 backend code, callback for XtOwnSelection
    1554  */
    1555 static void vboxClipboardReturnToX11(Widget, Atom *)
    1556 {
    1557     LogFlowFunc (("called, giving VBox clipboard ownership\n"));
    1558     g_ctxX11.eOwner = X11;
    1559     g_ctxX11.notifyVBox = true;
    1560 }
    1561 
    1562 /**
    1563  * VBox is taking possession of the shared clipboard.
    1564  *
    1565  * @param u32Formats Clipboard formats the guest is offering
    1566  * @note  X11 backend code
    1567  */
    1568 void vboxClipboardFormatAnnounceBackend (uint32_t u32Formats)
    1569 {
    1570     /*
    1571      * Immediately return if we are not connected to the host X server.
    1572      */
    1573     if (!g_fHaveX11)
    1574         return;
    1575 
    1576     g_ctxX11.vboxFormats = u32Formats;
    1577     LogFlowFunc (("u32Formats=%d\n", u32Formats));
    1578     if (u32Formats == 0)
    1579     {
    1580         /* This is just an automatism, not a genuine anouncement */
    1581         LogFlowFunc(("returning\n"));
    1582         return;
    1583     }
    1584     if (g_ctxX11.eOwner == VB)
    1585     {
    1586         /* We already own the clipboard, so no need to grab it, especially as that can lead
    1587            to races due to the asynchronous nature of the X11 clipboard.  This event may also
    1588            have been sent out by the guest to invalidate the Windows clipboard cache. */
    1589         LogFlowFunc(("returning\n"));
    1590         return;
    1591     }
    1592     Log2 (("%s: giving the guest clipboard ownership\n", __PRETTY_FUNCTION__));
    1593     g_ctxX11.eOwner = VB;
    1594     g_ctxX11.X11TextFormat = INVALID;
    1595     g_ctxX11.X11BitmapFormat = INVALID;
    1596     if (XtOwnSelection(g_ctxX11.widget, g_ctxX11.atomClipboard, CurrentTime, vboxClipboardConvertForX11,
    1597                        vboxClipboardReturnToX11, 0) != True)
    1598     {
    1599         Log2 (("%s: returning clipboard ownership to the host\n", __PRETTY_FUNCTION__));
    1600         /* We set this so that the guest gets notified when we take the clipboard, even if no
    1601           guest formats are found which we understand. */
    1602         g_ctxX11.notifyVBox = true;
    1603         g_ctxX11.eOwner = X11;
    1604     }
    1605     XtOwnSelection(g_ctxX11.widget, g_ctxX11.atomPrimary, CurrentTime, vboxClipboardConvertForX11,
    1606                    NULL, 0);
    1607     LogFlowFunc(("returning\n"));
    1608 
    1609265}
    1610266
     
    1618274void vboxClipboardFormatAnnounce (VBOXCLIPBOARDCLIENTDATA *pClient, uint32_t u32Formats)
    1619275{
    1620     vboxClipboardFormatAnnounceBackend (u32Formats);
    1621 }
    1622 
    1623 /**
    1624  * Called when VBox wants to read the X11 clipboard.
    1625  *
    1626  * @param  pClient   Context information about the guest VM
    1627  * @param  u32Format The format that the guest would like to receive the data in
    1628  * @param  pv        Where to write the data to
    1629  * @param  cb        The size of the buffer to write the data to
    1630  * @param  pcbActual Where to write the actual size of the written data
    1631  * @note   X11 backend code
    1632  */
    1633 int vboxClipboardReadDataBackend (uint32_t u32Format,
    1634                                   VBOXCLIPBOARDREQUEST *pRequest)
    1635 {
    1636     /*
    1637      * Immediately return if we are not connected to the host X server.
    1638      */
    1639     if (!g_fHaveX11)
    1640     {
    1641         /* no data available */
    1642         *pRequest->pcbActual = 0;
    1643         return VINF_SUCCESS;
    1644     }
    1645 
    1646     LogFlowFunc (("u32Format = %d, cb = %d\n", u32Format, pRequest->cb));
    1647 
    1648     /*
    1649      * The guest wants to read data in the given format.
    1650      */
    1651     if (u32Format & VBOX_SHARED_CLIPBOARD_FMT_UNICODETEXT)
    1652     {
    1653         if (g_ctxX11.X11TextFormat == INVALID)
    1654         {
    1655             /* No data available. */
    1656             *pRequest->pcbActual = 0;
    1657             return VERR_NO_DATA;  /* The guest thinks we have data and we don't */
    1658         }
    1659         /* Initially set the size of the data read to zero in case we fail
    1660          * somewhere. */
    1661         *pRequest->pcbActual = 0;
    1662         /* Send out a request for the data to the current clipboard owner */
    1663         XtGetSelectionValue(g_ctxX11.widget, g_ctxX11.atomClipboard,
    1664                             g_ctxX11.atomX11TextFormat,
    1665                             vboxClipboardGetDataFromX11,
    1666                             reinterpret_cast<XtPointer>(pRequest),
    1667                             CurrentTime);
    1668         /* When the data arrives, the vboxClipboardGetDataFromX11 callback will be called.  The
    1669            callback will signal the event semaphore when it has processed the data for us. */
    1670 
    1671         int rc = RTSemEventWait(g_ctxX11.waitForData, RT_INDEFINITE_WAIT);
    1672         if (RT_FAILURE(rc))
    1673             return rc;
    1674     }
    1675     else
    1676     {
    1677         return VERR_NOT_IMPLEMENTED;
    1678     }
    1679     return VINF_SUCCESS;
     276    VBoxX11ClipboardAnnounceVBoxFormat (u32Formats);
    1680277}
    1681278
     
    1705302    request.cb = cb;
    1706303    request.pcbActual = pcbActual;
    1707     rc = vboxClipboardReadDataBackend(u32Format, &request);
     304    rc = VBoxX11ClipboardReadX11Data(u32Format, &request);
    1708305    g_ctxHost.waiter = NONE;
    1709306    return rc;
     
    1721318void vboxClipboardWriteData (VBOXCLIPBOARDCLIENTDATA *pClient, void *pv, uint32_t cb, uint32_t u32Format)
    1722319{
     320/* Assume that if this gets called at all then the X11 backend is running. */
     321#if 0
    1723322    if (!g_fHaveX11)
    1724323        return;
     324#endif
    1725325
    1726326    LogFlowFunc (("called\n"));
     
    1733333
    1734334    /* Grab the mutex and check that X11 is still waiting for the data before
    1735      * delivering it.  See the explanation in vboxClipboardReadDataFromVBox. */
     335     * delivering it.  See the explanation in VBoxX11ClipboardReadVBoxData. */
    1736336    RTSemMutexRequest(g_ctxHost.clipboardMutex, RT_INDEFINITE_WAIT);
    1737337    if (g_ctxHost.waiter == X11 && cb > 0)
Note: See TracChangeset for help on using the changeset viewer.

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