VirtualBox

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

Last change on this file since 19223 was 19220, checked in by vboxsync, 16 years ago

SharedClipboard/x11: fix a potential crash if a zero-length compound text string is read

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 65.9 KB
Line 
1/** @file
2 *
3 * Shared Clipboard:
4 * X11 backend code.
5 */
6
7/*
8 * Copyright (C) 2006-2007 Sun Microsystems, Inc.
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 * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa
19 * Clara, CA 95054 USA or visit http://www.sun.com if you need
20 * additional information or have any questions.
21 */
22
23#define LOG_GROUP LOG_GROUP_SHARED_CLIPBOARD
24
25#include <errno.h>
26
27#include <unistd.h>
28
29#ifdef RT_OS_SOLARIS
30#include <tsol/label.h>
31#endif
32
33#include <X11/Xlib.h>
34#include <X11/Xatom.h>
35#include <X11/Intrinsic.h>
36#include <X11/Shell.h>
37#include <X11/Xproto.h>
38#include <X11/StringDefs.h>
39
40#include <iprt/env.h>
41#include <iprt/mem.h>
42#include <iprt/semaphore.h>
43#include <iprt/thread.h>
44
45#include <VBox/log.h>
46
47#include <VBox/GuestHost/SharedClipboard.h>
48#include <VBox/GuestHost/clipboard-helper.h>
49#include <VBox/HostServices/VBoxClipboardSvc.h>
50
51/** Do we want to test Utf8 by disabling other text formats? */
52static bool g_testUtf8 = false;
53/** Do we want to test compount text by disabling other text formats? */
54static bool g_testCText = false;
55/** Are we currently debugging the clipboard code? */
56static bool g_debugClipboard = false;
57
58/** The different clipboard formats which we support. */
59enum CLIPFORMAT
60{
61 INVALID = 0,
62 TARGETS,
63 CTEXT,
64 UTF8
65};
66
67/** The table mapping X11 names to data formats and to the corresponding
68 * VBox clipboard formats (currently only Unicode) */
69static struct _CLIPFORMATTABLE
70{
71 /** The X11 atom name of the format (several names can match one format)
72 */
73 const char *pcszAtom;
74 /** The format corresponding to the name */
75 CLIPFORMAT enmFormat;
76 /** The corresponding VBox clipboard format */
77 uint32_t u32VBoxFormat;
78} g_aFormats[] =
79{
80 { "UTF8_STRING", UTF8, VBOX_SHARED_CLIPBOARD_FMT_UNICODETEXT },
81 { "text/plain;charset=UTF-8", UTF8,
82 VBOX_SHARED_CLIPBOARD_FMT_UNICODETEXT },
83 { "text/plain;charset=utf-8", UTF8,
84 VBOX_SHARED_CLIPBOARD_FMT_UNICODETEXT },
85 { "STRING", UTF8, VBOX_SHARED_CLIPBOARD_FMT_UNICODETEXT },
86 { "TEXT", UTF8, VBOX_SHARED_CLIPBOARD_FMT_UNICODETEXT },
87 { "text/plain", UTF8, VBOX_SHARED_CLIPBOARD_FMT_UNICODETEXT },
88 { "COMPOUND_TEXT", CTEXT, VBOX_SHARED_CLIPBOARD_FMT_UNICODETEXT }
89};
90
91/** Global context information used by the X11 clipboard backend */
92struct _VBOXCLIPBOARDCONTEXTX11
93{
94 /** Opaque data structure describing the front-end. */
95 VBOXCLIPBOARDCONTEXT *pFrontend;
96 /** The X Toolkit application context structure */
97 XtAppContext appContext;
98
99 /** We have a separate thread to wait for Window and Clipboard events */
100 RTTHREAD thread;
101 /** The X Toolkit widget which we use as our clipboard client. It is never made visible. */
102 Widget widget;
103
104 /** Does VBox currently own the clipboard? If so, we don't need to poll
105 * X11 for supported formats. */
106 bool fOwnsClipboard;
107
108 /** What is the best text format X11 has to offer? INVALID for none. */
109 CLIPFORMAT X11TextFormat;
110 /** Atom corresponding to the X11 text format */
111 Atom atomX11TextFormat;
112 /** What is the best bitmap format X11 has to offer? INVALID for none.
113 */
114 CLIPFORMAT X11BitmapFormat;
115 /** Atom corresponding to the X11 Bitmap format */
116 Atom atomX11BitmapFormat;
117 /** What formats does VBox have on offer? */
118 uint32_t vboxFormats;
119 /** Windows hosts and guests cache the clipboard data they receive.
120 * Since we have no way of knowing whether their cache is still valid,
121 * we always send a "data changed" message after a successful transfer
122 * to invalidate it. */
123 bool notifyVBox;
124 /** Cache of the last unicode data that we received */
125 void *pvUnicodeCache;
126 /** Size of the unicode data in the cache */
127 uint32_t cbUnicodeCache;
128 /** When we wish the clipboard to exit, we have to wake up the event
129 * loop. We do this by writing into a pipe. This end of the pipe is
130 * the end that another thread can write to. */
131 int wakeupPipeWrite;
132 /** The reader end of the pipe */
133 int wakeupPipeRead;
134};
135
136/** The number of simultaneous instances we support. For all normal purposes
137 * we should never need more than one. For the testcase it is convenient to
138 * have a second instance that the first can interact with in order to have
139 * a more controlled environment. */
140enum { CLIPBOARD_NUM_CONTEXTS = 20 };
141
142/** Array of structures for mapping Xt widgets to context pointers. We
143 * need this because the widget clipboard callbacks do not pass user data. */
144static struct {
145 /** The widget we want to associate the context with */
146 Widget widget;
147 /** The context associated with the widget */
148 VBOXCLIPBOARDCONTEXTX11 *pCtx;
149} g_contexts[CLIPBOARD_NUM_CONTEXTS];
150
151/** Register a new X11 clipboard context. */
152static int vboxClipboardAddContext(VBOXCLIPBOARDCONTEXTX11 *pCtx)
153{
154 bool found = false;
155 AssertReturn(pCtx != NULL, VERR_INVALID_PARAMETER);
156 Widget widget = pCtx->widget;
157 AssertReturn(widget != NULL, VERR_INVALID_PARAMETER);
158 for (unsigned i = 0; i < RT_ELEMENTS(g_contexts); ++i)
159 {
160 AssertReturn( (g_contexts[i].widget != widget)
161 && (g_contexts[i].pCtx != pCtx), VERR_WRONG_ORDER);
162 if (g_contexts[i].widget == NULL && !found)
163 {
164 AssertReturn(g_contexts[i].pCtx == NULL, VERR_INTERNAL_ERROR);
165 g_contexts[i].widget = widget;
166 g_contexts[i].pCtx = pCtx;
167 found = true;
168 }
169 }
170 return found ? VINF_SUCCESS : VERR_OUT_OF_RESOURCES;
171}
172
173/** Unregister an X11 clipboard context. */
174static void vboxClipboardRemoveContext(VBOXCLIPBOARDCONTEXTX11 *pCtx)
175{
176 bool found = false;
177 AssertReturnVoid(pCtx != NULL);
178 Widget widget = pCtx->widget;
179 AssertReturnVoid(widget != NULL);
180 for (unsigned i = 0; i < RT_ELEMENTS(g_contexts); ++i)
181 {
182 Assert(!found || g_contexts[i].widget != widget);
183 if (g_contexts[i].widget == widget)
184 {
185 Assert(g_contexts[i].pCtx != NULL);
186 g_contexts[i].widget = NULL;
187 g_contexts[i].pCtx = NULL;
188 found = true;
189 }
190 }
191}
192
193/** Find an X11 clipboard context. */
194static VBOXCLIPBOARDCONTEXTX11 *vboxClipboardFindContext(Widget widget)
195{
196 AssertReturn(widget != NULL, NULL);
197 for (unsigned i = 0; i < RT_ELEMENTS(g_contexts); ++i)
198 {
199 if (g_contexts[i].widget == widget)
200 {
201 Assert(g_contexts[i].pCtx != NULL);
202 return g_contexts[i].pCtx;
203 }
204 }
205 return NULL;
206}
207
208/** Convert an atom name string to an X11 atom, looking it up in a cache
209 * before asking the server */
210static Atom clipGetAtom(Widget widget, const char *pszName)
211{
212 AssertPtrReturn(pszName, None);
213 Atom retval = None;
214 XrmValue nameVal, atomVal;
215 nameVal.addr = (char *) pszName;
216 nameVal.size = strlen(pszName);
217 atomVal.size = sizeof(Atom);
218 atomVal.addr = (char *) &retval;
219 XtConvertAndStore(widget, XtRString, &nameVal, XtRAtom, &atomVal);
220 return retval;
221}
222
223/* Are we actually connected to the X server? */
224static bool g_fHaveX11;
225
226static int vboxClipboardWriteUtf16LE(VBOXCLIPBOARDCONTEXTX11 *pCtx,
227 PRTUTF16 pu16SrcText,
228 size_t cwSrcLen,
229 void *pv, unsigned cb,
230 uint32_t *pcbActual)
231{
232 size_t cwDestLen;
233 PRTUTF16 pu16DestText = reinterpret_cast<PRTUTF16>(pv);
234 int rc = VINF_SUCCESS;
235 /* Check how much longer will the converted text will be. */
236 rc = vboxClipboardUtf16GetWinSize(pu16SrcText, cwSrcLen, &cwDestLen);
237 if (RT_SUCCESS(rc) && (cb < cwDestLen * 2))
238 {
239 /* Not enough buffer space provided - report the amount needed. */
240 LogFlowFunc (("guest buffer too small: size %d bytes, needed %d. Returning.\n",
241 cb, cwDestLen * 2));
242 *pcbActual = cwDestLen * 2;
243 rc = VERR_BUFFER_OVERFLOW;
244 }
245 /* Convert the text. */
246 if (RT_SUCCESS(rc))
247 rc = vboxClipboardUtf16LinToWin(pu16SrcText, cwSrcLen, pu16DestText, cb / 2);
248 if (RT_SUCCESS(rc))
249 {
250 LogFlowFunc (("converted string is %.*ls\n", cwDestLen, pu16DestText));
251 *pcbActual = cwDestLen * 2;
252 }
253 return rc;
254}
255
256/**
257 * Convert the UTF-8 text obtained from the X11 clipboard to UTF-16LE with
258 * Windows EOLs, place it in the buffer supplied and signal that data has
259 * arrived.
260 *
261 * @param pValue Source UTF-8 text
262 * @param cbSourceLen Length in 8-bit bytes of the source text
263 * @param pv Where to store the converted data
264 * @param cb Length in bytes of the buffer pointed to by pv
265 * @param pcbActual Where to store the size of the converted data
266 * @param pClient Pointer to the client context structure
267 * @note X11 backend code, called from the Xt callback when we wish to read
268 * the X11 clipboard.
269 */
270static int vboxClipboardGetUtf8FromX11(VBOXCLIPBOARDCONTEXTX11 *pCtx,
271 XtPointer pValue, unsigned cbSrcLen,
272 void *pv, unsigned cb,
273 uint32_t *pcbActual)
274{
275 size_t cwSrcLen;
276 char *pu8SrcText = reinterpret_cast<char *>(pValue);
277 PRTUTF16 pu16SrcText = NULL;
278
279 LogFlowFunc (("converting Utf-8 to Utf-16LE. cbSrcLen=%d, cb=%d, pu8SrcText=%.*s\n",
280 cbSrcLen, cb, cbSrcLen, pu8SrcText));
281 *pcbActual = 0; /* Only set this to the right value on success. */
282 /* First convert the UTF8 to UTF16 */
283 int rc = RTStrToUtf16Ex(pu8SrcText, cbSrcLen, &pu16SrcText, 0, &cwSrcLen);
284 if (RT_SUCCESS(rc))
285 rc = vboxClipboardWriteUtf16LE(pCtx, pu16SrcText, cwSrcLen,
286 pv, cb, pcbActual);
287 XtFree(reinterpret_cast<char *>(pValue));
288 RTUtf16Free(pu16SrcText);
289 LogFlowFunc(("Returning %Rrc\n", rc));
290 return rc;
291}
292
293/**
294 * Convert the COMPOUND_TEXT obtained from the X11 clipboard to UTF-16LE with
295 * Windows EOLs, place it in the buffer supplied and signal that data has
296 * arrived.
297 *
298 * @param pValue Source COMPOUND_TEXT
299 * @param cbSourceLen Length in 8-bit bytes of the source text
300 * @param pv Where to store the converted data
301 * @param cb Length in bytes of the buffer pointed to by pv
302 * @param pcbActual Where to store the size of the converted data
303 * @param pClient Pointer to the client context structure
304 * @note X11 backend code, called from the Xt callback when we wish to read
305 * the X11 clipboard.
306 */
307static int vboxClipboardGetCTextFromX11(VBOXCLIPBOARDCONTEXTX11 *pCtx,
308 XtPointer pValue, unsigned cbSrcLen,
309 void *pv, unsigned cb,
310 uint32_t *pcbActual)
311{
312 size_t cwSrcLen;
313 char **ppu8SrcText = NULL;
314 PRTUTF16 pu16SrcText = NULL;
315 XTextProperty property;
316 int rc = VINF_SUCCESS;
317 int cProps;
318
319 LogFlowFunc (("converting COMPOUND TEXT to Utf-16LE. cbSrcLen=%d, cb=%d, pu8SrcText=%.*s\n",
320 cbSrcLen, cb, cbSrcLen, reinterpret_cast<char *>(pValue)));
321 *pcbActual = 0; /* Only set this to the right value on success. */
322 /** @todo quick fix for 2.2, do this properly. */
323 if (cbSrcLen == 0)
324 {
325 XtFree(reinterpret_cast<char *>(pValue));
326 if (cb < 2)
327 return VERR_BUFFER_OVERFLOW;
328 *(PRTUTF16) pv = 0;
329 *pcbActual = 2;
330 return VINF_SUCCESS;
331 }
332 /* First convert the compound text to Utf8 */
333 property.value = reinterpret_cast<unsigned char *>(pValue);
334 property.encoding = clipGetAtom(pCtx->widget, "COMPOUND_TEXT");
335 property.format = 8;
336 property.nitems = cbSrcLen;
337#ifdef RT_OS_SOLARIS
338 int xrc = XmbTextPropertyToTextList(XtDisplay(pCtx->widget), &property,
339 &ppu8SrcText, &cProps);
340#else
341 int xrc = Xutf8TextPropertyToTextList(XtDisplay(pCtx->widget),
342 &property, &ppu8SrcText, &cProps);
343#endif
344 XtFree(reinterpret_cast<char *>(pValue));
345 if (xrc < 0)
346 switch(xrc)
347 {
348 case XNoMemory:
349 rc = VERR_NO_MEMORY;
350 break;
351 case XLocaleNotSupported:
352 case XConverterNotFound:
353 rc = VERR_NOT_SUPPORTED;
354 break;
355 default:
356 rc = VERR_UNRESOLVED_ERROR;
357 }
358 /* Now convert the UTF8 to UTF16 */
359 if (RT_SUCCESS(rc))
360 rc = RTStrToUtf16Ex(*ppu8SrcText, cbSrcLen, &pu16SrcText, 0, &cwSrcLen);
361 if (RT_SUCCESS(rc))
362 rc = vboxClipboardWriteUtf16LE(pCtx, pu16SrcText, cwSrcLen,
363 pv, cb, pcbActual);
364 if (ppu8SrcText != NULL)
365 XFreeStringList(ppu8SrcText);
366 RTUtf16Free(pu16SrcText);
367 LogFlowFunc(("Returning %Rrc\n", rc));
368 return rc;
369}
370
371/**
372 * Convert the Latin1 text obtained from the X11 clipboard to UTF-16LE with
373 * Windows EOLs, place it in the buffer supplied and signal that data has
374 * arrived.
375 *
376 * @param pValue Source Latin1 text
377 * @param cbSourceLen Length in 8-bit bytes of the source text
378 * @param pv Where to store the converted data
379 * @param cb Length in bytes of the buffer pointed to by cb
380 * @param pcbActual Where to store the size of the converted data
381 * @param pClient Pointer to the client context structure
382 * @note X11 backend code, called from the Xt callback when we wish to read
383 * the X11 clipboard.
384 */
385static int vboxClipboardGetLatin1FromX11(VBOXCLIPBOARDCONTEXTX11 *pCtx,
386 XtPointer pValue,
387 unsigned cbSourceLen, void *pv,
388 unsigned cb, uint32_t *pcbActual)
389{
390 unsigned cwDestLen = cbSourceLen + 1;
391 char *pu8SourceText = reinterpret_cast<char *>(pValue);
392 PRTUTF16 pu16DestText = reinterpret_cast<PRTUTF16>(pv);
393 int rc = VINF_SUCCESS;
394
395 LogFlowFunc (("converting Latin1 to Utf-16LE. Original is %.*s\n",
396 cbSourceLen, pu8SourceText));
397 *pcbActual = 0; /* Only set this to the right value on success. */
398 for (unsigned i = 0; i < cbSourceLen; i++)
399 if (pu8SourceText[i] == LINEFEED)
400 ++cwDestLen;
401 if (cb < cwDestLen * 2)
402 {
403 /* Not enough buffer space provided - report the amount needed. */
404 LogFlowFunc (("guest buffer too small: size %d bytes\n", cb));
405 *pcbActual = cwDestLen * 2;
406 rc = VERR_BUFFER_OVERFLOW;
407 }
408 if (RT_SUCCESS(rc))
409 {
410 for (unsigned i = 0, j = 0; i < cbSourceLen; ++i, ++j)
411 if (pu8SourceText[i] != LINEFEED)
412 pu16DestText[j] = pu8SourceText[i]; /* latin1 < utf-16LE */
413 else
414 {
415 pu16DestText[j] = CARRIAGERETURN;
416 ++j;
417 pu16DestText[j] = LINEFEED;
418 }
419 pu16DestText[cwDestLen - 1] = 0;
420 *pcbActual = cwDestLen * 2;
421 LogFlowFunc (("converted text is %.*ls\n", cwDestLen, pu16DestText));
422 }
423 XtFree(reinterpret_cast<char *>(pValue));
424 LogFlowFunc(("Returning %Rrc\n", rc));
425 return rc;
426}
427
428/**
429 * Convert the text obtained from the X11 clipboard to UTF-16LE with Windows
430 * EOLs, place it in the buffer supplied and signal that data has arrived.
431 * @note X11 backend code, callback for XtGetSelectionValue, for use when
432 * the X11 clipboard contains a text format we understand.
433 */
434static void vboxClipboardGetDataFromX11(Widget, XtPointer pClientData,
435 Atom * /* selection */,
436 Atom *atomType,
437 XtPointer pValue,
438 long unsigned int *pcLen,
439 int *piFormat)
440{
441 VBOXCLIPBOARDREQUEST *pRequest
442 = reinterpret_cast<VBOXCLIPBOARDREQUEST *>(pClientData);
443 VBOXCLIPBOARDCONTEXTX11 *pCtx = pRequest->pCtx;
444 if (pCtx->fOwnsClipboard == true)
445 {
446 /* We don't want to request data from ourselves! */
447 pRequest->rc = VERR_NO_DATA;
448 RTSemEventSignal(pRequest->finished);
449 return;
450 }
451 pRequest->rc = VINF_SUCCESS;
452 LogFlowFunc(("pClientData=%p, *pcLen=%lu, *piFormat=%d\n", pClientData,
453 *pcLen, *piFormat));
454 LogFlowFunc(("pCtx->X11TextFormat=%d, pRequest->cb=%d\n",
455 pCtx->X11TextFormat, pRequest->cb));
456 unsigned cTextLen = (*pcLen) * (*piFormat) / 8;
457 /* The X Toolkit may have failed to get the clipboard selection for us. */
458 if (*atomType == XT_CONVERT_FAIL) /* timeout */
459 {
460 pRequest->rc = VERR_TIMEOUT;
461 RTSemEventSignal(pRequest->finished);
462 return;
463 }
464 /* The clipboard selection may have changed before we could get it. */
465 if (NULL == pValue)
466 {
467 pRequest->rc = VERR_NO_DATA;
468 RTSemEventSignal(pRequest->finished);
469 return;
470 }
471 /* In which format is the clipboard data? */
472 switch (pCtx->X11TextFormat)
473 {
474 case CTEXT:
475 pRequest->rc = vboxClipboardGetCTextFromX11(pCtx, pValue, cTextLen,
476 pRequest->pv,
477 pRequest->cb,
478 pRequest->pcbActual);
479 RTSemEventSignal(pRequest->finished);
480 break;
481 case UTF8:
482 {
483 /* If we are given broken Utf-8, we treat it as Latin1. Is this acceptable? */
484 size_t cStringLen;
485 char *pu8SourceText = reinterpret_cast<char *>(pValue);
486
487 if ((pCtx->X11TextFormat == UTF8)
488 && (RTStrUniLenEx(pu8SourceText, *pcLen, &cStringLen) == VINF_SUCCESS))
489 {
490 pRequest->rc = vboxClipboardGetUtf8FromX11(pCtx, pValue,
491 cTextLen,
492 pRequest->pv,
493 pRequest->cb,
494 pRequest->pcbActual);
495 RTSemEventSignal(pRequest->finished);
496 break;
497 }
498 else
499 {
500 pRequest->rc = vboxClipboardGetLatin1FromX11(pCtx, pValue,
501 cTextLen,
502 pRequest->pv,
503 pRequest->cb,
504 pRequest->pcbActual);
505 RTSemEventSignal(pRequest->finished);
506 break;
507 }
508 }
509 default:
510 LogFunc (("bad target format\n"));
511 XtFree(reinterpret_cast<char *>(pValue));
512 pRequest->rc = VERR_INVALID_PARAMETER;
513 RTSemEventSignal(pRequest->finished);
514 return;
515 }
516 pCtx->notifyVBox = true;
517}
518
519/**
520 * Notify the host clipboard about the data formats we support, based on the
521 * "targets" (available data formats) information obtained from the X11
522 * clipboard.
523 * @note X11 backend code, callback for XtGetSelectionValue, called when we
524 * poll for available targets.
525 */
526static void vboxClipboardGetTargetsFromX11(Widget,
527 XtPointer pClientData,
528 Atom * /* selection */,
529 Atom *atomType,
530 XtPointer pValue,
531 long unsigned int *pcLen,
532 int *piFormat)
533{
534 VBOXCLIPBOARDCONTEXTX11 *pCtx =
535 reinterpret_cast<VBOXCLIPBOARDCONTEXTX11 *>(pClientData);
536 Atom *atomTargets = reinterpret_cast<Atom *>(pValue);
537 unsigned cAtoms = *pcLen;
538 CLIPFORMAT enmBestTarget = INVALID;
539 CLIPFORMAT enmRequiredTarget = INVALID;
540 Atom atomBestTarget = None;
541
542 Log3 (("%s: called\n", __PRETTY_FUNCTION__));
543 if ( (*atomType == XT_CONVERT_FAIL) /* timeout */
544 || (pCtx->fOwnsClipboard == true) /* VBox currently owns the
545 * clipboard */
546 )
547 {
548 pCtx->atomX11TextFormat = None;
549 pCtx->X11TextFormat = INVALID;
550 return;
551 }
552
553 /* Debugging stuff */
554 if (g_testUtf8)
555 enmRequiredTarget = UTF8;
556 else if (g_testCText)
557 enmRequiredTarget = CTEXT;
558
559 for (unsigned i = 0; i < cAtoms; ++i)
560 {
561 for (unsigned j = 0; j < RT_ELEMENTS(g_aFormats); ++j)
562 {
563 Atom formatAtom = clipGetAtom(pCtx->widget,
564 g_aFormats[j].pcszAtom);
565 if (atomTargets[i] == formatAtom)
566 {
567 if ( enmBestTarget < g_aFormats[j].enmFormat
568 /* debugging stuff */
569 && ( enmRequiredTarget == INVALID
570 || enmRequiredTarget == g_aFormats[j].enmFormat))
571 {
572 enmBestTarget = g_aFormats[j].enmFormat;
573 atomBestTarget = formatAtom;
574 }
575 break;
576 }
577 }
578 if (g_debugClipboard)
579 {
580 char *szAtomName = XGetAtomName(XtDisplay(pCtx->widget),
581 atomTargets[i]);
582 if (szAtomName != 0)
583 {
584 Log2 (("%s: the host offers target %s\n", __PRETTY_FUNCTION__,
585 szAtomName));
586 XFree(szAtomName);
587 }
588 }
589 }
590 pCtx->atomX11TextFormat = atomBestTarget;
591 if ((enmBestTarget != pCtx->X11TextFormat) || (pCtx->notifyVBox == true))
592 {
593 uint32_t u32Formats = 0;
594 if (g_debugClipboard)
595 {
596 if (atomBestTarget != None)
597 {
598 char *szAtomName = XGetAtomName(XtDisplay(pCtx->widget),
599 atomBestTarget);
600 Log2 (("%s: switching to host text target %s. Available targets are:\n",
601 __PRETTY_FUNCTION__, szAtomName));
602 XFree(szAtomName);
603 }
604 else
605 Log2(("%s: no supported host text target found. Available targets are:\n",
606 __PRETTY_FUNCTION__));
607 for (unsigned i = 0; i < cAtoms; ++i)
608 {
609 char *szAtomName = XGetAtomName(XtDisplay(pCtx->widget),
610 atomTargets[i]);
611 if (szAtomName != 0)
612 {
613 Log2 (("%s: %s\n", __PRETTY_FUNCTION__, szAtomName));
614 XFree(szAtomName);
615 }
616 }
617 }
618 pCtx->X11TextFormat = enmBestTarget;
619 if (enmBestTarget != INVALID)
620 u32Formats |= VBOX_SHARED_CLIPBOARD_FMT_UNICODETEXT;
621 VBoxX11ClipboardReportX11Formats(pCtx->pFrontend, u32Formats);
622 pCtx->notifyVBox = false;
623 }
624 XtFree(reinterpret_cast<char *>(pValue));
625}
626
627/**
628 * This timer callback is called every 200ms to check the contents of the X11
629 * clipboard.
630 * @note X11 backend code, callback for XtAppAddTimeOut, recursively
631 * re-armed.
632 * @todo Use the XFIXES extension to check for new clipboard data when
633 * available.
634 */
635static void vboxClipboardPollX11ForTargets(XtPointer pUserData,
636 XtIntervalId * /* hTimerId */)
637{
638 VBOXCLIPBOARDCONTEXTX11 *pCtx =
639 reinterpret_cast<VBOXCLIPBOARDCONTEXTX11 *>(pUserData);
640 Log3 (("%s: called\n", __PRETTY_FUNCTION__));
641 /* Get the current clipboard contents if we don't own it ourselves */
642 if (!pCtx->fOwnsClipboard)
643 {
644 Log3 (("%s: requesting the targets that the host clipboard offers\n",
645 __PRETTY_FUNCTION__));
646 XtGetSelectionValue(pCtx->widget, clipGetAtom(pCtx->widget, "CLIPBOARD"),
647 clipGetAtom(pCtx->widget, "TARGETS"),
648 vboxClipboardGetTargetsFromX11, pCtx,
649 CurrentTime);
650 }
651 /* Re-arm our timer */
652 XtAppAddTimeOut(pCtx->appContext, 200 /* ms */,
653 vboxClipboardPollX11ForTargets, pCtx);
654}
655
656/**
657 * The main loop of our clipboard reader.
658 * @note X11 backend code.
659 */
660static int vboxClipboardThread(RTTHREAD self, void *pvUser)
661{
662 LogRel(("Shared clipboard: starting host clipboard thread\n"));
663
664 VBOXCLIPBOARDCONTEXTX11 *pCtx =
665 reinterpret_cast<VBOXCLIPBOARDCONTEXTX11 *>(pvUser);
666 /* Set up a timer to poll the host clipboard */
667 XtAppAddTimeOut(pCtx->appContext, 200 /* ms */,
668 vboxClipboardPollX11ForTargets, pCtx);
669
670 while (XtAppGetExitFlag(pCtx->appContext) == FALSE)
671 XtAppProcessEvent(pCtx->appContext, XtIMAll);
672 LogRel(("Shared clipboard: host clipboard thread terminated successfully\n"));
673 return VINF_SUCCESS;
674}
675
676/** X11 specific uninitialisation for the shared clipboard.
677 * @note X11 backend code.
678 */
679static void vboxClipboardUninitX11(VBOXCLIPBOARDCONTEXTX11 *pCtx)
680{
681 AssertPtrReturnVoid(pCtx);
682 if (pCtx->widget)
683 {
684 /* Valid widget + invalid appcontext = bug. But don't return yet. */
685 AssertPtr(pCtx->appContext);
686 vboxClipboardRemoveContext(pCtx);
687 XtDestroyWidget(pCtx->widget);
688 }
689 pCtx->widget = NULL;
690 if (pCtx->appContext)
691 XtDestroyApplicationContext(pCtx->appContext);
692 pCtx->appContext = NULL;
693 if (pCtx->wakeupPipeRead != 0)
694 close(pCtx->wakeupPipeRead);
695 if (pCtx->wakeupPipeWrite != 0)
696 close(pCtx->wakeupPipeWrite);
697 pCtx->wakeupPipeRead = 0;
698 pCtx->wakeupPipeWrite = 0;
699}
700
701/** Worker function for stopping the clipboard which runs on the event
702 * thread. */
703static void vboxClipboardStopWorker(XtPointer pUserData, int * /* source */,
704 XtInputId * /* id */)
705{
706
707 VBOXCLIPBOARDCONTEXTX11 *pCtx = (VBOXCLIPBOARDCONTEXTX11 *)pUserData;
708
709 /* This might mean that we are getting stopped twice. */
710 Assert(pCtx->widget != NULL);
711
712 /* Set the termination flag to tell the Xt event loop to exit. We
713 * reiterate that any outstanding requests from the X11 event loop to
714 * the VBox part *must* have returned before we do this. */
715 XtAppSetExitFlag(pCtx->appContext);
716 pCtx->fOwnsClipboard = false;
717 pCtx->X11TextFormat = INVALID;
718 pCtx->X11BitmapFormat = INVALID;
719}
720
721/** X11 specific initialisation for the shared clipboard.
722 * @note X11 backend code.
723 */
724static int vboxClipboardInitX11 (VBOXCLIPBOARDCONTEXTX11 *pCtx)
725{
726 /* Create a window and make it a clipboard viewer. */
727 int cArgc = 0;
728 char *pcArgv = 0;
729 int rc = VINF_SUCCESS;
730 Display *pDisplay;
731
732 /* Make sure we are thread safe */
733 XtToolkitThreadInitialize();
734 /* Set up the Clipbard application context and main window. We call all these functions
735 directly instead of calling XtOpenApplication() so that we can fail gracefully if we
736 can't get an X11 display. */
737 XtToolkitInitialize();
738 pCtx->appContext = XtCreateApplicationContext();
739 pDisplay = XtOpenDisplay(pCtx->appContext, 0, 0, "VBoxClipboard", 0, 0, &cArgc, &pcArgv);
740 if (NULL == pDisplay)
741 {
742 LogRel(("Shared clipboard: failed to connect to the host clipboard - the window system may not be running.\n"));
743 rc = VERR_NOT_SUPPORTED;
744 }
745 if (RT_SUCCESS(rc))
746 {
747 pCtx->widget = XtVaAppCreateShell(0, "VBoxClipboard", applicationShellWidgetClass, pDisplay,
748 XtNwidth, 1, XtNheight, 1, NULL);
749 if (NULL == pCtx->widget)
750 {
751 LogRel(("Shared clipboard: failed to construct the X11 window for the host clipboard manager.\n"));
752 rc = VERR_NO_MEMORY;
753 }
754 else
755 rc = vboxClipboardAddContext(pCtx);
756 }
757 if (RT_SUCCESS(rc))
758 {
759 XtSetMappedWhenManaged(pCtx->widget, false);
760 XtRealizeWidget(pCtx->widget);
761 }
762 /* Create the pipes */
763 int pipes[2];
764 if (!pipe(pipes))
765 {
766 pCtx->wakeupPipeRead = pipes[0];
767 pCtx->wakeupPipeWrite = pipes[1];
768 XtAppAddInput(pCtx->appContext, pCtx->wakeupPipeRead,
769 (XtPointer) XtInputReadMask, vboxClipboardStopWorker,
770 (XtPointer) pCtx);
771 }
772 else
773 rc = RTErrConvertFromErrno(errno);
774 if (RT_FAILURE(rc))
775 vboxClipboardUninitX11(pCtx);
776 return rc;
777}
778
779/**
780 * Construct the X11 backend of the shared clipboard.
781 * @note X11 backend code
782 */
783VBOXCLIPBOARDCONTEXTX11 *VBoxX11ClipboardConstructX11
784 (VBOXCLIPBOARDCONTEXT *pFrontend)
785{
786 int rc;
787
788 VBOXCLIPBOARDCONTEXTX11 *pCtx = (VBOXCLIPBOARDCONTEXTX11 *)
789 RTMemAllocZ(sizeof(VBOXCLIPBOARDCONTEXTX11));
790 if (pCtx && !RTEnvGet("DISPLAY"))
791 {
792 /*
793 * If we don't find the DISPLAY environment variable we assume that
794 * we are not connected to an X11 server. Don't actually try to do
795 * this then, just fail silently and report success on every call.
796 * This is important for VBoxHeadless.
797 */
798 LogRelFunc(("X11 DISPLAY variable not set -- disabling shared clipboard\n"));
799 g_fHaveX11 = false;
800 return pCtx;
801 }
802
803 if (RTEnvGet("VBOX_CBTEST_UTF8"))
804 {
805 g_testUtf8 = true;
806 LogRel(("Host clipboard: testing Utf8\n"));
807 }
808 else if (RTEnvGet("VBOX_CBTEST_CTEXT"))
809 {
810 g_testCText = true;
811 LogRel(("Host clipboard: testing compound text\n"));
812 }
813 else if (RTEnvGet("VBOX_CBDEBUG"))
814 {
815 g_debugClipboard = true;
816 LogRel(("Host clipboard: enabling additional debugging output\n"));
817 }
818
819 g_fHaveX11 = true;
820
821 LogRel(("Initializing X11 clipboard backend\n"));
822 if (pCtx)
823 pCtx->pFrontend = pFrontend;
824 return pCtx;
825}
826
827/**
828 * Destruct the shared clipboard X11 backend.
829 * @note X11 backend code
830 */
831void VBoxX11ClipboardDestructX11(VBOXCLIPBOARDCONTEXTX11 *pCtx)
832{
833 /*
834 * Immediately return if we are not connected to the host X server.
835 */
836 if (!g_fHaveX11)
837 return;
838
839 /* We set this to NULL when the event thread exits. It really should
840 * have exited at this point, when we are about to unload the code from
841 * memory. */
842 Assert(pCtx->widget == NULL);
843}
844
845/**
846 * Announce to the X11 backend that we are ready to start.
847 */
848int VBoxX11ClipboardStartX11(VBOXCLIPBOARDCONTEXTX11 *pCtx)
849{
850 int rc = VINF_SUCCESS;
851 LogFlowFunc(("\n"));
852 /*
853 * Immediately return if we are not connected to the host X server.
854 */
855 if (!g_fHaveX11)
856 return VINF_SUCCESS;
857
858 rc = vboxClipboardInitX11(pCtx);
859 if (RT_SUCCESS(rc))
860 {
861 rc = RTThreadCreate(&pCtx->thread, vboxClipboardThread, pCtx, 0,
862 RTTHREADTYPE_IO, RTTHREADFLAGS_WAITABLE, "SHCLIP");
863 if (RT_FAILURE(rc))
864 LogRel(("Failed to initialise the shared clipboard X11 backend.\n"));
865 }
866 if (RT_SUCCESS(rc))
867 {
868 pCtx->fOwnsClipboard = false;
869 pCtx->notifyVBox = true;
870 }
871 return rc;
872}
873
874/** String written to the wakeup pipe. */
875#define WAKE_UP_STRING "WakeUp!"
876/** Length of the string written. */
877#define WAKE_UP_STRING_LEN ( sizeof(WAKE_UP_STRING) - 1 )
878
879/**
880 * Shut down the shared clipboard X11 backend.
881 * @note X11 backend code
882 * @note Any requests from this object to get clipboard data from VBox
883 * *must* have completed or aborted before we are called, as
884 * otherwise the X11 event loop will still be waiting for the request
885 * to return and will not be able to terminate.
886 */
887int VBoxX11ClipboardStopX11(VBOXCLIPBOARDCONTEXTX11 *pCtx)
888{
889 int rc, rcThread;
890 unsigned count = 0;
891 /*
892 * Immediately return if we are not connected to the host X server.
893 */
894 if (!g_fHaveX11)
895 return VINF_SUCCESS;
896
897 LogRelFunc(("stopping the shared clipboard X11 backend\n"));
898 /* Write to the "stop" pipe */
899 rc = write(pCtx->wakeupPipeWrite, WAKE_UP_STRING, WAKE_UP_STRING_LEN);
900 do
901 {
902 rc = RTThreadWait(pCtx->thread, 1000, &rcThread);
903 ++count;
904 Assert(RT_SUCCESS(rc) || ((VERR_TIMEOUT == rc) && (count != 5)));
905 } while ((VERR_TIMEOUT == rc) && (count < 300));
906 if (RT_SUCCESS(rc))
907 AssertRC(rcThread);
908 else
909 LogRelFunc(("rc=%Rrc\n", rc));
910 vboxClipboardUninitX11(pCtx);
911 LogFlowFunc(("returning %Rrc.\n", rc));
912 return rc;
913}
914
915/**
916 * Satisfy a request from X11 for clipboard targets supported by VBox.
917 *
918 * @returns true if we successfully convert the data to the format
919 * requested, false otherwise.
920 *
921 * @param atomTypeReturn The type of the data we are returning
922 * @param pValReturn A pointer to the data we are returning. This
923 * should be set to memory allocated by XtMalloc,
924 * which will be freed later by the Xt toolkit.
925 * @param pcLenReturn The length of the data we are returning
926 * @param piFormatReturn The format (8bit, 16bit, 32bit) of the data we are
927 * returning
928 * @note X11 backend code, called by the XtOwnSelection callback.
929 */
930static Boolean vboxClipboardConvertTargetsForX11(VBOXCLIPBOARDCONTEXTX11
931 *pCtx,
932 Atom *atomTypeReturn,
933 XtPointer *pValReturn,
934 unsigned long *pcLenReturn,
935 int *piFormatReturn)
936{
937 unsigned uListSize = RT_ELEMENTS(g_aFormats);
938 Atom *atomTargets = reinterpret_cast<Atom *>(XtMalloc((uListSize + 3) * sizeof(Atom)));
939 unsigned cTargets = 0;
940
941 LogFlowFunc (("called\n"));
942 for (unsigned i = 0; i < uListSize; ++i)
943 {
944 if ( (pCtx->vboxFormats & VBOX_SHARED_CLIPBOARD_FMT_UNICODETEXT)
945 && ( g_aFormats[i].u32VBoxFormat
946 == VBOX_SHARED_CLIPBOARD_FMT_UNICODETEXT))
947 {
948 atomTargets[cTargets] = clipGetAtom(pCtx->widget,
949 g_aFormats[i].pcszAtom);
950 ++cTargets;
951 }
952 }
953 atomTargets[cTargets] = clipGetAtom(pCtx->widget, "TARGETS");
954 atomTargets[cTargets + 1] = clipGetAtom(pCtx->widget, "MULTIPLE");
955 atomTargets[cTargets + 2] = clipGetAtom(pCtx->widget, "TIMESTAMP");
956 if (g_debugClipboard)
957 {
958 for (unsigned i = 0; i < cTargets + 3; i++)
959 {
960 char *szAtomName = XGetAtomName(XtDisplay(pCtx->widget), atomTargets[i]);
961 if (szAtomName != 0)
962 {
963 Log2 (("%s: returning target %s\n", __PRETTY_FUNCTION__,
964 szAtomName));
965 XFree(szAtomName);
966 }
967 else
968 {
969 Log(("%s: invalid atom %d in the list!\n", __PRETTY_FUNCTION__,
970 atomTargets[i]));
971 }
972 }
973 }
974 *atomTypeReturn = XA_ATOM;
975 *pValReturn = reinterpret_cast<XtPointer>(atomTargets);
976 *pcLenReturn = cTargets + 3;
977 *piFormatReturn = 32;
978 return true;
979}
980
981/** This is a wrapper around VBoxX11ClipboardReadVBoxData that will cache the
982 * data returned. This is unfortunately necessary, because if the other side
983 * of the shared clipboard is also an X11 system, it may send a format
984 * announcement message every time its clipboard is read, for reasons that
985 * are explained elsewhere. Unfortunately, some applications on our side
986 * like to read the clipboard several times in short succession in different
987 * formats. This can fail if it collides with a format announcement message.
988 * @todo any ideas about how to do this better are welcome.
989 */
990static int vboxClipboardReadVBoxData (VBOXCLIPBOARDCONTEXTX11 *pCtx,
991 uint32_t u32Format, void **ppv,
992 uint32_t *pcb)
993{
994 int rc = VINF_SUCCESS;
995 LogFlowFunc(("pCtx=%p, u32Format=%02X, ppv=%p, pcb=%p\n", pCtx,
996 u32Format, ppv, pcb));
997 if (u32Format == VBOX_SHARED_CLIPBOARD_FMT_UNICODETEXT)
998 {
999 if (pCtx->pvUnicodeCache == NULL)
1000 rc = VBoxX11ClipboardReadVBoxData(pCtx->pFrontend, u32Format,
1001 &pCtx->pvUnicodeCache,
1002 &pCtx->cbUnicodeCache);
1003 if (RT_SUCCESS(rc))
1004 {
1005 *ppv = RTMemDup(pCtx->pvUnicodeCache, pCtx->cbUnicodeCache);
1006 *pcb = pCtx->cbUnicodeCache;
1007 if (*ppv == NULL)
1008 rc = VERR_NO_MEMORY;
1009 }
1010 }
1011 else
1012 rc = VBoxX11ClipboardReadVBoxData(pCtx->pFrontend, u32Format,
1013 ppv, pcb);
1014 LogFlowFunc(("returning %Rrc\n", rc));
1015 if (RT_SUCCESS(rc))
1016 LogFlowFunc(("*ppv=%.*ls, *pcb=%u\n", *pcb, *ppv, *pcb));
1017 return rc;
1018}
1019
1020/**
1021 * Satisfy a request from X11 to convert the clipboard text to Utf8. We
1022 * return non-zero terminated text.
1023 * @todo that works, but it is bad. Change it to return zero-terminated
1024 * text.
1025 *
1026 * @returns true if we successfully convert the data to the format
1027 * requested, false otherwise.
1028 *
1029 * @param atomTypeReturn Where to store the atom for the type of the data
1030 * we are returning
1031 * @param pValReturn Where to store the pointer to the data we are
1032 * returning. This should be to memory allocated by
1033 * XtMalloc, which will be freed by the Xt toolkit
1034 * later.
1035 * @param pcLenReturn Where to store the length of the data we are
1036 * returning
1037 * @param piFormatReturn Where to store the bit width (8, 16, 32) of the
1038 * data we are returning
1039 * @note X11 backend code, called by the callback for XtOwnSelection.
1040 */
1041static Boolean vboxClipboardConvertToUtf8ForX11(VBOXCLIPBOARDCONTEXTX11
1042 *pCtx,
1043 Atom *atomTypeReturn,
1044 XtPointer *pValReturn,
1045 unsigned long *pcLenReturn,
1046 int *piFormatReturn)
1047{
1048 PRTUTF16 pu16SrcText, pu16DestText;
1049 char *pu8DestText;
1050 void *pvVBox = NULL;
1051 uint32_t cbVBox = 0;
1052 size_t cwSrcLen, cwDestLen, cbDestLen;
1053 int rc;
1054
1055 LogFlowFunc (("called\n"));
1056 /* Read the clipboard data from the guest. */
1057 rc = vboxClipboardReadVBoxData(pCtx,
1058 VBOX_SHARED_CLIPBOARD_FMT_UNICODETEXT,
1059 &pvVBox, &cbVBox);
1060 if ((rc != VINF_SUCCESS) || (cbVBox == 0))
1061 {
1062 /* If vboxClipboardReadVBoxData fails then we may be terminating */
1063 LogRelFunc (("vboxClipboardReadVBoxData returned %Rrc%s\n", rc,
1064 RT_SUCCESS(rc) ? ", cbVBox == 0" : ""));
1065 RTMemFree(pvVBox);
1066 return false;
1067 }
1068 pu16SrcText = reinterpret_cast<PRTUTF16>(pvVBox);
1069 cwSrcLen = cbVBox / 2;
1070 /* How long will the converted text be? */
1071 rc = vboxClipboardUtf16GetLinSize(pu16SrcText, cwSrcLen, &cwDestLen);
1072 if (RT_FAILURE(rc))
1073 {
1074 LogRelFunc (("clipboard conversion failed. vboxClipboardUtf16GetLinSize returned %Rrc. Abandoning.\n", rc));
1075 RTMemFree(pvVBox);
1076 AssertRCReturn(rc, false);
1077 }
1078 if (cwDestLen == 0)
1079 {
1080 LogFlowFunc(("received empty clipboard data from the guest, returning false.\n"));
1081 RTMemFree(pvVBox);
1082 return false;
1083 }
1084 pu16DestText = reinterpret_cast<PRTUTF16>(RTMemAlloc(cwDestLen * 2));
1085 if (pu16DestText == 0)
1086 {
1087 LogRelFunc (("failed to allocate %d bytes\n", cwDestLen * 2));
1088 RTMemFree(pvVBox);
1089 return false;
1090 }
1091 /* Convert the text. */
1092 rc = vboxClipboardUtf16WinToLin(pu16SrcText, cwSrcLen, pu16DestText, cwDestLen);
1093 if (RT_FAILURE(rc))
1094 {
1095 LogRelFunc (("clipboard conversion failed. vboxClipboardUtf16WinToLin() returned %Rrc. Abandoning.\n", rc));
1096 RTMemFree(reinterpret_cast<void *>(pu16DestText));
1097 RTMemFree(pvVBox);
1098 return false;
1099 }
1100 /* Allocate enough space, as RTUtf16ToUtf8Ex may fail if the
1101 space is too tightly calculated. */
1102 pu8DestText = XtMalloc(cwDestLen * 4);
1103 if (pu8DestText == 0)
1104 {
1105 LogRelFunc (("failed to allocate %d bytes\n", cwDestLen * 4));
1106 RTMemFree(reinterpret_cast<void *>(pu16DestText));
1107 RTMemFree(pvVBox);
1108 return false;
1109 }
1110 /* Convert the Utf16 string to Utf8. */
1111 rc = RTUtf16ToUtf8Ex(pu16DestText + 1, cwDestLen - 1, &pu8DestText, cwDestLen * 4,
1112 &cbDestLen);
1113 RTMemFree(reinterpret_cast<void *>(pu16DestText));
1114 if (RT_FAILURE(rc))
1115 {
1116 LogRelFunc (("clipboard conversion failed. RTUtf16ToUtf8Ex() returned %Rrc. Abandoning.\n", rc));
1117 XtFree(pu8DestText);
1118 RTMemFree(pvVBox);
1119 return false;
1120 }
1121 LogFlowFunc (("converted string is %.*s. Returning.\n", cbDestLen, pu8DestText));
1122 RTMemFree(pvVBox);
1123 *atomTypeReturn = clipGetAtom(pCtx->widget, "UTF8_STRING");
1124 *pValReturn = reinterpret_cast<XtPointer>(pu8DestText);
1125 *pcLenReturn = cbDestLen;
1126 *piFormatReturn = 8;
1127 return true;
1128}
1129
1130/**
1131 * Satisfy a request from X11 to convert the clipboard text to
1132 * COMPOUND_TEXT. We return non-zero terminated text.
1133 * @todo that works, but it is bad. Change it to return zero-terminated
1134 * text.
1135 *
1136 * @returns true if we successfully convert the data to the format
1137 * requested, false otherwise.
1138 *
1139 * @param atomTypeReturn Where to store the atom for the type of the data
1140 * we are returning
1141 * @param pValReturn Where to store the pointer to the data we are
1142 * returning. This should be to memory allocated by
1143 * XtMalloc, which will be freed by the Xt toolkit
1144 * later.
1145 * @param pcLenReturn Where to store the length of the data we are
1146 * returning
1147 * @param piFormatReturn Where to store the bit width (8, 16, 32) of the
1148 * data we are returning
1149 * @note X11 backend code, called by the callback for XtOwnSelection.
1150 */
1151static Boolean vboxClipboardConvertToCTextForX11(VBOXCLIPBOARDCONTEXTX11
1152 *pCtx,
1153 Atom *atomTypeReturn,
1154 XtPointer *pValReturn,
1155 unsigned long *pcLenReturn,
1156 int *piFormatReturn)
1157{
1158 PRTUTF16 pu16SrcText, pu16DestText;
1159 void *pvVBox = NULL;
1160 uint32_t cbVBox = 0;
1161 char *pu8DestText = 0;
1162 size_t cwSrcLen, cwDestLen, cbDestLen;
1163 XTextProperty property;
1164 int rc;
1165
1166 LogFlowFunc (("called\n"));
1167 /* Read the clipboard data from the guest. */
1168 rc = vboxClipboardReadVBoxData(pCtx,
1169 VBOX_SHARED_CLIPBOARD_FMT_UNICODETEXT,
1170 &pvVBox, &cbVBox);
1171 if ((rc != VINF_SUCCESS) || (cbVBox == 0))
1172 {
1173 /* If vboxClipboardReadVBoxData fails then we may be terminating */
1174 LogRelFunc (("vboxClipboardReadVBoxData returned %Rrc%s\n", rc,
1175 RT_SUCCESS(rc) ? ", cbVBox == 0" : ""));
1176 RTMemFree(pvVBox);
1177 return false;
1178 }
1179 pu16SrcText = reinterpret_cast<PRTUTF16>(pvVBox);
1180 cwSrcLen = cbVBox / 2;
1181 /* How long will the converted text be? */
1182 rc = vboxClipboardUtf16GetLinSize(pu16SrcText, cwSrcLen, &cwDestLen);
1183 if (RT_FAILURE(rc))
1184 {
1185 LogRelFunc (("clipboard conversion failed. vboxClipboardUtf16GetLinSize returned %Rrc. Abandoning.\n", rc));
1186 RTMemFree(pvVBox);
1187 AssertRCReturn(rc, false);
1188 }
1189 if (cwDestLen == 0)
1190 {
1191 LogFlowFunc(("received empty clipboard data from the guest, returning false.\n"));
1192 RTMemFree(pvVBox);
1193 return false;
1194 }
1195 pu16DestText = reinterpret_cast<PRTUTF16>(RTMemAlloc(cwDestLen * 2));
1196 if (pu16DestText == 0)
1197 {
1198 LogRelFunc (("failed to allocate %d bytes\n", cwDestLen * 2));
1199 RTMemFree(pvVBox);
1200 return false;
1201 }
1202 /* Convert the text. */
1203 rc = vboxClipboardUtf16WinToLin(pu16SrcText, cwSrcLen, pu16DestText, cwDestLen);
1204 if (RT_FAILURE(rc))
1205 {
1206 LogRelFunc (("clipboard conversion failed. vboxClipboardUtf16WinToLin() returned %Rrc. Abandoning.\n", rc));
1207 RTMemFree(reinterpret_cast<void *>(pu16DestText));
1208 RTMemFree(pvVBox);
1209 return false;
1210 }
1211 /* Convert the Utf16 string to Utf8. */
1212 rc = RTUtf16ToUtf8Ex(pu16DestText + 1, cwDestLen - 1, &pu8DestText, 0, &cbDestLen);
1213 RTMemFree(reinterpret_cast<void *>(pu16DestText));
1214 if (RT_FAILURE(rc))
1215 {
1216 LogRelFunc (("clipboard conversion failed. RTUtf16ToUtf8Ex() returned %Rrc. Abandoning.\n", rc));
1217 RTMemFree(pvVBox);
1218 return false;
1219 }
1220 /* And finally (!) convert the Utf8 text to compound text. */
1221#ifdef RT_OS_SOLARIS
1222 rc = XmbTextListToTextProperty(XtDisplay(pCtx->widget), &pu8DestText, 1,
1223 XCompoundTextStyle, &property);
1224#else
1225 rc = Xutf8TextListToTextProperty(XtDisplay(pCtx->widget), &pu8DestText, 1,
1226 XCompoundTextStyle, &property);
1227#endif
1228 RTMemFree(pu8DestText);
1229 if (rc < 0)
1230 {
1231 const char *pcReason;
1232 switch(rc)
1233 {
1234 case XNoMemory:
1235 pcReason = "out of memory";
1236 break;
1237 case XLocaleNotSupported:
1238 pcReason = "locale (Utf8) not supported";
1239 break;
1240 case XConverterNotFound:
1241 pcReason = "converter not found";
1242 break;
1243 default:
1244 pcReason = "unknown error";
1245 }
1246 LogRelFunc (("Xutf8TextListToTextProperty failed. Reason: %s\n",
1247 pcReason));
1248 RTMemFree(pvVBox);
1249 return false;
1250 }
1251 LogFlowFunc (("converted string is %s. Returning.\n", property.value));
1252 RTMemFree(pvVBox);
1253 *atomTypeReturn = property.encoding;
1254 *pValReturn = reinterpret_cast<XtPointer>(property.value);
1255 *pcLenReturn = property.nitems;
1256 *piFormatReturn = property.format;
1257 return true;
1258}
1259
1260/**
1261 * Return VBox's clipboard data for an X11 client.
1262 * @note X11 backend code, callback for XtOwnSelection
1263 */
1264static Boolean vboxClipboardConvertForX11(Widget widget, Atom *atomSelection,
1265 Atom *atomTarget,
1266 Atom *atomTypeReturn,
1267 XtPointer *pValReturn,
1268 unsigned long *pcLenReturn,
1269 int *piFormatReturn)
1270{
1271 CLIPFORMAT enmFormat = INVALID;
1272 VBOXCLIPBOARDCONTEXTX11 *pCtx = vboxClipboardFindContext(widget);
1273
1274 LogFlowFunc(("\n"));
1275 /* Drop requests that we receive too late. */
1276 if (!pCtx->fOwnsClipboard)
1277 return false;
1278 if ( (*atomSelection != clipGetAtom(pCtx->widget, "CLIPBOARD"))
1279 && (*atomSelection != clipGetAtom(pCtx->widget, "PRIMARY"))
1280 )
1281 {
1282 LogFlowFunc(("rc = false\n"));
1283 return false;
1284 }
1285 if (g_debugClipboard)
1286 {
1287 char *szAtomName = XGetAtomName(XtDisplay(pCtx->widget), *atomTarget);
1288 if (szAtomName != 0)
1289 {
1290 Log2 (("%s: request for format %s\n", __PRETTY_FUNCTION__, szAtomName));
1291 XFree(szAtomName);
1292 }
1293 else
1294 {
1295 LogFunc (("request for invalid target atom %d!\n", *atomTarget));
1296 }
1297 }
1298 if (*atomTarget == clipGetAtom(pCtx->widget, "TARGETS"))
1299 {
1300 enmFormat = TARGETS;
1301 }
1302 else
1303 {
1304 for (unsigned i = 0; i < RT_ELEMENTS(g_aFormats); ++i)
1305 {
1306 if (*atomTarget == clipGetAtom(pCtx->widget,
1307 g_aFormats[i].pcszAtom))
1308 {
1309 enmFormat = g_aFormats[i].enmFormat;
1310 break;
1311 }
1312 }
1313 }
1314 switch (enmFormat)
1315 {
1316 case TARGETS:
1317 return vboxClipboardConvertTargetsForX11(pCtx, atomTypeReturn,
1318 pValReturn, pcLenReturn,
1319 piFormatReturn);
1320 case UTF8:
1321 return vboxClipboardConvertToUtf8ForX11(pCtx, atomTypeReturn,
1322 pValReturn, pcLenReturn,
1323 piFormatReturn);
1324 case CTEXT:
1325 return vboxClipboardConvertToCTextForX11(pCtx, atomTypeReturn,
1326 pValReturn, pcLenReturn,
1327 piFormatReturn);
1328 default:
1329 LogFunc (("bad format\n"));
1330 return false;
1331 }
1332}
1333
1334/**
1335 * This is called by the X toolkit intrinsics to let us know that another
1336 * X11 client has taken the clipboard. In this case we notify VBox that
1337 * we want ownership of the clipboard.
1338 * @note X11 backend code, callback for XtOwnSelection
1339 */
1340static void vboxClipboardReturnToX11(Widget widget, Atom *)
1341{
1342 VBOXCLIPBOARDCONTEXTX11 *pCtx = vboxClipboardFindContext(widget);
1343 LogFlowFunc (("called, giving X11 clipboard ownership\n"));
1344 /* These should be set to the right values as soon as we start polling */
1345 pCtx->X11TextFormat = INVALID;
1346 pCtx->X11BitmapFormat = INVALID;
1347 pCtx->fOwnsClipboard = false;
1348 pCtx->notifyVBox = true;
1349}
1350
1351/** Structure used to pass information about formats that VBox supports */
1352typedef struct _VBOXCLIPBOARDFORMATS
1353{
1354 /** Context information for the X11 clipboard */
1355 VBOXCLIPBOARDCONTEXTX11 *pCtx;
1356 /** Formats supported by VBox */
1357 uint32_t formats;
1358} VBOXCLIPBOARDFORMATS;
1359
1360/** Worker function for VBoxX11ClipboardAnnounceVBoxFormat which runs on the
1361 * event thread. */
1362static void vboxClipboardAnnounceWorker(XtPointer pUserData,
1363 XtIntervalId * /* interval */)
1364{
1365 /* Extract and free the user data */
1366 VBOXCLIPBOARDFORMATS *pFormats = (VBOXCLIPBOARDFORMATS *)pUserData;
1367 VBOXCLIPBOARDCONTEXTX11 *pCtx = pFormats->pCtx;
1368 uint32_t u32Formats = pFormats->formats;
1369 RTMemFree(pFormats);
1370 LogFlowFunc (("u32Formats=%d\n", u32Formats));
1371 pCtx->vboxFormats = u32Formats;
1372 /* Invalidate the clipboard cache */
1373 if (pCtx->pvUnicodeCache != NULL)
1374 {
1375 RTMemFree(pCtx->pvUnicodeCache);
1376 pCtx->pvUnicodeCache = NULL;
1377 }
1378 if (u32Formats == 0)
1379 {
1380 /* This is just an automatism, not a genuine anouncement */
1381 XtDisownSelection(pCtx->widget, clipGetAtom(pCtx->widget, "CLIPBOARD"), CurrentTime);
1382 pCtx->fOwnsClipboard = false;
1383 LogFlowFunc(("returning\n"));
1384 return;
1385 }
1386 Log2 (("%s: giving the guest clipboard ownership\n", __PRETTY_FUNCTION__));
1387 if (XtOwnSelection(pCtx->widget, clipGetAtom(pCtx->widget, "CLIPBOARD"), CurrentTime,
1388 vboxClipboardConvertForX11, vboxClipboardReturnToX11,
1389 0) == True)
1390 {
1391 pCtx->fOwnsClipboard = true;
1392 /* Grab the middle-button paste selection too. */
1393 XtOwnSelection(pCtx->widget, clipGetAtom(pCtx->widget, "PRIMARY"), CurrentTime,
1394 vboxClipboardConvertForX11, NULL, 0);
1395 }
1396 else
1397 {
1398 /* Another X11 client claimed the clipboard just after us, so let it
1399 * go again. */
1400 Log2 (("%s: returning clipboard ownership to the X11\n",
1401 __PRETTY_FUNCTION__));
1402 /* VBox thinks it currently owns the clipboard, so we must notify it
1403 * as soon as we know what formats X11 has to offer. */
1404 pCtx->notifyVBox = true;
1405 pCtx->fOwnsClipboard = false;
1406 }
1407 LogFlowFunc(("returning\n"));
1408}
1409
1410/**
1411 * VBox is taking possession of the shared clipboard.
1412 *
1413 * @param u32Formats Clipboard formats the guest is offering
1414 * @note X11 backend code
1415 */
1416void VBoxX11ClipboardAnnounceVBoxFormat(VBOXCLIPBOARDCONTEXTX11 *pCtx,
1417 uint32_t u32Formats)
1418{
1419 /*
1420 * Immediately return if we are not connected to the host X server.
1421 */
1422 if (!g_fHaveX11)
1423 return;
1424 /* This must be freed by the worker callback */
1425 VBOXCLIPBOARDFORMATS *pFormats =
1426 (VBOXCLIPBOARDFORMATS *) RTMemAlloc(sizeof(VBOXCLIPBOARDFORMATS));
1427 if (pFormats != NULL) /* if it is we will soon have other problems */
1428 {
1429 pFormats->pCtx = pCtx;
1430 pFormats->formats = u32Formats;
1431 XtAppAddTimeOut(pCtx->appContext, 0, vboxClipboardAnnounceWorker,
1432 (XtPointer) pFormats);
1433 }
1434}
1435
1436/** Worker function for VBoxX11ClipboardReadX11Data which runs on the event
1437 * thread. */
1438static void vboxClipboardReadX11Worker(XtPointer pUserData,
1439 XtIntervalId * /* interval */)
1440{
1441 VBOXCLIPBOARDREQUEST *pRequest = (VBOXCLIPBOARDREQUEST *)pUserData;
1442 VBOXCLIPBOARDCONTEXTX11 *pCtx = pRequest->pCtx;
1443 LogFlowFunc (("u32Format = %d, cb = %d\n", pRequest->format,
1444 pRequest->cb));
1445
1446 int rc = VINF_SUCCESS;
1447 /* Set this to start with, just in case */
1448 *pRequest->pcbActual = 0;
1449 /* Do not continue if we already own the clipboard */
1450 if (pCtx->fOwnsClipboard == true)
1451 rc = VERR_NO_DATA; /** @todo should this be VINF? */
1452 else
1453 {
1454 /*
1455 * VBox wants to read data in the given format.
1456 */
1457 if (pRequest->format == VBOX_SHARED_CLIPBOARD_FMT_UNICODETEXT)
1458 {
1459 if (pCtx->atomX11TextFormat == None)
1460 /* VBox thinks we have data and we don't */
1461 rc = VERR_NO_DATA;
1462 else
1463 /* Send out a request for the data to the current clipboard
1464 * owner */
1465 XtGetSelectionValue(pCtx->widget, clipGetAtom(pCtx->widget, "CLIPBOARD"),
1466 pCtx->atomX11TextFormat,
1467 vboxClipboardGetDataFromX11,
1468 reinterpret_cast<XtPointer>(pRequest),
1469 CurrentTime);
1470 }
1471 else
1472 rc = VERR_NOT_IMPLEMENTED;
1473 }
1474 if (RT_FAILURE(rc))
1475 {
1476 pRequest->rc = rc;
1477 /* The clipboard callback was never scheduled, so we must signal
1478 * that the request processing is finished ourselves. */
1479 RTSemEventSignal(pRequest->finished);
1480 }
1481 LogFlowFunc(("status %Rrc\n", rc));
1482}
1483
1484/**
1485 * Called when VBox wants to read the X11 clipboard.
1486 *
1487 * @param pClient Context information about the guest VM
1488 * @param u32Format The format that the guest would like to receive the data in
1489 * @param pv Where to write the data to
1490 * @param cb The size of the buffer to write the data to
1491 * @param pcbActual Where to write the actual size of the written data
1492 * @note X11 backend code
1493 */
1494int VBoxX11ClipboardReadX11Data(VBOXCLIPBOARDCONTEXTX11 *pCtx,
1495 uint32_t u32Format, void *pv, uint32_t cb,
1496 uint32_t *pcbActual)
1497{
1498 /* Initially set the size of the data read to zero in case we fail
1499 * somewhere or X11 is not available. */
1500 *pcbActual = 0;
1501
1502 /*
1503 * Immediately return if we are not connected to the host X server.
1504 */
1505 if (!g_fHaveX11)
1506 return VINF_SUCCESS;
1507
1508 VBOXCLIPBOARDREQUEST request;
1509 request.rc = VERR_WRONG_ORDER;
1510 request.pv = pv;
1511 request.cb = cb;
1512 request.pcbActual = pcbActual;
1513 request.format = u32Format;
1514 request.pCtx = pCtx;
1515 /* The worker function will signal this when it has finished. */
1516 int rc = RTSemEventCreate(&request.finished);
1517 if (RT_SUCCESS(rc))
1518 {
1519 /* We use this to schedule a worker function on the event thread. */
1520 XtAppAddTimeOut(pCtx->appContext, 0, vboxClipboardReadX11Worker,
1521 (XtPointer) &request);
1522 rc = RTSemEventWait(request.finished, RT_INDEFINITE_WAIT);
1523 if (RT_SUCCESS(rc))
1524 rc = request.rc;
1525 RTSemEventDestroy(request.finished);
1526 }
1527 return rc;
1528}
1529
1530#ifdef TESTCASE
1531
1532#include <iprt/initterm.h>
1533#include <iprt/stream.h>
1534
1535enum { MAX_ATTEMPTS = 10 };
1536
1537static VBOXCLIPBOARDCONTEXTX11 *g_pCtxTestX11;
1538static VBOXCLIPBOARDCONTEXTX11 *g_pCtxTestVBox;
1539
1540/** Quick Utf16 string class, initialised from a Utf8 string */
1541class testUtf16String
1542{
1543 /** Stores the Utf16 data. NULL if something went wrong. */
1544 PRTUTF16 mData;
1545 /** Stores the size in bytes of the data. 0 if something went wrong. */
1546 size_t mSize;
1547public:
1548 /** Constructor
1549 * @param aString the Utf8 representation of the string data
1550 */
1551 testUtf16String(const char *aString)
1552 {
1553 int rc = RTStrToUtf16(aString, &mData);
1554 if (RT_FAILURE(rc))
1555 {
1556 mData = NULL;
1557 mSize = 0;
1558 return;
1559 }
1560 mSize = RTUtf16Len(mData) * 2 + 2;
1561 }
1562 /** Destructor */
1563 ~testUtf16String()
1564 {
1565 RTUtf16Free(mData);
1566 }
1567 /** Getter for the data */
1568 PCRTUTF16 getData() { return mData; }
1569 /** Getter for the data size */
1570 size_t getSize() { return mSize; }
1571};
1572
1573/** Parameters for a test run. */
1574static struct testData
1575{
1576 /** The string data we are offering. */
1577 testUtf16String data;
1578 /** The size of the buffer to receive the request data */
1579 uint32_t cchBuffer;
1580 /** The format we request the data in */
1581 PCRTUTF16 getData() { return data.getData(); }
1582 /** Getter for the Utf16 string data size in bytes */
1583 size_t getSize() { return data.getSize(); }
1584 /** Is the test expected to produce a buffer overflow? */
1585 bool overflow() { return cchBuffer < getSize(); }
1586} g_sTestData[] =
1587{
1588 { "Hello world\r\n", 256 },
1589 { "Goodbye world", 28 },
1590 { "", 2 },
1591 /* This should produce a buffer overflow */
1592 { "Goodbye world", 27 },
1593};
1594
1595/** Which line of the table above are we currently testing? */
1596size_t g_testNum = 0;
1597enum { MAX_TESTS = RT_ELEMENTS(g_sTestData) };
1598/** Are we doing a timeout test? Ugly, but whatever. */
1599static bool g_testTimeout = false;
1600
1601/** @todo the current code can't test the following conditions:
1602 * * the X11 clipboard offers non-Utf8 text
1603 * * the X11 clipboard offers invalid Utf8 text
1604 */
1605/** @todo stress test our context store code? Probably not important... */
1606
1607int VBoxX11ClipboardReadVBoxData(VBOXCLIPBOARDCONTEXT *pCtx,
1608 uint32_t u32Format, void **ppv,
1609 uint32_t *pcb)
1610{
1611 LogFlowFunc(("pCtx = %p\n", pCtx));
1612 /* This should only ever be called for the second "test" clipboard. */
1613 AssertReturn(pCtx == (VBOXCLIPBOARDCONTEXT *)g_pCtxTestX11
1614 /* Magic cookie */, VERR_WRONG_ORDER);
1615 /* Timeout test hack */
1616 if (g_testTimeout)
1617 {
1618 RTThreadSleep(200);
1619 /* Try to return data after we have been timed out. */
1620 *ppv = RTMemDup((const void *) "Scribblings", sizeof("Scribblings"));
1621 *pcb = sizeof("Scribblings");
1622 LogFlowFunc(("sleep finished, returning\n"));
1623 return VINF_SUCCESS;
1624 }
1625 /* Sanity. */
1626 AssertReturn(g_testNum < MAX_TESTS, VERR_WRONG_ORDER);
1627 testData *pData = &g_sTestData[g_testNum];
1628 AssertPtrReturn(pData->getData(), VERR_NO_MEMORY);
1629 AssertReturn(pData->getSize(), VERR_WRONG_ORDER);
1630 void *retval = RTMemDup((const void *) pData->getData(),
1631 pData->getSize());
1632 AssertPtrReturn(retval, VERR_NO_MEMORY);
1633 *ppv = (void *) retval;
1634 *pcb = pData->getSize();
1635 LogFlowFunc(("returning\n"));
1636 return VINF_SUCCESS;
1637}
1638
1639/** As long as our test is running, we grab back the clipboard if X11 ever
1640 * tries to take it away. I hope we don't end up with a tug of war... */
1641void VBoxX11ClipboardReportX11Formats(VBOXCLIPBOARDCONTEXT *pCtx,
1642 uint32_t u32Formats)
1643{
1644 LogFlowFunc(("pCtx = %p\n", pCtx));
1645 if (pCtx == (VBOXCLIPBOARDCONTEXT *)g_pCtxTestX11) /* Magic cookie */
1646 VBoxX11ClipboardAnnounceVBoxFormat(g_pCtxTestVBox,
1647 VBOX_SHARED_CLIPBOARD_FMT_UNICODETEXT);
1648}
1649
1650/** Initial tests that can be done while the clipboard contents are still
1651 * invalid.
1652 * @returns boolean success value
1653 * @note prints status information to stdout
1654 */
1655bool testInvalid(void)
1656{
1657 bool fSuccess = true;
1658 char pc[256];
1659 uint32_t cbActual;
1660 RTPrintf("tstClipboardX11: TESTING a request for an invalid data format\n");
1661 int rc = VBoxX11ClipboardReadX11Data(g_pCtxTestX11, 0xffff, (void *) pc,
1662 sizeof(pc), &cbActual);
1663 RTPrintf("Returned %Rrc - %s\n", rc,
1664 rc == VERR_NOT_IMPLEMENTED ? "SUCCESS" : "FAILURE");
1665 if (rc != VERR_NOT_IMPLEMENTED)
1666 fSuccess = false;
1667 RTPrintf("tstClipboardX11: TESTING a request for data from an empty clipboard\n");
1668 VBoxX11ClipboardAnnounceVBoxFormat(g_pCtxTestX11,
1669 VBOX_SHARED_CLIPBOARD_FMT_UNICODETEXT);
1670 VBoxX11ClipboardAnnounceVBoxFormat(g_pCtxTestX11,
1671 0);
1672 rc = VBoxX11ClipboardReadX11Data(g_pCtxTestX11,
1673 VBOX_SHARED_CLIPBOARD_FMT_UNICODETEXT,
1674 (void *) pc, sizeof(pc), &cbActual);
1675 RTPrintf("Returned %Rrc - %s\n", rc,
1676 rc == VERR_NO_DATA ? "SUCCESS" : "FAILURE");
1677 if (rc != VERR_NO_DATA)
1678 fSuccess = false;
1679 return fSuccess;
1680}
1681
1682/** Tests an entry in the table above.
1683 * @returns boolean success value
1684 * @note prints status information to stdout
1685 */
1686bool testEntry(testData *pData)
1687{
1688 bool fSuccess = false;
1689 char pc[256];
1690 uint32_t cbActual;
1691 for (int i = 0; (i < MAX_ATTEMPTS) && !fSuccess; ++i)
1692 {
1693 VBoxX11ClipboardAnnounceVBoxFormat(g_pCtxTestVBox,
1694 VBOX_SHARED_CLIPBOARD_FMT_UNICODETEXT);
1695 int rc = VBoxX11ClipboardReadX11Data(g_pCtxTestX11,
1696 VBOX_SHARED_CLIPBOARD_FMT_UNICODETEXT,
1697 (void *) pc, sizeof(pc),
1698 &cbActual);
1699 AssertRCReturn(rc, 1);
1700 /* Did we expect and get an overflow? */
1701 if ( RT_SUCCESS(rc)
1702 && (cbActual == pData->getSize())
1703 && pData->overflow())
1704 fSuccess = true;
1705 /* Did we expect a string and get it? */
1706 else if ( RT_SUCCESS(rc)
1707 && (memcmp(pc, pData->getData(),
1708 pData->getSize()) == 0))
1709 fSuccess = true;
1710 else
1711 RTThreadSleep(50);
1712 if (fSuccess)
1713 RTPrintf("text %ls, %sretval %Rrc - SUCCESS\n",
1714 pData->getData(),
1715 pData->overflow() ? "buffer overflow as expected, "
1716 : "", rc);
1717 else
1718 RTPrintf("text %ls, retval %Rrc, attempt %d of %d\n",
1719 pData->getData(), rc, i + 1,
1720 MAX_ATTEMPTS);
1721 }
1722 if (!fSuccess)
1723 RTPrintf("FAILURE. Last string obtained was %.*lS\n",
1724 RT_MIN(cbActual / 2, 20), pc);
1725 return fSuccess;
1726}
1727
1728int main()
1729{
1730 int rc = VINF_SUCCESS;
1731 int status = 0;
1732 g_debugClipboard = true;
1733 /* We can't test anything without an X session, so just return success
1734 * in that case. */
1735 if (!RTEnvGet("DISPLAY"))
1736 return 0;
1737 RTR3Init();
1738 g_pCtxTestX11 = VBoxX11ClipboardConstructX11(NULL);
1739 g_pCtxTestVBox =
1740 VBoxX11ClipboardConstructX11((VBOXCLIPBOARDCONTEXT *)g_pCtxTestX11);
1741 rc = VBoxX11ClipboardStartX11(g_pCtxTestVBox);
1742 AssertRCReturn(rc, 1);
1743 bool fSuccess = false;
1744 /* Test a request for an invalid data format and data from an empty
1745 * clipboard */
1746 for (unsigned i = 0; (i < MAX_ATTEMPTS) && !fSuccess; ++i)
1747 {
1748 rc = VBoxX11ClipboardStartX11(g_pCtxTestX11);
1749 AssertRCReturn(rc, 1);
1750 fSuccess = testInvalid();
1751 VBoxX11ClipboardStopX11(g_pCtxTestX11);
1752 if (!fSuccess)
1753 {
1754 RTPrintf("attempt %d of %d\n", i + 1, MAX_ATTEMPTS + 1);
1755 RTThreadSleep(50);
1756 }
1757 }
1758 if (!fSuccess)
1759 status = 1;
1760 rc = VBoxX11ClipboardStartX11(g_pCtxTestX11);
1761 AssertRCReturn(rc, 1);
1762 /* Claim the clipboard and make sure we get it */
1763 VBoxX11ClipboardAnnounceVBoxFormat(g_pCtxTestVBox,
1764 VBOX_SHARED_CLIPBOARD_FMT_UNICODETEXT);
1765 for (unsigned i = 0;
1766 i < 255
1767 && g_pCtxTestX11->X11TextFormat == INVALID;
1768 ++i)
1769 RTThreadSleep(50);
1770 AssertReturn(g_pCtxTestX11->X11TextFormat != INVALID, 1);
1771 /* Do general purpose clipboard tests */
1772 for (int i = 0; i < 2; ++i)
1773 {
1774 g_testUtf8 = (i == 0);
1775 g_testCText = (i == 1);
1776 RTPrintf("tstClipboardX11: TESTING sending and receiving of %s\n",
1777 i == 0 ? "Utf-8" : "COMPOUND TEXT");
1778 for (g_testNum = 0; g_testNum < MAX_TESTS; ++g_testNum)
1779 {
1780 if (!testEntry(&g_sTestData[g_testNum]))
1781 status = 1;
1782 }
1783 }
1784 /* Finally test timeouts. */
1785 XtAppSetSelectionTimeout(g_pCtxTestX11->appContext, 10);
1786 RTPrintf("tstClipboardX11: TESTING the clipboard timeout\n");
1787 rc = VINF_SUCCESS;
1788 g_testTimeout = true;
1789 for (unsigned i = 0; (i < MAX_ATTEMPTS) && (rc != VERR_TIMEOUT); ++i)
1790 {
1791 char pc[256];
1792 uint32_t cbActual;
1793 VBoxX11ClipboardAnnounceVBoxFormat(g_pCtxTestVBox,
1794 VBOX_SHARED_CLIPBOARD_FMT_UNICODETEXT);
1795 rc = VBoxX11ClipboardReadX11Data(g_pCtxTestX11,
1796 VBOX_SHARED_CLIPBOARD_FMT_UNICODETEXT,
1797 (void *) pc, sizeof(pc), &cbActual);
1798 AssertMsg( RT_SUCCESS(rc)
1799 || (rc == VERR_TIMEOUT)
1800 || (rc == VERR_NO_DATA),
1801 ("rc = %Rrc\n", rc));
1802 if (rc == VERR_TIMEOUT)
1803 RTPrintf("SUCCESS\n");
1804 else
1805 {
1806 RTPrintf("Attempt %d of %d\n", i + 1, MAX_ATTEMPTS);
1807 RTThreadSleep(50);
1808 }
1809 }
1810 if (rc != VERR_TIMEOUT)
1811 status = 1;
1812 rc = VBoxX11ClipboardStopX11(g_pCtxTestX11);
1813 AssertRCReturn(rc, 1);
1814 rc = VBoxX11ClipboardStopX11(g_pCtxTestVBox);
1815 AssertRCReturn(rc, 1);
1816 VBoxX11ClipboardDestructX11(g_pCtxTestX11);
1817 VBoxX11ClipboardDestructX11(g_pCtxTestVBox);
1818 return status;
1819}
1820
1821#endif /* TESTCASE defined */
Note: See TracBrowser for help on using the repository browser.

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