VirtualBox

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

最後變更 在這個檔案從103363是 103363,由 vboxsync 提交於 13 月 前

Shared Clipboard: Added a dedicated VERR_SHCLPB_NO_DATA error code, to indicate that clipboard data for a format currently is not available.

  • 屬性 svn:eol-style 設為 native
  • 屬性 svn:keywords 設為 Author Date Id Revision
檔案大小: 99.1 KB
 
1/** @file
2 * Shared Clipboard: Common X11 code.
3 */
4
5/*
6 * Copyright (C) 2006-2023 Oracle and/or its affiliates.
7 *
8 * This file is part of VirtualBox base platform packages, as
9 * available from https://www.alldomusa.eu.org.
10 *
11 * This program is free software; you can redistribute it and/or
12 * modify it under the terms of the GNU General Public License
13 * as published by the Free Software Foundation, in version 3 of the
14 * License.
15 *
16 * This program is distributed in the hope that it will be useful, but
17 * WITHOUT ANY WARRANTY; without even the implied warranty of
18 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
19 * General Public License for more details.
20 *
21 * You should have received a copy of the GNU General Public License
22 * along with this program; if not, see <https://www.gnu.org/licenses>.
23 *
24 * SPDX-License-Identifier: GPL-3.0-only
25 */
26
27/* Note: to automatically run regression tests on the Shared Clipboard,
28 * execute the tstClipboardGH-X11 testcase. If you often make changes to the
29 * clipboard code, adding the line
30 *
31 * OTHERS += $(PATH_tstClipboardGH-X11)/tstClipboardGH-X11.run
32 *
33 * to LocalConfig.kmk will cause the tests to be run every time the code is
34 * changed.
35 */
36
37
38/*********************************************************************************************************************************
39* Header Files *
40*********************************************************************************************************************************/
41#define LOG_GROUP LOG_GROUP_SHARED_CLIPBOARD
42
43#include <errno.h>
44
45#include <dlfcn.h>
46#include <fcntl.h>
47#include <unistd.h>
48
49#ifdef RT_OS_SOLARIS
50#include <tsol/label.h>
51#endif
52
53#include <X11/Xlib.h>
54#include <X11/Xatom.h>
55#include <X11/Intrinsic.h>
56#include <X11/Shell.h>
57#include <X11/Xproto.h>
58#include <X11/StringDefs.h>
59
60#include <iprt/assert.h>
61#include <iprt/types.h>
62#include <iprt/mem.h>
63#include <iprt/semaphore.h>
64#include <iprt/thread.h>
65#include <iprt/utf16.h>
66#include <iprt/uri.h>
67
68#ifdef VBOX_WITH_SHARED_CLIPBOARD_TRANSFERS
69# include <iprt/cpp/list.h>
70# include <iprt/cpp/ministring.h>
71# include <VBox/GuestHost/SharedClipboard-transfers.h>
72#endif
73
74#include <VBox/log.h>
75#include <VBox/version.h>
76
77#include <VBox/GuestHost/SharedClipboard.h>
78#include <VBox/GuestHost/SharedClipboard-x11.h>
79#include <VBox/GuestHost/clipboard-helper.h>
80#include <VBox/HostServices/VBoxClipboardSvc.h>
81
82/** Own macro for declaring function visibility / linkage based on whether this
83 * code runs as part of test cases or not. */
84#ifdef TESTCASE
85# define SHCL_X11_DECL(x) x
86#else
87# define SHCL_X11_DECL(x) static x
88#endif
89
90
91/*********************************************************************************************************************************
92* Externals *
93*********************************************************************************************************************************/
94#ifdef TESTCASE
95extern void tstThreadScheduleCall(void (*proc)(void *, void *), void *client_data);
96extern void tstClipRequestData(SHCLX11CTX* pCtx, SHCLX11FMTIDX target, void *closure);
97extern void tstRequestTargets(SHCLX11CTX* pCtx);
98#endif
99
100
101/*********************************************************************************************************************************
102* Prototypes *
103*********************************************************************************************************************************/
104class formats;
105SHCL_X11_DECL(Atom) clipGetAtom(PSHCLX11CTX pCtx, const char *pszName);
106SHCL_X11_DECL(void) clipQueryX11Targets(PSHCLX11CTX pCtx);
107
108static int clipInitInternal(PSHCLX11CTX pCtx);
109static void clipUninitInternal(PSHCLX11CTX pCtx);
110
111
112/*********************************************************************************************************************************
113* Global Variables *
114*********************************************************************************************************************************/
115
116/**
117 * The table maps X11 names to data formats
118 * and to the corresponding VBox clipboard formats.
119 */
120SHCL_X11_DECL(SHCLX11FMTTABLE) g_aFormats[] =
121{
122 { "INVALID", SHCLX11FMT_INVALID, VBOX_SHCL_FMT_NONE },
123
124 { "UTF8_STRING", SHCLX11FMT_UTF8, VBOX_SHCL_FMT_UNICODETEXT },
125 { "text/plain;charset=UTF-8", SHCLX11FMT_UTF8, VBOX_SHCL_FMT_UNICODETEXT },
126 { "text/plain;charset=utf-8", SHCLX11FMT_UTF8, VBOX_SHCL_FMT_UNICODETEXT },
127 { "STRING", SHCLX11FMT_TEXT, VBOX_SHCL_FMT_UNICODETEXT },
128 { "TEXT", SHCLX11FMT_TEXT, VBOX_SHCL_FMT_UNICODETEXT },
129 { "text/plain", SHCLX11FMT_TEXT, VBOX_SHCL_FMT_UNICODETEXT },
130
131 { "text/html", SHCLX11FMT_HTML, VBOX_SHCL_FMT_HTML },
132 { "text/html;charset=utf-8", SHCLX11FMT_HTML, VBOX_SHCL_FMT_HTML },
133 { "application/x-moz-nativehtml", SHCLX11FMT_HTML, VBOX_SHCL_FMT_HTML },
134
135 { "image/bmp", SHCLX11FMT_BMP, VBOX_SHCL_FMT_BITMAP },
136 { "image/x-bmp", SHCLX11FMT_BMP, VBOX_SHCL_FMT_BITMAP },
137 { "image/x-MS-bmp", SHCLX11FMT_BMP, VBOX_SHCL_FMT_BITMAP },
138 /** @todo Inkscape exports image/png but not bmp... */
139
140#ifdef VBOX_WITH_SHARED_CLIPBOARD_TRANSFERS
141 { "text/uri-list", SHCLX11FMT_URI_LIST, VBOX_SHCL_FMT_URI_LIST },
142 { "x-special/gnome-copied-files", SHCLX11FMT_URI_LIST_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 Assert(pResp->Read.cbData);
1590 RTMemFree(pResp->Read.pvData);
1591 break;
1592 }
1593
1594 case SHCLX11EVENTTYPE_REPORT_FORMATS:
1595 RT_FALL_THROUGH();
1596 case SHCLX11EVENTTYPE_WRITE:
1597 RT_FALL_THROUGH();
1598 default:
1599 break;
1600 }
1601
1602 RTMemFree(pResp);
1603}
1604
1605/**
1606 * Satisfies a request from X11 to convert the clipboard text to UTF-8 LF.
1607 *
1608 * @returns VBox status code. VERR_SHCLPB_NO_DATA if no data was converted.
1609 * @param pDisplay An X11 display structure, needed for conversions
1610 * performed by Xlib.
1611 * @param pv The text to be converted (UCS-2 with Windows EOLs).
1612 * @param cb The length of the text in @cb in bytes.
1613 * @param atomTypeReturn Where to store the atom for the type of the data
1614 * we are returning.
1615 * @param pValReturn Where to store the pointer to the data we are
1616 * returning. This should be to memory allocated by
1617 * XtMalloc, which will be freed by the Xt toolkit
1618 * later.
1619 * @param pcLenReturn Where to store the length of the data we are
1620 * returning.
1621 * @param piFormatReturn Where to store the bit width (8, 16, 32) of the
1622 * data we are returning.
1623 */
1624static int clipConvertUtf16ToX11Data(Display *pDisplay, PRTUTF16 pwszSrc,
1625 size_t cbSrc, Atom *atomTarget,
1626 Atom *atomTypeReturn,
1627 XtPointer *pValReturn,
1628 unsigned long *pcLenReturn,
1629 int *piFormatReturn)
1630{
1631 RT_NOREF(pDisplay);
1632 AssertReturn(cbSrc % sizeof(RTUTF16) == 0, VERR_INVALID_PARAMETER);
1633
1634 const size_t cwcSrc = cbSrc / sizeof(RTUTF16);
1635 if (!cwcSrc)
1636 return VERR_SHCLPB_NO_DATA;
1637
1638 /* This may slightly overestimate the space needed. */
1639 size_t chDst = 0;
1640 int rc = ShClUtf16LenUtf8(pwszSrc, cwcSrc, &chDst);
1641 if (RT_SUCCESS(rc))
1642 {
1643 chDst++; /* Add space for terminator. */
1644
1645 char *pszDst = (char *)XtMalloc(chDst);
1646 if (pszDst)
1647 {
1648 size_t cbActual = 0;
1649 rc = ShClConvUtf16CRLFToUtf8LF(pwszSrc, cwcSrc, pszDst, chDst, &cbActual);
1650 if (RT_SUCCESS(rc))
1651 {
1652 *atomTypeReturn = *atomTarget;
1653 *pValReturn = (XtPointer)pszDst;
1654 *pcLenReturn = cbActual + 1 /* Include terminator */;
1655 *piFormatReturn = 8;
1656 }
1657 }
1658 else
1659 rc = VERR_NO_MEMORY;
1660 }
1661
1662 LogFlowFuncLeaveRC(rc);
1663 return rc;
1664}
1665
1666/**
1667 * Satisfies a request from X11 to convert the clipboard HTML fragment to UTF-8. We
1668 * return null-terminated text, but can cope with non-null-terminated input.
1669 *
1670 * @returns VBox status code.
1671 * @param pDisplay An X11 display structure, needed for conversions
1672 * performed by Xlib.
1673 * @param pv The text to be converted (UTF8 with Windows EOLs).
1674 * @param cb The length of the text in @cb in bytes.
1675 * @param atomTypeReturn Where to store the atom for the type of the data
1676 * we are returning.
1677 * @param pValReturn Where to store the pointer to the data we are
1678 * returning. This should be to memory allocated by
1679 * XtMalloc, which will be freed by the Xt toolkit later.
1680 * @param pcLenReturn Where to store the length of the data we are returning.
1681 * @param piFormatReturn Where to store the bit width (8, 16, 32) of the
1682 * data we are returning.
1683 */
1684static int clipConvertHtmlToX11Data(Display *pDisplay, const char *pszSrc,
1685 size_t cbSrc, Atom *atomTarget,
1686 Atom *atomTypeReturn,
1687 XtPointer *pValReturn,
1688 unsigned long *pcLenReturn,
1689 int *piFormatReturn)
1690{
1691 RT_NOREF(pDisplay, pValReturn);
1692
1693 /* This may slightly overestimate the space needed. */
1694 LogFlowFunc(("Source: %s", pszSrc));
1695
1696 char *pszDest = (char *)XtMalloc(cbSrc);
1697 if (pszDest == NULL)
1698 return VERR_NO_MEMORY;
1699
1700 memcpy(pszDest, pszSrc, cbSrc);
1701
1702 *atomTypeReturn = *atomTarget;
1703 *pValReturn = (XtPointer)pszDest;
1704 *pcLenReturn = cbSrc;
1705 *piFormatReturn = 8;
1706
1707 return VINF_SUCCESS;
1708}
1709
1710
1711/**
1712 * Does this atom correspond to one of the two selection types we support?
1713 *
1714 * @param pCtx The X11 clipboard context to use.
1715 * @param selType The atom in question.
1716 */
1717static bool clipIsSupportedSelectionType(PSHCLX11CTX pCtx, Atom selType)
1718{
1719 return( (selType == clipGetAtom(pCtx, "CLIPBOARD"))
1720 || (selType == clipGetAtom(pCtx, "PRIMARY")));
1721}
1722
1723/**
1724 * Removes a trailing nul character from a string by adjusting the string
1725 * length. Some X11 applications don't like zero-terminated text...
1726 *
1727 * @param pText The text in question.
1728 * @param pcText The length of the text, adjusted on return.
1729 * @param format The format of the text.
1730 */
1731static void clipTrimTrailingNul(XtPointer pText, unsigned long *pcText,
1732 SHCLX11FMT format)
1733{
1734 AssertPtrReturnVoid(pText);
1735 AssertPtrReturnVoid(pcText);
1736 AssertReturnVoid((format == SHCLX11FMT_UTF8) || (format == SHCLX11FMT_TEXT) || (format == SHCLX11FMT_HTML));
1737
1738 if (((char *)pText)[*pcText - 1] == '\0')
1739 --(*pcText);
1740}
1741
1742static int clipConvertToX11Data(PSHCLX11CTX pCtx, Atom *atomTarget,
1743 Atom *atomTypeReturn,
1744 XtPointer *pValReturn,
1745 unsigned long *pcLenReturn,
1746 int *piFormatReturn)
1747{
1748 int rc = VERR_NOT_SUPPORTED; /* Play safe by default. */
1749
1750 SHCLX11FMTIDX idxFmtX11 = clipFindX11FormatByAtom(pCtx, *atomTarget);
1751 SHCLX11FMT fmtX11 = clipRealFormatForX11Format(idxFmtX11);
1752
1753 LogFlowFunc(("vboxFormats=0x%x, idxFmtX11=%u ('%s'), fmtX11=%u\n",
1754 pCtx->vboxFormats, idxFmtX11, g_aFormats[idxFmtX11].pcszAtom, fmtX11));
1755
1756 char *pszFmts = ShClFormatsToStrA(pCtx->vboxFormats);
1757 AssertPtrReturn(pszFmts, VERR_NO_MEMORY);
1758 LogRel2(("Shared Clipboard: Converting VBox formats '%s' to '%s' for X11\n",
1759 pszFmts, fmtX11 == SHCLX11FMT_INVALID ? "<invalid>" : g_aFormats[idxFmtX11].pcszAtom));
1760 RTStrFree(pszFmts);
1761
1762 void *pv = NULL;
1763 uint32_t cb = 0;
1764
1765 if ( ( (fmtX11 == SHCLX11FMT_UTF8)
1766 || (fmtX11 == SHCLX11FMT_TEXT)
1767 )
1768 && (pCtx->vboxFormats & VBOX_SHCL_FMT_UNICODETEXT))
1769 {
1770 rc = shClX11RequestDataForX11CallbackHelper(pCtx, VBOX_SHCL_FMT_UNICODETEXT, &pv, &cb);
1771 if ( RT_SUCCESS(rc)
1772 && ( (fmtX11 == SHCLX11FMT_UTF8)
1773 || (fmtX11 == SHCLX11FMT_TEXT)))
1774 {
1775 rc = clipConvertUtf16ToX11Data(XtDisplay(pCtx->pWidget),
1776 (PRTUTF16)pv, cb, atomTarget,
1777 atomTypeReturn, pValReturn,
1778 pcLenReturn, piFormatReturn);
1779 }
1780
1781 if (RT_SUCCESS(rc))
1782 clipTrimTrailingNul(*(XtPointer *)pValReturn, pcLenReturn, fmtX11);
1783
1784 RTMemFree(pv);
1785 }
1786 else if ( (fmtX11 == SHCLX11FMT_BMP)
1787 && (pCtx->vboxFormats & VBOX_SHCL_FMT_BITMAP))
1788 {
1789 rc = shClX11RequestDataForX11CallbackHelper(pCtx, VBOX_SHCL_FMT_BITMAP, &pv, &cb);
1790 if ( RT_SUCCESS(rc)
1791 && (fmtX11 == SHCLX11FMT_BMP))
1792 {
1793 /* Create a full BMP from it. */
1794 rc = ShClDibToBmp(pv, cb, (void **)pValReturn,
1795 (size_t *)pcLenReturn);
1796 }
1797
1798 if (RT_SUCCESS(rc))
1799 {
1800 *atomTypeReturn = *atomTarget;
1801 *piFormatReturn = 8;
1802 }
1803
1804 RTMemFree(pv);
1805 }
1806 else if ( (fmtX11 == SHCLX11FMT_HTML)
1807 && (pCtx->vboxFormats & VBOX_SHCL_FMT_HTML))
1808 {
1809 rc = shClX11RequestDataForX11CallbackHelper(pCtx, VBOX_SHCL_FMT_HTML, &pv, &cb);
1810 if (RT_SUCCESS(rc))
1811 {
1812 /**
1813 * The common VBox HTML encoding will be UTF-8.
1814 * Before sending it to the X11 clipboard we have to convert it to UTF-8 first.
1815 *
1816 * Strange that we get UTF-16 from the X11 clipboard, but
1817 * in same time we send UTF-8 to X11 clipboard and it works.
1818 ** @todo r=andy Verify this.
1819 */
1820 rc = clipConvertHtmlToX11Data(XtDisplay(pCtx->pWidget),
1821 (const char*)pv, cb, atomTarget,
1822 atomTypeReturn, pValReturn,
1823 pcLenReturn, piFormatReturn);
1824 if (RT_SUCCESS(rc))
1825 clipTrimTrailingNul(*(XtPointer *)pValReturn, pcLenReturn, fmtX11);
1826
1827 RTMemFree(pv);
1828 }
1829 }
1830#ifdef VBOX_WITH_SHARED_CLIPBOARD_TRANSFERS
1831 else if ( fmtX11 == SHCLX11FMT_URI_LIST
1832 || fmtX11 == SHCLX11FMT_URI_LIST_GNOME_COPIED_FILES
1833 /** @todo BUGBUG Not sure about the following ones; test those. */
1834 || fmtX11 == SHCLX11FMT_URI_LIST_MATE_COPIED_FILES
1835 || fmtX11 == SHCLX11FMT_URI_LIST_NAUTILUS_CLIPBOARD
1836 || fmtX11 == SHCLX11FMT_URI_LIST_KDE_CUTSELECTION)
1837 {
1838 if (pCtx->vboxFormats & VBOX_SHCL_FMT_URI_LIST)
1839 {
1840 rc = shClX11RequestDataForX11CallbackHelper(pCtx, VBOX_SHCL_FMT_URI_LIST, &pv, &cb);
1841 if (RT_SUCCESS(rc))
1842 {
1843 void *pvX11;
1844 size_t cbX11;
1845 rc = ShClX11TransferConvertToX11((const char *)pv, cb, fmtX11, &pvX11, &cbX11);
1846 if (RT_SUCCESS(rc))
1847 {
1848 *atomTypeReturn = *atomTarget;
1849 *pValReturn = (XtPointer)pvX11;
1850 *pcLenReturn = cbX11;
1851 *piFormatReturn = 8;
1852 }
1853 }
1854
1855 RTMemFree(pv);
1856 pv = NULL;
1857 }
1858 /* else not supported yet. */
1859 }
1860#endif /* VBOX_WITH_SHARED_CLIPBOARD_TRANSFERS */
1861 else
1862 {
1863 *atomTypeReturn = XT_CONVERT_FAIL;
1864 *pValReturn = (XtPointer)NULL;
1865 *pcLenReturn = 0;
1866 *piFormatReturn = 0;
1867 }
1868
1869 if ( RT_FAILURE(rc)
1870 && rc != VERR_SHCLPB_NO_DATA)
1871 {
1872 char *pszFmts2 = ShClFormatsToStrA(pCtx->vboxFormats);
1873 char *pszAtomName = XGetAtomName(XtDisplay(pCtx->pWidget), *atomTarget);
1874
1875 LogRel(("Shared Clipboard: Converting VBox formats '%s' to '%s' for X11 (idxFmtX11=%u, fmtX11=%u, atomTarget='%s') failed, rc=%Rrc\n",
1876 pszFmts2 ? pszFmts2 : "unknown", g_aFormats[idxFmtX11].pcszAtom, idxFmtX11, fmtX11, pszAtomName ? pszAtomName : "unknown", rc));
1877
1878 if (pszFmts2)
1879 RTStrFree(pszFmts2);
1880 if (pszAtomName)
1881 XFree(pszAtomName);
1882 }
1883
1884 LogFlowFuncLeaveRC(rc);
1885 return rc;
1886}
1887
1888/**
1889 * Returns VBox's clipboard data for an X11 client.
1890 *
1891 * @note Callback for XtOwnSelection.
1892 */
1893static Boolean clipXtConvertSelectionProc(Widget widget, Atom *atomSelection,
1894 Atom *atomTarget,
1895 Atom *atomTypeReturn,
1896 XtPointer *pValReturn,
1897 unsigned long *pcLenReturn,
1898 int *piFormatReturn)
1899{
1900 LogFlowFuncEnter();
1901
1902 PSHCLX11CTX pCtx = clipLookupContext(widget);
1903 if (!pCtx)
1904 return False;
1905
1906 /* Is this the rigt selection (clipboard) we were asked for? */
1907 if (!clipIsSupportedSelectionType(pCtx, *atomSelection))
1908 return False;
1909
1910 int rc;
1911 if (*atomTarget == clipGetAtom(pCtx, "TARGETS"))
1912 rc = clipCreateX11Targets(pCtx, atomTypeReturn, pValReturn,
1913 pcLenReturn, piFormatReturn);
1914 else
1915 rc = clipConvertToX11Data(pCtx, atomTarget, atomTypeReturn,
1916 pValReturn, pcLenReturn, piFormatReturn);
1917
1918#if 0 /** @todo Disabled -- crashes when running with tstClipboardGH-X11. */
1919 XSelectionRequestEvent* pReq =
1920 XtGetSelectionRequest(widget, *atomSelection, (XtRequestId)NULL);
1921 LogFlowFunc(("returning pVBoxWnd=%#x, ownerWnd=%#x, reqWnd=%#x, %RTbool, rc=%Rrc\n",
1922 XtWindow(pCtx->pWidget), pReq->owner, pReq->requestor, RT_SUCCESS(rc), rc));
1923#endif
1924 return RT_SUCCESS(rc) ? True : False;
1925}
1926
1927static void clipXtConvertSelectionProcLose(Widget widget, Atom *atomSelection)
1928{
1929 RT_NOREF(widget, atomSelection);
1930 LogFlowFuncEnter();
1931}
1932
1933static void clipXtConvertSelectionProcDone(Widget widget, Atom *atomSelection, Atom *atomTarget)
1934{
1935 RT_NOREF(widget, atomSelection, atomTarget);
1936 LogFlowFuncEnter();
1937}
1938
1939/**
1940 * Invalidates the local clipboard cache.
1941 *
1942 * @param pCtx The X11 clipboard context to use.
1943 */
1944static void clipInvalidateClipboardCache(PSHCLX11CTX pCtx)
1945{
1946 LogFlowFuncEnter();
1947
1948 ShClCacheInvalidate(&pCtx->Cache);
1949}
1950
1951/**
1952 * Takes possession of the X11 clipboard (and middle-button selection).
1953 *
1954 * @param pCtx The X11 clipboard context to use.
1955 * @param uFormats Clipboard formats to set.
1956 */
1957static void clipGrabX11Clipboard(PSHCLX11CTX pCtx, SHCLFORMATS uFormats)
1958{
1959 LogFlowFuncEnter();
1960
1961 /** @ŧodo r=andy The docs say: "the value CurrentTime is not acceptable" here!? */
1962 if (XtOwnSelection(pCtx->pWidget, clipGetAtom(pCtx, "CLIPBOARD"),
1963 CurrentTime,
1964 clipXtConvertSelectionProc, clipXtConvertSelectionProcLose, clipXtConvertSelectionProcDone))
1965 {
1966 pCtx->vboxFormats = uFormats;
1967
1968 /* Grab the middle-button paste selection too. */
1969 XtOwnSelection(pCtx->pWidget, clipGetAtom(pCtx, "PRIMARY"),
1970 CurrentTime, clipXtConvertSelectionProc, NULL, 0);
1971#ifndef TESTCASE
1972 /* Xt suppresses these if we already own the clipboard, so send them
1973 * ourselves. */
1974 XSetSelectionOwner(XtDisplay(pCtx->pWidget),
1975 clipGetAtom(pCtx, "CLIPBOARD"),
1976 XtWindow(pCtx->pWidget), CurrentTime);
1977 XSetSelectionOwner(XtDisplay(pCtx->pWidget),
1978 clipGetAtom(pCtx, "PRIMARY"),
1979 XtWindow(pCtx->pWidget), CurrentTime);
1980#endif
1981 }
1982}
1983
1984/**
1985 * Worker function for ShClX11ReportFormatsToX11Async.
1986 *
1987 * @param pvUserData Pointer to a CLIPNEWVBOXFORMATS structure containing
1988 * information about the VBox formats available and the
1989 * clipboard context data. Must be freed by the worker.
1990 *
1991 * @thread X11 event thread.
1992 */
1993static void shClX11ReportFormatsToX11Worker(void *pvUserData, void * /* interval */)
1994{
1995 AssertPtrReturnVoid(pvUserData);
1996
1997 PSHCLX11REQUEST pReq = (PSHCLX11REQUEST)pvUserData;
1998 AssertReturnVoid(pReq->enmType == SHCLX11EVENTTYPE_REPORT_FORMATS);
1999
2000 PSHCLX11CTX pCtx = pReq->pCtx;
2001 SHCLFORMATS fFormats = pReq->Formats.fFormats;
2002
2003 RTMemFree(pReq);
2004
2005#ifdef LOG_ENABLED
2006 char *pszFmts = ShClFormatsToStrA(fFormats);
2007 AssertPtrReturnVoid(pszFmts);
2008 LogRel2(("Shared Clipboard: Reported available VBox formats %s to X11\n", pszFmts));
2009 RTStrFree(pszFmts);
2010#endif
2011
2012 clipInvalidateClipboardCache(pCtx);
2013 clipGrabX11Clipboard(pCtx, fFormats);
2014 clipResetX11Formats(pCtx);
2015
2016 LogFlowFuncLeave();
2017}
2018
2019/**
2020 * Announces new clipboard formats to the X11 clipboard.
2021 *
2022 * @returns VBox status code.
2023 * @param pCtx Context data for the clipboard backend.
2024 * @param uFormats Clipboard formats offered.
2025 *
2026 * @note When calling this function, data for the clipboard already has to be available,
2027 * as we grab the clipboard, which in turn then calls the X11 data conversion callback.
2028 */
2029int ShClX11ReportFormatsToX11Async(PSHCLX11CTX pCtx, SHCLFORMATS uFormats)
2030{
2031 if (shClX11HeadlessIsEnabled(pCtx))
2032 return VINF_SUCCESS;
2033
2034 int rc;
2035
2036 PSHCLX11REQUEST pReq = (PSHCLX11REQUEST)RTMemAllocZ(sizeof(SHCLX11REQUEST));
2037 if (pReq)
2038 {
2039 pReq->enmType = SHCLX11EVENTTYPE_REPORT_FORMATS;
2040 pReq->pCtx = pCtx;
2041 pReq->Formats.fFormats = uFormats;
2042
2043 rc = clipThreadScheduleCall(pCtx, shClX11ReportFormatsToX11Worker, (XtPointer)pReq);
2044 if (RT_FAILURE(rc))
2045 RTMemFree(pReq);
2046 }
2047 else
2048 rc = VERR_NO_MEMORY;
2049
2050 LogFlowFuncLeaveRC(rc);
2051 return rc;
2052}
2053
2054#ifdef VBOX_WITH_SHARED_CLIPBOARD_TRANSFERS
2055/**
2056 * Converts transfer data to a format returned back to X11.
2057 *
2058 * @returns VBox status code.
2059 * @param pszSrc Transfer data to convert.
2060 * @param cbSrc Size of transfer data (in bytes) to convert.
2061 * @param enmFmtX11 X11 format to convert data to.
2062 * @param ppvDst Where to return converted data on success. Must be free'd with XtFree().
2063 * @param pcbDst Where to return the bytes of the converted data on success. Optional.
2064 */
2065int ShClX11TransferConvertToX11(const char *pszSrc, size_t cbSrc, SHCLX11FMT enmFmtX11, void **ppvDst, size_t *pcbDst)
2066{
2067 AssertPtrReturn(pszSrc, VERR_INVALID_POINTER);
2068 AssertReturn(cbSrc, VERR_INVALID_PARAMETER);
2069 AssertPtrReturn(ppvDst, VERR_INVALID_POINTER);
2070 /* pcbDst is optional. */
2071
2072 int rc = VINF_SUCCESS;
2073
2074 char *pszDst = NULL;
2075
2076# ifdef DEBUG_andy
2077 LogFlowFunc(("Src:\n%.*RhXd\n", cbSrc, pszSrc));
2078# endif
2079
2080 switch (enmFmtX11)
2081 {
2082 case SHCLX11FMT_URI_LIST_GNOME_COPIED_FILES:
2083 RT_FALL_THROUGH();
2084 case SHCLX11FMT_URI_LIST_MATE_COPIED_FILES:
2085 RT_FALL_THROUGH();
2086 case SHCLX11FMT_URI_LIST_NAUTILUS_CLIPBOARD:
2087 RT_FALL_THROUGH();
2088 case SHCLX11FMT_URI_LIST_KDE_CUTSELECTION:
2089 {
2090 const char chSep = '\n'; /* Currently (?) all entries need to be separated by '\n'. */
2091
2092 /* Note: There must be *no* final new line ('\n') at the end, otherwise Nautilus will crash! */
2093 pszDst = RTStrAPrintf2("copy%c%s", chSep, pszSrc);
2094 if (!pszDst)
2095 rc = VERR_NO_MEMORY;
2096 break;
2097 }
2098
2099 case SHCLX11FMT_URI_LIST:
2100 {
2101 pszDst = RTStrDup(pszSrc);
2102 AssertPtrBreakStmt(pszDst, rc = VERR_NO_MEMORY);
2103 break;
2104 }
2105
2106 default:
2107 AssertFailed(); /* Most likely a bug in the code; let me know. */
2108 break;
2109 }
2110
2111 if (RT_SUCCESS(rc))
2112 {
2113 size_t const cbDst = RTStrNLen(pszDst, RTSTR_MAX);
2114 void *pvDst = (void *)XtMalloc(cbDst);
2115 if (pvDst)
2116 {
2117 memcpy(pvDst, pszDst, cbDst);
2118# ifdef DEBUG_andy
2119 LogFlowFunc(("Dst:\n%.*RhXd\n", cbDst, pvDst));
2120# endif
2121 }
2122 else
2123 rc = VERR_NO_MEMORY;
2124
2125 if (pcbDst)
2126 *pcbDst = cbDst;
2127 *ppvDst = pvDst;
2128
2129 RTStrFree(pszDst);
2130 pszDst = NULL;
2131 }
2132
2133 LogFlowFuncLeaveRC(rc);
2134 return rc;
2135}
2136
2137/**
2138 * Converts X11 data to a string list usable for transfers.
2139 *
2140 * @returns VBox status code.
2141 * @param pvData Data to conver to a string list.
2142 * @param cbData Size (in bytes) of \a pvData.
2143 * @param ppszList Where to return the allocated string list on success.
2144 * Must be free'd with RTStrFree().
2145 * @param pcbList Size (in bytes) of the returned string list on success.
2146 * Includes terminator.
2147 */
2148int ShClX11TransferConvertFromX11(const char *pvData, size_t cbData, char **ppszList, size_t *pcbList)
2149{
2150 AssertPtrReturn(pvData, VERR_INVALID_POINTER);
2151 AssertReturn(cbData, VERR_INVALID_PARAMETER);
2152 AssertPtrNullReturn(ppszList, VERR_INVALID_POINTER);
2153 AssertPtrReturn(pcbList, VERR_INVALID_POINTER);
2154
2155 /* For URI lists we only accept valid UTF-8 encodings. */
2156 int rc = RTStrValidateEncodingEx((char *)pvData, cbData, 0 /* fFlags */);
2157 if (RT_FAILURE(rc))
2158 return rc;
2159
2160 /* We might need to skip some prefixes before actually reaching the file list. */
2161 static const char *s_aszPrefixesToSkip[] =
2162 { "copy\n" /* Nautilus / Nemo */ };
2163 for (size_t i = 0; i < RT_ELEMENTS(s_aszPrefixesToSkip); i++)
2164 {
2165 const char *pszNeedle = RTStrStr(pvData, s_aszPrefixesToSkip[i]);
2166 if (pszNeedle)
2167 {
2168 size_t const cbNeedle = strlen(s_aszPrefixesToSkip[i]);
2169 pszNeedle += cbNeedle;
2170 pvData = pszNeedle;
2171 Assert(cbData >= cbNeedle);
2172 cbData -= cbNeedle;
2173 }
2174 }
2175
2176 *pcbList = 0;
2177
2178# ifdef DEBUG_andy
2179 LogFlowFunc(("Data:\n%.*RhXd\n", cbData, pvData));
2180# endif
2181
2182 char **papszStrings;
2183 size_t cStrings;
2184 rc = RTStrSplit(pvData, cbData, SHCL_TRANSFER_URI_LIST_SEP_STR, &papszStrings, &cStrings);
2185 if (RT_SUCCESS(rc))
2186 {
2187 for (size_t i = 0; i < cStrings; i++)
2188 {
2189 const char *pszString = papszStrings[i];
2190 LogRel2(("Shared Clipboard: Received entry #%zu from X11: '%s'\n", i, pszString));
2191 rc = RTStrAAppend(ppszList, pszString);
2192 if (RT_FAILURE(rc))
2193 break;
2194 *pcbList += strlen(pszString);
2195 }
2196
2197 *pcbList++; /* Include terminator. */
2198 }
2199
2200 return rc;
2201}
2202#endif /* VBOX_WITH_SHARED_CLIPBOARD_TRANSFERS */
2203
2204/**
2205 * Worker function for clipConvertDataFromX11.
2206 *
2207 * Converts the data read from the X11 clipboard to the required format.
2208 * Signals the wait event.
2209 *
2210 * Converts the text obtained UTF-16LE with Windows EOLs.
2211 * Converts full BMP data to DIB format.
2212 *
2213 * @thread X11 event thread.
2214 */
2215SHCL_X11_DECL(void) clipConvertDataFromX11Worker(void *pClient, void *pvSrc, unsigned cbSrc)
2216{
2217 PSHCLX11REQUEST pReq = (PSHCLX11REQUEST)pClient;
2218 AssertPtrReturnVoid(pReq);
2219
2220 LogFlowFunc(("uFmtVBox=%#x, idxFmtX11=%u, pvSrc=%p, cbSrc=%u\n", pReq->Read.uFmtVBox, pReq->Read.idxFmtX11, pvSrc, cbSrc));
2221
2222 /* Sanity. */
2223 AssertReturnVoid(pReq->enmType == SHCLX11EVENTTYPE_READ);
2224 AssertReturnVoid(pReq->Read.uFmtVBox != VBOX_SHCL_FMT_NONE);
2225 AssertReturnVoid(pReq->Read.idxFmtX11 < SHCL_MAX_X11_FORMATS);
2226
2227 AssertPtrReturnVoid(pReq->pCtx);
2228
2229 LogRel2(("Shared Clipboard: Converting X11 format index %#x to VBox format %#x (%RU32 bytes max)\n",
2230 pReq->Read.idxFmtX11, pReq->Read.uFmtVBox, pReq->Read.cbMax));
2231
2232 int rc = VINF_SUCCESS;
2233
2234 void *pvDst = NULL;
2235 size_t cbDst = 0;
2236
2237 PSHCLX11CTX pCtx = pReq->pCtx;
2238 AssertPtr(pReq->pCtx);
2239
2240#ifdef VBOX_WITH_SHARED_CLIPBOARD_XT_BUSY
2241 clipSetXtBusy(pCtx, false);
2242 if (clipGetXtNeedsUpdate(pCtx))
2243 clipQueryX11Targets(pCtx);
2244#endif
2245
2246 /* If X11 clipboard buffer has no data, libXt can pass to XtGetSelectionValue()
2247 * callback an empty string, in this case cbSrc is 0. */
2248 if (pvSrc == NULL || cbSrc == 0)
2249 {
2250 /* The clipboard selection may have changed before we could get it. */
2251 rc = VERR_SHCLPB_NO_DATA;
2252 }
2253 else if (pReq->Read.uFmtVBox == VBOX_SHCL_FMT_UNICODETEXT)
2254 {
2255 /* In which format is the clipboard data? */
2256 switch (clipRealFormatForX11Format(pReq->Read.idxFmtX11))
2257 {
2258 case SHCLX11FMT_UTF8:
2259 RT_FALL_THROUGH();
2260 case SHCLX11FMT_TEXT:
2261 {
2262 size_t cwDst;
2263 /* If we are given broken UTF-8, we treat it as Latin1. */ /** @todo BUGBUG Is this acceptable? */
2264 if (RT_SUCCESS(RTStrValidateEncodingEx((char *)pvSrc, cbSrc, 0)))
2265 rc = ShClConvUtf8LFToUtf16CRLF((const char *)pvSrc, cbSrc,
2266 (PRTUTF16 *)&pvDst, &cwDst);
2267 else
2268 rc = ShClConvLatin1LFToUtf16CRLF((char *)pvSrc, cbSrc,
2269 (PRTUTF16 *)&pvDst, &cwDst);
2270 if (RT_SUCCESS(rc))
2271 {
2272 cwDst += 1 /* Include terminator */;
2273 cbDst = cwDst * sizeof(RTUTF16); /* Convert RTUTF16 units to bytes. */
2274
2275 LogFlowFunc(("UTF-16 text (%zu bytes):\n%ls\n", cbDst, pvDst));
2276 }
2277 break;
2278 }
2279
2280 default:
2281 {
2282 rc = VERR_INVALID_PARAMETER;
2283 break;
2284 }
2285 }
2286 }
2287 else if (pReq->Read.uFmtVBox == VBOX_SHCL_FMT_BITMAP)
2288 {
2289 /* In which format is the clipboard data? */
2290 switch (clipRealFormatForX11Format(pReq->Read.idxFmtX11))
2291 {
2292 case SHCLX11FMT_BMP:
2293 {
2294 const void *pDib;
2295 size_t cbDibSize;
2296 rc = ShClBmpGetDib((const void *)pvSrc, cbSrc,
2297 &pDib, &cbDibSize);
2298 if (RT_SUCCESS(rc))
2299 {
2300 pvDst = RTMemAlloc(cbDibSize);
2301 if (!pvDst)
2302 rc = VERR_NO_MEMORY;
2303 else
2304 {
2305 memcpy(pvDst, pDib, cbDibSize);
2306 cbDst = cbDibSize;
2307 }
2308 }
2309 break;
2310 }
2311
2312 default:
2313 {
2314 rc = VERR_INVALID_PARAMETER;
2315 break;
2316 }
2317 }
2318 }
2319 else if (pReq->Read.uFmtVBox == VBOX_SHCL_FMT_HTML)
2320 {
2321 /* In which format is the clipboard data? */
2322 switch (clipRealFormatForX11Format(pReq->Read.idxFmtX11))
2323 {
2324 case SHCLX11FMT_HTML:
2325 {
2326 /*
2327 * The common VBox HTML encoding will be - UTF-8
2328 * because it more general for HTML formats then UTF-16
2329 * X11 clipboard returns UTF-16, so before sending it we should
2330 * convert it to UTF-8.
2331 */
2332 pvDst = NULL;
2333 cbDst = 0;
2334
2335 /*
2336 * Some applications sends data in UTF-16, some in UTF-8,
2337 * without indication it in MIME.
2338 *
2339 * In case of UTF-16, at least [Open|Libre] Office adds an byte order mark (0xfeff)
2340 * at the start of the clipboard data.
2341 */
2342 if ( cbSrc >= sizeof(RTUTF16)
2343 && *(PRTUTF16)pvSrc == VBOX_SHCL_UTF16LEMARKER)
2344 {
2345 rc = ShClConvUtf16ToUtf8HTML((PRTUTF16)pvSrc, cbSrc / sizeof(RTUTF16), (char**)&pvDst, &cbDst);
2346 if (RT_SUCCESS(rc))
2347 {
2348 LogFlowFunc(("UTF-16 Unicode source (%u bytes):\n%ls\n\n", cbSrc, pvSrc));
2349 LogFlowFunc(("Byte Order Mark = %hx", ((PRTUTF16)pvSrc)[0]));
2350 LogFlowFunc(("UTF-8 Unicode dest (%u bytes):\n%s\n\n", cbDst, pvDst));
2351 }
2352 else
2353 LogRel(("Shared Clipboard: Converting UTF-16 Unicode failed with %Rrc\n", rc));
2354 }
2355 else /* Raw data. */
2356 {
2357 pvDst = RTMemAllocZ(cbSrc + 1 /* '\0' */);
2358 if(pvDst)
2359 {
2360 memcpy(pvDst, pvSrc, cbSrc);
2361 cbDst = cbSrc + 1 /* '\0' */;
2362 }
2363 else
2364 {
2365 rc = VERR_NO_MEMORY;
2366 break;
2367 }
2368 }
2369
2370 rc = VINF_SUCCESS;
2371 break;
2372 }
2373
2374 default:
2375 {
2376 rc = VERR_INVALID_PARAMETER;
2377 break;
2378 }
2379 }
2380 }
2381# ifdef VBOX_WITH_SHARED_CLIPBOARD_TRANSFERS
2382 else if (pReq->Read.uFmtVBox == VBOX_SHCL_FMT_URI_LIST)
2383 {
2384 /* In which format is the clipboard data? */
2385 switch (clipRealFormatForX11Format(pReq->Read.idxFmtX11))
2386 {
2387 case SHCLX11FMT_URI_LIST:
2388 RT_FALL_THROUGH();
2389 case SHCLX11FMT_URI_LIST_GNOME_COPIED_FILES:
2390 RT_FALL_THROUGH();
2391 case SHCLX11FMT_URI_LIST_MATE_COPIED_FILES:
2392 RT_FALL_THROUGH();
2393 case SHCLX11FMT_URI_LIST_NAUTILUS_CLIPBOARD:
2394 RT_FALL_THROUGH();
2395 case SHCLX11FMT_URI_LIST_KDE_CUTSELECTION:
2396 {
2397 rc = ShClX11TransferConvertFromX11((const char *)pvSrc, cbSrc, (char **)&pvDst, &cbDst);
2398 break;
2399 }
2400
2401 default:
2402 {
2403 AssertFailedStmt(rc = VERR_NOT_SUPPORTED); /* Missing code? */
2404 break;
2405 }
2406 }
2407 }
2408# endif /* VBOX_WITH_SHARED_CLIPBOARD_TRANSFERS */
2409 else
2410 rc = VERR_NOT_SUPPORTED;
2411
2412 LogFlowFunc(("pvDst=%p, cbDst=%RU32\n", pvDst, cbDst));
2413
2414 if (RT_FAILURE(rc))
2415 LogRel(("Shared Clipboard: Converting X11 format index %#x to VBox format %#x failed, rc=%Rrc\n",
2416 pReq->Read.idxFmtX11, pReq->Read.uFmtVBox, rc));
2417
2418 PSHCLEVENTPAYLOAD pPayload = NULL;
2419
2420 if ( pvDst
2421 && cbDst)
2422 {
2423 size_t cbResp = sizeof(SHCLX11RESPONSE);
2424 PSHCLX11RESPONSE pResp = (PSHCLX11RESPONSE)RTMemAllocZ(cbResp);
2425 if (pResp)
2426 {
2427 pResp->enmType = SHCLX11EVENTTYPE_READ;
2428 pResp->Read.pvData = pvDst;
2429 pResp->Read.cbData = cbDst;
2430
2431 pvDst = NULL; /* The response owns the data now. */
2432
2433 rc = ShClPayloadInit(0 /* ID, unused */, pResp, cbResp, &pPayload);
2434 }
2435 else
2436 rc = VERR_NO_MEMORY;
2437 }
2438
2439 /* Let the caller know in any case. */
2440 int rc2 = ShClEventSignal(pReq->pEvent, pPayload);
2441 if (RT_SUCCESS(rc2))
2442 pPayload = NULL; /* The event owns the payload now. */
2443
2444 if (RT_SUCCESS(rc))
2445 rc = rc2;
2446
2447 if (pPayload) /* Free payload on error. */
2448 {
2449 ShClPayloadFree(pPayload);
2450 pPayload = NULL;
2451 }
2452
2453 LogRel2(("Shared Clipboard: Converting X11 clipboard data completed with %Rrc\n", rc));
2454
2455 RTMemFree(pReq);
2456 RTMemFree(pvDst);
2457
2458 LogFlowFuncLeaveRC(rc);
2459}
2460
2461/**
2462 * Converts the data read from the X11 clipboard to the required format.
2463 *
2464 * @thread X11 event thread.
2465 */
2466SHCL_X11_DECL(void) clipConvertDataFromX11(Widget widget, XtPointer pClient,
2467 Atom * /* selection */, Atom *atomType,
2468 XtPointer pvSrc, long unsigned int *pcLen,
2469 int *piFormat)
2470{
2471 RT_NOREF(widget);
2472
2473 int rc = VINF_SUCCESS;
2474
2475 if (*atomType == XT_CONVERT_FAIL) /* Xt timeout */
2476 {
2477 LogRel(("Shared Clipboard: Reading clipboard data from X11 timed out\n"));
2478 rc = VERR_TIMEOUT;
2479 }
2480 else
2481 {
2482 PSHCLX11REQUEST pReq = (PSHCLX11REQUEST)pClient;
2483 if (pReq) /* Give some more clues, if available. */
2484 {
2485 AssertReturnVoid(pReq->enmType == SHCLX11EVENTTYPE_READ);
2486 char *pszFmts = ShClFormatsToStrA(pReq->Read.uFmtVBox);
2487 AssertPtrReturnVoid(pszFmts);
2488 AssertReturnVoid(pReq->Read.idxFmtX11 < SHCL_MAX_X11_FORMATS); /* Paranoia, should be checked already by the caller. */
2489 LogRel2(("Shared Clipboard: Converting X11 format '%s' -> VBox format(s) '%s'\n", g_aFormats[pReq->Read.idxFmtX11].pcszAtom, pszFmts));
2490 RTStrFree(pszFmts);
2491
2492 if (pReq->pCtx->Callbacks.pfnOnClipboardRead) /* Usually only used for testcases. */
2493 {
2494 void *pvData = NULL;
2495 size_t cbData = 0;
2496 rc = pReq->pCtx->Callbacks.pfnOnClipboardRead(pReq->pCtx->pFrontend, pReq->Read.uFmtVBox, &pvData, &cbData, NULL);
2497 if (RT_SUCCESS(rc))
2498 {
2499 /* Feed to conversion worker. */
2500 clipConvertDataFromX11Worker(pClient, pvData, cbData);
2501 RTMemFree(pvData);
2502 }
2503 }
2504 else /* Call conversion worker with current data provided by X (default). */
2505 clipConvertDataFromX11Worker(pClient, pvSrc, (*pcLen) * (*piFormat) / 8);
2506 }
2507 else
2508 rc = VERR_INVALID_POINTER;
2509 }
2510
2511 if (RT_FAILURE(rc))
2512 {
2513 LogRel(("Shared Clipboard: Reading clipboard data from X11 failed with %Rrc\n", rc));
2514
2515 /* Make sure to complete the request in any case by calling the conversion worker. */
2516 clipConvertDataFromX11Worker(pClient, NULL, 0);
2517 }
2518
2519 XtFree((char *)pvSrc);
2520}
2521
2522/**
2523 * Requests the current clipboard data from a specific selection.
2524 *
2525 * @returns VBox status code.
2526 * @param pCtx The X11 clipboard context to use.
2527 * @param pszWhere Clipboard selection to request the data from.
2528 * @param idxFmt The X11 format to request the data in.
2529 * @param pReq Where to store the requested data on success.
2530 */
2531static int clipGetSelectionValueEx(PSHCLX11CTX pCtx, const char *pszWhere, SHCLX11FMTIDX idxFmt,
2532 PSHCLX11REQUEST pReq)
2533{
2534 AssertPtrReturn(pszWhere, VERR_INVALID_POINTER);
2535 AssertReturn(idxFmt < SHCL_MAX_X11_FORMATS, VERR_INVALID_PARAMETER);
2536 AssertReturn(clipIsSupportedSelectionType(pCtx, clipGetAtom(pCtx, pszWhere)), VERR_INVALID_PARAMETER);
2537 AssertPtrReturn(pReq, VERR_INVALID_POINTER);
2538
2539 LogRel2(("Shared Clipboard: Requesting X11 selection value in %s for format '%s'\n", pszWhere, g_aFormats[idxFmt].pcszAtom));
2540
2541#ifndef TESTCASE
2542 XtGetSelectionValue(pCtx->pWidget, clipGetAtom(pCtx, pszWhere),
2543 clipAtomForX11Format(pCtx, idxFmt),
2544 clipConvertDataFromX11,
2545 reinterpret_cast<XtPointer>(pReq),
2546 CurrentTime);
2547#else
2548 tstClipRequestData(pCtx, idxFmt, (void *)pReq);
2549#endif
2550
2551 return VINF_SUCCESS; /** @todo Return real rc. */
2552}
2553
2554/**
2555 * Requests the current clipboard data from the CLIPBOARD selection.
2556 *
2557 * @returns VBox status code.
2558 * @param pCtx The X11 clipboard context to use.
2559 * @param idxFmt The X11 format to request the data in.
2560 * @param pReq Where to store the requested data on success.
2561 *
2562 * @sa clipGetSelectionValueEx() for requesting data for a specific selection.
2563 */
2564static int clipGetSelectionValue(PSHCLX11CTX pCtx, SHCLX11FMTIDX idxFmt, PSHCLX11REQUEST pReq)
2565{
2566 return clipGetSelectionValueEx(pCtx, "CLIPBOARD", idxFmt, pReq);
2567}
2568
2569/**
2570 * Worker function for ShClX11ReadDataFromX11Async.
2571 *
2572 * @param pvUserData Pointer to a PSHCLX11REQUEST structure containing
2573 * information about the clipboard read request.
2574 * Must be free'd by the worker.
2575 * @thread X11 event thread.
2576 */
2577static void ShClX11ReadDataFromX11Worker(void *pvUserData, void * /* interval */)
2578{
2579 AssertPtrReturnVoid(pvUserData);
2580
2581 PSHCLX11REQUEST pReq = (PSHCLX11REQUEST)pvUserData;
2582 AssertReturnVoid(pReq->enmType == SHCLX11EVENTTYPE_READ);
2583 SHCLX11CTX *pCtx = pReq->pCtx;
2584 AssertPtrReturnVoid(pCtx);
2585
2586 LogFlowFunc(("pReq->uFmtVBox=%#x, idxFmtX11=%#x\n", pReq->Read.uFmtVBox, pReq->Read.idxFmtX11));
2587
2588 int rc = VERR_SHCLPB_NO_DATA; /* VBox thinks we have data and we don't. */
2589
2590#ifdef VBOX_WITH_SHARED_CLIPBOARD_XT_BUSY
2591 const bool fXtBusy = clipGetXtBusy(pCtx);
2592 clipSetXtBusy(pCtx, true);
2593 if (fXtBusy)
2594 {
2595 /* If the clipboard is busy just fend off the request. */
2596 rc = VERR_TRY_AGAIN;
2597 }
2598 else
2599#endif
2600 if (pReq->Read.uFmtVBox & VBOX_SHCL_FMT_UNICODETEXT)
2601 {
2602 pReq->Read.idxFmtX11 = pCtx->idxFmtText;
2603 if (pReq->Read.idxFmtX11 != SHCLX11FMT_INVALID)
2604 {
2605 /* Send out a request for the data to the current clipboard owner. */
2606 rc = clipGetSelectionValue(pCtx, pCtx->idxFmtText, pReq);
2607 }
2608 }
2609 else if (pReq->Read.uFmtVBox & VBOX_SHCL_FMT_BITMAP)
2610 {
2611 pReq->Read.idxFmtX11 = pCtx->idxFmtBmp;
2612 if (pReq->Read.idxFmtX11 != SHCLX11FMT_INVALID)
2613 {
2614 /* Send out a request for the data to the current clipboard owner. */
2615 rc = clipGetSelectionValue(pCtx, pCtx->idxFmtBmp, pReq);
2616 }
2617 }
2618 else if (pReq->Read.uFmtVBox & VBOX_SHCL_FMT_HTML)
2619 {
2620 pReq->Read.idxFmtX11 = pCtx->idxFmtHTML;
2621 if (pReq->Read.idxFmtX11 != SHCLX11FMT_INVALID)
2622 {
2623 /* Send out a request for the data to the current clipboard owner. */
2624 rc = clipGetSelectionValue(pCtx, pCtx->idxFmtHTML, pReq);
2625 }
2626 }
2627#ifdef VBOX_WITH_SHARED_CLIPBOARD_TRANSFERS
2628 else if (pReq->Read.uFmtVBox & VBOX_SHCL_FMT_URI_LIST)
2629 {
2630 pReq->Read.idxFmtX11 = pCtx->idxFmtURI;
2631 if (pReq->Read.idxFmtX11 != SHCLX11FMT_INVALID)
2632 {
2633 /* Send out a request for the data to the current clipboard owner. */
2634 rc = clipGetSelectionValue(pCtx, pCtx->idxFmtURI, pReq);
2635 }
2636 }
2637#endif
2638 else
2639 {
2640#ifdef VBOX_WITH_SHARED_CLIPBOARD_XT_BUSY
2641 clipSetXtBusy(pCtx, false);
2642#endif
2643 rc = VERR_NOT_IMPLEMENTED;
2644 }
2645
2646 /* If the above stuff fails, make sure to let the waiters know.
2647 *
2648 * Getting the actual selection value via clipGetSelectionValue[Ex]() above will happen in the X event thread,
2649 * which has its own signalling then. So this check only handles errors which happens before we put anything
2650 * onto the X event thread.
2651 */
2652 if (RT_FAILURE(rc))
2653 {
2654 int rc2 = ShClEventSignalEx(pReq->pEvent, rc, NULL /* Payload */);
2655 AssertRC(rc2);
2656 }
2657
2658 LogRel2(("Shared Clipboard: Reading X11 clipboard data completed with %Rrc\n", rc));
2659
2660 LogFlowFuncLeaveRC(rc);
2661}
2662
2663/**
2664 * Reads from the X11 clipboard (asynchronously).
2665 *
2666 * @returns VBox status code.
2667 * @param pCtx Context data for the clipboard backend.
2668 * @param uFmt The format that the VBox would like to receive the data in.
2669 * @param cbMax Maximum data to read (in bytes).
2670 * Specify UINT32_MAX to read as much as available.
2671 * @param pEvent Event to use for waiting for data to arrive.
2672 * The event's payload will contain the data read. Needs to be free'd with ShClEventRelease().
2673 */
2674int ShClX11ReadDataFromX11Async(PSHCLX11CTX pCtx, SHCLFORMAT uFmt, uint32_t cbMax, PSHCLEVENT pEvent)
2675{
2676 AssertPtrReturn(pEvent, VERR_INVALID_POINTER);
2677
2678 if (shClX11HeadlessIsEnabled(pCtx))
2679 return VINF_SUCCESS;
2680
2681 int rc = VINF_SUCCESS;
2682
2683 PSHCLX11REQUEST pReq = (PSHCLX11REQUEST)RTMemAllocZ(sizeof(SHCLX11REQUEST));
2684 if (pReq)
2685 {
2686 pReq->enmType = SHCLX11EVENTTYPE_READ;
2687 pReq->pCtx = pCtx;
2688 pReq->Read.uFmtVBox = uFmt;
2689 pReq->Read.cbMax = cbMax;
2690 pReq->pEvent = pEvent;
2691
2692 /* We use this to schedule a worker function on the event thread. */
2693 rc = clipThreadScheduleCall(pCtx, ShClX11ReadDataFromX11Worker, (XtPointer)pReq);
2694 if (RT_FAILURE(rc))
2695 RTMemFree(pReq);
2696 }
2697 else
2698 rc = VERR_NO_MEMORY;
2699
2700 LogFlowFuncLeaveRC(rc);
2701 return rc;
2702}
2703
2704/**
2705 * Reads from the X11 clipboard, internal version.
2706 *
2707 * @returns VBox status code.
2708 * @retval VERR_SHCLPB_NO_DATA if format is supported but no data is available currently.
2709 * @retval VERR_NOT_IMPLEMENTED if the format is not implemented.
2710 * @param pCtx Context data for the clipboard backend.
2711 * @param pEventSource Event source to use.
2712 * @param msTimeout Timeout (in ms) for waiting.
2713 * @param uFmt The format that the VBox would like to receive the data in.
2714 * @param cbMax Maximum size (in bytes) to read.
2715 * @param pResp Where to return the allocated SHCLX11RESPONSE on success.
2716 * Must be free'd via shClX11ResponseFree() by the caller.
2717 */
2718static int shClX11ReadDataFromX11Internal(PSHCLX11CTX pCtx, PSHCLEVENTSOURCE pEventSource, RTMSINTERVAL msTimeout,
2719 SHCLFORMAT uFmt, uint32_t cbMax, PSHCLX11RESPONSE *ppResp)
2720{
2721 PSHCLEVENT pEvent;
2722 int rc = ShClEventSourceGenerateAndRegisterEvent(pEventSource, &pEvent);
2723 if (RT_SUCCESS(rc))
2724 {
2725 rc = ShClX11ReadDataFromX11Async(pCtx, uFmt, cbMax, pEvent);
2726 if (RT_SUCCESS(rc))
2727 {
2728 PSHCLEVENTPAYLOAD pPayload;
2729 int rcEvent;
2730 rc = ShClEventWaitEx(pEvent, msTimeout, &rcEvent, &pPayload);
2731 if (RT_SUCCESS(rc))
2732 {
2733 if (pPayload)
2734 {
2735 AssertReturn(pPayload->cbData == sizeof(SHCLX11RESPONSE), VERR_INVALID_PARAMETER);
2736 AssertPtrReturn(pPayload->pvData, VERR_INVALID_POINTER);
2737 PSHCLX11RESPONSE pResp = (PSHCLX11RESPONSE)pPayload->pvData;
2738 AssertReturn(pResp->enmType == SHCLX11EVENTTYPE_READ, VERR_INVALID_PARAMETER);
2739 AssertReturn(pResp->Read.cbData <= cbMax, VERR_BUFFER_OVERFLOW); /* Paranoia. */
2740
2741 pPayload->pvData = NULL; /* pvData (pResp) is owned by ppResp now. */
2742 pPayload->cbData = 0;
2743
2744 ShClPayloadFree(pPayload);
2745
2746 *ppResp = pResp;
2747 }
2748 else /* No payload given; could happen on invalid / not-expected formats. */
2749 rc = VERR_SHCLPB_NO_DATA;
2750 }
2751 else if (rc == VERR_SHCLPB_EVENT_FAILED)
2752 rc = rcEvent;
2753 }
2754
2755 ShClEventRelease(pEvent);
2756 }
2757
2758 LogFlowFuncLeaveRC(rc);
2759 return rc;
2760}
2761
2762/**
2763 * Reads from the X11 clipboard, extended version.
2764 *
2765 * @returns VBox status code.
2766 * @retval VERR_SHCLPB_NO_DATA if format is supported but no data is available currently.
2767 * @retval VERR_NOT_IMPLEMENTED if the format is not implemented.
2768 * @param pCtx Context data for the clipboard backend.
2769 * @param pEventSource Event source to use.
2770 * @param msTimeout Timeout (in ms) for waiting.
2771 * @param uFmt The format that the VBox would like to receive the data in.
2772 * @param ppvBuf Where to return the allocated received data on success.
2773 * Must be free'd by the caller.
2774 * @param pcbBuf Where to return the size (in bytes) of \a ppvBuf.
2775 */
2776int ShClX11ReadDataFromX11Ex(PSHCLX11CTX pCtx, PSHCLEVENTSOURCE pEventSource, RTMSINTERVAL msTimeout,
2777 SHCLFORMAT uFmt, void **ppvBuf, uint32_t *pcbBuf)
2778{
2779 AssertPtrReturn(pCtx, VERR_INVALID_POINTER);
2780 AssertPtrReturn(pEventSource, VERR_INVALID_POINTER);
2781 AssertPtrReturn(ppvBuf, VERR_INVALID_POINTER);
2782 AssertPtrReturn(pcbBuf, VERR_INVALID_POINTER);
2783
2784 if (shClX11HeadlessIsEnabled(pCtx))
2785 {
2786 *pcbBuf = 0;
2787 return VINF_SUCCESS;
2788 }
2789
2790 PSHCLX11RESPONSE pResp;
2791 int rc = shClX11ReadDataFromX11Internal(pCtx, pEventSource, msTimeout, uFmt, UINT32_MAX, &pResp);
2792 if (RT_SUCCESS(rc))
2793 {
2794 *ppvBuf = pResp->Read.pvData;
2795 *pcbBuf = pResp->Read.cbData;
2796
2797 pResp->Read.pvData = NULL; /* Is owned by ppvBuf now. */
2798 pResp->Read.cbData = 0;
2799
2800 shClX11ResponseFree(pResp);
2801 }
2802
2803 LogFlowFuncLeaveRC(rc);
2804 return rc;
2805}
2806
2807/**
2808 * Reads from the X11 clipboard.
2809 *
2810 * @returns VBox status code.
2811 * @retval VERR_SHCLPB_NO_DATA if format is supported but no data is available currently.
2812 * @retval VERR_NOT_IMPLEMENTED if the format is not implemented.
2813 * @param pCtx Context data for the clipboard backend.
2814 * @param pEventSource Event source to use.
2815 * @param msTimeout Timeout (in ms) for waiting.
2816 * @param uFmt The format that the VBox would like to receive the data in.
2817 * @param pvBuf Where to store the received data on success.
2818 * @param cbBuf Size (in bytes) of \a pvBuf. Also marks maximum data to read (in bytes).
2819 * @param pcbRead Where to return the read bytes on success. Optional.
2820 */
2821int ShClX11ReadDataFromX11(PSHCLX11CTX pCtx, PSHCLEVENTSOURCE pEventSource, RTMSINTERVAL msTimeout,
2822 SHCLFORMAT uFmt, void *pvBuf, uint32_t cbBuf, uint32_t *pcbRead)
2823{
2824 AssertPtrReturn(pCtx, VERR_INVALID_POINTER);
2825 AssertPtrReturn(pEventSource, VERR_INVALID_POINTER);
2826 AssertPtrReturn(pvBuf, VERR_INVALID_POINTER);
2827 AssertReturn(cbBuf, VERR_INVALID_PARAMETER);
2828 /* pcbRead is optional. */
2829
2830 if (shClX11HeadlessIsEnabled(pCtx))
2831 {
2832 if (pcbRead)
2833 *pcbRead = 0;
2834 return VINF_SUCCESS;
2835 }
2836
2837 PSHCLX11RESPONSE pResp;
2838 int rc = shClX11ReadDataFromX11Internal(pCtx, pEventSource, msTimeout, uFmt, cbBuf, &pResp);
2839 if (RT_SUCCESS(rc))
2840 {
2841 memcpy(pvBuf, pResp->Read.pvData, RT_MIN(cbBuf, pResp->Read.cbData));
2842 if (pcbRead)
2843 *pcbRead = pResp->Read.cbData;
2844
2845 shClX11ResponseFree(pResp);
2846 }
2847
2848 LogFlowFuncLeaveRC(rc);
2849 return rc;
2850}
2851
2852/**
2853 * Writes to the X11 clipboard (asynchronously).
2854 *
2855 * @returns VBox status code.
2856 * @param pCtx Context data for the clipboard backend.
2857 * @param uFmts The format(s) to write.
2858 * Conversions might be performed, if available.
2859 * @param pvBuf Pointer to data to write.
2860 * @param cbBuf Size (in bytes) of data to write.
2861 * @param pEvent Event to use for waiting for data to get written.
2862 * The event's payload will contain the amount of data written.
2863 * Needs to be free'd with ShClEventRelease().
2864 */
2865int ShClX11WriteDataToX11Async(PSHCLX11CTX pCtx, SHCLFORMATS uFmts, const void *pvBuf, uint32_t cbBuf, PSHCLEVENT pEvent)
2866{
2867 AssertPtrReturn(pCtx, VERR_INVALID_POINTER);
2868 AssertPtrReturn(pvBuf, VERR_INVALID_POINTER);
2869 AssertReturn(cbBuf, VERR_INVALID_PARAMETER);
2870 /* pEvent not used yet. */ RT_NOREF(pEvent);
2871
2872 if (shClX11HeadlessIsEnabled(pCtx))
2873 return VINF_SUCCESS;
2874
2875 int rc = ShClCacheSetMultiple(&pCtx->Cache, uFmts, pvBuf, cbBuf);
2876 if (RT_SUCCESS(rc))
2877 {
2878 clipResetX11Formats(pCtx);
2879 clipGrabX11Clipboard(pCtx, uFmts);
2880 }
2881
2882 return VINF_SUCCESS;
2883}
2884
2885/**
2886 * Writes to the X11 clipboard.
2887 *
2888 * This function currently only is implemented as asynchronous version.
2889 *
2890 * @returns VBox status code.
2891 * @retval VERR_NOT_AVAILABLE the the X11 clipboard is not available.
2892 * @retval VERR_TRY_AGAIN if format is supported but data could not be written.
2893 * @retval VERR_NOT_IMPLEMENTED if the format is not implemented.
2894 * @param pCtx Context data for the clipboard backend.
2895 * @param uFmt The format to write.
2896 * @param pvBuf Pointer to data to write. Must match format to write.
2897 * @param cbBuf Size (in bytes) of data to write.
2898 * @param pcbWritten Where to return the written bytes on success. Optional.
2899 * Currently always returns the value of \a cbBuf on success.
2900 *
2901 * @note Text data must be in UTF-8, always.
2902 */
2903int ShClX11WriteDataToX11(PSHCLX11CTX pCtx, PSHCLEVENTSOURCE pEventSource, RTMSINTERVAL msTimeout,
2904 SHCLFORMAT uFmt, const void *pvBuf, uint32_t cbBuf, uint32_t *pcbWritten)
2905{
2906 AssertPtrReturn(pCtx, VERR_INVALID_POINTER);
2907 AssertPtrReturn(pEventSource, VERR_INVALID_POINTER);
2908 AssertPtrReturn(pvBuf, VERR_INVALID_POINTER);
2909 AssertReturn(cbBuf, VERR_INVALID_PARAMETER);
2910 /* pcbWritetn is optional. */
2911
2912 RT_NOREF(msTimeout); /* Not used yet. */
2913
2914 int rc = ShClX11WriteDataToX11Async(pCtx, uFmt, pvBuf, cbBuf, NULL /* pEvent */);
2915 if (RT_SUCCESS(rc))
2916 {
2917 if (pcbWritten)
2918 *pcbWritten = cbBuf;
2919 }
2920
2921 LogFlowFuncLeaveRC(rc);
2922 return rc;
2923}
注意: 瀏覽 TracBrowser 來幫助您使用儲存庫瀏覽器

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