VirtualBox

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

Last change on this file was 104317, checked in by vboxsync, 6 weeks ago

Shared Clipboard/Transfers: ShClTransferRootsInitFromXXX() -> ShClTransferRootsSetFromXXX(). bugref:9437

  • Property svn:eol-style set to native
  • Property svn:keywords set to Id Revision
File size: 16.3 KB
RevLine 
[7007]1/** $Id: clipboard-x11.cpp 104317 2024-04-12 14:11:08Z vboxsync $ */
2/** @file
[99590]3 * Guest Additions - X11 Shared Clipboard implementation.
[7007]4 */
5
6/*
[98103]7 * Copyright (C) 2007-2023 Oracle and/or its affiliates.
[7007]8 *
[96407]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
[7007]26 */
27
[57358]28
29/*********************************************************************************************************************************
30* Header Files *
31*********************************************************************************************************************************/
[7007]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>
[82882]38#include <iprt/path.h>
[7007]39#include <iprt/process.h>
40#include <iprt/semaphore.h>
41
[21227]42#include <VBox/VBoxGuestLib.h>
[20552]43#include <VBox/HostServices/VBoxClipboardSvc.h>
44#include <VBox/GuestHost/SharedClipboard.h>
[82156]45#include <VBox/GuestHost/SharedClipboard-x11.h>
[7007]46
[18360]47#include "VBoxClient.h"
[86967]48#include "clipboard.h"
[7007]49
[100285]50#ifdef LOG_GROUP
51# undef LOG_GROUP
52#endif
53#define LOG_GROUP LOG_GROUP_SHARED_CLIPBOARD
54#include <iprt/log.h>
[82287]55
[100285]56
[100204]57#ifdef VBOX_WITH_SHARED_CLIPBOARD_TRANSFERS_HTTP
58/**
[103442]59 * @copydoc SHCLTRANSFERCALLBACKS::pfnOnInitialize
[100367]60 *
61 * @thread Clipboard main thread.
[100204]62 */
[103442]63static DECLCALLBACK(int) vbclX11OnTransferInitializeCallback(PSHCLTRANSFERCALLBACKCTX pCbCtx)
[100204]64{
65 LogFlowFuncEnter();
66
[100367]67 PSHCLCONTEXT pCtx = (PSHCLCONTEXT)pCbCtx->pvUser;
68 AssertPtr(pCtx);
[100204]69
[100367]70 PSHCLTRANSFER pTransfer = pCbCtx->pTransfer;
71 AssertPtr(pTransfer);
[100204]72
[100367]73 int rc = VINF_SUCCESS;
[100204]74
[100367]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))
[100204]78 {
[100367]79 case SHCLTRANSFERDIR_TO_REMOTE: /* G->H */
[100204]80 {
[103323]81 void *pvData;
82 uint32_t cbData;
83 rc = ShClX11ReadDataFromX11Ex(&g_Ctx.X11, &pCtx->EventSrc, SHCL_TIMEOUT_DEFAULT_MS, VBOX_SHCL_FMT_URI_LIST,
84 &pvData, &cbData);
[100367]85 if (RT_SUCCESS(rc))
86 {
[104317]87 rc = ShClTransferRootsSetFromStringListEx(pTransfer, (const char *)pvData, cbData,
88 "\n" /* X11-based Desktop environments separate entries with "\n" */);
[103323]89 RTMemFree(pvData);
[100367]90 }
[100204]91 break;
92 }
[100367]93
94 case SHCLTRANSFERDIR_FROM_REMOTE: /* H->G */
95 {
96 /* Retrieve the root entries as a first action, so that the transfer is ready to go
97 * once it gets registered to HTTP server. */
98 int rc2 = ShClTransferRootListRead(pTransfer);
99 if ( RT_SUCCESS(rc2)
100 /* As soon as we register the transfer with the HTTP server, the transfer needs to have its roots set. */
101 && ShClTransferRootsCount(pTransfer))
102 {
103 rc2 = ShClTransferHttpServerRegisterTransfer(&pCtx->X11.HttpCtx.HttpServer, pTransfer);
104 }
105 break;
106 }
107
108 default:
109 break;
[100204]110 }
111
[100367]112 LogFlowFuncLeaveRC(rc);
[103442]113 return rc;
[100204]114}
115
116/**
[100205]117 * @copydoc SHCLTRANSFERCALLBACKS::pfnOnRegistered
[100204]118 *
119 * This starts the HTTP server if not done yet and registers the transfer with it.
120 *
[100284]121 * @thread Clipboard main thread.
[100204]122 */
[100367]123static DECLCALLBACK(void) vbclX11OnTransferRegisteredCallback(PSHCLTRANSFERCALLBACKCTX pCbCtx, PSHCLTRANSFERCTX pTransferCtx)
[100204]124{
125 RT_NOREF(pTransferCtx);
126
127 LogFlowFuncEnter();
128
129 PSHCLCONTEXT pCtx = (PSHCLCONTEXT)pCbCtx->pvUser;
130 AssertPtr(pCtx);
131
132 PSHCLTRANSFER pTransfer = pCbCtx->pTransfer;
133 AssertPtr(pTransfer);
134
135 /* We only need to start the HTTP server when we actually receive data from the remote (host). */
[100367]136 if (ShClTransferGetDir(pTransfer) == SHCLTRANSFERDIR_FROM_REMOTE) /* H->G */
[100204]137 {
[100367]138 int rc2 = ShClTransferHttpServerMaybeStart(&pCtx->X11.HttpCtx);
[100204]139 if (RT_FAILURE(rc2))
140 LogRel(("Shared Clipboard: Registering HTTP transfer failed: %Rrc\n", rc2));
141 }
142
143 LogFlowFuncLeave();
144}
145
146/**
147 * Unregisters a transfer from a HTTP server.
148 *
149 * This also stops the HTTP server if no active transfers are found anymore.
150 *
151 * @param pCtx Shared clipboard context to unregister transfer for.
152 * @param pTransfer Transfer to unregister.
153 *
[100284]154 * @thread Clipboard main thread.
[100204]155 */
[100367]156static void vbclX11TransferUnregister(PSHCLCONTEXT pCtx, PSHCLTRANSFER pTransfer)
[100204]157{
158 if (ShClTransferGetDir(pTransfer) == SHCLTRANSFERDIR_FROM_REMOTE)
159 {
[100367]160 if (ShClTransferHttpServerIsInitialized(&pCtx->X11.HttpCtx.HttpServer))
161 {
162 ShClTransferHttpServerUnregisterTransfer(&pCtx->X11.HttpCtx.HttpServer, pTransfer);
163 ShClTransferHttpServerMaybeStop(&pCtx->X11.HttpCtx);
164 }
[100204]165 }
166}
167
168/**
[100205]169 * @copydoc SHCLTRANSFERCALLBACKS::pfnOnUnregistered
[100204]170 *
171 * Unregisters a (now) unregistered transfer from the HTTP server.
172 *
[100284]173 * @thread Clipboard main thread.
[100204]174 */
[100367]175static DECLCALLBACK(void) vbclX11OnTransferUnregisteredCallback(PSHCLTRANSFERCALLBACKCTX pCbCtx, PSHCLTRANSFERCTX pTransferCtx)
[100204]176{
177 RT_NOREF(pTransferCtx);
[100367]178 vbclX11TransferUnregister((PSHCLCONTEXT)pCbCtx->pvUser, pCbCtx->pTransfer);
[100204]179}
180
181/**
[100205]182 * @copydoc SHCLTRANSFERCALLBACKS::pfnOnCompleted
[100204]183 *
184 * Unregisters a complete transfer from the HTTP server.
185 *
[100284]186 * @thread Clipboard main thread.
[100204]187 */
[100367]188static DECLCALLBACK(void) vbclX11OnTransferCompletedCallback(PSHCLTRANSFERCALLBACKCTX pCbCtx, int rc)
[100204]189{
190 RT_NOREF(rc);
[100367]191 vbclX11TransferUnregister((PSHCLCONTEXT)pCbCtx->pvUser, pCbCtx->pTransfer);
[100204]192}
193
[100205]194/** @copydoc SHCLTRANSFERCALLBACKS::pfnOnError
[100204]195 *
196 * Unregisters a failed transfer from the HTTP server.
197 *
[100284]198 * @thread Clipboard main thread.
[100204]199 */
[100367]200static DECLCALLBACK(void) vbclX11OnTransferErrorCallback(PSHCLTRANSFERCALLBACKCTX pCtx, int rc)
[100204]201{
[100367]202 return vbclX11OnTransferCompletedCallback(pCtx, rc);
[100204]203}
204#endif /* VBOX_WITH_SHARED_CLIPBOARD_TRANSFERS_HTTP */
205
206/**
207 * Worker for a reading clipboard from the host.
208 */
[103365]209static DECLCALLBACK(int) vbclX11ReadDataWorker(PSHCLCONTEXT pCtx,
210 SHCLFORMAT uFmt, void **ppvData, uint32_t *pcbData, void *pvUser)
[100204]211{
[93505]212 RT_NOREF(pvUser);
213
[103365]214 return VbglR3ClipboardReadDataEx(&pCtx->CmdCtx, uFmt, ppvData, pcbData);
[100204]215}
[81843]216
[100204]217/**
218 * @copydoc SHCLCALLBACKS::pfnOnRequestDataFromSource
219 *
[100367]220 * Requests data from the host.
[100204]221 *
[100367]222 * For transfers: This requests a transfer from the host. Most of the handling will be done VbglR3 then.
223 *
[100204]224 * @thread X11 event thread.
225 */
226static DECLCALLBACK(int) vbclX11OnRequestDataFromSourceCallback(PSHCLCONTEXT pCtx,
227 SHCLFORMAT uFmt, void **ppv, uint32_t *pcb, void *pvUser)
228{
229 RT_NOREF(pvUser);
[85834]230
[100204]231 LogFlowFunc(("pCtx=%p, uFmt=%#x\n", pCtx, uFmt));
232
[100235]233 int rc;
[100204]234
235#ifdef VBOX_WITH_SHARED_CLIPBOARD_TRANSFERS_HTTP
236 if (uFmt == VBOX_SHCL_FMT_URI_LIST)
237 {
[100235]238 rc = vbclX11ReadDataWorker(pCtx, uFmt, ppv, pcb, pvUser);
[81871]239 if (RT_SUCCESS(rc))
240 {
[100367]241 /* Request a new H->G transfer from the host.
242 * This is needed in order to get a transfer ID from the host we can initialize our own local transfer with.
243 * Transfer creation and set up will be done in VbglR3. */
244 rc = VbglR3ClipboardTransferRequest(&pCtx->CmdCtx);
245 if (RT_SUCCESS(rc))
[100204]246 {
[100367]247 PSHCLHTTPSERVER pSrv = &pCtx->X11.HttpCtx.HttpServer;
248
249 /* Wait until the HTTP server got the transfer registered, so that we have something to work with. */
250 rc = ShClTransferHttpServerWaitForStatusChange(pSrv, SHCLHTTPSERVERSTATUS_TRANSFER_REGISTERED, SHCL_TIMEOUT_DEFAULT_MS);
[100204]251 if (RT_SUCCESS(rc))
252 {
[100367]253 PSHCLTRANSFER pTransfer = ShClTransferHttpServerGetTransferLast(pSrv);
254 if (pTransfer)
255 {
256 rc = ShClTransferWaitForStatus(pTransfer, SHCL_TIMEOUT_DEFAULT_MS, SHCLTRANSFERSTATUS_INITIALIZED);
257 if (RT_SUCCESS(rc))
258 {
[102818]259 char *pszData;
260 size_t cbData;
261 rc = ShClTransferHttpConvertToStringList(pSrv, pTransfer, &pszData, &cbData);
[100676]262 if (RT_SUCCESS(rc))
263 {
[102818]264 *ppv = pszData;
265 *pcb = cbData;
266 /* ppv has ownership of pszData now. */
[100367]267 }
268 }
269 }
270 else
271 AssertMsgFailed(("No registered transfer found for HTTP server\n"));
[100204]272 }
[100367]273 else
274 LogRel(("Shared Clipboard: Could not start transfer, as no new HTTP transfer was registered in time\n"));
[100204]275 }
[81871]276 }
[7007]277 }
[100235]278 else /* Anything else */
[100204]279#endif /* VBOX_WITH_SHARED_CLIPBOARD_TRANSFERS_HTTP */
[100235]280 {
281 rc = vbclX11ReadDataWorker(pCtx, uFmt, ppv, pcb, pvUser);
282 }
[81843]283
[87082]284 if (RT_FAILURE(rc))
[100367]285 LogRel(("Shared Clipboard: Requesting data in format %#x from host failed with %Rrc\n", uFmt, rc));
[87082]286
[81173]287 LogFlowFuncLeaveRC(rc);
[7007]288 return rc;
289}
290
[81843]291/**
[100204]292 * @copydoc SHCLCALLBACKS::pfnReportFormats
293 *
294 * Reports clipboard formats to the host.
295 *
296 * @thread X11 event thread.
297 */
298static DECLCALLBACK(int) vbclX11ReportFormatsCallback(PSHCLCONTEXT pCtx, uint32_t fFormats, void *pvUser)
[7007]299{
[100235]300 RT_NOREF(pvUser);
[81843]301
[100235]302 LogFlowFunc(("fFormats=%#x\n", fFormats));
303
304 int rc = VbglR3ClipboardReportFormats(pCtx->CmdCtx.idClient, fFormats);
305
[100204]306 LogFlowFuncLeaveRC(rc);
307 return rc;
[7007]308}
309
310/**
[99590]311 * Initializes the X11-specifc Shared Clipboard code.
[7007]312 *
[81843]313 * @returns VBox status code.
[7007]314 */
[99590]315int VBClX11ClipboardInit(void)
[7007]316{
[81060]317 LogFlowFuncEnter();
[7007]318
[100286]319 int rc = ShClEventSourceCreate(&g_Ctx.EventSrc, 0 /* uID */);
320 AssertRCReturn(rc, rc);
321
[93505]322 SHCLCALLBACKS Callbacks;
323 RT_ZERO(Callbacks);
[99590]324 Callbacks.pfnReportFormats = vbclX11ReportFormatsCallback;
325 Callbacks.pfnOnRequestDataFromSource = vbclX11OnRequestDataFromSourceCallback;
[93505]326
[100286]327 rc = ShClX11Init(&g_Ctx.X11, &Callbacks, &g_Ctx, false /* fHeadless */);
[82156]328 if (RT_SUCCESS(rc))
[81060]329 {
[82156]330 rc = ShClX11ThreadStart(&g_Ctx.X11, false /* grab */);
[81060]331 if (RT_SUCCESS(rc))
[7007]332 {
[82525]333 rc = VbglR3ClipboardConnectEx(&g_Ctx.CmdCtx, VBOX_SHCL_GF_0_CONTEXT_ID);
[81960]334 if (RT_FAILURE(rc))
[82156]335 ShClX11ThreadStop(&g_Ctx.X11);
[7007]336 }
337 }
[81060]338 else
[102920]339 VBClLogError("Initializing clipboard failed with %Rrc\n", rc);
[7007]340
[81843]341 if (RT_FAILURE(rc))
342 {
343 VbglR3ClipboardDisconnectEx(&g_Ctx.CmdCtx);
[82156]344 ShClX11Destroy(&g_Ctx.X11);
[81843]345 }
346
[81173]347 LogFlowFuncLeaveRC(rc);
[20552]348 return rc;
[7007]349}
350
351/**
[99590]352 * Destroys the X11-specifc Shared Clipboard code.
353 *
354 * @returns VBox status code.
[7007]355 */
[99590]356int VBClX11ClipboardDestroy(void)
[7007]357{
[100286]358 return ShClEventSourceDestroy(&g_Ctx.EventSrc);
[99590]359}
360
361/**
362 * The main loop of the X11-specifc Shared Clipboard code.
363 *
364 * @returns VBox status code.
[100204]365 *
366 * @thread Clipboard service worker thread.
[99590]367 */
368int VBClX11ClipboardMain(void)
369{
[82156]370 PSHCLCONTEXT pCtx = &g_Ctx;
[81843]371
372 bool fShutdown = false;
373
[99987]374#ifdef VBOX_WITH_SHARED_CLIPBOARD_TRANSFERS
375# ifdef VBOX_WITH_SHARED_CLIPBOARD_TRANSFERS_HTTP
376 /*
377 * Set callbacks.
378 * Those will be registered within VbglR3 when a new transfer gets initialized.
379 *
380 * Used for starting / stopping the HTTP server.
381 */
382 RT_ZERO(pCtx->CmdCtx.Transfers.Callbacks);
383
384 pCtx->CmdCtx.Transfers.Callbacks.pvUser = pCtx; /* Assign context as user-provided callback data. */
385 pCtx->CmdCtx.Transfers.Callbacks.cbUser = sizeof(SHCLCONTEXT);
386
[103442]387 pCtx->CmdCtx.Transfers.Callbacks.pfnOnInitialize = vbclX11OnTransferInitializeCallback;
[100367]388 pCtx->CmdCtx.Transfers.Callbacks.pfnOnRegistered = vbclX11OnTransferRegisteredCallback;
389 pCtx->CmdCtx.Transfers.Callbacks.pfnOnUnregistered = vbclX11OnTransferUnregisteredCallback;
390 pCtx->CmdCtx.Transfers.Callbacks.pfnOnCompleted = vbclX11OnTransferCompletedCallback;
391 pCtx->CmdCtx.Transfers.Callbacks.pfnOnError = vbclX11OnTransferErrorCallback;
[99987]392# endif /* VBOX_WITH_SHARED_CLIPBOARD_TRANSFERS_HTTP */
393#endif /* VBOX_WITH_SHARED_CLIPBOARD_TRANSFERS */
394
[100204]395 LogFlowFunc(("fUseLegacyProtocol=%RTbool, fHostFeatures=%#RX64 ...\n",
396 pCtx->CmdCtx.fUseLegacyProtocol, pCtx->CmdCtx.fHostFeatures));
397
[100235]398 int rc;
399
400 /* The thread waits for incoming messages from the host. */
[81843]401 for (;;)
[7007]402 {
[100235]403 PVBGLR3CLIPBOARDEVENT pEvent = (PVBGLR3CLIPBOARDEVENT)RTMemAllocZ(sizeof(VBGLR3CLIPBOARDEVENT));
[82527]404 AssertPtrBreakStmt(pEvent, rc = VERR_NO_MEMORY);
[81843]405
[82527]406 uint32_t idMsg = 0;
407 uint32_t cParms = 0;
[100235]408 rc = VbglR3ClipboardMsgPeekWait(&pCtx->CmdCtx, &idMsg, &cParms, NULL /* pidRestoreCheck */);
[82527]409 if (RT_SUCCESS(rc))
[7007]410 {
[81843]411#ifdef VBOX_WITH_SHARED_CLIPBOARD_TRANSFERS
[82527]412 rc = VbglR3ClipboardEventGetNextEx(idMsg, cParms, &pCtx->CmdCtx, &pCtx->TransferCtx, pEvent);
[81843]413#else
[82527]414 rc = VbglR3ClipboardEventGetNext(idMsg, cParms, &pCtx->CmdCtx, pEvent);
[81843]415#endif
416 }
417
418 if (RT_FAILURE(rc))
419 {
420 LogFlowFunc(("Getting next event failed with %Rrc\n", rc));
421
422 VbglR3ClipboardEventFree(pEvent);
423 pEvent = NULL;
424
425 if (fShutdown)
426 break;
427
428 /* Wait a bit before retrying. */
[100204]429 RTThreadSleep(RT_MS_1SEC);
[81843]430 continue;
431 }
432 else
433 {
434 AssertPtr(pEvent);
435 LogFlowFunc(("Event uType=%RU32\n", pEvent->enmType));
436
437 switch (pEvent->enmType)
438 {
439 case VBGLR3CLIPBOARDEVENTTYPE_REPORT_FORMATS:
440 {
[100204]441 ShClX11ReportFormatsToX11Async(&g_Ctx.X11, pEvent->u.fReportedFormats);
[7007]442 break;
443 }
444
[81843]445 case VBGLR3CLIPBOARDEVENTTYPE_READ_DATA:
[7007]446 {
[103323]447 void *pvData;
448 uint32_t cbData;
449 rc = ShClX11ReadDataFromX11Ex(&g_Ctx.X11, &pCtx->EventSrc, SHCL_TIMEOUT_DEFAULT_MS, pEvent->u.fReadData,
450 &pvData, &cbData);
[100204]451 if (RT_SUCCESS(rc))
[20552]452 {
[103323]453 rc = VbglR3ClipboardWriteDataEx(&pCtx->CmdCtx, pEvent->u.fReadData, pvData, cbData);
454 RTMemFree(pvData);
[20552]455 }
[100204]456
457 if (RT_FAILURE(rc))
458 VbglR3ClipboardWriteDataEx(&pCtx->CmdCtx, pEvent->u.fReadData, NULL, 0);
459
[7007]460 break;
461 }
462
[81843]463 case VBGLR3CLIPBOARDEVENTTYPE_QUIT:
[7007]464 {
[86871]465 VBClLogVerbose(2, "Host requested termination\n");
[81843]466 fShutdown = true;
[7007]467 break;
468 }
469
[81843]470#ifdef VBOX_WITH_SHARED_CLIPBOARD_TRANSFERS
471 case VBGLR3CLIPBOARDEVENTTYPE_TRANSFER_STATUS:
[81040]472 {
[100204]473 if (pEvent->u.TransferStatus.Report.uStatus == SHCLTRANSFERSTATUS_STARTED)
474 {
475
476 }
[81843]477 rc = VINF_SUCCESS;
[81040]478 break;
479 }
[81843]480#endif
481 case VBGLR3CLIPBOARDEVENTTYPE_NONE:
482 {
483 /* Nothing to do here. */
484 rc = VINF_SUCCESS;
485 break;
486 }
487
488 default:
489 {
490 AssertMsgFailedBreakStmt(("Event type %RU32 not implemented\n", pEvent->enmType), rc = VERR_NOT_SUPPORTED);
491 }
[7007]492 }
[81843]493
494 if (pEvent)
495 {
496 VbglR3ClipboardEventFree(pEvent);
497 pEvent = NULL;
498 }
[7007]499 }
500
[81843]501 if (fShutdown)
502 break;
[7007]503 }
[81843]504
[81173]505 LogFlowFuncLeaveRC(rc);
[7007]506 return rc;
507}
Note: See TracBrowser for help on using the repository browser.

© 2023 Oracle
ContactPrivacy policyTerms of Use