VirtualBox

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

Last change on this file since 83133 was 82893, checked in by vboxsync, 5 years ago

Shared Clipboard: Dealt with event retention for ShClSvcDataReadRequest(). bugref:9437

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 13.3 KB
Line 
1/* $Id: VBoxSharedClipboardSvc-x11.cpp 82893 2020-01-28 16:53:51Z 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 ShClSvcImplInit(void)
60{
61 LogFlowFuncEnter();
62 return VINF_SUCCESS;
63}
64
65void ShClSvcImplDestroy(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 ShClSvcImplConnect(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 ShClSvcImplSync(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 ShClSvcImplDisconnect(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 ShClSvcImplFormatAnnounce(PSHCLCLIENT pClient, PSHCLCLIENTCMDCTX pCmdCtx,
149 PSHCLFORMATDATA pFormats)
150{
151 RT_NOREF(pCmdCtx);
152
153 int rc = ShClX11ReportFormatsToX11(&pClient->State.pCtx->X11, pFormats->Formats);
154
155 LogFlowFuncLeaveRC(rc);
156 return rc;
157}
158
159/** Structure describing a request for clipoard data from the guest. */
160struct CLIPREADCBREQ
161{
162 /** User-supplied data pointer, based on the request type. */
163 void *pv;
164 /** The size (in bytes) of the the user-supplied pointer in pv. */
165 uint32_t cb;
166 /** The actual size of the data written. */
167 uint32_t *pcbActual;
168 /** The request's event ID. */
169 SHCLEVENTID idEvent;
170};
171
172/**
173 * @note We always fail or complete asynchronously.
174 * @note On success allocates a CLIPREADCBREQ structure which must be
175 * freed in ClipCompleteDataRequestFromX11 when it is called back from
176 * the backend code.
177 */
178int ShClSvcImplReadData(PSHCLCLIENT pClient,
179 PSHCLCLIENTCMDCTX pCmdCtx, SHCLFORMAT uFormat, void *pvData, uint32_t cbData, uint32_t *pcbActual)
180{
181 AssertPtrReturn(pClient, VERR_INVALID_POINTER);
182 AssertPtrReturn(pCmdCtx, VERR_INVALID_POINTER);
183 AssertPtrReturn(pvData, VERR_INVALID_POINTER);
184
185 RT_NOREF(pCmdCtx);
186
187 LogFlowFunc(("pClient=%p, uFormat=%02X, pv=%p, cb=%u, pcbActual=%p\n",
188 pClient, uFormat, pvData, cbData, pcbActual));
189
190 int rc = VINF_SUCCESS;
191
192 CLIPREADCBREQ *pReq = (CLIPREADCBREQ *)RTMemAllocZ(sizeof(CLIPREADCBREQ));
193 if (pReq)
194 {
195 pReq->pv = pvData;
196 pReq->cb = cbData;
197 pReq->pcbActual = pcbActual;
198 const SHCLEVENTID idEvent = ShClEventIdGenerateAndRegister(&pClient->EventSrc);
199 pReq->idEvent = idEvent;
200 if (idEvent != NIL_SHCLEVENTID)
201 {
202 rc = ShClX11ReadDataFromX11(&pClient->State.pCtx->X11, uFormat, pReq);
203 if (RT_SUCCESS(rc))
204 {
205 PSHCLEVENTPAYLOAD pPayload;
206 rc = ShClEventWait(&pClient->EventSrc, idEvent, 30 * 1000, &pPayload);
207 if (RT_SUCCESS(rc))
208 {
209 memcpy(pvData, pPayload->pvData, RT_MIN(cbData, pPayload->cbData));
210
211 *pcbActual = (uint32_t)pPayload->cbData;
212
213 ShClPayloadFree(pPayload);
214 }
215 }
216
217 ShClEventUnregister(&pClient->EventSrc, idEvent);
218 }
219 else
220 {
221 RTMemFree(pReq);
222 rc = VERR_SHCLPB_MAX_EVENTS_REACHED;
223 }
224 }
225 else
226 rc = VERR_NO_MEMORY;
227
228 LogFlowFuncLeaveRC(rc);
229 return rc;
230}
231
232int ShClSvcImplWriteData(PSHCLCLIENT pClient,
233 PSHCLCLIENTCMDCTX pCmdCtx, SHCLFORMAT uFormat, void *pvData, uint32_t cbData)
234{
235 AssertPtrReturn(pClient, VERR_INVALID_POINTER);
236 AssertPtrReturn(pCmdCtx, VERR_INVALID_POINTER);
237 AssertPtrReturn(pvData, VERR_INVALID_POINTER);
238
239 LogFlowFunc(("pClient=%p, pv=%p, cb=%RU32, uFormat=%02X\n",
240 pClient, pvData, cbData, uFormat));
241
242 int rc = ShClSvcDataReadSignal(pClient, pCmdCtx, uFormat, pvData, cbData);
243
244 LogFlowFuncLeaveRC(rc);
245 return rc;
246}
247
248/**
249 * Reports formats available in the X11 clipboard to VBox.
250 *
251 * @note Runs in Xt event thread.
252 *
253 * @param pCtx Opaque context pointer for the glue code.
254 * @param fFormats The formats available.
255 */
256DECLCALLBACK(void) ShClX11ReportFormatsCallback(PSHCLCONTEXT pCtx, uint32_t fFormats)
257{
258 LogFlowFunc(("pCtx=%p, fFormats=%#x\n", pCtx, fFormats));
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 LogFlowFuncLeaveRC(rc);
268}
269
270/**
271 * Completes a request from the host service for reading the X11 clipboard data.
272 * The data should be written to the buffer provided in the initial request.
273 *
274 * @note Runs in Xt event thread.
275 *
276 * @param pCtx Request context information.
277 * @param rcCompletion The completion status of the request.
278 * @param pReq Request to complete.
279 * @param pv Address of data from completed request. Optional.
280 * @param cb Size (in bytes) of data from completed request. Optional.
281 *
282 * @todo Change this to deal with the buffer issues rather than offloading them onto the caller.
283 */
284DECLCALLBACK(void) ShClX11RequestFromX11CompleteCallback(PSHCLCONTEXT pCtx, int rcCompletion,
285 CLIPREADCBREQ *pReq, void *pv, uint32_t cb)
286{
287 RT_NOREF(rcCompletion);
288
289 LogFlowFunc(("rcCompletion=%Rrc, pReq=%p, pv=%p, cb=%RU32, idEvent=%RU32\n", rcCompletion, pReq, pv, cb, pReq->idEvent));
290
291 AssertMsgRC(rcCompletion, ("Clipboard data completion from X11 failed with %Rrc\n", rcCompletion));
292
293 if (pReq->idEvent != NIL_SHCLEVENTID)
294 {
295 int rc2;
296
297 PSHCLEVENTPAYLOAD pPayload = NULL;
298 if (pv && cb)
299 {
300 rc2 = ShClPayloadAlloc(pReq->idEvent, pv, cb, &pPayload);
301 AssertRC(rc2);
302 }
303
304 RTCritSectEnter(&pCtx->pClient->CritSect);
305 rc2 = ShClEventSignal(&pCtx->pClient->EventSrc, pReq->idEvent, pPayload);
306 AssertRC(rc2);
307 RTCritSectLeave(&pCtx->pClient->CritSect);
308 }
309
310 RTMemFree(pReq);
311}
312
313/**
314 * Reads clipboard data from the guest and passes it to the X11 clipboard.
315 *
316 * @note Runs in Xt event thread.
317 *
318 * @param pCtx Pointer to the host clipboard structure.
319 * @param fFormat The format in which the data should be transferred
320 * (VBOX_SHCL_FMT_XXX).
321 * @param ppv On success and if pcb > 0, this will point to a buffer
322 * to be freed with RTMemFree containing the data read.
323 * @param pcb On success, this contains the number of bytes of data
324 * returned.
325 */
326DECLCALLBACK(int) ShClX11RequestDataForX11Callback(PSHCLCONTEXT pCtx, SHCLFORMAT fFormat, void **ppv, uint32_t *pcb)
327{
328 LogFlowFunc(("pCtx=%p, Format=0x%x\n", pCtx, fFormat));
329
330 if (pCtx->fShuttingDown)
331 {
332 /* The shared clipboard is disconnecting. */
333 LogRel(("Shared Clipboard: Host requested guest clipboard data after guest had disconnected\n"));
334 return VERR_WRONG_ORDER;
335 }
336
337 int rc;
338
339#ifdef VBOX_WITH_SHARED_CLIPBOARD_TRANSFERS
340 if (fFormat == VBOX_SHCL_FMT_URI_LIST)
341 rc = VINF_SUCCESS;
342 else
343#endif
344 {
345 /* Request data from the guest. */
346 SHCLEVENTID idEvent;
347 rc = ShClSvcDataReadRequest(pCtx->pClient, fFormat, &idEvent);
348 if (RT_SUCCESS(rc))
349 {
350 PSHCLEVENTPAYLOAD pPayload;
351 rc = ShClEventWait(&pCtx->pClient->EventSrc, idEvent, 30 * 1000, &pPayload);
352 if (RT_SUCCESS(rc))
353 {
354 *ppv = pPayload ? pPayload->pvData : NULL;
355 *pcb = pPayload ? pPayload->cbData : 0;
356 }
357
358 ShClEventRelease(&pCtx->pClient->EventSrc, idEvent);
359 ShClEventUnregister(&pCtx->pClient->EventSrc, idEvent);
360 }
361 }
362
363 LogFlowFuncLeaveRC(rc);
364 return rc;
365}
366
367#ifdef VBOX_WITH_SHARED_CLIPBOARD_TRANSFERS
368
369int ShClSvcImplTransferCreate(PSHCLCLIENT pClient, PSHCLTRANSFER pTransfer)
370{
371 RT_NOREF(pClient, pTransfer);
372
373 int rc = VINF_SUCCESS;
374
375 LogFlowFuncLeaveRC(rc);
376 return rc;
377}
378
379int ShClSvcImplTransferDestroy(PSHCLCLIENT pClient, PSHCLTRANSFER pTransfer)
380{
381 RT_NOREF(pClient, pTransfer);
382
383 int rc = VINF_SUCCESS;
384
385 LogFlowFuncLeaveRC(rc);
386 return rc;
387}
388
389int ShClSvcImplTransferGetRoots(PSHCLCLIENT pClient, PSHCLTRANSFER pTransfer)
390{
391 LogFlowFuncEnter();
392
393 int rc;
394
395 SHCLEVENTID idEvent = ShClEventIdGenerateAndRegister(&pClient->EventSrc);
396 if (idEvent != NIL_SHCLEVENTID)
397 {
398 CLIPREADCBREQ *pReq = (CLIPREADCBREQ *)RTMemAllocZ(sizeof(CLIPREADCBREQ));
399 if (pReq)
400 {
401 pReq->idEvent = idEvent;
402
403 rc = ShClX11ReadDataFromX11(&pClient->State.pCtx->X11, VBOX_SHCL_FMT_URI_LIST, pReq);
404 if (RT_SUCCESS(rc))
405 {
406 /* X supplies the data asynchronously, so we need to wait for data to arrive first. */
407 PSHCLEVENTPAYLOAD pPayload;
408 rc = ShClEventWait(&pClient->EventSrc, idEvent, 30 * 1000, &pPayload);
409 if (RT_SUCCESS(rc))
410 {
411 rc = ShClTransferRootsSet(pTransfer,
412 (char *)pPayload->pvData, pPayload->cbData + 1 /* Include termination */);
413 }
414 }
415 }
416 else
417 rc = VERR_NO_MEMORY;
418
419 ShClEventUnregister(&pClient->EventSrc, idEvent);
420 }
421 else
422 rc = VERR_SHCLPB_MAX_EVENTS_REACHED;
423
424 LogFlowFuncLeaveRC(rc);
425 return rc;
426}
427
428#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