VirtualBox

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

Last change on this file since 102464 was 102464, checked in by vboxsync, 12 months ago

Shared Clipboard: If something in ShClX11ReadDataFromX11Worker() fails, make sure to let the waiters know. bugref:10384

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 89.8 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 Log((", idxFmtURI=%u ('%s')", pCtx->idxFmtURI, g_aFormats[pCtx->idxFmtURI].pcszAtom));
443#endif
444 Log((" -> 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 /* Init clipboard cache. */
1213 ShClCacheInit(&pCtx->Cache);
1214
1215 /* Install given callbacks. */
1216 shClX11SetCallbacksInternal(pCtx, pCallbacks);
1217
1218 pCtx->fHaveX11 = !fHeadless;
1219 pCtx->pFrontend = pParent;
1220
1221#ifdef VBOX_WITH_SHARED_CLIPBOARD_XT_BUSY
1222 pCtx->fXtBusy = false;
1223 pCtx->fXtNeedsUpdate = false;
1224#endif
1225
1226#ifdef VBOX_WITH_SHARED_CLIPBOARD_TRANSFERS_HTTP
1227 ShClTransferHttpServerInit(&pCtx->HttpCtx.HttpServer);
1228#endif
1229
1230#ifdef TESTCASE
1231 if (RT_SUCCESS(rc))
1232 {
1233 /** @todo The testcases currently do not utilize the threading code. So init stuff here. */
1234 rc = clipInitInternal(pCtx);
1235 if (RT_SUCCESS(rc))
1236 rc = clipRegisterContext(pCtx);
1237 }
1238#endif
1239
1240 LogFlowFuncLeaveRC(rc);
1241 return rc;
1242}
1243
1244/**
1245 * Destroys a Shared Clipboard X11 context.
1246 *
1247 * @param pCtx The X11 clipboard context to destroy.
1248 */
1249void ShClX11Destroy(PSHCLX11CTX pCtx)
1250{
1251 if (!pCtx)
1252 return;
1253
1254 LogFlowFunc(("pCtx=%p\n", pCtx));
1255
1256 /* Destroy clipboard cache. */
1257 ShClCacheDestroy(&pCtx->Cache);
1258
1259#ifdef VBOX_WITH_SHARED_CLIPBOARD_TRANSFERS_HTTP
1260 ShClTransferHttpServerDestroy(&pCtx->HttpCtx.HttpServer);
1261#endif
1262
1263#ifdef TESTCASE
1264 /** @todo The testcases currently do not utilize the threading code. So uninit stuff here. */
1265 clipUnregisterContext(pCtx);
1266 clipUninitInternal(pCtx);
1267#endif
1268
1269 if (pCtx->fHaveX11)
1270 {
1271 /* We set this to NULL when the event thread exits. It really should
1272 * have exited at this point, when we are about to unload the code from
1273 * memory. */
1274 Assert(pCtx->pWidget == NULL);
1275 }
1276}
1277
1278#ifndef TESTCASE
1279/**
1280 * Starts our own Xt even thread for handling Shared Clipboard messages, extended version.
1281 *
1282 * @returns VBox status code.
1283 * @param pCtx The X11 clipboard context to use.
1284 * @param pszName Thread name to use.
1285 * @param fGrab Whether we should try to grab the shared clipboard at once.
1286 */
1287int ShClX11ThreadStartEx(PSHCLX11CTX pCtx, const char *pszName, bool fGrab)
1288{
1289 AssertPtrReturn(pCtx, VERR_INVALID_POINTER);
1290
1291 /*
1292 * Immediately return if we are not connected to the X server.
1293 */
1294 if (!pCtx->fHaveX11)
1295 return VINF_SUCCESS;
1296
1297 pCtx->fGrabClipboardOnStart = fGrab;
1298
1299 clipResetX11Formats(pCtx);
1300
1301 int rc;
1302
1303 /*
1304 * Create the pipes.
1305 ** @todo r=andy Replace this with RTPipe API.
1306 */
1307 int pipes[2];
1308 if (!pipe(pipes))
1309 {
1310 pCtx->wakeupPipeRead = pipes[0];
1311 pCtx->wakeupPipeWrite = pipes[1];
1312
1313 if (!fcntl(pCtx->wakeupPipeRead, F_SETFL, O_NONBLOCK))
1314 {
1315 rc = VINF_SUCCESS;
1316 }
1317 else
1318 rc = RTErrConvertFromErrno(errno);
1319 }
1320 else
1321 rc = RTErrConvertFromErrno(errno);
1322
1323 if (RT_SUCCESS(rc))
1324 {
1325 LogRel2(("Shared Clipboard: Starting X11 event thread ...\n"));
1326
1327 rc = RTThreadCreate(&pCtx->Thread, clipThreadMain, pCtx, 0,
1328 RTTHREADTYPE_IO, RTTHREADFLAGS_WAITABLE, pszName);
1329 if (RT_SUCCESS(rc))
1330 rc = RTThreadUserWait(pCtx->Thread, RT_MS_30SEC /* msTimeout */);
1331
1332 if (RT_FAILURE(rc))
1333 {
1334 LogRel(("Shared Clipboard: Failed to start the X11 event thread with %Rrc\n", rc));
1335 clipUninitInternal(pCtx);
1336 }
1337 else
1338 {
1339 if (!pCtx->fThreadStarted)
1340 {
1341 LogRel(("Shared Clipboard: X11 event thread reported an error while starting\n"));
1342 }
1343 else
1344 LogRel2(("Shared Clipboard: X11 event thread started\n"));
1345 }
1346 }
1347
1348 LogFlowFuncLeaveRC(rc);
1349 return rc;
1350}
1351
1352/**
1353 * Starts our own Xt even thread for handling Shared Clipboard messages.
1354 *
1355 * @returns VBox status code.
1356 * @param pCtx The X11 clipboard context to use.
1357 * @param fGrab Whether we should try to grab the shared clipboard at once.
1358 */
1359int ShClX11ThreadStart(PSHCLX11CTX pCtx, bool fGrab)
1360{
1361 return ShClX11ThreadStartEx(pCtx, "SHCLX11", fGrab);
1362}
1363
1364/**
1365 * Stops the Shared Clipboard Xt even thread.
1366 *
1367 * @note Any requests from this object to get clipboard data from VBox
1368 * *must* have completed or aborted before we are called, as
1369 * otherwise the X11 event loop will still be waiting for the request
1370 * to return and will not be able to terminate.
1371 *
1372 * @returns VBox status code.
1373 * @param pCtx The X11 clipboard context to use.
1374 */
1375int ShClX11ThreadStop(PSHCLX11CTX pCtx)
1376{
1377 int rc;
1378 /*
1379 * Immediately return if we are not connected to the X server.
1380 */
1381 if (!pCtx->fHaveX11)
1382 return VINF_SUCCESS;
1383
1384 LogRel2(("Shared Clipboard: Signalling the X11 event thread to stop\n"));
1385
1386 /* Write to the "stop" pipe. */
1387 rc = clipThreadScheduleCall(pCtx, clipThreadSignalStop, (XtPointer)pCtx);
1388 if (RT_FAILURE(rc))
1389 {
1390 LogRel(("Shared Clipboard: cannot notify X11 event thread on shutdown with %Rrc\n", rc));
1391 return rc;
1392 }
1393
1394 LogRel2(("Shared Clipboard: Waiting for X11 event thread to stop ...\n"));
1395
1396 int rcThread;
1397 rc = RTThreadWait(pCtx->Thread, RT_MS_30SEC /* msTimeout */, &rcThread);
1398 if (RT_SUCCESS(rc))
1399 rc = rcThread;
1400 if (RT_SUCCESS(rc))
1401 {
1402 if (pCtx->wakeupPipeRead != 0)
1403 {
1404 close(pCtx->wakeupPipeRead);
1405 pCtx->wakeupPipeRead = 0;
1406 }
1407
1408 if (pCtx->wakeupPipeWrite != 0)
1409 {
1410 close(pCtx->wakeupPipeWrite);
1411 pCtx->wakeupPipeWrite = 0;
1412 }
1413 }
1414
1415 if (RT_SUCCESS(rc))
1416 {
1417 LogRel2(("Shared Clipboard: X11 event thread stopped successfully\n"));
1418 }
1419 else
1420 LogRel(("Shared Clipboard: Stopping X11 event thread failed with %Rrc\n", rc));
1421
1422 LogFlowFuncLeaveRC(rc);
1423 return rc;
1424}
1425#endif /* !TESTCASE */
1426
1427/**
1428 * Returns the targets supported by VBox.
1429 *
1430 * This will return a list of atoms which tells the caller
1431 * what kind of clipboard formats we support.
1432 *
1433 * @returns VBox status code.
1434 * @param pCtx The X11 clipboard context to use.
1435 * @param atomTypeReturn The type of the data we are returning.
1436 * @param pValReturn A pointer to the data we are returning. This
1437 * should be set to memory allocated by XtMalloc,
1438 * which will be freed later by the Xt toolkit.
1439 * @param pcLenReturn The length of the data we are returning.
1440 * @param piFormatReturn The format (8bit, 16bit, 32bit) of the data we are
1441 * returning.
1442 * @note X11 backend code, called by the XtOwnSelection callback.
1443 */
1444static int clipCreateX11Targets(PSHCLX11CTX pCtx, Atom *atomTypeReturn,
1445 XtPointer *pValReturn,
1446 unsigned long *pcLenReturn,
1447 int *piFormatReturn)
1448{
1449 const unsigned cFixedTargets = 3; /* See below. */
1450
1451 Atom *pAtomTargets = (Atom *)XtMalloc((SHCL_MAX_X11_FORMATS + cFixedTargets) * sizeof(Atom));
1452 if (!pAtomTargets)
1453 return VERR_NO_MEMORY;
1454
1455 unsigned cTargets = 0;
1456 SHCLX11FMTIDX idxFmt = NIL_CLIPX11FORMAT;
1457 do
1458 {
1459 idxFmt = clipEnumX11Formats(pCtx->vboxFormats, idxFmt);
1460 if (idxFmt != NIL_CLIPX11FORMAT)
1461 {
1462 pAtomTargets[cTargets] = clipAtomForX11Format(pCtx, idxFmt);
1463 ++cTargets;
1464 }
1465 } while (idxFmt != NIL_CLIPX11FORMAT);
1466
1467 /* We always offer these fixed targets. */
1468 pAtomTargets[cTargets] = clipGetAtom(pCtx, "TARGETS");
1469 pAtomTargets[cTargets + 1] = clipGetAtom(pCtx, "MULTIPLE");
1470 pAtomTargets[cTargets + 2] = clipGetAtom(pCtx, "TIMESTAMP");
1471
1472 *atomTypeReturn = XA_ATOM;
1473 *pValReturn = (XtPointer)pAtomTargets;
1474 *pcLenReturn = cTargets + cFixedTargets;
1475 *piFormatReturn = 32;
1476
1477 LogFlowFunc(("cTargets=%u\n", cTargets + cFixedTargets));
1478
1479 return VINF_SUCCESS;
1480}
1481
1482/**
1483 * Helper for ShClX11RequestDataForX11Callback() that will cache the data returned.
1484 *
1485 * @returns VBox status code. VERR_NO_DATA if no data available.
1486 * @param pCtx The X11 clipboard context to use.
1487 * @param uFmt Clipboard format to read data in.
1488 * @param ppv Returns an allocated buffer with data read on success.
1489 * Needs to be free'd with RTMemFree() by the caller.
1490 * @param pcb Returns the amount of data read (in bytes) on success.
1491 *
1492 * @thread X11 event thread.
1493 */
1494static int shClX11RequestDataForX11CallbackHelper(PSHCLX11CTX pCtx, SHCLFORMAT uFmt,
1495 void **ppv, uint32_t *pcb)
1496{
1497 AssertPtrReturn(pCtx, VERR_INVALID_POINTER);
1498 AssertPtrReturn(ppv, VERR_INVALID_POINTER);
1499 AssertPtrReturn(pcb, VERR_INVALID_POINTER);
1500
1501 AssertPtrReturn(pCtx->Callbacks.pfnOnRequestDataFromSource, VERR_INVALID_POINTER);
1502
1503#ifdef LOG_ENABLED
1504 char *pszFmts = ShClFormatsToStrA(uFmt);
1505 AssertPtrReturn(pszFmts, VERR_NO_MEMORY);
1506 LogRel2(("Shared Clipboard: Requesting data for X11 from source as '%s'\n", pszFmts));
1507 RTStrFree(pszFmts);
1508#endif
1509
1510 int rc = VINF_SUCCESS;
1511
1512 void *pv = NULL;
1513 uint32_t cb = 0;
1514
1515 PSHCLCACHEENTRY pCacheEntry = ShClCacheGet(&pCtx->Cache, uFmt);
1516 if (!pCacheEntry) /* Cache miss */
1517 {
1518 rc = pCtx->Callbacks.pfnOnRequestDataFromSource(pCtx->pFrontend, uFmt, &pv, &cb,
1519 NULL /* pvUser */);
1520 if (RT_SUCCESS(rc))
1521 rc = ShClCacheSet(&pCtx->Cache, uFmt, pv, cb);
1522 }
1523 else /* Cache hit */
1524 {
1525 void *pvCache = NULL;
1526 size_t cbCache = 0;
1527 ShClCacheEntryGet(pCacheEntry, &pvCache, &cbCache);
1528 if ( pvCache
1529 && cbCache)
1530 {
1531 pv = RTMemDup(pvCache, cbCache);
1532 if (pv)
1533 {
1534 cb = cbCache;
1535 }
1536 else
1537 rc = VERR_NO_MEMORY;
1538 }
1539 }
1540
1541 LogFlowFunc(("pCtx=%p, uFmt=%#x -> Cache %s\n", pCtx, uFmt, pCacheEntry ? "HIT" : "MISS"));
1542
1543 /* Safey net in case the stuff above misbehaves
1544 * (must return VERR_NO_DATA if no data available). */
1545 if ( RT_SUCCESS(rc)
1546 && (pv == NULL || cb == 0))
1547 rc = VERR_NO_DATA;
1548
1549 if (RT_SUCCESS(rc))
1550 {
1551 *ppv = pv;
1552 *pcb = cb;
1553 }
1554
1555 if (RT_FAILURE(rc))
1556 LogRel(("Shared Clipboard: Requesting data for X11 from source failed with %Rrc\n", rc));
1557
1558 LogFlowFunc(("Returning pv=%p, cb=%RU32, rc=%Rrc\n", pv, cb, rc));
1559 return rc;
1560}
1561
1562/**
1563 * Satisfies a request from X11 to convert the clipboard text to UTF-8 LF.
1564 *
1565 * @returns VBox status code. VERR_NO_DATA if no data was converted.
1566 * @param pDisplay An X11 display structure, needed for conversions
1567 * performed by Xlib.
1568 * @param pv The text to be converted (UCS-2 with Windows EOLs).
1569 * @param cb The length of the text in @cb in bytes.
1570 * @param atomTypeReturn Where to store the atom for the type of the data
1571 * we are returning.
1572 * @param pValReturn Where to store the pointer to the data we are
1573 * returning. This should be to memory allocated by
1574 * XtMalloc, which will be freed by the Xt toolkit
1575 * later.
1576 * @param pcLenReturn Where to store the length of the data we are
1577 * returning.
1578 * @param piFormatReturn Where to store the bit width (8, 16, 32) of the
1579 * data we are returning.
1580 */
1581static int clipConvertUtf16ToX11Data(Display *pDisplay, PRTUTF16 pwszSrc,
1582 size_t cbSrc, Atom *atomTarget,
1583 Atom *atomTypeReturn,
1584 XtPointer *pValReturn,
1585 unsigned long *pcLenReturn,
1586 int *piFormatReturn)
1587{
1588 RT_NOREF(pDisplay);
1589 AssertReturn(cbSrc % sizeof(RTUTF16) == 0, VERR_INVALID_PARAMETER);
1590
1591 const size_t cwcSrc = cbSrc / sizeof(RTUTF16);
1592 if (!cwcSrc)
1593 return VERR_NO_DATA;
1594
1595 /* This may slightly overestimate the space needed. */
1596 size_t chDst = 0;
1597 int rc = ShClUtf16LenUtf8(pwszSrc, cwcSrc, &chDst);
1598 if (RT_SUCCESS(rc))
1599 {
1600 chDst++; /* Add space for terminator. */
1601
1602 char *pszDst = (char *)XtMalloc(chDst);
1603 if (pszDst)
1604 {
1605 size_t cbActual = 0;
1606 rc = ShClConvUtf16CRLFToUtf8LF(pwszSrc, cwcSrc, pszDst, chDst, &cbActual);
1607 if (RT_SUCCESS(rc))
1608 {
1609 *atomTypeReturn = *atomTarget;
1610 *pValReturn = (XtPointer)pszDst;
1611 *pcLenReturn = cbActual + 1 /* Include terminator */;
1612 *piFormatReturn = 8;
1613 }
1614 }
1615 else
1616 rc = VERR_NO_MEMORY;
1617 }
1618
1619 LogFlowFuncLeaveRC(rc);
1620 return rc;
1621}
1622
1623/**
1624 * Satisfies a request from X11 to convert the clipboard HTML fragment to UTF-8. We
1625 * return null-terminated text, but can cope with non-null-terminated input.
1626 *
1627 * @returns VBox status code.
1628 * @param pDisplay An X11 display structure, needed for conversions
1629 * performed by Xlib.
1630 * @param pv The text to be converted (UTF8 with Windows EOLs).
1631 * @param cb The length of the text in @cb in bytes.
1632 * @param atomTypeReturn Where to store the atom for the type of the data
1633 * we are returning.
1634 * @param pValReturn Where to store the pointer to the data we are
1635 * returning. This should be to memory allocated by
1636 * XtMalloc, which will be freed by the Xt toolkit later.
1637 * @param pcLenReturn Where to store the length of the data we are returning.
1638 * @param piFormatReturn Where to store the bit width (8, 16, 32) of the
1639 * data we are returning.
1640 */
1641static int clipConvertHtmlToX11Data(Display *pDisplay, const char *pszSrc,
1642 size_t cbSrc, Atom *atomTarget,
1643 Atom *atomTypeReturn,
1644 XtPointer *pValReturn,
1645 unsigned long *pcLenReturn,
1646 int *piFormatReturn)
1647{
1648 RT_NOREF(pDisplay, pValReturn);
1649
1650 /* This may slightly overestimate the space needed. */
1651 LogFlowFunc(("Source: %s", pszSrc));
1652
1653 char *pszDest = (char *)XtMalloc(cbSrc);
1654 if (pszDest == NULL)
1655 return VERR_NO_MEMORY;
1656
1657 memcpy(pszDest, pszSrc, cbSrc);
1658
1659 *atomTypeReturn = *atomTarget;
1660 *pValReturn = (XtPointer)pszDest;
1661 *pcLenReturn = cbSrc;
1662 *piFormatReturn = 8;
1663
1664 return VINF_SUCCESS;
1665}
1666
1667
1668/**
1669 * Does this atom correspond to one of the two selection types we support?
1670 *
1671 * @param pCtx The X11 clipboard context to use.
1672 * @param selType The atom in question.
1673 */
1674static bool clipIsSupportedSelectionType(PSHCLX11CTX pCtx, Atom selType)
1675{
1676 return( (selType == clipGetAtom(pCtx, "CLIPBOARD"))
1677 || (selType == clipGetAtom(pCtx, "PRIMARY")));
1678}
1679
1680/**
1681 * Removes a trailing nul character from a string by adjusting the string
1682 * length. Some X11 applications don't like zero-terminated text...
1683 *
1684 * @param pText The text in question.
1685 * @param pcText The length of the text, adjusted on return.
1686 * @param format The format of the text.
1687 */
1688static void clipTrimTrailingNul(XtPointer pText, unsigned long *pcText,
1689 SHCLX11FMT format)
1690{
1691 AssertPtrReturnVoid(pText);
1692 AssertPtrReturnVoid(pcText);
1693 AssertReturnVoid((format == SHCLX11FMT_UTF8) || (format == SHCLX11FMT_TEXT) || (format == SHCLX11FMT_HTML));
1694
1695 if (((char *)pText)[*pcText - 1] == '\0')
1696 --(*pcText);
1697}
1698
1699static int clipConvertToX11Data(PSHCLX11CTX pCtx, Atom *atomTarget,
1700 Atom *atomTypeReturn,
1701 XtPointer *pValReturn,
1702 unsigned long *pcLenReturn,
1703 int *piFormatReturn)
1704{
1705 int rc = VERR_NOT_SUPPORTED; /* Play safe by default. */
1706
1707 SHCLX11FMTIDX idxFmtX11 = clipFindX11FormatByAtom(pCtx, *atomTarget);
1708 SHCLX11FMT fmtX11 = clipRealFormatForX11Format(idxFmtX11);
1709
1710 LogFlowFunc(("vboxFormats=0x%x, idxFmtX11=%u ('%s'), fmtX11=%u\n",
1711 pCtx->vboxFormats, idxFmtX11, g_aFormats[idxFmtX11].pcszAtom, fmtX11));
1712
1713 char *pszFmts = ShClFormatsToStrA(pCtx->vboxFormats);
1714 AssertPtrReturn(pszFmts, VERR_NO_MEMORY);
1715 LogRel2(("Shared Clipboard: Converting VBox formats '%s' to '%s' for X11\n",
1716 pszFmts, fmtX11 == SHCLX11FMT_INVALID ? "<invalid>" : g_aFormats[idxFmtX11].pcszAtom));
1717 RTStrFree(pszFmts);
1718
1719 void *pv = NULL;
1720 uint32_t cb = 0;
1721
1722 if ( ( (fmtX11 == SHCLX11FMT_UTF8)
1723 || (fmtX11 == SHCLX11FMT_TEXT)
1724 )
1725 && (pCtx->vboxFormats & VBOX_SHCL_FMT_UNICODETEXT))
1726 {
1727 rc = shClX11RequestDataForX11CallbackHelper(pCtx, VBOX_SHCL_FMT_UNICODETEXT, &pv, &cb);
1728 if ( RT_SUCCESS(rc)
1729 && ( (fmtX11 == SHCLX11FMT_UTF8)
1730 || (fmtX11 == SHCLX11FMT_TEXT)))
1731 {
1732 rc = clipConvertUtf16ToX11Data(XtDisplay(pCtx->pWidget),
1733 (PRTUTF16)pv, cb, atomTarget,
1734 atomTypeReturn, pValReturn,
1735 pcLenReturn, piFormatReturn);
1736 }
1737
1738 if (RT_SUCCESS(rc))
1739 clipTrimTrailingNul(*(XtPointer *)pValReturn, pcLenReturn, fmtX11);
1740
1741 RTMemFree(pv);
1742 }
1743 else if ( (fmtX11 == SHCLX11FMT_BMP)
1744 && (pCtx->vboxFormats & VBOX_SHCL_FMT_BITMAP))
1745 {
1746 rc = shClX11RequestDataForX11CallbackHelper(pCtx, VBOX_SHCL_FMT_BITMAP, &pv, &cb);
1747 if ( RT_SUCCESS(rc)
1748 && (fmtX11 == SHCLX11FMT_BMP))
1749 {
1750 /* Create a full BMP from it. */
1751 rc = ShClDibToBmp(pv, cb, (void **)pValReturn,
1752 (size_t *)pcLenReturn);
1753 }
1754
1755 if (RT_SUCCESS(rc))
1756 {
1757 *atomTypeReturn = *atomTarget;
1758 *piFormatReturn = 8;
1759 }
1760
1761 RTMemFree(pv);
1762 }
1763 else if ( (fmtX11 == SHCLX11FMT_HTML)
1764 && (pCtx->vboxFormats & VBOX_SHCL_FMT_HTML))
1765 {
1766 rc = shClX11RequestDataForX11CallbackHelper(pCtx, VBOX_SHCL_FMT_HTML, &pv, &cb);
1767 if (RT_SUCCESS(rc))
1768 {
1769 /**
1770 * The common VBox HTML encoding will be UTF-8.
1771 * Before sending it to the X11 clipboard we have to convert it to UTF-8 first.
1772 *
1773 * Strange that we get UTF-16 from the X11 clipboard, but
1774 * in same time we send UTF-8 to X11 clipboard and it works.
1775 ** @todo r=andy Verify this.
1776 */
1777 rc = clipConvertHtmlToX11Data(XtDisplay(pCtx->pWidget),
1778 (const char*)pv, cb, atomTarget,
1779 atomTypeReturn, pValReturn,
1780 pcLenReturn, piFormatReturn);
1781 if (RT_SUCCESS(rc))
1782 clipTrimTrailingNul(*(XtPointer *)pValReturn, pcLenReturn, fmtX11);
1783
1784 RTMemFree(pv);
1785 }
1786 }
1787#ifdef VBOX_WITH_SHARED_CLIPBOARD_TRANSFERS
1788 else if ( fmtX11 == SHCLX11FMT_URI_LIST
1789 || fmtX11 == SHCLX11FMT_URI_LIST_GNOME_COPIED_FILES
1790 /** @todo BUGBUG Not sure about the following ones; test those. */
1791 || fmtX11 == SHCLX11FMT_URI_LIST_MATE_COPIED_FILES
1792 || fmtX11 == SHCLX11FMT_URI_LIST_NAUTILUS_CLIPBOARD
1793 || fmtX11 == SHCLX11FMT_URI_LIST_KDE_CUTSELECTION)
1794 {
1795 if (pCtx->vboxFormats & VBOX_SHCL_FMT_URI_LIST)
1796 {
1797 rc = shClX11RequestDataForX11CallbackHelper(pCtx, VBOX_SHCL_FMT_URI_LIST, &pv, &cb);
1798 if (RT_SUCCESS(rc))
1799 {
1800 if ( fmtX11 == SHCLX11FMT_URI_LIST_GNOME_COPIED_FILES
1801 /** @todo BUGBUG Ditto, see above. */
1802 || fmtX11 == SHCLX11FMT_URI_LIST_MATE_COPIED_FILES
1803 || fmtX11 == SHCLX11FMT_URI_LIST_NAUTILUS_CLIPBOARD
1804 || fmtX11 == SHCLX11FMT_URI_LIST_KDE_CUTSELECTION)
1805 {
1806 /* Note: There must be *no* final new line ('\n') at the end, otherwise Nautilus will crash! */
1807 char *pszData = NULL;
1808
1809 RTStrAPrintf(&pszData, "copy\n%s", (const char *)pv);
1810
1811 cb = strlen(pszData);
1812 pv = pszData;
1813 }
1814 else /* SHCLX11FMT_URI_LIST -> String only, w/o any special formatting. */
1815 {
1816 char *pszData = NULL;
1817 RTStrAPrintf(&pszData, "%s\n", (const char *)pv);
1818
1819 cb = strlen(pszData) + 1;
1820 pv = pszData;
1821 }
1822
1823# ifdef DEBUG_andy
1824 LogFlowFunc(("Data:\n%.*RhXd\n", cb, pv));
1825# endif
1826 void *pvDst = (void *)XtMalloc(cb);
1827 if (pvDst)
1828 {
1829 memcpy(pvDst, pv, cb);
1830
1831 *atomTypeReturn = *atomTarget;
1832 *pValReturn = (XtPointer)pvDst;
1833 *pcLenReturn = cb;
1834 *piFormatReturn = 8;
1835 }
1836 else
1837 rc = VERR_NO_MEMORY;
1838 }
1839
1840 RTMemFree(pv);
1841 pv = NULL;
1842 }
1843 /* else not supported yet. */
1844 }
1845#endif /* VBOX_WITH_SHARED_CLIPBOARD_TRANSFERS */
1846 else
1847 {
1848 *atomTypeReturn = XT_CONVERT_FAIL;
1849 *pValReturn = (XtPointer)NULL;
1850 *pcLenReturn = 0;
1851 *piFormatReturn = 0;
1852 }
1853
1854 if (RT_FAILURE(rc))
1855 {
1856 char *pszFmts2 = ShClFormatsToStrA(pCtx->vboxFormats);
1857 char *pszAtomName = XGetAtomName(XtDisplay(pCtx->pWidget), *atomTarget);
1858
1859 LogRel(("Shared Clipboard: Converting VBox formats '%s' to '%s' for X11 (idxFmtX11=%u, fmtX11=%u, atomTarget='%s') failed, rc=%Rrc\n",
1860 pszFmts2 ? pszFmts2 : "unknown", g_aFormats[idxFmtX11].pcszAtom, idxFmtX11, fmtX11, pszAtomName ? pszAtomName : "unknown", rc));
1861
1862 if (pszFmts2)
1863 RTStrFree(pszFmts2);
1864 if (pszAtomName)
1865 XFree(pszAtomName);
1866 }
1867
1868 LogFlowFuncLeaveRC(rc);
1869 return rc;
1870}
1871
1872/**
1873 * Returns VBox's clipboard data for an X11 client.
1874 *
1875 * @note Callback for XtOwnSelection.
1876 */
1877static Boolean clipXtConvertSelectionProc(Widget widget, Atom *atomSelection,
1878 Atom *atomTarget,
1879 Atom *atomTypeReturn,
1880 XtPointer *pValReturn,
1881 unsigned long *pcLenReturn,
1882 int *piFormatReturn)
1883{
1884 LogFlowFuncEnter();
1885
1886 PSHCLX11CTX pCtx = clipLookupContext(widget);
1887 if (!pCtx)
1888 return False;
1889
1890 /* Is this the rigt selection (clipboard) we were asked for? */
1891 if (!clipIsSupportedSelectionType(pCtx, *atomSelection))
1892 return False;
1893
1894 int rc;
1895 if (*atomTarget == clipGetAtom(pCtx, "TARGETS"))
1896 rc = clipCreateX11Targets(pCtx, atomTypeReturn, pValReturn,
1897 pcLenReturn, piFormatReturn);
1898 else
1899 rc = clipConvertToX11Data(pCtx, atomTarget, atomTypeReturn,
1900 pValReturn, pcLenReturn, piFormatReturn);
1901
1902#if 0 /** @todo Disabled -- crashes when running with tstClipboardGH-X11. */
1903 XSelectionRequestEvent* pReq =
1904 XtGetSelectionRequest(widget, *atomSelection, (XtRequestId)NULL);
1905 LogFlowFunc(("returning pVBoxWnd=%#x, ownerWnd=%#x, reqWnd=%#x, %RTbool, rc=%Rrc\n",
1906 XtWindow(pCtx->pWidget), pReq->owner, pReq->requestor, RT_SUCCESS(rc), rc));
1907#endif
1908 return RT_SUCCESS(rc) ? True : False;
1909}
1910
1911static void clipXtConvertSelectionProcLose(Widget widget, Atom *atomSelection)
1912{
1913 RT_NOREF(widget, atomSelection);
1914 LogFlowFuncEnter();
1915}
1916
1917static void clipXtConvertSelectionProcDone(Widget widget, Atom *atomSelection, Atom *atomTarget)
1918{
1919 RT_NOREF(widget, atomSelection, atomTarget);
1920 LogFlowFuncEnter();
1921}
1922
1923/**
1924 * Invalidates the local clipboard cache.
1925 *
1926 * @param pCtx The X11 clipboard context to use.
1927 */
1928static void clipInvalidateClipboardCache(PSHCLX11CTX pCtx)
1929{
1930 LogFlowFuncEnter();
1931
1932 ShClCacheInvalidate(&pCtx->Cache);
1933}
1934
1935/**
1936 * Takes possession of the X11 clipboard (and middle-button selection).
1937 *
1938 * @param pCtx The X11 clipboard context to use.
1939 * @param uFormats Clipboard formats to set.
1940 */
1941static void clipGrabX11Clipboard(PSHCLX11CTX pCtx, SHCLFORMATS uFormats)
1942{
1943 LogFlowFuncEnter();
1944
1945 /** @ŧodo r=andy The docs say: "the value CurrentTime is not acceptable" here!? */
1946 if (XtOwnSelection(pCtx->pWidget, clipGetAtom(pCtx, "CLIPBOARD"),
1947 CurrentTime,
1948 clipXtConvertSelectionProc, clipXtConvertSelectionProcLose, clipXtConvertSelectionProcDone))
1949 {
1950 pCtx->vboxFormats = uFormats;
1951
1952 /* Grab the middle-button paste selection too. */
1953 XtOwnSelection(pCtx->pWidget, clipGetAtom(pCtx, "PRIMARY"),
1954 CurrentTime, clipXtConvertSelectionProc, NULL, 0);
1955#ifndef TESTCASE
1956 /* Xt suppresses these if we already own the clipboard, so send them
1957 * ourselves. */
1958 XSetSelectionOwner(XtDisplay(pCtx->pWidget),
1959 clipGetAtom(pCtx, "CLIPBOARD"),
1960 XtWindow(pCtx->pWidget), CurrentTime);
1961 XSetSelectionOwner(XtDisplay(pCtx->pWidget),
1962 clipGetAtom(pCtx, "PRIMARY"),
1963 XtWindow(pCtx->pWidget), CurrentTime);
1964#endif
1965 }
1966}
1967
1968/**
1969 * Worker function for ShClX11ReportFormatsToX11Async.
1970 *
1971 * @param pvUserData Pointer to a CLIPNEWVBOXFORMATS structure containing
1972 * information about the VBox formats available and the
1973 * clipboard context data. Must be freed by the worker.
1974 *
1975 * @thread X11 event thread.
1976 */
1977static void ShClX11ReportFormatsToX11Worker(void *pvUserData, void * /* interval */)
1978{
1979 AssertPtrReturnVoid(pvUserData);
1980
1981 PSHCLX11REQUEST pReq = (PSHCLX11REQUEST)pvUserData;
1982
1983 PSHCLX11CTX pCtx = pReq->pCtx;
1984 SHCLFORMATS fFormats = pReq->Formats.fFormats;
1985
1986 RTMemFree(pReq);
1987
1988#ifdef LOG_ENABLED
1989 char *pszFmts = ShClFormatsToStrA(fFormats);
1990 AssertPtrReturnVoid(pszFmts);
1991 LogRel2(("Shared Clipboard: Reported available VBox formats %s to X11\n", pszFmts));
1992 RTStrFree(pszFmts);
1993#endif
1994
1995 clipInvalidateClipboardCache(pCtx);
1996 clipGrabX11Clipboard(pCtx, fFormats);
1997 clipResetX11Formats(pCtx);
1998
1999 LogFlowFuncLeave();
2000}
2001
2002/**
2003 * Announces new clipboard formats to the X11 clipboard.
2004 *
2005 * @returns VBox status code.
2006 * @param pCtx Context data for the clipboard backend.
2007 * @param uFormats Clipboard formats offered.
2008 */
2009int ShClX11ReportFormatsToX11Async(PSHCLX11CTX pCtx, SHCLFORMATS uFormats)
2010{
2011 /*
2012 * Immediately return if we are not connected to the X server.
2013 */
2014 if (!pCtx->fHaveX11)
2015 return VINF_SUCCESS;
2016
2017 int rc;
2018
2019 PSHCLX11REQUEST pReq = (PSHCLX11REQUEST)RTMemAllocZ(sizeof(SHCLX11REQUEST));
2020 if (pReq)
2021 {
2022 pReq->pCtx = pCtx;
2023 pReq->Formats.fFormats = uFormats;
2024
2025 rc = clipThreadScheduleCall(pCtx, ShClX11ReportFormatsToX11Worker, (XtPointer)pReq);
2026 if (RT_FAILURE(rc))
2027 RTMemFree(pReq);
2028 }
2029 else
2030 rc = VERR_NO_MEMORY;
2031
2032 LogFlowFuncLeaveRC(rc);
2033 return rc;
2034}
2035
2036#ifdef VBOX_WITH_SHARED_CLIPBOARD_TRANSFERS
2037/**
2038 * Converts an X11 data blob to a string list.
2039 *
2040 * @returns VBox status code.
2041 * @param pvData Data to conver to a string list.
2042 * @param cbData Size (in bytes) of \a pvData.
2043 * @param ppszList Where to return the allocated string list on success.
2044 * Must be free'd with RTStrFree().
2045 * @param pcbList Size (in bytes) of the returned string list on success.
2046 * Includes terminator.
2047 */
2048int ShClX11TransferConvertDataToStringList(const char *pvData, size_t cbData, char **ppszList, size_t *pcbList)
2049{
2050 AssertPtrReturn(pvData, VERR_INVALID_POINTER);
2051 AssertReturn(cbData, VERR_INVALID_PARAMETER);
2052 AssertPtrNullReturn(ppszList, VERR_INVALID_POINTER);
2053 AssertPtrReturn(pcbList, VERR_INVALID_POINTER);
2054
2055 /* For URI lists we only accept valid UTF-8 encodings. */
2056 int rc = RTStrValidateEncodingEx((char *)pvData, cbData, 0 /* fFlags */);
2057 if (RT_FAILURE(rc))
2058 return rc;
2059
2060 /* We might need to skip some prefixes before actually reaching the file list. */
2061 static const char *s_aszPrefixesToSkip[] =
2062 { "copy\n" /* Nautilus / Nemo */ };
2063 for (size_t i = 0; i < RT_ELEMENTS(s_aszPrefixesToSkip); i++)
2064 {
2065 const char *pszNeedle = RTStrStr(pvData, s_aszPrefixesToSkip[i]);
2066 if (pszNeedle)
2067 {
2068 size_t const cbNeedle = strlen(s_aszPrefixesToSkip[i]);
2069 pszNeedle += cbNeedle;
2070 pvData = pszNeedle;
2071 Assert(cbData >= cbNeedle);
2072 cbData -= cbNeedle;
2073 }
2074 }
2075
2076 *pcbList = 0;
2077
2078# ifdef DEBUG_andy
2079 LogFlowFunc(("Data:\n%.*RhXd\n", cbData, pvData));
2080# endif
2081
2082 char **papszStrings;
2083 size_t cStrings;
2084 rc = RTStrSplit(pvData, cbData, SHCL_TRANSFER_URI_LIST_SEP_STR, &papszStrings, &cStrings);
2085 if (RT_SUCCESS(rc))
2086 {
2087 for (size_t i = 0; i < cStrings; i++)
2088 {
2089 const char *pszString = papszStrings[i];
2090 LogRel2(("Shared Clipboard: Received entry #%zu from X11: '%s'\n", i, pszString));
2091 rc = RTStrAAppend(ppszList, pszString);
2092 if (RT_FAILURE(rc))
2093 break;
2094 *pcbList += strlen(pszString);
2095 }
2096
2097 *pcbList++; /* Include terminator. */
2098 }
2099
2100 return rc;
2101}
2102#endif /* VBOX_WITH_SHARED_CLIPBOARD_TRANSFERS */
2103
2104/**
2105 * Worker function for clipConvertDataFromX11.
2106 *
2107 * Converts the data read from the X11 clipboard to the required format.
2108 * Signals the wait event.
2109 *
2110 * Converts the text obtained UTF-16LE with Windows EOLs.
2111 * Converts full BMP data to DIB format.
2112 *
2113 * @thread X11 event thread.
2114 */
2115SHCL_X11_DECL(void) clipConvertDataFromX11Worker(void *pClient, void *pvSrc, unsigned cbSrc)
2116{
2117 PSHCLX11REQUEST pReq = (PSHCLX11REQUEST)pClient;
2118 AssertPtrReturnVoid(pReq);
2119
2120 LogFlowFunc(("uFmtVBox=%#x, idxFmtX11=%u, pvSrc=%p, cbSrc=%u\n", pReq->Read.uFmtVBox, pReq->Read.idxFmtX11, pvSrc, cbSrc));
2121
2122 /* Sanity. */
2123 AssertReturnVoid(pReq->Read.uFmtVBox != VBOX_SHCL_FMT_NONE);
2124 AssertReturnVoid(pReq->Read.idxFmtX11 < SHCL_MAX_X11_FORMATS);
2125
2126 AssertPtrReturnVoid(pReq->pCtx);
2127
2128 LogRel2(("Shared Clipboard: Converting X11 format index %#x to VBox format %#x (%RU32 bytes max)\n",
2129 pReq->Read.idxFmtX11, pReq->Read.uFmtVBox, pReq->Read.cbMax));
2130
2131 int rc = VINF_SUCCESS;
2132
2133 void *pvDst = NULL;
2134 size_t cbDst = 0;
2135
2136 PSHCLX11CTX pCtx = pReq->pCtx;
2137 AssertPtr(pReq->pCtx);
2138
2139#ifdef VBOX_WITH_SHARED_CLIPBOARD_XT_BUSY
2140 clipSetXtBusy(pCtx, false);
2141 if (clipGetXtNeedsUpdate(pCtx))
2142 clipQueryX11Targets(pCtx);
2143#endif
2144
2145 /* If X11 clipboard buffer has no data, libXt can pass to XtGetSelectionValue()
2146 * callback an empty string, in this case cbSrc is 0. */
2147 if (pvSrc == NULL || cbSrc == 0)
2148 {
2149 /* The clipboard selection may have changed before we could get it. */
2150 rc = VERR_NO_DATA;
2151 }
2152 else if (pReq->Read.uFmtVBox == VBOX_SHCL_FMT_UNICODETEXT)
2153 {
2154 /* In which format is the clipboard data? */
2155 switch (clipRealFormatForX11Format(pReq->Read.idxFmtX11))
2156 {
2157 case SHCLX11FMT_UTF8:
2158 RT_FALL_THROUGH();
2159 case SHCLX11FMT_TEXT:
2160 {
2161 size_t cwDst;
2162 /* If we are given broken UTF-8, we treat it as Latin1. */ /** @todo BUGBUG Is this acceptable? */
2163 if (RT_SUCCESS(RTStrValidateEncodingEx((char *)pvSrc, cbSrc, 0)))
2164 rc = ShClConvUtf8LFToUtf16CRLF((const char *)pvSrc, cbSrc,
2165 (PRTUTF16 *)&pvDst, &cwDst);
2166 else
2167 rc = ShClConvLatin1LFToUtf16CRLF((char *)pvSrc, cbSrc,
2168 (PRTUTF16 *)&pvDst, &cwDst);
2169 if (RT_SUCCESS(rc))
2170 {
2171 cwDst += 1 /* Include terminator */;
2172 cbDst = cwDst * sizeof(RTUTF16); /* Convert RTUTF16 units to bytes. */
2173
2174 LogFlowFunc(("UTF-16 text (%zu bytes):\n%ls\n", cbDst, pvDst));
2175 }
2176 break;
2177 }
2178
2179 default:
2180 {
2181 rc = VERR_INVALID_PARAMETER;
2182 break;
2183 }
2184 }
2185 }
2186 else if (pReq->Read.uFmtVBox == VBOX_SHCL_FMT_BITMAP)
2187 {
2188 /* In which format is the clipboard data? */
2189 switch (clipRealFormatForX11Format(pReq->Read.idxFmtX11))
2190 {
2191 case SHCLX11FMT_BMP:
2192 {
2193 const void *pDib;
2194 size_t cbDibSize;
2195 rc = ShClBmpGetDib((const void *)pvSrc, cbSrc,
2196 &pDib, &cbDibSize);
2197 if (RT_SUCCESS(rc))
2198 {
2199 pvDst = RTMemAlloc(cbDibSize);
2200 if (!pvDst)
2201 rc = VERR_NO_MEMORY;
2202 else
2203 {
2204 memcpy(pvDst, pDib, cbDibSize);
2205 cbDst = cbDibSize;
2206 }
2207 }
2208 break;
2209 }
2210
2211 default:
2212 {
2213 rc = VERR_INVALID_PARAMETER;
2214 break;
2215 }
2216 }
2217 }
2218 else if (pReq->Read.uFmtVBox == VBOX_SHCL_FMT_HTML)
2219 {
2220 /* In which format is the clipboard data? */
2221 switch (clipRealFormatForX11Format(pReq->Read.idxFmtX11))
2222 {
2223 case SHCLX11FMT_HTML:
2224 {
2225 /*
2226 * The common VBox HTML encoding will be - UTF-8
2227 * because it more general for HTML formats then UTF-16
2228 * X11 clipboard returns UTF-16, so before sending it we should
2229 * convert it to UTF-8.
2230 */
2231 pvDst = NULL;
2232 cbDst = 0;
2233
2234 /*
2235 * Some applications sends data in UTF-16, some in UTF-8,
2236 * without indication it in MIME.
2237 *
2238 * In case of UTF-16, at least [Open|Libre] Office adds an byte order mark (0xfeff)
2239 * at the start of the clipboard data.
2240 */
2241 if ( cbSrc >= sizeof(RTUTF16)
2242 && *(PRTUTF16)pvSrc == VBOX_SHCL_UTF16LEMARKER)
2243 {
2244 rc = ShClConvUtf16ToUtf8HTML((PRTUTF16)pvSrc, cbSrc / sizeof(RTUTF16), (char**)&pvDst, &cbDst);
2245 if (RT_SUCCESS(rc))
2246 {
2247 LogFlowFunc(("UTF-16 Unicode source (%u bytes):\n%ls\n\n", cbSrc, pvSrc));
2248 LogFlowFunc(("Byte Order Mark = %hx", ((PRTUTF16)pvSrc)[0]));
2249 LogFlowFunc(("UTF-8 Unicode dest (%u bytes):\n%s\n\n", cbDst, pvDst));
2250 }
2251 else
2252 LogRel(("Shared Clipboard: Converting UTF-16 Unicode failed with %Rrc\n", rc));
2253 }
2254 else /* Raw data. */
2255 {
2256 pvDst = RTMemAllocZ(cbSrc + 1 /* '\0' */);
2257 if(pvDst)
2258 {
2259 memcpy(pvDst, pvSrc, cbSrc);
2260 cbDst = cbSrc + 1 /* '\0' */;
2261 }
2262 else
2263 {
2264 rc = VERR_NO_MEMORY;
2265 break;
2266 }
2267 }
2268
2269 rc = VINF_SUCCESS;
2270 break;
2271 }
2272
2273 default:
2274 {
2275 rc = VERR_INVALID_PARAMETER;
2276 break;
2277 }
2278 }
2279 }
2280# ifdef VBOX_WITH_SHARED_CLIPBOARD_TRANSFERS
2281 else if (pReq->Read.uFmtVBox == VBOX_SHCL_FMT_URI_LIST)
2282 {
2283 /* In which format is the clipboard data? */
2284 switch (clipRealFormatForX11Format(pReq->Read.idxFmtX11))
2285 {
2286 case SHCLX11FMT_URI_LIST:
2287 RT_FALL_THROUGH();
2288 case SHCLX11FMT_URI_LIST_GNOME_COPIED_FILES:
2289 RT_FALL_THROUGH();
2290 case SHCLX11FMT_URI_LIST_MATE_COPIED_FILES:
2291 RT_FALL_THROUGH();
2292 case SHCLX11FMT_URI_LIST_NAUTILUS_CLIPBOARD:
2293 RT_FALL_THROUGH();
2294 case SHCLX11FMT_URI_LIST_KDE_CUTSELECTION:
2295 {
2296 rc = ShClX11TransferConvertDataToStringList((const char *)pvSrc, cbSrc, (char **)&pvDst, &cbDst);
2297 break;
2298 }
2299
2300 default:
2301 {
2302 AssertFailedStmt(rc = VERR_NOT_SUPPORTED); /* Missing code? */
2303 break;
2304 }
2305 }
2306 }
2307# endif /* VBOX_WITH_SHARED_CLIPBOARD_TRANSFERS */
2308 else
2309 rc = VERR_NOT_SUPPORTED;
2310
2311 LogFlowFunc(("pvDst=%p, cbDst=%RU32\n", pvDst, cbDst));
2312
2313 if (RT_FAILURE(rc))
2314 LogRel(("Shared Clipboard: Converting X11 format index %#x to VBox format %#x failed, rc=%Rrc\n",
2315 pReq->Read.idxFmtX11, pReq->Read.uFmtVBox, rc));
2316
2317 int rc2;
2318
2319 PSHCLEVENTPAYLOAD pPayload = NULL;
2320 size_t cbResp = sizeof(SHCLX11RESPONSE);
2321 PSHCLX11RESPONSE pResp = (PSHCLX11RESPONSE)RTMemAllocZ(cbResp);
2322 if (pResp)
2323 {
2324 pResp->Read.pvData = pvDst;
2325 pResp->Read.cbData = cbDst;
2326
2327 pvDst = NULL; /* The response owns the data now. */
2328
2329 if ( pResp->Read.pvData
2330 && pResp->Read.cbData)
2331 {
2332 rc2 = ShClPayloadInit(0 /* ID, unused */, pResp, cbResp, &pPayload);
2333 AssertRC(rc2);
2334 }
2335 }
2336 else
2337 rc = VERR_NO_MEMORY;
2338
2339 rc2 = ShClEventSignal(pReq->pEvent, pPayload);
2340 if (RT_SUCCESS(rc2))
2341 pPayload = NULL; /* The event owns the payload now. */
2342
2343 if (pPayload) /* Free payload on error. */
2344 {
2345 ShClPayloadFree(pPayload);
2346 pPayload = NULL;
2347 }
2348
2349 LogRel2(("Shared Clipboard: Converting X11 clipboard data completed with %Rrc\n", rc));
2350
2351 RTMemFree(pReq);
2352 RTMemFree(pvDst);
2353
2354 LogFlowFuncLeaveRC(rc);
2355}
2356
2357/**
2358 * Converts the data read from the X11 clipboard to the required format.
2359 *
2360 * @thread X11 event thread.
2361 */
2362SHCL_X11_DECL(void) clipConvertDataFromX11(Widget widget, XtPointer pClient,
2363 Atom * /* selection */, Atom *atomType,
2364 XtPointer pvSrc, long unsigned int *pcLen,
2365 int *piFormat)
2366{
2367 RT_NOREF(widget);
2368
2369 int rc = VINF_SUCCESS;
2370
2371 if (*atomType == XT_CONVERT_FAIL) /* Xt timeout */
2372 {
2373 LogRel(("Shared Clipboard: Reading clipboard data from X11 timed out\n"));
2374 rc = VERR_TIMEOUT;
2375 }
2376 else
2377 {
2378 PSHCLX11REQUEST pReq = (PSHCLX11REQUEST)pClient;
2379 if (pReq) /* Give some more clues, if available. */
2380 {
2381 char *pszFmts = ShClFormatsToStrA(pReq->Read.uFmtVBox);
2382 AssertPtrReturnVoid(pszFmts);
2383 AssertReturnVoid(pReq->Read.idxFmtX11 < SHCL_MAX_X11_FORMATS); /* Paranoia, should be checked already by the caller. */
2384 LogRel2(("Shared Clipboard: Converting X11 format '%s' -> VBox format(s) '%s'\n", g_aFormats[pReq->Read.idxFmtX11].pcszAtom, pszFmts));
2385 RTStrFree(pszFmts);
2386
2387 if (pReq->pCtx->Callbacks.pfnOnClipboardRead) /* Usually only used for testcases. */
2388 {
2389 void *pvData = NULL;
2390 size_t cbData = 0;
2391 rc = pReq->pCtx->Callbacks.pfnOnClipboardRead(pReq->pCtx->pFrontend, pReq->Read.uFmtVBox, &pvData, &cbData, NULL);
2392 if (RT_SUCCESS(rc))
2393 {
2394 /* Feed to conversion worker. */
2395 clipConvertDataFromX11Worker(pClient, pvData, cbData);
2396 RTMemFree(pvData);
2397 }
2398 }
2399 else /* Call conversion worker with current data provided by X (default). */
2400 clipConvertDataFromX11Worker(pClient, pvSrc, (*pcLen) * (*piFormat) / 8);
2401 }
2402 else
2403 rc = VERR_INVALID_POINTER;
2404 }
2405
2406 if (RT_FAILURE(rc))
2407 {
2408 LogRel(("Shared Clipboard: Reading clipboard data from X11 failed with %Rrc\n", rc));
2409
2410 /* Make sure to complete the request in any case by calling the conversion worker. */
2411 clipConvertDataFromX11Worker(pClient, NULL, 0);
2412 }
2413
2414 XtFree((char *)pvSrc);
2415}
2416
2417/**
2418 * Requests the current clipboard data from a specific selection.
2419 *
2420 * @returns VBox status code.
2421 * @param pCtx The X11 clipboard context to use.
2422 * @param pszWhere Clipboard selection to request the data from.
2423 * @param idxFmt The X11 format to request the data in.
2424 * @param pReq Where to store the requested data on success.
2425 */
2426static int clipGetSelectionValueEx(PSHCLX11CTX pCtx, const char *pszWhere, SHCLX11FMTIDX idxFmt,
2427 PSHCLX11REQUEST pReq)
2428{
2429 AssertPtrReturn(pszWhere, VERR_INVALID_POINTER);
2430 AssertReturn(idxFmt < SHCL_MAX_X11_FORMATS, VERR_INVALID_PARAMETER);
2431 AssertReturn(clipIsSupportedSelectionType(pCtx, clipGetAtom(pCtx, pszWhere)), VERR_INVALID_PARAMETER);
2432 AssertPtrReturn(pReq, VERR_INVALID_POINTER);
2433
2434 LogRel2(("Shared Clipboard: Requesting X11 selection value in %s for format '%s'\n", pszWhere, g_aFormats[idxFmt].pcszAtom));
2435
2436#ifndef TESTCASE
2437 XtGetSelectionValue(pCtx->pWidget, clipGetAtom(pCtx, pszWhere),
2438 clipAtomForX11Format(pCtx, idxFmt),
2439 clipConvertDataFromX11,
2440 reinterpret_cast<XtPointer>(pReq),
2441 CurrentTime);
2442#else
2443 tstClipRequestData(pCtx, idxFmt, (void *)pReq);
2444#endif
2445
2446 return VINF_SUCCESS; /** @todo Return real rc. */
2447}
2448
2449/**
2450 * Requests the current clipboard data from the CLIPBOARD selection.
2451 *
2452 * @returns VBox status code.
2453 * @param pCtx The X11 clipboard context to use.
2454 * @param idxFmt The X11 format to request the data in.
2455 * @param pReq Where to store the requested data on success.
2456 *
2457 * @sa clipGetSelectionValueEx() for requesting data for a specific selection.
2458 */
2459static int clipGetSelectionValue(PSHCLX11CTX pCtx, SHCLX11FMTIDX idxFmt, PSHCLX11REQUEST pReq)
2460{
2461 return clipGetSelectionValueEx(pCtx, "CLIPBOARD", idxFmt, pReq);
2462}
2463
2464/**
2465 * Worker function for ShClX11ReadDataFromX11Async.
2466 *
2467 * @param pvUserData Pointer to a CLIPREADX11CBREQ structure containing
2468 * information about the clipboard read request.
2469 * Must be free'd by the worker.
2470 * @thread X11 event thread.
2471 */
2472static void ShClX11ReadDataFromX11Worker(void *pvUserData, void * /* interval */)
2473{
2474 AssertPtrReturnVoid(pvUserData);
2475
2476 PSHCLX11REQUEST pReq = (PSHCLX11REQUEST)pvUserData;
2477 SHCLX11CTX *pCtx = pReq->pCtx;
2478 AssertPtrReturnVoid(pCtx);
2479
2480 LogFlowFunc(("pReq->uFmtVBox=%#x, idxFmtX11=%#x\n", pReq->Read.uFmtVBox, pReq->Read.idxFmtX11));
2481
2482 int rc = VERR_NO_DATA; /* VBox thinks we have data and we don't. */
2483
2484#ifdef VBOX_WITH_SHARED_CLIPBOARD_XT_BUSY
2485 const bool fXtBusy = clipGetXtBusy(pCtx);
2486 clipSetXtBusy(pCtx, true);
2487 if (fXtBusy)
2488 {
2489 /* If the clipboard is busy just fend off the request. */
2490 rc = VERR_TRY_AGAIN;
2491 }
2492 else
2493#endif
2494 if (pReq->Read.uFmtVBox & VBOX_SHCL_FMT_UNICODETEXT)
2495 {
2496 pReq->Read.idxFmtX11 = pCtx->idxFmtText;
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->idxFmtText, pReq);
2501 }
2502 }
2503 else if (pReq->Read.uFmtVBox & VBOX_SHCL_FMT_BITMAP)
2504 {
2505 pReq->Read.idxFmtX11 = pCtx->idxFmtBmp;
2506 if (pReq->Read.idxFmtX11 != SHCLX11FMT_INVALID)
2507 {
2508 /* Send out a request for the data to the current clipboard owner. */
2509 rc = clipGetSelectionValue(pCtx, pCtx->idxFmtBmp, pReq);
2510 }
2511 }
2512 else if (pReq->Read.uFmtVBox & VBOX_SHCL_FMT_HTML)
2513 {
2514 pReq->Read.idxFmtX11 = pCtx->idxFmtHTML;
2515 if (pReq->Read.idxFmtX11 != SHCLX11FMT_INVALID)
2516 {
2517 /* Send out a request for the data to the current clipboard owner. */
2518 rc = clipGetSelectionValue(pCtx, pCtx->idxFmtHTML, pReq);
2519 }
2520 }
2521#ifdef VBOX_WITH_SHARED_CLIPBOARD_TRANSFERS
2522 else if (pReq->Read.uFmtVBox & VBOX_SHCL_FMT_URI_LIST)
2523 {
2524 pReq->Read.idxFmtX11 = pCtx->idxFmtURI;
2525 if (pReq->Read.idxFmtX11 != SHCLX11FMT_INVALID)
2526 {
2527 /* Send out a request for the data to the current clipboard owner. */
2528 rc = clipGetSelectionValue(pCtx, pCtx->idxFmtURI, pReq);
2529 }
2530 }
2531#endif
2532 else
2533 {
2534#ifdef VBOX_WITH_SHARED_CLIPBOARD_XT_BUSY
2535 clipSetXtBusy(pCtx, false);
2536#endif
2537 rc = VERR_NOT_IMPLEMENTED;
2538 }
2539
2540 /* If the above stuff fails, make sure to let the waiters know.
2541 *
2542 * Getting the actual selection value via clipGetSelectionValue[Ex]() above will happen in the X event thread,
2543 * which has its own signalling then. So this check only handles errors which happens before we put anything
2544 * onto the X event thread.
2545 */
2546 if (RT_FAILURE(rc))
2547 {
2548 int rc2 = ShClEventSignalEx(pReq->pEvent, rc, NULL /* Payload */);
2549 AssertRC(rc2);
2550 }
2551
2552 LogRel2(("Shared Clipboard: Reading X11 clipboard data completed with %Rrc\n", rc));
2553
2554 LogFlowFuncLeaveRC(rc);
2555}
2556
2557/**
2558 * Reads the X11 clipboard (asynchronously).
2559 *
2560 * @returns VBox status code.
2561 * @retval VERR_NO_DATA if format is supported but no data is available currently.
2562 * @retval VERR_NOT_IMPLEMENTED if the format is not implemented.
2563 * @param pCtx Context data for the clipboard backend.
2564 * @param uFmt The format that the VBox would like to receive the data in.
2565 * @param cbMax Maximum data to read (in bytes).
2566 * Specify UINT32_MAX to read as much as available.
2567 * @param pEvent Event to use for waiting for data to arrive.
2568 * The event's payload will contain the data read. Needs to be free'd with ShClEventRelease().
2569 */
2570int ShClX11ReadDataFromX11Async(PSHCLX11CTX pCtx, SHCLFORMAT uFmt, uint32_t cbMax, PSHCLEVENT pEvent)
2571{
2572 AssertPtrReturn(pEvent, VERR_INVALID_POINTER);
2573 /*
2574 * Immediately return if we are not connected to the X server.
2575 */
2576 if (!pCtx->fHaveX11)
2577 return VERR_NO_DATA;
2578
2579 int rc = VINF_SUCCESS;
2580
2581 PSHCLX11REQUEST pReq = (PSHCLX11REQUEST)RTMemAllocZ(sizeof(SHCLX11REQUEST));
2582 if (pReq)
2583 {
2584 pReq->pCtx = pCtx;
2585 pReq->Read.uFmtVBox = uFmt;
2586 pReq->Read.cbMax = cbMax;
2587 pReq->pEvent = pEvent;
2588
2589 /* We use this to schedule a worker function on the event thread. */
2590 rc = clipThreadScheduleCall(pCtx, ShClX11ReadDataFromX11Worker, (XtPointer)pReq);
2591 if (RT_FAILURE(rc))
2592 RTMemFree(pReq);
2593 }
2594 else
2595 rc = VERR_NO_MEMORY;
2596
2597 LogFlowFuncLeaveRC(rc);
2598 return rc;
2599}
2600
2601/**
2602 * Reads the X11 clipboard.
2603 *
2604 * @returns VBox status code.
2605 * @retval VERR_NO_DATA if format is supported but no data is available currently.
2606 * @retval VERR_NOT_IMPLEMENTED if the format is not implemented.
2607 * @param pCtx Context data for the clipboard backend.
2608 * @param pEventSource Event source to use.
2609 * @param msTimeout Timeout (in ms) for waiting.
2610 * @param uFmt The format that the VBox would like to receive the data in.
2611 * @param pvBuf Where to store the received data on success.
2612 * @param cbBuf Size (in bytes) of \a pvBuf. Also marks maximum data to read (in bytes).
2613 * @param pcbRead Where to return the read bytes on success.
2614 */
2615int ShClX11ReadDataFromX11(PSHCLX11CTX pCtx, PSHCLEVENTSOURCE pEventSource, RTMSINTERVAL msTimeout,
2616 SHCLFORMAT uFmt, void *pvBuf, uint32_t cbBuf, uint32_t *pcbRead)
2617{
2618 AssertPtrReturn(pCtx, VERR_INVALID_POINTER);
2619 AssertPtrReturn(pEventSource, VERR_INVALID_POINTER);
2620 AssertPtrReturn(pvBuf, VERR_INVALID_POINTER);
2621 AssertReturn(cbBuf, VERR_INVALID_PARAMETER);
2622 AssertPtrReturn(pcbRead, VERR_INVALID_POINTER);
2623
2624 PSHCLEVENT pEvent;
2625 int rc = ShClEventSourceGenerateAndRegisterEvent(pEventSource, &pEvent);
2626 if (RT_SUCCESS(rc))
2627 {
2628 rc = ShClX11ReadDataFromX11Async(pCtx, uFmt, cbBuf, pEvent);
2629 if (RT_SUCCESS(rc))
2630 {
2631 int rcEvent;
2632 PSHCLEVENTPAYLOAD pPayload;
2633 rc = ShClEventWaitEx(pEvent, msTimeout, &rcEvent, &pPayload);
2634 if (RT_SUCCESS(rc))
2635 {
2636 if (pPayload)
2637 {
2638 Assert(pPayload->cbData == sizeof(SHCLX11RESPONSE));
2639 PSHCLX11RESPONSE pResp = (PSHCLX11RESPONSE)pPayload->pvData;
2640
2641 memcpy(pvBuf, pResp->Read.pvData, RT_MIN(cbBuf, pResp->Read.cbData));
2642 *pcbRead = pResp->Read.cbData;
2643
2644 RTMemFree(pResp->Read.pvData);
2645 pResp->Read.cbData = 0;
2646
2647 ShClPayloadFree(pPayload);
2648 }
2649 else /* No payload given; could happen on invalid / not-expected formats. */
2650 *pcbRead = 0;
2651 }
2652 else if (rc == VERR_SHCLPB_EVENT_FAILED)
2653 rc = rcEvent;
2654 }
2655 }
2656
2657 LogFlowFuncLeaveRC(rc);
2658 return rc;
2659}
2660
Note: See TracBrowser for help on using the repository browser.

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