summaryrefslogtreecommitdiffstats
path: root/src/VBox/Additions/darwin/VBoxClient/VBoxClientClipboard.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/VBox/Additions/darwin/VBoxClient/VBoxClientClipboard.cpp')
-rw-r--r--src/VBox/Additions/darwin/VBoxClient/VBoxClientClipboard.cpp341
1 files changed, 341 insertions, 0 deletions
diff --git a/src/VBox/Additions/darwin/VBoxClient/VBoxClientClipboard.cpp b/src/VBox/Additions/darwin/VBoxClient/VBoxClientClipboard.cpp
new file mode 100644
index 00000000..cab193f5
--- /dev/null
+++ b/src/VBox/Additions/darwin/VBoxClient/VBoxClientClipboard.cpp
@@ -0,0 +1,341 @@
+/** $Id: VBoxClientClipboard.cpp $ */
+/** @file
+ * VBoxClient - Shared Slipboard Dispatcher, Darwin.
+ */
+
+/*
+ * Copyright (C) 2007-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only
+ */
+
+
+/*********************************************************************************************************************************
+* Header Files *
+*********************************************************************************************************************************/
+#include <Carbon/Carbon.h>
+
+#include <iprt/asm.h>
+#include <iprt/stream.h>
+#include <iprt/thread.h>
+#include <iprt/critsect.h>
+#include <VBox/VBoxGuestLib.h>
+#include <VBox/GuestHost/SharedClipboard.h>
+#include <VBox/HostServices/VBoxClipboardSvc.h>
+#include <VBox/GuestHost/clipboard-helper.h>
+#include "VBoxClientInternal.h"
+
+
+/*********************************************************************************************************************************
+* Global Variables *
+*********************************************************************************************************************************/
+
+/** Host clipboard connection client ID */
+static uint32_t g_u32ClientId;
+/* Guest clipboard reference */
+static PasteboardRef g_PasteboardRef;
+/* Dispatcher tharead handle */
+RTTHREAD g_DispatcherThread;
+/* Pasteboard polling tharead handle */
+RTTHREAD g_GuestPasteboardThread;
+/* Flag that indicates whether or not dispatcher and Pasteboard polling threada should stop */
+static bool volatile g_fShouldStop;
+/* Barrier for Pasteboard */
+static RTCRITSECT g_critsect;
+
+
+/*********************************************************************************************************************************
+* Local Macros *
+*********************************************************************************************************************************/
+
+#define VBOXCLIENT_SERVICE_NAME "clipboard"
+
+
+/*********************************************************************************************************************************
+* Local Function Prototypes *
+*********************************************************************************************************************************/
+static DECLCALLBACK(int) vbclClipboardStop(void);
+
+
+/**
+ * Clipboard dispatcher function.
+ *
+ * Forwards cliproard content between host and guest.
+ *
+ * @param ThreadSelf Unused parameter.
+ * @param pvUser Unused parameter.
+ *
+ * @return IPRT status code.
+ */
+static DECLCALLBACK(int) vbclClipboardDispatcher(RTTHREAD ThreadSelf, void *pvUser)
+{
+ bool fQuit = false;
+ NOREF(ThreadSelf);
+ NOREF(pvUser);
+
+ VBoxClientVerbose(2, "starting host clipboard polling thread\n");
+
+ /*
+ * Block all signals for this thread. Only the main thread will handle signals.
+ */
+ sigset_t signalMask;
+ sigfillset(&signalMask);
+ pthread_sigmask(SIG_BLOCK, &signalMask, NULL);
+
+ while (!fQuit && !ASMAtomicReadBool(&g_fShouldStop))
+ {
+ int rc;
+ uint32_t Msg;
+ uint32_t fFormats;
+
+ VBoxClientVerbose(2, "waiting for new host request\n");
+
+ rc = VbglR3ClipboardGetHostMsgOld(g_u32ClientId, &Msg, &fFormats);
+ if (RT_SUCCESS(rc))
+ {
+ RTCritSectEnter(&g_critsect);
+ switch (Msg)
+ {
+ /* The host is terminating */
+ case VBOX_SHCL_HOST_MSG_QUIT:
+ VBoxClientVerbose(2, "host requested quit\n");
+ fQuit = true;
+ break;
+
+ /* The host needs data in the specified format */
+ case VBOX_SHCL_HOST_MSG_READ_DATA:
+ VBoxClientVerbose(2, "host requested guest's clipboard read\n");
+ rc = vbclClipboardForwardToHost(g_u32ClientId, g_PasteboardRef, fFormats);
+ AssertMsg(RT_SUCCESS(rc), ("paste to host failed\n"));
+ break;
+
+ /* The host has announced available clipboard formats */
+ case VBOX_SHCL_HOST_MSG_FORMATS_REPORT:
+ VBoxClientVerbose(2, "host requested guest's clipboard write\n");
+ rc = vbclClipboardForwardToGuest(g_u32ClientId, g_PasteboardRef, fFormats);
+ AssertMsg(RT_SUCCESS(rc), ("paste to guest failed\n"));
+ break;
+
+ default:
+ VBoxClientVerbose(2, "received unknow command from host service\n");
+ RTThreadSleep(1000);
+ }
+
+ RTCritSectLeave(&g_critsect);
+ }
+ else
+ {
+ RTThreadSleep(1000);
+ }
+ }
+
+ VBoxClientVerbose(2, "host clipboard polling thread stopped\n");
+
+ return VINF_SUCCESS;
+}
+
+
+/**
+ * Clipboard dispatcher function.
+ *
+ * Forwards cliproard content between host and guest.
+ *
+ * @param hThreadSelf Unused parameter.
+ * @param pvUser Unused parameter.
+ *
+ * @return IPRT status code.
+ */
+static DECLCALLBACK(int) vbclGuestPasteboardPoll(RTTHREAD hThreadSelf, void *pvUser)
+{
+ RT_NOREF(hThreadSelf, pvUser);
+
+ /*
+ * Block all signals for this thread. Only the main thread will handle signals.
+ */
+ sigset_t signalMask;
+ sigfillset(&signalMask);
+ pthread_sigmask(SIG_BLOCK, &signalMask, NULL);
+
+ VBoxClientVerbose(2, "starting guest clipboard polling thread\n");
+
+ while (!ASMAtomicReadBool(&g_fShouldStop))
+ {
+ PasteboardSyncFlags fSyncFlags;
+ uint32_t fFormats;
+ int rc;
+
+ RTCritSectEnter(&g_critsect);
+
+ fSyncFlags = PasteboardSynchronize(g_PasteboardRef);
+ if (fSyncFlags & kPasteboardModified)
+ {
+ fFormats = vbclClipboardGetAvailableFormats(g_PasteboardRef);
+ rc = VbglR3ClipboardReportFormats(g_u32ClientId, fFormats);
+ if (RT_FAILURE(rc))
+ {
+ VBoxClientVerbose(2, "failed to report pasteboard update (%Rrc)\n", rc);
+ }
+ else
+ {
+ VBoxClientVerbose(2, "guest clipboard update reported: %d\n", (int)fFormats);
+ }
+ }
+
+ RTCritSectLeave(&g_critsect);
+
+ /* Check pasteboard every 200 ms */
+ RTThreadSleep(200);
+ }
+
+ VBoxClientVerbose(2, "guest clipboard polling thread stopped\n");
+
+ return VINF_SUCCESS;
+}
+
+
+/**
+ * Initialize host and guest clipboards, start clipboard dispatcher loop.
+ *
+ * @return IPRT status code.
+ */
+static DECLCALLBACK(int) vbclClipboardStart(void)
+{
+ int rc;
+
+ VBoxClientVerbose(2, "starting clipboard\n");
+
+ rc = RTCritSectInit(&g_critsect);
+ if (RT_FAILURE(rc))
+ return VERR_GENERAL_FAILURE;
+
+ rc = VbglR3ClipboardConnect(&g_u32ClientId);
+ if (RT_SUCCESS(rc))
+ {
+ rc = PasteboardCreate(kPasteboardClipboard, &g_PasteboardRef);
+ if (rc == noErr)
+ {
+ /* Start dispatcher loop */
+ ASMAtomicWriteBool(&g_fShouldStop, false);
+ rc = RTThreadCreate(&g_DispatcherThread,
+ vbclClipboardDispatcher,
+ (void *)NULL,
+ 0,
+ RTTHREADTYPE_DEFAULT,
+ RTTHREADFLAGS_WAITABLE,
+ VBOXCLIENT_SERVICE_NAME);
+ if (RT_SUCCESS(rc))
+ {
+ /* Start dispatcher loop */
+ ASMAtomicWriteBool(&g_fShouldStop, false);
+ rc = RTThreadCreate(&g_GuestPasteboardThread,
+ vbclGuestPasteboardPoll,
+ (void *)NULL,
+ 0,
+ RTTHREADTYPE_DEFAULT,
+ RTTHREADFLAGS_WAITABLE,
+ VBOXCLIENT_SERVICE_NAME);
+ if (RT_SUCCESS(rc))
+ return VINF_SUCCESS;
+
+ /* Stop dispatcher thread */
+ ASMAtomicWriteBool(&g_fShouldStop, true);
+ RTThreadWait(g_DispatcherThread, 10 * 1000 /* Wait 10 seconds */, NULL);
+
+ }
+ VBoxClientVerbose(2, "unable create dispatcher thread\n");
+ CFRelease(g_PasteboardRef);
+ g_PasteboardRef = NULL;
+
+ }
+ else
+ {
+ rc = VERR_GENERAL_FAILURE;
+ VBoxClientVerbose(2, "unable access guest clipboard\n");
+ }
+
+ vbclClipboardStop();
+
+ }
+ else
+ {
+ VBoxClientVerbose(2, "unable to establish connection to clipboard service: %Rrc\n", rc);
+ }
+
+ RTCritSectDelete(&g_critsect);
+
+ return rc;
+}
+
+
+/**
+ * Release host and guest clipboards, stop clipboard dispatcher loop.
+ *
+ * @return IPRT status code.
+ */
+static DECLCALLBACK(int) vbclClipboardStop(void)
+{
+ int rc;
+
+ VBoxClientVerbose(2, "stopping clipboard\n");
+
+ AssertReturn(g_u32ClientId != 0, VERR_GENERAL_FAILURE);
+
+ VbglR3ClipboardReportFormats(g_u32ClientId, 0);
+
+ rc = VbglR3ClipboardDisconnect(g_u32ClientId);
+ if (RT_SUCCESS(rc))
+ g_u32ClientId = 0;
+ else
+ VBoxClientVerbose(2, "unable to close clipboard service connection: %Rrc\n", rc);
+
+ if (g_PasteboardRef)
+ {
+ CFRelease(g_PasteboardRef);
+ g_PasteboardRef = NULL;
+ }
+
+ /* Stop dispatcher thread */
+ ASMAtomicWriteBool(&g_fShouldStop, true);
+ rc = RTThreadWait(g_DispatcherThread, 10 * 1000 /* Wait 10 seconds */, NULL);
+ if (RT_FAILURE(rc))
+ VBoxClientVerbose(2, "failed to stop dispatcher thread");
+
+ /* Stop Pasteboard polling thread */
+ rc = RTThreadWait(g_GuestPasteboardThread, 10 * 1000 /* Wait 10 seconds */, NULL);
+ if (RT_FAILURE(rc))
+ VBoxClientVerbose(2, "failed to stop pasteboard polling thread");
+
+ RTCritSectDelete(&g_critsect);
+
+ return rc;
+}
+
+
+/* Clipboard service struct */
+VBOXCLIENTSERVICE g_ClipboardService =
+{
+ /* pszName */
+ VBOXCLIENT_SERVICE_NAME,
+
+ /* pfnStart */
+ vbclClipboardStart,
+
+ /* pfnStop */
+ vbclClipboardStop,
+};