VirtualBox

source: vbox/trunk/src/VBox/GuestHost/SharedClipboard/clipboard-x11.cpp@ 99965

最後變更 在這個檔案從99965是 99953,由 vboxsync 提交於 22 月 前

Shared Clipboard: Only need to start the HTTP server on the host when we actually doing a guest -> host transfer; make sure to destroy the HTTP server in ShClX11Destroy(). bugref:9437

  • 屬性 svn:eol-style 設為 native
  • 屬性 svn:keywords 設為 Author Date Id Revision
檔案大小: 83.7 KB
 
1/** @file
2 * Shared Clipboard: Common X11 code.
3 */
4
5/*
6 * Copyright (C) 2006-2023 Oracle and/or its affiliates.
7 *
8 * This file is part of VirtualBox base platform packages, as
9 * available from https://www.alldomusa.eu.org.
10 *
11 * This program is free software; you can redistribute it and/or
12 * modify it under the terms of the GNU General Public License
13 * as published by the Free Software Foundation, in version 3 of the
14 * License.
15 *
16 * This program is distributed in the hope that it will be useful, but
17 * WITHOUT ANY WARRANTY; without even the implied warranty of
18 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
19 * General Public License for more details.
20 *
21 * You should have received a copy of the GNU General Public License
22 * along with this program; if not, see <https://www.gnu.org/licenses>.
23 *
24 * SPDX-License-Identifier: GPL-3.0-only
25 */
26
27/* Note: to automatically run regression tests on the Shared Clipboard,
28 * execute the tstClipboardGH-X11 testcase. If you often make changes to the
29 * clipboard code, adding the line
30 *
31 * OTHERS += $(PATH_tstClipboardGH-X11)/tstClipboardGH-X11.run
32 *
33 * to LocalConfig.kmk will cause the tests to be run every time the code is
34 * changed.
35 */
36
37
38/*********************************************************************************************************************************
39* Header Files *
40*********************************************************************************************************************************/
41#define LOG_GROUP LOG_GROUP_SHARED_CLIPBOARD
42
43#include <errno.h>
44
45#include <dlfcn.h>
46#include <fcntl.h>
47#include <unistd.h>
48
49#ifdef RT_OS_SOLARIS
50#include <tsol/label.h>
51#endif
52
53#include <X11/Xlib.h>
54#include <X11/Xatom.h>
55#include <X11/Intrinsic.h>
56#include <X11/Shell.h>
57#include <X11/Xproto.h>
58#include <X11/StringDefs.h>
59
60#include <iprt/assert.h>
61#include <iprt/types.h>
62#include <iprt/mem.h>
63#include <iprt/semaphore.h>
64#include <iprt/thread.h>
65#include <iprt/utf16.h>
66#include <iprt/uri.h>
67
68#ifdef VBOX_WITH_SHARED_CLIPBOARD_TRANSFERS
69# include <iprt/cpp/list.h>
70# include <iprt/cpp/ministring.h>
71# include <VBox/GuestHost/SharedClipboard-transfers.h>
72#endif
73
74#include <VBox/log.h>
75#include <VBox/version.h>
76
77#include <VBox/GuestHost/SharedClipboard.h>
78#include <VBox/GuestHost/SharedClipboard-x11.h>
79#include <VBox/GuestHost/clipboard-helper.h>
80#include <VBox/HostServices/VBoxClipboardSvc.h>
81
82/** Own macro for declaring function visibility / linkage based on whether this
83 * code runs as part of test cases or not. */
84#ifdef TESTCASE
85# define SHCL_X11_DECL(x) x
86#else
87# define SHCL_X11_DECL(x) static x
88#endif
89
90
91/*********************************************************************************************************************************
92* Externals *
93*********************************************************************************************************************************/
94#ifdef TESTCASE
95extern void tstThreadScheduleCall(void (*proc)(void *, void *), void *client_data);
96extern void tstClipRequestData(SHCLX11CTX* pCtx, SHCLX11FMTIDX target, void *closure);
97extern void tstRequestTargets(SHCLX11CTX* pCtx);
98#endif
99
100
101/*********************************************************************************************************************************
102* Prototypes *
103*********************************************************************************************************************************/
104class formats;
105SHCL_X11_DECL(Atom) clipGetAtom(PSHCLX11CTX pCtx, const char *pszName);
106SHCL_X11_DECL(void) clipQueryX11Targets(PSHCLX11CTX pCtx);
107
108static int clipInitInternal(PSHCLX11CTX pCtx);
109static void clipUninitInternal(PSHCLX11CTX pCtx);
110
111
112/*********************************************************************************************************************************
113* Global Variables *
114*********************************************************************************************************************************/
115
116/**
117 * The table maps X11 names to data formats
118 * and to the corresponding VBox clipboard formats.
119 */
120SHCL_X11_DECL(SHCLX11FMTTABLE) g_aFormats[] =
121{
122 { "INVALID", SHCLX11FMT_INVALID, VBOX_SHCL_FMT_NONE },
123
124 { "UTF8_STRING", SHCLX11FMT_UTF8, VBOX_SHCL_FMT_UNICODETEXT },
125 { "text/plain;charset=UTF-8", SHCLX11FMT_UTF8, VBOX_SHCL_FMT_UNICODETEXT },
126 { "text/plain;charset=utf-8", SHCLX11FMT_UTF8, VBOX_SHCL_FMT_UNICODETEXT },
127 { "STRING", SHCLX11FMT_TEXT, VBOX_SHCL_FMT_UNICODETEXT },
128 { "TEXT", SHCLX11FMT_TEXT, VBOX_SHCL_FMT_UNICODETEXT },
129 { "text/plain", SHCLX11FMT_TEXT, VBOX_SHCL_FMT_UNICODETEXT },
130
131 { "text/html", SHCLX11FMT_HTML, VBOX_SHCL_FMT_HTML },
132 { "text/html;charset=utf-8", SHCLX11FMT_HTML, VBOX_SHCL_FMT_HTML },
133 { "application/x-moz-nativehtml", SHCLX11FMT_HTML, VBOX_SHCL_FMT_HTML },
134
135 { "image/bmp", SHCLX11FMT_BMP, VBOX_SHCL_FMT_BITMAP },
136 { "image/x-bmp", SHCLX11FMT_BMP, VBOX_SHCL_FMT_BITMAP },
137 { "image/x-MS-bmp", SHCLX11FMT_BMP, VBOX_SHCL_FMT_BITMAP },
138 /** @todo Inkscape exports image/png but not bmp... */
139
140#ifdef VBOX_WITH_SHARED_CLIPBOARD_TRANSFERS
141 { "text/uri-list", SHCLX11FMT_URI_LIST, VBOX_SHCL_FMT_URI_LIST },
142 { "x-special/gnome-copied-files", SHCLX11FMT_URI_LIST, VBOX_SHCL_FMT_URI_LIST },
143 { "x-special/nautilus-clipboard", SHCLX11FMT_URI_LIST, VBOX_SHCL_FMT_URI_LIST },
144 { "application/x-kde-cutselection", SHCLX11FMT_URI_LIST, VBOX_SHCL_FMT_URI_LIST },
145 /** @todo Anything else we need to add here? */
146 /** @todo Add Wayland / Weston support. */
147#endif
148};
149
150
151#ifdef TESTCASE
152# ifdef RT_OS_SOLARIS_10
153char XtStrings [] = "";
154WidgetClassRec* applicationShellWidgetClass;
155char XtShellStrings [] = "";
156int XmbTextPropertyToTextList(
157 Display* /* display */,
158 XTextProperty* /* text_prop */,
159 char*** /* list_return */,
160 int* /* count_return */
161)
162{
163 return 0;
164}
165# else /* !RT_OS_SOLARIS_10 */
166const char XtStrings [] = "";
167_WidgetClassRec* applicationShellWidgetClass;
168const char XtShellStrings [] = "";
169# endif /* RT_OS_SOLARIS_10 */
170#else /* !TESTCASE */
171# ifdef VBOX_WITH_VBOXCLIENT_LAZY_LOAD
172/* Defines needed for lazy loading global data from the shared objects (.so).
173 * See r157060. */
174DECLASM(WidgetClass * ) LazyGetPtr_applicationShellWidgetClass(void);
175#define applicationShellWidgetClass (*LazyGetPtr_applicationShellWidgetClass())
176DECLASM(const char *) LazyGetPtr_XtStrings(void);
177#define XtStrings (LazyGetPtr_XtStrings())
178# endif
179#endif /* TESTCASE */
180
181
182/*********************************************************************************************************************************
183* Defines *
184*********************************************************************************************************************************/
185
186#define SHCL_MAX_X11_FORMATS RT_ELEMENTS(g_aFormats)
187
188
189/*********************************************************************************************************************************
190* Internal structures *
191*********************************************************************************************************************************/
192
193/**
194 * A structure containing information about where to store a request
195 * for the X11 clipboard contents.
196 */
197typedef struct _CLIPREADX11CBREQ
198{
199 /** The format VBox would like the data in. */
200 SHCLFORMAT uFmtVBox;
201 /** The format we requested from X11. */
202 SHCLX11FMTIDX idxFmtX11;
203 /** The clipboard context this request is associated with. */
204 SHCLX11CTX *pCtx;
205 /** The request structure passed in from the backend. */
206 CLIPREADCBREQ *pReq;
207} CLIPREADX11CBREQ;
208
209
210
211#ifdef TESTCASE
212/**
213 * Return the max. number of elements in the X11 format table.
214 * Used by the testing code in tstClipboardGH-X11.cpp
215 * which cannot use RT_ELEMENTS(g_aFormats) directly.
216 *
217 * @return size_t The number of elements in the g_aFormats array.
218 */
219SHCL_X11_DECL(size_t) clipReportMaxX11Formats(void)
220{
221 return (RT_ELEMENTS(g_aFormats));
222}
223#endif
224
225/**
226 * Returns the atom corresponding to a supported X11 format.
227 *
228 * @returns Found atom to the corresponding X11 format.
229 * @param pCtx The X11 clipboard context to use.
230 * @param uFmtIdx Format index to look up atom for.
231 */
232static Atom clipAtomForX11Format(PSHCLX11CTX pCtx, SHCLX11FMTIDX uFmtIdx)
233{
234 AssertReturn(uFmtIdx < RT_ELEMENTS(g_aFormats), 0);
235 return clipGetAtom(pCtx, g_aFormats[uFmtIdx].pcszAtom);
236}
237
238/**
239 * Returns the SHCLX11FMT corresponding to a supported X11 format.
240 *
241 * @return SHCLX11FMT for a specific format index.
242 * @param uFmtIdx Format index to look up SHCLX11CLIPFMT for.
243 */
244SHCL_X11_DECL(SHCLX11FMT) clipRealFormatForX11Format(SHCLX11FMTIDX uFmtIdx)
245{
246 AssertReturn(uFmtIdx < RT_ELEMENTS(g_aFormats), SHCLX11FMT_INVALID);
247 return g_aFormats[uFmtIdx].enmFmtX11;
248}
249
250/**
251 * Returns the VBox format corresponding to a supported X11 format.
252 *
253 * @return SHCLFORMAT for a specific format index.
254 * @param uFmtIdx Format index to look up VBox format for.
255 */
256static SHCLFORMAT clipVBoxFormatForX11Format(SHCLX11FMTIDX uFmtIdx)
257{
258 AssertReturn(uFmtIdx < RT_ELEMENTS(g_aFormats), VBOX_SHCL_FMT_NONE);
259 return g_aFormats[uFmtIdx].uFmtVBox;
260}
261
262/**
263 * Looks up the X11 format matching a given X11 atom.
264 *
265 * @returns The format on success, NIL_CLIPX11FORMAT on failure.
266 * @param pCtx The X11 clipboard context to use.
267 * @param atomFormat Atom to look up X11 format for.
268 */
269static SHCLX11FMTIDX clipFindX11FormatByAtom(PSHCLX11CTX pCtx, Atom atomFormat)
270{
271 for (unsigned i = 0; i < RT_ELEMENTS(g_aFormats); ++i)
272 if (clipAtomForX11Format(pCtx, i) == atomFormat)
273 {
274 LogFlowFunc(("Returning index %u for atom '%s'\n", i, g_aFormats[i].pcszAtom));
275 return i;
276 }
277 return NIL_CLIPX11FORMAT;
278}
279
280/**
281 * Enumerates supported X11 clipboard formats corresponding to given VBox formats.
282 *
283 * @returns The next matching X11 format index in the list, or NIL_CLIPX11FORMAT if there are no more.
284 * @param uFormatsVBox VBox formats to enumerate supported X11 clipboard formats for.
285 * @param lastFmtIdx The value returned from the last call of this function.
286 * Use NIL_CLIPX11FORMAT to start the enumeration.
287 */
288static SHCLX11FMTIDX clipEnumX11Formats(SHCLFORMATS uFormatsVBox,
289 SHCLX11FMTIDX lastFmtIdx)
290{
291 for (unsigned i = lastFmtIdx + 1; i < RT_ELEMENTS(g_aFormats); ++i)
292 {
293 if (uFormatsVBox & clipVBoxFormatForX11Format(i))
294 return i;
295 }
296
297 return NIL_CLIPX11FORMAT;
298}
299
300/**
301 * Array of structures for mapping Xt widgets to context pointers. We
302 * need this because the widget clipboard callbacks do not pass user data.
303 */
304static struct
305{
306 /** Pointer to widget we want to associate the context with. */
307 Widget pWidget;
308 /** Pointer to X11 context associated with the widget. */
309 PSHCLX11CTX pCtx;
310} g_aContexts[VBOX_SHARED_CLIPBOARD_X11_CONNECTIONS_MAX];
311
312/**
313 * Registers a new X11 clipboard context.
314 *
315 * @returns VBox status code.
316 * @param pCtx The X11 clipboard context to use.
317 */
318static int clipRegisterContext(PSHCLX11CTX pCtx)
319{
320 AssertPtrReturn(pCtx, VERR_INVALID_PARAMETER);
321
322 bool fFound = false;
323
324 Widget pWidget = pCtx->pWidget;
325 AssertReturn(pWidget != NULL, VERR_INVALID_PARAMETER);
326
327 for (unsigned i = 0; i < RT_ELEMENTS(g_aContexts); ++i)
328 {
329 AssertReturn( (g_aContexts[i].pWidget != pWidget)
330 && (g_aContexts[i].pCtx != pCtx), VERR_WRONG_ORDER);
331 if (g_aContexts[i].pWidget == NULL && !fFound)
332 {
333 AssertReturn(g_aContexts[i].pCtx == NULL, VERR_INTERNAL_ERROR);
334 g_aContexts[i].pWidget = pWidget;
335 g_aContexts[i].pCtx = pCtx;
336 fFound = true;
337 }
338 }
339
340 return fFound ? VINF_SUCCESS : VERR_OUT_OF_RESOURCES;
341}
342
343/**
344 * Unregister an X11 clipboard context.
345 *
346 * @param pCtx The X11 clipboard context to use.
347 */
348static void clipUnregisterContext(PSHCLX11CTX pCtx)
349{
350 AssertPtrReturnVoid(pCtx);
351
352 Widget pWidget = pCtx->pWidget;
353 AssertPtrReturnVoid(pWidget);
354
355 bool fFound = false;
356 for (unsigned i = 0; i < RT_ELEMENTS(g_aContexts); ++i)
357 {
358 Assert(!fFound || g_aContexts[i].pWidget != pWidget);
359 if (g_aContexts[i].pWidget == pWidget)
360 {
361 Assert(g_aContexts[i].pCtx != NULL);
362 g_aContexts[i].pWidget = NULL;
363 g_aContexts[i].pCtx = NULL;
364 fFound = true;
365 }
366 }
367}
368
369/**
370 * Finds a X11 clipboard context for a specific X11 widget.
371 *
372 * @returns Pointer to associated X11 clipboard context if found, or NULL if not found.
373 * @param pWidget X11 widget to return X11 clipboard context for.
374 */
375static PSHCLX11CTX clipLookupContext(Widget pWidget)
376{
377 AssertPtrReturn(pWidget, NULL);
378
379 for (unsigned i = 0; i < RT_ELEMENTS(g_aContexts); ++i)
380 {
381 if (g_aContexts[i].pWidget == pWidget)
382 {
383 Assert(g_aContexts[i].pCtx != NULL);
384 return g_aContexts[i].pCtx;
385 }
386 }
387
388 return NULL;
389}
390
391/**
392 * Converts an atom name string to an X11 atom, looking it up in a cache before asking the server.
393 *
394 * @returns Found X11 atom.
395 * @param pCtx The X11 clipboard context to use.
396 * @param pcszName Name of atom to return atom for.
397 */
398SHCL_X11_DECL(Atom) clipGetAtom(PSHCLX11CTX pCtx, const char *pcszName)
399{
400 AssertPtrReturn(pcszName, None);
401 return XInternAtom(XtDisplay(pCtx->pWidget), pcszName, False);
402}
403
404/** String written to the wakeup pipe. */
405#define WAKE_UP_STRING "WakeUp!"
406/** Length of the string written. */
407#define WAKE_UP_STRING_LEN ( sizeof(WAKE_UP_STRING) - 1 )
408
409/**
410 * Schedules a function call to run on the Xt event thread by passing it to
411 * the application context as a 0ms timeout and waking up the event loop by
412 * writing to the wakeup pipe which it monitors.
413 */
414static int clipThreadScheduleCall(PSHCLX11CTX pCtx,
415 void (*proc)(void *, void *),
416 void *client_data)
417{
418 LogFlowFunc(("proc=%p, client_data=%p\n", proc, client_data));
419
420#ifndef TESTCASE
421 AssertReturn(pCtx, VERR_INVALID_POINTER);
422 AssertReturn(pCtx->pAppContext, VERR_INVALID_POINTER);
423
424 XtAppAddTimeOut(pCtx->pAppContext, 0, (XtTimerCallbackProc)proc,
425 (XtPointer)client_data);
426 ssize_t cbWritten = write(pCtx->wakeupPipeWrite, WAKE_UP_STRING, WAKE_UP_STRING_LEN);
427 Assert(cbWritten == WAKE_UP_STRING_LEN);
428 RT_NOREF(cbWritten);
429#else
430 RT_NOREF(pCtx);
431 tstThreadScheduleCall(proc, client_data);
432#endif
433
434 LogFlowFuncLeaveRC(VINF_SUCCESS);
435 return VINF_SUCCESS;
436}
437
438/**
439 * Reports the formats currently supported by the X11 clipboard to VBox.
440 *
441 * @note Runs in Xt event thread.
442 *
443 * @param pCtx The X11 clipboard context to use.
444 */
445static void clipReportFormatsToVBox(PSHCLX11CTX pCtx)
446{
447 SHCLFORMATS vboxFmt = clipVBoxFormatForX11Format(pCtx->idxFmtText);
448 vboxFmt |= clipVBoxFormatForX11Format(pCtx->idxFmtBmp);
449 vboxFmt |= clipVBoxFormatForX11Format(pCtx->idxFmtHTML);
450#ifdef VBOX_WITH_SHARED_CLIPBOARD_TRANSFERS
451 vboxFmt |= clipVBoxFormatForX11Format(pCtx->idxFmtURI);
452#endif
453
454 LogFlowFunc(("idxFmtText=%u ('%s'), idxFmtBmp=%u ('%s'), idxFmtHTML=%u ('%s')",
455 pCtx->idxFmtText, g_aFormats[pCtx->idxFmtText].pcszAtom,
456 pCtx->idxFmtBmp, g_aFormats[pCtx->idxFmtBmp].pcszAtom,
457 pCtx->idxFmtHTML, g_aFormats[pCtx->idxFmtHTML].pcszAtom));
458#ifdef VBOX_WITH_SHARED_CLIPBOARD_TRANSFERS
459 LogFlowFunc((", idxFmtURI=%u ('%s')", pCtx->idxFmtURI, g_aFormats[pCtx->idxFmtURI].pcszAtom));
460#endif
461 LogFlow((" -> vboxFmt=%#x\n", vboxFmt));
462
463#ifdef LOG_ENABLED
464 char *pszFmts = ShClFormatsToStrA(vboxFmt);
465 AssertPtrReturnVoid(pszFmts);
466 LogRel2(("Shared Clipboard: X11 reported available VBox formats '%s'\n", pszFmts));
467 RTStrFree(pszFmts);
468#endif
469
470 pCtx->Callbacks.pfnReportFormats(pCtx->pFrontend, vboxFmt, NULL /* pvUser */);
471}
472
473/**
474 * Forgets which formats were previously in the X11 clipboard. Called when we
475 * grab the clipboard.
476 *
477 * @param pCtx The X11 clipboard context to use.
478 */
479static void clipResetX11Formats(PSHCLX11CTX pCtx)
480{
481 LogFlowFuncEnter();
482
483 pCtx->idxFmtText = 0;
484 pCtx->idxFmtBmp = 0;
485 pCtx->idxFmtHTML = 0;
486#ifdef VBOX_WITH_SHARED_CLIPBOARD_TRANSFERS
487 pCtx->idxFmtURI = 0;
488#endif
489}
490
491/**
492 * Tells VBox that X11 currently has nothing in its clipboard.
493 *
494 * @param pCtx The X11 clipboard context to use.
495 */
496SHCL_X11_DECL(void) clipReportEmpty(PSHCLX11CTX pCtx)
497{
498 clipResetX11Formats(pCtx);
499 clipReportFormatsToVBox(pCtx);
500}
501
502/**
503 * Go through an array of X11 clipboard targets to see if they contain a text
504 * format we can support, and if so choose the ones we prefer (e.g. we like
505 * UTF-8 better than plain text).
506 *
507 * @return Index to supported X clipboard format.
508 * @param pCtx The X11 clipboard context to use.
509 * @param paIdxFmtTargets The list of targets.
510 * @param cTargets The size of the list in @a pTargets.
511 */
512SHCL_X11_DECL(SHCLX11FMTIDX) clipGetTextFormatFromTargets(PSHCLX11CTX pCtx,
513 SHCLX11FMTIDX *paIdxFmtTargets,
514 size_t cTargets)
515{
516 AssertPtrReturn(pCtx, NIL_CLIPX11FORMAT);
517 AssertReturn(RT_VALID_PTR(paIdxFmtTargets) || cTargets == 0, NIL_CLIPX11FORMAT);
518
519 SHCLX11FMTIDX idxFmtText = NIL_CLIPX11FORMAT;
520 SHCLX11FMT fmtTextX11 = SHCLX11FMT_INVALID;
521 for (unsigned i = 0; i < cTargets; ++i)
522 {
523 SHCLX11FMTIDX idxFmt = paIdxFmtTargets[i];
524 if (idxFmt != NIL_CLIPX11FORMAT)
525 {
526 if ( (clipVBoxFormatForX11Format(idxFmt) == VBOX_SHCL_FMT_UNICODETEXT)
527 && fmtTextX11 < clipRealFormatForX11Format(idxFmt))
528 {
529 fmtTextX11 = clipRealFormatForX11Format(idxFmt);
530 idxFmtText = idxFmt;
531 }
532 }
533 }
534 return idxFmtText;
535}
536
537/**
538 * Goes through an array of X11 clipboard targets to see if they contain a bitmap
539 * format we can support, and if so choose the ones we prefer (e.g. we like
540 * BMP better than PNG because we don't have to convert).
541 *
542 * @return Supported X clipboard format.
543 * @param pCtx The X11 clipboard context to use.
544 * @param paIdxFmtTargets The list of targets.
545 * @param cTargets The size of the list in @a pTargets.
546 */
547static SHCLX11FMTIDX clipGetBitmapFormatFromTargets(PSHCLX11CTX pCtx,
548 SHCLX11FMTIDX *paIdxFmtTargets,
549 size_t cTargets)
550{
551 AssertPtrReturn(pCtx, NIL_CLIPX11FORMAT);
552 AssertReturn(RT_VALID_PTR(paIdxFmtTargets) || cTargets == 0, NIL_CLIPX11FORMAT);
553
554 SHCLX11FMTIDX idxFmtBmp = NIL_CLIPX11FORMAT;
555 SHCLX11FMT fmtBmpX11 = SHCLX11FMT_INVALID;
556 for (unsigned i = 0; i < cTargets; ++i)
557 {
558 SHCLX11FMTIDX idxFmt = paIdxFmtTargets[i];
559 if (idxFmt != NIL_CLIPX11FORMAT)
560 {
561 if ( (clipVBoxFormatForX11Format(idxFmt) == VBOX_SHCL_FMT_BITMAP)
562 && fmtBmpX11 < clipRealFormatForX11Format(idxFmt))
563 {
564 fmtBmpX11 = clipRealFormatForX11Format(idxFmt);
565 idxFmtBmp = idxFmt;
566 }
567 }
568 }
569 return idxFmtBmp;
570}
571
572/**
573 * Goes through an array of X11 clipboard targets to see if they contain a HTML
574 * format we can support, and if so choose the ones we prefer.
575 *
576 * @return Supported X clipboard format.
577 * @param pCtx The X11 clipboard context to use.
578 * @param paIdxFmtTargets The list of targets.
579 * @param cTargets The size of the list in @a pTargets.
580 */
581static SHCLX11FMTIDX clipGetHtmlFormatFromTargets(PSHCLX11CTX pCtx,
582 SHCLX11FMTIDX *paIdxFmtTargets,
583 size_t cTargets)
584{
585 AssertPtrReturn(pCtx, NIL_CLIPX11FORMAT);
586 AssertReturn(RT_VALID_PTR(paIdxFmtTargets) || cTargets == 0, NIL_CLIPX11FORMAT);
587
588 SHCLX11FMTIDX idxFmtHTML = NIL_CLIPX11FORMAT;
589 SHCLX11FMT fmxHTMLX11 = SHCLX11FMT_INVALID;
590 for (unsigned i = 0; i < cTargets; ++i)
591 {
592 SHCLX11FMTIDX idxFmt = paIdxFmtTargets[i];
593 if (idxFmt != NIL_CLIPX11FORMAT)
594 {
595 if ( (clipVBoxFormatForX11Format(idxFmt) == VBOX_SHCL_FMT_HTML)
596 && fmxHTMLX11 < clipRealFormatForX11Format(idxFmt))
597 {
598 fmxHTMLX11 = clipRealFormatForX11Format(idxFmt);
599 idxFmtHTML = idxFmt;
600 }
601 }
602 }
603 return idxFmtHTML;
604}
605
606# ifdef VBOX_WITH_SHARED_CLIPBOARD_TRANSFERS
607/**
608 * Goes through an array of X11 clipboard targets to see if they contain an URI list
609 * format we can support, and if so choose the ones we prefer.
610 *
611 * @return Supported X clipboard format.
612 * @param pCtx The X11 clipboard context to use.
613 * @param paIdxFmtTargets The list of targets.
614 * @param cTargets The size of the list in @a pTargets.
615 */
616static SHCLX11FMTIDX clipGetURIListFormatFromTargets(PSHCLX11CTX pCtx,
617 SHCLX11FMTIDX *paIdxFmtTargets,
618 size_t cTargets)
619{
620 AssertPtrReturn(pCtx, NIL_CLIPX11FORMAT);
621 AssertReturn(RT_VALID_PTR(paIdxFmtTargets) || cTargets == 0, NIL_CLIPX11FORMAT);
622
623 SHCLX11FMTIDX idxFmtURI = NIL_CLIPX11FORMAT;
624 SHCLX11FMT fmtURIX11 = SHCLX11FMT_INVALID;
625 for (unsigned i = 0; i < cTargets; ++i)
626 {
627 SHCLX11FMTIDX idxFmt = paIdxFmtTargets[i];
628 if (idxFmt != NIL_CLIPX11FORMAT)
629 {
630 if ( (clipVBoxFormatForX11Format(idxFmt) == VBOX_SHCL_FMT_URI_LIST)
631 && fmtURIX11 < clipRealFormatForX11Format(idxFmt))
632 {
633 fmtURIX11 = clipRealFormatForX11Format(idxFmt);
634 idxFmtURI = idxFmt;
635 }
636 }
637 }
638 return idxFmtURI;
639}
640# endif /* VBOX_WITH_SHARED_CLIPBOARD_TRANSFERS */
641
642/**
643 * Goes through an array of X11 clipboard targets to see if we can support any
644 * of them and if relevant to choose the ones we prefer (e.g. we like Utf8
645 * better than plain text).
646 *
647 * @param pCtx The X11 clipboard context to use.
648 * @param paIdxFmtTargets The list of targets.
649 * @param cTargets The size of the list in @a pTargets.
650 */
651static void clipGetFormatsFromTargets(PSHCLX11CTX pCtx,
652 SHCLX11FMTIDX *paIdxFmtTargets, size_t cTargets)
653{
654 AssertPtrReturnVoid(pCtx);
655 AssertPtrReturnVoid(paIdxFmtTargets);
656
657 SHCLX11FMTIDX idxFmtText = clipGetTextFormatFromTargets(pCtx, paIdxFmtTargets, cTargets);
658 if (pCtx->idxFmtText != idxFmtText)
659 pCtx->idxFmtText = idxFmtText;
660
661 pCtx->idxFmtBmp = SHCLX11FMT_INVALID; /* not yet supported */ /** @todo r=andy Check this. */
662 SHCLX11FMTIDX idxFmtBmp = clipGetBitmapFormatFromTargets(pCtx, paIdxFmtTargets, cTargets);
663 if (pCtx->idxFmtBmp != idxFmtBmp)
664 pCtx->idxFmtBmp = idxFmtBmp;
665
666 SHCLX11FMTIDX idxFmtHTML = clipGetHtmlFormatFromTargets(pCtx, paIdxFmtTargets, cTargets);
667 if (pCtx->idxFmtHTML != idxFmtHTML)
668 pCtx->idxFmtHTML = idxFmtHTML;
669
670#ifdef VBOX_WITH_SHARED_CLIPBOARD_TRANSFERS
671 SHCLX11FMTIDX idxFmtURI = clipGetURIListFormatFromTargets(pCtx, paIdxFmtTargets, cTargets);
672 if (pCtx->idxFmtURI != idxFmtURI)
673 pCtx->idxFmtURI = idxFmtURI;
674#endif
675}
676
677#ifdef VBOX_WITH_SHARED_CLIPBOARD_XT_BUSY
678DECLINLINE(bool) clipGetXtBusy(PSHCLX11CTX pCtx)
679{
680 LogFlowFunc(("fXtBusy=%RTbool, fXtNeedsUpdate=%RTbool\n", pCtx->fXtBusy, pCtx->fXtNeedsUpdate));
681 return pCtx->fXtBusy;
682}
683
684DECLINLINE(bool) clipGetXtNeedsUpdate(PSHCLX11CTX pCtx)
685{
686 LogFlowFunc(("fXtBusy=%RTbool, fXtNeedsUpdate=%RTbool\n", pCtx->fXtBusy, pCtx->fXtNeedsUpdate));
687 return pCtx->fXtNeedsUpdate;
688}
689
690DECLINLINE(bool) clipSetXtBusy(PSHCLX11CTX pCtx, bool fBusy)
691{
692 pCtx->fXtBusy = fBusy;
693 LogFlowFunc(("fXtBusy=%RTbool, fXtNeedsUpdate=%RTbool\n", pCtx->fXtBusy, pCtx->fXtNeedsUpdate));
694 return pCtx->fXtBusy;
695}
696
697DECLINLINE(bool) clipSetXtNeedsUpdate(PSHCLX11CTX pCtx, bool fNeedsUpdate)
698{
699 pCtx->fXtNeedsUpdate = fNeedsUpdate;
700 LogFlowFunc(("fXtBusy=%RTbool, fXtNeedsUpdate=%RTbool\n", pCtx->fXtBusy, pCtx->fXtNeedsUpdate));
701 return pCtx->fXtNeedsUpdate;
702}
703#endif /* VBOX_WITH_SHARED_CLIPBOARD_XT_BUSY */
704
705/**
706 * Updates the context's information about targets currently supported by X11,
707 * based on an array of X11 atoms.
708 *
709 * @param pCtx The X11 clipboard context to use.
710 * @param pTargets The array of atoms describing the targets supported.
711 * @param cTargets The size of the array @a pTargets.
712 */
713SHCL_X11_DECL(void) clipUpdateX11Targets(PSHCLX11CTX pCtx, SHCLX11FMTIDX *paIdxFmtTargets, size_t cTargets)
714{
715 LogFlowFuncEnter();
716
717#ifdef VBOX_WITH_SHARED_CLIPBOARD_XT_BUSY
718 clipSetXtBusy(pCtx, false);
719 if (clipGetXtNeedsUpdate(pCtx))
720 {
721 /* We may already be out of date. */
722 clipSetXtNeedsUpdate(pCtx, false);
723 clipQueryX11Targets(pCtx);
724 return;
725 }
726#endif
727
728 if (paIdxFmtTargets == NULL)
729 {
730 /* No data available */
731 clipReportEmpty(pCtx);
732 return;
733 }
734
735 clipGetFormatsFromTargets(pCtx, paIdxFmtTargets, cTargets);
736 clipReportFormatsToVBox(pCtx);
737}
738
739/**
740 * Notifies the VBox clipboard about available data formats ("targets" on X11),
741 * based on the information obtained from the X11 clipboard.
742 *
743 * @note Callback installed by clipQueryX11Targets() for XtGetSelectionValue().
744 * @note This function is treated as API glue, and as such is not part of any
745 * unit test. So keep it simple, be paranoid and log everything.
746 */
747SHCL_X11_DECL(void) clipQueryX11TargetsCallback(Widget widget, XtPointer pClient,
748 Atom * /* selection */, Atom *atomType,
749 XtPointer pValue, long unsigned int *pcLen,
750 int *piFormat)
751{
752 RT_NOREF(piFormat);
753
754 PSHCLX11CTX pCtx = reinterpret_cast<SHCLX11CTX *>(pClient);
755
756 LogFlowFunc(("pValue=%p, *pcLen=%u, *atomType=%d%s\n",
757 pValue, *pcLen, *atomType, *atomType == XT_CONVERT_FAIL ? " (XT_CONVERT_FAIL)" : ""));
758
759 Atom *pAtoms = (Atom *)pValue;
760
761 unsigned cFormats = *pcLen;
762
763 LogRel2(("Shared Clipboard: Querying X11 formats ...\n"));
764 LogRel2(("Shared Clipboard: %u X11 formats were found\n", cFormats));
765
766 SHCLX11FMTIDX *paIdxFmt = NULL;
767 if ( cFormats
768 && pValue
769 && (*atomType != XT_CONVERT_FAIL /* time out */))
770 {
771 /* Allocated array to hold the format indices. */
772 paIdxFmt = (SHCLX11FMTIDX *)RTMemAllocZ(cFormats * sizeof(SHCLX11FMTIDX));
773 }
774
775#if !defined(TESTCASE)
776 if (pValue)
777 {
778 for (unsigned i = 0; i < cFormats; ++i)
779 {
780 if (pAtoms[i])
781 {
782 char *pszName = XGetAtomName(XtDisplay(widget), pAtoms[i]);
783 LogRel2(("Shared Clipboard: Found X11 format '%s'\n", pszName));
784 XFree(pszName);
785 }
786 else
787 LogFunc(("Found empty target\n"));
788 }
789 }
790#endif
791
792 if (paIdxFmt)
793 {
794 for (unsigned i = 0; i < cFormats; ++i)
795 {
796 for (unsigned j = 0; j < RT_ELEMENTS(g_aFormats); ++j)
797 {
798 Atom target = XInternAtom(XtDisplay(widget),
799 g_aFormats[j].pcszAtom, False);
800 if (*(pAtoms + i) == target)
801 paIdxFmt[i] = j;
802 }
803#if !defined(TESTCASE)
804 if (paIdxFmt[i] != SHCLX11FMT_INVALID)
805 LogRel2(("Shared Clipboard: Reporting X11 format '%s'\n", g_aFormats[paIdxFmt[i]].pcszAtom));
806#endif
807 }
808 }
809 else
810 LogFunc(("Reporting empty targets (none reported or allocation failure)\n"));
811
812 clipUpdateX11Targets(pCtx, paIdxFmt, cFormats);
813 RTMemFree(paIdxFmt);
814
815 XtFree(reinterpret_cast<char *>(pValue));
816}
817
818/**
819 * Queries the current formats ("targets") of the X11 clipboard ("CLIPBOARD").
820 *
821 * @param pCtx The X11 clipboard context to use.
822 */
823SHCL_X11_DECL(void) clipQueryX11Targets(PSHCLX11CTX pCtx)
824{
825#ifndef TESTCASE
826
827# ifdef VBOX_WITH_SHARED_CLIPBOARD_XT_BUSY
828 if (clipGetXtBusy(pCtx))
829 {
830 clipSetXtNeedsUpdate(pCtx, true);
831 return;
832 }
833 clipSetXtBusy(pCtx, true);
834# endif
835
836 XtGetSelectionValue(pCtx->pWidget,
837 clipGetAtom(pCtx, "CLIPBOARD"),
838 clipGetAtom(pCtx, "TARGETS"),
839 clipQueryX11TargetsCallback, pCtx,
840 CurrentTime);
841#else
842 tstRequestTargets(pCtx);
843#endif
844}
845
846typedef struct
847{
848 int type; /* event base */
849 unsigned long serial;
850 Bool send_event;
851 Display *display;
852 Window window;
853 int subtype;
854 Window owner;
855 Atom selection;
856 Time timestamp;
857 Time selection_timestamp;
858} XFixesSelectionNotifyEvent;
859
860#ifndef TESTCASE
861/**
862 * Waits until an event arrives and handle it if it is an XFIXES selection
863 * event, which Xt doesn't know about.
864 *
865 * @param pCtx The X11 clipboard context to use.
866 */
867static void clipPeekEventAndDoXFixesHandling(PSHCLX11CTX pCtx)
868{
869 union
870 {
871 XEvent event;
872 XFixesSelectionNotifyEvent fixes;
873 } event = { { 0 } };
874
875 if (XtAppPeekEvent(pCtx->pAppContext, &event.event))
876 {
877 if ( (event.event.type == pCtx->fixesEventBase)
878 && (event.fixes.owner != XtWindow(pCtx->pWidget)))
879 {
880 if ( (event.fixes.subtype == 0 /* XFixesSetSelectionOwnerNotify */)
881 && (event.fixes.owner != 0))
882 clipQueryX11Targets(pCtx);
883 else
884 clipReportEmpty(pCtx);
885 }
886 }
887}
888
889/**
890 * The main loop of our X11 event thread.
891 *
892 * @returns VBox status code.
893 * @param hThreadSelf Associated thread handle.
894 * @param pvUser Pointer to the X11 clipboard context to use.
895 */
896static DECLCALLBACK(int) clipThreadMain(RTTHREAD hThreadSelf, void *pvUser)
897{
898 PSHCLX11CTX pCtx = (PSHCLX11CTX)pvUser;
899 AssertPtr(pCtx);
900
901 LogFlowFunc(("pCtx=%p\n", pCtx));
902
903 bool fSignalled = false; /* Whether we have signalled the parent already or not. */
904
905 int rc = clipInitInternal(pCtx);
906 if (RT_SUCCESS(rc))
907 {
908 rc = clipRegisterContext(pCtx);
909 if (RT_SUCCESS(rc))
910 {
911 if (pCtx->fGrabClipboardOnStart)
912 clipQueryX11Targets(pCtx);
913
914 pCtx->fThreadStarted = true;
915
916 /* We're now ready to run, tell parent. */
917 int rc2 = RTThreadUserSignal(hThreadSelf);
918 AssertRC(rc2);
919
920 fSignalled = true;
921
922 while (XtAppGetExitFlag(pCtx->pAppContext) == FALSE)
923 {
924 clipPeekEventAndDoXFixesHandling(pCtx);
925 XtAppProcessEvent(pCtx->pAppContext, XtIMAll);
926 }
927
928 LogRel(("Shared Clipboard: X11 event thread exiting\n"));
929
930 clipUnregisterContext(pCtx);
931 }
932 else
933 {
934 LogRel(("Shared Clipboard: unable to register clip context: %Rrc\n", rc));
935 }
936
937 clipUninitInternal(pCtx);
938 }
939
940 if (!fSignalled) /* Signal parent if we didn't do so yet. */
941 {
942 int rc2 = RTThreadUserSignal(hThreadSelf);
943 AssertRC(rc2);
944 }
945
946 LogFlowFuncLeaveRC(rc);
947 return rc;
948}
949
950/**
951 * Worker function for stopping the clipboard which runs on the event
952 * thread.
953 *
954 * @param pvUserData Pointer to the X11 clipboard context to use.
955 */
956static void clipThreadSignalStop(void *pvUserData, void *)
957{
958 PSHCLX11CTX pCtx = (PSHCLX11CTX)pvUserData;
959
960 /* This might mean that we are getting stopped twice. */
961 Assert(pCtx->pWidget != NULL);
962
963 /* Set the termination flag to tell the Xt event loop to exit. We
964 * reiterate that any outstanding requests from the X11 event loop to
965 * the VBox part *must* have returned before we do this. */
966 XtAppSetExitFlag(pCtx->pAppContext);
967}
968
969/**
970 * Sets up the XFixes library and load the XFixesSelectSelectionInput symbol.
971 */
972static int clipLoadXFixes(Display *pDisplay, PSHCLX11CTX pCtx)
973{
974 int rc;
975
976 void *hFixesLib = dlopen("libXfixes.so.1", RTLD_LAZY);
977 if (!hFixesLib)
978 hFixesLib = dlopen("libXfixes.so.2", RTLD_LAZY);
979 if (!hFixesLib)
980 hFixesLib = dlopen("libXfixes.so.3", RTLD_LAZY);
981 if (!hFixesLib)
982 hFixesLib = dlopen("libXfixes.so.4", RTLD_LAZY);
983 if (hFixesLib)
984 {
985 /* For us, a NULL function pointer is a failure */
986 pCtx->fixesSelectInput = (void (*)(Display *, Window, Atom, long unsigned int))
987 (uintptr_t)dlsym(hFixesLib, "XFixesSelectSelectionInput");
988 if (pCtx->fixesSelectInput)
989 {
990 int dummy1 = 0;
991 int dummy2 = 0;
992 if (XQueryExtension(pDisplay, "XFIXES", &dummy1, &pCtx->fixesEventBase, &dummy2) != 0)
993 {
994 if (pCtx->fixesEventBase >= 0)
995 {
996 rc = VINF_SUCCESS;
997 }
998 else
999 {
1000 LogRel(("Shared Clipboard: fixesEventBase is less than zero: %d\n", pCtx->fixesEventBase));
1001 rc = VERR_NOT_SUPPORTED;
1002 }
1003 }
1004 else
1005 {
1006 LogRel(("Shared Clipboard: XQueryExtension failed\n"));
1007 rc = VERR_NOT_SUPPORTED;
1008 }
1009 }
1010 else
1011 {
1012 LogRel(("Shared Clipboard: Symbol XFixesSelectSelectionInput not found!\n"));
1013 rc = VERR_NOT_SUPPORTED;
1014 }
1015 }
1016 else
1017 {
1018 LogRel(("Shared Clipboard: libxFixes.so.* not found!\n"));
1019 rc = VERR_NOT_SUPPORTED;
1020 }
1021 return rc;
1022}
1023
1024/**
1025 * This is the callback which is scheduled when data is available on the
1026 * wakeup pipe. It simply reads all data from the pipe.
1027 *
1028 * @param pvUserData Pointer to the X11 clipboard context to use.
1029 */
1030static void clipThreadDrainWakeupPipe(XtPointer pvUserData, int *, XtInputId *)
1031{
1032 LogFlowFuncEnter();
1033
1034 PSHCLX11CTX pCtx = (PSHCLX11CTX)pvUserData;
1035 char acBuf[WAKE_UP_STRING_LEN];
1036
1037 while (read(pCtx->wakeupPipeRead, acBuf, sizeof(acBuf)) > 0) {}
1038}
1039#endif /* !TESTCASE */
1040
1041/**
1042 * X11-specific initialisation for the Shared Clipboard.
1043 *
1044 * Note: Must be called from the thread serving the Xt stuff.
1045 *
1046 * @returns VBox status code.
1047 * @param pCtx The X11 clipboard context to init.
1048 */
1049static int clipInitInternal(PSHCLX11CTX pCtx)
1050{
1051 LogFlowFunc(("pCtx=%p\n", pCtx));
1052
1053 /* Make sure we are thread safe. */
1054 XtToolkitThreadInitialize();
1055
1056 /*
1057 * Set up the Clipboard application context and main window. We call all
1058 * these functions directly instead of calling XtOpenApplication() so
1059 * that we can fail gracefully if we can't get an X11 display.
1060 */
1061 XtToolkitInitialize();
1062
1063 int rc = VINF_SUCCESS;
1064
1065 Assert(pCtx->pAppContext == NULL); /* No nested initialization. */
1066 pCtx->pAppContext = XtCreateApplicationContext();
1067 if (pCtx->pAppContext == NULL)
1068 {
1069 LogRel(("Shared Clipboard: Failed to create Xt application context\n"));
1070 return VERR_NOT_SUPPORTED; /** @todo Fudge! */
1071 }
1072
1073 /* Create a window and make it a clipboard viewer. */
1074 int cArgc = 0;
1075 char *pcArgv = 0;
1076 Display *pDisplay = XtOpenDisplay(pCtx->pAppContext, 0, 0, "VBoxShCl", 0, 0, &cArgc, &pcArgv);
1077 if (pDisplay == NULL)
1078 {
1079 LogRel(("Shared Clipboard: Failed to connect to the X11 clipboard - the window system may not be running\n"));
1080 rc = VERR_NOT_SUPPORTED;
1081 }
1082
1083#ifndef TESTCASE
1084 if (RT_SUCCESS(rc))
1085 {
1086 rc = clipLoadXFixes(pDisplay, pCtx);
1087 if (RT_FAILURE(rc))
1088 LogRel(("Shared Clipboard: Failed to load the XFIXES extension\n"));
1089 }
1090#endif
1091
1092 if (RT_SUCCESS(rc))
1093 {
1094 pCtx->pWidget = XtVaAppCreateShell(0, "VBoxShCl",
1095 applicationShellWidgetClass,
1096 pDisplay,
1097 XtNwidth, 1, XtNheight, 1,
1098 NULL);
1099 if (pCtx->pWidget == NULL)
1100 {
1101 LogRel(("Shared Clipboard: Failed to create Xt app shell\n"));
1102 rc = VERR_NO_MEMORY; /** @todo r=andy Improve this. */
1103 }
1104 else
1105 {
1106#ifndef TESTCASE
1107 if (!XtAppAddInput(pCtx->pAppContext, pCtx->wakeupPipeRead,
1108 (XtPointer) XtInputReadMask,
1109 clipThreadDrainWakeupPipe, (XtPointer) pCtx))
1110 {
1111 LogRel(("Shared Clipboard: Failed to add input to Xt app context\n"));
1112 rc = VERR_ACCESS_DENIED; /** @todo r=andy Improve this. */
1113 }
1114#endif
1115 }
1116 }
1117
1118 if (RT_SUCCESS(rc))
1119 {
1120 XtSetMappedWhenManaged(pCtx->pWidget, false);
1121 XtRealizeWidget(pCtx->pWidget);
1122
1123#ifndef TESTCASE
1124 /* Enable clipboard update notification. */
1125 pCtx->fixesSelectInput(pDisplay, XtWindow(pCtx->pWidget),
1126 clipGetAtom(pCtx, "CLIPBOARD"),
1127 7 /* All XFixes*Selection*NotifyMask flags */);
1128#endif
1129 }
1130
1131 if (RT_FAILURE(rc))
1132 {
1133 LogRel(("Shared Clipboard: Initialisation failed: %Rrc\n", rc));
1134 clipUninitInternal(pCtx);
1135 }
1136
1137 LogFlowFuncLeaveRC(rc);
1138 return rc;
1139}
1140
1141/**
1142 * X11-specific uninitialisation for the Shared Clipboard.
1143 *
1144 * Note: Must be called from the thread serving the Xt stuff.
1145 *
1146 * @param pCtx The X11 clipboard context to uninit.
1147 */
1148static void clipUninitInternal(PSHCLX11CTX pCtx)
1149{
1150 AssertPtrReturnVoid(pCtx);
1151
1152 LogFlowFunc(("pCtx=%p\n", pCtx));
1153
1154 if (pCtx->pWidget)
1155 {
1156 /* Valid widget + invalid appcontext = bug. But don't return yet. */
1157 AssertPtr(pCtx->pAppContext);
1158
1159 XtDestroyWidget(pCtx->pWidget);
1160 pCtx->pWidget = NULL;
1161 }
1162
1163 if (pCtx->pAppContext)
1164 {
1165 XtDestroyApplicationContext(pCtx->pAppContext);
1166 pCtx->pAppContext = NULL;
1167 }
1168
1169 LogFlowFuncLeaveRC(VINF_SUCCESS);
1170}
1171
1172/**
1173 * Sets the callback table, internal version.
1174 *
1175 * @param pCtx The clipboard context.
1176 * @param pCallbacks Callback table to set. If NULL, the current callback table will be cleared.
1177 */
1178static void shClX11SetCallbacksInternal(PSHCLX11CTX pCtx, PSHCLCALLBACKS pCallbacks)
1179{
1180 if (pCallbacks)
1181 {
1182 memcpy(&pCtx->Callbacks, pCallbacks, sizeof(SHCLCALLBACKS));
1183 }
1184 else
1185 RT_ZERO(pCtx->Callbacks);
1186}
1187
1188/**
1189 * Sets the callback table.
1190 *
1191 * @param pCtx The clipboard context.
1192 * @param pCallbacks Callback table to set. If NULL, the current callback table will be cleared.
1193 */
1194void ShClX11SetCallbacks(PSHCLX11CTX pCtx, PSHCLCALLBACKS pCallbacks)
1195{
1196 shClX11SetCallbacksInternal(pCtx, pCallbacks);
1197}
1198
1199/**
1200 * Initializes a X11 context of the Shared Clipboard.
1201 *
1202 * @returns VBox status code.
1203 * @param pCtx The clipboard context to initialize.
1204 * @param pCallbacks Callback table to use.
1205 * @param pParent Parent context to use.
1206 * @param fHeadless Whether the code runs in a headless environment or not.
1207 */
1208int ShClX11Init(PSHCLX11CTX pCtx, PSHCLCALLBACKS pCallbacks, PSHCLCONTEXT pParent, bool fHeadless)
1209{
1210 AssertPtrReturn(pCtx, VERR_INVALID_POINTER);
1211
1212 LogFlowFunc(("pCtx=%p\n", pCtx));
1213
1214 int rc = VINF_SUCCESS;
1215
1216 RT_BZERO(pCtx, sizeof(SHCLX11CTX));
1217
1218 if (fHeadless)
1219 {
1220 /*
1221 * If we don't find the DISPLAY environment variable we assume that
1222 * we are not connected to an X11 server. Don't actually try to do
1223 * this then, just fail silently and report success on every call.
1224 * This is important for VBoxHeadless.
1225 */
1226 LogRel(("Shared Clipboard: X11 DISPLAY variable not set -- disabling clipboard sharing\n"));
1227 }
1228
1229 /* Install given callbacks. */
1230 shClX11SetCallbacksInternal(pCtx, pCallbacks);
1231
1232 pCtx->fHaveX11 = !fHeadless;
1233 pCtx->pFrontend = pParent;
1234
1235#ifdef VBOX_WITH_SHARED_CLIPBOARD_XT_BUSY
1236 pCtx->fXtBusy = false;
1237 pCtx->fXtNeedsUpdate = false;
1238#endif
1239
1240#ifdef VBOX_WITH_SHARED_CLIPBOARD_TRANSFERS_HTTP
1241 ShClTransferHttpServerInit(&pCtx->HttpCtx.HttpServer);
1242#endif
1243
1244#ifdef TESTCASE
1245 if (RT_SUCCESS(rc))
1246 {
1247 /** @todo The testcases currently do not utilize the threading code. So init stuff here. */
1248 rc = clipInitInternal(pCtx);
1249 if (RT_SUCCESS(rc))
1250 rc = clipRegisterContext(pCtx);
1251 }
1252#endif
1253
1254 LogFlowFuncLeaveRC(rc);
1255 return rc;
1256}
1257
1258/**
1259 * Destroys a Shared Clipboard X11 context.
1260 *
1261 * @param pCtx The X11 clipboard context to destroy.
1262 */
1263void ShClX11Destroy(PSHCLX11CTX pCtx)
1264{
1265 if (!pCtx)
1266 return;
1267
1268 LogFlowFunc(("pCtx=%p\n", pCtx));
1269
1270#ifdef VBOX_WITH_SHARED_CLIPBOARD_TRANSFERS_HTTP
1271 ShClTransferHttpServerDestroy(&pCtx->HttpCtx.HttpServer);
1272#endif
1273
1274#ifdef TESTCASE
1275 /** @todo The testcases currently do not utilize the threading code. So uninit stuff here. */
1276 clipUnregisterContext(pCtx);
1277 clipUninitInternal(pCtx);
1278#endif
1279
1280 if (pCtx->fHaveX11)
1281 {
1282 /* We set this to NULL when the event thread exits. It really should
1283 * have exited at this point, when we are about to unload the code from
1284 * memory. */
1285 Assert(pCtx->pWidget == NULL);
1286 }
1287}
1288
1289#ifndef TESTCASE
1290/**
1291 * Starts our own Xt even thread for handling Shared Clipboard messages, extended version.
1292 *
1293 * @returns VBox status code.
1294 * @param pCtx The X11 clipboard context to use.
1295 * @param pszName Thread name to use.
1296 * @param fGrab Whether we should try to grab the shared clipboard at once.
1297 */
1298int ShClX11ThreadStartEx(PSHCLX11CTX pCtx, const char *pszName, bool fGrab)
1299{
1300 AssertPtrReturn(pCtx, VERR_INVALID_POINTER);
1301
1302 /*
1303 * Immediately return if we are not connected to the X server.
1304 */
1305 if (!pCtx->fHaveX11)
1306 return VINF_SUCCESS;
1307
1308 pCtx->fGrabClipboardOnStart = fGrab;
1309
1310 clipResetX11Formats(pCtx);
1311
1312 int rc;
1313
1314 /*
1315 * Create the pipes.
1316 ** @todo r=andy Replace this with RTPipe API.
1317 */
1318 int pipes[2];
1319 if (!pipe(pipes))
1320 {
1321 pCtx->wakeupPipeRead = pipes[0];
1322 pCtx->wakeupPipeWrite = pipes[1];
1323
1324 if (!fcntl(pCtx->wakeupPipeRead, F_SETFL, O_NONBLOCK))
1325 {
1326 rc = VINF_SUCCESS;
1327 }
1328 else
1329 rc = RTErrConvertFromErrno(errno);
1330 }
1331 else
1332 rc = RTErrConvertFromErrno(errno);
1333
1334 if (RT_SUCCESS(rc))
1335 {
1336 LogRel2(("Shared Clipboard: Starting X11 event thread ...\n"));
1337
1338 rc = RTThreadCreate(&pCtx->Thread, clipThreadMain, pCtx, 0,
1339 RTTHREADTYPE_IO, RTTHREADFLAGS_WAITABLE, pszName);
1340 if (RT_SUCCESS(rc))
1341 rc = RTThreadUserWait(pCtx->Thread, RT_MS_30SEC /* msTimeout */);
1342
1343 if (RT_FAILURE(rc))
1344 {
1345 LogRel(("Shared Clipboard: Failed to start the X11 event thread with %Rrc\n", rc));
1346 clipUninitInternal(pCtx);
1347 }
1348 else
1349 {
1350 if (!pCtx->fThreadStarted)
1351 {
1352 LogRel(("Shared Clipboard: X11 event thread reported an error while starting\n"));
1353 }
1354 else
1355 LogRel2(("Shared Clipboard: X11 event thread started\n"));
1356 }
1357 }
1358
1359 LogFlowFuncLeaveRC(rc);
1360 return rc;
1361}
1362
1363/**
1364 * Starts our own Xt even thread for handling Shared Clipboard messages.
1365 *
1366 * @returns VBox status code.
1367 * @param pCtx The X11 clipboard context to use.
1368 * @param fGrab Whether we should try to grab the shared clipboard at once.
1369 */
1370int ShClX11ThreadStart(PSHCLX11CTX pCtx, bool fGrab)
1371{
1372 return ShClX11ThreadStartEx(pCtx, "SHCLX11", fGrab);
1373}
1374
1375/**
1376 * Stops the Shared Clipboard Xt even thread.
1377 *
1378 * @note Any requests from this object to get clipboard data from VBox
1379 * *must* have completed or aborted before we are called, as
1380 * otherwise the X11 event loop will still be waiting for the request
1381 * to return and will not be able to terminate.
1382 *
1383 * @returns VBox status code.
1384 * @param pCtx The X11 clipboard context to use.
1385 */
1386int ShClX11ThreadStop(PSHCLX11CTX pCtx)
1387{
1388 int rc;
1389 /*
1390 * Immediately return if we are not connected to the X server.
1391 */
1392 if (!pCtx->fHaveX11)
1393 return VINF_SUCCESS;
1394
1395 LogRel2(("Shared Clipboard: Signalling the X11 event thread to stop\n"));
1396
1397 /* Write to the "stop" pipe. */
1398 rc = clipThreadScheduleCall(pCtx, clipThreadSignalStop, (XtPointer)pCtx);
1399 if (RT_FAILURE(rc))
1400 {
1401 LogRel(("Shared Clipboard: cannot notify X11 event thread on shutdown with %Rrc\n", rc));
1402 return rc;
1403 }
1404
1405 LogRel2(("Shared Clipboard: Waiting for X11 event thread to stop ...\n"));
1406
1407 int rcThread;
1408 rc = RTThreadWait(pCtx->Thread, RT_MS_30SEC /* msTimeout */, &rcThread);
1409 if (RT_SUCCESS(rc))
1410 rc = rcThread;
1411 if (RT_SUCCESS(rc))
1412 {
1413 if (pCtx->wakeupPipeRead != 0)
1414 {
1415 close(pCtx->wakeupPipeRead);
1416 pCtx->wakeupPipeRead = 0;
1417 }
1418
1419 if (pCtx->wakeupPipeWrite != 0)
1420 {
1421 close(pCtx->wakeupPipeWrite);
1422 pCtx->wakeupPipeWrite = 0;
1423 }
1424 }
1425
1426 if (RT_SUCCESS(rc))
1427 {
1428 LogRel2(("Shared Clipboard: X11 event thread stopped successfully\n"));
1429 }
1430 else
1431 LogRel(("Shared Clipboard: Stopping X11 event thread failed with %Rrc\n", rc));
1432
1433 LogFlowFuncLeaveRC(rc);
1434 return rc;
1435}
1436#endif /* !TESTCASE */
1437
1438/**
1439 * Returns the targets supported by VBox.
1440 *
1441 * This will return a list of atoms which tells the caller
1442 * what kind of clipboard formats we support.
1443 *
1444 * @returns VBox status code.
1445 * @param pCtx The X11 clipboard context to use.
1446 * @param atomTypeReturn The type of the data we are returning.
1447 * @param pValReturn A pointer to the data we are returning. This
1448 * should be set to memory allocated by XtMalloc,
1449 * which will be freed later by the Xt toolkit.
1450 * @param pcLenReturn The length of the data we are returning.
1451 * @param piFormatReturn The format (8bit, 16bit, 32bit) of the data we are
1452 * returning.
1453 * @note X11 backend code, called by the XtOwnSelection callback.
1454 */
1455static int clipCreateX11Targets(PSHCLX11CTX pCtx, Atom *atomTypeReturn,
1456 XtPointer *pValReturn,
1457 unsigned long *pcLenReturn,
1458 int *piFormatReturn)
1459{
1460 const unsigned cFixedTargets = 3; /* See below. */
1461
1462 Atom *pAtomTargets = (Atom *)XtMalloc((SHCL_MAX_X11_FORMATS + cFixedTargets) * sizeof(Atom));
1463 if (!pAtomTargets)
1464 return VERR_NO_MEMORY;
1465
1466 unsigned cTargets = 0;
1467 SHCLX11FMTIDX idxFmt = NIL_CLIPX11FORMAT;
1468 do
1469 {
1470 idxFmt = clipEnumX11Formats(pCtx->vboxFormats, idxFmt);
1471 if (idxFmt != NIL_CLIPX11FORMAT)
1472 {
1473 pAtomTargets[cTargets] = clipAtomForX11Format(pCtx, idxFmt);
1474 ++cTargets;
1475 }
1476 } while (idxFmt != NIL_CLIPX11FORMAT);
1477
1478 /* We always offer these fixed targets. */
1479 pAtomTargets[cTargets] = clipGetAtom(pCtx, "TARGETS");
1480 pAtomTargets[cTargets + 1] = clipGetAtom(pCtx, "MULTIPLE");
1481 pAtomTargets[cTargets + 2] = clipGetAtom(pCtx, "TIMESTAMP");
1482
1483 *atomTypeReturn = XA_ATOM;
1484 *pValReturn = (XtPointer)pAtomTargets;
1485 *pcLenReturn = cTargets + cFixedTargets;
1486 *piFormatReturn = 32;
1487
1488 LogFlowFunc(("cTargets=%u\n", cTargets + cFixedTargets));
1489
1490 return VINF_SUCCESS;
1491}
1492
1493/**
1494 * Helper for ShClX11RequestDataForX11Callback() that will cache the data returned.
1495 *
1496 * @returns VBox status code. VERR_NO_DATA if no data available.
1497 * @param pCtx The X11 clipboard context to use.
1498 * @param uFmt Clipboard format to read data in.
1499 * @param ppv Returns an allocated buffer with data read on success.
1500 * Needs to be free'd with RTMemFree() by the caller.
1501 * @param pcb Returns the amount of data read (in bytes) on success.
1502 */
1503static int shClX11RequestDataForX11CallbackHelper(PSHCLX11CTX pCtx, SHCLFORMAT uFmt,
1504 void **ppv, uint32_t *pcb)
1505{
1506 AssertPtrReturn(pCtx, VERR_INVALID_POINTER);
1507 AssertPtrReturn(ppv, VERR_INVALID_POINTER);
1508 AssertPtrReturn(pcb, VERR_INVALID_POINTER);
1509
1510 LogFlowFunc(("pCtx=%p, uFmt=%#x\n", pCtx, uFmt));
1511
1512 int rc = VINF_SUCCESS;
1513
1514 void *pv = NULL;
1515 uint32_t cb = 0;
1516
1517 if (uFmt == VBOX_SHCL_FMT_UNICODETEXT)
1518 {
1519 if (pCtx->pvUnicodeCache == NULL) /** @todo r=andy Using string cache here? */
1520 rc = pCtx->Callbacks.pfnOnRequestDataFromSource(pCtx->pFrontend, uFmt, &pCtx->pvUnicodeCache, &pCtx->cbUnicodeCache,
1521 NULL /* pvUser */);
1522 if ( RT_SUCCESS(rc)
1523 /* Catch misbehaving callbacks. */
1524 && pCtx->pvUnicodeCache
1525 && pCtx->cbUnicodeCache)
1526 {
1527 pv = RTMemDup(pCtx->pvUnicodeCache, pCtx->cbUnicodeCache);
1528 if (pv)
1529 cb = pCtx->cbUnicodeCache;
1530 else
1531 rc = VERR_NO_MEMORY;
1532 }
1533 }
1534 else
1535 rc = pCtx->Callbacks.pfnOnRequestDataFromSource(pCtx->pFrontend, uFmt, &pv, &cb, NULL /* pvUser */);
1536
1537
1538 /* Safey net in case the callbacks above misbehave
1539 * (must return VERR_NO_DATA if no data available). */
1540 if ( RT_SUCCESS(rc)
1541 && (pv == NULL || cb == 0))
1542 rc = VERR_NO_DATA;
1543
1544 if (RT_SUCCESS(rc))
1545 {
1546 *ppv = pv;
1547 *pcb = cb;
1548 }
1549
1550 LogFlowFunc(("Returning pv=%p, cb=%RU32, rc=%Rrc\n", pv, cb, rc));
1551 return rc;
1552}
1553
1554/**
1555 * Satisfies a request from X11 to convert the clipboard text to UTF-8 LF.
1556 *
1557 * @returns VBox status code. VERR_NO_DATA if no data was converted.
1558 * @param pDisplay An X11 display structure, needed for conversions
1559 * performed by Xlib.
1560 * @param pv The text to be converted (UCS-2 with Windows EOLs).
1561 * @param cb The length of the text in @cb in bytes.
1562 * @param atomTypeReturn Where to store the atom for the type of the data
1563 * we are returning.
1564 * @param pValReturn Where to store the pointer to the data we are
1565 * returning. This should be to memory allocated by
1566 * XtMalloc, which will be freed by the Xt toolkit
1567 * later.
1568 * @param pcLenReturn Where to store the length of the data we are
1569 * returning.
1570 * @param piFormatReturn Where to store the bit width (8, 16, 32) of the
1571 * data we are returning.
1572 */
1573static int clipConvertUtf16ToX11Data(Display *pDisplay, PRTUTF16 pwszSrc,
1574 size_t cbSrc, Atom *atomTarget,
1575 Atom *atomTypeReturn,
1576 XtPointer *pValReturn,
1577 unsigned long *pcLenReturn,
1578 int *piFormatReturn)
1579{
1580 RT_NOREF(pDisplay);
1581 AssertReturn(cbSrc % sizeof(RTUTF16) == 0, VERR_INVALID_PARAMETER);
1582
1583 const size_t cwcSrc = cbSrc / sizeof(RTUTF16);
1584 if (!cwcSrc)
1585 return VERR_NO_DATA;
1586
1587 /* This may slightly overestimate the space needed. */
1588 size_t chDst = 0;
1589 int rc = ShClUtf16LenUtf8(pwszSrc, cwcSrc, &chDst);
1590 if (RT_SUCCESS(rc))
1591 {
1592 chDst++; /* Add space for terminator. */
1593
1594 char *pszDst = (char *)XtMalloc(chDst);
1595 if (pszDst)
1596 {
1597 size_t cbActual = 0;
1598 rc = ShClConvUtf16CRLFToUtf8LF(pwszSrc, cwcSrc, pszDst, chDst, &cbActual);
1599 if (RT_SUCCESS(rc))
1600 {
1601 *atomTypeReturn = *atomTarget;
1602 *pValReturn = (XtPointer)pszDst;
1603 *pcLenReturn = cbActual + 1 /* Include terminator */;
1604 *piFormatReturn = 8;
1605 }
1606 }
1607 else
1608 rc = VERR_NO_MEMORY;
1609 }
1610
1611 LogFlowFuncLeaveRC(rc);
1612 return rc;
1613}
1614
1615/**
1616 * Satisfies a request from X11 to convert the clipboard HTML fragment to UTF-8. We
1617 * return null-terminated text, but can cope with non-null-terminated input.
1618 *
1619 * @returns VBox status code.
1620 * @param pDisplay An X11 display structure, needed for conversions
1621 * performed by Xlib.
1622 * @param pv The text to be converted (UTF8 with Windows EOLs).
1623 * @param cb The length of the text in @cb in bytes.
1624 * @param atomTypeReturn Where to store the atom for the type of the data
1625 * we are returning.
1626 * @param pValReturn Where to store the pointer to the data we are
1627 * returning. This should be to memory allocated by
1628 * XtMalloc, which will be freed by the Xt toolkit later.
1629 * @param pcLenReturn Where to store the length of the data we are returning.
1630 * @param piFormatReturn Where to store the bit width (8, 16, 32) of the
1631 * data we are returning.
1632 */
1633static int clipConvertHtmlToX11Data(Display *pDisplay, const char *pszSrc,
1634 size_t cbSrc, Atom *atomTarget,
1635 Atom *atomTypeReturn,
1636 XtPointer *pValReturn,
1637 unsigned long *pcLenReturn,
1638 int *piFormatReturn)
1639{
1640 RT_NOREF(pDisplay, pValReturn);
1641
1642 /* This may slightly overestimate the space needed. */
1643 LogFlowFunc(("Source: %s", pszSrc));
1644
1645 char *pszDest = (char *)XtMalloc(cbSrc);
1646 if (pszDest == NULL)
1647 return VERR_NO_MEMORY;
1648
1649 memcpy(pszDest, pszSrc, cbSrc);
1650
1651 *atomTypeReturn = *atomTarget;
1652 *pValReturn = (XtPointer)pszDest;
1653 *pcLenReturn = cbSrc;
1654 *piFormatReturn = 8;
1655
1656 return VINF_SUCCESS;
1657}
1658
1659
1660/**
1661 * Does this atom correspond to one of the two selection types we support?
1662 *
1663 * @param pCtx The X11 clipboard context to use.
1664 * @param selType The atom in question.
1665 */
1666static bool clipIsSupportedSelectionType(PSHCLX11CTX pCtx, Atom selType)
1667{
1668 return( (selType == clipGetAtom(pCtx, "CLIPBOARD"))
1669 || (selType == clipGetAtom(pCtx, "PRIMARY")));
1670}
1671
1672/**
1673 * Removes a trailing nul character from a string by adjusting the string
1674 * length. Some X11 applications don't like zero-terminated text...
1675 *
1676 * @param pText The text in question.
1677 * @param pcText The length of the text, adjusted on return.
1678 * @param format The format of the text.
1679 */
1680static void clipTrimTrailingNul(XtPointer pText, unsigned long *pcText,
1681 SHCLX11FMT format)
1682{
1683 AssertPtrReturnVoid(pText);
1684 AssertPtrReturnVoid(pcText);
1685 AssertReturnVoid((format == SHCLX11FMT_UTF8) || (format == SHCLX11FMT_TEXT) || (format == SHCLX11FMT_HTML));
1686
1687 if (((char *)pText)[*pcText - 1] == '\0')
1688 --(*pcText);
1689}
1690
1691static int clipConvertToX11Data(PSHCLX11CTX pCtx, Atom *atomTarget,
1692 Atom *atomTypeReturn,
1693 XtPointer *pValReturn,
1694 unsigned long *pcLenReturn,
1695 int *piFormatReturn)
1696{
1697 int rc = VERR_NOT_SUPPORTED; /* Play safe by default. */
1698
1699 SHCLX11FMTIDX idxFmtX11 = clipFindX11FormatByAtom(pCtx, *atomTarget);
1700 SHCLX11FMT fmtX11 = clipRealFormatForX11Format(idxFmtX11);
1701
1702 LogFlowFunc(("vboxFormats=0x%x, idxFmtX11=%u ('%s'), fmtX11=%u\n",
1703 pCtx->vboxFormats, idxFmtX11, g_aFormats[idxFmtX11].pcszAtom, fmtX11));
1704
1705 char *pszFmts = ShClFormatsToStrA(pCtx->vboxFormats);
1706 AssertPtrReturn(pszFmts, VERR_NO_MEMORY);
1707 LogRel2(("Shared Clipboard: Converting VBox formats '%s' to '%s' for X11\n",
1708 pszFmts, fmtX11 == SHCLX11FMT_INVALID ? "<invalid>" : g_aFormats[idxFmtX11].pcszAtom));
1709 RTStrFree(pszFmts);
1710
1711 void *pv = NULL;
1712 uint32_t cb = 0;
1713
1714 if ( ( (fmtX11 == SHCLX11FMT_UTF8)
1715 || (fmtX11 == SHCLX11FMT_TEXT)
1716 )
1717 && (pCtx->vboxFormats & VBOX_SHCL_FMT_UNICODETEXT))
1718 {
1719 rc = shClX11RequestDataForX11CallbackHelper(pCtx, VBOX_SHCL_FMT_UNICODETEXT, &pv, &cb);
1720 if ( RT_SUCCESS(rc)
1721 && ( (fmtX11 == SHCLX11FMT_UTF8)
1722 || (fmtX11 == SHCLX11FMT_TEXT)))
1723 {
1724 rc = clipConvertUtf16ToX11Data(XtDisplay(pCtx->pWidget),
1725 (PRTUTF16)pv, cb, atomTarget,
1726 atomTypeReturn, pValReturn,
1727 pcLenReturn, piFormatReturn);
1728 }
1729
1730 if (RT_SUCCESS(rc))
1731 clipTrimTrailingNul(*(XtPointer *)pValReturn, pcLenReturn, fmtX11);
1732
1733 RTMemFree(pv);
1734 }
1735 else if ( (fmtX11 == SHCLX11FMT_BMP)
1736 && (pCtx->vboxFormats & VBOX_SHCL_FMT_BITMAP))
1737 {
1738 rc = shClX11RequestDataForX11CallbackHelper(pCtx, VBOX_SHCL_FMT_BITMAP, &pv, &cb);
1739 if ( RT_SUCCESS(rc)
1740 && (fmtX11 == SHCLX11FMT_BMP))
1741 {
1742 /* Create a full BMP from it. */
1743 rc = ShClDibToBmp(pv, cb, (void **)pValReturn,
1744 (size_t *)pcLenReturn);
1745 }
1746
1747 if (RT_SUCCESS(rc))
1748 {
1749 *atomTypeReturn = *atomTarget;
1750 *piFormatReturn = 8;
1751 }
1752
1753 RTMemFree(pv);
1754 }
1755 else if ( (fmtX11 == SHCLX11FMT_HTML)
1756 && (pCtx->vboxFormats & VBOX_SHCL_FMT_HTML))
1757 {
1758 rc = shClX11RequestDataForX11CallbackHelper(pCtx, VBOX_SHCL_FMT_HTML, &pv, &cb);
1759 if (RT_SUCCESS(rc))
1760 {
1761 /**
1762 * The common VBox HTML encoding will be UTF-8.
1763 * Before sending it to the X11 clipboard we have to convert it to UTF-8 first.
1764 *
1765 * Strange that we get UTF-16 from the X11 clipboard, but
1766 * in same time we send UTF-8 to X11 clipboard and it works.
1767 ** @todo r=andy Verify this.
1768 */
1769 rc = clipConvertHtmlToX11Data(XtDisplay(pCtx->pWidget),
1770 (const char*)pv, cb, atomTarget,
1771 atomTypeReturn, pValReturn,
1772 pcLenReturn, piFormatReturn);
1773 if (RT_SUCCESS(rc))
1774 clipTrimTrailingNul(*(XtPointer *)pValReturn, pcLenReturn, fmtX11);
1775
1776 RTMemFree(pv);
1777 }
1778 }
1779#ifdef VBOX_WITH_SHARED_CLIPBOARD_TRANSFERS
1780 else if (fmtX11 == SHCLX11FMT_URI_LIST)
1781 {
1782 if (pCtx->vboxFormats & VBOX_SHCL_FMT_URI_LIST)
1783 {
1784 rc = shClX11RequestDataForX11CallbackHelper(pCtx, VBOX_SHCL_FMT_URI_LIST, &pv, &cb);
1785 if (RT_SUCCESS(rc))
1786 {
1787 void *pvDst = (void *)XtMalloc(cb);
1788 if (pvDst)
1789 {
1790 memcpy(pvDst, pv, cb);
1791
1792 *atomTypeReturn = *atomTarget;
1793 *pValReturn = (XtPointer)pvDst;
1794 *pcLenReturn = cb;
1795 *piFormatReturn = 8;
1796 }
1797 else
1798 rc = VERR_NO_MEMORY;
1799 }
1800 }
1801 /* else not supported yet. */
1802 }
1803#endif
1804 else
1805 {
1806 *atomTypeReturn = XT_CONVERT_FAIL;
1807 *pValReturn = (XtPointer)NULL;
1808 *pcLenReturn = 0;
1809 *piFormatReturn = 0;
1810 }
1811
1812 if (RT_FAILURE(rc))
1813 {
1814 char *pszFmts2 = ShClFormatsToStrA(pCtx->vboxFormats);
1815 char *pszAtomName = XGetAtomName(XtDisplay(pCtx->pWidget), *atomTarget);
1816
1817 LogRel(("Shared Clipboard: Converting VBox formats '%s' to '%s' for X11 (idxFmtX11=%u, fmtX11=%u, atomTarget='%s') failed, rc=%Rrc\n",
1818 pszFmts2 ? pszFmts2 : "unknown", g_aFormats[idxFmtX11].pcszAtom, idxFmtX11, fmtX11, pszAtomName ? pszAtomName : "unknown", rc));
1819
1820 if (pszFmts2)
1821 RTStrFree(pszFmts2);
1822 if (pszAtomName)
1823 XFree(pszAtomName);
1824 }
1825
1826 LogFlowFuncLeaveRC(rc);
1827 return rc;
1828}
1829
1830/**
1831 * Returns VBox's clipboard data for an X11 client.
1832 *
1833 * @note Callback for XtOwnSelection.
1834 */
1835static Boolean clipXtConvertSelectionProc(Widget widget, Atom *atomSelection,
1836 Atom *atomTarget,
1837 Atom *atomTypeReturn,
1838 XtPointer *pValReturn,
1839 unsigned long *pcLenReturn,
1840 int *piFormatReturn)
1841{
1842 LogFlowFuncEnter();
1843
1844 PSHCLX11CTX pCtx = clipLookupContext(widget);
1845 if (!pCtx)
1846 return False;
1847
1848 /* Is this the rigt selection (clipboard) we were asked for? */
1849 if (!clipIsSupportedSelectionType(pCtx, *atomSelection))
1850 return False;
1851
1852 int rc;
1853 if (*atomTarget == clipGetAtom(pCtx, "TARGETS"))
1854 rc = clipCreateX11Targets(pCtx, atomTypeReturn, pValReturn,
1855 pcLenReturn, piFormatReturn);
1856 else
1857 rc = clipConvertToX11Data(pCtx, atomTarget, atomTypeReturn,
1858 pValReturn, pcLenReturn, piFormatReturn);
1859
1860#if 0 /** @todo Disabled -- crashes when running with tstClipboardGH-X11. */
1861 XSelectionRequestEvent* pReq =
1862 XtGetSelectionRequest(widget, *atomSelection, (XtRequestId)NULL);
1863 LogFlowFunc(("returning pVBoxWnd=%#x, ownerWnd=%#x, reqWnd=%#x, %RTbool, rc=%Rrc\n",
1864 XtWindow(pCtx->pWidget), pReq->owner, pReq->requestor, RT_SUCCESS(rc), rc));
1865#endif
1866 return RT_SUCCESS(rc) ? True : False;
1867}
1868
1869static void clipXtConvertSelectionProcLose(Widget widget, Atom *atomSelection)
1870{
1871 RT_NOREF(widget, atomSelection);
1872 LogFlowFuncEnter();
1873}
1874
1875static void clipXtConvertSelectionProcDone(Widget widget, Atom *atomSelection, Atom *atomTarget)
1876{
1877 RT_NOREF(widget, atomSelection, atomTarget);
1878 LogFlowFuncEnter();
1879}
1880
1881/**
1882 * Structure used to pass information about formats that VBox supports.
1883 */
1884typedef struct _CLIPNEWVBOXFORMATS
1885{
1886 /** Context information for the X11 clipboard. */
1887 PSHCLX11CTX pCtx;
1888 /** Formats supported by VBox. */
1889 SHCLFORMATS Formats;
1890} CLIPNEWVBOXFORMATS, *PCLIPNEWVBOXFORMATS;
1891
1892
1893
1894/**
1895 * Invalidates the local cache of the data in the VBox clipboard.
1896 *
1897 * @param pCtx The X11 clipboard context to use.
1898 */
1899static void clipInvalidateClipboardCache(PSHCLX11CTX pCtx)
1900{
1901 if (pCtx->pvUnicodeCache != NULL)
1902 {
1903 RTMemFree(pCtx->pvUnicodeCache);
1904 pCtx->pvUnicodeCache = NULL;
1905 }
1906}
1907
1908/**
1909 * Takes possession of the X11 clipboard (and middle-button selection).
1910 *
1911 * @param pCtx The X11 clipboard context to use.
1912 * @param uFormats Clipboard formats to set.
1913 */
1914static void clipGrabX11Clipboard(PSHCLX11CTX pCtx, SHCLFORMATS uFormats)
1915{
1916 LogFlowFuncEnter();
1917
1918 /** @ŧodo r=andy The docs say: "the value CurrentTime is not acceptable" here!? */
1919 if (XtOwnSelection(pCtx->pWidget, clipGetAtom(pCtx, "CLIPBOARD"),
1920 CurrentTime,
1921 clipXtConvertSelectionProc, clipXtConvertSelectionProcLose, clipXtConvertSelectionProcDone))
1922 {
1923 pCtx->vboxFormats = uFormats;
1924
1925 /* Grab the middle-button paste selection too. */
1926 XtOwnSelection(pCtx->pWidget, clipGetAtom(pCtx, "PRIMARY"),
1927 CurrentTime, clipXtConvertSelectionProc, NULL, 0);
1928#ifndef TESTCASE
1929 /* Xt suppresses these if we already own the clipboard, so send them
1930 * ourselves. */
1931 XSetSelectionOwner(XtDisplay(pCtx->pWidget),
1932 clipGetAtom(pCtx, "CLIPBOARD"),
1933 XtWindow(pCtx->pWidget), CurrentTime);
1934 XSetSelectionOwner(XtDisplay(pCtx->pWidget),
1935 clipGetAtom(pCtx, "PRIMARY"),
1936 XtWindow(pCtx->pWidget), CurrentTime);
1937#endif
1938 }
1939}
1940
1941/**
1942 * Worker function for ShClX11ReportFormatsToX11 which runs on the
1943 * event thread.
1944 *
1945 * @param pvUserData Pointer to a CLIPNEWVBOXFORMATS structure containing
1946 * information about the VBox formats available and the
1947 * clipboard context data. Must be freed by the worker.
1948 */
1949static void ShClX11ReportFormatsToX11Worker(void *pvUserData, void * /* interval */)
1950{
1951 AssertPtrReturnVoid(pvUserData);
1952
1953 CLIPNEWVBOXFORMATS *pFormats = (CLIPNEWVBOXFORMATS *)pvUserData;
1954
1955 PSHCLX11CTX pCtx = pFormats->pCtx;
1956 SHCLFORMATS fFormats = pFormats->Formats;
1957
1958 RTMemFree(pFormats);
1959
1960#ifdef LOG_ENABLED
1961 char *pszFmts = ShClFormatsToStrA(fFormats);
1962 AssertPtrReturnVoid(pszFmts);
1963 LogRel2(("Shared Clipboard: Reported available VBox formats %s to X11\n", pszFmts));
1964 RTStrFree(pszFmts);
1965#endif
1966
1967 clipInvalidateClipboardCache(pCtx);
1968 clipGrabX11Clipboard(pCtx, fFormats);
1969 clipResetX11Formats(pCtx);
1970
1971 LogFlowFuncLeave();
1972}
1973
1974/**
1975 * Announces new clipboard formats to the X11 clipboard.
1976 *
1977 * @returns VBox status code.
1978 * @param pCtx Context data for the clipboard backend.
1979 * @param uFormats Clipboard formats offered.
1980 */
1981int ShClX11ReportFormatsToX11(PSHCLX11CTX pCtx, SHCLFORMATS uFormats)
1982{
1983 /*
1984 * Immediately return if we are not connected to the X server.
1985 */
1986 if (!pCtx->fHaveX11)
1987 return VINF_SUCCESS;
1988
1989 int rc;
1990
1991 /* This must be free'd by the worker callback. */
1992 PCLIPNEWVBOXFORMATS pFormats = (PCLIPNEWVBOXFORMATS)RTMemAlloc(sizeof(CLIPNEWVBOXFORMATS));
1993 if (pFormats)
1994 {
1995 pFormats->pCtx = pCtx;
1996 pFormats->Formats = uFormats;
1997 rc = clipThreadScheduleCall(pCtx, ShClX11ReportFormatsToX11Worker,
1998 (XtPointer)pFormats);
1999 if (RT_FAILURE(rc))
2000 RTMemFree(pFormats);
2001 }
2002 else
2003 rc = VERR_NO_MEMORY;
2004
2005 LogFlowFuncLeaveRC(rc);
2006 return rc;
2007}
2008
2009/**
2010 * Converts the data obtained from the X11 clipboard to the required format,
2011 * place it in the buffer supplied and signal that data has arrived.
2012 *
2013 * Converts the text obtained UTF-16LE with Windows EOLs.
2014 * Converts full BMP data to DIB format.
2015 */
2016SHCL_X11_DECL(void) clipConvertDataFromX11Worker(void *pClient, void *pvSrc, unsigned cbSrc)
2017{
2018 CLIPREADX11CBREQ *pReq = (CLIPREADX11CBREQ *)pClient;
2019 AssertPtrReturnVoid(pReq);
2020
2021 LogFlowFunc(("uFmtVBox=%#x, idxFmtX11=%u, pvSrc=%p, cbSrc=%u\n", pReq->uFmtVBox, pReq->idxFmtX11, pvSrc, cbSrc));
2022
2023 LogRel2(("Shared Clipboard: Converting X11 format '%s' to VBox format %#x\n",
2024 g_aFormats[pReq->idxFmtX11].pcszAtom, pReq->uFmtVBox));
2025
2026 AssertPtr(pReq->pCtx);
2027 Assert(pReq->uFmtVBox != VBOX_SHCL_FMT_NONE); /* Sanity. */
2028
2029 int rc = VINF_SUCCESS;
2030
2031 void *pvDst = NULL;
2032 size_t cbDst = 0;
2033
2034 PSHCLX11CTX pCtx = pReq->pCtx;
2035 AssertPtr(pReq->pCtx);
2036
2037#ifdef VBOX_WITH_SHARED_CLIPBOARD_XT_BUSY
2038 clipSetXtBusy(pCtx, false);
2039 if (clipGetXtNeedsUpdate(pCtx))
2040 clipQueryX11Targets(pCtx);
2041#endif
2042
2043 /* If X11 clipboard buffer has no data, libXt can pass to XtGetSelectionValue()
2044 * callback an empty string, in this case cbSrc is 0. */
2045 if (pvSrc == NULL || cbSrc == 0)
2046 {
2047 /* The clipboard selection may have changed before we could get it. */
2048 rc = VERR_NO_DATA;
2049 }
2050 else if (pReq->uFmtVBox == VBOX_SHCL_FMT_UNICODETEXT)
2051 {
2052 /* In which format is the clipboard data? */
2053 switch (clipRealFormatForX11Format(pReq->idxFmtX11))
2054 {
2055 case SHCLX11FMT_UTF8:
2056 RT_FALL_THROUGH();
2057 case SHCLX11FMT_TEXT:
2058 {
2059 size_t cwDst;
2060 /* If we are given broken UTF-8, we treat it as Latin1. */ /** @todo BUGBUG Is this acceptable? */
2061 if (RT_SUCCESS(RTStrValidateEncodingEx((char *)pvSrc, cbSrc, 0)))
2062 rc = ShClConvUtf8LFToUtf16CRLF((const char *)pvSrc, cbSrc,
2063 (PRTUTF16 *)&pvDst, &cwDst);
2064 else
2065 rc = ShClConvLatin1LFToUtf16CRLF((char *)pvSrc, cbSrc,
2066 (PRTUTF16 *)&pvDst, &cwDst);
2067 if (RT_SUCCESS(rc))
2068 {
2069 cwDst += 1 /* Include terminator */;
2070 cbDst = cwDst * sizeof(RTUTF16); /* Convert RTUTF16 units to bytes. */
2071 }
2072 break;
2073 }
2074
2075 default:
2076 {
2077 rc = VERR_INVALID_PARAMETER;
2078 break;
2079 }
2080 }
2081 }
2082 else if (pReq->uFmtVBox == VBOX_SHCL_FMT_BITMAP)
2083 {
2084 /* In which format is the clipboard data? */
2085 switch (clipRealFormatForX11Format(pReq->idxFmtX11))
2086 {
2087 case SHCLX11FMT_BMP:
2088 {
2089 const void *pDib;
2090 size_t cbDibSize;
2091 rc = ShClBmpGetDib((const void *)pvSrc, cbSrc,
2092 &pDib, &cbDibSize);
2093 if (RT_SUCCESS(rc))
2094 {
2095 pvDst = RTMemAlloc(cbDibSize);
2096 if (!pvDst)
2097 rc = VERR_NO_MEMORY;
2098 else
2099 {
2100 memcpy(pvDst, pDib, cbDibSize);
2101 cbDst = cbDibSize;
2102 }
2103 }
2104 break;
2105 }
2106
2107 default:
2108 {
2109 rc = VERR_INVALID_PARAMETER;
2110 break;
2111 }
2112 }
2113 }
2114 else if (pReq->uFmtVBox == VBOX_SHCL_FMT_HTML)
2115 {
2116 /* In which format is the clipboard data? */
2117 switch (clipRealFormatForX11Format(pReq->idxFmtX11))
2118 {
2119 case SHCLX11FMT_HTML:
2120 {
2121 /*
2122 * The common VBox HTML encoding will be - UTF-8
2123 * because it more general for HTML formats then UTF-16
2124 * X11 clipboard returns UTF-16, so before sending it we should
2125 * convert it to UTF-8.
2126 */
2127 pvDst = NULL;
2128 cbDst = 0;
2129
2130 /*
2131 * Some applications sends data in UTF-16, some in UTF-8,
2132 * without indication it in MIME.
2133 *
2134 * In case of UTF-16, at least [Open|Libre] Office adds an byte order mark (0xfeff)
2135 * at the start of the clipboard data.
2136 */
2137 if ( cbSrc >= sizeof(RTUTF16)
2138 && *(PRTUTF16)pvSrc == VBOX_SHCL_UTF16LEMARKER)
2139 {
2140 rc = ShClConvUtf16ToUtf8HTML((PRTUTF16)pvSrc, cbSrc / sizeof(RTUTF16), (char**)&pvDst, &cbDst);
2141 if (RT_SUCCESS(rc))
2142 {
2143 LogFlowFunc(("UTF-16 Unicode source (%u bytes):\n%ls\n\n", cbSrc, pvSrc));
2144 LogFlowFunc(("Byte Order Mark = %hx", ((PRTUTF16)pvSrc)[0]));
2145 LogFlowFunc(("UTF-8 Unicode dest (%u bytes):\n%s\n\n", cbDst, pvDst));
2146 }
2147 else
2148 LogRel(("Shared Clipboard: Converting UTF-16 Unicode failed with %Rrc\n", rc));
2149 }
2150 else /* Raw data. */
2151 {
2152 pvDst = RTMemAllocZ(cbSrc + 1 /* '\0' */);
2153 if(pvDst)
2154 {
2155 memcpy(pvDst, pvSrc, cbSrc);
2156 cbDst = cbSrc + 1 /* '\0' */;
2157 }
2158 else
2159 {
2160 rc = VERR_NO_MEMORY;
2161 break;
2162 }
2163 }
2164
2165 rc = VINF_SUCCESS;
2166 break;
2167 }
2168
2169 default:
2170 {
2171 rc = VERR_INVALID_PARAMETER;
2172 break;
2173 }
2174 }
2175 }
2176# ifdef VBOX_WITH_SHARED_CLIPBOARD_TRANSFERS
2177 else if (pReq->uFmtVBox == VBOX_SHCL_FMT_URI_LIST)
2178 {
2179 /* In which format is the clipboard data? */
2180 switch (clipRealFormatForX11Format(pReq->idxFmtX11))
2181 {
2182 case SHCLX11FMT_URI_LIST:
2183 {
2184 /* For URI lists we only accept valid UTF-8 encodings. */
2185 if (RT_SUCCESS(RTStrValidateEncodingEx((char *)pvSrc, cbSrc, 0)))
2186 {
2187 /* URI lists on X are strings separated with "\r\n". */
2188 RTCList<RTCString> lstRootEntries = RTCString((char *)pvSrc, cbSrc).split("\r\n");
2189 for (size_t i = 0; i < lstRootEntries.size(); ++i)
2190 {
2191 char *pszEntry = RTUriFilePath(lstRootEntries.at(i).c_str());
2192 AssertPtrBreakStmt(pszEntry, VERR_INVALID_PARAMETER);
2193
2194 rc = RTStrAAppend((char **)&pvDst, "http://localhost");
2195 AssertRCBreakStmt(rc, VERR_NO_MEMORY);
2196 cbDst += (uint32_t)strlen(pszEntry);
2197
2198
2199
2200 /** @todo BUGBUG Fix port! */
2201 /** @todo Add port + UUID (virtual path). */
2202
2203 rc = RTStrAAppend((char **)&pvDst, pszEntry);
2204 AssertRCBreakStmt(rc, VERR_NO_MEMORY);
2205 cbDst += (uint32_t)strlen(pszEntry);
2206
2207 LogFlowFunc(("URI list entry '%s'\n", (char *)pvDst));
2208
2209 rc = RTStrAAppend((char **)&pvDst, "\r\n");
2210 AssertRCBreakStmt(rc, VERR_NO_MEMORY);
2211 cbDst += (uint32_t)strlen("\r\n");
2212
2213 RTStrFree(pszEntry);
2214 }
2215
2216 if (cbDst)
2217 cbDst++; /* Include final (zero) termination. */
2218
2219 LogFlowFunc(("URI list: cbDst=%RU32\n", cbDst));
2220 }
2221 else
2222 rc = VERR_INVALID_PARAMETER;
2223 break;
2224 }
2225
2226 default:
2227 {
2228 rc = VERR_INVALID_PARAMETER;
2229 break;
2230 }
2231 }
2232 }
2233# endif /* VBOX_WITH_SHARED_CLIPBOARD_TRANSFERS */
2234 else
2235 rc = VERR_NOT_SUPPORTED;
2236
2237 if (RT_FAILURE(rc))
2238 LogRel(("Shared Clipboard: Converting X11 format '%s' (idxFmtX11=%u) to VBox format %#x failed, rc=%Rrc\n",
2239 g_aFormats[pReq->idxFmtX11].pcszAtom, pReq->idxFmtX11, pReq->uFmtVBox, rc));
2240
2241 SHCLX11READDATAREQ SendData;
2242 RT_ZERO(SendData);
2243 SendData.pReq = pReq->pReq;
2244 SendData.rcCompletion = rc;
2245
2246 pCtx->Callbacks.pfnOnSendDataToDest(pReq->pCtx->pFrontend, pvDst, cbDst, &SendData);
2247
2248 RTMemFree(pvDst);
2249 RTMemFree(pReq);
2250
2251 LogFlowFuncLeaveRC(rc);
2252}
2253
2254/**
2255 * Converts the data obtained from the X11 clipboard to the required format,
2256 * place it in the buffer supplied and signal that data has arrived.
2257 *
2258 * Converts the text obtained UTF-16LE with Windows EOLs.
2259 * Converts full BMP data to DIB format.
2260 */
2261SHCL_X11_DECL(void) clipConvertDataFromX11(Widget widget, XtPointer pClient,
2262 Atom * /* selection */, Atom *atomType,
2263 XtPointer pvSrc, long unsigned int *pcLen,
2264 int *piFormat)
2265{
2266 RT_NOREF(widget);
2267
2268 int rc = VINF_SUCCESS;
2269
2270 CLIPREADX11CBREQ *pReq = (CLIPREADX11CBREQ *)pClient;
2271 if (pReq) /* Give some more clues, if available. */
2272 {
2273 char *pszFmts = ShClFormatsToStrA(pReq->uFmtVBox);
2274 AssertPtrReturnVoid(pszFmts);
2275 AssertReturnVoid(pReq->idxFmtX11 <= SHCL_MAX_X11_FORMATS); /* Paranoia, should be checked already by the caller. */
2276 LogRel2(("Shared Clipboard: Converting X11 format '%s' -> VBox format(s) '%s'\n", g_aFormats[pReq->idxFmtX11].pcszAtom, pszFmts));
2277 RTStrFree(pszFmts);
2278 }
2279
2280 if (*atomType == XT_CONVERT_FAIL) /* Xt timeout */
2281 {
2282 LogRel(("Shared Clipboard: Reading clipboard data from X11 timed out\n"));
2283 rc = VERR_TIMEOUT;
2284 }
2285 else
2286 {
2287 if ( pReq
2288 && pReq->pCtx->Callbacks.pfnOnClipboardRead)
2289 {
2290 void *pvData = NULL;
2291 size_t cbData = 0;
2292 rc = pReq->pCtx->Callbacks.pfnOnClipboardRead(pReq->pCtx->pFrontend, pReq->uFmtVBox, &pvData, &cbData, NULL);
2293 if (RT_SUCCESS(rc))
2294 {
2295 /* Feed to conversion worker. */
2296 clipConvertDataFromX11Worker(pClient, pvData, cbData);
2297 RTMemFree(pvData);
2298 }
2299 }
2300 else /* Call with current data provided by X (default). */
2301 clipConvertDataFromX11Worker(pClient, pvSrc, (*pcLen) * (*piFormat) / 8);
2302 }
2303
2304 if (RT_FAILURE(rc))
2305 {
2306 LogRel(("Shared Clipboard: Reading clipboard data from X11 failed with %Rrc\n", rc));
2307 /* Make sure to complete the request in any case. */
2308 clipConvertDataFromX11Worker(pClient, NULL, 0);
2309 }
2310
2311 XtFree((char *)pvSrc);
2312}
2313
2314/**
2315 * Requests the current clipboard data from a specific selection.
2316 *
2317 * @returns VBox status code.
2318 * @param pCtx The X11 clipboard context to use.
2319 * @param pszWhere Clipboard selection to request the data from.
2320 * @param idxFmt The X11 format to request the data in.
2321 * @param pReq Where to store the requested data on success.
2322 */
2323static int clipGetSelectionValueEx(PSHCLX11CTX pCtx, const char *pszWhere, SHCLX11FMTIDX idxFmt,
2324 CLIPREADX11CBREQ *pReq)
2325{
2326 AssertPtrReturn(pszWhere, VERR_INVALID_POINTER);
2327 AssertReturn(idxFmt <= SHCL_MAX_X11_FORMATS, VERR_INVALID_PARAMETER);
2328 AssertReturn(clipIsSupportedSelectionType(pCtx, clipGetAtom(pCtx, pszWhere)), VERR_INVALID_PARAMETER);
2329 AssertPtrReturn(pReq, VERR_INVALID_POINTER);
2330
2331 LogRel2(("Shared Clipboard: Requesting X11 selection value in %s for format '%s'\n", pszWhere, g_aFormats[idxFmt].pcszAtom));
2332
2333#ifndef TESTCASE
2334 XtGetSelectionValue(pCtx->pWidget, clipGetAtom(pCtx, pszWhere),
2335 clipAtomForX11Format(pCtx, idxFmt),
2336 clipConvertDataFromX11,
2337 reinterpret_cast<XtPointer>(pReq),
2338 CurrentTime);
2339#else
2340 tstClipRequestData(pCtx, idxFmt, (void *)pReq);
2341#endif
2342
2343 return VINF_SUCCESS; /** @todo Return real rc. */
2344}
2345
2346/**
2347 * Requests the current clipboard data from the CLIPBOARD selection.
2348 *
2349 * @returns VBox status code.
2350 * @param pCtx The X11 clipboard context to use.
2351 * @param idxFmt The X11 format to request the data in.
2352 * @param pReq Where to store the requested data on success.
2353 *
2354 * @sa clipGetSelectionValueEx() for requesting data for a specific selection.
2355 */
2356static int clipGetSelectionValue(PSHCLX11CTX pCtx, SHCLX11FMTIDX idxFmt, CLIPREADX11CBREQ *pReq)
2357{
2358 return clipGetSelectionValueEx(pCtx, "CLIPBOARD", idxFmt, pReq);
2359}
2360
2361/**
2362 * Worker function for ShClX11ReadDataFromX11 which runs on the event thread.
2363 *
2364 * @param pvUserData Pointer to a CLIPREADX11CBREQ structure containing
2365 * information about the clipboard read request.
2366 * Must be free'd by the worker.
2367 */
2368static void ShClX11ReadDataFromX11Worker(void *pvUserData, void * /* interval */)
2369{
2370 AssertPtrReturnVoid(pvUserData);
2371
2372 CLIPREADX11CBREQ *pReq = (CLIPREADX11CBREQ *)pvUserData;
2373 SHCLX11CTX *pCtx = pReq->pCtx;
2374 AssertPtrReturnVoid(pCtx);
2375
2376 LogFlowFunc(("pReq->uFmtVBox=%#x, idxFmtX11=%#x\n", pReq->uFmtVBox, pReq->idxFmtX11));
2377
2378 int rc = VERR_NO_DATA; /* VBox thinks we have data and we don't. */
2379
2380#ifdef VBOX_WITH_SHARED_CLIPBOARD_XT_BUSY
2381 const bool fXtBusy = clipGetXtBusy(pCtx);
2382 clipSetXtBusy(pCtx, true);
2383 if (fXtBusy)
2384 {
2385 /* If the clipboard is busy just fend off the request. */
2386 rc = VERR_TRY_AGAIN;
2387 }
2388 else
2389#endif
2390 if (pReq->uFmtVBox & VBOX_SHCL_FMT_UNICODETEXT)
2391 {
2392 pReq->idxFmtX11 = pCtx->idxFmtText;
2393 if (pReq->idxFmtX11 != SHCLX11FMT_INVALID)
2394 {
2395 /* Send out a request for the data to the current clipboard owner. */
2396 rc = clipGetSelectionValue(pCtx, pCtx->idxFmtText, pReq);
2397 }
2398 }
2399 else if (pReq->uFmtVBox & VBOX_SHCL_FMT_BITMAP)
2400 {
2401 pReq->idxFmtX11 = pCtx->idxFmtBmp;
2402 if (pReq->idxFmtX11 != SHCLX11FMT_INVALID)
2403 {
2404 /* Send out a request for the data to the current clipboard owner. */
2405 rc = clipGetSelectionValue(pCtx, pCtx->idxFmtBmp, pReq);
2406 }
2407 }
2408 else if (pReq->uFmtVBox & VBOX_SHCL_FMT_HTML)
2409 {
2410 pReq->idxFmtX11 = pCtx->idxFmtHTML;
2411 if (pReq->idxFmtX11 != SHCLX11FMT_INVALID)
2412 {
2413 /* Send out a request for the data to the current clipboard owner. */
2414 rc = clipGetSelectionValue(pCtx, pCtx->idxFmtHTML, pReq);
2415 }
2416 }
2417#ifdef VBOX_WITH_SHARED_CLIPBOARD_TRANSFERS
2418 else if (pReq->uFmtVBox & VBOX_SHCL_FMT_URI_LIST)
2419 {
2420 pReq->idxFmtX11 = pCtx->idxFmtURI;
2421 if (pReq->idxFmtX11 != SHCLX11FMT_INVALID)
2422 {
2423 /* Send out a request for the data to the current clipboard owner. */
2424 rc = clipGetSelectionValue(pCtx, pCtx->idxFmtURI, pReq);
2425 }
2426 }
2427#endif
2428 else
2429 {
2430#ifdef VBOX_WITH_SHARED_CLIPBOARD_XT_BUSY
2431 clipSetXtBusy(pCtx, false);
2432#endif
2433 rc = VERR_NOT_IMPLEMENTED;
2434 }
2435
2436 if (RT_FAILURE(rc))
2437 {
2438 /* The clipboard callback was never scheduled, so we must signal
2439 * that the request processing is finished and clean up ourselves. */
2440 SHCLX11READDATAREQ SendData;
2441 RT_ZERO(SendData);
2442 SendData.pReq = pReq->pReq;
2443 SendData.rcCompletion = rc;
2444
2445 pCtx->Callbacks.pfnOnSendDataToDest(pReq->pCtx->pFrontend, NULL /* pv */ ,0 /* cb */, &SendData);
2446 RTMemFree(pReq);
2447 }
2448
2449 LogFlowFuncLeaveRC(rc);
2450}
2451
2452/**
2453 * Called when VBox wants to read the X11 clipboard.
2454 *
2455 * @returns VBox status code.
2456 * @retval VERR_NO_DATA if format is supported but no data is available currently.
2457 * @retval VERR_NOT_IMPLEMENTED if the format is not implemented.
2458 * @param pCtx Context data for the clipboard backend.
2459 * @param uFmt The format that the VBox would like to receive the data in.
2460 * @param pReq Read callback request to use. Will be free'd in the callback on success.
2461 * Otherwise the caller has to free this again on error.
2462 *
2463 * @note We allocate a request structure which must be freed by the worker.
2464 */
2465int ShClX11ReadDataFromX11(PSHCLX11CTX pCtx, SHCLFORMAT uFmt, CLIPREADCBREQ *pReq)
2466{
2467 AssertPtrReturn(pReq, VERR_INVALID_POINTER);
2468 /*
2469 * Immediately return if we are not connected to the X server.
2470 */
2471 if (!pCtx->fHaveX11)
2472 return VERR_NO_DATA;
2473
2474 int rc = VINF_SUCCESS;
2475
2476 CLIPREADX11CBREQ *pX11Req = (CLIPREADX11CBREQ *)RTMemAllocZ(sizeof(CLIPREADX11CBREQ));
2477 if (pX11Req)
2478 {
2479 pX11Req->pCtx = pCtx;
2480 pX11Req->uFmtVBox = uFmt;
2481 pX11Req->pReq = pReq;
2482
2483 /* We use this to schedule a worker function on the event thread. */
2484 rc = clipThreadScheduleCall(pCtx, ShClX11ReadDataFromX11Worker, (XtPointer)pX11Req);
2485 if (RT_FAILURE(rc))
2486 RTMemFree(pX11Req);
2487 }
2488 else
2489 rc = VERR_NO_MEMORY;
2490
2491 LogFlowFuncLeaveRC(rc);
2492 return rc;
2493}
注意: 瀏覽 TracBrowser 來幫助您使用儲存庫瀏覽器

© 2025 Oracle Support Privacy / Do Not Sell My Info Terms of Use Trademark Policy Automated Access Etiquette