VirtualBox

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

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

Shared Clipboard/X11: Renamed mandatory (own) X11 callbacks to better point out the data flow, added some docs.

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 77.3 KB
Line 
1/** @file
2 * Shared Clipboard: Common X11 code.
3 */
4
5/*
6 * Copyright (C) 2006-2022 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) clipQueryX11Targets(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#ifdef VBOX_WITH_SHARED_CLIPBOARD_XT_BUSY
659DECLINLINE(bool) clipGetXtBusy(PSHCLX11CTX pCtx)
660{
661 LogFlowFunc(("fXtBusy=%RTbool, fXtNeedsUpdate=%RTbool\n", pCtx->fXtBusy, pCtx->fXtNeedsUpdate));
662 return pCtx->fXtBusy;
663}
664
665DECLINLINE(bool) clipGetXtNeedsUpdate(PSHCLX11CTX pCtx)
666{
667 LogFlowFunc(("fXtBusy=%RTbool, fXtNeedsUpdate=%RTbool\n", pCtx->fXtBusy, pCtx->fXtNeedsUpdate));
668 return pCtx->fXtNeedsUpdate;
669}
670
671DECLINLINE(bool) clipSetXtBusy(PSHCLX11CTX pCtx, bool fBusy)
672{
673 pCtx->fXtBusy = fBusy;
674 LogFlowFunc(("fXtBusy=%RTbool, fXtNeedsUpdate=%RTbool\n", pCtx->fXtBusy, pCtx->fXtNeedsUpdate));
675 return pCtx->fXtBusy;
676}
677
678DECLINLINE(bool) clipSetXtNeedsUpdate(PSHCLX11CTX pCtx, bool fNeedsUpdate)
679{
680 pCtx->fXtNeedsUpdate = fNeedsUpdate;
681 LogFlowFunc(("fXtBusy=%RTbool, fXtNeedsUpdate=%RTbool\n", pCtx->fXtBusy, pCtx->fXtNeedsUpdate));
682 return pCtx->fXtNeedsUpdate;
683}
684#endif /* VBOX_WITH_SHARED_CLIPBOARD_XT_BUSY */
685
686/**
687 * Updates the context's information about targets currently supported by X11,
688 * based on an array of X11 atoms.
689 *
690 * @param pCtx The X11 clipboard context to use.
691 * @param pTargets The array of atoms describing the targets supported.
692 * @param cTargets The size of the array @a pTargets.
693 */
694SHCL_X11_DECL(void) clipUpdateX11Targets(PSHCLX11CTX pCtx, SHCLX11FMTIDX *paIdxFmtTargets, size_t cTargets)
695{
696 LogFlowFuncEnter();
697
698#ifdef VBOX_WITH_SHARED_CLIPBOARD_XT_BUSY
699 clipSetXtBusy(pCtx, false);
700 if (clipGetXtNeedsUpdate(pCtx))
701 {
702 /* We may already be out of date. */
703 clipSetXtNeedsUpdate(pCtx, false);
704 clipQueryX11Targets(pCtx);
705 return;
706 }
707#endif
708
709 if (paIdxFmtTargets == NULL)
710 {
711 /* No data available */
712 clipReportEmpty(pCtx);
713 return;
714 }
715
716 clipGetFormatsFromTargets(pCtx, paIdxFmtTargets, cTargets);
717 clipReportFormatsToVBox(pCtx);
718}
719
720/**
721 * Notifies the VBox clipboard about available data formats ("targets" on X11),
722 * based on the information obtained from the X11 clipboard.
723 *
724 * @note Callback installed by clipQueryX11Targets() for XtGetSelectionValue().
725 * @note This function is treated as API glue, and as such is not part of any
726 * unit test. So keep it simple, be paranoid and log everything.
727 */
728SHCL_X11_DECL(void) clipQueryX11TargetsCallback(Widget widget, XtPointer pClient,
729 Atom * /* selection */, Atom *atomType,
730 XtPointer pValue, long unsigned int *pcLen,
731 int *piFormat)
732{
733 RT_NOREF(piFormat);
734
735 PSHCLX11CTX pCtx = reinterpret_cast<SHCLX11CTX *>(pClient);
736
737 LogFlowFunc(("pValue=%p, *pcLen=%u, *atomType=%d%s\n",
738 pValue, *pcLen, *atomType, *atomType == XT_CONVERT_FAIL ? " (XT_CONVERT_FAIL)" : ""));
739
740 Atom *pAtoms = (Atom *)pValue;
741
742 unsigned cFormats = *pcLen;
743
744 LogRel2(("Shared Clipboard: %u formats were found\n", cFormats));
745
746 SHCLX11FMTIDX *paIdxFmt = NULL;
747 if ( cFormats
748 && pValue
749 && (*atomType != XT_CONVERT_FAIL /* time out */))
750 {
751 /* Allocated array to hold the format indices. */
752 paIdxFmt = (SHCLX11FMTIDX *)RTMemAllocZ(cFormats * sizeof(SHCLX11FMTIDX));
753 }
754
755#if !defined(TESTCASE)
756 if (pValue)
757 {
758 for (unsigned i = 0; i < cFormats; ++i)
759 {
760 if (pAtoms[i])
761 {
762 char *pszName = XGetAtomName(XtDisplay(widget), pAtoms[i]);
763 LogRel2(("Shared Clipboard: Found target '%s'\n", pszName));
764 XFree(pszName);
765 }
766 else
767 LogFunc(("Found empty target\n"));
768 }
769 }
770#endif
771
772 if (paIdxFmt)
773 {
774 for (unsigned i = 0; i < cFormats; ++i)
775 {
776 for (unsigned j = 0; j < RT_ELEMENTS(g_aFormats); ++j)
777 {
778 Atom target = XInternAtom(XtDisplay(widget),
779 g_aFormats[j].pcszAtom, False);
780 if (*(pAtoms + i) == target)
781 paIdxFmt[i] = j;
782 }
783#if !defined(TESTCASE)
784 if (paIdxFmt[i] != SHCLX11FMT_INVALID)
785 LogRel2(("Shared Clipboard: Reporting format '%s'\n", g_aFormats[paIdxFmt[i]].pcszAtom));
786#endif
787 }
788 }
789 else
790 LogFunc(("Reporting empty targets (none reported or allocation failure)\n"));
791
792 clipUpdateX11Targets(pCtx, paIdxFmt, cFormats);
793 RTMemFree(paIdxFmt);
794
795 XtFree(reinterpret_cast<char *>(pValue));
796}
797
798/**
799 * Queries the current formats ("targets") of the X11 clipboard ("CLIPBOARD").
800 *
801 * @param pCtx The X11 clipboard context to use.
802 */
803SHCL_X11_DECL(void) clipQueryX11Targets(PSHCLX11CTX pCtx)
804{
805#ifndef TESTCASE
806
807# ifdef VBOX_WITH_SHARED_CLIPBOARD_XT_BUSY
808 if (clipGetXtBusy(pCtx))
809 {
810 clipSetXtNeedsUpdate(pCtx, true);
811 return;
812 }
813 clipSetXtBusy(pCtx, true);
814# endif
815
816 XtGetSelectionValue(pCtx->pWidget,
817 clipGetAtom(pCtx, "CLIPBOARD"),
818 clipGetAtom(pCtx, "TARGETS"),
819 clipQueryX11TargetsCallback, pCtx,
820 CurrentTime);
821#else
822 tstRequestTargets(pCtx);
823#endif
824}
825
826typedef struct
827{
828 int type; /* event base */
829 unsigned long serial;
830 Bool send_event;
831 Display *display;
832 Window window;
833 int subtype;
834 Window owner;
835 Atom selection;
836 Time timestamp;
837 Time selection_timestamp;
838} XFixesSelectionNotifyEvent;
839
840#ifndef TESTCASE
841/**
842 * Waits until an event arrives and handle it if it is an XFIXES selection
843 * event, which Xt doesn't know about.
844 *
845 * @param pCtx The X11 clipboard context to use.
846 */
847static void clipPeekEventAndDoXFixesHandling(PSHCLX11CTX pCtx)
848{
849 union
850 {
851 XEvent event;
852 XFixesSelectionNotifyEvent fixes;
853 } event = { { 0 } };
854
855 if (XtAppPeekEvent(pCtx->pAppContext, &event.event))
856 {
857 if ( (event.event.type == pCtx->fixesEventBase)
858 && (event.fixes.owner != XtWindow(pCtx->pWidget)))
859 {
860 if ( (event.fixes.subtype == 0 /* XFixesSetSelectionOwnerNotify */)
861 && (event.fixes.owner != 0))
862 clipQueryX11Targets(pCtx);
863 else
864 clipReportEmpty(pCtx);
865 }
866 }
867}
868
869/**
870 * The main loop of our X11 event thread.
871 *
872 * @returns VBox status code.
873 * @param hThreadSelf Associated thread handle.
874 * @param pvUser Pointer to the X11 clipboard context to use.
875 */
876static DECLCALLBACK(int) clipThreadMain(RTTHREAD hThreadSelf, void *pvUser)
877{
878 PSHCLX11CTX pCtx = (PSHCLX11CTX)pvUser;
879 AssertPtr(pCtx);
880
881 LogFlowFunc(("pCtx=%p\n", pCtx));
882
883 bool fSignalled = false; /* Whether we have signalled the parent already or not. */
884
885 int rc = clipInitInternal(pCtx);
886 if (RT_SUCCESS(rc))
887 {
888 rc = clipRegisterContext(pCtx);
889 if (RT_SUCCESS(rc))
890 {
891 if (pCtx->fGrabClipboardOnStart)
892 clipQueryX11Targets(pCtx);
893
894 pCtx->fThreadStarted = true;
895
896 /* We're now ready to run, tell parent. */
897 int rc2 = RTThreadUserSignal(hThreadSelf);
898 AssertRC(rc2);
899
900 fSignalled = true;
901
902 while (XtAppGetExitFlag(pCtx->pAppContext) == FALSE)
903 {
904 clipPeekEventAndDoXFixesHandling(pCtx);
905 XtAppProcessEvent(pCtx->pAppContext, XtIMAll);
906 }
907
908 LogRel(("Shared Clipboard: X11 event thread exiting\n"));
909
910 clipUnregisterContext(pCtx);
911 }
912 else
913 {
914 LogRel(("Shared Clipboard: unable to register clip context: %Rrc\n", rc));
915 }
916
917 clipUninitInternal(pCtx);
918 }
919
920 if (!fSignalled) /* Signal parent if we didn't do so yet. */
921 {
922 int rc2 = RTThreadUserSignal(hThreadSelf);
923 AssertRC(rc2);
924 }
925
926 LogFlowFuncLeaveRC(rc);
927 return rc;
928}
929
930/**
931 * Worker function for stopping the clipboard which runs on the event
932 * thread.
933 *
934 * @param pvUserData Pointer to the X11 clipboard context to use.
935 */
936static void clipThreadSignalStop(void *pvUserData, void *)
937{
938 PSHCLX11CTX pCtx = (PSHCLX11CTX)pvUserData;
939
940 /* This might mean that we are getting stopped twice. */
941 Assert(pCtx->pWidget != NULL);
942
943 /* Set the termination flag to tell the Xt event loop to exit. We
944 * reiterate that any outstanding requests from the X11 event loop to
945 * the VBox part *must* have returned before we do this. */
946 XtAppSetExitFlag(pCtx->pAppContext);
947}
948
949/**
950 * Sets up the XFixes library and load the XFixesSelectSelectionInput symbol.
951 */
952static int clipLoadXFixes(Display *pDisplay, PSHCLX11CTX pCtx)
953{
954 int rc;
955
956 void *hFixesLib = dlopen("libXfixes.so.1", RTLD_LAZY);
957 if (!hFixesLib)
958 hFixesLib = dlopen("libXfixes.so.2", RTLD_LAZY);
959 if (!hFixesLib)
960 hFixesLib = dlopen("libXfixes.so.3", RTLD_LAZY);
961 if (!hFixesLib)
962 hFixesLib = dlopen("libXfixes.so.4", RTLD_LAZY);
963 if (hFixesLib)
964 {
965 /* For us, a NULL function pointer is a failure */
966 pCtx->fixesSelectInput = (void (*)(Display *, Window, Atom, long unsigned int))
967 (uintptr_t)dlsym(hFixesLib, "XFixesSelectSelectionInput");
968 if (pCtx->fixesSelectInput)
969 {
970 int dummy1 = 0;
971 int dummy2 = 0;
972 if (XQueryExtension(pDisplay, "XFIXES", &dummy1, &pCtx->fixesEventBase, &dummy2) != 0)
973 {
974 if (pCtx->fixesEventBase >= 0)
975 {
976 rc = VINF_SUCCESS;
977 }
978 else
979 {
980 LogRel(("Shared Clipboard: fixesEventBase is less than zero: %d\n", pCtx->fixesEventBase));
981 rc = VERR_NOT_SUPPORTED;
982 }
983 }
984 else
985 {
986 LogRel(("Shared Clipboard: XQueryExtension failed\n"));
987 rc = VERR_NOT_SUPPORTED;
988 }
989 }
990 else
991 {
992 LogRel(("Shared Clipboard: Symbol XFixesSelectSelectionInput not found!\n"));
993 rc = VERR_NOT_SUPPORTED;
994 }
995 }
996 else
997 {
998 LogRel(("Shared Clipboard: libxFixes.so.* not found!\n"));
999 rc = VERR_NOT_SUPPORTED;
1000 }
1001 return rc;
1002}
1003
1004/**
1005 * This is the callback which is scheduled when data is available on the
1006 * wakeup pipe. It simply reads all data from the pipe.
1007 *
1008 * @param pvUserData Pointer to the X11 clipboard context to use.
1009 */
1010static void clipThreadDrainWakeupPipe(XtPointer pvUserData, int *, XtInputId *)
1011{
1012 LogFlowFuncEnter();
1013
1014 PSHCLX11CTX pCtx = (PSHCLX11CTX)pvUserData;
1015 char acBuf[WAKE_UP_STRING_LEN];
1016
1017 while (read(pCtx->wakeupPipeRead, acBuf, sizeof(acBuf)) > 0) {}
1018}
1019#endif /* !TESTCASE */
1020
1021/**
1022 * X11-specific initialisation for the Shared Clipboard.
1023 *
1024 * Note: Must be called from the thread serving the Xt stuff.
1025 *
1026 * @returns VBox status code.
1027 * @param pCtx The X11 clipboard context to init.
1028 */
1029static int clipInitInternal(PSHCLX11CTX pCtx)
1030{
1031 LogFlowFunc(("pCtx=%p\n", pCtx));
1032
1033 /* Make sure we are thread safe. */
1034 XtToolkitThreadInitialize();
1035
1036 /*
1037 * Set up the Clipboard application context and main window. We call all
1038 * these functions directly instead of calling XtOpenApplication() so
1039 * that we can fail gracefully if we can't get an X11 display.
1040 */
1041 XtToolkitInitialize();
1042
1043 int rc = VINF_SUCCESS;
1044
1045 Assert(pCtx->pAppContext == NULL); /* No nested initialization. */
1046 pCtx->pAppContext = XtCreateApplicationContext();
1047 if (pCtx->pAppContext == NULL)
1048 {
1049 LogRel(("Shared Clipboard: Failed to create Xt application context\n"));
1050 return VERR_NOT_SUPPORTED; /** @todo Fudge! */
1051 }
1052
1053 /* Create a window and make it a clipboard viewer. */
1054 int cArgc = 0;
1055 char *pcArgv = 0;
1056 Display *pDisplay = XtOpenDisplay(pCtx->pAppContext, 0, 0, "VBoxShCl", 0, 0, &cArgc, &pcArgv);
1057 if (pDisplay == NULL)
1058 {
1059 LogRel(("Shared Clipboard: Failed to connect to the X11 clipboard - the window system may not be running\n"));
1060 rc = VERR_NOT_SUPPORTED;
1061 }
1062
1063#ifndef TESTCASE
1064 if (RT_SUCCESS(rc))
1065 {
1066 rc = clipLoadXFixes(pDisplay, pCtx);
1067 if (RT_FAILURE(rc))
1068 LogRel(("Shared Clipboard: Failed to load the XFIXES extension\n"));
1069 }
1070#endif
1071
1072 if (RT_SUCCESS(rc))
1073 {
1074 pCtx->pWidget = XtVaAppCreateShell(0, "VBoxShCl",
1075 applicationShellWidgetClass,
1076 pDisplay, XtNwidth, 1, XtNheight,
1077 1, NULL);
1078 if (pCtx->pWidget == NULL)
1079 {
1080 LogRel(("Shared Clipboard: Failed to create Xt app shell\n"));
1081 rc = VERR_NO_MEMORY; /** @todo r=andy Improve this. */
1082 }
1083 else
1084 {
1085#ifndef TESTCASE
1086 if (!XtAppAddInput(pCtx->pAppContext, pCtx->wakeupPipeRead,
1087 (XtPointer) XtInputReadMask,
1088 clipThreadDrainWakeupPipe, (XtPointer) pCtx))
1089 {
1090 LogRel(("Shared Clipboard: Failed to add input to Xt app context\n"));
1091 rc = VERR_ACCESS_DENIED; /** @todo r=andy Improve this. */
1092 }
1093#endif
1094 }
1095 }
1096
1097 if (RT_SUCCESS(rc))
1098 {
1099 XtSetMappedWhenManaged(pCtx->pWidget, false);
1100 XtRealizeWidget(pCtx->pWidget);
1101
1102#ifndef TESTCASE
1103 /* Enable clipboard update notification. */
1104 pCtx->fixesSelectInput(pDisplay, XtWindow(pCtx->pWidget),
1105 clipGetAtom(pCtx, "CLIPBOARD"),
1106 7 /* All XFixes*Selection*NotifyMask flags */);
1107#endif
1108 }
1109
1110 if (RT_FAILURE(rc))
1111 {
1112 LogRel(("Shared Clipboard: Initialisation failed: %Rrc\n", rc));
1113 clipUninitInternal(pCtx);
1114 }
1115
1116 LogFlowFuncLeaveRC(rc);
1117 return rc;
1118}
1119
1120/**
1121 * X11-specific uninitialisation for the Shared Clipboard.
1122 *
1123 * Note: Must be called from the thread serving the Xt stuff.
1124 *
1125 * @param pCtx The X11 clipboard context to uninit.
1126 */
1127static void clipUninitInternal(PSHCLX11CTX pCtx)
1128{
1129 AssertPtrReturnVoid(pCtx);
1130
1131 LogFlowFunc(("pCtx=%p\n", pCtx));
1132
1133 if (pCtx->pWidget)
1134 {
1135 /* Valid widget + invalid appcontext = bug. But don't return yet. */
1136 AssertPtr(pCtx->pAppContext);
1137
1138 XtDestroyWidget(pCtx->pWidget);
1139 pCtx->pWidget = NULL;
1140 }
1141
1142 if (pCtx->pAppContext)
1143 {
1144 XtDestroyApplicationContext(pCtx->pAppContext);
1145 pCtx->pAppContext = NULL;
1146 }
1147
1148 LogFlowFuncLeaveRC(VINF_SUCCESS);
1149}
1150
1151/**
1152 * Initializes a X11 context of the Shared Clipboard.
1153 *
1154 * @returns VBox status code.
1155 * @param pCtx The clipboard context to initialize.
1156 * @param pParent Parent context to use.
1157 * @param fHeadless Whether the code runs in a headless environment or not.
1158 */
1159int ShClX11Init(PSHCLX11CTX pCtx, PSHCLCONTEXT pParent, bool fHeadless)
1160{
1161 AssertPtrReturn(pCtx, VERR_INVALID_POINTER);
1162#if !defined(SMOKETEST) && !defined(TESTCASE)
1163 /* Smoktests / Testcases don't have a (valid) parent. */
1164 AssertPtrReturn(pParent, VERR_INVALID_POINTER);
1165#endif
1166
1167 LogFlowFunc(("pCtx=%p\n", pCtx));
1168
1169 int rc = VINF_SUCCESS;
1170
1171 RT_BZERO(pCtx, sizeof(SHCLX11CTX));
1172
1173 if (fHeadless)
1174 {
1175 /*
1176 * If we don't find the DISPLAY environment variable we assume that
1177 * we are not connected to an X11 server. Don't actually try to do
1178 * this then, just fail silently and report success on every call.
1179 * This is important for VBoxHeadless.
1180 */
1181 LogRel(("Shared Clipboard: X11 DISPLAY variable not set -- disabling clipboard sharing\n"));
1182 }
1183
1184 pCtx->fHaveX11 = !fHeadless;
1185 pCtx->pFrontend = pParent;
1186
1187#ifdef VBOX_WITH_SHARED_CLIPBOARD_XT_BUSY
1188 pCtx->fXtBusy = false;
1189 pCtx->fXtNeedsUpdate = false;
1190#endif
1191
1192#ifdef VBOX_WITH_SHARED_CLIPBOARD_TRANSFERS_HTTP
1193 ShClTransferHttpServerInit(&pCtx->HttpCtx.HttpServer);
1194#endif
1195
1196#ifdef TESTCASE
1197 if (RT_SUCCESS(rc))
1198 {
1199 /** @todo The testcases currently do not utilize the threading code. So init stuff here. */
1200 rc = clipInitInternal(pCtx);
1201 if (RT_SUCCESS(rc))
1202 rc = clipRegisterContext(pCtx);
1203 }
1204#endif
1205
1206 LogFlowFuncLeaveRC(rc);
1207 return rc;
1208}
1209
1210/**
1211 * Destroys a Shared Clipboard X11 context.
1212 *
1213 * @param pCtx The X11 clipboard context to destroy.
1214 */
1215void ShClX11Destroy(PSHCLX11CTX pCtx)
1216{
1217 if (!pCtx)
1218 return;
1219
1220 LogFlowFunc(("pCtx=%p\n", pCtx));
1221
1222#ifdef TESTCASE
1223 /** @todo The testcases currently do not utilize the threading code. So uninit stuff here. */
1224 clipUnregisterContext(pCtx);
1225 clipUninitInternal(pCtx);
1226#endif
1227
1228 if (pCtx->fHaveX11)
1229 {
1230 /* We set this to NULL when the event thread exits. It really should
1231 * have exited at this point, when we are about to unload the code from
1232 * memory. */
1233 Assert(pCtx->pWidget == NULL);
1234 }
1235}
1236
1237#ifndef TESTCASE
1238/**
1239 * Starts our own Xt even thread for handling Shared Clipboard messages.
1240 *
1241 * @returns VBox status code.
1242 * @param pCtx The X11 clipboard context to use.
1243 * @param fGrab Whether we should try to grab the shared clipboard at once.
1244 */
1245int ShClX11ThreadStart(PSHCLX11CTX pCtx, bool fGrab)
1246{
1247 AssertPtrReturn(pCtx, VERR_INVALID_POINTER);
1248
1249 /*
1250 * Immediately return if we are not connected to the X server.
1251 */
1252 if (!pCtx->fHaveX11)
1253 return VINF_SUCCESS;
1254
1255 pCtx->fGrabClipboardOnStart = fGrab;
1256
1257 clipResetX11Formats(pCtx);
1258
1259 int rc;
1260
1261 /*
1262 * Create the pipes.
1263 ** @todo r=andy Replace this with RTPipe API.
1264 */
1265 int pipes[2];
1266 if (!pipe(pipes))
1267 {
1268 pCtx->wakeupPipeRead = pipes[0];
1269 pCtx->wakeupPipeWrite = pipes[1];
1270
1271 if (!fcntl(pCtx->wakeupPipeRead, F_SETFL, O_NONBLOCK))
1272 {
1273 rc = VINF_SUCCESS;
1274 }
1275 else
1276 rc = RTErrConvertFromErrno(errno);
1277 }
1278 else
1279 rc = RTErrConvertFromErrno(errno);
1280
1281 if (RT_SUCCESS(rc))
1282 {
1283 LogRel2(("Shared Clipboard: Starting X11 event thread ...\n"));
1284
1285 rc = RTThreadCreate(&pCtx->Thread, clipThreadMain, pCtx, 0,
1286 RTTHREADTYPE_IO, RTTHREADFLAGS_WAITABLE, "SHCLX11");
1287 if (RT_SUCCESS(rc))
1288 rc = RTThreadUserWait(pCtx->Thread, RT_MS_30SEC /* msTimeout */);
1289
1290 if (RT_FAILURE(rc))
1291 {
1292 LogRel(("Shared Clipboard: Failed to start the X11 event thread with %Rrc\n", rc));
1293 clipUninitInternal(pCtx);
1294 }
1295 else
1296 {
1297 if (!pCtx->fThreadStarted)
1298 {
1299 LogRel(("Shared Clipboard: X11 event thread reported an error while starting\n"));
1300 }
1301 else
1302 LogRel2(("Shared Clipboard: X11 event thread started\n"));
1303 }
1304 }
1305
1306 LogFlowFuncLeaveRC(rc);
1307 return rc;
1308}
1309
1310/**
1311 * Stops the Shared Clipboard Xt even thread.
1312 *
1313 * @note Any requests from this object to get clipboard data from VBox
1314 * *must* have completed or aborted before we are called, as
1315 * otherwise the X11 event loop will still be waiting for the request
1316 * to return and will not be able to terminate.
1317 *
1318 * @returns VBox status code.
1319 * @param pCtx The X11 clipboard context to use.
1320 */
1321int ShClX11ThreadStop(PSHCLX11CTX pCtx)
1322{
1323 int rc;
1324 /*
1325 * Immediately return if we are not connected to the X server.
1326 */
1327 if (!pCtx->fHaveX11)
1328 return VINF_SUCCESS;
1329
1330 LogRel2(("Shared Clipboard: Signalling the X11 event thread to stop\n"));
1331
1332 /* Write to the "stop" pipe. */
1333 rc = clipThreadScheduleCall(pCtx, clipThreadSignalStop, (XtPointer)pCtx);
1334 if (RT_FAILURE(rc))
1335 {
1336 LogRel(("Shared Clipboard: cannot notify X11 event thread on shutdown with %Rrc\n", rc));
1337 return rc;
1338 }
1339
1340 LogRel2(("Shared Clipboard: Waiting for X11 event thread to stop ...\n"));
1341
1342 int rcThread;
1343 rc = RTThreadWait(pCtx->Thread, RT_MS_30SEC /* msTimeout */, &rcThread);
1344 if (RT_SUCCESS(rc))
1345 rc = rcThread;
1346 if (RT_SUCCESS(rc))
1347 {
1348 if (pCtx->wakeupPipeRead != 0)
1349 {
1350 close(pCtx->wakeupPipeRead);
1351 pCtx->wakeupPipeRead = 0;
1352 }
1353
1354 if (pCtx->wakeupPipeWrite != 0)
1355 {
1356 close(pCtx->wakeupPipeWrite);
1357 pCtx->wakeupPipeWrite = 0;
1358 }
1359 }
1360
1361 if (RT_SUCCESS(rc))
1362 {
1363 LogRel2(("Shared Clipboard: X11 event thread stopped successfully\n"));
1364 }
1365 else
1366 LogRel(("Shared Clipboard: Stopping X11 event thread failed with %Rrc\n", rc));
1367
1368 LogFlowFuncLeaveRC(rc);
1369 return rc;
1370}
1371#endif /* !TESTCASE */
1372
1373/**
1374 * Returns the targets supported by VBox.
1375 *
1376 * This will return a list of atoms which tells the caller
1377 * what kind of clipboard formats we support.
1378 *
1379 * @returns VBox status code.
1380 * @param pCtx The X11 clipboard context to use.
1381 * @param atomTypeReturn The type of the data we are returning.
1382 * @param pValReturn A pointer to the data we are returning. This
1383 * should be set to memory allocated by XtMalloc,
1384 * which will be freed later by the Xt toolkit.
1385 * @param pcLenReturn The length of the data we are returning.
1386 * @param piFormatReturn The format (8bit, 16bit, 32bit) of the data we are
1387 * returning.
1388 * @note X11 backend code, called by the XtOwnSelection callback.
1389 */
1390static int clipCreateX11Targets(PSHCLX11CTX pCtx, Atom *atomTypeReturn,
1391 XtPointer *pValReturn,
1392 unsigned long *pcLenReturn,
1393 int *piFormatReturn)
1394{
1395 const unsigned cFixedTargets = 3; /* See below. */
1396
1397 Atom *pAtomTargets = (Atom *)XtMalloc((SHCL_MAX_X11_FORMATS + cFixedTargets) * sizeof(Atom));
1398 if (!pAtomTargets)
1399 return VERR_NO_MEMORY;
1400
1401 unsigned cTargets = 0;
1402 SHCLX11FMTIDX idxFmt = NIL_CLIPX11FORMAT;
1403 do
1404 {
1405 idxFmt = clipEnumX11Formats(pCtx->vboxFormats, idxFmt);
1406 if (idxFmt != NIL_CLIPX11FORMAT)
1407 {
1408 pAtomTargets[cTargets] = clipAtomForX11Format(pCtx, idxFmt);
1409 ++cTargets;
1410 }
1411 } while (idxFmt != NIL_CLIPX11FORMAT);
1412
1413 /* We always offer these fixed targets. */
1414 pAtomTargets[cTargets] = clipGetAtom(pCtx, "TARGETS");
1415 pAtomTargets[cTargets + 1] = clipGetAtom(pCtx, "MULTIPLE");
1416 pAtomTargets[cTargets + 2] = clipGetAtom(pCtx, "TIMESTAMP");
1417
1418 *atomTypeReturn = XA_ATOM;
1419 *pValReturn = (XtPointer)pAtomTargets;
1420 *pcLenReturn = cTargets + cFixedTargets;
1421 *piFormatReturn = 32;
1422
1423 LogFlowFunc(("cTargets=%u\n", cTargets + cFixedTargets));
1424
1425 return VINF_SUCCESS;
1426}
1427
1428/**
1429 * Helper for ShClX11RequestDataForX11Callback() that will cache the data returned.
1430 *
1431 * @returns VBox status code. VERR_NO_DATA if no data available.
1432 * @param pCtx The X11 clipboard context to use.
1433 * @param uFmt Clipboard format to read data in.
1434 * @param ppv Returns an allocated buffer with data read on success.
1435 * Needs to be free'd with RTMemFree() by the caller.
1436 * @param pcb Returns the amount of data read (in bytes) on success.
1437 */
1438static int shClX11RequestDataForX11CallbackHelper(PSHCLX11CTX pCtx, SHCLFORMAT uFmt,
1439 void **ppv, uint32_t *pcb)
1440{
1441 AssertPtrReturn(pCtx, VERR_INVALID_POINTER);
1442 AssertPtrReturn(ppv, VERR_INVALID_POINTER);
1443 AssertPtrReturn(pcb, VERR_INVALID_POINTER);
1444
1445 LogFlowFunc(("pCtx=%p, uFmt=%#x\n", pCtx, uFmt));
1446
1447 int rc = VINF_SUCCESS;
1448
1449 void *pv = NULL;
1450 uint32_t cb = 0;
1451
1452 if (uFmt == VBOX_SHCL_FMT_UNICODETEXT)
1453 {
1454 if (pCtx->pvUnicodeCache == NULL) /** @todo r=andy Using string cache here? */
1455 rc = ShClX11RequestDataCallback(pCtx->pFrontend, uFmt,
1456 &pCtx->pvUnicodeCache,
1457 &pCtx->cbUnicodeCache);
1458 if (RT_SUCCESS(rc))
1459 {
1460 pv = RTMemDup(pCtx->pvUnicodeCache, pCtx->cbUnicodeCache);
1461 if (pv)
1462 cb = pCtx->cbUnicodeCache;
1463 else
1464 rc = VERR_NO_MEMORY;
1465 }
1466 }
1467 else
1468 rc = ShClX11RequestDataCallback(pCtx->pFrontend, uFmt, &pv, &cb);
1469
1470
1471 /* Safey net in case the callbacks above misbehave
1472 * (must return VERR_NO_DATA if no data available). */
1473 if ( RT_SUCCESS(rc)
1474 && (pv == NULL || cb == 0))
1475 rc = VERR_NO_DATA;
1476
1477 if (RT_SUCCESS(rc))
1478 {
1479 *ppv = pv;
1480 *pcb = cb;
1481 }
1482
1483 LogFlowFunc(("Returning pv=%p, cb=%RU32, rc=%Rrc\n", pv, cb, rc));
1484 return rc;
1485}
1486
1487/**
1488 * Satisfies a request from X11 to convert the clipboard text to UTF-8 LF.
1489 *
1490 * @returns VBox status code. VERR_NO_DATA if no data was converted.
1491 * @param pDisplay An X11 display structure, needed for conversions
1492 * performed by Xlib.
1493 * @param pv The text to be converted (UCS-2 with Windows EOLs).
1494 * @param cb The length of the text in @cb in bytes.
1495 * @param atomTypeReturn Where to store the atom for the type of the data
1496 * we are returning.
1497 * @param pValReturn Where to store the pointer to the data we are
1498 * returning. This should be to memory allocated by
1499 * XtMalloc, which will be freed by the Xt toolkit
1500 * later.
1501 * @param pcLenReturn Where to store the length of the data we are
1502 * returning.
1503 * @param piFormatReturn Where to store the bit width (8, 16, 32) of the
1504 * data we are returning.
1505 */
1506static int clipConvertUtf16ToX11Data(Display *pDisplay, PRTUTF16 pwszSrc,
1507 size_t cbSrc, Atom *atomTarget,
1508 Atom *atomTypeReturn,
1509 XtPointer *pValReturn,
1510 unsigned long *pcLenReturn,
1511 int *piFormatReturn)
1512{
1513 RT_NOREF(pDisplay);
1514 AssertReturn(cbSrc % sizeof(RTUTF16) == 0, VERR_INVALID_PARAMETER);
1515
1516 const size_t cwcSrc = cbSrc / sizeof(RTUTF16);
1517 if (!cwcSrc)
1518 return VERR_NO_DATA;
1519
1520 /* This may slightly overestimate the space needed. */
1521 size_t chDst = 0;
1522 int rc = ShClUtf16LenUtf8(pwszSrc, cwcSrc, &chDst);
1523 if (RT_SUCCESS(rc))
1524 {
1525 chDst++; /* Add space for terminator. */
1526
1527 char *pszDst = (char *)XtMalloc(chDst);
1528 if (pszDst)
1529 {
1530 size_t cbActual = 0;
1531 rc = ShClConvUtf16CRLFToUtf8LF(pwszSrc, cwcSrc, pszDst, chDst, &cbActual);
1532 if (RT_SUCCESS(rc))
1533 {
1534 *atomTypeReturn = *atomTarget;
1535 *pValReturn = (XtPointer)pszDst;
1536 *pcLenReturn = cbActual + 1 /* Include terminator */;
1537 *piFormatReturn = 8;
1538 }
1539 }
1540 else
1541 rc = VERR_NO_MEMORY;
1542 }
1543
1544 LogFlowFuncLeaveRC(rc);
1545 return rc;
1546}
1547
1548/**
1549 * Satisfies a request from X11 to convert the clipboard HTML fragment to UTF-8. We
1550 * return null-terminated text, but can cope with non-null-terminated input.
1551 *
1552 * @returns VBox status code.
1553 * @param pDisplay An X11 display structure, needed for conversions
1554 * performed by Xlib.
1555 * @param pv The text to be converted (UTF8 with Windows EOLs).
1556 * @param cb The length of the text in @cb in bytes.
1557 * @param atomTypeReturn Where to store the atom for the type of the data
1558 * we are returning.
1559 * @param pValReturn Where to store the pointer to the data we are
1560 * returning. This should be to memory allocated by
1561 * XtMalloc, which will be freed by the Xt toolkit later.
1562 * @param pcLenReturn Where to store the length of the data we are returning.
1563 * @param piFormatReturn Where to store the bit width (8, 16, 32) of the
1564 * data we are returning.
1565 */
1566static int clipConvertHtmlToX11Data(Display *pDisplay, const char *pszSrc,
1567 size_t cbSrc, Atom *atomTarget,
1568 Atom *atomTypeReturn,
1569 XtPointer *pValReturn,
1570 unsigned long *pcLenReturn,
1571 int *piFormatReturn)
1572{
1573 RT_NOREF(pDisplay, pValReturn);
1574
1575 /* This may slightly overestimate the space needed. */
1576 LogFlowFunc(("Source: %s", pszSrc));
1577
1578 char *pszDest = (char *)XtMalloc(cbSrc);
1579 if (pszDest == NULL)
1580 return VERR_NO_MEMORY;
1581
1582 memcpy(pszDest, pszSrc, cbSrc);
1583
1584 *atomTypeReturn = *atomTarget;
1585 *pValReturn = (XtPointer)pszDest;
1586 *pcLenReturn = cbSrc;
1587 *piFormatReturn = 8;
1588
1589 return VINF_SUCCESS;
1590}
1591
1592
1593/**
1594 * Does this atom correspond to one of the two selection types we support?
1595 *
1596 * @param pCtx The X11 clipboard context to use.
1597 * @param selType The atom in question.
1598 */
1599static bool clipIsSupportedSelectionType(PSHCLX11CTX pCtx, Atom selType)
1600{
1601 return( (selType == clipGetAtom(pCtx, "CLIPBOARD"))
1602 || (selType == clipGetAtom(pCtx, "PRIMARY")));
1603}
1604
1605/**
1606 * Removes a trailing nul character from a string by adjusting the string
1607 * length. Some X11 applications don't like zero-terminated text...
1608 *
1609 * @param pText The text in question.
1610 * @param pcText The length of the text, adjusted on return.
1611 * @param format The format of the text.
1612 */
1613static void clipTrimTrailingNul(XtPointer pText, unsigned long *pcText,
1614 SHCLX11FMT format)
1615{
1616 AssertPtrReturnVoid(pText);
1617 AssertPtrReturnVoid(pcText);
1618 AssertReturnVoid((format == SHCLX11FMT_UTF8) || (format == SHCLX11FMT_TEXT) || (format == SHCLX11FMT_HTML));
1619
1620 if (((char *)pText)[*pcText - 1] == '\0')
1621 --(*pcText);
1622}
1623
1624static int clipConvertToX11Data(PSHCLX11CTX pCtx, Atom *atomTarget,
1625 Atom *atomTypeReturn,
1626 XtPointer *pValReturn,
1627 unsigned long *pcLenReturn,
1628 int *piFormatReturn)
1629{
1630 int rc = VERR_NOT_SUPPORTED; /* Play safe by default. */
1631
1632 SHCLX11FMTIDX idxFmtX11 = clipFindX11FormatByAtom(pCtx, *atomTarget);
1633 SHCLX11FMT fmtX11 = clipRealFormatForX11Format(idxFmtX11);
1634
1635 LogFlowFunc(("vboxFormats=0x%x, idxFmtX11=%u ('%s'), fmtX11=%u\n",
1636 pCtx->vboxFormats, idxFmtX11, g_aFormats[idxFmtX11].pcszAtom, fmtX11));
1637
1638#ifdef LOG_ENABLED
1639 char *pszFmts = ShClFormatsToStrA(pCtx->vboxFormats);
1640 AssertPtrReturn(pszFmts, VERR_NO_MEMORY);
1641 LogRel2(("Shared Clipboard: Converting VBox formats '%s' to '%s' for X11\n",
1642 pszFmts, g_aFormats[idxFmtX11].pcszAtom));
1643 RTStrFree(pszFmts);
1644#endif
1645
1646 void *pv = NULL;
1647 uint32_t cb = 0;
1648
1649 if ( ( (fmtX11 == SHCLX11FMT_UTF8)
1650 || (fmtX11 == SHCLX11FMT_TEXT)
1651 )
1652 && (pCtx->vboxFormats & VBOX_SHCL_FMT_UNICODETEXT))
1653 {
1654 rc = shClX11RequestDataForX11CallbackHelper(pCtx, VBOX_SHCL_FMT_UNICODETEXT, &pv, &cb);
1655 if ( RT_SUCCESS(rc)
1656 && ( (fmtX11 == SHCLX11FMT_UTF8)
1657 || (fmtX11 == SHCLX11FMT_TEXT)))
1658 {
1659 rc = clipConvertUtf16ToX11Data(XtDisplay(pCtx->pWidget),
1660 (PRTUTF16)pv, cb, atomTarget,
1661 atomTypeReturn, pValReturn,
1662 pcLenReturn, piFormatReturn);
1663 }
1664
1665 if (RT_SUCCESS(rc))
1666 clipTrimTrailingNul(*(XtPointer *)pValReturn, pcLenReturn, fmtX11);
1667
1668 RTMemFree(pv);
1669 }
1670 else if ( (fmtX11 == SHCLX11FMT_BMP)
1671 && (pCtx->vboxFormats & VBOX_SHCL_FMT_BITMAP))
1672 {
1673 rc = shClX11RequestDataForX11CallbackHelper(pCtx, VBOX_SHCL_FMT_BITMAP, &pv, &cb);
1674 if ( RT_SUCCESS(rc)
1675 && (fmtX11 == SHCLX11FMT_BMP))
1676 {
1677 /* Create a full BMP from it. */
1678 rc = ShClDibToBmp(pv, cb, (void **)pValReturn,
1679 (size_t *)pcLenReturn);
1680 }
1681
1682 if (RT_SUCCESS(rc))
1683 {
1684 *atomTypeReturn = *atomTarget;
1685 *piFormatReturn = 8;
1686 }
1687
1688 RTMemFree(pv);
1689 }
1690 else if ( (fmtX11 == SHCLX11FMT_HTML)
1691 && (pCtx->vboxFormats & VBOX_SHCL_FMT_HTML))
1692 {
1693 rc = shClX11RequestDataForX11CallbackHelper(pCtx, VBOX_SHCL_FMT_HTML, &pv, &cb);
1694 if (RT_SUCCESS(rc))
1695 {
1696 /**
1697 * The common VBox HTML encoding will be UTF-8.
1698 * Before sending it to the X11 clipboard we have to convert it to UTF-8 first.
1699 *
1700 * Strange that we get UTF-16 from the X11 clipboard, but
1701 * in same time we send UTF-8 to X11 clipboard and it works.
1702 ** @todo r=andy Verify this.
1703 */
1704 rc = clipConvertHtmlToX11Data(XtDisplay(pCtx->pWidget),
1705 (const char*)pv, cb, atomTarget,
1706 atomTypeReturn, pValReturn,
1707 pcLenReturn, piFormatReturn);
1708 if (RT_SUCCESS(rc))
1709 clipTrimTrailingNul(*(XtPointer *)pValReturn, pcLenReturn, fmtX11);
1710
1711 RTMemFree(pv);
1712 }
1713 }
1714#ifdef VBOX_WITH_SHARED_CLIPBOARD_TRANSFERS
1715 else if (fmtX11 == SHCLX11FMT_URI_LIST)
1716 {
1717 if (pCtx->vboxFormats & VBOX_SHCL_FMT_URI_LIST)
1718 {
1719 rc = shClX11RequestDataForX11CallbackHelper(pCtx, VBOX_SHCL_FMT_URI_LIST, &pv, &cb);
1720 if (RT_SUCCESS(rc))
1721 {
1722 void *pvDst = (void *)XtMalloc(cb);
1723 if (pvDst)
1724 {
1725 memcpy(pvDst, pv, cb);
1726
1727 *atomTypeReturn = *atomTarget;
1728 *pValReturn = (XtPointer)pvDst;
1729 *pcLenReturn = cb;
1730 *piFormatReturn = 8;
1731 }
1732 else
1733 rc = VERR_NO_MEMORY;
1734 }
1735 }
1736 /* else not supported yet. */
1737 }
1738#endif
1739 else
1740 {
1741 *atomTypeReturn = XT_CONVERT_FAIL;
1742 *pValReturn = (XtPointer)NULL;
1743 *pcLenReturn = 0;
1744 *piFormatReturn = 0;
1745 }
1746
1747 if (RT_FAILURE(rc))
1748 {
1749 char *pszFmts2 = ShClFormatsToStrA(pCtx->vboxFormats);
1750 char *pszAtomName = XGetAtomName(XtDisplay(pCtx->pWidget), *atomTarget);
1751
1752 LogRel(("Shared Clipboard: Converting VBox formats '%s' to '%s' for X11 (idxFmtX11=%u, fmtX11=%u, atomTarget='%s') failed, rc=%Rrc\n",
1753 pszFmts2 ? pszFmts2 : "unknown", g_aFormats[idxFmtX11].pcszAtom, idxFmtX11, fmtX11, pszAtomName ? pszAtomName : "unknown", rc));
1754
1755 if (pszFmts2)
1756 RTStrFree(pszFmts2);
1757 if (pszAtomName)
1758 XFree(pszAtomName);
1759 }
1760
1761 LogFlowFuncLeaveRC(rc);
1762 return rc;
1763}
1764
1765/**
1766 * Returns VBox's clipboard data for an X11 client.
1767 *
1768 * @note Callback for XtOwnSelection.
1769 */
1770static Boolean clipXtConvertSelectionProc(Widget widget, Atom *atomSelection,
1771 Atom *atomTarget,
1772 Atom *atomTypeReturn,
1773 XtPointer *pValReturn,
1774 unsigned long *pcLenReturn,
1775 int *piFormatReturn)
1776{
1777 LogFlowFuncEnter();
1778
1779 PSHCLX11CTX pCtx = clipLookupContext(widget);
1780 if (!pCtx)
1781 return False;
1782
1783 /* Is this the rigt selection (clipboard) we were asked for? */
1784 if (!clipIsSupportedSelectionType(pCtx, *atomSelection))
1785 return False;
1786
1787 int rc;
1788 if (*atomTarget == clipGetAtom(pCtx, "TARGETS"))
1789 rc = clipCreateX11Targets(pCtx, atomTypeReturn, pValReturn,
1790 pcLenReturn, piFormatReturn);
1791 else
1792 rc = clipConvertToX11Data(pCtx, atomTarget, atomTypeReturn,
1793 pValReturn, pcLenReturn, piFormatReturn);
1794
1795 LogFlowFunc(("returning %RTbool, rc=%Rrc\n", RT_SUCCESS(rc), rc));
1796 return RT_SUCCESS(rc) ? True : False;
1797}
1798
1799/**
1800 * Structure used to pass information about formats that VBox supports.
1801 */
1802typedef struct _CLIPNEWVBOXFORMATS
1803{
1804 /** Context information for the X11 clipboard. */
1805 PSHCLX11CTX pCtx;
1806 /** Formats supported by VBox. */
1807 SHCLFORMATS Formats;
1808} CLIPNEWVBOXFORMATS, *PCLIPNEWVBOXFORMATS;
1809
1810
1811
1812/**
1813 * Invalidates the local cache of the data in the VBox clipboard.
1814 *
1815 * @param pCtx The X11 clipboard context to use.
1816 */
1817static void clipInvalidateClipboardCache(PSHCLX11CTX pCtx)
1818{
1819 if (pCtx->pvUnicodeCache != NULL)
1820 {
1821 RTMemFree(pCtx->pvUnicodeCache);
1822 pCtx->pvUnicodeCache = NULL;
1823 }
1824}
1825
1826/**
1827 * Takes possession of the X11 clipboard (and middle-button selection).
1828 *
1829 * @param pCtx The X11 clipboard context to use.
1830 * @param uFormats Clipboard formats to set.
1831 */
1832static void clipGrabX11Clipboard(PSHCLX11CTX pCtx, SHCLFORMATS uFormats)
1833{
1834 LogFlowFuncEnter();
1835
1836 if (XtOwnSelection(pCtx->pWidget, clipGetAtom(pCtx, "CLIPBOARD"),
1837 CurrentTime, clipXtConvertSelectionProc, NULL, 0))
1838 {
1839 pCtx->vboxFormats = uFormats;
1840
1841 /* Grab the middle-button paste selection too. */
1842 XtOwnSelection(pCtx->pWidget, clipGetAtom(pCtx, "PRIMARY"),
1843 CurrentTime, clipXtConvertSelectionProc, NULL, 0);
1844#ifndef TESTCASE
1845 /* Xt suppresses these if we already own the clipboard, so send them
1846 * ourselves. */
1847 XSetSelectionOwner(XtDisplay(pCtx->pWidget),
1848 clipGetAtom(pCtx, "CLIPBOARD"),
1849 XtWindow(pCtx->pWidget), CurrentTime);
1850 XSetSelectionOwner(XtDisplay(pCtx->pWidget),
1851 clipGetAtom(pCtx, "PRIMARY"),
1852 XtWindow(pCtx->pWidget), CurrentTime);
1853#endif
1854 }
1855}
1856
1857/**
1858 * Worker function for ShClX11ReportFormatsToX11 which runs on the
1859 * event thread.
1860 *
1861 * @param pvUserData Pointer to a CLIPNEWVBOXFORMATS structure containing
1862 * information about the VBox formats available and the
1863 * clipboard context data. Must be freed by the worker.
1864 */
1865static void ShClX11ReportFormatsToX11Worker(void *pvUserData, void * /* interval */)
1866{
1867 AssertPtrReturnVoid(pvUserData);
1868
1869 CLIPNEWVBOXFORMATS *pFormats = (CLIPNEWVBOXFORMATS *)pvUserData;
1870
1871 PSHCLX11CTX pCtx = pFormats->pCtx;
1872 SHCLFORMATS fFormats = pFormats->Formats;
1873
1874 RTMemFree(pFormats);
1875
1876 LogFlowFunc (("fFormats=0x%x\n", fFormats));
1877
1878 clipInvalidateClipboardCache(pCtx);
1879 clipGrabX11Clipboard(pCtx, fFormats);
1880 clipResetX11Formats(pCtx);
1881
1882 LogFlowFuncLeave();
1883}
1884
1885/**
1886 * Announces new clipboard formats to the X11 clipboard.
1887 *
1888 * @returns VBox status code.
1889 * @param pCtx Context data for the clipboard backend.
1890 * @param uFormats Clipboard formats offered.
1891 */
1892int ShClX11ReportFormatsToX11(PSHCLX11CTX pCtx, SHCLFORMATS uFormats)
1893{
1894 /*
1895 * Immediately return if we are not connected to the X server.
1896 */
1897 if (!pCtx->fHaveX11)
1898 return VINF_SUCCESS;
1899
1900 int rc;
1901
1902 /* This must be free'd by the worker callback. */
1903 PCLIPNEWVBOXFORMATS pFormats = (PCLIPNEWVBOXFORMATS)RTMemAlloc(sizeof(CLIPNEWVBOXFORMATS));
1904 if (pFormats)
1905 {
1906 pFormats->pCtx = pCtx;
1907 pFormats->Formats = uFormats;
1908
1909 rc = clipThreadScheduleCall(pCtx, ShClX11ReportFormatsToX11Worker,
1910 (XtPointer)pFormats);
1911 if (RT_FAILURE(rc))
1912 RTMemFree(pFormats);
1913 }
1914 else
1915 rc = VERR_NO_MEMORY;
1916
1917 LogFlowFuncLeaveRC(rc);
1918 return rc;
1919}
1920
1921/**
1922 * Converts the data obtained from the X11 clipboard to the required format,
1923 * place it in the buffer supplied and signal that data has arrived.
1924 *
1925 * Converts the text obtained UTF-16LE with Windows EOLs.
1926 * Converts full BMP data to DIB format.
1927 */
1928SHCL_X11_DECL(void) clipConvertDataFromX11Worker(void *pClient, void *pvSrc, unsigned cbSrc)
1929{
1930 CLIPREADX11CBREQ *pReq = (CLIPREADX11CBREQ *)pClient;
1931 AssertPtrReturnVoid(pReq);
1932
1933 LogFlowFunc(("pReq->uFmtVBox=%#x, pReq->idxFmtX11=%u, pReq->pCtx=%p\n", pReq->uFmtVBox, pReq->idxFmtX11, pReq->pCtx));
1934
1935 LogRel2(("Shared Clipboard: Converting X11 format '%s' to VBox format %#x\n",
1936 g_aFormats[pReq->idxFmtX11].pcszAtom, pReq->uFmtVBox));
1937
1938 AssertPtr(pReq->pCtx);
1939 Assert(pReq->uFmtVBox != VBOX_SHCL_FMT_NONE); /* Sanity. */
1940
1941 int rc = VINF_SUCCESS;
1942
1943 void *pvDst = NULL;
1944 size_t cbDst = 0;
1945
1946#ifdef VBOX_WITH_SHARED_CLIPBOARD_XT_BUSY
1947 PSHCLX11CTX pCtx = pReq->pCtx;
1948 AssertPtr(pReq->pCtx);
1949 clipSetXtBusy(pCtx, false);
1950 if (clipGetXtNeedsUpdate(pCtx))
1951 clipQueryX11Targets(pCtx);
1952#endif
1953
1954 /* If X11 clipboard buffer has no data, libXt can pass to XtGetSelectionValue()
1955 * callback an empty string, in this case cbSrc is 0. */
1956 if (pvSrc == NULL || cbSrc == 0)
1957 {
1958 /* The clipboard selection may have changed before we could get it. */
1959 rc = VERR_NO_DATA;
1960 }
1961 else if (pReq->uFmtVBox == VBOX_SHCL_FMT_UNICODETEXT)
1962 {
1963 /* In which format is the clipboard data? */
1964 switch (clipRealFormatForX11Format(pReq->idxFmtX11))
1965 {
1966 case SHCLX11FMT_UTF8:
1967 RT_FALL_THROUGH();
1968 case SHCLX11FMT_TEXT:
1969 {
1970 size_t cwDst;
1971
1972 /* If we are given broken UTF-8, we treat it as Latin1. */ /** @todo BUGBUG Is this acceptable? */
1973 if (RT_SUCCESS(RTStrValidateEncodingEx((char *)pvSrc, cbSrc, 0)))
1974 rc = ShClConvUtf8LFToUtf16CRLF((const char *)pvSrc, cbSrc,
1975 (PRTUTF16 *)&pvDst, &cwDst);
1976 else
1977 rc = ShClConvLatin1LFToUtf16CRLF((char *)pvSrc, cbSrc,
1978 (PRTUTF16 *)&pvDst, &cwDst);
1979 if (RT_SUCCESS(rc))
1980 {
1981 cwDst += 1 /* Include terminator */;
1982 cbDst = cwDst * sizeof(RTUTF16); /* Convert RTUTF16 units to bytes. */
1983 }
1984 break;
1985 }
1986
1987 default:
1988 {
1989 rc = VERR_INVALID_PARAMETER;
1990 break;
1991 }
1992 }
1993 }
1994 else if (pReq->uFmtVBox == VBOX_SHCL_FMT_BITMAP)
1995 {
1996 /* In which format is the clipboard data? */
1997 switch (clipRealFormatForX11Format(pReq->idxFmtX11))
1998 {
1999 case SHCLX11FMT_BMP:
2000 {
2001 const void *pDib;
2002 size_t cbDibSize;
2003 rc = ShClBmpGetDib((const void *)pvSrc, cbSrc,
2004 &pDib, &cbDibSize);
2005 if (RT_SUCCESS(rc))
2006 {
2007 pvDst = RTMemAlloc(cbDibSize);
2008 if (!pvDst)
2009 rc = VERR_NO_MEMORY;
2010 else
2011 {
2012 memcpy(pvDst, pDib, cbDibSize);
2013 cbDst = cbDibSize;
2014 }
2015 }
2016 break;
2017 }
2018
2019 default:
2020 {
2021 rc = VERR_INVALID_PARAMETER;
2022 break;
2023 }
2024 }
2025 }
2026 else if (pReq->uFmtVBox == VBOX_SHCL_FMT_HTML)
2027 {
2028 /* In which format is the clipboard data? */
2029 switch (clipRealFormatForX11Format(pReq->idxFmtX11))
2030 {
2031 case SHCLX11FMT_HTML:
2032 {
2033 /*
2034 * The common VBox HTML encoding will be - UTF-8
2035 * because it more general for HTML formats then UTF-16
2036 * X11 clipboard returns UTF-16, so before sending it we should
2037 * convert it to UTF-8.
2038 */
2039 pvDst = NULL;
2040 cbDst = 0;
2041
2042 /*
2043 * Some applications sends data in UTF-16, some in UTF-8,
2044 * without indication it in MIME.
2045 *
2046 * In case of UTF-16, at least [Open|Libre] Office adds an byte order mark (0xfeff)
2047 * at the start of the clipboard data.
2048 */
2049 if ( cbSrc >= sizeof(RTUTF16)
2050 && *(PRTUTF16)pvSrc == VBOX_SHCL_UTF16LEMARKER)
2051 {
2052 rc = ShClConvUtf16ToUtf8HTML((PRTUTF16)pvSrc, cbSrc / sizeof(RTUTF16), (char**)&pvDst, &cbDst);
2053 if (RT_SUCCESS(rc))
2054 {
2055 LogFlowFunc(("UTF-16 Unicode source (%u bytes):\n%ls\n\n", cbSrc, pvSrc));
2056 LogFlowFunc(("Byte Order Mark = %hx", ((PRTUTF16)pvSrc)[0]));
2057 LogFlowFunc(("UTF-8 Unicode dest (%u bytes):\n%s\n\n", cbDst, pvDst));
2058 }
2059 else
2060 LogRel(("Shared Clipboard: Converting UTF-16 Unicode failed with %Rrc\n", rc));
2061 }
2062 else /* Raw data. */
2063 {
2064 pvDst = RTMemAllocZ(cbSrc + 1 /* '\0' */);
2065 if(pvDst)
2066 {
2067 memcpy(pvDst, pvSrc, cbSrc);
2068 cbDst = cbSrc + 1 /* '\0' */;
2069 }
2070 else
2071 {
2072 rc = VERR_NO_MEMORY;
2073 break;
2074 }
2075 }
2076
2077 rc = VINF_SUCCESS;
2078 break;
2079 }
2080
2081 default:
2082 {
2083 rc = VERR_INVALID_PARAMETER;
2084 break;
2085 }
2086 }
2087 }
2088# ifdef VBOX_WITH_SHARED_CLIPBOARD_TRANSFERS
2089 else if (pReq->uFmtVBox == VBOX_SHCL_FMT_URI_LIST)
2090 {
2091 /* In which format is the clipboard data? */
2092 switch (clipRealFormatForX11Format(pReq->idxFmtX11))
2093 {
2094 case SHCLX11FMT_URI_LIST:
2095 {
2096 /* For URI lists we only accept valid UTF-8 encodings. */
2097 if (RT_SUCCESS(RTStrValidateEncodingEx((char *)pvSrc, cbSrc, 0)))
2098 {
2099 /* URI lists on X are strings separated with "\r\n". */
2100 RTCList<RTCString> lstRootEntries = RTCString((char *)pvSrc, cbSrc).split("\r\n");
2101 for (size_t i = 0; i < lstRootEntries.size(); ++i)
2102 {
2103 char *pszEntry = RTUriFilePath(lstRootEntries.at(i).c_str());
2104 AssertPtrBreakStmt(pszEntry, VERR_INVALID_PARAMETER);
2105
2106 rc = RTStrAAppend((char **)&pvDst, "http://localhost");
2107 AssertRCBreakStmt(rc, VERR_NO_MEMORY);
2108 cbDst += (uint32_t)strlen(pszEntry);
2109
2110
2111
2112 /** @todo BUGBUG Fix port! */
2113 /** @todo Add port + UUID (virtual path). */
2114
2115 rc = RTStrAAppend((char **)&pvDst, pszEntry);
2116 AssertRCBreakStmt(rc, VERR_NO_MEMORY);
2117 cbDst += (uint32_t)strlen(pszEntry);
2118
2119 LogFlowFunc(("URI list entry '%s'\n", (char *)pvDst));
2120
2121 rc = RTStrAAppend((char **)&pvDst, "\r\n");
2122 AssertRCBreakStmt(rc, VERR_NO_MEMORY);
2123 cbDst += (uint32_t)strlen("\r\n");
2124
2125 RTStrFree(pszEntry);
2126 }
2127
2128 if (cbDst)
2129 cbDst++; /* Include final (zero) termination. */
2130
2131 LogFlowFunc(("URI list: cbDst=%RU32\n", cbDst));
2132 }
2133 else
2134 rc = VERR_INVALID_PARAMETER;
2135 break;
2136 }
2137
2138 default:
2139 {
2140 rc = VERR_INVALID_PARAMETER;
2141 break;
2142 }
2143 }
2144 }
2145# endif /* VBOX_WITH_SHARED_CLIPBOARD_TRANSFERS */
2146 else
2147 rc = VERR_NOT_SUPPORTED;
2148
2149 if (RT_FAILURE(rc))
2150 LogRel(("Shared Clipboard: Converting X11 format '%s' (idxFmtX11=%u) to VBox format %#x failed, rc=%Rrc\n",
2151 g_aFormats[pReq->idxFmtX11].pcszAtom, pReq->idxFmtX11, pReq->uFmtVBox, rc));
2152
2153 ShClX11ReportDataCallback(pReq->pCtx->pFrontend, rc, pReq->pReq, pvDst, cbDst);
2154
2155 RTMemFree(pvDst);
2156 RTMemFree(pReq);
2157
2158 LogFlowFuncLeaveRC(rc);
2159}
2160
2161/**
2162 * Converts the data obtained from the X11 clipboard to the required format,
2163 * place it in the buffer supplied and signal that data has arrived.
2164 *
2165 * Converts the text obtained UTF-16LE with Windows EOLs.
2166 * Converts full BMP data to DIB format.
2167 */
2168SHCL_X11_DECL(void) clipConvertDataFromX11(Widget widget, XtPointer pClient,
2169 Atom * /* selection */, Atom *atomType,
2170 XtPointer pvSrc, long unsigned int *pcLen,
2171 int *piFormat)
2172{
2173 RT_NOREF(widget);
2174 if (*atomType == XT_CONVERT_FAIL) /* Xt timeout */
2175 clipConvertDataFromX11Worker(pClient, NULL, 0);
2176 else
2177 clipConvertDataFromX11Worker(pClient, pvSrc, (*pcLen) * (*piFormat) / 8);
2178
2179 XtFree((char *)pvSrc);
2180}
2181
2182static int clipGetSelectionValue(PSHCLX11CTX pCtx, SHCLX11FMTIDX idxFmt,
2183 CLIPREADX11CBREQ *pReq)
2184{
2185#ifndef TESTCASE
2186 XtGetSelectionValue(pCtx->pWidget, clipGetAtom(pCtx, "CLIPBOARD"),
2187 clipAtomForX11Format(pCtx, idxFmt),
2188 clipConvertDataFromX11,
2189 reinterpret_cast<XtPointer>(pReq),
2190 CurrentTime);
2191#else
2192 tstClipRequestData(pCtx, idxFmt, (void *)pReq);
2193#endif
2194
2195 return VINF_SUCCESS; /** @todo Return real rc. */
2196}
2197
2198/**
2199 * Worker function for ShClX11ReadDataFromX11 which runs on the event thread.
2200 *
2201 * @param pvUserData Pointer to a CLIPREADX11CBREQ structure containing
2202 * information about the clipboard read request.
2203 * Must be free'd by the worker.
2204 */
2205static void ShClX11ReadDataFromX11Worker(void *pvUserData, void * /* interval */)
2206{
2207 AssertPtrReturnVoid(pvUserData);
2208
2209 CLIPREADX11CBREQ *pReq = (CLIPREADX11CBREQ *)pvUserData;
2210 SHCLX11CTX *pCtx = pReq->pCtx;
2211
2212 LogFlowFunc(("pReq->mFormat = %02x\n", pReq->uFmtVBox));
2213
2214 int rc = VERR_NO_DATA; /* VBox thinks we have data and we don't. */
2215
2216#ifdef VBOX_WITH_SHARED_CLIPBOARD_XT_BUSY
2217 const bool fXtBusy = clipGetXtBusy(pCtx);
2218 clipSetXtBusy(pCtx, true);
2219 if (fXtBusy)
2220 {
2221 /* If the clipboard is busy just fend off the request. */
2222 rc = VERR_TRY_AGAIN;
2223 }
2224 else
2225#endif
2226 if (pReq->uFmtVBox & VBOX_SHCL_FMT_UNICODETEXT)
2227 {
2228 pReq->idxFmtX11 = pCtx->idxFmtText;
2229 if (pReq->idxFmtX11 != SHCLX11FMT_INVALID)
2230 {
2231 /* Send out a request for the data to the current clipboard owner. */
2232 rc = clipGetSelectionValue(pCtx, pCtx->idxFmtText, pReq);
2233 }
2234 }
2235 else if (pReq->uFmtVBox & VBOX_SHCL_FMT_BITMAP)
2236 {
2237 pReq->idxFmtX11 = pCtx->idxFmtBmp;
2238 if (pReq->idxFmtX11 != SHCLX11FMT_INVALID)
2239 {
2240 /* Send out a request for the data to the current clipboard owner. */
2241 rc = clipGetSelectionValue(pCtx, pCtx->idxFmtBmp, pReq);
2242 }
2243 }
2244 else if (pReq->uFmtVBox & VBOX_SHCL_FMT_HTML)
2245 {
2246 pReq->idxFmtX11 = pCtx->idxFmtHTML;
2247 if (pReq->idxFmtX11 != SHCLX11FMT_INVALID)
2248 {
2249 /* Send out a request for the data to the current clipboard owner. */
2250 rc = clipGetSelectionValue(pCtx, pCtx->idxFmtHTML, pReq);
2251 }
2252 }
2253#ifdef VBOX_WITH_SHARED_CLIPBOARD_TRANSFERS
2254 else if (pReq->uFmtVBox & VBOX_SHCL_FMT_URI_LIST)
2255 {
2256 pReq->idxFmtX11 = pCtx->idxFmtURI;
2257 if (pReq->idxFmtX11 != SHCLX11FMT_INVALID)
2258 {
2259 /* Send out a request for the data to the current clipboard owner. */
2260 rc = clipGetSelectionValue(pCtx, pCtx->idxFmtURI, pReq);
2261 }
2262 }
2263#endif
2264 else
2265 {
2266#ifdef VBOX_WITH_SHARED_CLIPBOARD_XT_BUSY
2267 clipSetXtBusy(pCtx, false);
2268#endif
2269 rc = VERR_NOT_IMPLEMENTED;
2270 }
2271
2272 if (RT_FAILURE(rc))
2273 {
2274 /* The clipboard callback was never scheduled, so we must signal
2275 * that the request processing is finished and clean up ourselves. */
2276 ShClX11ReportDataCallback(pReq->pCtx->pFrontend, rc, pReq->pReq,
2277 NULL /* pv */ ,0 /* cb */);
2278 RTMemFree(pReq);
2279 }
2280
2281 LogFlowFuncLeaveRC(rc);
2282}
2283
2284/**
2285 * Called when VBox wants to read the X11 clipboard.
2286 *
2287 * @returns VBox status code.
2288 * @retval VERR_NO_DATA if format is supported but no data is available currently.
2289 * @retval VERR_NOT_IMPLEMENTED if the format is not implemented.
2290 * @param pCtx Context data for the clipboard backend.
2291 * @param Format The format that the VBox would like to receive the data in.
2292 * @param pReq Read callback request to use. Will be free'd in the callback on success.
2293 * Otherwise the caller has to free this again on error.
2294 *
2295 * @note We allocate a request structure which must be freed by the worker.
2296 */
2297int ShClX11ReadDataFromX11(PSHCLX11CTX pCtx, SHCLFORMAT Format, CLIPREADCBREQ *pReq)
2298{
2299 AssertPtrReturn(pReq, VERR_INVALID_POINTER);
2300
2301 /*
2302 * Immediately return if we are not connected to the X server.
2303 */
2304 if (!pCtx->fHaveX11)
2305 return VERR_NO_DATA;
2306
2307 int rc = VINF_SUCCESS;
2308
2309 CLIPREADX11CBREQ *pX11Req = (CLIPREADX11CBREQ *)RTMemAllocZ(sizeof(CLIPREADX11CBREQ));
2310 if (pX11Req)
2311 {
2312 pX11Req->pCtx = pCtx;
2313 pX11Req->uFmtVBox = Format;
2314 pX11Req->pReq = pReq;
2315
2316 /* We use this to schedule a worker function on the event thread. */
2317 rc = clipThreadScheduleCall(pCtx, ShClX11ReadDataFromX11Worker, (XtPointer)pX11Req);
2318 if (RT_FAILURE(rc))
2319 RTMemFree(pX11Req);
2320 }
2321 else
2322 rc = VERR_NO_MEMORY;
2323
2324 LogFlowFuncLeaveRC(rc);
2325 return rc;
2326}
Note: See TracBrowser for help on using the repository browser.

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