VirtualBox

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

Last change on this file since 87030 was 86959, checked in by vboxsync, 4 years ago

Shared Clipboard: Convert VBox clipboard formats to strings to improve logging.

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

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