VirtualBox

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

Last change on this file since 100762 was 100655, checked in by vboxsync, 17 months ago

Shared Clipboard/VRDE: Integrated the logic of ShClSvcIsBackendActive() in ShClSvcReportFormats(), so that the specific backends don't need to be in charge for that anymore. Renamed VBOX_CLIPBOARD_EXT_FN_FORMAT_ANNOUNCE -> VBOX_CLIPBOARD_EXT_FN_FORMAT_REPORT_TO_HOST to emphasize its usage. Added VBOX_CLIPBOARD_EXT_FN_FORMAT_REPORT_TO_GUEST; see comment for details.

The whole concept of VRDE being a (one and only) service extension nowadays isn't optimal anymore and needs to be replaced with a more generic mechanism, which is out of this scope for this task. So just adapt this as needed and keep the current way.

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 100655 2023-07-19 16:06:24Z 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 rc = ShClSvcReportFormats(pCtx->pClient, fFormats);
98
99 LogFlowFuncLeaveRC(rc);
100 return rc;
101}
102
103/**
104 * @callback_method_impl{FNRTTHREAD, The poller thread.
105 *
106 * This thread will check for the arrival of new data on the clipboard.}
107 */
108static DECLCALLBACK(int) vboxClipboardThread(RTTHREAD ThreadSelf, void *pvUser)
109{
110 SHCLCONTEXT *pCtx = (SHCLCONTEXT *)pvUser;
111 AssertPtr(pCtx);
112 LogFlowFuncEnter();
113
114 while (!pCtx->fTerminate)
115 {
116 /* call this behind the lock because we don't know if the api is
117 thread safe and in any case we're calling several methods. */
118 ShClSvcLock();
119 vboxClipboardChanged(pCtx);
120 ShClSvcUnlock();
121
122 /* Sleep for 200 msecs before next poll */
123 RTThreadUserWait(ThreadSelf, 200);
124 }
125
126 LogFlowFuncLeaveRC(VINF_SUCCESS);
127 return VINF_SUCCESS;
128}
129
130
131int ShClBackendInit(PSHCLBACKEND pBackend, VBOXHGCMSVCFNTABLE *pTable)
132{
133 RT_NOREF(pBackend, pTable);
134 g_ctx.fTerminate = false;
135
136 int rc = initPasteboard(&g_ctx.hPasteboard);
137 AssertRCReturn(rc, rc);
138
139 rc = RTThreadCreate(&g_ctx.hThread, vboxClipboardThread, &g_ctx, 0,
140 RTTHREADTYPE_IO, RTTHREADFLAGS_WAITABLE, "SHCLIP");
141 if (RT_FAILURE(rc))
142 {
143 g_ctx.hThread = NIL_RTTHREAD;
144 destroyPasteboard(&g_ctx.hPasteboard);
145 }
146
147 return rc;
148}
149
150void ShClBackendDestroy(PSHCLBACKEND pBackend)
151{
152 RT_NOREF(pBackend);
153
154 /*
155 * Signal the termination of the polling thread and wait for it to respond.
156 */
157 ASMAtomicWriteBool(&g_ctx.fTerminate, true);
158 int rc = RTThreadUserSignal(g_ctx.hThread);
159 AssertRC(rc);
160 rc = RTThreadWait(g_ctx.hThread, RT_INDEFINITE_WAIT, NULL);
161 AssertRC(rc);
162
163 /*
164 * Destroy the hPasteboard and uninitialize the global context record.
165 */
166 destroyPasteboard(&g_ctx.hPasteboard);
167 g_ctx.hThread = NIL_RTTHREAD;
168 g_ctx.pClient = NULL;
169}
170
171int ShClBackendConnect(PSHCLBACKEND pBackend, PSHCLCLIENT pClient, bool fHeadless)
172{
173 RT_NOREF(pBackend, fHeadless);
174
175 if (g_ctx.pClient != NULL)
176 {
177 /* One client only. */
178 return VERR_NOT_SUPPORTED;
179 }
180
181 ShClSvcLock();
182
183 pClient->State.pCtx = &g_ctx;
184 pClient->State.pCtx->pClient = pClient;
185
186 ShClSvcUnlock();
187
188 return VINF_SUCCESS;
189}
190
191int ShClBackendSync(PSHCLBACKEND pBackend, PSHCLCLIENT pClient)
192{
193 RT_NOREF(pBackend);
194
195 /* Sync the host clipboard content with the client. */
196 ShClSvcLock();
197
198 int rc = vboxClipboardChanged(pClient->State.pCtx);
199
200 ShClSvcUnlock();
201
202 return rc;
203}
204
205int ShClBackendDisconnect(PSHCLBACKEND pBackend, PSHCLCLIENT pClient)
206{
207 RT_NOREF(pBackend);
208
209 ShClSvcLock();
210
211 pClient->State.pCtx->pClient = NULL;
212
213 ShClSvcUnlock();
214
215 return VINF_SUCCESS;
216}
217
218int ShClBackendReportFormats(PSHCLBACKEND pBackend, PSHCLCLIENT pClient, SHCLFORMATS fFormats)
219{
220 RT_NOREF(pBackend);
221
222 LogFlowFunc(("fFormats=%02X\n", fFormats));
223
224 /** @todo r=bird: BUGBUG: The following is probably a mistake. */
225 /** @todo r=andy: BUGBUG: Has been there since forever; needs investigation first before removing. */
226 if (fFormats == VBOX_SHCL_FMT_NONE)
227 {
228 /* This is just an automatism, not a genuine announcement */
229 return VINF_SUCCESS;
230 }
231
232#ifdef VBOX_WITH_SHARED_CLIPBOARD_TRANSFERS
233 if (fFormats & VBOX_SHCL_FMT_URI_LIST) /* No transfer support yet. */
234 return VINF_SUCCESS;
235#endif
236
237 SHCLCONTEXT *pCtx = pClient->State.pCtx;
238 ShClSvcLock();
239
240 /*
241 * Generate a unique flavor string for this format announcement.
242 */
243 uint64_t idFlavor = RTRandU64();
244 pCtx->idGuestOwnership = idFlavor;
245 RTStrPrintf(pCtx->szGuestOwnershipFlavor, sizeof(pCtx->szGuestOwnershipFlavor),
246 "org.virtualbox.sharedclipboard.%RTproc.%RX64", RTProcSelf(), idFlavor);
247
248 /*
249 * Empty the pasteboard and put our ownership indicator flavor there
250 * with the stringified formats as value.
251 */
252 char szValue[32];
253 RTStrPrintf(szValue, sizeof(szValue), "%#x", fFormats);
254
255 takePasteboardOwnership(pCtx->hPasteboard, pCtx->idGuestOwnership, pCtx->szGuestOwnershipFlavor, szValue,
256 &pCtx->hStrOwnershipFlavor);
257
258 ShClSvcUnlock();
259
260 /*
261 * Now, request the data from the guest.
262 */
263 return ShClSvcReadDataFromGuestAsync(pClient, fFormats, NULL /* ppEvent */);
264}
265
266int ShClBackendReadData(PSHCLBACKEND pBackend, PSHCLCLIENT pClient, PSHCLCLIENTCMDCTX pCmdCtx, SHCLFORMAT fFormat,
267 void *pvData, uint32_t cbData, uint32_t *pcbActual)
268{
269 AssertPtrReturn(pClient, VERR_INVALID_POINTER);
270 AssertPtrReturn(pCmdCtx, VERR_INVALID_POINTER);
271 AssertPtrReturn(pvData, VERR_INVALID_POINTER);
272 AssertPtrReturn(pcbActual, VERR_INVALID_POINTER);
273
274 RT_NOREF(pBackend, pCmdCtx);
275
276 ShClSvcLock();
277
278 /* Default to no data available. */
279 *pcbActual = 0;
280
281 int rc = readFromPasteboard(pClient->State.pCtx->hPasteboard, fFormat, pvData, cbData, pcbActual);
282 if (RT_FAILURE(rc))
283 LogRel(("Shared Clipboard: Error reading host clipboard data from macOS, rc=%Rrc\n", rc));
284
285 ShClSvcUnlock();
286
287 return rc;
288}
289
290int ShClBackendWriteData(PSHCLBACKEND pBackend, PSHCLCLIENT pClient, PSHCLCLIENTCMDCTX pCmdCtx, SHCLFORMAT fFormat, void *pvData, uint32_t cbData)
291{
292 RT_NOREF(pBackend, pCmdCtx);
293
294 LogFlowFuncEnter();
295
296 ShClSvcLock();
297
298 writeToPasteboard(pClient->State.pCtx->hPasteboard, pClient->State.pCtx->idGuestOwnership, pvData, cbData, fFormat);
299
300 ShClSvcUnlock();
301
302 LogFlowFuncLeaveRC(VINF_SUCCESS);
303 return VINF_SUCCESS;
304}
305
306#ifdef VBOX_WITH_SHARED_CLIPBOARD_TRANSFERS
307
308int ShClBackendTransferReadDir(PSHCLBACKEND pBackend, PSHCLCLIENT pClient, PSHCLDIRDATA pDirData)
309{
310 RT_NOREF(pBackend, pClient, pDirData);
311 return VERR_NOT_IMPLEMENTED;
312}
313
314int ShClBackendTransferWriteDir(PSHCLBACKEND pBackend, PSHCLCLIENT pClient, PSHCLDIRDATA pDirData)
315{
316 RT_NOREF(pBackend, pClient, pDirData);
317 return VERR_NOT_IMPLEMENTED;
318}
319
320int ShClBackendTransferReadFileHdr(PSHCLBACKEND pBackend, PSHCLCLIENT pClient, PSHCLFILEHDR pFileHdr)
321{
322 RT_NOREF(pBackend, pClient, pFileHdr);
323 return VERR_NOT_IMPLEMENTED;
324}
325
326int ShClBackendTransferWriteFileHdr(PSHCLBACKEND pBackend, PSHCLCLIENT pClient, PSHCLFILEHDR pFileHdr)
327{
328 RT_NOREF(pBackend, pClient, pFileHdr);
329 return VERR_NOT_IMPLEMENTED;
330}
331
332int ShClBackendTransferReadFileData(PSHCLBACKEND pBackend, PSHCLCLIENT pClient, PSHCLFILEDATA pFileData)
333{
334 RT_NOREF(pBackend, pClient, pFileData);
335 return VERR_NOT_IMPLEMENTED;
336}
337
338int ShClBackendTransferWriteFileData(PSHCLBACKEND pBackend, PSHCLCLIENT pClient, PSHCLFILEDATA pFileData)
339{
340 RT_NOREF(pBackend, pClient, pFileData);
341 return VERR_NOT_IMPLEMENTED;
342}
343
344#endif /* VBOX_WITH_SHARED_CLIPBOARD_TRANSFERS */
345
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