VirtualBox

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

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

Copyright year updates by scm.

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