VirtualBox

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

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

GuestHost/SharedClipboard/x11: memory leak in the testcase

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 83.5 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/* Note: to automatically run regression tests on the shared clipboard, set
24 * the make variable VBOX_RUN_X11_CLIPBOARD_TEST=1 while building. If you
25 * often make changes to the clipboard code, setting this variable in
26 * LocalConfig.kmk will cause the tests to be run every time the code is
27 * changed. */
28
29#define LOG_GROUP LOG_GROUP_SHARED_CLIPBOARD
30
31#include <errno.h>
32
33#include <unistd.h>
34
35#ifdef RT_OS_SOLARIS
36#include <tsol/label.h>
37#endif
38
39#include <X11/Xlib.h>
40#include <X11/Xatom.h>
41#include <X11/Intrinsic.h>
42#include <X11/Shell.h>
43#include <X11/Xproto.h>
44#include <X11/StringDefs.h>
45
46#include <iprt/env.h>
47#include <iprt/mem.h>
48#include <iprt/semaphore.h>
49#include <iprt/thread.h>
50
51#include <VBox/log.h>
52
53#include <VBox/GuestHost/SharedClipboard.h>
54#include <VBox/GuestHost/clipboard-helper.h>
55#include <VBox/HostServices/VBoxClipboardSvc.h>
56
57/** The different clipboard formats which we support. */
58enum CLIPFORMAT
59{
60 INVALID = 0,
61 TARGETS,
62 CTEXT,
63 UTF8
64};
65
66/** The table mapping X11 names to data formats and to the corresponding
67 * VBox clipboard formats (currently only Unicode) */
68static struct _CLIPFORMATTABLE
69{
70 /** The X11 atom name of the format (several names can match one format)
71 */
72 const char *pcszAtom;
73 /** The format corresponding to the name */
74 CLIPFORMAT enmFormat;
75 /** The corresponding VBox clipboard format */
76 uint32_t u32VBoxFormat;
77} g_aFormats[] =
78{
79 { "UTF8_STRING", UTF8, VBOX_SHARED_CLIPBOARD_FMT_UNICODETEXT },
80 { "text/plain;charset=UTF-8", UTF8,
81 VBOX_SHARED_CLIPBOARD_FMT_UNICODETEXT },
82 { "text/plain;charset=utf-8", UTF8,
83 VBOX_SHARED_CLIPBOARD_FMT_UNICODETEXT },
84 { "STRING", UTF8, VBOX_SHARED_CLIPBOARD_FMT_UNICODETEXT },
85 { "TEXT", UTF8, VBOX_SHARED_CLIPBOARD_FMT_UNICODETEXT },
86 { "text/plain", UTF8, VBOX_SHARED_CLIPBOARD_FMT_UNICODETEXT },
87 { "COMPOUND_TEXT", CTEXT, VBOX_SHARED_CLIPBOARD_FMT_UNICODETEXT }
88};
89
90/** Global context information used by the X11 clipboard backend */
91struct _CLIPBACKEND
92{
93 /** Opaque data structure describing the front-end. */
94 VBOXCLIPBOARDCONTEXT *pFrontend;
95 /** The X Toolkit application context structure */
96 XtAppContext appContext;
97
98 /** We have a separate thread to wait for Window and Clipboard events */
99 RTTHREAD thread;
100 /** The X Toolkit widget which we use as our clipboard client. It is never made visible. */
101 Widget widget;
102
103 /** Does VBox currently own the clipboard? If so, we don't need to poll
104 * X11 for supported formats. */
105 bool fOwnsClipboard;
106
107 /** What is the best text format X11 has to offer? INVALID for none. */
108 CLIPFORMAT X11TextFormat;
109 /** Atom corresponding to the X11 text format */
110 Atom atomX11TextFormat;
111 /** What is the best bitmap format X11 has to offer? INVALID for none.
112 */
113 CLIPFORMAT X11BitmapFormat;
114 /** Atom corresponding to the X11 Bitmap format */
115 Atom atomX11BitmapFormat;
116 /** What formats does VBox have on offer? */
117 uint32_t vboxFormats;
118 /** Windows hosts and guests cache the clipboard data they receive.
119 * Since we have no way of knowing whether their cache is still valid,
120 * we always send a "data changed" message after a successful transfer
121 * to invalidate it. */
122 bool notifyVBox;
123 /** Cache of the last unicode data that we received */
124 void *pvUnicodeCache;
125 /** Size of the unicode data in the cache */
126 uint32_t cbUnicodeCache;
127 /** When we wish the clipboard to exit, we have to wake up the event
128 * loop. We do this by writing into a pipe. This end of the pipe is
129 * the end that another thread can write to. */
130 int wakeupPipeWrite;
131 /** The reader end of the pipe */
132 int wakeupPipeRead;
133};
134
135/** The number of simultaneous instances we support. For all normal purposes
136 * we should never need more than one. For the testcase it is convenient to
137 * have a second instance that the first can interact with in order to have
138 * a more controlled environment. */
139enum { CLIP_MAX_CONTEXTS = 20 };
140
141/** Array of structures for mapping Xt widgets to context pointers. We
142 * need this because the widget clipboard callbacks do not pass user data. */
143static struct {
144 /** The widget we want to associate the context with */
145 Widget widget;
146 /** The context associated with the widget */
147 CLIPBACKEND *pCtx;
148} g_contexts[CLIP_MAX_CONTEXTS];
149
150/** Register a new X11 clipboard context. */
151static int clipRegisterContext(CLIPBACKEND *pCtx)
152{
153 bool found = false;
154 AssertReturn(pCtx != NULL, VERR_INVALID_PARAMETER);
155 Widget widget = pCtx->widget;
156 AssertReturn(widget != NULL, VERR_INVALID_PARAMETER);
157 for (unsigned i = 0; i < RT_ELEMENTS(g_contexts); ++i)
158 {
159 AssertReturn( (g_contexts[i].widget != widget)
160 && (g_contexts[i].pCtx != pCtx), VERR_WRONG_ORDER);
161 if (g_contexts[i].widget == NULL && !found)
162 {
163 AssertReturn(g_contexts[i].pCtx == NULL, VERR_INTERNAL_ERROR);
164 g_contexts[i].widget = widget;
165 g_contexts[i].pCtx = pCtx;
166 found = true;
167 }
168 }
169 return found ? VINF_SUCCESS : VERR_OUT_OF_RESOURCES;
170}
171
172/** Unregister an X11 clipboard context. */
173static void clipUnregisterContext(CLIPBACKEND *pCtx)
174{
175 bool found = false;
176 AssertReturnVoid(pCtx != NULL);
177 Widget widget = pCtx->widget;
178 AssertReturnVoid(widget != NULL);
179 for (unsigned i = 0; i < RT_ELEMENTS(g_contexts); ++i)
180 {
181 Assert(!found || g_contexts[i].widget != widget);
182 if (g_contexts[i].widget == widget)
183 {
184 Assert(g_contexts[i].pCtx != NULL);
185 g_contexts[i].widget = NULL;
186 g_contexts[i].pCtx = NULL;
187 found = true;
188 }
189 }
190}
191
192/** Find an X11 clipboard context. */
193static CLIPBACKEND *clipLookupContext(Widget widget)
194{
195 AssertReturn(widget != NULL, NULL);
196 for (unsigned i = 0; i < RT_ELEMENTS(g_contexts); ++i)
197 {
198 if (g_contexts[i].widget == widget)
199 {
200 Assert(g_contexts[i].pCtx != NULL);
201 return g_contexts[i].pCtx;
202 }
203 }
204 return NULL;
205}
206
207/** Convert an atom name string to an X11 atom, looking it up in a cache
208 * before asking the server */
209static Atom clipGetAtom(Widget widget, const char *pszName)
210{
211 AssertPtrReturn(pszName, None);
212 Atom retval = None;
213 XrmValue nameVal, atomVal;
214 nameVal.addr = (char *) pszName;
215 nameVal.size = strlen(pszName);
216 atomVal.size = sizeof(Atom);
217 atomVal.addr = (char *) &retval;
218 XtConvertAndStore(widget, XtRString, &nameVal, XtRAtom, &atomVal);
219 return retval;
220}
221
222/* Are we actually connected to the X server? */
223static bool g_fHaveX11;
224
225/**
226 * Massage generic Utf16 with CR end-of-lines into the format Windows expects
227 * and put the result in a user-supplied buffer.
228 * @returns IPRT status code
229 * @returns VERR_BUFFER_OVERFLOW if the buffer is not large enough
230 * @param pwcSrc The source Utf16
231 * @param cwcSrc The number of 16bit elements in @a pwcSrc, not counting
232 * the terminating zero
233 * @param pvBuf The buffer to write the result into
234 * @param cbBuf The size of the buffer
235 * @param pcbActual On success, where to store the number of bytes written.
236 * On overflow, the buffer size needed. Undefined
237 * otherwise. Optional
238 */
239static int clipUtf16ToWinTxt(RTUTF16 *pwcSrc, size_t cwcSrc,
240 void *pvBuf, unsigned cbBuf, uint32_t *pcbActual)
241{
242 LogFlowFunc(("pwcSrc=%p, cwcSrc=%u, pvBuf=%p, cbBuf=%u", pwcSrc, cwcSrc,
243 pvBuf, cbBuf));
244 AssertPtrReturn(pwcSrc, VERR_INVALID_POINTER);
245 AssertPtrReturn(pvBuf, VERR_INVALID_POINTER);
246 if (pcbActual)
247 *pcbActual = 0;
248 PRTUTF16 pwcDest = reinterpret_cast<PRTUTF16>(pvBuf);
249 size_t cwcDest;
250 int rc = vboxClipboardUtf16GetWinSize(pwcSrc, cwcSrc + 1, &cwcDest);
251 if (RT_SUCCESS(rc) && (cbBuf < cwcDest * 2))
252 {
253 rc = VERR_BUFFER_OVERFLOW;
254 if (pcbActual)
255 *pcbActual = cwcDest * 2;
256 }
257 if (RT_SUCCESS(rc))
258 rc = vboxClipboardUtf16LinToWin(pwcSrc, cwcSrc + 1, pwcDest,
259 cbBuf / 2);
260 if (RT_SUCCESS(rc))
261 {
262 LogFlowFunc (("converted string is %.*ls\n", cwcDest, pwcDest));
263 if (pcbActual)
264 *pcbActual = cwcDest * 2;
265 }
266 LogFlowFunc(("returning %Rrc\n", rc));
267 if (pcbActual)
268 LogFlowFunc(("*pcbActual=%u\n", *pcbActual));
269 return rc;
270}
271
272/**
273 * Convert Utf-8 text with CR end-of-lines into Utf-16 as Windows expects it
274 * and put the result in a user-supplied buffer.
275 * @returns IPRT status code
276 * @returns VERR_BUFFER_OVERFLOW if the buffer is not large enough
277 * @param pcSrc The source Utf-8
278 * @param cbSrc The size of the source in bytes, not counting the
279 * terminating zero
280 * @param pvBuf The buffer to write the result into
281 * @param cbBuf The size of the buffer
282 * @param pcbActual On success, where to store the number of bytes written.
283 * On overflow, the buffer size needed. Undefined
284 * otherwise. Optional
285 */
286static int clipUtf8ToWinTxt(const char *pcSrc, unsigned cbSrc, void *pvBuf,
287 unsigned cbBuf, uint32_t *pcbActual)
288{
289 LogFlowFunc (("pcSrc = %.*s, cbSrc=%d, cbBuf=%d\n", cbSrc, pcSrc, cbSrc,
290 cbBuf));
291 AssertPtrReturn(pcSrc, VERR_INVALID_POINTER);
292 AssertPtrReturn(pvBuf, VERR_INVALID_POINTER);
293 if (pcbActual)
294 *pcbActual = 0;
295 /* Intermediate conversion to UTF16 */
296 size_t cwcTmp;
297 PRTUTF16 pwcTmp = NULL;
298 int rc = RTStrToUtf16Ex(pcSrc, cbSrc, &pwcTmp, 0, &cwcTmp);
299 if (RT_SUCCESS(rc))
300 rc = clipUtf16ToWinTxt(pwcTmp, cwcTmp, pvBuf, cbBuf, pcbActual);
301 RTUtf16Free(pwcTmp);
302 LogFlowFunc(("Returning %Rrc\n", rc));
303 if (pcbActual)
304 LogFlowFunc(("*pcbActual=%u\n", *pcbActual));
305 return rc;
306}
307
308/**
309 * Convert COMPOUND TEXT with CR end-of-lines into Utf-16 as Windows expects
310 * it and put the result in a user-supplied buffer.
311 * @returns IPRT status code
312 * @returns VERR_BUFFER_OVERFLOW if the buffer is not large enough
313 * @param widget An Xt widget, necessary because we use Xt/Xlib for the
314 * conversion
315 * @param pcSrc The source text
316 * @param cbSrc The size of the source in bytes, not counting the
317 * terminating zero
318 * @param pvBuf The buffer to write the result into
319 * @param cbBuf The size of the buffer
320 * @param pcbActual On success, where to store the number of bytes written.
321 * On overflow, the buffer size needed. Undefined
322 * otherwise. Optional
323 */
324static int clipCTextToWinTxt(Widget widget, unsigned char *pcSrc,
325 unsigned cbSrc, void *pvBuf, unsigned cbBuf,
326 uint32_t *pcbActual)
327{
328 LogFlowFunc (("widget=%p, pcSrc=%.*s, cbSrc=%u, pvBuf=%p, cbBuf=%u\n",
329 widget, cbSrc, (char *) pcSrc, cbSrc, pvBuf, cbBuf));
330 AssertReturn(widget, VERR_INVALID_PARAMETER);
331 AssertPtrReturn(pcSrc, VERR_INVALID_POINTER);
332 AssertPtrReturn(pvBuf, VERR_INVALID_POINTER);
333
334 /* Special case as X*TextProperty* can't seem to handle empty strings. */
335 if (cbSrc == 0)
336 {
337 *pcbActual = 2;
338 if (cbBuf < 2)
339 return VERR_BUFFER_OVERFLOW;
340 *(PRTUTF16) pvBuf = 0;
341 return VINF_SUCCESS;
342 }
343
344 if (pcbActual)
345 *pcbActual = 0;
346 /* Intermediate conversion to Utf8 */
347 int rc = VINF_SUCCESS;
348 XTextProperty property;
349 char **ppcTmp = NULL;
350 int cProps;
351
352 property.value = pcSrc;
353 property.encoding = clipGetAtom(widget, "COMPOUND_TEXT");
354 property.format = 8;
355 property.nitems = cbSrc;
356#ifdef X_HAVE_UTF8_STRING
357 int xrc = Xutf8TextPropertyToTextList(XtDisplay(widget), &property,
358 &ppcTmp, &cProps);
359#else
360 int xrc = XmbTextPropertyToTextList(XtDisplay(widget), &property,
361 &ppcTmp, &cProps);
362#endif
363 if (xrc < 0)
364 rc = ( xrc == XNoMemory ? VERR_NO_MEMORY
365 : xrc == XLocaleNotSupported ? VERR_NOT_SUPPORTED
366 : xrc == XConverterNotFound ? VERR_NOT_SUPPORTED
367 : VERR_UNRESOLVED_ERROR);
368 /* Now convert the UTF8 to UTF16 */
369 if (RT_SUCCESS(rc))
370 rc = clipUtf8ToWinTxt(*ppcTmp, strlen(*ppcTmp), pvBuf, cbBuf,
371 pcbActual);
372 if (ppcTmp != NULL)
373 XFreeStringList(ppcTmp);
374 LogFlowFunc(("Returning %Rrc\n", rc));
375 if (pcbActual)
376 LogFlowFunc(("*pcbActual=%u\n", *pcbActual));
377 return rc;
378}
379
380/**
381 * Convert Latin-1 text with CR end-of-lines into Utf-16 as Windows expects
382 * it and put the result in a user-supplied buffer.
383 * @returns IPRT status code
384 * @returns VERR_BUFFER_OVERFLOW if the buffer is not large enough
385 * @param pcSrc The source text
386 * @param cbSrc The size of the source in bytes, not counting the
387 * terminating zero
388 * @param pvBuf The buffer to write the result into
389 * @param cbBuf The size of the buffer
390 * @param pcbActual On success, where to store the number of bytes written.
391 * On overflow, the buffer size needed. Undefined
392 * otherwise. Optional
393 */
394static int clipLatin1ToWinTxt(char *pcSrc, unsigned cbSrc, void *pvBuf,
395 size_t cbBuf, uint32_t *pcbActual)
396{
397 LogFlowFunc (("pcSrc=%.*s, cbSrc=%u, pvBuf=%p, cbBuf=%u\n", cbSrc,
398 (char *) pcSrc, cbSrc, pvBuf, cbBuf));
399 AssertPtrReturn(pcSrc, VERR_INVALID_POINTER);
400 AssertPtrReturn(pvBuf, VERR_INVALID_POINTER);
401 int rc = VINF_SUCCESS;
402
403 /* Calculate the space needed */
404 unsigned cwcDest = 0;
405 for (unsigned i = 0; i < cbSrc && pcSrc[i] != '\0'; ++i)
406 if (pcSrc[i] == LINEFEED)
407 cwcDest += 2;
408 else
409 ++cwcDest;
410 ++cwcDest; /* Leave space for the terminator */
411 if (pcbActual)
412 *pcbActual = cwcDest * 2;
413 if (cbBuf < cwcDest * 2)
414 rc = VERR_BUFFER_OVERFLOW;
415
416 /* And do the convertion, bearing in mind that Latin-1 expands "naturally"
417 * to Utf-16. */
418 if (RT_SUCCESS(rc))
419 {
420 PRTUTF16 pwcDest = (PRTUTF16) pvBuf;
421 for (unsigned i = 0, j = 0; i < cbSrc; ++i, ++j)
422 if (pcSrc[i] != LINEFEED)
423 pwcDest[j] = pcSrc[i];
424 else
425 {
426 pwcDest[j] = CARRIAGERETURN;
427 pwcDest[j + 1] = LINEFEED;
428 ++j;
429 }
430 pwcDest[cwcDest - 1] = '\0'; /* Make sure we are zero-terminated. */
431 LogFlowFunc (("converted text is %.*ls\n", cwcDest, pwcDest));
432 }
433 LogFlowFunc(("Returning %Rrc\n", rc));
434 if (pcbActual)
435 LogFlowFunc(("*pcbActual=%u\n", *pcbActual));
436 return rc;
437}
438
439/** A structure containing information about where to store a request
440 * for the X11 clipboard contents. */
441struct _CLIPX11CLIPBOARDREQ
442{
443 /** The buffer to write X11 clipboard data to (valid during a request
444 * for the clipboard contents) */
445 void *pv;
446 /** The size of the buffer to write X11 clipboard data to (valid during
447 * a request for the clipboard contents) */
448 unsigned cb;
449 /** The size of the X11 clipboard data written to the buffer (valid
450 * during a request for the clipboard contents) */
451 uint32_t *pcbActual;
452 /** The format VBox would like the data in */
453 uint32_t format;
454 /** Return code for the request processing code */
455 int rc;
456 /** Semaphore which is signalled when the request is completed */
457 RTSEMEVENT finished;
458 /** The clipboard context this request is associated with */
459 CLIPBACKEND *pCtx;
460};
461
462typedef struct _CLIPX11CLIPBOARDREQ CLIPX11CLIPBOARDREQ;
463
464/**
465 * Convert the text obtained from the X11 clipboard to UTF-16LE with Windows
466 * EOLs, place it in the buffer supplied and signal that data has arrived.
467 * @note X11 backend code, callback for XtGetSelectionValue, for use when
468 * the X11 clipboard contains a text format we understand.
469 */
470static void clipConvertX11CB(Widget widget, XtPointer pClientData,
471 Atom * /* selection */, Atom *atomType,
472 XtPointer pvSrc, long unsigned int *pcLen,
473 int *piFormat)
474{
475 CLIPX11CLIPBOARDREQ *pReq = (CLIPX11CLIPBOARDREQ *) pClientData;
476 LogFlowFunc(("pReq->pv=%p, pReq->cb=%u, pReq->format=%02X, pReq->pCtx=%p\n",
477 pReq->pv, pReq->cb, pReq->format, pReq->pCtx));
478 AssertPtr(pReq->pv); /* We can't really return either... */
479 AssertPtr(pReq->pCtx);
480 Assert(pReq->format != 0); /* sanity */
481 int rc = VINF_SUCCESS;
482 CLIPBACKEND *pCtx = pReq->pCtx;
483 unsigned cbSrc = (*pcLen) * (*piFormat) / 8;
484
485 if ( pCtx->fOwnsClipboard
486 /* We don't want to request data from ourselves! */
487 || (pvSrc == NULL)
488 /* The clipboard selection may have changed before we could get it. */
489 )
490 rc = VERR_NO_DATA;
491 else if (*atomType == XT_CONVERT_FAIL) /* Xt timeout */
492 rc = VERR_TIMEOUT;
493 else
494 {
495 /* In which format is the clipboard data? */
496 switch (pCtx->X11TextFormat)
497 {
498 case CTEXT:
499 rc = clipCTextToWinTxt(widget, (unsigned char *)pvSrc, cbSrc,
500 pReq->pv, pReq->cb, pReq->pcbActual);
501 break;
502 case UTF8:
503 {
504 /* If we are given broken Utf-8, we treat it as Latin1. Is
505 * this acceptable? */
506 if (RT_SUCCESS(RTStrValidateEncoding((char *)pvSrc)))
507 rc = clipUtf8ToWinTxt((const char *)pvSrc, cbSrc,
508 pReq->pv, pReq->cb,
509 pReq->pcbActual);
510 else
511 rc = clipLatin1ToWinTxt((char *) pvSrc, cbSrc,
512 pReq->pv, pReq->cb,
513 pReq->pcbActual);
514 break;
515 }
516 default:
517 rc = VERR_INVALID_PARAMETER;
518 }
519 }
520 if (RT_SUCCESS(rc))
521 /* The other end may cache the data, so invalidate it again. */
522 pCtx->notifyVBox = true;
523 else
524 {
525 pCtx->X11TextFormat = INVALID;
526 pCtx->X11BitmapFormat = INVALID;
527 }
528 XtFree((char *)pvSrc);
529 pReq->rc = rc;
530 RTSemEventSignal(pReq->finished);
531 LogFlowFunc(("rc=%Rrc\n", rc));
532}
533
534/**
535 * Notify the host clipboard about the data formats we support, based on the
536 * "targets" (available data formats) information obtained from the X11
537 * clipboard.
538 * @note X11 backend code, callback for XtGetSelectionValue, called when we
539 * poll for available targets.
540 */
541static void vboxClipboardGetTargetsFromX11(Widget,
542 XtPointer pClientData,
543 Atom * /* selection */,
544 Atom *atomType,
545 XtPointer pValue,
546 long unsigned int *pcLen,
547 int *piFormat)
548{
549 CLIPBACKEND *pCtx =
550 reinterpret_cast<CLIPBACKEND *>(pClientData);
551 Atom *atomTargets = reinterpret_cast<Atom *>(pValue);
552 unsigned cAtoms = *pcLen;
553 CLIPFORMAT enmBestTarget = INVALID;
554 Atom atomBestTarget = None;
555
556 Log3 (("%s: called\n", __PRETTY_FUNCTION__));
557 if ( (*atomType == XT_CONVERT_FAIL) /* timeout */
558 || (pCtx->fOwnsClipboard == true) /* VBox currently owns the
559 * clipboard */
560 )
561 {
562 pCtx->atomX11TextFormat = None;
563 pCtx->X11TextFormat = INVALID;
564 return;
565 }
566
567 for (unsigned i = 0; i < cAtoms; ++i)
568 {
569 for (unsigned j = 0; j < RT_ELEMENTS(g_aFormats); ++j)
570 {
571 Atom formatAtom = clipGetAtom(pCtx->widget,
572 g_aFormats[j].pcszAtom);
573 if (atomTargets[i] == formatAtom)
574 {
575 if ( enmBestTarget < g_aFormats[j].enmFormat)
576 {
577 enmBestTarget = g_aFormats[j].enmFormat;
578 atomBestTarget = formatAtom;
579 }
580 break;
581 }
582 }
583 }
584 pCtx->atomX11TextFormat = atomBestTarget;
585 if ((enmBestTarget != pCtx->X11TextFormat) || (pCtx->notifyVBox == true))
586 {
587 uint32_t u32Formats = 0;
588 pCtx->X11TextFormat = enmBestTarget;
589 if (enmBestTarget != INVALID)
590 u32Formats |= VBOX_SHARED_CLIPBOARD_FMT_UNICODETEXT;
591 VBoxX11ClipboardReportX11Formats(pCtx->pFrontend, u32Formats);
592 pCtx->notifyVBox = false;
593 }
594 XtFree(reinterpret_cast<char *>(pValue));
595}
596
597enum { TIMER_FREQ = 200 /* ms */ };
598
599static void vboxClipboardPollX11ForTargets(XtPointer pUserData,
600 XtIntervalId * /* hTimerId */);
601static void clipSchedulePoller(CLIPBACKEND *pCtx,
602 XtTimerCallbackProc proc);
603
604#ifndef TESTCASE
605void clipSchedulePoller(CLIPBACKEND *pCtx,
606 XtTimerCallbackProc proc)
607{
608 XtAppAddTimeOut(pCtx->appContext, TIMER_FREQ, proc, pCtx);
609}
610#endif
611
612/**
613 * This timer callback is called every 200ms to check the contents of the X11
614 * clipboard.
615 * @note X11 backend code, callback for XtAppAddTimeOut, recursively
616 * re-armed.
617 * @todo Use the XFIXES extension to check for new clipboard data when
618 * available.
619 */
620void vboxClipboardPollX11ForTargets(XtPointer pUserData,
621 XtIntervalId * /* hTimerId */)
622{
623 CLIPBACKEND *pCtx =
624 reinterpret_cast<CLIPBACKEND *>(pUserData);
625 Log3 (("%s: called\n", __PRETTY_FUNCTION__));
626 /* Get the current clipboard contents if we don't own it ourselves */
627 if (!pCtx->fOwnsClipboard)
628 {
629 Log3 (("%s: requesting the targets that the host clipboard offers\n",
630 __PRETTY_FUNCTION__));
631 XtGetSelectionValue(pCtx->widget, clipGetAtom(pCtx->widget, "CLIPBOARD"),
632 clipGetAtom(pCtx->widget, "TARGETS"),
633 vboxClipboardGetTargetsFromX11, pCtx,
634 CurrentTime);
635 }
636 /* Re-arm our timer */
637 clipSchedulePoller(pCtx, vboxClipboardPollX11ForTargets);
638}
639
640#ifndef TESTCASE
641/**
642 * The main loop of our clipboard reader.
643 * @note X11 backend code.
644 */
645static int vboxClipboardThread(RTTHREAD self, void *pvUser)
646{
647 LogRel(("Shared clipboard: starting host clipboard thread\n"));
648
649 CLIPBACKEND *pCtx =
650 reinterpret_cast<CLIPBACKEND *>(pvUser);
651 while (XtAppGetExitFlag(pCtx->appContext) == FALSE)
652 XtAppProcessEvent(pCtx->appContext, XtIMAll);
653 LogRel(("Shared clipboard: host clipboard thread terminated successfully\n"));
654 return VINF_SUCCESS;
655}
656#endif
657
658/** X11 specific uninitialisation for the shared clipboard.
659 * @note X11 backend code.
660 */
661static void vboxClipboardUninitX11(CLIPBACKEND *pCtx)
662{
663 AssertPtrReturnVoid(pCtx);
664 if (pCtx->widget)
665 {
666 /* Valid widget + invalid appcontext = bug. But don't return yet. */
667 AssertPtr(pCtx->appContext);
668 clipUnregisterContext(pCtx);
669 XtDestroyWidget(pCtx->widget);
670 }
671 pCtx->widget = NULL;
672 if (pCtx->appContext)
673 XtDestroyApplicationContext(pCtx->appContext);
674 pCtx->appContext = NULL;
675 if (pCtx->wakeupPipeRead != 0)
676 close(pCtx->wakeupPipeRead);
677 if (pCtx->wakeupPipeWrite != 0)
678 close(pCtx->wakeupPipeWrite);
679 pCtx->wakeupPipeRead = 0;
680 pCtx->wakeupPipeWrite = 0;
681}
682
683/** Worker function for stopping the clipboard which runs on the event
684 * thread. */
685static void vboxClipboardStopWorker(XtPointer pUserData, int * /* source */,
686 XtInputId * /* id */)
687{
688
689 CLIPBACKEND *pCtx = (CLIPBACKEND *)pUserData;
690
691 /* This might mean that we are getting stopped twice. */
692 Assert(pCtx->widget != NULL);
693
694 /* Set the termination flag to tell the Xt event loop to exit. We
695 * reiterate that any outstanding requests from the X11 event loop to
696 * the VBox part *must* have returned before we do this. */
697 XtAppSetExitFlag(pCtx->appContext);
698 pCtx->fOwnsClipboard = false;
699 pCtx->X11TextFormat = INVALID;
700 pCtx->X11BitmapFormat = INVALID;
701}
702
703/** X11 specific initialisation for the shared clipboard.
704 * @note X11 backend code.
705 */
706static int vboxClipboardInitX11 (CLIPBACKEND *pCtx)
707{
708 /* Create a window and make it a clipboard viewer. */
709 int cArgc = 0;
710 char *pcArgv = 0;
711 int rc = VINF_SUCCESS;
712 Display *pDisplay;
713
714 /* Make sure we are thread safe */
715 XtToolkitThreadInitialize();
716 /* Set up the Clipbard application context and main window. We call all these functions
717 directly instead of calling XtOpenApplication() so that we can fail gracefully if we
718 can't get an X11 display. */
719 XtToolkitInitialize();
720 pCtx->appContext = XtCreateApplicationContext();
721 pDisplay = XtOpenDisplay(pCtx->appContext, 0, 0, "VBoxClipboard", 0, 0, &cArgc, &pcArgv);
722 if (NULL == pDisplay)
723 {
724 LogRel(("Shared clipboard: failed to connect to the host clipboard - the window system may not be running.\n"));
725 rc = VERR_NOT_SUPPORTED;
726 }
727 if (RT_SUCCESS(rc))
728 {
729 pCtx->widget = XtVaAppCreateShell(0, "VBoxClipboard", applicationShellWidgetClass, pDisplay,
730 XtNwidth, 1, XtNheight, 1, NULL);
731 if (NULL == pCtx->widget)
732 {
733 LogRel(("Shared clipboard: failed to construct the X11 window for the host clipboard manager.\n"));
734 rc = VERR_NO_MEMORY;
735 }
736 else
737 rc = clipRegisterContext(pCtx);
738 }
739 if (RT_SUCCESS(rc))
740 {
741 XtSetMappedWhenManaged(pCtx->widget, false);
742 XtRealizeWidget(pCtx->widget);
743 /* Set up a timer to poll the host clipboard */
744 clipSchedulePoller(pCtx, vboxClipboardPollX11ForTargets);
745 }
746 /* Create the pipes */
747 int pipes[2];
748 if (!pipe(pipes))
749 {
750 pCtx->wakeupPipeRead = pipes[0];
751 pCtx->wakeupPipeWrite = pipes[1];
752 if (!XtAppAddInput(pCtx->appContext, pCtx->wakeupPipeRead,
753 (XtPointer) XtInputReadMask,
754 vboxClipboardStopWorker, (XtPointer) pCtx))
755 rc = VERR_NO_MEMORY; /* What failure means is not doc'ed. */
756 }
757 else
758 rc = RTErrConvertFromErrno(errno);
759 if (RT_FAILURE(rc))
760 vboxClipboardUninitX11(pCtx);
761 return rc;
762}
763
764/**
765 * Construct the X11 backend of the shared clipboard.
766 * @note X11 backend code
767 */
768CLIPBACKEND *VBoxX11ClipboardConstructX11
769 (VBOXCLIPBOARDCONTEXT *pFrontend)
770{
771 int rc;
772
773 CLIPBACKEND *pCtx = (CLIPBACKEND *)
774 RTMemAllocZ(sizeof(CLIPBACKEND));
775 if (pCtx && !RTEnvGet("DISPLAY"))
776 {
777 /*
778 * If we don't find the DISPLAY environment variable we assume that
779 * we are not connected to an X11 server. Don't actually try to do
780 * this then, just fail silently and report success on every call.
781 * This is important for VBoxHeadless.
782 */
783 LogRelFunc(("X11 DISPLAY variable not set -- disabling shared clipboard\n"));
784 g_fHaveX11 = false;
785 return pCtx;
786 }
787
788 g_fHaveX11 = true;
789
790 LogRel(("Initializing X11 clipboard backend\n"));
791 if (pCtx)
792 pCtx->pFrontend = pFrontend;
793 return pCtx;
794}
795
796/**
797 * Destruct the shared clipboard X11 backend.
798 * @note X11 backend code
799 */
800void VBoxX11ClipboardDestructX11(CLIPBACKEND *pCtx)
801{
802 /*
803 * Immediately return if we are not connected to the host X server.
804 */
805 if (!g_fHaveX11)
806 return;
807
808 /* We set this to NULL when the event thread exits. It really should
809 * have exited at this point, when we are about to unload the code from
810 * memory. */
811 Assert(pCtx->widget == NULL);
812}
813
814/**
815 * Announce to the X11 backend that we are ready to start.
816 */
817int VBoxX11ClipboardStartX11(CLIPBACKEND *pCtx)
818{
819 int rc = VINF_SUCCESS;
820 LogFlowFunc(("\n"));
821 /*
822 * Immediately return if we are not connected to the host X server.
823 */
824 if (!g_fHaveX11)
825 return VINF_SUCCESS;
826
827 rc = vboxClipboardInitX11(pCtx);
828#ifndef TESTCASE
829 if (RT_SUCCESS(rc))
830 {
831 rc = RTThreadCreate(&pCtx->thread, vboxClipboardThread, pCtx, 0,
832 RTTHREADTYPE_IO, RTTHREADFLAGS_WAITABLE, "SHCLIP");
833 if (RT_FAILURE(rc))
834 LogRel(("Failed to initialise the shared clipboard X11 backend.\n"));
835 }
836#endif
837 if (RT_SUCCESS(rc))
838 {
839 pCtx->fOwnsClipboard = false;
840 pCtx->notifyVBox = true;
841 }
842 return rc;
843}
844
845/** String written to the wakeup pipe. */
846#define WAKE_UP_STRING "WakeUp!"
847/** Length of the string written. */
848#define WAKE_UP_STRING_LEN ( sizeof(WAKE_UP_STRING) - 1 )
849
850/**
851 * Shut down the shared clipboard X11 backend.
852 * @note X11 backend code
853 * @note Any requests from this object to get clipboard data from VBox
854 * *must* have completed or aborted before we are called, as
855 * otherwise the X11 event loop will still be waiting for the request
856 * to return and will not be able to terminate.
857 */
858int VBoxX11ClipboardStopX11(CLIPBACKEND *pCtx)
859{
860 int rc, rcThread;
861 unsigned count = 0;
862 /*
863 * Immediately return if we are not connected to the host X server.
864 */
865 if (!g_fHaveX11)
866 return VINF_SUCCESS;
867
868 LogRelFunc(("stopping the shared clipboard X11 backend\n"));
869 /* Write to the "stop" pipe */
870 rc = write(pCtx->wakeupPipeWrite, WAKE_UP_STRING, WAKE_UP_STRING_LEN);
871 do
872 {
873 rc = RTThreadWait(pCtx->thread, 1000, &rcThread);
874 ++count;
875 Assert(RT_SUCCESS(rc) || ((VERR_TIMEOUT == rc) && (count != 5)));
876 } while ((VERR_TIMEOUT == rc) && (count < 300));
877 if (RT_SUCCESS(rc))
878 AssertRC(rcThread);
879 else
880 LogRelFunc(("rc=%Rrc\n", rc));
881 vboxClipboardUninitX11(pCtx);
882 LogFlowFunc(("returning %Rrc.\n", rc));
883 return rc;
884}
885
886/**
887 * Satisfy a request from X11 for clipboard targets supported by VBox.
888 *
889 * @returns true if we successfully convert the data to the format
890 * requested, false otherwise.
891 *
892 * @param atomTypeReturn The type of the data we are returning
893 * @param pValReturn A pointer to the data we are returning. This
894 * should be set to memory allocated by XtMalloc,
895 * which will be freed later by the Xt toolkit.
896 * @param pcLenReturn The length of the data we are returning
897 * @param piFormatReturn The format (8bit, 16bit, 32bit) of the data we are
898 * returning
899 * @note X11 backend code, called by the XtOwnSelection callback.
900 */
901static Boolean vboxClipboardConvertTargetsForX11(CLIPBACKEND
902 *pCtx,
903 Atom *atomTypeReturn,
904 XtPointer *pValReturn,
905 unsigned long *pcLenReturn,
906 int *piFormatReturn)
907{
908 unsigned uListSize = RT_ELEMENTS(g_aFormats);
909 Atom *atomTargets = reinterpret_cast<Atom *>(XtMalloc((uListSize + 3) * sizeof(Atom)));
910 unsigned cTargets = 0;
911
912 LogFlowFunc (("called\n"));
913 for (unsigned i = 0; i < uListSize; ++i)
914 {
915 if ( (pCtx->vboxFormats & VBOX_SHARED_CLIPBOARD_FMT_UNICODETEXT)
916 && ( g_aFormats[i].u32VBoxFormat
917 == VBOX_SHARED_CLIPBOARD_FMT_UNICODETEXT))
918 {
919 atomTargets[cTargets] = clipGetAtom(pCtx->widget,
920 g_aFormats[i].pcszAtom);
921 ++cTargets;
922 }
923 }
924 atomTargets[cTargets] = clipGetAtom(pCtx->widget, "TARGETS");
925 atomTargets[cTargets + 1] = clipGetAtom(pCtx->widget, "MULTIPLE");
926 atomTargets[cTargets + 2] = clipGetAtom(pCtx->widget, "TIMESTAMP");
927 *atomTypeReturn = XA_ATOM;
928 *pValReturn = reinterpret_cast<XtPointer>(atomTargets);
929 *pcLenReturn = cTargets + 3;
930 *piFormatReturn = 32;
931 return true;
932}
933
934/** This is a wrapper around VBoxX11ClipboardReadVBoxData that will cache the
935 * data returned. This is unfortunately necessary, because if the other side
936 * of the shared clipboard is also an X11 system, it may send a format
937 * announcement message every time its clipboard is read, for reasons that
938 * are explained elsewhere. Unfortunately, some applications on our side
939 * like to read the clipboard several times in short succession in different
940 * formats. This can fail if it collides with a format announcement message.
941 * @todo any ideas about how to do this better are welcome.
942 */
943static int vboxClipboardReadVBoxData (CLIPBACKEND *pCtx,
944 uint32_t u32Format, void **ppv,
945 uint32_t *pcb)
946{
947 int rc = VINF_SUCCESS;
948 LogFlowFunc(("pCtx=%p, u32Format=%02X, ppv=%p, pcb=%p\n", pCtx,
949 u32Format, ppv, pcb));
950 if (u32Format == VBOX_SHARED_CLIPBOARD_FMT_UNICODETEXT)
951 {
952 if (pCtx->pvUnicodeCache == NULL)
953 rc = VBoxX11ClipboardReadVBoxData(pCtx->pFrontend, u32Format,
954 &pCtx->pvUnicodeCache,
955 &pCtx->cbUnicodeCache);
956 if (RT_SUCCESS(rc))
957 {
958 *ppv = RTMemDup(pCtx->pvUnicodeCache, pCtx->cbUnicodeCache);
959 *pcb = pCtx->cbUnicodeCache;
960 if (*ppv == NULL)
961 rc = VERR_NO_MEMORY;
962 }
963 }
964 else
965 rc = VBoxX11ClipboardReadVBoxData(pCtx->pFrontend, u32Format,
966 ppv, pcb);
967 LogFlowFunc(("returning %Rrc\n", rc));
968 if (RT_SUCCESS(rc))
969 LogFlowFunc(("*ppv=%.*ls, *pcb=%u\n", *pcb, *ppv, *pcb));
970 return rc;
971}
972
973/**
974 * Satisfy a request from X11 to convert the clipboard text to Utf8. We
975 * return non-zero terminated text.
976 * @todo that works, but it is bad. Change it to return zero-terminated
977 * text.
978 *
979 * @returns true if we successfully convert the data to the format
980 * requested, false otherwise.
981 *
982 * @param atomTypeReturn Where to store the atom for the type of the data
983 * we are returning
984 * @param pValReturn Where to store the pointer to the data we are
985 * returning. This should be to memory allocated by
986 * XtMalloc, which will be freed by the Xt toolkit
987 * later.
988 * @param pcLenReturn Where to store the length of the data we are
989 * returning
990 * @param piFormatReturn Where to store the bit width (8, 16, 32) of the
991 * data we are returning
992 * @note X11 backend code, called by the callback for XtOwnSelection.
993 */
994static Boolean vboxClipboardConvertToUtf8ForX11(CLIPBACKEND
995 *pCtx,
996 Atom *atomTarget,
997 Atom *atomTypeReturn,
998 XtPointer *pValReturn,
999 unsigned long *pcLenReturn,
1000 int *piFormatReturn)
1001{
1002 PRTUTF16 pu16SrcText, pu16DestText;
1003 char *pu8DestText;
1004 void *pvVBox = NULL;
1005 uint32_t cbVBox = 0;
1006 size_t cwSrcLen, cwDestLen, cbDestLen;
1007 int rc;
1008
1009 LogFlowFunc (("called\n"));
1010 /* Read the clipboard data from the guest. */
1011 rc = vboxClipboardReadVBoxData(pCtx,
1012 VBOX_SHARED_CLIPBOARD_FMT_UNICODETEXT,
1013 &pvVBox, &cbVBox);
1014 if ((rc != VINF_SUCCESS) || (cbVBox == 0))
1015 {
1016 /* If vboxClipboardReadVBoxData fails then we may be terminating */
1017 LogRelFunc (("vboxClipboardReadVBoxData returned %Rrc%s\n", rc,
1018 RT_SUCCESS(rc) ? ", cbVBox == 0" : ""));
1019 RTMemFree(pvVBox);
1020 return false;
1021 }
1022 pu16SrcText = reinterpret_cast<PRTUTF16>(pvVBox);
1023 cwSrcLen = cbVBox / 2;
1024 /* How long will the converted text be? */
1025 rc = vboxClipboardUtf16GetLinSize(pu16SrcText, cwSrcLen, &cwDestLen);
1026 if (RT_FAILURE(rc))
1027 {
1028 LogRelFunc (("clipboard conversion failed. vboxClipboardUtf16GetLinSize returned %Rrc. Abandoning.\n", rc));
1029 RTMemFree(pvVBox);
1030 AssertRCReturn(rc, false);
1031 }
1032 if (cwDestLen == 0)
1033 {
1034 LogFlowFunc(("received empty clipboard data from the guest, returning false.\n"));
1035 RTMemFree(pvVBox);
1036 return false;
1037 }
1038 pu16DestText = reinterpret_cast<PRTUTF16>(RTMemAlloc(cwDestLen * 2));
1039 if (pu16DestText == 0)
1040 {
1041 LogRelFunc (("failed to allocate %d bytes\n", cwDestLen * 2));
1042 RTMemFree(pvVBox);
1043 return false;
1044 }
1045 /* Convert the text. */
1046 rc = vboxClipboardUtf16WinToLin(pu16SrcText, cwSrcLen, pu16DestText, cwDestLen);
1047 if (RT_FAILURE(rc))
1048 {
1049 LogRelFunc (("clipboard conversion failed. vboxClipboardUtf16WinToLin() returned %Rrc. Abandoning.\n", rc));
1050 RTMemFree(reinterpret_cast<void *>(pu16DestText));
1051 RTMemFree(pvVBox);
1052 return false;
1053 }
1054 /* Allocate enough space, as RTUtf16ToUtf8Ex may fail if the
1055 space is too tightly calculated. */
1056 pu8DestText = XtMalloc(cwDestLen * 4);
1057 if (pu8DestText == 0)
1058 {
1059 LogRelFunc (("failed to allocate %d bytes\n", cwDestLen * 4));
1060 RTMemFree(reinterpret_cast<void *>(pu16DestText));
1061 RTMemFree(pvVBox);
1062 return false;
1063 }
1064 /* Convert the Utf16 string to Utf8. */
1065 rc = RTUtf16ToUtf8Ex(pu16DestText + 1, cwDestLen - 1, &pu8DestText, cwDestLen * 4,
1066 &cbDestLen);
1067 RTMemFree(reinterpret_cast<void *>(pu16DestText));
1068 if (RT_FAILURE(rc))
1069 {
1070 LogRelFunc (("clipboard conversion failed. RTUtf16ToUtf8Ex() returned %Rrc. Abandoning.\n", rc));
1071 XtFree(pu8DestText);
1072 RTMemFree(pvVBox);
1073 return false;
1074 }
1075 LogFlowFunc (("converted string is %.*s. Returning.\n", cbDestLen, pu8DestText));
1076 RTMemFree(pvVBox);
1077 *atomTypeReturn = *atomTarget;
1078 *pValReturn = reinterpret_cast<XtPointer>(pu8DestText);
1079 *pcLenReturn = cbDestLen + 1;
1080 *piFormatReturn = 8;
1081 return true;
1082}
1083
1084/**
1085 * Satisfy a request from X11 to convert the clipboard text to
1086 * COMPOUND_TEXT. We return non-zero terminated text.
1087 * @todo that works, but it is bad. Change it to return zero-terminated
1088 * text.
1089 *
1090 * @returns true if we successfully convert the data to the format
1091 * requested, false otherwise.
1092 *
1093 * @param atomTypeReturn Where to store the atom for the type of the data
1094 * we are returning
1095 * @param pValReturn Where to store the pointer to the data we are
1096 * returning. This should be to memory allocated by
1097 * XtMalloc, which will be freed by the Xt toolkit
1098 * later.
1099 * @param pcLenReturn Where to store the length of the data we are
1100 * returning
1101 * @param piFormatReturn Where to store the bit width (8, 16, 32) of the
1102 * data we are returning
1103 * @note X11 backend code, called by the callback for XtOwnSelection.
1104 */
1105static Boolean vboxClipboardConvertToCTextForX11(CLIPBACKEND
1106 *pCtx,
1107 Atom *atomTypeReturn,
1108 XtPointer *pValReturn,
1109 unsigned long *pcLenReturn,
1110 int *piFormatReturn)
1111{
1112 PRTUTF16 pu16SrcText, pu16DestText;
1113 void *pvVBox = NULL;
1114 uint32_t cbVBox = 0;
1115 char *pu8DestText = 0;
1116 size_t cwSrcLen, cwDestLen, cbDestLen;
1117 XTextProperty property;
1118 int rc;
1119
1120 LogFlowFunc (("called\n"));
1121 /* Read the clipboard data from the guest. */
1122 rc = vboxClipboardReadVBoxData(pCtx,
1123 VBOX_SHARED_CLIPBOARD_FMT_UNICODETEXT,
1124 &pvVBox, &cbVBox);
1125 if ((rc != VINF_SUCCESS) || (cbVBox == 0))
1126 {
1127 /* If vboxClipboardReadVBoxData fails then we may be terminating */
1128 LogRelFunc (("vboxClipboardReadVBoxData returned %Rrc%s\n", rc,
1129 RT_SUCCESS(rc) ? ", cbVBox == 0" : ""));
1130 RTMemFree(pvVBox);
1131 return false;
1132 }
1133 pu16SrcText = reinterpret_cast<PRTUTF16>(pvVBox);
1134 cwSrcLen = cbVBox / 2;
1135 /* How long will the converted text be? */
1136 rc = vboxClipboardUtf16GetLinSize(pu16SrcText, cwSrcLen, &cwDestLen);
1137 if (RT_FAILURE(rc))
1138 {
1139 LogRelFunc (("clipboard conversion failed. vboxClipboardUtf16GetLinSize returned %Rrc. Abandoning.\n", rc));
1140 RTMemFree(pvVBox);
1141 AssertRCReturn(rc, false);
1142 }
1143 if (cwDestLen == 0)
1144 {
1145 LogFlowFunc(("received empty clipboard data from the guest, returning false.\n"));
1146 RTMemFree(pvVBox);
1147 return false;
1148 }
1149 pu16DestText = reinterpret_cast<PRTUTF16>(RTMemAlloc(cwDestLen * 2));
1150 if (pu16DestText == 0)
1151 {
1152 LogRelFunc (("failed to allocate %d bytes\n", cwDestLen * 2));
1153 RTMemFree(pvVBox);
1154 return false;
1155 }
1156 /* Convert the text. */
1157 rc = vboxClipboardUtf16WinToLin(pu16SrcText, cwSrcLen, pu16DestText, cwDestLen);
1158 if (RT_FAILURE(rc))
1159 {
1160 LogRelFunc (("clipboard conversion failed. vboxClipboardUtf16WinToLin() returned %Rrc. Abandoning.\n", rc));
1161 RTMemFree(reinterpret_cast<void *>(pu16DestText));
1162 RTMemFree(pvVBox);
1163 return false;
1164 }
1165 /* Convert the Utf16 string to Utf8. */
1166 rc = RTUtf16ToUtf8Ex(pu16DestText + 1, cwDestLen - 1, &pu8DestText, 0, &cbDestLen);
1167 RTMemFree(reinterpret_cast<void *>(pu16DestText));
1168 if (RT_FAILURE(rc))
1169 {
1170 LogRelFunc (("clipboard conversion failed. RTUtf16ToUtf8Ex() returned %Rrc. Abandoning.\n", rc));
1171 RTMemFree(pvVBox);
1172 return false;
1173 }
1174 /* And finally (!) convert the Utf8 text to compound text. */
1175#ifdef RT_OS_SOLARIS
1176 rc = XmbTextListToTextProperty(XtDisplay(pCtx->widget), &pu8DestText, 1,
1177 XCompoundTextStyle, &property);
1178#else
1179 rc = Xutf8TextListToTextProperty(XtDisplay(pCtx->widget), &pu8DestText, 1,
1180 XCompoundTextStyle, &property);
1181#endif
1182 RTMemFree(pu8DestText);
1183 if (rc < 0)
1184 {
1185 const char *pcReason;
1186 switch(rc)
1187 {
1188 case XNoMemory:
1189 pcReason = "out of memory";
1190 break;
1191 case XLocaleNotSupported:
1192 pcReason = "locale (Utf8) not supported";
1193 break;
1194 case XConverterNotFound:
1195 pcReason = "converter not found";
1196 break;
1197 default:
1198 pcReason = "unknown error";
1199 }
1200 LogRelFunc (("Xutf8TextListToTextProperty failed. Reason: %s\n",
1201 pcReason));
1202 RTMemFree(pvVBox);
1203 return false;
1204 }
1205 LogFlowFunc (("converted string is %s. Returning.\n", property.value));
1206 RTMemFree(pvVBox);
1207 *atomTypeReturn = property.encoding;
1208 *pValReturn = reinterpret_cast<XtPointer>(property.value);
1209 *pcLenReturn = property.nitems + 1;
1210 *piFormatReturn = property.format;
1211 return true;
1212}
1213
1214/**
1215 * Return VBox's clipboard data for an X11 client.
1216 * @note X11 backend code, callback for XtOwnSelection
1217 */
1218static Boolean vboxClipboardConvertForX11(Widget widget, Atom *atomSelection,
1219 Atom *atomTarget,
1220 Atom *atomTypeReturn,
1221 XtPointer *pValReturn,
1222 unsigned long *pcLenReturn,
1223 int *piFormatReturn)
1224{
1225 CLIPFORMAT enmFormat = INVALID;
1226 CLIPBACKEND *pCtx = clipLookupContext(widget);
1227
1228 LogFlowFunc(("\n"));
1229 /* Drop requests that we receive too late. */
1230 if (!pCtx->fOwnsClipboard)
1231 return false;
1232 if ( (*atomSelection != clipGetAtom(pCtx->widget, "CLIPBOARD"))
1233 && (*atomSelection != clipGetAtom(pCtx->widget, "PRIMARY"))
1234 )
1235 {
1236 LogFlowFunc(("rc = false\n"));
1237 return false;
1238 }
1239 if (*atomTarget == clipGetAtom(pCtx->widget, "TARGETS"))
1240 {
1241 enmFormat = TARGETS;
1242 }
1243 else
1244 {
1245 for (unsigned i = 0; i < RT_ELEMENTS(g_aFormats); ++i)
1246 {
1247 if (*atomTarget == clipGetAtom(pCtx->widget,
1248 g_aFormats[i].pcszAtom))
1249 {
1250 enmFormat = g_aFormats[i].enmFormat;
1251 break;
1252 }
1253 }
1254 }
1255 switch (enmFormat)
1256 {
1257 case TARGETS:
1258 return vboxClipboardConvertTargetsForX11(pCtx, atomTypeReturn,
1259 pValReturn, pcLenReturn,
1260 piFormatReturn);
1261 case UTF8:
1262 return vboxClipboardConvertToUtf8ForX11(pCtx, atomTarget,
1263 atomTypeReturn,
1264 pValReturn, pcLenReturn,
1265 piFormatReturn);
1266 case CTEXT:
1267 return vboxClipboardConvertToCTextForX11(pCtx, atomTypeReturn,
1268 pValReturn, pcLenReturn,
1269 piFormatReturn);
1270 default:
1271 LogFunc (("bad format\n"));
1272 return false;
1273 }
1274}
1275
1276/**
1277 * This is called by the X toolkit intrinsics to let us know that another
1278 * X11 client has taken the clipboard. In this case we notify VBox that
1279 * we want ownership of the clipboard.
1280 * @note X11 backend code, callback for XtOwnSelection
1281 */
1282static void vboxClipboardReturnToX11(Widget widget, Atom *)
1283{
1284 CLIPBACKEND *pCtx = clipLookupContext(widget);
1285 LogFlowFunc (("called, giving X11 clipboard ownership\n"));
1286 /* These should be set to the right values as soon as we start polling */
1287 pCtx->X11TextFormat = INVALID;
1288 pCtx->X11BitmapFormat = INVALID;
1289 pCtx->fOwnsClipboard = false;
1290 pCtx->notifyVBox = true;
1291}
1292
1293static void clipSchedule(XtAppContext app_context, XtTimerCallbackProc proc,
1294 XtPointer client_data);
1295#ifndef TESTCASE
1296void clipSchedule(XtAppContext app_context, XtTimerCallbackProc proc,
1297 XtPointer client_data)
1298{
1299 XtAppAddTimeOut(app_context, 0, proc, client_data);
1300}
1301#endif
1302
1303/** Structure used to pass information about formats that VBox supports */
1304typedef struct _VBOXCLIPBOARDFORMATS
1305{
1306 /** Context information for the X11 clipboard */
1307 CLIPBACKEND *pCtx;
1308 /** Formats supported by VBox */
1309 uint32_t formats;
1310} VBOXCLIPBOARDFORMATS;
1311
1312/** Worker function for VBoxX11ClipboardAnnounceVBoxFormat which runs on the
1313 * event thread. */
1314static void vboxClipboardAnnounceWorker(XtPointer pUserData,
1315 XtIntervalId * /* interval */)
1316{
1317 /* Extract and free the user data */
1318 VBOXCLIPBOARDFORMATS *pFormats = (VBOXCLIPBOARDFORMATS *)pUserData;
1319 CLIPBACKEND *pCtx = pFormats->pCtx;
1320 uint32_t u32Formats = pFormats->formats;
1321 RTMemFree(pFormats);
1322 LogFlowFunc (("u32Formats=%d\n", u32Formats));
1323 pCtx->vboxFormats = u32Formats;
1324 /* Invalidate the clipboard cache */
1325 if (pCtx->pvUnicodeCache != NULL)
1326 {
1327 RTMemFree(pCtx->pvUnicodeCache);
1328 pCtx->pvUnicodeCache = NULL;
1329 }
1330 if (u32Formats == 0)
1331 {
1332 /* This is just an automatism, not a genuine anouncement */
1333 XtDisownSelection(pCtx->widget, clipGetAtom(pCtx->widget, "CLIPBOARD"), CurrentTime);
1334 pCtx->fOwnsClipboard = false;
1335 LogFlowFunc(("returning\n"));
1336 return;
1337 }
1338 Log2 (("%s: giving the guest clipboard ownership\n", __PRETTY_FUNCTION__));
1339 if (XtOwnSelection(pCtx->widget, clipGetAtom(pCtx->widget, "CLIPBOARD"), CurrentTime,
1340 vboxClipboardConvertForX11, vboxClipboardReturnToX11,
1341 0) == True)
1342 {
1343 pCtx->fOwnsClipboard = true;
1344 /* Grab the middle-button paste selection too. */
1345 XtOwnSelection(pCtx->widget, clipGetAtom(pCtx->widget, "PRIMARY"), CurrentTime,
1346 vboxClipboardConvertForX11, NULL, 0);
1347 }
1348 else
1349 {
1350 /* Another X11 client claimed the clipboard just after us, so let it
1351 * go again. */
1352 Log2 (("%s: returning clipboard ownership to the X11\n",
1353 __PRETTY_FUNCTION__));
1354 /* VBox thinks it currently owns the clipboard, so we must notify it
1355 * as soon as we know what formats X11 has to offer. */
1356 pCtx->notifyVBox = true;
1357 pCtx->fOwnsClipboard = false;
1358 }
1359 LogFlowFunc(("returning\n"));
1360}
1361
1362/**
1363 * VBox is taking possession of the shared clipboard.
1364 *
1365 * @param u32Formats Clipboard formats the guest is offering
1366 * @note X11 backend code
1367 */
1368void VBoxX11ClipboardAnnounceVBoxFormat(CLIPBACKEND *pCtx,
1369 uint32_t u32Formats)
1370{
1371 /*
1372 * Immediately return if we are not connected to the host X server.
1373 */
1374 if (!g_fHaveX11)
1375 return;
1376 /* This must be freed by the worker callback */
1377 VBOXCLIPBOARDFORMATS *pFormats =
1378 (VBOXCLIPBOARDFORMATS *) RTMemAlloc(sizeof(VBOXCLIPBOARDFORMATS));
1379 if (pFormats != NULL) /* if it is we will soon have other problems */
1380 {
1381 pFormats->pCtx = pCtx;
1382 pFormats->formats = u32Formats;
1383 clipSchedule(pCtx->appContext, vboxClipboardAnnounceWorker,
1384 (XtPointer) pFormats);
1385 }
1386}
1387
1388/** Worker function for VBoxX11ClipboardReadX11Data which runs on the event
1389 * thread. */
1390static void vboxClipboardReadX11Worker(XtPointer pUserData,
1391 XtIntervalId * /* interval */)
1392{
1393 CLIPX11CLIPBOARDREQ *pRequest = (CLIPX11CLIPBOARDREQ *)pUserData;
1394 CLIPBACKEND *pCtx = pRequest->pCtx;
1395 LogFlowFunc (("u32Format = %d, cb = %d\n", pRequest->format,
1396 pRequest->cb));
1397
1398 int rc = VINF_SUCCESS;
1399 /* Set this to start with, just in case */
1400 *pRequest->pcbActual = 0;
1401 /* Do not continue if we already own the clipboard */
1402 if (pCtx->fOwnsClipboard == true)
1403 rc = VERR_NO_DATA; /** @todo should this be VINF? */
1404 else
1405 {
1406 /*
1407 * VBox wants to read data in the given format.
1408 */
1409 if (pRequest->format == VBOX_SHARED_CLIPBOARD_FMT_UNICODETEXT)
1410 {
1411 if (pCtx->atomX11TextFormat == None)
1412 /* VBox thinks we have data and we don't */
1413 rc = VERR_NO_DATA;
1414 else
1415 /* Send out a request for the data to the current clipboard
1416 * owner */
1417 XtGetSelectionValue(pCtx->widget, clipGetAtom(pCtx->widget, "CLIPBOARD"),
1418 pCtx->atomX11TextFormat,
1419 clipConvertX11CB,
1420 reinterpret_cast<XtPointer>(pRequest),
1421 CurrentTime);
1422 }
1423 else
1424 rc = VERR_NOT_IMPLEMENTED;
1425 }
1426 if (RT_FAILURE(rc))
1427 {
1428 pRequest->rc = rc;
1429 /* The clipboard callback was never scheduled, so we must signal
1430 * that the request processing is finished ourselves. */
1431 RTSemEventSignal(pRequest->finished);
1432 }
1433 LogFlowFunc(("status %Rrc\n", rc));
1434}
1435
1436/**
1437 * Called when VBox wants to read the X11 clipboard.
1438 *
1439 * @param pClient Context information about the guest VM
1440 * @param u32Format The format that the guest would like to receive the data in
1441 * @param pv Where to write the data to
1442 * @param cb The size of the buffer to write the data to
1443 * @param pcbActual Where to write the actual size of the written data
1444 * @note X11 backend code
1445 */
1446int VBoxX11ClipboardReadX11Data(CLIPBACKEND *pCtx,
1447 uint32_t u32Format, void *pv, uint32_t cb,
1448 uint32_t *pcbActual)
1449{
1450 /* Initially set the size of the data read to zero in case we fail
1451 * somewhere or X11 is not available. */
1452 *pcbActual = 0;
1453
1454 /*
1455 * Immediately return if we are not connected to the host X server.
1456 */
1457 if (!g_fHaveX11)
1458 return VINF_SUCCESS;
1459
1460 CLIPX11CLIPBOARDREQ request;
1461 request.rc = VERR_WRONG_ORDER;
1462 request.pv = pv;
1463 request.cb = cb;
1464 request.pcbActual = pcbActual;
1465 request.format = u32Format;
1466 request.pCtx = pCtx;
1467 /* The worker function will signal this when it has finished. */
1468 int rc = RTSemEventCreate(&request.finished);
1469 if (RT_SUCCESS(rc))
1470 {
1471 /* We use this to schedule a worker function on the event thread. */
1472 clipSchedule(pCtx->appContext, vboxClipboardReadX11Worker,
1473 (XtPointer) &request);
1474 rc = RTSemEventWait(request.finished, RT_INDEFINITE_WAIT);
1475 if (RT_SUCCESS(rc))
1476 rc = request.rc;
1477 RTSemEventDestroy(request.finished);
1478 }
1479 return rc;
1480}
1481
1482#ifdef TESTCASE
1483
1484#include <iprt/initterm.h>
1485#include <iprt/stream.h>
1486#include <poll.h>
1487
1488#define TEST_NAME "tstClipboardX11"
1489#define TEST_WIDGET (Widget)0xffff
1490
1491/* Our X11 clipboard target poller */
1492static XtTimerCallbackProc g_pfnPoller = NULL;
1493/* User data for the poller function. */
1494static XtPointer g_pPollerData = NULL;
1495
1496/* For the testcase, we install the poller function in a global variable
1497 * which is called when the testcase updates the X11 targets. */
1498void clipSchedulePoller(CLIPBACKEND *pCtx,
1499 XtTimerCallbackProc proc)
1500{
1501 g_pfnPoller = proc;
1502 g_pPollerData = (XtPointer)pCtx;
1503}
1504
1505static bool clipPollTargets()
1506{
1507 if (!g_pfnPoller)
1508 return false;
1509 g_pfnPoller(g_pPollerData, NULL);
1510 return true;
1511}
1512
1513/* For the purpose of the test case, we just execute the procedure to be
1514 * scheduled, as we are running single threaded. */
1515void clipSchedule(XtAppContext app_context, XtTimerCallbackProc proc,
1516 XtPointer client_data)
1517{
1518 proc(client_data, NULL);
1519}
1520
1521void XtFree(char *ptr)
1522{ RTMemFree((void *) ptr); }
1523
1524/* The data in the simulated VBox clipboard */
1525static int g_vboxDataRC = VINF_SUCCESS;
1526static void *g_vboxDatapv = NULL;
1527static uint32_t g_vboxDatacb = 0;
1528
1529/* Set empty data in the simulated VBox clipboard. */
1530static void clipEmptyVBox(CLIPBACKEND *pCtx, int retval)
1531{
1532 g_vboxDataRC = retval;
1533 RTMemFree(g_vboxDatapv);
1534 g_vboxDatapv = NULL;
1535 g_vboxDatacb = 0;
1536 VBoxX11ClipboardAnnounceVBoxFormat(pCtx, 0);
1537}
1538
1539/* Set the data in the simulated VBox clipboard. */
1540static int clipSetVBoxUtf16(CLIPBACKEND *pCtx, int retval,
1541 const char *pcszData, size_t cb)
1542{
1543 PRTUTF16 pwszData = NULL;
1544 size_t cwData = 0;
1545 int rc = RTStrToUtf16Ex(pcszData, RTSTR_MAX, &pwszData, 0, &cwData);
1546 if (RT_FAILURE(rc))
1547 return rc;
1548 AssertReturn(cb <= cwData * 2 + 2, VERR_BUFFER_OVERFLOW);
1549 void *pv = RTMemDup(pwszData, cb);
1550 RTUtf16Free(pwszData);
1551 if (pv == NULL)
1552 return VERR_NO_MEMORY;
1553 if (g_vboxDatapv)
1554 RTMemFree(g_vboxDatapv);
1555 g_vboxDataRC = retval;
1556 g_vboxDatapv = pv;
1557 g_vboxDatacb = cb;
1558 VBoxX11ClipboardAnnounceVBoxFormat(pCtx,
1559 VBOX_SHARED_CLIPBOARD_FMT_UNICODETEXT);
1560 return VINF_SUCCESS;
1561}
1562
1563/* Return the data in the simulated VBox clipboard. */
1564int VBoxX11ClipboardReadVBoxData(VBOXCLIPBOARDCONTEXT *pCtx,
1565 uint32_t u32Format, void **ppv,
1566 uint32_t *pcb)
1567{
1568 *pcb = g_vboxDatacb;
1569 if (g_vboxDatapv != NULL)
1570 {
1571 void *pv = RTMemDup(g_vboxDatapv, g_vboxDatacb);
1572 *ppv = pv;
1573 return pv != NULL ? g_vboxDataRC : VERR_NO_MEMORY;
1574 }
1575 *ppv = NULL;
1576 return g_vboxDataRC;
1577}
1578
1579Display *XtDisplay(Widget w)
1580{ return (Display *) 0xffff; }
1581
1582int XmbTextListToTextProperty(Display *display, char **list, int count,
1583 XICCEncodingStyle style,
1584 XTextProperty *text_prop_return)
1585{
1586 /* We don't fully reimplement this API for obvious reasons. */
1587 AssertReturn(count == 1, XLocaleNotSupported);
1588 AssertReturn(style == XCompoundTextStyle, XLocaleNotSupported);
1589 /* We simplify the conversion by only accepting ASCII. */
1590 for (unsigned i = 0; (*list)[i] != 0; ++i)
1591 AssertReturn(((*list)[i] & 0x80) == 0, XLocaleNotSupported);
1592 text_prop_return->value =
1593 (unsigned char*)RTMemDup(*list, strlen(*list) + 1);
1594 text_prop_return->encoding = clipGetAtom(NULL, "COMPOUND_TEXT");
1595 text_prop_return->format = 8;
1596 text_prop_return->nitems = strlen(*list);
1597 return 0;
1598}
1599
1600int Xutf8TextListToTextProperty(Display *display, char **list, int count,
1601 XICCEncodingStyle style,
1602 XTextProperty *text_prop_return)
1603{
1604 return XmbTextListToTextProperty(display, list, count, style,
1605 text_prop_return);
1606}
1607
1608int XmbTextPropertyToTextList(Display *display,
1609 const XTextProperty *text_prop,
1610 char ***list_return, int *count_return)
1611{
1612 int rc = 0;
1613 if (text_prop->nitems == 0)
1614 {
1615 *list_return = NULL;
1616 *count_return = 0;
1617 return 0;
1618 }
1619 /* Only accept simple ASCII properties */
1620 for (unsigned i = 0; i < text_prop->nitems; ++i)
1621 AssertReturn(!(text_prop->value[i] & 0x80), XConverterNotFound);
1622 char **ppList = (char **)RTMemAlloc(sizeof(char *));
1623 char *pValue = (char *)RTMemAlloc(text_prop->nitems + 1);
1624 if (pValue)
1625 {
1626 memcpy(pValue, text_prop->value, text_prop->nitems);
1627 pValue[text_prop->nitems] = 0;
1628 }
1629 if (ppList)
1630 *ppList = pValue;
1631 if (!ppList || !pValue)
1632 {
1633 RTMemFree(ppList);
1634 RTMemFree(pValue);
1635 rc = XNoMemory;
1636 }
1637 else
1638 {
1639 /* NULL-terminate the string */
1640 pValue[text_prop->nitems] = '\0';
1641 *count_return = 1;
1642 *list_return = ppList;
1643 }
1644 return rc;
1645}
1646
1647int Xutf8TextPropertyToTextList(Display *display,
1648 const XTextProperty *text_prop,
1649 char ***list_return, int *count_return)
1650{
1651 return XmbTextPropertyToTextList(display, text_prop, list_return,
1652 count_return);
1653}
1654
1655void XtAppSetExitFlag(XtAppContext app_context) {}
1656
1657void XtDestroyWidget(Widget w) {}
1658
1659XtAppContext XtCreateApplicationContext(void) { return (XtAppContext)0xffff; }
1660
1661void XtDestroyApplicationContext(XtAppContext app_context) {}
1662
1663void XtToolkitInitialize(void) {}
1664
1665Boolean XtToolkitThreadInitialize(void) { return True; }
1666
1667Display *XtOpenDisplay(XtAppContext app_context,
1668 _Xconst _XtString display_string,
1669 _Xconst _XtString application_name,
1670 _Xconst _XtString application_class,
1671 XrmOptionDescRec *options, Cardinal num_options,
1672 int *argc, char **argv)
1673{ return (Display *)0xffff; }
1674
1675Widget XtVaAppCreateShell(_Xconst _XtString application_name,
1676 _Xconst _XtString application_class,
1677 WidgetClass widget_class, Display *display, ...)
1678{ return TEST_WIDGET; }
1679
1680void XtSetMappedWhenManaged(Widget widget, _XtBoolean mapped_when_managed) {}
1681
1682void XtRealizeWidget(Widget widget) {}
1683
1684XtInputId XtAppAddInput(XtAppContext app_context, int source,
1685 XtPointer condition, XtInputCallbackProc proc,
1686 XtPointer closure)
1687{ return 0xffff; }
1688
1689/* Atoms we need other than the formats we support. */
1690static const char *g_apszSupAtoms[] =
1691{
1692 "PRIMARY", "CLIPBOARD", "TARGETS", "MULTIPLE", "TIMESTAMP"
1693};
1694
1695/* This just looks for the atom names in a couple of tables and returns an
1696 * index with an offset added. */
1697Boolean XtConvertAndStore(Widget widget, _Xconst _XtString from_type,
1698 XrmValue* from, _Xconst _XtString to_type,
1699 XrmValue* to_in_out)
1700{
1701 Boolean rc = False;
1702 /* What we support is: */
1703 AssertReturn(from_type == XtRString, False);
1704 AssertReturn(to_type == XtRAtom, False);
1705 for (unsigned i = 0; i < RT_ELEMENTS(g_aFormats); ++i)
1706 if (!strcmp(from->addr, g_aFormats[i].pcszAtom))
1707 {
1708 *(Atom *)(to_in_out->addr) = (Atom) (i + 0x1000);
1709 rc = True;
1710 }
1711 for (unsigned i = 0; i < RT_ELEMENTS(g_apszSupAtoms); ++i)
1712 if (!strcmp(from->addr, g_apszSupAtoms[i]))
1713 {
1714 *(Atom *)(to_in_out->addr) = (Atom) (i + 0x2000);
1715 rc = True;
1716 }
1717 Assert(rc == True); /* Have we missed any atoms? */
1718 return rc;
1719}
1720
1721/* The current values of the X selection, which will be returned to the
1722 * XtGetSelectionValue callback. */
1723static Atom g_selTarget = 0;
1724static Atom g_selType = 0;
1725static const void *g_pSelData = NULL;
1726static unsigned long g_cSelData = 0;
1727static int g_selFormat = 0;
1728
1729void XtGetSelectionValue(Widget widget, Atom selection, Atom target,
1730 XtSelectionCallbackProc callback,
1731 XtPointer closure, Time time)
1732{
1733 unsigned long count = 0;
1734 int format = 0;
1735 Atom type = XA_STRING;
1736 if ( ( selection != clipGetAtom(NULL, "PRIMARY")
1737 && selection != clipGetAtom(NULL, "CLIPBOARD")
1738 && selection != clipGetAtom(NULL, "TARGETS"))
1739 || ( target != g_selTarget
1740 && target != clipGetAtom(NULL, "TARGETS")))
1741 {
1742 /* Otherwise this is probably a caller error. */
1743 Assert(target != g_selTarget);
1744 callback(widget, closure, &selection, &type, NULL, &count, &format);
1745 /* Could not convert to target. */
1746 return;
1747 }
1748 XtPointer pValue = NULL;
1749 if (target == clipGetAtom(NULL, "TARGETS"))
1750 {
1751 pValue = (XtPointer) RTMemDup(&g_selTarget, sizeof(g_selTarget));
1752 type = XA_ATOM;
1753 count = 1;
1754 format = 32;
1755 }
1756 else
1757 {
1758 pValue = (XtPointer) g_pSelData ? RTMemDup(g_pSelData, g_cSelData)
1759 : NULL;
1760 type = g_selType;
1761 count = g_pSelData ? g_cSelData : 0;
1762 format = g_selFormat;
1763 }
1764 if (!pValue)
1765 {
1766 count = 0;
1767 format = 0;
1768 }
1769 callback(widget, closure, &selection, &type, pValue,
1770 &count, &format);
1771}
1772
1773/* The formats currently on offer from X11 via the shared clipboard */
1774static uint32_t g_fX11Formats = 0;
1775
1776void VBoxX11ClipboardReportX11Formats(VBOXCLIPBOARDCONTEXT* pCtx,
1777 uint32_t u32Formats)
1778{
1779 g_fX11Formats = u32Formats;
1780}
1781
1782static uint32_t clipQueryFormats()
1783{
1784 return g_fX11Formats;
1785}
1786
1787/* Does our clipboard code currently own the selection? */
1788static bool g_ownsSel = false;
1789/* The procedure that is called when we should convert the selection to a
1790 * given format. */
1791static XtConvertSelectionProc g_pfnSelConvert = NULL;
1792/* The procedure which is called when we lose the selection. */
1793static XtLoseSelectionProc g_pfnSelLose = NULL;
1794/* The procedure which is called when the selection transfer has completed. */
1795static XtSelectionDoneProc g_pfnSelDone = NULL;
1796
1797Boolean XtOwnSelection(Widget widget, Atom selection, Time time,
1798 XtConvertSelectionProc convert,
1799 XtLoseSelectionProc lose,
1800 XtSelectionDoneProc done)
1801{
1802 if (selection != clipGetAtom(NULL, "CLIPBOARD"))
1803 return True; /* We don't really care about this. */
1804 g_ownsSel = true; /* Always succeed. */
1805 g_pfnSelConvert = convert;
1806 g_pfnSelLose = lose;
1807 g_pfnSelDone = done;
1808 return True;
1809}
1810
1811void XtDisownSelection(Widget widget, Atom selection, Time time)
1812{
1813 g_ownsSel = false;
1814 g_pfnSelConvert = NULL;
1815 g_pfnSelLose = NULL;
1816 g_pfnSelDone = NULL;
1817}
1818
1819/* Request the shared clipboard to convert its data to a given format. */
1820static bool clipConvertSelection(const char *pcszTarget, Atom *type,
1821 XtPointer *value, unsigned long *length,
1822 int *format)
1823{
1824 Atom target = clipGetAtom(NULL, pcszTarget);
1825 if (target == 0)
1826 return false;
1827 /* Initialise all return values in case we make a quick exit. */
1828 *type = XA_STRING;
1829 *value = NULL;
1830 *length = 0;
1831 *format = 0;
1832 if (!g_ownsSel)
1833 return false;
1834 if (!g_pfnSelConvert)
1835 return false;
1836 Atom clipAtom = clipGetAtom(NULL, "CLIPBOARD");
1837 if (!g_pfnSelConvert(TEST_WIDGET, &clipAtom, &target, type,
1838 value, length, format))
1839 return false;
1840 if (g_pfnSelDone)
1841 g_pfnSelDone(TEST_WIDGET, &clipAtom, &target);
1842 return true;
1843}
1844
1845/* Set the current X selection data */
1846static void clipSetSelectionValues(const char *pcszTarget, Atom type,
1847 const void *data,
1848 unsigned long count, int format)
1849{
1850 Atom clipAtom = clipGetAtom(NULL, "CLIPBOARD");
1851 g_selTarget = clipGetAtom(NULL, pcszTarget);
1852 g_selType = type;
1853 g_pSelData = data;
1854 g_cSelData = count;
1855 g_selFormat = format;
1856 if (g_pfnSelLose)
1857 g_pfnSelLose(TEST_WIDGET, &clipAtom);
1858 g_ownsSel = false;
1859 g_fX11Formats = 0;
1860}
1861
1862char *XtMalloc(Cardinal size) { return (char *) RTMemAlloc(size); }
1863
1864char *XGetAtomName(Display *display, Atom atom)
1865{
1866 AssertReturn((unsigned)atom < RT_ELEMENTS(g_aFormats) + 1, NULL);
1867 const char *pcszName = NULL;
1868 if (atom < 0x1000)
1869 return NULL;
1870 else if (0x1000 <= atom && atom < 0x2000)
1871 {
1872 unsigned index = atom - 0x1000;
1873 AssertReturn(index < RT_ELEMENTS(g_aFormats), NULL);
1874 pcszName = g_aFormats[index].pcszAtom;
1875 }
1876 else
1877 {
1878 unsigned index = atom - 0x2000;
1879 AssertReturn(index < RT_ELEMENTS(g_apszSupAtoms), NULL);
1880 pcszName = g_apszSupAtoms[index];
1881 }
1882 return (char *)RTMemDup(pcszName, sizeof(pcszName) + 1);
1883}
1884
1885int XFree(void *data)
1886{
1887 RTMemFree(data);
1888 return 0;
1889}
1890
1891void XFreeStringList(char **list)
1892{
1893 if (list)
1894 RTMemFree(*list);
1895 RTMemFree(list);
1896}
1897
1898const char XtStrings [] = "";
1899_WidgetClassRec* applicationShellWidgetClass;
1900const char XtShellStrings [] = "";
1901
1902#define MAX_BUF_SIZE 256
1903
1904static bool testStringFromX11(CLIPBACKEND *pCtx, uint32_t cbBuf,
1905 const char *pcszExp, int rcExp)
1906{
1907 bool retval = false;
1908 AssertReturn(cbBuf <= MAX_BUF_SIZE, false);
1909 if (!clipPollTargets())
1910 RTPrintf("Failed to poll for targets\n");
1911 else if (clipQueryFormats() != VBOX_SHARED_CLIPBOARD_FMT_UNICODETEXT)
1912 RTPrintf("Wrong targets reported: %02X\n", clipQueryFormats());
1913 else
1914 {
1915 char pc[MAX_BUF_SIZE];
1916 uint32_t cbActual;
1917 int rc = VBoxX11ClipboardReadX11Data(pCtx,
1918 VBOX_SHARED_CLIPBOARD_FMT_UNICODETEXT,
1919 (void *) pc, cbBuf, &cbActual);
1920 if (rc != rcExp)
1921 RTPrintf("Wrong return code, expected %Rrc, got %Rrc\n", rcExp,
1922 rc);
1923 else if (RT_FAILURE(rcExp))
1924 retval = true;
1925 else
1926 {
1927 RTUTF16 wcExp[MAX_BUF_SIZE / 2];
1928 RTUTF16 *pwcExp = wcExp;
1929 size_t cwc = 0;
1930 rc = RTStrToUtf16Ex(pcszExp, RTSTR_MAX, &pwcExp,
1931 RT_ELEMENTS(wcExp), &cwc);
1932 size_t cbExp = cwc * 2 + 2;
1933 AssertRC(rc);
1934 if (RT_SUCCESS(rc))
1935 {
1936 if (cbActual != cbExp)
1937 {
1938 RTPrintf("Returned string is the wrong size, string \"%.*ls\", size %u\n",
1939 RT_MIN(MAX_BUF_SIZE, cbActual), pc, cbActual);
1940 RTPrintf("Expected \"%s\", size %u\n", pcszExp,
1941 cbExp);
1942 }
1943 else
1944 {
1945 if (memcmp(pc, wcExp, cbExp) == 0)
1946 retval = true;
1947 else
1948 RTPrintf("Returned string \"%.*ls\" does not match expected string \"%s\"\n",
1949 MAX_BUF_SIZE, pc, pcszExp);
1950 }
1951 }
1952 }
1953 }
1954 if (!retval)
1955 RTPrintf("Expected: string \"%s\", rc %Rrc (buffer size %u)\n",
1956 pcszExp, rcExp, cbBuf);
1957 return retval;
1958}
1959
1960static bool testLatin1FromX11(CLIPBACKEND *pCtx, uint32_t cbBuf,
1961 const char *pcszExp, int rcExp)
1962{
1963 bool retval = false;
1964 AssertReturn(cbBuf <= MAX_BUF_SIZE, false);
1965 if (!clipPollTargets())
1966 RTPrintf("Failed to poll for targets\n");
1967 else if (clipQueryFormats() != VBOX_SHARED_CLIPBOARD_FMT_UNICODETEXT)
1968 RTPrintf("Wrong targets reported: %02X\n", clipQueryFormats());
1969 else
1970 {
1971 char pc[MAX_BUF_SIZE];
1972 uint32_t cbActual;
1973 int rc = VBoxX11ClipboardReadX11Data(pCtx,
1974 VBOX_SHARED_CLIPBOARD_FMT_UNICODETEXT,
1975 (void *) pc, cbBuf, &cbActual);
1976 if (rc != rcExp)
1977 RTPrintf("Wrong return code, expected %Rrc, got %Rrc\n", rcExp,
1978 rc);
1979 else if (RT_FAILURE(rcExp))
1980 retval = true;
1981 else
1982 {
1983 RTUTF16 wcExp[MAX_BUF_SIZE / 2];
1984 RTUTF16 *pwcExp = wcExp;
1985 size_t cwc;
1986 for (cwc = 0; cwc == 0 || pcszExp[cwc - 1] != '\0'; ++cwc)
1987 wcExp[cwc] = pcszExp[cwc];
1988 size_t cbExp = cwc * 2;
1989 if (cbActual != cbExp)
1990 {
1991 RTPrintf("Returned string is the wrong size, string \"%.*ls\", size %u\n",
1992 RT_MIN(MAX_BUF_SIZE, cbActual), pc, cbActual);
1993 RTPrintf("Expected \"%s\", size %u\n", pcszExp,
1994 cbExp);
1995 }
1996 else
1997 {
1998 if (memcmp(pc, wcExp, cbExp) == 0)
1999 retval = true;
2000 else
2001 RTPrintf("Returned string \"%.*ls\" does not match expected string \"%s\"\n",
2002 MAX_BUF_SIZE, pc, pcszExp);
2003 }
2004 }
2005 }
2006 if (!retval)
2007 RTPrintf("Expected: string \"%s\", rc %Rrc (buffer size %u)\n",
2008 pcszExp, rcExp, cbBuf);
2009 return retval;
2010}
2011
2012static bool testStringFromVBox(CLIPBACKEND *pCtx,
2013 const char *pcszTarget, Atom typeExp,
2014 const void *valueExp, unsigned long lenExp,
2015 int formatExp)
2016{
2017 bool retval = false;
2018 Atom type;
2019 XtPointer value = NULL;
2020 unsigned long length;
2021 int format;
2022 if (clipConvertSelection(pcszTarget, &type, &value, &length, &format))
2023 {
2024 if ( type != typeExp
2025 || length != lenExp
2026 || format != formatExp
2027 || memcmp((const void *) value, (const void *)valueExp,
2028 lenExp))
2029 {
2030 RTPrintf("Bad data: type %d, (expected %d), length %u, (%u), format %d (%d),\n",
2031 type, typeExp, length, lenExp, format, formatExp);
2032 RTPrintf("value \"%.*s\" (\"%.*s\")", RT_MIN(length, 20), value,
2033 RT_MIN(lenExp, 20), valueExp);
2034 }
2035 else
2036 retval = true;
2037 }
2038 else
2039 RTPrintf("Conversion failed\n");
2040 XtFree((char *)value);
2041 if (!retval)
2042 RTPrintf("Conversion to %s, expected \"%s\"\n", pcszTarget, valueExp);
2043 return retval;
2044}
2045
2046static bool testStringFromVBoxFailed(CLIPBACKEND *pCtx,
2047 const char *pcszTarget)
2048{
2049 bool retval = false;
2050 Atom type;
2051 XtPointer value = NULL;
2052 unsigned long length;
2053 int format;
2054 if (!clipConvertSelection(pcszTarget, &type, &value, &length, &format))
2055 retval = true;
2056 XtFree((char *)value);
2057 if (!retval)
2058 {
2059 RTPrintf("Conversion to target %s, should have failed but didn't\n",
2060 pcszTarget);
2061 RTPrintf("Returned type %d, length %u, format %d, value \"%.*s\"\n",
2062 type, length, format, RT_MIN(length, 20), value);
2063 }
2064 return retval;
2065}
2066
2067int main()
2068{
2069 RTR3Init();
2070 CLIPBACKEND *pCtx = VBoxX11ClipboardConstructX11(NULL);
2071 unsigned cErrs = 0;
2072 char pc[MAX_BUF_SIZE];
2073 uint32_t cbActual;
2074 int rc = VBoxX11ClipboardStartX11(pCtx);
2075 AssertRCReturn(rc, 1);
2076
2077 /*** Utf-8 from X11 ***/
2078 RTPrintf(TEST_NAME ": TESTING reading Utf-8 from X11\n");
2079 /* Simple test */
2080 clipSetSelectionValues("UTF8_STRING", XA_STRING, "hello world",
2081 sizeof("hello world"), 8);
2082 if (!testStringFromX11(pCtx, 256, "hello world", VINF_SUCCESS))
2083 ++cErrs;
2084 /* Receiving buffer of the exact size needed */
2085 if (!testStringFromX11(pCtx, sizeof("hello world") * 2, "hello world",
2086 VINF_SUCCESS))
2087 ++cErrs;
2088 /* Buffer one too small */
2089 if (!testStringFromX11(pCtx, sizeof("hello world") * 2 - 1, "hello world",
2090 VERR_BUFFER_OVERFLOW))
2091 ++cErrs;
2092 /* Zero-size buffer */
2093 if (!testStringFromX11(pCtx, 0, "hello world", VERR_BUFFER_OVERFLOW))
2094 ++cErrs;
2095 /* With an embedded carriage return */
2096 clipSetSelectionValues("text/plain;charset=UTF-8", XA_STRING,
2097 "hello\nworld", sizeof("hello\nworld"), 8);
2098 if (!testStringFromX11(pCtx, sizeof("hello\r\nworld") * 2,
2099 "hello\r\nworld", VINF_SUCCESS))
2100 ++cErrs;
2101 /* An empty string */
2102 clipSetSelectionValues("text/plain;charset=utf-8", XA_STRING, "",
2103 sizeof(""), 8);
2104 if (!testStringFromX11(pCtx, sizeof("") * 2, "", VINF_SUCCESS))
2105 ++cErrs;
2106 /* With an embedded Utf-8 character. */
2107 clipSetSelectionValues("STRING", XA_STRING,
2108 "100\xE2\x82\xAC" /* 100 Euro */,
2109 sizeof("100\xE2\x82\xAC"), 8);
2110 if (!testStringFromX11(pCtx, sizeof("100\xE2\x82\xAC") * 2,
2111 "100\xE2\x82\xAC", VINF_SUCCESS))
2112 ++cErrs;
2113 /* A non-zero-terminated string */
2114 clipSetSelectionValues("TEXT", XA_STRING,
2115 "hello world", sizeof("hello world") - 2, 8);
2116 if (!testStringFromX11(pCtx, sizeof("hello world") * 2 - 2,
2117 "hello worl", VINF_SUCCESS))
2118 ++cErrs;
2119
2120 /*** COMPOUND TEXT from X11 ***/
2121 RTPrintf(TEST_NAME ": TESTING reading compound text from X11\n");
2122 /* Simple test */
2123 clipSetSelectionValues("COMPOUND_TEXT", XA_STRING, "hello world",
2124 sizeof("hello world"), 8);
2125 if (!testStringFromX11(pCtx, 256, "hello world", VINF_SUCCESS))
2126 ++cErrs;
2127 /* Receiving buffer of the exact size needed */
2128 if (!testStringFromX11(pCtx, sizeof("hello world") * 2, "hello world",
2129 VINF_SUCCESS))
2130 ++cErrs;
2131 /* Buffer one too small */
2132 if (!testStringFromX11(pCtx, sizeof("hello world") * 2 - 1, "hello world",
2133 VERR_BUFFER_OVERFLOW))
2134 ++cErrs;
2135 /* Zero-size buffer */
2136 if (!testStringFromX11(pCtx, 0, "hello world", VERR_BUFFER_OVERFLOW))
2137 ++cErrs;
2138 /* With an embedded carriage return */
2139 clipSetSelectionValues("COMPOUND_TEXT", XA_STRING, "hello\nworld",
2140 sizeof("hello\nworld"), 8);
2141 if (!testStringFromX11(pCtx, sizeof("hello\r\nworld") * 2,
2142 "hello\r\nworld", VINF_SUCCESS))
2143 ++cErrs;
2144 /* An empty string */
2145 clipSetSelectionValues("COMPOUND_TEXT", XA_STRING, "",
2146 sizeof(""), 8);
2147 if (!testStringFromX11(pCtx, sizeof("") * 2, "", VINF_SUCCESS))
2148 ++cErrs;
2149 /* A non-zero-terminated string */
2150 clipSetSelectionValues("COMPOUND_TEXT", XA_STRING,
2151 "hello world", sizeof("hello world") - 2, 8);
2152 if (!testStringFromX11(pCtx, sizeof("hello world") * 2 - 2,
2153 "hello worl", VINF_SUCCESS))
2154 ++cErrs;
2155
2156 /*** Latin1 from X11 ***/
2157 RTPrintf(TEST_NAME ": TESTING reading Latin1 from X11\n");
2158 /* Simple test */
2159 clipSetSelectionValues("STRING", XA_STRING, "Georges Dupr\xEA",
2160 sizeof("Georges Dupr\xEA"), 8);
2161 if (!testLatin1FromX11(pCtx, 256, "Georges Dupr\xEA", VINF_SUCCESS))
2162 ++cErrs;
2163 /* Receiving buffer of the exact size needed */
2164 if (!testLatin1FromX11(pCtx, sizeof("Georges Dupr\xEA") * 2,
2165 "Georges Dupr\xEA", VINF_SUCCESS))
2166 ++cErrs;
2167 /* Buffer one too small */
2168 if (!testLatin1FromX11(pCtx, sizeof("Georges Dupr\xEA") * 2 - 1,
2169 "Georges Dupr\xEA", VERR_BUFFER_OVERFLOW))
2170 ++cErrs;
2171 /* Zero-size buffer */
2172 if (!testLatin1FromX11(pCtx, 0, "Georges Dupr\xEA", VERR_BUFFER_OVERFLOW))
2173 ++cErrs;
2174 /* With an embedded carriage return */
2175 clipSetSelectionValues("TEXT", XA_STRING, "Georges\nDupr\xEA",
2176 sizeof("Georges\nDupr\xEA"), 8);
2177 if (!testLatin1FromX11(pCtx, sizeof("Georges\r\nDupr\xEA") * 2,
2178 "Georges\r\nDupr\xEA", VINF_SUCCESS))
2179 ++cErrs;
2180 /* A non-zero-terminated string */
2181 clipSetSelectionValues("text/plain", XA_STRING,
2182 "Georges Dupr\xEA!",
2183 sizeof("Georges Dupr\xEA!") - 2, 8);
2184 if (!testLatin1FromX11(pCtx, sizeof("Georges Dupr\xEA!") * 2 - 2,
2185 "Georges Dupr\xEA", VINF_SUCCESS))
2186 ++cErrs;
2187
2188
2189 /*** Timeout from X11 ***/
2190 RTPrintf(TEST_NAME ": TESTING X11 timeout\n");
2191 clipSetSelectionValues("UTF8_STRING", XT_CONVERT_FAIL, "hello world",
2192 sizeof("hello world"), 8);
2193 if (!testStringFromX11(pCtx, 256, "hello world", VERR_TIMEOUT))
2194 ++cErrs;
2195
2196 /*** No data in X11 clipboard ***/
2197 RTPrintf(TEST_NAME ": TESTING a data request from an empty X11 clipboard\n");
2198 clipSetSelectionValues("UTF8_STRING", XA_STRING, NULL,
2199 0, 8);
2200 rc = VBoxX11ClipboardReadX11Data(pCtx,
2201 VBOX_SHARED_CLIPBOARD_FMT_UNICODETEXT,
2202 (void *) pc, sizeof(pc), &cbActual);
2203 if (rc != VERR_NO_DATA)
2204 {
2205 RTPrintf("Returned %Rrc instead of VERR_NO_DATA\n", rc);
2206 ++cErrs;
2207 }
2208
2209 /*** request for an invalid VBox format from X11 ***/
2210 RTPrintf(TEST_NAME ": TESTING a request for an invalid host format from X11\n");
2211 rc = VBoxX11ClipboardReadX11Data(pCtx, 0xffff, (void *) pc,
2212 sizeof(pc), &cbActual);
2213 if (rc != VERR_NOT_IMPLEMENTED)
2214 {
2215 RTPrintf("Returned %Rrc instead of VERR_NOT_IMPLEMENTED\n", rc);
2216 ++cErrs;
2217 }
2218
2219 /*** Utf-8 from VBox ***/
2220 RTPrintf(TEST_NAME ": TESTING reading Utf-8 from VBox\n");
2221 /* Simple test */
2222 clipSetVBoxUtf16(pCtx, VINF_SUCCESS, "hello world",
2223 sizeof("hello world") * 2);
2224 if (!testStringFromVBox(pCtx, "UTF8_STRING",
2225 clipGetAtom(NULL, "UTF8_STRING"),
2226 "hello world", sizeof("hello world"), 8))
2227 ++cErrs;
2228 /* With an embedded carriage return */
2229 clipSetVBoxUtf16(pCtx, VINF_SUCCESS, "hello\r\nworld",
2230 sizeof("hello\r\nworld") * 2);
2231 if (!testStringFromVBox(pCtx, "text/plain;charset=UTF-8",
2232 clipGetAtom(NULL, "text/plain;charset=UTF-8"),
2233 "hello\nworld", sizeof("hello\nworld"), 8))
2234 ++cErrs;
2235 /* An empty string */
2236 clipSetVBoxUtf16(pCtx, VINF_SUCCESS, "", 2);
2237 if (!testStringFromVBox(pCtx, "text/plain;charset=utf-8",
2238 clipGetAtom(NULL, "text/plain;charset=utf-8"),
2239 "", sizeof(""), 8))
2240 ++cErrs;
2241 /* With an embedded Utf-8 character. */
2242 clipSetVBoxUtf16(pCtx, VINF_SUCCESS, "100\xE2\x82\xAC" /* 100 Euro */,
2243 10);
2244 if (!testStringFromVBox(pCtx, "STRING",
2245 clipGetAtom(NULL, "STRING"),
2246 "100\xE2\x82\xAC", sizeof("100\xE2\x82\xAC"), 8))
2247 ++cErrs;
2248 /* A non-zero-terminated string */
2249 clipSetVBoxUtf16(pCtx, VINF_SUCCESS, "hello world",
2250 sizeof("hello world") * 2 - 4);
2251 if (!testStringFromVBox(pCtx, "TEXT",
2252 clipGetAtom(NULL, "TEXT"),
2253 "hello worl", sizeof("hello worl"), 8))
2254 ++cErrs;
2255
2256 /*** COMPOUND TEXT from VBox ***/
2257 RTPrintf(TEST_NAME ": TESTING reading COMPOUND TEXT from VBox\n");
2258 /* Simple test */
2259 clipSetVBoxUtf16(pCtx, VINF_SUCCESS, "hello world",
2260 sizeof("hello world") * 2);
2261 if (!testStringFromVBox(pCtx, "COMPOUND_TEXT",
2262 clipGetAtom(NULL, "COMPOUND_TEXT"),
2263 "hello world", sizeof("hello world"), 8))
2264 ++cErrs;
2265 /* With an embedded carriage return */
2266 clipSetVBoxUtf16(pCtx, VINF_SUCCESS, "hello\r\nworld",
2267 sizeof("hello\r\nworld") * 2);
2268 if (!testStringFromVBox(pCtx, "COMPOUND_TEXT",
2269 clipGetAtom(NULL, "COMPOUND_TEXT"),
2270 "hello\nworld", sizeof("hello\nworld"), 8))
2271 ++cErrs;
2272 /* An empty string */
2273 clipSetVBoxUtf16(pCtx, VINF_SUCCESS, "", 2);
2274 if (!testStringFromVBox(pCtx, "COMPOUND_TEXT",
2275 clipGetAtom(NULL, "COMPOUND_TEXT"),
2276 "", sizeof(""), 8))
2277 ++cErrs;
2278 /* A non-zero-terminated string */
2279 clipSetVBoxUtf16(pCtx, VINF_SUCCESS, "hello world",
2280 sizeof("hello world") * 2 - 4);
2281 if (!testStringFromVBox(pCtx, "COMPOUND_TEXT",
2282 clipGetAtom(NULL, "COMPOUND_TEXT"),
2283 "hello worl", sizeof("hello worl"), 8))
2284 ++cErrs;
2285
2286 /*** Timeout from VBox ***/
2287 RTPrintf(TEST_NAME ": TESTING reading from VBox with timeout\n");
2288 clipEmptyVBox(pCtx, VERR_TIMEOUT);
2289 if (!testStringFromVBoxFailed(pCtx, "UTF8_STRING"))
2290 ++cErrs;
2291
2292 /*** No data in VBox clipboard ***/
2293 RTPrintf(TEST_NAME ": TESTING reading from VBox with no data\n");
2294 clipEmptyVBox(pCtx, VINF_SUCCESS);
2295 if (!testStringFromVBoxFailed(pCtx, "UTF8_STRING"))
2296 ++cErrs;
2297 if (cErrs > 0)
2298 RTPrintf("Failed with %u error(s)\n", cErrs);
2299 return cErrs > 0 ? 1 : 0;
2300}
2301
2302#endif
2303
2304#ifdef SMOKETEST
2305
2306/* This is a simple test case that just starts a copy of the X11 clipboard
2307 * backend, checks the X11 clipboard and exits. If ever needed I will add an
2308 * interactive mode in which the user can read and copy to the clipboard from
2309 * the command line. */
2310
2311#include <iprt/initterm.h>
2312#include <iprt/stream.h>
2313
2314#define TEST_NAME "tstClipboardX11Smoke"
2315
2316int VBoxX11ClipboardReadVBoxData(VBOXCLIPBOARDCONTEXT *pCtx,
2317 uint32_t u32Format, void **ppv,
2318 uint32_t *pcb)
2319{
2320 return VERR_NO_DATA;
2321}
2322
2323void VBoxX11ClipboardReportX11Formats(VBOXCLIPBOARDCONTEXT *pCtx,
2324 uint32_t u32Formats)
2325{}
2326
2327int main()
2328{
2329 int rc = VINF_SUCCESS;
2330 RTR3Init();
2331 /* We can't test anything without an X session, so just return success
2332 * in that case. */
2333 if (!RTEnvGet("DISPLAY"))
2334 {
2335 RTPrintf(TEST_NAME ": X11 not available, not running test\n");
2336 return 0;
2337 }
2338 RTPrintf(TEST_NAME ": TESTING\n");
2339 CLIPBACKEND *pCtx = VBoxX11ClipboardConstructX11(NULL);
2340 AssertReturn(pCtx, 1);
2341 rc = VBoxX11ClipboardStartX11(pCtx);
2342 AssertRCReturn(rc, 1);
2343 /* Give the clipboard time to synchronise. */
2344 RTThreadSleep(500);
2345 rc = VBoxX11ClipboardStopX11(pCtx);
2346 AssertRCReturn(rc, 1);
2347 VBoxX11ClipboardDestructX11(pCtx);
2348 return 0;
2349}
2350
2351#endif /* SMOKETEST 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