VirtualBox

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

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

Shared Clipboard/Transfers: Update.

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