VirtualBox

source: vbox/trunk/src/VBox/Additions/x11/VBoxClient/clipboard.cpp@ 87685

Last change on this file since 87685 was 87566, checked in by vboxsync, 4 years ago

Shared Clipboard: doxygen fixes for unnecessary @copydoc in definitions and duplicate @param docs. (probably bugref:9437)

  • Property svn:eol-style set to native
  • Property svn:keywords set to Id Revision
File size: 12.1 KB
Line 
1/** $Id: clipboard.cpp 87566 2021-02-03 13:48:48Z vboxsync $ */
2/** @file
3 * Guest Additions - X11 Shared Clipboard.
4 */
5
6/*
7 * Copyright (C) 2007-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#include <iprt/alloc.h>
23#include <iprt/asm.h>
24#include <iprt/assert.h>
25#ifdef VBOX_WITH_SHARED_CLIPBOARD_FUSE
26# include <iprt/dir.h>
27#endif
28#include <iprt/initterm.h>
29#include <iprt/mem.h>
30#include <iprt/string.h>
31#include <iprt/path.h>
32#include <iprt/process.h>
33#include <iprt/semaphore.h>
34
35#include <VBox/log.h>
36#include <VBox/VBoxGuestLib.h>
37#include <VBox/HostServices/VBoxClipboardSvc.h>
38#include <VBox/GuestHost/SharedClipboard.h>
39#include <VBox/GuestHost/SharedClipboard-x11.h>
40
41#include "VBoxClient.h"
42
43#include "clipboard.h"
44#ifdef VBOX_WITH_SHARED_CLIPBOARD_FUSE
45# include "clipboard-fuse.h"
46#endif
47
48
49/*********************************************************************************************************************************
50* Global Variables *
51*********************************************************************************************************************************/
52
53/** Only one context is supported at a time for now. */
54SHCLCONTEXT g_Ctx;
55#ifdef VBOX_WITH_SHARED_CLIPBOARD_FUSE
56SHCLFUSECTX g_FuseCtx;
57#endif
58
59
60DECLCALLBACK(int) ShClX11RequestDataForX11Callback(PSHCLCONTEXT pCtx, SHCLFORMAT uFmt, void **ppv, uint32_t *pcb)
61{
62 LogFlowFunc(("pCtx=%p, uFmt=%#x\n", pCtx, uFmt));
63
64 int rc = VINF_SUCCESS;
65
66#ifdef VBOX_WITH_SHARED_CLIPBOARD_TRANSFERS
67 if (uFmt == VBOX_SHCL_FMT_URI_LIST)
68 {
69 //rc = VbglR3ClipboardRootListRead()
70 rc = VERR_NO_DATA;
71 }
72 else
73#endif
74 {
75 uint32_t cbRead = 0;
76
77 uint32_t cbData = _4K; /** @todo Make this dynamic. */
78 void *pvData = RTMemAlloc(cbData);
79 if (pvData)
80 {
81 rc = VbglR3ClipboardReadDataEx(&pCtx->CmdCtx, uFmt, pvData, cbData, &cbRead);
82 }
83 else
84 rc = VERR_NO_MEMORY;
85
86 /*
87 * A return value of VINF_BUFFER_OVERFLOW tells us to try again with a
88 * larger buffer. The size of the buffer needed is placed in *pcb.
89 * So we start all over again.
90 */
91 if (rc == VINF_BUFFER_OVERFLOW)
92 {
93 /* cbRead contains the size required. */
94
95 cbData = cbRead;
96 pvData = RTMemRealloc(pvData, cbRead);
97 if (pvData)
98 {
99 rc = VbglR3ClipboardReadDataEx(&pCtx->CmdCtx, uFmt, pvData, cbData, &cbRead);
100 if (rc == VINF_BUFFER_OVERFLOW)
101 rc = VERR_BUFFER_OVERFLOW;
102 }
103 else
104 rc = VERR_NO_MEMORY;
105 }
106
107 if (!cbRead)
108 rc = VERR_NO_DATA;
109
110 if (RT_SUCCESS(rc))
111 {
112 *pcb = cbRead; /* Actual bytes read. */
113 *ppv = pvData;
114 }
115 else
116 {
117 /*
118 * Catch other errors. This also catches the case in which the buffer was
119 * too small a second time, possibly because the clipboard contents
120 * changed half-way through the operation. Since we can't say whether or
121 * not this is actually an error, we just return size 0.
122 */
123 RTMemFree(pvData);
124 }
125 }
126
127 if (RT_FAILURE(rc))
128 LogRel(("Requesting data in format %#x from host failed with %Rrc\n", uFmt, rc));
129
130 LogFlowFuncLeaveRC(rc);
131 return rc;
132}
133
134/**
135 * Opaque data structure describing a request from the host for clipboard
136 * data, passed in when the request is forwarded to the X11 backend so that
137 * it can be completed correctly.
138 */
139struct CLIPREADCBREQ
140{
141 /** The data format that was requested. */
142 SHCLFORMAT Format;
143};
144
145DECLCALLBACK(void) ShClX11ReportFormatsCallback(PSHCLCONTEXT pCtx, SHCLFORMATS fFormats)
146{
147 RT_NOREF(pCtx);
148
149 LogFlowFunc(("fFormats=%#x\n", fFormats));
150
151 int rc2 = VbglR3ClipboardReportFormats(pCtx->CmdCtx.idClient, fFormats);
152 RT_NOREF(rc2);
153 LogFlowFuncLeaveRC(rc2);
154}
155
156DECLCALLBACK(void) ShClX11RequestFromX11CompleteCallback(PSHCLCONTEXT pCtx, int rcCompletion,
157 CLIPREADCBREQ *pReq, void *pv, uint32_t cb)
158{
159 LogFlowFunc(("rcCompletion=%Rrc, Format=0x%x, pv=%p, cb=%RU32\n", rcCompletion, pReq->Format, pv, cb));
160
161 if (RT_SUCCESS(rcCompletion)) /* Only write data if the request succeeded. */
162 {
163 AssertPtrReturnVoid(pv);
164 AssertReturnVoid(pv);
165
166 rcCompletion = VbglR3ClipboardWriteDataEx(&pCtx->CmdCtx, pReq->Format, pv, cb);
167 }
168
169 RTMemFree(pReq);
170
171 LogFlowFuncLeaveRC(rcCompletion);
172}
173
174/**
175 * Connect the guest clipboard to the host.
176 *
177 * @returns VBox status code.
178 */
179static int vboxClipboardConnect(void)
180{
181 LogFlowFuncEnter();
182
183 int rc = ShClX11Init(&g_Ctx.X11, &g_Ctx, false /* fHeadless */);
184 if (RT_SUCCESS(rc))
185 {
186 rc = ShClX11ThreadStart(&g_Ctx.X11, false /* grab */);
187 if (RT_SUCCESS(rc))
188 {
189 rc = VbglR3ClipboardConnectEx(&g_Ctx.CmdCtx, VBOX_SHCL_GF_0_CONTEXT_ID);
190 if (RT_FAILURE(rc))
191 ShClX11ThreadStop(&g_Ctx.X11);
192 }
193 }
194 else
195 rc = VERR_NO_MEMORY;
196
197 if (RT_FAILURE(rc))
198 {
199 VBClLogError("Error connecting to host service, rc=%Rrc\n", rc);
200
201 VbglR3ClipboardDisconnectEx(&g_Ctx.CmdCtx);
202 ShClX11Destroy(&g_Ctx.X11);
203 }
204
205 LogFlowFuncLeaveRC(rc);
206 return rc;
207}
208
209/**
210 * The main loop of our clipboard reader.
211 */
212int vboxClipboardMain(void)
213{
214 int rc;
215
216 PSHCLCONTEXT pCtx = &g_Ctx;
217
218 bool fShutdown = false;
219
220 /* The thread waits for incoming messages from the host. */
221 for (;;)
222 {
223 PVBGLR3CLIPBOARDEVENT pEvent = (PVBGLR3CLIPBOARDEVENT)RTMemAllocZ(sizeof(VBGLR3CLIPBOARDEVENT));
224 AssertPtrBreakStmt(pEvent, rc = VERR_NO_MEMORY);
225
226 LogFlowFunc(("Waiting for host message (fUseLegacyProtocol=%RTbool, fHostFeatures=%#RX64) ...\n",
227 pCtx->CmdCtx.fUseLegacyProtocol, pCtx->CmdCtx.fHostFeatures));
228
229 uint32_t idMsg = 0;
230 uint32_t cParms = 0;
231 rc = VbglR3ClipboardMsgPeekWait(&pCtx->CmdCtx, &idMsg, &cParms, NULL /* pidRestoreCheck */);
232 if (RT_SUCCESS(rc))
233 {
234#ifdef VBOX_WITH_SHARED_CLIPBOARD_TRANSFERS
235 rc = VbglR3ClipboardEventGetNextEx(idMsg, cParms, &pCtx->CmdCtx, &pCtx->TransferCtx, pEvent);
236#else
237 rc = VbglR3ClipboardEventGetNext(idMsg, cParms, &pCtx->CmdCtx, pEvent);
238#endif
239 }
240
241 if (RT_FAILURE(rc))
242 {
243 LogFlowFunc(("Getting next event failed with %Rrc\n", rc));
244
245 VbglR3ClipboardEventFree(pEvent);
246 pEvent = NULL;
247
248 if (fShutdown)
249 break;
250
251 /* Wait a bit before retrying. */
252 RTThreadSleep(1000);
253 continue;
254 }
255 else
256 {
257 AssertPtr(pEvent);
258 LogFlowFunc(("Event uType=%RU32\n", pEvent->enmType));
259
260 switch (pEvent->enmType)
261 {
262 case VBGLR3CLIPBOARDEVENTTYPE_REPORT_FORMATS:
263 {
264 ShClX11ReportFormatsToX11(&g_Ctx.X11, pEvent->u.fReportedFormats);
265 break;
266 }
267
268 case VBGLR3CLIPBOARDEVENTTYPE_READ_DATA:
269 {
270 /* The host needs data in the specified format. */
271 CLIPREADCBREQ *pReq;
272 pReq = (CLIPREADCBREQ *)RTMemAllocZ(sizeof(CLIPREADCBREQ));
273 if (pReq)
274 {
275 pReq->Format = pEvent->u.fReadData;
276 ShClX11ReadDataFromX11(&g_Ctx.X11, pReq->Format, pReq);
277 }
278 else
279 rc = VERR_NO_MEMORY;
280 break;
281 }
282
283 case VBGLR3CLIPBOARDEVENTTYPE_QUIT:
284 {
285 VBClLogVerbose(2, "Host requested termination\n");
286 fShutdown = true;
287 break;
288 }
289
290#ifdef VBOX_WITH_SHARED_CLIPBOARD_TRANSFERS
291 case VBGLR3CLIPBOARDEVENTTYPE_TRANSFER_STATUS:
292 {
293 /* Nothing to do here. */
294 rc = VINF_SUCCESS;
295 break;
296 }
297#endif
298 case VBGLR3CLIPBOARDEVENTTYPE_NONE:
299 {
300 /* Nothing to do here. */
301 rc = VINF_SUCCESS;
302 break;
303 }
304
305 default:
306 {
307 AssertMsgFailedBreakStmt(("Event type %RU32 not implemented\n", pEvent->enmType), rc = VERR_NOT_SUPPORTED);
308 }
309 }
310
311 if (pEvent)
312 {
313 VbglR3ClipboardEventFree(pEvent);
314 pEvent = NULL;
315 }
316 }
317
318 if (fShutdown)
319 break;
320 }
321
322 LogFlowFuncLeaveRC(rc);
323 return rc;
324}
325
326/**
327 * @interface_method_impl{VBCLSERVICE,pfnInit}
328 */
329static DECLCALLBACK(int) vbclShClInit(void)
330{
331 int rc;
332
333#ifdef VBOX_WITH_SHARED_CLIPBOARD_TRANSFERS
334 rc = ShClTransferCtxInit(&g_Ctx.TransferCtx);
335#else
336 rc = VINF_SUCCESS;
337#endif
338
339 LogFlowFuncLeaveRC(rc);
340 return rc;
341}
342
343/**
344 * @interface_method_impl{VBCLSERVICE,pfnWorker}
345 */
346static DECLCALLBACK(int) vbclShClWorker(bool volatile *pfShutdown)
347{
348 RT_NOREF(pfShutdown);
349
350 /* Initialise the guest library. */
351 int rc = vboxClipboardConnect();
352 if (RT_SUCCESS(rc))
353 {
354#ifdef VBOX_WITH_SHARED_CLIPBOARD_FUSE
355 rc = VbClShClFUSEInit(&g_FuseCtx, &g_Ctx);
356 if (RT_SUCCESS(rc))
357 {
358 rc = VbClShClFUSEStart(&g_FuseCtx);
359 if (RT_SUCCESS(rc))
360 {
361#endif
362 /* Let the main thread know that it can continue spawning services. */
363 RTThreadUserSignal(RTThreadSelf());
364
365 rc = vboxClipboardMain();
366
367#ifdef VBOX_WITH_SHARED_CLIPBOARD_FUSE
368 int rc2 = VbClShClFUSEStop(&g_FuseCtx);
369 if (RT_SUCCESS(rc))
370 rc = rc2;
371 }
372 }
373#endif
374 }
375
376 if (RT_FAILURE(rc))
377 VBClLogError("Service terminated abnormally with %Rrc\n", rc);
378
379 if (rc == VERR_HGCM_SERVICE_NOT_FOUND)
380 rc = VINF_SUCCESS; /* Prevent automatic restart by daemon script if host service not available. */
381
382 return rc;
383}
384
385/**
386 * @interface_method_impl{VBCLSERVICE,pfnStop}
387 */
388static DECLCALLBACK(void) vbclShClStop(void)
389{
390 /* Disconnect from the host service.
391 * This will also send a VBOX_SHCL_HOST_MSG_QUIT from the host so that we can break out from our message worker. */
392 VbglR3ClipboardDisconnect(g_Ctx.CmdCtx.idClient);
393 g_Ctx.CmdCtx.idClient = 0;
394}
395
396/**
397 * @interface_method_impl{VBCLSERVICE,pfnTerm}
398 */
399static DECLCALLBACK(int) vbclShClTerm(void)
400{
401#ifdef VBOX_WITH_SHARED_CLIPBOARD_TRANSFERS
402 ShClTransferCtxDestroy(&g_Ctx.TransferCtx);
403#endif
404
405 return VINF_SUCCESS;
406}
407
408VBCLSERVICE g_SvcClipboard =
409{
410 "shcl", /* szName */
411 "Shared Clipboard", /* pszDescription */
412 ".vboxclient-clipboard.pid", /* pszPidFilePath */
413 NULL, /* pszUsage */
414 NULL, /* pszOptions */
415 NULL, /* pfnOption */
416 vbclShClInit, /* pfnInit */
417 vbclShClWorker, /* pfnWorker */
418 vbclShClStop, /* pfnStop*/
419 vbclShClTerm /* pfnTerm */
420};
421
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