summaryrefslogtreecommitdiffstats
path: root/src/VBox/Additions/x11/VBoxClient/clipboard.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/VBox/Additions/x11/VBoxClient/clipboard.cpp')
-rw-r--r--src/VBox/Additions/x11/VBoxClient/clipboard.cpp335
1 files changed, 335 insertions, 0 deletions
diff --git a/src/VBox/Additions/x11/VBoxClient/clipboard.cpp b/src/VBox/Additions/x11/VBoxClient/clipboard.cpp
new file mode 100644
index 00000000..145eba9c
--- /dev/null
+++ b/src/VBox/Additions/x11/VBoxClient/clipboard.cpp
@@ -0,0 +1,335 @@
+/** $Id: clipboard.cpp $ */
+/** @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 <iprt/alloc.h>
+#include <iprt/asm.h>
+#include <iprt/assert.h>
+#include <iprt/initterm.h>
+#include <iprt/mem.h>
+#include <iprt/string.h>
+#include <iprt/process.h>
+#include <iprt/semaphore.h>
+
+#include <VBox/log.h>
+#include <VBox/VBoxGuestLib.h>
+#include <VBox/HostServices/VBoxClipboardSvc.h>
+#include <VBox/GuestHost/SharedClipboard.h>
+
+#include "VBoxClient.h"
+
+
+/*********************************************************************************************************************************
+* Global Variables *
+*********************************************************************************************************************************/
+
+/**
+ * Global clipboard context information.
+ */
+struct _VBOXCLIPBOARDCONTEXT
+{
+ /** 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 VBOXCLIPBOARDCONTEXT 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);
+ LogRelFlowFunc(("rc=%Rrc\n", 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
+ */
+int ClipRequestDataForX11(VBOXCLIPBOARDCONTEXT *pCtx, uint32_t u32Format, void **ppv, uint32_t *pcb)
+{
+ RT_NOREF1(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);
+ }
+ LogRelFlowFunc(("returning %Rrc\n", 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 advertise
+ */
+void ClipReportX11Formats(VBOXCLIPBOARDCONTEXT *pCtx, uint32_t u32Formats)
+{
+ RT_NOREF1(pCtx);
+ LogRelFlowFunc(("u32Formats=%d\n", u32Formats));
+ int rc = VbglR3ClipboardReportFormats(g_ctx.client, u32Formats);
+ LogRelFlowFunc(("rc=%Rrc\n", 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
+ */
+void ClipCompleteDataRequestFromX11(VBOXCLIPBOARDCONTEXT *pCtx, int rc, CLIPREADCBREQ *pReq, void *pv, uint32_t cb)
+{
+ RT_NOREF1(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 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;
+}
+
+/**
+ * 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;
+}
+
+static const char *getPidFilePath()
+{
+ return ".vboxclient-clipboard.pid";
+}
+
+static int run(struct VBCLSERVICE **ppInterface, bool fDaemonised)
+{
+ RT_NOREF2(ppInterface, fDaemonised);
+
+ /* Initialise the guest library. */
+ int rc = VbglR3InitUser();
+ if (RT_FAILURE(rc))
+ VBClFatalError(("Failed to connect to the VirtualBox kernel service, rc=%Rrc\n", rc));
+ rc = vboxClipboardConnect();
+ /* 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)
+{
+ NOREF(ppInterface);
+ VbglR3Term();
+}
+
+struct VBCLSERVICE vbclClipboardInterface =
+{
+ getPidFilePath,
+ VBClServiceDefaultHandler, /* init */
+ run,
+ cleanup
+};
+
+struct CLIPBOARDSERVICE
+{
+ struct VBCLSERVICE *pInterface;
+};
+
+struct VBCLSERVICE **VBClGetClipboardService()
+{
+ struct CLIPBOARDSERVICE *pService =
+ (struct CLIPBOARDSERVICE *)RTMemAlloc(sizeof(*pService));
+
+ if (!pService)
+ VBClFatalError(("Out of memory\n"));
+ pService->pInterface = &vbclClipboardInterface;
+ return &pService->pInterface;
+}