VirtualBox

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

Last change on this file since 100307 was 100256, checked in by vboxsync, 21 months ago

Shared Clipboard: Logging tweaks. bugref:9437

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