VirtualBox

source: vbox/trunk/src/VBox/HostServices/DragAndDrop/service.cpp@ 59083

Last change on this file since 59083 was 58329, checked in by vboxsync, 9 years ago

DnD: Updates/bugfixes:

  • Added separate VBOXDNDDISCONNECTMSG message for letting Main know about client disconnects.
  • Various cleanups and bugfixes.
  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 42.5 KB
Line 
1/* $Id: service.cpp 58329 2015-10-20 10:05:12Z vboxsync $ */
2/** @file
3 * Drag and Drop Service.
4 */
5
6/*
7 * Copyright (C) 2011-2015 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/** @page pg_svc_guest_control Drag and drop HGCM Service
19 *
20 * TODO
21 */
22
23
24/*********************************************************************************************************************************
25* Header Files *
26*********************************************************************************************************************************/
27#ifdef LOG_GROUP
28 #undef LOG_GROUP
29#endif
30#define LOG_GROUP LOG_GROUP_GUEST_DND
31
32#include <algorithm>
33#include <list>
34#include <map>
35
36#include <VBox/GuestHost/DragAndDrop.h>
37#include <VBox/HostServices/Service.h>
38#include <VBox/HostServices/DragAndDropSvc.h>
39
40#include "dndmanager.h"
41
42using namespace DragAndDropSvc;
43
44/*********************************************************************************************************************************
45* Service class declaration *
46*********************************************************************************************************************************/
47
48class DragAndDropClient : public HGCM::Client
49{
50public:
51
52 DragAndDropClient(uint32_t uClientId, VBOXHGCMCALLHANDLE hHandle = NULL,
53 uint32_t uMsg = 0, uint32_t cParms = 0, VBOXHGCMSVCPARM aParms[] = NULL)
54 : HGCM::Client(uClientId, hHandle, uMsg, cParms, aParms)
55 , m_fDeferred(false)
56 {
57 RT_ZERO(m_SvcCtx);
58 }
59
60 virtual ~DragAndDropClient(void)
61 {
62 disconnect();
63 }
64
65public:
66
67 void complete(VBOXHGCMCALLHANDLE hHandle, int rcOp);
68 void completeDeferred(int rcOp);
69 void disconnect(void);
70 bool isDeferred(void) const { return m_fDeferred; }
71 void setDeferred(VBOXHGCMCALLHANDLE hHandle, uint32_t u32Function, uint32_t cParms, VBOXHGCMSVCPARM paParms[]);
72 void setSvcContext(const HGCM::VBOXHGCMSVCTX &SvcCtx) { m_SvcCtx = SvcCtx; }
73
74protected:
75
76 /** The HGCM service context this client is bound to. */
77 HGCM::VBOXHGCMSVCTX m_SvcCtx;
78 /** Flag indicating whether this client currently is deferred mode,
79 * meaning that it did not return to the caller yet. */
80 bool m_fDeferred;
81};
82
83/** Map holding pointers to drag and drop clients. Key is the (unique) HGCM client ID. */
84typedef std::map<uint32_t, DragAndDropClient*> DnDClientMap;
85
86/** Simple queue (list) which holds deferred (waiting) clients. */
87typedef std::list<uint32_t> DnDClientQueue;
88
89/**
90 * Specialized drag & drop service class.
91 */
92class DragAndDropService : public HGCM::AbstractService<DragAndDropService>
93{
94public:
95
96 explicit DragAndDropService(PVBOXHGCMSVCHELPERS pHelpers)
97 : HGCM::AbstractService<DragAndDropService>(pHelpers)
98 , m_pManager(NULL) {}
99
100protected:
101
102 int init(VBOXHGCMSVCFNTABLE *pTable);
103 int uninit(void);
104 int clientConnect(uint32_t u32ClientID, void *pvClient);
105 int clientDisconnect(uint32_t u32ClientID, void *pvClient);
106 void guestCall(VBOXHGCMCALLHANDLE callHandle, uint32_t u32ClientID, void *pvClient, uint32_t u32Function, uint32_t cParms, VBOXHGCMSVCPARM paParms[]);
107 int hostCall(uint32_t u32Function, uint32_t cParms, VBOXHGCMSVCPARM paParms[]);
108
109 int modeSet(uint32_t u32Mode);
110 inline uint32_t modeGet(void) const { return m_u32Mode; };
111
112protected:
113
114 static DECLCALLBACK(int) progressCallback(uint32_t uStatus, uint32_t uPercentage, int rc, void *pvUser);
115
116protected:
117
118 /** Pointer to our DnD manager instance. */
119 DnDManager *m_pManager;
120 /** Map of all connected clients.
121 * The primary key is the (unique) client ID, the secondary value
122 * an allocated pointer to the DragAndDropClient class, managed
123 * by this service class. */
124 DnDClientMap m_clientMap;
125 /** List of all clients which are queued up (deferred return) and ready
126 * to process new commands. The key is the (unique) client ID. */
127 DnDClientQueue m_clientQueue;
128 /** Current drag and drop mode. */
129 uint32_t m_u32Mode;
130};
131
132
133/*********************************************************************************************************************************
134* Client implementation *
135*********************************************************************************************************************************/
136
137/**
138 * Completes the call by returning the control back to the guest
139 * side code.
140 */
141void DragAndDropClient::complete(VBOXHGCMCALLHANDLE hHandle, int rcOp)
142{
143 LogFlowThisFunc(("uClientID=%RU32\n", m_uClientId));
144
145 if ( m_SvcCtx.pHelpers
146 && m_SvcCtx.pHelpers->pfnCallComplete)
147 {
148 m_SvcCtx.pHelpers->pfnCallComplete(hHandle, rcOp);
149 }
150}
151
152/**
153 * Completes a deferred call by returning the control back to the guest
154 * side code.
155 */
156void DragAndDropClient::completeDeferred(int rcOp)
157{
158 AssertMsg(m_fDeferred, ("Client %RU32 is not in deferred mode\n", m_uClientId));
159 Assert(m_hHandle != NULL);
160
161 LogFlowThisFunc(("uClientID=%RU32\n", m_uClientId));
162
163 complete(m_hHandle, rcOp);
164 m_fDeferred = false;
165}
166
167/**
168 * Called when the HGCM client disconnected on the guest side.
169 * This function takes care of the client's data cleanup and also lets the host
170 * know that the client has been disconnected.
171 *
172 */
173void DragAndDropClient::disconnect(void)
174{
175 LogFlowThisFunc(("uClient=%RU32\n", m_uClientId));
176
177 if (isDeferred())
178 completeDeferred(VERR_INTERRUPTED);
179
180 /*
181 * Let the host know.
182 */
183 VBOXDNDCBDISCONNECTMSGDATA data;
184 RT_ZERO(data);
185 /** @todo Magic needed? */
186
187 if (m_SvcCtx.pfnHostCallback)
188 {
189 int rc2 = m_SvcCtx.pfnHostCallback(m_SvcCtx.pvHostData, GUEST_DND_DISCONNECT, &data, sizeof(data));
190 if (RT_FAILURE(rc2))
191 LogFlowFunc(("Warning: Unable to notify host about client %RU32 disconnect, rc=%Rrc\n", m_uClientId, rc2));
192 /* Not fatal. */
193 }
194}
195
196/**
197 * Set the client's status to deferred, meaning that it does not return to the caller
198 * on the guest side yet.
199 */
200void DragAndDropClient::setDeferred(VBOXHGCMCALLHANDLE hHandle, uint32_t u32Function, uint32_t cParms, VBOXHGCMSVCPARM paParms[])
201{
202 LogFlowThisFunc(("uClient=%RU32\n", m_uClientId));
203
204 AssertMsg(m_fDeferred == false, ("Client already in deferred mode\n"));
205 m_fDeferred = true;
206
207 m_hHandle = hHandle;
208 m_uMsg = u32Function;
209 m_cParms = cParms;
210 m_paParms = paParms;
211}
212
213/*********************************************************************************************************************************
214* Service class implementation *
215*********************************************************************************************************************************/
216
217int DragAndDropService::init(VBOXHGCMSVCFNTABLE *pTable)
218{
219 /* Register functions. */
220 pTable->pfnHostCall = svcHostCall;
221 pTable->pfnSaveState = NULL; /* The service is stateless, so the normal */
222 pTable->pfnLoadState = NULL; /* construction done before restoring suffices */
223 pTable->pfnRegisterExtension = svcRegisterExtension;
224
225 /* Drag'n drop mode is disabled by default. */
226 modeSet(VBOX_DRAG_AND_DROP_MODE_OFF);
227
228 int rc = VINF_SUCCESS;
229
230 try
231 {
232 m_pManager = new DnDManager(&DragAndDropService::progressCallback, this);
233 }
234 catch(std::bad_alloc &)
235 {
236 rc = VERR_NO_MEMORY;
237 }
238
239 LogFlowFuncLeaveRC(rc);
240 return rc;
241}
242
243int DragAndDropService::uninit(void)
244{
245 LogFlowFuncEnter();
246
247 if (m_pManager)
248 {
249 delete m_pManager;
250 m_pManager = NULL;
251 }
252
253 DnDClientMap::iterator itClient = m_clientMap.begin();
254 while (itClient != m_clientMap.end())
255 {
256 delete itClient->second;
257 m_clientMap.erase(itClient);
258 itClient = m_clientMap.begin();
259 }
260
261 LogFlowFuncLeave();
262 return VINF_SUCCESS;
263}
264
265int DragAndDropService::clientConnect(uint32_t u32ClientID, void *pvClient)
266{
267 if (m_clientMap.size() >= UINT8_MAX) /* Don't allow too much clients at the same time. */
268 {
269 AssertMsgFailed(("Maximum number of clients reached\n"));
270 return VERR_MAX_PROCS_REACHED;
271 }
272
273 int rc = VINF_SUCCESS;
274
275 /*
276 * Add client to our client map.
277 */
278 if (m_clientMap.find(u32ClientID) != m_clientMap.end())
279 rc = VERR_ALREADY_EXISTS;
280
281 if (RT_SUCCESS(rc))
282 {
283 try
284 {
285 DragAndDropClient *pClient = new DragAndDropClient(u32ClientID);
286 pClient->setSvcContext(m_SvcCtx);
287 m_clientMap[u32ClientID] = pClient;
288 }
289 catch(std::bad_alloc &)
290 {
291 rc = VERR_NO_MEMORY;
292 }
293
294 if (RT_SUCCESS(rc))
295 {
296 /*
297 * Clear the message queue as soon as a new clients connect
298 * to ensure that every client has the same state.
299 */
300 if (m_pManager)
301 m_pManager->clear();
302 }
303 }
304
305 LogFlowFunc(("Client %RU32 connected, rc=%Rrc\n", u32ClientID, rc));
306 return rc;
307}
308
309int DragAndDropService::clientDisconnect(uint32_t u32ClientID, void *pvClient)
310{
311 /* Client not found? Bail out early. */
312 DnDClientMap::iterator itClient = m_clientMap.find(u32ClientID);
313 if (itClient == m_clientMap.end())
314 return VERR_NOT_FOUND;
315
316 /*
317 * Remove from waiters queue.
318 */
319 m_clientQueue.remove(u32ClientID);
320
321 /*
322 * Remove from client map and deallocate.
323 */
324 AssertPtr(itClient->second);
325 delete itClient->second;
326
327 m_clientMap.erase(itClient);
328
329 LogFlowFunc(("Client %RU32 disconnected\n", u32ClientID));
330 return VINF_SUCCESS;
331}
332
333int DragAndDropService::modeSet(uint32_t u32Mode)
334{
335#ifndef VBOX_WITH_DRAG_AND_DROP_GH
336 if ( u32Mode == VBOX_DRAG_AND_DROP_MODE_GUEST_TO_HOST
337 || u32Mode == VBOX_DRAG_AND_DROP_MODE_BIDIRECTIONAL)
338 {
339 m_u32Mode = VBOX_DRAG_AND_DROP_MODE_OFF;
340 return VERR_NOT_SUPPORTED;
341 }
342#endif
343
344 switch (u32Mode)
345 {
346 case VBOX_DRAG_AND_DROP_MODE_OFF:
347 case VBOX_DRAG_AND_DROP_MODE_HOST_TO_GUEST:
348 case VBOX_DRAG_AND_DROP_MODE_GUEST_TO_HOST:
349 case VBOX_DRAG_AND_DROP_MODE_BIDIRECTIONAL:
350 m_u32Mode = u32Mode;
351 break;
352
353 default:
354 m_u32Mode = VBOX_DRAG_AND_DROP_MODE_OFF;
355 break;
356 }
357
358 return VINF_SUCCESS;
359}
360
361void DragAndDropService::guestCall(VBOXHGCMCALLHANDLE callHandle, uint32_t u32ClientID,
362 void *pvClient, uint32_t u32Function,
363 uint32_t cParms, VBOXHGCMSVCPARM paParms[])
364{
365 LogFlowFunc(("u32ClientID=%RU32, u32Function=%RU32, cParms=%RU32\n",
366 u32ClientID, u32Function, cParms));
367
368 /* Check if we've the right mode set. */
369 int rc = VERR_ACCESS_DENIED; /* Play safe. */
370 switch (u32Function)
371 {
372 case GUEST_DND_GET_NEXT_HOST_MSG:
373 {
374 if (modeGet() != VBOX_DRAG_AND_DROP_MODE_OFF)
375 {
376 rc = VINF_SUCCESS;
377 }
378 else
379 {
380 LogFlowFunc(("DnD disabled, deferring request\n"));
381 rc = VINF_HGCM_ASYNC_EXECUTE;
382 }
383 break;
384 }
385
386 /* New since protocol v2. */
387 case GUEST_DND_CONNECT:
388 {
389 /*
390 * Never block the initial connect call, as the clients do this when
391 * initializing and might get stuck if drag and drop is set to "disabled" at
392 * that time.
393 */
394 rc = VINF_SUCCESS;
395 break;
396 }
397 case GUEST_DND_HG_ACK_OP:
398 /* Fall through is intentional. */
399 case GUEST_DND_HG_REQ_DATA:
400 /* Fall through is intentional. */
401 case GUEST_DND_HG_EVT_PROGRESS:
402 {
403 if ( modeGet() == VBOX_DRAG_AND_DROP_MODE_BIDIRECTIONAL
404 || modeGet() == VBOX_DRAG_AND_DROP_MODE_HOST_TO_GUEST)
405 {
406 rc = VINF_SUCCESS;
407 }
408 else
409 LogFlowFunc(("Host -> Guest DnD mode disabled, ignoring request\n"));
410 break;
411 }
412
413 case GUEST_DND_GH_ACK_PENDING:
414 case GUEST_DND_GH_SND_DATA_HDR:
415 case GUEST_DND_GH_SND_DATA:
416 case GUEST_DND_GH_SND_DIR:
417 case GUEST_DND_GH_SND_FILE_HDR:
418 case GUEST_DND_GH_SND_FILE_DATA:
419 case GUEST_DND_GH_EVT_ERROR:
420 {
421#ifdef VBOX_WITH_DRAG_AND_DROP_GH
422 if ( modeGet() == VBOX_DRAG_AND_DROP_MODE_BIDIRECTIONAL
423 || modeGet() == VBOX_DRAG_AND_DROP_MODE_GUEST_TO_HOST)
424 {
425 rc = VINF_SUCCESS;
426 }
427 else
428#endif
429 LogFlowFunc(("Guest -> Host DnD mode disabled, ignoring request\n"));
430 break;
431 }
432
433 default:
434 /* Reach through to DnD manager. */
435 rc = VINF_SUCCESS;
436 break;
437 }
438
439#ifdef DEBUG_andy
440 LogFlowFunc(("Mode (%RU32) check rc=%Rrc\n", modeGet(), rc));
441#endif
442
443#define DO_HOST_CALLBACK(); \
444 if ( RT_SUCCESS(rc) \
445 && m_SvcCtx.pfnHostCallback) \
446 { \
447 rc = m_SvcCtx.pfnHostCallback(m_SvcCtx.pvHostData, u32Function, &data, sizeof(data)); \
448 }
449
450 /*
451 * Lookup client.
452 */
453 DragAndDropClient *pClient = NULL;
454
455 DnDClientMap::iterator itClient = m_clientMap.find(u32ClientID);
456 if (itClient != m_clientMap.end())
457 {
458 pClient = itClient->second;
459 AssertPtr(pClient);
460 }
461 else
462 {
463 LogFunc(("Client %RU32 was not found\n", u32ClientID));
464 rc = VERR_NOT_FOUND;
465 }
466
467 if (rc == VINF_SUCCESS) /* Note: rc might be VINF_HGCM_ASYNC_EXECUTE! */
468 {
469 LogFlowFunc(("Client %RU32: Protocol v%RU32\n", pClient->clientId(), pClient->protocol()));
470
471 rc = VERR_INVALID_PARAMETER; /* Play safe. */
472
473 switch (u32Function)
474 {
475 /*
476 * Note: Older VBox versions with enabled DnD guest->host support (< 5.0)
477 * used the same message ID (300) for GUEST_DND_GET_NEXT_HOST_MSG and
478 * HOST_DND_GH_REQ_PENDING, which led this service returning
479 * VERR_INVALID_PARAMETER when the guest wanted to actually
480 * handle HOST_DND_GH_REQ_PENDING.
481 */
482 case GUEST_DND_GET_NEXT_HOST_MSG:
483 {
484 LogFlowFunc(("GUEST_DND_GET_NEXT_HOST_MSG\n"));
485 if (cParms == 3)
486 {
487 rc = m_pManager->nextMessageInfo(&paParms[0].u.uint32 /* uMsg */, &paParms[1].u.uint32 /* cParms */);
488 if (RT_FAILURE(rc)) /* No queued messages available? */
489 {
490 if (m_SvcCtx.pfnHostCallback) /* Try asking the host. */
491 {
492 VBOXDNDCBHGGETNEXTHOSTMSG data;
493 RT_ZERO(data);
494 data.hdr.uMagic = CB_MAGIC_DND_HG_GET_NEXT_HOST_MSG;
495 rc = m_SvcCtx.pfnHostCallback(m_SvcCtx.pvHostData, u32Function, &data, sizeof(data));
496 if (RT_SUCCESS(rc))
497 {
498 paParms[0].u.uint32 = data.uMsg; /* uMsg */
499 paParms[1].u.uint32 = data.cParms; /* cParms */
500 /* Note: paParms[2] was set by the guest as blocking flag. */
501 }
502 }
503 else /* No host callback in place, so drag and drop is not supported by the host. */
504 rc = VERR_NOT_SUPPORTED;
505
506 if (RT_FAILURE(rc))
507 rc = m_pManager->nextMessage(u32Function, cParms, paParms);
508
509 /* Some error occurred or no (new) messages available? */
510 if (RT_FAILURE(rc))
511 {
512 uint32_t fFlags = 0;
513 int rc2 = paParms[2].getUInt32(&fFlags);
514 if ( RT_SUCCESS(rc2)
515 && fFlags) /* Blocking flag set? */
516 {
517 /* Defer client returning. */
518 rc = VINF_HGCM_ASYNC_EXECUTE;
519 }
520 else
521 rc = VERR_INVALID_PARAMETER;
522
523 LogFlowFunc(("Message queue is empty, returning %Rrc to guest\n", rc));
524 }
525 }
526 }
527 break;
528 }
529 case GUEST_DND_CONNECT:
530 {
531 LogFlowFunc(("GUEST_DND_CONNECT\n"));
532 if (cParms >= 2)
533 {
534 const uint8_t idxProto = cParms >= 3 ? 1 : 0;
535
536 VBOXDNDCBCONNECTMSGDATA data;
537 RT_ZERO(data);
538 data.hdr.uMagic = CB_MAGIC_DND_CONNECT;
539 if (cParms >= 3)
540 rc = paParms[0].getUInt32(&data.hdr.uContextID);
541 else /* Older protocols don't have a context ID. */
542 rc = VINF_SUCCESS;
543 if (RT_SUCCESS(rc))
544 rc = paParms[idxProto].getUInt32(&data.uProtocol);
545 if (RT_SUCCESS(rc))
546 rc = paParms[idxProto + 1].getUInt32(&data.uFlags);
547 if (RT_SUCCESS(rc))
548 rc = pClient->setProtocol(data.uProtocol);
549 if (RT_SUCCESS(rc))
550 {
551 LogFlowFunc(("Client %RU32 is now using protocol v%RU32\n", pClient->clientId(), pClient->protocol()));
552 DO_HOST_CALLBACK();
553 }
554 }
555 break;
556 }
557 case GUEST_DND_HG_ACK_OP:
558 {
559 LogFlowFunc(("GUEST_DND_HG_ACK_OP\n"));
560
561 VBOXDNDCBHGACKOPDATA data;
562 RT_ZERO(data);
563 data.hdr.uMagic = CB_MAGIC_DND_HG_ACK_OP;
564
565 switch (pClient->protocol())
566 {
567 case 3:
568 {
569 if (cParms == 2)
570 {
571 rc = paParms[0].getUInt32(&data.hdr.uContextID);
572 if (RT_SUCCESS(rc))
573 rc = paParms[1].getUInt32(&data.uAction); /* Get drop action. */
574 }
575 break;
576 }
577
578 case 2:
579 default:
580 {
581 if (cParms == 1)
582 rc = paParms[0].getUInt32(&data.uAction); /* Get drop action. */
583 break;
584 }
585 }
586
587 DO_HOST_CALLBACK();
588 break;
589 }
590 case GUEST_DND_HG_REQ_DATA:
591 {
592 LogFlowFunc(("GUEST_DND_HG_REQ_DATA\n"));
593
594 VBOXDNDCBHGREQDATADATA data;
595 RT_ZERO(data);
596 data.hdr.uMagic = CB_MAGIC_DND_HG_REQ_DATA;
597
598 switch (pClient->protocol())
599 {
600 case 3:
601 {
602 if (cParms == 3)
603 {
604 rc = paParms[0].getUInt32(&data.hdr.uContextID);
605 if (RT_SUCCESS(rc))
606 rc = paParms[1].getPointer((void **)&data.pszFormat, &data.cbFormat);
607 if (RT_SUCCESS(rc))
608 rc = paParms[2].getUInt32(&data.cbFormat);
609 }
610 break;
611 }
612
613 case 2:
614 default:
615 {
616 if (cParms == 1)
617 rc = paParms[0].getPointer((void**)&data.pszFormat, &data.cbFormat);
618 break;
619 }
620 }
621
622 DO_HOST_CALLBACK();
623 break;
624 }
625 case GUEST_DND_HG_EVT_PROGRESS:
626 {
627 LogFlowFunc(("GUEST_DND_HG_EVT_PROGRESS\n"));
628
629 VBOXDNDCBHGEVTPROGRESSDATA data;
630 RT_ZERO(data);
631 data.hdr.uMagic = CB_MAGIC_DND_HG_EVT_PROGRESS;
632
633 switch (pClient->protocol())
634 {
635 case 3:
636 {
637 if (cParms == 4)
638 {
639 rc = paParms[0].getUInt32(&data.uStatus);
640 if (RT_SUCCESS(rc))
641 rc = paParms[1].getUInt32(&data.uStatus);
642 if (RT_SUCCESS(rc))
643 rc = paParms[2].getUInt32(&data.uPercentage);
644 if (RT_SUCCESS(rc))
645 rc = paParms[3].getUInt32(&data.rc);
646 }
647 break;
648 }
649
650 case 2:
651 default:
652 {
653 if (cParms == 3)
654 {
655 rc = paParms[0].getUInt32(&data.uStatus);
656 if (RT_SUCCESS(rc))
657 rc = paParms[1].getUInt32(&data.uPercentage);
658 if (RT_SUCCESS(rc))
659 rc = paParms[2].getUInt32(&data.rc);
660 }
661 break;
662 }
663 }
664
665 DO_HOST_CALLBACK();
666 break;
667 }
668#ifdef VBOX_WITH_DRAG_AND_DROP_GH
669 case GUEST_DND_GH_ACK_PENDING:
670 {
671 LogFlowFunc(("GUEST_DND_GH_ACK_PENDING\n"));
672
673 VBOXDNDCBGHACKPENDINGDATA data;
674 RT_ZERO(data);
675 data.hdr.uMagic = CB_MAGIC_DND_GH_ACK_PENDING;
676
677 switch (pClient->protocol())
678 {
679 case 3:
680 {
681 if (cParms == 5)
682 {
683 rc = paParms[0].getUInt32(&data.hdr.uContextID);
684 if (RT_SUCCESS(rc))
685 rc = paParms[1].getUInt32(&data.uDefAction);
686 if (RT_SUCCESS(rc))
687 rc = paParms[2].getUInt32(&data.uAllActions);
688 if (RT_SUCCESS(rc))
689 rc = paParms[3].getPointer((void**)&data.pszFormat, &data.cbFormat);
690 if (RT_SUCCESS(rc))
691 rc = paParms[4].getUInt32(&data.cbFormat);
692 }
693 break;
694 }
695
696 case 2:
697 default:
698 {
699 if (cParms == 3)
700 {
701 rc = paParms[0].getUInt32(&data.uDefAction);
702 if (RT_SUCCESS(rc))
703 rc = paParms[1].getUInt32(&data.uAllActions);
704 if (RT_SUCCESS(rc))
705 rc = paParms[2].getPointer((void**)&data.pszFormat, &data.cbFormat);
706 }
707 break;
708 }
709 }
710
711 DO_HOST_CALLBACK();
712 break;
713 }
714 /* New since protocol v3. */
715 case GUEST_DND_GH_SND_DATA_HDR:
716 {
717 LogFlowFunc(("GUEST_DND_GH_SND_DATA_HDR\n"));
718 if (cParms == 12)
719 {
720 VBOXDNDCBSNDDATAHDRDATA data;
721 RT_ZERO(data);
722 data.hdr.uMagic = CB_MAGIC_DND_GH_SND_DATA_HDR;
723 rc = paParms[0].getUInt32(&data.hdr.uContextID);
724 if (RT_SUCCESS(rc))
725 rc = paParms[1].getUInt32(&data.data.uFlags);
726 if (RT_SUCCESS(rc))
727 rc = paParms[2].getUInt32(&data.data.uScreenId);
728 if (RT_SUCCESS(rc))
729 rc = paParms[3].getUInt64(&data.data.cbTotal);
730 if (RT_SUCCESS(rc))
731 rc = paParms[4].getUInt32(&data.data.cbMeta);
732 if (RT_SUCCESS(rc))
733 rc = paParms[5].getPointer(&data.data.pvMetaFmt, &data.data.cbMetaFmt);
734 if (RT_SUCCESS(rc))
735 rc = paParms[6].getUInt32(&data.data.cbMetaFmt);
736 if (RT_SUCCESS(rc))
737 rc = paParms[7].getUInt64(&data.data.cObjects);
738 if (RT_SUCCESS(rc))
739 rc = paParms[8].getUInt32(&data.data.enmCompression);
740 if (RT_SUCCESS(rc))
741 rc = paParms[9].getUInt32((uint32_t *)&data.data.enmChecksumType);
742 if (RT_SUCCESS(rc))
743 rc = paParms[10].getPointer(&data.data.pvChecksum, &data.data.cbChecksum);
744 if (RT_SUCCESS(rc))
745 rc = paParms[11].getUInt32(&data.data.cbChecksum);
746
747 LogFlowFunc(("fFlags=0x%x, cbTotalSize=%RU64, cObj=%RU64\n",
748 data.data.uFlags, data.data.cbTotal, data.data.cObjects));
749 DO_HOST_CALLBACK();
750 }
751 break;
752 }
753 case GUEST_DND_GH_SND_DATA:
754 {
755 LogFlowFunc(("GUEST_DND_GH_SND_DATA\n"));
756 switch (pClient->protocol())
757 {
758 case 3:
759 {
760 if (cParms == 5)
761 {
762 VBOXDNDCBSNDDATADATA data;
763 RT_ZERO(data);
764 data.hdr.uMagic = CB_MAGIC_DND_GH_SND_DATA;
765 rc = paParms[0].getUInt32(&data.hdr.uContextID);
766 if (RT_SUCCESS(rc))
767 rc = paParms[1].getPointer((void**)&data.data.u.v3.pvData, &data.data.u.v3.cbData);
768 if (RT_SUCCESS(rc))
769 rc = paParms[2].getUInt32(&data.data.u.v3.cbData);
770 if (RT_SUCCESS(rc))
771 rc = paParms[3].getPointer((void**)&data.data.u.v3.pvChecksum, &data.data.u.v3.cbChecksum);
772 if (RT_SUCCESS(rc))
773 rc = paParms[4].getUInt32(&data.data.u.v3.cbChecksum);
774 DO_HOST_CALLBACK();
775 }
776 break;
777 }
778
779 case 2:
780 default:
781 {
782 if (cParms == 2)
783 {
784 VBOXDNDCBSNDDATADATA data;
785 RT_ZERO(data);
786 data.hdr.uMagic = CB_MAGIC_DND_GH_SND_DATA;
787 rc = paParms[0].getPointer((void**)&data.data.u.v1.pvData, &data.data.u.v1.cbData);
788 if (RT_SUCCESS(rc))
789 rc = paParms[1].getUInt32(&data.data.u.v1.cbTotalSize);
790 DO_HOST_CALLBACK();
791 }
792 break;
793 }
794 }
795 break;
796 }
797 case GUEST_DND_GH_SND_DIR:
798 {
799 LogFlowFunc(("GUEST_DND_GH_SND_DIR\n"));
800
801 VBOXDNDCBSNDDIRDATA data;
802 RT_ZERO(data);
803 data.hdr.uMagic = CB_MAGIC_DND_GH_SND_DIR;
804
805 switch (pClient->protocol())
806 {
807 case 3:
808 {
809 if (cParms == 4)
810 {
811 rc = paParms[0].getUInt32(&data.hdr.uContextID);
812 if (RT_SUCCESS(rc))
813 rc = paParms[1].getPointer((void**)&data.pszPath, &data.cbPath);
814 if (RT_SUCCESS(rc))
815 rc = paParms[2].getUInt32(&data.cbPath);
816 if (RT_SUCCESS(rc))
817 rc = paParms[3].getUInt32(&data.fMode);
818 }
819 break;
820 }
821
822 case 2:
823 default:
824 {
825 if (cParms == 3)
826 {
827 rc = paParms[0].getPointer((void**)&data.pszPath, &data.cbPath);
828 if (RT_SUCCESS(rc))
829 rc = paParms[1].getUInt32(&data.cbPath);
830 if (RT_SUCCESS(rc))
831 rc = paParms[2].getUInt32(&data.fMode);
832 }
833 break;
834 }
835 }
836
837 DO_HOST_CALLBACK();
838 break;
839 }
840 /* New since protocol v2 (>= VBox 5.0). */
841 case GUEST_DND_GH_SND_FILE_HDR:
842 {
843 LogFlowFunc(("GUEST_DND_GH_SND_FILE_HDR\n"));
844 if (cParms == 6)
845 {
846 VBOXDNDCBSNDFILEHDRDATA data;
847 RT_ZERO(data);
848 data.hdr.uMagic = CB_MAGIC_DND_GH_SND_FILE_HDR;
849
850 rc = paParms[0].getUInt32(&data.hdr.uContextID);
851 if (RT_SUCCESS(rc))
852 rc = paParms[1].getPointer((void**)&data.pszFilePath, &data.cbFilePath);
853 if (RT_SUCCESS(rc))
854 rc = paParms[2].getUInt32(&data.cbFilePath);
855 if (RT_SUCCESS(rc))
856 rc = paParms[3].getUInt32(&data.fFlags);
857 if (RT_SUCCESS(rc))
858 rc = paParms[4].getUInt32(&data.fMode);
859 if (RT_SUCCESS(rc))
860 rc = paParms[5].getUInt64(&data.cbSize);
861
862 LogFlowFunc(("pszPath=%s, cbPath=%RU32, fMode=0x%x, cbSize=%RU64\n",
863 data.pszFilePath, data.cbFilePath, data.fMode, data.cbSize));
864 DO_HOST_CALLBACK();
865 }
866 break;
867 }
868 case GUEST_DND_GH_SND_FILE_DATA:
869 {
870 LogFlowFunc(("GUEST_DND_GH_SND_FILE_DATA\n"));
871
872 switch (pClient->protocol())
873 {
874 /* Protocol v3 adds (optional) checksums. */
875 case 3:
876 {
877 if (cParms == 5)
878 {
879 VBOXDNDCBSNDFILEDATADATA data;
880 RT_ZERO(data);
881 data.hdr.uMagic = CB_MAGIC_DND_GH_SND_FILE_DATA;
882
883 rc = paParms[0].getUInt32(&data.hdr.uContextID);
884 if (RT_SUCCESS(rc))
885 rc = paParms[1].getPointer((void**)&data.pvData, &data.cbData);
886 if (RT_SUCCESS(rc))
887 rc = paParms[2].getUInt32(&data.cbData);
888 if (RT_SUCCESS(rc))
889 rc = paParms[3].getPointer((void**)&data.u.v3.pvChecksum, &data.u.v3.cbChecksum);
890 if (RT_SUCCESS(rc))
891 rc = paParms[4].getUInt32(&data.u.v3.cbChecksum);
892
893 LogFlowFunc(("pvData=0x%p, cbData=%RU32\n", data.pvData, data.cbData));
894 DO_HOST_CALLBACK();
895 }
896 break;
897 }
898 /* Protocol v2 only sends the next data chunks to reduce traffic. */
899 case 2:
900 {
901 if (cParms == 3)
902 {
903 VBOXDNDCBSNDFILEDATADATA data;
904 RT_ZERO(data);
905 data.hdr.uMagic = CB_MAGIC_DND_GH_SND_FILE_DATA;
906 rc = paParms[0].getUInt32(&data.hdr.uContextID);
907 if (RT_SUCCESS(rc))
908 rc = paParms[1].getPointer((void**)&data.pvData, &data.cbData);
909 if (RT_SUCCESS(rc))
910 rc = paParms[2].getUInt32(&data.cbData);
911
912 LogFlowFunc(("cbData=%RU32, pvData=0x%p\n", data.cbData, data.pvData));
913 DO_HOST_CALLBACK();
914 }
915 break;
916 }
917 /* Protocol v1 sends the file path and attributes for every file chunk (!). */
918 default:
919 {
920 if (cParms == 5)
921 {
922 VBOXDNDCBSNDFILEDATADATA data;
923 RT_ZERO(data);
924 data.hdr.uMagic = CB_MAGIC_DND_GH_SND_FILE_DATA;
925 uint32_t cTmp;
926 rc = paParms[0].getPointer((void**)&data.u.v1.pszFilePath, &cTmp);
927 if (RT_SUCCESS(rc))
928 rc = paParms[1].getUInt32(&data.u.v1.cbFilePath);
929 if (RT_SUCCESS(rc))
930 rc = paParms[2].getPointer((void**)&data.pvData, &cTmp);
931 if (RT_SUCCESS(rc))
932 rc = paParms[3].getUInt32(&data.cbData);
933 if (RT_SUCCESS(rc))
934 rc = paParms[4].getUInt32(&data.u.v1.fMode);
935
936 LogFlowFunc(("pszFilePath=%s, cbData=%RU32, pvData=0x%p, fMode=0x%x\n",
937 data.u.v1.pszFilePath, data.cbData, data.pvData, data.u.v1.fMode));
938 DO_HOST_CALLBACK();
939 }
940 break;
941 }
942 }
943 break;
944 }
945 case GUEST_DND_GH_EVT_ERROR:
946 {
947 LogFlowFunc(("GUEST_DND_GH_EVT_ERROR\n"));
948
949 VBOXDNDCBEVTERRORDATA data;
950 RT_ZERO(data);
951 data.hdr.uMagic = CB_MAGIC_DND_GH_EVT_ERROR;
952
953 switch (pClient->protocol())
954 {
955 case 3:
956 {
957 if (cParms == 2)
958 {
959 rc = paParms[0].getUInt32(&data.hdr.uContextID);
960 if (RT_SUCCESS(rc))
961 {
962 uint32_t rcOp;
963 rc = paParms[1].getUInt32(&rcOp);
964 if (RT_SUCCESS(rc))
965 data.rc = rcOp;
966 }
967 }
968 break;
969 }
970
971 case 2:
972 default:
973 {
974 if (cParms == 1)
975 {
976 uint32_t rcOp;
977 rc = paParms[0].getUInt32(&rcOp);
978 if (RT_SUCCESS(rc))
979 data.rc = (int32_t)rcOp;
980 }
981 break;
982 }
983 }
984
985 DO_HOST_CALLBACK();
986 break;
987 }
988#endif /* VBOX_WITH_DRAG_AND_DROP_GH */
989 default:
990 {
991 /* All other messages are handled by the DnD manager. */
992 rc = m_pManager->nextMessage(u32Function, cParms, paParms);
993 if (rc == VERR_NO_DATA) /* Manager has no new messsages? Try asking the host. */
994 {
995 if (m_SvcCtx.pfnHostCallback)
996 {
997 VBOXDNDCBHGGETNEXTHOSTMSGDATA data;
998 RT_ZERO(data);
999 data.hdr.uMagic = VBOX_DND_CB_MAGIC_MAKE(0 /* uFn */, 0 /* uVer */);
1000 data.uMsg = u32Function;
1001 data.cParms = cParms;
1002 data.paParms = paParms;
1003
1004 rc = m_SvcCtx.pfnHostCallback(m_SvcCtx.pvHostData, u32Function,
1005 &data, sizeof(data));
1006 if (RT_SUCCESS(rc))
1007 {
1008 cParms = data.cParms;
1009 paParms = data.paParms;
1010 }
1011 }
1012 else /* No host callback in place, so drag and drop is not supported by the host. */
1013 rc = VERR_NOT_SUPPORTED;
1014 }
1015 break;
1016 }
1017 }
1018 }
1019
1020 /*
1021 * If async execution is requested, we didn't notify the guest yet about
1022 * completion. The client is queued into the waiters list and will be
1023 * notified as soon as a new event is available.
1024 */
1025 if (rc == VINF_HGCM_ASYNC_EXECUTE)
1026 {
1027 try
1028 {
1029 AssertPtr(pClient);
1030 pClient->setDeferred(callHandle, u32Function, cParms, paParms);
1031 m_clientQueue.push_back(u32ClientID);
1032 }
1033 catch (std::bad_alloc)
1034 {
1035 rc = VERR_NO_MEMORY;
1036 /* Don't report to guest. */
1037 }
1038 }
1039 else if (pClient)
1040 pClient->complete(callHandle, rc);
1041 else
1042 rc = VERR_NOT_IMPLEMENTED;
1043
1044 LogFlowFunc(("Returning rc=%Rrc\n", rc));
1045}
1046
1047int DragAndDropService::hostCall(uint32_t u32Function,
1048 uint32_t cParms, VBOXHGCMSVCPARM paParms[])
1049{
1050 LogFlowFunc(("u32Function=%RU32, cParms=%RU32, cClients=%zu, cQueue=%zu\n",
1051 u32Function, cParms, m_clientMap.size(), m_clientQueue.size()));
1052
1053 int rc;
1054 if (u32Function == HOST_DND_SET_MODE)
1055 {
1056 if (cParms != 1)
1057 rc = VERR_INVALID_PARAMETER;
1058 else if (paParms[0].type != VBOX_HGCM_SVC_PARM_32BIT)
1059 rc = VERR_INVALID_PARAMETER;
1060 else
1061 rc = modeSet(paParms[0].u.uint32);
1062 }
1063 else if (modeGet() != VBOX_DRAG_AND_DROP_MODE_OFF)
1064 {
1065 if (m_clientMap.size()) /* At least one client on the guest connected? */
1066 {
1067 rc = m_pManager->addMessage(u32Function, cParms, paParms, true /* fAppend */);
1068 if (RT_SUCCESS(rc))
1069 {
1070 if (m_clientQueue.size()) /* Any clients in our queue ready for processing the next command? */
1071 {
1072 uint32_t uClientNext = m_clientQueue.front();
1073 DnDClientMap::iterator itClientNext = m_clientMap.find(uClientNext);
1074 Assert(itClientNext != m_clientMap.end());
1075
1076 DragAndDropClient *pClient = itClientNext->second;
1077 AssertPtr(pClient);
1078
1079 /*
1080 * Check if this was a request for getting the next host
1081 * message. If so, return the message ID and the parameter
1082 * count. The message itself has to be queued.
1083 */
1084 uint32_t uMsg = pClient->message();
1085 if (uMsg == GUEST_DND_GET_NEXT_HOST_MSG)
1086 {
1087 LogFlowFunc(("Client %RU32 is waiting for next host msg\n", pClient->clientId()));
1088
1089 uint32_t uMsg1;
1090 uint32_t cParms1;
1091 rc = m_pManager->nextMessageInfo(&uMsg1, &cParms1);
1092 if (RT_SUCCESS(rc))
1093 {
1094 rc = pClient->addMessageInfo(uMsg1, cParms1);
1095
1096 /* Note: Report the current rc back to the guest. */
1097 pClient->completeDeferred(rc);
1098
1099 m_clientQueue.pop_front();
1100 }
1101 }
1102 else
1103 AssertMsgFailed(("Client ID=%RU32 in wrong state with uMsg=%RU32\n",
1104 pClient->clientId(), uMsg));
1105 }
1106 else
1107 LogFlowFunc(("All clients busy; delaying execution\n"));
1108 }
1109 else
1110 AssertMsgFailed(("Adding new message of type=%RU32 failed with rc=%Rrc\n",
1111 u32Function, rc));
1112 }
1113 else
1114 {
1115 /*
1116 * Tell the host that the guest does not support drag'n drop.
1117 * This might happen due to not installed Guest Additions or
1118 * not running VBoxTray/VBoxClient.
1119 */
1120 rc = VERR_NOT_SUPPORTED;
1121 }
1122 }
1123 else
1124 {
1125 /* Tell the host that a wrong drag'n drop mode is set. */
1126 rc = VERR_ACCESS_DENIED;
1127 }
1128
1129 LogFlowFuncLeaveRC(rc);
1130 return rc;
1131}
1132
1133DECLCALLBACK(int) DragAndDropService::progressCallback(uint32_t uStatus, uint32_t uPercentage, int rc, void *pvUser)
1134{
1135 AssertPtrReturn(pvUser, VERR_INVALID_POINTER);
1136
1137 DragAndDropService *pSelf = static_cast<DragAndDropService *>(pvUser);
1138 AssertPtr(pSelf);
1139
1140 if (pSelf->m_SvcCtx.pfnHostCallback)
1141 {
1142 LogFlowFunc(("GUEST_DND_HG_EVT_PROGRESS: uStatus=%RU32, uPercentage=%RU32, rc=%Rrc\n",
1143 uStatus, uPercentage, rc));
1144
1145 VBOXDNDCBHGEVTPROGRESSDATA data;
1146 data.hdr.uMagic = CB_MAGIC_DND_HG_EVT_PROGRESS;
1147 data.uPercentage = RT_MIN(uPercentage, 100);
1148 data.uStatus = uStatus;
1149 data.rc = rc; /** @todo uin32_t vs. int. */
1150
1151 return pSelf->m_SvcCtx.pfnHostCallback(pSelf->m_SvcCtx.pvHostData,
1152 GUEST_DND_HG_EVT_PROGRESS,
1153 &data, sizeof(data));
1154 }
1155
1156 return VINF_SUCCESS;
1157}
1158
1159/**
1160 * @copydoc VBOXHGCMSVCLOAD
1161 */
1162extern "C" DECLCALLBACK(DECLEXPORT(int)) VBoxHGCMSvcLoad(VBOXHGCMSVCFNTABLE *pTable)
1163{
1164 return DragAndDropService::svcLoad(pTable);
1165}
1166
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