[42261] | 1 | /* $Id: GuestDnDPrivate.cpp 72980 2018-07-08 14:32:09Z vboxsync $ */
|
---|
| 2 | /** @file
|
---|
[67914] | 3 | * Private guest drag and drop code, used by GuestDnDTarget + GuestDnDSource.
|
---|
[42261] | 4 | */
|
---|
| 5 |
|
---|
| 6 | /*
|
---|
[69500] | 7 | * Copyright (C) 2011-2017 Oracle Corporation
|
---|
[42261] | 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 |
|
---|
[67914] | 18 | #define LOG_GROUP LOG_GROUP_GUEST_DND
|
---|
| 19 | #include "LoggingNew.h"
|
---|
| 20 |
|
---|
[42261] | 21 | #include "GuestImpl.h"
|
---|
| 22 | #include "AutoCaller.h"
|
---|
| 23 |
|
---|
| 24 | #ifdef VBOX_WITH_DRAG_AND_DROP
|
---|
| 25 | # include "ConsoleImpl.h"
|
---|
| 26 | # include "ProgressImpl.h"
|
---|
[51476] | 27 | # include "GuestDnDPrivate.h"
|
---|
[42261] | 28 |
|
---|
[51476] | 29 | # include <algorithm>
|
---|
| 30 |
|
---|
[50460] | 31 | # include <iprt/dir.h>
|
---|
| 32 | # include <iprt/path.h>
|
---|
| 33 | # include <iprt/stream.h>
|
---|
| 34 | # include <iprt/semaphore.h>
|
---|
| 35 | # include <iprt/cpp/utils.h>
|
---|
| 36 |
|
---|
[42261] | 37 | # include <VMMDev.h>
|
---|
| 38 |
|
---|
[50460] | 39 | # include <VBox/GuestHost/DragAndDrop.h>
|
---|
[42261] | 40 | # include <VBox/HostServices/DragAndDropSvc.h>
|
---|
[55422] | 41 | # include <VBox/version.h>
|
---|
[42261] | 42 |
|
---|
[67914] | 43 | /** @page pg_main_dnd Dungeons & Dragons - Overview
|
---|
[51476] | 44 | * Overview:
|
---|
[42261] | 45 | *
|
---|
| 46 | * Drag and Drop is handled over the internal HGCM service for the host <->
|
---|
| 47 | * guest communication. Beside that we need to map the Drag and Drop protocols
|
---|
| 48 | * of the various OS's we support to our internal channels, this is also highly
|
---|
| 49 | * communicative in both directions. Unfortunately HGCM isn't really designed
|
---|
| 50 | * for that. Next we have to foul some of the components. This includes to
|
---|
| 51 | * trick X11 on the guest side, but also Qt needs to be tricked on the host
|
---|
| 52 | * side a little bit.
|
---|
| 53 | *
|
---|
| 54 | * The following components are involved:
|
---|
| 55 | *
|
---|
| 56 | * 1. GUI: Uses the Qt classes for Drag and Drop and mainly forward the content
|
---|
| 57 | * of it to the Main IGuest interface (see UIDnDHandler.cpp).
|
---|
| 58 | * 2. Main: Public interface for doing Drag and Drop. Also manage the IProgress
|
---|
[49891] | 59 | * interfaces for blocking the caller by showing a progress dialog (see
|
---|
| 60 | * this file).
|
---|
[42261] | 61 | * 3. HGCM service: Handle all messages from the host to the guest at once and
|
---|
[49891] | 62 | * encapsulate the internal communication details (see dndmanager.cpp and
|
---|
| 63 | * friends).
|
---|
[42261] | 64 | * 4. Guest additions: Split into the platform neutral part (see
|
---|
| 65 | * VBoxGuestR3LibDragAndDrop.cpp) and the guest OS specific parts.
|
---|
| 66 | * Receive/send message from/to the HGCM service and does all guest specific
|
---|
[49891] | 67 | * operations. Currently only X11 is supported (see draganddrop.cpp within
|
---|
| 68 | * VBoxClient).
|
---|
[42261] | 69 | *
|
---|
[49891] | 70 | * Host -> Guest:
|
---|
[42261] | 71 | * 1. There are DnD Enter, Move, Leave events which are send exactly like this
|
---|
| 72 | * to the guest. The info includes the pos, mimetypes and allowed actions.
|
---|
| 73 | * The guest has to respond with an action it would accept, so the GUI could
|
---|
| 74 | * change the cursor.
|
---|
[51476] | 75 | * 2. On drop, first a drop event is sent. If this is accepted a drop data
|
---|
[42261] | 76 | * event follows. This blocks the GUI and shows some progress indicator.
|
---|
| 77 | *
|
---|
| 78 | * Guest -> Host:
|
---|
| 79 | * 1. The GUI is asking the guest if a DnD event is pending when the user moves
|
---|
| 80 | * the cursor out of the view window. If so, this returns the mimetypes and
|
---|
| 81 | * allowed actions.
|
---|
| 82 | * (2. On every mouse move this is asked again, to make sure the DnD event is
|
---|
| 83 | * still valid.)
|
---|
| 84 | * 3. On drop the host request the data from the guest. This blocks the GUI and
|
---|
| 85 | * shows some progress indicator.
|
---|
| 86 | *
|
---|
| 87 | * Some hints:
|
---|
[51476] | 88 | * m_strSupportedFormats here in this file defines the allowed mime-types.
|
---|
[42261] | 89 | * This is necessary because we need special handling for some of the
|
---|
| 90 | * mime-types. E.g. for URI lists we need to transfer the actual dirs and
|
---|
| 91 | * files. Text EOL may to be changed. Also unknown mime-types may need special
|
---|
| 92 | * handling as well, which may lead to undefined behavior in the host/guest, if
|
---|
| 93 | * not done.
|
---|
| 94 | *
|
---|
| 95 | * Dropping of a directory, means recursively transferring _all_ the content.
|
---|
| 96 | *
|
---|
[49891] | 97 | * Directories and files are placed into the user's temporary directory on the
|
---|
| 98 | * guest (e.g. /tmp/VirtualBox Dropped Files). We can't delete them after the
|
---|
[42261] | 99 | * DnD operation, because we didn't know what the DnD target does with it. E.g.
|
---|
| 100 | * it could just be opened in place. This could lead ofc to filling up the disk
|
---|
| 101 | * within the guest. To inform the user about this, a small app could be
|
---|
| 102 | * developed which scans this directory regularly and inform the user with a
|
---|
| 103 | * tray icon hint (and maybe the possibility to clean this up instantly). The
|
---|
| 104 | * same has to be done in the G->H direction when it is implemented.
|
---|
| 105 | *
|
---|
| 106 | * Of course only regularly files are supported. Symlinks are resolved and
|
---|
| 107 | * transfered as regularly files. First we don't know if the other side support
|
---|
| 108 | * symlinks at all and second they could point to somewhere in a directory tree
|
---|
| 109 | * which not exists on the other side.
|
---|
| 110 | *
|
---|
| 111 | * The code tries to preserve the file modes of the transfered dirs/files. This
|
---|
| 112 | * is useful (and maybe necessary) for two things:
|
---|
| 113 | * 1. If a file is executable, it should be also after the transfer, so the
|
---|
| 114 | * user can just execute it, without manually tweaking the modes first.
|
---|
| 115 | * 2. If a dir/file is not accessible by group/others in the host, it shouldn't
|
---|
| 116 | * be in the guest.
|
---|
| 117 | * In any case, the user mode is always set to rwx (so that we can access it
|
---|
| 118 | * ourself, in e.g. for a cleanup case after cancel).
|
---|
| 119 | *
|
---|
| 120 | * Cancel is supported in both directions and cleans up all previous steps
|
---|
| 121 | * (thats is: deleting already transfered dirs/files).
|
---|
| 122 | *
|
---|
| 123 | * In general I propose the following changes in the VBox HGCM infrastructure
|
---|
| 124 | * for the future:
|
---|
| 125 | * - Currently it isn't really possible to send messages to the guest from the
|
---|
| 126 | * host. The host informs the guest just that there is something, the guest
|
---|
| 127 | * than has to ask which message and depending on that send the appropriate
|
---|
| 128 | * message to the host, which is filled with the right data.
|
---|
| 129 | * - There is no generic interface for sending bigger memory blocks to/from the
|
---|
| 130 | * guest. This is now done here, but I guess was also necessary for e.g.
|
---|
| 131 | * guest execution. So something generic which brake this up into smaller
|
---|
| 132 | * blocks and send it would be nice (with all the error handling and such
|
---|
| 133 | * ofc).
|
---|
| 134 | * - I developed a "protocol" for the DnD communication here. So the host and
|
---|
| 135 | * the guest have always to match in the revision. This is ofc bad, because
|
---|
| 136 | * the additions could be outdated easily. So some generic protocol number
|
---|
| 137 | * support in HGCM for asking the host and the guest of the support version,
|
---|
| 138 | * would be nice. Ofc at least the host should be able to talk to the guest,
|
---|
| 139 | * even when the version is below the host one.
|
---|
| 140 | * All this stuff would be useful for the current services, but also for future
|
---|
| 141 | * onces.
|
---|
| 142 | *
|
---|
[51476] | 143 | ** @todo
|
---|
| 144 | * - ESC doesn't really work (on Windows guests it's already implemented)
|
---|
[42261] | 145 | * ... in any case it seems a little bit difficult to handle from the Qt
|
---|
| 146 | * side. Maybe also a host specific implementation becomes necessary ...
|
---|
| 147 | * this would be really worst ofc.
|
---|
[51476] | 148 | * - Add support for more mime-types (especially images, csv)
|
---|
| 149 | * - Test unusual behavior:
|
---|
[42261] | 150 | * - DnD service crash in the guest during a DnD op (e.g. crash of VBoxClient or X11)
|
---|
[51476] | 151 | * - Not expected order of the events between HGCM and the guest
|
---|
[42261] | 152 | * - Security considerations: We transfer a lot of memory between the guest and
|
---|
| 153 | * the host and even allow the creation of dirs/files. Maybe there should be
|
---|
| 154 | * limits introduced to preventing DOS attacks or filling up all the memory
|
---|
| 155 | * (both in the host and the guest).
|
---|
| 156 | */
|
---|
| 157 |
|
---|
[55512] | 158 | GuestDnDCallbackEvent::~GuestDnDCallbackEvent(void)
|
---|
| 159 | {
|
---|
| 160 | if (NIL_RTSEMEVENT != mSemEvent)
|
---|
| 161 | RTSemEventDestroy(mSemEvent);
|
---|
| 162 | }
|
---|
| 163 |
|
---|
| 164 | int GuestDnDCallbackEvent::Reset(void)
|
---|
| 165 | {
|
---|
| 166 | int rc = VINF_SUCCESS;
|
---|
| 167 |
|
---|
| 168 | if (NIL_RTSEMEVENT == mSemEvent)
|
---|
| 169 | rc = RTSemEventCreate(&mSemEvent);
|
---|
| 170 |
|
---|
| 171 | mRc = VINF_SUCCESS;
|
---|
| 172 | return rc;
|
---|
| 173 | }
|
---|
| 174 |
|
---|
[55963] | 175 | int GuestDnDCallbackEvent::Notify(int rc /* = VINF_SUCCESS */)
|
---|
[55512] | 176 | {
|
---|
| 177 | mRc = rc;
|
---|
| 178 | return RTSemEventSignal(mSemEvent);
|
---|
| 179 | }
|
---|
| 180 |
|
---|
| 181 | int GuestDnDCallbackEvent::Wait(RTMSINTERVAL msTimeout)
|
---|
| 182 | {
|
---|
| 183 | return RTSemEventWait(mSemEvent, msTimeout);
|
---|
| 184 | }
|
---|
| 185 |
|
---|
| 186 | ///////////////////////////////////////////////////////////////////////////////
|
---|
| 187 |
|
---|
[51476] | 188 | GuestDnDResponse::GuestDnDResponse(const ComObjPtr<Guest>& pGuest)
|
---|
| 189 | : m_EventSem(NIL_RTSEMEVENT)
|
---|
| 190 | , m_defAction(0)
|
---|
| 191 | , m_allActions(0)
|
---|
[58212] | 192 | , m_pParent(pGuest)
|
---|
[42261] | 193 | {
|
---|
| 194 | int rc = RTSemEventCreate(&m_EventSem);
|
---|
[55422] | 195 | if (RT_FAILURE(rc))
|
---|
| 196 | throw rc;
|
---|
[42261] | 197 | }
|
---|
| 198 |
|
---|
[51476] | 199 | GuestDnDResponse::~GuestDnDResponse(void)
|
---|
[42261] | 200 | {
|
---|
[50265] | 201 | reset();
|
---|
[50734] | 202 |
|
---|
[42261] | 203 | int rc = RTSemEventDestroy(m_EventSem);
|
---|
| 204 | AssertRC(rc);
|
---|
| 205 | }
|
---|
| 206 |
|
---|
[55422] | 207 | int GuestDnDResponse::notifyAboutGuestResponse(void) const
|
---|
[50609] | 208 | {
|
---|
| 209 | return RTSemEventSignal(m_EventSem);
|
---|
| 210 | }
|
---|
| 211 |
|
---|
[51476] | 212 | void GuestDnDResponse::reset(void)
|
---|
[42261] | 213 | {
|
---|
[50734] | 214 | LogFlowThisFuncEnter();
|
---|
| 215 |
|
---|
[55512] | 216 | m_defAction = 0;
|
---|
[50734] | 217 | m_allActions = 0;
|
---|
| 218 |
|
---|
[57221] | 219 | m_lstFormats.clear();
|
---|
[42261] | 220 | }
|
---|
| 221 |
|
---|
[51476] | 222 | HRESULT GuestDnDResponse::resetProgress(const ComObjPtr<Guest>& pParent)
|
---|
[42261] | 223 | {
|
---|
[58212] | 224 | m_pProgress.setNull();
|
---|
[55549] | 225 |
|
---|
[58212] | 226 | HRESULT hr = m_pProgress.createObject();
|
---|
[55549] | 227 | if (SUCCEEDED(hr))
|
---|
[42261] | 228 | {
|
---|
[58212] | 229 | hr = m_pProgress->init(static_cast<IGuest *>(pParent),
|
---|
| 230 | Bstr(pParent->tr("Dropping data")).raw(),
|
---|
| 231 | TRUE /* aCancelable */);
|
---|
[42261] | 232 | }
|
---|
[55549] | 233 |
|
---|
| 234 | return hr;
|
---|
[42261] | 235 | }
|
---|
| 236 |
|
---|
[55422] | 237 | bool GuestDnDResponse::isProgressCanceled(void) const
|
---|
| 238 | {
|
---|
| 239 | BOOL fCanceled;
|
---|
[58212] | 240 | if (!m_pProgress.isNull())
|
---|
[55422] | 241 | {
|
---|
[58212] | 242 | HRESULT hr = m_pProgress->COMGETTER(Canceled)(&fCanceled);
|
---|
[55422] | 243 | AssertComRC(hr);
|
---|
| 244 | }
|
---|
[55512] | 245 | else
|
---|
| 246 | fCanceled = TRUE;
|
---|
[55422] | 247 |
|
---|
| 248 | return RT_BOOL(fCanceled);
|
---|
| 249 | }
|
---|
| 250 |
|
---|
| 251 | int GuestDnDResponse::setCallback(uint32_t uMsg, PFNGUESTDNDCALLBACK pfnCallback, void *pvUser /* = NULL */)
|
---|
| 252 | {
|
---|
[55424] | 253 | GuestDnDCallbackMap::iterator it = m_mapCallbacks.find(uMsg);
|
---|
[55422] | 254 |
|
---|
| 255 | /* Add. */
|
---|
| 256 | if (pfnCallback)
|
---|
| 257 | {
|
---|
| 258 | if (it == m_mapCallbacks.end())
|
---|
| 259 | {
|
---|
| 260 | m_mapCallbacks[uMsg] = GuestDnDCallback(pfnCallback, uMsg, pvUser);
|
---|
| 261 | return VINF_SUCCESS;
|
---|
| 262 | }
|
---|
| 263 |
|
---|
| 264 | AssertMsgFailed(("Callback for message %RU32 already registered\n", uMsg));
|
---|
| 265 | return VERR_ALREADY_EXISTS;
|
---|
| 266 | }
|
---|
| 267 |
|
---|
| 268 | /* Remove. */
|
---|
| 269 | if (it != m_mapCallbacks.end())
|
---|
| 270 | m_mapCallbacks.erase(it);
|
---|
| 271 |
|
---|
| 272 | return VINF_SUCCESS;
|
---|
| 273 | }
|
---|
| 274 |
|
---|
[51476] | 275 | int GuestDnDResponse::setProgress(unsigned uPercentage,
|
---|
[55963] | 276 | uint32_t uStatus,
|
---|
| 277 | int rcOp /* = VINF_SUCCESS */, const Utf8Str &strMsg /* = "" */)
|
---|
[42261] | 278 | {
|
---|
[63259] | 279 | RT_NOREF(rcOp);
|
---|
[55963] | 280 | LogFlowFunc(("uStatus=%RU32, uPercentage=%RU32, rcOp=%Rrc, strMsg=%s\n",
|
---|
| 281 | uStatus, uPercentage, rcOp, strMsg.c_str()));
|
---|
[49891] | 282 |
|
---|
[55556] | 283 | int rc = VINF_SUCCESS;
|
---|
[58212] | 284 | if (!m_pProgress.isNull())
|
---|
[42261] | 285 | {
|
---|
| 286 | BOOL fCompleted;
|
---|
[58212] | 287 | HRESULT hr = m_pProgress->COMGETTER(Completed)(&fCompleted);
|
---|
[55422] | 288 | AssertComRC(hr);
|
---|
| 289 |
|
---|
| 290 | BOOL fCanceled;
|
---|
[58212] | 291 | hr = m_pProgress->COMGETTER(Canceled)(&fCanceled);
|
---|
[55422] | 292 | AssertComRC(hr);
|
---|
| 293 |
|
---|
[55512] | 294 | LogFlowFunc(("Current: fCompleted=%RTbool, fCanceled=%RTbool\n", fCompleted, fCanceled));
|
---|
[55422] | 295 |
|
---|
[42261] | 296 | if (!fCompleted)
|
---|
| 297 | {
|
---|
[55422] | 298 | switch (uStatus)
|
---|
[49891] | 299 | {
|
---|
[55422] | 300 | case DragAndDropSvc::DND_PROGRESS_ERROR:
|
---|
| 301 | {
|
---|
[58212] | 302 | hr = m_pProgress->i_notifyComplete(VBOX_E_IPRT_ERROR,
|
---|
| 303 | COM_IIDOF(IGuest),
|
---|
| 304 | m_pParent->getComponentName(), strMsg.c_str());
|
---|
[55422] | 305 | reset();
|
---|
| 306 | break;
|
---|
| 307 | }
|
---|
[50561] | 308 |
|
---|
[55422] | 309 | case DragAndDropSvc::DND_PROGRESS_CANCELLED:
|
---|
| 310 | {
|
---|
[58329] | 311 | hr = m_pProgress->Cancel();
|
---|
[55422] | 312 | AssertComRC(hr);
|
---|
[58330] | 313 | hr = m_pProgress->i_notifyComplete(S_OK);
|
---|
[58329] | 314 | AssertComRC(hr);
|
---|
[55422] | 315 |
|
---|
| 316 | reset();
|
---|
| 317 | break;
|
---|
| 318 | }
|
---|
| 319 |
|
---|
| 320 | case DragAndDropSvc::DND_PROGRESS_RUNNING:
|
---|
| 321 | case DragAndDropSvc::DND_PROGRESS_COMPLETE:
|
---|
| 322 | {
|
---|
| 323 | if (!fCanceled)
|
---|
| 324 | {
|
---|
[58212] | 325 | hr = m_pProgress->SetCurrentOperationProgress(uPercentage);
|
---|
[55422] | 326 | AssertComRC(hr);
|
---|
| 327 | if ( uStatus == DragAndDropSvc::DND_PROGRESS_COMPLETE
|
---|
| 328 | || uPercentage >= 100)
|
---|
| 329 | {
|
---|
[58212] | 330 | hr = m_pProgress->i_notifyComplete(S_OK);
|
---|
[55422] | 331 | AssertComRC(hr);
|
---|
| 332 | }
|
---|
| 333 | }
|
---|
| 334 | break;
|
---|
| 335 | }
|
---|
| 336 |
|
---|
| 337 | default:
|
---|
| 338 | break;
|
---|
[42261] | 339 | }
|
---|
| 340 | }
|
---|
[55512] | 341 |
|
---|
[58212] | 342 | hr = m_pProgress->COMGETTER(Completed)(&fCompleted);
|
---|
[55512] | 343 | AssertComRC(hr);
|
---|
[58212] | 344 | hr = m_pProgress->COMGETTER(Canceled)(&fCanceled);
|
---|
[55512] | 345 | AssertComRC(hr);
|
---|
| 346 |
|
---|
| 347 | LogFlowFunc(("New: fCompleted=%RTbool, fCanceled=%RTbool\n", fCompleted, fCanceled));
|
---|
[42261] | 348 | }
|
---|
[49891] | 349 |
|
---|
[55556] | 350 | LogFlowFuncLeaveRC(rc);
|
---|
| 351 | return rc;
|
---|
[42261] | 352 | }
|
---|
| 353 |
|
---|
[55422] | 354 | int GuestDnDResponse::onDispatch(uint32_t u32Function, void *pvParms, uint32_t cbParms)
|
---|
[42261] | 355 | {
|
---|
[55422] | 356 | LogFlowFunc(("u32Function=%RU32, pvParms=%p, cbParms=%RU32\n", u32Function, pvParms, cbParms));
|
---|
[42261] | 357 |
|
---|
[55422] | 358 | int rc = VERR_WRONG_ORDER; /* Play safe. */
|
---|
| 359 | bool fTryCallbacks = false;
|
---|
[50609] | 360 |
|
---|
[55422] | 361 | switch (u32Function)
|
---|
[50734] | 362 | {
|
---|
[58230] | 363 | case DragAndDropSvc::GUEST_DND_CONNECT:
|
---|
| 364 | {
|
---|
[58329] | 365 | LogThisFunc(("Client connected\n"));
|
---|
| 366 |
|
---|
| 367 | /* Nothing to do here (yet). */
|
---|
[58230] | 368 | rc = VINF_SUCCESS;
|
---|
| 369 | break;
|
---|
| 370 | }
|
---|
| 371 |
|
---|
[58329] | 372 | case DragAndDropSvc::GUEST_DND_DISCONNECT:
|
---|
| 373 | {
|
---|
| 374 | LogThisFunc(("Client disconnected\n"));
|
---|
| 375 | rc = setProgress(100, DND_PROGRESS_CANCELLED, VINF_SUCCESS);
|
---|
| 376 | break;
|
---|
| 377 | }
|
---|
| 378 |
|
---|
[55422] | 379 | case DragAndDropSvc::GUEST_DND_HG_ACK_OP:
|
---|
[50734] | 380 | {
|
---|
[55422] | 381 | DragAndDropSvc::PVBOXDNDCBHGACKOPDATA pCBData = reinterpret_cast<DragAndDropSvc::PVBOXDNDCBHGACKOPDATA>(pvParms);
|
---|
| 382 | AssertPtr(pCBData);
|
---|
| 383 | AssertReturn(sizeof(DragAndDropSvc::VBOXDNDCBHGACKOPDATA) == cbParms, VERR_INVALID_PARAMETER);
|
---|
[58257] | 384 | AssertReturn(DragAndDropSvc::CB_MAGIC_DND_HG_ACK_OP == pCBData->hdr.uMagic, VERR_INVALID_PARAMETER);
|
---|
[55422] | 385 |
|
---|
| 386 | setDefAction(pCBData->uAction);
|
---|
| 387 | rc = notifyAboutGuestResponse();
|
---|
| 388 | break;
|
---|
| 389 | }
|
---|
| 390 |
|
---|
| 391 | case DragAndDropSvc::GUEST_DND_HG_REQ_DATA:
|
---|
| 392 | {
|
---|
| 393 | DragAndDropSvc::PVBOXDNDCBHGREQDATADATA pCBData = reinterpret_cast<DragAndDropSvc::PVBOXDNDCBHGREQDATADATA>(pvParms);
|
---|
| 394 | AssertPtr(pCBData);
|
---|
| 395 | AssertReturn(sizeof(DragAndDropSvc::VBOXDNDCBHGREQDATADATA) == cbParms, VERR_INVALID_PARAMETER);
|
---|
[58257] | 396 | AssertReturn(DragAndDropSvc::CB_MAGIC_DND_HG_REQ_DATA == pCBData->hdr.uMagic, VERR_INVALID_PARAMETER);
|
---|
[55422] | 397 |
|
---|
[58232] | 398 | if ( pCBData->cbFormat == 0
|
---|
| 399 | || pCBData->cbFormat > _64K /** @todo Make this configurable? */
|
---|
| 400 | || pCBData->pszFormat == NULL)
|
---|
[55539] | 401 | {
|
---|
| 402 | rc = VERR_INVALID_PARAMETER;
|
---|
| 403 | }
|
---|
[58232] | 404 | else if (!RTStrIsValidEncoding(pCBData->pszFormat))
|
---|
| 405 | {
|
---|
| 406 | rc = VERR_INVALID_PARAMETER;
|
---|
| 407 | }
|
---|
[55539] | 408 | else
|
---|
| 409 | {
|
---|
[57221] | 410 | setFormats(GuestDnD::toFormatList(pCBData->pszFormat));
|
---|
[55539] | 411 | rc = VINF_SUCCESS;
|
---|
| 412 | }
|
---|
| 413 |
|
---|
| 414 | int rc2 = notifyAboutGuestResponse();
|
---|
| 415 | if (RT_SUCCESS(rc))
|
---|
| 416 | rc = rc2;
|
---|
[55422] | 417 | break;
|
---|
| 418 | }
|
---|
| 419 |
|
---|
| 420 | case DragAndDropSvc::GUEST_DND_HG_EVT_PROGRESS:
|
---|
| 421 | {
|
---|
| 422 | DragAndDropSvc::PVBOXDNDCBHGEVTPROGRESSDATA pCBData =
|
---|
| 423 | reinterpret_cast<DragAndDropSvc::PVBOXDNDCBHGEVTPROGRESSDATA>(pvParms);
|
---|
| 424 | AssertPtr(pCBData);
|
---|
| 425 | AssertReturn(sizeof(DragAndDropSvc::VBOXDNDCBHGEVTPROGRESSDATA) == cbParms, VERR_INVALID_PARAMETER);
|
---|
[58257] | 426 | AssertReturn(DragAndDropSvc::CB_MAGIC_DND_HG_EVT_PROGRESS == pCBData->hdr.uMagic, VERR_INVALID_PARAMETER);
|
---|
[55422] | 427 |
|
---|
| 428 | rc = setProgress(pCBData->uPercentage, pCBData->uStatus, pCBData->rc);
|
---|
[50734] | 429 | if (RT_SUCCESS(rc))
|
---|
[55422] | 430 | rc = notifyAboutGuestResponse();
|
---|
| 431 | break;
|
---|
| 432 | }
|
---|
| 433 | #ifdef VBOX_WITH_DRAG_AND_DROP_GH
|
---|
| 434 | case DragAndDropSvc::GUEST_DND_GH_ACK_PENDING:
|
---|
| 435 | {
|
---|
| 436 | DragAndDropSvc::PVBOXDNDCBGHACKPENDINGDATA pCBData =
|
---|
| 437 | reinterpret_cast<DragAndDropSvc::PVBOXDNDCBGHACKPENDINGDATA>(pvParms);
|
---|
| 438 | AssertPtr(pCBData);
|
---|
| 439 | AssertReturn(sizeof(DragAndDropSvc::VBOXDNDCBGHACKPENDINGDATA) == cbParms, VERR_INVALID_PARAMETER);
|
---|
[58257] | 440 | AssertReturn(DragAndDropSvc::CB_MAGIC_DND_GH_ACK_PENDING == pCBData->hdr.uMagic, VERR_INVALID_PARAMETER);
|
---|
[50734] | 441 |
|
---|
[58232] | 442 | if ( pCBData->cbFormat == 0
|
---|
| 443 | || pCBData->cbFormat > _64K /** @todo Make the maximum size configurable? */
|
---|
| 444 | || pCBData->pszFormat == NULL)
|
---|
[55539] | 445 | {
|
---|
| 446 | rc = VERR_INVALID_PARAMETER;
|
---|
| 447 | }
|
---|
[58232] | 448 | else if (!RTStrIsValidEncoding(pCBData->pszFormat))
|
---|
| 449 | {
|
---|
| 450 | rc = VERR_INVALID_PARAMETER;
|
---|
| 451 | }
|
---|
[55539] | 452 | else
|
---|
| 453 | {
|
---|
[57221] | 454 | setFormats (GuestDnD::toFormatList(pCBData->pszFormat));
|
---|
[55539] | 455 | setDefAction (pCBData->uDefAction);
|
---|
| 456 | setAllActions(pCBData->uAllActions);
|
---|
[50734] | 457 |
|
---|
[55539] | 458 | rc = VINF_SUCCESS;
|
---|
| 459 | }
|
---|
| 460 |
|
---|
| 461 | int rc2 = notifyAboutGuestResponse();
|
---|
| 462 | if (RT_SUCCESS(rc))
|
---|
| 463 | rc = rc2;
|
---|
[55422] | 464 | break;
|
---|
[50734] | 465 | }
|
---|
[55422] | 466 | #endif /* VBOX_WITH_DRAG_AND_DROP_GH */
|
---|
| 467 | default:
|
---|
| 468 | /* * Try if the event is covered by a registered callback. */
|
---|
| 469 | fTryCallbacks = true;
|
---|
| 470 | break;
|
---|
[50734] | 471 | }
|
---|
| 472 |
|
---|
[55422] | 473 | /*
|
---|
| 474 | * Try the host's installed callbacks (if any).
|
---|
| 475 | */
|
---|
| 476 | if (fTryCallbacks)
|
---|
[50734] | 477 | {
|
---|
[55422] | 478 | GuestDnDCallbackMap::const_iterator it = m_mapCallbacks.find(u32Function);
|
---|
| 479 | if (it != m_mapCallbacks.end())
|
---|
| 480 | {
|
---|
| 481 | AssertPtr(it->second.pfnCallback);
|
---|
| 482 | rc = it->second.pfnCallback(u32Function, pvParms, cbParms, it->second.pvUser);
|
---|
| 483 | }
|
---|
| 484 | else
|
---|
[57291] | 485 | {
|
---|
[57295] | 486 | LogFlowFunc(("No callback for function %RU32 defined (%zu callbacks total)\n", u32Function, m_mapCallbacks.size()));
|
---|
[57287] | 487 | rc = VERR_NOT_SUPPORTED; /* Tell the guest. */
|
---|
[57291] | 488 | }
|
---|
[50734] | 489 | }
|
---|
| 490 |
|
---|
[55422] | 491 | LogFlowFunc(("Returning rc=%Rrc\n", rc));
|
---|
[50734] | 492 | return rc;
|
---|
| 493 | }
|
---|
| 494 |
|
---|
[55422] | 495 | HRESULT GuestDnDResponse::queryProgressTo(IProgress **ppProgress)
|
---|
| 496 | {
|
---|
[58212] | 497 | return m_pProgress.queryInterfaceTo(ppProgress);
|
---|
[55422] | 498 | }
|
---|
| 499 |
|
---|
| 500 | int GuestDnDResponse::waitForGuestResponse(RTMSINTERVAL msTimeout /*= 500 */) const
|
---|
| 501 | {
|
---|
| 502 | int rc = RTSemEventWait(m_EventSem, msTimeout);
|
---|
| 503 | #ifdef DEBUG_andy
|
---|
| 504 | LogFlowFunc(("msTimeout=%RU32, rc=%Rrc\n", msTimeout, rc));
|
---|
| 505 | #endif
|
---|
| 506 | return rc;
|
---|
| 507 | }
|
---|
| 508 |
|
---|
[50609] | 509 | ///////////////////////////////////////////////////////////////////////////////
|
---|
| 510 |
|
---|
[51476] | 511 | GuestDnD* GuestDnD::s_pInstance = NULL;
|
---|
| 512 |
|
---|
| 513 | GuestDnD::GuestDnD(const ComObjPtr<Guest> &pGuest)
|
---|
| 514 | : m_pGuest(pGuest)
|
---|
[42261] | 515 | {
|
---|
[51476] | 516 | LogFlowFuncEnter();
|
---|
| 517 |
|
---|
| 518 | m_pResponse = new GuestDnDResponse(pGuest);
|
---|
| 519 |
|
---|
[51556] | 520 | /* List of supported default MIME types. */
|
---|
[56653] | 521 | LogRel2(("DnD: Supported default host formats:\n"));
|
---|
[51556] | 522 | const com::Utf8Str arrEntries[] = { VBOX_DND_FORMATS_DEFAULT };
|
---|
[51476] | 523 | for (size_t i = 0; i < RT_ELEMENTS(arrEntries); i++)
|
---|
[56653] | 524 | {
|
---|
[51556] | 525 | m_strDefaultFormats.push_back(arrEntries[i]);
|
---|
[56653] | 526 | LogRel2(("DnD: \t%s\n", arrEntries[i].c_str()));
|
---|
| 527 | }
|
---|
[51476] | 528 | }
|
---|
| 529 |
|
---|
| 530 | GuestDnD::~GuestDnD(void)
|
---|
| 531 | {
|
---|
| 532 | LogFlowFuncEnter();
|
---|
| 533 |
|
---|
| 534 | if (m_pResponse)
|
---|
| 535 | delete m_pResponse;
|
---|
| 536 | }
|
---|
| 537 |
|
---|
[57221] | 538 | HRESULT GuestDnD::adjustScreenCoordinates(ULONG uScreenId, ULONG *puX, ULONG *puY) const
|
---|
[51476] | 539 | {
|
---|
| 540 | /** @todo r=andy Save the current screen's shifting coordinates to speed things up.
|
---|
| 541 | * Only query for new offsets when the screen ID has changed. */
|
---|
| 542 |
|
---|
[42261] | 543 | /* For multi-monitor support we need to add shift values to the coordinates
|
---|
| 544 | * (depending on the screen number). */
|
---|
[52082] | 545 | ComObjPtr<Console> pConsole = m_pGuest->i_getConsole();
|
---|
[42261] | 546 | ComPtr<IDisplay> pDisplay;
|
---|
[51476] | 547 | HRESULT hr = pConsole->COMGETTER(Display)(pDisplay.asOutParam());
|
---|
[50561] | 548 | if (FAILED(hr))
|
---|
| 549 | return hr;
|
---|
[50460] | 550 |
|
---|
[52064] | 551 | ULONG dummy;
|
---|
[42261] | 552 | LONG xShift, yShift;
|
---|
[52978] | 553 | GuestMonitorStatus_T monitorStatus;
|
---|
[52064] | 554 | hr = pDisplay->GetScreenResolution(uScreenId, &dummy, &dummy, &dummy,
|
---|
[52978] | 555 | &xShift, &yShift, &monitorStatus);
|
---|
[50561] | 556 | if (FAILED(hr))
|
---|
| 557 | return hr;
|
---|
[50460] | 558 |
|
---|
[51476] | 559 | if (puX)
|
---|
| 560 | *puX += xShift;
|
---|
| 561 | if (puY)
|
---|
| 562 | *puY += yShift;
|
---|
[50561] | 563 |
|
---|
[57221] | 564 | LogFlowFunc(("uScreenId=%RU32, x=%RU32, y=%RU32\n", uScreenId, puX ? *puX : 0, puY ? *puY : 0));
|
---|
| 565 | return S_OK;
|
---|
[42261] | 566 | }
|
---|
| 567 |
|
---|
[51476] | 568 | int GuestDnD::hostCall(uint32_t u32Function, uint32_t cParms, PVBOXHGCMSVCPARM paParms) const
|
---|
[42261] | 569 | {
|
---|
[51476] | 570 | Assert(!m_pGuest.isNull());
|
---|
[52082] | 571 | ComObjPtr<Console> pConsole = m_pGuest->i_getConsole();
|
---|
[42261] | 572 |
|
---|
[51476] | 573 | /* Forward the information to the VMM device. */
|
---|
| 574 | Assert(!pConsole.isNull());
|
---|
[51612] | 575 | VMMDev *pVMMDev = pConsole->i_getVMMDev();
|
---|
[50561] | 576 | if (!pVMMDev)
|
---|
[51476] | 577 | return VERR_COM_OBJECT_NOT_FOUND;
|
---|
[42261] | 578 |
|
---|
[57221] | 579 | return pVMMDev->hgcmHostCall("VBoxDragAndDropSvc", u32Function, cParms, paParms);
|
---|
[42261] | 580 | }
|
---|
| 581 |
|
---|
| 582 | /* static */
|
---|
[55422] | 583 | DECLCALLBACK(int) GuestDnD::notifyDnDDispatcher(void *pvExtension, uint32_t u32Function,
|
---|
| 584 | void *pvParms, uint32_t cbParms)
|
---|
| 585 | {
|
---|
| 586 | LogFlowFunc(("pvExtension=%p, u32Function=%RU32, pvParms=%p, cbParms=%RU32\n",
|
---|
| 587 | pvExtension, u32Function, pvParms, cbParms));
|
---|
| 588 |
|
---|
| 589 | GuestDnD *pGuestDnD = reinterpret_cast<GuestDnD*>(pvExtension);
|
---|
| 590 | AssertPtrReturn(pGuestDnD, VERR_INVALID_POINTER);
|
---|
| 591 |
|
---|
| 592 | /** @todo In case we need to handle multiple guest DnD responses at a time this
|
---|
| 593 | * would be the place to lookup and dispatch to those. For the moment we
|
---|
| 594 | * only have one response -- simple. */
|
---|
| 595 | GuestDnDResponse *pResp = pGuestDnD->m_pResponse;
|
---|
| 596 | if (pResp)
|
---|
| 597 | return pResp->onDispatch(u32Function, pvParms, cbParms);
|
---|
| 598 |
|
---|
| 599 | return VERR_NOT_SUPPORTED;
|
---|
| 600 | }
|
---|
| 601 |
|
---|
[57221] | 602 | /* static */
|
---|
| 603 | bool GuestDnD::isFormatInFormatList(const com::Utf8Str &strFormat, const GuestDnDMIMEList &lstFormats)
|
---|
| 604 | {
|
---|
| 605 | return std::find(lstFormats.begin(), lstFormats.end(), strFormat) != lstFormats.end();
|
---|
| 606 | }
|
---|
[55422] | 607 |
|
---|
| 608 | /* static */
|
---|
[57221] | 609 | GuestDnDMIMEList GuestDnD::toFormatList(const com::Utf8Str &strFormats)
|
---|
[42261] | 610 | {
|
---|
[57221] | 611 | GuestDnDMIMEList lstFormats;
|
---|
| 612 | RTCList<RTCString> lstFormatsTmp = strFormats.split("\r\n");
|
---|
| 613 |
|
---|
| 614 | for (size_t i = 0; i < lstFormatsTmp.size(); i++)
|
---|
| 615 | lstFormats.push_back(com::Utf8Str(lstFormatsTmp.at(i)));
|
---|
| 616 |
|
---|
| 617 | return lstFormats;
|
---|
| 618 | }
|
---|
| 619 |
|
---|
| 620 | /* static */
|
---|
| 621 | com::Utf8Str GuestDnD::toFormatString(const GuestDnDMIMEList &lstFormats)
|
---|
| 622 | {
|
---|
[51476] | 623 | com::Utf8Str strFormat;
|
---|
[57221] | 624 | for (size_t i = 0; i < lstFormats.size(); i++)
|
---|
[42261] | 625 | {
|
---|
[57221] | 626 | const com::Utf8Str &f = lstFormats.at(i);
|
---|
| 627 | strFormat += f + "\r\n";
|
---|
[42261] | 628 | }
|
---|
[51476] | 629 |
|
---|
[42261] | 630 | return strFormat;
|
---|
| 631 | }
|
---|
| 632 |
|
---|
| 633 | /* static */
|
---|
[57221] | 634 | GuestDnDMIMEList GuestDnD::toFilteredFormatList(const GuestDnDMIMEList &lstFormatsSupported, const GuestDnDMIMEList &lstFormatsWanted)
|
---|
[42261] | 635 | {
|
---|
[57221] | 636 | GuestDnDMIMEList lstFormats;
|
---|
| 637 |
|
---|
| 638 | for (size_t i = 0; i < lstFormatsWanted.size(); i++)
|
---|
| 639 | {
|
---|
[59842] | 640 | /* Only keep supported format types. */
|
---|
[57221] | 641 | if (std::find(lstFormatsSupported.begin(),
|
---|
| 642 | lstFormatsSupported.end(), lstFormatsWanted.at(i)) != lstFormatsSupported.end())
|
---|
| 643 | {
|
---|
| 644 | lstFormats.push_back(lstFormatsWanted[i]);
|
---|
| 645 | }
|
---|
| 646 | }
|
---|
| 647 |
|
---|
| 648 | return lstFormats;
|
---|
| 649 | }
|
---|
| 650 |
|
---|
| 651 | /* static */
|
---|
| 652 | GuestDnDMIMEList GuestDnD::toFilteredFormatList(const GuestDnDMIMEList &lstFormatsSupported, const com::Utf8Str &strFormatsWanted)
|
---|
| 653 | {
|
---|
| 654 | GuestDnDMIMEList lstFmt;
|
---|
| 655 |
|
---|
| 656 | RTCList<RTCString> lstFormats = strFormatsWanted.split("\r\n");
|
---|
[42261] | 657 | size_t i = 0;
|
---|
[51476] | 658 | while (i < lstFormats.size())
|
---|
[42261] | 659 | {
|
---|
| 660 | /* Only keep allowed format types. */
|
---|
[57221] | 661 | if (std::find(lstFormatsSupported.begin(),
|
---|
| 662 | lstFormatsSupported.end(), lstFormats.at(i)) != lstFormatsSupported.end())
|
---|
| 663 | {
|
---|
| 664 | lstFmt.push_back(lstFormats[i]);
|
---|
| 665 | }
|
---|
| 666 | i++;
|
---|
[42261] | 667 | }
|
---|
[51476] | 668 |
|
---|
[57221] | 669 | return lstFmt;
|
---|
[42261] | 670 | }
|
---|
| 671 |
|
---|
| 672 | /* static */
|
---|
[51476] | 673 | uint32_t GuestDnD::toHGCMAction(DnDAction_T enmAction)
|
---|
[42261] | 674 | {
|
---|
[51476] | 675 | uint32_t uAction = DND_IGNORE_ACTION;
|
---|
| 676 | switch (enmAction)
|
---|
[42261] | 677 | {
|
---|
[51476] | 678 | case DnDAction_Copy:
|
---|
| 679 | uAction = DND_COPY_ACTION;
|
---|
| 680 | break;
|
---|
| 681 | case DnDAction_Move:
|
---|
| 682 | uAction = DND_MOVE_ACTION;
|
---|
| 683 | break;
|
---|
| 684 | case DnDAction_Link:
|
---|
| 685 | /* For now it doesn't seems useful to allow a link
|
---|
| 686 | action between host & guest. Later? */
|
---|
| 687 | case DnDAction_Ignore:
|
---|
| 688 | /* Ignored. */
|
---|
| 689 | break;
|
---|
| 690 | default:
|
---|
| 691 | AssertMsgFailed(("Action %RU32 not recognized!\n", enmAction));
|
---|
| 692 | break;
|
---|
[42261] | 693 | }
|
---|
[51476] | 694 |
|
---|
| 695 | return uAction;
|
---|
[42261] | 696 | }
|
---|
| 697 |
|
---|
| 698 | /* static */
|
---|
[56734] | 699 | void GuestDnD::toHGCMActions(DnDAction_T enmDefAction,
|
---|
| 700 | uint32_t *puDefAction,
|
---|
[51476] | 701 | const std::vector<DnDAction_T> vecAllowedActions,
|
---|
[56734] | 702 | uint32_t *puAllowedActions)
|
---|
[42261] | 703 | {
|
---|
[56734] | 704 | uint32_t uAllowedActions = DND_IGNORE_ACTION;
|
---|
| 705 | uint32_t uDefAction = toHGCMAction(enmDefAction);
|
---|
| 706 |
|
---|
| 707 | if (!vecAllowedActions.empty())
|
---|
[51476] | 708 | {
|
---|
| 709 | /* First convert the allowed actions to a bit array. */
|
---|
[56734] | 710 | for (size_t i = 0; i < vecAllowedActions.size(); i++)
|
---|
| 711 | uAllowedActions |= toHGCMAction(vecAllowedActions[i]);
|
---|
[42261] | 712 |
|
---|
[56734] | 713 | /*
|
---|
| 714 | * If no default action is set (ignoring), try one of the
|
---|
| 715 | * set allowed actions, preferring copy, move (in that order).
|
---|
| 716 | */
|
---|
| 717 | if (isDnDIgnoreAction(uDefAction))
|
---|
[51476] | 718 | {
|
---|
[56734] | 719 | if (hasDnDCopyAction(uAllowedActions))
|
---|
| 720 | uDefAction = DND_COPY_ACTION;
|
---|
| 721 | else if (hasDnDMoveAction(uAllowedActions))
|
---|
| 722 | uDefAction = DND_MOVE_ACTION;
|
---|
[51476] | 723 | }
|
---|
[42261] | 724 | }
|
---|
[56734] | 725 |
|
---|
| 726 | if (puDefAction)
|
---|
| 727 | *puDefAction = uDefAction;
|
---|
| 728 | if (puAllowedActions)
|
---|
| 729 | *puAllowedActions = uAllowedActions;
|
---|
[42261] | 730 | }
|
---|
| 731 |
|
---|
| 732 | /* static */
|
---|
[51476] | 733 | DnDAction_T GuestDnD::toMainAction(uint32_t uAction)
|
---|
[42261] | 734 | {
|
---|
[50508] | 735 | /* For now it doesn't seems useful to allow a
|
---|
| 736 | * link action between host & guest. Maybe later! */
|
---|
[72980] | 737 | return isDnDCopyAction(uAction) ? DnDAction_Copy
|
---|
| 738 | : isDnDMoveAction(uAction) ? DnDAction_Move
|
---|
| 739 | : DnDAction_Ignore;
|
---|
[42261] | 740 | }
|
---|
| 741 |
|
---|
| 742 | /* static */
|
---|
[57221] | 743 | std::vector<DnDAction_T> GuestDnD::toMainActions(uint32_t uActions)
|
---|
[42261] | 744 | {
|
---|
[57221] | 745 | std::vector<DnDAction_T> vecActions;
|
---|
| 746 |
|
---|
[50508] | 747 | /* For now it doesn't seems useful to allow a
|
---|
| 748 | * link action between host & guest. Maybe later! */
|
---|
[51476] | 749 | RTCList<DnDAction_T> lstActions;
|
---|
[42261] | 750 | if (hasDnDCopyAction(uActions))
|
---|
[51476] | 751 | lstActions.append(DnDAction_Copy);
|
---|
[42261] | 752 | if (hasDnDMoveAction(uActions))
|
---|
[51476] | 753 | lstActions.append(DnDAction_Move);
|
---|
[42261] | 754 |
|
---|
[51476] | 755 | for (size_t i = 0; i < lstActions.size(); ++i)
|
---|
| 756 | vecActions.push_back(lstActions.at(i));
|
---|
[57221] | 757 |
|
---|
| 758 | return vecActions;
|
---|
[42261] | 759 | }
|
---|
| 760 |
|
---|
[51556] | 761 | ///////////////////////////////////////////////////////////////////////////////
|
---|
| 762 |
|
---|
| 763 | GuestDnDBase::GuestDnDBase(void)
|
---|
| 764 | {
|
---|
[58212] | 765 | /* Initialize public attributes. */
|
---|
| 766 | m_lstFmtSupported = GuestDnDInst()->defaultFormats();
|
---|
[55549] | 767 |
|
---|
[58212] | 768 | /* Initialzie private stuff. */
|
---|
| 769 | mDataBase.m_cTransfersPending = 0;
|
---|
[58230] | 770 | mDataBase.m_uProtocolVersion = 0;
|
---|
[51556] | 771 | }
|
---|
| 772 |
|
---|
[55422] | 773 | HRESULT GuestDnDBase::i_isFormatSupported(const com::Utf8Str &aFormat, BOOL *aSupported)
|
---|
[51556] | 774 | {
|
---|
[57221] | 775 | *aSupported = std::find(m_lstFmtSupported.begin(),
|
---|
| 776 | m_lstFmtSupported.end(), aFormat) != m_lstFmtSupported.end()
|
---|
[51556] | 777 | ? TRUE : FALSE;
|
---|
| 778 | return S_OK;
|
---|
| 779 | }
|
---|
| 780 |
|
---|
[57221] | 781 | HRESULT GuestDnDBase::i_getFormats(GuestDnDMIMEList &aFormats)
|
---|
[51556] | 782 | {
|
---|
[57221] | 783 | aFormats = m_lstFmtSupported;
|
---|
[51556] | 784 |
|
---|
| 785 | return S_OK;
|
---|
| 786 | }
|
---|
| 787 |
|
---|
[57221] | 788 | HRESULT GuestDnDBase::i_addFormats(const GuestDnDMIMEList &aFormats)
|
---|
[51556] | 789 | {
|
---|
| 790 | for (size_t i = 0; i < aFormats.size(); ++i)
|
---|
| 791 | {
|
---|
| 792 | Utf8Str strFormat = aFormats.at(i);
|
---|
[57221] | 793 | if (std::find(m_lstFmtSupported.begin(),
|
---|
| 794 | m_lstFmtSupported.end(), strFormat) == m_lstFmtSupported.end())
|
---|
[51556] | 795 | {
|
---|
[57221] | 796 | m_lstFmtSupported.push_back(strFormat);
|
---|
[51556] | 797 | }
|
---|
| 798 | }
|
---|
| 799 |
|
---|
| 800 | return S_OK;
|
---|
| 801 | }
|
---|
| 802 |
|
---|
[57221] | 803 | HRESULT GuestDnDBase::i_removeFormats(const GuestDnDMIMEList &aFormats)
|
---|
[51556] | 804 | {
|
---|
| 805 | for (size_t i = 0; i < aFormats.size(); ++i)
|
---|
| 806 | {
|
---|
| 807 | Utf8Str strFormat = aFormats.at(i);
|
---|
[57221] | 808 | GuestDnDMIMEList::iterator itFormat = std::find(m_lstFmtSupported.begin(),
|
---|
| 809 | m_lstFmtSupported.end(), strFormat);
|
---|
| 810 | if (itFormat != m_lstFmtSupported.end())
|
---|
| 811 | m_lstFmtSupported.erase(itFormat);
|
---|
[51556] | 812 | }
|
---|
| 813 |
|
---|
| 814 | return S_OK;
|
---|
| 815 | }
|
---|
| 816 |
|
---|
[55422] | 817 | HRESULT GuestDnDBase::i_getProtocolVersion(ULONG *puVersion)
|
---|
| 818 | {
|
---|
| 819 | int rc = getProtocolVersion((uint32_t *)puVersion);
|
---|
| 820 | return RT_SUCCESS(rc) ? S_OK : E_FAIL;
|
---|
| 821 | }
|
---|
| 822 |
|
---|
[58230] | 823 | /**
|
---|
| 824 | * Tries to guess the DnD protocol version to use on the guest, based on the
|
---|
| 825 | * installed Guest Additions version + revision.
|
---|
| 826 | *
|
---|
| 827 | * If unable to retrieve the protocol version, VERR_NOT_FOUND is returned along
|
---|
| 828 | * with protocol version 1.
|
---|
| 829 | *
|
---|
| 830 | * @return IPRT status code.
|
---|
| 831 | * @param puProto Where to store the protocol version.
|
---|
| 832 | */
|
---|
[58212] | 833 | int GuestDnDBase::getProtocolVersion(uint32_t *puProto)
|
---|
[55422] | 834 | {
|
---|
[58212] | 835 | AssertPtrReturn(puProto, VERR_INVALID_POINTER);
|
---|
[55422] | 836 |
|
---|
| 837 | int rc;
|
---|
| 838 |
|
---|
[63158] | 839 | uint32_t uProto = 0;
|
---|
| 840 | uint32_t uVerAdditions;
|
---|
| 841 | uint32_t uRevAdditions;
|
---|
[55422] | 842 | if ( m_pGuest
|
---|
[58230] | 843 | && (uVerAdditions = m_pGuest->i_getAdditionsVersion()) > 0
|
---|
| 844 | && (uRevAdditions = m_pGuest->i_getAdditionsRevision()) > 0)
|
---|
[55422] | 845 | {
|
---|
[63158] | 846 | #if 0 && defined(DEBUG)
|
---|
[55422] | 847 | /* Hardcode the to-used protocol version; nice for testing side effects. */
|
---|
[63158] | 848 | if (true)
|
---|
| 849 | uProto = 3;
|
---|
| 850 | else
|
---|
[58230] | 851 | #endif
|
---|
[63158] | 852 | if (uVerAdditions >= VBOX_FULL_VERSION_MAKE(5, 0, 0))
|
---|
[58212] | 853 | {
|
---|
[63158] | 854 | /** @todo
|
---|
| 855 | * r=bird: This is just too bad for anyone using an OSE additions build...
|
---|
| 856 | */
|
---|
| 857 | if (uRevAdditions >= 103344) /* Since r103344: Protocol v3. */
|
---|
| 858 | uProto = 3;
|
---|
| 859 | else
|
---|
| 860 | uProto = 2; /* VBox 5.0.0 - 5.0.8: Protocol v2. */
|
---|
| 861 | }
|
---|
| 862 | /* else: uProto: 0 */
|
---|
[58230] | 863 |
|
---|
[63158] | 864 | LogFlowFunc(("uVerAdditions=%RU32 (%RU32.%RU32.%RU32), r%RU32\n",
|
---|
| 865 | uVerAdditions, VBOX_FULL_VERSION_GET_MAJOR(uVerAdditions), VBOX_FULL_VERSION_GET_MINOR(uVerAdditions),
|
---|
| 866 | VBOX_FULL_VERSION_GET_BUILD(uVerAdditions), uRevAdditions));
|
---|
| 867 | rc = VINF_SUCCESS;
|
---|
[55422] | 868 | }
|
---|
| 869 | else
|
---|
| 870 | {
|
---|
[58212] | 871 | uProto = 1; /* Fallback. */
|
---|
[55422] | 872 | rc = VERR_NOT_FOUND;
|
---|
| 873 | }
|
---|
| 874 |
|
---|
[58230] | 875 | LogRel2(("DnD: Guest is using protocol v%RU32, rc=%Rrc\n", uProto, rc));
|
---|
[55422] | 876 |
|
---|
[58212] | 877 | *puProto = uProto;
|
---|
[55422] | 878 | return rc;
|
---|
| 879 | }
|
---|
[55571] | 880 |
|
---|
| 881 | int GuestDnDBase::msgQueueAdd(GuestDnDMsg *pMsg)
|
---|
| 882 | {
|
---|
[58212] | 883 | mDataBase.m_lstMsgOut.push_back(pMsg);
|
---|
[55571] | 884 | return VINF_SUCCESS;
|
---|
| 885 | }
|
---|
| 886 |
|
---|
| 887 | GuestDnDMsg *GuestDnDBase::msgQueueGetNext(void)
|
---|
| 888 | {
|
---|
[58212] | 889 | if (mDataBase.m_lstMsgOut.empty())
|
---|
[55571] | 890 | return NULL;
|
---|
[58212] | 891 | return mDataBase.m_lstMsgOut.front();
|
---|
[55571] | 892 | }
|
---|
| 893 |
|
---|
| 894 | void GuestDnDBase::msgQueueRemoveNext(void)
|
---|
| 895 | {
|
---|
[58212] | 896 | if (!mDataBase.m_lstMsgOut.empty())
|
---|
[55571] | 897 | {
|
---|
[58212] | 898 | GuestDnDMsg *pMsg = mDataBase.m_lstMsgOut.front();
|
---|
[55571] | 899 | if (pMsg)
|
---|
| 900 | delete pMsg;
|
---|
[58212] | 901 | mDataBase.m_lstMsgOut.pop_front();
|
---|
[55571] | 902 | }
|
---|
| 903 | }
|
---|
| 904 |
|
---|
| 905 | void GuestDnDBase::msgQueueClear(void)
|
---|
| 906 | {
|
---|
[58212] | 907 | LogFlowFunc(("cMsg=%zu\n", mDataBase.m_lstMsgOut.size()));
|
---|
| 908 |
|
---|
| 909 | GuestDnDMsgList::iterator itMsg = mDataBase.m_lstMsgOut.begin();
|
---|
| 910 | while (itMsg != mDataBase.m_lstMsgOut.end())
|
---|
[55571] | 911 | {
|
---|
[58212] | 912 | GuestDnDMsg *pMsg = *itMsg;
|
---|
| 913 | if (pMsg)
|
---|
| 914 | delete pMsg;
|
---|
| 915 |
|
---|
| 916 | itMsg++;
|
---|
[55571] | 917 | }
|
---|
| 918 |
|
---|
[58212] | 919 | mDataBase.m_lstMsgOut.clear();
|
---|
[55571] | 920 | }
|
---|
| 921 |
|
---|
| 922 | int GuestDnDBase::sendCancel(void)
|
---|
| 923 | {
|
---|
[59842] | 924 | int rc = GuestDnDInst()->hostCall(HOST_DND_HG_EVT_CANCEL,
|
---|
| 925 | 0 /* cParms */, NULL /* paParms */);
|
---|
[55571] | 926 |
|
---|
[56494] | 927 | LogFlowFunc(("Generated cancelling request, rc=%Rrc\n", rc));
|
---|
[55640] | 928 | return rc;
|
---|
[55571] | 929 | }
|
---|
| 930 |
|
---|
[58212] | 931 | int GuestDnDBase::updateProgress(GuestDnDData *pData, GuestDnDResponse *pResp,
|
---|
| 932 | uint32_t cbDataAdd /* = 0 */)
|
---|
| 933 | {
|
---|
| 934 | AssertPtrReturn(pData, VERR_INVALID_POINTER);
|
---|
| 935 | AssertPtrReturn(pResp, VERR_INVALID_POINTER);
|
---|
| 936 | /* cbDataAdd is optional. */
|
---|
| 937 |
|
---|
| 938 | LogFlowFunc(("cbTotal=%RU64, cbProcessed=%RU64, cbRemaining=%RU64, cbDataAdd=%RU32\n",
|
---|
[58329] | 939 | pData->getTotal(), pData->getProcessed(), pData->getRemaining(), cbDataAdd));
|
---|
[58212] | 940 |
|
---|
| 941 | if (!pResp)
|
---|
| 942 | return VINF_SUCCESS;
|
---|
| 943 |
|
---|
| 944 | if (cbDataAdd)
|
---|
| 945 | pData->addProcessed(cbDataAdd);
|
---|
| 946 |
|
---|
| 947 | int rc = pResp->setProgress(pData->getPercentComplete(),
|
---|
| 948 | pData->isComplete()
|
---|
| 949 | ? DND_PROGRESS_COMPLETE
|
---|
| 950 | : DND_PROGRESS_RUNNING);
|
---|
| 951 | LogFlowFuncLeaveRC(rc);
|
---|
| 952 | return rc;
|
---|
| 953 | }
|
---|
| 954 |
|
---|
[55571] | 955 | /** @todo GuestDnDResponse *pResp needs to go. */
|
---|
[58212] | 956 | int GuestDnDBase::waitForEvent(GuestDnDCallbackEvent *pEvent, GuestDnDResponse *pResp, RTMSINTERVAL msTimeout)
|
---|
[55571] | 957 | {
|
---|
[58212] | 958 | AssertPtrReturn(pEvent, VERR_INVALID_POINTER);
|
---|
| 959 | AssertPtrReturn(pResp, VERR_INVALID_POINTER);
|
---|
| 960 |
|
---|
[55571] | 961 | int rc;
|
---|
| 962 |
|
---|
[59842] | 963 | LogFlowFunc(("msTimeout=%RU32\n", msTimeout));
|
---|
| 964 |
|
---|
[55571] | 965 | uint64_t tsStart = RTTimeMilliTS();
|
---|
| 966 | do
|
---|
| 967 | {
|
---|
| 968 | /*
|
---|
| 969 | * Wait until our desired callback triggered the
|
---|
| 970 | * wait event. As we don't want to block if the guest does not
|
---|
[55963] | 971 | * respond, do busy waiting here.
|
---|
[55571] | 972 | */
|
---|
[58212] | 973 | rc = pEvent->Wait(500 /* ms */);
|
---|
[55571] | 974 | if (RT_SUCCESS(rc))
|
---|
| 975 | {
|
---|
[58212] | 976 | rc = pEvent->Result();
|
---|
[55571] | 977 | LogFlowFunc(("Callback done, result is %Rrc\n", rc));
|
---|
| 978 | break;
|
---|
| 979 | }
|
---|
| 980 | else if (rc == VERR_TIMEOUT) /* Continue waiting. */
|
---|
[56494] | 981 | rc = VINF_SUCCESS;
|
---|
[55571] | 982 |
|
---|
| 983 | if ( msTimeout != RT_INDEFINITE_WAIT
|
---|
| 984 | && RTTimeMilliTS() - tsStart > msTimeout)
|
---|
| 985 | {
|
---|
| 986 | rc = VERR_TIMEOUT;
|
---|
[56494] | 987 | LogRel2(("DnD: Error: Guest did not respond within time\n"));
|
---|
[55571] | 988 | }
|
---|
[55963] | 989 | else if (pResp->isProgressCanceled()) /** @todo GuestDnDResponse *pResp needs to go. */
|
---|
[55571] | 990 | {
|
---|
[56494] | 991 | LogRel2(("DnD: Operation was canceled by user\n"));
|
---|
[55571] | 992 | rc = VERR_CANCELLED;
|
---|
| 993 | }
|
---|
| 994 |
|
---|
| 995 | } while (RT_SUCCESS(rc));
|
---|
| 996 |
|
---|
| 997 | LogFlowFuncLeaveRC(rc);
|
---|
| 998 | return rc;
|
---|
| 999 | }
|
---|
[42261] | 1000 | #endif /* VBOX_WITH_DRAG_AND_DROP */
|
---|
| 1001 |
|
---|