VirtualBox

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

Last change on this file since 103914 was 103428, checked in by vboxsync, 10 months ago

Shared Clipboard: Moved debug assertion within shClX11ResponseFree(). bugref:9437

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