VirtualBox

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

Last change on this file since 100068 was 99987, checked in by vboxsync, 21 months ago

Guest Additions/VBoxClient: Add transfer callbacks to start/stop the HTTP server. ​bugref:9437

  • Property svn:eol-style set to native
  • Property svn:keywords set to Id Revision
File size: 12.2 KB
Line 
1/** $Id: clipboard-x11.cpp 99987 2023-05-26 07:28:01Z vboxsync $ */
2/** @file
3 * Guest Additions - X11 Shared Clipboard implementation.
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.virtualbox.org.
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
53static DECLCALLBACK(int) vbclX11OnRequestDataFromSourceCallback(PSHCLCONTEXT pCtx,
54 SHCLFORMAT uFmt, void **ppv, uint32_t *pcb, void *pvUser)
55{
56 RT_NOREF(pvUser);
57
58 LogFlowFunc(("pCtx=%p, uFmt=%#x\n", pCtx, uFmt));
59
60 int rc = VINF_SUCCESS;
61
62#ifdef VBOX_WITH_SHARED_CLIPBOARD_TRANSFERS
63 if (uFmt == VBOX_SHCL_FMT_URI_LIST)
64 {
65 //rc = VbglR3ClipboardRootListRead()
66 rc = VERR_NO_DATA;
67 }
68 else
69#endif
70 {
71 uint32_t cbRead = 0;
72
73 uint32_t cbData = _4K; /** @todo Make this dynamic. */
74 void *pvData = RTMemAlloc(cbData);
75 if (pvData)
76 {
77 rc = VbglR3ClipboardReadDataEx(&pCtx->CmdCtx, uFmt, pvData, cbData, &cbRead);
78 }
79 else
80 rc = VERR_NO_MEMORY;
81
82 /*
83 * A return value of VINF_BUFFER_OVERFLOW tells us to try again with a
84 * larger buffer. The size of the buffer needed is placed in *pcb.
85 * So we start all over again.
86 */
87 if (rc == VINF_BUFFER_OVERFLOW)
88 {
89 /* cbRead contains the size required. */
90
91 cbData = cbRead;
92 pvData = RTMemRealloc(pvData, cbRead);
93 if (pvData)
94 {
95 rc = VbglR3ClipboardReadDataEx(&pCtx->CmdCtx, uFmt, pvData, cbData, &cbRead);
96 if (rc == VINF_BUFFER_OVERFLOW)
97 rc = VERR_BUFFER_OVERFLOW;
98 }
99 else
100 rc = VERR_NO_MEMORY;
101 }
102
103 if (!cbRead)
104 rc = VERR_NO_DATA;
105
106 if (RT_SUCCESS(rc))
107 {
108 *pcb = cbRead; /* Actual bytes read. */
109 *ppv = pvData;
110 }
111 else
112 {
113 /*
114 * Catch other errors. This also catches the case in which the buffer was
115 * too small a second time, possibly because the clipboard contents
116 * changed half-way through the operation. Since we can't say whether or
117 * not this is actually an error, we just return size 0.
118 */
119 RTMemFree(pvData);
120 }
121 }
122
123 if (RT_FAILURE(rc))
124 LogRel(("Requesting data in format %#x from host failed with %Rrc\n", uFmt, rc));
125
126 LogFlowFuncLeaveRC(rc);
127 return rc;
128}
129
130/**
131 * Opaque data structure describing a request from the host for clipboard
132 * data, passed in when the request is forwarded to the X11 backend so that
133 * it can be completed correctly.
134 */
135struct CLIPREADCBREQ
136{
137 /** The data format that was requested. */
138 SHCLFORMAT uFmt;
139};
140
141static DECLCALLBACK(int) vbclX11ReportFormatsCallback(PSHCLCONTEXT pCtx, uint32_t fFormats, void *pvUser)
142{
143 RT_NOREF(pvUser);
144
145 LogFlowFunc(("fFormats=%#x\n", fFormats));
146
147 int rc = VbglR3ClipboardReportFormats(pCtx->CmdCtx.idClient, fFormats);
148 LogFlowFuncLeaveRC(rc);
149
150 return rc;
151}
152
153static DECLCALLBACK(int) vbclX11OnSendDataToDestCallback(PSHCLCONTEXT pCtx, void *pv, uint32_t cb, void *pvUser)
154{
155 PSHCLX11READDATAREQ pData = (PSHCLX11READDATAREQ)pvUser;
156 AssertPtrReturn(pData, VERR_INVALID_POINTER);
157
158 LogFlowFunc(("rcCompletion=%Rrc, Format=0x%x, pv=%p, cb=%RU32\n", pData->rcCompletion, pData->pReq->uFmt, pv, cb));
159
160 Assert((cb == 0 && pv == NULL) || (cb != 0 && pv != NULL));
161 pData->rcCompletion = VbglR3ClipboardWriteDataEx(&pCtx->CmdCtx, pData->pReq->uFmt, pv, cb);
162
163 RTMemFree(pData->pReq);
164
165 LogFlowFuncLeaveRC(pData->rcCompletion);
166
167 return VINF_SUCCESS;
168}
169
170/**
171 * Initializes the X11-specifc Shared Clipboard code.
172 *
173 * @returns VBox status code.
174 */
175int VBClX11ClipboardInit(void)
176{
177 LogFlowFuncEnter();
178
179 SHCLCALLBACKS Callbacks;
180 RT_ZERO(Callbacks);
181 Callbacks.pfnReportFormats = vbclX11ReportFormatsCallback;
182 Callbacks.pfnOnRequestDataFromSource = vbclX11OnRequestDataFromSourceCallback;
183 Callbacks.pfnOnSendDataToDest = vbclX11OnSendDataToDestCallback;
184
185 int rc = ShClX11Init(&g_Ctx.X11, &Callbacks, &g_Ctx, false /* fHeadless */);
186 if (RT_SUCCESS(rc))
187 {
188 rc = ShClX11ThreadStart(&g_Ctx.X11, false /* grab */);
189 if (RT_SUCCESS(rc))
190 {
191 rc = VbglR3ClipboardConnectEx(&g_Ctx.CmdCtx, VBOX_SHCL_GF_0_CONTEXT_ID);
192 if (RT_FAILURE(rc))
193 ShClX11ThreadStop(&g_Ctx.X11);
194 }
195 }
196 else
197 rc = VERR_NO_MEMORY;
198
199 if (RT_FAILURE(rc))
200 {
201 VBClLogError("Error connecting to host service, rc=%Rrc\n", rc);
202
203 VbglR3ClipboardDisconnectEx(&g_Ctx.CmdCtx);
204 ShClX11Destroy(&g_Ctx.X11);
205 }
206
207 LogFlowFuncLeaveRC(rc);
208 return rc;
209}
210
211/**
212 * Destroys the X11-specifc Shared Clipboard code.
213 *
214 * @returns VBox status code.
215 */
216int VBClX11ClipboardDestroy(void)
217{
218 /* Nothing to do here currently. */
219 return VINF_SUCCESS;
220}
221
222#ifdef VBOX_WITH_SHARED_CLIPBOARD_TRANSFERS_HTTP
223/** @copydoc SHCLTRANSFERCALLBACKTABLE::pfnOnStart */
224static DECLCALLBACK(int) vboxClipboardOnTransferStartCallback(PSHCLTRANSFERCALLBACKCTX pCbCtx)
225{
226 PSHCLCONTEXT pCtx = (PSHCLCONTEXT)pCbCtx->pvUser;
227 AssertPtr(pCtx);
228
229 PSHCLTRANSFER pTransfer = pCbCtx->pTransfer;
230 AssertPtr(pTransfer);
231
232 /* We only need to start the HTTP server (and register the transfer to it) when we actually receive data from the host. */
233 if (ShClTransferGetDir(pTransfer) == SHCLTRANSFERDIR_FROM_REMOTE)
234 return ShClHttpTransferRegisterAndMaybeStart(&pCtx->X11.HttpCtx, pTransfer);
235
236 return VINF_SUCCESS;
237}
238
239/** @copydoc SHCLTRANSFERCALLBACKTABLE::pfnOnCompleted */
240static DECLCALLBACK(void) vboxClipboardOnTransferCompletedCallback(PSHCLTRANSFERCALLBACKCTX pCbCtx, int rc)
241{
242 RT_NOREF(rc);
243
244 PSHCLCONTEXT pCtx = (PSHCLCONTEXT)pCbCtx->pvUser;
245 AssertPtr(pCtx);
246
247 PSHCLTRANSFER pTransfer = pCbCtx->pTransfer;
248 AssertPtr(pTransfer);
249
250 /* See comment in vboxClipboardOnTransferInitCallback(). */
251 if (ShClTransferGetDir(pTransfer) == SHCLTRANSFERDIR_FROM_REMOTE)
252 ShClHttpTransferUnregisterAndMaybeStop(&pCtx->X11.HttpCtx, pTransfer);
253}
254
255/** @copydoc SHCLTRANSFERCALLBACKTABLE::pfnOnError */
256static DECLCALLBACK(void) vboxClipboardOnTransferErrorCallback(PSHCLTRANSFERCALLBACKCTX pCtx, int rc)
257{
258 return vboxClipboardOnTransferCompletedCallback(pCtx, rc);
259}
260#endif /* VBOX_WITH_SHARED_CLIPBOARD_TRANSFERS_HTTP */
261
262/**
263 * The main loop of the X11-specifc Shared Clipboard code.
264 *
265 * @returns VBox status code.
266 */
267int VBClX11ClipboardMain(void)
268{
269 int rc;
270
271 PSHCLCONTEXT pCtx = &g_Ctx;
272
273 bool fShutdown = false;
274
275#ifdef VBOX_WITH_SHARED_CLIPBOARD_TRANSFERS
276# ifdef VBOX_WITH_SHARED_CLIPBOARD_TRANSFERS_HTTP
277 /*
278 * Set callbacks.
279 * Those will be registered within VbglR3 when a new transfer gets initialized.
280 *
281 * Used for starting / stopping the HTTP server.
282 */
283 RT_ZERO(pCtx->CmdCtx.Transfers.Callbacks);
284
285 pCtx->CmdCtx.Transfers.Callbacks.pvUser = pCtx; /* Assign context as user-provided callback data. */
286 pCtx->CmdCtx.Transfers.Callbacks.cbUser = sizeof(SHCLCONTEXT);
287
288 pCtx->CmdCtx.Transfers.Callbacks.pfnOnStart = vboxClipboardOnTransferStartCallback;
289 pCtx->CmdCtx.Transfers.Callbacks.pfnOnCompleted = vboxClipboardOnTransferCompletedCallback;
290 pCtx->CmdCtx.Transfers.Callbacks.pfnOnError = vboxClipboardOnTransferErrorCallback;
291# endif /* VBOX_WITH_SHARED_CLIPBOARD_TRANSFERS_HTTP */
292#endif /* VBOX_WITH_SHARED_CLIPBOARD_TRANSFERS */
293
294 /* The thread waits for incoming messages from the host. */
295 for (;;)
296 {
297 PVBGLR3CLIPBOARDEVENT pEvent = (PVBGLR3CLIPBOARDEVENT)RTMemAllocZ(sizeof(VBGLR3CLIPBOARDEVENT));
298 AssertPtrBreakStmt(pEvent, rc = VERR_NO_MEMORY);
299
300 LogFlowFunc(("Waiting for host message (fUseLegacyProtocol=%RTbool, fHostFeatures=%#RX64) ...\n",
301 pCtx->CmdCtx.fUseLegacyProtocol, pCtx->CmdCtx.fHostFeatures));
302
303 uint32_t idMsg = 0;
304 uint32_t cParms = 0;
305 rc = VbglR3ClipboardMsgPeekWait(&pCtx->CmdCtx, &idMsg, &cParms, NULL /* pidRestoreCheck */);
306 if (RT_SUCCESS(rc))
307 {
308#ifdef VBOX_WITH_SHARED_CLIPBOARD_TRANSFERS
309 rc = VbglR3ClipboardEventGetNextEx(idMsg, cParms, &pCtx->CmdCtx, &pCtx->TransferCtx, pEvent);
310#else
311 rc = VbglR3ClipboardEventGetNext(idMsg, cParms, &pCtx->CmdCtx, pEvent);
312#endif
313 }
314
315 if (RT_FAILURE(rc))
316 {
317 LogFlowFunc(("Getting next event failed with %Rrc\n", rc));
318
319 VbglR3ClipboardEventFree(pEvent);
320 pEvent = NULL;
321
322 if (fShutdown)
323 break;
324
325 /* Wait a bit before retrying. */
326 RTThreadSleep(1000);
327 continue;
328 }
329 else
330 {
331 AssertPtr(pEvent);
332 LogFlowFunc(("Event uType=%RU32\n", pEvent->enmType));
333
334 switch (pEvent->enmType)
335 {
336 case VBGLR3CLIPBOARDEVENTTYPE_REPORT_FORMATS:
337 {
338 ShClX11ReportFormatsToX11(&g_Ctx.X11, pEvent->u.fReportedFormats);
339 break;
340 }
341
342 case VBGLR3CLIPBOARDEVENTTYPE_READ_DATA:
343 {
344 /* The host needs data in the specified format. */
345 CLIPREADCBREQ *pReq;
346 pReq = (CLIPREADCBREQ *)RTMemAllocZ(sizeof(CLIPREADCBREQ));
347 if (pReq)
348 {
349 pReq->uFmt = pEvent->u.fReadData;
350 ShClX11ReadDataFromX11(&g_Ctx.X11, pReq->uFmt, pReq);
351 }
352 else
353 rc = VERR_NO_MEMORY;
354 break;
355 }
356
357 case VBGLR3CLIPBOARDEVENTTYPE_QUIT:
358 {
359 VBClLogVerbose(2, "Host requested termination\n");
360 fShutdown = true;
361 break;
362 }
363
364#ifdef VBOX_WITH_SHARED_CLIPBOARD_TRANSFERS
365 case VBGLR3CLIPBOARDEVENTTYPE_TRANSFER_STATUS:
366 {
367 /* Nothing to do here. */
368 rc = VINF_SUCCESS;
369 break;
370 }
371#endif
372 case VBGLR3CLIPBOARDEVENTTYPE_NONE:
373 {
374 /* Nothing to do here. */
375 rc = VINF_SUCCESS;
376 break;
377 }
378
379 default:
380 {
381 AssertMsgFailedBreakStmt(("Event type %RU32 not implemented\n", pEvent->enmType), rc = VERR_NOT_SUPPORTED);
382 }
383 }
384
385 if (pEvent)
386 {
387 VbglR3ClipboardEventFree(pEvent);
388 pEvent = NULL;
389 }
390 }
391
392 if (fShutdown)
393 break;
394 }
395
396 LogFlowFuncLeaveRC(rc);
397 return rc;
398}
399
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