VirtualBox

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

Last change on this file since 102468 was 102468, checked in by vboxsync, 13 months ago

Shared Clipboard: Added a dedicated event type for the X11 requests/responses. Needed for union access. Added some more checks. ​bugref:10384

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 90.5 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 AssertReturnVoid(pReq->enmType == SHCLX11EVENTTYPE_REPORT_FORMATS);
1983
1984 PSHCLX11CTX pCtx = pReq->pCtx;
1985 SHCLFORMATS fFormats = pReq->Formats.fFormats;
1986
1987 RTMemFree(pReq);
1988
1989#ifdef LOG_ENABLED
1990 char *pszFmts = ShClFormatsToStrA(fFormats);
1991 AssertPtrReturnVoid(pszFmts);
1992 LogRel2(("Shared Clipboard: Reported available VBox formats %s to X11\n", pszFmts));
1993 RTStrFree(pszFmts);
1994#endif
1995
1996 clipInvalidateClipboardCache(pCtx);
1997 clipGrabX11Clipboard(pCtx, fFormats);
1998 clipResetX11Formats(pCtx);
1999
2000 LogFlowFuncLeave();
2001}
2002
2003/**
2004 * Announces new clipboard formats to the X11 clipboard.
2005 *
2006 * @returns VBox status code.
2007 * @param pCtx Context data for the clipboard backend.
2008 * @param uFormats Clipboard formats offered.
2009 */
2010int ShClX11ReportFormatsToX11Async(PSHCLX11CTX pCtx, SHCLFORMATS uFormats)
2011{
2012 /*
2013 * Immediately return if we are not connected to the X server.
2014 */
2015 if (!pCtx->fHaveX11)
2016 return VINF_SUCCESS;
2017
2018 int rc;
2019
2020 PSHCLX11REQUEST pReq = (PSHCLX11REQUEST)RTMemAllocZ(sizeof(SHCLX11REQUEST));
2021 if (pReq)
2022 {
2023 pReq->enmType = SHCLX11EVENTTYPE_REPORT_FORMATS;
2024 pReq->pCtx = pCtx;
2025 pReq->Formats.fFormats = uFormats;
2026
2027 rc = clipThreadScheduleCall(pCtx, ShClX11ReportFormatsToX11Worker, (XtPointer)pReq);
2028 if (RT_FAILURE(rc))
2029 RTMemFree(pReq);
2030 }
2031 else
2032 rc = VERR_NO_MEMORY;
2033
2034 LogFlowFuncLeaveRC(rc);
2035 return rc;
2036}
2037
2038#ifdef VBOX_WITH_SHARED_CLIPBOARD_TRANSFERS
2039/**
2040 * Converts an X11 data blob to a string list.
2041 *
2042 * @returns VBox status code.
2043 * @param pvData Data to conver to a string list.
2044 * @param cbData Size (in bytes) of \a pvData.
2045 * @param ppszList Where to return the allocated string list on success.
2046 * Must be free'd with RTStrFree().
2047 * @param pcbList Size (in bytes) of the returned string list on success.
2048 * Includes terminator.
2049 */
2050int ShClX11TransferConvertDataToStringList(const char *pvData, size_t cbData, char **ppszList, size_t *pcbList)
2051{
2052 AssertPtrReturn(pvData, VERR_INVALID_POINTER);
2053 AssertReturn(cbData, VERR_INVALID_PARAMETER);
2054 AssertPtrNullReturn(ppszList, VERR_INVALID_POINTER);
2055 AssertPtrReturn(pcbList, VERR_INVALID_POINTER);
2056
2057 /* For URI lists we only accept valid UTF-8 encodings. */
2058 int rc = RTStrValidateEncodingEx((char *)pvData, cbData, 0 /* fFlags */);
2059 if (RT_FAILURE(rc))
2060 return rc;
2061
2062 /* We might need to skip some prefixes before actually reaching the file list. */
2063 static const char *s_aszPrefixesToSkip[] =
2064 { "copy\n" /* Nautilus / Nemo */ };
2065 for (size_t i = 0; i < RT_ELEMENTS(s_aszPrefixesToSkip); i++)
2066 {
2067 const char *pszNeedle = RTStrStr(pvData, s_aszPrefixesToSkip[i]);
2068 if (pszNeedle)
2069 {
2070 size_t const cbNeedle = strlen(s_aszPrefixesToSkip[i]);
2071 pszNeedle += cbNeedle;
2072 pvData = pszNeedle;
2073 Assert(cbData >= cbNeedle);
2074 cbData -= cbNeedle;
2075 }
2076 }
2077
2078 *pcbList = 0;
2079
2080# ifdef DEBUG_andy
2081 LogFlowFunc(("Data:\n%.*RhXd\n", cbData, pvData));
2082# endif
2083
2084 char **papszStrings;
2085 size_t cStrings;
2086 rc = RTStrSplit(pvData, cbData, SHCL_TRANSFER_URI_LIST_SEP_STR, &papszStrings, &cStrings);
2087 if (RT_SUCCESS(rc))
2088 {
2089 for (size_t i = 0; i < cStrings; i++)
2090 {
2091 const char *pszString = papszStrings[i];
2092 LogRel2(("Shared Clipboard: Received entry #%zu from X11: '%s'\n", i, pszString));
2093 rc = RTStrAAppend(ppszList, pszString);
2094 if (RT_FAILURE(rc))
2095 break;
2096 *pcbList += strlen(pszString);
2097 }
2098
2099 *pcbList++; /* Include terminator. */
2100 }
2101
2102 return rc;
2103}
2104#endif /* VBOX_WITH_SHARED_CLIPBOARD_TRANSFERS */
2105
2106/**
2107 * Worker function for clipConvertDataFromX11.
2108 *
2109 * Converts the data read from the X11 clipboard to the required format.
2110 * Signals the wait event.
2111 *
2112 * Converts the text obtained UTF-16LE with Windows EOLs.
2113 * Converts full BMP data to DIB format.
2114 *
2115 * @thread X11 event thread.
2116 */
2117SHCL_X11_DECL(void) clipConvertDataFromX11Worker(void *pClient, void *pvSrc, unsigned cbSrc)
2118{
2119 PSHCLX11REQUEST pReq = (PSHCLX11REQUEST)pClient;
2120 AssertPtrReturnVoid(pReq);
2121
2122 LogFlowFunc(("uFmtVBox=%#x, idxFmtX11=%u, pvSrc=%p, cbSrc=%u\n", pReq->Read.uFmtVBox, pReq->Read.idxFmtX11, pvSrc, cbSrc));
2123
2124 /* Sanity. */
2125 AssertReturnVoid(pReq->enmType == SHCLX11EVENTTYPE_READ);
2126 AssertReturnVoid(pReq->Read.uFmtVBox != VBOX_SHCL_FMT_NONE);
2127 AssertReturnVoid(pReq->Read.idxFmtX11 < SHCL_MAX_X11_FORMATS);
2128
2129 AssertPtrReturnVoid(pReq->pCtx);
2130
2131 LogRel2(("Shared Clipboard: Converting X11 format index %#x to VBox format %#x (%RU32 bytes max)\n",
2132 pReq->Read.idxFmtX11, pReq->Read.uFmtVBox, pReq->Read.cbMax));
2133
2134 int rc = VINF_SUCCESS;
2135
2136 void *pvDst = NULL;
2137 size_t cbDst = 0;
2138
2139 PSHCLX11CTX pCtx = pReq->pCtx;
2140 AssertPtr(pReq->pCtx);
2141
2142#ifdef VBOX_WITH_SHARED_CLIPBOARD_XT_BUSY
2143 clipSetXtBusy(pCtx, false);
2144 if (clipGetXtNeedsUpdate(pCtx))
2145 clipQueryX11Targets(pCtx);
2146#endif
2147
2148 /* If X11 clipboard buffer has no data, libXt can pass to XtGetSelectionValue()
2149 * callback an empty string, in this case cbSrc is 0. */
2150 if (pvSrc == NULL || cbSrc == 0)
2151 {
2152 /* The clipboard selection may have changed before we could get it. */
2153 rc = VERR_NO_DATA;
2154 }
2155 else if (pReq->Read.uFmtVBox == VBOX_SHCL_FMT_UNICODETEXT)
2156 {
2157 /* In which format is the clipboard data? */
2158 switch (clipRealFormatForX11Format(pReq->Read.idxFmtX11))
2159 {
2160 case SHCLX11FMT_UTF8:
2161 RT_FALL_THROUGH();
2162 case SHCLX11FMT_TEXT:
2163 {
2164 size_t cwDst;
2165 /* If we are given broken UTF-8, we treat it as Latin1. */ /** @todo BUGBUG Is this acceptable? */
2166 if (RT_SUCCESS(RTStrValidateEncodingEx((char *)pvSrc, cbSrc, 0)))
2167 rc = ShClConvUtf8LFToUtf16CRLF((const char *)pvSrc, cbSrc,
2168 (PRTUTF16 *)&pvDst, &cwDst);
2169 else
2170 rc = ShClConvLatin1LFToUtf16CRLF((char *)pvSrc, cbSrc,
2171 (PRTUTF16 *)&pvDst, &cwDst);
2172 if (RT_SUCCESS(rc))
2173 {
2174 cwDst += 1 /* Include terminator */;
2175 cbDst = cwDst * sizeof(RTUTF16); /* Convert RTUTF16 units to bytes. */
2176
2177 LogFlowFunc(("UTF-16 text (%zu bytes):\n%ls\n", cbDst, pvDst));
2178 }
2179 break;
2180 }
2181
2182 default:
2183 {
2184 rc = VERR_INVALID_PARAMETER;
2185 break;
2186 }
2187 }
2188 }
2189 else if (pReq->Read.uFmtVBox == VBOX_SHCL_FMT_BITMAP)
2190 {
2191 /* In which format is the clipboard data? */
2192 switch (clipRealFormatForX11Format(pReq->Read.idxFmtX11))
2193 {
2194 case SHCLX11FMT_BMP:
2195 {
2196 const void *pDib;
2197 size_t cbDibSize;
2198 rc = ShClBmpGetDib((const void *)pvSrc, cbSrc,
2199 &pDib, &cbDibSize);
2200 if (RT_SUCCESS(rc))
2201 {
2202 pvDst = RTMemAlloc(cbDibSize);
2203 if (!pvDst)
2204 rc = VERR_NO_MEMORY;
2205 else
2206 {
2207 memcpy(pvDst, pDib, cbDibSize);
2208 cbDst = cbDibSize;
2209 }
2210 }
2211 break;
2212 }
2213
2214 default:
2215 {
2216 rc = VERR_INVALID_PARAMETER;
2217 break;
2218 }
2219 }
2220 }
2221 else if (pReq->Read.uFmtVBox == VBOX_SHCL_FMT_HTML)
2222 {
2223 /* In which format is the clipboard data? */
2224 switch (clipRealFormatForX11Format(pReq->Read.idxFmtX11))
2225 {
2226 case SHCLX11FMT_HTML:
2227 {
2228 /*
2229 * The common VBox HTML encoding will be - UTF-8
2230 * because it more general for HTML formats then UTF-16
2231 * X11 clipboard returns UTF-16, so before sending it we should
2232 * convert it to UTF-8.
2233 */
2234 pvDst = NULL;
2235 cbDst = 0;
2236
2237 /*
2238 * Some applications sends data in UTF-16, some in UTF-8,
2239 * without indication it in MIME.
2240 *
2241 * In case of UTF-16, at least [Open|Libre] Office adds an byte order mark (0xfeff)
2242 * at the start of the clipboard data.
2243 */
2244 if ( cbSrc >= sizeof(RTUTF16)
2245 && *(PRTUTF16)pvSrc == VBOX_SHCL_UTF16LEMARKER)
2246 {
2247 rc = ShClConvUtf16ToUtf8HTML((PRTUTF16)pvSrc, cbSrc / sizeof(RTUTF16), (char**)&pvDst, &cbDst);
2248 if (RT_SUCCESS(rc))
2249 {
2250 LogFlowFunc(("UTF-16 Unicode source (%u bytes):\n%ls\n\n", cbSrc, pvSrc));
2251 LogFlowFunc(("Byte Order Mark = %hx", ((PRTUTF16)pvSrc)[0]));
2252 LogFlowFunc(("UTF-8 Unicode dest (%u bytes):\n%s\n\n", cbDst, pvDst));
2253 }
2254 else
2255 LogRel(("Shared Clipboard: Converting UTF-16 Unicode failed with %Rrc\n", rc));
2256 }
2257 else /* Raw data. */
2258 {
2259 pvDst = RTMemAllocZ(cbSrc + 1 /* '\0' */);
2260 if(pvDst)
2261 {
2262 memcpy(pvDst, pvSrc, cbSrc);
2263 cbDst = cbSrc + 1 /* '\0' */;
2264 }
2265 else
2266 {
2267 rc = VERR_NO_MEMORY;
2268 break;
2269 }
2270 }
2271
2272 rc = VINF_SUCCESS;
2273 break;
2274 }
2275
2276 default:
2277 {
2278 rc = VERR_INVALID_PARAMETER;
2279 break;
2280 }
2281 }
2282 }
2283# ifdef VBOX_WITH_SHARED_CLIPBOARD_TRANSFERS
2284 else if (pReq->Read.uFmtVBox == VBOX_SHCL_FMT_URI_LIST)
2285 {
2286 /* In which format is the clipboard data? */
2287 switch (clipRealFormatForX11Format(pReq->Read.idxFmtX11))
2288 {
2289 case SHCLX11FMT_URI_LIST:
2290 RT_FALL_THROUGH();
2291 case SHCLX11FMT_URI_LIST_GNOME_COPIED_FILES:
2292 RT_FALL_THROUGH();
2293 case SHCLX11FMT_URI_LIST_MATE_COPIED_FILES:
2294 RT_FALL_THROUGH();
2295 case SHCLX11FMT_URI_LIST_NAUTILUS_CLIPBOARD:
2296 RT_FALL_THROUGH();
2297 case SHCLX11FMT_URI_LIST_KDE_CUTSELECTION:
2298 {
2299 rc = ShClX11TransferConvertDataToStringList((const char *)pvSrc, cbSrc, (char **)&pvDst, &cbDst);
2300 break;
2301 }
2302
2303 default:
2304 {
2305 AssertFailedStmt(rc = VERR_NOT_SUPPORTED); /* Missing code? */
2306 break;
2307 }
2308 }
2309 }
2310# endif /* VBOX_WITH_SHARED_CLIPBOARD_TRANSFERS */
2311 else
2312 rc = VERR_NOT_SUPPORTED;
2313
2314 LogFlowFunc(("pvDst=%p, cbDst=%RU32\n", pvDst, cbDst));
2315
2316 if (RT_FAILURE(rc))
2317 LogRel(("Shared Clipboard: Converting X11 format index %#x to VBox format %#x failed, rc=%Rrc\n",
2318 pReq->Read.idxFmtX11, pReq->Read.uFmtVBox, rc));
2319
2320 int rc2;
2321
2322 PSHCLEVENTPAYLOAD pPayload = NULL;
2323 size_t cbResp = sizeof(SHCLX11RESPONSE);
2324 PSHCLX11RESPONSE pResp = (PSHCLX11RESPONSE)RTMemAllocZ(cbResp);
2325 if (pResp)
2326 {
2327 pResp->Read.pvData = pvDst;
2328 pResp->Read.cbData = cbDst;
2329
2330 pvDst = NULL; /* The response owns the data now. */
2331
2332 if ( pResp->Read.pvData
2333 && pResp->Read.cbData)
2334 {
2335 rc2 = ShClPayloadInit(0 /* ID, unused */, pResp, cbResp, &pPayload);
2336 AssertRC(rc2);
2337 }
2338 }
2339 else
2340 rc = VERR_NO_MEMORY;
2341
2342 rc2 = ShClEventSignal(pReq->pEvent, pPayload);
2343 if (RT_SUCCESS(rc2))
2344 pPayload = NULL; /* The event owns the payload now. */
2345
2346 if (pPayload) /* Free payload on error. */
2347 {
2348 ShClPayloadFree(pPayload);
2349 pPayload = NULL;
2350 }
2351
2352 LogRel2(("Shared Clipboard: Converting X11 clipboard data completed with %Rrc\n", rc));
2353
2354 RTMemFree(pReq);
2355 RTMemFree(pvDst);
2356
2357 LogFlowFuncLeaveRC(rc);
2358}
2359
2360/**
2361 * Converts the data read from the X11 clipboard to the required format.
2362 *
2363 * @thread X11 event thread.
2364 */
2365SHCL_X11_DECL(void) clipConvertDataFromX11(Widget widget, XtPointer pClient,
2366 Atom * /* selection */, Atom *atomType,
2367 XtPointer pvSrc, long unsigned int *pcLen,
2368 int *piFormat)
2369{
2370 RT_NOREF(widget);
2371
2372 int rc = VINF_SUCCESS;
2373
2374 if (*atomType == XT_CONVERT_FAIL) /* Xt timeout */
2375 {
2376 LogRel(("Shared Clipboard: Reading clipboard data from X11 timed out\n"));
2377 rc = VERR_TIMEOUT;
2378 }
2379 else
2380 {
2381 PSHCLX11REQUEST pReq = (PSHCLX11REQUEST)pClient;
2382 if (pReq) /* Give some more clues, if available. */
2383 {
2384 AssertReturnVoid(pReq->enmType == SHCLX11EVENTTYPE_READ);
2385 char *pszFmts = ShClFormatsToStrA(pReq->Read.uFmtVBox);
2386 AssertPtrReturnVoid(pszFmts);
2387 AssertReturnVoid(pReq->Read.idxFmtX11 < SHCL_MAX_X11_FORMATS); /* Paranoia, should be checked already by the caller. */
2388 LogRel2(("Shared Clipboard: Converting X11 format '%s' -> VBox format(s) '%s'\n", g_aFormats[pReq->Read.idxFmtX11].pcszAtom, pszFmts));
2389 RTStrFree(pszFmts);
2390
2391 if (pReq->pCtx->Callbacks.pfnOnClipboardRead) /* Usually only used for testcases. */
2392 {
2393 void *pvData = NULL;
2394 size_t cbData = 0;
2395 rc = pReq->pCtx->Callbacks.pfnOnClipboardRead(pReq->pCtx->pFrontend, pReq->Read.uFmtVBox, &pvData, &cbData, NULL);
2396 if (RT_SUCCESS(rc))
2397 {
2398 /* Feed to conversion worker. */
2399 clipConvertDataFromX11Worker(pClient, pvData, cbData);
2400 RTMemFree(pvData);
2401 }
2402 }
2403 else /* Call conversion worker with current data provided by X (default). */
2404 clipConvertDataFromX11Worker(pClient, pvSrc, (*pcLen) * (*piFormat) / 8);
2405 }
2406 else
2407 rc = VERR_INVALID_POINTER;
2408 }
2409
2410 if (RT_FAILURE(rc))
2411 {
2412 LogRel(("Shared Clipboard: Reading clipboard data from X11 failed with %Rrc\n", rc));
2413
2414 /* Make sure to complete the request in any case by calling the conversion worker. */
2415 clipConvertDataFromX11Worker(pClient, NULL, 0);
2416 }
2417
2418 XtFree((char *)pvSrc);
2419}
2420
2421/**
2422 * Requests the current clipboard data from a specific selection.
2423 *
2424 * @returns VBox status code.
2425 * @param pCtx The X11 clipboard context to use.
2426 * @param pszWhere Clipboard selection to request the data from.
2427 * @param idxFmt The X11 format to request the data in.
2428 * @param pReq Where to store the requested data on success.
2429 */
2430static int clipGetSelectionValueEx(PSHCLX11CTX pCtx, const char *pszWhere, SHCLX11FMTIDX idxFmt,
2431 PSHCLX11REQUEST pReq)
2432{
2433 AssertPtrReturn(pszWhere, VERR_INVALID_POINTER);
2434 AssertReturn(idxFmt < SHCL_MAX_X11_FORMATS, VERR_INVALID_PARAMETER);
2435 AssertReturn(clipIsSupportedSelectionType(pCtx, clipGetAtom(pCtx, pszWhere)), VERR_INVALID_PARAMETER);
2436 AssertPtrReturn(pReq, VERR_INVALID_POINTER);
2437
2438 LogRel2(("Shared Clipboard: Requesting X11 selection value in %s for format '%s'\n", pszWhere, g_aFormats[idxFmt].pcszAtom));
2439
2440#ifndef TESTCASE
2441 XtGetSelectionValue(pCtx->pWidget, clipGetAtom(pCtx, pszWhere),
2442 clipAtomForX11Format(pCtx, idxFmt),
2443 clipConvertDataFromX11,
2444 reinterpret_cast<XtPointer>(pReq),
2445 CurrentTime);
2446#else
2447 tstClipRequestData(pCtx, idxFmt, (void *)pReq);
2448#endif
2449
2450 return VINF_SUCCESS; /** @todo Return real rc. */
2451}
2452
2453/**
2454 * Requests the current clipboard data from the CLIPBOARD selection.
2455 *
2456 * @returns VBox status code.
2457 * @param pCtx The X11 clipboard context to use.
2458 * @param idxFmt The X11 format to request the data in.
2459 * @param pReq Where to store the requested data on success.
2460 *
2461 * @sa clipGetSelectionValueEx() for requesting data for a specific selection.
2462 */
2463static int clipGetSelectionValue(PSHCLX11CTX pCtx, SHCLX11FMTIDX idxFmt, PSHCLX11REQUEST pReq)
2464{
2465 return clipGetSelectionValueEx(pCtx, "CLIPBOARD", idxFmt, pReq);
2466}
2467
2468/**
2469 * Worker function for ShClX11ReadDataFromX11Async.
2470 *
2471 * @param pvUserData Pointer to a CLIPREADX11CBREQ structure containing
2472 * information about the clipboard read request.
2473 * Must be free'd by the worker.
2474 * @thread X11 event thread.
2475 */
2476static void ShClX11ReadDataFromX11Worker(void *pvUserData, void * /* interval */)
2477{
2478 AssertPtrReturnVoid(pvUserData);
2479
2480 PSHCLX11REQUEST pReq = (PSHCLX11REQUEST)pvUserData;
2481 AssertReturnVoid(pReq->enmType == SHCLX11EVENTTYPE_READ);
2482 SHCLX11CTX *pCtx = pReq->pCtx;
2483 AssertPtrReturnVoid(pCtx);
2484
2485 LogFlowFunc(("pReq->uFmtVBox=%#x, idxFmtX11=%#x\n", pReq->Read.uFmtVBox, pReq->Read.idxFmtX11));
2486
2487 int rc = VERR_NO_DATA; /* VBox thinks we have data and we don't. */
2488
2489#ifdef VBOX_WITH_SHARED_CLIPBOARD_XT_BUSY
2490 const bool fXtBusy = clipGetXtBusy(pCtx);
2491 clipSetXtBusy(pCtx, true);
2492 if (fXtBusy)
2493 {
2494 /* If the clipboard is busy just fend off the request. */
2495 rc = VERR_TRY_AGAIN;
2496 }
2497 else
2498#endif
2499 if (pReq->Read.uFmtVBox & VBOX_SHCL_FMT_UNICODETEXT)
2500 {
2501 pReq->Read.idxFmtX11 = pCtx->idxFmtText;
2502 if (pReq->Read.idxFmtX11 != SHCLX11FMT_INVALID)
2503 {
2504 /* Send out a request for the data to the current clipboard owner. */
2505 rc = clipGetSelectionValue(pCtx, pCtx->idxFmtText, pReq);
2506 }
2507 }
2508 else if (pReq->Read.uFmtVBox & VBOX_SHCL_FMT_BITMAP)
2509 {
2510 pReq->Read.idxFmtX11 = pCtx->idxFmtBmp;
2511 if (pReq->Read.idxFmtX11 != SHCLX11FMT_INVALID)
2512 {
2513 /* Send out a request for the data to the current clipboard owner. */
2514 rc = clipGetSelectionValue(pCtx, pCtx->idxFmtBmp, pReq);
2515 }
2516 }
2517 else if (pReq->Read.uFmtVBox & VBOX_SHCL_FMT_HTML)
2518 {
2519 pReq->Read.idxFmtX11 = pCtx->idxFmtHTML;
2520 if (pReq->Read.idxFmtX11 != SHCLX11FMT_INVALID)
2521 {
2522 /* Send out a request for the data to the current clipboard owner. */
2523 rc = clipGetSelectionValue(pCtx, pCtx->idxFmtHTML, pReq);
2524 }
2525 }
2526#ifdef VBOX_WITH_SHARED_CLIPBOARD_TRANSFERS
2527 else if (pReq->Read.uFmtVBox & VBOX_SHCL_FMT_URI_LIST)
2528 {
2529 pReq->Read.idxFmtX11 = pCtx->idxFmtURI;
2530 if (pReq->Read.idxFmtX11 != SHCLX11FMT_INVALID)
2531 {
2532 /* Send out a request for the data to the current clipboard owner. */
2533 rc = clipGetSelectionValue(pCtx, pCtx->idxFmtURI, pReq);
2534 }
2535 }
2536#endif
2537 else
2538 {
2539#ifdef VBOX_WITH_SHARED_CLIPBOARD_XT_BUSY
2540 clipSetXtBusy(pCtx, false);
2541#endif
2542 rc = VERR_NOT_IMPLEMENTED;
2543 }
2544
2545 /* If the above stuff fails, make sure to let the waiters know.
2546 *
2547 * Getting the actual selection value via clipGetSelectionValue[Ex]() above will happen in the X event thread,
2548 * which has its own signalling then. So this check only handles errors which happens before we put anything
2549 * onto the X event thread.
2550 */
2551 if (RT_FAILURE(rc))
2552 {
2553 int rc2 = ShClEventSignalEx(pReq->pEvent, rc, NULL /* Payload */);
2554 AssertRC(rc2);
2555 }
2556
2557 LogRel2(("Shared Clipboard: Reading X11 clipboard data completed with %Rrc\n", rc));
2558
2559 LogFlowFuncLeaveRC(rc);
2560}
2561
2562/**
2563 * Reads the X11 clipboard (asynchronously).
2564 *
2565 * @returns VBox status code.
2566 * @retval VERR_NO_DATA if format is supported but no data is available currently.
2567 * @retval VERR_NOT_IMPLEMENTED if the format is not implemented.
2568 * @param pCtx Context data for the clipboard backend.
2569 * @param uFmt The format that the VBox would like to receive the data in.
2570 * @param cbMax Maximum data to read (in bytes).
2571 * Specify UINT32_MAX to read as much as available.
2572 * @param pEvent Event to use for waiting for data to arrive.
2573 * The event's payload will contain the data read. Needs to be free'd with ShClEventRelease().
2574 */
2575int ShClX11ReadDataFromX11Async(PSHCLX11CTX pCtx, SHCLFORMAT uFmt, uint32_t cbMax, PSHCLEVENT pEvent)
2576{
2577 AssertPtrReturn(pEvent, VERR_INVALID_POINTER);
2578 /*
2579 * Immediately return if we are not connected to the X server.
2580 */
2581 if (!pCtx->fHaveX11)
2582 return VERR_NO_DATA;
2583
2584 int rc = VINF_SUCCESS;
2585
2586 PSHCLX11REQUEST pReq = (PSHCLX11REQUEST)RTMemAllocZ(sizeof(SHCLX11REQUEST));
2587 if (pReq)
2588 {
2589 pReq->enmType = SHCLX11EVENTTYPE_READ;
2590 pReq->pCtx = pCtx;
2591 pReq->Read.uFmtVBox = uFmt;
2592 pReq->Read.cbMax = cbMax;
2593 pReq->pEvent = pEvent;
2594
2595 /* We use this to schedule a worker function on the event thread. */
2596 rc = clipThreadScheduleCall(pCtx, ShClX11ReadDataFromX11Worker, (XtPointer)pReq);
2597 if (RT_FAILURE(rc))
2598 RTMemFree(pReq);
2599 }
2600 else
2601 rc = VERR_NO_MEMORY;
2602
2603 LogFlowFuncLeaveRC(rc);
2604 return rc;
2605}
2606
2607/**
2608 * Reads the X11 clipboard.
2609 *
2610 * @returns VBox status code.
2611 * @retval VERR_NO_DATA if format is supported but no data is available currently.
2612 * @retval VERR_NOT_IMPLEMENTED if the format is not implemented.
2613 * @param pCtx Context data for the clipboard backend.
2614 * @param pEventSource Event source to use.
2615 * @param msTimeout Timeout (in ms) for waiting.
2616 * @param uFmt The format that the VBox would like to receive the data in.
2617 * @param pvBuf Where to store the received data on success.
2618 * @param cbBuf Size (in bytes) of \a pvBuf. Also marks maximum data to read (in bytes).
2619 * @param pcbRead Where to return the read bytes on success.
2620 */
2621int ShClX11ReadDataFromX11(PSHCLX11CTX pCtx, PSHCLEVENTSOURCE pEventSource, RTMSINTERVAL msTimeout,
2622 SHCLFORMAT uFmt, void *pvBuf, uint32_t cbBuf, uint32_t *pcbRead)
2623{
2624 AssertPtrReturn(pCtx, VERR_INVALID_POINTER);
2625 AssertPtrReturn(pEventSource, VERR_INVALID_POINTER);
2626 AssertPtrReturn(pvBuf, VERR_INVALID_POINTER);
2627 AssertReturn(cbBuf, VERR_INVALID_PARAMETER);
2628 AssertPtrReturn(pcbRead, VERR_INVALID_POINTER);
2629
2630 PSHCLEVENT pEvent;
2631 int rc = ShClEventSourceGenerateAndRegisterEvent(pEventSource, &pEvent);
2632 if (RT_SUCCESS(rc))
2633 {
2634 rc = ShClX11ReadDataFromX11Async(pCtx, uFmt, cbBuf, pEvent);
2635 if (RT_SUCCESS(rc))
2636 {
2637 int rcEvent;
2638 PSHCLEVENTPAYLOAD pPayload;
2639 rc = ShClEventWaitEx(pEvent, msTimeout, &rcEvent, &pPayload);
2640 if (RT_SUCCESS(rc))
2641 {
2642 if (pPayload)
2643 {
2644 AssertReturn(pPayload->cbData == sizeof(SHCLX11RESPONSE), VERR_INVALID_PARAMETER);
2645 AssertPtrReturn(pPayload->pvData, VERR_INVALID_POINTER);
2646 PSHCLX11RESPONSE pResp = (PSHCLX11RESPONSE)pPayload->pvData;
2647 AssertReturn(pResp->enmType == SHCLX11EVENTTYPE_READ, VERR_INVALID_PARAMETER);
2648
2649 memcpy(pvBuf, pResp->Read.pvData, RT_MIN(cbBuf, pResp->Read.cbData));
2650 *pcbRead = pResp->Read.cbData;
2651
2652 RTMemFree(pResp->Read.pvData);
2653 pResp->Read.cbData = 0;
2654
2655 ShClPayloadFree(pPayload);
2656 }
2657 else /* No payload given; could happen on invalid / not-expected formats. */
2658 {
2659 rc = VERR_NO_DATA;
2660 *pcbRead = 0;
2661 }
2662 }
2663 else if (rc == VERR_SHCLPB_EVENT_FAILED)
2664 rc = rcEvent;
2665 }
2666 }
2667
2668 LogFlowFuncLeaveRC(rc);
2669 return rc;
2670}
2671
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