Ejemplo n.º 1
0
/**
 * Connect the guest clipboard to the host.
 *
 * @returns VBox status code
 */
int vboxClipboardConnect(void)
{
    int rc = VINF_SUCCESS;
    LogRelFlowFunc(("\n"));

    /* Sanity */
    AssertReturn(g_ctx.client == 0, VERR_WRONG_ORDER);
    g_ctx.pBackend = ClipConstructX11(&g_ctx, false);
    if (!g_ctx.pBackend)
        rc = VERR_NO_MEMORY;
    if (RT_SUCCESS(rc))
        rc = ClipStartX11(g_ctx.pBackend);
    if (RT_SUCCESS(rc))
    {
        rc = VbglR3ClipboardConnect(&g_ctx.client);
        if (RT_FAILURE(rc))
            LogRel(("Error connecting to host. rc=%Rrc\n", rc));
        else if (!g_ctx.client)
        {
            LogRel(("Invalid client ID of 0\n"));
            rc = VERR_NOT_SUPPORTED;
        }
    }

    if (rc != VINF_SUCCESS && g_ctx.pBackend)
        ClipDestructX11(g_ctx.pBackend);
    LogRelFlowFunc(("g_ctx.client=%u rc=%Rrc\n", g_ctx.client, rc));
    return rc;
}
Ejemplo n.º 2
0
/**
 * Tell the host that new clipboard formats are available.
 *
 * @param u32Formats      The formats to advertise
 */
void ClipReportX11Formats(VBOXCLIPBOARDCONTEXT *pCtx, uint32_t u32Formats)
{
    int rc;
    LogRelFlowFunc(("u32Formats=%d\n", u32Formats));
    rc = VbglR3ClipboardReportFormats(g_ctx.client, u32Formats);
    LogRelFlowFunc(("rc=%Rrc\n", rc));
}
Ejemplo n.º 3
0
status_t VBoxClipboardService::_ServiceThread()
{
    printf("VBoxClipboardService::%s()\n", __FUNCTION__);

    /* The thread waits for incoming messages from the host. */
    for (;;)
    {
        uint32_t u32Msg;
        uint32_t u32Formats;
        int rc = VbglR3ClipboardGetHostMsg(fClientId, &u32Msg, &u32Formats);
        if (RT_SUCCESS(rc))
        {
            switch (u32Msg)
            {
                case VBOX_SHARED_CLIPBOARD_HOST_MSG_FORMATS:
                {
                    /*
                     * The host has announced available clipboard formats. Forward
                     * the information to the handler.
                     */
                    LogRelFlowFunc(("VBOX_SHARED_CLIPBOARD_HOST_MSG_FORMATS u32Formats=%x\n", u32Formats));
                    BMessage msg(VBOX_GUEST_CLIPBOARD_HOST_MSG_FORMATS);
                    msg.AddInt32("Formats", (uint32)u32Formats);
                    Looper()->PostMessage(&msg, this);
                    break;
                }

                case VBOX_SHARED_CLIPBOARD_HOST_MSG_READ_DATA:
                {
                    /* The host needs data in the specified format. */
                    LogRelFlowFunc(("VBOX_SHARED_CLIPBOARD_HOST_MSG_READ_DATA u32Formats=%x\n", u32Formats));
                    BMessage msg(VBOX_GUEST_CLIPBOARD_HOST_MSG_READ_DATA);
                    msg.AddInt32("Formats", (uint32)u32Formats);
                    Looper()->PostMessage(&msg, this);
                    break;
                }

                case VBOX_SHARED_CLIPBOARD_HOST_MSG_QUIT:
                {
                    /* The host is terminating. */
                    LogRelFlowFunc(("VBOX_SHARED_CLIPBOARD_HOST_MSG_QUIT\n"));
                    fExiting = true;
                    return VERR_INTERRUPTED;
                }

                default:
                    Log(("VBoxClipboardService::%s: Unsupported message from host! Message = %u\n", __FUNCTION__, u32Msg));
            }
        }
        else
            fExiting = true;

        LogRelFlow(("processed host event rc = %d\n", rc));

        if (fExiting)
            break;
    }
    return 0;
}
Ejemplo n.º 4
0
/**
 * Transfer clipboard data from the guest to the host.
 *
 * @returns VBox result code
 * @param   u32Format The format of the data being sent
 * @param   pv        Pointer to the data being sent
 * @param   cb        Size of the data being sent in bytes
 */
static int vboxClipboardSendData(uint32_t u32Format, void *pv, uint32_t cb)
{
    int rc;
    LogRelFlowFunc(("u32Format=%d, pv=%p, cb=%d\n", u32Format, pv, cb));
    rc = VbglR3ClipboardWriteData(g_ctx.client, u32Format, pv, cb);
    LogRelFlowFunc(("rc=%Rrc\n", rc));
    return rc;
}
/** Stops the service. */
void SeamlessMain::stop()
{
    LogRelFlowFunc(("\n"));
    VbglR3SeamlessSetCap(false);
    VbglR3CtlFilterMask(0, VMMDEV_EVENT_SEAMLESS_MODE_CHANGE_REQUEST);
    stopX11MonitorThread();
    mX11Monitor.uninit();
    LogRelFlowFunc(("returning\n"));
}
Ejemplo n.º 6
0
/**
 * @thread EMT
 */
int Display::i_VideoAccelEnable(bool fEnable, VBVAMEMORY *pVbvaMemory, PPDMIDISPLAYPORT pUpPort)
{
    int rc;
    LogRelFlowFunc(("fEnable = %d\n", fEnable));

    rc = i_videoAccelEnable(fEnable, pVbvaMemory, pUpPort);

    LogRelFlowFunc(("%Rrc.\n", rc));
    return rc;
}
/**
 * Update the set of visible rectangles in the host.
 */
static void sendRegionUpdate(RTRECT *pRects, size_t cRects)
{
    LogRelFlowFunc(("\n"));
    if (cRects && !pRects)  /* Assertion */
    {
        LogRelFunc(("ERROR: called with null pointer!\n"));
        return;
    }
    VbglR3SeamlessSendRects(cRects, pRects);
    LogRelFlowFunc(("returning\n"));
}
Ejemplo n.º 8
0
void cleanupDisplay(void)
{
    uint32_t fMouseFeatures = 0;
    LogRelFlowFunc(("\n"));
    VbglR3CtlFilterMask(0,   VMMDEV_EVENT_DISPLAY_CHANGE_REQUEST
                           | VMMDEV_EVENT_MOUSE_CAPABILITIES_CHANGED);
    int rc = VbglR3GetMouseStatus(&fMouseFeatures, NULL, NULL);
    if (RT_SUCCESS(rc))
        VbglR3SetMouseStatus(  fMouseFeatures
                             | VMMDEV_MOUSE_GUEST_NEEDS_HOST_CURSOR);
    LogRelFlowFunc(("returning\n"));
}
Ejemplo n.º 9
0
/* 
* Converts clipboard data from CF_HTML format to mimie clipboard format
* Returns allocated buffer that contains html converted to text/html mime type
* return result code
* parameters - output buffer and size of output buffer
* It allocates the buffer needed for storing converted fragment 
* Allocated buffer should be destroyed by RTMemFree after usage
*/
int ConvertCFHtmlToMime(const char *pcszSource, const uint32_t cch, char **ppszOutput, size_t *pcCh)
{
    char* result = NULL;

    Assert(pcszSource);
    Assert(cch);
    Assert(ppszOutput);
    Assert(pcCh);

    size_t cStartOffset, cEndOffset;
    int rc = GetHeaderValue(pcszSource, "StartFragment:", &cStartOffset);
    if (!RT_SUCCESS(rc))
    {
        LogRelFlowFunc(("Error: Unknown CF_HTML format. Expected StartFragment. rc = %Rrc.\n", rc));
        return VERR_INVALID_PARAMETER;
    }
    rc = GetHeaderValue(pcszSource, "EndFragment:", &cEndOffset);
    if (!RT_SUCCESS(rc))
    {
        LogRelFlowFunc(("Error: Unknown CF_HTML format. Expected EndFragment. rc = %Rrc.\n", rc));
        return VERR_INVALID_PARAMETER;
    }
    if (cStartOffset > 0 && cEndOffset > 0 && cEndOffset > cStartOffset)
    {
        size_t cSubstrlen = cEndOffset - cStartOffset;
        result = (char*)RTMemAlloc(cSubstrlen + 1);
        if (result)
        {
            RT_BZERO(result, cSubstrlen + 1);
            rc = RTStrCopyEx(result, cSubstrlen + 1, pcszSource + cStartOffset, cSubstrlen);
            if (RT_SUCCESS(rc))
            {
                *ppszOutput = result;
                *pcCh = cSubstrlen + 1;
            }
            else
            {
                LogRelFlowFunc(("Error: Unknown CF_HTML format. Expected EndFragment. rc = %Rrc\n", rc));
                return rc;
            }
        }
        else
        {
            LogRelFlowFunc(("Error: Unknown CF_HTML format. Expected EndFragment.\n"));
            return VERR_NO_MEMORY;
        }
    }

return VINF_SUCCESS;
}
Ejemplo n.º 10
0
/**
 * Send an SCSI INQUIRY command to a device and return selected information.
 * @returns  iprt status code
 * @returns  VERR_TRY_AGAIN if the query failed but might succeed next time
 * @param pcszNode    the full path to the device node
 * @param pu8Type    where to store the SCSI device type on success (optional)
 * @param pchVendor  where to store the vendor id string on success (optional)
 * @param cchVendor  the size of the @a pchVendor buffer
 * @param pchModel   where to store the product id string on success (optional)
 * @param cchModel   the size of the @a pchModel buffer
 * @note check documentation on the SCSI INQUIRY command and the Linux kernel
 *       SCSI headers included above if you want to understand what is going
 *       on in this method.
 */
static int cdromDoInquiry(const char *pcszNode, uint8_t *pu8Type,
                          char *pchVendor, size_t cchVendor, char *pchModel,
                          size_t cchModel)
{
    LogRelFlowFunc(("pcszNode=%s, pu8Type=%p, pchVendor=%p, cchVendor=%llu, pchModel=%p, cchModel=%llu\n",
                    pcszNode, pu8Type, pchVendor, cchVendor, pchModel,
                    cchModel));
    AssertPtrReturn(pcszNode, VERR_INVALID_POINTER);
    AssertPtrNullReturn(pu8Type, VERR_INVALID_POINTER);
    AssertPtrNullReturn(pchVendor, VERR_INVALID_POINTER);
    AssertPtrNullReturn(pchModel, VERR_INVALID_POINTER);

    RTFILE hFile;
    int rc = RTFileOpen(&hFile, pcszNode, RTFILE_O_READ | RTFILE_O_OPEN | RTFILE_O_DENY_NONE | RTFILE_O_NON_BLOCK);
    if (RT_SUCCESS(rc))
    {
        int                             rcIoCtl        = 0;
        unsigned char                   u8Response[96] = { 0 };
        struct cdrom_generic_command    CdromCommandReq;
        RT_ZERO(CdromCommandReq);
        CdromCommandReq.cmd[0]         = INQUIRY;
        CdromCommandReq.cmd[4]         = sizeof(u8Response);
        CdromCommandReq.buffer         = u8Response;
        CdromCommandReq.buflen         = sizeof(u8Response);
        CdromCommandReq.data_direction = CGC_DATA_READ;
        CdromCommandReq.timeout        = 5000;  /* ms */
        rc = RTFileIoCtl(hFile, CDROM_SEND_PACKET, &CdromCommandReq, 0, &rcIoCtl);
        if (RT_SUCCESS(rc) && rcIoCtl < 0)
            rc = RTErrConvertFromErrno(-CdromCommandReq.stat);
        RTFileClose(hFile);

        if (RT_SUCCESS(rc))
        {
            if (pu8Type)
                *pu8Type = u8Response[0] & 0x1f;
            if (pchVendor)
                RTStrPrintf(pchVendor, cchVendor, "%.8s",
                            &u8Response[8] /* vendor id string */);
            if (pchModel)
                RTStrPrintf(pchModel, cchModel, "%.16s",
                            &u8Response[16] /* product id string */);
            LogRelFlowFunc(("returning success: type=%u, vendor=%.8s, product=%.16s\n",
                            u8Response[0] & 0x1f, &u8Response[8], &u8Response[16]));
            return VINF_SUCCESS;
        }
    }
    LogRelFlowFunc(("returning %Rrc\n", rc));
    return rc;
}
Ejemplo n.º 11
0
/**
 * Add a byte to a queue.
 *
 * @param   pQ                  Pointer to the queue.
 * @param   val                 The byte to store.
 */
static void ps2kInsertQueue(GeneriQ *pQ, uint8_t val)
{
    /* Check if queue is full. */
    if (pQ->cUsed >= pQ->cSize)
    {
        LogRelFlowFunc(("queue %p full (%d entries)\n", pQ, pQ->cUsed));
        return;
    }
    /* Insert data and update circular buffer write position. */
    pQ->abQueue[pQ->wpos] = val;
    if (++pQ->wpos == pQ->cSize)
        pQ->wpos = 0;   /* Roll over. */
    ++pQ->cUsed;
    LogRelFlowFunc(("inserted 0x%02X into queue %p\n", val, pQ));
}
Ejemplo n.º 12
0
static DECLCALLBACK(int) svcRegisterExtension(void *, PFNHGCMSVCEXT pfnExtension, void *pvExtension)
{
    LogRelFlowFunc(("pfnExtension = %p\n", pfnExtension));

    VBOXCLIPBOARDEXTPARMS parms;

    if (pfnExtension)
    {
        /* Install extension. */
        g_pfnExtension = pfnExtension;
        g_pvExtension = pvExtension;

        parms.u.pfnCallback = extCallback;
        g_pfnExtension (g_pvExtension, VBOX_CLIPBOARD_EXT_FN_SET_CALLBACK, &parms, sizeof (parms));
    }
    else
    {
        if (g_pfnExtension)
        {
            parms.u.pfnCallback = NULL;
            g_pfnExtension (g_pvExtension, VBOX_CLIPBOARD_EXT_FN_SET_CALLBACK, &parms, sizeof (parms));
        }

        /* Uninstall extension. */
        g_pfnExtension = NULL;
        g_pvExtension = NULL;
    }

    return VINF_SUCCESS;
}
/**
 * initialise the service.
 */
int SeamlessMain::init(void)
{
    int rc;
    const char *pcszStage;

    LogRelFlowFunc(("\n"));
    do {
        pcszStage = "Connecting to the X server";
        rc = mX11Monitor.init(sendRegionUpdate);
        if (RT_FAILURE(rc))
            break;
        pcszStage = "Setting guest IRQ filter mask";
        rc = VbglR3CtlFilterMask(VMMDEV_EVENT_SEAMLESS_MODE_CHANGE_REQUEST, 0);
        if (RT_FAILURE(rc))
            break;
        pcszStage = "Reporting support for seamless capability";
        rc = VbglR3SeamlessSetCap(true);
        if (RT_FAILURE(rc))
            break;
        rc = startX11MonitorThread();
        if (RT_FAILURE(rc))
            break;
    } while(0);
    if (RT_FAILURE(rc))
        VBClFatalError(("VBoxClient (seamless): failed to start.  Stage: \"%s\"  Error: %Rrc\n",
                pcszStage, rc));
    return rc;
}
Ejemplo n.º 14
0
/**
 * Get clipboard data from the host.
 *
 * @returns VBox result code
 * @param   u32Format The format of the data being requested
 * @retval  ppv       On success and if pcb > 0, this will point to a buffer
 *                    to be freed with RTMemFree containing the data read.
 * @retval  pcb       On success, this contains the number of bytes of data
 *                    returned
 */
int ClipRequestDataForX11(VBOXCLIPBOARDCONTEXT *pCtx, uint32_t u32Format,
                          void **ppv, uint32_t *pcb)
{
    int rc = VINF_SUCCESS;
    uint32_t cb = 1024;
    void *pv = RTMemAlloc(cb);

    *ppv = 0;
    LogRelFlowFunc(("u32Format=%u\n", u32Format));
    if (RT_UNLIKELY(!pv))
        rc = VERR_NO_MEMORY;
    if (RT_SUCCESS(rc))
        rc = VbglR3ClipboardReadData(g_ctx.client, u32Format, pv, cb, pcb);
    if (RT_SUCCESS(rc) && (rc != VINF_BUFFER_OVERFLOW))
        *ppv = pv;
    /* A return value of VINF_BUFFER_OVERFLOW tells us to try again with a
     * larger buffer.  The size of the buffer needed is placed in *pcb.
     * So we start all over again. */
    if (rc == VINF_BUFFER_OVERFLOW)
    {
        cb = *pcb;
        RTMemFree(pv);
        pv = RTMemAlloc(cb);
        if (RT_UNLIKELY(!pv))
            rc = VERR_NO_MEMORY;
        if (RT_SUCCESS(rc))
            rc = VbglR3ClipboardReadData(g_ctx.client, u32Format, pv, cb, pcb);
        if (RT_SUCCESS(rc) && (rc != VINF_BUFFER_OVERFLOW))
            *ppv = pv;
    }
    /* Catch other errors. This also catches the case in which the buffer was
     * too small a second time, possibly because the clipboard contents
     * changed half-way through the operation.  Since we can't say whether or
     * not this is actually an error, we just return size 0.
     */
    if (RT_FAILURE(rc) || (VINF_BUFFER_OVERFLOW == rc))
    {
        *pcb = 0;
        if (pv != NULL)
            RTMemFree(pv);
    }
    LogRelFlowFunc(("returning %Rrc\n", rc));
    if (RT_SUCCESS(rc))
        LogRelFlow(("    *pcb=%d\n", *pcb));
    return rc;
}
SeamlessMain::SeamlessMain(void)
{
    LogRelFlowFunc(("\n"));
    mX11MonitorThread = NIL_RTTHREAD;
    mX11MonitorThreadStopping = false;
    mMode = VMMDev_Seamless_Disabled;
    mfPaused = true;
}
Ejemplo n.º 16
0
/**
 * Reset all standard termination signals to call our signal handler, which
 * cleans up and exits.
 */
void vboxClientSetSignalHandlers(void)
{
    struct sigaction sigAction;

    LogRelFlowFunc(("\n"));
    sigAction.sa_handler = vboxClientSignalHandler;
    sigemptyset(&sigAction.sa_mask);
    sigAction.sa_flags = 0;
    sigaction(SIGHUP, &sigAction, NULL);
    sigaction(SIGINT, &sigAction, NULL);
    sigaction(SIGQUIT, &sigAction, NULL);
    sigaction(SIGPIPE, &sigAction, NULL);
    sigaction(SIGALRM, &sigAction, NULL);
    sigaction(SIGTERM, &sigAction, NULL);
    sigaction(SIGUSR1, &sigAction, NULL);
    sigaction(SIGUSR2, &sigAction, NULL);
    LogRelFlowFunc(("returning\n"));
}
/**
 * Waits for a seamless state change events from the host and dispatch it.
 *
 * @returns        IRPT return code.
 */
int SeamlessMain::nextStateChangeEvent(void)
{
    VMMDevSeamlessMode newMode = VMMDev_Seamless_Disabled;

    LogRelFlowFunc(("\n"));
    int rc = VbglR3SeamlessWaitEvent(&newMode);
    if (RT_SUCCESS(rc))
    {
        mMode = newMode;
        switch (newMode)
        {
            case VMMDev_Seamless_Visible_Region:
            /* A simplified seamless mode, obtained by making the host VM window
             * borderless and making the guest desktop transparent. */
                LogRelFlowFunc(("\"Visible region\" mode requested (VBoxClient).\n"));
                break;
            case VMMDev_Seamless_Disabled:
                LogRelFlowFunc(("\"Disabled\" mode requested (VBoxClient).\n"));
                break;
            case VMMDev_Seamless_Host_Window:
            /* One host window represents one guest window.  Not yet implemented. */
                LogRelFunc(("Unsupported \"host window\" mode requested (VBoxClient).\n"));
                return VERR_NOT_SUPPORTED;
            default:
                LogRelFunc(("Unsupported mode %d requested (VBoxClient).\n",
                            newMode));
                return VERR_NOT_SUPPORTED;
        }
    }
    if (RT_SUCCESS(rc) || rc == VERR_TRY_AGAIN)
    {
        if (mMode == VMMDev_Seamless_Visible_Region)
            mfPaused = false;
        else
            mfPaused = true;
        mX11Monitor.interruptEventWait();
    }
    else
    {
        LogRelFunc(("VbglR3SeamlessWaitEvent returned %Rrc (VBoxClient)\n", rc));
    }
    LogRelFlowFunc(("returning %Rrc\n", rc));
    return rc;
}
Ejemplo n.º 18
0
/**
 * This method first resets the current resolution using RandR to wake up
 * the graphics driver, then sets the resolution requested if it is among
 * those offered by the driver.
 */
static void setSize(Display *pDisplay, uint32_t cx, uint32_t cy)
{
    XRRScreenConfiguration *pConfig;
    XRRScreenSize *pSizes;
    int cSizes;
    pConfig = XRRGetScreenInfo(pDisplay, DefaultRootWindow(pDisplay));
    /* Reset the current mode */
    LogRelFlowFunc(("Setting size %ux%u\n", cx, cy));
    if (pConfig)
    {
        pSizes = XRRConfigSizes(pConfig, &cSizes);
        unsigned uDist = UINT32_MAX;
        int iMode = -1;
        for (int i = 0; i < cSizes; ++i)
        {
#define VBCL_SQUARE(x) (x) * (x)
            unsigned uThisDist =   VBCL_SQUARE(pSizes[i].width - cx)
                                 + VBCL_SQUARE(pSizes[i].height - cy);
            LogRelFlowFunc(("Found size %dx%d, distance %u\n", pSizes[i].width,
                         pSizes[i].height, uThisDist));
#undef VBCL_SQUARE
            if (uThisDist < uDist)
            {
                uDist = uThisDist;
                iMode = i;
            }
        }
        if (iMode >= 0)
        {
            Time config_timestamp = 0;
            XRRConfigTimes(pConfig, &config_timestamp);
            LogRelFlowFunc(("Setting new size %d\n", iMode));
            XRRSetScreenConfig(pDisplay, pConfig,
                               DefaultRootWindow(pDisplay), iMode,
                               RR_Rotate_0, config_timestamp);
        }
        XRRFreeScreenConfigInfo(pConfig);
    }
}
/**
 * The actual X11 window configuration change monitor thread function.
 */
int SeamlessMain::x11MonitorThread(RTTHREAD hThreadSelf, void *pvUser)
{
    RT_NOREF1(hThreadSelf);
    SeamlessMain *pHost = (SeamlessMain *)pvUser;
    int rc = VINF_SUCCESS;

    LogRelFlowFunc(("\n"));
    while (!pHost->mX11MonitorThreadStopping)
    {
        if (!pHost->mfPaused)
        {
            rc = pHost->mX11Monitor.start();
            if (RT_FAILURE(rc))
                VBClFatalError(("Failed to change the X11 seamless service state, mfPaused=%RTbool, rc=%Rrc\n",
                                pHost->mfPaused, rc));
        }
        pHost->mX11Monitor.nextConfigurationEvent();
        if (pHost->mfPaused || pHost->mX11MonitorThreadStopping)
            pHost->mX11Monitor.stop();
    }
    LogRelFlowFunc(("returning %Rrc\n", rc));
    return rc;
}
Ejemplo n.º 20
0
/**
 * @interface_method_impl{PDMIMOUSEPORT,pfnPutEvent}
 */
static DECLCALLBACK(int) ps2mPutEvent(PPDMIMOUSEPORT pInterface, int32_t dx, int32_t dy,
                                      int32_t dz, int32_t dw, uint32_t fButtons)
{
    PPS2M       pThis = RT_FROM_MEMBER(pInterface, PS2M, Mouse.IPort);
    int rc = PDMCritSectEnter(pThis->pCritSectR3, VERR_SEM_BUSY);
    AssertReleaseRC(rc);

    LogRelFlowFunc(("dX=%d dY=%d dZ=%d dW=%d buttons=%02X\n", dx, dy, dz, dw, fButtons));
    /* NB: The PS/2 Y axis direction is inverted relative to ours. */
    ps2mPutEventWorker(pThis, dx, -dy, dz, dw, fButtons);

    PDMCritSectLeave(pThis->pCritSectR3);
    return VINF_SUCCESS;
}
Ejemplo n.º 21
0
static int initDisplay(Display *pDisplay)
{
    int rc = VINF_SUCCESS;
    uint32_t fMouseFeatures = 0;

    LogRelFlowFunc(("testing dynamic resizing\n"));
    int iDummy;
    if (!XRRQueryExtension(pDisplay, &iDummy, &iDummy))
        rc = VERR_NOT_SUPPORTED;
    if (RT_SUCCESS(rc))
        rc = VbglR3CtlFilterMask(VMMDEV_EVENT_DISPLAY_CHANGE_REQUEST, 0);
    else
        VbglR3CtlFilterMask(0, VMMDEV_EVENT_DISPLAY_CHANGE_REQUEST);
    /* Log and ignore the return value, as there is not much we can do with
     * it. */
    LogRelFlowFunc(("dynamic resizing: result %Rrc\n", rc));
    /* Enable support for switching between hardware and software cursors */
    LogRelFlowFunc(("enabling relative mouse re-capturing support\n"));
    rc = VbglR3GetMouseStatus(&fMouseFeatures, NULL, NULL);
    if (RT_SUCCESS(rc))
    {
        rc = VbglR3CtlFilterMask(VMMDEV_EVENT_MOUSE_CAPABILITIES_CHANGED,
                                 0);
        if (RT_SUCCESS(rc))
            rc = VbglR3SetMouseStatus
                               (  fMouseFeatures
                                & ~VMMDEV_MOUSE_GUEST_NEEDS_HOST_CURSOR);
    }
    if (RT_FAILURE(rc))
    {
        VbglR3CtlFilterMask(0, VMMDEV_EVENT_MOUSE_CAPABILITIES_CHANGED);
        VbglR3SetMouseStatus(  fMouseFeatures
                             | VMMDEV_MOUSE_GUEST_NEEDS_HOST_CURSOR);
    }
    LogRelFlowFunc(("mouse re-capturing support: result %Rrc\n", rc));
    return VINF_SUCCESS;
}
Ejemplo n.º 22
0
extern "C" DECLCALLBACK(DECLEXPORT(int)) VBoxHGCMSvcLoad (VBOXHGCMSVCFNTABLE *ptable)
{
    int rc = VINF_SUCCESS;

    LogRelFlowFunc(("ptable = %p\n", ptable));

    if (!ptable)
    {
        rc = VERR_INVALID_PARAMETER;
    }
    else
    {
        LogRel2(("VBoxHGCMSvcLoad: ptable->cbSize = %d, ptable->u32Version = 0x%08X\n", ptable->cbSize, ptable->u32Version));

        if (   ptable->cbSize != sizeof (VBOXHGCMSVCFNTABLE)
            || ptable->u32Version != VBOX_HGCM_SVC_VERSION)
        {
            rc = VERR_INVALID_PARAMETER;
        }
        else
        {
            g_pHelpers = ptable->pHelpers;

            ptable->cbClient = sizeof (VBOXCLIPBOARDCLIENTDATA);

            ptable->pfnUnload     = svcUnload;
            ptable->pfnConnect    = svcConnect;
            ptable->pfnDisconnect = svcDisconnect;
            ptable->pfnCall       = svcCall;
            ptable->pfnHostCall   = svcHostCall;
            ptable->pfnSaveState  = svcSaveState;
            ptable->pfnLoadState  = svcLoadState;
            ptable->pfnRegisterExtension  = svcRegisterExtension;
            ptable->pvService     = NULL;

            /* Service specific initialization. */
            rc = svcInit ();
        }
    }

    return rc;
}
/**
 * Run the main service thread which listens for host state change
 * notifications.
 * @returns iprt status value.  Service will be set to the stopped state on
 *          failure.
 */
int SeamlessMain::run(void)
{
    int rc = VINF_SUCCESS;

    LogRelFlowFunc(("\n"));
    /* This will only exit if something goes wrong. */
    while (RT_SUCCESS(rc) || rc == VERR_INTERRUPTED)
    {
        if (RT_FAILURE(rc))
            /* If we are not stopping, sleep for a bit to avoid using up too
                much CPU while retrying. */
            RTThreadYield();
        rc = nextStateChangeEvent();
    }
    if (RT_FAILURE(rc))
    {
        LogRel(("VBoxClient (seamless): event loop failed with error: %Rrc\n",
                rc));
        stop();
    }
    return rc;
}
Ejemplo n.º 24
0
/* For contiguous chunks just return the address in the buffer.
 * For crossing boundary - allocate a buffer from heap.
 */
static bool i_vbvaFetchCmd(VIDEOACCEL *pVideoAccel, VBVACMDHDR **ppHdr, uint32_t *pcbCmd)
{
    VBVAMEMORY *pVbvaMemory = pVideoAccel->pVbvaMemory;

    uint32_t indexRecordFirst = pVbvaMemory->indexRecordFirst;
    uint32_t indexRecordFree = pVbvaMemory->indexRecordFree;

#ifdef DEBUG_sunlover
    LogFlowFunc(("first = %d, free = %d\n",
                 indexRecordFirst, indexRecordFree));
#endif /* DEBUG_sunlover */

    if (!i_vbvaVerifyRingBuffer(pVbvaMemory))
    {
        return false;
    }

    if (indexRecordFirst == indexRecordFree)
    {
        /* No records to process. Return without assigning output variables. */
        return true;
    }

    uint32_t cbRecordCurrent = ASMAtomicReadU32(&pVbvaMemory->aRecords[indexRecordFirst].cbRecord);

#ifdef DEBUG_sunlover
    LogFlowFunc(("cbRecord = 0x%08X\n", cbRecordCurrent));
#endif /* DEBUG_sunlover */

    uint32_t cbRecord = cbRecordCurrent & ~VBVA_F_RECORD_PARTIAL;

    if (pVideoAccel->cbVbvaPartial)
    {
        /* There is a partial read in process. Continue with it. */

        Assert(pVideoAccel->pu8VbvaPartial);

        LogFlowFunc(("continue partial record cbVbvaPartial = %d cbRecord 0x%08X, first = %d, free = %d\n",
                      pVideoAccel->cbVbvaPartial, cbRecordCurrent, indexRecordFirst, indexRecordFree));

        if (cbRecord > pVideoAccel->cbVbvaPartial)
        {
            /* New data has been added to the record. */
            if (!i_vbvaPartialRead(&pVideoAccel->pu8VbvaPartial, &pVideoAccel->cbVbvaPartial, cbRecord, pVbvaMemory))
            {
                return false;
            }
        }

        if (!(cbRecordCurrent & VBVA_F_RECORD_PARTIAL))
        {
            /* The record is completed by guest. Return it to the caller. */
            *ppHdr = (VBVACMDHDR *)pVideoAccel->pu8VbvaPartial;
            *pcbCmd = pVideoAccel->cbVbvaPartial;

            pVideoAccel->pu8VbvaPartial = NULL;
            pVideoAccel->cbVbvaPartial = 0;

            /* Advance the record index. */
            pVbvaMemory->indexRecordFirst = (indexRecordFirst + 1) % VBVA_MAX_RECORDS;

#ifdef DEBUG_sunlover
            LogFlowFunc(("partial done ok, data = %d, free = %d\n",
                         pVbvaMemory->off32Data, pVbvaMemory->off32Free));
#endif /* DEBUG_sunlover */
        }

        return true;
    }

    /* A new record need to be processed. */
    if (cbRecordCurrent & VBVA_F_RECORD_PARTIAL)
    {
        /* Current record is being written by guest. '=' is important here. */
        if (cbRecord >= VBVA_RING_BUFFER_SIZE - VBVA_RING_BUFFER_THRESHOLD)
        {
            /* Partial read must be started. */
            if (!i_vbvaPartialRead(&pVideoAccel->pu8VbvaPartial, &pVideoAccel->cbVbvaPartial, cbRecord, pVbvaMemory))
            {
                return false;
            }

            LogFlowFunc(("started partial record cbVbvaPartial = 0x%08X cbRecord 0x%08X, first = %d, free = %d\n",
                          pVideoAccel->cbVbvaPartial, cbRecordCurrent, indexRecordFirst, indexRecordFree));
        }

        return true;
    }

    /* Current record is complete. If it is not empty, process it. */
    if (cbRecord)
    {
        /* The size of largest contiguous chunk in the ring biffer. */
        uint32_t u32BytesTillBoundary = VBVA_RING_BUFFER_SIZE - pVbvaMemory->off32Data;

        /* The ring buffer pointer. */
        uint8_t *au8RingBuffer = &pVbvaMemory->au8RingBuffer[0];

        /* The pointer to data in the ring buffer. */
        uint8_t *src = &au8RingBuffer[pVbvaMemory->off32Data];

        /* Fetch or point the data. */
        if (u32BytesTillBoundary >= cbRecord)
        {
            /* The command does not cross buffer boundary. Return address in the buffer. */
            *ppHdr = (VBVACMDHDR *)src;

            /* Advance data offset. */
            pVbvaMemory->off32Data = (pVbvaMemory->off32Data + cbRecord) % VBVA_RING_BUFFER_SIZE;
        }
        else
        {
            /* The command crosses buffer boundary. Rare case, so not optimized. */
            uint8_t *dst = (uint8_t *)RTMemAlloc(cbRecord);

            if (!dst)
            {
                LogRelFlowFunc(("could not allocate %d bytes from heap!!!\n", cbRecord));
                pVbvaMemory->off32Data = (pVbvaMemory->off32Data + cbRecord) % VBVA_RING_BUFFER_SIZE;
                return false;
            }

            i_vbvaFetchBytes(pVbvaMemory, dst, cbRecord);

            *ppHdr = (VBVACMDHDR *)dst;

#ifdef DEBUG_sunlover
            LogFlowFunc(("Allocated from heap %p\n", dst));
#endif /* DEBUG_sunlover */
        }
    }

    *pcbCmd = cbRecord;

    /* Advance the record index. */
    pVbvaMemory->indexRecordFirst = (indexRecordFirst + 1) % VBVA_MAX_RECORDS;

#ifdef DEBUG_sunlover
    LogFlowFunc(("done ok, data = %d, free = %d\n",
                 pVbvaMemory->off32Data, pVbvaMemory->off32Free));
#endif /* DEBUG_sunlover */

    return true;
}
Ejemplo n.º 25
0
int Display::i_videoAccelEnable(bool fEnable, VBVAMEMORY *pVbvaMemory, PPDMIDISPLAYPORT pUpPort)
{
    int rc = VINF_SUCCESS;

    VIDEOACCEL *pVideoAccel = &mVideoAccelLegacy;

    /* Called each time the guest wants to use acceleration,
     * or when the VGA device disables acceleration,
     * or when restoring the saved state with accel enabled.
     *
     * VGA device disables acceleration on each video mode change
     * and on reset.
     *
     * Guest enabled acceleration at will. And it has to enable
     * acceleration after a mode change.
     */
    LogRelFlowFunc(("mfVideoAccelEnabled = %d, fEnable = %d, pVbvaMemory = %p\n",
                  pVideoAccel->fVideoAccelEnabled, fEnable, pVbvaMemory));

    /* Strictly check parameters. Callers must not pass anything in the case. */
    Assert((fEnable && pVbvaMemory) || (!fEnable && pVbvaMemory == NULL));

    if (!i_VideoAccelAllowed ())
        return VERR_NOT_SUPPORTED;

    /* Check that current status is not being changed */
    if (pVideoAccel->fVideoAccelEnabled == fEnable)
        return rc;

    if (pVideoAccel->fVideoAccelEnabled)
    {
        /* Process any pending orders and empty the VBVA ring buffer. */
        i_videoAccelFlush (pUpPort);
    }

    if (!fEnable && pVideoAccel->pVbvaMemory)
        pVideoAccel->pVbvaMemory->fu32ModeFlags &= ~VBVA_F_MODE_ENABLED;

    if (fEnable)
    {
        /* Process any pending VGA device changes, resize. */
        pUpPort->pfnUpdateDisplayAll(pUpPort, /* fFailOnResize = */ false);
    }

    /* Protect the videoaccel state transition. */
    RTCritSectEnter(&mVideoAccelLock);

    if (fEnable)
    {
        /* Initialize the hardware memory. */
        i_vbvaSetMemoryFlags(pVbvaMemory, true, mfVideoAccelVRDP,
                             mfu32SupportedOrders, maFramebuffers, mcMonitors);
        pVbvaMemory->off32Data = 0;
        pVbvaMemory->off32Free = 0;

        memset(pVbvaMemory->aRecords, 0, sizeof(pVbvaMemory->aRecords));
        pVbvaMemory->indexRecordFirst = 0;
        pVbvaMemory->indexRecordFree = 0;

        pVideoAccel->pVbvaMemory = pVbvaMemory;
        pVideoAccel->fVideoAccelEnabled = true;

        LogRel(("VBVA: Enabled.\n"));
    }
    else
    {
        pVideoAccel->pVbvaMemory = NULL;
        pVideoAccel->fVideoAccelEnabled = false;

        LogRel(("VBVA: Disabled.\n"));
    }

    RTCritSectLeave(&mVideoAccelLock);

    if (!fEnable)
    {
        pUpPort->pfnUpdateDisplayAll(pUpPort, /* fFailOnResize = */ false);
    }

    /* Notify the VMMDev, which saves VBVA status in the saved state,
     * and needs to know current status.
     */
    VMMDev *pVMMDev = mParent->i_getVMMDev();
    if (pVMMDev)
    {
        PPDMIVMMDEVPORT pVMMDevPort = pVMMDev->getVMMDevPort();
        if (pVMMDevPort)
            pVMMDevPort->pfnVBVAChange(pVMMDevPort, fEnable);
    }

    LogRelFlowFunc(("%Rrc.\n", rc));
    return rc;
}
Ejemplo n.º 26
0
/**
 * Display change request monitor thread function.
 * Before entering the loop, we re-read the last request
 * received, and if the first one received inside the
 * loop is identical we ignore it, because it is probably
 * stale.
 */
static int runDisplay(Display *pDisplay)
{
    LogRelFlowFunc(("\n"));
    Cursor hClockCursor = XCreateFontCursor(pDisplay, XC_watch);
    Cursor hArrowCursor = XCreateFontCursor(pDisplay, XC_left_ptr);
    int RRMaj, RRMin;
    if (!XRRQueryVersion(pDisplay, &RRMaj, &RRMin))
        RRMin = 0;
    const char *pcszXrandr = "xrandr";
    if (RTFileExists("/usr/X11/bin/xrandr"))
        pcszXrandr = "/usr/X11/bin/xrandr";
    int rc = RTThreadCreate(NULL, x11ConnectionMonitor, NULL, 0,
                   RTTHREADTYPE_INFREQUENT_POLLER, 0, "X11 monitor");
    if (RT_FAILURE(rc))
        return rc;
    while (true)
    {
        uint32_t fEvents = 0, cx = 0, cy = 0, cBits = 0, iDisplay = 0;
        rc = VbglR3WaitEvent(  VMMDEV_EVENT_DISPLAY_CHANGE_REQUEST
                             | VMMDEV_EVENT_MOUSE_CAPABILITIES_CHANGED,
                             RT_INDEFINITE_WAIT, &fEvents);
        if (RT_FAILURE(rc) && rc != VERR_INTERRUPTED)  /* VERR_NO_MEMORY? */
            return rc;
        /* Jiggle the mouse pointer to wake up the driver. */
        XGrabPointer(pDisplay,
                     DefaultRootWindow(pDisplay), true, 0, GrabModeAsync,
                     GrabModeAsync, None, hClockCursor, CurrentTime);
        XFlush(pDisplay);
        XGrabPointer(pDisplay,
                     DefaultRootWindow(pDisplay), true, 0, GrabModeAsync,
                     GrabModeAsync, None, hArrowCursor, CurrentTime);
        XFlush(pDisplay);
        XUngrabPointer(pDisplay, CurrentTime);
        XFlush(pDisplay);
        /* And if it is a size hint, set the new size now that the video
         * driver has had a chance to update its list. */
        if (RT_SUCCESS(rc) && (fEvents & VMMDEV_EVENT_DISPLAY_CHANGE_REQUEST))
        {
            int rc2 = VbglR3GetDisplayChangeRequest(&cx, &cy, &cBits,
                                                    &iDisplay, true);
            /* If we are not stopping, sleep for a bit to avoid using up
                too much CPU while retrying. */
            if (RT_FAILURE(rc2))
                RTThreadYield();
            else
                if (RRMin < 2)
                    setSize(pDisplay, cx, cy);
                else
                {
                    char szCommand[256];
                    RTStrPrintf(szCommand, sizeof(szCommand),
                                "%s --output VBOX%u --set VBOX_MODE %dx%d",
                                pcszXrandr, iDisplay, cx, cy);
                    system(szCommand);
                    RTStrPrintf(szCommand, sizeof(szCommand),
                                "%s --output VBOX%u --preferred",
                                pcszXrandr, iDisplay);
                    system(szCommand);
                }
        }
    }
    LogRelFlowFunc(("returning VINF_SUCCESS\n"));
    return VINF_SUCCESS;
}
Ejemplo n.º 27
0
static int showNotify(const char *pcHeader, const char *pcBody)
{
    int rc;
# ifdef VBOX_WITH_DBUS
    DBusConnection *conn;
    DBusMessage* msg = NULL;
    conn = dbus_bus_get (DBUS_BUS_SESSION, NULL);
    if (conn == NULL)
    {
        LogRelFlowFunc(("Could not retrieve D-BUS session bus!\n"));
        rc = VERR_INVALID_HANDLE;
    }
    else
    {
        msg = dbus_message_new_method_call("org.freedesktop.Notifications",
                                           "/org/freedesktop/Notifications",
                                           "org.freedesktop.Notifications",
                                           "Notify");
        if (msg == NULL)
        {
            LogRel(("Could not create D-BUS message!\n"));
            rc = VERR_INVALID_HANDLE;
        }
        else
            rc = VINF_SUCCESS;
    }
    if (RT_SUCCESS(rc))
    {
        uint32_t msg_replace_id = 0;
        const char *msg_app = "VBoxClient";
        const char *msg_icon = "";
        const char *msg_summary = pcHeader;
        const char *msg_body = pcBody;
        int32_t msg_timeout = -1;           /* Let the notification server decide */

        DBusMessageIter iter;
        DBusMessageIter array;
        DBusMessageIter dict;
        DBusMessageIter value;
        DBusMessageIter variant;
        DBusMessageIter data;

        /* Format: UINT32 org.freedesktop.Notifications.Notify
         *         (STRING app_name, UINT32 replaces_id, STRING app_icon, STRING summary, STRING body,
         *          ARRAY actions, DICT hints, INT32 expire_timeout)
         */
        dbus_message_iter_init_append(msg,&iter);
        dbus_message_iter_append_basic(&iter,DBUS_TYPE_STRING,&msg_app);
        dbus_message_iter_append_basic(&iter,DBUS_TYPE_UINT32,&msg_replace_id);
        dbus_message_iter_append_basic(&iter,DBUS_TYPE_STRING,&msg_icon);
        dbus_message_iter_append_basic(&iter,DBUS_TYPE_STRING,&msg_summary);
        dbus_message_iter_append_basic(&iter,DBUS_TYPE_STRING,&msg_body);
        dbus_message_iter_open_container(&iter,DBUS_TYPE_ARRAY,DBUS_TYPE_STRING_AS_STRING,&array);
        dbus_message_iter_close_container(&iter,&array);
        dbus_message_iter_open_container(&iter,DBUS_TYPE_ARRAY,"{sv}",&array);
        dbus_message_iter_close_container(&iter,&array);
        dbus_message_iter_append_basic(&iter,DBUS_TYPE_INT32,&msg_timeout);

        DBusError err;
        dbus_error_init(&err);

        DBusMessage *reply;
        reply = dbus_connection_send_with_reply_and_block(conn, msg,
            30 * 1000 /* 30 seconds timeout */, &err);
        if (dbus_error_is_set(&err))
        {
            LogRel(("D-BUS returned an error while sending the notification: %s", err.message));
        }
        else if (reply)
        {
            dbus_connection_flush(conn);
            dbus_message_unref(reply);
        }
        if (dbus_error_is_set(&err))
            dbus_error_free(&err);
    }
    if (msg != NULL)
        dbus_message_unref(msg);
# else
    /* TODO: Implement me */
    rc = VINF_SUCCESS;
# endif /* VBOX_WITH_DBUS */
    return rc;
}
SeamlessMain::~SeamlessMain()
{
    LogRelFlowFunc(("\n"));
    stop();
}
Ejemplo n.º 29
0
/**
 * The main loop of our clipboard reader.
 */
int vboxClipboardMain(void)
{
    int rc;
    LogRelFlowFunc(("Starting guest clipboard service\n"));
    bool fExiting = false;

    while (!fExiting)
    {
        uint32_t Msg;
        uint32_t fFormats;
        rc = VbglR3ClipboardGetHostMsg(g_ctx.client, &Msg, &fFormats);
        if (RT_SUCCESS(rc))
        {
            switch (Msg)
            {
                case VBOX_SHARED_CLIPBOARD_HOST_MSG_FORMATS:
                {
                    /* The host has announced available clipboard formats.
                     * Save the information so that it is available for
                     * future requests from guest applications.
                     */
                    LogRelFlowFunc(("VBOX_SHARED_CLIPBOARD_HOST_MSG_FORMATS fFormats=%x\n", fFormats));
                    ClipAnnounceFormatToX11(g_ctx.pBackend, fFormats);
                    break;
                }

                case VBOX_SHARED_CLIPBOARD_HOST_MSG_READ_DATA:
                {
                    /* The host needs data in the specified format. */
                    LogRelFlowFunc(("VBOX_SHARED_CLIPBOARD_HOST_MSG_READ_DATA fFormats=%x\n", fFormats));
                    CLIPREADCBREQ *pReq;
                    pReq = (CLIPREADCBREQ *)RTMemAllocZ(sizeof(*pReq));
                    if (!pReq)
                    {
                        rc = VERR_NO_MEMORY;
                        fExiting = true;
                    }
                    else
                    {
                        pReq->u32Format = fFormats;
                        ClipRequestDataFromX11(g_ctx.pBackend, fFormats,
                                               pReq);
                    }
                    break;
                }

                case VBOX_SHARED_CLIPBOARD_HOST_MSG_QUIT:
                {
                    /* The host is terminating. */
                    LogRelFlowFunc(("VBOX_SHARED_CLIPBOARD_HOST_MSG_QUIT\n"));
                    if (RT_SUCCESS(ClipStopX11(g_ctx.pBackend)))
                        ClipDestructX11(g_ctx.pBackend);
                    fExiting = true;
                    break;
                }

                default:
                    LogRel2(("Unsupported message from host!!!\n"));
            }
        }

        LogRelFlow(("processed host event rc = %d\n", rc));
    }
    LogRelFlowFunc(("rc=%d\n", rc));
    return rc;
}
Ejemplo n.º 30
0
int vboxClipboardReadData (VBOXCLIPBOARDCLIENTDATA *pClient, uint32_t u32Format, void *pv, uint32_t cb, uint32_t *pcbActual)
{
    LogFlow(("vboxClipboardReadData: u32Format = %02X\n", u32Format));

    HANDLE hClip = NULL;

    /*
     * The guest wants to read data in the given format.
     */
    int rc = vboxOpenClipboard(pClient->pCtx->hwnd);
    if (RT_SUCCESS(rc))
    {
        dprintf(("Clipboard opened.\n"));

        if (u32Format & VBOX_SHARED_CLIPBOARD_FMT_BITMAP)
        {
            hClip = GetClipboardData (CF_DIB);

            if (hClip != NULL)
            {
                LPVOID lp = GlobalLock (hClip);

                if (lp != NULL)
                {
                    dprintf(("CF_DIB\n"));

                    vboxClipboardGetData (VBOX_SHARED_CLIPBOARD_FMT_BITMAP, lp, GlobalSize (hClip),
                                          pv, cb, pcbActual);

                    GlobalUnlock(hClip);
                }
                else
                {
                    hClip = NULL;
                }
            }
        }
        else if (u32Format & VBOX_SHARED_CLIPBOARD_FMT_UNICODETEXT)
        {
            hClip = GetClipboardData(CF_UNICODETEXT);

            if (hClip != NULL)
            {
                LPWSTR uniString = (LPWSTR)GlobalLock (hClip);

                if (uniString != NULL)
                {
                    dprintf(("CF_UNICODETEXT\n"));

                    vboxClipboardGetData (VBOX_SHARED_CLIPBOARD_FMT_UNICODETEXT, uniString, (lstrlenW (uniString) + 1) * 2,
                                          pv, cb, pcbActual);

                    GlobalUnlock(hClip);
                }
                else
                {
                    hClip = NULL;
                }
            }
        }
        else if (u32Format & VBOX_SHARED_CLIPBOARD_FMT_HTML)
        {
            UINT format = RegisterClipboardFormat ("HTML Format");

            if (format != 0)
            {
                hClip = GetClipboardData (format);

                if (hClip != NULL)
                {
                    LPVOID lp = GlobalLock (hClip);

                    if (lp != NULL)
                    {
                        dprintf(("CF_HTML\n"));

                        vboxClipboardGetData (VBOX_SHARED_CLIPBOARD_FMT_HTML, lp, GlobalSize (hClip),
                                              pv, cb, pcbActual);
                        LogRelFlowFunc(("Raw HTML clipboard data from host :"));
                        DumpHtml((char*)pv, cb);
                        GlobalUnlock(hClip);
                    }
                    else
                    {
                        hClip = NULL;
                    }
                }
            }
        }

        CloseClipboard ();
    }
    else
    {
        dprintf(("vboxClipboardReadData: failed to open clipboard, rc: %Rrc\n", rc));
    }

    if (hClip == NULL)
    {
        /* Reply with empty data. */
        vboxClipboardGetData (0, NULL, 0,
                              pv, cb, pcbActual);
    }

    return VINF_SUCCESS;
}