VirtualBox

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

Last change on this file since 68563 was 68563, checked in by vboxsync, 7 years ago

merging vbglioc r117720: x11-clipboard.cpp: clipLoadXFixes: pyramid code and LogRel statements detailing failure reason.

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 98.8 KB
Line 
1/** @file
2 *
3 * Shared Clipboard:
4 * X11 backend code.
5 */
6
7/*
8 * Copyright (C) 2006-2016 Oracle Corporation
9 *
10 * This file is part of VirtualBox Open Source Edition (OSE), as
11 * available from http://www.virtualbox.org. This file is free software;
12 * you can redistribute it and/or modify it under the terms of the GNU
13 * General Public License (GPL) as published by the Free Software
14 * Foundation, in version 2 as it comes in the "COPYING" file of the
15 * VirtualBox OSE distribution. VirtualBox OSE is distributed in the
16 * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
17 */
18
19/* Note: to automatically run regression tests on the shared clipboard,
20 * execute the tstClipboardX11 testcase. If you often make changes to the
21 * clipboard code, adding the line
22 * OTHERS += $(PATH_tstClipboardX11)/tstClipboardX11.run
23 * to LocalConfig.kmk will cause the tests to be run every time the code is
24 * changed. */
25
26#define LOG_GROUP LOG_GROUP_SHARED_CLIPBOARD
27
28#include <errno.h>
29
30#include <dlfcn.h>
31#include <fcntl.h>
32#include <unistd.h>
33
34#ifdef RT_OS_SOLARIS
35#include <tsol/label.h>
36#endif
37
38#include <X11/Xlib.h>
39#include <X11/Xatom.h>
40#include <X11/Intrinsic.h>
41#include <X11/Shell.h>
42#include <X11/Xproto.h>
43#include <X11/StringDefs.h>
44
45#include <iprt/types.h>
46#include <iprt/mem.h>
47#include <iprt/semaphore.h>
48#include <iprt/thread.h>
49
50#include <VBox/log.h>
51
52#include <VBox/GuestHost/SharedClipboard.h>
53#include <VBox/GuestHost/clipboard-helper.h>
54#include <VBox/HostServices/VBoxClipboardSvc.h>
55
56class formats;
57static Atom clipGetAtom(CLIPBACKEND *pCtx, const char *pszName);
58
59/** The different clipboard formats which we support. */
60enum CLIPFORMAT
61{
62 INVALID = 0,
63 TARGETS,
64 TEXT, /* Treat this as Utf8, but it may really be ascii */
65 UTF8,
66 BMP,
67 HTML
68};
69
70/** The table mapping X11 names to data formats and to the corresponding
71 * VBox clipboard formats (currently only Unicode) */
72static struct _CLIPFORMATTABLE
73{
74 /** The X11 atom name of the format (several names can match one format)
75 */
76 const char *pcszAtom;
77 /** The format corresponding to the name */
78 CLIPFORMAT enmFormat;
79 /** The corresponding VBox clipboard format */
80 uint32_t u32VBoxFormat;
81} g_aFormats[] =
82{
83 { "INVALID", INVALID, 0 },
84 { "UTF8_STRING", UTF8, VBOX_SHARED_CLIPBOARD_FMT_UNICODETEXT },
85 { "text/plain;charset=UTF-8", UTF8,
86 VBOX_SHARED_CLIPBOARD_FMT_UNICODETEXT },
87 { "text/plain;charset=utf-8", UTF8,
88 VBOX_SHARED_CLIPBOARD_FMT_UNICODETEXT },
89 { "STRING", TEXT, VBOX_SHARED_CLIPBOARD_FMT_UNICODETEXT },
90 { "TEXT", TEXT, VBOX_SHARED_CLIPBOARD_FMT_UNICODETEXT },
91 { "text/plain", TEXT, VBOX_SHARED_CLIPBOARD_FMT_UNICODETEXT },
92 { "text/html", HTML, VBOX_SHARED_CLIPBOARD_FMT_HTML },
93 { "text/html;charset=utf-8", HTML,
94 VBOX_SHARED_CLIPBOARD_FMT_HTML },
95 { "image/bmp", BMP, VBOX_SHARED_CLIPBOARD_FMT_BITMAP },
96 { "image/x-bmp", BMP, VBOX_SHARED_CLIPBOARD_FMT_BITMAP },
97 { "image/x-MS-bmp", BMP, VBOX_SHARED_CLIPBOARD_FMT_BITMAP }
98
99
100 /** @todo Inkscape exports image/png but not bmp... */
101};
102
103typedef unsigned CLIPX11FORMAT;
104
105enum
106{
107 NIL_CLIPX11FORMAT = 0,
108 MAX_CLIP_X11_FORMATS = RT_ELEMENTS(g_aFormats)
109};
110
111/** Return the atom corresponding to a supported X11 format.
112 * @param widget a valid Xt widget
113 */
114static Atom clipAtomForX11Format(CLIPBACKEND *pCtx, CLIPX11FORMAT format)
115{
116 return clipGetAtom(pCtx, g_aFormats[format].pcszAtom);
117}
118
119/** Return the CLIPFORMAT corresponding to a supported X11 format. */
120static CLIPFORMAT clipRealFormatForX11Format(CLIPX11FORMAT format)
121{
122 return g_aFormats[format].enmFormat;
123}
124
125/** Return the atom corresponding to a supported X11 format. */
126static uint32_t clipVBoxFormatForX11Format(CLIPX11FORMAT format)
127{
128 return g_aFormats[format].u32VBoxFormat;
129}
130
131/** Lookup the X11 format matching a given X11 atom.
132 * @returns the format on success, NIL_CLIPX11FORMAT on failure
133 * @param widget a valid Xt widget
134 */
135static CLIPX11FORMAT clipFindX11FormatByAtom(CLIPBACKEND *pCtx, Atom atomFormat)
136{
137 for (unsigned i = 0; i < RT_ELEMENTS(g_aFormats); ++i)
138 if (clipAtomForX11Format(pCtx, i) == atomFormat)
139 return i;
140 return NIL_CLIPX11FORMAT;
141}
142
143#ifdef TESTCASE
144/** Lookup the X11 format matching a given X11 atom text.
145 * @returns the format on success, NIL_CLIPX11FORMAT on failure
146 * @param widget a valid Xt widget
147 */
148static CLIPX11FORMAT clipFindX11FormatByAtomText(const char *pcsz)
149{
150 for (unsigned i = 0; i < RT_ELEMENTS(g_aFormats); ++i)
151 if (!strcmp(g_aFormats[i].pcszAtom, pcsz))
152 return i;
153 return NIL_CLIPX11FORMAT;
154}
155#endif
156
157/**
158 * Enumerates supported X11 clipboard formats corresponding to a given VBox
159 * format.
160 * @returns the next matching X11 format in the list, or NIL_CLIPX11FORMAT if
161 * there are no more
162 * @param lastFormat The value returned from the last call of this function.
163 * Use NIL_CLIPX11FORMAT to start the enumeration.
164 */
165static CLIPX11FORMAT clipEnumX11Formats(uint32_t u32VBoxFormats,
166 CLIPX11FORMAT lastFormat)
167{
168 for (unsigned i = lastFormat + 1; i < RT_ELEMENTS(g_aFormats); ++i)
169 if (u32VBoxFormats & clipVBoxFormatForX11Format(i))
170 return i;
171 return NIL_CLIPX11FORMAT;
172}
173
174/** Global context information used by the X11 clipboard backend */
175struct _CLIPBACKEND
176{
177 /** Opaque data structure describing the front-end. */
178 VBOXCLIPBOARDCONTEXT *pFrontend;
179 /** Is an X server actually available? */
180 bool fHaveX11;
181 /** The X Toolkit application context structure */
182 XtAppContext appContext;
183
184 /** We have a separate thread to wait for Window and Clipboard events */
185 RTTHREAD thread;
186 /** The X Toolkit widget which we use as our clipboard client. It is never made visible. */
187 Widget widget;
188
189 /** Should we try to grab the clipboard on startup? */
190 bool fGrabClipboardOnStart;
191
192 /** The best text format X11 has to offer, as an index into the formats
193 * table */
194 CLIPX11FORMAT X11TextFormat;
195 /** The best bitmap format X11 has to offer, as an index into the formats
196 * table */
197 CLIPX11FORMAT X11BitmapFormat;
198 /** The best HTML format X11 has to offer, as an index into the formats
199 * table */
200 CLIPX11FORMAT X11HTMLFormat;
201 /** What formats does VBox have on offer? */
202 uint32_t vboxFormats;
203 /** Cache of the last unicode data that we received */
204 void *pvUnicodeCache;
205 /** Size of the unicode data in the cache */
206 uint32_t cbUnicodeCache;
207 /** When we wish the clipboard to exit, we have to wake up the event
208 * loop. We do this by writing into a pipe. This end of the pipe is
209 * the end that another thread can write to. */
210 int wakeupPipeWrite;
211 /** The reader end of the pipe */
212 int wakeupPipeRead;
213 /** A pointer to the XFixesSelectSelectionInput function */
214 void (*fixesSelectInput)(Display *, Window, Atom, unsigned long);
215 /** The first XFixes event number */
216 int fixesEventBase;
217 /** The Xt Intrinsics can only handle one outstanding clipboard operation
218 * at a time, so we keep track of whether one is in process. */
219 bool fBusy;
220 /** We can't handle a clipboard update event while we are busy, so remember
221 * it for later. */
222 bool fUpdateNeeded;
223};
224
225/** The number of simultaneous instances we support. For all normal purposes
226 * we should never need more than one. For the testcase it is convenient to
227 * have a second instance that the first can interact with in order to have
228 * a more controlled environment. */
229enum { CLIP_MAX_CONTEXTS = 20 };
230
231/** Array of structures for mapping Xt widgets to context pointers. We
232 * need this because the widget clipboard callbacks do not pass user data. */
233static struct {
234 /** The widget we want to associate the context with */
235 Widget widget;
236 /** The context associated with the widget */
237 CLIPBACKEND *pCtx;
238} g_contexts[CLIP_MAX_CONTEXTS];
239
240/** Register a new X11 clipboard context. */
241static int clipRegisterContext(CLIPBACKEND *pCtx)
242{
243 bool found = false;
244 AssertReturn(pCtx != NULL, VERR_INVALID_PARAMETER);
245 Widget widget = pCtx->widget;
246 AssertReturn(widget != NULL, VERR_INVALID_PARAMETER);
247 for (unsigned i = 0; i < RT_ELEMENTS(g_contexts); ++i)
248 {
249 AssertReturn( (g_contexts[i].widget != widget)
250 && (g_contexts[i].pCtx != pCtx), VERR_WRONG_ORDER);
251 if (g_contexts[i].widget == NULL && !found)
252 {
253 AssertReturn(g_contexts[i].pCtx == NULL, VERR_INTERNAL_ERROR);
254 g_contexts[i].widget = widget;
255 g_contexts[i].pCtx = pCtx;
256 found = true;
257 }
258 }
259 return found ? VINF_SUCCESS : VERR_OUT_OF_RESOURCES;
260}
261
262/** Unregister an X11 clipboard context. */
263static void clipUnregisterContext(CLIPBACKEND *pCtx)
264{
265 bool found = false;
266 AssertReturnVoid(pCtx != NULL);
267 Widget widget = pCtx->widget;
268 AssertReturnVoid(widget != NULL);
269 for (unsigned i = 0; i < RT_ELEMENTS(g_contexts); ++i)
270 {
271 Assert(!found || g_contexts[i].widget != widget);
272 if (g_contexts[i].widget == widget)
273 {
274 Assert(g_contexts[i].pCtx != NULL);
275 g_contexts[i].widget = NULL;
276 g_contexts[i].pCtx = NULL;
277 found = true;
278 }
279 }
280}
281
282/** Find an X11 clipboard context. */
283static CLIPBACKEND *clipLookupContext(Widget widget)
284{
285 AssertReturn(widget != NULL, NULL);
286 for (unsigned i = 0; i < RT_ELEMENTS(g_contexts); ++i)
287 {
288 if (g_contexts[i].widget == widget)
289 {
290 Assert(g_contexts[i].pCtx != NULL);
291 return g_contexts[i].pCtx;
292 }
293 }
294 return NULL;
295}
296
297/** Convert an atom name string to an X11 atom, looking it up in a cache
298 * before asking the server */
299static Atom clipGetAtom(CLIPBACKEND *pCtx, const char *pszName)
300{
301 AssertPtrReturn(pszName, None);
302 return XInternAtom(XtDisplay(pCtx->widget), pszName, False);
303}
304
305#ifdef TESTCASE
306static void testQueueToEventThread(void (*proc)(void *, void *),
307 void *client_data);
308#endif
309
310/** String written to the wakeup pipe. */
311#define WAKE_UP_STRING "WakeUp!"
312/** Length of the string written. */
313#define WAKE_UP_STRING_LEN ( sizeof(WAKE_UP_STRING) - 1 )
314
315/** Schedule a function call to run on the Xt event thread by passing it to
316 * the application context as a 0ms timeout and waking up the event loop by
317 * writing to the wakeup pipe which it monitors. */
318void clipQueueToEventThread(CLIPBACKEND *pCtx,
319 void (*proc)(void *, void *),
320 void *client_data)
321{
322 LogRel2(("clipQueueToEventThread: proc=%p, client_data=%p\n",
323 proc, client_data));
324#ifndef TESTCASE
325 XtAppAddTimeOut(pCtx->appContext, 0, (XtTimerCallbackProc)proc,
326 (XtPointer)client_data);
327 ssize_t cbWritten = write(pCtx->wakeupPipeWrite, WAKE_UP_STRING, WAKE_UP_STRING_LEN);
328 NOREF(cbWritten);
329#else
330 RT_NOREF1(pCtx);
331 testQueueToEventThread(proc, client_data);
332#endif
333}
334
335/**
336 * Report the formats currently supported by the X11 clipboard to VBox.
337 */
338static void clipReportFormatsToVBox(CLIPBACKEND *pCtx)
339{
340 uint32_t u32VBoxFormats = clipVBoxFormatForX11Format(pCtx->X11TextFormat);
341 u32VBoxFormats |= clipVBoxFormatForX11Format(pCtx->X11BitmapFormat);
342 u32VBoxFormats |= clipVBoxFormatForX11Format(pCtx->X11HTMLFormat);
343 LogRelFlowFunc(("clipReportFormatsToVBox format: %d\n", u32VBoxFormats));
344 LogRelFlowFunc(("clipReportFormatsToVBox txt: %d, bitm: %d, html:%d, u32VBoxFormats: %d\n",
345 pCtx->X11TextFormat, pCtx->X11BitmapFormat, pCtx->X11HTMLFormat,
346 u32VBoxFormats ));
347 ClipReportX11Formats(pCtx->pFrontend, u32VBoxFormats);
348}
349
350/**
351 * Forget which formats were previously in the X11 clipboard. Called when we
352 * grab the clipboard. */
353static void clipResetX11Formats(CLIPBACKEND *pCtx)
354{
355 pCtx->X11TextFormat = INVALID;
356 pCtx->X11BitmapFormat = INVALID;
357 pCtx->X11HTMLFormat = INVALID;
358}
359
360/** Tell VBox that X11 currently has nothing in its clipboard. */
361static void clipReportEmptyX11CB(CLIPBACKEND *pCtx)
362{
363 clipResetX11Formats(pCtx);
364 clipReportFormatsToVBox(pCtx);
365}
366
367/**
368 * Go through an array of X11 clipboard targets to see if they contain a text
369 * format we can support, and if so choose the ones we prefer (e.g. we like
370 * Utf8 better than plain text).
371 * @param pCtx the clipboard backend context structure
372 * @param pTargets the list of targets
373 * @param cTargets the size of the list in @a pTargets
374 */
375static CLIPX11FORMAT clipGetTextFormatFromTargets(CLIPBACKEND *pCtx,
376 CLIPX11FORMAT *pTargets,
377 size_t cTargets)
378{
379 CLIPX11FORMAT bestTextFormat = NIL_CLIPX11FORMAT;
380 CLIPFORMAT enmBestTextTarget = INVALID;
381 AssertPtrReturn(pCtx, NIL_CLIPX11FORMAT);
382 AssertReturn(VALID_PTR(pTargets) || cTargets == 0, NIL_CLIPX11FORMAT);
383 for (unsigned i = 0; i < cTargets; ++i)
384 {
385 CLIPX11FORMAT format = pTargets[i];
386 if (format != NIL_CLIPX11FORMAT)
387 {
388 if ( (clipVBoxFormatForX11Format(format)
389 == VBOX_SHARED_CLIPBOARD_FMT_UNICODETEXT)
390 && enmBestTextTarget < clipRealFormatForX11Format(format))
391 {
392 enmBestTextTarget = clipRealFormatForX11Format(format);
393 bestTextFormat = format;
394 }
395 }
396 }
397 return bestTextFormat;
398}
399
400#ifdef TESTCASE
401static bool clipTestTextFormatConversion(CLIPBACKEND *pCtx)
402{
403 bool success = true;
404 CLIPX11FORMAT targets[2];
405 CLIPX11FORMAT x11Format;
406 targets[0] = clipFindX11FormatByAtomText("text/plain");
407 targets[1] = clipFindX11FormatByAtomText("image/bmp");
408 x11Format = clipGetTextFormatFromTargets(pCtx, targets, 2);
409 if (clipRealFormatForX11Format(x11Format) != TEXT)
410 success = false;
411 targets[0] = clipFindX11FormatByAtomText("UTF8_STRING");
412 targets[1] = clipFindX11FormatByAtomText("text/plain");
413 x11Format = clipGetTextFormatFromTargets(pCtx, targets, 2);
414 if (clipRealFormatForX11Format(x11Format) != UTF8)
415 success = false;
416 return success;
417}
418#endif
419
420/**
421 * Go through an array of X11 clipboard targets to see if they contain a bitmap
422 * format we can support, and if so choose the ones we prefer (e.g. we like
423 * BMP better than PNG because we don't have to convert).
424 * @param pCtx the clipboard backend context structure
425 * @param pTargets the list of targets
426 * @param cTargets the size of the list in @a pTargets
427 */
428static CLIPX11FORMAT clipGetBitmapFormatFromTargets(CLIPBACKEND *pCtx,
429 CLIPX11FORMAT *pTargets,
430 size_t cTargets)
431{
432 CLIPX11FORMAT bestBitmapFormat = NIL_CLIPX11FORMAT;
433 CLIPFORMAT enmBestBitmapTarget = INVALID;
434 AssertPtrReturn(pCtx, NIL_CLIPX11FORMAT);
435 AssertReturn(VALID_PTR(pTargets) || cTargets == 0, NIL_CLIPX11FORMAT);
436 for (unsigned i = 0; i < cTargets; ++i)
437 {
438 CLIPX11FORMAT format = pTargets[i];
439 if (format != NIL_CLIPX11FORMAT)
440 {
441 if ( (clipVBoxFormatForX11Format(format)
442 == VBOX_SHARED_CLIPBOARD_FMT_BITMAP)
443 && enmBestBitmapTarget < clipRealFormatForX11Format(format))
444 {
445 enmBestBitmapTarget = clipRealFormatForX11Format(format);
446 bestBitmapFormat = format;
447 }
448 }
449 }
450 return bestBitmapFormat;
451}
452
453/**
454 * Go through an array of X11 clipboard targets to see if they contain a HTML
455 * format we can support, and if so choose the ones we prefer
456 * @param pCtx the clipboard backend context structure
457 * @param pTargets the list of targets
458 * @param cTargets the size of the list in @a pTargets
459 */
460static CLIPX11FORMAT clipGetHtmlFormatFromTargets(CLIPBACKEND *pCtx,
461 CLIPX11FORMAT *pTargets,
462 size_t cTargets)
463{
464 CLIPX11FORMAT bestHTMLFormat = NIL_CLIPX11FORMAT;
465 CLIPFORMAT enmBestHtmlTarget = INVALID;
466 AssertPtrReturn(pCtx, NIL_CLIPX11FORMAT);
467 AssertReturn(VALID_PTR(pTargets) || cTargets == 0, NIL_CLIPX11FORMAT);
468 for (unsigned i = 0; i < cTargets; ++i)
469 {
470 CLIPX11FORMAT format = pTargets[i];
471 if (format != NIL_CLIPX11FORMAT)
472 {
473 if ( (clipVBoxFormatForX11Format(format) == VBOX_SHARED_CLIPBOARD_FMT_HTML)
474 && enmBestHtmlTarget < clipRealFormatForX11Format(format))
475 {
476 enmBestHtmlTarget = clipRealFormatForX11Format(format);
477 bestHTMLFormat = format;
478 }
479 }
480 }
481 return bestHTMLFormat;
482}
483
484
485/**
486 * Go through an array of X11 clipboard targets to see if we can support any
487 * of them and if relevant to choose the ones we prefer (e.g. we like Utf8
488 * better than plain text).
489 * @param pCtx the clipboard backend context structure
490 * @param pTargets the list of targets
491 * @param cTargets the size of the list in @a pTargets
492 */
493static void clipGetFormatsFromTargets(CLIPBACKEND *pCtx,
494 CLIPX11FORMAT *pTargets, size_t cTargets)
495{
496 AssertPtrReturnVoid(pCtx);
497 AssertPtrReturnVoid(pTargets);
498 CLIPX11FORMAT bestTextFormat;
499 CLIPX11FORMAT bestBitmapFormat;
500 CLIPX11FORMAT bestHtmlFormat;
501 bestTextFormat = clipGetTextFormatFromTargets(pCtx, pTargets, cTargets);
502 if (pCtx->X11TextFormat != bestTextFormat)
503 {
504 pCtx->X11TextFormat = bestTextFormat;
505 }
506 pCtx->X11BitmapFormat = INVALID; /* not yet supported */
507 bestBitmapFormat = clipGetBitmapFormatFromTargets(pCtx, pTargets, cTargets);
508 if (pCtx->X11BitmapFormat != bestBitmapFormat)
509 {
510 pCtx->X11BitmapFormat = bestBitmapFormat;
511 }
512 bestHtmlFormat = clipGetHtmlFormatFromTargets(pCtx, pTargets, cTargets);
513 if(pCtx->X11HTMLFormat != bestHtmlFormat)
514 {
515 pCtx->X11HTMLFormat = bestHtmlFormat;
516 }
517}
518
519static void clipQueryX11CBFormats(CLIPBACKEND *pCtx);
520
521/**
522 * Update the context's information about targets currently supported by X11,
523 * based on an array of X11 atoms.
524 * @param pCtx the context to be updated
525 * @param pTargets the array of atoms describing the targets supported
526 * @param cTargets the size of the array @a pTargets
527 */
528static void clipUpdateX11Targets(CLIPBACKEND *pCtx, CLIPX11FORMAT *pTargets,
529 size_t cTargets)
530{
531 LogRel2 (("%s: called\n", __FUNCTION__));
532 pCtx->fBusy = false;
533 if (pCtx->fUpdateNeeded)
534 {
535 /* We may already be out of date. */
536 pCtx->fUpdateNeeded = false;
537 clipQueryX11CBFormats(pCtx);
538 return;
539 }
540 if (pTargets == NULL) {
541 /* No data available */
542 clipReportEmptyX11CB(pCtx);
543 return;
544 }
545 clipGetFormatsFromTargets(pCtx, pTargets, cTargets);
546 clipReportFormatsToVBox(pCtx);
547}
548
549/**
550 * Notify the VBox clipboard about available data formats, based on the
551 * "targets" information obtained from the X11 clipboard.
552 * @note Callback for XtGetSelectionValue
553 * @note This function is treated as API glue, and as such is not part of any
554 * unit test. So keep it simple, be paranoid and log everything.
555 */
556static void clipConvertX11Targets(Widget widget, XtPointer pClientData,
557 Atom * /* selection */, Atom *atomType,
558 XtPointer pValue, long unsigned int *pcLen,
559 int *piFormat)
560{
561 RT_NOREF1(piFormat);
562 CLIPBACKEND *pCtx = reinterpret_cast<CLIPBACKEND *>(pClientData);
563 Atom *pAtoms = (Atom *)pValue;
564 unsigned i, j;
565 LogRel2(("%s: pValue=%p, *pcLen=%u, *atomType=%d%s\n", __FUNCTION__,
566 pValue, *pcLen, *atomType,
567 *atomType == XT_CONVERT_FAIL ? " (XT_CONVERT_FAIL)" : ""));
568 CLIPX11FORMAT *pFormats = NULL;
569 if (*pcLen && pValue && (*atomType != XT_CONVERT_FAIL /* time out */))
570 pFormats = (CLIPX11FORMAT *)RTMemAllocZ(*pcLen * sizeof(CLIPX11FORMAT));
571#if defined(DEBUG) && !defined(TESTCASE)
572 if (pValue)
573 {
574 for (i = 0; i < *pcLen; ++i)
575 if (pAtoms[i])
576 {
577 char *pszName = XGetAtomName(XtDisplay(widget), pAtoms[i]);
578 LogRel2(("%s: found target %s\n", __FUNCTION__,
579 pszName));
580 XFree(pszName);
581 }
582 else
583 LogRel2(("%s: found empty target.\n", __FUNCTION__));
584 }
585#endif
586 if (pFormats)
587 {
588 for (i = 0; i < *pcLen; ++i)
589 {
590 for (j = 0; j < RT_ELEMENTS(g_aFormats); ++j)
591 {
592 Atom target = XInternAtom(XtDisplay(widget),
593 g_aFormats[j].pcszAtom, False);
594 if (*(pAtoms + i) == target)
595 pFormats[i] = j;
596 }
597#if defined(DEBUG) && !defined(TESTCASE)
598 LogRel2(("%s: reporting format %d (%s)\n", __FUNCTION__,
599 pFormats[i], g_aFormats[pFormats[i]].pcszAtom));
600#endif
601 }
602 }
603 else
604 LogRel2(("%s: reporting empty targets (none reported or allocation failure).\n",
605 __FUNCTION__));
606 clipUpdateX11Targets(pCtx, pFormats, *pcLen);
607 RTMemFree(pFormats);
608 XtFree(reinterpret_cast<char *>(pValue));
609}
610
611#ifdef TESTCASE
612 void testRequestTargets(CLIPBACKEND *pCtx);
613#endif
614
615/**
616 * Callback to notify us when the contents of the X11 clipboard change.
617 */
618static void clipQueryX11CBFormats(CLIPBACKEND *pCtx)
619{
620 LogRel2 (("%s: requesting the targets that the X11 clipboard offers\n",
621 __PRETTY_FUNCTION__));
622 if (pCtx->fBusy)
623 {
624 pCtx->fUpdateNeeded = true;
625 return;
626 }
627 pCtx->fBusy = true;
628#ifndef TESTCASE
629 XtGetSelectionValue(pCtx->widget,
630 clipGetAtom(pCtx, "CLIPBOARD"),
631 clipGetAtom(pCtx, "TARGETS"),
632 clipConvertX11Targets, pCtx,
633 CurrentTime);
634#else
635 testRequestTargets(pCtx);
636#endif
637}
638
639#ifndef TESTCASE
640
641typedef struct {
642 int type; /* event base */
643 unsigned long serial;
644 Bool send_event;
645 Display *display;
646 Window window;
647 int subtype;
648 Window owner;
649 Atom selection;
650 Time timestamp;
651 Time selection_timestamp;
652} XFixesSelectionNotifyEvent;
653
654/**
655 * Wait until an event arrives and handle it if it is an XFIXES selection
656 * event, which Xt doesn't know about.
657 */
658void clipPeekEventAndDoXFixesHandling(CLIPBACKEND *pCtx)
659{
660 union
661 {
662 XEvent event;
663 XFixesSelectionNotifyEvent fixes;
664 } event = { { 0 } };
665
666 if (XtAppPeekEvent(pCtx->appContext, &event.event))
667 if ( (event.event.type == pCtx->fixesEventBase)
668 && (event.fixes.owner != XtWindow(pCtx->widget)))
669 {
670 if ( (event.fixes.subtype == 0 /* XFixesSetSelectionOwnerNotify */)
671 && (event.fixes.owner != 0))
672 clipQueryX11CBFormats(pCtx);
673 else
674 clipReportEmptyX11CB(pCtx);
675 }
676}
677
678/**
679 * The main loop of our clipboard reader.
680 * @note X11 backend code.
681 */
682static DECLCALLBACK(int) clipEventThread(RTTHREAD hThreadSelf, void *pvUser)
683{
684 RT_NOREF1(hThreadSelf);
685 LogRel(("Shared clipboard: Starting shared clipboard thread\n"));
686
687 CLIPBACKEND *pCtx = (CLIPBACKEND *)pvUser;
688
689 if (pCtx->fGrabClipboardOnStart)
690 clipQueryX11CBFormats(pCtx);
691 while (XtAppGetExitFlag(pCtx->appContext) == FALSE)
692 {
693 clipPeekEventAndDoXFixesHandling(pCtx);
694 XtAppProcessEvent(pCtx->appContext, XtIMAll);
695 }
696 LogRel(("Shared clipboard: Shared clipboard thread terminated successfully\n"));
697 return VINF_SUCCESS;
698}
699#endif
700
701/** X11 specific uninitialisation for the shared clipboard.
702 * @note X11 backend code.
703 */
704static void clipUninit(CLIPBACKEND *pCtx)
705{
706 AssertPtrReturnVoid(pCtx);
707 if (pCtx->widget)
708 {
709 /* Valid widget + invalid appcontext = bug. But don't return yet. */
710 AssertPtr(pCtx->appContext);
711 clipUnregisterContext(pCtx);
712 XtDestroyWidget(pCtx->widget);
713 }
714 pCtx->widget = NULL;
715 if (pCtx->appContext)
716 XtDestroyApplicationContext(pCtx->appContext);
717 pCtx->appContext = NULL;
718 if (pCtx->wakeupPipeRead != 0)
719 close(pCtx->wakeupPipeRead);
720 if (pCtx->wakeupPipeWrite != 0)
721 close(pCtx->wakeupPipeWrite);
722 pCtx->wakeupPipeRead = 0;
723 pCtx->wakeupPipeWrite = 0;
724}
725
726/** Worker function for stopping the clipboard which runs on the event
727 * thread. */
728static void clipStopEventThreadWorker(void *pUserData, void *)
729{
730
731 CLIPBACKEND *pCtx = (CLIPBACKEND *)pUserData;
732
733 /* This might mean that we are getting stopped twice. */
734 Assert(pCtx->widget != NULL);
735
736 /* Set the termination flag to tell the Xt event loop to exit. We
737 * reiterate that any outstanding requests from the X11 event loop to
738 * the VBox part *must* have returned before we do this. */
739 XtAppSetExitFlag(pCtx->appContext);
740}
741
742#ifndef TESTCASE
743/** Setup the XFixes library and load the XFixesSelectSelectionInput symbol */
744static int clipLoadXFixes(Display *pDisplay, CLIPBACKEND *pCtx)
745{
746 int rc;
747
748 void *hFixesLib = dlopen("libXfixes.so.1", RTLD_LAZY);
749 if (!hFixesLib)
750 hFixesLib = dlopen("libXfixes.so.2", RTLD_LAZY);
751 if (!hFixesLib)
752 hFixesLib = dlopen("libXfixes.so.3", RTLD_LAZY);
753 if (!hFixesLib)
754 hFixesLib = dlopen("libXfixes.so.4", RTLD_LAZY);
755 if (hFixesLib)
756 {
757 /* For us, a NULL function pointer is a failure */
758 pCtx->fixesSelectInput = (void (*)(Display *, Window, Atom, long unsigned int))
759 (uintptr_t)dlsym(hFixesLib, "XFixesSelectSelectionInput");
760 if (pCtx->fixesSelectInput)
761 {
762 int dummy1 = 0;
763 int dummy2 = 0;
764 if (XQueryExtension(pDisplay, "XFIXES", &dummy1, &pCtx->fixesEventBase, &dummy2) != 0)
765 {
766 if (pCtx->fixesEventBase >= 0)
767 rc = VINF_SUCCESS;
768 else
769 {
770 LogRel(("clipLoadXFixes: fixesEventBase is less than zero: %d\n", pCtx->fixesEventBase));
771 rc = VERR_NOT_SUPPORTED;
772 }
773 }
774 else
775 {
776 LogRel(("clipLoadXFixes: XQueryExtension failed\n"));
777 rc = VERR_NOT_SUPPORTED;
778 }
779 }
780 else
781 {
782 LogRel(("clipLoadXFixes: Symbol XFixesSelectSelectionInput not found!\n"));
783 rc = VERR_NOT_SUPPORTED;
784 }
785 }
786 else
787 {
788 LogRel(("clipLoadXFixes: libxFixes.so.* not found!\n"));
789 rc = VERR_NOT_SUPPORTED;
790 }
791 return rc;
792}
793#endif
794
795/** This is the callback which is scheduled when data is available on the
796 * wakeup pipe. It simply reads all data from the pipe. */
797static void clipDrainWakeupPipe(XtPointer pUserData, int *, XtInputId *)
798{
799 CLIPBACKEND *pCtx = (CLIPBACKEND *)pUserData;
800 char acBuf[WAKE_UP_STRING_LEN];
801
802 LogRel2(("clipDrainWakeupPipe: called\n"));
803 while (read(pCtx->wakeupPipeRead, acBuf, sizeof(acBuf)) > 0) {}
804}
805
806/** X11 specific initialisation for the shared clipboard.
807 * @note X11 backend code.
808 */
809static int clipInit(CLIPBACKEND *pCtx)
810{
811 /* Create a window and make it a clipboard viewer. */
812 int cArgc = 0;
813 char *pcArgv = 0;
814 int rc = VINF_SUCCESS;
815 Display *pDisplay;
816
817 /* Make sure we are thread safe */
818 XtToolkitThreadInitialize();
819 /* Set up the Clipboard application context and main window. We call all
820 * these functions directly instead of calling XtOpenApplication() so
821 * that we can fail gracefully if we can't get an X11 display. */
822 XtToolkitInitialize();
823 pCtx->appContext = XtCreateApplicationContext();
824 pDisplay = XtOpenDisplay(pCtx->appContext, 0, 0, "VBoxClipboard", 0, 0, &cArgc, &pcArgv);
825 if (NULL == pDisplay)
826 {
827 LogRel(("Shared clipboard: Failed to connect to the X11 clipboard - the window system may not be running.\n"));
828 rc = VERR_NOT_SUPPORTED;
829 }
830#ifndef TESTCASE
831 if (RT_SUCCESS(rc))
832 {
833 rc = clipLoadXFixes(pDisplay, pCtx);
834 if (RT_FAILURE(rc))
835 LogRel(("Shared clipboard: Failed to load the XFIXES extension.\n"));
836 }
837#endif
838 if (RT_SUCCESS(rc))
839 {
840 pCtx->widget = XtVaAppCreateShell(0, "VBoxClipboard",
841 applicationShellWidgetClass,
842 pDisplay, XtNwidth, 1, XtNheight,
843 1, NULL);
844 if (NULL == pCtx->widget)
845 {
846 LogRel(("Shared clipboard: Failed to construct the X11 window for the shared clipboard manager.\n"));
847 rc = VERR_NO_MEMORY;
848 }
849 else
850 rc = clipRegisterContext(pCtx);
851 }
852 if (RT_SUCCESS(rc))
853 {
854 XtSetMappedWhenManaged(pCtx->widget, false);
855 XtRealizeWidget(pCtx->widget);
856#ifndef TESTCASE
857 /* Enable clipboard update notification */
858 pCtx->fixesSelectInput(pDisplay, XtWindow(pCtx->widget),
859 clipGetAtom(pCtx, "CLIPBOARD"),
860 7 /* All XFixes*Selection*NotifyMask flags */);
861#endif
862 }
863 /* Create the pipes */
864 int pipes[2];
865 if (!pipe(pipes))
866 {
867 pCtx->wakeupPipeRead = pipes[0];
868 pCtx->wakeupPipeWrite = pipes[1];
869 if (!XtAppAddInput(pCtx->appContext, pCtx->wakeupPipeRead,
870 (XtPointer) XtInputReadMask,
871 clipDrainWakeupPipe, (XtPointer) pCtx))
872 rc = VERR_NO_MEMORY; /* What failure means is not doc'ed. */
873 if ( RT_SUCCESS(rc)
874 && (fcntl(pCtx->wakeupPipeRead, F_SETFL, O_NONBLOCK) != 0))
875 rc = RTErrConvertFromErrno(errno);
876 if (RT_FAILURE(rc))
877 LogRel(("Shared clipboard: Failed to setup the termination mechanism.\n"));
878 }
879 else
880 rc = RTErrConvertFromErrno(errno);
881 if (RT_FAILURE(rc))
882 clipUninit(pCtx);
883 if (RT_FAILURE(rc))
884 LogRel(("Shared clipboard: Initialisation failed: %Rrc\n", rc));
885 return rc;
886}
887
888/**
889 * Construct the X11 backend of the shared clipboard.
890 * @note X11 backend code
891 */
892CLIPBACKEND *ClipConstructX11(VBOXCLIPBOARDCONTEXT *pFrontend, bool fHeadless)
893{
894 CLIPBACKEND *pCtx = (CLIPBACKEND *)RTMemAllocZ(sizeof(CLIPBACKEND));
895 if (pCtx && fHeadless)
896 {
897 /*
898 * If we don't find the DISPLAY environment variable we assume that
899 * we are not connected to an X11 server. Don't actually try to do
900 * this then, just fail silently and report success on every call.
901 * This is important for VBoxHeadless.
902 */
903 LogRelFunc(("X11 DISPLAY variable not set -- disabling shared clipboard\n"));
904 pCtx->fHaveX11 = false;
905 return pCtx;
906 }
907
908 pCtx->fHaveX11 = true;
909
910 LogRel(("Shared clipboard: Initializing X11 clipboard backend\n"));
911 if (pCtx)
912 pCtx->pFrontend = pFrontend;
913 return pCtx;
914}
915
916/**
917 * Destruct the shared clipboard X11 backend.
918 * @note X11 backend code
919 */
920void ClipDestructX11(CLIPBACKEND *pCtx)
921{
922 /*
923 * Immediately return if we are not connected to the X server.
924 */
925 if (!pCtx->fHaveX11)
926 return;
927
928 /* We set this to NULL when the event thread exits. It really should
929 * have exited at this point, when we are about to unload the code from
930 * memory. */
931 Assert(pCtx->widget == NULL);
932}
933
934/**
935 * Announce to the X11 backend that we are ready to start.
936 * @param grab whether we should try to grab the shared clipboard at once
937 */
938int ClipStartX11(CLIPBACKEND *pCtx, bool grab)
939{
940 int rc = VINF_SUCCESS;
941 LogRelFlowFunc(("\n"));
942 /*
943 * Immediately return if we are not connected to the X server.
944 */
945 if (!pCtx->fHaveX11)
946 return VINF_SUCCESS;
947
948 rc = clipInit(pCtx);
949 if (RT_SUCCESS(rc))
950 {
951 clipResetX11Formats(pCtx);
952 pCtx->fGrabClipboardOnStart = grab;
953 }
954#ifndef TESTCASE
955 if (RT_SUCCESS(rc))
956 {
957 rc = RTThreadCreate(&pCtx->thread, clipEventThread, pCtx, 0,
958 RTTHREADTYPE_IO, RTTHREADFLAGS_WAITABLE, "SHCLIP");
959 if (RT_FAILURE(rc))
960 {
961 LogRel(("Shared clipboard: Failed to start the shared clipboard thread.\n"));
962 clipUninit(pCtx);
963 }
964 }
965#endif
966 return rc;
967}
968
969/**
970 * Shut down the shared clipboard X11 backend.
971 * @note X11 backend code
972 * @note Any requests from this object to get clipboard data from VBox
973 * *must* have completed or aborted before we are called, as
974 * otherwise the X11 event loop will still be waiting for the request
975 * to return and will not be able to terminate.
976 */
977int ClipStopX11(CLIPBACKEND *pCtx)
978{
979 int rc, rcThread;
980 unsigned count = 0;
981 /*
982 * Immediately return if we are not connected to the X server.
983 */
984 if (!pCtx->fHaveX11)
985 return VINF_SUCCESS;
986
987 LogRelFunc(("stopping the shared clipboard X11 backend\n"));
988 /* Write to the "stop" pipe */
989 clipQueueToEventThread(pCtx, clipStopEventThreadWorker, (XtPointer) pCtx);
990#ifndef TESTCASE
991 do
992 {
993 rc = RTThreadWait(pCtx->thread, 1000, &rcThread);
994 ++count;
995 Assert(RT_SUCCESS(rc) || ((VERR_TIMEOUT == rc) && (count != 5)));
996 } while ((VERR_TIMEOUT == rc) && (count < 300));
997#else
998 rc = VINF_SUCCESS;
999 rcThread = VINF_SUCCESS;
1000 RT_NOREF_PV(count);
1001#endif
1002 if (RT_SUCCESS(rc))
1003 AssertRC(rcThread);
1004 else
1005 LogRelFunc(("rc=%Rrc\n", rc));
1006 clipUninit(pCtx);
1007 LogRelFlowFunc(("returning %Rrc.\n", rc));
1008 RT_NOREF_PV(rcThread);
1009 return rc;
1010}
1011
1012/**
1013 * Satisfy a request from X11 for clipboard targets supported by VBox.
1014 *
1015 * @returns iprt status code
1016 * @param atomTypeReturn The type of the data we are returning
1017 * @param pValReturn A pointer to the data we are returning. This
1018 * should be set to memory allocated by XtMalloc,
1019 * which will be freed later by the Xt toolkit.
1020 * @param pcLenReturn The length of the data we are returning
1021 * @param piFormatReturn The format (8bit, 16bit, 32bit) of the data we are
1022 * returning
1023 * @note X11 backend code, called by the XtOwnSelection callback.
1024 */
1025static int clipCreateX11Targets(CLIPBACKEND *pCtx, Atom *atomTypeReturn,
1026 XtPointer *pValReturn,
1027 unsigned long *pcLenReturn,
1028 int *piFormatReturn)
1029{
1030 Atom *atomTargets = (Atom *)XtMalloc( (MAX_CLIP_X11_FORMATS + 3)
1031 * sizeof(Atom));
1032 unsigned cTargets = 0;
1033 LogRelFlowFunc (("called\n"));
1034 CLIPX11FORMAT format = NIL_CLIPX11FORMAT;
1035 do
1036 {
1037 format = clipEnumX11Formats(pCtx->vboxFormats, format);
1038 if (format != NIL_CLIPX11FORMAT)
1039 {
1040 atomTargets[cTargets] = clipAtomForX11Format(pCtx, format);
1041 ++cTargets;
1042 }
1043 } while (format != NIL_CLIPX11FORMAT);
1044 /* We always offer these */
1045 atomTargets[cTargets] = clipGetAtom(pCtx, "TARGETS");
1046 atomTargets[cTargets + 1] = clipGetAtom(pCtx, "MULTIPLE");
1047 atomTargets[cTargets + 2] = clipGetAtom(pCtx, "TIMESTAMP");
1048 *atomTypeReturn = XA_ATOM;
1049 *pValReturn = (XtPointer)atomTargets;
1050 *pcLenReturn = cTargets + 3;
1051 *piFormatReturn = 32;
1052 return VINF_SUCCESS;
1053}
1054
1055/** This is a wrapper around ClipRequestDataForX11 that will cache the
1056 * data returned.
1057 */
1058static int clipReadVBoxClipboard(CLIPBACKEND *pCtx, uint32_t u32Format,
1059 void **ppv, uint32_t *pcb)
1060{
1061 int rc = VINF_SUCCESS;
1062 LogRelFlowFunc(("pCtx=%p, u32Format=%02X, ppv=%p, pcb=%p\n", pCtx,
1063 u32Format, ppv, pcb));
1064 if (u32Format == VBOX_SHARED_CLIPBOARD_FMT_UNICODETEXT)
1065 {
1066 if (pCtx->pvUnicodeCache == NULL)
1067 rc = ClipRequestDataForX11(pCtx->pFrontend, u32Format,
1068 &pCtx->pvUnicodeCache,
1069 &pCtx->cbUnicodeCache);
1070 if (RT_SUCCESS(rc))
1071 {
1072 *ppv = RTMemDup(pCtx->pvUnicodeCache, pCtx->cbUnicodeCache);
1073 *pcb = pCtx->cbUnicodeCache;
1074 if (*ppv == NULL)
1075 rc = VERR_NO_MEMORY;
1076 }
1077 }
1078 else
1079 rc = ClipRequestDataForX11(pCtx->pFrontend, u32Format,
1080 ppv, pcb);
1081 LogRelFlowFunc(("returning %Rrc\n", rc));
1082 if (RT_SUCCESS(rc))
1083 LogRelFlowFunc(("*ppv=%.*ls, *pcb=%u\n", *pcb, *ppv, *pcb));
1084 return rc;
1085}
1086
1087/**
1088 * Calculate a buffer size large enough to hold the source Windows format
1089 * text converted into Unix Utf8, including the null terminator
1090 * @returns iprt status code
1091 * @param pwsz the source text in UCS-2 with Windows EOLs
1092 * @param cwc the size in USC-2 elements of the source text, with or
1093 * without the terminator
1094 * @param pcbActual where to store the buffer size needed
1095 */
1096static int clipWinTxtBufSizeForUtf8(PRTUTF16 pwsz, size_t cwc,
1097 size_t *pcbActual)
1098{
1099 size_t cbRet = 0;
1100 int rc = RTUtf16CalcUtf8LenEx(pwsz, cwc, &cbRet);
1101 if (RT_SUCCESS(rc))
1102 *pcbActual = cbRet + 1; /* null terminator */
1103 return rc;
1104}
1105
1106/**
1107 * Convert text from Windows format (UCS-2 with CRLF line endings) to standard
1108 * Utf-8.
1109 *
1110 * @returns iprt status code
1111 *
1112 * @param pwszSrc the text to be converted
1113 * @param cbSrc the length of @a pwszSrc in bytes
1114 * @param pszBuf where to write the converted string
1115 * @param cbBuf the size of the buffer pointed to by @a pszBuf
1116 * @param pcbActual where to store the size of the converted string.
1117 * optional.
1118 */
1119static int clipWinTxtToUtf8(PRTUTF16 pwszSrc, size_t cbSrc, char *pszBuf,
1120 size_t cbBuf, size_t *pcbActual)
1121{
1122 PRTUTF16 pwszTmp = NULL;
1123 size_t cwSrc = cbSrc / 2, cwTmp = 0, cbDest = 0;
1124 int rc = VINF_SUCCESS;
1125
1126 LogRelFlowFunc (("pwszSrc=%.*ls, cbSrc=%u\n", cbSrc, pwszSrc, cbSrc));
1127 /* How long will the converted text be? */
1128 AssertPtr(pwszSrc);
1129 AssertPtr(pszBuf);
1130 rc = vboxClipboardUtf16GetLinSize(pwszSrc, cwSrc, &cwTmp);
1131 if (RT_SUCCESS(rc) && cwTmp == 0)
1132 rc = VERR_NO_DATA;
1133 if (RT_SUCCESS(rc))
1134 pwszTmp = (PRTUTF16)RTMemAlloc(cwTmp * 2);
1135 if (!pwszTmp)
1136 rc = VERR_NO_MEMORY;
1137 /* Convert the text. */
1138 if (RT_SUCCESS(rc))
1139 rc = vboxClipboardUtf16WinToLin(pwszSrc, cwSrc, pwszTmp, cwTmp);
1140 if (RT_SUCCESS(rc))
1141 /* Convert the Utf16 string to Utf8. */
1142 rc = RTUtf16ToUtf8Ex(pwszTmp + 1, cwTmp - 1, &pszBuf, cbBuf,
1143 &cbDest);
1144 RTMemFree(reinterpret_cast<void *>(pwszTmp));
1145 if (pcbActual)
1146 *pcbActual = cbDest + 1;
1147 LogRelFlowFunc(("returning %Rrc\n", rc));
1148 if (RT_SUCCESS(rc))
1149 LogRelFlowFunc (("converted string is %.*s. Returning.\n", cbDest,
1150 pszBuf));
1151 return rc;
1152}
1153
1154/**
1155 * Satisfy a request from X11 to convert the clipboard text to Utf-8. We
1156 * return null-terminated text, but can cope with non-null-terminated input.
1157 *
1158 * @returns iprt status code
1159 * @param pDisplay an X11 display structure, needed for conversions
1160 * performed by Xlib
1161 * @param pv the text to be converted (UCS-2 with Windows EOLs)
1162 * @param cb the length of the text in @cb in bytes
1163 * @param atomTypeReturn where to store the atom for the type of the data
1164 * we are returning
1165 * @param pValReturn where to store the pointer to the data we are
1166 * returning. This should be to memory allocated by
1167 * XtMalloc, which will be freed by the Xt toolkit
1168 * later.
1169 * @param pcLenReturn where to store the length of the data we are
1170 * returning
1171 * @param piFormatReturn where to store the bit width (8, 16, 32) of the
1172 * data we are returning
1173 */
1174static int clipWinTxtToUtf8ForX11CB(Display *pDisplay, PRTUTF16 pwszSrc,
1175 size_t cbSrc, Atom *atomTarget,
1176 Atom *atomTypeReturn,
1177 XtPointer *pValReturn,
1178 unsigned long *pcLenReturn,
1179 int *piFormatReturn)
1180{
1181 RT_NOREF2(pDisplay, pcLenReturn);
1182
1183 /* This may slightly overestimate the space needed. */
1184 size_t cbDest = 0;
1185 int rc = clipWinTxtBufSizeForUtf8(pwszSrc, cbSrc / 2, &cbDest);
1186 if (RT_SUCCESS(rc))
1187 {
1188 char *pszDest = (char *)XtMalloc(cbDest);
1189 size_t cbActual = 0;
1190 if (pszDest)
1191 rc = clipWinTxtToUtf8(pwszSrc, cbSrc, pszDest, cbDest,
1192 &cbActual);
1193 if (RT_SUCCESS(rc))
1194 {
1195 *atomTypeReturn = *atomTarget;
1196 *pValReturn = (XtPointer)pszDest;
1197 *pcLenReturn = cbActual;
1198 *piFormatReturn = 8;
1199 }
1200 }
1201 return rc;
1202}
1203
1204/**
1205 * Satisfy a request from X11 to convert the clipboard HTML fragment to Utf-8. We
1206 * return null-terminated text, but can cope with non-null-terminated input.
1207 *
1208 * @returns iprt status code
1209 * @param pDisplay an X11 display structure, needed for conversions
1210 * performed by Xlib
1211 * @param pv the text to be converted (UTF8 with Windows EOLs)
1212 * @param cb the length of the text in @cb in bytes
1213 * @param atomTypeReturn where to store the atom for the type of the data
1214 * we are returning
1215 * @param pValReturn where to store the pointer to the data we are
1216 * returning. This should be to memory allocated by
1217 * XtMalloc, which will be freed by the Xt toolkit
1218 * later.
1219 * @param pcLenReturn where to store the length of the data we are
1220 * returning
1221 * @param piFormatReturn where to store the bit width (8, 16, 32) of the
1222 * data we are returning
1223 */
1224static int clipWinHTMLToUtf8ForX11CB(Display *pDisplay, const char *pszSrc,
1225 size_t cbSrc, Atom *atomTarget,
1226 Atom *atomTypeReturn,
1227 XtPointer *pValReturn,
1228 unsigned long *pcLenReturn,
1229 int *piFormatReturn)
1230{
1231 RT_NOREF2(pDisplay, pValReturn);
1232
1233 /* This may slightly overestimate the space needed. */
1234 LogRelFlowFunc(("source: %s", pszSrc));
1235
1236 char *pszDest = (char *)XtMalloc(cbSrc);
1237 if(pszDest == NULL)
1238 return VERR_NO_MEMORY;
1239
1240 memcpy(pszDest, pszSrc, cbSrc);
1241
1242 *atomTypeReturn = *atomTarget;
1243 *pValReturn = (XtPointer)pszDest;
1244 *pcLenReturn = cbSrc;
1245 *piFormatReturn = 8;
1246
1247 return VINF_SUCCESS;
1248}
1249
1250
1251/**
1252 * Does this atom correspond to one of the two selection types we support?
1253 * @param widget a valid Xt widget
1254 * @param selType the atom in question
1255 */
1256static bool clipIsSupportedSelectionType(CLIPBACKEND *pCtx, Atom selType)
1257{
1258 return( (selType == clipGetAtom(pCtx, "CLIPBOARD"))
1259 || (selType == clipGetAtom(pCtx, "PRIMARY")));
1260}
1261
1262/**
1263 * Remove a trailing nul character from a string by adjusting the string
1264 * length. Some X11 applications don't like zero-terminated text...
1265 * @param pText the text in question
1266 * @param pcText the length of the text, adjusted on return
1267 * @param format the format of the text
1268 */
1269static void clipTrimTrailingNul(XtPointer pText, unsigned long *pcText,
1270 CLIPFORMAT format)
1271{
1272 AssertPtrReturnVoid(pText);
1273 AssertPtrReturnVoid(pcText);
1274 AssertReturnVoid((format == UTF8) || (format == TEXT) || (format == HTML));
1275 if (((char *)pText)[*pcText - 1] == '\0')
1276 --(*pcText);
1277}
1278
1279static int clipConvertVBoxCBForX11(CLIPBACKEND *pCtx, Atom *atomTarget,
1280 Atom *atomTypeReturn,
1281 XtPointer *pValReturn,
1282 unsigned long *pcLenReturn,
1283 int *piFormatReturn)
1284{
1285 int rc = VINF_SUCCESS;
1286 CLIPX11FORMAT x11Format = clipFindX11FormatByAtom(pCtx, *atomTarget);
1287 CLIPFORMAT format = clipRealFormatForX11Format(x11Format);
1288 if ( ((format == UTF8) || (format == TEXT))
1289 && (pCtx->vboxFormats & VBOX_SHARED_CLIPBOARD_FMT_UNICODETEXT))
1290 {
1291 void *pv = NULL;
1292 uint32_t cb = 0;
1293 rc = clipReadVBoxClipboard(pCtx,
1294 VBOX_SHARED_CLIPBOARD_FMT_UNICODETEXT,
1295 &pv, &cb);
1296 if (RT_SUCCESS(rc) && (cb == 0))
1297 rc = VERR_NO_DATA;
1298 if (RT_SUCCESS(rc) && ((format == UTF8) || (format == TEXT)))
1299 rc = clipWinTxtToUtf8ForX11CB(XtDisplay(pCtx->widget),
1300 (PRTUTF16)pv, cb, atomTarget,
1301 atomTypeReturn, pValReturn,
1302 pcLenReturn, piFormatReturn);
1303 if (RT_SUCCESS(rc))
1304 clipTrimTrailingNul(*(XtPointer *)pValReturn, pcLenReturn, format);
1305 RTMemFree(pv);
1306 }
1307 else if ( (format == BMP)
1308 && (pCtx->vboxFormats & VBOX_SHARED_CLIPBOARD_FMT_BITMAP))
1309 {
1310 void *pv = NULL;
1311 uint32_t cb = 0;
1312 rc = clipReadVBoxClipboard(pCtx,
1313 VBOX_SHARED_CLIPBOARD_FMT_BITMAP,
1314 &pv, &cb);
1315 if (RT_SUCCESS(rc) && (cb == 0))
1316 rc = VERR_NO_DATA;
1317 if (RT_SUCCESS(rc) && (format == BMP))
1318 {
1319 /* Create a full BMP from it */
1320 rc = vboxClipboardDibToBmp(pv, cb, (void **)pValReturn,
1321 (size_t *)pcLenReturn);
1322 }
1323 else
1324 rc = VERR_NOT_SUPPORTED;
1325
1326 if (RT_SUCCESS(rc))
1327 {
1328 *atomTypeReturn = *atomTarget;
1329 *piFormatReturn = 8;
1330 }
1331 RTMemFree(pv);
1332 }
1333 else if ( (format == HTML)
1334 && (pCtx->vboxFormats & VBOX_SHARED_CLIPBOARD_FMT_HTML))
1335 {
1336 void *pv = NULL;
1337 uint32_t cb = 0;
1338 rc = clipReadVBoxClipboard(pCtx,
1339 VBOX_SHARED_CLIPBOARD_FMT_HTML,
1340 &pv, &cb);
1341 if (RT_SUCCESS(rc) && (cb == 0))
1342 rc = VERR_NO_DATA;
1343 if (RT_SUCCESS(rc))
1344 {
1345 /*
1346 * The common VBox HTML encoding will be - Utf8
1347 * becuase it more general for HTML formats then UTF16
1348 * X11 clipboard returns UTF16, so before sending it we should
1349 * convert it to UTF8
1350 * It's very strange but here we get utf16 from x11 clipboard
1351 * in same time we send utf8 to x11 clipboard and it's work
1352 */
1353 rc = clipWinHTMLToUtf8ForX11CB(XtDisplay(pCtx->widget),
1354 (const char*)pv, cb, atomTarget,
1355 atomTypeReturn, pValReturn,
1356 pcLenReturn, piFormatReturn);
1357
1358
1359 if (RT_SUCCESS(rc))
1360 clipTrimTrailingNul(*(XtPointer *)pValReturn, pcLenReturn, format);
1361 RTMemFree(pv);
1362 }
1363 }
1364 else
1365 rc = VERR_NOT_SUPPORTED;
1366 return rc;
1367}
1368
1369/**
1370 * Return VBox's clipboard data for an X11 client.
1371 * @note X11 backend code, callback for XtOwnSelection
1372 */
1373static Boolean clipXtConvertSelectionProc(Widget widget, Atom *atomSelection,
1374 Atom *atomTarget,
1375 Atom *atomTypeReturn,
1376 XtPointer *pValReturn,
1377 unsigned long *pcLenReturn,
1378 int *piFormatReturn)
1379{
1380 CLIPBACKEND *pCtx = clipLookupContext(widget);
1381 int rc = VINF_SUCCESS;
1382
1383 LogRelFlowFunc(("\n"));
1384 if (!pCtx)
1385 return false;
1386 if (!clipIsSupportedSelectionType(pCtx, *atomSelection))
1387 return false;
1388 if (*atomTarget == clipGetAtom(pCtx, "TARGETS"))
1389 rc = clipCreateX11Targets(pCtx, atomTypeReturn, pValReturn,
1390 pcLenReturn, piFormatReturn);
1391 else
1392 rc = clipConvertVBoxCBForX11(pCtx, atomTarget, atomTypeReturn,
1393 pValReturn, pcLenReturn, piFormatReturn);
1394 LogRelFlowFunc(("returning, internal status code %Rrc\n", rc));
1395 return RT_SUCCESS(rc);
1396}
1397
1398/** Structure used to pass information about formats that VBox supports */
1399typedef struct _CLIPNEWVBOXFORMATS
1400{
1401 /** Context information for the X11 clipboard */
1402 CLIPBACKEND *pCtx;
1403 /** Formats supported by VBox */
1404 uint32_t formats;
1405} CLIPNEWVBOXFORMATS;
1406
1407/** Invalidates the local cache of the data in the VBox clipboard. */
1408static void clipInvalidateVBoxCBCache(CLIPBACKEND *pCtx)
1409{
1410 if (pCtx->pvUnicodeCache != NULL)
1411 {
1412 RTMemFree(pCtx->pvUnicodeCache);
1413 pCtx->pvUnicodeCache = NULL;
1414 }
1415}
1416
1417/**
1418 * Take possession of the X11 clipboard (and middle-button selection).
1419 */
1420static void clipGrabX11CB(CLIPBACKEND *pCtx, uint32_t u32Formats)
1421{
1422 if (XtOwnSelection(pCtx->widget, clipGetAtom(pCtx, "CLIPBOARD"),
1423 CurrentTime, clipXtConvertSelectionProc, NULL, 0))
1424 {
1425 pCtx->vboxFormats = u32Formats;
1426 /* Grab the middle-button paste selection too. */
1427 XtOwnSelection(pCtx->widget, clipGetAtom(pCtx, "PRIMARY"),
1428 CurrentTime, clipXtConvertSelectionProc, NULL, 0);
1429#ifndef TESTCASE
1430 /* Xt suppresses these if we already own the clipboard, so send them
1431 * ourselves. */
1432 XSetSelectionOwner(XtDisplay(pCtx->widget),
1433 clipGetAtom(pCtx, "CLIPBOARD"),
1434 XtWindow(pCtx->widget), CurrentTime);
1435 XSetSelectionOwner(XtDisplay(pCtx->widget),
1436 clipGetAtom(pCtx, "PRIMARY"),
1437 XtWindow(pCtx->widget), CurrentTime);
1438#endif
1439 }
1440}
1441
1442/**
1443 * Worker function for ClipAnnounceFormatToX11 which runs on the
1444 * event thread.
1445 * @param pUserData Pointer to a CLIPNEWVBOXFORMATS structure containing
1446 * information about the VBox formats available and the
1447 * clipboard context data. Must be freed by the worker.
1448 */
1449static void clipNewVBoxFormatsWorker(void *pUserData,
1450 void * /* interval */)
1451{
1452 CLIPNEWVBOXFORMATS *pFormats = (CLIPNEWVBOXFORMATS *)pUserData;
1453 CLIPBACKEND *pCtx = pFormats->pCtx;
1454 uint32_t u32Formats = pFormats->formats;
1455 RTMemFree(pFormats);
1456 LogRelFlowFunc (("u32Formats=%d\n", u32Formats));
1457 clipInvalidateVBoxCBCache(pCtx);
1458 clipGrabX11CB(pCtx, u32Formats);
1459 clipResetX11Formats(pCtx);
1460 LogRelFlowFunc(("returning\n"));
1461}
1462
1463/**
1464 * VBox is taking possession of the shared clipboard.
1465 *
1466 * @param u32Formats Clipboard formats that VBox is offering
1467 * @note X11 backend code
1468 */
1469void ClipAnnounceFormatToX11(CLIPBACKEND *pCtx,
1470 uint32_t u32Formats)
1471{
1472 /*
1473 * Immediately return if we are not connected to the X server.
1474 */
1475 if (!pCtx->fHaveX11)
1476 return;
1477 /* This must be freed by the worker callback */
1478 CLIPNEWVBOXFORMATS *pFormats =
1479 (CLIPNEWVBOXFORMATS *) RTMemAlloc(sizeof(CLIPNEWVBOXFORMATS));
1480 if (pFormats != NULL) /* if it is we will soon have other problems */
1481 {
1482 pFormats->pCtx = pCtx;
1483 pFormats->formats = u32Formats;
1484 clipQueueToEventThread(pCtx, clipNewVBoxFormatsWorker,
1485 (XtPointer) pFormats);
1486 }
1487}
1488
1489/**
1490 * Massage generic Utf16 with CR end-of-lines into the format Windows expects
1491 * and return the result in a RTMemAlloc allocated buffer.
1492 * @returns IPRT status code
1493 * @param pwcSrc The source Utf16
1494 * @param cwcSrc The number of 16bit elements in @a pwcSrc, not counting
1495 * the terminating zero
1496 * @param ppwszDest Where to store the buffer address
1497 * @param pcbDest On success, where to store the number of bytes written.
1498 * Undefined otherwise. Optional
1499 */
1500static int clipUtf16ToWinTxt(RTUTF16 *pwcSrc, size_t cwcSrc,
1501 PRTUTF16 *ppwszDest, uint32_t *pcbDest)
1502{
1503 LogRelFlowFunc(("pwcSrc=%p, cwcSrc=%u, ppwszDest=%p\n", pwcSrc, cwcSrc,
1504 ppwszDest));
1505 AssertPtrReturn(pwcSrc, VERR_INVALID_POINTER);
1506 AssertPtrReturn(ppwszDest, VERR_INVALID_POINTER);
1507 if (pcbDest)
1508 *pcbDest = 0;
1509 PRTUTF16 pwszDest = NULL;
1510 size_t cwcDest;
1511 int rc = vboxClipboardUtf16GetWinSize(pwcSrc, cwcSrc + 1, &cwcDest);
1512 if (RT_SUCCESS(rc))
1513 {
1514 pwszDest = (PRTUTF16) RTMemAlloc(cwcDest * 2);
1515 if (!pwszDest)
1516 rc = VERR_NO_MEMORY;
1517 }
1518 if (RT_SUCCESS(rc))
1519 rc = vboxClipboardUtf16LinToWin(pwcSrc, cwcSrc + 1, pwszDest,
1520 cwcDest);
1521 if (RT_SUCCESS(rc))
1522 {
1523 LogRelFlowFunc (("converted string is %.*ls\n", cwcDest, pwszDest));
1524 *ppwszDest = pwszDest;
1525 if (pcbDest)
1526 *pcbDest = cwcDest * 2;
1527 }
1528 else
1529 RTMemFree(pwszDest);
1530 LogRelFlowFunc(("returning %Rrc\n", rc));
1531 if (pcbDest)
1532 LogRelFlowFunc(("*pcbDest=%u\n", *pcbDest));
1533 return rc;
1534}
1535
1536/**
1537 * Convert Utf-8 text with CR end-of-lines into Utf-16 as Windows expects it
1538 * and return the result in a RTMemAlloc allocated buffer.
1539 * @returns IPRT status code
1540 * @param pcSrc The source Utf-8
1541 * @param cbSrc The size of the source in bytes, not counting the
1542 * terminating zero
1543 * @param ppwszDest Where to store the buffer address
1544 * @param pcbDest On success, where to store the number of bytes written.
1545 * Undefined otherwise. Optional
1546 */
1547static int clipUtf8ToWinTxt(const char *pcSrc, unsigned cbSrc,
1548 PRTUTF16 *ppwszDest, uint32_t *pcbDest)
1549{
1550 LogRelFlowFunc(("pcSrc=%p, cbSrc=%u, ppwszDest=%p\n", pcSrc, cbSrc,
1551 ppwszDest));
1552 AssertPtrReturn(pcSrc, VERR_INVALID_POINTER);
1553 AssertPtrReturn(ppwszDest, VERR_INVALID_POINTER);
1554 if (pcbDest)
1555 *pcbDest = 0;
1556 /* Intermediate conversion to UTF16 */
1557 size_t cwcTmp;
1558 PRTUTF16 pwcTmp = NULL;
1559 int rc = RTStrToUtf16Ex(pcSrc, cbSrc, &pwcTmp, 0, &cwcTmp);
1560 if (RT_SUCCESS(rc))
1561 rc = clipUtf16ToWinTxt(pwcTmp, cwcTmp, ppwszDest, pcbDest);
1562 RTUtf16Free(pwcTmp);
1563 LogRelFlowFunc(("Returning %Rrc\n", rc));
1564 if (pcbDest)
1565 LogRelFlowFunc(("*pcbDest=%u\n", *pcbDest));
1566 return rc;
1567}
1568
1569/**
1570 * Convert Latin-1 text with CR end-of-lines into Utf-16 as Windows expects
1571 * it and return the result in a RTMemAlloc allocated buffer.
1572 * @returns IPRT status code
1573 * @param pcSrc The source text
1574 * @param cbSrc The size of the source in bytes, not counting the
1575 * terminating zero
1576 * @param ppwszDest Where to store the buffer address
1577 * @param pcbDest On success, where to store the number of bytes written.
1578 * Undefined otherwise. Optional
1579 */
1580static int clipLatin1ToWinTxt(char *pcSrc, unsigned cbSrc,
1581 PRTUTF16 *ppwszDest, uint32_t *pcbDest)
1582{
1583 LogRelFlowFunc (("pcSrc=%.*s, cbSrc=%u, ppwszDest=%p\n", cbSrc,
1584 (char *) pcSrc, cbSrc, ppwszDest));
1585 AssertPtrReturn(pcSrc, VERR_INVALID_POINTER);
1586 AssertPtrReturn(ppwszDest, VERR_INVALID_POINTER);
1587 PRTUTF16 pwszDest = NULL;
1588 int rc = VINF_SUCCESS;
1589
1590 /* Calculate the space needed */
1591 unsigned cwcDest = 0;
1592 for (unsigned i = 0; i < cbSrc && pcSrc[i] != '\0'; ++i)
1593 if (pcSrc[i] == LINEFEED)
1594 cwcDest += 2;
1595 else
1596 ++cwcDest;
1597 ++cwcDest; /* Leave space for the terminator */
1598 if (pcbDest)
1599 *pcbDest = cwcDest * 2;
1600 pwszDest = (PRTUTF16) RTMemAlloc(cwcDest * 2);
1601 if (!pwszDest)
1602 rc = VERR_NO_MEMORY;
1603
1604 /* And do the conversion, bearing in mind that Latin-1 expands "naturally"
1605 * to Utf-16. */
1606 if (RT_SUCCESS(rc))
1607 {
1608 for (unsigned i = 0, j = 0; i < cbSrc; ++i, ++j)
1609 if (pcSrc[i] != LINEFEED)
1610 pwszDest[j] = pcSrc[i];
1611 else
1612 {
1613 pwszDest[j] = CARRIAGERETURN;
1614 pwszDest[j + 1] = LINEFEED;
1615 ++j;
1616 }
1617 pwszDest[cwcDest - 1] = '\0'; /* Make sure we are zero-terminated. */
1618 LogRelFlowFunc (("converted text is %.*ls\n", cwcDest, pwszDest));
1619 }
1620 if (RT_SUCCESS(rc))
1621 *ppwszDest = pwszDest;
1622 else
1623 RTMemFree(pwszDest);
1624 LogRelFlowFunc(("Returning %Rrc\n", rc));
1625 if (pcbDest)
1626 LogRelFlowFunc(("*pcbDest=%u\n", *pcbDest));
1627 return rc;
1628}
1629
1630
1631/**
1632* Convert Utf16 text into UTF8 as Windows expects
1633* it and return the result in a RTMemAlloc allocated buffer.
1634* @returns IPRT status code
1635* @param pcSrc The source text
1636* @param cbSrc The size of the source in bytes, not counting the
1637* terminating zero
1638* @param ppwszDest Where to store the buffer address
1639* @param pcbDest On success, where to store the number of bytes written.
1640* Undefined otherwise. Optional
1641*/
1642int clipUTF16ToWinHTML(RTUTF16 *pwcBuf, size_t cb, char **ppszOut, uint32_t *pcOut)
1643{
1644 Assert(pwcBuf);
1645 Assert(cb);
1646 Assert(ppszOut);
1647 Assert(pcOut);
1648
1649 if (cb % 2)
1650 return VERR_INVALID_PARAMETER;
1651 size_t cwc = cb / 2;
1652 size_t i = 0;
1653 RTUTF16 *pwc = pwcBuf;
1654 char *pchRes = NULL;
1655 size_t cRes = 0;
1656 LogRelFlowFunc(("clipUTF16ToWinHTML src= %ls cb=%d i=%i, %x %x\n", pwcBuf, cb, i, ppszOut, pcOut));
1657 while (i < cwc)
1658 {
1659 /* find zero symbol (end of string) */
1660 for (; i < cwc && pwcBuf[i] != 0; i++)
1661 ;
1662 LogRelFlowFunc(("skipped nulls i=%d cwc=%d\n", i, cwc));
1663
1664 /* convert found string */
1665 char *psz = NULL;
1666 size_t cch = 0;
1667 int rc = RTUtf16ToUtf8Ex(pwc, cwc, &psz, pwc - pwcBuf, &cch);
1668 LogRelFlowFunc(("utf16toutf8 src= %ls res=%s i=%i\n", pwc, psz, i));
1669 if (RT_FAILURE(rc))
1670 {
1671 RTMemFree(pchRes);
1672 return rc;
1673 }
1674
1675 /* append new substring */
1676 char *pchNew = (char*)RTMemRealloc(pchRes, cRes + cch + 1);
1677 if (!pchNew)
1678 {
1679 RTMemFree(pchRes);
1680 RTStrFree(psz);
1681 return VERR_NO_MEMORY;
1682 }
1683 pchRes = pchNew;
1684 memcpy(pchRes + cRes, psz, cch + 1);
1685 LogRelFlowFunc(("Temp result res=%s\n", pchRes + cRes));
1686
1687 /* remove temporary buffer */
1688 RTStrFree(psz);
1689 cRes += cch + 1;
1690 /* skip zero symbols */
1691 for (; i < cwc && pwcBuf[i] == 0; i++)
1692 ;
1693 /* remember start of string */
1694 pwc += i;
1695 }
1696 *ppszOut = pchRes;
1697 *pcOut = cRes;
1698
1699 return VINF_SUCCESS;
1700}
1701
1702
1703
1704/** A structure containing information about where to store a request
1705 * for the X11 clipboard contents. */
1706struct _CLIPREADX11CBREQ
1707{
1708 /** The format VBox would like the data in */
1709 uint32_t mFormat;
1710 /** The text format we requested from X11 if we requested text */
1711 CLIPX11FORMAT mTextFormat;
1712 /** The bitmap format we requested from X11 if we requested bitmap */
1713 CLIPX11FORMAT mBitmapFormat;
1714 /** The HTML format we requested from X11 if we requested HTML */
1715 CLIPX11FORMAT mHtmlFormat;
1716 /** The clipboard context this request is associated with */
1717 CLIPBACKEND *mCtx;
1718 /** The request structure passed in from the backend. */
1719 CLIPREADCBREQ *mReq;
1720};
1721
1722typedef struct _CLIPREADX11CBREQ CLIPREADX11CBREQ;
1723
1724/**
1725 * Convert the data obtained from the X11 clipboard to the required format,
1726 * place it in the buffer supplied and signal that data has arrived.
1727 * Convert the text obtained UTF-16LE with Windows EOLs.
1728 * Convert full BMP data to DIB format.
1729 * @note X11 backend code, callback for XtGetSelectionValue, for use when
1730 * the X11 clipboard contains a format we understand.
1731 */
1732static void clipConvertX11CB(void *pClientData, void *pvSrc, unsigned cbSrc)
1733{
1734 CLIPREADX11CBREQ *pReq = (CLIPREADX11CBREQ *) pClientData;
1735 LogRelFlowFunc(("pReq->mFormat=%02X, pReq->mTextFormat=%u, "
1736 "pReq->mBitmapFormat=%u, pReq->mHtmlFormat=%u, pReq->mCtx=%p\n",
1737 pReq->mFormat, pReq->mTextFormat, pReq->mBitmapFormat,
1738 pReq->mHtmlFormat, pReq->mCtx));
1739 AssertPtr(pReq->mCtx);
1740 Assert(pReq->mFormat != 0); /* sanity */
1741 int rc = VINF_SUCCESS;
1742 CLIPBACKEND *pCtx = pReq->mCtx;
1743 void *pvDest = NULL;
1744 uint32_t cbDest = 0;
1745
1746 pCtx->fBusy = false;
1747 if (pCtx->fUpdateNeeded)
1748 clipQueryX11CBFormats(pCtx);
1749 if (pvSrc == NULL)
1750 /* The clipboard selection may have changed before we could get it. */
1751 rc = VERR_NO_DATA;
1752 else if (pReq->mFormat == VBOX_SHARED_CLIPBOARD_FMT_UNICODETEXT)
1753 {
1754 /* In which format is the clipboard data? */
1755 switch (clipRealFormatForX11Format(pReq->mTextFormat))
1756 {
1757 case UTF8:
1758 case TEXT:
1759 {
1760 /* If we are given broken Utf-8, we treat it as Latin1. Is
1761 * this acceptable? */
1762 if (RT_SUCCESS(RTStrValidateEncodingEx((char *)pvSrc, cbSrc,
1763 0)))
1764 rc = clipUtf8ToWinTxt((const char *)pvSrc, cbSrc,
1765 (PRTUTF16 *) &pvDest, &cbDest);
1766 else
1767 rc = clipLatin1ToWinTxt((char *) pvSrc, cbSrc,
1768 (PRTUTF16 *) &pvDest, &cbDest);
1769 break;
1770 }
1771 default:
1772 rc = VERR_INVALID_PARAMETER;
1773 }
1774 }
1775 else if (pReq->mFormat == VBOX_SHARED_CLIPBOARD_FMT_BITMAP)
1776 {
1777 /* In which format is the clipboard data? */
1778 switch (clipRealFormatForX11Format(pReq->mBitmapFormat))
1779 {
1780 case BMP:
1781 {
1782 const void *pDib;
1783 size_t cbDibSize;
1784 rc = vboxClipboardBmpGetDib((const void *)pvSrc, cbSrc,
1785 &pDib, &cbDibSize);
1786 if (RT_SUCCESS(rc))
1787 {
1788 pvDest = RTMemAlloc(cbDibSize);
1789 if (!pvDest)
1790 rc = VERR_NO_MEMORY;
1791 else
1792 {
1793 memcpy(pvDest, pDib, cbDibSize);
1794 cbDest = cbDibSize;
1795 }
1796 }
1797 break;
1798 }
1799 default:
1800 rc = VERR_INVALID_PARAMETER;
1801 }
1802 }
1803 else if(pReq->mFormat == VBOX_SHARED_CLIPBOARD_FMT_HTML)
1804 {
1805 /* In which format is the clipboard data? */
1806 switch (clipRealFormatForX11Format(pReq->mHtmlFormat))
1807 {
1808 case HTML:
1809 {
1810 /* The common VBox HTML encoding will be - Utf8
1811 * becuase it more general for HTML formats then UTF16
1812 * X11 clipboard returns UTF16, so before sending it we should
1813 * convert it to UTF8
1814 */
1815 pvDest = NULL;
1816 cbDest = 0;
1817 /* Some applications sends data in utf16, some in itf8,
1818 * without indication it in MIME.
1819 * But in case of utf16, at least an OpenOffice adds Byte Order Mark - 0xfeff
1820 * at start of clipboard data
1821 */
1822 if( cbSrc >= sizeof(RTUTF16) && *(PRTUTF16)pvSrc == 0xfeff )
1823 {
1824 LogRelFlowFunc((" \n"));
1825 rc = clipUTF16ToWinHTML((RTUTF16*)pvSrc, cbSrc,
1826 (char**)&pvDest, &cbDest);
1827 }
1828 else
1829 {
1830 pvDest = RTMemAlloc(cbSrc);
1831 if(pvDest)
1832 {
1833 memcpy(pvDest, pvSrc, cbSrc);
1834 cbDest = cbSrc;
1835 }
1836 else
1837 {
1838 rc = VERR_NO_MEMORY;
1839 break;
1840 }
1841 }
1842
1843 LogRelFlowFunc(("Source unicode %ls, cbSrc = %d\n, Byte Order Mark = %hx",
1844 pvSrc, cbSrc, ((PRTUTF16)pvSrc)[0]));
1845 LogRelFlowFunc(("converted to win unicode %s, cbDest = %d, rc = %Rrc\n", pvDest, cbDest, rc));
1846 rc = VINF_SUCCESS;
1847 break;
1848 }
1849 default:
1850 {
1851 rc = VERR_INVALID_PARAMETER;
1852 }
1853 }
1854 }
1855 else
1856 rc = VERR_NOT_IMPLEMENTED;
1857 ClipCompleteDataRequestFromX11(pReq->mCtx->pFrontend, rc, pReq->mReq,
1858 pvDest, cbDest);
1859 RTMemFree(pvDest);
1860 RTMemFree(pReq);
1861 LogRelFlowFunc(("rc=%Rrc\n", rc));
1862}
1863
1864#ifndef TESTCASE
1865/**
1866 * Convert the data obtained from the X11 clipboard to the required format,
1867 * place it in the buffer supplied and signal that data has arrived.
1868 * Convert the text obtained UTF-16LE with Windows EOLs.
1869 * Convert full BMP data to DIB format.
1870 * @note X11 backend code, callback for XtGetSelectionValue, for use when
1871 * the X11 clipboard contains a format we understand.
1872 */
1873static void cbConvertX11CB(Widget widget, XtPointer pClientData,
1874 Atom * /* selection */, Atom *atomType,
1875 XtPointer pvSrc, long unsigned int *pcLen,
1876 int *piFormat)
1877{
1878 RT_NOREF1(widget);
1879 if (*atomType == XT_CONVERT_FAIL) /* Xt timeout */
1880 clipConvertX11CB(pClientData, NULL, 0);
1881 else
1882 clipConvertX11CB(pClientData, pvSrc, (*pcLen) * (*piFormat) / 8);
1883
1884 XtFree((char *)pvSrc);
1885}
1886#endif
1887
1888#ifdef TESTCASE
1889static void testRequestData(CLIPBACKEND* pCtx, CLIPX11FORMAT target,
1890 void *closure);
1891#endif
1892
1893static void getSelectionValue(CLIPBACKEND *pCtx, CLIPX11FORMAT format,
1894 CLIPREADX11CBREQ *pReq)
1895{
1896#ifndef TESTCASE
1897 XtGetSelectionValue(pCtx->widget, clipGetAtom(pCtx, "CLIPBOARD"),
1898 clipAtomForX11Format(pCtx, format),
1899 cbConvertX11CB,
1900 reinterpret_cast<XtPointer>(pReq),
1901 CurrentTime);
1902#else
1903 testRequestData(pCtx, format, (void *)pReq);
1904#endif
1905}
1906
1907/** Worker function for ClipRequestDataFromX11 which runs on the event
1908 * thread. */
1909static void vboxClipboardReadX11Worker(void *pUserData,
1910 void * /* interval */)
1911{
1912 CLIPREADX11CBREQ *pReq = (CLIPREADX11CBREQ *)pUserData;
1913 CLIPBACKEND *pCtx = pReq->mCtx;
1914 LogRelFlowFunc (("pReq->mFormat = %02X\n", pReq->mFormat));
1915
1916 int rc = VINF_SUCCESS;
1917 bool fBusy = pCtx->fBusy;
1918 pCtx->fBusy = true;
1919 if (fBusy)
1920 /* If the clipboard is busy just fend off the request. */
1921 rc = VERR_TRY_AGAIN;
1922 else if (pReq->mFormat == VBOX_SHARED_CLIPBOARD_FMT_UNICODETEXT)
1923 {
1924 /*
1925 * VBox wants to read data in the given format.
1926 */
1927 pReq->mTextFormat = pCtx->X11TextFormat;
1928 if (pReq->mTextFormat == INVALID)
1929 /* VBox thinks we have data and we don't */
1930 rc = VERR_NO_DATA;
1931 else
1932 /* Send out a request for the data to the current clipboard
1933 * owner */
1934 getSelectionValue(pCtx, pCtx->X11TextFormat, pReq);
1935 }
1936 else if (pReq->mFormat == VBOX_SHARED_CLIPBOARD_FMT_BITMAP)
1937 {
1938 pReq->mBitmapFormat = pCtx->X11BitmapFormat;
1939 if (pReq->mBitmapFormat == INVALID)
1940 /* VBox thinks we have data and we don't */
1941 rc = VERR_NO_DATA;
1942 else
1943 /* Send out a request for the data to the current clipboard
1944 * owner */
1945 getSelectionValue(pCtx, pCtx->X11BitmapFormat, pReq);
1946 }
1947 else if(pReq->mFormat == VBOX_SHARED_CLIPBOARD_FMT_HTML)
1948 {
1949 /* Send out a request for the data to the current clipboard
1950 * owner */
1951 pReq->mHtmlFormat = pCtx->X11HTMLFormat;
1952 if(pReq->mHtmlFormat == INVALID)
1953 /* VBox thinks we have data and we don't */
1954 rc = VERR_NO_DATA;
1955 else
1956 /* Send out a request for the data to the current clipboard
1957 * owner */
1958 getSelectionValue(pCtx, pCtx->X11HTMLFormat, pReq);
1959 }
1960 else
1961 rc = VERR_NOT_IMPLEMENTED;
1962 if (RT_FAILURE(rc))
1963 {
1964 /* The clipboard callback was never scheduled, so we must signal
1965 * that the request processing is finished and clean up ourselves. */
1966 ClipCompleteDataRequestFromX11(pReq->mCtx->pFrontend, rc, pReq->mReq,
1967 NULL, 0);
1968 RTMemFree(pReq);
1969 }
1970 LogRelFlowFunc(("status %Rrc\n", rc));
1971}
1972
1973/**
1974 * Called when VBox wants to read the X11 clipboard.
1975 *
1976 * @returns iprt status code
1977 * @param pCtx Context data for the clipboard backend
1978 * @param u32Format The format that the VBox would like to receive the data
1979 * in
1980 * @param pv Where to write the data to
1981 * @param cb The size of the buffer to write the data to
1982 * @param pcbActual Where to write the actual size of the written data
1983 * @note We allocate a request structure which must be freed by the worker
1984 */
1985int ClipRequestDataFromX11(CLIPBACKEND *pCtx, uint32_t u32Format,
1986 CLIPREADCBREQ *pReq)
1987{
1988 /*
1989 * Immediately return if we are not connected to the X server.
1990 */
1991 if (!pCtx->fHaveX11)
1992 return VERR_NO_DATA;
1993 int rc = VINF_SUCCESS;
1994 CLIPREADX11CBREQ *pX11Req;
1995 pX11Req = (CLIPREADX11CBREQ *)RTMemAllocZ(sizeof(*pX11Req));
1996 if (!pX11Req)
1997 rc = VERR_NO_MEMORY;
1998 else
1999 {
2000 pX11Req->mFormat = u32Format;
2001 pX11Req->mCtx = pCtx;
2002 pX11Req->mReq = pReq;
2003 /* We use this to schedule a worker function on the event thread. */
2004 clipQueueToEventThread(pCtx, vboxClipboardReadX11Worker,
2005 (XtPointer) pX11Req);
2006 }
2007 return rc;
2008}
2009
2010#ifdef TESTCASE
2011
2012/** @todo This unit test currently works by emulating the X11 and X toolkit
2013 * APIs to exercise the code, since I didn't want to rewrite the code too much
2014 * when I wrote the tests. However, this makes it rather ugly and hard to
2015 * understand. Anyone doing any work on the code should feel free to
2016 * rewrite the tests and the code to make them cleaner and more readable. */
2017
2018#include <iprt/test.h>
2019#include <poll.h>
2020
2021#define TEST_WIDGET (Widget)0xffff
2022
2023/* For the purpose of the test case, we just execute the procedure to be
2024 * scheduled, as we are running single threaded. */
2025void testQueueToEventThread(void (*proc)(void *, void *),
2026 void *client_data)
2027{
2028 proc(client_data, NULL);
2029}
2030
2031void XtFree(char *ptr)
2032{ RTMemFree((void *) ptr); }
2033
2034/* The data in the simulated VBox clipboard */
2035static int g_vboxDataRC = VINF_SUCCESS;
2036static void *g_vboxDatapv = NULL;
2037static uint32_t g_vboxDatacb = 0;
2038
2039/* Set empty data in the simulated VBox clipboard. */
2040static void clipEmptyVBox(CLIPBACKEND *pCtx, int retval)
2041{
2042 g_vboxDataRC = retval;
2043 RTMemFree(g_vboxDatapv);
2044 g_vboxDatapv = NULL;
2045 g_vboxDatacb = 0;
2046 ClipAnnounceFormatToX11(pCtx, 0);
2047}
2048
2049/* Set the data in the simulated VBox clipboard. */
2050static int clipSetVBoxUtf16(CLIPBACKEND *pCtx, int retval,
2051 const char *pcszData, size_t cb)
2052{
2053 PRTUTF16 pwszData = NULL;
2054 size_t cwData = 0;
2055 int rc = RTStrToUtf16Ex(pcszData, RTSTR_MAX, &pwszData, 0, &cwData);
2056 if (RT_FAILURE(rc))
2057 return rc;
2058 AssertReturn(cb <= cwData * 2 + 2, VERR_BUFFER_OVERFLOW);
2059 void *pv = RTMemDup(pwszData, cb);
2060 RTUtf16Free(pwszData);
2061 if (pv == NULL)
2062 return VERR_NO_MEMORY;
2063 if (g_vboxDatapv)
2064 RTMemFree(g_vboxDatapv);
2065 g_vboxDataRC = retval;
2066 g_vboxDatapv = pv;
2067 g_vboxDatacb = cb;
2068 ClipAnnounceFormatToX11(pCtx,
2069 VBOX_SHARED_CLIPBOARD_FMT_UNICODETEXT);
2070 return VINF_SUCCESS;
2071}
2072
2073/* Return the data in the simulated VBox clipboard. */
2074int ClipRequestDataForX11(VBOXCLIPBOARDCONTEXT *pCtx, uint32_t u32Format, void **ppv, uint32_t *pcb)
2075{
2076 RT_NOREF2(pCtx, u32Format);
2077 *pcb = g_vboxDatacb;
2078 if (g_vboxDatapv != NULL)
2079 {
2080 void *pv = RTMemDup(g_vboxDatapv, g_vboxDatacb);
2081 *ppv = pv;
2082 return pv != NULL ? g_vboxDataRC : VERR_NO_MEMORY;
2083 }
2084 *ppv = NULL;
2085 return g_vboxDataRC;
2086}
2087
2088Display *XtDisplay(Widget w)
2089{ NOREF(w); return (Display *) 0xffff; }
2090
2091void XtAppSetExitFlag(XtAppContext app_context) { NOREF(app_context); }
2092
2093void XtDestroyWidget(Widget w) { NOREF(w); }
2094
2095XtAppContext XtCreateApplicationContext(void) { return (XtAppContext)0xffff; }
2096
2097void XtDestroyApplicationContext(XtAppContext app_context) { NOREF(app_context); }
2098
2099void XtToolkitInitialize(void) {}
2100
2101Boolean XtToolkitThreadInitialize(void) { return True; }
2102
2103Display *XtOpenDisplay(XtAppContext app_context,
2104 _Xconst _XtString display_string,
2105 _Xconst _XtString application_name,
2106 _Xconst _XtString application_class,
2107 XrmOptionDescRec *options, Cardinal num_options,
2108 int *argc, char **argv)
2109{
2110 RT_NOREF8(app_context, display_string, application_name, application_class, options, num_options, argc, argv);
2111 return (Display *)0xffff;
2112}
2113
2114Widget XtVaAppCreateShell(_Xconst _XtString application_name, _Xconst _XtString application_class,
2115 WidgetClass widget_class, Display *display, ...)
2116{
2117 RT_NOREF4(application_name, application_class, widget_class, display);
2118 return TEST_WIDGET;
2119}
2120
2121void XtSetMappedWhenManaged(Widget widget, _XtBoolean mapped_when_managed) { RT_NOREF2(widget, mapped_when_managed); }
2122
2123void XtRealizeWidget(Widget widget) { NOREF(widget); }
2124
2125XtInputId XtAppAddInput(XtAppContext app_context, int source, XtPointer condition, XtInputCallbackProc proc, XtPointer closure)
2126{
2127 RT_NOREF5(app_context, source, condition, proc, closure);
2128 return 0xffff;
2129}
2130
2131/* Atoms we need other than the formats we support. */
2132static const char *g_apszSupAtoms[] =
2133{
2134 "PRIMARY", "CLIPBOARD", "TARGETS", "MULTIPLE", "TIMESTAMP"
2135};
2136
2137/* This just looks for the atom names in a couple of tables and returns an
2138 * index with an offset added. */
2139Atom XInternAtom(Display *, const char *pcsz, int)
2140{
2141 Atom atom = 0;
2142 for (unsigned i = 0; i < RT_ELEMENTS(g_aFormats); ++i)
2143 if (!strcmp(pcsz, g_aFormats[i].pcszAtom))
2144 atom = (Atom) (i + 0x1000);
2145 for (unsigned i = 0; i < RT_ELEMENTS(g_apszSupAtoms); ++i)
2146 if (!strcmp(pcsz, g_apszSupAtoms[i]))
2147 atom = (Atom) (i + 0x2000);
2148 Assert(atom); /* Have we missed any atoms? */
2149 return atom;
2150}
2151
2152/* Take a request for the targets we are currently offering. */
2153static CLIPX11FORMAT g_selTargets[10] = { 0 };
2154static size_t g_cTargets = 0;
2155
2156void testRequestTargets(CLIPBACKEND* pCtx)
2157{
2158 clipUpdateX11Targets(pCtx, g_selTargets, g_cTargets);
2159}
2160
2161/* The current values of the X selection, which will be returned to the
2162 * XtGetSelectionValue callback. */
2163static Atom g_selType = 0;
2164static const void *g_pSelData = NULL;
2165static unsigned long g_cSelData = 0;
2166static int g_selFormat = 0;
2167
2168void testRequestData(CLIPBACKEND *pCtx, CLIPX11FORMAT target, void *closure)
2169{
2170 RT_NOREF1(pCtx);
2171 unsigned long count = 0;
2172 int format = 0;
2173 if (target != g_selTargets[0])
2174 {
2175 clipConvertX11CB(closure, NULL, 0); /* Could not convert to target. */
2176 return;
2177 }
2178 void *pValue = NULL;
2179 pValue = g_pSelData ? RTMemDup(g_pSelData, g_cSelData) : NULL;
2180 count = g_pSelData ? g_cSelData : 0;
2181 format = g_selFormat;
2182 if (!pValue)
2183 {
2184 count = 0;
2185 format = 0;
2186 }
2187 clipConvertX11CB(closure, pValue, count * format / 8);
2188}
2189
2190/* The formats currently on offer from X11 via the shared clipboard */
2191static uint32_t g_fX11Formats = 0;
2192
2193void ClipReportX11Formats(VBOXCLIPBOARDCONTEXT *pCtx, uint32_t u32Formats)
2194{
2195 RT_NOREF1(pCtx);
2196 g_fX11Formats = u32Formats;
2197}
2198
2199static uint32_t clipQueryFormats()
2200{
2201 return g_fX11Formats;
2202}
2203
2204static void clipInvalidateFormats()
2205{
2206 g_fX11Formats = ~0;
2207}
2208
2209/* Does our clipboard code currently own the selection? */
2210static bool g_ownsSel = false;
2211/* The procedure that is called when we should convert the selection to a
2212 * given format. */
2213static XtConvertSelectionProc g_pfnSelConvert = NULL;
2214/* The procedure which is called when we lose the selection. */
2215static XtLoseSelectionProc g_pfnSelLose = NULL;
2216/* The procedure which is called when the selection transfer has completed. */
2217static XtSelectionDoneProc g_pfnSelDone = NULL;
2218
2219Boolean XtOwnSelection(Widget widget, Atom selection, Time time,
2220 XtConvertSelectionProc convert,
2221 XtLoseSelectionProc lose,
2222 XtSelectionDoneProc done)
2223{
2224 RT_NOREF2(widget, time);
2225 if (selection != XInternAtom(NULL, "CLIPBOARD", 0))
2226 return True; /* We don't really care about this. */
2227 g_ownsSel = true; /* Always succeed. */
2228 g_pfnSelConvert = convert;
2229 g_pfnSelLose = lose;
2230 g_pfnSelDone = done;
2231 return True;
2232}
2233
2234void XtDisownSelection(Widget widget, Atom selection, Time time)
2235{
2236 RT_NOREF3(widget, time, selection);
2237 g_ownsSel = false;
2238 g_pfnSelConvert = NULL;
2239 g_pfnSelLose = NULL;
2240 g_pfnSelDone = NULL;
2241}
2242
2243/* Request the shared clipboard to convert its data to a given format. */
2244static bool clipConvertSelection(const char *pcszTarget, Atom *type,
2245 XtPointer *value, unsigned long *length,
2246 int *format)
2247{
2248 Atom target = XInternAtom(NULL, pcszTarget, 0);
2249 if (target == 0)
2250 return false;
2251 /* Initialise all return values in case we make a quick exit. */
2252 *type = XA_STRING;
2253 *value = NULL;
2254 *length = 0;
2255 *format = 0;
2256 if (!g_ownsSel)
2257 return false;
2258 if (!g_pfnSelConvert)
2259 return false;
2260 Atom clipAtom = XInternAtom(NULL, "CLIPBOARD", 0);
2261 if (!g_pfnSelConvert(TEST_WIDGET, &clipAtom, &target, type,
2262 value, length, format))
2263 return false;
2264 if (g_pfnSelDone)
2265 g_pfnSelDone(TEST_WIDGET, &clipAtom, &target);
2266 return true;
2267}
2268
2269/* Set the current X selection data */
2270static void clipSetSelectionValues(const char *pcszTarget, Atom type,
2271 const void *data,
2272 unsigned long count, int format)
2273{
2274 Atom clipAtom = XInternAtom(NULL, "CLIPBOARD", 0);
2275 g_selTargets[0] = clipFindX11FormatByAtomText(pcszTarget);
2276 g_cTargets = 1;
2277 g_selType = type;
2278 g_pSelData = data;
2279 g_cSelData = count;
2280 g_selFormat = format;
2281 if (g_pfnSelLose)
2282 g_pfnSelLose(TEST_WIDGET, &clipAtom);
2283 g_ownsSel = false;
2284}
2285
2286static void clipSendTargetUpdate(CLIPBACKEND *pCtx)
2287{
2288 clipQueryX11CBFormats(pCtx);
2289}
2290
2291/* Configure if and how the X11 TARGETS clipboard target will fail */
2292static void clipSetTargetsFailure(void)
2293{
2294 g_cTargets = 0;
2295}
2296
2297char *XtMalloc(Cardinal size) { return (char *) RTMemAlloc(size); }
2298
2299char *XGetAtomName(Display *display, Atom atom)
2300{
2301 RT_NOREF1(display);
2302 AssertReturn((unsigned)atom < RT_ELEMENTS(g_aFormats) + 1, NULL);
2303 const char *pcszName = NULL;
2304 if (atom < 0x1000)
2305 return NULL;
2306 if (0x1000 <= atom && atom < 0x2000)
2307 {
2308 unsigned index = atom - 0x1000;
2309 AssertReturn(index < RT_ELEMENTS(g_aFormats), NULL);
2310 pcszName = g_aFormats[index].pcszAtom;
2311 }
2312 else
2313 {
2314 unsigned index = atom - 0x2000;
2315 AssertReturn(index < RT_ELEMENTS(g_apszSupAtoms), NULL);
2316 pcszName = g_apszSupAtoms[index];
2317 }
2318 return (char *)RTMemDup(pcszName, sizeof(pcszName) + 1);
2319}
2320
2321int XFree(void *data)
2322{
2323 RTMemFree(data);
2324 return 0;
2325}
2326
2327void XFreeStringList(char **list)
2328{
2329 if (list)
2330 RTMemFree(*list);
2331 RTMemFree(list);
2332}
2333
2334#define MAX_BUF_SIZE 256
2335
2336static int g_completedRC = VINF_SUCCESS;
2337static int g_completedCB = 0;
2338static CLIPREADCBREQ *g_completedReq = NULL;
2339static char g_completedBuf[MAX_BUF_SIZE];
2340
2341void ClipCompleteDataRequestFromX11(VBOXCLIPBOARDCONTEXT *pCtx, int rc, CLIPREADCBREQ *pReq, void *pv, uint32_t cb)
2342{
2343 RT_NOREF1(pCtx);
2344 if (cb <= MAX_BUF_SIZE)
2345 {
2346 g_completedRC = rc;
2347 memcpy(g_completedBuf, pv, cb);
2348 }
2349 else
2350 g_completedRC = VERR_BUFFER_OVERFLOW;
2351 g_completedCB = cb;
2352 g_completedReq = pReq;
2353}
2354
2355static void clipGetCompletedRequest(int *prc, char ** ppc, uint32_t *pcb, CLIPREADCBREQ **ppReq)
2356{
2357 *prc = g_completedRC;
2358 *ppc = g_completedBuf;
2359 *pcb = g_completedCB;
2360 *ppReq = g_completedReq;
2361}
2362#ifdef RT_OS_SOLARIS_10
2363char XtStrings [] = "";
2364_WidgetClassRec* applicationShellWidgetClass;
2365char XtShellStrings [] = "";
2366int XmbTextPropertyToTextList(
2367 Display* /* display */,
2368 XTextProperty* /* text_prop */,
2369 char*** /* list_return */,
2370 int* /* count_return */
2371)
2372{
2373 return 0;
2374}
2375#else
2376const char XtStrings [] = "";
2377_WidgetClassRec* applicationShellWidgetClass;
2378const char XtShellStrings [] = "";
2379#endif
2380
2381static void testStringFromX11(RTTEST hTest, CLIPBACKEND *pCtx,
2382 const char *pcszExp, int rcExp)
2383{
2384 bool retval = true;
2385 clipSendTargetUpdate(pCtx);
2386 if (clipQueryFormats() != VBOX_SHARED_CLIPBOARD_FMT_UNICODETEXT)
2387 RTTestFailed(hTest, "Wrong targets reported: %02X\n",
2388 clipQueryFormats());
2389 else
2390 {
2391 char *pc;
2392 CLIPREADCBREQ *pReq = (CLIPREADCBREQ *)&pReq, *pReqRet = NULL;
2393 ClipRequestDataFromX11(pCtx, VBOX_SHARED_CLIPBOARD_FMT_UNICODETEXT,
2394 pReq);
2395 int rc = VINF_SUCCESS;
2396 uint32_t cbActual = 0;
2397 clipGetCompletedRequest(&rc, &pc, &cbActual, &pReqRet);
2398 if (rc != rcExp)
2399 RTTestFailed(hTest, "Wrong return code, expected %Rrc, got %Rrc\n",
2400 rcExp, rc);
2401 else if (pReqRet != pReq)
2402 RTTestFailed(hTest, "Wrong returned request data, expected %p, got %p\n",
2403 pReq, pReqRet);
2404 else if (RT_FAILURE(rcExp))
2405 retval = true;
2406 else
2407 {
2408 RTUTF16 wcExp[MAX_BUF_SIZE / 2];
2409 RTUTF16 *pwcExp = wcExp;
2410 size_t cwc = 0;
2411 rc = RTStrToUtf16Ex(pcszExp, RTSTR_MAX, &pwcExp,
2412 RT_ELEMENTS(wcExp), &cwc);
2413 size_t cbExp = cwc * 2 + 2;
2414 AssertRC(rc);
2415 if (RT_SUCCESS(rc))
2416 {
2417 if (cbActual != cbExp)
2418 {
2419 RTTestFailed(hTest, "Returned string is the wrong size, string \"%.*ls\", size %u, expected \"%s\", size %u\n",
2420 RT_MIN(MAX_BUF_SIZE, cbActual), pc, cbActual,
2421 pcszExp, cbExp);
2422 }
2423 else
2424 {
2425 if (memcmp(pc, wcExp, cbExp) == 0)
2426 retval = true;
2427 else
2428 RTTestFailed(hTest, "Returned string \"%.*ls\" does not match expected string \"%s\"\n",
2429 MAX_BUF_SIZE, pc, pcszExp);
2430 }
2431 }
2432 }
2433 }
2434 if (!retval)
2435 RTTestFailureDetails(hTest, "Expected: string \"%s\", rc %Rrc\n",
2436 pcszExp, rcExp);
2437}
2438
2439static void testLatin1FromX11(RTTEST hTest, CLIPBACKEND *pCtx,
2440 const char *pcszExp, int rcExp)
2441{
2442 bool retval = false;
2443 clipSendTargetUpdate(pCtx);
2444 if (clipQueryFormats() != VBOX_SHARED_CLIPBOARD_FMT_UNICODETEXT)
2445 RTTestFailed(hTest, "Wrong targets reported: %02X\n",
2446 clipQueryFormats());
2447 else
2448 {
2449 char *pc;
2450 CLIPREADCBREQ *pReq = (CLIPREADCBREQ *)&pReq, *pReqRet = NULL;
2451 ClipRequestDataFromX11(pCtx, VBOX_SHARED_CLIPBOARD_FMT_UNICODETEXT,
2452 pReq);
2453 int rc = VINF_SUCCESS;
2454 uint32_t cbActual = 0;
2455 clipGetCompletedRequest(&rc, &pc, &cbActual, &pReqRet);
2456 if (rc != rcExp)
2457 RTTestFailed(hTest, "Wrong return code, expected %Rrc, got %Rrc\n",
2458 rcExp, rc);
2459 else if (pReqRet != pReq)
2460 RTTestFailed(hTest, "Wrong returned request data, expected %p, got %p\n",
2461 pReq, pReqRet);
2462 else if (RT_FAILURE(rcExp))
2463 retval = true;
2464 else
2465 {
2466 RTUTF16 wcExp[MAX_BUF_SIZE / 2];
2467 //RTUTF16 *pwcExp = wcExp; - unused
2468 size_t cwc;
2469 for (cwc = 0; cwc == 0 || pcszExp[cwc - 1] != '\0'; ++cwc)
2470 wcExp[cwc] = pcszExp[cwc];
2471 size_t cbExp = cwc * 2;
2472 if (cbActual != cbExp)
2473 {
2474 RTTestFailed(hTest, "Returned string is the wrong size, string \"%.*ls\", size %u, expected \"%s\", size %u\n",
2475 RT_MIN(MAX_BUF_SIZE, cbActual), pc, cbActual,
2476 pcszExp, cbExp);
2477 }
2478 else
2479 {
2480 if (memcmp(pc, wcExp, cbExp) == 0)
2481 retval = true;
2482 else
2483 RTTestFailed(hTest, "Returned string \"%.*ls\" does not match expected string \"%s\"\n",
2484 MAX_BUF_SIZE, pc, pcszExp);
2485 }
2486 }
2487 }
2488 if (!retval)
2489 RTTestFailureDetails(hTest, "Expected: string \"%s\", rc %Rrc\n",
2490 pcszExp, rcExp);
2491}
2492
2493static void testStringFromVBox(RTTEST hTest, CLIPBACKEND *pCtx, const char *pcszTarget, Atom typeExp, const char *valueExp)
2494{
2495 RT_NOREF1(pCtx);
2496 bool retval = false;
2497 Atom type;
2498 XtPointer value = NULL;
2499 unsigned long length;
2500 int format;
2501 size_t lenExp = strlen(valueExp);
2502 if (clipConvertSelection(pcszTarget, &type, &value, &length, &format))
2503 {
2504 if ( type != typeExp
2505 || length != lenExp
2506 || format != 8
2507 || memcmp((const void *) value, (const void *)valueExp,
2508 lenExp))
2509 {
2510 RTTestFailed(hTest, "Bad data: type %d, (expected %d), length %u, (%u), format %d (%d), value \"%.*s\" (\"%.*s\")\n",
2511 type, typeExp, length, lenExp, format, 8,
2512 RT_MIN(length, 20), value, RT_MIN(lenExp, 20), valueExp);
2513 }
2514 else
2515 retval = true;
2516 }
2517 else
2518 RTTestFailed(hTest, "Conversion failed\n");
2519 XtFree((char *)value);
2520 if (!retval)
2521 RTTestFailureDetails(hTest, "Conversion to %s, expected \"%s\"\n",
2522 pcszTarget, valueExp);
2523}
2524
2525static void testNoX11(CLIPBACKEND *pCtx, const char *pcszTestCtx)
2526{
2527 CLIPREADCBREQ *pReq = (CLIPREADCBREQ *)&pReq;
2528 int rc = ClipRequestDataFromX11(pCtx,
2529 VBOX_SHARED_CLIPBOARD_FMT_UNICODETEXT,
2530 pReq);
2531 RTTESTI_CHECK_MSG(rc == VERR_NO_DATA, ("context: %s\n", pcszTestCtx));
2532}
2533
2534static void testStringFromVBoxFailed(RTTEST hTest, CLIPBACKEND *pCtx, const char *pcszTarget)
2535{
2536 RT_NOREF1(pCtx);
2537 Atom type;
2538 XtPointer value = NULL;
2539 unsigned long length;
2540 int format;
2541 RTTEST_CHECK_MSG(hTest, !clipConvertSelection(pcszTarget, &type, &value,
2542 &length, &format),
2543 (hTest, "Conversion to target %s, should have failed but didn't, returned type %d, length %u, format %d, value \"%.*s\"\n",
2544 pcszTarget, type, length, format, RT_MIN(length, 20),
2545 value));
2546 XtFree((char *)value);
2547}
2548
2549static void testNoSelectionOwnership(CLIPBACKEND *pCtx, const char *pcszTestCtx)
2550{
2551 RT_NOREF1(pCtx);
2552 RTTESTI_CHECK_MSG(!g_ownsSel, ("context: %s\n", pcszTestCtx));
2553}
2554
2555int main()
2556{
2557 /*
2558 * Init the runtime, test and say hello.
2559 */
2560 RTTEST hTest;
2561 int rc = RTTestInitAndCreate("tstClipboardX11", &hTest);
2562 if (rc)
2563 return rc;
2564 RTTestBanner(hTest);
2565
2566 /*
2567 * Run the test.
2568 */
2569 CLIPBACKEND *pCtx = ClipConstructX11(NULL, false);
2570 char *pc;
2571 uint32_t cbActual;
2572 CLIPREADCBREQ *pReq = (CLIPREADCBREQ *)&pReq, *pReqRet = NULL;
2573 rc = ClipStartX11(pCtx);
2574 AssertRCReturn(rc, 1);
2575
2576 /*** Utf-8 from X11 ***/
2577 RTTestSub(hTest, "reading Utf-8 from X11");
2578 /* Simple test */
2579 clipSetSelectionValues("UTF8_STRING", XA_STRING, "hello world",
2580 sizeof("hello world"), 8);
2581 testStringFromX11(hTest, pCtx, "hello world", VINF_SUCCESS);
2582 /* With an embedded carriage return */
2583 clipSetSelectionValues("text/plain;charset=UTF-8", XA_STRING,
2584 "hello\nworld", sizeof("hello\nworld"), 8);
2585 testStringFromX11(hTest, pCtx, "hello\r\nworld", VINF_SUCCESS);
2586 /* With an embedded CRLF */
2587 clipSetSelectionValues("text/plain;charset=UTF-8", XA_STRING,
2588 "hello\r\nworld", sizeof("hello\r\nworld"), 8);
2589 testStringFromX11(hTest, pCtx, "hello\r\r\nworld", VINF_SUCCESS);
2590 /* With an embedded LFCR */
2591 clipSetSelectionValues("text/plain;charset=UTF-8", XA_STRING,
2592 "hello\n\rworld", sizeof("hello\n\rworld"), 8);
2593 testStringFromX11(hTest, pCtx, "hello\r\n\rworld", VINF_SUCCESS);
2594 /* An empty string */
2595 clipSetSelectionValues("text/plain;charset=utf-8", XA_STRING, "",
2596 sizeof(""), 8);
2597 testStringFromX11(hTest, pCtx, "", VINF_SUCCESS);
2598 /* With an embedded Utf-8 character. */
2599 clipSetSelectionValues("STRING", XA_STRING,
2600 "100\xE2\x82\xAC" /* 100 Euro */,
2601 sizeof("100\xE2\x82\xAC"), 8);
2602 testStringFromX11(hTest, pCtx, "100\xE2\x82\xAC", VINF_SUCCESS);
2603 /* A non-zero-terminated string */
2604 clipSetSelectionValues("TEXT", XA_STRING,
2605 "hello world", sizeof("hello world") - 1, 8);
2606 testStringFromX11(hTest, pCtx, "hello world", VINF_SUCCESS);
2607
2608 /*** Latin1 from X11 ***/
2609 RTTestSub(hTest, "reading Latin1 from X11");
2610 /* Simple test */
2611 clipSetSelectionValues("STRING", XA_STRING, "Georges Dupr\xEA",
2612 sizeof("Georges Dupr\xEA"), 8);
2613 testLatin1FromX11(hTest, pCtx, "Georges Dupr\xEA", VINF_SUCCESS);
2614 /* With an embedded carriage return */
2615 clipSetSelectionValues("TEXT", XA_STRING, "Georges\nDupr\xEA",
2616 sizeof("Georges\nDupr\xEA"), 8);
2617 testLatin1FromX11(hTest, pCtx, "Georges\r\nDupr\xEA", VINF_SUCCESS);
2618 /* With an embedded CRLF */
2619 clipSetSelectionValues("TEXT", XA_STRING, "Georges\r\nDupr\xEA",
2620 sizeof("Georges\r\nDupr\xEA"), 8);
2621 testLatin1FromX11(hTest, pCtx, "Georges\r\r\nDupr\xEA", VINF_SUCCESS);
2622 /* With an embedded LFCR */
2623 clipSetSelectionValues("TEXT", XA_STRING, "Georges\n\rDupr\xEA",
2624 sizeof("Georges\n\rDupr\xEA"), 8);
2625 testLatin1FromX11(hTest, pCtx, "Georges\r\n\rDupr\xEA", VINF_SUCCESS);
2626 /* A non-zero-terminated string */
2627 clipSetSelectionValues("text/plain", XA_STRING,
2628 "Georges Dupr\xEA!",
2629 sizeof("Georges Dupr\xEA!") - 1, 8);
2630 testLatin1FromX11(hTest, pCtx, "Georges Dupr\xEA!", VINF_SUCCESS);
2631
2632 /*** Unknown X11 format ***/
2633 RTTestSub(hTest, "handling of an unknown X11 format");
2634 clipInvalidateFormats();
2635 clipSetSelectionValues("CLIPBOARD", XA_STRING, "Test",
2636 sizeof("Test"), 8);
2637 clipSendTargetUpdate(pCtx);
2638 RTTEST_CHECK_MSG(hTest, clipQueryFormats() == 0,
2639 (hTest, "Failed to send a format update notification\n"));
2640
2641 /*** Timeout from X11 ***/
2642 RTTestSub(hTest, "X11 timeout");
2643 clipSetSelectionValues("UTF8_STRING", XT_CONVERT_FAIL, NULL,0, 8);
2644 testStringFromX11(hTest, pCtx, "", VERR_NO_DATA);
2645
2646 /*** No data in X11 clipboard ***/
2647 RTTestSub(hTest, "a data request from an empty X11 clipboard");
2648 clipSetSelectionValues("UTF8_STRING", XA_STRING, NULL,
2649 0, 8);
2650 ClipRequestDataFromX11(pCtx, VBOX_SHARED_CLIPBOARD_FMT_UNICODETEXT,
2651 pReq);
2652 clipGetCompletedRequest(&rc, &pc, &cbActual, &pReqRet);
2653 RTTEST_CHECK_MSG(hTest, rc == VERR_NO_DATA,
2654 (hTest, "Returned %Rrc instead of VERR_NO_DATA\n",
2655 rc));
2656 RTTEST_CHECK_MSG(hTest, pReqRet == pReq,
2657 (hTest, "Wrong returned request data, expected %p, got %p\n",
2658 pReq, pReqRet));
2659
2660 /*** Ensure that VBox is notified when we return the CB to X11 ***/
2661 RTTestSub(hTest, "notification of switch to X11 clipboard");
2662 clipInvalidateFormats();
2663 clipReportEmptyX11CB(pCtx);
2664 RTTEST_CHECK_MSG(hTest, clipQueryFormats() == 0,
2665 (hTest, "Failed to send a format update (release) notification\n"));
2666
2667 /*** request for an invalid VBox format from X11 ***/
2668 RTTestSub(hTest, "a request for an invalid VBox format from X11");
2669 ClipRequestDataFromX11(pCtx, 0xffff, pReq);
2670 clipGetCompletedRequest(&rc, &pc, &cbActual, &pReqRet);
2671 RTTEST_CHECK_MSG(hTest, rc == VERR_NOT_IMPLEMENTED,
2672 (hTest, "Returned %Rrc instead of VERR_NOT_IMPLEMENTED\n",
2673 rc));
2674 RTTEST_CHECK_MSG(hTest, pReqRet == pReq,
2675 (hTest, "Wrong returned request data, expected %p, got %p\n",
2676 pReq, pReqRet));
2677
2678 /*** Targets failure from X11 ***/
2679 RTTestSub(hTest, "X11 targets conversion failure");
2680 clipSetSelectionValues("UTF8_STRING", XA_STRING, "hello world",
2681 sizeof("hello world"), 8);
2682 clipSetTargetsFailure();
2683 Atom atom = XA_STRING;
2684 long unsigned int cLen = 0;
2685 int format = 8;
2686 clipConvertX11Targets(NULL, (XtPointer) pCtx, NULL, &atom, NULL, &cLen,
2687 &format);
2688 RTTEST_CHECK_MSG(hTest, clipQueryFormats() == 0,
2689 (hTest, "Wrong targets reported: %02X\n",
2690 clipQueryFormats()));
2691
2692 /*** X11 text format conversion ***/
2693 RTTestSub(hTest, "handling of X11 selection targets");
2694 RTTEST_CHECK_MSG(hTest, clipTestTextFormatConversion(pCtx),
2695 (hTest, "failed to select the right X11 text formats\n"));
2696
2697 /*** Utf-8 from VBox ***/
2698 RTTestSub(hTest, "reading Utf-8 from VBox");
2699 /* Simple test */
2700 clipSetVBoxUtf16(pCtx, VINF_SUCCESS, "hello world",
2701 sizeof("hello world") * 2);
2702 testStringFromVBox(hTest, pCtx, "UTF8_STRING",
2703 clipGetAtom(pCtx, "UTF8_STRING"), "hello world");
2704 /* With an embedded carriage return */
2705 clipSetVBoxUtf16(pCtx, VINF_SUCCESS, "hello\r\nworld",
2706 sizeof("hello\r\nworld") * 2);
2707 testStringFromVBox(hTest, pCtx, "text/plain;charset=UTF-8",
2708 clipGetAtom(pCtx, "text/plain;charset=UTF-8"),
2709 "hello\nworld");
2710 /* With an embedded CRCRLF */
2711 clipSetVBoxUtf16(pCtx, VINF_SUCCESS, "hello\r\r\nworld",
2712 sizeof("hello\r\r\nworld") * 2);
2713 testStringFromVBox(hTest, pCtx, "text/plain;charset=UTF-8",
2714 clipGetAtom(pCtx, "text/plain;charset=UTF-8"),
2715 "hello\r\nworld");
2716 /* With an embedded CRLFCR */
2717 clipSetVBoxUtf16(pCtx, VINF_SUCCESS, "hello\r\n\rworld",
2718 sizeof("hello\r\n\rworld") * 2);
2719 testStringFromVBox(hTest, pCtx, "text/plain;charset=UTF-8",
2720 clipGetAtom(pCtx, "text/plain;charset=UTF-8"),
2721 "hello\n\rworld");
2722 /* An empty string */
2723 clipSetVBoxUtf16(pCtx, VINF_SUCCESS, "", 2);
2724 testStringFromVBox(hTest, pCtx, "text/plain;charset=utf-8",
2725 clipGetAtom(pCtx, "text/plain;charset=utf-8"), "");
2726 /* With an embedded Utf-8 character. */
2727 clipSetVBoxUtf16(pCtx, VINF_SUCCESS, "100\xE2\x82\xAC" /* 100 Euro */,
2728 10);
2729 testStringFromVBox(hTest, pCtx, "STRING",
2730 clipGetAtom(pCtx, "STRING"), "100\xE2\x82\xAC");
2731 /* A non-zero-terminated string */
2732 clipSetVBoxUtf16(pCtx, VINF_SUCCESS, "hello world",
2733 sizeof("hello world") * 2 - 2);
2734 testStringFromVBox(hTest, pCtx, "TEXT", clipGetAtom(pCtx, "TEXT"),
2735 "hello world");
2736
2737 /*** Timeout from VBox ***/
2738 RTTestSub(hTest, "reading from VBox with timeout");
2739 clipEmptyVBox(pCtx, VERR_TIMEOUT);
2740 testStringFromVBoxFailed(hTest, pCtx, "UTF8_STRING");
2741
2742 /*** No data in VBox clipboard ***/
2743 RTTestSub(hTest, "an empty VBox clipboard");
2744 clipSetSelectionValues("TEXT", XA_STRING, "", sizeof(""), 8);
2745 clipEmptyVBox(pCtx, VINF_SUCCESS);
2746 RTTEST_CHECK_MSG(hTest, g_ownsSel,
2747 (hTest, "VBox grabbed the clipboard with no data and we ignored it\n"));
2748 testStringFromVBoxFailed(hTest, pCtx, "UTF8_STRING");
2749
2750 /*** An unknown VBox format ***/
2751 RTTestSub(hTest, "reading an unknown VBox format");
2752 clipSetSelectionValues("TEXT", XA_STRING, "", sizeof(""), 8);
2753 clipSetVBoxUtf16(pCtx, VINF_SUCCESS, "", 2);
2754 ClipAnnounceFormatToX11(pCtx, 0xa0000);
2755 RTTEST_CHECK_MSG(hTest, g_ownsSel,
2756 (hTest, "VBox grabbed the clipboard with unknown data and we ignored it\n"));
2757 testStringFromVBoxFailed(hTest, pCtx, "UTF8_STRING");
2758 rc = ClipStopX11(pCtx);
2759 AssertRCReturn(rc, 1);
2760 ClipDestructX11(pCtx);
2761
2762 /*** Headless clipboard tests ***/
2763
2764 pCtx = ClipConstructX11(NULL, true);
2765 rc = ClipStartX11(pCtx);
2766 AssertRCReturn(rc, 1);
2767
2768 /*** Read from X11 ***/
2769 RTTestSub(hTest, "reading from X11, headless clipboard");
2770 /* Simple test */
2771 clipSetVBoxUtf16(pCtx, VINF_SUCCESS, "",
2772 sizeof("") * 2);
2773 clipSetSelectionValues("UTF8_STRING", XA_STRING, "hello world",
2774 sizeof("hello world"), 8);
2775 testNoX11(pCtx, "reading from X11, headless clipboard");
2776
2777 /*** Read from VBox ***/
2778 RTTestSub(hTest, "reading from VBox, headless clipboard");
2779 /* Simple test */
2780 clipEmptyVBox(pCtx, VERR_WRONG_ORDER);
2781 clipSetSelectionValues("TEXT", XA_STRING, "", sizeof(""), 8);
2782 clipSetVBoxUtf16(pCtx, VINF_SUCCESS, "hello world",
2783 sizeof("hello world") * 2);
2784 testNoSelectionOwnership(pCtx, "reading from VBox, headless clipboard");
2785
2786 rc = ClipStopX11(pCtx);
2787 AssertRCReturn(rc, 1);
2788 ClipDestructX11(pCtx);
2789
2790 return RTTestSummaryAndDestroy(hTest);
2791}
2792
2793#endif
2794
2795#ifdef SMOKETEST
2796
2797/* This is a simple test case that just starts a copy of the X11 clipboard
2798 * backend, checks the X11 clipboard and exits. If ever needed I will add an
2799 * interactive mode in which the user can read and copy to the clipboard from
2800 * the command line. */
2801
2802# include <iprt/env.h>
2803# include <iprt/test.h>
2804
2805int ClipRequestDataForX11(VBOXCLIPBOARDCONTEXT *pCtx, uint32_t u32Format, void **ppv, uint32_t *pcb)
2806{
2807 RT_NOREF4(pCtx, u32Format, ppv, pcb);
2808 return VERR_NO_DATA;
2809}
2810
2811void ClipReportX11Formats(VBOXCLIPBOARDCONTEXT *pCtx, uint32_t u32Formats)
2812{
2813 RT_NOREF2(pCtx, u32Formats);
2814}
2815
2816void ClipCompleteDataRequestFromX11(VBOXCLIPBOARDCONTEXT *pCtx, int rc, CLIPREADCBREQ *pReq, void *pv, uint32_t cb)
2817{
2818 RT_NOREF5(pCtx, rc, pReq, pv, cb);
2819}
2820
2821int main()
2822{
2823 /*
2824 * Init the runtime, test and say hello.
2825 */
2826 RTTEST hTest;
2827 int rc = RTTestInitAndCreate("tstClipboardX11Smoke", &hTest);
2828 if (rc)
2829 return rc;
2830 RTTestBanner(hTest);
2831
2832 /*
2833 * Run the test.
2834 */
2835 rc = VINF_SUCCESS;
2836 /* We can't test anything without an X session, so just return success
2837 * in that case. */
2838 if (!RTEnvExist("DISPLAY"))
2839 {
2840 RTTestPrintf(hTest, RTTESTLVL_INFO,
2841 "X11 not available, not running test\n");
2842 return RTTestSummaryAndDestroy(hTest);
2843 }
2844 CLIPBACKEND *pCtx = ClipConstructX11(NULL, false);
2845 AssertReturn(pCtx, 1);
2846 rc = ClipStartX11(pCtx);
2847 AssertRCReturn(rc, 1);
2848 /* Give the clipboard time to synchronise. */
2849 RTThreadSleep(500);
2850 rc = ClipStopX11(pCtx);
2851 AssertRCReturn(rc, 1);
2852 ClipDestructX11(pCtx);
2853 return RTTestSummaryAndDestroy(hTest);
2854}
2855
2856#endif /* SMOKETEST defined */
2857
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