VirtualBox

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

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

SharedClipboardSvc,Vbgl: Started looking over the host message handling, adding missing locking and fixing docs. bugref:9437

  • 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 82526 2019-12-09 21:48:53Z vboxsync $ */
2/** @file
3 * Shared Clipboard Service - Linux host.
4 */
5
6/*
7 * Copyright (C) 2006-2019 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 SHCLFORMATDATA formatData;
116 RT_ZERO(formatData);
117
118 formatData.Formats = VBOX_SHCL_FMT_NONE;
119
120 return ShClSvcFormatsReport(pClient, &formatData);
121}
122
123/**
124 * Shut down the shared clipboard service and "disconnect" the guest.
125 * @note Host glue code
126 */
127int ShClSvcImplDisconnect(PSHCLCLIENT pClient)
128{
129 LogFlowFuncEnter();
130
131 PSHCLCONTEXT pCtx = pClient->State.pCtx;
132 AssertPtr(pCtx);
133
134 /* Drop the reference to the client, in case it is still there. This
135 * will cause any outstanding clipboard data requests from X11 to fail
136 * immediately. */
137 pCtx->fShuttingDown = true;
138
139 int rc = ShClX11ThreadStop(&pCtx->X11);
140 /** @todo handle this slightly more reasonably, or be really sure
141 * it won't go wrong. */
142 AssertRC(rc);
143
144 ShClX11Destroy(&pCtx->X11);
145 RTCritSectDelete(&pCtx->CritSect);
146
147 RTMemFree(pCtx);
148
149 LogFlowFuncLeaveRC(rc);
150 return rc;
151}
152
153int ShClSvcImplFormatAnnounce(PSHCLCLIENT pClient, PSHCLCLIENTCMDCTX pCmdCtx,
154 PSHCLFORMATDATA pFormats)
155{
156 RT_NOREF(pCmdCtx);
157
158 int rc = ShClX11ReportFormatsToX11(&pClient->State.pCtx->X11, pFormats->Formats);
159
160 LogFlowFuncLeaveRC(rc);
161 return rc;
162}
163
164/** Structure describing a request for clipoard data from the guest. */
165struct CLIPREADCBREQ
166{
167 /** User-supplied data pointer, based on the request type. */
168 void *pv;
169 /** The size (in bytes) of the the user-supplied pointer in pv. */
170 uint32_t cb;
171 /** The actual size of the data written. */
172 uint32_t *pcbActual;
173 /** The request's event ID. */
174 SHCLEVENTID uEvent;
175};
176
177/**
178 * @note We always fail or complete asynchronously.
179 * @note On success allocates a CLIPREADCBREQ structure which must be
180 * freed in ClipCompleteDataRequestFromX11 when it is called back from
181 * the backend code.
182 */
183int ShClSvcImplReadData(PSHCLCLIENT pClient,
184 PSHCLCLIENTCMDCTX pCmdCtx, PSHCLDATABLOCK pData, uint32_t *pcbActual)
185{
186 RT_NOREF(pCmdCtx);
187
188 LogFlowFunc(("pClient=%p, uFormat=%02X, pv=%p, cb=%u, pcbActual=%p\n",
189 pClient, pData->uFormat, pData->pvData, pData->cbData, pcbActual));
190
191 int rc = VINF_SUCCESS;
192
193 CLIPREADCBREQ *pReq = (CLIPREADCBREQ *)RTMemAllocZ(sizeof(CLIPREADCBREQ));
194 if (pReq)
195 {
196 const SHCLEVENTID uEvent = ShClEventIDGenerate(&pClient->EventSrc);
197
198 pReq->pv = pData->pvData;
199 pReq->cb = pData->cbData;
200 pReq->pcbActual = pcbActual;
201 pReq->uEvent = uEvent;
202
203 rc = ShClEventRegister(&pClient->EventSrc, uEvent);
204 if (RT_SUCCESS(rc))
205 {
206 rc = ShClX11ReadDataFromX11(&pClient->State.pCtx->X11, pData->uFormat, pReq);
207 if (RT_SUCCESS(rc))
208 {
209 PSHCLEVENTPAYLOAD pPayload;
210 rc = ShClEventWait(&pClient->EventSrc, uEvent, 30 * 1000, &pPayload);
211 if (RT_SUCCESS(rc))
212 {
213 memcpy(pData->pvData, pPayload->pvData, RT_MIN(pData->cbData, pPayload->cbData));
214 pData->cbData = pPayload->cbData;
215
216 Assert(pData->cbData == pPayload->cbData); /* Sanity. */
217 }
218 }
219
220 ShClEventUnregister(&pClient->EventSrc, uEvent);
221 }
222 }
223 else
224 rc = VERR_NO_MEMORY;
225
226 LogFlowFuncLeaveRC(rc);
227 return rc;
228}
229
230int ShClSvcImplWriteData(PSHCLCLIENT pClient,
231 PSHCLCLIENTCMDCTX pCmdCtx, PSHCLDATABLOCK pData)
232{
233 AssertPtrReturn(pClient, VERR_INVALID_POINTER);
234 AssertPtrReturn(pCmdCtx, VERR_INVALID_POINTER);
235 AssertPtrReturn(pData, VERR_INVALID_POINTER);
236
237 LogFlowFunc(("pClient=%p, pv=%p, cb=%RU32, uFormat=%02X\n",
238 pClient, pData->pvData, pData->cbData, pData->uFormat));
239
240 int rc = ShClSvcDataReadSignal(pClient, pCmdCtx, pData);
241
242 LogFlowFuncLeaveRC(rc);
243 return rc;
244}
245
246/**
247 * Reports formats available in the X11 clipboard to VBox.
248 *
249 * @note Runs in Xt event thread.
250 *
251 * @param pCtx Opaque context pointer for the glue code.
252 * @param Formats The formats available.
253 */
254DECLCALLBACK(void) ShClX11ReportFormatsCallback(PSHCLCONTEXT pCtx, uint32_t Formats)
255{
256 LogFlowFunc(("pCtx=%p, Formats=%02X\n", pCtx, Formats));
257
258 if (Formats == VBOX_SHCL_FMT_NONE) /* No formats to report? Bail out early. */
259 return;
260
261 SHCLFORMATDATA formatData;
262 RT_ZERO(formatData);
263
264 formatData.Formats = Formats;
265
266 int rc = ShClSvcFormatsReport(pCtx->pClient, &formatData);
267 RT_NOREF(rc);
268
269 LogFlowFuncLeaveRC(rc);
270}
271
272/**
273 * Completes a request from the host service for reading the X11 clipboard data.
274 * The data should be written to the buffer provided in the initial request.
275 *
276 * @note Runs in Xt event thread.
277 *
278 * @param pCtx Request context information.
279 * @param rcCompletion The completion status of the request.
280 * @param pReq Request to complete.
281 * @param pv Address of data from completed request. Optional.
282 * @param cb Size (in bytes) of data from completed request. Optional.
283 *
284 * @todo Change this to deal with the buffer issues rather than offloading them onto the caller.
285 */
286DECLCALLBACK(void) ShClX11RequestFromX11CompleteCallback(PSHCLCONTEXT pCtx, int rcCompletion,
287 CLIPREADCBREQ *pReq, void *pv, uint32_t cb)
288{
289 RT_NOREF(rcCompletion);
290
291 LogFlowFunc(("rcCompletion=%Rrc, pReq=%p, pv=%p, cb=%RU32, uEvent=%RU32\n", rcCompletion, pReq, pv, cb, pReq->uEvent));
292
293 AssertMsgRC(rcCompletion, ("Clipboard data completion from X11 failed with %Rrc\n", rcCompletion));
294
295 if (pReq->uEvent != NIL_SHCLEVENTID)
296 {
297 int rc2;
298
299 PSHCLEVENTPAYLOAD pPayload = NULL;
300 if (pv && cb)
301 {
302 rc2 = ShClPayloadAlloc(pReq->uEvent, pv, cb, &pPayload);
303 AssertRC(rc2);
304 }
305
306 rc2 = ShClEventSignal(&pCtx->pClient->EventSrc, pReq->uEvent, pPayload);
307 AssertRC(rc2);
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 Format The format in which the data should be transferred.
320 * @param ppv On success and if pcb > 0, this will point to a buffer
321 * to be freed with RTMemFree containing the data read.
322 * @param pcb On success, this contains the number of bytes of data
323 * returned.
324 */
325DECLCALLBACK(int) ShClX11RequestDataForX11Callback(PSHCLCONTEXT pCtx, SHCLFORMAT Format, void **ppv, uint32_t *pcb)
326{
327 LogFlowFunc(("pCtx=%p, Format=0x%x\n", pCtx, Format));
328
329 if (pCtx->fShuttingDown)
330 {
331 /* The shared clipboard is disconnecting. */
332 LogRel(("Shared Clipboard: Host requested guest clipboard data after guest had disconnected\n"));
333 return VERR_WRONG_ORDER;
334 }
335
336 int rc;
337
338#ifdef VBOX_WITH_SHARED_CLIPBOARD_TRANSFERS
339 if (Format == VBOX_SHCL_FMT_URI_LIST)
340 {
341 rc = 0;
342 }
343 else
344#endif
345 {
346 /* Request data from the guest. */
347 SHCLDATAREQ dataReq;
348 RT_ZERO(dataReq);
349
350 dataReq.uFmt = Format;
351 dataReq.cbSize = _64K; /** @todo Make this more dynamic. */
352
353 SHCLEVENTID uEvent;
354 rc = ShClSvcDataReadRequest(pCtx->pClient, &dataReq, &uEvent);
355 if (RT_SUCCESS(rc))
356 {
357 PSHCLEVENTPAYLOAD pPayload;
358 rc = ShClEventWait(&pCtx->pClient->EventSrc, uEvent, 30 * 1000, &pPayload);
359 if (RT_SUCCESS(rc))
360 {
361 *ppv = pPayload->pvData;
362 *pcb = pPayload->cbData;
363
364 /* Detach the payload, as the caller then will own the data. */
365 ShClEventPayloadDetach(&pCtx->pClient->EventSrc, uEvent);
366 }
367
368 ShClEventUnregister(&pCtx->pClient->EventSrc, uEvent);
369 }
370 }
371
372 LogFlowFuncLeaveRC(rc);
373 return rc;
374}
375
376#ifdef VBOX_WITH_SHARED_CLIPBOARD_TRANSFERS
377int ShClSvcImplTransferCreate(PSHCLCLIENT pClient, PSHCLTRANSFER pTransfer)
378{
379 RT_NOREF(pClient, pTransfer);
380
381 int rc = VINF_SUCCESS;
382
383 LogFlowFuncLeaveRC(rc);
384 return rc;
385}
386
387int ShClSvcImplTransferDestroy(PSHCLCLIENT pClient, PSHCLTRANSFER pTransfer)
388{
389 RT_NOREF(pClient, pTransfer);
390
391 int rc = VINF_SUCCESS;
392
393 LogFlowFuncLeaveRC(rc);
394 return rc;
395}
396
397int ShClSvcImplTransferGetRoots(PSHCLCLIENT pClient, PSHCLTRANSFER pTransfer)
398{
399 LogFlowFuncEnter();
400
401 SHCLEVENTID uEvent = ShClEventIDGenerate(&pClient->EventSrc);
402
403 int rc = ShClEventRegister(&pClient->EventSrc, uEvent);
404 if (RT_SUCCESS(rc))
405 {
406 CLIPREADCBREQ *pReq = (CLIPREADCBREQ *)RTMemAllocZ(sizeof(CLIPREADCBREQ));
407 if (pReq)
408 {
409 pReq->uEvent = uEvent;
410
411 rc = ShClX11ReadDataFromX11(&pClient->State.pCtx->X11, VBOX_SHCL_FMT_URI_LIST, pReq);
412 if (RT_SUCCESS(rc))
413 {
414 /* X supplies the data asynchronously, so we need to wait for data to arrive first. */
415 PSHCLEVENTPAYLOAD pPayload;
416 rc = ShClEventWait(&pClient->EventSrc, uEvent, 30 * 1000, &pPayload);
417 if (RT_SUCCESS(rc))
418 {
419 rc = ShClTransferRootsSet(pTransfer,
420 (char *)pPayload->pvData, pPayload->cbData + 1 /* Include termination */);
421 }
422 }
423 }
424
425 ShClEventUnregister(&pClient->EventSrc, uEvent);
426 }
427
428 LogFlowFuncLeaveRC(rc);
429 return rc;
430}
431#endif
Note: See TracBrowser for help on using the repository browser.

© 2025 Oracle Support Privacy / Do Not Sell My Info Terms of Use Trademark Policy Automated Access Etiquette