VirtualBox

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

Last change on this file since 34575 was 33540, checked in by vboxsync, 14 years ago

*: spelling fixes, thanks Timeless!

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