VirtualBox

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

Last change on this file since 88837 was 87566, checked in by vboxsync, 4 years ago

Shared Clipboard: doxygen fixes for unnecessary @copydoc in definitions and duplicate @param docs. (probably bugref:9437)

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

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