VirtualBox

source: vbox/trunk/src/VBox/HostServices/SharedClipboard/x11-clipboard.cpp@ 18626

Last change on this file since 18626 was 18586, checked in by vboxsync, 16 years ago

HostServices/SharedClipboard/X11: added opaque data pointers to the X11 backend APIs, as yet unused

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 13.3 KB
Line 
1/** @file
2 *
3 * Shared Clipboard:
4 * Linux host.
5 */
6
7/*
8 * Copyright (C) 2006-2007 Sun Microsystems, Inc.
9 *
10 * This file is part of VirtualBox Open Source Edition (OSE), as
11 * available from http://www.virtualbox.org. This file is free software;
12 * you can redistribute it and/or modify it under the terms of the GNU
13 * General Public License (GPL) as published by the Free Software
14 * Foundation, in version 2 as it comes in the "COPYING" file of the
15 * VirtualBox OSE distribution. VirtualBox OSE is distributed in the
16 * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
17 *
18 * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa
19 * Clara, CA 95054 USA or visit http://www.sun.com if you need
20 * additional information or have any questions.
21 */
22
23/** @todo create a clipboard log group */
24#define LOG_GROUP LOG_GROUP_HGCM
25
26#include <string.h>
27
28#include <iprt/asm.h> /* For atomic operations */
29#include <iprt/assert.h>
30#include <iprt/mem.h>
31#include <iprt/semaphore.h>
32
33#include <VBox/log.h>
34
35#include <VBox/GuestHost/SharedClipboard.h>
36#include <VBox/HostServices/VBoxClipboardSvc.h>
37
38#include "VBoxClipboard.h"
39
40/** Global context information used by the host glue for the X11 clipboard
41 * backend */
42struct _VBOXCLIPBOARDCONTEXT
43{
44 /** Since the clipboard data moves asynchronously, we use an event
45 * semaphore to wait for it. When a function issues a request for
46 * clipboard data it must wait for this semaphore, which is triggered
47 * when the data arrives. */
48 RTSEMEVENT waitForData;
49 /** Who (if anyone) is currently waiting for data? Used for sanity
50 * checks when data arrives. */
51 volatile uint32_t waiter;
52 /** This mutex is grabbed during any critical operations on the clipboard
53 * which might clash with others. */
54 RTSEMMUTEX clipboardMutex;
55
56 /** Pointer to the opaque X11 backend structure */
57 VBOXCLIPBOARDCONTEXTX11 *pBackend;
58 /** Pointer to the client data structure */
59 VBOXCLIPBOARDCLIENTDATA *pClient;
60};
61
62/* Only one client is supported. There seems to be no need for more clients.
63 */
64static VBOXCLIPBOARDCONTEXT g_ctxHost;
65
66/**
67 * Send a request to VBox to transfer the contents of its clipboard to X11.
68 *
69 * @param pCtx Pointer to the host clipboard structure
70 * @param u32Format The format in which the data should be transfered
71 * @param ppv On success and if pcb > 0, this will point to a buffer
72 * to be freed with RTMemFree containing the data read.
73 * @param pcb On success, this contains the number of bytes of data
74 * returned
75 * @note Host glue code.
76 */
77int VBoxX11ClipboardReadVBoxData (VBOXCLIPBOARDCONTEXT *pCtx,
78 uint32_t u32Format, void **ppv,
79 uint32_t *pcb)
80{
81 volatile VBOXCLIPBOARDCLIENTDATA *pClient = pCtx->pClient;
82
83 LogFlowFunc(("u32Format=%02X\n", u32Format));
84 if (pClient == NULL)
85 {
86 /* This can legitimately happen if we disconnect during a request for
87 * data from X11. */
88 LogFunc(("host requested guest clipboard data after guest had disconnected.\n"));
89 VBoxX11ClipboardAnnounceVBoxFormat(pCtx->pBackend, 0);
90 pCtx->waiter = NONE;
91 return VERR_TIMEOUT;
92 }
93 /* Assert that no other transfer is in process (requests are serialised)
94 * and that the last transfer cleaned up properly. */
95 AssertLogRelReturn( pClient->data.pv == NULL
96 && pClient->data.cb == 0
97 && pClient->data.u32Format == 0,
98 VERR_WRONG_ORDER
99 );
100 /* No one else (X11 or VBox) should currently be waiting. The first because
101 * requests from X11 are serialised and the second because VBox previously
102 * grabbed the clipboard, so it should not be waiting for data from us. */
103 AssertLogRelReturn (ASMAtomicCmpXchgU32(&pCtx->waiter, X11, NONE), VERR_DEADLOCK);
104 /* Request data from VBox */
105 vboxSvcClipboardReportMsg(pCtx->pClient,
106 VBOX_SHARED_CLIPBOARD_HOST_MSG_READ_DATA,
107 u32Format);
108 /* Which will signal us when it is ready. We use a timeout here because
109 * we can't be sure that the guest will behave correctly. */
110 int rc = RTSemEventWait(pCtx->waitForData, CLIPBOARDTIMEOUT);
111 if (rc == VERR_TIMEOUT)
112 rc = VINF_SUCCESS; /* Timeout handling follows. */
113 /* Now we have a potential race between the HGCM thread delivering the data
114 * and our setting waiter to NONE to say that we are no longer waiting for
115 * it. We solve this as follows: both of these operations are done under
116 * the clipboard mutex. The HGCM thread will only deliver the data if we
117 * are still waiting after it acquires the mutex. After we release the
118 * mutex, we finally do our check to see whether the data was delivered. */
119 RTSemMutexRequest(g_ctxHost.clipboardMutex, RT_INDEFINITE_WAIT);
120 pCtx->waiter = NONE;
121 RTSemMutexRelease(g_ctxHost.clipboardMutex);
122 AssertLogRelRCSuccess(rc);
123 if (RT_FAILURE(rc))
124 {
125 /* I believe this should not happen. Wait until the assertions arrive
126 * to prove the contrary. */
127 RTMemFree(pClient->data.pv);
128 g_ctxHost.pClient->data.pv = 0;
129 g_ctxHost.pClient->data.cb = 0;
130 g_ctxHost.pClient->data.u32Format = 0;
131 vboxClipboardFormatAnnounce(NULL, 0);
132 return rc;
133 }
134 if (pClient->data.pv == NULL)
135 return VERR_TIMEOUT;
136 LogFlowFunc(("wait completed. Returning.\n"));
137 *ppv = pClient->data.pv;
138 *pcb = pClient->data.cb;
139 g_ctxHost.pClient->data.pv = 0;
140 g_ctxHost.pClient->data.cb = 0;
141 g_ctxHost.pClient->data.u32Format = 0;
142 return VINF_SUCCESS;
143}
144
145/**
146 * Report formats available in the X11 clipboard to VBox.
147 * @param pCtx Opaque context pointer for the glue code
148 * @param u32Formats The formats available
149 * @note Host glue code
150 */
151void VBoxX11ClipboardReportX11Formats(VBOXCLIPBOARDCONTEXT *pCtx,
152 uint32_t u32Formats)
153{
154 vboxSvcClipboardReportMsg(pCtx->pClient,
155 VBOX_SHARED_CLIPBOARD_HOST_MSG_FORMATS,
156 u32Formats);
157}
158
159/**
160 * Initialise the host side of the shared clipboard.
161 * @note Host glue code
162 */
163int vboxClipboardInit (void)
164{
165 int rc = VINF_SUCCESS;
166 VBOXCLIPBOARDCONTEXTX11 *pBackend = NULL;
167
168 LogRel(("Initializing host clipboard service\n"));
169 RTSemEventCreate(&g_ctxHost.waitForData);
170 RTSemMutexCreate(&g_ctxHost.clipboardMutex);
171 rc = VBoxX11ClipboardInitX11(&g_ctxHost, &pBackend);
172 if (RT_FAILURE(rc))
173 {
174 RTSemEventDestroy(g_ctxHost.waitForData);
175 RTSemMutexDestroy(g_ctxHost.clipboardMutex);
176 LogRel(("Failed to start the host shared clipboard service.\n"));
177 }
178 else
179 g_ctxHost.pBackend = pBackend;
180 return rc;
181}
182
183/**
184 * Terminate the host side of the shared clipboard.
185 * @note host glue code
186 */
187void vboxClipboardDestroy (void)
188{
189 int rc = VINF_SUCCESS;
190 LogRelFunc(("shutting down host clipboard\n"));
191 /* Drop the reference to the client, in case it is still there. This
192 * will cause any outstanding clipboard data requests from X11 to fail
193 * immediately. */
194 g_ctxHost.pClient = NULL;
195 /* The backend may be waiting for data from VBox. At this point it is no
196 * longer going to arrive, and we must release it to allow the event
197 * loop to terminate. In this case the buffer where VBox would have
198 * written the clipboard data will still be empty and we will just
199 * return "no data" to the backend. Any subsequent attempts to get the
200 * data from VBox will fail immediately as the client reference is gone.
201 */
202 /** @note This has been made unconditional, as it should do no harm
203 * even if we are not waiting. */
204 RTSemEventSignal(g_ctxHost.waitForData);
205 rc = VBoxX11ClipboardTermX11(g_ctxHost.pBackend);
206 if (RT_SUCCESS(rc))
207 {
208 /* We can safely destroy these as the backend has exited
209 * successfully and no other calls from the host code should be
210 * forthcoming. */
211 /** @todo can the backend fail to exit successfully? What then? */
212 RTSemEventDestroy(g_ctxHost.waitForData);
213 RTSemMutexDestroy(g_ctxHost.clipboardMutex);
214 }
215}
216
217/**
218 * Connect a guest to the shared clipboard.
219 * @note host glue code
220 * @note on the host, we assume that some other application already owns
221 * the clipboard and leave ownership to X11.
222 */
223int vboxClipboardConnect (VBOXCLIPBOARDCLIENTDATA *pClient)
224{
225 int rc = VINF_SUCCESS;
226 LogFlowFunc(("\n"));
227 /* Only one client is supported for now */
228 AssertLogRelReturn(g_ctxHost.pClient == 0, VERR_NOT_SUPPORTED);
229 pClient->pCtx = &g_ctxHost;
230 pClient->pCtx->pClient = pClient;
231 /** The pClient pointer is a dummy anyway, as we only support a single
232 * client at a time. */
233 rc = VBoxX11ClipboardStartX11(g_ctxHost.pBackend,
234 X11 /* initial owner */);
235 return rc;
236}
237
238/**
239 * Synchronise the contents of the host clipboard with the guest, called
240 * after a save and restore of the guest.
241 * @note Host glue code
242 */
243int vboxClipboardSync (VBOXCLIPBOARDCLIENTDATA *pClient)
244{
245
246 /* On a Linux host, the guest should never synchronise/cache its
247 * clipboard contents, as we have no way of reliably telling when the
248 * host clipboard data changes. So instead of synchronising, we tell
249 * the guest to empty its clipboard, and we set the cached flag so that
250 * we report formats to the guest next time we poll for them. */
251 /** @note This has been changed so that the message is sent even if
252 * X11 is not available. */
253 vboxSvcClipboardReportMsg (g_ctxHost.pClient,
254 VBOX_SHARED_CLIPBOARD_HOST_MSG_FORMATS, 0);
255 VBoxX11ClipboardRequestSyncX11(g_ctxHost.pBackend);
256
257 return VINF_SUCCESS;
258}
259
260/**
261 * Shut down the shared clipboard service and "disconnect" the guest.
262 * @note Host glue code
263 */
264void vboxClipboardDisconnect (VBOXCLIPBOARDCLIENTDATA *)
265{
266 LogFlow(("vboxClipboardDisconnect\n"));
267
268 RTSemMutexRequest(g_ctxHost.clipboardMutex, RT_INDEFINITE_WAIT);
269 g_ctxHost.pClient = NULL;
270 VBoxX11ClipboardStopX11(g_ctxHost.pBackend);
271 RTSemMutexRelease(g_ctxHost.clipboardMutex);
272}
273
274/**
275 * VBox is taking possession of the shared clipboard.
276 *
277 * @param pClient Context data for the guest system
278 * @param u32Formats Clipboard formats the guest is offering
279 * @note Host glue code
280 */
281void vboxClipboardFormatAnnounce (VBOXCLIPBOARDCLIENTDATA *pClient, uint32_t u32Formats)
282{
283 VBoxX11ClipboardAnnounceVBoxFormat (g_ctxHost.pBackend, u32Formats);
284}
285
286/**
287 * Called when VBox wants to read the X11 clipboard.
288 *
289 * @param pClient Context information about the guest VM
290 * @param u32Format The format that the guest would like to receive the data in
291 * @param pv Where to write the data to
292 * @param cb The size of the buffer to write the data to
293 * @param pcbActual Where to write the actual size of the written data
294 * @note Host glue code
295 */
296int vboxClipboardReadData (VBOXCLIPBOARDCLIENTDATA *pClient,
297 uint32_t u32Format, void *pv, uint32_t cb,
298 uint32_t *pcbActual)
299{
300 int rc = VINF_SUCCESS;
301 VBOXCLIPBOARDREQUEST request;
302 /* No one else (VBox or X11) should currently be waiting. The first
303 * because requests from VBox are serialised and the second because X11
304 * previously grabbed the clipboard, so it should not be waiting for
305 * data from us. */
306 AssertLogRelReturn (ASMAtomicCmpXchgU32(&g_ctxHost.waiter, VB, NONE),
307 VERR_DEADLOCK);
308 request.pv = pv;
309 request.cb = cb;
310 request.pcbActual = pcbActual;
311 rc = VBoxX11ClipboardReadX11Data(g_ctxHost.pBackend, u32Format, &request);
312 g_ctxHost.waiter = NONE;
313 return rc;
314}
315
316/**
317 * Called when we have requested data from VBox and that data has arrived.
318 *
319 * @param pClient Context information about the guest VM
320 * @param pv Buffer to which the data was written
321 * @param cb The size of the data written
322 * @param u32Format The format of the data written
323 * @note Host glue code
324 */
325void vboxClipboardWriteData (VBOXCLIPBOARDCLIENTDATA *pClient,
326 void *pv, uint32_t cb, uint32_t u32Format)
327{
328/* Assume that if this gets called at all then the X11 backend is running. */
329#if 0
330 if (!g_fHaveX11)
331 return;
332#endif
333
334 LogFlowFunc (("called\n"));
335
336 /* Assert that no other transfer is in process (requests are serialised)
337 * or has not cleaned up properly. */
338 AssertLogRelReturnVoid ( pClient->data.pv == NULL
339 && pClient->data.cb == 0
340 && pClient->data.u32Format == 0);
341
342 /* Grab the mutex and check that X11 is still waiting for the data before
343 * delivering it. See the explanation in VBoxX11ClipboardReadVBoxData. */
344 RTSemMutexRequest(g_ctxHost.clipboardMutex, RT_INDEFINITE_WAIT);
345 if (g_ctxHost.waiter == X11 && cb > 0)
346 {
347 pClient->data.pv = RTMemAlloc (cb);
348
349 if (pClient->data.pv)
350 {
351 memcpy (pClient->data.pv, pv, cb);
352 pClient->data.cb = cb;
353 pClient->data.u32Format = u32Format;
354 }
355 }
356 RTSemMutexRelease(g_ctxHost.clipboardMutex);
357
358 RTSemEventSignal(g_ctxHost.waitForData);
359}
360
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