VirtualBox

source: vbox/trunk/src/VBox/HostServices/SharedClipboard/service.cpp@ 35353

Last change on this file since 35353 was 35346, checked in by vboxsync, 14 years ago

VMM reorg: Moving the public include files from include/VBox to include/VBox/vmm.

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 32.8 KB
Line 
1/* $Id: service.cpp 35346 2010-12-27 16:13:13Z vboxsync $ */
2/** @file
3 * Shared Clipboard: Host service entry points.
4 */
5
6/*
7 * Copyright (C) 2006-2007 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/** @page pg_hostclip The Shared Clipboard Host Service
20 *
21 * The shared clipboard host service provides a proxy between the host's
22 * clipboard and a similar proxy running on a guest. The service is split
23 * into a platform-independent core and platform-specific backends. The
24 * service defines two communication protocols - one to communicate with the
25 * clipboard service running on the guest, and one to communicate with the
26 * backend. These will be described in a very skeletal fashion here.
27 *
28 * @section sec_hostclip_guest_proto The guest communication protocol
29 *
30 * The guest clipboard service communicates with the host service via HGCM
31 * (the host service runs as an HGCM service). The guest clipboard must
32 * connect to the host service before all else (Windows hosts currently only
33 * support one simultaneous connection). Once it has connected, it can send
34 * HGCM messages to the host services, some of which will receive replies from
35 * the host. The host can only reply to a guest message, it cannot initiate
36 * any communication. The guest can in theory send any number of messages in
37 * parallel (see the descriptions of the messages for the practice), and the
38 * host will receive these in sequence, and may reply to them at once
39 * (releasing the caller in the guest) or defer the reply until later.
40 *
41 * There are currently four messages defined. The first is
42 * VBOX_SHARED_CLIPBOARD_FN_GET_HOST_MSG, which waits for a message from the
43 * host. Host messages currently defined are
44 * VBOX_SHARED_CLIPBOARD_HOST_MSG_QUIT (unused),
45 * VBOX_SHARED_CLIPBOARD_HOST_MSG_READ_DATA (request that the guest send the
46 * contents of its clipboard to the host) and
47 * VBOX_SHARED_CLIPBOARD_HOST_MSG_FORMATS (to notify the guest that new
48 * clipboard data is available). If a host message is sent while the guest is
49 * not waiting, it will be queued until the guest requests it. At most one
50 * host message of each type will be kept in the queue. The host code only
51 * supports a single simultaneous VBOX_SHARED_CLIPBOARD_FN_GET_HOST_MSG call
52 * from the guest.
53 *
54 * The second guest message is VBOX_SHARED_CLIPBOARD_FN_FORMATS, which tells
55 * the host that the guest has new clipboard data available. The third is
56 * VBOX_SHARED_CLIPBOARD_FN_READ_DATA, which asks the host to send its
57 * clipboard data and waits until it arrives. The host supports at most one
58 * simultaneous VBOX_SHARED_CLIPBOARD_FN_READ_DATA call from the guest - if a
59 * second call is made before the first has returned, the first will be
60 * aborted.
61 *
62 * The last guest message is VBOX_SHARED_CLIPBOARD_FN_WRITE_DATA, which is
63 * used to send the contents of the guest clipboard to the host. This call
64 * should be used after the host has requested data from the guest.
65 *
66 * @section sec_hostclip_backend_proto The communication protocol with the
67 * platform-specific backend
68 *
69 * This section may be written in the future :)
70 */
71
72#include <VBox/HostServices/VBoxClipboardSvc.h>
73#include <VBox/HostServices/VBoxClipboardExt.h>
74
75#include <iprt/alloc.h>
76#include <iprt/string.h>
77#include <iprt/assert.h>
78#include <iprt/critsect.h>
79#include <VBox/vmm/ssm.h>
80
81#include "VBoxClipboard.h"
82
83static void VBoxHGCMParmUInt32Set (VBOXHGCMSVCPARM *pParm, uint32_t u32)
84{
85 pParm->type = VBOX_HGCM_SVC_PARM_32BIT;
86 pParm->u.uint32 = u32;
87}
88
89static int VBoxHGCMParmUInt32Get (VBOXHGCMSVCPARM *pParm, uint32_t *pu32)
90{
91 if (pParm->type == VBOX_HGCM_SVC_PARM_32BIT)
92 {
93 *pu32 = pParm->u.uint32;
94 return VINF_SUCCESS;
95 }
96
97 return VERR_INVALID_PARAMETER;
98}
99
100#if 0
101static void VBoxHGCMParmPtrSet (VBOXHGCMSVCPARM *pParm, void *pv, uint32_t cb)
102{
103 pParm->type = VBOX_HGCM_SVC_PARM_PTR;
104 pParm->u.pointer.size = cb;
105 pParm->u.pointer.addr = pv;
106}
107#endif
108
109static int VBoxHGCMParmPtrGet (VBOXHGCMSVCPARM *pParm, void **ppv, uint32_t *pcb)
110{
111 if (pParm->type == VBOX_HGCM_SVC_PARM_PTR)
112 {
113 *ppv = pParm->u.pointer.addr;
114 *pcb = pParm->u.pointer.size;
115 return VINF_SUCCESS;
116 }
117
118 return VERR_INVALID_PARAMETER;
119}
120
121static PVBOXHGCMSVCHELPERS g_pHelpers;
122
123static RTCRITSECT critsect;
124static uint32_t g_u32Mode;
125
126static PFNHGCMSVCEXT g_pfnExtension;
127static void *g_pvExtension;
128
129static VBOXCLIPBOARDCLIENTDATA *g_pClient;
130
131/* Serialization of data reading and format announcements from the RDP client. */
132static bool g_fReadingData = false;
133static bool g_fDelayedAnnouncement = false;
134static uint32_t g_u32DelayedFormats = 0;
135
136static uint32_t vboxSvcClipboardMode (void)
137{
138 return g_u32Mode;
139}
140
141static void vboxSvcClipboardModeSet (uint32_t u32Mode)
142{
143 switch (u32Mode)
144 {
145 case VBOX_SHARED_CLIPBOARD_MODE_OFF:
146 case VBOX_SHARED_CLIPBOARD_MODE_HOST_TO_GUEST:
147 case VBOX_SHARED_CLIPBOARD_MODE_GUEST_TO_HOST:
148 case VBOX_SHARED_CLIPBOARD_MODE_BIDIRECTIONAL:
149 g_u32Mode = u32Mode;
150 break;
151
152 default:
153 g_u32Mode = VBOX_SHARED_CLIPBOARD_MODE_OFF;
154 }
155}
156
157bool vboxSvcClipboardLock (void)
158{
159 return RT_SUCCESS(RTCritSectEnter (&critsect));
160}
161
162void vboxSvcClipboardUnlock (void)
163{
164 RTCritSectLeave (&critsect);
165}
166
167/* Set the HGCM parameters according to pending messages.
168 * Executed under the clipboard lock.
169 */
170static bool vboxSvcClipboardReturnMsg (VBOXCLIPBOARDCLIENTDATA *pClient, VBOXHGCMSVCPARM paParms[])
171{
172 /* Message priority is taken into account. */
173 if (pClient->fMsgQuit)
174 {
175 LogRelFlow(("vboxSvcClipboardReturnMsg: Quit\n"));
176 VBoxHGCMParmUInt32Set (&paParms[0], VBOX_SHARED_CLIPBOARD_HOST_MSG_QUIT);
177 VBoxHGCMParmUInt32Set (&paParms[1], 0);
178 pClient->fMsgQuit = false;
179 }
180 else if (pClient->fMsgReadData)
181 {
182 LogRelFlow(("vboxSvcClipboardReturnMsg: ReadData %02X\n", pClient->u32RequestedFormat));
183 VBoxHGCMParmUInt32Set (&paParms[0], VBOX_SHARED_CLIPBOARD_HOST_MSG_READ_DATA);
184 VBoxHGCMParmUInt32Set (&paParms[1], pClient->u32RequestedFormat);
185 pClient->fMsgReadData = false;
186 }
187 else if (pClient->fMsgFormats)
188 {
189 LogRelFlow(("vboxSvcClipboardReturnMsg: Formats %02X\n", pClient->u32AvailableFormats));
190 VBoxHGCMParmUInt32Set (&paParms[0], VBOX_SHARED_CLIPBOARD_HOST_MSG_FORMATS);
191 VBoxHGCMParmUInt32Set (&paParms[1], pClient->u32AvailableFormats);
192 pClient->fMsgFormats = false;
193 }
194 else
195 {
196 /* No pending messages. */
197 LogRelFlow(("vboxSvcClipboardReturnMsg: no message\n"));
198 return false;
199 }
200
201 /* Message information assigned. */
202 return true;
203}
204
205void vboxSvcClipboardReportMsg (VBOXCLIPBOARDCLIENTDATA *pClient, uint32_t u32Msg, uint32_t u32Formats)
206{
207 if (vboxSvcClipboardLock ())
208 {
209 switch (u32Msg)
210 {
211 case VBOX_SHARED_CLIPBOARD_HOST_MSG_QUIT:
212 {
213 LogRelFlow(("vboxSvcClipboardReportMsg: Quit\n"));
214 pClient->fMsgQuit = true;
215 } break;
216 case VBOX_SHARED_CLIPBOARD_HOST_MSG_READ_DATA:
217 {
218 if ( vboxSvcClipboardMode () != VBOX_SHARED_CLIPBOARD_MODE_GUEST_TO_HOST
219 && vboxSvcClipboardMode () != VBOX_SHARED_CLIPBOARD_MODE_BIDIRECTIONAL)
220 {
221 /* Skip the message. */
222 break;
223 }
224
225 LogRelFlow(("vboxSvcClipboardReportMsg: ReadData %02X\n", u32Formats));
226 pClient->u32RequestedFormat = u32Formats;
227 pClient->fMsgReadData = true;
228 } break;
229 case VBOX_SHARED_CLIPBOARD_HOST_MSG_FORMATS:
230 {
231 if ( vboxSvcClipboardMode () != VBOX_SHARED_CLIPBOARD_MODE_HOST_TO_GUEST
232 && vboxSvcClipboardMode () != VBOX_SHARED_CLIPBOARD_MODE_BIDIRECTIONAL)
233 {
234 /* Skip the message. */
235 break;
236 }
237
238 LogRelFlow(("vboxSvcClipboardReportMsg: Formats %02X\n", u32Formats));
239 pClient->u32AvailableFormats = u32Formats;
240 pClient->fMsgFormats = true;
241 } break;
242 default:
243 {
244 /* Invalid message. */
245 LogRelFlow(("vboxSvcClipboardReportMsg: invalid message %d\n", u32Msg));
246 } break;
247 }
248
249 if (pClient->fAsync)
250 {
251 /* The client waits for a response. */
252 bool fMessageReturned = vboxSvcClipboardReturnMsg (pClient, pClient->async.paParms);
253
254 /* Make a copy of the handle. */
255 VBOXHGCMCALLHANDLE callHandle = pClient->async.callHandle;
256
257 if (fMessageReturned)
258 {
259 /* There is a response. */
260 pClient->fAsync = false;
261 }
262
263 vboxSvcClipboardUnlock ();
264
265 if (fMessageReturned)
266 {
267 LogRelFlow(("vboxSvcClipboardReportMsg: CallComplete\n"));
268 g_pHelpers->pfnCallComplete (callHandle, VINF_SUCCESS);
269 }
270 }
271 else
272 {
273 vboxSvcClipboardUnlock ();
274 }
275 }
276}
277
278static int svcInit (void)
279{
280 int rc = RTCritSectInit (&critsect);
281
282 if (RT_SUCCESS (rc))
283 {
284 vboxSvcClipboardModeSet (VBOX_SHARED_CLIPBOARD_MODE_OFF);
285
286 rc = vboxClipboardInit ();
287
288 /* Clean up on failure, because 'svnUnload' will not be called
289 * if the 'svcInit' returns an error.
290 */
291 if (RT_FAILURE (rc))
292 {
293 RTCritSectDelete (&critsect);
294 }
295 }
296
297 return rc;
298}
299
300static DECLCALLBACK(int) svcUnload (void *)
301{
302 vboxClipboardDestroy ();
303 RTCritSectDelete (&critsect);
304 return VINF_SUCCESS;
305}
306
307/**
308 * Disconnect the host side of the shared clipboard and send a "host disconnected" message
309 * to the guest side.
310 */
311static DECLCALLBACK(int) svcDisconnect (void *, uint32_t u32ClientID, void *pvClient)
312{
313 VBOXCLIPBOARDCLIENTDATA *pClient = (VBOXCLIPBOARDCLIENTDATA *)pvClient;
314
315 vboxSvcClipboardReportMsg (pClient, VBOX_SHARED_CLIPBOARD_HOST_MSG_QUIT, 0);
316
317 vboxClipboardDisconnect (pClient);
318
319 memset (pClient, 0, sizeof (*pClient));
320
321 g_pClient = NULL;
322
323 return VINF_SUCCESS;
324}
325
326static DECLCALLBACK(int) svcConnect (void *, uint32_t u32ClientID, void *pvClient)
327{
328 VBOXCLIPBOARDCLIENTDATA *pClient = (VBOXCLIPBOARDCLIENTDATA *)pvClient;
329
330 int rc = VINF_SUCCESS;
331
332 /* If there is already a client connected then we want to release it first. */
333 if (g_pClient != NULL)
334 {
335 uint32_t u32OldClientID = g_pClient->u32ClientID;
336
337 svcDisconnect(NULL, u32OldClientID, g_pClient);
338 /* And free the resources in the hgcm subsystem. */
339 g_pHelpers->pfnDisconnectClient(g_pHelpers->pvInstance, u32OldClientID);
340 }
341
342 /* Register the client. */
343 memset (pClient, 0, sizeof (*pClient));
344
345 pClient->u32ClientID = u32ClientID;
346
347 rc = vboxClipboardConnect (pClient);
348
349 if (RT_SUCCESS (rc))
350 {
351 g_pClient = pClient;
352 }
353
354 LogRel2(("vboxClipboardConnect: rc = %Rrc\n", rc));
355
356 return rc;
357}
358
359static DECLCALLBACK(void) svcCall (void *,
360 VBOXHGCMCALLHANDLE callHandle,
361 uint32_t u32ClientID,
362 void *pvClient,
363 uint32_t u32Function,
364 uint32_t cParms,
365 VBOXHGCMSVCPARM paParms[])
366{
367 int rc = VINF_SUCCESS;
368
369 LogRel2(("svcCall: u32ClientID = %d, fn = %d, cParms = %d, pparms = %d\n",
370 u32ClientID, u32Function, cParms, paParms));
371
372 VBOXCLIPBOARDCLIENTDATA *pClient = (VBOXCLIPBOARDCLIENTDATA *)pvClient;
373
374 bool fAsynchronousProcessing = false;
375
376#ifdef DEBUG
377 uint32_t i;
378
379 for (i = 0; i < cParms; i++)
380 {
381 /** @todo parameters other than 32 bit */
382 LogRel2((" pparms[%d]: type %d value %d\n", i, paParms[i].type, paParms[i].u.uint32));
383 }
384#endif
385
386 switch (u32Function)
387 {
388 case VBOX_SHARED_CLIPBOARD_FN_GET_HOST_MSG:
389 {
390 /* The quest requests a host message. */
391 LogRel2(("svcCall: VBOX_SHARED_CLIPBOARD_FN_GET_HOST_MSG\n"));
392
393 if (cParms != VBOX_SHARED_CLIPBOARD_CPARMS_GET_HOST_MSG)
394 {
395 rc = VERR_INVALID_PARAMETER;
396 }
397 else if ( paParms[0].type != VBOX_HGCM_SVC_PARM_32BIT /* msg */
398 || paParms[1].type != VBOX_HGCM_SVC_PARM_32BIT /* formats */
399 )
400 {
401 rc = VERR_INVALID_PARAMETER;
402 }
403 else
404 {
405 /* Atomically verify the client's state. */
406 if (vboxSvcClipboardLock ())
407 {
408 bool fMessageReturned = vboxSvcClipboardReturnMsg (pClient, paParms);
409
410 if (fMessageReturned)
411 {
412 /* Just return to the caller. */
413 pClient->fAsync = false;
414 }
415 else
416 {
417 /* No event available at the time. Process asynchronously. */
418 fAsynchronousProcessing = true;
419
420 pClient->fAsync = true;
421 pClient->async.callHandle = callHandle;
422 pClient->async.paParms = paParms;
423
424 LogRel2(("svcCall: async.\n"));
425 }
426
427 vboxSvcClipboardUnlock ();
428 }
429 else
430 {
431 rc = VERR_NOT_SUPPORTED;
432 }
433 }
434 } break;
435
436 case VBOX_SHARED_CLIPBOARD_FN_FORMATS:
437 {
438 /* The guest reports that some formats are available. */
439 LogRel2(("svcCall: VBOX_SHARED_CLIPBOARD_FN_FORMATS\n"));
440
441 if (cParms != VBOX_SHARED_CLIPBOARD_CPARMS_FORMATS)
442 {
443 rc = VERR_INVALID_PARAMETER;
444 }
445 else if ( paParms[0].type != VBOX_HGCM_SVC_PARM_32BIT /* formats */
446 )
447 {
448 rc = VERR_INVALID_PARAMETER;
449 }
450 else
451 {
452 uint32_t u32Formats;
453
454 rc = VBoxHGCMParmUInt32Get (&paParms[0], &u32Formats);
455
456 if (RT_SUCCESS (rc))
457 {
458 if ( vboxSvcClipboardMode () != VBOX_SHARED_CLIPBOARD_MODE_GUEST_TO_HOST
459 && vboxSvcClipboardMode () != VBOX_SHARED_CLIPBOARD_MODE_BIDIRECTIONAL)
460 {
461 rc = VERR_NOT_SUPPORTED;
462 break;
463 }
464
465 if (g_pfnExtension)
466 {
467 VBOXCLIPBOARDEXTPARMS parms;
468
469 parms.u32Format = u32Formats;
470
471 g_pfnExtension (g_pvExtension, VBOX_CLIPBOARD_EXT_FN_FORMAT_ANNOUNCE, &parms, sizeof (parms));
472 }
473 else
474 {
475 vboxClipboardFormatAnnounce (pClient, u32Formats);
476 }
477 }
478 }
479 } break;
480
481 case VBOX_SHARED_CLIPBOARD_FN_READ_DATA:
482 {
483 /* The guest wants to read data in the given format. */
484 LogRel2(("svcCall: VBOX_SHARED_CLIPBOARD_FN_READ_DATA\n"));
485
486 if (cParms != VBOX_SHARED_CLIPBOARD_CPARMS_READ_DATA)
487 {
488 rc = VERR_INVALID_PARAMETER;
489 }
490 else if ( paParms[0].type != VBOX_HGCM_SVC_PARM_32BIT /* format */
491 || paParms[1].type != VBOX_HGCM_SVC_PARM_PTR /* ptr */
492 || paParms[2].type != VBOX_HGCM_SVC_PARM_32BIT /* size */
493 )
494 {
495 rc = VERR_INVALID_PARAMETER;
496 }
497 else
498 {
499 uint32_t u32Format;
500 void *pv;
501 uint32_t cb;
502
503 rc = VBoxHGCMParmUInt32Get (&paParms[0], &u32Format);
504
505 if (RT_SUCCESS (rc))
506 {
507 rc = VBoxHGCMParmPtrGet (&paParms[1], &pv, &cb);
508
509 if (RT_SUCCESS (rc))
510 {
511 if ( vboxSvcClipboardMode () != VBOX_SHARED_CLIPBOARD_MODE_HOST_TO_GUEST
512 && vboxSvcClipboardMode () != VBOX_SHARED_CLIPBOARD_MODE_BIDIRECTIONAL)
513 {
514 rc = VERR_NOT_SUPPORTED;
515 break;
516 }
517
518 uint32_t cbActual = 0;
519
520 if (g_pfnExtension)
521 {
522 VBOXCLIPBOARDEXTPARMS parms;
523
524 parms.u32Format = u32Format;
525 parms.u.pvData = pv;
526 parms.cbData = cb;
527
528 g_fReadingData = true;
529 rc = g_pfnExtension (g_pvExtension, VBOX_CLIPBOARD_EXT_FN_DATA_READ, &parms, sizeof (parms));
530 LogRelFlow(("DATA: g_fDelayedAnnouncement = %d, g_u32DelayedFormats = 0x%x\n", g_fDelayedAnnouncement, g_u32DelayedFormats));
531 if (g_fDelayedAnnouncement)
532 {
533 vboxSvcClipboardReportMsg (g_pClient, VBOX_SHARED_CLIPBOARD_HOST_MSG_FORMATS, g_u32DelayedFormats);
534 g_fDelayedAnnouncement = false;
535 g_u32DelayedFormats = 0;
536 }
537 g_fReadingData = false;
538
539 if (RT_SUCCESS (rc))
540 {
541 cbActual = parms.cbData;
542 }
543 }
544 else
545 {
546 /* Release any other pending read, as we only
547 * support one pending read at one time. */
548 vboxSvcClipboardCompleteReadData(pClient, VERR_NO_DATA, 0);
549 rc = vboxClipboardReadData (pClient, u32Format, pv, cb, &cbActual);
550 }
551
552 /* Remember our read request until it is completed.
553 * See the protocol description above for more
554 * information. */
555 if (rc == VINF_HGCM_ASYNC_EXECUTE)
556 {
557 if (vboxSvcClipboardLock())
558 {
559 pClient->asyncRead.callHandle = callHandle;
560 pClient->asyncRead.paParms = paParms;
561 pClient->fReadPending = true;
562 fAsynchronousProcessing = true;
563 vboxSvcClipboardUnlock();
564 }
565 else
566 rc = VERR_NOT_SUPPORTED;
567 }
568 else if (RT_SUCCESS (rc))
569 {
570 VBoxHGCMParmUInt32Set (&paParms[2], cbActual);
571 }
572 }
573 }
574 }
575 } break;
576
577 case VBOX_SHARED_CLIPBOARD_FN_WRITE_DATA:
578 {
579 /* The guest writes the requested data. */
580 LogRel2(("svcCall: VBOX_SHARED_CLIPBOARD_FN_WRITE_DATA\n"));
581
582 if (cParms != VBOX_SHARED_CLIPBOARD_CPARMS_WRITE_DATA)
583 {
584 rc = VERR_INVALID_PARAMETER;
585 }
586 else if ( paParms[0].type != VBOX_HGCM_SVC_PARM_32BIT /* format */
587 || paParms[1].type != VBOX_HGCM_SVC_PARM_PTR /* ptr */
588 )
589 {
590 rc = VERR_INVALID_PARAMETER;
591 }
592 else
593 {
594 void *pv;
595 uint32_t cb;
596 uint32_t u32Format;
597
598 rc = VBoxHGCMParmUInt32Get (&paParms[0], &u32Format);
599
600 if (RT_SUCCESS (rc))
601 {
602 rc = VBoxHGCMParmPtrGet (&paParms[1], &pv, &cb);
603
604 if (RT_SUCCESS (rc))
605 {
606 if ( vboxSvcClipboardMode () != VBOX_SHARED_CLIPBOARD_MODE_GUEST_TO_HOST
607 && vboxSvcClipboardMode () != VBOX_SHARED_CLIPBOARD_MODE_BIDIRECTIONAL)
608 {
609 rc = VERR_NOT_SUPPORTED;
610 break;
611 }
612
613 if (g_pfnExtension)
614 {
615 VBOXCLIPBOARDEXTPARMS parms;
616
617 parms.u32Format = u32Format;
618 parms.u.pvData = pv;
619 parms.cbData = cb;
620
621 g_pfnExtension (g_pvExtension, VBOX_CLIPBOARD_EXT_FN_DATA_WRITE, &parms, sizeof (parms));
622 }
623 else
624 {
625 vboxClipboardWriteData (pClient, pv, cb, u32Format);
626 }
627 }
628 }
629 }
630 } break;
631
632 default:
633 {
634 rc = VERR_NOT_IMPLEMENTED;
635 }
636 }
637
638 LogRelFlow(("svcCall: rc = %Rrc\n", rc));
639
640 if (!fAsynchronousProcessing)
641 {
642 g_pHelpers->pfnCallComplete (callHandle, rc);
643 }
644}
645
646/** If the client in the guest is waiting for a read operation to complete
647 * then complete it, otherwise return. See the protocol description in the
648 * shared clipboard module description. */
649void vboxSvcClipboardCompleteReadData(VBOXCLIPBOARDCLIENTDATA *pClient, int rc, uint32_t cbActual)
650{
651 VBOXHGCMCALLHANDLE callHandle = NULL;
652 VBOXHGCMSVCPARM *paParms = NULL;
653 bool fReadPending = false;
654 if (vboxSvcClipboardLock()) /* if not can we do anything useful? */
655 {
656 callHandle = pClient->asyncRead.callHandle;
657 paParms = pClient->asyncRead.paParms;
658 fReadPending = pClient->fReadPending;
659 pClient->fReadPending = false;
660 vboxSvcClipboardUnlock();
661 }
662 if (fReadPending)
663 {
664 VBoxHGCMParmUInt32Set (&paParms[2], cbActual);
665 g_pHelpers->pfnCallComplete (callHandle, rc);
666 }
667}
668
669/*
670 * We differentiate between a function handler for the guest and one for the host.
671 */
672static DECLCALLBACK(int) svcHostCall (void *,
673 uint32_t u32Function,
674 uint32_t cParms,
675 VBOXHGCMSVCPARM paParms[])
676{
677 int rc = VINF_SUCCESS;
678
679 LogRel2(("svcHostCall: fn = %d, cParms = %d, pparms = %d\n",
680 u32Function, cParms, paParms));
681
682 switch (u32Function)
683 {
684 case VBOX_SHARED_CLIPBOARD_HOST_FN_SET_MODE:
685 {
686 LogRel2(("svcCall: VBOX_SHARED_CLIPBOARD_HOST_FN_SET_MODE\n"));
687
688 if (cParms != 1)
689 {
690 rc = VERR_INVALID_PARAMETER;
691 }
692 else if ( paParms[0].type != VBOX_HGCM_SVC_PARM_32BIT /* mode */
693 )
694 {
695 rc = VERR_INVALID_PARAMETER;
696 }
697 else
698 {
699 uint32_t u32Mode = VBOX_SHARED_CLIPBOARD_MODE_OFF;
700
701 rc = VBoxHGCMParmUInt32Get (&paParms[0], &u32Mode);
702
703 /* The setter takes care of invalid values. */
704 vboxSvcClipboardModeSet (u32Mode);
705 }
706 } break;
707
708 default:
709 break;
710 }
711
712 LogRelFlow(("svcHostCall: rc = %Rrc\n", rc));
713 return rc;
714}
715
716/**
717 * SSM descriptor table for the VBOXCLIPBOARDCLIENTDATA structure.
718 */
719static SSMFIELD const g_aClipboardClientDataFields[] =
720{
721 SSMFIELD_ENTRY(VBOXCLIPBOARDCLIENTDATA, u32ClientID), /* for validation purposes */
722 SSMFIELD_ENTRY(VBOXCLIPBOARDCLIENTDATA, fMsgQuit),
723 SSMFIELD_ENTRY(VBOXCLIPBOARDCLIENTDATA, fMsgReadData),
724 SSMFIELD_ENTRY(VBOXCLIPBOARDCLIENTDATA, fMsgFormats),
725 SSMFIELD_ENTRY(VBOXCLIPBOARDCLIENTDATA, u32RequestedFormat),
726 SSMFIELD_ENTRY_TERM()
727};
728
729static DECLCALLBACK(int) svcSaveState(void *, uint32_t u32ClientID, void *pvClient, PSSMHANDLE pSSM)
730{
731 /* If there are any pending requests, they must be completed here. Since
732 * the service is single threaded, there could be only requests
733 * which the service itself has postponed.
734 *
735 * HGCM knows that the state is being saved and that the pfnComplete
736 * calls are just clean ups. These requests are saved by the VMMDev.
737 *
738 * When the state will be restored, these requests will be reissued
739 * by VMMDev. The service therefore must save state as if there were no
740 * pending request.
741 */
742 LogRel2 (("svcSaveState: u32ClientID = %d\n", u32ClientID));
743
744 VBOXCLIPBOARDCLIENTDATA *pClient = (VBOXCLIPBOARDCLIENTDATA *)pvClient;
745
746 /* This field used to be the length. We're using it as a version field
747 with the high bit set. */
748 SSMR3PutU32 (pSSM, UINT32_C (0x80000002));
749 int rc = SSMR3PutStructEx (pSSM, pClient, sizeof(*pClient), 0 /*fFlags*/, &g_aClipboardClientDataFields[0], NULL);
750 AssertRCReturn (rc, rc);
751
752 if (pClient->fAsync)
753 {
754 g_pHelpers->pfnCallComplete (pClient->async.callHandle, VINF_SUCCESS /* error code is not important here. */);
755 pClient->fAsync = false;
756 }
757
758 vboxSvcClipboardCompleteReadData (pClient, VINF_SUCCESS, 0);
759
760 return VINF_SUCCESS;
761}
762
763/**
764 * This structure corresponds to the original layout of the
765 * VBOXCLIPBOARDCLIENTDATA structure. As the structure was saved as a whole
766 * when saving state, we need to remember it forever in order to preserve
767 * compatibility.
768 *
769 * (Starting with 3.1 this is no longer used.)
770 *
771 * @remarks Putting this outside svcLoadState to avoid visibility warning caused
772 * by -Wattributes.
773 */
774typedef struct CLIPSAVEDSTATEDATA
775{
776 struct CLIPSAVEDSTATEDATA *pNext;
777 struct CLIPSAVEDSTATEDATA *pPrev;
778
779 VBOXCLIPBOARDCONTEXT *pCtx;
780
781 uint32_t u32ClientID;
782
783 bool fAsync: 1; /* Guest is waiting for a message. */
784
785 bool fMsgQuit: 1;
786 bool fMsgReadData: 1;
787 bool fMsgFormats: 1;
788
789 struct {
790 VBOXHGCMCALLHANDLE callHandle;
791 VBOXHGCMSVCPARM *paParms;
792 } async;
793
794 struct {
795 void *pv;
796 uint32_t cb;
797 uint32_t u32Format;
798 } data;
799
800 uint32_t u32AvailableFormats;
801 uint32_t u32RequestedFormat;
802
803} CLIPSAVEDSTATEDATA;
804
805static DECLCALLBACK(int) svcLoadState(void *, uint32_t u32ClientID, void *pvClient, PSSMHANDLE pSSM)
806{
807 LogRel2 (("svcLoadState: u32ClientID = %d\n", u32ClientID));
808
809 VBOXCLIPBOARDCLIENTDATA *pClient = (VBOXCLIPBOARDCLIENTDATA *)pvClient;
810
811 /* Existing client can not be in async state yet. */
812 Assert (!pClient->fAsync);
813
814 /* Save the client ID for data validation. */
815 /** @todo isn't this the same as u32ClientID? Playing safe for now... */
816 uint32_t const u32ClientIDOld = pClient->u32ClientID;
817
818 /* Restore the client data. */
819 uint32_t lenOrVer;
820 int rc = SSMR3GetU32 (pSSM, &lenOrVer);
821 AssertRCReturn (rc, rc);
822 if (lenOrVer == UINT32_C (0x80000002))
823 {
824 rc = SSMR3GetStructEx (pSSM, pClient, sizeof(*pClient), 0 /*fFlags*/, &g_aClipboardClientDataFields[0], NULL);
825 AssertRCReturn (rc, rc);
826 }
827 else if (lenOrVer == (SSMR3HandleHostBits (pSSM) == 64 ? 72 : 48))
828 {
829 /**
830 * SSM descriptor table for the CLIPSAVEDSTATEDATA structure.
831 */
832 static SSMFIELD const s_aClipSavedStateDataFields30[] =
833 {
834 SSMFIELD_ENTRY_IGN_HCPTR( CLIPSAVEDSTATEDATA, pNext),
835 SSMFIELD_ENTRY_IGN_HCPTR( CLIPSAVEDSTATEDATA, pPrev),
836 SSMFIELD_ENTRY_IGN_HCPTR( CLIPSAVEDSTATEDATA, pCtx),
837 SSMFIELD_ENTRY( CLIPSAVEDSTATEDATA, u32ClientID),
838 SSMFIELD_ENTRY_CUSTOM(fMsgQuit+fMsgReadData+fMsgFormats, RT_OFFSETOF(CLIPSAVEDSTATEDATA, u32ClientID) + 4, 4),
839 SSMFIELD_ENTRY_IGN_HCPTR( CLIPSAVEDSTATEDATA, async.callHandle),
840 SSMFIELD_ENTRY_IGN_HCPTR( CLIPSAVEDSTATEDATA, async.paParms),
841 SSMFIELD_ENTRY_IGNORE( CLIPSAVEDSTATEDATA, data.pv),
842 SSMFIELD_ENTRY_IGNORE( CLIPSAVEDSTATEDATA, data.cb),
843 SSMFIELD_ENTRY_IGNORE( CLIPSAVEDSTATEDATA, data.u32Format),
844 SSMFIELD_ENTRY_IGNORE( CLIPSAVEDSTATEDATA, u32AvailableFormats),
845 SSMFIELD_ENTRY( CLIPSAVEDSTATEDATA, u32RequestedFormat),
846 SSMFIELD_ENTRY_TERM()
847 };
848
849 CLIPSAVEDSTATEDATA savedState;
850 RT_ZERO (savedState);
851 rc = SSMR3GetStructEx (pSSM, &savedState, sizeof(savedState), SSMSTRUCT_FLAGS_MEM_BAND_AID,
852 &s_aClipSavedStateDataFields30[0], NULL);
853 AssertRCReturn (rc, rc);
854
855 pClient->fMsgQuit = savedState.fMsgQuit;
856 pClient->fMsgReadData = savedState.fMsgReadData;
857 pClient->fMsgFormats = savedState.fMsgFormats;
858 pClient->u32RequestedFormat = savedState.u32RequestedFormat;
859 }
860 else
861 {
862 LogRel (("Client data size mismatch: got %#x\n", lenOrVer));
863 return VERR_SSM_DATA_UNIT_FORMAT_CHANGED;
864 }
865
866 /* Verify the client ID. */
867 if (pClient->u32ClientID != u32ClientIDOld)
868 {
869 LogRel (("Client ID mismatch: expected %d, got %d\n", u32ClientIDOld, pClient->u32ClientID));
870 pClient->u32ClientID = u32ClientIDOld;
871 return VERR_SSM_DATA_UNIT_FORMAT_CHANGED;
872 }
873
874 /* Actual host data are to be reported to guest (SYNC). */
875 vboxClipboardSync (pClient);
876
877 return VINF_SUCCESS;
878}
879
880static DECLCALLBACK(int) extCallback (uint32_t u32Function, uint32_t u32Format, void *pvData, uint32_t cbData)
881{
882 if (g_pClient != NULL)
883 {
884 switch (u32Function)
885 {
886 case VBOX_CLIPBOARD_EXT_FN_FORMAT_ANNOUNCE:
887 {
888 LogRelFlow(("ANNOUNCE: g_fReadingData = %d\n", g_fReadingData));
889 if (g_fReadingData)
890 {
891 g_fDelayedAnnouncement = true;
892 g_u32DelayedFormats = u32Format;
893 }
894 else
895 {
896 vboxSvcClipboardReportMsg (g_pClient, VBOX_SHARED_CLIPBOARD_HOST_MSG_FORMATS, u32Format);
897 }
898 } break;
899
900 case VBOX_CLIPBOARD_EXT_FN_DATA_READ:
901 {
902 vboxSvcClipboardReportMsg (g_pClient, VBOX_SHARED_CLIPBOARD_HOST_MSG_READ_DATA, u32Format);
903 } break;
904
905 default:
906 return VERR_NOT_SUPPORTED;
907 }
908 }
909
910 return VINF_SUCCESS;
911}
912
913static DECLCALLBACK(int) svcRegisterExtension(void *, PFNHGCMSVCEXT pfnExtension, void *pvExtension)
914{
915 LogRelFlowFunc(("pfnExtension = %p\n", pfnExtension));
916
917 VBOXCLIPBOARDEXTPARMS parms;
918
919 if (pfnExtension)
920 {
921 /* Install extension. */
922 g_pfnExtension = pfnExtension;
923 g_pvExtension = pvExtension;
924
925 parms.u.pfnCallback = extCallback;
926 g_pfnExtension (g_pvExtension, VBOX_CLIPBOARD_EXT_FN_SET_CALLBACK, &parms, sizeof (parms));
927 }
928 else
929 {
930 if (g_pfnExtension)
931 {
932 parms.u.pfnCallback = NULL;
933 g_pfnExtension (g_pvExtension, VBOX_CLIPBOARD_EXT_FN_SET_CALLBACK, &parms, sizeof (parms));
934 }
935
936 /* Uninstall extension. */
937 g_pfnExtension = NULL;
938 g_pvExtension = NULL;
939 }
940
941 return VINF_SUCCESS;
942}
943
944extern "C" DECLCALLBACK(DECLEXPORT(int)) VBoxHGCMSvcLoad (VBOXHGCMSVCFNTABLE *ptable)
945{
946 int rc = VINF_SUCCESS;
947
948 LogRelFlowFunc(("ptable = %p\n", ptable));
949
950 if (!ptable)
951 {
952 rc = VERR_INVALID_PARAMETER;
953 }
954 else
955 {
956 LogRel2(("VBoxHGCMSvcLoad: ptable->cbSize = %d, ptable->u32Version = 0x%08X\n", ptable->cbSize, ptable->u32Version));
957
958 if ( ptable->cbSize != sizeof (VBOXHGCMSVCFNTABLE)
959 || ptable->u32Version != VBOX_HGCM_SVC_VERSION)
960 {
961 rc = VERR_INVALID_PARAMETER;
962 }
963 else
964 {
965 g_pHelpers = ptable->pHelpers;
966
967 ptable->cbClient = sizeof (VBOXCLIPBOARDCLIENTDATA);
968
969 ptable->pfnUnload = svcUnload;
970 ptable->pfnConnect = svcConnect;
971 ptable->pfnDisconnect = svcDisconnect;
972 ptable->pfnCall = svcCall;
973 ptable->pfnHostCall = svcHostCall;
974 ptable->pfnSaveState = svcSaveState;
975 ptable->pfnLoadState = svcLoadState;
976 ptable->pfnRegisterExtension = svcRegisterExtension;
977 ptable->pvService = NULL;
978
979 /* Service specific initialization. */
980 rc = svcInit ();
981 }
982 }
983
984 return rc;
985}
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