VirtualBox

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

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

X11: Shared Clipboard: extend list of mime types, bugref:10160.

Add Firefox specific format application/x-moz-nativehtml to the
list of supported formats.

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

© 2023 Oracle
ContactPrivacy policyTerms of Use