VirtualBox

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

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

HostServices and GuestHost/X11 clipboard: lots of changes, see defect 3621 comment 41

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