VirtualBox

source: vbox/trunk/src/VBox/Additions/x11/VBoxClient/clipboard.cpp@ 97728

Last change on this file since 97728 was 96407, checked in by vboxsync, 2 years ago

scm copyright and license note update

  • Property svn:eol-style set to native
  • Property svn:keywords set to Id Revision
File size: 12.8 KB
Line 
1/** $Id: clipboard.cpp 96407 2022-08-22 17:43:14Z vboxsync $ */
2/** @file
3 * Guest Additions - X11 Shared Clipboard.
4 */
5
6/*
7 * Copyright (C) 2007-2022 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#include <iprt/alloc.h>
33#include <iprt/asm.h>
34#include <iprt/assert.h>
35#ifdef VBOX_WITH_SHARED_CLIPBOARD_FUSE
36# include <iprt/dir.h>
37#endif
38#include <iprt/initterm.h>
39#include <iprt/mem.h>
40#include <iprt/string.h>
41#include <iprt/path.h>
42#include <iprt/process.h>
43#include <iprt/semaphore.h>
44
45#include <VBox/log.h>
46#include <VBox/VBoxGuestLib.h>
47#include <VBox/HostServices/VBoxClipboardSvc.h>
48#include <VBox/GuestHost/SharedClipboard.h>
49#include <VBox/GuestHost/SharedClipboard-x11.h>
50
51#include "VBoxClient.h"
52
53#include "clipboard.h"
54#ifdef VBOX_WITH_SHARED_CLIPBOARD_FUSE
55# include "clipboard-fuse.h"
56#endif
57
58
59/*********************************************************************************************************************************
60* Global Variables *
61*********************************************************************************************************************************/
62
63/** Only one context is supported at a time for now. */
64SHCLCONTEXT g_Ctx;
65#ifdef VBOX_WITH_SHARED_CLIPBOARD_FUSE
66SHCLFUSECTX g_FuseCtx;
67#endif
68
69
70static DECLCALLBACK(int) vbclOnRequestDataFromSourceCallback(PSHCLCONTEXT pCtx,
71 SHCLFORMAT uFmt, void **ppv, uint32_t *pcb, void *pvUser)
72{
73 RT_NOREF(pvUser);
74
75 LogFlowFunc(("pCtx=%p, uFmt=%#x\n", pCtx, uFmt));
76
77 int rc = VINF_SUCCESS;
78
79#ifdef VBOX_WITH_SHARED_CLIPBOARD_TRANSFERS
80 if (uFmt == VBOX_SHCL_FMT_URI_LIST)
81 {
82 //rc = VbglR3ClipboardRootListRead()
83 rc = VERR_NO_DATA;
84 }
85 else
86#endif
87 {
88 uint32_t cbRead = 0;
89
90 uint32_t cbData = _4K; /** @todo Make this dynamic. */
91 void *pvData = RTMemAlloc(cbData);
92 if (pvData)
93 {
94 rc = VbglR3ClipboardReadDataEx(&pCtx->CmdCtx, uFmt, pvData, cbData, &cbRead);
95 }
96 else
97 rc = VERR_NO_MEMORY;
98
99 /*
100 * A return value of VINF_BUFFER_OVERFLOW tells us to try again with a
101 * larger buffer. The size of the buffer needed is placed in *pcb.
102 * So we start all over again.
103 */
104 if (rc == VINF_BUFFER_OVERFLOW)
105 {
106 /* cbRead contains the size required. */
107
108 cbData = cbRead;
109 pvData = RTMemRealloc(pvData, cbRead);
110 if (pvData)
111 {
112 rc = VbglR3ClipboardReadDataEx(&pCtx->CmdCtx, uFmt, pvData, cbData, &cbRead);
113 if (rc == VINF_BUFFER_OVERFLOW)
114 rc = VERR_BUFFER_OVERFLOW;
115 }
116 else
117 rc = VERR_NO_MEMORY;
118 }
119
120 if (!cbRead)
121 rc = VERR_NO_DATA;
122
123 if (RT_SUCCESS(rc))
124 {
125 *pcb = cbRead; /* Actual bytes read. */
126 *ppv = pvData;
127 }
128 else
129 {
130 /*
131 * Catch other errors. This also catches the case in which the buffer was
132 * too small a second time, possibly because the clipboard contents
133 * changed half-way through the operation. Since we can't say whether or
134 * not this is actually an error, we just return size 0.
135 */
136 RTMemFree(pvData);
137 }
138 }
139
140 if (RT_FAILURE(rc))
141 LogRel(("Requesting data in format %#x from host failed with %Rrc\n", uFmt, rc));
142
143 LogFlowFuncLeaveRC(rc);
144 return rc;
145}
146
147/**
148 * Opaque data structure describing a request from the host for clipboard
149 * data, passed in when the request is forwarded to the X11 backend so that
150 * it can be completed correctly.
151 */
152struct CLIPREADCBREQ
153{
154 /** The data format that was requested. */
155 SHCLFORMAT uFmt;
156};
157
158static DECLCALLBACK(int) vbclReportFormatsCallback(PSHCLCONTEXT pCtx, uint32_t fFormats, void *pvUser)
159{
160 RT_NOREF(pvUser);
161
162 LogFlowFunc(("fFormats=%#x\n", fFormats));
163
164 int rc = VbglR3ClipboardReportFormats(pCtx->CmdCtx.idClient, fFormats);
165 LogFlowFuncLeaveRC(rc);
166
167 return rc;
168}
169
170static DECLCALLBACK(int) vbclOnSendDataToDestCallback(PSHCLCONTEXT pCtx, void *pv, uint32_t cb, void *pvUser)
171{
172 PSHCLX11READDATAREQ pData = (PSHCLX11READDATAREQ)pvUser;
173 AssertPtrReturn(pData, VERR_INVALID_POINTER);
174
175 LogFlowFunc(("rcCompletion=%Rrc, Format=0x%x, pv=%p, cb=%RU32\n", pData->rcCompletion, pData->pReq->uFmt, pv, cb));
176
177 Assert((cb == 0 && pv == NULL) || (cb != 0 && pv != NULL));
178 pData->rcCompletion = VbglR3ClipboardWriteDataEx(&pCtx->CmdCtx, pData->pReq->uFmt, pv, cb);
179
180 RTMemFree(pData->pReq);
181
182 LogFlowFuncLeaveRC(pData->rcCompletion);
183
184 return VINF_SUCCESS;
185}
186
187/**
188 * Connect the guest clipboard to the host.
189 *
190 * @returns VBox status code.
191 */
192static int vboxClipboardConnect(void)
193{
194 LogFlowFuncEnter();
195
196 SHCLCALLBACKS Callbacks;
197 RT_ZERO(Callbacks);
198 Callbacks.pfnReportFormats = vbclReportFormatsCallback;
199 Callbacks.pfnOnRequestDataFromSource = vbclOnRequestDataFromSourceCallback;
200 Callbacks.pfnOnSendDataToDest = vbclOnSendDataToDestCallback;
201
202 int rc = ShClX11Init(&g_Ctx.X11, &Callbacks, &g_Ctx, false /* fHeadless */);
203 if (RT_SUCCESS(rc))
204 {
205 rc = ShClX11ThreadStart(&g_Ctx.X11, false /* grab */);
206 if (RT_SUCCESS(rc))
207 {
208 rc = VbglR3ClipboardConnectEx(&g_Ctx.CmdCtx, VBOX_SHCL_GF_0_CONTEXT_ID);
209 if (RT_FAILURE(rc))
210 ShClX11ThreadStop(&g_Ctx.X11);
211 }
212 }
213 else
214 rc = VERR_NO_MEMORY;
215
216 if (RT_FAILURE(rc))
217 {
218 VBClLogError("Error connecting to host service, rc=%Rrc\n", rc);
219
220 VbglR3ClipboardDisconnectEx(&g_Ctx.CmdCtx);
221 ShClX11Destroy(&g_Ctx.X11);
222 }
223
224 LogFlowFuncLeaveRC(rc);
225 return rc;
226}
227
228/**
229 * The main loop of our clipboard reader.
230 */
231int vboxClipboardMain(void)
232{
233 int rc;
234
235 PSHCLCONTEXT pCtx = &g_Ctx;
236
237 bool fShutdown = false;
238
239 /* The thread waits for incoming messages from the host. */
240 for (;;)
241 {
242 PVBGLR3CLIPBOARDEVENT pEvent = (PVBGLR3CLIPBOARDEVENT)RTMemAllocZ(sizeof(VBGLR3CLIPBOARDEVENT));
243 AssertPtrBreakStmt(pEvent, rc = VERR_NO_MEMORY);
244
245 LogFlowFunc(("Waiting for host message (fUseLegacyProtocol=%RTbool, fHostFeatures=%#RX64) ...\n",
246 pCtx->CmdCtx.fUseLegacyProtocol, pCtx->CmdCtx.fHostFeatures));
247
248 uint32_t idMsg = 0;
249 uint32_t cParms = 0;
250 rc = VbglR3ClipboardMsgPeekWait(&pCtx->CmdCtx, &idMsg, &cParms, NULL /* pidRestoreCheck */);
251 if (RT_SUCCESS(rc))
252 {
253#ifdef VBOX_WITH_SHARED_CLIPBOARD_TRANSFERS
254 rc = VbglR3ClipboardEventGetNextEx(idMsg, cParms, &pCtx->CmdCtx, &pCtx->TransferCtx, pEvent);
255#else
256 rc = VbglR3ClipboardEventGetNext(idMsg, cParms, &pCtx->CmdCtx, pEvent);
257#endif
258 }
259
260 if (RT_FAILURE(rc))
261 {
262 LogFlowFunc(("Getting next event failed with %Rrc\n", rc));
263
264 VbglR3ClipboardEventFree(pEvent);
265 pEvent = NULL;
266
267 if (fShutdown)
268 break;
269
270 /* Wait a bit before retrying. */
271 RTThreadSleep(1000);
272 continue;
273 }
274 else
275 {
276 AssertPtr(pEvent);
277 LogFlowFunc(("Event uType=%RU32\n", pEvent->enmType));
278
279 switch (pEvent->enmType)
280 {
281 case VBGLR3CLIPBOARDEVENTTYPE_REPORT_FORMATS:
282 {
283 ShClX11ReportFormatsToX11(&g_Ctx.X11, pEvent->u.fReportedFormats);
284 break;
285 }
286
287 case VBGLR3CLIPBOARDEVENTTYPE_READ_DATA:
288 {
289 /* The host needs data in the specified format. */
290 CLIPREADCBREQ *pReq;
291 pReq = (CLIPREADCBREQ *)RTMemAllocZ(sizeof(CLIPREADCBREQ));
292 if (pReq)
293 {
294 pReq->uFmt = pEvent->u.fReadData;
295 ShClX11ReadDataFromX11(&g_Ctx.X11, pReq->uFmt, pReq);
296 }
297 else
298 rc = VERR_NO_MEMORY;
299 break;
300 }
301
302 case VBGLR3CLIPBOARDEVENTTYPE_QUIT:
303 {
304 VBClLogVerbose(2, "Host requested termination\n");
305 fShutdown = true;
306 break;
307 }
308
309#ifdef VBOX_WITH_SHARED_CLIPBOARD_TRANSFERS
310 case VBGLR3CLIPBOARDEVENTTYPE_TRANSFER_STATUS:
311 {
312 /* Nothing to do here. */
313 rc = VINF_SUCCESS;
314 break;
315 }
316#endif
317 case VBGLR3CLIPBOARDEVENTTYPE_NONE:
318 {
319 /* Nothing to do here. */
320 rc = VINF_SUCCESS;
321 break;
322 }
323
324 default:
325 {
326 AssertMsgFailedBreakStmt(("Event type %RU32 not implemented\n", pEvent->enmType), rc = VERR_NOT_SUPPORTED);
327 }
328 }
329
330 if (pEvent)
331 {
332 VbglR3ClipboardEventFree(pEvent);
333 pEvent = NULL;
334 }
335 }
336
337 if (fShutdown)
338 break;
339 }
340
341 LogFlowFuncLeaveRC(rc);
342 return rc;
343}
344
345/**
346 * @interface_method_impl{VBCLSERVICE,pfnInit}
347 */
348static DECLCALLBACK(int) vbclShClInit(void)
349{
350 int rc;
351
352#ifdef VBOX_WITH_SHARED_CLIPBOARD_TRANSFERS
353 rc = ShClTransferCtxInit(&g_Ctx.TransferCtx);
354#else
355 rc = VINF_SUCCESS;
356#endif
357
358 LogFlowFuncLeaveRC(rc);
359 return rc;
360}
361
362/**
363 * @interface_method_impl{VBCLSERVICE,pfnWorker}
364 */
365static DECLCALLBACK(int) vbclShClWorker(bool volatile *pfShutdown)
366{
367 RT_NOREF(pfShutdown);
368
369 /* Initialise the guest library. */
370 int rc = vboxClipboardConnect();
371 if (RT_SUCCESS(rc))
372 {
373#ifdef VBOX_WITH_SHARED_CLIPBOARD_FUSE
374 rc = VbClShClFUSEInit(&g_FuseCtx, &g_Ctx);
375 if (RT_SUCCESS(rc))
376 {
377 rc = VbClShClFUSEStart(&g_FuseCtx);
378 if (RT_SUCCESS(rc))
379 {
380#endif
381 /* Let the main thread know that it can continue spawning services. */
382 RTThreadUserSignal(RTThreadSelf());
383
384 rc = vboxClipboardMain();
385
386#ifdef VBOX_WITH_SHARED_CLIPBOARD_FUSE
387 int rc2 = VbClShClFUSEStop(&g_FuseCtx);
388 if (RT_SUCCESS(rc))
389 rc = rc2;
390 }
391 }
392#endif
393 }
394
395 if (RT_FAILURE(rc))
396 VBClLogError("Service terminated abnormally with %Rrc\n", rc);
397
398 if (rc == VERR_HGCM_SERVICE_NOT_FOUND)
399 rc = VINF_SUCCESS; /* Prevent automatic restart by daemon script if host service not available. */
400
401 return rc;
402}
403
404/**
405 * @interface_method_impl{VBCLSERVICE,pfnStop}
406 */
407static DECLCALLBACK(void) vbclShClStop(void)
408{
409 /* Disconnect from the host service.
410 * This will also send a VBOX_SHCL_HOST_MSG_QUIT from the host so that we can break out from our message worker. */
411 VbglR3ClipboardDisconnect(g_Ctx.CmdCtx.idClient);
412 g_Ctx.CmdCtx.idClient = 0;
413}
414
415/**
416 * @interface_method_impl{VBCLSERVICE,pfnTerm}
417 */
418static DECLCALLBACK(int) vbclShClTerm(void)
419{
420#ifdef VBOX_WITH_SHARED_CLIPBOARD_TRANSFERS
421 ShClTransferCtxDestroy(&g_Ctx.TransferCtx);
422#endif
423
424 return VINF_SUCCESS;
425}
426
427VBCLSERVICE g_SvcClipboard =
428{
429 "shcl", /* szName */
430 "Shared Clipboard", /* pszDescription */
431 ".vboxclient-clipboard.pid", /* pszPidFilePath */
432 NULL, /* pszUsage */
433 NULL, /* pszOptions */
434 NULL, /* pfnOption */
435 vbclShClInit, /* pfnInit */
436 vbclShClWorker, /* pfnWorker */
437 vbclShClStop, /* pfnStop*/
438 vbclShClTerm /* pfnTerm */
439};
440
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