VirtualBox

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

Last change on this file since 86663 was 85983, checked in by vboxsync, 4 years ago

Shared Clipboard/Host Service: No need to go forth and back between common service and backend-specific code when calling ShClBackendWriteData(). Just call ShClSvcGuestDataReceived() from the common service code after calling ShClBackendWriteData() instead.

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