VirtualBox

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

Last change on this file since 81150 was 81044, checked in by vboxsync, 6 years ago

Shared Clipboard/X11: Emphasize which X11 backend APIs are callbacks and which aren't.

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 12.8 KB
Line 
1/* $Id: VBoxSharedClipboardSvc-x11.cpp 81044 2019-09-27 10:49:07Z 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/HostServices/VBoxClipboardSvc.h>
32#include <VBox/err.h>
33
34#include "VBoxSharedClipboardSvc-internal.h"
35
36
37/*********************************************************************************************************************************
38* Structures and Typedefs *
39*********************************************************************************************************************************/
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 /** Pointer to the opaque X11 backend structure */
50 CLIPBACKEND *pBackend;
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
59/**
60 * Report formats available in the X11 clipboard to VBox.
61 * @param pCtx Opaque context pointer for the glue code.
62 * @param u32Formats The formats available.
63 */
64DECLCALLBACK(void) ClipReportX11FormatsCallback(SHCLCONTEXT *pCtx, uint32_t u32Formats)
65{
66 LogFlowFunc(("pCtx=%p, u32Formats=%02X\n", pCtx, u32Formats));
67
68 if (u32Formats == VBOX_SHCL_FMT_NONE) /* No formats to report? Bail out early. */
69 return;
70
71 SHCLFORMATDATA formatData;
72 RT_ZERO(formatData);
73
74 formatData.uFormats = u32Formats;
75
76 int rc2 = shclSvcFormatsReport(pCtx->pClient, &formatData);
77 AssertRC(rc2);
78}
79
80/**
81 * Initialise the host side of the shared clipboard.
82 * @note Host glue code
83 */
84int ShClSvcImplInit(void)
85{
86 LogFlowFuncEnter();
87 return VINF_SUCCESS;
88}
89
90/**
91 * Terminate the host side of the shared clipboard.
92 * @note host glue code
93 */
94void ShClSvcImplDestroy(void)
95{
96 LogFlowFuncEnter();
97}
98
99/**
100 * Connect a guest to the shared clipboard.
101 * @note on the host, we assume that some other application already owns
102 * the clipboard and leave ownership to X11.
103 */
104int ShClSvcImplConnect(PSHCLCLIENT pClient, bool fHeadless)
105{
106 int rc = VINF_SUCCESS;
107
108 PSHCLCONTEXT pCtx = (PSHCLCONTEXT)RTMemAllocZ(sizeof(SHCLCONTEXT));
109 if (pCtx)
110 {
111 RTCritSectInit(&pCtx->CritSect);
112 CLIPBACKEND *pBackend = ClipConstructX11(pCtx, fHeadless);
113 if (!pBackend)
114 {
115 rc = VERR_NO_MEMORY;
116 }
117 else
118 {
119 pCtx->pBackend = pBackend;
120 pClient->State.pCtx = pCtx;
121 pCtx->pClient = pClient;
122
123 rc = ClipStartX11(pBackend, true /* grab shared clipboard */);
124 if (RT_FAILURE(rc))
125 ClipDestructX11(pBackend);
126 }
127
128 if (RT_FAILURE(rc))
129 {
130 RTCritSectDelete(&pCtx->CritSect);
131 RTMemFree(pCtx);
132 }
133 }
134 else
135 rc = VERR_NO_MEMORY;
136
137 LogFlowFuncLeaveRC(rc);
138 return rc;
139}
140
141/**
142 * Synchronise the contents of the host clipboard with the guest, called
143 * after a save and restore of the guest.
144 * @note Host glue code
145 */
146int ShClSvcImplSync(PSHCLCLIENT pClient)
147{
148 LogFlowFuncEnter();
149
150 /* Tell the guest we have no data in case X11 is not available. If
151 * there is data in the host clipboard it will automatically be sent to
152 * the guest when the clipboard starts up. */
153 SHCLFORMATDATA formatData;
154 RT_ZERO(formatData);
155
156 formatData.uFormats = VBOX_SHCL_FMT_NONE;
157
158 return shclSvcFormatsReport(pClient, &formatData);
159}
160
161/**
162 * Shut down the shared clipboard service and "disconnect" the guest.
163 * @note Host glue code
164 */
165int ShClSvcImplDisconnect(PSHCLCLIENT pClient)
166{
167 LogFlowFuncEnter();
168
169 PSHCLCONTEXT pCtx = pClient->State.pCtx;
170
171 /* Drop the reference to the client, in case it is still there. This
172 * will cause any outstanding clipboard data requests from X11 to fail
173 * immediately. */
174 pCtx->fShuttingDown = true;
175
176 /* If there is a currently pending request, release it immediately. */
177 SHCLDATABLOCK dataBlock = { 0, NULL, 0 };
178 ShClSvcImplWriteData(pClient, NULL, &dataBlock);
179
180 int rc = ClipStopX11(pCtx->pBackend);
181 /** @todo handle this slightly more reasonably, or be really sure
182 * it won't go wrong. */
183 AssertRC(rc);
184
185 if (RT_SUCCESS(rc)) /* And if not? */
186 {
187 ClipDestructX11(pCtx->pBackend);
188 RTCritSectDelete(&pCtx->CritSect);
189 RTMemFree(pCtx);
190 }
191
192 LogFlowFuncLeaveRC(rc);
193 return rc;
194}
195
196/**
197 * VBox is taking possession of the shared clipboard.
198 *
199 * @param pClient Context data for the guest system.
200 * @param pCmdCtx Command context to use.
201 * @param pFormats Clipboard formats the guest is offering.
202 */
203int ShClSvcImplFormatAnnounce(PSHCLCLIENT pClient, PSHCLCLIENTCMDCTX pCmdCtx,
204 PSHCLFORMATDATA pFormats)
205{
206 RT_NOREF(pCmdCtx);
207
208#ifdef VBOX_WITH_SHARED_CLIPBOARD_TRANSFERS
209 if (pFormats->uFormats & VBOX_SHCL_FMT_URI_LIST) /* No transfer support yet. */
210 return VINF_SUCCESS;
211#endif
212
213 ClipAnnounceFormatToX11(pClient->State.pCtx->pBackend, pFormats->uFormats);
214
215 return VINF_SUCCESS;
216}
217
218/** Structure describing a request for clipoard data from the guest. */
219struct _CLIPREADCBREQ
220{
221 /** Where to write the returned data to. Weak pointer! */
222 void *pv;
223 /** The size of the buffer in pv. */
224 uint32_t cb;
225 /** The actual size of the data written. */
226 uint32_t *pcbActual;
227 /** The request's event ID. */
228 SHCLEVENTID uEvent;
229};
230
231/**
232 * Called when the host service wants to read the X11 clipboard.
233 *
234 * @returns VINF_SUCCESS on successful completion.
235 * @returns VINF_HGCM_ASYNC_EXECUTE if the operation will complete asynchronously.
236 * @returns IPRT status code on failure.
237 *
238 * @param pClient Context information about the guest VM.
239 * @param pCmdCtx Command context to use.
240 * @param pData Data block to put read data into.
241 * @param pcbActual Where to write the actual size of the written data.
242 *
243 * @note We always fail or complete asynchronously.
244 * @note On success allocates a CLIPREADCBREQ structure which must be
245 * freed in ClipCompleteDataRequestFromX11 when it is called back from
246 * the backend code.
247 *
248 */
249int ShClSvcImplReadData(PSHCLCLIENT pClient,
250 PSHCLCLIENTCMDCTX pCmdCtx, PSHCLDATABLOCK pData, uint32_t *pcbActual)
251{
252 RT_NOREF(pCmdCtx);
253
254 LogFlowFunc(("pClient=%p, uFormat=%02X, pv=%p, cb=%u, pcbActual=%p\n",
255 pClient, pData->uFormat, pData->pvData, pData->cbData, pcbActual));
256
257 int rc = VINF_SUCCESS;
258
259 CLIPREADCBREQ *pReq = (CLIPREADCBREQ *)RTMemAllocZ(sizeof(CLIPREADCBREQ));
260 if (pReq)
261 {
262 const SHCLEVENTID uEvent = SharedClipboardEventIDGenerate(&pClient->Events);
263
264 pReq->pv = pData->pvData;
265 pReq->cb = pData->cbData;
266 pReq->pcbActual = pcbActual;
267 pReq->uEvent = uEvent;
268
269 rc = ClipRequestDataFromX11(pClient->State.pCtx->pBackend, pData->uFormat, pReq);
270 if (RT_SUCCESS(rc))
271 {
272 rc = SharedClipboardEventRegister(&pClient->Events, uEvent);
273 if (RT_SUCCESS(rc))
274 {
275 PSHCLEVENTPAYLOAD pPayload;
276 rc = SharedClipboardEventWait(&pClient->Events, uEvent, 30 * 1000, &pPayload);
277 if (RT_SUCCESS(rc))
278 {
279 memcpy(pData->pvData, pPayload->pvData, RT_MIN(pData->cbData, pPayload->cbData));
280 pData->cbData = pPayload->cbData;
281
282 Assert(pData->cbData == pPayload->cbData); /* Sanity. */
283 }
284
285 SharedClipboardEventUnregister(&pClient->Events, uEvent);
286 }
287 }
288 }
289 else
290 rc = VERR_NO_MEMORY;
291
292 LogFlowFuncLeaveRC(rc);
293 return rc;
294}
295
296/**
297 * Called when writing guest clipboard data to the host service.
298 *
299 * @param pClient Context information about the guest VM.
300 * @param pCmdCtx Pointer to the clipboard command context.
301 * @param pData Data block to write to clipboard.
302 */
303int ShClSvcImplWriteData(PSHCLCLIENT pClient,
304 PSHCLCLIENTCMDCTX pCmdCtx, PSHCLDATABLOCK pData)
305{
306 LogFlowFunc(("pClient=%p, pv=%p, cb=%RU32, uFormat=%02X\n",
307 pClient, pData->pvData, pData->cbData, pData->uFormat));
308
309 int rc = shclSvcDataReadSignal(pClient, pCmdCtx, pData);
310
311 LogFlowFuncLeaveRC(rc);
312 return rc;
313}
314
315/**
316 * Completes a request from the host service for reading the X11 clipboard data.
317 * The data should be written to the buffer provided in the initial request.
318 *
319 * @param pCtx Request context information.
320 * @param rc The completion status of the request.
321 * @param pReq Request.
322 * @param pv Address.
323 * @param cb Size.
324 *
325 * @todo Change this to deal with the buffer issues rather than offloading them onto the caller.
326 */
327void ClipRequestFromX11CompleteCallback(SHCLCONTEXT *pCtx, int rc,
328 CLIPREADCBREQ *pReq, void *pv, uint32_t cb)
329{
330 AssertMsgRC(rc, ("Clipboard data completion from X11 failed with %Rrc\n", rc));
331
332 PSHCLEVENTPAYLOAD pPayload;
333 int rc2 = SharedClipboardPayloadAlloc(pReq->uEvent, pv, cb, &pPayload);
334 if (RT_SUCCESS(rc2))
335 rc2 = SharedClipboardEventSignal(&pCtx->pClient->Events, pReq->uEvent, pPayload);
336
337 AssertRC(rc);
338
339 RTMemFree(pReq);
340}
341
342/**
343 * Reads clipboard data from the guest and passes it to the X11 clipboard.
344 *
345 * @param pCtx Pointer to the host clipboard structure
346 * @param u32Format The format in which the data should be transferred
347 * @param ppv On success and if pcb > 0, this will point to a buffer
348 * to be freed with RTMemFree containing the data read.
349 * @param pcb On success, this contains the number of bytes of data
350 * returned
351 * @note Host glue code.
352 */
353DECLCALLBACK(int) ClipRequestDataForX11Callback(SHCLCONTEXT *pCtx, uint32_t u32Format, void **ppv, uint32_t *pcb)
354{
355 LogFlowFunc(("pCtx=%p, u32Format=%02X, ppv=%p\n", pCtx, u32Format, ppv));
356
357 if (pCtx->fShuttingDown)
358 {
359 /* The shared clipboard is disconnecting. */
360 LogRel(("Shared Clipboard: Host requested guest clipboard data after guest had disconnected\n"));
361 return VERR_WRONG_ORDER;
362 }
363
364 /* Request data from the guest. */
365 SHCLDATAREQ dataReq;
366 RT_ZERO(dataReq);
367
368 dataReq.uFmt = u32Format;
369 dataReq.cbSize = _64K; /** @todo Make this more dynamic. */
370
371 SHCLEVENTID uEvent;
372 int rc = shclSvcDataReadRequest(pCtx->pClient, &dataReq, &uEvent);
373 if (RT_SUCCESS(rc))
374 {
375 PSHCLEVENTPAYLOAD pPayload;
376 rc = SharedClipboardEventWait(&pCtx->pClient->Events, uEvent, 30 * 1000, &pPayload);
377 if (RT_SUCCESS(rc))
378 {
379 *ppv = pPayload->pvData;
380 *pcb = pPayload->cbData;
381
382 /* Detach the payload, as the caller then will own the data. */
383 SharedClipboardEventPayloadDetach(&pCtx->pClient->Events, uEvent);
384 }
385
386 SharedClipboardEventUnregister(&pCtx->pClient->Events, uEvent);
387 }
388
389 LogFlowFuncLeaveRC(rc);
390 return rc;
391}
392
393#ifdef VBOX_WITH_SHARED_CLIPBOARD_TRANSFERS
394int ShClSvcImplTransferCreate(PSHCLCLIENT pClient, PSHCLTRANSFER pTransfer)
395{
396 RT_NOREF(pClient, pTransfer);
397 return VINF_SUCCESS;
398}
399
400int ShClSvcImplTransferDestroy(PSHCLCLIENT pClient, PSHCLTRANSFER pTransfer)
401{
402 RT_NOREF(pClient, pTransfer);
403 return VINF_SUCCESS;
404}
405#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