VirtualBox

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

最後變更 在這個檔案從100246是 100235,由 vboxsync 提交於 21 月 前

Shared Clipboard: Got rid of the X11 request worker, as this simplifies code (again). bugref:9437

  • 屬性 svn:eol-style 設為 native
  • 屬性 svn:keywords 設為 Id Revision
檔案大小: 17.0 KB
 
1/** $Id: clipboard-x11.cpp 100235 2023-06-21 10:39:24Z 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.alldomusa.eu.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#define LOG_GROUP LOG_GROUP_SHARED_CLIPBOARD
43#include <iprt/log.h>
44
45#include <VBox/VBoxGuestLib.h>
46#include <VBox/HostServices/VBoxClipboardSvc.h>
47#include <VBox/GuestHost/SharedClipboard.h>
48#include <VBox/GuestHost/SharedClipboard-x11.h>
49
50#include "VBoxClient.h"
51
52#include "clipboard.h"
53
54
55#ifdef VBOX_WITH_SHARED_CLIPBOARD_TRANSFERS_HTTP
56/**
57 * Worker for waiting for a transfer status change.
58 */
59static DECLCALLBACK(int) vbclX11TransferWaitForStatusWorker(PSHCLCONTEXT pCtx, PSHCLTRANSFER pTransfer, SHCLTRANSFERSTATUS enmSts,
60 RTMSINTERVAL msTimeout)
61{
62 RT_NOREF(pCtx);
63
64 LogFlowFuncEnter();
65
66 int rc = VERR_TIMEOUT;
67
68 ShClTransferAcquire(pTransfer);
69
70 uint64_t const tsStartMs = RTTimeMilliTS();
71
72 while (RTTimeMilliTS() - tsStartMs <= msTimeout)
73 {
74 if (ShClTransferGetStatus(pTransfer) == enmSts) /* Currently we only have busy waiting here. */
75 {
76 rc = VINF_SUCCESS;
77 break;
78 }
79 RTThreadSleep(100);
80 }
81
82 ShClTransferRelease(pTransfer);
83
84 return rc;
85}
86
87/**
88 * @copydoc SHCLTRANSFERCALLBACKS::pfnOnRegistered
89 *
90 * This starts the HTTP server if not done yet and registers the transfer with it.
91 *
92 * @thread Clipbpoard main thread.
93 */
94static DECLCALLBACK(void) vbclX11OnHttpTransferRegisteredCallback(PSHCLTRANSFERCALLBACKCTX pCbCtx, PSHCLTRANSFERCTX pTransferCtx)
95{
96 RT_NOREF(pTransferCtx);
97
98 LogFlowFuncEnter();
99
100 PSHCLCONTEXT pCtx = (PSHCLCONTEXT)pCbCtx->pvUser;
101 AssertPtr(pCtx);
102
103 PSHCLTRANSFER pTransfer = pCbCtx->pTransfer;
104 AssertPtr(pTransfer);
105
106 ShClTransferAcquire(pTransfer);
107
108 /* We only need to start the HTTP server when we actually receive data from the remote (host). */
109 if (ShClTransferGetDir(pTransfer) == SHCLTRANSFERDIR_FROM_REMOTE)
110 {
111 /* Retrieve the root entries as a first action, so that the transfer is ready to go
112 * once it gets registered to HTTP server below. */
113 int rc2 = ShClTransferRootListRead(pTransfer);
114 if (RT_SUCCESS(rc2))
115 {
116 ShClTransferHttpServerMaybeStart(&pCtx->X11.HttpCtx);
117 rc2 = ShClTransferHttpServerRegisterTransfer(&pCtx->X11.HttpCtx.HttpServer, pTransfer);
118 }
119
120 if (RT_FAILURE(rc2))
121 LogRel(("Shared Clipboard: Registering HTTP transfer failed: %Rrc\n", rc2));
122 }
123
124 LogFlowFuncLeave();
125}
126
127/**
128 * Unregisters a transfer from a HTTP server.
129 *
130 * This also stops the HTTP server if no active transfers are found anymore.
131 *
132 * @param pCtx Shared clipboard context to unregister transfer for.
133 * @param pTransfer Transfer to unregister.
134 *
135 * @thread Clipbpoard main thread.
136 */
137static void vbclX11HttpTransferUnregister(PSHCLCONTEXT pCtx, PSHCLTRANSFER pTransfer)
138{
139 if (ShClTransferGetDir(pTransfer) == SHCLTRANSFERDIR_FROM_REMOTE)
140 {
141 ShClTransferHttpServerUnregisterTransfer(&pCtx->X11.HttpCtx.HttpServer, pTransfer);
142 ShClTransferHttpServerMaybeStop(&pCtx->X11.HttpCtx);
143 }
144
145 ShClTransferRelease(pTransfer);
146}
147
148/**
149 * @copydoc SHCLTRANSFERCALLBACKS::pfnOnUnregistered
150 *
151 * Unregisters a (now) unregistered transfer from the HTTP server.
152 *
153 * @thread Clipbpoard main thread.
154 */
155static DECLCALLBACK(void) vbclX11OnHttpTransferUnregisteredCallback(PSHCLTRANSFERCALLBACKCTX pCbCtx, PSHCLTRANSFERCTX pTransferCtx)
156{
157 RT_NOREF(pTransferCtx);
158 vbclX11HttpTransferUnregister((PSHCLCONTEXT)pCbCtx->pvUser, pCbCtx->pTransfer);
159}
160
161/**
162 * @copydoc SHCLTRANSFERCALLBACKS::pfnOnCompleted
163 *
164 * Unregisters a complete transfer from the HTTP server.
165 *
166 * @thread Clipbpoard main thread.
167 */
168static DECLCALLBACK(void) vbclX11OnHttpTransferCompletedCallback(PSHCLTRANSFERCALLBACKCTX pCbCtx, int rc)
169{
170 RT_NOREF(rc);
171 vbclX11HttpTransferUnregister((PSHCLCONTEXT)pCbCtx->pvUser, pCbCtx->pTransfer);
172}
173
174/** @copydoc SHCLTRANSFERCALLBACKS::pfnOnError
175 *
176 * Unregisters a failed transfer from the HTTP server.
177 *
178 * @thread Clipbpoard main thread.
179 */
180static DECLCALLBACK(void) vbclX11OnHttpTransferErrorCallback(PSHCLTRANSFERCALLBACKCTX pCtx, int rc)
181{
182 return vbclX11OnHttpTransferCompletedCallback(pCtx, rc);
183}
184#endif /* VBOX_WITH_SHARED_CLIPBOARD_TRANSFERS_HTTP */
185
186/**
187 * Worker for a reading clipboard from the host.
188 */
189static DECLCALLBACK(int) vbclX11ReadDataWorker(PSHCLCONTEXT pCtx, SHCLFORMAT uFmt, void **ppv, uint32_t *pcb, void *pvUser)
190{
191 RT_NOREF(pvUser);
192
193 LogFlowFuncEnter();
194
195 int rc = VERR_NO_DATA; /* Play safe. */
196
197 uint32_t cbRead = 0;
198
199 uint32_t cbData = _4K; /** @todo Make this dynamic. */
200 void *pvData = RTMemAlloc(cbData);
201 if (pvData)
202 {
203 rc = VbglR3ClipboardReadDataEx(&pCtx->CmdCtx, uFmt, pvData, cbData, &cbRead);
204 }
205 else
206 rc = VERR_NO_MEMORY;
207
208 /*
209 * A return value of VINF_BUFFER_OVERFLOW tells us to try again with a
210 * larger buffer. The size of the buffer needed is placed in *pcb.
211 * So we start all over again.
212 */
213 if (rc == VINF_BUFFER_OVERFLOW)
214 {
215 /* cbRead contains the size required. */
216
217 cbData = cbRead;
218 pvData = RTMemRealloc(pvData, cbRead);
219 if (pvData)
220 {
221 rc = VbglR3ClipboardReadDataEx(&pCtx->CmdCtx, uFmt, pvData, cbData, &cbRead);
222 if (rc == VINF_BUFFER_OVERFLOW)
223 rc = VERR_BUFFER_OVERFLOW;
224 }
225 else
226 rc = VERR_NO_MEMORY;
227 }
228
229 if (!cbRead)
230 rc = VERR_NO_DATA;
231
232 if (RT_SUCCESS(rc))
233 {
234 if (ppv)
235 *ppv = pvData;
236 if (pcb)
237 *pcb = cbRead; /* Actual bytes read. */
238 }
239 else
240 {
241 /*
242 * Catch other errors. This also catches the case in which the buffer was
243 * too small a second time, possibly because the clipboard contents
244 * changed half-way through the operation. Since we can't say whether or
245 * not this is actually an error, we just return size 0.
246 */
247 RTMemFree(pvData);
248 }
249
250 LogFlowFuncLeaveRC(rc);
251 return rc;
252}
253
254/**
255 * @copydoc SHCLCALLBACKS::pfnOnRequestDataFromSource
256 *
257 * Requests URI data from the host.
258 * This initiates a transfer on the host. Most of the handling will be done VbglR3 then.
259 *
260 * @thread X11 event thread.
261 */
262static DECLCALLBACK(int) vbclX11OnRequestDataFromSourceCallback(PSHCLCONTEXT pCtx,
263 SHCLFORMAT uFmt, void **ppv, uint32_t *pcb, void *pvUser)
264{
265 RT_NOREF(pvUser);
266
267 LogFlowFunc(("pCtx=%p, uFmt=%#x\n", pCtx, uFmt));
268
269 int rc;
270
271#ifdef VBOX_WITH_SHARED_CLIPBOARD_TRANSFERS_HTTP
272 if (uFmt == VBOX_SHCL_FMT_URI_LIST)
273 {
274 PSHCLHTTPSERVER pSrv = &pCtx->X11.HttpCtx.HttpServer;
275
276 rc = vbclX11ReadDataWorker(pCtx, uFmt, ppv, pcb, pvUser);
277 if (RT_SUCCESS(rc))
278 rc = ShClTransferHttpServerWaitForStatusChange(pSrv, SHCLHTTPSERVERSTATUS_TRANSFER_REGISTERED, 5000 /* SHCL_TIMEOUT_DEFAULT_MS */);
279 if (RT_SUCCESS(rc))
280 {
281 PSHCLTRANSFER pTransfer = ShClTransferHttpServerGetTransferLast(pSrv);
282 if (pTransfer)
283 {
284 rc = vbclX11TransferWaitForStatusWorker(pCtx, pTransfer, SHCLTRANSFERSTATUS_STARTED, SHCL_TIMEOUT_DEFAULT_MS);
285 if (RT_SUCCESS(rc))
286 {
287 char *pszURL = ShClTransferHttpServerGetUrlA(pSrv, pTransfer->State.uID);
288 char *pszData = NULL;
289 RTStrAPrintf(&pszData, "copy\n%s", pszURL);
290
291 *ppv = pszData;
292 *pcb = strlen(pszData) + 1 /* Include terminator */;
293
294 LogFlowFunc(("pszURL=%s\n", pszURL));
295
296 RTStrFree(pszURL);
297
298 rc = VINF_SUCCESS;
299 }
300 }
301 else
302 AssertMsgFailed(("No registered transfer found for HTTP server\n"));
303 }
304 else
305 LogRel(("Shared Clipboard: Could not start transfer, as the HTTP server is not running\n"));
306 }
307 else /* Anything else */
308#endif /* VBOX_WITH_SHARED_CLIPBOARD_TRANSFERS_HTTP */
309 {
310 rc = vbclX11ReadDataWorker(pCtx, uFmt, ppv, pcb, pvUser);
311 }
312
313 if (RT_FAILURE(rc))
314 LogRel(("Requesting data in format %#x from host failed with %Rrc\n", uFmt, rc));
315
316 LogFlowFuncLeaveRC(rc);
317 return rc;
318}
319
320/**
321 * @copydoc SHCLCALLBACKS::pfnReportFormats
322 *
323 * Reports clipboard formats to the host.
324 *
325 * @thread X11 event thread.
326 */
327static DECLCALLBACK(int) vbclX11ReportFormatsCallback(PSHCLCONTEXT pCtx, uint32_t fFormats, void *pvUser)
328{
329 RT_NOREF(pvUser);
330
331 LogFlowFunc(("fFormats=%#x\n", fFormats));
332
333 int rc = VbglR3ClipboardReportFormats(pCtx->CmdCtx.idClient, fFormats);
334
335 LogFlowFuncLeaveRC(rc);
336 return rc;
337}
338
339/**
340 * Initializes the X11-specifc Shared Clipboard code.
341 *
342 * @returns VBox status code.
343 */
344int VBClX11ClipboardInit(void)
345{
346 LogFlowFuncEnter();
347
348 SHCLCALLBACKS Callbacks;
349 RT_ZERO(Callbacks);
350 Callbacks.pfnReportFormats = vbclX11ReportFormatsCallback;
351 Callbacks.pfnOnRequestDataFromSource = vbclX11OnRequestDataFromSourceCallback;
352
353 int rc = ShClX11Init(&g_Ctx.X11, &Callbacks, &g_Ctx, false /* fHeadless */);
354 if (RT_SUCCESS(rc))
355 {
356 rc = ShClX11ThreadStart(&g_Ctx.X11, false /* grab */);
357 if (RT_SUCCESS(rc))
358 {
359 rc = VbglR3ClipboardConnectEx(&g_Ctx.CmdCtx, VBOX_SHCL_GF_0_CONTEXT_ID);
360 if (RT_FAILURE(rc))
361 ShClX11ThreadStop(&g_Ctx.X11);
362 }
363 }
364 else
365 rc = VERR_NO_MEMORY;
366
367 if (RT_FAILURE(rc))
368 {
369 VBClLogError("Error connecting to host service, rc=%Rrc\n", rc);
370
371 VbglR3ClipboardDisconnectEx(&g_Ctx.CmdCtx);
372 ShClX11Destroy(&g_Ctx.X11);
373 }
374
375 LogFlowFuncLeaveRC(rc);
376 return rc;
377}
378
379/**
380 * Destroys the X11-specifc Shared Clipboard code.
381 *
382 * @returns VBox status code.
383 */
384int VBClX11ClipboardDestroy(void)
385{
386 /* Nothing to do here currently. */
387 return VINF_SUCCESS;
388}
389
390/**
391 * The main loop of the X11-specifc Shared Clipboard code.
392 *
393 * @returns VBox status code.
394 *
395 * @thread Clipboard service worker thread.
396 */
397int VBClX11ClipboardMain(void)
398{
399 PSHCLCONTEXT pCtx = &g_Ctx;
400
401 bool fShutdown = false;
402
403#ifdef VBOX_WITH_SHARED_CLIPBOARD_TRANSFERS
404# ifdef VBOX_WITH_SHARED_CLIPBOARD_TRANSFERS_HTTP
405 /*
406 * Set callbacks.
407 * Those will be registered within VbglR3 when a new transfer gets initialized.
408 *
409 * Used for starting / stopping the HTTP server.
410 */
411 RT_ZERO(pCtx->CmdCtx.Transfers.Callbacks);
412
413 pCtx->CmdCtx.Transfers.Callbacks.pvUser = pCtx; /* Assign context as user-provided callback data. */
414 pCtx->CmdCtx.Transfers.Callbacks.cbUser = sizeof(SHCLCONTEXT);
415
416 pCtx->CmdCtx.Transfers.Callbacks.pfnOnRegistered = vbclX11OnHttpTransferRegisteredCallback;
417 pCtx->CmdCtx.Transfers.Callbacks.pfnOnUnregistered = vbclX11OnHttpTransferUnregisteredCallback;
418 pCtx->CmdCtx.Transfers.Callbacks.pfnOnCompleted = vbclX11OnHttpTransferCompletedCallback;
419 pCtx->CmdCtx.Transfers.Callbacks.pfnOnError = vbclX11OnHttpTransferErrorCallback;
420# endif /* VBOX_WITH_SHARED_CLIPBOARD_TRANSFERS_HTTP */
421#endif /* VBOX_WITH_SHARED_CLIPBOARD_TRANSFERS */
422
423 LogFlowFunc(("fUseLegacyProtocol=%RTbool, fHostFeatures=%#RX64 ...\n",
424 pCtx->CmdCtx.fUseLegacyProtocol, pCtx->CmdCtx.fHostFeatures));
425
426 int rc;
427
428 /* The thread waits for incoming messages from the host. */
429 for (;;)
430 {
431 PVBGLR3CLIPBOARDEVENT pEvent = (PVBGLR3CLIPBOARDEVENT)RTMemAllocZ(sizeof(VBGLR3CLIPBOARDEVENT));
432 AssertPtrBreakStmt(pEvent, rc = VERR_NO_MEMORY);
433
434 uint32_t idMsg = 0;
435 uint32_t cParms = 0;
436 rc = VbglR3ClipboardMsgPeekWait(&pCtx->CmdCtx, &idMsg, &cParms, NULL /* pidRestoreCheck */);
437 if (RT_SUCCESS(rc))
438 {
439#ifdef VBOX_WITH_SHARED_CLIPBOARD_TRANSFERS
440 rc = VbglR3ClipboardEventGetNextEx(idMsg, cParms, &pCtx->CmdCtx, &pCtx->TransferCtx, pEvent);
441#else
442 rc = VbglR3ClipboardEventGetNext(idMsg, cParms, &pCtx->CmdCtx, pEvent);
443#endif
444 }
445
446 if (RT_FAILURE(rc))
447 {
448 LogFlowFunc(("Getting next event failed with %Rrc\n", rc));
449
450 VbglR3ClipboardEventFree(pEvent);
451 pEvent = NULL;
452
453 if (fShutdown)
454 break;
455
456 /* Wait a bit before retrying. */
457 RTThreadSleep(RT_MS_1SEC);
458 continue;
459 }
460 else
461 {
462 AssertPtr(pEvent);
463 LogFlowFunc(("Event uType=%RU32\n", pEvent->enmType));
464
465 switch (pEvent->enmType)
466 {
467 case VBGLR3CLIPBOARDEVENTTYPE_REPORT_FORMATS:
468 {
469 ShClX11ReportFormatsToX11Async(&g_Ctx.X11, pEvent->u.fReportedFormats);
470 break;
471 }
472
473 case VBGLR3CLIPBOARDEVENTTYPE_READ_DATA:
474 {
475 PSHCLEVENT pReadDataEvent;
476 rc = ShClEventSourceGenerateAndRegisterEvent(&pCtx->EventSrc, &pReadDataEvent);
477 if (RT_SUCCESS(rc))
478 {
479 rc = ShClX11ReadDataFromX11Async(&g_Ctx.X11, pEvent->u.fReadData, UINT32_MAX, pReadDataEvent);
480 if (RT_SUCCESS(rc))
481 {
482 PSHCLEVENTPAYLOAD pPayload;
483 rc = ShClEventWait(pReadDataEvent, SHCL_TIMEOUT_DEFAULT_MS, &pPayload);
484 if (RT_SUCCESS(rc))
485 {
486 if (pPayload)
487 {
488 Assert(pPayload->cbData == sizeof(SHCLX11RESPONSE));
489 PSHCLX11RESPONSE pResp = (PSHCLX11RESPONSE)pPayload->pvData;
490
491 rc = VbglR3ClipboardWriteDataEx(&pCtx->CmdCtx, pEvent->u.fReadData,
492 pResp->Read.pvData, pResp->Read.cbData);
493
494 RTMemFree(pResp->Read.pvData);
495 pResp->Read.cbData = 0;
496
497 ShClPayloadFree(pPayload);
498 }
499 }
500 }
501
502 ShClEventRelease(pReadDataEvent);
503 pReadDataEvent = NULL;
504 }
505
506 if (RT_FAILURE(rc))
507 VbglR3ClipboardWriteDataEx(&pCtx->CmdCtx, pEvent->u.fReadData, NULL, 0);
508
509 break;
510 }
511
512 case VBGLR3CLIPBOARDEVENTTYPE_QUIT:
513 {
514 VBClLogVerbose(2, "Host requested termination\n");
515 fShutdown = true;
516 break;
517 }
518
519#ifdef VBOX_WITH_SHARED_CLIPBOARD_TRANSFERS
520 case VBGLR3CLIPBOARDEVENTTYPE_TRANSFER_STATUS:
521 {
522 if (pEvent->u.TransferStatus.Report.uStatus == SHCLTRANSFERSTATUS_STARTED)
523 {
524
525 }
526 rc = VINF_SUCCESS;
527 break;
528 }
529#endif
530 case VBGLR3CLIPBOARDEVENTTYPE_NONE:
531 {
532 /* Nothing to do here. */
533 rc = VINF_SUCCESS;
534 break;
535 }
536
537 default:
538 {
539 AssertMsgFailedBreakStmt(("Event type %RU32 not implemented\n", pEvent->enmType), rc = VERR_NOT_SUPPORTED);
540 }
541 }
542
543 if (pEvent)
544 {
545 VbglR3ClipboardEventFree(pEvent);
546 pEvent = NULL;
547 }
548 }
549
550 if (fShutdown)
551 break;
552 }
553
554 LogFlowFuncLeaveRC(rc);
555 return rc;
556}
注意: 瀏覽 TracBrowser 來幫助您使用儲存庫瀏覽器

© 2025 Oracle Support Privacy / Do Not Sell My Info Terms of Use Trademark Policy Automated Access Etiquette