summaryrefslogtreecommitdiffstats
path: root/src/VBox/Additions/darwin/VBoxClient
diff options
context:
space:
mode:
Diffstat (limited to '')
-rw-r--r--src/VBox/Additions/darwin/VBoxClient/Makefile.kmk57
-rw-r--r--src/VBox/Additions/darwin/VBoxClient/VBoxClient.cpp298
-rw-r--r--src/VBox/Additions/darwin/VBoxClient/VBoxClientClipboard.cpp341
-rw-r--r--src/VBox/Additions/darwin/VBoxClient/VBoxClientClipboardGuestToHost.cpp390
-rw-r--r--src/VBox/Additions/darwin/VBoxClient/VBoxClientClipboardHostToGuest.cpp315
-rw-r--r--src/VBox/Additions/darwin/VBoxClient/VBoxClientInternal.h112
6 files changed, 1513 insertions, 0 deletions
diff --git a/src/VBox/Additions/darwin/VBoxClient/Makefile.kmk b/src/VBox/Additions/darwin/VBoxClient/Makefile.kmk
new file mode 100644
index 00000000..ba520a28
--- /dev/null
+++ b/src/VBox/Additions/darwin/VBoxClient/Makefile.kmk
@@ -0,0 +1,57 @@
+# $Id: Makefile.kmk $
+## @file
+# Sub-Makefile for the VirtualBox Guest Addition Darwin Client.
+#
+
+#
+# Copyright (C) 2006-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
+#
+
+SUB_DEPTH = ../../../../..
+include $(KBUILD_PATH)/subheader.kmk
+
+#
+# VBoxClient - shared clipboard support.
+#
+
+PROGRAMS += VBoxClient
+
+VBoxClient_TEMPLATE = VBoxGuestR3Exe
+VBoxClient_DEFS += VBOX_WITH_HGCM
+
+VBoxClient_SOURCES = \
+ VBoxClient.cpp
+
+ifdef VBOX_WITH_SHARED_CLIPBOARD
+ VBoxClient_DEFS += \
+ VBOX_WITH_SHARED_CLIPBOARD
+ VBoxClient_SOURCES += \
+ VBoxClientClipboard.cpp \
+ VBoxClientClipboardHostToGuest.cpp \
+ VBoxClientClipboardGuestToHost.cpp \
+ $(PATH_ROOT)/src/VBox/GuestHost/SharedClipboard/clipboard-common.cpp
+endif
+
+VBoxClient_LDFLAGS = -framework IOKit -framework ApplicationServices
+VBoxClient_INST = $(INST_ADDITIONS)
+
+include $(FILE_KBUILD_SUB_FOOTER)
+
diff --git a/src/VBox/Additions/darwin/VBoxClient/VBoxClient.cpp b/src/VBox/Additions/darwin/VBoxClient/VBoxClient.cpp
new file mode 100644
index 00000000..c80b1c9b
--- /dev/null
+++ b/src/VBox/Additions/darwin/VBoxClient/VBoxClient.cpp
@@ -0,0 +1,298 @@
+/** $Id: VBoxClient.cpp $ */
+/** @file
+ * VBoxClient - User specific services, 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 <stdio.h>
+#include <signal.h>
+#include <unistd.h>
+#include <stdlib.h>
+
+#include <VBox/log.h>
+#include <VBox/VBoxGuestLib.h>
+#include <iprt/stream.h>
+#include <iprt/initterm.h>
+#include <iprt/message.h>
+#include <iprt/process.h>
+#include <iprt/string.h>
+
+#include "VBoxClientInternal.h"
+
+
+/*********************************************************************************************************************************
+* Glogal Variables *
+*********************************************************************************************************************************/
+
+static int g_cVerbosity = 0;
+static PRTLOGGER g_pLogger = NULL;
+
+static VBOXCLIENTSERVICE g_aServices[] =
+{
+#ifdef VBOX_WITH_SHARED_CLIPBOARD
+ g_ClipboardService
+#endif
+};
+
+
+/**
+ * Create default logger in order to print output to the specified file.
+ *
+ * @return IPRT status code.
+ */
+static int vbclInitLogger(char *pszLogFileName)
+{
+ static const char * const s_apszGroups[] = VBOX_LOGGROUP_NAMES;
+ int rc = RTLogCreateEx(&g_pLogger, "VBOXCLIENT_RELEASE_LOG", RTLOGFLAGS_PREFIX_THREAD | RTLOGFLAGS_PREFIX_TIME_PROG, "all",
+ RT_ELEMENTS(s_apszGroups), s_apszGroups, UINT32_MAX /*cMaxEntriesPerGroup*/,
+ 0 /*cBufDescs*/, NULL /*paBufDescs*/, RTLOGDEST_STDOUT,
+ NULL /*pfnPhase*/,
+ pszLogFileName ? 10 : 0 /*cHistory*/,
+ pszLogFileName ? 100 * _1M : 0 /*cbHistoryFileMax*/,
+ pszLogFileName ? RT_SEC_1DAY : 0 /*cSecsHistoryTimeSlot*/,
+ NULL /*pOutputIf*/, NULL /*pvOutputIfUser*/,
+ NULL /*pErrInfo*/, "%s", pszLogFileName ? pszLogFileName : "");
+ AssertRCReturn(rc, rc);
+
+ /* Register this logger as the release logger */
+ RTLogRelSetDefaultInstance(g_pLogger);
+
+ /* Explicitly flush the log in case of VBOXCLIENT_RELEASE_LOG=buffered. */
+ RTLogFlush(g_pLogger);
+
+ return VINF_SUCCESS;
+}
+
+
+/**
+ * Destroy logger.
+ */
+static void vbclTermLogger(char *szLogFileName)
+{
+ // Why SIGBUS here?
+ RTLogDestroy(RTLogRelSetDefaultInstance(NULL));
+
+ if (szLogFileName)
+ RTStrFree(szLogFileName);
+}
+
+/**
+ * Displays a verbose message.
+ *
+ * @param iLevel Minimum log level required to display this message.
+ * @param pszFormat The message text.
+ * @param ... Format arguments.
+ */
+void VBoxClientVerbose(int iLevel, const char *pszFormat, ...)
+{
+ if (iLevel > g_cVerbosity)
+ return;
+
+ va_list args;
+ va_start(args, pszFormat);
+ char *psz = NULL;
+ RTStrAPrintfV(&psz, pszFormat, args);
+ va_end(args);
+
+ AssertPtr(psz);
+ LogRel(("%s", psz));
+
+ RTStrFree(psz);
+}
+
+/**
+ * Wait for signals in order to safely terminate process.
+ */
+static void vbclWait(void)
+{
+ sigset_t signalMask;
+ int iSignal;
+
+ /* Register signals that we are waiting for */
+ sigemptyset(&signalMask);
+ sigaddset(&signalMask, SIGHUP);
+ sigaddset(&signalMask, SIGINT);
+ sigaddset(&signalMask, SIGQUIT);
+ sigaddset(&signalMask, SIGABRT);
+ sigaddset(&signalMask, SIGTERM);
+ pthread_sigmask(SIG_BLOCK, &signalMask, NULL);
+
+ /* Ignoring return status */
+ sigwait(&signalMask, &iSignal);
+}
+
+/**
+ * Start registered services.
+ *
+ * @return IPRT status code.
+ */
+static int vbclStartServices(void)
+{
+ int rc;
+ unsigned int iServiceId = 0;
+
+ VBoxClientVerbose(1, "Starting services...\n");
+ for (iServiceId = 0; iServiceId < RT_ELEMENTS(g_aServices); iServiceId++)
+ {
+ VBoxClientVerbose(1, "Starting service: %s\n", g_aServices[iServiceId].pszName);
+ rc = (g_aServices[iServiceId].pfnStart)();
+ if (RT_FAILURE(rc))
+ {
+ VBoxClientVerbose(1, "unable to start service: %s (%Rrc)\n", g_aServices[iServiceId].pszName, rc);
+ VBoxClientVerbose(1, "Rolling back..\n");
+
+ /* Stop running services */
+ do
+ {
+ VBoxClientVerbose(1, "Stopping service: %s\n", g_aServices[iServiceId].pszName);
+ int rcStop = (g_aServices[iServiceId].pfnStop)();
+ if (RT_FAILURE(rcStop))
+ VBoxClientVerbose(1, "unable to stop service: %s (%Rrc)\n", g_aServices[iServiceId].pszName, rcStop);
+ } while (--iServiceId != 0);
+
+ break;
+ }
+ }
+
+ if (RT_SUCCESS(rc))
+ VBoxClientVerbose(1, "Services start completed.\n");
+
+ return rc;
+}
+
+/**
+ * Stop registered services.
+ *
+ * @return IPRT status code.
+ */
+static void vbclStopServices(void)
+{
+ unsigned int iServiceId = 0;
+
+ VBoxClientVerbose(1, "Stopping services...\n");
+ for (iServiceId = 0; iServiceId < RT_ELEMENTS(g_aServices); iServiceId++)
+ {
+ VBoxClientVerbose(1, "Stopping service: %s\n", g_aServices[iServiceId].pszName);
+ int rc = (g_aServices[iServiceId].pfnStop)();
+ if (RT_FAILURE(rc))
+ VBoxClientVerbose(1, "unable to stop service: %s (%Rrc)\n", g_aServices[iServiceId].pszName, rc);
+ }
+ VBoxClientVerbose(1, "Services stop completed\n");
+}
+
+
+static void usage(char *sProgName)
+{
+ RTPrintf("usage: %s [-fvl]\n", sProgName);
+ RTPrintf(" -f\tRun in foreground (default: no)\n");
+ RTPrintf(" -v\tIncrease verbosity level (default: no verbosity)\n");
+ RTPrintf(" -l\tSpecify log file name (default: no log file)\n");
+ exit(1);
+}
+
+int main(int argc, char *argv[])
+{
+ int rc;
+ int c;
+
+ bool fDemonize = true;
+ static char *szLogFileName = NULL;
+
+ rc = RTR3InitExe(argc, &argv, 0);
+ if (RT_FAILURE(rc))
+ {
+ RTPrintf("RTR3InitExe() failed: (%Rrc)\n", rc);
+ return RTMsgInitFailure(rc);
+ }
+
+ /* Parse command line */
+ while((c = getopt(argc, argv, "fvl:")) != -1)
+ {
+ switch(c)
+ {
+ case 'f':
+ fDemonize = false;
+ break;
+ case 'v':
+ g_cVerbosity++;
+ break;
+ case 'l':
+ szLogFileName = RTStrDup(optarg);
+ break;
+
+ default : usage(argv[0]);
+ }
+ }
+
+ /* No more arguments allowed */
+ if ((argc - optind) != 0)
+ usage(argv[0]);
+
+ if (fDemonize)
+ {
+ rc = RTProcDaemonizeUsingFork(true /* fNoChDir */, false /* fNoClose */, NULL);
+ if (RT_FAILURE(rc))
+ {
+ RTPrintf("failed to run into background\n");
+ return 1;
+ }
+ }
+
+ rc = VbglR3Init();
+ if (RT_SUCCESS(rc))
+ {
+ rc = vbclInitLogger(szLogFileName);
+ if (RT_SUCCESS(rc))
+ {
+ rc = vbclStartServices();
+ if (RT_SUCCESS(rc))
+ {
+ vbclWait();
+ vbclStopServices();
+ }
+ else
+ {
+ RTPrintf("failed to start services: (%Rrc)\n", rc);
+ }
+
+ vbclTermLogger(szLogFileName);
+ }
+ else
+ {
+ RTPrintf("failed to start logger: (%Rrc)\n", rc);
+ }
+
+ VbglR3Term();
+ }
+ else
+ {
+ RTPrintf("failed to initialize guest library: (%Rrc)\n", rc);
+ }
+
+ return 0;
+}
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,
+};
diff --git a/src/VBox/Additions/darwin/VBoxClient/VBoxClientClipboardGuestToHost.cpp b/src/VBox/Additions/darwin/VBoxClient/VBoxClientClipboardGuestToHost.cpp
new file mode 100644
index 00000000..2cef7257
--- /dev/null
+++ b/src/VBox/Additions/darwin/VBoxClient/VBoxClientClipboardGuestToHost.cpp
@@ -0,0 +1,390 @@
+/** $Id: VBoxClientClipboardGuestToHost.cpp $ */
+/** @file
+ * VBoxClient - Shared Clipboard Guest -> Host copying, 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 <signal.h>
+#include <stdlib.h>
+
+#include <iprt/thread.h>
+#include <iprt/mem.h>
+#include <iprt/initterm.h>
+#include <iprt/message.h>
+#include <iprt/stream.h>
+#include <iprt/utf16.h>
+#include <VBox/VBoxGuestLib.h>
+#include <VBox/GuestHost/SharedClipboard.h>
+#include <VBox/HostServices/VBoxClipboardSvc.h>
+#include <VBox/GuestHost/clipboard-helper.h>
+#include "VBoxClientInternal.h"
+
+/**
+ * Walk through pasteboard items and report currently available item types.
+ *
+ * @param pPasteboard Reference to guest Pasteboard.
+ * @returns Available formats bit field.
+ */
+uint32_t vbclClipboardGetAvailableFormats(PasteboardRef pPasteboard)
+{
+ uint32_t fFormats = 0;
+ ItemCount cItems = 0;
+ ItemCount iItem;
+ OSStatus rc;
+
+#define VBOXCL_ADD_FORMAT_IF_PRESENT(a_kDarwinFmt, a_fVBoxFmt) \
+ if (PasteboardCopyItemFlavorData(pPasteboard, iItemID, a_kDarwinFmt, &flavorData) == noErr) \
+ { \
+ fFormats |= (uint32_t)a_fVBoxFmt; \
+ CFRelease(flavorData); \
+ }
+
+ rc = PasteboardGetItemCount(pPasteboard, &cItems);
+ AssertReturn((rc == noErr) && (cItems > 0), fFormats);
+
+ for (iItem = 1; iItem <= cItems; iItem++)
+ {
+ PasteboardItemID iItemID;
+ CFDataRef flavorData;
+
+ rc = PasteboardGetItemIdentifier(pPasteboard, iItem, &iItemID);
+ if (rc == noErr)
+ {
+ VBOXCL_ADD_FORMAT_IF_PRESENT(kUTTypeUTF16PlainText, VBOX_SHCL_FMT_UNICODETEXT);
+ VBOXCL_ADD_FORMAT_IF_PRESENT(kUTTypeUTF8PlainText, VBOX_SHCL_FMT_UNICODETEXT);
+ VBOXCL_ADD_FORMAT_IF_PRESENT(kUTTypeBMP, VBOX_SHCL_FMT_BITMAP );
+ VBOXCL_ADD_FORMAT_IF_PRESENT(kUTTypeHTML, VBOX_SHCL_FMT_HTML );
+
+#ifdef CLIPBOARD_DUMP_CONTENT_FORMATS
+ CFArrayRef flavorTypeArray;
+ CFIndex flavorCount;
+ CFStringRef flavorType;
+
+ rc = PasteboardCopyItemFlavors(pPasteboard, iItemID, &flavorTypeArray);
+ if (rc == noErr)
+ {
+ VBoxClientVerbose(3, "SCAN..\n");
+ flavorCount = CFArrayGetCount(flavorTypeArray);
+ VBoxClientVerbose(3, "SCAN (%d)..\n", (int)flavorCount);
+ for(CFIndex flavorIndex = 0; flavorIndex < flavorCount; flavorIndex++)
+ {
+ VBoxClientVerbose(3, "SCAN #%d..\n", (int)flavorIndex);
+ flavorType = (CFStringRef)CFArrayGetValueAtIndex(flavorTypeArray, flavorIndex);
+
+ CFDataRef flavorData1;
+ rc = PasteboardCopyItemFlavorData(pPasteboard, iItemID, flavorType, &flavorData1);
+ if (rc == noErr)
+ {
+ VBoxClientVerbose(3, "Found: %s, size: %d\n", (char *)CFStringGetCStringPtr(flavorType, kCFStringEncodingMacRoman), (int)CFDataGetLength(flavorData1));
+ CFRelease(flavorData1);
+ }
+ }
+ VBoxClientVerbose(3, "SCAN COMPLETE\n");
+ CFRelease(flavorTypeArray);
+ }
+#endif /* CLIPBOARD_DUMP_CONTENT_FORMATS */
+ }
+ }
+
+#undef VBOXCL_ADD_FORMAT_IF_PRESENT
+
+ return fFormats;
+}
+
+
+/**
+ * Search for content of specified type in guest clipboard buffer and put
+ * it into newly allocated buffer.
+ *
+ * @param pPasteboard Guest PasteBoard reference.
+ * @param fFormat Data formats we are looking for.
+ * @param ppvData Where to return pointer to the received data. M
+ * @param pcbData Where to return the size of the data.
+ * @param pcbAlloc Where to return the size of the memory block
+ * *ppvData pointes to. (Usually greater than *cbData
+ * because the allocation is page aligned.)
+ * @returns IPRT status code.
+ */
+static int vbclClipboardReadGuestData(PasteboardRef pPasteboard, CFStringRef sFormat, void **ppvData, uint32_t *pcbData,
+ uint32_t *pcbAlloc)
+{
+ ItemCount cItems, iItem;
+ OSStatus rc;
+
+ void *pvData = NULL;
+ uint32_t cbData = 0;
+ uint32_t cbAlloc = 0;
+
+ AssertPtrReturn(ppvData, VERR_INVALID_POINTER);
+ AssertPtrReturn(pcbData, VERR_INVALID_POINTER);
+ AssertPtrReturn(pcbAlloc, VERR_INVALID_POINTER);
+
+ rc = PasteboardGetItemCount(pPasteboard, &cItems);
+ AssertReturn(rc == noErr, VERR_INVALID_PARAMETER);
+ AssertReturn(cItems > 0, VERR_INVALID_PARAMETER);
+
+ /* Walk through all the items in PasteBoard in order to find
+ that one that correcponds to requested data format. */
+ for (iItem = 1; iItem <= cItems; iItem++)
+ {
+ PasteboardItemID iItemID;
+ CFDataRef flavorData;
+
+ /* Now, get the item's flavors that corresponds to requested type. */
+ rc = PasteboardGetItemIdentifier(pPasteboard, iItem, &iItemID);
+ AssertReturn(rc == noErr, VERR_INVALID_PARAMETER);
+ rc = PasteboardCopyItemFlavorData(pPasteboard, iItemID, sFormat, &flavorData);
+ if (rc == noErr)
+ {
+ void *flavorDataPtr = (void *)CFDataGetBytePtr(flavorData);
+ cbData = CFDataGetLength(flavorData);
+ if (flavorDataPtr && cbData > 0)
+ {
+ cbAlloc = RT_ALIGN_32(cbData, PAGE_SIZE);
+ pvData = RTMemPageAllocZ(cbAlloc);
+ if (pvData)
+ memcpy(pvData, flavorDataPtr, cbData);
+ }
+
+ CFRelease(flavorData);
+
+ /* Found first matching item, no more search. */
+ break;
+ }
+
+ }
+
+ /* Found match */
+ if (pvData)
+ {
+ *ppvData = pvData;
+ *pcbData = cbData;
+ *pcbAlloc = cbAlloc;
+
+ return VINF_SUCCESS;
+ }
+
+ return VERR_INVALID_PARAMETER;
+}
+
+
+/**
+ * Release resources occupied by vbclClipboardReadGuestData().
+ */
+static void vbclClipboardReleaseGuestData(void **ppvData, uint32_t cbAlloc)
+{
+ AssertReturnVoid(ppvData);
+ RTMemPageFree(*ppvData, cbAlloc);
+ *ppvData = NULL;
+}
+
+/**
+ * Pass data to host.
+ */
+static int vbclClipboardHostPasteData(uint32_t u32ClientId, uint32_t u32Format, const void *pvData, uint32_t cbData)
+{
+ /* Allow empty buffers */
+ if (cbData == 0)
+ return VbglR3ClipboardWriteData(u32ClientId, u32Format, NULL, 0);
+
+ AssertReturn(pvData, VERR_INVALID_PARAMETER);
+ return VbglR3ClipboardWriteData(u32ClientId, u32Format, (void *)pvData, cbData); /** @todo r=bird: Why on earth does a write function like VbglR3ClipboardWriteData take a non-const parameter? */
+}
+
+/**
+ * Paste text data into host clipboard.
+ *
+ * @param u32ClientId Host clipboard connection.
+ * @param pwszData UTF-16 encoded string.
+ * @param cbData The length of the string, in bytes, probably
+ * including a terminating zero.
+ */
+static int vbclClipboardHostPasteText(uint32_t u32ClientId, PRTUTF16 pwszData, uint32_t cbData)
+{
+ AssertReturn(cbData > 0, VERR_INVALID_PARAMETER);
+ AssertPtrReturn(pwszData, VERR_INVALID_POINTER);
+
+ size_t cwcTmp; /* (includes a schwarzenegger character) */
+ int rc = ShClUtf16LFLenUtf8(pwszData, cbData / sizeof(RTUTF16), &cwcTmp);
+ AssertRCReturn(rc, rc);
+
+ cwcTmp++; /* Add space for terminator. */
+
+ PRTUTF16 pwszTmp = (PRTUTF16)RTMemAlloc(cwcTmp * sizeof(RTUTF16));
+ AssertReturn(pwszTmp, VERR_NO_MEMORY);
+
+ rc = ShClConvUtf16LFToCRLF(pwszData, cbData / sizeof(RTUTF16), pwszTmp, cwcTmp);
+ if (RT_SUCCESS(rc))
+ rc = vbclClipboardHostPasteData(u32ClientId, VBOX_SHCL_FMT_UNICODETEXT,
+ pwszTmp, cwcTmp * sizeof(RTUTF16));
+
+ RTMemFree(pwszTmp);
+
+ return rc;
+}
+
+
+/**
+ * Paste a bitmap onto the host clipboard.
+ *
+ * @param u32ClientId Host clipboard connection.
+ * @param pvData The bitmap data.
+ * @param cbData The size of the bitmap.
+ */
+static int vbclClipboardHostPasteBitmap(uint32_t u32ClientId, void *pvData, uint32_t cbData)
+{
+ const void *pvDib;
+ size_t cbDib;
+ int rc = ShClBmpGetDib(pvData, cbData, &pvDib, &cbDib);
+ AssertRCReturn(rc, rc);
+
+ rc = vbclClipboardHostPasteData(u32ClientId, VBOX_SHCL_FMT_BITMAP, pvDib, cbDib);
+
+ return rc;
+}
+
+
+/**
+ * Read guest's clipboard buffer and forward its content to host.
+ *
+ * @param u32ClientId Host clipboard connection.
+ * @param pPasteboard Guest PasteBoard reference.
+ * @param fFormats List of data formats (bit field) received from host.
+ *
+ * @returns IPRT status code.
+ */
+int vbclClipboardForwardToHost(uint32_t u32ClientId, PasteboardRef pPasteboard, uint32_t fFormats)
+{
+ int rc = VINF_SUCCESS;
+
+ void *pvData = NULL;
+ uint32_t cbData = 0;
+ uint32_t cbAlloc = 0;
+
+ VBoxClientVerbose(3, "vbclClipboardForwardToHost: %d\n", fFormats);
+
+ /* Walk across all item(s) formats */
+ uint32_t fFormatsLeft = fFormats;
+ while (fFormatsLeft)
+ {
+ if (fFormatsLeft & VBOX_SHCL_FMT_UNICODETEXT)
+ {
+ VBoxClientVerbose(3, "requested VBOX_SHCL_FMT_UNICODETEXT: %d\n", fFormats);
+
+ RTUTF16 *pUtf16Str = NULL;
+
+ /* First, try to get UTF16 encoded buffer */
+ rc = vbclClipboardReadGuestData(pPasteboard, kUTTypeUTF16PlainText, &pvData, &cbData, &cbAlloc);
+ if (RT_SUCCESS(rc))
+ {
+ rc = RTUtf16DupEx(&pUtf16Str, (PRTUTF16)pvData, 0);
+ if (RT_FAILURE(rc))
+ pUtf16Str = NULL;
+ }
+ else /* Failed to get UTF16 buffer */
+ {
+ /* Then, try to get UTF8 encoded buffer */
+ rc = vbclClipboardReadGuestData(pPasteboard, kUTTypeUTF8PlainText, &pvData, &cbData, &cbAlloc);
+ if (RT_SUCCESS(rc))
+ {
+ rc = RTStrToUtf16((const char *)pvData, &pUtf16Str);
+ if (RT_FAILURE(rc))
+ pUtf16Str = NULL;
+ }
+ }
+
+ /* Finally, we got UTF16 encoded buffer */
+ if (RT_SUCCESS(rc))
+ {
+ rc = vbclClipboardHostPasteText(u32ClientId, (PRTUTF16)pvData, cbData);
+
+ if (pUtf16Str)
+ {
+ RTUtf16Free(pUtf16Str);
+ pUtf16Str = NULL;
+ }
+
+ vbclClipboardReleaseGuestData(&pvData, cbAlloc);
+ }
+ else
+ {
+ /* No data found or error occurred: send empty buffer */
+ rc = vbclClipboardHostPasteData(u32ClientId, VBOX_SHCL_FMT_UNICODETEXT, NULL, 0);
+ }
+
+ fFormatsLeft &= ~(uint32_t)VBOX_SHCL_FMT_UNICODETEXT;
+ }
+
+ else if (fFormatsLeft & VBOX_SHCL_FMT_BITMAP)
+ {
+ VBoxClientVerbose(3, "requested VBOX_SHCL_FMT_BITMAP: %d\n", fFormats);
+
+ rc = vbclClipboardReadGuestData(pPasteboard, kUTTypeBMP, &pvData, &cbData, &cbAlloc);
+ if (RT_SUCCESS(rc))
+ {
+ rc = vbclClipboardHostPasteBitmap(u32ClientId, pvData, cbData);
+ vbclClipboardReleaseGuestData(&pvData, cbAlloc);
+ }
+ else
+ {
+ /* No data found or error occurred: send empty buffer */
+ rc = vbclClipboardHostPasteData(u32ClientId, VBOX_SHCL_FMT_BITMAP, NULL, 0);
+ }
+
+ fFormatsLeft &= ~(uint32_t)VBOX_SHCL_FMT_BITMAP;
+ }
+
+ else if (fFormatsLeft & VBOX_SHCL_FMT_HTML)
+ {
+ VBoxClientVerbose(3, "requested VBOX_SHCL_FMT_HTML: %d\n", fFormats);
+
+ rc = vbclClipboardReadGuestData(pPasteboard, kUTTypeHTML, &pvData, &cbData, &cbAlloc);
+ if (RT_SUCCESS(rc))
+ {
+ rc = vbclClipboardHostPasteData(u32ClientId, VBOX_SHCL_FMT_HTML, pvData, cbData);
+ vbclClipboardReleaseGuestData(&pvData, cbAlloc);
+ }
+ else
+ {
+ /* No data found or error occurred: send empty buffer */
+ rc = vbclClipboardHostPasteData(u32ClientId, VBOX_SHCL_FMT_HTML, NULL, 0);
+ }
+
+ fFormatsLeft &= ~(uint32_t)VBOX_SHCL_FMT_HTML;
+ }
+
+ else
+ {
+ VBoxClientVerbose(3, "requested data in unsupported format: %#x\n", fFormatsLeft);
+ break;
+ }
+ }
+
+ return rc; /** @todo r=bird: If there are multiple formats available, which rc is returned here? Does it matter? */
+}
diff --git a/src/VBox/Additions/darwin/VBoxClient/VBoxClientClipboardHostToGuest.cpp b/src/VBox/Additions/darwin/VBoxClient/VBoxClientClipboardHostToGuest.cpp
new file mode 100644
index 00000000..1b2d729f
--- /dev/null
+++ b/src/VBox/Additions/darwin/VBoxClient/VBoxClientClipboardHostToGuest.cpp
@@ -0,0 +1,315 @@
+/** $Id: VBoxClientClipboardHostToGuest.cpp $ */
+/** @file
+ * VBoxClient - Shared Clipboard Host -> Guest copying, 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 <signal.h>
+#include <stdlib.h>
+
+#include <iprt/initterm.h>
+#include <iprt/mem.h>
+#include <iprt/message.h>
+#include <iprt/stream.h>
+#include <iprt/thread.h>
+#include <iprt/utf16.h>
+#include <VBox/VBoxGuestLib.h>
+#include <VBox/GuestHost/SharedClipboard.h>
+#include <VBox/HostServices/VBoxClipboardSvc.h>
+#include <VBox/GuestHost/clipboard-helper.h>
+#include "VBoxClientInternal.h"
+
+/**
+ * Allocate memory for host buffer and receive it.
+ *
+ * @param u32ClientId Host connection.
+ * @param fFormat Buffer data format.
+ * @param pData Where to store received data.
+ * @param cbDataSize The size of the received data.
+ * @param cbMemSize The actual size of memory occupied by *pData.
+ *
+ * @returns IPRT status code.
+ */
+static int vbclClipboardReadHostData(uint32_t u32ClientId, uint32_t fFormat, void **pData, uint32_t *cbDataSize, uint32_t *cbMemSize)
+{
+ int rc;
+
+ AssertReturn(pData && cbDataSize && cbMemSize, VERR_INVALID_PARAMETER);
+
+ uint32_t cbDataSizeInternal = _4K;
+ uint32_t cbMemSizeInternal = cbDataSizeInternal;
+ void *pDataInternal = RTMemPageAllocZ(cbDataSizeInternal);
+
+ if (!pDataInternal)
+ return VERR_NO_MEMORY;
+
+ rc = VbglR3ClipboardReadData(u32ClientId, fFormat, pDataInternal, cbMemSizeInternal, &cbDataSizeInternal);
+ if (rc == VINF_BUFFER_OVERFLOW)
+ {
+ /* Reallocate bigger buffer and receive all the data */
+ RTMemPageFree(pDataInternal, cbMemSizeInternal);
+ cbDataSizeInternal = cbMemSizeInternal = RT_ALIGN_32(cbDataSizeInternal, PAGE_SIZE);
+ pDataInternal = RTMemPageAllocZ(cbMemSizeInternal);
+ if (!pDataInternal)
+ return VERR_NO_MEMORY;
+
+ rc = VbglR3ClipboardReadData(u32ClientId, fFormat, pDataInternal, cbMemSizeInternal, &cbDataSizeInternal);
+ }
+
+ /* Error occurred of zero-sized buffer */
+ if (RT_FAILURE(rc))
+ {
+ RTMemPageFree(pDataInternal, cbMemSizeInternal);
+ return VERR_NO_MEMORY;
+ }
+
+ *pData = pDataInternal;
+ *cbDataSize = cbDataSizeInternal;
+ *cbMemSize = cbMemSizeInternal;
+
+ return rc;
+}
+
+/**
+ * Release memory occupied by host buffer.
+ *
+ * @param pData Pointer to memory occupied by host buffer.
+ * @param cbMemSize The actual size of memory occupied by *pData.
+ */
+static void vbclClipboardReleaseHostData(void **pData, uint32_t cbMemSize)
+{
+ AssertReturnVoid(pData && cbMemSize > 0);
+ RTMemPageFree(*pData, cbMemSize);
+}
+
+/**
+ * Paste buffer into guest clipboard.
+ *
+ * @param pPasteboard Guest PasteBoard reference.
+ * @param pData Data to be pasted.
+ * @param cbDataSize The size of *pData.
+ * @param fFormat Buffer data format.
+ * @param fClear Whether or not clear guest clipboard before insert data.
+ *
+ * @returns IPRT status code.
+ */
+static int vbclClipboardGuestPasteData(PasteboardRef pPasteboard, UInt8 *pData, CFIndex cbDataSize, CFStringRef sFormat, bool fClear)
+{
+ PasteboardItemID itemId = (PasteboardItemID)1;
+ CFDataRef textData = NULL;
+ OSStatus rc;
+
+ /* Ignoring sunchronization flags here */
+ PasteboardSynchronize(pPasteboard);
+
+ if (fClear)
+ {
+ rc = PasteboardClear(pPasteboard);
+ AssertReturn(rc == noErr, VERR_NOT_SUPPORTED);
+ }
+
+ /* Create a CData object which we could pass to the pasteboard */
+ if ((textData = CFDataCreate(kCFAllocatorDefault, pData, cbDataSize)))
+ {
+ /* Put the Utf-8 version to the pasteboard */
+ rc = PasteboardPutItemFlavor(pPasteboard, itemId, sFormat, textData, 0);
+ CFRelease(textData);
+ if (rc != noErr)
+ {
+ VBoxClientVerbose(3, "unable to put data into guest's clipboard: %d\n", rc);
+ return VERR_GENERAL_FAILURE;
+ }
+ }
+ else
+ return VERR_NO_MEMORY;
+
+ /* Synchronize updated content */
+ PasteboardSynchronize(pPasteboard);
+
+ return VINF_SUCCESS;
+}
+
+/**
+ * Paste text data into guest clipboard.
+ *
+ * @param pPasteboard Guest PasteBoard reference.
+ * @param pData Data to be pasted.
+ * @param cbDataSize Size of *pData.
+ */
+static int vbclClipboardGuestPasteText(PasteboardRef pPasteboard, void *pData, uint32_t cbDataSize)
+{
+ AssertReturn(pData, VERR_INVALID_PARAMETER);
+
+ /* Skip zero-sized buffer */
+ AssertReturn(cbDataSize > 0, VINF_SUCCESS);
+
+ /* If buffer content is Unicode text, then deliver
+ it in both formats UTF16 (original) and UTF8. */
+
+ /* Convert END-OF-LINE */
+ size_t cwcDst;
+ int rc = ShClUtf16CRLFLenUtf8((RTUTF16 *)pData, cbDataSize / sizeof(RTUTF16), &cwcDst);
+ AssertRCReturn(rc, rc);
+
+ cwcDst++; /* Add space for terminator. */
+
+ PRTUTF16 pwszDst = (RTUTF16 *)RTMemAlloc(cwcDst * sizeof(RTUTF16));
+ AssertPtrReturn(pwszDst, VERR_NO_MEMORY);
+
+ rc = ShClConvUtf16CRLFToLF((RTUTF16 *)pData, cbDataSize / sizeof(RTUTF16), pwszDst, cwcDst);
+ if (RT_SUCCESS(rc))
+ {
+ /* Paste UTF16 */
+ rc = vbclClipboardGuestPasteData(pPasteboard, (UInt8 *)pwszDst, cwcDst * sizeof(RTUTF16), kUTTypeUTF16PlainText, true);
+ if (RT_SUCCESS(rc))
+ {
+ /* Paste UTF8 */
+ char *pszDst;
+ rc = RTUtf16ToUtf8((PRTUTF16)pwszDst, &pszDst);
+ if (RT_SUCCESS(rc))
+ {
+ rc = vbclClipboardGuestPasteData(pPasteboard, (UInt8 *)pszDst, strlen(pszDst), kUTTypeUTF8PlainText, false);
+ RTStrFree(pszDst);
+ }
+ }
+
+ }
+
+ RTMemFree(pwszDst);
+
+ return rc;
+}
+
+/**
+ * Paste picture data into guest clipboard.
+ *
+ * @param pPasteboard Guest PasteBoard reference.
+ * @param pData Data to be pasted.
+ * @param cbDataSize The size of *pData.
+ *
+ * @returns IPRT status code.
+ */
+static int vbclClipboardGuestPastePicture(PasteboardRef pPasteboard, void *pData, uint32_t cbDataSize)
+{
+ int rc;
+ void *pBmp;
+ size_t cbBmpSize;
+
+ AssertReturn(pData, VERR_INVALID_PARAMETER);
+ /* Skip zero-sized buffer */
+ AssertReturn(cbDataSize > 0, VINF_SUCCESS);
+
+ rc = ShClDibToBmp(pData, cbDataSize, &pBmp, &cbBmpSize);
+ AssertReturn(RT_SUCCESS(rc), rc);
+
+ rc = vbclClipboardGuestPasteData(pPasteboard, (UInt8 *)pBmp, cbBmpSize, kUTTypeBMP, true);
+ RTMemFree(pBmp);
+
+ return rc;
+}
+
+/**
+ * Read host's clipboard buffer and put its content to guest clipboard.
+ *
+ * @param u32ClientId Host connection.
+ * @param pPasteboard Guest PasteBoard reference.
+ * @param fFormats List of data formats (bit field) received from host.
+ *
+ * @returns IPRT status code.
+ */
+int vbclClipboardForwardToGuest(uint32_t u32ClientId, PasteboardRef pPasteboard, uint32_t fFormats)
+{
+ int rc = VERR_INVALID_PARAMETER;
+ void *pData;
+ uint32_t cbDataSize, cbMemSize;
+ uint32_t fFormatsInternal = fFormats;
+
+ /* Walk across all item(s) formats */
+ while (fFormatsInternal)
+ {
+ if (fFormatsInternal & VBOX_SHCL_FMT_UNICODETEXT)
+ {
+ VBoxClientVerbose(3, "found VBOX_SHCL_FMT_UNICODETEXT: %d\n", fFormatsInternal);
+
+ rc = vbclClipboardReadHostData(u32ClientId, VBOX_SHCL_FMT_UNICODETEXT, &pData, &cbDataSize, &cbMemSize);
+ if (RT_SUCCESS(rc))
+ {
+ /* Store data in guest buffer */
+ rc = vbclClipboardGuestPasteText(pPasteboard, pData, cbDataSize);
+
+ /* Release occupied resources */
+ vbclClipboardReleaseHostData(&pData, cbMemSize);
+ }
+
+ fFormatsInternal &= ~((uint32_t)VBOX_SHCL_FMT_UNICODETEXT);
+ }
+
+ else if (fFormatsInternal & VBOX_SHCL_FMT_BITMAP)
+ {
+ VBoxClientVerbose(3, "found VBOX_SHCL_FMT_BITMAP: %d\n", fFormatsInternal);
+
+ rc = vbclClipboardReadHostData(u32ClientId, VBOX_SHCL_FMT_BITMAP, &pData, &cbDataSize, &cbMemSize);
+ if (RT_SUCCESS(rc))
+ {
+ /* Store data in guest buffer */
+ rc = vbclClipboardGuestPastePicture(pPasteboard, pData, cbDataSize);
+
+ /* Release occupied resources */
+ vbclClipboardReleaseHostData(&pData, cbMemSize);
+ }
+
+ fFormatsInternal &= ~((uint32_t)VBOX_SHCL_FMT_BITMAP);
+ }
+
+ else if (fFormatsInternal & VBOX_SHCL_FMT_HTML)
+ {
+ VBoxClientVerbose(3, "found VBOX_SHCL_FMT_HTML: %d\n", fFormatsInternal);
+
+ rc = vbclClipboardReadHostData(u32ClientId, VBOX_SHCL_FMT_HTML, &pData, &cbDataSize, &cbMemSize);
+ if (RT_SUCCESS(rc))
+ {
+ /* Store data in guest buffer */
+ rc = vbclClipboardGuestPasteData(pPasteboard, (UInt8 *)pData, cbDataSize, kUTTypeHTML, true);
+
+ /* Release occupied resources */
+ vbclClipboardReleaseHostData(&pData, cbMemSize);
+ }
+
+ fFormatsInternal &= ~((uint32_t)VBOX_SHCL_FMT_HTML);
+ }
+
+ else
+ {
+ VBoxClientVerbose(3, "received data in unsupported format: %d\n", fFormats);
+ break;
+ }
+ }
+
+ return rc;
+}
diff --git a/src/VBox/Additions/darwin/VBoxClient/VBoxClientInternal.h b/src/VBox/Additions/darwin/VBoxClient/VBoxClientInternal.h
new file mode 100644
index 00000000..73a93b2a
--- /dev/null
+++ b/src/VBox/Additions/darwin/VBoxClient/VBoxClientInternal.h
@@ -0,0 +1,112 @@
+/** $Id: VBoxClientInternal.h $ */
+/** @file
+ * VBoxClient - common definitions, 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
+ */
+
+#ifndef GA_INCLUDED_SRC_darwin_VBoxClient_VBoxClientInternal_h
+#define GA_INCLUDED_SRC_darwin_VBoxClient_VBoxClientInternal_h
+#ifndef RT_WITHOUT_PRAGMA_ONCE
+# pragma once
+#endif
+
+#include <VBox/VBoxGuestLib.h>
+#include <Carbon/Carbon.h>
+
+/* Service description */
+typedef struct
+{
+ /** The service name. */
+ const char *pszName;
+
+ /**
+ * Start service.
+ * @returns VBox status code.
+ */
+ DECLCALLBACKMEMBER(int, pfnStart,(void));
+
+ /**
+ * Stop service.
+ * @returns VBox status code.
+ */
+ DECLCALLBACKMEMBER(int, pfnStop,(void));
+
+} VBOXCLIENTSERVICE;
+
+
+/*
+ * Services
+ */
+
+RT_C_DECLS_BEGIN
+
+extern VBOXCLIENTSERVICE g_ClipboardService;
+
+RT_C_DECLS_END
+
+
+/*
+ * Functions
+ */
+
+/**
+ * Displays a verbose message.
+ *
+ * @param iLevel Minimum log level required to display this message.
+ * @param pszFormat The message text.
+ * @param ... Format arguments.
+ */
+extern void VBoxClientVerbose(int iLevel, const char *pszFormat, ...);
+
+/**
+ * Walk through pasteboard items and report currently available item types.
+ *
+ * @param pPasteboard Reference to guest Pasteboard.
+ # @returns Available formats bit field.
+ */
+extern uint32_t vbclClipboardGetAvailableFormats(PasteboardRef pPasteboard);
+
+/**
+ * Read host's clipboard buffer and put its content to guest clipboard.
+ *
+ * @param u32ClientId Host connection.
+ * @param pPasteboard Guest PasteBoard reference.
+ * @param fFormats List of data formats (bit field) received from host.
+ *
+ * @returns IPRT status code.
+ */
+extern int vbclClipboardForwardToGuest(uint32_t u32ClientId, PasteboardRef pPasteboard, uint32_t fFormats);
+
+/**
+ * Read guest's clipboard buffer and forward its content to host.
+ *
+ * @param u32ClientId Host clipboard connection.
+ * @param pPasteboard Guest PasteBoard reference.
+ * @param fFormats List of data formats (bit field) received from host.
+ *
+ * @returns IPRT status code.
+ */
+extern int vbclClipboardForwardToHost(uint32_t u32ClientId, PasteboardRef pPasteboard, uint32_t fFormats);
+
+#endif /* !GA_INCLUDED_SRC_darwin_VBoxClient_VBoxClientInternal_h */