VirtualBox

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

Last change on this file since 97218 was 96407, checked in by vboxsync, 2 years ago

scm copyright and license note update

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

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