VirtualBox

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

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

SharedClipboard/clipboard-x11.cpp: Fix the crashing tstClipboardGH-X11 testcase as it expects a NULL terminated array of supported formats, leave two todos for the author

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