VirtualBox

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

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

Shared Clipboard/X11: Renamed mandatory (own) X11 callbacks to better point out the data flow, added some docs [build fix].

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 14.9 KB
Line 
1/* $Id: VBoxSharedClipboardSvc-x11.cpp 93326 2022-01-18 16:05:48Z 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
63int ShClBackendInit(VBOXHGCMSVCFNTABLE *pTable)
64{
65 LogFlowFuncEnter();
66
67 /* Override the connection limit. */
68 for (uintptr_t i = 0; i < RT_ELEMENTS(pTable->acMaxClients); i++)
69 pTable->acMaxClients[i] = RT_MIN(VBOX_SHARED_CLIPBOARD_X11_CONNECTIONS_MAX, pTable->acMaxClients[i]);
70
71 return VINF_SUCCESS;
72}
73
74void ShClBackendDestroy(void)
75{
76 LogFlowFuncEnter();
77}
78
79/**
80 * @note On the host, we assume that some other application already owns
81 * the clipboard and leave ownership to X11.
82 */
83int ShClBackendConnect(PSHCLCLIENT pClient, bool fHeadless)
84{
85 int rc;
86
87 /* Check if maximum allowed connections count has reached. */
88 if (ASMAtomicIncU32(&g_cShClConnections) > VBOX_SHARED_CLIPBOARD_X11_CONNECTIONS_MAX)
89 {
90 ASMAtomicDecU32(&g_cShClConnections);
91 LogRel(("Shared Clipboard: maximum amount for client connections reached\n"));
92 return VERR_OUT_OF_RESOURCES;
93 }
94
95 PSHCLCONTEXT pCtx = (PSHCLCONTEXT)RTMemAllocZ(sizeof(SHCLCONTEXT));
96 if (pCtx)
97 {
98 rc = RTCritSectInit(&pCtx->CritSect);
99 if (RT_SUCCESS(rc))
100 {
101 rc = ShClX11Init(&pCtx->X11, pCtx, fHeadless);
102 if (RT_SUCCESS(rc))
103 {
104 pClient->State.pCtx = pCtx;
105 pCtx->pClient = pClient;
106
107 rc = ShClX11ThreadStart(&pCtx->X11, true /* grab shared clipboard */);
108 if (RT_FAILURE(rc))
109 ShClX11Destroy(&pCtx->X11);
110 }
111
112 if (RT_FAILURE(rc))
113 RTCritSectDelete(&pCtx->CritSect);
114 }
115
116 if (RT_FAILURE(rc))
117 {
118 pClient->State.pCtx = NULL;
119 RTMemFree(pCtx);
120 }
121 }
122 else
123 rc = VERR_NO_MEMORY;
124
125 if (RT_FAILURE(rc))
126 {
127 /* Restore active connections count. */
128 ASMAtomicDecU32(&g_cShClConnections);
129 }
130
131 LogFlowFuncLeaveRC(rc);
132 return rc;
133}
134
135int ShClBackendSync(PSHCLCLIENT pClient)
136{
137 LogFlowFuncEnter();
138
139 /* Tell the guest we have no data in case X11 is not available. If
140 * there is data in the host clipboard it will automatically be sent to
141 * the guest when the clipboard starts up. */
142 if (ShClSvcIsBackendActive())
143 return ShClSvcHostReportFormats(pClient, VBOX_SHCL_FMT_NONE);
144 return VINF_SUCCESS;
145}
146
147/*
148 * Shut down the shared clipboard service and "disconnect" the guest.
149 * Note! Host glue code
150 */
151int ShClBackendDisconnect(PSHCLCLIENT pClient)
152{
153 LogFlowFuncEnter();
154
155 PSHCLCONTEXT pCtx = pClient->State.pCtx;
156 AssertPtr(pCtx);
157
158 /* Drop the reference to the client, in case it is still there. This
159 * will cause any outstanding clipboard data requests from X11 to fail
160 * immediately. */
161 pCtx->fShuttingDown = true;
162
163 int rc = ShClX11ThreadStop(&pCtx->X11);
164 /** @todo handle this slightly more reasonably, or be really sure
165 * it won't go wrong. */
166 AssertRC(rc);
167
168 ShClX11Destroy(&pCtx->X11);
169 RTCritSectDelete(&pCtx->CritSect);
170
171 RTMemFree(pCtx);
172
173 /* Decrease active connections count. */
174 ASMAtomicDecU32(&g_cShClConnections);
175
176 LogFlowFuncLeaveRC(rc);
177 return rc;
178}
179
180int ShClBackendFormatAnnounce(PSHCLCLIENT pClient, SHCLFORMATS fFormats)
181{
182 int rc = ShClX11ReportFormatsToX11(&pClient->State.pCtx->X11, fFormats);
183
184 LogFlowFuncLeaveRC(rc);
185 return rc;
186}
187
188/** Structure describing a request for clipoard data from the guest. */
189struct CLIPREADCBREQ
190{
191 /** User-supplied data pointer, based on the request type. */
192 void *pv;
193 /** The size (in bytes) of the the user-supplied pointer in pv. */
194 uint32_t cb;
195 /** The actual size of the data written. */
196 uint32_t *pcbActual;
197 /** The request's event ID. */
198 SHCLEVENTID idEvent;
199};
200
201/**
202 * @note We always fail or complete asynchronously.
203 * @note On success allocates a CLIPREADCBREQ structure which must be
204 * freed in ClipCompleteDataRequestFromX11 when it is called back from
205 * the backend code.
206 */
207int ShClBackendReadData(PSHCLCLIENT pClient, PSHCLCLIENTCMDCTX pCmdCtx, SHCLFORMAT uFormat,
208 void *pvData, uint32_t cbData, uint32_t *pcbActual)
209{
210 AssertPtrReturn(pClient, VERR_INVALID_POINTER);
211 AssertPtrReturn(pCmdCtx, VERR_INVALID_POINTER);
212 AssertPtrReturn(pvData, VERR_INVALID_POINTER);
213 AssertPtrReturn(pcbActual, VERR_INVALID_POINTER);
214
215 RT_NOREF(pCmdCtx);
216
217 LogFlowFunc(("pClient=%p, uFormat=%02X, pv=%p, cb=%u, pcbActual=%p\n",
218 pClient, uFormat, pvData, cbData, pcbActual));
219
220 int rc;
221
222 CLIPREADCBREQ *pReq = (CLIPREADCBREQ *)RTMemAllocZ(sizeof(CLIPREADCBREQ));
223 if (pReq)
224 {
225 PSHCLEVENT pEvent;
226 rc = ShClEventSourceGenerateAndRegisterEvent(&pClient->EventSrc, &pEvent);
227 if (RT_SUCCESS(rc))
228 {
229 pReq->pv = pvData;
230 pReq->cb = cbData;
231 pReq->pcbActual = pcbActual;
232 pReq->idEvent = pEvent->idEvent;
233
234 /* Note: ShClX11ReadDataFromX11() will consume pReq on success. */
235 rc = ShClX11ReadDataFromX11(&pClient->State.pCtx->X11, uFormat, pReq);
236 if (RT_SUCCESS(rc))
237 {
238 PSHCLEVENTPAYLOAD pPayload;
239 rc = ShClEventWait(pEvent, 30 * 1000, &pPayload);
240 if (RT_SUCCESS(rc))
241 {
242 if (pPayload)
243 {
244 memcpy(pvData, pPayload->pvData, RT_MIN(cbData, pPayload->cbData));
245
246 *pcbActual = (uint32_t)pPayload->cbData;
247
248 ShClPayloadFree(pPayload);
249 }
250 else /* No payload given; could happen on invalid / not-expected formats. */
251 *pcbActual = 0;
252 }
253 }
254
255 ShClEventRelease(pEvent);
256 }
257
258 if (RT_FAILURE(rc))
259 RTMemFree(pReq);
260 }
261 else
262 rc = VERR_NO_MEMORY;
263
264 if (RT_FAILURE(rc))
265 LogRel(("Shared Clipboard: Error reading host clipboard data from X11, rc=%Rrc\n", rc));
266
267 LogFlowFuncLeaveRC(rc);
268 return rc;
269}
270
271int ShClBackendWriteData(PSHCLCLIENT pClient, PSHCLCLIENTCMDCTX pCmdCtx, SHCLFORMAT uFormat, void *pvData, uint32_t cbData)
272{
273 RT_NOREF(pClient, pCmdCtx, uFormat, pvData, cbData);
274
275 LogFlowFuncEnter();
276
277 /* Nothing to do here yet. */
278
279 LogFlowFuncLeave();
280 return VINF_SUCCESS;
281}
282
283DECLCALLBACK(void) ShClX11ReportFormatsCallback(PSHCLCONTEXT pCtx, uint32_t fFormats)
284{
285 LogFlowFunc(("pCtx=%p, fFormats=%#x\n", pCtx, fFormats));
286
287 int rc = VINF_SUCCESS;
288 PSHCLCLIENT pClient = pCtx->pClient;
289 AssertPtr(pClient);
290
291 rc = RTCritSectEnter(&pClient->CritSect);
292 if (RT_SUCCESS(rc))
293 {
294 if (ShClSvcIsBackendActive())
295 {
296 /** @todo r=bird: BUGBUG: Revisit this */
297 if (fFormats != VBOX_SHCL_FMT_NONE) /* No formats to report? */
298 {
299 rc = ShClSvcHostReportFormats(pCtx->pClient, fFormats);
300 }
301 }
302
303 RTCritSectLeave(&pClient->CritSect);
304 }
305
306 LogFlowFuncLeaveRC(rc);
307}
308
309DECLCALLBACK(void) ShClX11ReportDataCallback(PSHCLCONTEXT pCtx, int rcCompletion,
310 CLIPREADCBREQ *pReq, void *pv, uint32_t cb)
311{
312 AssertPtrReturnVoid(pCtx);
313 AssertPtrReturnVoid(pReq);
314
315 LogFlowFunc(("rcCompletion=%Rrc, pReq=%p, pv=%p, cb=%RU32, idEvent=%RU32\n", rcCompletion, pReq, pv, cb, pReq->idEvent));
316
317 if (pReq->idEvent != NIL_SHCLEVENTID)
318 {
319 int rc2;
320
321 PSHCLEVENTPAYLOAD pPayload = NULL;
322 if ( RT_SUCCESS(rcCompletion)
323 && pv
324 && cb)
325 {
326 rc2 = ShClPayloadAlloc(pReq->idEvent, pv, cb, &pPayload);
327 AssertRC(rc2);
328 }
329
330 rc2 = RTCritSectEnter(&pCtx->pClient->CritSect);
331 if (RT_SUCCESS(rc2))
332 {
333 const PSHCLEVENT pEvent = ShClEventSourceGetFromId(&pCtx->pClient->EventSrc, pReq->idEvent);
334 if (pEvent)
335 rc2 = ShClEventSignal(pEvent, pPayload);
336
337 RTCritSectLeave(&pCtx->pClient->CritSect);
338
339 if (RT_SUCCESS(rc2))
340 pPayload = NULL;
341 }
342
343 if (pPayload)
344 ShClPayloadFree(pPayload);
345 }
346
347 if (pReq)
348 RTMemFree(pReq);
349
350 LogRel2(("Shared Clipboard: Reading X11 clipboard data from host completed with %Rrc\n", rcCompletion));
351}
352
353DECLCALLBACK(int) ShClX11RequestDataCallback(PSHCLCONTEXT pCtx, SHCLFORMAT uFmt, void **ppv, uint32_t *pcb)
354{
355 LogFlowFunc(("pCtx=%p, uFmt=0x%x\n", pCtx, uFmt));
356
357 if (pCtx->fShuttingDown)
358 {
359 /* The shared clipboard is disconnecting. */
360 LogRel(("Shared Clipboard: Host requested guest clipboard data after guest had disconnected\n"));
361 return VERR_WRONG_ORDER;
362 }
363
364 PSHCLCLIENT pClient = pCtx->pClient;
365 AssertPtr(pClient);
366
367 RTCritSectEnter(&pClient->CritSect);
368
369 int rc = VINF_SUCCESS;
370
371#ifdef VBOX_WITH_SHARED_CLIPBOARD_TRANSFERS
372 /*
373 * Note: We always return a generic URI list here.
374 * As we don't know which Atom target format was requested by the caller, the X11 clipboard codes needs
375 * to decide & transform the list into the actual clipboard Atom target format the caller wanted.
376 */
377 if (uFmt == VBOX_SHCL_FMT_URI_LIST)
378 {
379 PSHCLTRANSFER pTransfer;
380 rc = shClSvcTransferStart(pCtx->pClient,
381 SHCLTRANSFERDIR_FROM_REMOTE, SHCLSOURCE_REMOTE,
382 &pTransfer);
383 if (RT_SUCCESS(rc))
384 {
385
386 }
387 else
388 LogRel(("Shared Clipboard: Initializing read transfer from guest failed with %Rrc\n", rc));
389
390 *ppv = NULL;
391 *pcb = 0;
392
393 rc = VERR_NO_DATA;
394 }
395#endif
396
397 if (RT_SUCCESS(rc))
398 {
399 /* Request data from the guest. */
400 PSHCLEVENT pEvent;
401 rc = ShClSvcGuestDataRequest(pCtx->pClient, uFmt, &pEvent);
402 if (RT_SUCCESS(rc))
403 {
404 RTCritSectLeave(&pClient->CritSect);
405
406 PSHCLEVENTPAYLOAD pPayload;
407 rc = ShClEventWait(pEvent, 30 * 1000, &pPayload);
408 if (RT_SUCCESS(rc))
409 {
410 if ( !pPayload
411 || !pPayload->cbData)
412 {
413 rc = VERR_NO_DATA;
414 }
415 else
416 {
417 *ppv = pPayload->pvData;
418 *pcb = pPayload->cbData;
419 }
420 }
421
422 RTCritSectEnter(&pClient->CritSect);
423
424 ShClEventRelease(pEvent);
425 }
426 }
427
428 RTCritSectLeave(&pClient->CritSect);
429
430 if (RT_FAILURE(rc))
431 LogRel(("Shared Clipboard: Requesting data in format %#x for X11 host failed with %Rrc\n", uFmt, rc));
432
433 LogFlowFuncLeaveRC(rc);
434 return rc;
435}
436
437#ifdef VBOX_WITH_SHARED_CLIPBOARD_TRANSFERS
438
439int ShClBackendTransferCreate(PSHCLCLIENT pClient, PSHCLTRANSFER pTransfer)
440{
441#ifdef VBOX_WITH_SHARED_CLIPBOARD_TRANSFERS_HTTP
442 return ShClHttpTransferRegister(&pClient->State.pCtx->X11.HttpCtx, pTransfer);
443#else
444 RT_NOREF(pClient, pTransfer);
445#endif
446 return VERR_NOT_IMPLEMENTED;
447}
448
449int ShClBackendTransferDestroy(PSHCLCLIENT pClient, PSHCLTRANSFER pTransfer)
450{
451#ifdef VBOX_WITH_SHARED_CLIPBOARD_TRANSFERS_HTTP
452 return ShClHttpTransferUnregister(&pClient->State.pCtx->X11.HttpCtx, pTransfer);
453#else
454 RT_NOREF(pClient, pTransfer);
455#endif
456
457 return VINF_SUCCESS;
458}
459
460int ShClBackendTransferGetRoots(PSHCLCLIENT pClient, PSHCLTRANSFER pTransfer)
461{
462 LogFlowFuncEnter();
463
464 PSHCLEVENT pEvent;
465 int rc = ShClEventSourceGenerateAndRegisterEvent(&pClient->EventSrc, &pEvent);
466 if (RT_SUCCESS(rc))
467 {
468 CLIPREADCBREQ *pReq = (CLIPREADCBREQ *)RTMemAllocZ(sizeof(CLIPREADCBREQ));
469 if (pReq)
470 {
471 pReq->idEvent = pEvent->idEvent;
472
473 rc = ShClX11ReadDataFromX11(&pClient->State.pCtx->X11, VBOX_SHCL_FMT_URI_LIST, pReq);
474 if (RT_SUCCESS(rc))
475 {
476 /* X supplies the data asynchronously, so we need to wait for data to arrive first. */
477 PSHCLEVENTPAYLOAD pPayload;
478 rc = ShClEventWait(pEvent, 30 * 1000, &pPayload);
479 if (RT_SUCCESS(rc))
480 {
481 rc = ShClTransferRootsSet(pTransfer,
482 (char *)pPayload->pvData, pPayload->cbData + 1 /* Include termination */);
483 }
484 }
485 }
486 else
487 rc = VERR_NO_MEMORY;
488
489 ShClEventRelease(pEvent);
490 }
491 else
492 rc = VERR_SHCLPB_MAX_EVENTS_REACHED;
493
494 LogFlowFuncLeaveRC(rc);
495 return rc;
496}
497#endif /* VBOX_WITH_SHARED_CLIPBOARD_TRANSFERS */
498
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