VirtualBox

Changeset 19152 in vbox for trunk/src/VBox


Ignore:
Timestamp:
Apr 23, 2009 9:23:45 PM (16 years ago)
Author:
vboxsync
Message:

HostServices and GuestHost/X11 clipboard: lots of changes, see defect 3621 comment 41

Location:
trunk/src/VBox
Files:
2 edited

Legend:

Unmodified
Added
Removed
  • trunk/src/VBox/GuestHost/SharedClipboard/x11-clipboard.cpp

    r19027 r19152  
    2323#define LOG_GROUP LOG_GROUP_SHARED_CLIPBOARD
    2424
     25#include <errno.h>
    2526#include <vector>
     27
     28#include <unistd.h>
    2629
    2730#ifdef RT_OS_SOLARIS
     
    103106    std::vector<VBOXCLIPBOARDFORMAT> formatList;
    104107
    105     /** Does VBox or X11 currently own the clipboard? */
    106     volatile enum g_eOwner eOwner;
     108    /** Does VBox currently own the clipboard?  If so, we don't need to poll
     109     * X11 for supported formats. */
     110    bool fOwnsClipboard;
    107111
    108112    /** What is the best text format X11 has to offer?  INVALID for none. */
     
    110114    /** Atom corresponding to the X11 text format */
    111115    Atom atomX11TextFormat;
    112     /** What is the best bitmap format X11 has to offer?  INVALID for none. */
     116    /** What is the best bitmap format X11 has to offer?  INVALID for none.
     117     */
    113118    g_eClipboardFormats X11BitmapFormat;
    114119    /** Atom corresponding to the X11 Bitmap format */
     
    121126     * to invalidate it. */
    122127    bool notifyVBox;
    123 
    124     /** Since the clipboard data moves asynchronously, we use an event
    125      * semaphore to wait for it.  When a function issues a request for
    126      * clipboard data it must wait for this semaphore, which is triggered
    127      * when the data arrives. */
    128     RTSEMEVENT waitForData;
     128    /** Cache of the last unicode data that we received */
     129    void *pvUnicodeCache;
     130    /** Size of the unicode data in the cache */
     131    uint32_t cbUnicodeCache;
     132    /** When we wish the clipboard to exit, we have to wake up the event
     133     * loop.  We do this by writing into a pipe.  This end of the pipe is
     134     * the end that another thread can write to. */
     135    int wakeupPipeWrite;
     136    /** The reader end of the pipe */
     137    int wakeupPipeRead;
    129138};
    130139
     
    248257 *        the X11 clipboard.
    249258 */
    250 static void vboxClipboardGetUtf8FromX11(VBOXCLIPBOARDCONTEXTX11 *pCtx,
    251                                         XtPointer pValue, unsigned cbSrcLen,
    252                                         void *pv, unsigned cb,
    253                                         uint32_t *pcbActual)
     259static int vboxClipboardGetUtf8FromX11(VBOXCLIPBOARDCONTEXTX11 *pCtx,
     260                                       XtPointer pValue, unsigned cbSrcLen,
     261                                       void *pv, unsigned cb,
     262                                       uint32_t *pcbActual)
    254263{
    255264    size_t cwSrcLen;
     
    267276    XtFree(reinterpret_cast<char *>(pValue));
    268277    RTUtf16Free(pu16SrcText);
    269     RTSemEventSignal(pCtx->waitForData);
    270     LogFlowFunc(("Returning.  Status is %Rrc\n", rc));
     278    LogFlowFunc(("Returning %Rrc\n", rc));
     279    return rc;
    271280}
    272281
     
    285294 *        the X11 clipboard.
    286295 */
    287 static void vboxClipboardGetCTextFromX11(VBOXCLIPBOARDCONTEXTX11 *pCtx,
    288                                          XtPointer pValue, unsigned cbSrcLen,
    289                                          void *pv, unsigned cb,
    290                                          uint32_t *pcbActual)
     296static int vboxClipboardGetCTextFromX11(VBOXCLIPBOARDCONTEXTX11 *pCtx,
     297                                        XtPointer pValue, unsigned cbSrcLen,
     298                                        void *pv, unsigned cb,
     299                                        uint32_t *pcbActual)
    291300{
    292301    size_t cwSrcLen;
     
    335344        XFreeStringList(ppu8SrcText);
    336345    RTUtf16Free(pu16SrcText);
    337     LogFlowFunc(("Returning.  Status is %Rrc\n", rc));
    338     RTSemEventSignal(pCtx->waitForData);
     346    LogFlowFunc(("Returning %Rrc\n", rc));
     347    return rc;
    339348}
    340349
     
    353362 *        the X11 clipboard.
    354363 */
    355 static void vboxClipboardGetLatin1FromX11(VBOXCLIPBOARDCONTEXTX11 *pCtx,
    356                                           XtPointer pValue,
    357                                           unsigned cbSourceLen, void *pv,
    358                                           unsigned cb, uint32_t *pcbActual)
     364static int vboxClipboardGetLatin1FromX11(VBOXCLIPBOARDCONTEXTX11 *pCtx,
     365                                         XtPointer pValue,
     366                                         unsigned cbSourceLen, void *pv,
     367                                         unsigned cb, uint32_t *pcbActual)
    359368{
    360369    unsigned cwDestLen = cbSourceLen + 1;
     
    392401    }
    393402    XtFree(reinterpret_cast<char *>(pValue));
    394     RTSemEventSignal(pCtx->waitForData);
    395     LogFlowFunc(("Returning.  Status is %Rrc\n", rc));
     403    LogFlowFunc(("Returning %Rrc\n", rc));
     404    return rc;
    396405}
    397406
     
    412421        = reinterpret_cast<VBOXCLIPBOARDREQUEST *>(pClientData);
    413422    VBOXCLIPBOARDCONTEXTX11 *pCtx = pRequest->pCtx;
     423    if (pCtx->fOwnsClipboard == true)
     424    {
     425        /* We don't want to request data from ourselves! */
     426        pRequest->rc = VERR_NO_DATA;
     427        RTSemEventSignal(pRequest->finished);
     428        return;
     429    }
    414430    pRequest->rc = VINF_SUCCESS;
    415431    LogFlowFunc(("pClientData=%p, *pcLen=%lu, *piFormat=%d\n", pClientData,
     
    422438    {
    423439        pRequest->rc = VERR_TIMEOUT;
    424         RTSemEventSignal(pCtx->waitForData);
     440        RTSemEventSignal(pRequest->finished);
    425441        return;
    426442    }
     
    429445    {
    430446        pRequest->rc = VERR_NO_DATA;
    431         RTSemEventSignal(pCtx->waitForData);
     447        RTSemEventSignal(pRequest->finished);
    432448        return;
    433449    }
     
    436452    {
    437453    case CTEXT:
    438         vboxClipboardGetCTextFromX11(pCtx, pValue, cTextLen, pRequest->pv,
    439                                      pRequest->cb, pRequest->pcbActual);
     454        pRequest->rc = vboxClipboardGetCTextFromX11(pCtx, pValue, cTextLen,
     455                                                    pRequest->pv,
     456                                                    pRequest->cb,
     457                                                    pRequest->pcbActual);
     458        RTSemEventSignal(pRequest->finished);
    440459        break;
    441460    case UTF8:
     
    448467            && (RTStrUniLenEx(pu8SourceText, *pcLen, &cStringLen) == VINF_SUCCESS))
    449468        {
    450             vboxClipboardGetUtf8FromX11(pCtx, pValue, cTextLen, pRequest->pv,
    451                                      pRequest->cb, pRequest->pcbActual);
     469            pRequest->rc = vboxClipboardGetUtf8FromX11(pCtx, pValue,
     470                                                       cTextLen,
     471                                                       pRequest->pv,
     472                                                       pRequest->cb,
     473                                                       pRequest->pcbActual);
     474            RTSemEventSignal(pRequest->finished);
    452475            break;
    453476        }
    454477        else
    455478        {
    456             vboxClipboardGetLatin1FromX11(pCtx, pValue, cTextLen,
    457                                           pRequest->pv, pRequest->cb,
    458                                           pRequest->pcbActual);
     479            pRequest->rc = vboxClipboardGetLatin1FromX11(pCtx, pValue,
     480                                                         cTextLen,
     481                                                         pRequest->pv,
     482                                                         pRequest->cb,
     483                                                        pRequest->pcbActual);
     484            RTSemEventSignal(pRequest->finished);
    459485            break;
    460486        }
     
    464490        XtFree(reinterpret_cast<char *>(pValue));
    465491        pRequest->rc = VERR_INVALID_PARAMETER;
    466         RTSemEventSignal(pCtx->waitForData);
     492        RTSemEventSignal(pRequest->finished);
    467493        return;
    468494    }
     
    493519
    494520    Log3 (("%s: called\n", __PRETTY_FUNCTION__));
    495     if (*atomType == XT_CONVERT_FAIL) /* timeout */
    496     {
    497         LogFunc (("reading clipboard from host, X toolkit failed to convert the selection\n"));
     521    if (   (*atomType == XT_CONVERT_FAIL) /* timeout */
     522        || (pCtx->fOwnsClipboard == true) /* VBox currently owns the
     523                                           * clipboard */
     524       )
     525    {
    498526        pCtx->atomX11TextFormat = None;
    499527        pCtx->X11TextFormat = INVALID;
     
    576604            reinterpret_cast<VBOXCLIPBOARDCONTEXTX11 *>(pUserData);
    577605    Log3 (("%s: called\n", __PRETTY_FUNCTION__));
    578     /* Get the current clipboard contents */
    579     if ((pCtx->eOwner != VB))
     606    /* Get the current clipboard contents if we don't own it ourselves */
     607    if (!pCtx->fOwnsClipboard)
    580608    {
    581609        Log3 (("%s: requesting the targets that the host clipboard offers\n",
     
    642670        vboxClipboardRemoveContext(pCtx);
    643671        XtDestroyWidget(pCtx->widget);
    644         pCtx->widget = NULL;
    645     }
     672    }
     673    pCtx->widget = NULL;
    646674    if (pCtx->appContext)
    647     {
    648675        XtDestroyApplicationContext(pCtx->appContext);
    649         pCtx->appContext = NULL;
    650     }
     676    pCtx->appContext = NULL;
     677    if (pCtx->wakeupPipeRead != 0)
     678        close(pCtx->wakeupPipeRead);
     679    if (pCtx->wakeupPipeWrite != 0)
     680        close(pCtx->wakeupPipeWrite);
     681    pCtx->wakeupPipeRead = 0;
     682    pCtx->wakeupPipeWrite = 0;
     683}
     684
     685/** Worker function for stopping the clipboard which runs on the event
     686 * thread. */
     687static void vboxClipboardStopWorker(XtPointer pUserData, int * /* source */,
     688                                    XtInputId * /* id */)
     689{
     690   
     691    VBOXCLIPBOARDCONTEXTX11 *pCtx = (VBOXCLIPBOARDCONTEXTX11 *)pUserData;
     692
     693    /* This might mean that we are getting stopped twice. */
     694    Assert(pCtx->widget != NULL);
     695
     696    /* Set the termination flag to tell the Xt event loop to exit.  We
     697     * reiterate that any outstanding requests from the X11 event loop to
     698     * the VBox part *must* have returned before we do this. */
     699    XtAppSetExitFlag(pCtx->appContext);
     700    pCtx->fOwnsClipboard = false;
     701    pCtx->X11TextFormat = INVALID;
     702    pCtx->X11BitmapFormat = INVALID;
    651703}
    652704
     
    721773                                   VBOX_SHARED_CLIPBOARD_FMT_UNICODETEXT);
    722774    }
     775    /* Create the pipes */
     776    int pipes[2];
     777    if (!pipe(pipes))
     778    {
     779        pCtx->wakeupPipeRead = pipes[0];
     780        pCtx->wakeupPipeWrite = pipes[1];
     781        XtAppAddInput(pCtx->appContext, pCtx->wakeupPipeRead,
     782                      (XtPointer) XtInputReadMask, vboxClipboardStopWorker,
     783                      (XtPointer) pCtx);
     784    }
     785    else
     786        rc = RTErrConvertFromErrno(errno);
    723787    if (RT_FAILURE(rc))
    724788        vboxClipboardUninitX11(pCtx);
     
    770834    LogRel(("Initializing X11 clipboard backend\n"));
    771835    if (pCtx)
    772     {
    773836        pCtx->pFrontend = pFrontend;
    774         RTSemEventCreate(&pCtx->waitForData);
    775     }
    776837    return pCtx;
    777838}
     
    793854     * memory. */
    794855    Assert(pCtx->widget == NULL);
    795     RTSemEventDestroy(pCtx->waitForData);
    796856}
    797857
    798858/**
    799859 * Announce to the X11 backend that we are ready to start.
    800  * @param  owner who is the initial clipboard owner
    801  */
    802 int VBoxX11ClipboardStartX11(VBOXCLIPBOARDCONTEXTX11 *pCtx,
    803                              bool fOwnsClipboard)
     860 */
     861int VBoxX11ClipboardStartX11(VBOXCLIPBOARDCONTEXTX11 *pCtx)
    804862{
    805863    int rc = VINF_SUCCESS;
     
    821879    if (RT_SUCCESS(rc))
    822880    {
    823         if (fOwnsClipboard)
    824         {
    825             pCtx->eOwner = X11;
    826             pCtx->notifyVBox = true;
    827         }
    828         else
    829         {
    830             /** @todo Check whether the guest gets a format announcement at
    831               *       startup. */
    832             pCtx->eOwner = NONE;
    833             VBoxX11ClipboardAnnounceVBoxFormat(pCtx, 0);
    834         }
     881        pCtx->fOwnsClipboard = false;
     882        pCtx->notifyVBox = true;
    835883    }
    836884    return rc;
    837885}
    838886
    839 /**
    840  * Called when the VBox may have fallen out of sync with the backend.
    841  * @note  X11 backend code
    842  */
    843 void VBoxX11ClipboardRequestSyncX11(VBOXCLIPBOARDCONTEXTX11 *pCtx)
    844 {
    845     /*
    846      * Immediately return if we are not connected to the host X server.
    847      */
    848     if (!g_fHaveX11)
    849         return;
    850     pCtx->notifyVBox = true;
    851 }
     887/** String written to the wakeup pipe. */
     888#define WAKE_UP_STRING      "WakeUp!"
     889/** Length of the string written. */
     890#define WAKE_UP_STRING_LEN  ( sizeof(WAKE_UP_STRING) - 1 )
    852891
    853892/**
     
    863902    int rc, rcThread;
    864903    unsigned count = 0;
    865     XEvent ev;
    866904    /*
    867905     * Immediately return if we are not connected to the host X server.
     
    870908        return VINF_SUCCESS;
    871909
    872     /* This might mean that we are getting stopped twice. */
    873     AssertReturn(pCtx->widget != NULL, VERR_WRONG_ORDER);
    874910    LogRelFunc(("stopping the shared clipboard X11 backend\n"));
    875 
    876     /* Set the termination flag to tell the Xt event loop to exit.  We
    877      * reiterate that any outstanding requests from the X11 event loop to
    878      * the VBox part *must* have returned before we do this. */
    879     XtAppSetExitFlag(pCtx->appContext);
    880     /* Wake up the event loop */
    881     memset(&ev, 0, sizeof(ev));
    882     ev.xclient.type = ClientMessage;
    883     ev.xclient.format = 8;
    884     XSendEvent(XtDisplay(pCtx->widget), XtWindow(pCtx->widget), false, 0, &ev);
    885     XFlush(XtDisplay(pCtx->widget));
    886     pCtx->eOwner = NONE;
    887     pCtx->X11TextFormat = INVALID;
    888     pCtx->X11BitmapFormat = INVALID;
     911    /* Write to the "stop" pipe */
     912    rc = write(pCtx->wakeupPipeWrite, WAKE_UP_STRING, WAKE_UP_STRING_LEN);
    889913    do
    890914    {
     
    965989    *piFormatReturn = 32;
    966990    return true;
     991}
     992
     993/** This is a wrapper around VBoxX11ClipboardReadVBoxData that will cache the
     994 * data returned.  This is unfortunately necessary, because if the other side
     995 * of the shared clipboard is also an X11 system, it may send a format
     996 * announcement message every time its clipboard is read, for reasons that
     997 * are explained elsewhere.  Unfortunately, some applications on our side
     998 * like to read the clipboard several times in short succession in different
     999 * formats.  This can fail if it collides with a format announcement message.
     1000 * @todo any ideas about how to do this better are welcome.
     1001 */
     1002static int vboxClipboardReadVBoxData (VBOXCLIPBOARDCONTEXTX11 *pCtx,
     1003                               uint32_t u32Format, void **ppv,
     1004                               uint32_t *pcb)
     1005{
     1006    int rc = VINF_SUCCESS;
     1007    LogFlowFunc(("pCtx=%p, u32Format=%02X, ppv=%p, pcb=%p\n", pCtx,
     1008                 u32Format, ppv, pcb));
     1009    if (u32Format == VBOX_SHARED_CLIPBOARD_FMT_UNICODETEXT)
     1010    {
     1011        if (pCtx->pvUnicodeCache == NULL)
     1012            rc = VBoxX11ClipboardReadVBoxData(pCtx->pFrontend, u32Format,
     1013                                              &pCtx->pvUnicodeCache,
     1014                                              &pCtx->cbUnicodeCache);
     1015        if (RT_SUCCESS(rc))
     1016        {
     1017            *ppv = RTMemDup(pCtx->pvUnicodeCache, pCtx->cbUnicodeCache);
     1018            *pcb = pCtx->cbUnicodeCache;
     1019            if (*ppv == NULL)
     1020                rc = VERR_NO_MEMORY;
     1021        }
     1022    }
     1023    else
     1024        rc = VBoxX11ClipboardReadVBoxData(pCtx->pFrontend, u32Format,
     1025                                          ppv, pcb);
     1026    LogFlowFunc(("returning %Rrc\n", rc));
     1027    if (RT_SUCCESS(rc))
     1028        LogFlowFunc(("*ppv=%.*ls, *pcb=%u\n", *pcb, *ppv, *pcb));
     1029    return rc;
    9671030}
    9681031
     
    10041067    LogFlowFunc (("called\n"));
    10051068    /* Read the clipboard data from the guest. */
    1006     rc = VBoxX11ClipboardReadVBoxData(pCtx->pFrontend, VBOX_SHARED_CLIPBOARD_FMT_UNICODETEXT, &pvVBox, &cbVBox);
     1069    rc = vboxClipboardReadVBoxData(pCtx,
     1070                                   VBOX_SHARED_CLIPBOARD_FMT_UNICODETEXT,
     1071                                   &pvVBox, &cbVBox);
    10071072    if ((rc != VINF_SUCCESS) || (cbVBox == 0))
    10081073    {
    1009         /* If VBoxX11ClipboardReadVBoxData fails then pClient may be invalid */
    1010         LogRelFunc (("VBoxX11ClipboardReadVBoxData returned %Rrc%s\n", rc,
     1074        /* If vboxClipboardReadVBoxData fails then we may be terminating */
     1075        LogRelFunc (("vboxClipboardReadVBoxData returned %Rrc%s\n", rc,
    10111076                     RT_SUCCESS(rc) ? ", cbVBox == 0" :  ""));
    10121077        RTMemFree(pvVBox);
     
    11131178    LogFlowFunc (("called\n"));
    11141179    /* Read the clipboard data from the guest. */
    1115     rc = VBoxX11ClipboardReadVBoxData(pCtx->pFrontend, VBOX_SHARED_CLIPBOARD_FMT_UNICODETEXT, &pvVBox, &cbVBox);
     1180    rc = vboxClipboardReadVBoxData(pCtx,
     1181                                   VBOX_SHARED_CLIPBOARD_FMT_UNICODETEXT,
     1182                                   &pvVBox, &cbVBox);
    11161183    if ((rc != VINF_SUCCESS) || (cbVBox == 0))
    11171184    {
    1118         /* If VBoxX11ClipboardReadVBoxData fails then pClient may be invalid */
    1119         LogRelFunc (("VBoxX11ClipboardReadVBoxData returned %Rrc%s\n", rc,
     1185        /* If vboxClipboardReadVBoxData fails then we may be terminating */
     1186        LogRelFunc (("vboxClipboardReadVBoxData returned %Rrc%s\n", rc,
    11201187                      RT_SUCCESS(rc) ? ", cbVBox == 0" :  ""));
    11211188        RTMemFree(pvVBox);
     
    12191286    LogFlowFunc(("\n"));
    12201287    /* Drop requests that we receive too late. */
    1221     if (pCtx->eOwner != VB)
     1288    if (!pCtx->fOwnsClipboard)
    12221289        return false;
    12231290    if (   (*atomSelection != pCtx->atomClipboard)
     
    12851352{
    12861353    VBOXCLIPBOARDCONTEXTX11 *pCtx = vboxClipboardFindContext(widget);
    1287     LogFlowFunc (("called, giving VBox clipboard ownership\n"));
    1288     pCtx->eOwner = X11;
     1354    LogFlowFunc (("called, giving X11 clipboard ownership\n"));
     1355    /* These should be set to the right values as soon as we start polling */
     1356    pCtx->X11TextFormat = INVALID;
     1357    pCtx->X11BitmapFormat = INVALID;
     1358    pCtx->fOwnsClipboard = false;
    12891359    pCtx->notifyVBox = true;
     1360}
     1361
     1362/** Structure used to pass information about formats that VBox supports */
     1363typedef struct _VBOXCLIPBOARDFORMATS
     1364{
     1365    /** Context information for the X11 clipboard */
     1366    VBOXCLIPBOARDCONTEXTX11 *pCtx;
     1367    /** Formats supported by VBox */
     1368    uint32_t formats;
     1369} VBOXCLIPBOARDFORMATS;
     1370
     1371/** Worker function for VBoxX11ClipboardAnnounceVBoxFormat which runs on the
     1372 * event thread. */
     1373static void vboxClipboardAnnounceWorker(XtPointer pUserData,
     1374                                        XtIntervalId * /* interval */)
     1375{
     1376    /* Extract and free the user data */
     1377    VBOXCLIPBOARDFORMATS *pFormats = (VBOXCLIPBOARDFORMATS *)pUserData;
     1378    VBOXCLIPBOARDCONTEXTX11 *pCtx = pFormats->pCtx;
     1379    uint32_t u32Formats = pFormats->formats;
     1380    RTMemFree(pFormats);
     1381    LogFlowFunc (("u32Formats=%d\n", u32Formats));
     1382    pCtx->vboxFormats = u32Formats;
     1383    /* Invalidate the clipboard cache */
     1384    if (pCtx->pvUnicodeCache != NULL)
     1385    {
     1386        RTMemFree(pCtx->pvUnicodeCache);
     1387        pCtx->pvUnicodeCache = NULL;
     1388    }
     1389    if (u32Formats == 0)
     1390    {
     1391        /* This is just an automatism, not a genuine anouncement */
     1392        XtDisownSelection(pCtx->widget, pCtx->atomClipboard, CurrentTime);
     1393        pCtx->fOwnsClipboard = false;
     1394        LogFlowFunc(("returning\n"));
     1395        return;
     1396    }
     1397    Log2 (("%s: giving the guest clipboard ownership\n", __PRETTY_FUNCTION__));
     1398    if (XtOwnSelection(pCtx->widget, pCtx->atomClipboard, CurrentTime,
     1399                       vboxClipboardConvertForX11, vboxClipboardReturnToX11,
     1400                       0) == True)
     1401    {
     1402        pCtx->fOwnsClipboard = true;
     1403        /* Grab the middle-button paste selection too. */
     1404        XtOwnSelection(pCtx->widget, pCtx->atomPrimary, CurrentTime,
     1405                       vboxClipboardConvertForX11, NULL, 0);
     1406    }
     1407    else
     1408    {
     1409        /* Another X11 client claimed the clipboard just after us, so let it
     1410         * go again. */
     1411        Log2 (("%s: returning clipboard ownership to the X11\n",
     1412               __PRETTY_FUNCTION__));
     1413        /* VBox thinks it currently owns the clipboard, so we must notify it
     1414         * as soon as we know what formats X11 has to offer. */
     1415        pCtx->notifyVBox = true;
     1416        pCtx->fOwnsClipboard = false;
     1417    }
     1418    LogFlowFunc(("returning\n"));
    12901419}
    12911420
     
    13041433    if (!g_fHaveX11)
    13051434        return;
    1306 
    1307     LogFlowFunc (("u32Formats=%d\n", u32Formats));
    1308     pCtx->vboxFormats = u32Formats;
    1309     if (u32Formats == 0)
    1310     {
    1311         /* This is just an automatism, not a genuine anouncement */
    1312         XtDisownSelection(pCtx->widget, pCtx->atomClipboard, CurrentTime);
    1313         pCtx->eOwner = NONE;
    1314         LogFlowFunc(("returning\n"));
    1315         return;
    1316     }
    1317     Log2 (("%s: giving the guest clipboard ownership\n", __PRETTY_FUNCTION__));
    1318     pCtx->eOwner = VB;
    1319     pCtx->X11TextFormat = INVALID;
    1320     pCtx->X11BitmapFormat = INVALID;
    1321     if (XtOwnSelection(pCtx->widget, pCtx->atomClipboard, CurrentTime,
    1322                        vboxClipboardConvertForX11, vboxClipboardReturnToX11,
    1323                        0) != True)
    1324     {
    1325         Log2 (("%s: returning clipboard ownership to the host\n", __PRETTY_FUNCTION__));
    1326         /* We set this so that the guest gets notified when we take the clipboard, even if no
    1327           guest formats are found which we understand. */
    1328         pCtx->notifyVBox = true;
    1329         pCtx->eOwner = X11;
    1330     }
    1331     XtOwnSelection(pCtx->widget, pCtx->atomPrimary, CurrentTime, vboxClipboardConvertForX11,
    1332                    NULL, 0);
    1333     LogFlowFunc(("returning\n"));
    1334 
     1435    /* This must be freed by the worker callback */
     1436    VBOXCLIPBOARDFORMATS *pFormats =
     1437        (VBOXCLIPBOARDFORMATS *) RTMemAlloc(sizeof(VBOXCLIPBOARDFORMATS));
     1438    if (pFormats != NULL)  /* if it is we will soon have other problems */
     1439    {
     1440        pFormats->pCtx = pCtx;
     1441        pFormats->formats = u32Formats;
     1442        XtAppAddTimeOut(pCtx->appContext, 0, vboxClipboardAnnounceWorker,
     1443                        (XtPointer) pFormats);
     1444    }
     1445}
     1446
     1447/** Worker function for VBoxX11ClipboardReadX11Data which runs on the event
     1448 * thread. */
     1449static void vboxClipboardReadX11Worker(XtPointer pUserData,
     1450                                       XtIntervalId * /* interval */)
     1451{
     1452    VBOXCLIPBOARDREQUEST *pRequest = (VBOXCLIPBOARDREQUEST *)pUserData;
     1453    VBOXCLIPBOARDCONTEXTX11 *pCtx = pRequest->pCtx;
     1454    LogFlowFunc (("u32Format = %d, cb = %d\n", pRequest->format,
     1455                  pRequest->cb));
     1456
     1457    int rc = VINF_SUCCESS;
     1458    /* Set this to start with, just in case */
     1459    *pRequest->pcbActual = 0;
     1460    /* Do not continue if we already own the clipboard */
     1461    if (pCtx->fOwnsClipboard == true)
     1462        rc = VERR_NO_DATA;  /** @todo should this be VINF? */
     1463    else
     1464    {
     1465        /*
     1466         * VBox wants to read data in the given format.
     1467         */
     1468        if (pRequest->format == VBOX_SHARED_CLIPBOARD_FMT_UNICODETEXT)
     1469        {
     1470            if (pCtx->atomX11TextFormat == None)
     1471                /* VBox thinks we have data and we don't */
     1472                rc = VERR_NO_DATA;
     1473            else
     1474                /* Send out a request for the data to the current clipboard
     1475                 * owner */
     1476                XtGetSelectionValue(pCtx->widget, pCtx->atomClipboard,
     1477                                    pCtx->atomX11TextFormat,
     1478                                    vboxClipboardGetDataFromX11,
     1479                                    reinterpret_cast<XtPointer>(pRequest),
     1480                                    CurrentTime);
     1481        }
     1482        else
     1483            rc = VERR_NOT_IMPLEMENTED;
     1484    }
     1485    if (RT_FAILURE(rc))
     1486    {
     1487        pRequest->rc = rc;
     1488        /* The clipboard callback was never scheduled, so we must signal
     1489         * that the request processing is finished ourselves. */
     1490        RTSemEventSignal(pRequest->finished);
     1491    }
     1492    LogFlowFunc(("status %Rrc\n", rc));
    13351493}
    13361494
     
    13461504 */
    13471505int VBoxX11ClipboardReadX11Data(VBOXCLIPBOARDCONTEXTX11 *pCtx,
    1348                                 uint32_t u32Format,
    1349                                 VBOXCLIPBOARDREQUEST *pRequest)
    1350 {
     1506                                uint32_t u32Format, void *pv, uint32_t cb,
     1507                                uint32_t *pcbActual)
     1508{
     1509    /* Initially set the size of the data read to zero in case we fail
     1510     * somewhere or X11 is not available. */
     1511    *pcbActual = 0;
     1512
    13511513    /*
    13521514     * Immediately return if we are not connected to the host X server.
    13531515     */
    13541516    if (!g_fHaveX11)
    1355     {
    1356         /* no data available */
    1357         *pRequest->pcbActual = 0;
    1358         pRequest->rc = VINF_SUCCESS;
    13591517        return VINF_SUCCESS;
    1360     }
    1361 
    1362     LogFlowFunc (("u32Format = %d, cb = %d\n", u32Format, pRequest->cb));
    1363     pRequest->rc = VERR_WRONG_ORDER;
    1364 
    1365     /*
    1366      * VBox wants to read data in the given format.
    1367      */
    1368     if (u32Format == VBOX_SHARED_CLIPBOARD_FMT_UNICODETEXT)
    1369     {
    1370         if (pCtx->X11TextFormat == INVALID)
    1371         {
    1372             /* No data available. */
    1373             *pRequest->pcbActual = 0;
    1374             return VERR_NO_DATA;  /* VBox thinks we have data and we don't */
    1375         }
    1376         /* Initially set the size of the data read to zero in case we fail
    1377          * somewhere. */
    1378         *pRequest->pcbActual = 0;
    1379         /* Send out a request for the data to the current clipboard owner */
    1380         XtGetSelectionValue(pCtx->widget, pCtx->atomClipboard,
    1381                             pCtx->atomX11TextFormat,
    1382                             vboxClipboardGetDataFromX11,
    1383                             reinterpret_cast<XtPointer>(pRequest),
    1384                             CurrentTime);
    1385         /* When the data arrives, the vboxClipboardGetDataFromX11 callback
    1386         * will be called.  The callback will signal the event semaphore when
    1387         * it has processed the data for us. */
    1388 
    1389         int rc = RTSemEventWait(pCtx->waitForData, RT_INDEFINITE_WAIT);
    1390         if (RT_FAILURE(rc))
    1391             return rc;
    1392     }
    1393     else
    1394     {
    1395         return VERR_NOT_IMPLEMENTED;
    1396     }
    1397     LogFlowFunc(("returning %Rrc\n", pRequest->rc));
    1398     return pRequest->rc;
     1518
     1519    VBOXCLIPBOARDREQUEST request;
     1520    request.rc = VERR_WRONG_ORDER;
     1521    request.pv = pv;
     1522    request.cb = cb;
     1523    request.pcbActual = pcbActual;
     1524    request.format = u32Format;
     1525    request.pCtx = pCtx;
     1526    /* The worker function will signal this when it has finished. */
     1527    int rc = RTSemEventCreate(&request.finished);
     1528    if (RT_SUCCESS(rc))
     1529    {
     1530        /* We use this to schedule a worker function on the event thread. */
     1531        XtAppAddTimeOut(pCtx->appContext, 0, vboxClipboardReadX11Worker,
     1532                        (XtPointer) &request);
     1533        rc = RTSemEventWait(request.finished, RT_INDEFINITE_WAIT);
     1534        if (RT_SUCCESS(rc))
     1535            rc = request.rc;
     1536        RTSemEventDestroy(request.finished);
     1537    }
     1538    return rc;
    13991539}
    14001540
     
    15291669    char pc[256];
    15301670    uint32_t cbActual;
    1531     VBOXCLIPBOARDREQUEST request = {(void *) pc, sizeof(pc),
    1532                                     &cbActual, VINF_SUCCESS, g_pCtxTestX11};
    15331671    RTPrintf("tstClipboardX11:  TESTING a request for an invalid data format\n");
    1534     int rc = VBoxX11ClipboardReadX11Data(g_pCtxTestX11,
    1535                                          0xffff, &request);
     1672    int rc = VBoxX11ClipboardReadX11Data(g_pCtxTestX11, 0xffff, (void *) pc,
     1673                                         sizeof(pc), &cbActual);
    15361674    RTPrintf("Returned %Rrc - %s\n", rc,
    15371675             rc == VERR_NOT_IMPLEMENTED ? "SUCCESS" : "FAILURE");
     
    15391677        fSuccess = false;
    15401678    RTPrintf("tstClipboardX11:  TESTING a request for data from an empty clipboard\n");
     1679    VBoxX11ClipboardAnnounceVBoxFormat(g_pCtxTestX11,
     1680                                      VBOX_SHARED_CLIPBOARD_FMT_UNICODETEXT);
     1681    VBoxX11ClipboardAnnounceVBoxFormat(g_pCtxTestX11,
     1682                                       0);
    15411683    rc = VBoxX11ClipboardReadX11Data(g_pCtxTestX11,
    1542                          VBOX_SHARED_CLIPBOARD_FMT_UNICODETEXT, &request);
     1684                                     VBOX_SHARED_CLIPBOARD_FMT_UNICODETEXT,
     1685                                     (void *) pc, sizeof(pc), &cbActual);
    15431686    RTPrintf("Returned %Rrc - %s\n", rc,
    15441687             rc == VERR_NO_DATA ? "SUCCESS" : "FAILURE");
     
    15571700    char pc[256];
    15581701    uint32_t cbActual;
    1559     VBOXCLIPBOARDREQUEST request = {(void *) pc, pData->cchBuffer,
    1560                                     &cbActual, VINF_SUCCESS, g_pCtxTestX11};
    15611702    for (int i = 0; (i < MAX_ATTEMPTS) && !fSuccess; ++i)
    15621703    {
     1704        VBoxX11ClipboardAnnounceVBoxFormat(g_pCtxTestVBox,
     1705                                      VBOX_SHARED_CLIPBOARD_FMT_UNICODETEXT);
    15631706        int rc = VBoxX11ClipboardReadX11Data(g_pCtxTestX11,
    1564                         VBOX_SHARED_CLIPBOARD_FMT_UNICODETEXT, &request);
    1565         AssertReturn(RT_SUCCESS(rc), 1);
     1707                        VBOX_SHARED_CLIPBOARD_FMT_UNICODETEXT,
     1708                                             (void *) pc, sizeof(pc),
     1709                                             &cbActual);
     1710        AssertRCReturn(rc, 1);
    15661711        /* Did we expect and get an overflow? */
    15671712        if (   RT_SUCCESS(rc)
     
    15961741    int rc = VINF_SUCCESS;
    15971742    int status = 0;
    1598     /* We can't reasonably test anything without an X session, so just
    1599      * continue. */
     1743    /* We can't test anything without an X session, so just return success
     1744     * in that case. */
    16001745    if (!RTEnvGet("DISPLAY"))
    16011746        return 0;
    1602     g_debugClipboard = true;
    16031747    RTR3Init();
    16041748    g_pCtxTestX11 = VBoxX11ClipboardConstructX11(NULL);
    16051749    g_pCtxTestVBox =
    16061750        VBoxX11ClipboardConstructX11((VBOXCLIPBOARDCONTEXT *)g_pCtxTestX11);
    1607     rc = VBoxX11ClipboardStartX11(g_pCtxTestVBox, false);
     1751    rc = VBoxX11ClipboardStartX11(g_pCtxTestVBox);
    16081752    AssertRCReturn(rc, 1);
    16091753    bool fSuccess = false;
     
    16121756    for (unsigned i = 0; (i < MAX_ATTEMPTS) && !fSuccess; ++i)
    16131757    {
    1614         rc = VBoxX11ClipboardStartX11(g_pCtxTestX11, false);
     1758        rc = VBoxX11ClipboardStartX11(g_pCtxTestX11);
    16151759        AssertRCReturn(rc, 1);
    16161760        fSuccess = testInvalid();
     
    16241768    if (!fSuccess)
    16251769        status = 1;
    1626     rc = VBoxX11ClipboardStartX11(g_pCtxTestX11, true);
     1770    rc = VBoxX11ClipboardStartX11(g_pCtxTestX11);
    16271771    AssertRCReturn(rc, 1);
    16281772    /* Claim the clipboard and make sure we get it */
     
    16571801        char pc[256];
    16581802        uint32_t cbActual;
    1659         VBOXCLIPBOARDREQUEST request = {(void *) pc, sizeof(pc),
    1660                                         &cbActual, VINF_SUCCESS,
    1661                                         g_pCtxTestX11};
     1803        VBoxX11ClipboardAnnounceVBoxFormat(g_pCtxTestVBox,
     1804                                      VBOX_SHARED_CLIPBOARD_FMT_UNICODETEXT);
    16621805        rc = VBoxX11ClipboardReadX11Data(g_pCtxTestX11,
    1663                     VBOX_SHARED_CLIPBOARD_FMT_UNICODETEXT, &request);
     1806                    VBOX_SHARED_CLIPBOARD_FMT_UNICODETEXT,
     1807                                         (void *) pc, sizeof(pc), &cbActual);
    16641808        AssertMsg(   RT_SUCCESS(rc)
    16651809                  || (rc == VERR_TIMEOUT)
  • trunk/src/VBox/HostServices/SharedClipboard/x11-clipboard.cpp

    r18886 r19152  
    2121 */
    2222
     23#define LOG_GROUP LOG_GROUP_SHARED_CLIPBOARD
     24
    2325#include <string.h>
    2426
    25 #include <iprt/asm.h>        /* For atomic operations */
    2627#include <iprt/assert.h>
     28#include <iprt/critsect.h>
    2729#include <iprt/mem.h>
    2830#include <iprt/semaphore.h>
     
    3234
    3335#include "VBoxClipboard.h"
     36
     37/** A request for clipboard data from VBox */
     38typedef struct _VBOXCLIPBOARDREQFROMVBOX
     39{
     40    /** Data received */
     41    void *pv;
     42    /** The size of the data */
     43    uint32_t cb;
     44    /** Format of the data */
     45    uint32_t format;
     46    /** A semaphore for waiting for the data */
     47    RTSEMEVENT finished;
     48} VBOXCLIPBOARDREQFROMVBOX;
    3449
    3550/** Global context information used by the host glue for the X11 clipboard
     
    3752struct _VBOXCLIPBOARDCONTEXT
    3853{
    39     /** Since the clipboard data moves asynchronously, we use an event
    40      * semaphore to wait for it.  When a function issues a request for
    41      * clipboard data it must wait for this semaphore, which is triggered
    42      * when the data arrives. */
    43     RTSEMEVENT waitForData;
    44     /** Who (if anyone) is currently waiting for data?  Used for sanity
    45      * checks when data arrives. */
    46     volatile uint32_t waiter;
    4754    /** This mutex is grabbed during any critical operations on the clipboard
    4855     * which might clash with others. */
    49     RTSEMMUTEX clipboardMutex;
    50 
     56    RTCRITSECT clipboardMutex;
     57    /** The currently pending request for data from VBox.  NULL if there is
     58     * no request pending.  The protocol for completing a request is to grab
     59     * the critical section, check that @a pReq is not NULL, fill in the data
     60     * fields and set @a pReq to NULL.  The protocol for cancelling a pending
     61     * request is to grab the critical section and set pReq to NULL.
     62     * It is an error if a request arrives while another one is pending, and
     63     * the backend is responsible for ensuring that this does not happen. */
     64    VBOXCLIPBOARDREQFROMVBOX *pReq;
     65   
    5166    /** Pointer to the opaque X11 backend structure */
    5267    VBOXCLIPBOARDCONTEXTX11 *pBackend;
    53     /** Pointer to the client data structure */
     68    /** Pointer to the VBox host client data structure. */
    5469    VBOXCLIPBOARDCLIENTDATA *pClient;
     70    /** We set this when we start shutting down as a hint not to post any new
     71     * requests. */
     72    bool fShuttingDown;
    5573};
    5674
    57 /* Only one client is supported. There seems to be no need for more clients.
    58  */
    59 static VBOXCLIPBOARDCONTEXT g_ctxHost;
     75static int vboxClipboardWaitForReq(VBOXCLIPBOARDCONTEXT *pCtx,
     76                                   VBOXCLIPBOARDREQFROMVBOX *pReq,
     77                                   uint32_t u32Format)
     78{
     79    int rc = VINF_SUCCESS;
     80    LogFlowFunc(("pCtx=%p, pReq=%p, u32Format=%02X\n", pCtx, pReq, u32Format));
     81    /* Request data from VBox */
     82    vboxSvcClipboardReportMsg(pCtx->pClient,
     83                              VBOX_SHARED_CLIPBOARD_HOST_MSG_READ_DATA,
     84                              u32Format);
     85    /* Which will signal us when it is ready.  We use a timeout here
     86     * because we can't be sure that the guest will behave correctly.
     87     */
     88    rc = RTSemEventWait(pReq->finished, CLIPBOARD_TIMEOUT);
     89    /* If the request hasn't yet completed then we cancel it.  We use
     90     * the critical section to prevent these operations colliding. */
     91    RTCritSectEnter(&pCtx->clipboardMutex);
     92    /* The data may have arrived between the semaphore timing out and
     93     * our grabbing the mutex. */
     94    if (rc == VERR_TIMEOUT && pReq->pv != NULL)
     95        rc = VINF_SUCCESS;
     96    if (pCtx->pReq == pReq)
     97        pCtx->pReq = NULL;
     98    Assert(pCtx->pReq == NULL);
     99    RTCritSectLeave(&pCtx->clipboardMutex);
     100    LogFlowFunc(("returning %Rrc\n", rc));
     101    return rc;
     102}
     103
     104/** Post a request for clipboard data to VBox/the guest and wait for it to be
     105 * completed. */
     106static int vboxClipboardPostAndWaitForReq(VBOXCLIPBOARDCONTEXT *pCtx,
     107                                          VBOXCLIPBOARDREQFROMVBOX *pReq,
     108                                          uint32_t u32Format)
     109{
     110    int rc = VINF_SUCCESS;
     111    LogFlowFunc(("pCtx=%p, pReq=%p, u32Format=%02X\n", pCtx, pReq,
     112                 u32Format));
     113    /* Start by "posting" the request for the next invocation of
     114     * vboxClipboardWriteData. */
     115    RTCritSectEnter(&pCtx->clipboardMutex);
     116    if (pCtx->pReq != NULL)
     117    {
     118        /* This would be a violation of the protocol, see the comments in the
     119         * context structure definition. */
     120        Assert(false);
     121        rc = VERR_WRONG_ORDER;
     122    }
     123    else
     124        pCtx->pReq = pReq;
     125    RTCritSectLeave(&pCtx->clipboardMutex);
     126    if (RT_SUCCESS(rc))
     127        rc = vboxClipboardWaitForReq(pCtx, pReq, u32Format);
     128    LogFlowFunc(("returning %Rrc\n", rc));
     129    return rc;
     130}
    60131
    61132/**
     
    74145                                   uint32_t *pcb)
    75146{
    76     volatile VBOXCLIPBOARDCLIENTDATA *pClient = pCtx->pClient;
    77 
    78     LogFlowFunc(("u32Format=%02X\n", u32Format));
    79     /* Zero our output parameters */
    80     *ppv = NULL;
    81     *pcb = 0;
    82     if (pClient == NULL)
    83     {
    84         /* This can legitimately happen if we disconnect during a request for
    85          * data from X11. */
     147    VBOXCLIPBOARDREQFROMVBOX request = { NULL };
     148
     149    LogFlowFunc(("pCtx=%p, u32Format=%02X, ppv=%p, pcb=%p\n", pCtx,
     150                 u32Format, ppv, pcb));
     151    if (pCtx->fShuttingDown)
     152    {
     153        /* The shared clipboard is disconnecting. */
    86154        LogFunc(("host requested guest clipboard data after guest had disconnected.\n"));
    87         VBoxX11ClipboardAnnounceVBoxFormat(pCtx->pBackend, 0);
    88         pCtx->waiter = NONE;
    89         return VERR_TIMEOUT;
    90     }
    91     /* Assert that no other transfer is in process (requests are serialised)
    92      * and that the last transfer cleaned up properly. */
    93     AssertLogRelReturn(   pClient->data.pv == NULL
    94                        && pClient->data.cb == 0
    95                        && pClient->data.u32Format == 0,
    96                        VERR_WRONG_ORDER
    97                       );
    98     /* No one else (X11 or VBox) should currently be waiting.  The first because
    99      * requests from X11 are serialised and the second because VBox previously
    100      * grabbed the clipboard, so it should not be waiting for data from us. */
    101     AssertLogRelReturn (ASMAtomicCmpXchgU32(&pCtx->waiter, X11, NONE), VERR_DEADLOCK);
    102     /* Request data from VBox */
    103     vboxSvcClipboardReportMsg(pCtx->pClient,
    104                               VBOX_SHARED_CLIPBOARD_HOST_MSG_READ_DATA,
    105                               u32Format);
    106     /* Which will signal us when it is ready.  We use a timeout here because
    107      * we can't be sure that the guest will behave correctly. */
    108     int rc = RTSemEventWait(pCtx->waitForData, CLIPBOARDTIMEOUT);
    109     if (rc == VERR_TIMEOUT)
    110         rc = VINF_SUCCESS;  /* Timeout handling follows. */
    111     /* Now we have a potential race between the HGCM thread delivering the data
    112      * and our setting waiter to NONE to say that we are no longer waiting for
    113      * it.  We solve this as follows: both of these operations are done under
    114      * the clipboard mutex.  The HGCM thread will only deliver the data if we
    115      * are still waiting after it acquires the mutex.  After we release the
    116      * mutex, we finally do our check to see whether the data was delivered. */
    117     RTSemMutexRequest(g_ctxHost.clipboardMutex, RT_INDEFINITE_WAIT);
    118     pCtx->waiter = NONE;
    119     RTSemMutexRelease(g_ctxHost.clipboardMutex);
    120     AssertLogRelRCSuccess(rc);
    121     if (RT_FAILURE(rc))
    122     {
    123         /* I believe this should not happen.  Wait until the assertions arrive
    124          * to prove the contrary. */
    125         RTMemFree(pClient->data.pv);
    126         g_ctxHost.pClient->data.pv = 0;
    127         g_ctxHost.pClient->data.cb = 0;
    128         g_ctxHost.pClient->data.u32Format = 0;
    129         vboxClipboardFormatAnnounce(NULL, 0);
    130         return rc;
    131     }
    132     if (pClient->data.pv == NULL)
    133         return VERR_TIMEOUT;
    134     LogFlowFunc(("wait completed.  Returning.\n"));
    135     *ppv = pClient->data.pv;
    136     *pcb = pClient->data.cb;
    137     g_ctxHost.pClient->data.pv = 0;
    138     g_ctxHost.pClient->data.cb = 0;
    139     g_ctxHost.pClient->data.u32Format = 0;
    140     return VINF_SUCCESS;
     155        return VERR_WRONG_ORDER;
     156    }
     157    int rc = RTSemEventCreate(&request.finished);
     158    if (RT_SUCCESS(rc))
     159    {
     160        rc = vboxClipboardPostAndWaitForReq(pCtx, &request,
     161                                            u32Format);
     162        RTSemEventDestroy(request.finished);
     163    }
     164    if (RT_SUCCESS(rc))
     165    {
     166        *ppv = request.pv;
     167        *pcb = request.cb;
     168    }
     169    LogFlowFunc(("returning %Rrc\n", rc));
     170    if (RT_SUCCESS(rc))
     171        LogFlowFunc(("*ppv=%.*ls, *pcb=%u\n", *pcb / 2, *ppv, *pcb));
     172    return rc;
    141173}
    142174
     
    150182                                      uint32_t u32Formats)
    151183{
     184    LogFlowFunc(("called.  pCtx=%p, u32Formats=%02X\n", pCtx, u32Formats));
    152185    vboxSvcClipboardReportMsg(pCtx->pClient,
    153186                              VBOX_SHARED_CLIPBOARD_HOST_MSG_FORMATS,
     
    161194int vboxClipboardInit (void)
    162195{
    163     int rc = VINF_SUCCESS;
    164     VBOXCLIPBOARDCONTEXTX11 *pBackend = NULL;
    165 
    166     LogRel(("Initializing host clipboard service\n"));
    167     RTSemEventCreate(&g_ctxHost.waitForData);
    168     RTSemMutexCreate(&g_ctxHost.clipboardMutex);
    169     pBackend = VBoxX11ClipboardConstructX11(&g_ctxHost);
    170     if (pBackend == NULL)
    171     {
    172         RTSemEventDestroy(g_ctxHost.waitForData);
    173         RTSemMutexDestroy(g_ctxHost.clipboardMutex);
    174         LogRel(("Failed to start the host shared clipboard service, out of memory.\n"));
    175         rc = VERR_NO_MEMORY;
    176     }
    177     else
    178         g_ctxHost.pBackend = pBackend;
    179     return rc;
     196    return VINF_SUCCESS;
    180197}
    181198
     
    186203void vboxClipboardDestroy (void)
    187204{
    188     int rc = VINF_SUCCESS;
    189     LogRelFunc(("shutting down host clipboard\n"));
    190     VBoxX11ClipboardDestructX11(g_ctxHost.pBackend);
    191     /* We can safely destroy these as the backend has exited
    192      * successfully and no other calls from the host code should be
    193      * forthcoming. */
    194     RTSemEventDestroy(g_ctxHost.waitForData);
    195     RTSemMutexDestroy(g_ctxHost.clipboardMutex);
     205
    196206}
    197207
     
    205215{
    206216    int rc = VINF_SUCCESS;
    207     LogFlowFunc(("\n"));
    208     /* Only one client is supported for now */
    209     AssertLogRelReturn(g_ctxHost.pClient == 0, VERR_NOT_SUPPORTED);
    210     pClient->pCtx = &g_ctxHost;
    211     pClient->pCtx->pClient = pClient;
    212     /** The pClient pointer is a dummy anyway, as we only support a single
    213      * client at a time. */
    214     rc = VBoxX11ClipboardStartX11(g_ctxHost.pBackend,
    215                                   true /* fOwnClipboard */);
     217    VBOXCLIPBOARDCONTEXTX11 *pBackend = NULL;
     218
     219    LogRel(("Starting host clipboard service\n"));
     220    VBOXCLIPBOARDCONTEXT *pCtx =
     221        (VBOXCLIPBOARDCONTEXT *) RTMemAllocZ(sizeof(VBOXCLIPBOARDCONTEXT));
     222    if (!pCtx)
     223        rc = VERR_NO_MEMORY;
     224    else
     225    {
     226        RTCritSectInit(&pCtx->clipboardMutex);
     227        pBackend = VBoxX11ClipboardConstructX11(pCtx);
     228        if (pBackend == NULL)
     229            rc = VERR_NO_MEMORY;
     230        else
     231        {
     232            pCtx->pBackend = pBackend;
     233            pClient->pCtx = pCtx;
     234            pCtx->pClient = pClient;
     235            rc = VBoxX11ClipboardStartX11(pBackend);
     236        }
     237        if (RT_FAILURE(rc) && pBackend)
     238            VBoxX11ClipboardStopX11(pCtx->pBackend);
     239        if (RT_FAILURE(rc))
     240            RTCritSectDelete(&pCtx->clipboardMutex);
     241    }
     242    if (RT_FAILURE(rc))
     243    {
     244        RTMemFree(pCtx);
     245        LogRel(("Failed to initialise the shared clipboard\n"));
     246    }
     247    LogFlowFunc(("returning %Rrc\n", rc));
    216248    return rc;
    217249}
     
    224256int vboxClipboardSync (VBOXCLIPBOARDCLIENTDATA *pClient)
    225257{
    226 
    227     /* On a Linux host, the guest should never synchronise/cache its
    228      * clipboard contents, as we have no way of reliably telling when the
    229      * host clipboard data changes.  So instead of synchronising, we tell
    230      * the guest to empty its clipboard, and we set the cached flag so that
    231      * we report formats to the guest next time we poll for them. */
    232     /** @note  This has been changed so that the message is sent even if
    233      *         X11 is not available. */
    234     vboxSvcClipboardReportMsg (g_ctxHost.pClient,
     258    /* Tell the guest we have no data in case X11 is not available.  If
     259     * there is data in the host clipboard it will automatically be sent to
     260     * the guest when the clipboard starts up. */
     261    vboxSvcClipboardReportMsg (pClient,
    235262                               VBOX_SHARED_CLIPBOARD_HOST_MSG_FORMATS, 0);
    236     VBoxX11ClipboardRequestSyncX11(g_ctxHost.pBackend);
    237 
    238263    return VINF_SUCCESS;
    239264}
     
    243268 * @note  Host glue code
    244269 */
    245 void vboxClipboardDisconnect (VBOXCLIPBOARDCLIENTDATA *)
     270void vboxClipboardDisconnect (VBOXCLIPBOARDCLIENTDATA *pClient)
    246271{
    247272    LogFlow(("vboxClipboardDisconnect\n"));
    248273
    249     RTSemMutexRequest(g_ctxHost.clipboardMutex, RT_INDEFINITE_WAIT);
     274    LogRel(("Stopping the host clipboard service\n"));
     275    VBOXCLIPBOARDCONTEXT *pCtx = pClient->pCtx;
    250276    /* Drop the reference to the client, in case it is still there.  This
    251277     * will cause any outstanding clipboard data requests from X11 to fail
    252278     * immediately. */
    253     g_ctxHost.pClient = NULL;
    254     /* The backend may be waiting for data from VBox.  At this point it is no
    255      * longer going to arrive, and we must release it to allow the event
    256      * loop to terminate.  In this case the buffer where VBox would have
    257      * written the clipboard data will still be empty and we will just
    258      * return "no data" to the backend.  Any subsequent attempts to get the
    259      * data from VBox will fail immediately as the client reference is gone.
    260      */
    261     /** @note  This has been made unconditional, as it should do no harm
    262      *         even if we are not waiting. */
    263     RTSemEventSignal(g_ctxHost.waitForData);
    264     int rc = VBoxX11ClipboardStopX11(g_ctxHost.pBackend);
     279    pCtx->fShuttingDown = true;
     280    /* If there is a currently pending request, release it immediately. */
     281    vboxClipboardWriteData(pClient, NULL, 0, 0);
     282    int rc = VBoxX11ClipboardStopX11(pCtx->pBackend);
    265283    /** @todo handle this slightly more reasonably, or be really sure
    266284     *        it won't go wrong. */
    267285    AssertRC(rc);
    268     RTSemMutexRelease(g_ctxHost.clipboardMutex);
     286    if (RT_SUCCESS(rc))  /* And if not? */
     287    {
     288        VBoxX11ClipboardDestructX11(pCtx->pBackend);
     289        RTCritSectDelete(&pCtx->clipboardMutex);
     290        RTMemFree(pCtx);
     291    }
    269292}
    270293
     
    276299 * @note  Host glue code
    277300 */
    278 void vboxClipboardFormatAnnounce (VBOXCLIPBOARDCLIENTDATA *pClient, uint32_t u32Formats)
    279 {
    280     VBoxX11ClipboardAnnounceVBoxFormat (g_ctxHost.pBackend, u32Formats);
     301void vboxClipboardFormatAnnounce (VBOXCLIPBOARDCLIENTDATA *pClient,
     302                                  uint32_t u32Formats)
     303{
     304    LogFlowFunc(("called.  pClient=%p, u32Formats=%02X\n", pClient,
     305                 u32Formats));
     306    VBoxX11ClipboardAnnounceVBoxFormat (pClient->pCtx->pBackend, u32Formats);
    281307}
    282308
     
    295321                           uint32_t *pcbActual)
    296322{
    297     int rc = VINF_SUCCESS;
    298     VBOXCLIPBOARDREQUEST request;
    299     /* No one else (VBox or X11) should currently be waiting.  The first
    300      * because requests from VBox are serialised and the second because X11
    301      * previously grabbed the clipboard, so it should not be waiting for
    302      * data from us. */
    303     AssertLogRelReturn (ASMAtomicCmpXchgU32(&g_ctxHost.waiter, VB, NONE),
    304                         VERR_DEADLOCK);
    305     request.pv = pv;
    306     request.cb = cb;
    307     request.pcbActual = pcbActual;
    308     request.pCtx = g_ctxHost.pBackend;
    309     rc = VBoxX11ClipboardReadX11Data(g_ctxHost.pBackend, u32Format, &request);
    310     g_ctxHost.waiter = NONE;
     323    LogFlowFunc(("pClient=%p, u32Format=%02X, pv=%p, cb=%u, pcbActual=%p",
     324                 pClient, u32Format, pv, cb, pcbActual));
     325    int rc = VBoxX11ClipboardReadX11Data(pClient->pCtx->pBackend, u32Format,
     326                                         pv, cb, pcbActual);
     327    LogFlowFunc(("returning %Rrc\n", rc));
     328    if (RT_SUCCESS(rc))
     329        LogFlowFunc(("*pv=%.*ls, *pcbActual=%u\n", *pcbActual / 2, pv,
     330                     *pcbActual));
    311331    return rc;
    312332}
     
    324344                             void *pv, uint32_t cb, uint32_t u32Format)
    325345{
    326 /* Assume that if this gets called at all then the X11 backend is running. */
    327 #if 0
    328     if (!g_fHaveX11)
    329         return;
    330 #endif
    331 
    332     LogFlowFunc (("called\n"));
    333 
    334     /* Assert that no other transfer is in process (requests are serialised)
    335      * or has not cleaned up properly. */
    336     AssertLogRelReturnVoid (   pClient->data.pv == NULL
    337                             && pClient->data.cb == 0
    338                             && pClient->data.u32Format == 0);
    339 
    340     /* Grab the mutex and check that X11 is still waiting for the data before
    341      * delivering it.  See the explanation in VBoxX11ClipboardReadVBoxData. */
    342     RTSemMutexRequest(g_ctxHost.clipboardMutex, RT_INDEFINITE_WAIT);
    343     if (g_ctxHost.waiter == X11 && cb > 0)
    344     {
    345         pClient->data.pv = RTMemAlloc (cb);
    346 
    347         if (pClient->data.pv)
     346    LogFlowFunc (("called.  pClient=%p, pv=%p (%.*ls), cb=%u, u32Format=%02X\n",
     347                  pClient, pv, cb / 2, pv, cb, u32Format));
     348
     349    VBOXCLIPBOARDCONTEXT *pCtx = pClient->pCtx;
     350    /* Grab the mutex and check whether there is a pending request for data.
     351     */
     352    RTCritSectEnter(&pCtx->clipboardMutex);
     353    VBOXCLIPBOARDREQFROMVBOX *pReq = pCtx->pReq;
     354    if (pReq != NULL)
     355    {
     356        if (cb > 0)
    348357        {
    349             memcpy (pClient->data.pv, pv, cb);
    350             pClient->data.cb = cb;
    351             pClient->data.u32Format = u32Format;
     358            pReq->pv = RTMemDup(pv, cb);
     359            if (pReq->pv != NULL)  /* NULL may also mean no memory... */
     360            {
     361                pReq->cb = cb;
     362                pReq->format = u32Format;
     363            }           
    352364        }
    353     }
    354     RTSemMutexRelease(g_ctxHost.clipboardMutex);
    355 
    356     RTSemEventSignal(g_ctxHost.waitForData);
    357 }
    358 
     365        /* Signal that the request has been completed. */
     366        RTSemEventSignal(pReq->finished);
     367    }
     368    pCtx->pReq = NULL;
     369    RTCritSectLeave(&pCtx->clipboardMutex);
     370}
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