VirtualBox

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

Last change on this file since 19875 was 19875, checked in by vboxsync, 16 years ago

HostServices and GuestHost/SharedClipboard: hopefully fixed the broken saved state and improved on my changes to the platform-neutral code for asynchronous handling of guest data requests

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