VirtualBox

source: vbox/trunk/src/VBox/Main/include/GuestDnDPrivate.h@ 78048

Last change on this file since 78048 was 76562, checked in by vboxsync, 6 years ago

Main: Use MAIN_INCLUDED_ and MAIN_INCLUDED_SRC_ as header guard prefixes with scm.

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 31.3 KB
Line 
1/* $Id: GuestDnDPrivate.h 76562 2019-01-01 03:22:50Z vboxsync $ */
2/** @file
3 * Private guest drag and drop code, used by GuestDnDTarget +
4 * GuestDnDSource.
5 */
6
7/*
8 * Copyright (C) 2011-2019 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#ifndef MAIN_INCLUDED_GuestDnDPrivate_h
20#define MAIN_INCLUDED_GuestDnDPrivate_h
21#ifndef RT_WITHOUT_PRAGMA_ONCE
22# pragma once
23#endif
24
25#include <iprt/dir.h>
26#include <iprt/file.h>
27#include <iprt/path.h>
28
29#include <VBox/hgcmsvc.h> /* For PVBOXHGCMSVCPARM. */
30#include <VBox/GuestHost/DragAndDrop.h>
31#include <VBox/GuestHost/DragAndDropDefs.h>
32#include <VBox/HostServices/DragAndDropSvc.h>
33
34/**
35 * Forward prototype declarations.
36 */
37class Guest;
38class GuestDnDBase;
39class GuestDnDResponse;
40class GuestDnDSource;
41class GuestDnDTarget;
42class Progress;
43
44/**
45 * Type definitions.
46 */
47
48/** List (vector) of MIME types. */
49typedef std::vector<com::Utf8Str> GuestDnDMIMEList;
50
51/*
52 ** @todo Put most of the implementations below in GuestDnDPrivate.cpp!
53 */
54
55class GuestDnDCallbackEvent
56{
57public:
58
59 GuestDnDCallbackEvent(void)
60 : mSemEvent(NIL_RTSEMEVENT)
61 , mRc(VINF_SUCCESS) { }
62
63 virtual ~GuestDnDCallbackEvent(void);
64
65public:
66
67 int Reset(void);
68
69 int Notify(int rc = VINF_SUCCESS);
70
71 int Result(void) const { return mRc; }
72
73 int Wait(RTMSINTERVAL msTimeout);
74
75protected:
76
77 /** Event semaphore to notify on error/completion. */
78 RTSEMEVENT mSemEvent;
79 /** Callback result. */
80 int mRc;
81};
82
83/**
84 * Class for handling the (raw) meta data.
85 */
86class GuestDnDMetaData
87{
88public:
89
90 GuestDnDMetaData(void)
91 : pvData(NULL)
92 , cbData(0)
93 , cbDataUsed(0) { }
94
95 virtual ~GuestDnDMetaData(void)
96 {
97 reset();
98 }
99
100public:
101
102 uint32_t add(const void *pvDataAdd, uint32_t cbDataAdd)
103 {
104 LogFlowThisFunc(("pvDataAdd=%p, cbDataAdd=%zu\n", pvDataAdd, cbDataAdd));
105
106 if (!cbDataAdd)
107 return 0;
108 AssertPtrReturn(pvDataAdd, 0);
109
110 int rc = resize(cbData + cbDataAdd);
111 if (RT_FAILURE(rc))
112 return 0;
113
114 Assert(cbData >= cbDataUsed + cbDataAdd);
115 memcpy((uint8_t *)pvData + cbDataUsed, pvDataAdd, cbDataAdd);
116
117 cbDataUsed += cbDataAdd;
118
119 return cbDataAdd;
120 }
121
122 uint32_t add(const std::vector<BYTE> &vecAdd)
123 {
124 if (!vecAdd.size())
125 return 0;
126
127 if (vecAdd.size() > UINT32_MAX) /* Paranoia. */
128 return 0;
129
130 return add(&vecAdd.front(), (uint32_t)vecAdd.size());
131 }
132
133 void reset(void)
134 {
135 if (pvData)
136 {
137 Assert(cbData);
138 RTMemFree(pvData);
139 pvData = NULL;
140 }
141
142 cbData = 0;
143 cbDataUsed = 0;
144 }
145
146 const void *getData(void) const { return pvData; }
147
148 void *getDataMutable(void) { return pvData; }
149
150 uint32_t getSize(void) const { return cbDataUsed; }
151
152public:
153
154 int fromString(const RTCString &strData)
155 {
156 int rc = VINF_SUCCESS;
157
158 if (strData.isNotEmpty())
159 {
160 const uint32_t cbStrData = (uint32_t)strData.length() + 1; /* Include terminating zero. */
161 rc = resize(cbStrData);
162 if (RT_SUCCESS(rc))
163 memcpy(pvData, strData.c_str(), cbStrData);
164 }
165
166 return rc;
167 }
168
169 int fromURIList(const DnDURIList &lstURI)
170 {
171 return fromString(lstURI.GetRootEntries());
172 }
173
174protected:
175
176 int resize(uint32_t cbSize)
177 {
178 if (!cbSize)
179 {
180 reset();
181 return VINF_SUCCESS;
182 }
183
184 if (cbSize == cbData)
185 return VINF_SUCCESS;
186
187 void *pvTmp = NULL;
188 if (!cbData)
189 {
190 Assert(cbDataUsed == 0);
191 pvTmp = RTMemAllocZ(cbSize);
192 }
193 else
194 {
195 AssertPtr(pvData);
196 pvTmp = RTMemRealloc(pvData, cbSize);
197 RT_BZERO(pvTmp, cbSize);
198 }
199
200 if (pvTmp)
201 {
202 pvData = pvTmp;
203 cbData = cbSize;
204 return VINF_SUCCESS;
205 }
206
207 return VERR_NO_MEMORY;
208 }
209
210protected:
211
212 void *pvData;
213 uint32_t cbData;
214 uint32_t cbDataUsed;
215};
216
217/**
218 * Class for keeping drag and drop (meta) data
219 * to be sent/received.
220 */
221class GuestDnDData
222{
223public:
224
225 GuestDnDData(void)
226 : cbEstTotal(0)
227 , cbEstMeta(0)
228 , cbProcessed(0)
229 {
230 RT_ZERO(dataHdr);
231 }
232
233 virtual ~GuestDnDData(void)
234 {
235 reset();
236 }
237
238public:
239
240 uint64_t addProcessed(uint32_t cbDataAdd)
241 {
242 const uint64_t cbTotal = getTotal(); NOREF(cbTotal);
243 Assert(cbProcessed + cbDataAdd <= cbTotal);
244 cbProcessed += cbDataAdd;
245 return cbProcessed;
246 }
247
248 bool isComplete(void) const
249 {
250 const uint64_t cbTotal = getTotal();
251 LogFlowFunc(("cbProcessed=%RU64, cbTotal=%RU64\n", cbProcessed, cbTotal));
252 Assert(cbProcessed <= cbTotal);
253 return (cbProcessed == cbTotal);
254 }
255
256 void *getChkSumMutable(void) { return dataHdr.pvChecksum; }
257
258 uint32_t getChkSumSize(void) const { return dataHdr.cbChecksum; }
259
260 void *getFmtMutable(void) { return dataHdr.pvMetaFmt; }
261
262 uint32_t getFmtSize(void) const { return dataHdr.cbMetaFmt; }
263
264 GuestDnDMetaData &getMeta(void) { return dataMeta; }
265
266 uint8_t getPercentComplete(void) const
267 {
268 int64_t cbTotal = RT_MAX(getTotal(), 1);
269 return (uint8_t)(cbProcessed * 100 / cbTotal);
270 }
271
272 uint64_t getProcessed(void) const { return cbProcessed; }
273
274 uint64_t getRemaining(void) const
275 {
276 const uint64_t cbTotal = getTotal();
277 Assert(cbProcessed <= cbTotal);
278 return cbTotal - cbProcessed;
279 }
280
281 uint64_t getTotal(void) const { return cbEstTotal; }
282
283 void reset(void)
284 {
285 clearFmt();
286 clearChkSum();
287
288 RT_ZERO(dataHdr);
289
290 dataMeta.reset();
291
292 cbEstTotal = 0;
293 cbEstMeta = 0;
294 cbProcessed = 0;
295 }
296
297 int setFmt(const void *pvFmt, uint32_t cbFmt)
298 {
299 if (cbFmt)
300 {
301 AssertPtrReturn(pvFmt, VERR_INVALID_POINTER);
302 void *pvFmtTmp = RTMemAlloc(cbFmt);
303 if (!pvFmtTmp)
304 return VERR_NO_MEMORY;
305
306 clearFmt();
307
308 memcpy(pvFmtTmp, pvFmt, cbFmt);
309
310 dataHdr.pvMetaFmt = pvFmtTmp;
311 dataHdr.cbMetaFmt = cbFmt;
312 }
313 else
314 clearFmt();
315
316 return VINF_SUCCESS;
317 }
318
319 void setEstimatedSize(uint64_t cbTotal, uint32_t cbMeta)
320 {
321 Assert(cbMeta <= cbTotal);
322
323 LogFlowFunc(("cbTotal=%RU64, cbMeta=%RU32\n", cbTotal, cbMeta));
324
325 cbEstTotal = cbTotal;
326 cbEstMeta = cbMeta;
327 }
328
329protected:
330
331 void clearChkSum(void)
332 {
333 if (dataHdr.pvChecksum)
334 {
335 Assert(dataHdr.cbChecksum);
336 RTMemFree(dataHdr.pvChecksum);
337 dataHdr.pvChecksum = NULL;
338 }
339
340 dataHdr.cbChecksum = 0;
341 }
342
343 void clearFmt(void)
344 {
345 if (dataHdr.pvMetaFmt)
346 {
347 Assert(dataHdr.cbMetaFmt);
348 RTMemFree(dataHdr.pvMetaFmt);
349 dataHdr.pvMetaFmt = NULL;
350 }
351
352 dataHdr.cbMetaFmt = 0;
353 }
354
355protected:
356
357 /** The data header. */
358 VBOXDNDDATAHDR dataHdr;
359 /** For storing the actual meta data.
360 * This might be an URI list or just plain raw data,
361 * according to the format being sent. */
362 GuestDnDMetaData dataMeta;
363 /** Estimated total data size when receiving data. */
364 uint64_t cbEstTotal;
365 /** Estimated meta data size when receiving data. */
366 uint32_t cbEstMeta;
367 /** Overall size (in bytes) of processed data. */
368 uint64_t cbProcessed;
369};
370
371/** Initial state. */
372#define DND_OBJCTX_STATE_NONE 0
373/** The header was received/sent. */
374#define DND_OBJCTX_STATE_HAS_HDR RT_BIT(0)
375
376/**
377 * Structure for keeping a DnDURIObject context around.
378 */
379class GuestDnDURIObjCtx
380{
381public:
382
383 GuestDnDURIObjCtx(void)
384 : pObjURI(NULL)
385 , fIntermediate(false)
386 , fState(DND_OBJCTX_STATE_NONE) { }
387
388 virtual ~GuestDnDURIObjCtx(void)
389 {
390 destroy();
391 }
392
393public:
394
395 int createIntermediate(DnDURIObject::Type enmType)
396 {
397 reset();
398
399 int rc;
400
401 try
402 {
403 pObjURI = new DnDURIObject(enmType);
404 fIntermediate = true;
405
406 rc = VINF_SUCCESS;
407 }
408 catch (std::bad_alloc &)
409 {
410 rc = VERR_NO_MEMORY;
411 }
412
413 LogThisFunc(("Returning %Rrc\n", rc));
414 return rc;
415 }
416
417 void destroy(void)
418 {
419 LogFlowThisFuncEnter();
420
421 if ( pObjURI
422 && fIntermediate)
423 {
424 delete pObjURI;
425 }
426
427 pObjURI = NULL;
428 fIntermediate = false;
429 }
430
431 DnDURIObject *getObj(void) const { return pObjURI; }
432
433 bool isIntermediate(void) { return fIntermediate; }
434
435 bool isValid(void) const { return (pObjURI != NULL); }
436
437 uint32_t getState(void) const { return fState; }
438
439 void reset(void)
440 {
441 LogFlowThisFuncEnter();
442
443 destroy();
444
445 fIntermediate = false;
446 fState = 0;
447 }
448
449 void setObj(DnDURIObject *pObj)
450 {
451 LogFlowThisFunc(("%p\n", pObj));
452
453 destroy();
454
455 pObjURI = pObj;
456 }
457
458 uint32_t setState(uint32_t fStateNew)
459 {
460 /** @todo Add validation. */
461 fState = fStateNew;
462 return fState;
463 }
464
465protected:
466
467 /** Pointer to current object being handled. */
468 DnDURIObject *pObjURI;
469 /** Flag whether pObjURI needs deletion after use. */
470 bool fIntermediate;
471 /** Internal context state, corresponding to DND_OBJCTX_STATE_XXX. */
472 uint32_t fState;
473 /** @todo Add more statistics / information here. */
474};
475
476/**
477 * Structure for keeping around an URI (data) transfer.
478 */
479class GuestDnDURIData
480{
481
482public:
483
484 GuestDnDURIData(void)
485 : cObjToProcess(0)
486 , cObjProcessed(0)
487 , pvScratchBuf(NULL)
488 , cbScratchBuf(0) { }
489
490 virtual ~GuestDnDURIData(void)
491 {
492 reset();
493
494 if (pvScratchBuf)
495 {
496 Assert(cbScratchBuf);
497 RTMemFree(pvScratchBuf);
498 pvScratchBuf = NULL;
499 }
500 cbScratchBuf = 0;
501 }
502
503 DnDDroppedFiles &getDroppedFiles(void) { return droppedFiles; }
504
505 DnDURIList &getURIList(void) { return lstURI; }
506
507 int init(size_t cbBuf = _64K)
508 {
509 reset();
510
511 pvScratchBuf = RTMemAlloc(cbBuf);
512 if (!pvScratchBuf)
513 return VERR_NO_MEMORY;
514
515 cbScratchBuf = cbBuf;
516 return VINF_SUCCESS;
517 }
518
519 bool isComplete(void) const
520 {
521 LogFlowFunc(("cObjProcessed=%RU64, cObjToProcess=%RU64\n", cObjProcessed, cObjToProcess));
522
523 if (!cObjToProcess) /* Always return true if we don't have an object count. */
524 return true;
525
526 Assert(cObjProcessed <= cObjToProcess);
527 return (cObjProcessed == cObjToProcess);
528 }
529
530 const void *getBuffer(void) const { return pvScratchBuf; }
531
532 void *getBufferMutable(void) { return pvScratchBuf; }
533
534 size_t getBufferSize(void) const { return cbScratchBuf; }
535
536 GuestDnDURIObjCtx &getObj(uint64_t uID = 0)
537 {
538 RT_NOREF(uID);
539 AssertMsg(uID == 0, ("Other objects than object 0 is not supported yet\n"));
540 return objCtx;
541 }
542
543 GuestDnDURIObjCtx &getObjCurrent(void)
544 {
545 DnDURIObject *pCurObj = lstURI.First();
546 if ( !lstURI.IsEmpty()
547 && pCurObj)
548 {
549 /* Point the context object to the current DnDURIObject to process. */
550 objCtx.setObj(pCurObj);
551 }
552 else
553 objCtx.reset();
554
555 return objCtx;
556 }
557
558 uint64_t getObjToProcess(void) const { return cObjToProcess; }
559
560 uint64_t getObjProcessed(void) const { return cObjProcessed; }
561
562 int processObject(const DnDURIObject &Obj)
563 {
564 int rc;
565
566 /** @todo Find objct in lstURI first! */
567 switch (Obj.GetType())
568 {
569 case DnDURIObject::Type_Directory:
570 case DnDURIObject::Type_File:
571 rc = VINF_SUCCESS;
572 break;
573
574 default:
575 rc = VERR_NOT_IMPLEMENTED;
576 break;
577 }
578
579 if (RT_SUCCESS(rc))
580 {
581 if (cObjToProcess)
582 {
583 cObjProcessed++;
584 Assert(cObjProcessed <= cObjToProcess);
585 }
586 }
587
588 return rc;
589 }
590
591 void removeObjCurrent(void)
592 {
593 if (cObjToProcess)
594 {
595 cObjProcessed++;
596 Assert(cObjProcessed <= cObjToProcess);
597 }
598
599 lstURI.RemoveFirst();
600 objCtx.reset();
601 }
602
603 void reset(void)
604 {
605 LogFlowFuncEnter();
606
607 cObjToProcess = 0;
608 cObjProcessed = 0;
609
610 droppedFiles.Close();
611
612 lstURI.Clear();
613 objCtx.reset();
614 }
615
616 void setEstimatedObjects(uint64_t cObjs)
617 {
618 Assert(cObjToProcess == 0);
619 cObjToProcess = cObjs;
620 LogFlowFunc(("cObjToProcess=%RU64\n", cObjs));
621 }
622
623public:
624
625 int fromLocalMetaData(const GuestDnDMetaData &Data)
626 {
627 reset();
628
629 if (!Data.getSize())
630 return VINF_SUCCESS;
631
632 char *pszList;
633 int rc = RTStrCurrentCPToUtf8(&pszList, (const char *)Data.getData());
634 if (RT_FAILURE(rc))
635 {
636 LogFlowThisFunc(("String conversion failed with rc=%Rrc\n", rc));
637 return rc;
638 }
639
640 const size_t cbList = Data.getSize();
641 LogFlowThisFunc(("metaData=%p, cbList=%zu\n", &Data, cbList));
642
643 if (cbList)
644 {
645 RTCList<RTCString> lstURIOrg = RTCString(pszList, cbList).split("\r\n");
646 if (!lstURIOrg.isEmpty())
647 {
648 /* Note: All files to be transferred will be kept open during the entire DnD
649 * operation, also to keep the accounting right. */
650 rc = lstURI.AppendURIPathsFromList(lstURIOrg, DNDURILIST_FLAGS_KEEP_OPEN);
651 if (RT_SUCCESS(rc))
652 cObjToProcess = lstURI.GetTotalCount();
653 }
654 }
655
656 RTStrFree(pszList);
657 return rc;
658 }
659
660 int fromRemoteMetaData(const GuestDnDMetaData &Data)
661 {
662 LogFlowFuncEnter();
663
664 int rc = lstURI.SetFromURIData(Data.getData(), Data.getSize(), 0 /* uFlags */);
665 if (RT_SUCCESS(rc))
666 {
667 const size_t cRootCount = lstURI.GetRootCount();
668 LogFlowFunc(("cRootCount=%zu, cObjToProcess=%RU64\n", cRootCount, cObjToProcess));
669 if (cRootCount > cObjToProcess)
670 rc = VERR_INVALID_PARAMETER;
671 }
672
673 return rc;
674 }
675
676 int toMetaData(std::vector<BYTE> &vecData)
677 {
678 const char *pszDroppedFilesDir = droppedFiles.GetDirAbs();
679
680 Utf8Str strURIs = lstURI.GetRootEntries(RTCString(pszDroppedFilesDir));
681 size_t cbData = strURIs.length();
682
683 LogFlowFunc(("%zu root URIs (%zu bytes)\n", lstURI.GetRootCount(), cbData));
684
685 int rc;
686
687 try
688 {
689 vecData.resize(cbData + 1 /* Include termination */);
690 memcpy(&vecData.front(), strURIs.c_str(), cbData);
691
692 rc = VINF_SUCCESS;
693 }
694 catch (std::bad_alloc &)
695 {
696 rc = VERR_NO_MEMORY;
697 }
698
699 return rc;
700 }
701
702protected:
703
704 int processDirectory(const char *pszPath, uint32_t fMode)
705 {
706 /** @todo Find directory in lstURI first! */
707 int rc;
708
709 const char *pszDroppedFilesDir = droppedFiles.GetDirAbs();
710 char *pszDir = RTPathJoinA(pszDroppedFilesDir, pszPath);
711 if (pszDir)
712 {
713 rc = RTDirCreateFullPath(pszDir, fMode);
714 if (cObjToProcess)
715 {
716 cObjProcessed++;
717 Assert(cObjProcessed <= cObjToProcess);
718 }
719
720 RTStrFree(pszDir);
721 }
722 else
723 rc = VERR_NO_MEMORY;
724
725 return rc;
726 }
727
728protected:
729
730 /** Number of objects to process. */
731 uint64_t cObjToProcess;
732 /** Number of objects already processed. */
733 uint64_t cObjProcessed;
734 /** Handles all drop files for this operation. */
735 DnDDroppedFiles droppedFiles;
736 /** (Non-recursive) List of URI objects to handle. */
737 DnDURIList lstURI;
738 /** Context to current object being handled.
739 * As we currently do all transfers one after another we
740 * only have one context at a time. */
741 GuestDnDURIObjCtx objCtx;
742 /** Pointer to an optional scratch buffer to use for
743 * doing the actual chunk transfers. */
744 void *pvScratchBuf;
745 /** Size (in bytes) of scratch buffer. */
746 size_t cbScratchBuf;
747};
748
749/**
750 * Context structure for sending data to the guest.
751 */
752typedef struct SENDDATACTX
753{
754 /** Pointer to guest target class this context belongs to. */
755 GuestDnDTarget *mpTarget;
756 /** Pointer to guest response class this context belongs to. */
757 GuestDnDResponse *mpResp;
758 /** Flag indicating whether a file transfer is active and
759 * initiated by the host. */
760 bool mIsActive;
761 /** Target (VM) screen ID. */
762 uint32_t mScreenID;
763 /** Drag'n drop format requested by the guest. */
764 com::Utf8Str mFmtReq;
765 /** Drag'n drop data to send.
766 * This can be arbitrary data or an URI list. */
767 GuestDnDData mData;
768 /** URI data structure. */
769 GuestDnDURIData mURI;
770 /** Callback event to use. */
771 GuestDnDCallbackEvent mCBEvent;
772
773} SENDDATACTX, *PSENDDATACTX;
774
775/**
776 * Context structure for receiving data from the guest.
777 */
778typedef struct RECVDATACTX
779{
780 /** Pointer to guest source class this context belongs to. */
781 GuestDnDSource *mpSource;
782 /** Pointer to guest response class this context belongs to. */
783 GuestDnDResponse *mpResp;
784 /** Flag indicating whether a file transfer is active and
785 * initiated by the host. */
786 bool mIsActive;
787 /** Formats offered by the guest (and supported by the host). */
788 GuestDnDMIMEList mFmtOffered;
789 /** Original drop format requested to receive from the guest. */
790 com::Utf8Str mFmtReq;
791 /** Intermediate drop format to be received from the guest.
792 * Some original drop formats require a different intermediate
793 * drop format:
794 *
795 * Receiving a file link as "text/plain" requires still to
796 * receive the file from the guest as "text/uri-list" first,
797 * then pointing to the file path on the host with the data
798 * in "text/plain" format returned. */
799 com::Utf8Str mFmtRecv;
800 /** Desired drop action to perform on the host.
801 * Needed to tell the guest if data has to be
802 * deleted e.g. when moving instead of copying. */
803 VBOXDNDACTION mAction;
804 /** Drag'n drop received from the guest.
805 * This can be arbitrary data or an URI list. */
806 GuestDnDData mData;
807 /** URI data structure. */
808 GuestDnDURIData mURI;
809 /** Callback event to use. */
810 GuestDnDCallbackEvent mCBEvent;
811
812} RECVDATACTX, *PRECVDATACTX;
813
814/**
815 * Simple structure for a buffered guest DnD message.
816 */
817class GuestDnDMsg
818{
819public:
820
821 GuestDnDMsg(void)
822 : uMsg(0)
823 , cParms(0)
824 , cParmsAlloc(0)
825 , paParms(NULL) { }
826
827 virtual ~GuestDnDMsg(void)
828 {
829 reset();
830 }
831
832public:
833
834 PVBOXHGCMSVCPARM getNextParam(void)
835 {
836 if (cParms >= cParmsAlloc)
837 {
838 if (!paParms)
839 paParms = (PVBOXHGCMSVCPARM)RTMemAlloc(4 * sizeof(VBOXHGCMSVCPARM));
840 else
841 paParms = (PVBOXHGCMSVCPARM)RTMemRealloc(paParms, (cParmsAlloc + 4) * sizeof(VBOXHGCMSVCPARM));
842 if (!paParms)
843 throw VERR_NO_MEMORY;
844 RT_BZERO(&paParms[cParmsAlloc], 4 * sizeof(VBOXHGCMSVCPARM));
845 cParmsAlloc += 4;
846 }
847
848 return &paParms[cParms++];
849 }
850
851 uint32_t getCount(void) const { return cParms; }
852 PVBOXHGCMSVCPARM getParms(void) const { return paParms; }
853 uint32_t getType(void) const { return uMsg; }
854
855 void reset(void)
856 {
857 if (paParms)
858 {
859 /* Remove deep copies. */
860 for (uint32_t i = 0; i < cParms; i++)
861 {
862 if ( paParms[i].type == VBOX_HGCM_SVC_PARM_PTR
863 && paParms[i].u.pointer.size)
864 {
865 AssertPtr(paParms[i].u.pointer.addr);
866 RTMemFree(paParms[i].u.pointer.addr);
867 }
868 }
869
870 RTMemFree(paParms);
871 paParms = NULL;
872 }
873
874 uMsg = cParms = cParmsAlloc = 0;
875 }
876
877 int setNextPointer(void *pvBuf, uint32_t cbBuf)
878 {
879 PVBOXHGCMSVCPARM pParm = getNextParam();
880 if (!pParm)
881 return VERR_NO_MEMORY;
882
883 void *pvTmp = NULL;
884 if (pvBuf)
885 {
886 Assert(cbBuf);
887 pvTmp = RTMemDup(pvBuf, cbBuf);
888 if (!pvTmp)
889 return VERR_NO_MEMORY;
890 }
891
892 HGCMSvcSetPv(pParm, pvTmp, cbBuf);
893 return VINF_SUCCESS;
894 }
895
896 int setNextString(const char *pszString)
897 {
898 PVBOXHGCMSVCPARM pParm = getNextParam();
899 if (!pParm)
900 return VERR_NO_MEMORY;
901
902 char *pszTemp = RTStrDup(pszString);
903 if (!pszTemp)
904 return VERR_NO_MEMORY;
905
906 HGCMSvcSetStr(pParm, pszTemp);
907 return VINF_SUCCESS;
908 }
909
910 int setNextUInt32(uint32_t u32Val)
911 {
912 PVBOXHGCMSVCPARM pParm = getNextParam();
913 if (!pParm)
914 return VERR_NO_MEMORY;
915
916 HGCMSvcSetU32(pParm, u32Val);
917 return VINF_SUCCESS;
918 }
919
920 int setNextUInt64(uint64_t u64Val)
921 {
922 PVBOXHGCMSVCPARM pParm = getNextParam();
923 if (!pParm)
924 return VERR_NO_MEMORY;
925
926 HGCMSvcSetU64(pParm, u64Val);
927 return VINF_SUCCESS;
928 }
929
930 void setType(uint32_t uMsgType) { uMsg = uMsgType; }
931
932protected:
933
934 /** Message type. */
935 uint32_t uMsg;
936 /** Message parameters. */
937 uint32_t cParms;
938 /** Size of array. */
939 uint32_t cParmsAlloc;
940 /** Array of HGCM parameters */
941 PVBOXHGCMSVCPARM paParms;
942};
943
944/** Guest DnD callback function definition. */
945typedef DECLCALLBACKPTR(int, PFNGUESTDNDCALLBACK) (uint32_t uMsg, void *pvParms, size_t cbParms, void *pvUser);
946
947/**
948 * Structure for keeping a guest DnD callback.
949 * Each callback can handle one HGCM message, however, multiple HGCM messages can be registered
950 * to the same callback (function).
951 */
952typedef struct GuestDnDCallback
953{
954 GuestDnDCallback(void)
955 : uMessgage(0)
956 , pfnCallback(NULL)
957 , pvUser(NULL) { }
958
959 GuestDnDCallback(PFNGUESTDNDCALLBACK pvCB, uint32_t uMsg, void *pvUsr = NULL)
960 : uMessgage(uMsg)
961 , pfnCallback(pvCB)
962 , pvUser(pvUsr) { }
963
964 /** The HGCM message ID to handle. */
965 uint32_t uMessgage;
966 /** Pointer to callback function. */
967 PFNGUESTDNDCALLBACK pfnCallback;
968 /** Pointer to user-supplied data. */
969 void *pvUser;
970
971} GuestDnDCallback;
972
973/** Contains registered callback pointers for specific HGCM message types. */
974typedef std::map<uint32_t, GuestDnDCallback> GuestDnDCallbackMap;
975
976/** @todo r=andy This class needs to go, as this now is too inflexible when it comes to all
977 * the callback handling/dispatching. It's part of the initial code and only adds
978 * unnecessary complexity. */
979class GuestDnDResponse
980{
981
982public:
983
984 GuestDnDResponse(const ComObjPtr<Guest>& pGuest);
985 virtual ~GuestDnDResponse(void);
986
987public:
988
989 int notifyAboutGuestResponse(void) const;
990 int waitForGuestResponse(RTMSINTERVAL msTimeout = 500) const;
991
992 void setActionsAllowed(VBOXDNDACTIONLIST a) { m_dndLstActionsAllowed = a; }
993 VBOXDNDACTIONLIST getActionsAllowed(void) const { return m_dndLstActionsAllowed; }
994
995 void setActionDefault(VBOXDNDACTION a) { m_dndActionDefault = a; }
996 VBOXDNDACTION getActionDefault(void) const { return m_dndActionDefault; }
997
998 void setFormats(const GuestDnDMIMEList &lstFormats) { m_lstFormats = lstFormats; }
999 GuestDnDMIMEList formats(void) const { return m_lstFormats; }
1000
1001 void reset(void);
1002
1003 bool isProgressCanceled(void) const;
1004 int setCallback(uint32_t uMsg, PFNGUESTDNDCALLBACK pfnCallback, void *pvUser = NULL);
1005 int setProgress(unsigned uPercentage, uint32_t uState, int rcOp = VINF_SUCCESS, const Utf8Str &strMsg = "");
1006 HRESULT resetProgress(const ComObjPtr<Guest>& pParent);
1007 HRESULT queryProgressTo(IProgress **ppProgress);
1008
1009public:
1010
1011 /** @name HGCM callback handling.
1012 @{ */
1013 int onDispatch(uint32_t u32Function, void *pvParms, uint32_t cbParms);
1014 /** @} */
1015
1016protected:
1017
1018 /** Pointer to context this class is tied to. */
1019 void *m_pvCtx;
1020 /** Event for waiting for response. */
1021 RTSEMEVENT m_EventSem;
1022 /** Default action to perform in case of a
1023 * successful drop. */
1024 VBOXDNDACTION m_dndActionDefault;
1025 /** Actions supported by the guest in case of a successful drop. */
1026 VBOXDNDACTIONLIST m_dndLstActionsAllowed;
1027 /** Format(s) requested/supported from the guest. */
1028 GuestDnDMIMEList m_lstFormats;
1029 /** Pointer to IGuest parent object. */
1030 ComObjPtr<Guest> m_pParent;
1031 /** Pointer to associated progress object. Optional. */
1032 ComObjPtr<Progress> m_pProgress;
1033 /** Callback map. */
1034 GuestDnDCallbackMap m_mapCallbacks;
1035};
1036
1037/**
1038 * Private singleton class for the guest's DnD
1039 * implementation. Can't be instanciated directly, only via
1040 * the factory pattern.
1041 *
1042 ** @todo Move this into GuestDnDBase.
1043 */
1044class GuestDnD
1045{
1046public:
1047
1048 static GuestDnD *createInstance(const ComObjPtr<Guest>& pGuest)
1049 {
1050 Assert(NULL == GuestDnD::s_pInstance);
1051 GuestDnD::s_pInstance = new GuestDnD(pGuest);
1052 return GuestDnD::s_pInstance;
1053 }
1054
1055 static void destroyInstance(void)
1056 {
1057 if (GuestDnD::s_pInstance)
1058 {
1059 delete GuestDnD::s_pInstance;
1060 GuestDnD::s_pInstance = NULL;
1061 }
1062 }
1063
1064 static inline GuestDnD *getInstance(void)
1065 {
1066 AssertPtr(GuestDnD::s_pInstance);
1067 return GuestDnD::s_pInstance;
1068 }
1069
1070protected:
1071
1072 GuestDnD(const ComObjPtr<Guest>& pGuest);
1073 virtual ~GuestDnD(void);
1074
1075public:
1076
1077 /** @name Public helper functions.
1078 * @{ */
1079 HRESULT adjustScreenCoordinates(ULONG uScreenId, ULONG *puX, ULONG *puY) const;
1080 int hostCall(uint32_t u32Function, uint32_t cParms, PVBOXHGCMSVCPARM paParms) const;
1081 GuestDnDResponse *response(void) { return m_pResponse; }
1082 GuestDnDMIMEList defaultFormats(void) const { return m_strDefaultFormats; }
1083 /** @} */
1084
1085public:
1086
1087 /** @name Static low-level HGCM callback handler.
1088 * @{ */
1089 static DECLCALLBACK(int) notifyDnDDispatcher(void *pvExtension, uint32_t u32Function, void *pvParms, uint32_t cbParms);
1090 /** @} */
1091
1092 /** @name Static helper methods.
1093 * @{ */
1094 static bool isFormatInFormatList(const com::Utf8Str &strFormat, const GuestDnDMIMEList &lstFormats);
1095 static GuestDnDMIMEList toFormatList(const com::Utf8Str &strFormats);
1096 static com::Utf8Str toFormatString(const GuestDnDMIMEList &lstFormats);
1097 static GuestDnDMIMEList toFilteredFormatList(const GuestDnDMIMEList &lstFormatsSupported, const GuestDnDMIMEList &lstFormatsWanted);
1098 static GuestDnDMIMEList toFilteredFormatList(const GuestDnDMIMEList &lstFormatsSupported, const com::Utf8Str &strFormatsWanted);
1099 static DnDAction_T toMainAction(VBOXDNDACTION dndAction);
1100 static std::vector<DnDAction_T> toMainActions(VBOXDNDACTIONLIST dndActionList);
1101 static VBOXDNDACTION toHGCMAction(DnDAction_T enmAction);
1102 static void toHGCMActions(DnDAction_T enmDefAction, VBOXDNDACTION *pDefAction, const std::vector<DnDAction_T> vecAllowedActions, VBOXDNDACTIONLIST *pLstAllowedActions);
1103 /** @} */
1104
1105protected:
1106
1107 /** @name Singleton properties.
1108 * @{ */
1109 /** List of supported default MIME/Content-type formats. */
1110 GuestDnDMIMEList m_strDefaultFormats;
1111 /** Pointer to guest implementation. */
1112 const ComObjPtr<Guest> m_pGuest;
1113 /** The current (last) response from the guest. At the
1114 * moment we only support only response a time (ARQ-style). */
1115 GuestDnDResponse *m_pResponse;
1116 /** @} */
1117
1118private:
1119
1120 /** Staic pointer to singleton instance. */
1121 static GuestDnD *s_pInstance;
1122};
1123
1124/** Access to the GuestDnD's singleton instance.
1125 * @todo r=bird: Please add a 'Get' or something to this as it currently looks
1126 * like a class instantiation rather than a getter. Alternatively, use
1127 * UPPER_CASE like the coding guideline suggest for macros. */
1128#define GuestDnDInst() GuestDnD::getInstance()
1129
1130/** List of pointers to guest DnD Messages. */
1131typedef std::list<GuestDnDMsg *> GuestDnDMsgList;
1132
1133/**
1134 * IDnDBase class implementation for sharing code between
1135 * IGuestDnDSource and IGuestDnDTarget implementation.
1136 */
1137class GuestDnDBase
1138{
1139protected:
1140
1141 GuestDnDBase(void);
1142
1143protected:
1144
1145 /** Shared (internal) IDnDBase method implementations.
1146 * @{ */
1147 HRESULT i_isFormatSupported(const com::Utf8Str &aFormat, BOOL *aSupported);
1148 HRESULT i_getFormats(GuestDnDMIMEList &aFormats);
1149 HRESULT i_addFormats(const GuestDnDMIMEList &aFormats);
1150 HRESULT i_removeFormats(const GuestDnDMIMEList &aFormats);
1151
1152 HRESULT i_getProtocolVersion(ULONG *puVersion);
1153 /** @} */
1154
1155protected:
1156
1157 int getProtocolVersion(uint32_t *puVersion);
1158
1159 /** @name Functions for handling a simple host HGCM message queue.
1160 * @{ */
1161 int msgQueueAdd(GuestDnDMsg *pMsg);
1162 GuestDnDMsg *msgQueueGetNext(void);
1163 void msgQueueRemoveNext(void);
1164 void msgQueueClear(void);
1165 /** @} */
1166
1167 int sendCancel(void);
1168 int updateProgress(GuestDnDData *pData, GuestDnDResponse *pResp, uint32_t cbDataAdd = 0);
1169 int waitForEvent(GuestDnDCallbackEvent *pEvent, GuestDnDResponse *pResp, RTMSINTERVAL msTimeout);
1170
1171protected:
1172
1173 /** @name Public attributes (through getters/setters).
1174 * @{ */
1175 /** Pointer to guest implementation. */
1176 const ComObjPtr<Guest> m_pGuest;
1177 /** List of supported MIME types by the source. */
1178 GuestDnDMIMEList m_lstFmtSupported;
1179 /** List of offered MIME types to the counterpart. */
1180 GuestDnDMIMEList m_lstFmtOffered;
1181 /** @} */
1182
1183 /**
1184 * Internal stuff.
1185 */
1186 struct
1187 {
1188 /** Number of active transfers (guest->host or host->guest). */
1189 uint32_t m_cTransfersPending;
1190 /** The DnD protocol version to use, depending on the
1191 * installed Guest Additions. See DragAndDropSvc.h for
1192 * a protocol changelog. */
1193 uint32_t m_uProtocolVersion;
1194 /** Outgoing message queue (FIFO). */
1195 GuestDnDMsgList m_lstMsgOut;
1196 } mDataBase;
1197};
1198#endif /* !MAIN_INCLUDED_GuestDnDPrivate_h */
1199
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