VirtualBox

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

Last change on this file since 69498 was 69498, checked in by vboxsync, 7 years ago

backed out r118835 as it incorrectly updated the 'This file is based on' file headers.

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