VirtualBox

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

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

X11: Host Services: Shared Clipboard: improve error code path handling, bugref:10038.

This commit improves error code path handling for X11 part of host Shared Clipboard
service. In particular, it adjusts initialization calls to match to its de-init counterpart.
It also adds more resources release on error path. In addition, this commit limits number of
simmultaneous HGCM connections to ShCl host service in order to prevent potential host flooding.

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

© 2023 Oracle
ContactPrivacy policyTerms of Use