VirtualBox

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

Last change on this file since 86728 was 86717, checked in by vboxsync, 4 years ago

Shared Clipboard/X11: More work on untangling / cleaning up the threading code. bugref:9848

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