VirtualBox

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

Last change on this file since 88754 was 87686, checked in by vboxsync, 4 years ago

Shared Clipboard/Transfers: Renaming. ​bugref:9437

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