VirtualBox

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

Last change on this file since 80444 was 80444, checked in by vboxsync, 5 years ago

Shared Clipboard/URI: Added protocol versioning support plus enhanced versions of existing commands (to also provide context IDs, among other stuff). So far only the host service(s) and the Windows guest is using the new(er) protocol.

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

© 2023 Oracle
ContactPrivacy policyTerms of Use