VirtualBox

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

Last change on this file since 93072 was 91621, checked in by vboxsync, 3 years ago

X11/VBoxClient: Reverted most of r139955 because it's wrong and we do _need_ to write data to the host even when we failed to get it. The host would otherwise be waiting till it times out for a reply, possibly locking the host clipboard. bugref:10094

  • Property svn:eol-style set to native
  • Property svn:keywords set to Id Revision
File size: 12.0 KB
Line 
1/** $Id: clipboard.cpp 91621 2021-10-07 20:40:03Z 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 RT_NOREF(rcCompletion);
161
162 Assert((cb == 0 && pv == NULL) || (cb != 0 && pv != NULL));
163 rcCompletion = VbglR3ClipboardWriteDataEx(&pCtx->CmdCtx, pReq->Format, pv, cb);
164
165 RTMemFree(pReq);
166
167 LogFlowFuncLeaveRC(rcCompletion);
168}
169
170/**
171 * Connect the guest clipboard to the host.
172 *
173 * @returns VBox status code.
174 */
175static int vboxClipboardConnect(void)
176{
177 LogFlowFuncEnter();
178
179 int rc = ShClX11Init(&g_Ctx.X11, &g_Ctx, false /* fHeadless */);
180 if (RT_SUCCESS(rc))
181 {
182 rc = ShClX11ThreadStart(&g_Ctx.X11, false /* grab */);
183 if (RT_SUCCESS(rc))
184 {
185 rc = VbglR3ClipboardConnectEx(&g_Ctx.CmdCtx, VBOX_SHCL_GF_0_CONTEXT_ID);
186 if (RT_FAILURE(rc))
187 ShClX11ThreadStop(&g_Ctx.X11);
188 }
189 }
190 else
191 rc = VERR_NO_MEMORY;
192
193 if (RT_FAILURE(rc))
194 {
195 VBClLogError("Error connecting to host service, rc=%Rrc\n", rc);
196
197 VbglR3ClipboardDisconnectEx(&g_Ctx.CmdCtx);
198 ShClX11Destroy(&g_Ctx.X11);
199 }
200
201 LogFlowFuncLeaveRC(rc);
202 return rc;
203}
204
205/**
206 * The main loop of our clipboard reader.
207 */
208int vboxClipboardMain(void)
209{
210 int rc;
211
212 PSHCLCONTEXT pCtx = &g_Ctx;
213
214 bool fShutdown = false;
215
216 /* The thread waits for incoming messages from the host. */
217 for (;;)
218 {
219 PVBGLR3CLIPBOARDEVENT pEvent = (PVBGLR3CLIPBOARDEVENT)RTMemAllocZ(sizeof(VBGLR3CLIPBOARDEVENT));
220 AssertPtrBreakStmt(pEvent, rc = VERR_NO_MEMORY);
221
222 LogFlowFunc(("Waiting for host message (fUseLegacyProtocol=%RTbool, fHostFeatures=%#RX64) ...\n",
223 pCtx->CmdCtx.fUseLegacyProtocol, pCtx->CmdCtx.fHostFeatures));
224
225 uint32_t idMsg = 0;
226 uint32_t cParms = 0;
227 rc = VbglR3ClipboardMsgPeekWait(&pCtx->CmdCtx, &idMsg, &cParms, NULL /* pidRestoreCheck */);
228 if (RT_SUCCESS(rc))
229 {
230#ifdef VBOX_WITH_SHARED_CLIPBOARD_TRANSFERS
231 rc = VbglR3ClipboardEventGetNextEx(idMsg, cParms, &pCtx->CmdCtx, &pCtx->TransferCtx, pEvent);
232#else
233 rc = VbglR3ClipboardEventGetNext(idMsg, cParms, &pCtx->CmdCtx, pEvent);
234#endif
235 }
236
237 if (RT_FAILURE(rc))
238 {
239 LogFlowFunc(("Getting next event failed with %Rrc\n", rc));
240
241 VbglR3ClipboardEventFree(pEvent);
242 pEvent = NULL;
243
244 if (fShutdown)
245 break;
246
247 /* Wait a bit before retrying. */
248 RTThreadSleep(1000);
249 continue;
250 }
251 else
252 {
253 AssertPtr(pEvent);
254 LogFlowFunc(("Event uType=%RU32\n", pEvent->enmType));
255
256 switch (pEvent->enmType)
257 {
258 case VBGLR3CLIPBOARDEVENTTYPE_REPORT_FORMATS:
259 {
260 ShClX11ReportFormatsToX11(&g_Ctx.X11, pEvent->u.fReportedFormats);
261 break;
262 }
263
264 case VBGLR3CLIPBOARDEVENTTYPE_READ_DATA:
265 {
266 /* The host needs data in the specified format. */
267 CLIPREADCBREQ *pReq;
268 pReq = (CLIPREADCBREQ *)RTMemAllocZ(sizeof(CLIPREADCBREQ));
269 if (pReq)
270 {
271 pReq->Format = pEvent->u.fReadData;
272 ShClX11ReadDataFromX11(&g_Ctx.X11, pReq->Format, pReq);
273 }
274 else
275 rc = VERR_NO_MEMORY;
276 break;
277 }
278
279 case VBGLR3CLIPBOARDEVENTTYPE_QUIT:
280 {
281 VBClLogVerbose(2, "Host requested termination\n");
282 fShutdown = true;
283 break;
284 }
285
286#ifdef VBOX_WITH_SHARED_CLIPBOARD_TRANSFERS
287 case VBGLR3CLIPBOARDEVENTTYPE_TRANSFER_STATUS:
288 {
289 /* Nothing to do here. */
290 rc = VINF_SUCCESS;
291 break;
292 }
293#endif
294 case VBGLR3CLIPBOARDEVENTTYPE_NONE:
295 {
296 /* Nothing to do here. */
297 rc = VINF_SUCCESS;
298 break;
299 }
300
301 default:
302 {
303 AssertMsgFailedBreakStmt(("Event type %RU32 not implemented\n", pEvent->enmType), rc = VERR_NOT_SUPPORTED);
304 }
305 }
306
307 if (pEvent)
308 {
309 VbglR3ClipboardEventFree(pEvent);
310 pEvent = NULL;
311 }
312 }
313
314 if (fShutdown)
315 break;
316 }
317
318 LogFlowFuncLeaveRC(rc);
319 return rc;
320}
321
322/**
323 * @interface_method_impl{VBCLSERVICE,pfnInit}
324 */
325static DECLCALLBACK(int) vbclShClInit(void)
326{
327 int rc;
328
329#ifdef VBOX_WITH_SHARED_CLIPBOARD_TRANSFERS
330 rc = ShClTransferCtxInit(&g_Ctx.TransferCtx);
331#else
332 rc = VINF_SUCCESS;
333#endif
334
335 LogFlowFuncLeaveRC(rc);
336 return rc;
337}
338
339/**
340 * @interface_method_impl{VBCLSERVICE,pfnWorker}
341 */
342static DECLCALLBACK(int) vbclShClWorker(bool volatile *pfShutdown)
343{
344 RT_NOREF(pfShutdown);
345
346 /* Initialise the guest library. */
347 int rc = vboxClipboardConnect();
348 if (RT_SUCCESS(rc))
349 {
350#ifdef VBOX_WITH_SHARED_CLIPBOARD_FUSE
351 rc = VbClShClFUSEInit(&g_FuseCtx, &g_Ctx);
352 if (RT_SUCCESS(rc))
353 {
354 rc = VbClShClFUSEStart(&g_FuseCtx);
355 if (RT_SUCCESS(rc))
356 {
357#endif
358 /* Let the main thread know that it can continue spawning services. */
359 RTThreadUserSignal(RTThreadSelf());
360
361 rc = vboxClipboardMain();
362
363#ifdef VBOX_WITH_SHARED_CLIPBOARD_FUSE
364 int rc2 = VbClShClFUSEStop(&g_FuseCtx);
365 if (RT_SUCCESS(rc))
366 rc = rc2;
367 }
368 }
369#endif
370 }
371
372 if (RT_FAILURE(rc))
373 VBClLogError("Service terminated abnormally with %Rrc\n", rc);
374
375 if (rc == VERR_HGCM_SERVICE_NOT_FOUND)
376 rc = VINF_SUCCESS; /* Prevent automatic restart by daemon script if host service not available. */
377
378 return rc;
379}
380
381/**
382 * @interface_method_impl{VBCLSERVICE,pfnStop}
383 */
384static DECLCALLBACK(void) vbclShClStop(void)
385{
386 /* Disconnect from the host service.
387 * This will also send a VBOX_SHCL_HOST_MSG_QUIT from the host so that we can break out from our message worker. */
388 VbglR3ClipboardDisconnect(g_Ctx.CmdCtx.idClient);
389 g_Ctx.CmdCtx.idClient = 0;
390}
391
392/**
393 * @interface_method_impl{VBCLSERVICE,pfnTerm}
394 */
395static DECLCALLBACK(int) vbclShClTerm(void)
396{
397#ifdef VBOX_WITH_SHARED_CLIPBOARD_TRANSFERS
398 ShClTransferCtxDestroy(&g_Ctx.TransferCtx);
399#endif
400
401 return VINF_SUCCESS;
402}
403
404VBCLSERVICE g_SvcClipboard =
405{
406 "shcl", /* szName */
407 "Shared Clipboard", /* pszDescription */
408 ".vboxclient-clipboard.pid", /* pszPidFilePath */
409 NULL, /* pszUsage */
410 NULL, /* pszOptions */
411 NULL, /* pfnOption */
412 vbclShClInit, /* pfnInit */
413 vbclShClWorker, /* pfnWorker */
414 vbclShClStop, /* pfnStop*/
415 vbclShClTerm /* pfnTerm */
416};
417
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