VirtualBox

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

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

DnD: Renaming for DnDURIObject / DnDURIList classes, some typedefs to distinguish flags better. No functional changes.

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