VirtualBox

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

Last change on this file since 99586 was 99586, checked in by vboxsync, 2 years ago

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