VirtualBox

source: vbox/trunk/src/VBox/HostServices/SharedClipboard/VBoxSharedClipboardSvc-darwin.cpp@ 100619

Last change on this file since 100619 was 100204, checked in by vboxsync, 18 months ago

Shared Clipboard: Unified root list entry code to also use the generic list entry code, a lot of updates for the cross OS transfer handling code, more updates for HTTP server transfer handling.

This also changed the handling of how that transfers are being initiated, as we needed to have this for X11: Before, transfers were initiated as soon as on side announced the URI list format -- now we postpone initiating the transfer until the receiving side requests the data as URI list.

bugref:9437

  • Property eol-style set to native
  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 10.6 KB
Line 
1/* $Id: VBoxSharedClipboardSvc-darwin.cpp 100204 2023-06-19 09:11:37Z vboxsync $ */
2/** @file
3 * Shared Clipboard Service - Mac OS X host.
4 */
5
6/*
7 * Copyright (C) 2008-2023 Oracle and/or its affiliates.
8 *
9 * This file is part of VirtualBox base platform packages, as
10 * available from https://www.virtualbox.org.
11 *
12 * This program is free software; you can redistribute it and/or
13 * modify it under the terms of the GNU General Public License
14 * as published by the Free Software Foundation, in version 3 of the
15 * License.
16 *
17 * This program is distributed in the hope that it will be useful, but
18 * WITHOUT ANY WARRANTY; without even the implied warranty of
19 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
20 * General Public License for more details.
21 *
22 * You should have received a copy of the GNU General Public License
23 * along with this program; if not, see <https://www.gnu.org/licenses>.
24 *
25 * SPDX-License-Identifier: GPL-3.0-only
26 */
27
28
29/*********************************************************************************************************************************
30* Header Files *
31*********************************************************************************************************************************/
32#define LOG_GROUP LOG_GROUP_SHARED_CLIPBOARD
33#include <VBox/HostServices/VBoxClipboardSvc.h>
34
35#include <iprt/assert.h>
36#include <iprt/asm.h>
37#include <iprt/process.h>
38#include <iprt/rand.h>
39#include <iprt/string.h>
40#include <iprt/thread.h>
41
42#include "VBoxSharedClipboardSvc-internal.h"
43#include "darwin-pasteboard.h"
44
45
46/*********************************************************************************************************************************
47* Structures and Typedefs *
48*********************************************************************************************************************************/
49/** Global clipboard context information */
50typedef struct SHCLCONTEXT
51{
52 /** We have a separate thread to poll for new clipboard content. */
53 RTTHREAD hThread;
54 /** Termination indicator. */
55 bool volatile fTerminate;
56 /** The reference to the current pasteboard */
57 PasteboardRef hPasteboard;
58 /** Shared clipboard client. */
59 PSHCLCLIENT pClient;
60 /** Random 64-bit number embedded into szGuestOwnershipFlavor. */
61 uint64_t idGuestOwnership;
62 /** Ownership flavor CFStringRef returned by takePasteboardOwnership().
63 * This is the same a szGuestOwnershipFlavor only in core foundation terms. */
64 void *hStrOwnershipFlavor;
65 /** The guest ownership flavor (type) string. */
66 char szGuestOwnershipFlavor[64];
67} SHCLCONTEXT;
68
69
70/*********************************************************************************************************************************
71* Global Variables *
72*********************************************************************************************************************************/
73/** Only one client is supported. There seems to be no need for more clients. */
74static SHCLCONTEXT g_ctx;
75
76
77/**
78 * Checks if something is present on the clipboard and calls shclSvcReportMsg.
79 *
80 * @returns IPRT status code (ignored).
81 * @param pCtx The context.
82 *
83 * @note Call must own lock.
84 */
85static int vboxClipboardChanged(SHCLCONTEXT *pCtx)
86{
87 if (pCtx->pClient == NULL)
88 return VINF_SUCCESS;
89
90 /* Retrieve the formats currently in the clipboard and supported by vbox */
91 uint32_t fFormats = 0;
92 bool fChanged = false;
93 int rc = queryNewPasteboardFormats(pCtx->hPasteboard, pCtx->idGuestOwnership, pCtx->hStrOwnershipFlavor,
94 &fFormats, &fChanged);
95 if ( RT_SUCCESS(rc)
96 && fChanged
97 && ShClSvcIsBackendActive())
98 rc = ShClSvcHostReportFormats(pCtx->pClient, fFormats);
99
100 LogFlowFuncLeaveRC(rc);
101 return rc;
102}
103
104/**
105 * @callback_method_impl{FNRTTHREAD, The poller thread.
106 *
107 * This thread will check for the arrival of new data on the clipboard.}
108 */
109static DECLCALLBACK(int) vboxClipboardThread(RTTHREAD ThreadSelf, void *pvUser)
110{
111 SHCLCONTEXT *pCtx = (SHCLCONTEXT *)pvUser;
112 AssertPtr(pCtx);
113 LogFlowFuncEnter();
114
115 while (!pCtx->fTerminate)
116 {
117 /* call this behind the lock because we don't know if the api is
118 thread safe and in any case we're calling several methods. */
119 ShClSvcLock();
120 vboxClipboardChanged(pCtx);
121 ShClSvcUnlock();
122
123 /* Sleep for 200 msecs before next poll */
124 RTThreadUserWait(ThreadSelf, 200);
125 }
126
127 LogFlowFuncLeaveRC(VINF_SUCCESS);
128 return VINF_SUCCESS;
129}
130
131
132int ShClBackendInit(PSHCLBACKEND pBackend, VBOXHGCMSVCFNTABLE *pTable)
133{
134 RT_NOREF(pBackend, pTable);
135 g_ctx.fTerminate = false;
136
137 int rc = initPasteboard(&g_ctx.hPasteboard);
138 AssertRCReturn(rc, rc);
139
140 rc = RTThreadCreate(&g_ctx.hThread, vboxClipboardThread, &g_ctx, 0,
141 RTTHREADTYPE_IO, RTTHREADFLAGS_WAITABLE, "SHCLIP");
142 if (RT_FAILURE(rc))
143 {
144 g_ctx.hThread = NIL_RTTHREAD;
145 destroyPasteboard(&g_ctx.hPasteboard);
146 }
147
148 return rc;
149}
150
151void ShClBackendDestroy(PSHCLBACKEND pBackend)
152{
153 RT_NOREF(pBackend);
154
155 /*
156 * Signal the termination of the polling thread and wait for it to respond.
157 */
158 ASMAtomicWriteBool(&g_ctx.fTerminate, true);
159 int rc = RTThreadUserSignal(g_ctx.hThread);
160 AssertRC(rc);
161 rc = RTThreadWait(g_ctx.hThread, RT_INDEFINITE_WAIT, NULL);
162 AssertRC(rc);
163
164 /*
165 * Destroy the hPasteboard and uninitialize the global context record.
166 */
167 destroyPasteboard(&g_ctx.hPasteboard);
168 g_ctx.hThread = NIL_RTTHREAD;
169 g_ctx.pClient = NULL;
170}
171
172int ShClBackendConnect(PSHCLBACKEND pBackend, PSHCLCLIENT pClient, bool fHeadless)
173{
174 RT_NOREF(pBackend, fHeadless);
175
176 if (g_ctx.pClient != NULL)
177 {
178 /* One client only. */
179 return VERR_NOT_SUPPORTED;
180 }
181
182 ShClSvcLock();
183
184 pClient->State.pCtx = &g_ctx;
185 pClient->State.pCtx->pClient = pClient;
186
187 ShClSvcUnlock();
188
189 return VINF_SUCCESS;
190}
191
192int ShClBackendSync(PSHCLBACKEND pBackend, PSHCLCLIENT pClient)
193{
194 RT_NOREF(pBackend);
195
196 /* Sync the host clipboard content with the client. */
197 ShClSvcLock();
198
199 int rc = vboxClipboardChanged(pClient->State.pCtx);
200
201 ShClSvcUnlock();
202
203 return rc;
204}
205
206int ShClBackendDisconnect(PSHCLBACKEND pBackend, PSHCLCLIENT pClient)
207{
208 RT_NOREF(pBackend);
209
210 ShClSvcLock();
211
212 pClient->State.pCtx->pClient = NULL;
213
214 ShClSvcUnlock();
215
216 return VINF_SUCCESS;
217}
218
219int ShClBackendReportFormats(PSHCLBACKEND pBackend, PSHCLCLIENT pClient, SHCLFORMATS fFormats)
220{
221 RT_NOREF(pBackend);
222
223 LogFlowFunc(("fFormats=%02X\n", fFormats));
224
225 /** @todo r=bird: BUGBUG: The following is probably a mistake. */
226 /** @todo r=andy: BUGBUG: Has been there since forever; needs investigation first before removing. */
227 if (fFormats == VBOX_SHCL_FMT_NONE)
228 {
229 /* This is just an automatism, not a genuine announcement */
230 return VINF_SUCCESS;
231 }
232
233#ifdef VBOX_WITH_SHARED_CLIPBOARD_TRANSFERS
234 if (fFormats & VBOX_SHCL_FMT_URI_LIST) /* No transfer support yet. */
235 return VINF_SUCCESS;
236#endif
237
238 SHCLCONTEXT *pCtx = pClient->State.pCtx;
239 ShClSvcLock();
240
241 /*
242 * Generate a unique flavor string for this format announcement.
243 */
244 uint64_t idFlavor = RTRandU64();
245 pCtx->idGuestOwnership = idFlavor;
246 RTStrPrintf(pCtx->szGuestOwnershipFlavor, sizeof(pCtx->szGuestOwnershipFlavor),
247 "org.virtualbox.sharedclipboard.%RTproc.%RX64", RTProcSelf(), idFlavor);
248
249 /*
250 * Empty the pasteboard and put our ownership indicator flavor there
251 * with the stringified formats as value.
252 */
253 char szValue[32];
254 RTStrPrintf(szValue, sizeof(szValue), "%#x", fFormats);
255
256 takePasteboardOwnership(pCtx->hPasteboard, pCtx->idGuestOwnership, pCtx->szGuestOwnershipFlavor, szValue,
257 &pCtx->hStrOwnershipFlavor);
258
259 ShClSvcUnlock();
260
261 /*
262 * Now, request the data from the guest.
263 */
264 return ShClSvcReadDataFromGuestAsync(pClient, fFormats, NULL /* ppEvent */);
265}
266
267int ShClBackendReadData(PSHCLBACKEND pBackend, PSHCLCLIENT pClient, PSHCLCLIENTCMDCTX pCmdCtx, SHCLFORMAT fFormat,
268 void *pvData, uint32_t cbData, uint32_t *pcbActual)
269{
270 AssertPtrReturn(pClient, VERR_INVALID_POINTER);
271 AssertPtrReturn(pCmdCtx, VERR_INVALID_POINTER);
272 AssertPtrReturn(pvData, VERR_INVALID_POINTER);
273 AssertPtrReturn(pcbActual, VERR_INVALID_POINTER);
274
275 RT_NOREF(pBackend, pCmdCtx);
276
277 ShClSvcLock();
278
279 /* Default to no data available. */
280 *pcbActual = 0;
281
282 int rc = readFromPasteboard(pClient->State.pCtx->hPasteboard, fFormat, pvData, cbData, pcbActual);
283 if (RT_FAILURE(rc))
284 LogRel(("Shared Clipboard: Error reading host clipboard data from macOS, rc=%Rrc\n", rc));
285
286 ShClSvcUnlock();
287
288 return rc;
289}
290
291int ShClBackendWriteData(PSHCLBACKEND pBackend, PSHCLCLIENT pClient, PSHCLCLIENTCMDCTX pCmdCtx, SHCLFORMAT fFormat, void *pvData, uint32_t cbData)
292{
293 RT_NOREF(pBackend, pCmdCtx);
294
295 LogFlowFuncEnter();
296
297 ShClSvcLock();
298
299 writeToPasteboard(pClient->State.pCtx->hPasteboard, pClient->State.pCtx->idGuestOwnership, pvData, cbData, fFormat);
300
301 ShClSvcUnlock();
302
303 LogFlowFuncLeaveRC(VINF_SUCCESS);
304 return VINF_SUCCESS;
305}
306
307#ifdef VBOX_WITH_SHARED_CLIPBOARD_TRANSFERS
308
309int ShClBackendTransferReadDir(PSHCLBACKEND pBackend, PSHCLCLIENT pClient, PSHCLDIRDATA pDirData)
310{
311 RT_NOREF(pBackend, pClient, pDirData);
312 return VERR_NOT_IMPLEMENTED;
313}
314
315int ShClBackendTransferWriteDir(PSHCLBACKEND pBackend, PSHCLCLIENT pClient, PSHCLDIRDATA pDirData)
316{
317 RT_NOREF(pBackend, pClient, pDirData);
318 return VERR_NOT_IMPLEMENTED;
319}
320
321int ShClBackendTransferReadFileHdr(PSHCLBACKEND pBackend, PSHCLCLIENT pClient, PSHCLFILEHDR pFileHdr)
322{
323 RT_NOREF(pBackend, pClient, pFileHdr);
324 return VERR_NOT_IMPLEMENTED;
325}
326
327int ShClBackendTransferWriteFileHdr(PSHCLBACKEND pBackend, PSHCLCLIENT pClient, PSHCLFILEHDR pFileHdr)
328{
329 RT_NOREF(pBackend, pClient, pFileHdr);
330 return VERR_NOT_IMPLEMENTED;
331}
332
333int ShClBackendTransferReadFileData(PSHCLBACKEND pBackend, PSHCLCLIENT pClient, PSHCLFILEDATA pFileData)
334{
335 RT_NOREF(pBackend, pClient, pFileData);
336 return VERR_NOT_IMPLEMENTED;
337}
338
339int ShClBackendTransferWriteFileData(PSHCLBACKEND pBackend, PSHCLCLIENT pClient, PSHCLFILEDATA pFileData)
340{
341 RT_NOREF(pBackend, pClient, pFileData);
342 return VERR_NOT_IMPLEMENTED;
343}
344
345#endif /* VBOX_WITH_SHARED_CLIPBOARD_TRANSFERS */
346
Note: See TracBrowser for help on using the repository browser.

© 2024 Oracle Support Privacy / Do Not Sell My Info Terms of Use Trademark Policy Automated Access Etiquette