VirtualBox

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

Last change on this file since 85915 was 85834, checked in by vboxsync, 4 years ago

Shared Clipboard/X11: Return VERR_NO_DATA in X11 callback implementations to make it easier to spot that there is no data available.

  • 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 85834 2020-08-19 14:05:37Z 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,
176 PSHCLCLIENTCMDCTX pCmdCtx, SHCLFORMAT uFormat, 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 LogFlowFuncLeaveRC(rc);
233 return rc;
234}
235
236int ShClBackendWriteData(PSHCLCLIENT pClient,
237 PSHCLCLIENTCMDCTX pCmdCtx, SHCLFORMAT uFormat, void *pvData, uint32_t cbData)
238{
239 AssertPtrReturn(pClient, VERR_INVALID_POINTER);
240 AssertPtrReturn(pCmdCtx, VERR_INVALID_POINTER);
241 AssertPtrReturn(pvData, VERR_INVALID_POINTER);
242
243 LogFlowFunc(("pClient=%p, pv=%p, cb=%RU32, uFormat=%02X\n",
244 pClient, pvData, cbData, uFormat));
245
246 int rc = ShClSvcDataReadSignal(pClient, pCmdCtx, uFormat, pvData, cbData);
247
248 LogFlowFuncLeaveRC(rc);
249 return rc;
250}
251
252/**
253 * Reports formats available in the X11 clipboard to VBox.
254 *
255 * @note Runs in Xt event thread.
256 *
257 * @param pCtx Opaque context pointer for the glue code.
258 * @param fFormats The formats available.
259 */
260DECLCALLBACK(void) ShClX11ReportFormatsCallback(PSHCLCONTEXT pCtx, uint32_t fFormats)
261{
262 LogFlowFunc(("pCtx=%p, fFormats=%#x\n", pCtx, fFormats));
263
264 /** @todo r=bird: BUGBUG: Revisit this */
265 if (fFormats == VBOX_SHCL_FMT_NONE) /* No formats to report? Bail out early. */
266 return;
267
268 int rc = ShClSvcHostReportFormats(pCtx->pClient, fFormats);
269 RT_NOREF(rc);
270
271 LogFlowFuncLeaveRC(rc);
272}
273
274/**
275 * Completes a request from the host service for reading the X11 clipboard data.
276 * The data should be written to the buffer provided in the initial request.
277 *
278 * @note Runs in Xt event thread.
279 *
280 * @param pCtx Request context information.
281 * @param rcCompletion The completion status of the request.
282 * @param pReq Request to complete.
283 * @param pv Address of data from completed request. Optional.
284 * @param cb Size (in bytes) of data from completed request. Optional.
285 *
286 * @todo Change this to deal with the buffer issues rather than offloading them onto the caller.
287 */
288DECLCALLBACK(void) ShClX11RequestFromX11CompleteCallback(PSHCLCONTEXT pCtx, int rcCompletion,
289 CLIPREADCBREQ *pReq, void *pv, uint32_t cb)
290{
291 RT_NOREF(rcCompletion);
292
293 LogFlowFunc(("rcCompletion=%Rrc, pReq=%p, pv=%p, cb=%RU32, idEvent=%RU32\n", rcCompletion, pReq, pv, cb, pReq->idEvent));
294
295 AssertMsgRC(rcCompletion, ("Clipboard data completion from X11 failed with %Rrc\n", rcCompletion));
296
297 if (pReq->idEvent != NIL_SHCLEVENTID)
298 {
299 int rc2;
300
301 PSHCLEVENTPAYLOAD pPayload = NULL;
302 if (pv && cb)
303 {
304 rc2 = ShClPayloadAlloc(pReq->idEvent, pv, cb, &pPayload);
305 AssertRC(rc2);
306 }
307
308 RTCritSectEnter(&pCtx->pClient->CritSect);
309 rc2 = ShClEventSignal(&pCtx->pClient->EventSrc, pReq->idEvent, pPayload);
310 AssertRC(rc2);
311 RTCritSectLeave(&pCtx->pClient->CritSect);
312 }
313
314 RTMemFree(pReq);
315}
316
317/**
318 * Callback implementation for reading clipboard data from the guest.
319 *
320 * @note Runs in Xt event thread.
321 *
322 * @returns VBox status code. VERR_NO_DATA if no data available.
323 * @param pCtx Pointer to the host clipboard structure.
324 * @param fFormat The format in which the data should be transferred
325 * (VBOX_SHCL_FMT_XXX).
326 * @param ppv On success and if pcb > 0, this will point to a buffer
327 * to be freed with RTMemFree containing the data read.
328 * @param pcb On success, this contains the number of bytes of data returned.
329 */
330DECLCALLBACK(int) ShClX11RequestDataForX11Callback(PSHCLCONTEXT pCtx, SHCLFORMAT fFormat, void **ppv, uint32_t *pcb)
331{
332 LogFlowFunc(("pCtx=%p, Format=0x%x\n", pCtx, fFormat));
333
334 if (pCtx->fShuttingDown)
335 {
336 /* The shared clipboard is disconnecting. */
337 LogRel(("Shared Clipboard: Host requested guest clipboard data after guest had disconnected\n"));
338 return VERR_WRONG_ORDER;
339 }
340
341 int rc;
342
343#ifdef VBOX_WITH_SHARED_CLIPBOARD_TRANSFERS
344 if (fFormat == VBOX_SHCL_FMT_URI_LIST)
345 rc = VINF_SUCCESS;
346 else
347#endif
348 {
349 /* Request data from the guest. */
350 SHCLEVENTID idEvent;
351 rc = ShClSvcDataReadRequest(pCtx->pClient, fFormat, &idEvent);
352 if (RT_SUCCESS(rc))
353 {
354 PSHCLEVENTPAYLOAD pPayload;
355 rc = ShClEventWait(&pCtx->pClient->EventSrc, idEvent, 30 * 1000, &pPayload);
356 if (RT_SUCCESS(rc))
357 {
358 if ( !pPayload
359 || !pPayload->cbData)
360 {
361 rc = VERR_NO_DATA;
362 }
363 else
364 {
365 *ppv = pPayload->pvData;
366 *pcb = pPayload->cbData;
367 }
368 }
369
370 ShClEventRelease(&pCtx->pClient->EventSrc, idEvent);
371 ShClEventUnregister(&pCtx->pClient->EventSrc, idEvent);
372 }
373 }
374
375 LogFlowFuncLeaveRC(rc);
376 return rc;
377}
378
379#ifdef VBOX_WITH_SHARED_CLIPBOARD_TRANSFERS
380
381int ShClBackendTransferCreate(PSHCLCLIENT pClient, PSHCLTRANSFER pTransfer)
382{
383 RT_NOREF(pClient, pTransfer);
384
385 int rc = VINF_SUCCESS;
386
387 LogFlowFuncLeaveRC(rc);
388 return rc;
389}
390
391int ShClBackendTransferDestroy(PSHCLCLIENT pClient, PSHCLTRANSFER pTransfer)
392{
393 RT_NOREF(pClient, pTransfer);
394
395 int rc = VINF_SUCCESS;
396
397 LogFlowFuncLeaveRC(rc);
398 return rc;
399}
400
401int ShClBackendTransferGetRoots(PSHCLCLIENT pClient, PSHCLTRANSFER pTransfer)
402{
403 LogFlowFuncEnter();
404
405 int rc;
406
407 SHCLEVENTID idEvent = ShClEventIdGenerateAndRegister(&pClient->EventSrc);
408 if (idEvent != NIL_SHCLEVENTID)
409 {
410 CLIPREADCBREQ *pReq = (CLIPREADCBREQ *)RTMemAllocZ(sizeof(CLIPREADCBREQ));
411 if (pReq)
412 {
413 pReq->idEvent = idEvent;
414
415 rc = ShClX11ReadDataFromX11(&pClient->State.pCtx->X11, VBOX_SHCL_FMT_URI_LIST, pReq);
416 if (RT_SUCCESS(rc))
417 {
418 /* X supplies the data asynchronously, so we need to wait for data to arrive first. */
419 PSHCLEVENTPAYLOAD pPayload;
420 rc = ShClEventWait(&pClient->EventSrc, idEvent, 30 * 1000, &pPayload);
421 if (RT_SUCCESS(rc))
422 {
423 rc = ShClTransferRootsSet(pTransfer,
424 (char *)pPayload->pvData, pPayload->cbData + 1 /* Include termination */);
425 }
426 }
427 }
428 else
429 rc = VERR_NO_MEMORY;
430
431 ShClEventUnregister(&pClient->EventSrc, idEvent);
432 }
433 else
434 rc = VERR_SHCLPB_MAX_EVENTS_REACHED;
435
436 LogFlowFuncLeaveRC(rc);
437 return rc;
438}
439
440#endif /* VBOX_WITH_SHARED_CLIPBOARD_TRANSFERS */
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