VirtualBox

source: vbox/trunk/src/VBox/Additions/x11/VBoxClient/clipboard-x11.cpp@ 103085

Last change on this file since 103085 was 102920, checked in by vboxsync, 12 months ago

Shared Clipboard: Show the correct error code when VBClX11ClipboardInit() fails.

  • Property svn:eol-style set to native
  • Property svn:keywords set to Id Revision
File size: 20.5 KB
Line 
1/** $Id: clipboard-x11.cpp 102920 2024-01-17 11:20:27Z vboxsync $ */
2/** @file
3 * Guest Additions - X11 Shared Clipboard implementation.
4 */
5
6/*
7 * Copyright (C) 2007-2023 Oracle and/or its affiliates.
8 *
9 * This file is part of VirtualBox base platform packages, as
10 * available from https://www.virtualbox.org.
11 *
12 * This program is free software; you can redistribute it and/or
13 * modify it under the terms of the GNU General Public License
14 * as published by the Free Software Foundation, in version 3 of the
15 * License.
16 *
17 * This program is distributed in the hope that it will be useful, but
18 * WITHOUT ANY WARRANTY; without even the implied warranty of
19 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
20 * General Public License for more details.
21 *
22 * You should have received a copy of the GNU General Public License
23 * along with this program; if not, see <https://www.gnu.org/licenses>.
24 *
25 * SPDX-License-Identifier: GPL-3.0-only
26 */
27
28
29/*********************************************************************************************************************************
30* Header Files *
31*********************************************************************************************************************************/
32#include <iprt/alloc.h>
33#include <iprt/asm.h>
34#include <iprt/assert.h>
35#include <iprt/initterm.h>
36#include <iprt/mem.h>
37#include <iprt/string.h>
38#include <iprt/path.h>
39#include <iprt/process.h>
40#include <iprt/semaphore.h>
41
42#include <VBox/VBoxGuestLib.h>
43#include <VBox/HostServices/VBoxClipboardSvc.h>
44#include <VBox/GuestHost/SharedClipboard.h>
45#include <VBox/GuestHost/SharedClipboard-x11.h>
46
47#include "VBoxClient.h"
48#include "clipboard.h"
49
50#ifdef LOG_GROUP
51# undef LOG_GROUP
52#endif
53#define LOG_GROUP LOG_GROUP_SHARED_CLIPBOARD
54#include <iprt/log.h>
55
56
57#ifdef VBOX_WITH_SHARED_CLIPBOARD_TRANSFERS_HTTP
58/**
59 * @copydoc SHCLTRANSFERCALLBACKS::pfnOnInitialized
60 *
61 * @thread Clipboard main thread.
62 */
63static DECLCALLBACK(void) vbclX11OnTransferInitializedCallback(PSHCLTRANSFERCALLBACKCTX pCbCtx)
64{
65 LogFlowFuncEnter();
66
67 PSHCLCONTEXT pCtx = (PSHCLCONTEXT)pCbCtx->pvUser;
68 AssertPtr(pCtx);
69
70 PSHCLTRANSFER pTransfer = pCbCtx->pTransfer;
71 AssertPtr(pTransfer);
72
73 int rc = VINF_SUCCESS;
74
75 /* If this is a G->H transfer, we need to set the root list entries here, as the host
76 * will start reading those as soon as we report the INITIALIZED status. */
77 switch (ShClTransferGetDir(pTransfer))
78 {
79 case SHCLTRANSFERDIR_TO_REMOTE: /* G->H */
80 {
81 PSHCLEVENT pEvent;
82 rc = ShClEventSourceGenerateAndRegisterEvent(&pCtx->EventSrc, &pEvent);
83 if (RT_SUCCESS(rc))
84 {
85 rc = ShClX11ReadDataFromX11Async(&g_Ctx.X11, VBOX_SHCL_FMT_URI_LIST, UINT32_MAX, pEvent);
86 if (RT_SUCCESS(rc))
87 {
88 int rcEvent;
89 PSHCLEVENTPAYLOAD pPayload;
90 rc = ShClEventWaitEx(pEvent, SHCL_TIMEOUT_DEFAULT_MS, &rcEvent, &pPayload);
91 if (RT_SUCCESS(rc))
92 {
93 if (pPayload)
94 {
95 AssertReturnVoid(pPayload->cbData == sizeof(SHCLX11RESPONSE));
96 AssertReturnVoid(pPayload->pvData);
97 PSHCLX11RESPONSE pResp = (PSHCLX11RESPONSE)pPayload->pvData;
98 AssertReturnVoid(pResp->enmType == SHCLX11EVENTTYPE_READ);
99
100 rc = ShClTransferRootsInitFromStringListEx(pTransfer, (const char *)pResp->Read.pvData, pResp->Read.cbData,
101 "\n" /* X11-based Desktop environments separate entries with "\n" */);
102
103 RTMemFree(pResp->Read.pvData);
104 pResp->Read.cbData = 0;
105
106 ShClPayloadFree(pPayload);
107 }
108 else /* No payload given; could happen on invalid / not-expected formats. */
109 rc = VERR_NO_DATA;
110 }
111 else if (rc == VERR_SHCLPB_EVENT_FAILED)
112 rc = rcEvent;
113 }
114
115 ShClEventRelease(pEvent);
116 pEvent = NULL;
117 }
118 break;
119 }
120
121 case SHCLTRANSFERDIR_FROM_REMOTE: /* H->G */
122 {
123 /* Retrieve the root entries as a first action, so that the transfer is ready to go
124 * once it gets registered to HTTP server. */
125 int rc2 = ShClTransferRootListRead(pTransfer);
126 if ( RT_SUCCESS(rc2)
127 /* As soon as we register the transfer with the HTTP server, the transfer needs to have its roots set. */
128 && ShClTransferRootsCount(pTransfer))
129 {
130 rc2 = ShClTransferHttpServerRegisterTransfer(&pCtx->X11.HttpCtx.HttpServer, pTransfer);
131 }
132 break;
133 }
134
135 default:
136 break;
137 }
138
139 LogFlowFuncLeaveRC(rc);
140}
141
142/**
143 * @copydoc SHCLTRANSFERCALLBACKS::pfnOnRegistered
144 *
145 * This starts the HTTP server if not done yet and registers the transfer with it.
146 *
147 * @thread Clipboard main thread.
148 */
149static DECLCALLBACK(void) vbclX11OnTransferRegisteredCallback(PSHCLTRANSFERCALLBACKCTX pCbCtx, PSHCLTRANSFERCTX pTransferCtx)
150{
151 RT_NOREF(pTransferCtx);
152
153 LogFlowFuncEnter();
154
155 PSHCLCONTEXT pCtx = (PSHCLCONTEXT)pCbCtx->pvUser;
156 AssertPtr(pCtx);
157
158 PSHCLTRANSFER pTransfer = pCbCtx->pTransfer;
159 AssertPtr(pTransfer);
160
161 /* We only need to start the HTTP server when we actually receive data from the remote (host). */
162 if (ShClTransferGetDir(pTransfer) == SHCLTRANSFERDIR_FROM_REMOTE) /* H->G */
163 {
164 int rc2 = ShClTransferHttpServerMaybeStart(&pCtx->X11.HttpCtx);
165 if (RT_FAILURE(rc2))
166 LogRel(("Shared Clipboard: Registering HTTP transfer failed: %Rrc\n", rc2));
167 }
168
169 LogFlowFuncLeave();
170}
171
172/**
173 * Unregisters a transfer from a HTTP server.
174 *
175 * This also stops the HTTP server if no active transfers are found anymore.
176 *
177 * @param pCtx Shared clipboard context to unregister transfer for.
178 * @param pTransfer Transfer to unregister.
179 *
180 * @thread Clipboard main thread.
181 */
182static void vbclX11TransferUnregister(PSHCLCONTEXT pCtx, PSHCLTRANSFER pTransfer)
183{
184 if (ShClTransferGetDir(pTransfer) == SHCLTRANSFERDIR_FROM_REMOTE)
185 {
186 if (ShClTransferHttpServerIsInitialized(&pCtx->X11.HttpCtx.HttpServer))
187 {
188 ShClTransferHttpServerUnregisterTransfer(&pCtx->X11.HttpCtx.HttpServer, pTransfer);
189 ShClTransferHttpServerMaybeStop(&pCtx->X11.HttpCtx);
190 }
191 }
192}
193
194/**
195 * @copydoc SHCLTRANSFERCALLBACKS::pfnOnUnregistered
196 *
197 * Unregisters a (now) unregistered transfer from the HTTP server.
198 *
199 * @thread Clipboard main thread.
200 */
201static DECLCALLBACK(void) vbclX11OnTransferUnregisteredCallback(PSHCLTRANSFERCALLBACKCTX pCbCtx, PSHCLTRANSFERCTX pTransferCtx)
202{
203 RT_NOREF(pTransferCtx);
204 vbclX11TransferUnregister((PSHCLCONTEXT)pCbCtx->pvUser, pCbCtx->pTransfer);
205}
206
207/**
208 * @copydoc SHCLTRANSFERCALLBACKS::pfnOnCompleted
209 *
210 * Unregisters a complete transfer from the HTTP server.
211 *
212 * @thread Clipboard main thread.
213 */
214static DECLCALLBACK(void) vbclX11OnTransferCompletedCallback(PSHCLTRANSFERCALLBACKCTX pCbCtx, int rc)
215{
216 RT_NOREF(rc);
217 vbclX11TransferUnregister((PSHCLCONTEXT)pCbCtx->pvUser, pCbCtx->pTransfer);
218}
219
220/** @copydoc SHCLTRANSFERCALLBACKS::pfnOnError
221 *
222 * Unregisters a failed transfer from the HTTP server.
223 *
224 * @thread Clipboard main thread.
225 */
226static DECLCALLBACK(void) vbclX11OnTransferErrorCallback(PSHCLTRANSFERCALLBACKCTX pCtx, int rc)
227{
228 return vbclX11OnTransferCompletedCallback(pCtx, rc);
229}
230#endif /* VBOX_WITH_SHARED_CLIPBOARD_TRANSFERS_HTTP */
231
232/**
233 * Worker for a reading clipboard from the host.
234 */
235static DECLCALLBACK(int) vbclX11ReadDataWorker(PSHCLCONTEXT pCtx, SHCLFORMAT uFmt, void **ppv, uint32_t *pcb, void *pvUser)
236{
237 RT_NOREF(pvUser);
238
239 LogFlowFuncEnter();
240
241 int rc = VERR_NO_DATA; /* Play safe. */
242
243 uint32_t cbRead = 0;
244
245 uint32_t cbData = _4K; /** @todo Make this dynamic. */
246 void *pvData = RTMemAlloc(cbData);
247 if (pvData)
248 {
249 rc = VbglR3ClipboardReadDataEx(&pCtx->CmdCtx, uFmt, pvData, cbData, &cbRead);
250 }
251 else
252 rc = VERR_NO_MEMORY;
253
254 /*
255 * A return value of VINF_BUFFER_OVERFLOW tells us to try again with a
256 * larger buffer. The size of the buffer needed is placed in *pcb.
257 * So we start all over again.
258 */
259 if (rc == VINF_BUFFER_OVERFLOW)
260 {
261 /* cbRead contains the size required. */
262
263 cbData = cbRead;
264 pvData = RTMemRealloc(pvData, cbRead);
265 if (pvData)
266 {
267 rc = VbglR3ClipboardReadDataEx(&pCtx->CmdCtx, uFmt, pvData, cbData, &cbRead);
268 if (rc == VINF_BUFFER_OVERFLOW)
269 rc = VERR_BUFFER_OVERFLOW;
270 }
271 else
272 rc = VERR_NO_MEMORY;
273 }
274
275 if (!cbRead)
276 rc = VERR_NO_DATA;
277
278 if (RT_SUCCESS(rc))
279 {
280 if (ppv)
281 *ppv = pvData;
282 if (pcb)
283 *pcb = cbRead; /* Actual bytes read. */
284 }
285 else
286 {
287 /*
288 * Catch other errors. This also catches the case in which the buffer was
289 * too small a second time, possibly because the clipboard contents
290 * changed half-way through the operation. Since we can't say whether or
291 * not this is actually an error, we just return size 0.
292 */
293 RTMemFree(pvData);
294 }
295
296 LogFlowFuncLeaveRC(rc);
297 return rc;
298}
299
300/**
301 * @copydoc SHCLCALLBACKS::pfnOnRequestDataFromSource
302 *
303 * Requests data from the host.
304 *
305 * For transfers: This requests a transfer from the host. Most of the handling will be done VbglR3 then.
306 *
307 * @thread X11 event thread.
308 */
309static DECLCALLBACK(int) vbclX11OnRequestDataFromSourceCallback(PSHCLCONTEXT pCtx,
310 SHCLFORMAT uFmt, void **ppv, uint32_t *pcb, void *pvUser)
311{
312 RT_NOREF(pvUser);
313
314 LogFlowFunc(("pCtx=%p, uFmt=%#x\n", pCtx, uFmt));
315
316 int rc;
317
318#ifdef VBOX_WITH_SHARED_CLIPBOARD_TRANSFERS_HTTP
319 if (uFmt == VBOX_SHCL_FMT_URI_LIST)
320 {
321 rc = vbclX11ReadDataWorker(pCtx, uFmt, ppv, pcb, pvUser);
322 if (RT_SUCCESS(rc))
323 {
324 /* Request a new H->G transfer from the host.
325 * This is needed in order to get a transfer ID from the host we can initialize our own local transfer with.
326 * Transfer creation and set up will be done in VbglR3. */
327 rc = VbglR3ClipboardTransferRequest(&pCtx->CmdCtx);
328 if (RT_SUCCESS(rc))
329 {
330 PSHCLHTTPSERVER pSrv = &pCtx->X11.HttpCtx.HttpServer;
331
332 /* Wait until the HTTP server got the transfer registered, so that we have something to work with. */
333 rc = ShClTransferHttpServerWaitForStatusChange(pSrv, SHCLHTTPSERVERSTATUS_TRANSFER_REGISTERED, SHCL_TIMEOUT_DEFAULT_MS);
334 if (RT_SUCCESS(rc))
335 {
336 PSHCLTRANSFER pTransfer = ShClTransferHttpServerGetTransferLast(pSrv);
337 if (pTransfer)
338 {
339 rc = ShClTransferWaitForStatus(pTransfer, SHCL_TIMEOUT_DEFAULT_MS, SHCLTRANSFERSTATUS_INITIALIZED);
340 if (RT_SUCCESS(rc))
341 {
342 char *pszData;
343 size_t cbData;
344 rc = ShClTransferHttpConvertToStringList(pSrv, pTransfer, &pszData, &cbData);
345 if (RT_SUCCESS(rc))
346 {
347 *ppv = pszData;
348 *pcb = cbData;
349 /* ppv has ownership of pszData now. */
350 }
351 }
352 }
353 else
354 AssertMsgFailed(("No registered transfer found for HTTP server\n"));
355 }
356 else
357 LogRel(("Shared Clipboard: Could not start transfer, as no new HTTP transfer was registered in time\n"));
358 }
359 }
360 }
361 else /* Anything else */
362#endif /* VBOX_WITH_SHARED_CLIPBOARD_TRANSFERS_HTTP */
363 {
364 rc = vbclX11ReadDataWorker(pCtx, uFmt, ppv, pcb, pvUser);
365 }
366
367 if (RT_FAILURE(rc))
368 LogRel(("Shared Clipboard: Requesting data in format %#x from host failed with %Rrc\n", uFmt, rc));
369
370 LogFlowFuncLeaveRC(rc);
371 return rc;
372}
373
374/**
375 * @copydoc SHCLCALLBACKS::pfnReportFormats
376 *
377 * Reports clipboard formats to the host.
378 *
379 * @thread X11 event thread.
380 */
381static DECLCALLBACK(int) vbclX11ReportFormatsCallback(PSHCLCONTEXT pCtx, uint32_t fFormats, void *pvUser)
382{
383 RT_NOREF(pvUser);
384
385 LogFlowFunc(("fFormats=%#x\n", fFormats));
386
387 int rc = VbglR3ClipboardReportFormats(pCtx->CmdCtx.idClient, fFormats);
388
389 LogFlowFuncLeaveRC(rc);
390 return rc;
391}
392
393/**
394 * Initializes the X11-specifc Shared Clipboard code.
395 *
396 * @returns VBox status code.
397 */
398int VBClX11ClipboardInit(void)
399{
400 LogFlowFuncEnter();
401
402 int rc = ShClEventSourceCreate(&g_Ctx.EventSrc, 0 /* uID */);
403 AssertRCReturn(rc, rc);
404
405 SHCLCALLBACKS Callbacks;
406 RT_ZERO(Callbacks);
407 Callbacks.pfnReportFormats = vbclX11ReportFormatsCallback;
408 Callbacks.pfnOnRequestDataFromSource = vbclX11OnRequestDataFromSourceCallback;
409
410 rc = ShClX11Init(&g_Ctx.X11, &Callbacks, &g_Ctx, false /* fHeadless */);
411 if (RT_SUCCESS(rc))
412 {
413 rc = ShClX11ThreadStart(&g_Ctx.X11, false /* grab */);
414 if (RT_SUCCESS(rc))
415 {
416 rc = VbglR3ClipboardConnectEx(&g_Ctx.CmdCtx, VBOX_SHCL_GF_0_CONTEXT_ID);
417 if (RT_FAILURE(rc))
418 ShClX11ThreadStop(&g_Ctx.X11);
419 }
420 }
421 else
422 VBClLogError("Initializing clipboard failed with %Rrc\n", rc);
423
424 if (RT_FAILURE(rc))
425 {
426 VbglR3ClipboardDisconnectEx(&g_Ctx.CmdCtx);
427 ShClX11Destroy(&g_Ctx.X11);
428 }
429
430 LogFlowFuncLeaveRC(rc);
431 return rc;
432}
433
434/**
435 * Destroys the X11-specifc Shared Clipboard code.
436 *
437 * @returns VBox status code.
438 */
439int VBClX11ClipboardDestroy(void)
440{
441 return ShClEventSourceDestroy(&g_Ctx.EventSrc);
442}
443
444/**
445 * The main loop of the X11-specifc Shared Clipboard code.
446 *
447 * @returns VBox status code.
448 *
449 * @thread Clipboard service worker thread.
450 */
451int VBClX11ClipboardMain(void)
452{
453 PSHCLCONTEXT pCtx = &g_Ctx;
454
455 bool fShutdown = false;
456
457#ifdef VBOX_WITH_SHARED_CLIPBOARD_TRANSFERS
458# ifdef VBOX_WITH_SHARED_CLIPBOARD_TRANSFERS_HTTP
459 /*
460 * Set callbacks.
461 * Those will be registered within VbglR3 when a new transfer gets initialized.
462 *
463 * Used for starting / stopping the HTTP server.
464 */
465 RT_ZERO(pCtx->CmdCtx.Transfers.Callbacks);
466
467 pCtx->CmdCtx.Transfers.Callbacks.pvUser = pCtx; /* Assign context as user-provided callback data. */
468 pCtx->CmdCtx.Transfers.Callbacks.cbUser = sizeof(SHCLCONTEXT);
469
470 pCtx->CmdCtx.Transfers.Callbacks.pfnOnInitialized = vbclX11OnTransferInitializedCallback;
471 pCtx->CmdCtx.Transfers.Callbacks.pfnOnRegistered = vbclX11OnTransferRegisteredCallback;
472 pCtx->CmdCtx.Transfers.Callbacks.pfnOnUnregistered = vbclX11OnTransferUnregisteredCallback;
473 pCtx->CmdCtx.Transfers.Callbacks.pfnOnCompleted = vbclX11OnTransferCompletedCallback;
474 pCtx->CmdCtx.Transfers.Callbacks.pfnOnError = vbclX11OnTransferErrorCallback;
475# endif /* VBOX_WITH_SHARED_CLIPBOARD_TRANSFERS_HTTP */
476#endif /* VBOX_WITH_SHARED_CLIPBOARD_TRANSFERS */
477
478 LogFlowFunc(("fUseLegacyProtocol=%RTbool, fHostFeatures=%#RX64 ...\n",
479 pCtx->CmdCtx.fUseLegacyProtocol, pCtx->CmdCtx.fHostFeatures));
480
481 int rc;
482
483 /* The thread waits for incoming messages from the host. */
484 for (;;)
485 {
486 PVBGLR3CLIPBOARDEVENT pEvent = (PVBGLR3CLIPBOARDEVENT)RTMemAllocZ(sizeof(VBGLR3CLIPBOARDEVENT));
487 AssertPtrBreakStmt(pEvent, rc = VERR_NO_MEMORY);
488
489 uint32_t idMsg = 0;
490 uint32_t cParms = 0;
491 rc = VbglR3ClipboardMsgPeekWait(&pCtx->CmdCtx, &idMsg, &cParms, NULL /* pidRestoreCheck */);
492 if (RT_SUCCESS(rc))
493 {
494#ifdef VBOX_WITH_SHARED_CLIPBOARD_TRANSFERS
495 rc = VbglR3ClipboardEventGetNextEx(idMsg, cParms, &pCtx->CmdCtx, &pCtx->TransferCtx, pEvent);
496#else
497 rc = VbglR3ClipboardEventGetNext(idMsg, cParms, &pCtx->CmdCtx, pEvent);
498#endif
499 }
500
501 if (RT_FAILURE(rc))
502 {
503 LogFlowFunc(("Getting next event failed with %Rrc\n", rc));
504
505 VbglR3ClipboardEventFree(pEvent);
506 pEvent = NULL;
507
508 if (fShutdown)
509 break;
510
511 /* Wait a bit before retrying. */
512 RTThreadSleep(RT_MS_1SEC);
513 continue;
514 }
515 else
516 {
517 AssertPtr(pEvent);
518 LogFlowFunc(("Event uType=%RU32\n", pEvent->enmType));
519
520 switch (pEvent->enmType)
521 {
522 case VBGLR3CLIPBOARDEVENTTYPE_REPORT_FORMATS:
523 {
524 ShClX11ReportFormatsToX11Async(&g_Ctx.X11, pEvent->u.fReportedFormats);
525 break;
526 }
527
528 case VBGLR3CLIPBOARDEVENTTYPE_READ_DATA:
529 {
530 PSHCLEVENT pReadDataEvent;
531 rc = ShClEventSourceGenerateAndRegisterEvent(&pCtx->EventSrc, &pReadDataEvent);
532 if (RT_SUCCESS(rc))
533 {
534 rc = ShClX11ReadDataFromX11Async(&g_Ctx.X11, pEvent->u.fReadData, UINT32_MAX, pReadDataEvent);
535 if (RT_SUCCESS(rc))
536 {
537 int rcEvent;
538 PSHCLEVENTPAYLOAD pPayload;
539 rc = ShClEventWaitEx(pReadDataEvent, SHCL_TIMEOUT_DEFAULT_MS, &rcEvent, &pPayload);
540 if (RT_SUCCESS(rc))
541 {
542 if (pPayload)
543 {
544 AssertBreakStmt(pPayload->cbData == sizeof(SHCLX11RESPONSE), rc = VERR_INVALID_PARAMETER);
545 AssertPtrBreakStmt(pPayload->pvData, rc = VERR_INVALID_POINTER);
546 PSHCLX11RESPONSE pResp = (PSHCLX11RESPONSE)pPayload->pvData;
547 AssertBreakStmt(pResp->enmType == SHCLX11EVENTTYPE_READ, rc = VERR_INVALID_PARAMETER);
548
549 rc = VbglR3ClipboardWriteDataEx(&pCtx->CmdCtx, pEvent->u.fReadData,
550 pResp->Read.pvData, pResp->Read.cbData);
551
552 RTMemFree(pResp->Read.pvData);
553 pResp->Read.cbData = 0;
554
555 ShClPayloadFree(pPayload);
556 }
557 else /* No payload given; could happen on invalid / not-expected formats. */
558 rc = VERR_NO_DATA;
559 }
560 else if (rc == VERR_SHCLPB_EVENT_FAILED)
561 rc = rcEvent;
562 }
563
564 ShClEventRelease(pReadDataEvent);
565 pReadDataEvent = NULL;
566 }
567
568 if (RT_FAILURE(rc))
569 VbglR3ClipboardWriteDataEx(&pCtx->CmdCtx, pEvent->u.fReadData, NULL, 0);
570
571 break;
572 }
573
574 case VBGLR3CLIPBOARDEVENTTYPE_QUIT:
575 {
576 VBClLogVerbose(2, "Host requested termination\n");
577 fShutdown = true;
578 break;
579 }
580
581#ifdef VBOX_WITH_SHARED_CLIPBOARD_TRANSFERS
582 case VBGLR3CLIPBOARDEVENTTYPE_TRANSFER_STATUS:
583 {
584 if (pEvent->u.TransferStatus.Report.uStatus == SHCLTRANSFERSTATUS_STARTED)
585 {
586
587 }
588 rc = VINF_SUCCESS;
589 break;
590 }
591#endif
592 case VBGLR3CLIPBOARDEVENTTYPE_NONE:
593 {
594 /* Nothing to do here. */
595 rc = VINF_SUCCESS;
596 break;
597 }
598
599 default:
600 {
601 AssertMsgFailedBreakStmt(("Event type %RU32 not implemented\n", pEvent->enmType), rc = VERR_NOT_SUPPORTED);
602 }
603 }
604
605 if (pEvent)
606 {
607 VbglR3ClipboardEventFree(pEvent);
608 pEvent = NULL;
609 }
610 }
611
612 if (fShutdown)
613 break;
614 }
615
616 LogFlowFuncLeaveRC(rc);
617 return rc;
618}
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