/** $Id: clipboard.cpp 81052 2019-09-27 12:44:45Z vboxsync $ */ /** @file * Guest Additions - X11 Shared Clipboard. */ /* * Copyright (C) 2007-2019 Oracle Corporation * * This file is part of VirtualBox Open Source Edition (OSE), as * available from http://www.virtualbox.org. This file is free software; * you can redistribute it and/or modify it under the terms of the GNU * General Public License (GPL) as published by the Free Software * Foundation, in version 2 as it comes in the "COPYING" file of the * VirtualBox OSE distribution. VirtualBox OSE is distributed in the * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind. */ /********************************************************************************************************************************* * Header Files * *********************************************************************************************************************************/ #include #include #include #include #include #include #include #include #include #include #include #include #include "VBoxClient.h" /********************************************************************************************************************************* * Global Variables * *********************************************************************************************************************************/ /** * Global clipboard context information. */ struct _SHCLCONTEXT { /** Client ID for the clipboard subsystem */ uint32_t client; /** Pointer to the X11 clipboard backend */ CLIPBACKEND *pBackend; }; /** Only one client is supported. There seems to be no need for more clients. */ static SHCLCONTEXT g_ctx; /** * 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); LogRelFlowFuncLeaveRC(rc); return rc; } /** * Get clipboard data from the host. * * @returns VBox result code * @param pCtx Our context information * @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 */ DECLCALLBACK(int) ClipRequestDataForX11Callback(SHCLCONTEXT *pCtx, uint32_t u32Format, void **ppv, uint32_t *pcb) { RT_NOREF(pCtx); 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); } LogRelFlowFuncLeaveRC(rc); if (RT_SUCCESS(rc)) LogRelFlow((" *pcb=%d\n", *pcb)); return rc; } /** Opaque data structure describing a request from the host for clipboard * data, passed in when the request is forwarded to the X11 backend so that * it can be completed correctly. */ struct _CLIPREADCBREQ { /** The data format that was requested. */ uint32_t u32Format; }; /** * Tell the host that new clipboard formats are available. * * @param pCtx Our context information. * @param u32Formats The formats to report. */ DECLCALLBACK(void) ClipReportX11FormatsCallback(SHCLCONTEXT *pCtx, uint32_t u32Formats) { RT_NOREF(pCtx); LogRelFlowFunc(("u32Formats=%d\n", u32Formats)); int rc = VbglR3ClipboardFormatsReport(g_ctx.client, u32Formats); LogRelFlowFuncLeaveRC(rc); } /** This is called by the backend to tell us that a request for data from * X11 has completed. * @param pCtx Our context information * @param rc the iprt result code of the request * @param pReq the request structure that we passed in when we started * the request. We RTMemFree() this in this function. * @param pv the clipboard data returned from X11 if the request * succeeded (see @a rc) * @param cb the size of the data in @a pv */ DECLCALLBACK(void) ClipRequestFromX11CompleteCallback(SHCLCONTEXT *pCtx, int rc, CLIPREADCBREQ *pReq, void *pv, uint32_t cb) { RT_NOREF(pCtx); if (RT_SUCCESS(rc)) vboxClipboardSendData(pReq->u32Format, pv, cb); else vboxClipboardSendData(0, NULL, 0); RTMemFree(pReq); } /** * Connect the guest clipboard to the host. * * @returns VBox status code */ int SharedClipboardSvcImplConnect(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, false /* grab */); if (RT_SUCCESS(rc)) { rc = VbglR3ClipboardConnect(&g_ctx.client); if (RT_FAILURE(rc)) VBClLogError("Error connecting to host, rc=%Rrc\n", rc); else if (!g_ctx.client) { VBClLogError("Invalid client ID of 0\n"); rc = VERR_NOT_SUPPORTED; } } if (rc != VINF_SUCCESS && g_ctx.pBackend) ClipDestructX11(g_ctx.pBackend); LogRelFlowFuncLeaveRC(rc); return rc; } /** * 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 = VbglR3ClipboardGetHostMsgOld(g_ctx.client, &Msg, &fFormats); if (RT_SUCCESS(rc)) { switch (Msg) { case VBOX_SHCL_HOST_MSG_FORMATS_REPORT: { /* The host has announced available clipboard formats. * Save the information so that it is available for * future requests from guest applications. */ LogRelFlowFunc(("VBOX_SHCL_HOST_MSG_FORMATS_WRITE fFormats=%x\n", fFormats)); ClipAnnounceFormatToX11(g_ctx.pBackend, fFormats); break; } case VBOX_SHCL_HOST_MSG_READ_DATA: { /* The host needs data in the specified format. */ LogRelFlowFunc(("VBOX_SHCL_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_SHCL_HOST_MSG_QUIT: { /* The host is terminating. */ LogRelFlowFunc(("VBOX_SHCL_HOST_MSG_QUIT\n")); if (RT_SUCCESS(ClipStopX11(g_ctx.pBackend))) ClipDestructX11(g_ctx.pBackend); fExiting = true; break; } default: { VBClLogInfo("Unsupported message from host (%RU32)\n", Msg); break; } } } LogRelFlow(("processed host event rc = %d\n", rc)); } LogRelFlowFuncLeaveRC(rc); return rc; } static const char *getName() { return "Shared Clipboard"; } static const char *getPidFilePath() { return ".vboxclient-clipboard.pid"; } static int run(struct VBCLSERVICE **ppInterface, bool fDaemonised) { RT_NOREF(ppInterface, fDaemonised); /* Initialise the guest library. */ int rc = SharedClipboardSvcImplConnect(); /* Not RT_SUCCESS: VINF_PERMISSION_DENIED is host service not present. */ if (rc == VINF_SUCCESS) rc = vboxClipboardMain(); if (rc == VERR_NOT_SUPPORTED) rc = VINF_SUCCESS; /* Prevent automatic restart. */ if (RT_FAILURE(rc)) LogRelFunc(("guest clipboard service terminated abnormally: return code %Rrc\n", rc)); return rc; } static void cleanup(struct VBCLSERVICE **ppInterface) { RT_NOREF(ppInterface); VbglR3Term(); } struct VBCLSERVICE vbclClipboardInterface = { getName, getPidFilePath, VBClServiceDefaultHandler, /* init */ run, cleanup }; struct CLIPBOARDSERVICE { struct VBCLSERVICE *pInterface; }; struct VBCLSERVICE **VBClGetClipboardService() { struct CLIPBOARDSERVICE *pService = (struct CLIPBOARDSERVICE *)RTMemAlloc(sizeof(*pService)); if (!pService) VBClLogFatalError("Out of memory\n"); pService->pInterface = &vbclClipboardInterface; return &pService->pInterface; }