VirtualBox

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

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

Shared Clipboard/VBoxClient: More cleanup for X11-specific code.

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