VirtualBox

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

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

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