VirtualBox

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

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

Shared Clipboard/X11: A bit of cleanup for threading start/stop code. bugref:9848

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