VirtualBox

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

最後變更 在這個檔案從82968是 82922,由 vboxsync 提交於 5 年 前

Shared Clipboard/tstClipboardGH-X11: More linking fun required for the testboxes.

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

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