VirtualBox

Changeset 19152 in vbox for trunk/src/VBox/HostServices


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

File:
1 edited

Legend:

Unmodified
Added
Removed
  • 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