VirtualBox

source: vbox/trunk/src/VBox/HostServices/SharedClipboard/VBoxSharedClipboardSvc-x11.cpp@ 95274

Last change on this file since 95274 was 93919, checked in by vboxsync, 3 years ago

Shared Clipboard: Resolved a @todo (renamed ShClBackendFormatAnnounce -> ShClBackendReportFormats).

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 17.3 KB
Line 
1/* $Id: VBoxSharedClipboardSvc-x11.cpp 93919 2022-02-24 13:59:11Z vboxsync $ */
2/** @file
3 * Shared Clipboard Service - Linux host.
4 */
5
6/*
7 * Copyright (C) 2006-2022 Oracle Corporation
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
18
19/*********************************************************************************************************************************
20* Header Files *
21*********************************************************************************************************************************/
22#define LOG_GROUP LOG_GROUP_SHARED_CLIPBOARD
23#include <iprt/assert.h>
24#include <iprt/critsect.h>
25#include <iprt/env.h>
26#include <iprt/mem.h>
27#include <iprt/semaphore.h>
28#include <iprt/string.h>
29#include <iprt/asm.h>
30
31#include <VBox/GuestHost/SharedClipboard.h>
32#include <VBox/GuestHost/SharedClipboard-x11.h>
33#include <VBox/HostServices/VBoxClipboardSvc.h>
34#include <iprt/errcore.h>
35
36#include "VBoxSharedClipboardSvc-internal.h"
37
38/* Number of currently extablished connections. */
39static volatile uint32_t g_cShClConnections;
40
41
42/*********************************************************************************************************************************
43* Structures and Typedefs *
44*********************************************************************************************************************************/
45/**
46 * Global context information used by the host glue for the X11 clipboard backend.
47 */
48struct SHCLCONTEXT
49{
50 /** This mutex is grabbed during any critical operations on the clipboard
51 * which might clash with others. */
52 RTCRITSECT CritSect;
53 /** X11 context data. */
54 SHCLX11CTX X11;
55 /** Pointer to the VBox host client data structure. */
56 PSHCLCLIENT pClient;
57 /** We set this when we start shutting down as a hint not to post any new
58 * requests. */
59 bool fShuttingDown;
60};
61
62
63/*********************************************************************************************************************************
64* Prototypes *
65*********************************************************************************************************************************/
66static DECLCALLBACK(int) shClReportFormatsCallback(PSHCLCONTEXT pCtx, uint32_t fFormats, void *pvUser);
67static DECLCALLBACK(int) shClSendDataToDestCallback(PSHCLCONTEXT pCtx, void *pv, uint32_t cb, void *pvUser);
68static DECLCALLBACK(int) shClRequestDataFromSourceCallback(PSHCLCONTEXT pCtx, SHCLFORMAT uFmt, void **ppv, uint32_t *pcb, void *pvUser);
69
70
71int ShClBackendInit(PSHCLBACKEND pBackend, VBOXHGCMSVCFNTABLE *pTable)
72{
73 RT_NOREF(pBackend);
74
75 LogFlowFuncEnter();
76
77 /* Override the connection limit. */
78 for (uintptr_t i = 0; i < RT_ELEMENTS(pTable->acMaxClients); i++)
79 pTable->acMaxClients[i] = RT_MIN(VBOX_SHARED_CLIPBOARD_X11_CONNECTIONS_MAX, pTable->acMaxClients[i]);
80
81 RT_ZERO(pBackend->Callbacks);
82 /* Use internal callbacks by default. */
83 pBackend->Callbacks.pfnReportFormats = shClReportFormatsCallback;
84 pBackend->Callbacks.pfnOnRequestDataFromSource = shClRequestDataFromSourceCallback;
85 pBackend->Callbacks.pfnOnSendDataToDest = shClSendDataToDestCallback;
86
87 return VINF_SUCCESS;
88}
89
90void ShClBackendDestroy(PSHCLBACKEND pBackend)
91{
92 RT_NOREF(pBackend);
93
94 LogFlowFuncEnter();
95}
96
97void ShClBackendSetCallbacks(PSHCLBACKEND pBackend, PSHCLCALLBACKS pCallbacks)
98{
99#define SET_FN_IF_NOT_NULL(a_Fn) \
100 if (pCallbacks->pfn##a_Fn) \
101 pBackend->Callbacks.pfn##a_Fn = pCallbacks->pfn##a_Fn;
102
103 SET_FN_IF_NOT_NULL(ReportFormats);
104 SET_FN_IF_NOT_NULL(OnClipboardRead);
105 SET_FN_IF_NOT_NULL(OnClipboardWrite);
106 SET_FN_IF_NOT_NULL(OnRequestDataFromSource);
107 SET_FN_IF_NOT_NULL(OnSendDataToDest);
108
109#undef SET_FN_IF_NOT_NULL
110}
111
112/**
113 * @note On the host, we assume that some other application already owns
114 * the clipboard and leave ownership to X11.
115 */
116int ShClBackendConnect(PSHCLBACKEND pBackend, PSHCLCLIENT pClient, bool fHeadless)
117{
118 int rc;
119
120 /* Check if maximum allowed connections count has reached. */
121 if (ASMAtomicIncU32(&g_cShClConnections) > VBOX_SHARED_CLIPBOARD_X11_CONNECTIONS_MAX)
122 {
123 ASMAtomicDecU32(&g_cShClConnections);
124 LogRel(("Shared Clipboard: maximum amount for client connections reached\n"));
125 return VERR_OUT_OF_RESOURCES;
126 }
127
128 PSHCLCONTEXT pCtx = (PSHCLCONTEXT)RTMemAllocZ(sizeof(SHCLCONTEXT));
129 if (pCtx)
130 {
131 rc = RTCritSectInit(&pCtx->CritSect);
132 if (RT_SUCCESS(rc))
133 {
134 rc = ShClX11Init(&pCtx->X11, &pBackend->Callbacks, pCtx, fHeadless);
135 if (RT_SUCCESS(rc))
136 {
137 pClient->State.pCtx = pCtx;
138 pCtx->pClient = pClient;
139
140 rc = ShClX11ThreadStart(&pCtx->X11, true /* grab shared clipboard */);
141 if (RT_FAILURE(rc))
142 ShClX11Destroy(&pCtx->X11);
143 }
144
145 if (RT_FAILURE(rc))
146 RTCritSectDelete(&pCtx->CritSect);
147 }
148
149 if (RT_FAILURE(rc))
150 {
151 pClient->State.pCtx = NULL;
152 RTMemFree(pCtx);
153 }
154 }
155 else
156 rc = VERR_NO_MEMORY;
157
158 if (RT_FAILURE(rc))
159 {
160 /* Restore active connections count. */
161 ASMAtomicDecU32(&g_cShClConnections);
162 }
163
164 LogFlowFuncLeaveRC(rc);
165 return rc;
166}
167
168int ShClBackendSync(PSHCLBACKEND pBackend, PSHCLCLIENT pClient)
169{
170 RT_NOREF(pBackend);
171
172 LogFlowFuncEnter();
173
174 /* Tell the guest we have no data in case X11 is not available. If
175 * there is data in the host clipboard it will automatically be sent to
176 * the guest when the clipboard starts up. */
177 if (ShClSvcIsBackendActive())
178 return ShClSvcHostReportFormats(pClient, VBOX_SHCL_FMT_NONE);
179 return VINF_SUCCESS;
180}
181
182/*
183 * Shut down the shared clipboard service and "disconnect" the guest.
184 * Note! Host glue code
185 */
186int ShClBackendDisconnect(PSHCLBACKEND pBackend, PSHCLCLIENT pClient)
187{
188 RT_NOREF(pBackend);
189
190 LogFlowFuncEnter();
191
192 PSHCLCONTEXT pCtx = pClient->State.pCtx;
193 AssertPtr(pCtx);
194
195 /* Drop the reference to the client, in case it is still there. This
196 * will cause any outstanding clipboard data requests from X11 to fail
197 * immediately. */
198 pCtx->fShuttingDown = true;
199
200 int rc = ShClX11ThreadStop(&pCtx->X11);
201 /** @todo handle this slightly more reasonably, or be really sure
202 * it won't go wrong. */
203 AssertRC(rc);
204
205 ShClX11Destroy(&pCtx->X11);
206 RTCritSectDelete(&pCtx->CritSect);
207
208 RTMemFree(pCtx);
209
210 /* Decrease active connections count. */
211 ASMAtomicDecU32(&g_cShClConnections);
212
213 LogFlowFuncLeaveRC(rc);
214 return rc;
215}
216
217int ShClBackendReportFormats(PSHCLBACKEND pBackend, PSHCLCLIENT pClient, SHCLFORMATS fFormats)
218{
219 RT_NOREF(pBackend);
220
221 int rc = ShClX11ReportFormatsToX11(&pClient->State.pCtx->X11, fFormats);
222
223 LogFlowFuncLeaveRC(rc);
224 return rc;
225}
226
227/** Structure describing a request for clipoard data from the guest. */
228struct CLIPREADCBREQ
229{
230 /** User-supplied data pointer, based on the request type. */
231 void *pv;
232 /** The size (in bytes) of the the user-supplied pointer in pv. */
233 uint32_t cb;
234 /** The actual size of the data written. */
235 uint32_t *pcbActual;
236 /** The request's event ID. */
237 SHCLEVENTID idEvent;
238};
239
240/**
241 * @note We always fail or complete asynchronously.
242 * @note On success allocates a CLIPREADCBREQ structure which must be
243 * freed in ClipCompleteDataRequestFromX11 when it is called back from
244 * the backend code.
245 */
246int ShClBackendReadData(PSHCLBACKEND pBackend, PSHCLCLIENT pClient, PSHCLCLIENTCMDCTX pCmdCtx, SHCLFORMAT uFormat,
247 void *pvData, uint32_t cbData, uint32_t *pcbActual)
248{
249 RT_NOREF(pBackend);
250
251 AssertPtrReturn(pClient, VERR_INVALID_POINTER);
252 AssertPtrReturn(pCmdCtx, VERR_INVALID_POINTER);
253 AssertPtrReturn(pvData, VERR_INVALID_POINTER);
254 AssertPtrReturn(pcbActual, VERR_INVALID_POINTER);
255
256 RT_NOREF(pCmdCtx);
257
258 LogFlowFunc(("pClient=%p, uFormat=%#x, pv=%p, cb=%RU32, pcbActual=%p\n",
259 pClient, uFormat, pvData, cbData, pcbActual));
260
261 int rc;
262
263 CLIPREADCBREQ *pReq = (CLIPREADCBREQ *)RTMemAllocZ(sizeof(CLIPREADCBREQ));
264 if (pReq)
265 {
266 PSHCLEVENT pEvent;
267 rc = ShClEventSourceGenerateAndRegisterEvent(&pClient->EventSrc, &pEvent);
268 if (RT_SUCCESS(rc))
269 {
270 pReq->pv = pvData;
271 pReq->cb = cbData;
272 pReq->pcbActual = pcbActual;
273 pReq->idEvent = pEvent->idEvent;
274
275 /* Note: ShClX11ReadDataFromX11() will consume pReq on success. */
276 rc = ShClX11ReadDataFromX11(&pClient->State.pCtx->X11, uFormat, pReq);
277 if (RT_SUCCESS(rc))
278 {
279 PSHCLEVENTPAYLOAD pPayload;
280 rc = ShClEventWait(pEvent, 30 * 1000, &pPayload);
281 if (RT_SUCCESS(rc))
282 {
283 if (pPayload)
284 {
285 memcpy(pvData, pPayload->pvData, RT_MIN(cbData, pPayload->cbData));
286
287 *pcbActual = (uint32_t)pPayload->cbData;
288
289 ShClPayloadFree(pPayload);
290 }
291 else /* No payload given; could happen on invalid / not-expected formats. */
292 *pcbActual = 0;
293 }
294 }
295
296 ShClEventRelease(pEvent);
297 }
298
299 if (RT_FAILURE(rc))
300 RTMemFree(pReq);
301 }
302 else
303 rc = VERR_NO_MEMORY;
304
305 if (RT_FAILURE(rc))
306 LogRel(("Shared Clipboard: Error reading host clipboard data from X11, rc=%Rrc\n", rc));
307
308 LogFlowFuncLeaveRC(rc);
309 return rc;
310}
311
312int ShClBackendWriteData(PSHCLBACKEND pBackend, PSHCLCLIENT pClient, PSHCLCLIENTCMDCTX pCmdCtx, SHCLFORMAT uFormat, void *pvData, uint32_t cbData)
313{
314 RT_NOREF(pBackend, pClient, pCmdCtx, uFormat, pvData, cbData);
315
316 LogFlowFuncEnter();
317
318 /* Nothing to do here yet. */
319
320 LogFlowFuncLeave();
321 return VINF_SUCCESS;
322}
323
324/** @copydoc SHCLCALLBACKS::pfnReportFormats */
325static DECLCALLBACK(int) shClReportFormatsCallback(PSHCLCONTEXT pCtx, uint32_t fFormats, void *pvUser)
326{
327 RT_NOREF(pvUser);
328
329 LogFlowFunc(("pCtx=%p, fFormats=%#x\n", pCtx, fFormats));
330
331 int rc = VINF_SUCCESS;
332 PSHCLCLIENT pClient = pCtx->pClient;
333 AssertPtr(pClient);
334
335 rc = RTCritSectEnter(&pClient->CritSect);
336 if (RT_SUCCESS(rc))
337 {
338 if (ShClSvcIsBackendActive())
339 {
340 /** @todo r=bird: BUGBUG: Revisit this */
341 if (fFormats != VBOX_SHCL_FMT_NONE) /* No formats to report? */
342 {
343 rc = ShClSvcHostReportFormats(pCtx->pClient, fFormats);
344 }
345 }
346
347 RTCritSectLeave(&pClient->CritSect);
348 }
349
350 LogFlowFuncLeaveRC(rc);
351 return rc;
352}
353
354/** @copydoc SHCLCALLBACKS::pfnOnSendDataToDest */
355static DECLCALLBACK(int) shClSendDataToDestCallback(PSHCLCONTEXT pCtx, void *pv, uint32_t cb, void *pvUser)
356{
357 AssertPtrReturn(pCtx, VERR_INVALID_POINTER);
358 AssertPtrReturn(pvUser, VERR_INVALID_POINTER);
359
360 PSHCLX11READDATAREQ pData = (PSHCLX11READDATAREQ)pvUser;
361 CLIPREADCBREQ *pReq = pData->pReq;
362
363 LogFlowFunc(("rcCompletion=%Rrc, pReq=%p, pv=%p, cb=%RU32, idEvent=%RU32\n",
364 pData->rcCompletion, pReq, pv, cb, pReq->idEvent));
365
366 if (pReq->idEvent != NIL_SHCLEVENTID)
367 {
368 int rc2;
369
370 PSHCLEVENTPAYLOAD pPayload = NULL;
371 if ( RT_SUCCESS(pData->rcCompletion)
372 && pv
373 && cb)
374 {
375 rc2 = ShClPayloadAlloc(pReq->idEvent, pv, cb, &pPayload);
376 AssertRC(rc2);
377 }
378
379 rc2 = RTCritSectEnter(&pCtx->pClient->CritSect);
380 if (RT_SUCCESS(rc2))
381 {
382 const PSHCLEVENT pEvent = ShClEventSourceGetFromId(&pCtx->pClient->EventSrc, pReq->idEvent);
383 if (pEvent)
384 rc2 = ShClEventSignal(pEvent, pPayload);
385
386 RTCritSectLeave(&pCtx->pClient->CritSect);
387
388 if (RT_SUCCESS(rc2))
389 pPayload = NULL;
390 }
391
392 if (pPayload)
393 ShClPayloadFree(pPayload);
394 }
395
396 if (pReq)
397 RTMemFree(pReq);
398
399 LogRel2(("Shared Clipboard: Reading X11 clipboard data from host completed with %Rrc\n", pData->rcCompletion));
400
401 return VINF_SUCCESS;
402}
403
404/** @copydoc SHCLCALLBACKS::pfnOnRequestDataFromSource */
405static DECLCALLBACK(int) shClRequestDataFromSourceCallback(PSHCLCONTEXT pCtx, SHCLFORMAT uFmt, void **ppv, uint32_t *pcb, void *pvUser)
406{
407 RT_NOREF(pvUser);
408
409 LogFlowFunc(("pCtx=%p, uFmt=0x%x\n", pCtx, uFmt));
410
411 if (pCtx->fShuttingDown)
412 {
413 /* The shared clipboard is disconnecting. */
414 LogRel(("Shared Clipboard: Host requested guest clipboard data after guest had disconnected\n"));
415 return VERR_WRONG_ORDER;
416 }
417
418 PSHCLCLIENT pClient = pCtx->pClient;
419 AssertPtr(pClient);
420
421 RTCritSectEnter(&pClient->CritSect);
422
423 int rc = VINF_SUCCESS;
424
425#ifdef VBOX_WITH_SHARED_CLIPBOARD_TRANSFERS
426 /*
427 * Note: We always return a generic URI list here.
428 * As we don't know which Atom target format was requested by the caller, the X11 clipboard codes needs
429 * to decide & transform the list into the actual clipboard Atom target format the caller wanted.
430 */
431 if (uFmt == VBOX_SHCL_FMT_URI_LIST)
432 {
433 PSHCLTRANSFER pTransfer;
434 rc = shClSvcTransferStart(pCtx->pClient,
435 SHCLTRANSFERDIR_FROM_REMOTE, SHCLSOURCE_REMOTE,
436 &pTransfer);
437 if (RT_SUCCESS(rc))
438 {
439
440 }
441 else
442 LogRel(("Shared Clipboard: Initializing read transfer from guest failed with %Rrc\n", rc));
443
444 *ppv = NULL;
445 *pcb = 0;
446
447 rc = VERR_NO_DATA;
448 }
449#endif
450
451 if (RT_SUCCESS(rc))
452 {
453 /* Request data from the guest. */
454 PSHCLEVENT pEvent;
455 rc = ShClSvcGuestDataRequest(pCtx->pClient, uFmt, &pEvent);
456 if (RT_SUCCESS(rc))
457 {
458 RTCritSectLeave(&pClient->CritSect);
459
460 PSHCLEVENTPAYLOAD pPayload;
461 rc = ShClEventWait(pEvent, 30 * 1000, &pPayload);
462 if (RT_SUCCESS(rc))
463 {
464 if ( !pPayload
465 || !pPayload->cbData)
466 {
467 rc = VERR_NO_DATA;
468 }
469 else
470 {
471 *ppv = pPayload->pvData;
472 *pcb = pPayload->cbData;
473 }
474 }
475
476 RTCritSectEnter(&pClient->CritSect);
477
478 ShClEventRelease(pEvent);
479 }
480 }
481
482 RTCritSectLeave(&pClient->CritSect);
483
484 if (RT_FAILURE(rc))
485 LogRel(("Shared Clipboard: Requesting data in format %#x for X11 host failed with %Rrc\n", uFmt, rc));
486
487 LogFlowFuncLeaveRC(rc);
488 return rc;
489}
490
491#ifdef VBOX_WITH_SHARED_CLIPBOARD_TRANSFERS
492
493int ShClBackendTransferCreate(PSHCLBACKEND pBackend, PSHCLCLIENT pClient, PSHCLTRANSFER pTransfer)
494{
495 RT_NOREF(pBackend);
496#ifdef VBOX_WITH_SHARED_CLIPBOARD_TRANSFERS_HTTP
497 return ShClHttpTransferRegister(&pClient->State.pCtx->X11.HttpCtx, pTransfer);
498#else
499 RT_NOREF(pClient, pTransfer);
500#endif
501 return VERR_NOT_IMPLEMENTED;
502}
503
504int ShClBackendTransferDestroy(PSHCLBACKEND pBackend, PSHCLCLIENT pClient, PSHCLTRANSFER pTransfer)
505{
506 RT_NOREF(pBackend);
507#ifdef VBOX_WITH_SHARED_CLIPBOARD_TRANSFERS_HTTP
508 return ShClHttpTransferUnregister(&pClient->State.pCtx->X11.HttpCtx, pTransfer);
509#else
510 RT_NOREF(pClient, pTransfer);
511#endif
512
513 return VINF_SUCCESS;
514}
515
516int ShClBackendTransferGetRoots(PSHCLBACKEND pBackend, PSHCLCLIENT pClient, PSHCLTRANSFER pTransfer)
517{
518 RT_NOREF(pBackend);
519
520 LogFlowFuncEnter();
521
522 PSHCLEVENT pEvent;
523 int rc = ShClEventSourceGenerateAndRegisterEvent(&pClient->EventSrc, &pEvent);
524 if (RT_SUCCESS(rc))
525 {
526 CLIPREADCBREQ *pReq = (CLIPREADCBREQ *)RTMemAllocZ(sizeof(CLIPREADCBREQ));
527 if (pReq)
528 {
529 pReq->idEvent = pEvent->idEvent;
530
531 rc = ShClX11ReadDataFromX11(&pClient->State.pCtx->X11, VBOX_SHCL_FMT_URI_LIST, pReq);
532 if (RT_SUCCESS(rc))
533 {
534 /* X supplies the data asynchronously, so we need to wait for data to arrive first. */
535 PSHCLEVENTPAYLOAD pPayload;
536 rc = ShClEventWait(pEvent, 30 * 1000, &pPayload);
537 if (RT_SUCCESS(rc))
538 {
539 rc = ShClTransferRootsSet(pTransfer,
540 (char *)pPayload->pvData, pPayload->cbData + 1 /* Include termination */);
541 }
542 }
543 }
544 else
545 rc = VERR_NO_MEMORY;
546
547 ShClEventRelease(pEvent);
548 }
549 else
550 rc = VERR_SHCLPB_MAX_EVENTS_REACHED;
551
552 LogFlowFuncLeaveRC(rc);
553 return rc;
554}
555#endif /* VBOX_WITH_SHARED_CLIPBOARD_TRANSFERS */
556
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