VirtualBox

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

Last change on this file since 95512 was 93504, checked in by vboxsync, 3 years ago

Shared Clipboard: Implemented backend callbacks and a dedicated backend context, together with a new testcase which mocks HGCM to also test the guest-side clipboard code (disabled by default for now). Work in progress, only tested on Linux so far [testcase fixes].

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