VirtualBox

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

Last change on this file since 86603 was 85845, checked in by vboxsync, 4 years ago

Shared Clipboard/X11: Fixes for empty strings (was too strict before), don't include the terminating zeros when return string lengths to match IPRT. Also should fix the testcases.

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