VirtualBox

source: vbox/trunk/src/VBox/HostServices/SharedClipboard/x11-clipboard.cpp@ 52776

Last change on this file since 52776 was 44528, checked in by vboxsync, 12 years ago

header (C) fixes

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 19.7 KB
Line 
1/** @file
2 *
3 * Shared Clipboard:
4 * Linux host.
5 */
6
7/*
8 * Copyright (C) 2006-2011 Oracle Corporation
9 *
10 * This file is part of VirtualBox Open Source Edition (OSE), as
11 * available from http://www.virtualbox.org. This file is free software;
12 * you can redistribute it and/or modify it under the terms of the GNU
13 * General Public License (GPL) as published by the Free Software
14 * Foundation, in version 2 as it comes in the "COPYING" file of the
15 * VirtualBox OSE distribution. VirtualBox OSE is distributed in the
16 * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
17 */
18
19#define LOG_GROUP LOG_GROUP_SHARED_CLIPBOARD
20
21#include <string.h>
22
23#include <iprt/assert.h>
24#include <iprt/critsect.h>
25#include <iprt/env.h>
26#include <iprt/mem.h>
27#include <iprt/semaphore.h>
28
29#include <VBox/GuestHost/SharedClipboard.h>
30#include <VBox/HostServices/VBoxClipboardSvc.h>
31
32#include "VBoxClipboard.h"
33
34struct _VBOXCLIPBOARDREQFROMVBOX;
35typedef struct _VBOXCLIPBOARDREQFROMVBOX VBOXCLIPBOARDREQFROMVBOX;
36
37/** Global context information used by the host glue for the X11 clipboard
38 * backend */
39struct _VBOXCLIPBOARDCONTEXT
40{
41 /** This mutex is grabbed during any critical operations on the clipboard
42 * which might clash with others. */
43 RTCRITSECT clipboardMutex;
44 /** The currently pending request for data from VBox. NULL if there is
45 * no request pending. The protocol for completing a request is to grab
46 * the critical section, check that @a pReq is not NULL, fill in the data
47 * fields and set @a pReq to NULL. The protocol for cancelling a pending
48 * request is to grab the critical section and set pReq to NULL.
49 * It is an error if a request arrives while another one is pending, and
50 * the backend is responsible for ensuring that this does not happen. */
51 VBOXCLIPBOARDREQFROMVBOX *pReq;
52
53 /** Pointer to the opaque X11 backend structure */
54 CLIPBACKEND *pBackend;
55 /** Pointer to the VBox host client data structure. */
56 VBOXCLIPBOARDCLIENTDATA *pClient;
57 /** We set this when we start shutting down as a hint not to post any new
58 * requests. */
59 bool fShuttingDown;
60};
61
62/**
63 * Report formats available in the X11 clipboard to VBox.
64 * @param pCtx Opaque context pointer for the glue code
65 * @param u32Formats The formats available
66 * @note Host glue code
67 */
68void ClipReportX11Formats(VBOXCLIPBOARDCONTEXT *pCtx,
69 uint32_t u32Formats)
70{
71 LogRelFlowFunc(("called. pCtx=%p, u32Formats=%02X\n", pCtx, u32Formats));
72 vboxSvcClipboardReportMsg(pCtx->pClient,
73 VBOX_SHARED_CLIPBOARD_HOST_MSG_FORMATS,
74 u32Formats);
75}
76
77/**
78 * Initialise the host side of the shared clipboard.
79 * @note Host glue code
80 */
81int vboxClipboardInit (void)
82{
83 return VINF_SUCCESS;
84}
85
86/**
87 * Terminate the host side of the shared clipboard.
88 * @note host glue code
89 */
90void vboxClipboardDestroy (void)
91{
92
93}
94
95/**
96 * Connect a guest to the shared clipboard.
97 * @note host glue code
98 * @note on the host, we assume that some other application already owns
99 * the clipboard and leave ownership to X11.
100 */
101int vboxClipboardConnect (VBOXCLIPBOARDCLIENTDATA *pClient, bool fHeadless)
102{
103 int rc = VINF_SUCCESS;
104 CLIPBACKEND *pBackend = NULL;
105
106 LogRel(("Starting host clipboard service\n"));
107 VBOXCLIPBOARDCONTEXT *pCtx =
108 (VBOXCLIPBOARDCONTEXT *) RTMemAllocZ(sizeof(VBOXCLIPBOARDCONTEXT));
109 if (!pCtx)
110 rc = VERR_NO_MEMORY;
111 else
112 {
113 RTCritSectInit(&pCtx->clipboardMutex);
114 pBackend = ClipConstructX11(pCtx, fHeadless);
115 if (pBackend == NULL)
116 rc = VERR_NO_MEMORY;
117 else
118 {
119 pCtx->pBackend = pBackend;
120 pClient->pCtx = pCtx;
121 pCtx->pClient = pClient;
122 rc = ClipStartX11(pBackend, true /* grab shared clipboard */);
123 }
124 if (RT_FAILURE(rc))
125 RTCritSectDelete(&pCtx->clipboardMutex);
126 }
127 if (RT_FAILURE(rc))
128 {
129 RTMemFree(pCtx);
130 LogRel(("Failed to initialise the shared clipboard\n"));
131 }
132 LogRelFlowFunc(("returning %Rrc\n", rc));
133 return rc;
134}
135
136/**
137 * Synchronise the contents of the host clipboard with the guest, called
138 * after a save and restore of the guest.
139 * @note Host glue code
140 */
141int vboxClipboardSync (VBOXCLIPBOARDCLIENTDATA *pClient)
142{
143 /* Tell the guest we have no data in case X11 is not available. If
144 * there is data in the host clipboard it will automatically be sent to
145 * the guest when the clipboard starts up. */
146 vboxSvcClipboardReportMsg (pClient,
147 VBOX_SHARED_CLIPBOARD_HOST_MSG_FORMATS, 0);
148 return VINF_SUCCESS;
149}
150
151/**
152 * Shut down the shared clipboard service and "disconnect" the guest.
153 * @note Host glue code
154 */
155void vboxClipboardDisconnect (VBOXCLIPBOARDCLIENTDATA *pClient)
156{
157 LogRelFlow(("vboxClipboardDisconnect\n"));
158
159 LogRel(("Stopping the host clipboard service\n"));
160 VBOXCLIPBOARDCONTEXT *pCtx = pClient->pCtx;
161 /* Drop the reference to the client, in case it is still there. This
162 * will cause any outstanding clipboard data requests from X11 to fail
163 * immediately. */
164 pCtx->fShuttingDown = true;
165 /* If there is a currently pending request, release it immediately. */
166 vboxClipboardWriteData(pClient, NULL, 0, 0);
167 int rc = ClipStopX11(pCtx->pBackend);
168 /** @todo handle this slightly more reasonably, or be really sure
169 * it won't go wrong. */
170 AssertRC(rc);
171 if (RT_SUCCESS(rc)) /* And if not? */
172 {
173 ClipDestructX11(pCtx->pBackend);
174 RTCritSectDelete(&pCtx->clipboardMutex);
175 RTMemFree(pCtx);
176 }
177}
178
179/**
180 * VBox is taking possession of the shared clipboard.
181 *
182 * @param pClient Context data for the guest system
183 * @param u32Formats Clipboard formats the guest is offering
184 * @note Host glue code
185 */
186void vboxClipboardFormatAnnounce (VBOXCLIPBOARDCLIENTDATA *pClient,
187 uint32_t u32Formats)
188{
189 LogRelFlowFunc(("called. pClient=%p, u32Formats=%02X\n", pClient,
190 u32Formats));
191 ClipAnnounceFormatToX11 (pClient->pCtx->pBackend, u32Formats);
192}
193
194/** Structure describing a request for clipoard data from the guest. */
195struct _CLIPREADCBREQ
196{
197 /** Where to write the returned data to. */
198 void *pv;
199 /** The size of the buffer in pv */
200 uint32_t cb;
201 /** The actual size of the data written */
202 uint32_t *pcbActual;
203};
204
205/**
206 * Called when VBox wants to read the X11 clipboard.
207 *
208 * @returns VINF_SUCCESS on successful completion
209 * @returns VINF_HGCM_ASYNC_EXECUTE if the operation will complete
210 * asynchronously
211 * @returns iprt status code on failure
212 * @param pClient Context information about the guest VM
213 * @param u32Format The format that the guest would like to receive the data in
214 * @param pv Where to write the data to
215 * @param cb The size of the buffer to write the data to
216 * @param pcbActual Where to write the actual size of the written data
217 * @note We always fail or complete asynchronously
218 * @note On success allocates a CLIPREADCBREQ structure which must be
219 * freed in ClipCompleteDataRequestFromX11 when it is called back from
220 * the backend code.
221 *
222 */
223int vboxClipboardReadData (VBOXCLIPBOARDCLIENTDATA *pClient,
224 uint32_t u32Format, void *pv, uint32_t cb,
225 uint32_t *pcbActual)
226{
227 LogRelFlowFunc(("pClient=%p, u32Format=%02X, pv=%p, cb=%u, pcbActual=%p",
228 pClient, u32Format, pv, cb, pcbActual));
229
230 int rc = VINF_SUCCESS;
231 CLIPREADCBREQ *pReq = (CLIPREADCBREQ *) RTMemAlloc(sizeof(CLIPREADCBREQ));
232 if (!pReq)
233 rc = VERR_NO_MEMORY;
234 else
235 {
236 pReq->pv = pv;
237 pReq->cb = cb;
238 pReq->pcbActual = pcbActual;
239 rc = ClipRequestDataFromX11(pClient->pCtx->pBackend, u32Format, pReq);
240 if (RT_SUCCESS(rc))
241 rc = VINF_HGCM_ASYNC_EXECUTE;
242 }
243 LogRelFlowFunc(("returning %Rrc\n", rc));
244 return rc;
245}
246
247/**
248 * Complete a request from VBox for the X11 clipboard data. The data should
249 * be written to the buffer provided in the initial request.
250 * @param pCtx request context information
251 * @param rc the completion status of the request
252 * @param cbActual on successful completion, the number of bytes of data
253 * actually written, on buffer overflow the size of the
254 * buffer needed, ignored otherwise
255 * @todo change this to deal with the buffer issues rather than offloading
256 * them onto the caller
257 */
258void ClipCompleteDataRequestFromX11(VBOXCLIPBOARDCONTEXT *pCtx, int rc,
259 CLIPREADCBREQ *pReq, void *pv,
260 uint32_t cb)
261{
262 if (cb <= pReq->cb)
263 memcpy(pReq->pv, pv, cb);
264 RTMemFree(pReq);
265 vboxSvcClipboardCompleteReadData(pCtx->pClient, rc, cb);
266}
267
268/** A request for clipboard data from VBox */
269struct _VBOXCLIPBOARDREQFROMVBOX
270{
271 /** Data received */
272 void *pv;
273 /** The size of the data */
274 uint32_t cb;
275 /** Format of the data */
276 uint32_t format;
277 /** A semaphore for waiting for the data */
278 RTSEMEVENT finished;
279};
280
281/** Wait for clipboard data requested from VBox to arrive. */
282static int clipWaitForDataFromVBox(VBOXCLIPBOARDCONTEXT *pCtx,
283 VBOXCLIPBOARDREQFROMVBOX *pReq,
284 uint32_t u32Format)
285{
286 int rc = VINF_SUCCESS;
287 LogRelFlowFunc(("pCtx=%p, pReq=%p, u32Format=%02X\n", pCtx, pReq, u32Format));
288 /* Request data from VBox */
289 vboxSvcClipboardReportMsg(pCtx->pClient,
290 VBOX_SHARED_CLIPBOARD_HOST_MSG_READ_DATA,
291 u32Format);
292 /* Which will signal us when it is ready. We use a timeout here
293 * because we can't be sure that the guest will behave correctly.
294 */
295 rc = RTSemEventWait(pReq->finished, CLIPBOARD_TIMEOUT);
296 /* If the request hasn't yet completed then we cancel it. We use
297 * the critical section to prevent these operations colliding. */
298 RTCritSectEnter(&pCtx->clipboardMutex);
299 /* The data may have arrived between the semaphore timing out and
300 * our grabbing the mutex. */
301 if (rc == VERR_TIMEOUT && pReq->pv != NULL)
302 rc = VINF_SUCCESS;
303 if (pCtx->pReq == pReq)
304 pCtx->pReq = NULL;
305 Assert(pCtx->pReq == NULL);
306 RTCritSectLeave(&pCtx->clipboardMutex);
307 if (RT_SUCCESS(rc) && (pReq->pv == NULL))
308 rc = VERR_NO_DATA;
309 LogRelFlowFunc(("returning %Rrc\n", rc));
310 return rc;
311}
312
313/** Post a request for clipboard data to VBox/the guest and wait for it to be
314 * completed. */
315static int clipRequestDataFromVBox(VBOXCLIPBOARDCONTEXT *pCtx,
316 VBOXCLIPBOARDREQFROMVBOX *pReq,
317 uint32_t u32Format)
318{
319 int rc = VINF_SUCCESS;
320 LogRelFlowFunc(("pCtx=%p, pReq=%p, u32Format=%02X\n", pCtx, pReq,
321 u32Format));
322 /* Start by "posting" the request for the next invocation of
323 * vboxClipboardWriteData. */
324 RTCritSectEnter(&pCtx->clipboardMutex);
325 if (pCtx->pReq != NULL)
326 {
327 /* This would be a violation of the protocol, see the comments in the
328 * context structure definition. */
329 Assert(false);
330 rc = VERR_WRONG_ORDER;
331 }
332 else
333 pCtx->pReq = pReq;
334 RTCritSectLeave(&pCtx->clipboardMutex);
335 if (RT_SUCCESS(rc))
336 rc = clipWaitForDataFromVBox(pCtx, pReq, u32Format);
337 LogRelFlowFunc(("returning %Rrc\n", rc));
338 return rc;
339}
340
341/**
342 * Send a request to VBox to transfer the contents of its clipboard to X11.
343 *
344 * @param pCtx Pointer to the host clipboard structure
345 * @param u32Format The format in which the data should be transferred
346 * @param ppv On success and if pcb > 0, this will point to a buffer
347 * to be freed with RTMemFree containing the data read.
348 * @param pcb On success, this contains the number of bytes of data
349 * returned
350 * @note Host glue code.
351 */
352int ClipRequestDataForX11 (VBOXCLIPBOARDCONTEXT *pCtx,
353 uint32_t u32Format, void **ppv,
354 uint32_t *pcb)
355{
356 VBOXCLIPBOARDREQFROMVBOX request = { NULL, 0, 0, NIL_RTSEMEVENT };
357
358 LogRelFlowFunc(("pCtx=%p, u32Format=%02X, ppv=%p, pcb=%p\n", pCtx,
359 u32Format, ppv, pcb));
360 if (pCtx->fShuttingDown)
361 {
362 /* The shared clipboard is disconnecting. */
363 LogRelFunc(("host requested guest clipboard data after guest had disconnected.\n"));
364 return VERR_WRONG_ORDER;
365 }
366 int rc = RTSemEventCreate(&request.finished);
367 if (RT_SUCCESS(rc))
368 {
369 rc = clipRequestDataFromVBox(pCtx, &request, u32Format);
370 RTSemEventDestroy(request.finished);
371 }
372 if (RT_SUCCESS(rc))
373 {
374 *ppv = request.pv;
375 *pcb = request.cb;
376 }
377 LogRelFlowFunc(("returning %Rrc\n", rc));
378 if (RT_SUCCESS(rc))
379 LogRelFlowFunc(("*ppv=%.*ls, *pcb=%u\n", *pcb / 2, *ppv, *pcb));
380 return rc;
381}
382
383/**
384 * Called when we have requested data from VBox and that data has arrived.
385 *
386 * @param pClient Context information about the guest VM
387 * @param pv Buffer to which the data was written
388 * @param cb The size of the data written
389 * @param u32Format The format of the data written
390 * @note Host glue code
391 */
392void vboxClipboardWriteData (VBOXCLIPBOARDCLIENTDATA *pClient,
393 void *pv, uint32_t cb, uint32_t u32Format)
394{
395 LogRelFlowFunc (("called. pClient=%p, pv=%p (%.*ls), cb=%u, u32Format=%02X\n",
396 pClient, pv, cb / 2, pv, cb, u32Format));
397
398 VBOXCLIPBOARDCONTEXT *pCtx = pClient->pCtx;
399 /* Grab the mutex and check whether there is a pending request for data.
400 */
401 RTCritSectEnter(&pCtx->clipboardMutex);
402 VBOXCLIPBOARDREQFROMVBOX *pReq = pCtx->pReq;
403 if (pReq != NULL)
404 {
405 if (cb > 0)
406 {
407 pReq->pv = RTMemDup(pv, cb);
408 if (pReq->pv != NULL) /* NULL may also mean no memory... */
409 {
410 pReq->cb = cb;
411 pReq->format = u32Format;
412 }
413 }
414 /* Signal that the request has been completed. */
415 RTSemEventSignal(pReq->finished);
416 pCtx->pReq = NULL;
417 }
418 RTCritSectLeave(&pCtx->clipboardMutex);
419}
420
421#ifdef TESTCASE
422#include <iprt/initterm.h>
423#include <iprt/stream.h>
424
425#define TEST_NAME "tstClipboardX11-2"
426
427struct _CLIPBACKEND
428{
429 uint32_t formats;
430 struct _READDATA
431 {
432 uint32_t format;
433 int rc;
434 CLIPREADCBREQ *pReq;
435 } readData;
436 struct _COMPLETEREAD
437 {
438 int rc;
439 uint32_t cbActual;
440 } completeRead;
441 struct _WRITEDATA
442 {
443 void *pv;
444 uint32_t cb;
445 uint32_t format;
446 bool timeout;
447 } writeData;
448 struct _REPORTDATA
449 {
450 uint32_t format;
451 } reportData;
452};
453
454void vboxSvcClipboardReportMsg (VBOXCLIPBOARDCLIENTDATA *pClient, uint32_t u32Msg, uint32_t u32Formats)
455{
456 CLIPBACKEND *pBackend = pClient->pCtx->pBackend;
457 if ( (u32Msg == VBOX_SHARED_CLIPBOARD_HOST_MSG_READ_DATA)
458 && !pBackend->writeData.timeout)
459 vboxClipboardWriteData(pClient, pBackend->writeData.pv,
460 pBackend->writeData.cb,
461 pBackend->writeData.format);
462 else
463 return;
464}
465
466void vboxSvcClipboardCompleteReadData(VBOXCLIPBOARDCLIENTDATA *pClient, int rc, uint32_t cbActual)
467{
468 CLIPBACKEND *pBackend = pClient->pCtx->pBackend;
469 pBackend->completeRead.rc = rc;
470 pBackend->completeRead.cbActual = cbActual;
471}
472
473CLIPBACKEND *ClipConstructX11(VBOXCLIPBOARDCONTEXT *pFrontend, bool)
474{
475 return (CLIPBACKEND *)RTMemAllocZ(sizeof(CLIPBACKEND));
476}
477
478void ClipDestructX11(CLIPBACKEND *pBackend)
479{
480 RTMemFree(pBackend);
481}
482
483int ClipStartX11(CLIPBACKEND *pBackend, bool)
484{
485 return VINF_SUCCESS;
486}
487
488int ClipStopX11(CLIPBACKEND *pBackend)
489{
490 return VINF_SUCCESS;
491}
492
493void ClipAnnounceFormatToX11(CLIPBACKEND *pBackend,
494 uint32_t u32Formats)
495{
496 pBackend->formats = u32Formats;
497}
498
499extern int ClipRequestDataFromX11(CLIPBACKEND *pBackend, uint32_t u32Format,
500 CLIPREADCBREQ *pReq)
501{
502 pBackend->readData.format = u32Format;
503 pBackend->readData.pReq = pReq;
504 return pBackend->readData.rc;
505}
506
507int main()
508{
509 VBOXCLIPBOARDCLIENTDATA client;
510 unsigned cErrors = 0;
511 int rc = RTR3InitExeNoArguments(0);
512 RTPrintf(TEST_NAME ": TESTING\n");
513 AssertRCReturn(rc, 1);
514 rc = vboxClipboardConnect(&client, false);
515 CLIPBACKEND *pBackend = client.pCtx->pBackend;
516 AssertRCReturn(rc, 1);
517 vboxClipboardFormatAnnounce(&client,
518 VBOX_SHARED_CLIPBOARD_FMT_UNICODETEXT);
519 if (pBackend->formats != VBOX_SHARED_CLIPBOARD_FMT_UNICODETEXT)
520 {
521 RTPrintf(TEST_NAME ": vboxClipboardFormatAnnounce failed with VBOX_SHARED_CLIPBOARD_FMT_UNICODETEXT\n");
522 ++cErrors;
523 }
524 pBackend->readData.rc = VINF_SUCCESS;
525 client.asyncRead.callHandle = (VBOXHGCMCALLHANDLE)pBackend;
526 client.asyncRead.paParms = (VBOXHGCMSVCPARM *)&client;
527 uint32_t u32Dummy;
528 rc = vboxClipboardReadData(&client, VBOX_SHARED_CLIPBOARD_FMT_UNICODETEXT,
529 &u32Dummy, 42, &u32Dummy);
530 if (rc != VINF_HGCM_ASYNC_EXECUTE)
531 {
532 RTPrintf(TEST_NAME ": vboxClipboardReadData returned %Rrc\n", rc);
533 ++cErrors;
534 }
535 else
536 {
537 if ( pBackend->readData.format !=
538 VBOX_SHARED_CLIPBOARD_FMT_UNICODETEXT
539 || pBackend->readData.pReq->pv != &u32Dummy
540 || pBackend->readData.pReq->cb != 42
541 || pBackend->readData.pReq->pcbActual != &u32Dummy)
542 {
543 RTPrintf(TEST_NAME ": format=%u, pReq->pv=%p, pReq->cb=%u, pReq->pcbActual=%p\n",
544 pBackend->readData.format, pBackend->readData.pReq->pv,
545 pBackend->readData.pReq->cb,
546 pBackend->readData.pReq->pcbActual);
547 ++cErrors;
548 }
549 else
550 {
551 ClipCompleteDataRequestFromX11(client.pCtx, VERR_NO_DATA,
552 pBackend->readData.pReq, NULL, 43);
553 if ( pBackend->completeRead.rc != VERR_NO_DATA
554 || pBackend->completeRead.cbActual != 43)
555 {
556 RTPrintf(TEST_NAME ": rc=%Rrc, cbActual=%u\n",
557 pBackend->completeRead.rc,
558 pBackend->completeRead.cbActual);
559 ++cErrors;
560 }
561 }
562 }
563 void *pv;
564 uint32_t cb;
565 pBackend->writeData.pv = (void *)"testing";
566 pBackend->writeData.cb = sizeof("testing");
567 pBackend->writeData.format = 1234;
568 pBackend->reportData.format = 4321; /* XX this should be handled! */
569 rc = ClipRequestDataForX11(client.pCtx, 23, &pv, &cb);
570 if ( rc != VINF_SUCCESS
571 || strcmp((const char *)pv, "testing") != 0
572 || cb != sizeof("testing"))
573 {
574 RTPrintf("rc=%Rrc, pv=%p, cb=%u\n", rc, pv, cb);
575 ++cErrors;
576 }
577 else
578 RTMemFree(pv);
579 pBackend->writeData.timeout = true;
580 rc = ClipRequestDataForX11(client.pCtx, 23, &pv, &cb);
581 if (rc != VERR_TIMEOUT)
582 {
583 RTPrintf("rc=%Rrc, expected VERR_TIMEOUT\n", rc);
584 ++cErrors;
585 }
586 pBackend->writeData.pv = NULL;
587 pBackend->writeData.cb = 0;
588 pBackend->writeData.timeout = false;
589 rc = ClipRequestDataForX11(client.pCtx, 23, &pv, &cb);
590 if (rc != VERR_NO_DATA)
591 {
592 RTPrintf("rc=%Rrc, expected VERR_NO_DATA\n", rc);
593 ++cErrors;
594 }
595 /* Data arriving after a timeout should *not* cause any segfaults or
596 * memory leaks. Check with Valgrind! */
597 vboxClipboardWriteData(&client, (void *)"tested", sizeof("tested"), 999);
598 vboxClipboardDisconnect(&client);
599 if (cErrors > 0)
600 RTPrintf(TEST_NAME ": errors: %u\n", cErrors);
601 return cErrors > 0 ? 1 : 0;
602}
603#endif /* TESTCASE */
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