VirtualBox

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

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

Shared Clipboard/X11: More cleanup.

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 13.1 KB
Line 
1/* $Id: VBoxSharedClipboardSvc-x11.cpp 82266 2019-11-28 14:39:23Z 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/**
43 * Global context information used by the host glue for the X11 clipboard backend.
44 */
45struct _SHCLCONTEXT
46{
47 /** This mutex is grabbed during any critical operations on the clipboard
48 * which might clash with others. */
49 RTCRITSECT CritSect;
50 /** X11 context data. */
51 SHCLX11CTX X11;
52 /** Pointer to the VBox host client data structure. */
53 PSHCLCLIENT pClient;
54 /** We set this when we start shutting down as a hint not to post any new
55 * requests. */
56 bool fShuttingDown;
57};
58
59
60int ShClSvcImplInit(void)
61{
62 LogFlowFuncEnter();
63 return VINF_SUCCESS;
64}
65
66void ShClSvcImplDestroy(void)
67{
68 LogFlowFuncEnter();
69}
70
71/**
72 * @note On the host, we assume that some other application already owns
73 * the clipboard and leave ownership to X11.
74 */
75int ShClSvcImplConnect(PSHCLCLIENT pClient, bool fHeadless)
76{
77 int rc;
78
79 PSHCLCONTEXT pCtx = (PSHCLCONTEXT)RTMemAllocZ(sizeof(SHCLCONTEXT));
80 if (pCtx)
81 {
82 rc = RTCritSectInit(&pCtx->CritSect);
83 if (RT_SUCCESS(rc))
84 {
85 rc = ShClX11Init(&pCtx->X11, pCtx, fHeadless);
86 if (RT_SUCCESS(rc))
87 {
88 pClient->State.pCtx = pCtx;
89 pCtx->pClient = pClient;
90
91 rc = ShClX11ThreadStart(&pCtx->X11, true /* grab shared clipboard */);
92 if (RT_FAILURE(rc))
93 ShClX11Destroy(&pCtx->X11);
94 }
95
96 if (RT_FAILURE(rc))
97 RTCritSectDelete(&pCtx->CritSect);
98 }
99 else
100 RTMemFree(pCtx);
101 }
102 else
103 rc = VERR_NO_MEMORY;
104
105 LogFlowFuncLeaveRC(rc);
106 return rc;
107}
108
109int ShClSvcImplSync(PSHCLCLIENT pClient)
110{
111 LogFlowFuncEnter();
112
113 /* Tell the guest we have no data in case X11 is not available. If
114 * there is data in the host clipboard it will automatically be sent to
115 * the guest when the clipboard starts up. */
116 SHCLFORMATDATA formatData;
117 RT_ZERO(formatData);
118
119 formatData.Formats = VBOX_SHCL_FMT_NONE;
120
121 return ShClSvcFormatsReport(pClient, &formatData);
122}
123
124/**
125 * Shut down the shared clipboard service and "disconnect" the guest.
126 * @note Host glue code
127 */
128int ShClSvcImplDisconnect(PSHCLCLIENT pClient)
129{
130 LogFlowFuncEnter();
131
132 PSHCLCONTEXT pCtx = pClient->State.pCtx;
133 AssertPtr(pCtx);
134
135 /* Drop the reference to the client, in case it is still there. This
136 * will cause any outstanding clipboard data requests from X11 to fail
137 * immediately. */
138 pCtx->fShuttingDown = true;
139
140 int rc = ShClX11ThreadStop(&pCtx->X11);
141 /** @todo handle this slightly more reasonably, or be really sure
142 * it won't go wrong. */
143 AssertRC(rc);
144
145 ShClX11Destroy(&pCtx->X11);
146 RTCritSectDelete(&pCtx->CritSect);
147
148 RTMemFree(pCtx);
149
150 LogFlowFuncLeaveRC(rc);
151 return rc;
152}
153
154int ShClSvcImplFormatAnnounce(PSHCLCLIENT pClient, PSHCLCLIENTCMDCTX pCmdCtx,
155 PSHCLFORMATDATA pFormats)
156{
157 RT_NOREF(pCmdCtx);
158
159 int rc = ShClX11ReportFormatsToX11(&pClient->State.pCtx->X11, pFormats->Formats);
160
161 LogFlowFuncLeaveRC(rc);
162 return rc;
163}
164
165/** Structure describing a request for clipoard data from the guest. */
166struct _CLIPREADCBREQ
167{
168 /** User-supplied data pointer, based on the request type. */
169 void *pv;
170 /** The size (in bytes) of the the user-supplied pointer in pv. */
171 uint32_t cb;
172 /** The actual size of the data written. */
173 uint32_t *pcbActual;
174 /** The request's event ID. */
175 SHCLEVENTID uEvent;
176};
177
178/**
179 * @note We always fail or complete asynchronously.
180 * @note On success allocates a CLIPREADCBREQ structure which must be
181 * freed in ClipCompleteDataRequestFromX11 when it is called back from
182 * the backend code.
183 */
184int ShClSvcImplReadData(PSHCLCLIENT pClient,
185 PSHCLCLIENTCMDCTX pCmdCtx, PSHCLDATABLOCK pData, uint32_t *pcbActual)
186{
187 RT_NOREF(pCmdCtx);
188
189 LogFlowFunc(("pClient=%p, uFormat=%02X, pv=%p, cb=%u, pcbActual=%p\n",
190 pClient, pData->uFormat, pData->pvData, pData->cbData, pcbActual));
191
192 int rc = VINF_SUCCESS;
193
194 CLIPREADCBREQ *pReq = (CLIPREADCBREQ *)RTMemAllocZ(sizeof(CLIPREADCBREQ));
195 if (pReq)
196 {
197 const SHCLEVENTID uEvent = ShClEventIDGenerate(&pClient->Events);
198
199 pReq->pv = pData->pvData;
200 pReq->cb = pData->cbData;
201 pReq->pcbActual = pcbActual;
202 pReq->uEvent = uEvent;
203
204 rc = ShClEventRegister(&pClient->Events, uEvent);
205 if (RT_SUCCESS(rc))
206 {
207 rc = ShClX11ReadDataFromX11(&pClient->State.pCtx->X11, pData->uFormat, pReq);
208 if (RT_SUCCESS(rc))
209 {
210 PSHCLEVENTPAYLOAD pPayload;
211 rc = ShClEventWait(&pClient->Events, uEvent, 30 * 1000, &pPayload);
212 if (RT_SUCCESS(rc))
213 {
214 memcpy(pData->pvData, pPayload->pvData, RT_MIN(pData->cbData, pPayload->cbData));
215 pData->cbData = pPayload->cbData;
216
217 Assert(pData->cbData == pPayload->cbData); /* Sanity. */
218 }
219 }
220
221 ShClEventUnregister(&pClient->Events, uEvent);
222 }
223 }
224 else
225 rc = VERR_NO_MEMORY;
226
227 LogFlowFuncLeaveRC(rc);
228 return rc;
229}
230
231int ShClSvcImplWriteData(PSHCLCLIENT pClient,
232 PSHCLCLIENTCMDCTX pCmdCtx, PSHCLDATABLOCK pData)
233{
234 AssertPtrReturn(pClient, VERR_INVALID_POINTER);
235 AssertPtrReturn(pCmdCtx, VERR_INVALID_POINTER);
236 AssertPtrReturn(pData, VERR_INVALID_POINTER);
237
238 LogFlowFunc(("pClient=%p, pv=%p, cb=%RU32, uFormat=%02X\n",
239 pClient, pData->pvData, pData->cbData, pData->uFormat));
240
241 int rc = ShClSvcDataReadSignal(pClient, pCmdCtx, pData);
242
243 LogFlowFuncLeaveRC(rc);
244 return rc;
245}
246
247/**
248 * Reports formats available in the X11 clipboard to VBox.
249 *
250 * @note Runs in Xt event thread.
251 *
252 * @param pCtx Opaque context pointer for the glue code.
253 * @param Formats The formats available.
254 */
255DECLCALLBACK(void) ShClX11ReportFormatsCallback(PSHCLCONTEXT pCtx, uint32_t Formats)
256{
257 LogFlowFunc(("pCtx=%p, Formats=%02X\n", pCtx, Formats));
258
259 if (Formats == VBOX_SHCL_FMT_NONE) /* No formats to report? Bail out early. */
260 return;
261
262 SHCLFORMATDATA formatData;
263 RT_ZERO(formatData);
264
265 formatData.Formats = Formats;
266
267 int rc = ShClSvcFormatsReport(pCtx->pClient, &formatData);
268 RT_NOREF(rc);
269
270 LogFlowFuncLeaveRC(rc);
271}
272
273/**
274 * Completes a request from the host service for reading the X11 clipboard data.
275 * The data should be written to the buffer provided in the initial request.
276 *
277 * @note Runs in Xt event thread.
278 *
279 * @param pCtx Request context information.
280 * @param rcCompletion The completion status of the request.
281 * @param pReq Request to complete.
282 * @param pv Address of data from completed request. Optional.
283 * @param cb Size (in bytes) of data from completed request. Optional.
284 *
285 * @todo Change this to deal with the buffer issues rather than offloading them onto the caller.
286 */
287DECLCALLBACK(void) ShClX11RequestFromX11CompleteCallback(PSHCLCONTEXT pCtx, int rcCompletion,
288 CLIPREADCBREQ *pReq, void *pv, uint32_t cb)
289{
290 RT_NOREF(rcCompletion);
291
292 LogFlowFunc(("rcCompletion=%Rrc, pReq=%p, pv=%p, cb=%RU32, uEvent=%RU32\n", rcCompletion, pReq, pv, cb, pReq->uEvent));
293
294 AssertMsgRC(rcCompletion, ("Clipboard data completion from X11 failed with %Rrc\n", rcCompletion));
295
296 if (pReq->uEvent != NIL_SHCLEVENTID)
297 {
298 int rc2;
299
300 PSHCLEVENTPAYLOAD pPayload = NULL;
301 if (pv && cb)
302 {
303 rc2 = ShClPayloadAlloc(pReq->uEvent, pv, cb, &pPayload);
304 AssertRC(rc2);
305 }
306
307 rc2 = ShClEventSignal(&pCtx->pClient->Events, pReq->uEvent, pPayload);
308 AssertRC(rc2);
309 }
310
311 RTMemFree(pReq);
312}
313
314/**
315 * Reads clipboard data from the guest and passes it to the X11 clipboard.
316 *
317 * @note Runs in Xt event thread.
318 *
319 * @param pCtx Pointer to the host clipboard structure.
320 * @param Format The format in which the data should be transferred.
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 Format, void **ppv, uint32_t *pcb)
327{
328 LogFlowFunc(("pCtx=%p, Format=0x%x\n", pCtx, Format));
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 (Format == VBOX_SHCL_FMT_URI_LIST)
341 {
342 rc = 0;
343 }
344 else
345#endif
346 {
347 /* Request data from the guest. */
348 SHCLDATAREQ dataReq;
349 RT_ZERO(dataReq);
350
351 dataReq.uFmt = Format;
352 dataReq.cbSize = _64K; /** @todo Make this more dynamic. */
353
354 SHCLEVENTID uEvent;
355 rc = ShClSvcDataReadRequest(pCtx->pClient, &dataReq, &uEvent);
356 if (RT_SUCCESS(rc))
357 {
358 PSHCLEVENTPAYLOAD pPayload;
359 rc = ShClEventWait(&pCtx->pClient->Events, uEvent, 30 * 1000, &pPayload);
360 if (RT_SUCCESS(rc))
361 {
362 *ppv = pPayload->pvData;
363 *pcb = pPayload->cbData;
364
365 /* Detach the payload, as the caller then will own the data. */
366 ShClEventPayloadDetach(&pCtx->pClient->Events, uEvent);
367 }
368
369 ShClEventUnregister(&pCtx->pClient->Events, uEvent);
370 }
371 }
372
373 LogFlowFuncLeaveRC(rc);
374 return rc;
375}
376
377#ifdef VBOX_WITH_SHARED_CLIPBOARD_TRANSFERS
378int ShClSvcImplTransferCreate(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 ShClSvcImplTransferDestroy(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 ShClSvcImplTransferGetRoots(PSHCLCLIENT pClient, PSHCLTRANSFER pTransfer)
399{
400 LogFlowFuncEnter();
401
402 SHCLEVENTID uEvent = ShClEventIDGenerate(&pClient->Events);
403
404 int rc = ShClEventRegister(&pClient->Events, uEvent);
405 if (RT_SUCCESS(rc))
406 {
407 CLIPREADCBREQ *pReq = (CLIPREADCBREQ *)RTMemAllocZ(sizeof(CLIPREADCBREQ));
408 if (pReq)
409 {
410 pReq->uEvent = uEvent;
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->Events, uEvent, 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
426 ShClEventUnregister(&pClient->Events, uEvent);
427 }
428
429 LogFlowFuncLeaveRC(rc);
430 return rc;
431}
432#endif
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