VirtualBox

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

Last change on this file since 85416 was 84142, checked in by vboxsync, 5 years ago

Shared Clipboard/HostService: Renaming to match terminology (*SvcImpl* -> *Backend*).

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