VirtualBox

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

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

GuestHost/x11 clipboard: replaced the Xt main loop with a custom one which exits when we want it to

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 67.0 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 while (XtAppGetExitFlag(pCtx->appContext) == FALSE)
655 XtAppProcessEvent(pCtx->appContext, XtIMAll);
656 pCtx->formatList.clear();
657 LogRel(("Shared clipboard: host clipboard thread terminated successfully\n"));
658 return VINF_SUCCESS;
659}
660
661/** X11 specific uninitialisation for the shared clipboard.
662 * @note X11 backend code.
663 */
664static void vboxClipboardUninitX11(VBOXCLIPBOARDCONTEXTX11 *pCtx)
665{
666 AssertPtrReturnVoid(pCtx);
667 if (pCtx->widget)
668 {
669 /* Valid widget + invalid appcontext = bug. But don't return yet. */
670 AssertPtr(pCtx->appContext);
671 vboxClipboardRemoveContext(pCtx);
672 XtDestroyWidget(pCtx->widget);
673 }
674 pCtx->widget = NULL;
675 if (pCtx->appContext)
676 XtDestroyApplicationContext(pCtx->appContext);
677 pCtx->appContext = NULL;
678 if (pCtx->wakeupPipeRead != 0)
679 close(pCtx->wakeupPipeRead);
680 if (pCtx->wakeupPipeWrite != 0)
681 close(pCtx->wakeupPipeWrite);
682 pCtx->wakeupPipeRead = 0;
683 pCtx->wakeupPipeWrite = 0;
684}
685
686/** Worker function for stopping the clipboard which runs on the event
687 * thread. */
688static void vboxClipboardStopWorker(XtPointer pUserData, int * /* source */,
689 XtInputId * /* id */)
690{
691
692 VBOXCLIPBOARDCONTEXTX11 *pCtx = (VBOXCLIPBOARDCONTEXTX11 *)pUserData;
693
694 /* This might mean that we are getting stopped twice. */
695 Assert(pCtx->widget != NULL);
696
697 /* Set the termination flag to tell the Xt event loop to exit. We
698 * reiterate that any outstanding requests from the X11 event loop to
699 * the VBox part *must* have returned before we do this. */
700 XtAppSetExitFlag(pCtx->appContext);
701 pCtx->fOwnsClipboard = false;
702 pCtx->X11TextFormat = INVALID;
703 pCtx->X11BitmapFormat = INVALID;
704}
705
706/** X11 specific initialisation for the shared clipboard.
707 * @note X11 backend code.
708 */
709static int vboxClipboardInitX11 (VBOXCLIPBOARDCONTEXTX11 *pCtx)
710{
711 /* Create a window and make it a clipboard viewer. */
712 int cArgc = 0;
713 char *pcArgv = 0;
714 int rc = VINF_SUCCESS;
715 Display *pDisplay;
716
717 /* Make sure we are thread safe */
718 XtToolkitThreadInitialize();
719 /* Set up the Clipbard application context and main window. We call all these functions
720 directly instead of calling XtOpenApplication() so that we can fail gracefully if we
721 can't get an X11 display. */
722 XtToolkitInitialize();
723 pCtx->appContext = XtCreateApplicationContext();
724 pDisplay = XtOpenDisplay(pCtx->appContext, 0, 0, "VBoxClipboard", 0, 0, &cArgc, &pcArgv);
725 if (NULL == pDisplay)
726 {
727 LogRel(("Shared clipboard: failed to connect to the host clipboard - the window system may not be running.\n"));
728 rc = VERR_NOT_SUPPORTED;
729 }
730 if (RT_SUCCESS(rc))
731 {
732 pCtx->widget = XtVaAppCreateShell(0, "VBoxClipboard", applicationShellWidgetClass, pDisplay,
733 XtNwidth, 1, XtNheight, 1, NULL);
734 if (NULL == pCtx->widget)
735 {
736 LogRel(("Shared clipboard: failed to construct the X11 window for the host clipboard manager.\n"));
737 rc = VERR_NO_MEMORY;
738 }
739 else
740 rc = vboxClipboardAddContext(pCtx);
741 }
742 if (RT_SUCCESS(rc))
743 {
744 XtSetMappedWhenManaged(pCtx->widget, false);
745 XtRealizeWidget(pCtx->widget);
746
747 /* Get hold of the atoms which we need */
748 pCtx->atomClipboard = XInternAtom(XtDisplay(pCtx->widget), "CLIPBOARD", false /* only_if_exists */);
749 pCtx->atomPrimary = XInternAtom(XtDisplay(pCtx->widget), "PRIMARY", false);
750 pCtx->atomTargets = XInternAtom(XtDisplay(pCtx->widget), "TARGETS", false);
751 pCtx->atomMultiple = XInternAtom(XtDisplay(pCtx->widget), "MULTIPLE", false);
752 pCtx->atomTimestamp = XInternAtom(XtDisplay(pCtx->widget), "TIMESTAMP", false);
753 pCtx->atomUtf8 = XInternAtom(XtDisplay(pCtx->widget), "UTF_STRING", false);
754 /* And build up the vector of supported formats */
755 pCtx->atomCText = XInternAtom(XtDisplay(pCtx->widget), "COMPOUND_TEXT", false);
756 /* And build up the vector of supported formats */
757 if (!g_testCText)
758 {
759 vboxClipboardAddFormat(pCtx, "UTF8_STRING", UTF8,
760 VBOX_SHARED_CLIPBOARD_FMT_UNICODETEXT);
761 vboxClipboardAddFormat(pCtx, "text/plain;charset=UTF-8", UTF8,
762 VBOX_SHARED_CLIPBOARD_FMT_UNICODETEXT);
763 vboxClipboardAddFormat(pCtx, "text/plain;charset=utf-8", UTF8,
764 VBOX_SHARED_CLIPBOARD_FMT_UNICODETEXT);
765 vboxClipboardAddFormat(pCtx, "STRING", UTF8,
766 VBOX_SHARED_CLIPBOARD_FMT_UNICODETEXT);
767 vboxClipboardAddFormat(pCtx, "TEXT", UTF8,
768 VBOX_SHARED_CLIPBOARD_FMT_UNICODETEXT);
769 vboxClipboardAddFormat(pCtx, "text/plain", UTF8,
770 VBOX_SHARED_CLIPBOARD_FMT_UNICODETEXT);
771}
772 if (!g_testUtf8)
773 vboxClipboardAddFormat(pCtx, "COMPOUND_TEXT", CTEXT,
774 VBOX_SHARED_CLIPBOARD_FMT_UNICODETEXT);
775 }
776 /* Create the pipes */
777 int pipes[2];
778 if (!pipe(pipes))
779 {
780 pCtx->wakeupPipeRead = pipes[0];
781 pCtx->wakeupPipeWrite = pipes[1];
782 XtAppAddInput(pCtx->appContext, pCtx->wakeupPipeRead,
783 (XtPointer) XtInputReadMask, vboxClipboardStopWorker,
784 (XtPointer) pCtx);
785 }
786 else
787 rc = RTErrConvertFromErrno(errno);
788 if (RT_FAILURE(rc))
789 vboxClipboardUninitX11(pCtx);
790 return rc;
791}
792
793/**
794 * Construct the X11 backend of the shared clipboard.
795 * @note X11 backend code
796 */
797VBOXCLIPBOARDCONTEXTX11 *VBoxX11ClipboardConstructX11
798 (VBOXCLIPBOARDCONTEXT *pFrontend)
799{
800 int rc;
801
802 VBOXCLIPBOARDCONTEXTX11 *pCtx = (VBOXCLIPBOARDCONTEXTX11 *)
803 RTMemAllocZ(sizeof(VBOXCLIPBOARDCONTEXTX11));
804 if (pCtx && !RTEnvGet("DISPLAY"))
805 {
806 /*
807 * If we don't find the DISPLAY environment variable we assume that
808 * we are not connected to an X11 server. Don't actually try to do
809 * this then, just fail silently and report success on every call.
810 * This is important for VBoxHeadless.
811 */
812 LogRelFunc(("X11 DISPLAY variable not set -- disabling shared clipboard\n"));
813 g_fHaveX11 = false;
814 return pCtx;
815 }
816
817 if (RTEnvGet("VBOX_CBTEST_UTF8"))
818 {
819 g_testUtf8 = true;
820 LogRel(("Host clipboard: testing Utf8\n"));
821 }
822 else if (RTEnvGet("VBOX_CBTEST_CTEXT"))
823 {
824 g_testCText = true;
825 LogRel(("Host clipboard: testing compound text\n"));
826 }
827 else if (RTEnvGet("VBOX_CBDEBUG"))
828 {
829 g_debugClipboard = true;
830 LogRel(("Host clipboard: enabling additional debugging output\n"));
831 }
832
833 g_fHaveX11 = true;
834
835 LogRel(("Initializing X11 clipboard backend\n"));
836 if (pCtx)
837 pCtx->pFrontend = pFrontend;
838 return pCtx;
839}
840
841/**
842 * Destruct the shared clipboard X11 backend.
843 * @note X11 backend code
844 */
845void VBoxX11ClipboardDestructX11(VBOXCLIPBOARDCONTEXTX11 *pCtx)
846{
847 /*
848 * Immediately return if we are not connected to the host X server.
849 */
850 if (!g_fHaveX11)
851 return;
852
853 /* We set this to NULL when the event thread exits. It really should
854 * have exited at this point, when we are about to unload the code from
855 * memory. */
856 Assert(pCtx->widget == NULL);
857}
858
859/**
860 * Announce to the X11 backend that we are ready to start.
861 */
862int VBoxX11ClipboardStartX11(VBOXCLIPBOARDCONTEXTX11 *pCtx)
863{
864 int rc = VINF_SUCCESS;
865 LogFlowFunc(("\n"));
866 /*
867 * Immediately return if we are not connected to the host X server.
868 */
869 if (!g_fHaveX11)
870 return VINF_SUCCESS;
871
872 rc = vboxClipboardInitX11(pCtx);
873 if (RT_SUCCESS(rc))
874 {
875 rc = RTThreadCreate(&pCtx->thread, vboxClipboardThread, pCtx, 0,
876 RTTHREADTYPE_IO, RTTHREADFLAGS_WAITABLE, "SHCLIP");
877 if (RT_FAILURE(rc))
878 LogRel(("Failed to initialise the shared clipboard X11 backend.\n"));
879 }
880 if (RT_SUCCESS(rc))
881 {
882 pCtx->fOwnsClipboard = false;
883 pCtx->notifyVBox = true;
884 }
885 return rc;
886}
887
888/** String written to the wakeup pipe. */
889#define WAKE_UP_STRING "WakeUp!"
890/** Length of the string written. */
891#define WAKE_UP_STRING_LEN ( sizeof(WAKE_UP_STRING) - 1 )
892
893/**
894 * Shut down the shared clipboard X11 backend.
895 * @note X11 backend code
896 * @note Any requests from this object to get clipboard data from VBox
897 * *must* have completed or aborted before we are called, as
898 * otherwise the X11 event loop will still be waiting for the request
899 * to return and will not be able to terminate.
900 */
901int VBoxX11ClipboardStopX11(VBOXCLIPBOARDCONTEXTX11 *pCtx)
902{
903 int rc, rcThread;
904 unsigned count = 0;
905 /*
906 * Immediately return if we are not connected to the host X server.
907 */
908 if (!g_fHaveX11)
909 return VINF_SUCCESS;
910
911 LogRelFunc(("stopping the shared clipboard X11 backend\n"));
912 /* Write to the "stop" pipe */
913 rc = write(pCtx->wakeupPipeWrite, WAKE_UP_STRING, WAKE_UP_STRING_LEN);
914 do
915 {
916 rc = RTThreadWait(pCtx->thread, 1000, &rcThread);
917 ++count;
918 Assert(RT_SUCCESS(rc) || ((VERR_TIMEOUT == rc) && (count != 5)));
919 } while ((VERR_TIMEOUT == rc) && (count < 300));
920 if (RT_SUCCESS(rc))
921 AssertRC(rcThread);
922 else
923 LogRelFunc(("rc=%Rrc\n", rc));
924 vboxClipboardUninitX11(pCtx);
925 LogFlowFunc(("returning %Rrc.\n", rc));
926 return rc;
927}
928
929/**
930 * Satisfy a request from X11 for clipboard targets supported by VBox.
931 *
932 * @returns true if we successfully convert the data to the format
933 * requested, false otherwise.
934 *
935 * @param atomTypeReturn The type of the data we are returning
936 * @param pValReturn A pointer to the data we are returning. This
937 * should be set to memory allocated by XtMalloc,
938 * which will be freed later by the Xt toolkit.
939 * @param pcLenReturn The length of the data we are returning
940 * @param piFormatReturn The format (8bit, 16bit, 32bit) of the data we are
941 * returning
942 * @note X11 backend code, called by the XtOwnSelection callback.
943 */
944static Boolean vboxClipboardConvertTargetsForX11(VBOXCLIPBOARDCONTEXTX11
945 *pCtx,
946 Atom *atomTypeReturn,
947 XtPointer *pValReturn,
948 unsigned long *pcLenReturn,
949 int *piFormatReturn)
950{
951 unsigned uListSize = pCtx->formatList.size();
952 Atom *atomTargets = reinterpret_cast<Atom *>(XtMalloc((uListSize + 3) * sizeof(Atom)));
953 unsigned cTargets = 0;
954
955 LogFlowFunc (("called\n"));
956 for (unsigned i = 0; i < uListSize; ++i)
957 {
958 if ( ((pCtx->vboxFormats & VBOX_SHARED_CLIPBOARD_FMT_UNICODETEXT) != 0)
959 && ( pCtx->formatList[i].guestFormat
960 == VBOX_SHARED_CLIPBOARD_FMT_UNICODETEXT))
961 {
962 atomTargets[cTargets] = pCtx->formatList[i].atom;
963 ++cTargets;
964 }
965 }
966 atomTargets[cTargets] = pCtx->atomTargets;
967 atomTargets[cTargets + 1] = pCtx->atomMultiple;
968 atomTargets[cTargets + 2] = pCtx->atomTimestamp;
969 if (g_debugClipboard)
970 {
971 for (unsigned i = 0; i < cTargets + 3; i++)
972 {
973 char *szAtomName = XGetAtomName(XtDisplay(pCtx->widget), atomTargets[i]);
974 if (szAtomName != 0)
975 {
976 Log2 (("%s: returning target %s\n", __PRETTY_FUNCTION__,
977 szAtomName));
978 XFree(szAtomName);
979 }
980 else
981 {
982 Log(("%s: invalid atom %d in the list!\n", __PRETTY_FUNCTION__,
983 atomTargets[i]));
984 }
985 }
986 }
987 *atomTypeReturn = XA_ATOM;
988 *pValReturn = reinterpret_cast<XtPointer>(atomTargets);
989 *pcLenReturn = cTargets + 3;
990 *piFormatReturn = 32;
991 return true;
992}
993
994/** This is a wrapper around VBoxX11ClipboardReadVBoxData that will cache the
995 * data returned. This is unfortunately necessary, because if the other side
996 * of the shared clipboard is also an X11 system, it may send a format
997 * announcement message every time its clipboard is read, for reasons that
998 * are explained elsewhere. Unfortunately, some applications on our side
999 * like to read the clipboard several times in short succession in different
1000 * formats. This can fail if it collides with a format announcement message.
1001 * @todo any ideas about how to do this better are welcome.
1002 */
1003static int vboxClipboardReadVBoxData (VBOXCLIPBOARDCONTEXTX11 *pCtx,
1004 uint32_t u32Format, void **ppv,
1005 uint32_t *pcb)
1006{
1007 int rc = VINF_SUCCESS;
1008 LogFlowFunc(("pCtx=%p, u32Format=%02X, ppv=%p, pcb=%p\n", pCtx,
1009 u32Format, ppv, pcb));
1010 if (u32Format == VBOX_SHARED_CLIPBOARD_FMT_UNICODETEXT)
1011 {
1012 if (pCtx->pvUnicodeCache == NULL)
1013 rc = VBoxX11ClipboardReadVBoxData(pCtx->pFrontend, u32Format,
1014 &pCtx->pvUnicodeCache,
1015 &pCtx->cbUnicodeCache);
1016 if (RT_SUCCESS(rc))
1017 {
1018 *ppv = RTMemDup(pCtx->pvUnicodeCache, pCtx->cbUnicodeCache);
1019 *pcb = pCtx->cbUnicodeCache;
1020 if (*ppv == NULL)
1021 rc = VERR_NO_MEMORY;
1022 }
1023 }
1024 else
1025 rc = VBoxX11ClipboardReadVBoxData(pCtx->pFrontend, u32Format,
1026 ppv, pcb);
1027 LogFlowFunc(("returning %Rrc\n", rc));
1028 if (RT_SUCCESS(rc))
1029 LogFlowFunc(("*ppv=%.*ls, *pcb=%u\n", *pcb, *ppv, *pcb));
1030 return rc;
1031}
1032
1033/**
1034 * Satisfy a request from X11 to convert the clipboard text to Utf8. We
1035 * return non-zero terminated text.
1036 * @todo that works, but it is bad. Change it to return zero-terminated
1037 * text.
1038 *
1039 * @returns true if we successfully convert the data to the format
1040 * requested, false otherwise.
1041 *
1042 * @param atomTypeReturn Where to store the atom for the type of the data
1043 * we are returning
1044 * @param pValReturn Where to store the pointer to the data we are
1045 * returning. This should be to memory allocated by
1046 * XtMalloc, which will be freed by the Xt toolkit
1047 * later.
1048 * @param pcLenReturn Where to store the length of the data we are
1049 * returning
1050 * @param piFormatReturn Where to store the bit width (8, 16, 32) of the
1051 * data we are returning
1052 * @note X11 backend code, called by the callback for XtOwnSelection.
1053 */
1054static Boolean vboxClipboardConvertToUtf8ForX11(VBOXCLIPBOARDCONTEXTX11
1055 *pCtx,
1056 Atom *atomTypeReturn,
1057 XtPointer *pValReturn,
1058 unsigned long *pcLenReturn,
1059 int *piFormatReturn)
1060{
1061 PRTUTF16 pu16SrcText, pu16DestText;
1062 char *pu8DestText;
1063 void *pvVBox = NULL;
1064 uint32_t cbVBox = 0;
1065 size_t cwSrcLen, cwDestLen, cbDestLen;
1066 int rc;
1067
1068 LogFlowFunc (("called\n"));
1069 /* Read the clipboard data from the guest. */
1070 rc = vboxClipboardReadVBoxData(pCtx,
1071 VBOX_SHARED_CLIPBOARD_FMT_UNICODETEXT,
1072 &pvVBox, &cbVBox);
1073 if ((rc != VINF_SUCCESS) || (cbVBox == 0))
1074 {
1075 /* If vboxClipboardReadVBoxData fails then we may be terminating */
1076 LogRelFunc (("vboxClipboardReadVBoxData returned %Rrc%s\n", rc,
1077 RT_SUCCESS(rc) ? ", cbVBox == 0" : ""));
1078 RTMemFree(pvVBox);
1079 return false;
1080 }
1081 pu16SrcText = reinterpret_cast<PRTUTF16>(pvVBox);
1082 cwSrcLen = cbVBox / 2;
1083 /* How long will the converted text be? */
1084 rc = vboxClipboardUtf16GetLinSize(pu16SrcText, cwSrcLen, &cwDestLen);
1085 if (RT_FAILURE(rc))
1086 {
1087 LogRelFunc (("clipboard conversion failed. vboxClipboardUtf16GetLinSize returned %Rrc. Abandoning.\n", rc));
1088 RTMemFree(pvVBox);
1089 AssertRCReturn(rc, false);
1090 }
1091 if (cwDestLen == 0)
1092 {
1093 LogFlowFunc(("received empty clipboard data from the guest, returning false.\n"));
1094 RTMemFree(pvVBox);
1095 return false;
1096 }
1097 pu16DestText = reinterpret_cast<PRTUTF16>(RTMemAlloc(cwDestLen * 2));
1098 if (pu16DestText == 0)
1099 {
1100 LogRelFunc (("failed to allocate %d bytes\n", cwDestLen * 2));
1101 RTMemFree(pvVBox);
1102 return false;
1103 }
1104 /* Convert the text. */
1105 rc = vboxClipboardUtf16WinToLin(pu16SrcText, cwSrcLen, pu16DestText, cwDestLen);
1106 if (RT_FAILURE(rc))
1107 {
1108 LogRelFunc (("clipboard conversion failed. vboxClipboardUtf16WinToLin() returned %Rrc. Abandoning.\n", rc));
1109 RTMemFree(reinterpret_cast<void *>(pu16DestText));
1110 RTMemFree(pvVBox);
1111 return false;
1112 }
1113 /* Allocate enough space, as RTUtf16ToUtf8Ex may fail if the
1114 space is too tightly calculated. */
1115 pu8DestText = XtMalloc(cwDestLen * 4);
1116 if (pu8DestText == 0)
1117 {
1118 LogRelFunc (("failed to allocate %d bytes\n", cwDestLen * 4));
1119 RTMemFree(reinterpret_cast<void *>(pu16DestText));
1120 RTMemFree(pvVBox);
1121 return false;
1122 }
1123 /* Convert the Utf16 string to Utf8. */
1124 rc = RTUtf16ToUtf8Ex(pu16DestText + 1, cwDestLen - 1, &pu8DestText, cwDestLen * 4,
1125 &cbDestLen);
1126 RTMemFree(reinterpret_cast<void *>(pu16DestText));
1127 if (RT_FAILURE(rc))
1128 {
1129 LogRelFunc (("clipboard conversion failed. RTUtf16ToUtf8Ex() returned %Rrc. Abandoning.\n", rc));
1130 XtFree(pu8DestText);
1131 RTMemFree(pvVBox);
1132 return false;
1133 }
1134 LogFlowFunc (("converted string is %.*s. Returning.\n", cbDestLen, pu8DestText));
1135 RTMemFree(pvVBox);
1136 *atomTypeReturn = pCtx->atomUtf8;
1137 *pValReturn = reinterpret_cast<XtPointer>(pu8DestText);
1138 *pcLenReturn = cbDestLen;
1139 *piFormatReturn = 8;
1140 return true;
1141}
1142
1143/**
1144 * Satisfy a request from X11 to convert the clipboard text to
1145 * COMPOUND_TEXT. We return non-zero terminated text.
1146 * @todo that works, but it is bad. Change it to return zero-terminated
1147 * text.
1148 *
1149 * @returns true if we successfully convert the data to the format
1150 * requested, false otherwise.
1151 *
1152 * @param atomTypeReturn Where to store the atom for the type of the data
1153 * we are returning
1154 * @param pValReturn Where to store the pointer to the data we are
1155 * returning. This should be to memory allocated by
1156 * XtMalloc, which will be freed by the Xt toolkit
1157 * later.
1158 * @param pcLenReturn Where to store the length of the data we are
1159 * returning
1160 * @param piFormatReturn Where to store the bit width (8, 16, 32) of the
1161 * data we are returning
1162 * @note X11 backend code, called by the callback for XtOwnSelection.
1163 */
1164static Boolean vboxClipboardConvertToCTextForX11(VBOXCLIPBOARDCONTEXTX11
1165 *pCtx,
1166 Atom *atomTypeReturn,
1167 XtPointer *pValReturn,
1168 unsigned long *pcLenReturn,
1169 int *piFormatReturn)
1170{
1171 PRTUTF16 pu16SrcText, pu16DestText;
1172 void *pvVBox = NULL;
1173 uint32_t cbVBox = 0;
1174 char *pu8DestText = 0;
1175 size_t cwSrcLen, cwDestLen, cbDestLen;
1176 XTextProperty property;
1177 int rc;
1178
1179 LogFlowFunc (("called\n"));
1180 /* Read the clipboard data from the guest. */
1181 rc = vboxClipboardReadVBoxData(pCtx,
1182 VBOX_SHARED_CLIPBOARD_FMT_UNICODETEXT,
1183 &pvVBox, &cbVBox);
1184 if ((rc != VINF_SUCCESS) || (cbVBox == 0))
1185 {
1186 /* If vboxClipboardReadVBoxData fails then we may be terminating */
1187 LogRelFunc (("vboxClipboardReadVBoxData returned %Rrc%s\n", rc,
1188 RT_SUCCESS(rc) ? ", cbVBox == 0" : ""));
1189 RTMemFree(pvVBox);
1190 return false;
1191 }
1192 pu16SrcText = reinterpret_cast<PRTUTF16>(pvVBox);
1193 cwSrcLen = cbVBox / 2;
1194 /* How long will the converted text be? */
1195 rc = vboxClipboardUtf16GetLinSize(pu16SrcText, cwSrcLen, &cwDestLen);
1196 if (RT_FAILURE(rc))
1197 {
1198 LogRelFunc (("clipboard conversion failed. vboxClipboardUtf16GetLinSize returned %Rrc. Abandoning.\n", rc));
1199 RTMemFree(pvVBox);
1200 AssertRCReturn(rc, false);
1201 }
1202 if (cwDestLen == 0)
1203 {
1204 LogFlowFunc(("received empty clipboard data from the guest, returning false.\n"));
1205 RTMemFree(pvVBox);
1206 return false;
1207 }
1208 pu16DestText = reinterpret_cast<PRTUTF16>(RTMemAlloc(cwDestLen * 2));
1209 if (pu16DestText == 0)
1210 {
1211 LogRelFunc (("failed to allocate %d bytes\n", cwDestLen * 2));
1212 RTMemFree(pvVBox);
1213 return false;
1214 }
1215 /* Convert the text. */
1216 rc = vboxClipboardUtf16WinToLin(pu16SrcText, cwSrcLen, pu16DestText, cwDestLen);
1217 if (RT_FAILURE(rc))
1218 {
1219 LogRelFunc (("clipboard conversion failed. vboxClipboardUtf16WinToLin() returned %Rrc. Abandoning.\n", rc));
1220 RTMemFree(reinterpret_cast<void *>(pu16DestText));
1221 RTMemFree(pvVBox);
1222 return false;
1223 }
1224 /* Convert the Utf16 string to Utf8. */
1225 rc = RTUtf16ToUtf8Ex(pu16DestText + 1, cwDestLen - 1, &pu8DestText, 0, &cbDestLen);
1226 RTMemFree(reinterpret_cast<void *>(pu16DestText));
1227 if (RT_FAILURE(rc))
1228 {
1229 LogRelFunc (("clipboard conversion failed. RTUtf16ToUtf8Ex() returned %Rrc. Abandoning.\n", rc));
1230 RTMemFree(pvVBox);
1231 return false;
1232 }
1233 /* And finally (!) convert the Utf8 text to compound text. */
1234#ifdef RT_OS_SOLARIS
1235 rc = XmbTextListToTextProperty(XtDisplay(pCtx->widget), &pu8DestText, 1,
1236 XCompoundTextStyle, &property);
1237#else
1238 rc = Xutf8TextListToTextProperty(XtDisplay(pCtx->widget), &pu8DestText, 1,
1239 XCompoundTextStyle, &property);
1240#endif
1241 RTMemFree(pu8DestText);
1242 if (rc < 0)
1243 {
1244 const char *pcReason;
1245 switch(rc)
1246 {
1247 case XNoMemory:
1248 pcReason = "out of memory";
1249 break;
1250 case XLocaleNotSupported:
1251 pcReason = "locale (Utf8) not supported";
1252 break;
1253 case XConverterNotFound:
1254 pcReason = "converter not found";
1255 break;
1256 default:
1257 pcReason = "unknown error";
1258 }
1259 LogRelFunc (("Xutf8TextListToTextProperty failed. Reason: %s\n",
1260 pcReason));
1261 RTMemFree(pvVBox);
1262 return false;
1263 }
1264 LogFlowFunc (("converted string is %s. Returning.\n", property.value));
1265 RTMemFree(pvVBox);
1266 *atomTypeReturn = property.encoding;
1267 *pValReturn = reinterpret_cast<XtPointer>(property.value);
1268 *pcLenReturn = property.nitems;
1269 *piFormatReturn = property.format;
1270 return true;
1271}
1272
1273/**
1274 * Return VBox's clipboard data for an X11 client.
1275 * @note X11 backend code, callback for XtOwnSelection
1276 */
1277static Boolean vboxClipboardConvertForX11(Widget widget, Atom *atomSelection,
1278 Atom *atomTarget,
1279 Atom *atomTypeReturn,
1280 XtPointer *pValReturn,
1281 unsigned long *pcLenReturn,
1282 int *piFormatReturn)
1283{
1284 g_eClipboardFormats eFormat = INVALID;
1285 VBOXCLIPBOARDCONTEXTX11 *pCtx = vboxClipboardFindContext(widget);
1286
1287 LogFlowFunc(("\n"));
1288 /* Drop requests that we receive too late. */
1289 if (!pCtx->fOwnsClipboard)
1290 return false;
1291 if ( (*atomSelection != pCtx->atomClipboard)
1292 && (*atomSelection != pCtx->atomPrimary)
1293 )
1294 {
1295 LogFlowFunc(("rc = false\n"));
1296 return false;
1297 }
1298 if (g_debugClipboard)
1299 {
1300 char *szAtomName = XGetAtomName(XtDisplay(pCtx->widget), *atomTarget);
1301 if (szAtomName != 0)
1302 {
1303 Log2 (("%s: request for format %s\n", __PRETTY_FUNCTION__, szAtomName));
1304 XFree(szAtomName);
1305 }
1306 else
1307 {
1308 LogFunc (("request for invalid target atom %d!\n", *atomTarget));
1309 }
1310 }
1311 if (*atomTarget == pCtx->atomTargets)
1312 {
1313 eFormat = TARGETS;
1314 }
1315 else
1316 {
1317 for (unsigned i = 0; i != pCtx->formatList.size(); ++i)
1318 {
1319 if (pCtx->formatList[i].atom == *atomTarget)
1320 {
1321 eFormat = pCtx->formatList[i].format;
1322 break;
1323 }
1324 }
1325 }
1326 switch (eFormat)
1327 {
1328 case TARGETS:
1329 return vboxClipboardConvertTargetsForX11(pCtx, atomTypeReturn,
1330 pValReturn, pcLenReturn,
1331 piFormatReturn);
1332 case UTF8:
1333 return vboxClipboardConvertToUtf8ForX11(pCtx, atomTypeReturn,
1334 pValReturn, pcLenReturn,
1335 piFormatReturn);
1336 case CTEXT:
1337 return vboxClipboardConvertToCTextForX11(pCtx, atomTypeReturn,
1338 pValReturn, pcLenReturn,
1339 piFormatReturn);
1340 default:
1341 LogFunc (("bad format\n"));
1342 return false;
1343 }
1344}
1345
1346/**
1347 * This is called by the X toolkit intrinsics to let us know that another
1348 * X11 client has taken the clipboard. In this case we notify VBox that
1349 * we want ownership of the clipboard.
1350 * @note X11 backend code, callback for XtOwnSelection
1351 */
1352static void vboxClipboardReturnToX11(Widget widget, Atom *)
1353{
1354 VBOXCLIPBOARDCONTEXTX11 *pCtx = vboxClipboardFindContext(widget);
1355 LogFlowFunc (("called, giving X11 clipboard ownership\n"));
1356 /* These should be set to the right values as soon as we start polling */
1357 pCtx->X11TextFormat = INVALID;
1358 pCtx->X11BitmapFormat = INVALID;
1359 pCtx->fOwnsClipboard = false;
1360 pCtx->notifyVBox = true;
1361}
1362
1363/** Structure used to pass information about formats that VBox supports */
1364typedef struct _VBOXCLIPBOARDFORMATS
1365{
1366 /** Context information for the X11 clipboard */
1367 VBOXCLIPBOARDCONTEXTX11 *pCtx;
1368 /** Formats supported by VBox */
1369 uint32_t formats;
1370} VBOXCLIPBOARDFORMATS;
1371
1372/** Worker function for VBoxX11ClipboardAnnounceVBoxFormat which runs on the
1373 * event thread. */
1374static void vboxClipboardAnnounceWorker(XtPointer pUserData,
1375 XtIntervalId * /* interval */)
1376{
1377 /* Extract and free the user data */
1378 VBOXCLIPBOARDFORMATS *pFormats = (VBOXCLIPBOARDFORMATS *)pUserData;
1379 VBOXCLIPBOARDCONTEXTX11 *pCtx = pFormats->pCtx;
1380 uint32_t u32Formats = pFormats->formats;
1381 RTMemFree(pFormats);
1382 LogFlowFunc (("u32Formats=%d\n", u32Formats));
1383 pCtx->vboxFormats = u32Formats;
1384 /* Invalidate the clipboard cache */
1385 if (pCtx->pvUnicodeCache != NULL)
1386 {
1387 RTMemFree(pCtx->pvUnicodeCache);
1388 pCtx->pvUnicodeCache = NULL;
1389 }
1390 if (u32Formats == 0)
1391 {
1392 /* This is just an automatism, not a genuine anouncement */
1393 XtDisownSelection(pCtx->widget, pCtx->atomClipboard, CurrentTime);
1394 pCtx->fOwnsClipboard = false;
1395 LogFlowFunc(("returning\n"));
1396 return;
1397 }
1398 Log2 (("%s: giving the guest clipboard ownership\n", __PRETTY_FUNCTION__));
1399 if (XtOwnSelection(pCtx->widget, pCtx->atomClipboard, CurrentTime,
1400 vboxClipboardConvertForX11, vboxClipboardReturnToX11,
1401 0) == True)
1402 {
1403 pCtx->fOwnsClipboard = true;
1404 /* Grab the middle-button paste selection too. */
1405 XtOwnSelection(pCtx->widget, pCtx->atomPrimary, CurrentTime,
1406 vboxClipboardConvertForX11, NULL, 0);
1407 }
1408 else
1409 {
1410 /* Another X11 client claimed the clipboard just after us, so let it
1411 * go again. */
1412 Log2 (("%s: returning clipboard ownership to the X11\n",
1413 __PRETTY_FUNCTION__));
1414 /* VBox thinks it currently owns the clipboard, so we must notify it
1415 * as soon as we know what formats X11 has to offer. */
1416 pCtx->notifyVBox = true;
1417 pCtx->fOwnsClipboard = false;
1418 }
1419 LogFlowFunc(("returning\n"));
1420}
1421
1422/**
1423 * VBox is taking possession of the shared clipboard.
1424 *
1425 * @param u32Formats Clipboard formats the guest is offering
1426 * @note X11 backend code
1427 */
1428void VBoxX11ClipboardAnnounceVBoxFormat(VBOXCLIPBOARDCONTEXTX11 *pCtx,
1429 uint32_t u32Formats)
1430{
1431 /*
1432 * Immediately return if we are not connected to the host X server.
1433 */
1434 if (!g_fHaveX11)
1435 return;
1436 /* This must be freed by the worker callback */
1437 VBOXCLIPBOARDFORMATS *pFormats =
1438 (VBOXCLIPBOARDFORMATS *) RTMemAlloc(sizeof(VBOXCLIPBOARDFORMATS));
1439 if (pFormats != NULL) /* if it is we will soon have other problems */
1440 {
1441 pFormats->pCtx = pCtx;
1442 pFormats->formats = u32Formats;
1443 XtAppAddTimeOut(pCtx->appContext, 0, vboxClipboardAnnounceWorker,
1444 (XtPointer) pFormats);
1445 }
1446}
1447
1448/** Worker function for VBoxX11ClipboardReadX11Data which runs on the event
1449 * thread. */
1450static void vboxClipboardReadX11Worker(XtPointer pUserData,
1451 XtIntervalId * /* interval */)
1452{
1453 VBOXCLIPBOARDREQUEST *pRequest = (VBOXCLIPBOARDREQUEST *)pUserData;
1454 VBOXCLIPBOARDCONTEXTX11 *pCtx = pRequest->pCtx;
1455 LogFlowFunc (("u32Format = %d, cb = %d\n", pRequest->format,
1456 pRequest->cb));
1457
1458 int rc = VINF_SUCCESS;
1459 /* Set this to start with, just in case */
1460 *pRequest->pcbActual = 0;
1461 /* Do not continue if we already own the clipboard */
1462 if (pCtx->fOwnsClipboard == true)
1463 rc = VERR_NO_DATA; /** @todo should this be VINF? */
1464 else
1465 {
1466 /*
1467 * VBox wants to read data in the given format.
1468 */
1469 if (pRequest->format == VBOX_SHARED_CLIPBOARD_FMT_UNICODETEXT)
1470 {
1471 if (pCtx->atomX11TextFormat == None)
1472 /* VBox thinks we have data and we don't */
1473 rc = VERR_NO_DATA;
1474 else
1475 /* Send out a request for the data to the current clipboard
1476 * owner */
1477 XtGetSelectionValue(pCtx->widget, pCtx->atomClipboard,
1478 pCtx->atomX11TextFormat,
1479 vboxClipboardGetDataFromX11,
1480 reinterpret_cast<XtPointer>(pRequest),
1481 CurrentTime);
1482 }
1483 else
1484 rc = VERR_NOT_IMPLEMENTED;
1485 }
1486 if (RT_FAILURE(rc))
1487 {
1488 pRequest->rc = rc;
1489 /* The clipboard callback was never scheduled, so we must signal
1490 * that the request processing is finished ourselves. */
1491 RTSemEventSignal(pRequest->finished);
1492 }
1493 LogFlowFunc(("status %Rrc\n", rc));
1494}
1495
1496/**
1497 * Called when VBox wants to read the X11 clipboard.
1498 *
1499 * @param pClient Context information about the guest VM
1500 * @param u32Format The format that the guest would like to receive the data in
1501 * @param pv Where to write the data to
1502 * @param cb The size of the buffer to write the data to
1503 * @param pcbActual Where to write the actual size of the written data
1504 * @note X11 backend code
1505 */
1506int VBoxX11ClipboardReadX11Data(VBOXCLIPBOARDCONTEXTX11 *pCtx,
1507 uint32_t u32Format, void *pv, uint32_t cb,
1508 uint32_t *pcbActual)
1509{
1510 /* Initially set the size of the data read to zero in case we fail
1511 * somewhere or X11 is not available. */
1512 *pcbActual = 0;
1513
1514 /*
1515 * Immediately return if we are not connected to the host X server.
1516 */
1517 if (!g_fHaveX11)
1518 return VINF_SUCCESS;
1519
1520 VBOXCLIPBOARDREQUEST request;
1521 request.rc = VERR_WRONG_ORDER;
1522 request.pv = pv;
1523 request.cb = cb;
1524 request.pcbActual = pcbActual;
1525 request.format = u32Format;
1526 request.pCtx = pCtx;
1527 /* The worker function will signal this when it has finished. */
1528 int rc = RTSemEventCreate(&request.finished);
1529 if (RT_SUCCESS(rc))
1530 {
1531 /* We use this to schedule a worker function on the event thread. */
1532 XtAppAddTimeOut(pCtx->appContext, 0, vboxClipboardReadX11Worker,
1533 (XtPointer) &request);
1534 rc = RTSemEventWait(request.finished, RT_INDEFINITE_WAIT);
1535 if (RT_SUCCESS(rc))
1536 rc = request.rc;
1537 RTSemEventDestroy(request.finished);
1538 }
1539 return rc;
1540}
1541
1542#ifdef TESTCASE
1543
1544#include <iprt/initterm.h>
1545#include <iprt/stream.h>
1546
1547enum { MAX_ATTEMPTS = 10 };
1548
1549static VBOXCLIPBOARDCONTEXTX11 *g_pCtxTestX11;
1550static VBOXCLIPBOARDCONTEXTX11 *g_pCtxTestVBox;
1551
1552/** Quick Utf16 string class, initialised from a Utf8 string */
1553class testUtf16String
1554{
1555 /** Stores the Utf16 data. NULL if something went wrong. */
1556 PRTUTF16 mData;
1557 /** Stores the size in bytes of the data. 0 if something went wrong. */
1558 size_t mSize;
1559public:
1560 /** Constructor
1561 * @param aString the Utf8 representation of the string data
1562 */
1563 testUtf16String(const char *aString)
1564 {
1565 int rc = RTStrToUtf16(aString, &mData);
1566 if (RT_FAILURE(rc))
1567 {
1568 mData = NULL;
1569 mSize = 0;
1570 return;
1571 }
1572 mSize = RTUtf16Len(mData) * 2 + 2;
1573 }
1574 /** Destructor */
1575 ~testUtf16String()
1576 {
1577 RTUtf16Free(mData);
1578 }
1579 /** Getter for the data */
1580 PCRTUTF16 getData() { return mData; }
1581 /** Getter for the data size */
1582 size_t getSize() { return mSize; }
1583};
1584
1585/** Parameters for a test run. */
1586static struct testData
1587{
1588 /** The string data we are offering. */
1589 testUtf16String data;
1590 /** The size of the buffer to receive the request data */
1591 uint32_t cchBuffer;
1592 /** The format we request the data in */
1593 PCRTUTF16 getData() { return data.getData(); }
1594 /** Getter for the Utf16 string data size in bytes */
1595 size_t getSize() { return data.getSize(); }
1596 /** Is the test expected to produce a buffer overflow? */
1597 bool overflow() { return cchBuffer < getSize(); }
1598} g_sTestData[] =
1599{
1600 { "Hello world\r\n", 256 },
1601 { "Goodbye world", 28 },
1602 { "", 2 },
1603 /* This should produce a buffer overflow */
1604 { "Goodbye world", 27 },
1605};
1606
1607/** Which line of the table above are we currently testing? */
1608size_t g_testNum = 0;
1609enum { MAX_TESTS = RT_ELEMENTS(g_sTestData) };
1610/** Are we doing a timeout test? Ugly, but whatever. */
1611static bool g_testTimeout = false;
1612
1613/** @todo the current code can't test the following conditions:
1614 * * the X11 clipboard offers non-Utf8 text
1615 * * the X11 clipboard offers invalid Utf8 text
1616 */
1617/** @todo stress test our context store code? Probably not important... */
1618
1619int VBoxX11ClipboardReadVBoxData(VBOXCLIPBOARDCONTEXT *pCtx,
1620 uint32_t u32Format, void **ppv,
1621 uint32_t *pcb)
1622{
1623 LogFlowFunc(("pCtx = %p\n", pCtx));
1624 /* This should only ever be called for the second "test" clipboard. */
1625 AssertReturn(pCtx == (VBOXCLIPBOARDCONTEXT *)g_pCtxTestX11
1626 /* Magic cookie */, VERR_WRONG_ORDER);
1627 /* Timeout test hack */
1628 if (g_testTimeout)
1629 {
1630 RTThreadSleep(200);
1631 /* Try to return data after we have been timed out. */
1632 *ppv = RTMemDup((const void *) "Scribblings", sizeof("Scribblings"));
1633 *pcb = sizeof("Scribblings");
1634 LogFlowFunc(("sleep finished, returning\n"));
1635 return VINF_SUCCESS;
1636 }
1637 /* Sanity. */
1638 AssertReturn(g_testNum < MAX_TESTS, VERR_WRONG_ORDER);
1639 testData *pData = &g_sTestData[g_testNum];
1640 AssertPtrReturn(pData->getData(), VERR_NO_MEMORY);
1641 AssertReturn(pData->getSize(), VERR_WRONG_ORDER);
1642 void *retval = RTMemDup((const void *) pData->getData(),
1643 pData->getSize());
1644 AssertPtrReturn(retval, VERR_NO_MEMORY);
1645 *ppv = (void *) retval;
1646 *pcb = pData->getSize();
1647 LogFlowFunc(("returning\n"));
1648 return VINF_SUCCESS;
1649}
1650
1651/** As long as our test is running, we grab back the clipboard if X11 ever
1652 * tries to take it away. I hope we don't end up with a tug of war... */
1653void VBoxX11ClipboardReportX11Formats(VBOXCLIPBOARDCONTEXT *pCtx,
1654 uint32_t u32Formats)
1655{
1656 LogFlowFunc(("pCtx = %p\n", pCtx));
1657 if (pCtx == (VBOXCLIPBOARDCONTEXT *)g_pCtxTestX11) /* Magic cookie */
1658 VBoxX11ClipboardAnnounceVBoxFormat(g_pCtxTestVBox,
1659 VBOX_SHARED_CLIPBOARD_FMT_UNICODETEXT);
1660}
1661
1662/** Initial tests that can be done while the clipboard contents are still
1663 * invalid.
1664 * @returns boolean success value
1665 * @note prints status information to stdout
1666 */
1667bool testInvalid(void)
1668{
1669 bool fSuccess = true;
1670 char pc[256];
1671 uint32_t cbActual;
1672 RTPrintf("tstClipboardX11: TESTING a request for an invalid data format\n");
1673 int rc = VBoxX11ClipboardReadX11Data(g_pCtxTestX11, 0xffff, (void *) pc,
1674 sizeof(pc), &cbActual);
1675 RTPrintf("Returned %Rrc - %s\n", rc,
1676 rc == VERR_NOT_IMPLEMENTED ? "SUCCESS" : "FAILURE");
1677 if (rc != VERR_NOT_IMPLEMENTED)
1678 fSuccess = false;
1679 RTPrintf("tstClipboardX11: TESTING a request for data from an empty clipboard\n");
1680 VBoxX11ClipboardAnnounceVBoxFormat(g_pCtxTestX11,
1681 VBOX_SHARED_CLIPBOARD_FMT_UNICODETEXT);
1682 VBoxX11ClipboardAnnounceVBoxFormat(g_pCtxTestX11,
1683 0);
1684 rc = VBoxX11ClipboardReadX11Data(g_pCtxTestX11,
1685 VBOX_SHARED_CLIPBOARD_FMT_UNICODETEXT,
1686 (void *) pc, sizeof(pc), &cbActual);
1687 RTPrintf("Returned %Rrc - %s\n", rc,
1688 rc == VERR_NO_DATA ? "SUCCESS" : "FAILURE");
1689 if (rc != VERR_NO_DATA)
1690 fSuccess = false;
1691 return fSuccess;
1692}
1693
1694/** Tests an entry in the table above.
1695 * @returns boolean success value
1696 * @note prints status information to stdout
1697 */
1698bool testEntry(testData *pData)
1699{
1700 bool fSuccess = false;
1701 char pc[256];
1702 uint32_t cbActual;
1703 for (int i = 0; (i < MAX_ATTEMPTS) && !fSuccess; ++i)
1704 {
1705 VBoxX11ClipboardAnnounceVBoxFormat(g_pCtxTestVBox,
1706 VBOX_SHARED_CLIPBOARD_FMT_UNICODETEXT);
1707 int rc = VBoxX11ClipboardReadX11Data(g_pCtxTestX11,
1708 VBOX_SHARED_CLIPBOARD_FMT_UNICODETEXT,
1709 (void *) pc, sizeof(pc),
1710 &cbActual);
1711 AssertRCReturn(rc, 1);
1712 /* Did we expect and get an overflow? */
1713 if ( RT_SUCCESS(rc)
1714 && (cbActual == pData->getSize())
1715 && pData->overflow())
1716 fSuccess = true;
1717 /* Did we expect a string and get it? */
1718 else if ( RT_SUCCESS(rc)
1719 && (memcmp(pc, pData->getData(),
1720 pData->getSize()) == 0))
1721 fSuccess = true;
1722 else
1723 RTThreadSleep(50);
1724 if (fSuccess)
1725 RTPrintf("text %ls, %sretval %Rrc - SUCCESS\n",
1726 pData->getData(),
1727 pData->overflow() ? "buffer overflow as expected, "
1728 : "", rc);
1729 else
1730 RTPrintf("text %ls, retval %Rrc, attempt %d of %d\n",
1731 pData->getData(), rc, i + 1,
1732 MAX_ATTEMPTS);
1733 }
1734 if (!fSuccess)
1735 RTPrintf("FAILURE. Last string obtained was %.*lS\n",
1736 RT_MIN(cbActual / 2, 20), pc);
1737 return fSuccess;
1738}
1739
1740int main()
1741{
1742 int rc = VINF_SUCCESS;
1743 int status = 0;
1744 /* We can't test anything without an X session, so just return success
1745 * in that case. */
1746 if (!RTEnvGet("DISPLAY"))
1747 return 0;
1748 RTR3Init();
1749 g_pCtxTestX11 = VBoxX11ClipboardConstructX11(NULL);
1750 g_pCtxTestVBox =
1751 VBoxX11ClipboardConstructX11((VBOXCLIPBOARDCONTEXT *)g_pCtxTestX11);
1752 rc = VBoxX11ClipboardStartX11(g_pCtxTestVBox);
1753 AssertRCReturn(rc, 1);
1754 bool fSuccess = false;
1755 /* Test a request for an invalid data format and data from an empty
1756 * clipboard */
1757 for (unsigned i = 0; (i < MAX_ATTEMPTS) && !fSuccess; ++i)
1758 {
1759 rc = VBoxX11ClipboardStartX11(g_pCtxTestX11);
1760 AssertRCReturn(rc, 1);
1761 fSuccess = testInvalid();
1762 VBoxX11ClipboardStopX11(g_pCtxTestX11);
1763 if (!fSuccess)
1764 {
1765 RTPrintf("attempt %d of %d\n", i + 1, MAX_ATTEMPTS + 1);
1766 RTThreadSleep(50);
1767 }
1768 }
1769 if (!fSuccess)
1770 status = 1;
1771 rc = VBoxX11ClipboardStartX11(g_pCtxTestX11);
1772 AssertRCReturn(rc, 1);
1773 /* Claim the clipboard and make sure we get it */
1774 VBoxX11ClipboardAnnounceVBoxFormat(g_pCtxTestVBox,
1775 VBOX_SHARED_CLIPBOARD_FMT_UNICODETEXT);
1776 for (unsigned i = 0;
1777 i < 255
1778 && g_pCtxTestX11->X11TextFormat == INVALID;
1779 ++i)
1780 RTThreadSleep(50);
1781 AssertReturn(g_pCtxTestX11->X11TextFormat != INVALID, 1);
1782 /* Do general purpose clipboard tests */
1783 for (int i = 0; i < 2; ++i)
1784 {
1785 g_testUtf8 = (i == 0);
1786 g_testCText = (i == 1);
1787 RTPrintf("tstClipboardX11: TESTING sending and receiving of %s\n",
1788 i == 0 ? "Utf-8" : "COMPOUND TEXT");
1789 for (g_testNum = 0; g_testNum < MAX_TESTS; ++g_testNum)
1790 {
1791 if (!testEntry(&g_sTestData[g_testNum]))
1792 status = 1;
1793 }
1794 }
1795 /* Finally test timeouts. */
1796 XtAppSetSelectionTimeout(g_pCtxTestX11->appContext, 10);
1797 RTPrintf("tstClipboardX11: TESTING the clipboard timeout\n");
1798 rc = VINF_SUCCESS;
1799 g_testTimeout = true;
1800 for (unsigned i = 0; (i < MAX_ATTEMPTS) && (rc != VERR_TIMEOUT); ++i)
1801 {
1802 char pc[256];
1803 uint32_t cbActual;
1804 VBoxX11ClipboardAnnounceVBoxFormat(g_pCtxTestVBox,
1805 VBOX_SHARED_CLIPBOARD_FMT_UNICODETEXT);
1806 rc = VBoxX11ClipboardReadX11Data(g_pCtxTestX11,
1807 VBOX_SHARED_CLIPBOARD_FMT_UNICODETEXT,
1808 (void *) pc, sizeof(pc), &cbActual);
1809 AssertMsg( RT_SUCCESS(rc)
1810 || (rc == VERR_TIMEOUT)
1811 || (rc == VERR_NO_DATA),
1812 ("rc = %Rrc\n", rc));
1813 if (rc == VERR_TIMEOUT)
1814 RTPrintf("SUCCESS\n");
1815 else
1816 {
1817 RTPrintf("Attempt %d of %d\n", i + 1, MAX_ATTEMPTS);
1818 RTThreadSleep(50);
1819 }
1820 }
1821 if (rc != VERR_TIMEOUT)
1822 status = 1;
1823 rc = VBoxX11ClipboardStopX11(g_pCtxTestX11);
1824 AssertRCReturn(rc, 1);
1825 rc = VBoxX11ClipboardStopX11(g_pCtxTestVBox);
1826 AssertRCReturn(rc, 1);
1827 VBoxX11ClipboardDestructX11(g_pCtxTestX11);
1828 VBoxX11ClipboardDestructX11(g_pCtxTestVBox);
1829 return status;
1830}
1831
1832#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