VirtualBox

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

Last change on this file since 82513 was 82513, checked in by vboxsync, 5 years ago

SharedClipboardSvc,Vbgl: Reviewed and adjusted the handling of the VBOX_SHCL_GUEST_FN_REPORT_FORMATS message, paddling back the parameter changes from the 6.1 dev cycle and fixing a couple of bugs introduced. Also documented the message and renamed it to a more sensible name. Dropped the new Vbgl method, renaming the old one. bugref:9437

  • Property svn:eol-style set to native
  • Property svn:keywords set to Id Revision
File size: 16.3 KB
Line 
1/** $Id: clipboard.cpp 82513 2019-12-09 13:21:55Z vboxsync $ */
2/** @file
3 * Guest Additions - X11 Shared Clipboard.
4 */
5
6/*
7 * Copyright (C) 2007-2019 Oracle Corporation
8 *
9 * This file is part of VirtualBox Open Source Edition (OSE), as
10 * available from http://www.virtualbox.org. This file is free software;
11 * you can redistribute it and/or modify it under the terms of the GNU
12 * General Public License (GPL) as published by the Free Software
13 * Foundation, in version 2 as it comes in the "COPYING" file of the
14 * VirtualBox OSE distribution. VirtualBox OSE is distributed in the
15 * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
16 */
17
18
19/*********************************************************************************************************************************
20* Header Files *
21*********************************************************************************************************************************/
22#include <iprt/alloc.h>
23#include <iprt/asm.h>
24#include <iprt/assert.h>
25#ifdef VBOX_WITH_SHARED_CLIPBOARD_FUSE
26# include <iprt/dir.h>
27#endif
28#include <iprt/initterm.h>
29#include <iprt/mem.h>
30#include <iprt/string.h>
31#include <iprt/process.h>
32#include <iprt/semaphore.h>
33
34#include <VBox/log.h>
35#include <VBox/VBoxGuestLib.h>
36#include <VBox/HostServices/VBoxClipboardSvc.h>
37#include <VBox/GuestHost/SharedClipboard.h>
38#include <VBox/GuestHost/SharedClipboard-x11.h>
39
40#include "VBoxClient.h"
41
42#ifdef VBOX_WITH_SHARED_CLIPBOARD_FUSE
43# include "clipboard-fuse.h"
44#endif
45
46
47/*********************************************************************************************************************************
48* Global Variables *
49*********************************************************************************************************************************/
50
51#ifdef VBOX_WITH_SHARED_CLIPBOARD_FUSE
52typedef struct _SHCLCTXFUSE
53{
54 RTTHREAD Thread;
55} SHCLCTXFUSE;
56#endif /* VBOX_WITH_SHARED_CLIPBOARD_FUSE */
57
58/**
59 * Global clipboard context information.
60 */
61struct SHCLCONTEXT
62{
63 /** Client command context */
64 VBGLR3SHCLCMDCTX CmdCtx;
65#ifdef VBOX_WITH_SHARED_CLIPBOARD_TRANSFERS
66 /** Associated transfer data. */
67 SHCLTRANSFERCTX TransferCtx;
68# ifdef VBOX_WITH_SHARED_CLIPBOARD_FUSE
69 SHCLCTXFUSE FUSE;
70# endif
71#endif
72 /** X11 clipboard context. */
73 SHCLX11CTX X11;
74};
75
76/** Only one client is supported. There seems to be no need for more clients. */
77static SHCLCONTEXT g_Ctx;
78
79
80/**
81 * Get clipboard data from the host.
82 *
83 * @returns VBox result code
84 * @param pCtx Our context information.
85 * @param Format The format of the data being requested.
86 * @param ppv On success and if pcb > 0, this will point to a buffer
87 * to be freed with RTMemFree containing the data read.
88 * @param pcb On success, this contains the number of bytes of data
89 * returned.
90 */
91DECLCALLBACK(int) ShClX11RequestDataForX11Callback(PSHCLCONTEXT pCtx, SHCLFORMAT Format, void **ppv, uint32_t *pcb)
92{
93 RT_NOREF(pCtx);
94
95 LogFlowFunc(("Format=0x%x\n", Format));
96
97 int rc = VINF_SUCCESS;
98
99 uint32_t cbRead = 0;
100
101#ifdef VBOX_WITH_SHARED_CLIPBOARD_TRANSFERS
102 if (Format == VBOX_SHCL_FMT_URI_LIST)
103 {
104 //rc = VbglR3ClipboardRootListRead()
105 }
106 else
107#endif
108 {
109 SHCLDATABLOCK dataBlock;
110 RT_ZERO(dataBlock);
111
112 dataBlock.uFormat = Format;
113 dataBlock.cbData = _4K;
114 dataBlock.pvData = RTMemAlloc(dataBlock.cbData);
115 if (dataBlock.pvData)
116 {
117 rc = VbglR3ClipboardReadDataEx(&pCtx->CmdCtx, &dataBlock, &cbRead);
118 }
119 else
120 rc = VERR_NO_MEMORY;
121
122 /*
123 * A return value of VINF_BUFFER_OVERFLOW tells us to try again with a
124 * larger buffer. The size of the buffer needed is placed in *pcb.
125 * So we start all over again.
126 */
127 if (rc == VINF_BUFFER_OVERFLOW)
128 {
129 /* cbRead contains the size required. */
130
131 dataBlock.cbData = cbRead;
132 dataBlock.pvData = RTMemRealloc(dataBlock.pvData, cbRead);
133 if (dataBlock.pvData)
134 {
135 rc = VbglR3ClipboardReadDataEx(&pCtx->CmdCtx, &dataBlock, &cbRead);
136 if (rc == VINF_BUFFER_OVERFLOW)
137 rc = VERR_BUFFER_OVERFLOW;
138 }
139 else
140 rc = VERR_NO_MEMORY;
141 }
142
143 if (RT_SUCCESS(rc))
144 {
145 *pcb = cbRead; /* Actual bytes read. */
146 *ppv = dataBlock.pvData;
147 }
148
149 /*
150 * Catch other errors. This also catches the case in which the buffer was
151 * too small a second time, possibly because the clipboard contents
152 * changed half-way through the operation. Since we can't say whether or
153 * not this is actually an error, we just return size 0.
154 */
155 if (RT_FAILURE(rc))
156 RTMemFree(dataBlock.pvData);
157 }
158
159 LogFlowFuncLeaveRC(rc);
160 return rc;
161}
162
163/**
164 * Opaque data structure describing a request from the host for clipboard
165 * data, passed in when the request is forwarded to the X11 backend so that
166 * it can be completed correctly.
167 */
168struct CLIPREADCBREQ
169{
170 /** The data format that was requested. */
171 SHCLFORMAT Format;
172};
173
174/**
175 * Tell the host that new clipboard formats are available.
176 *
177 * @param pCtx Our context information.
178 * @param fFormats The formats to report.
179 */
180DECLCALLBACK(void) ShClX11ReportFormatsCallback(PSHCLCONTEXT pCtx, SHCLFORMATS fFormats)
181{
182 RT_NOREF(pCtx);
183
184 LogFlowFunc(("Formats=0x%x\n", fFormats));
185
186 int rc2 = VbglR3ClipboardReportFormats(pCtx->CmdCtx.uClientID, fFormats);
187 RT_NOREF(rc2);
188 LogFlowFuncLeaveRC(rc2);
189}
190
191/**
192 * This is called by the backend to tell us that a request for data from
193 * X11 has completed.
194 *
195 * @param pCtx Our context information.
196 * @param rc The IPRT result code of the request.
197 * @param pReq The request structure that we passed in when we started
198 * the request. We RTMemFree() this in this function.
199 * @param pv The clipboard data returned from X11 if the request succeeded (see @a rc).
200 * @param cb The size of the data in @a pv.
201 */
202DECLCALLBACK(void) ShClX11RequestFromX11CompleteCallback(PSHCLCONTEXT pCtx, int rc, CLIPREADCBREQ *pReq, void *pv, uint32_t cb)
203{
204 RT_NOREF(pCtx);
205
206 LogFlowFunc(("rc=%Rrc, Format=0x%x, pv=%p, cb=%RU32\n", rc, pReq->Format, pv, cb));
207
208 SHCLDATABLOCK dataBlock;
209 RT_ZERO(dataBlock);
210
211 dataBlock.uFormat = pReq->Format;
212
213 if (RT_SUCCESS(rc))
214 {
215 dataBlock.pvData = pv;
216 dataBlock.cbData = cb;
217 }
218
219 int rc2 = VbglR3ClipboardWriteDataEx(&pCtx->CmdCtx, &dataBlock);
220 RT_NOREF(rc2);
221
222 RTMemFree(pReq);
223
224 LogFlowFuncLeaveRC(rc2);
225}
226
227/**
228 * Connect the guest clipboard to the host.
229 *
230 * @returns VBox status code.
231 */
232static int vboxClipboardConnect(void)
233{
234 LogFlowFuncEnter();
235
236 int rc = ShClX11Init(&g_Ctx.X11, &g_Ctx, false /* fHeadless */);
237 if (RT_SUCCESS(rc))
238 {
239 rc = ShClX11ThreadStart(&g_Ctx.X11, false /* grab */);
240 if (RT_SUCCESS(rc))
241 {
242 rc = VbglR3ClipboardConnectEx(&g_Ctx.CmdCtx);
243 if (RT_FAILURE(rc))
244 ShClX11ThreadStop(&g_Ctx.X11);
245 }
246 }
247 else
248 rc = VERR_NO_MEMORY;
249
250 if (RT_FAILURE(rc))
251 {
252 VBClLogError("Error connecting to host service, rc=%Rrc\n", rc);
253
254 VbglR3ClipboardDisconnectEx(&g_Ctx.CmdCtx);
255 ShClX11Destroy(&g_Ctx.X11);
256 }
257
258 LogFlowFuncLeaveRC(rc);
259 return rc;
260}
261
262/**
263 * The main loop of our clipboard reader.
264 */
265int vboxClipboardMain(void)
266{
267 LogRel(("Worker loop running\n"));
268
269 int rc;
270
271 PSHCLCONTEXT pCtx = &g_Ctx;
272
273 bool fShutdown = false;
274
275 /* The thread waits for incoming messages from the host. */
276 for (;;)
277 {
278 PVBGLR3CLIPBOARDEVENT pEvent = NULL;
279
280 LogFlowFunc(("Waiting for host message (fUseLegacyProtocol=%RTbool, fHostFeatures=%#RX64) ...\n",
281 pCtx->CmdCtx.fUseLegacyProtocol, pCtx->CmdCtx.fHostFeatures));
282
283 if (pCtx->CmdCtx.fUseLegacyProtocol)
284 {
285 uint32_t uMsg;
286 uint32_t uFormats;
287
288 rc = VbglR3ClipboardGetHostMsgOld(pCtx->CmdCtx.uClientID, &uMsg, &uFormats);
289 if (RT_FAILURE(rc))
290 {
291 if (rc == VERR_INTERRUPTED)
292 break;
293
294 LogFunc(("Error getting host message, rc=%Rrc\n", rc));
295 }
296 else
297 {
298 pEvent = (PVBGLR3CLIPBOARDEVENT)RTMemAllocZ(sizeof(VBGLR3CLIPBOARDEVENT));
299 AssertPtrBreakStmt(pEvent, rc = VERR_NO_MEMORY);
300
301 switch (uMsg)
302 {
303 case VBOX_SHCL_HOST_MSG_FORMATS_REPORT:
304 {
305 pEvent->enmType = VBGLR3CLIPBOARDEVENTTYPE_REPORT_FORMATS;
306 pEvent->u.ReportedFormats.Formats = uFormats;
307 break;
308 }
309
310 case VBOX_SHCL_HOST_MSG_READ_DATA:
311 {
312 pEvent->enmType = VBGLR3CLIPBOARDEVENTTYPE_READ_DATA;
313 pEvent->u.ReadData.uFmt = uFormats;
314 break;
315 }
316
317 case VBOX_SHCL_HOST_MSG_QUIT:
318 {
319 pEvent->enmType = VBGLR3CLIPBOARDEVENTTYPE_QUIT;
320 break;
321 }
322
323 default:
324 rc = VERR_NOT_SUPPORTED;
325 break;
326 }
327
328 if (RT_SUCCESS(rc))
329 {
330 /* Copy over our command context to the event. */
331 pEvent->cmdCtx = pCtx->CmdCtx;
332 }
333 }
334 }
335 else /* Host service has peeking for messages support. */
336 {
337 pEvent = (PVBGLR3CLIPBOARDEVENT)RTMemAllocZ(sizeof(VBGLR3CLIPBOARDEVENT));
338 AssertPtrBreakStmt(pEvent, rc = VERR_NO_MEMORY);
339
340 uint32_t uMsg = 0;
341 uint32_t cParms = 0;
342 rc = VbglR3ClipboardMsgPeekWait(&pCtx->CmdCtx, &uMsg, &cParms, NULL /* pidRestoreCheck */);
343 if (RT_SUCCESS(rc))
344 {
345#ifdef VBOX_WITH_SHARED_CLIPBOARD_TRANSFERS
346 rc = VbglR3ClipboardEventGetNextEx(uMsg, cParms, &pCtx->CmdCtx, &pCtx->TransferCtx, pEvent);
347#else
348 rc = VbglR3ClipboardEventGetNext(uMsg, cParms, &pCtx->CmdCtx, pEvent);
349#endif
350 }
351 }
352
353 if (RT_FAILURE(rc))
354 {
355 LogFlowFunc(("Getting next event failed with %Rrc\n", rc));
356
357 VbglR3ClipboardEventFree(pEvent);
358 pEvent = NULL;
359
360 if (fShutdown)
361 break;
362
363 /* Wait a bit before retrying. */
364 RTThreadSleep(1000);
365 continue;
366 }
367 else
368 {
369 AssertPtr(pEvent);
370 LogFlowFunc(("Event uType=%RU32\n", pEvent->enmType));
371
372 switch (pEvent->enmType)
373 {
374 case VBGLR3CLIPBOARDEVENTTYPE_REPORT_FORMATS:
375 {
376 ShClX11ReportFormatsToX11(&g_Ctx.X11, pEvent->u.ReportedFormats.Formats);
377 break;
378 }
379
380 case VBGLR3CLIPBOARDEVENTTYPE_READ_DATA:
381 {
382 /* The host needs data in the specified format. */
383 CLIPREADCBREQ *pReq;
384 pReq = (CLIPREADCBREQ *)RTMemAllocZ(sizeof(CLIPREADCBREQ));
385 if (pReq)
386 {
387 pReq->Format = pEvent->u.ReadData.uFmt;
388 ShClX11ReadDataFromX11(&g_Ctx.X11, pReq->Format, pReq);
389 }
390 else
391 rc = VERR_NO_MEMORY;
392 break;
393 }
394
395 case VBGLR3CLIPBOARDEVENTTYPE_QUIT:
396 {
397 LogRel2(("Host requested termination\n"));
398 fShutdown = true;
399 break;
400 }
401
402#ifdef VBOX_WITH_SHARED_CLIPBOARD_TRANSFERS
403 case VBGLR3CLIPBOARDEVENTTYPE_TRANSFER_STATUS:
404 {
405 /* Nothing to do here. */
406 rc = VINF_SUCCESS;
407 break;
408 }
409#endif
410 case VBGLR3CLIPBOARDEVENTTYPE_NONE:
411 {
412 /* Nothing to do here. */
413 rc = VINF_SUCCESS;
414 break;
415 }
416
417 default:
418 {
419 AssertMsgFailedBreakStmt(("Event type %RU32 not implemented\n", pEvent->enmType), rc = VERR_NOT_SUPPORTED);
420 }
421 }
422
423 if (pEvent)
424 {
425 VbglR3ClipboardEventFree(pEvent);
426 pEvent = NULL;
427 }
428 }
429
430 if (fShutdown)
431 break;
432 }
433
434 LogRel(("Worker loop ended\n"));
435
436 LogFlowFuncLeaveRC(rc);
437 return rc;
438}
439
440#ifdef VBOX_WITH_SHARED_CLIPBOARD_FUSE
441static DECLCALLBACK(int) vboxClipoardFUSEThread(RTTHREAD hThreadSelf, void *pvUser)
442{
443 RT_NOREF(hThreadSelf, pvUser);
444
445 VbglR3Init();
446
447 LogFlowFuncEnter();
448
449 RTThreadUserSignal(hThreadSelf);
450
451 char szExecPath[RTPATH_MAX];
452 RTProcGetExecutablePath(szExecPath, sizeof(szExecPath));
453
454 char szTempDir[RTPATH_MAX];
455 RTStrPrintf(szTempDir, sizeof(szTempDir), "VBoxSharedClipboard-XXXXXXXX");
456
457 int rc = RTDirCreateTemp(szTempDir, 0700);
458 if (RT_SUCCESS(rc))
459 {
460 char *paArgs[2];
461 paArgs[0] = szExecPath;
462 paArgs[1] = szTempDir;
463
464 rc = ShClFuseMain(2 /* argc */, paArgs);
465 }
466
467 LogFlowFuncLeaveRC(rc);
468 return rc;
469}
470
471static int vboxClipboardFUSEStart()
472{
473 LogFlowFuncEnter();
474
475 PSHCLCONTEXT pCtx = &g_Ctx;
476
477 int rc = RTThreadCreate(&pCtx->FUSE.Thread, vboxClipoardFUSEThread, &pCtx->FUSE, 0,
478 RTTHREADTYPE_IO, RTTHREADFLAGS_WAITABLE, "SHCLFUSE");
479 if (RT_SUCCESS(rc))
480 rc = RTThreadUserWait(pCtx->FUSE.Thread, 30 * 1000);
481
482 LogFlowFuncLeaveRC(rc);
483 return rc;
484}
485
486static int vboxClipboardFUSEStop()
487{
488 LogFlowFuncEnter();
489
490 PSHCLCONTEXT pCtx = &g_Ctx;
491
492 int rcThread;
493 int rc = RTThreadWait(pCtx->FUSE.Thread, 1000, &rcThread);
494
495 LogFlowFuncLeaveRC(rc);
496 return rc;
497}
498#endif /* VBOX_WITH_SHARED_CLIPBOARD_FUSE */
499
500static const char *getName()
501{
502 return "Shared Clipboard";
503}
504
505static const char *getPidFilePath()
506{
507 return ".vboxclient-clipboard.pid";
508}
509
510static int init(struct VBCLSERVICE **pSelf)
511{
512 RT_NOREF(pSelf);
513
514 int rc;
515
516#ifdef VBOX_WITH_SHARED_CLIPBOARD_TRANSFERS
517 rc = ShClTransferCtxInit(&g_Ctx.TransferCtx);
518#else
519 rc = VINF_SUCCESS;
520#endif
521
522 LogFlowFuncLeaveRC(rc);
523 return rc;
524}
525
526static int run(struct VBCLSERVICE **ppInterface, bool fDaemonised)
527{
528 RT_NOREF(ppInterface, fDaemonised);
529
530 /* Initialise the guest library. */
531 int rc = vboxClipboardConnect();
532 if (RT_SUCCESS(rc))
533 {
534#ifdef VBOX_WITH_SHARED_CLIPBOARD_FUSE
535 rc = vboxClipboardFUSEStart();
536 if (RT_SUCCESS(rc))
537 {
538#endif
539 rc = vboxClipboardMain();
540
541#ifdef VBOX_WITH_SHARED_CLIPBOARD_FUSE
542 int rc2 = vboxClipboardFUSEStop();
543 if (RT_SUCCESS(rc))
544 rc = rc2;
545 }
546#endif
547 }
548
549 if (RT_FAILURE(rc))
550 VBClLogError("Service terminated abnormally with %Rrc\n", rc);
551
552 if (rc == VERR_HGCM_SERVICE_NOT_FOUND)
553 rc = VINF_SUCCESS; /* Prevent automatic restart by daemon script if host service not available. */
554
555 return rc;
556}
557
558static void cleanup(struct VBCLSERVICE **ppInterface)
559{
560 RT_NOREF(ppInterface);
561
562#ifdef VBOX_WITH_SHARED_CLIPBOARD_TRANSFERS
563 ShClTransferCtxDestroy(&g_Ctx.TransferCtx);
564#endif
565}
566
567struct VBCLSERVICE vbclClipboardInterface =
568{
569 getName,
570 getPidFilePath,
571 init,
572 run,
573 cleanup
574};
575
576struct CLIPBOARDSERVICE
577{
578 struct VBCLSERVICE *pInterface;
579};
580
581struct VBCLSERVICE **VBClGetClipboardService(void)
582{
583 struct CLIPBOARDSERVICE *pService =
584 (struct CLIPBOARDSERVICE *)RTMemAlloc(sizeof(*pService));
585
586 if (!pService)
587 VBClLogFatalError("Out of memory\n");
588 pService->pInterface = &vbclClipboardInterface;
589 return &pService->pInterface;
590}
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