VirtualBox

source: vbox/trunk/src/VBox/Additions/linux/xclient/clipboard.cpp@ 4997

Last change on this file since 4997 was 4350, checked in by vboxsync, 17 years ago

Initial code for seamless mode in Linux guests

File size: 59.8 KB
Line 
1/** @file
2 *
3 * Shared Clipboard:
4 * Linux guest.
5 */
6
7/*
8 * Copyright (C) 2006-2007 innotek GmbH
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 as published by the Free Software Foundation,
14 * in version 2 as it comes in the "COPYING" file of the VirtualBox OSE
15 * distribution. VirtualBox OSE is distributed in the hope that it will
16 * be useful, but WITHOUT ANY WARRANTY of any kind.
17 */
18
19/* The formats which we support in the guest. These can be deactivated in order to test specific
20 code paths. */
21#define USE_UTF16
22#define USE_UTF8
23#define USE_CTEXT
24
25#define LOG_GROUP LOG_GROUP_DEV_VMM_BACKDOOR
26
27#include <vector>
28#include <iostream>
29
30using std::cout;
31using std::endl;
32
33#include <VBox/VBoxGuest.h>
34#include <VBox/HostServices/VBoxClipboardSvc.h>
35#include <VBox/log.h>
36#include <iprt/alloc.h>
37#include <iprt/asm.h> /* For atomic operations */
38#include <iprt/assert.h>
39#include <iprt/initterm.h>
40#include <iprt/mem.h>
41#include <iprt/string.h>
42#include <iprt/thread.h>
43#include <iprt/process.h>
44#include <iprt/semaphore.h>
45#include <string.h>
46#include <stdio.h>
47#include <stdint.h>
48#include <sys/ioctl.h>
49// #include <sys/types.h>
50#include <sys/stat.h>
51#include <fcntl.h>
52#include <errno.h>
53#include <signal.h>
54// #include <unistd.h>
55// #include <getopt.h>
56
57#include <X11/Xlib.h>
58#include <X11/Xatom.h>
59#include <X11/Intrinsic.h>
60#include <X11/Shell.h>
61#include <X11/X.h>
62
63#include "clipboard.h"
64
65#define VBOX_INIT_CALL(__a, __b, __c) do { \
66 (__a)->hdr.result = VINF_SUCCESS; \
67 (__a)->hdr.u32ClientID = (__c); \
68 (__a)->hdr.u32Function = (__b); \
69 (__a)->hdr.cParms = (sizeof (*(__a)) - sizeof ((__a)->hdr)) / sizeof (HGCMFunctionParameter); \
70} while (0)
71
72/** The different clipboard formats which we support. */
73enum g_eClipboardFormats
74{
75 INVALID = 0,
76 TARGETS,
77 CTEXT,
78 UTF8,
79 UTF16
80};
81
82/** The X11 clipboard uses several names for the same format. This structure maps an X11
83 name to a format. */
84typedef struct {
85 Atom atom;
86 g_eClipboardFormats format;
87 unsigned hostFormat;
88} VBOXCLIPBOARDFORMAT;
89
90/** Does the host or the guest currently own the clipboard? */
91enum g_eClipboardOwner { NONE = 0, HOST, GUEST };
92
93typedef struct {
94 /** BMP file type marker - must always contain 'BM' */
95 uint16_t bfType;
96 /** The size of the BMP file in bytes (the MS docs say that this is not reliable) */
97 uint32_t bfSize;
98 /** Reserved, must always be zero */
99 uint16_t bfReserved1;
100 /** Reserved, must always be zero */
101 uint16_t bfReserved2;
102 /** Offset from the beginning of this header to the actual image bits */
103} VBOXBITMAPFILEHEADER;
104
105/** Global clipboard context information */
106typedef struct
107{
108 /** The Xt application context structure */
109 XtAppContext appContext;
110
111 /** We have a separate thread to wait for Window and Clipboard events */
112 RTTHREAD thread;
113 /** The Xt widget which we use as our clipboard client. It is never made visible. */
114 Widget widget;
115 /** The file descriptor for the VirtualBox device which we use for pushing requests
116 to the host. */
117 int sendDevice;
118 /** The file descriptor for the VirtualBox device which we use for polling requests
119 from the host. */
120 int receiveDevice;
121
122 /** X11 atom refering to the clipboard: CLIPBOARD */
123 Atom atomClipboard;
124 /** X11 atom refering to the clipboard: TARGETS */
125 Atom atomTargets;
126 /** X11 atom refering to the clipboard: MULTIPLE */
127 Atom atomMultiple;
128 /** X11 atom refering to the clipboard: TIMESTAMP */
129 Atom atomTimestamp;
130 /** X11 atom refering to the clipboard utf16 text format: text/plain;charset=ISO-10646-UCS-2 */
131 Atom atomUtf16;
132 /** X11 atom refering to the clipboard utf8 text format: UTF8_STRING */
133 Atom atomUtf8;
134 /** X11 atom refering to the native X11 clipboard text format: COMPOUND_TEXT */
135 Atom atomCText;
136
137 /** A list of the X11 formats which we support, mapped to our identifier for them, in the order
138 we prefer to have them in. */
139 std::vector<VBOXCLIPBOARDFORMAT> formatList;
140
141 /** Does the host or the guest currently own the clipboard? */
142 volatile enum g_eClipboardOwner eOwner;
143
144 /** What is the best text format the guest has to offer? INVALID for none. */
145 g_eClipboardFormats guestTextFormat;
146 /** Atom corresponding to the guest text format */
147 Atom atomGuestTextFormat;
148 /** What is the best bitmap format the guest has to offer? INVALID for none. */
149 g_eClipboardFormats guestBitmapFormat;
150 /** Atom corresponding to the guest Bitmap format */
151 Atom atomGuestBitmapFormat;
152 /** What formats does the host have on offer? */
153 int hostFormats;
154 /** Windows caches the clipboard data it receives. Since we have no way of knowing whether
155 that data is still valid, we always send a "data changed" message after a successful
156 transfer to invalidate the cache. */
157 bool notifyHost;
158
159 /** Since the clipboard data moves asynchronously, we use an event semaphore to wait for
160 it. */
161 RTSEMEVENT terminating;
162
163 /** Format which we are reading from the guest clipboard (valid during a request for the
164 guest clipboard) */
165 g_eClipboardFormats requestGuestFormat;
166 /** The guest buffer to write guest clipboard data to (valid during a request for the host
167 clipboard) */
168 void *requestBuffer;
169 /** The size of the host buffer to write guest clipboard data to (valid during a request for
170 the guest clipboard) */
171 unsigned requestBufferSize;
172 /** The size of the guest clipboard data written to the host buffer (valid during a request
173 for the guest clipboard) */
174 uint32_t *requestActualSize;
175
176 /** Client ID for the clipboard subsystem */
177 uint32_t client;
178} VBOXCLIPBOARDCONTEXT;
179
180/* Only one client is supported. There seems to be no need for more clients. */
181static VBOXCLIPBOARDCONTEXT g_ctx;
182
183static void VBoxHGCMParmUInt32Set(HGCMFunctionParameter *pParm, uint32_t u32)
184{
185 LogFlowFunc(("pParm=%p, u32=%d\n", pParm, u32));
186 pParm->type = VMMDevHGCMParmType_32bit;
187 pParm->u.value32 = u32;
188}
189
190static int VBoxHGCMParmUInt32Get (HGCMFunctionParameter *pParm, uint32_t *pu32)
191{
192 LogFlowFunc(("pParm=%p, pu32=%p\n", pParm, pu32));
193 if (pParm->type == VMMDevHGCMParmType_32bit)
194 {
195 *pu32 = pParm->u.value32;
196 LogFlowFunc(("rc=VINF_SUCCESS, *pu32=%d\n", *pu32));
197 return VINF_SUCCESS;
198 }
199
200 LogFlowFunc(("rc=VERR_INVALID_PARAMETER\n"));
201 return VERR_INVALID_PARAMETER;
202}
203
204static void VBoxHGCMParmPtrSet (HGCMFunctionParameter *pParm, void *pv, uint32_t cb)
205{
206 LogFlowFunc(("pParm=%p, pv=%p, cb=%d\n", pParm, pv, cb));
207 pParm->type = VMMDevHGCMParmType_LinAddr;
208 pParm->u.Pointer.size = cb;
209 pParm->u.Pointer.u.linearAddr = (vmmDevHypPtr)pv;
210}
211
212/**
213 * Transfer clipboard data from the guest to the host
214 *
215 * @returns VBox result code
216 * @param u32Format The format of the data being sent
217 * @param pv Pointer to the data being sent
218 * @param cb Size of the data being sent in bytes
219 */
220static int vboxClipboardSendData (uint32_t u32Format, void *pv, uint32_t cb)
221{
222 VBoxClipboardWriteData parms;
223
224 LogFlowFunc(("u32Format=%d, pv=%p, cb=%d\n", u32Format, pv, cb));
225 VBOX_INIT_CALL(&parms, VBOX_SHARED_CLIPBOARD_FN_WRITE_DATA, g_ctx.client);
226
227 VBoxHGCMParmUInt32Set (&parms.format, u32Format);
228 VBoxHGCMParmPtrSet (&parms.ptr, pv, cb);
229
230 int rc = VERR_DEV_IO_ERROR;
231 if (ioctl(g_ctx.sendDevice, IOCTL_VBOXGUEST_HGCM_CALL, &parms) == 0)
232 {
233 rc = parms.hdr.result;
234 }
235
236 LogFlowFunc(("rc=%Vrc\n", rc));
237 return rc;
238}
239
240/**
241 * Get clipboard data from the host
242 *
243 * @returns VBox result code
244 * @param u32Format The format of the data being requested
245 * @retval ppv On success, this will point to a buffer to be freed with RTMemFree
246 * containing the data read if pcb > 0.
247 * @retval pcb On success, this contains the number of bytes of data returned
248 */
249static int vboxClipboardReadHostData (uint32_t u32Format, void **ppv, uint32_t *pcb)
250{
251 VBoxClipboardReadData parms;
252 int rc;
253
254 LogFlowFunc(("u32Format=%d, ppv=%p, *pcb=%d\n", u32Format, ppv, *pcb));
255 /* Allocate a 1K buffer for receiving the clipboard data to start with. If this is too small,
256 the host will tell us what size of buffer we need, and we will try again with a buffer of
257 that size. */
258 uint32_t cb = 1024, u32Size;
259 void *pv = RTMemAlloc(cb);
260 if (pv == 0)
261 {
262 LogFlowFunc(("rc=VERR_NO_MEMORY\n"));
263 return VERR_NO_MEMORY;
264 }
265 /* Set up the HGCM call structure and make the call to the host. */
266 VBOX_INIT_CALL(&parms, VBOX_SHARED_CLIPBOARD_FN_READ_DATA, g_ctx.client);
267 VBoxHGCMParmUInt32Set (&parms.format, u32Format);
268 VBoxHGCMParmPtrSet (&parms.ptr, pv, cb);
269 VBoxHGCMParmUInt32Set (&parms.size, 0);
270 if (ioctl(g_ctx.sendDevice, IOCTL_VBOXGUEST_HGCM_CALL, (void*)&parms) < 0)
271 {
272 RTMemFree(pv);
273 LogFlowFunc(("rc=VERR_DEV_IO_ERROR\n"));
274 return VERR_DEV_IO_ERROR;
275 }
276 else if (parms.hdr.result != VINF_SUCCESS)
277 {
278 RTMemFree(pv);
279 LogFlowFunc(("rc=%Vrc\n", parms.hdr.result));
280 return parms.hdr.result;
281 }
282 /* Check whether the buffer we supplied was big enough. */
283 rc = VBoxHGCMParmUInt32Get (&parms.size, &u32Size);
284 if (rc != VINF_SUCCESS || u32Size == 0)
285 {
286 *pcb = u32Size;
287 RTMemFree(pv);
288 *ppv = 0;
289 LogFlowFunc(("*pcb=%d, *ppv=%p, rc=%Vrc\n", *pcb, *ppv, rc));
290 return rc;
291 }
292 if (u32Size <= cb)
293 {
294 /* Our initial 1024 byte buffer was big enough for the clipboard data. */
295 *ppv = pv;
296 *pcb = u32Size;
297 LogFlowFunc(("*pcb=%d, *ppv=%p, rc=VINF_SUCCESS\n", *pcb, *ppv));
298 return VINF_SUCCESS;
299 }
300 /* Else if u32Size > cb, we try again with a buffer of size u32Size. */
301 cb = u32Size;
302 RTMemFree(pv);
303 pv = RTMemAlloc(cb);
304 if (pv == 0)
305 {
306 LogFlowFunc(("rc=VERR_NO_MEMORY\n"));
307 return VERR_NO_MEMORY;
308 }
309 /* Set up the HGCM call structure and make the call to the host. */
310 VBOX_INIT_CALL(&parms, VBOX_SHARED_CLIPBOARD_FN_READ_DATA, g_ctx.client);
311 VBoxHGCMParmUInt32Set (&parms.format, u32Format);
312 VBoxHGCMParmPtrSet (&parms.ptr, pv, cb);
313 VBoxHGCMParmUInt32Set (&parms.size, 0);
314 if (ioctl(g_ctx.sendDevice, IOCTL_VBOXGUEST_HGCM_CALL, (void*)&parms) < 0)
315 {
316 RTMemFree(pv);
317 LogFlowFunc(("rc=VERR_DEV_IO_ERROR\n"));
318 return VERR_DEV_IO_ERROR;
319 }
320 else if (parms.hdr.result != VINF_SUCCESS)
321 {
322 RTMemFree(pv);
323 LogFlowFunc(("rc=%Vrc\n", parms.hdr.result));
324 return parms.hdr.result;
325 }
326 /* Check whether the buffer we supplied was big enough. */
327 rc = VBoxHGCMParmUInt32Get (&parms.size, &u32Size);
328 if (rc != VINF_SUCCESS || u32Size == 0)
329 {
330 *pcb = u32Size;
331 RTMemFree(pv);
332 *ppv = 0;
333 LogFlowFunc(("*pcb=%d, *ppv=%p, rc=%Vrc\n", *pcb, *ppv, rc));
334 return rc;
335 }
336 if (u32Size <= cb)
337 {
338 /* The buffer was big enough. */
339 *ppv = pv;
340 *pcb = cb;
341 LogFlowFunc(("*pcb=%d, *ppv=%p, rc=VINF_SUCCESS\n", *pcb, *ppv));
342 return VINF_SUCCESS;
343 }
344 /* The buffer was to small again. Perhaps the clipboard contents changed half-way through
345 the operation. Since I can't say whether or not this is actually an error, we will just
346 return size 0. */
347 RTMemFree(pv);
348 *pcb = 0;
349 LogFlowFunc(("*pcb=0 rc=VINF_SUCCESS\n"));
350 return VINF_SUCCESS;
351}
352
353/**
354 * Convert a Utf16 text with Linux EOLs to zero-terminated Utf16-LE with Windows EOLs, allocating
355 * memory for the converted text. Does no checking for validity.
356 *
357 * @returns VBox status code
358 *
359 * @param pu16Src Source Utf16 text to convert
360 * @param cwSrc Size of the source text in 16 bit words
361 * @retval ppu16Dest Where to store the pointer to the converted text. Only valid on success
362 * and if pcwDest is greater than 0.
363 * @retval pcwDest Size of the converted text in 16 bit words, including the trailing null
364 * if present
365 */
366static int vboxClipboardUtf16LinToWin(PRTUTF16 pu16Src, size_t cwSrc, PRTUTF16 *ppu16Dest,
367 size_t *pcwDest)
368{
369 enum { LINEFEED = 0xa, CARRIAGERETURN = 0xd };
370 PRTUTF16 pu16Dest;
371 size_t cwDest, i, j;
372 LogFlowFunc(("pu16Src=%.*ls, cwSrc=%u\n", cwSrc, pu16Src, cwSrc));
373 if (cwSrc == 0)
374 {
375 *ppu16Dest = 0;
376 *pcwDest = 0;
377 LogFlowFunc(("*ppu16Dest=0, *pcwDest=0, rc=VINF_SUCCESS\n"));
378 return VINF_SUCCESS;
379 }
380 AssertReturn(VALID_PTR(pu16Src), VERR_INVALID_PARAMETER);
381 cwDest = 0;
382 for (i = 0; i < cwSrc; ++i, ++cwDest)
383 {
384 if (pu16Src[i] == LINEFEED)
385 {
386 ++cwDest;
387 }
388 if (pu16Src[i] == 0)
389 {
390 break;
391 }
392 }
393 /* Leave space for a trailing null */
394 ++cwDest;
395 pu16Dest = reinterpret_cast<PRTUTF16>(RTMemAlloc(cwDest * 2));
396 if (pu16Dest == 0)
397 {
398 LogFlowFunc(("rc=VERR_NO_MEMORY\n"));
399 return VERR_NO_MEMORY;
400 }
401 for (i = (pu16Src[0] == 0xfeff ? 1 : 0), j = 0; i < cwSrc; ++i, ++j)
402 {
403 if (pu16Src[i] == LINEFEED)
404 {
405 pu16Dest[j] = CARRIAGERETURN;
406 ++j;
407 }
408 if (pu16Src[i] == 0)
409 {
410 break;
411 }
412 pu16Dest[j] = pu16Src[i];
413 }
414 /* The trailing null */
415 pu16Dest[j] = 0;
416 *ppu16Dest = pu16Dest;
417 *pcwDest = cwDest;
418 LogFlowFunc(("*ppu16Dest=%p, *pcwDest=%d, rc=VINF_SUCCESS\n", pu16Dest, cwDest));
419 return VINF_SUCCESS;
420}
421
422/**
423 * Convert the UTF-16 text returned from the guest X11 clipboard to UTF-16LE with Windows EOLs
424 * and send it to the host.
425 *
426 * @param pValue Source UTF-16 text
427 * @param cwSourceLen Length in 16-bit words of the source text
428 */
429static void vboxClipboardGetUtf16(XtPointer pValue, size_t cwSourceLen)
430{
431 size_t cwDestLen;
432 PRTUTF16 pu16DestText;
433 PRTUTF16 pu16SourceText = reinterpret_cast<PRTUTF16>(pValue);
434 int rc;
435
436 LogFlowFunc(("converting Utf-16 to Utf-16LE. Original is %.*ls\n",
437 cwSourceLen - 1, pu16SourceText + 1));
438 rc = vboxClipboardUtf16LinToWin(pu16SourceText, cwSourceLen, &pu16DestText, &cwDestLen);
439 if (rc != VINF_SUCCESS)
440 {
441 XtFree(reinterpret_cast<char *>(pValue));
442 vboxClipboardSendData (0, 0, 0);
443 LogFlowFunc(("sending empty data and returning\n"));
444 return;
445 }
446 Log2 (("vboxClipboardGetUtf16: converted string is %.*ls\n", cwDestLen, pu16DestText));
447 vboxClipboardSendData (VBOX_SHARED_CLIPBOARD_FMT_UNICODETEXT,
448 reinterpret_cast<void *>(pu16DestText), cwDestLen * 2);
449 RTMemFree(reinterpret_cast<void *>(pu16DestText));
450 XtFree(reinterpret_cast<char *>(pValue));
451 LogFlowFunc(("returning\n"));
452}
453
454/**
455 * Convert the UTF-8 text returned from the guest X11 clipboard to UTF-16LE with Windows EOLs
456 * and send it to the host.
457 *
458 * @param pValue Source UTF-8 text
459 * @param cbSourceLen Length in 8-bit bytes of the source text
460 */
461static void vboxClipboardGetUtf8(XtPointer pValue, size_t cbSourceLen)
462{
463 size_t cwSourceLen, cwDestLen;
464 char *pu8SourceText = reinterpret_cast<char *>(pValue);
465 PRTUTF16 pu16SourceText = 0, pu16DestText;
466 int rc;
467
468 LogFlowFunc(("\n"));
469 Log2 (("vboxClipboardGetUtf8: converting Utf-8 to Utf-16LE. Original is %.*s\n", cbSourceLen,
470 pu8SourceText));
471 /* First convert the UTF8 to UTF16 */
472 rc = RTStrToUtf16Ex(pu8SourceText, cbSourceLen, &pu16SourceText, 0, &cwSourceLen);
473 if (rc != VINF_SUCCESS)
474 {
475 XtFree(reinterpret_cast<char *>(pValue));
476 vboxClipboardSendData (0, 0, 0);
477 LogFlowFunc(("sending empty data and returning\n"));
478 return;
479 }
480 rc = vboxClipboardUtf16LinToWin(pu16SourceText, cwSourceLen, &pu16DestText, &cwDestLen);
481 if (rc != VINF_SUCCESS)
482 {
483 RTMemFree(reinterpret_cast<void *>(pu16SourceText));
484 XtFree(reinterpret_cast<char *>(pValue));
485 vboxClipboardSendData (0, 0, 0);
486 LogFlowFunc(("sending empty data and returning\n"));
487 return;
488 }
489 Log2 (("vboxClipboardGetUtf8: converted string is %.*ls\n", cwDestLen, pu16DestText));
490 vboxClipboardSendData (VBOX_SHARED_CLIPBOARD_FMT_UNICODETEXT,
491 reinterpret_cast<void *>(pu16DestText), cwDestLen * 2);
492 RTMemFree(reinterpret_cast<void *>(pu16SourceText));
493 RTMemFree(reinterpret_cast<void *>(pu16DestText));
494 XtFree(reinterpret_cast<char *>(pValue));
495 LogFlowFunc(("returning\n"));
496}
497
498/**
499 * Convert the compound text returned from the guest X11 clipboard to UTF-16LE with Windows EOLs
500 * and send it to the host.
501 *
502 * @param pValue Source compound text
503 * @param cbSourceLen Length in 8-bit bytes of the source text
504 */
505static void vboxClipboardGetCText(XtPointer pValue, size_t cbSourceLen)
506{
507 size_t cwSourceLen, cwDestLen;
508 char **ppu8SourceText = 0;
509 PRTUTF16 pu16SourceText = 0, pu16DestText;
510 XTextProperty property;
511 int rc, cProps;
512
513 LogFlowFunc(("\n"));
514 Log2 (("vboxClipboardGetCText: converting compound text to Utf-16LE. Original is %.*s\n",
515 cbSourceLen, pValue));
516 /* First convert the compound text to Utf8 */
517 property.value = reinterpret_cast<unsigned char *>(pValue);
518 property.encoding = g_ctx.atomCText;
519 property.format = 8;
520 property.nitems = cbSourceLen;
521 rc = Xutf8TextPropertyToTextList(XtDisplay(g_ctx.widget), &property, &ppu8SourceText, &cProps);
522 XtFree(reinterpret_cast<char *>(pValue));
523 if (rc < 0)
524 {
525 const char *pcReason;
526 switch(rc)
527 {
528 case XNoMemory:
529 pcReason = "out of memory";
530 break;
531 case XLocaleNotSupported:
532 pcReason = "locale (Utf8) not supported";
533 break;
534 case XConverterNotFound:
535 pcReason = "converter not found";
536 break;
537 default:
538 pcReason = "unknown error";
539 }
540 XFreeStringList(ppu8SourceText);
541 LogRel(("vboxClipboardGetCText: Xutf8TextPropertyToTextList failed. Reason: %s\n",
542 pcReason));
543 vboxClipboardSendData (0, 0, 0);
544 LogFlowFunc(("sending empty data and returning\n"));
545 return;
546 }
547 /* Next convert the UTF8 to UTF16 */
548 rc = RTStrToUtf16Ex(*ppu8SourceText, cbSourceLen, &pu16SourceText, 0, &cwSourceLen);
549 XFreeStringList(ppu8SourceText);
550 if (rc != VINF_SUCCESS)
551 {
552 vboxClipboardSendData (0, 0, 0);
553 LogFlowFunc(("sending empty data and returning\n"));
554 return;
555 }
556 rc = vboxClipboardUtf16LinToWin(pu16SourceText, cwSourceLen, &pu16DestText, &cwDestLen);
557 RTMemFree(reinterpret_cast<void *>(pu16SourceText));
558 if (rc != VINF_SUCCESS)
559 {
560 vboxClipboardSendData (0, 0, 0);
561 LogFlowFunc(("sending empty data and returning\n"));
562 return;
563 }
564 Log2 (("vboxClipboardGetCText: converted string is %.*ls\n", cwDestLen, pu16DestText));
565 vboxClipboardSendData (VBOX_SHARED_CLIPBOARD_FMT_UNICODETEXT,
566 reinterpret_cast<void *>(pu16DestText), cwDestLen * 2);
567 RTMemFree(reinterpret_cast<void *>(pu16DestText));
568 LogFlowFunc(("returning\n"));
569}
570
571/**
572 * Convert the Latin1 text returned from the guest X11 clipboard to UTF-16LE with Windows EOLs
573 * and send it to the host.
574 *
575 * @param pValue Source Latin1 text
576 * @param cbSourceLen Length in 8-bit bytes of the source text
577 */
578static void vboxClipboardGetLatin1(XtPointer pValue, size_t cbSourceLen)
579{
580 enum { LINEFEED = 0xa, CARRIAGERETURN = 0xd };
581 /* Leave space for an additional null character at the end of the destination text. */
582 size_t cwDestLen = cbSourceLen + 1, cwDestPos;
583 char *pu8SourceText = reinterpret_cast<char *>(pValue);
584 PRTUTF16 pu16DestText;
585
586 LogFlowFunc(("converting Latin1 to Utf-16LE. Original is %.*s\n", cbSourceLen,
587 pu8SourceText));
588 /* Find the size of the destination text */
589 for (size_t i = 0; i < cbSourceLen; i++)
590 {
591 if (pu8SourceText[i] == LINEFEED)
592 ++cwDestLen;
593 }
594 pu16DestText = reinterpret_cast<PRTUTF16>(RTMemAlloc(cwDestLen * 2));
595 if (pu16DestText == 0)
596 {
597 XtFree(reinterpret_cast<char *>(pValue));
598 Log2 (("vboxClipboardGetLatin1: failed to allocate %d bytes!\n", cwDestLen * 2));
599 vboxClipboardSendData (0, NULL, 0);
600 LogFlowFunc(("sending empty data and returning\n"));
601 return;
602 }
603 /* Copy the original X clipboard string to the destination, replacing Linux EOLs with
604 Windows ones */
605 cwDestPos = 0;
606 for (size_t i = 0; i < cbSourceLen; ++i, ++cwDestPos)
607 {
608 if (pu8SourceText[i] == LINEFEED)
609 {
610 pu16DestText[cwDestPos] = CARRIAGERETURN;
611 ++cwDestPos;
612 }
613 /* latin1 < utf-16LE */
614 pu16DestText[cwDestPos] = pu8SourceText[i];
615 if (pu8SourceText[i] == 0)
616 break;
617 }
618 pu16DestText[cwDestPos] = 0;
619 Log2 (("vboxClipboardGetLatin1: converted text is %.*ls\n", cwDestPos, pu16DestText));
620 vboxClipboardSendData (VBOX_SHARED_CLIPBOARD_FMT_UNICODETEXT,
621 reinterpret_cast<void *>(pu16DestText), cwDestPos * 2);
622 RTMemFree(reinterpret_cast<void *>(pu16DestText));
623 XtFree(reinterpret_cast<char *>(pValue));
624 LogFlowFunc(("returning\n"));
625}
626
627/** Convert the clipboard text from the current format to Utf-16 with Windows line breaks.
628 We are reading the guest clipboard to make it available to the host. */
629static void vboxClipboardGetProc(Widget, XtPointer /* pClientData */, Atom * /* selection */,
630 Atom *atomType, XtPointer pValue, long unsigned int *pcLen,
631 int *piFormat)
632{
633 /* The X Toolkit may have failed to get the clipboard selection for us. */
634 LogFlowFunc(("*pcLen=%lu, *piFormat=%d, requested target format: %d, g_ctx.requestBufferSize=%d\n",
635 *pcLen, *piFormat, g_ctx.requestGuestFormat, g_ctx.requestBufferSize));
636 if (*atomType == XT_CONVERT_FAIL)
637 {
638 vboxClipboardSendData (0, NULL, 0);
639 LogFlowFunc(("Xt failed to convert the data. Sending empty data and returning\n"));
640 return;
641 }
642 /* In which format did we request the clipboard data? */
643 unsigned cTextLen = (*pcLen) * (*piFormat) / 8;
644 switch (g_ctx.requestGuestFormat)
645 {
646 case UTF16:
647 vboxClipboardGetUtf16(pValue, cTextLen / 2);
648 break;
649 case CTEXT:
650 vboxClipboardGetCText(pValue, cTextLen);
651 break;
652 case UTF8:
653 {
654 /* If we are given broken Utf-8, we treat it as Latin1. Is this acceptable? */
655 size_t cStringLen;
656 char *pu8SourceText = reinterpret_cast<char *>(pValue);
657
658 if ((g_ctx.requestGuestFormat == UTF8)
659 && (RTStrUniLenEx(pu8SourceText, *pcLen, &cStringLen) == VINF_SUCCESS))
660 {
661 vboxClipboardGetUtf8(pValue, cTextLen);
662 break;
663 }
664 else
665 {
666 vboxClipboardGetLatin1(pValue, cTextLen);
667 break;
668 }
669 }
670 default:
671 XtFree(reinterpret_cast<char *>(pValue));
672 Log (("vboxClipboardGetProc: bad target format\n"));
673 vboxClipboardSendData (0, NULL, 0);
674 LogFlowFunc(("sending empty data and returning\n"));
675 return;
676 }
677 g_ctx.notifyHost = true;
678 LogFlowFunc(("returning\n"));
679}
680
681/**
682 * Tell the host that new clipboard formats are available
683 */
684static int vboxClipboardReportFormats (uint32_t u32Formats)
685{
686 VBoxClipboardFormats parms;
687 int rc = VERR_DEV_IO_ERROR;
688
689 LogFlowFunc(("u32Formats=%d\n", u32Formats));
690 VBOX_INIT_CALL(&parms, VBOX_SHARED_CLIPBOARD_FN_FORMATS, g_ctx.client);
691
692 VBoxHGCMParmUInt32Set (&parms.formats, u32Formats);
693
694 if (ioctl(g_ctx.sendDevice, IOCTL_VBOXGUEST_HGCM_CALL, &parms) == 0)
695 {
696 rc = parms.hdr.result;
697 }
698
699 if (VBOX_SUCCESS (rc))
700 {
701 rc = parms.hdr.result;
702 }
703
704 LogFlowFunc(("rc=%Vrc\n", rc));
705 return rc;
706}
707
708/** Callback to handle a reply to a request for the targets the current clipboard holder can
709 handle. We are reading the guest clipboard to make it available to the host. */
710static void vboxClipboardTargetsProc(Widget, XtPointer, Atom * /* selection */, Atom *atomType,
711 XtPointer pValue, long unsigned int *pcLen, int *piFormat)
712{
713 static int cCalls = 0;
714 Atom *atomTargets = reinterpret_cast<Atom *>(pValue);
715 /* The number of format atoms the clipboard holder is offering us */
716 unsigned cAtoms = *pcLen;
717 /* The best clipboard format we have found so far */
718 g_eClipboardFormats eBestTarget = INVALID;
719 /* The atom corresponding to our best clipboard format found */
720 Atom atomBestTarget = None;
721
722 if ((cCalls % 10) == 0)
723 Log3 (("vboxClipboardTargetsProc called, cAtoms=%d\n", cAtoms));
724 if (*atomType == XT_CONVERT_FAIL)
725 {
726 Log (("vboxClipboardTargetsProc: reading clipboard from guest, X toolkit failed to convert the selection\n"));
727 LogFlowFunc(("returning\n"));
728 return;
729 }
730 /* Run through the atoms offered to us to see if we recognise them. If we find the atom for
731 a "better" format than the best we have found so far, we remember it as our new "best"
732 format. */
733 for (unsigned i = 0; i < cAtoms; ++i)
734 {
735 for (unsigned j = 0; j != g_ctx.formatList.size(); ++j)
736 if (g_ctx.formatList[j].atom == atomTargets[i])
737 {
738 if (eBestTarget < g_ctx.formatList[j].format)
739 {
740 eBestTarget = g_ctx.formatList[j].format;
741 atomBestTarget = g_ctx.formatList[j].atom;
742 }
743 break;
744 }
745#ifdef DEBUG
746 char *szAtomName = XGetAtomName(XtDisplay(g_ctx.widget), atomTargets[i]);
747 if (szAtomName != 0)
748 {
749 if ((cCalls % 10) == 0)
750 Log3 (("vboxClipboardTargetsProc: the guest offers target %s\n", szAtomName));
751 XFree(szAtomName);
752 }
753 else
754 {
755 if ((cCalls % 10) == 0)
756 Log3 (("vboxClipboardTargetsProc: the guest returned a bad atom: %d\n",
757 atomTargets[i]));
758 }
759#endif
760 }
761 g_ctx.atomGuestTextFormat = atomBestTarget;
762 /* If the available formats as seen by the host have changed, or if we suspect that
763 the host has cached the clipboard data (which can change without our noticing it),
764 then tell the host that new clipboard contents are available. */
765 if ((eBestTarget != g_ctx.guestTextFormat) || (g_ctx.notifyHost == true))
766 {
767 uint32_t u32Formats = 0;
768#ifdef DEBUG
769 if (atomBestTarget != None)
770 {
771 char *szAtomName = XGetAtomName(XtDisplay(g_ctx.widget), atomBestTarget);
772 Log2 (("vboxClipboardTargetsProc: switching to guest text target %s. Available targets are:\n",
773 szAtomName));
774 XFree(szAtomName);
775 }
776 else
777 {
778 Log2(("vboxClipboardTargetsProc: no supported host text target found. Available targets are:\n"));
779 }
780 for (unsigned i = 0; i < cAtoms; ++i)
781 {
782 char *szAtomName = XGetAtomName(XtDisplay(g_ctx.widget), atomTargets[i]);
783 if (szAtomName != 0)
784 {
785 Log2 (("vboxClipboardTargetsProc: %s\n", szAtomName));
786 XFree(szAtomName);
787 }
788 }
789#endif
790 g_ctx.guestTextFormat = eBestTarget;
791 if (eBestTarget != INVALID)
792 u32Formats |= VBOX_SHARED_CLIPBOARD_FMT_UNICODETEXT;
793 vboxClipboardReportFormats(u32Formats);
794 g_ctx.notifyHost = false;
795 }
796 XtFree(reinterpret_cast<char *>(pValue));
797 ++cCalls;
798}
799
800/**
801 * This callback is called every 200ms to check the contents of the guest clipboard.
802 */
803static void vboxClipboardTimerProc(XtPointer /* pUserData */, XtIntervalId * /* hTimerId */)
804{
805 static int cCalls = 0;
806 Log3 (("vboxClipboardTimerProc called\n"));
807 /* Get the current clipboard contents */
808 if (g_ctx.eOwner == GUEST)
809 {
810 if ((cCalls % 10) == 0)
811 Log3 (("vboxClipboardTimerProc: requesting the targets that the guest clipboard offers\n"));
812 XtGetSelectionValue(g_ctx.widget, g_ctx.atomClipboard, g_ctx.atomTargets,
813 vboxClipboardTargetsProc, 0, CurrentTime);
814 }
815 /* Re-arm our timer */
816 XtAppAddTimeOut(g_ctx.appContext, 200 /* ms */, vboxClipboardTimerProc, 0);
817 ++cCalls;
818}
819
820/**
821 * Satisfy a request from the guest for available clipboard targets.
822 *
823 * @returns true if we successfully convert the data to the format requested, false otherwise.
824 *
825 * @param atomTypeReturn The type of the data we are returning
826 * @param pValReturn A pointer to the data we are returning. This should be to memory
827 * allocated by XtMalloc, which will be freed by the toolkit later
828 * @param pcLenReturn The length of the data we are returning
829 * @param piFormatReturn The format (8bit, 16bit, 32bit) of the data we are returning
830 */
831static Boolean vboxClipboardConvertTargets(Atom *atomTypeReturn, XtPointer *pValReturn,
832 unsigned long *pcLenReturn, int *piFormatReturn)
833{
834 unsigned uListSize = g_ctx.formatList.size();
835 Atom *atomTargets = reinterpret_cast<Atom *>(XtMalloc((uListSize + 3) * sizeof(Atom)));
836 unsigned cTargets = 0;
837
838 LogFlowFunc(("\n"));
839 Log2(("vboxClipboardConvertTargets: uListSize=%u\n", uListSize));
840 for (unsigned i = 0; i < uListSize; ++i)
841 {
842 if ( ((g_ctx.hostFormats & VBOX_SHARED_CLIPBOARD_FMT_UNICODETEXT) != 0)
843 && (g_ctx.formatList[i].hostFormat == VBOX_SHARED_CLIPBOARD_FMT_UNICODETEXT))
844 {
845 atomTargets[cTargets] = g_ctx.formatList[i].atom;
846 ++cTargets;
847 }
848 }
849 atomTargets[cTargets] = g_ctx.atomTargets;
850 atomTargets[cTargets + 1] = g_ctx.atomMultiple;
851 atomTargets[cTargets + 2] = g_ctx.atomTimestamp;
852#ifdef DEBUG
853 for (unsigned i = 0; i < cTargets + 3; i++)
854 {
855 char *szAtomName = XGetAtomName(XtDisplay(g_ctx.widget), atomTargets[i]);
856 if (szAtomName != 0)
857 {
858 Log2 (("vboxClipboardConvertTargets: returning target %s\n", szAtomName));
859 XFree(szAtomName);
860 }
861 else
862 {
863 Log(("vboxClipboardConvertTargets: invalid atom %d in the list!\n", atomTargets[i]));
864 }
865 }
866#endif
867 *atomTypeReturn = XA_ATOM;
868 *pValReturn = reinterpret_cast<XtPointer>(atomTargets);
869 *pcLenReturn = cTargets + 3;
870 *piFormatReturn = 32;
871 LogFlowFunc(("returning true\n"));
872 return true;
873}
874
875/**
876 * Get the size of the buffer needed to hold a zero-terminated Utf16 string with Linux EOLs
877 * converted from a Utf16 string with Windows EOLs.
878 *
879 * @returns The size of the buffer needed in bytes
880 *
881 * @param pu16Src The source Utf16 string
882 * @param cwSrc The length in 16 bit words of the source string
883 */
884static int vboxClipboardUtf16GetLinSize(PRTUTF16 pu16Src, size_t cwSrc)
885{
886 enum { LINEFEED = 0xa, CARRIAGERETURN = 0xd };
887 size_t cwDest;
888
889 LogFlowFunc(("pu16Src=%.*ls, cwSrc=%u\n", cwSrc, pu16Src, cwSrc));
890 AssertReturn(pu16Src != 0, VERR_INVALID_PARAMETER);
891 /* We only take little endian Utf16 */
892 AssertReturn(pu16Src[0] != 0xfffe, VERR_INVALID_PARAMETER);
893 if (cwSrc == 0)
894 {
895 LogFlowFunc(("returning 0\n"));
896 return 0;
897 }
898 /* Calculate the size of the destination text string. */
899 /* Is this Utf16 or Utf16-LE? */
900 if (pu16Src[0] == 0xfeff)
901 cwDest = 0;
902 else
903 cwDest = 1;
904 for (size_t i = 0; i < cwSrc; ++i, ++cwDest)
905 {
906 if ( (i + 1 < cwSrc)
907 && (pu16Src[i] == CARRIAGERETURN)
908 && (pu16Src[i + 1] == LINEFEED))
909 ++i;
910 if (pu16Src[i] == 0)
911 {
912 break;
913 }
914 }
915 /* The terminating null */
916 ++cwDest;
917 LogFlowFunc(("returning %d\n", cwDest * 2));
918 return cwDest * 2;
919}
920
921/**
922 * Convert Utf16-LE text with Windows EOLs to Utf16 with Linux EOLs. This function does not
923 * verify that the Utf16 is valid.
924 *
925 * @returns VBox status code
926 *
927 * @param pu16Src Text to convert
928 * @param cwSrc Size of the source text in 16 bit words
929 * @param pu16Dest The buffer to store the converted text to
930 * @param cwDest The size of the buffer for the destination text
931 */
932static int vboxClipboardUtf16WinToLin(PRTUTF16 pu16Src, size_t cwSrc, PRTUTF16 pu16Dest,
933 size_t cwDest)
934{
935 enum { LINEFEED = 0xa, CARRIAGERETURN = 0xd };
936 size_t cwDestPos;
937
938 LogFlowFunc(("pu16Src=%.*ls, cwSrc=%u, pu16Dest=%p, cwDest=%u\n",
939 cwSrc, pu16Src, cwSrc, pu16Dest, cwDest));
940 /* A buffer of size 0 may not be an error, but it is not a good idea either. */
941 Assert(cwDest > 0);
942 AssertReturn(VALID_PTR(pu16Src), VERR_INVALID_PARAMETER);
943 /* We only take little endian Utf16 */
944 AssertReturn(pu16Src[0] != 0xfffe, VERR_INVALID_PARAMETER);
945 if (cwSrc == 0)
946 {
947 if (cwDest != 0)
948 pu16Dest[0] = 0;
949 LogFlowFunc(("set empty string. Returning VINF_SUCCESS\n"));
950 return VINF_SUCCESS;
951 }
952 /* Prepend the Utf16 byte order marker if it is missing. */
953 if (pu16Src[0] == 0xfeff)
954 {
955 cwDestPos = 0;
956 }
957 else
958 {
959 if (cwDest == 0)
960 {
961 LogFlowFunc(("returning VERR_BUFFER_OVERFLOW\n"));
962 return VERR_BUFFER_OVERFLOW;
963 }
964 pu16Dest[0] = 0xfeff;
965 cwDestPos = 1;
966 }
967 for (size_t i = 0; i < cwSrc; ++i, ++cwDestPos)
968 {
969 if (cwDestPos == cwDest)
970 {
971 LogFlowFunc(("returning VERR_BUFFER_OVERFLOW\n"));
972 return VERR_BUFFER_OVERFLOW;
973 }
974 if ( (i + 1 < cwSrc)
975 && (pu16Src[i] == CARRIAGERETURN)
976 && (pu16Src[i + 1] == LINEFEED))
977 {
978 ++i;
979 }
980 pu16Dest[cwDestPos] = pu16Src[i];
981 if (pu16Src[i] == 0)
982 {
983 break;
984 }
985 }
986 if (cwDestPos == cwDest)
987 {
988 LogFlowFunc(("returning VERR_BUFFER_OVERFLOW\n"));
989 return VERR_BUFFER_OVERFLOW;
990 }
991 pu16Dest[cwDestPos] = 0;
992 LogFlowFunc(("set string %ls. Returning\n", pu16Dest + 1));
993 return VINF_SUCCESS;
994}
995
996/**
997 * Satisfy a request from the guest to convert the clipboard text to Utf16.
998 *
999 * @returns true if we successfully convert the data to the format requested, false otherwise.
1000 *
1001 * @param atomTypeReturn The type of the data we are returning
1002 * @param pValReturn A pointer to the data we are returning. This should be to memory
1003 * allocated by XtMalloc, which will be freed by the toolkit later
1004 * @param pcLenReturn The length of the data we are returning
1005 * @param piFormatReturn The format (8bit, 16bit, 32bit) of the data we are returning
1006 */
1007static Boolean vboxClipboardConvertUtf16(Atom *atomTypeReturn, XtPointer *pValReturn,
1008 unsigned long *pcLenReturn, int *piFormatReturn)
1009{
1010 PRTUTF16 pu16GuestText, pu16HostText;
1011 unsigned cbHostText, cwHostText, cwGuestText;
1012 int rc;
1013
1014 LogFlowFunc(("\n"));
1015 rc = vboxClipboardReadHostData(VBOX_SHARED_CLIPBOARD_FMT_UNICODETEXT,
1016 reinterpret_cast<void **>(&pu16HostText), &cbHostText);
1017 if ((rc != VINF_SUCCESS) || cbHostText == 0)
1018 {
1019 Log (("vboxClipboardConvertUtf16: vboxClipboardReadHostData returned %Vrc, %d bytes of data\n", rc, cbHostText));
1020 g_ctx.hostFormats = 0;
1021 LogFlowFunc(("rc = false\n"));
1022 return false;
1023 }
1024 cwHostText = cbHostText / 2;
1025 cwGuestText = vboxClipboardUtf16GetLinSize(pu16HostText, cwHostText);
1026 pu16GuestText = reinterpret_cast<PRTUTF16>(XtMalloc(cwGuestText * 2));
1027 if (pu16GuestText == 0)
1028 {
1029 RTMemFree(reinterpret_cast<void *>(pu16HostText));
1030 LogFlowFunc(("rc = false\n"));
1031 return false;
1032 }
1033 rc = vboxClipboardUtf16WinToLin(pu16HostText, cwHostText, pu16GuestText, cwGuestText);
1034 if (rc != VINF_SUCCESS)
1035 {
1036 Log2(("vboxClipboardConvertUtf16: vboxClipboardUtf16WinToLin returned %Vrc\n", rc));
1037 RTMemFree(reinterpret_cast<void *>(pu16HostText));
1038 XtFree(reinterpret_cast<char *>(pu16GuestText));
1039 LogFlowFunc(("rc = false\n"));
1040 return false;
1041 }
1042 Log2 (("vboxClipboardConvertUtf16: returning Unicode, original text is %.*ls\n",
1043 cwHostText, pu16HostText));
1044 Log2 (("vboxClipboardConvertUtf16: converted text is %.*ls\n", cwGuestText - 1,
1045 pu16GuestText + 1));
1046 RTMemFree(reinterpret_cast<void *>(pu16HostText));
1047 *atomTypeReturn = g_ctx.atomUtf16;
1048 *pValReturn = reinterpret_cast<XtPointer>(pu16GuestText);
1049 *pcLenReturn = cwGuestText * 2;
1050 *piFormatReturn = 8;
1051 LogFlowFunc(("rc = true\n"));
1052 return true;
1053}
1054
1055/**
1056 * Satisfy a request from the guest to convert the clipboard text to Utf8.
1057 *
1058 * @returns true if we successfully convert the data to the format requested, false otherwise.
1059 *
1060 * @param atomTypeReturn The type of the data we are returning
1061 * @param pValReturn A pointer to the data we are returning. This should be to memory
1062 * allocated by XtMalloc, which will be freed by the toolkit later
1063 * @param pcLenReturn The length of the data we are returning
1064 * @param piFormatReturn The format (8bit, 16bit, 32bit) of the data we are returning
1065 */
1066static Boolean vboxClipboardConvertUtf8(Atom *atomTypeReturn, XtPointer *pValReturn,
1067 unsigned long *pcLenReturn, int *piFormatReturn)
1068{
1069 PRTUTF16 pu16GuestText, pu16HostText;
1070 char *pcGuestText;
1071 unsigned cbHostText, cwHostText, cwGuestText, cbGuestText;
1072 int rc;
1073
1074 LogFlowFunc(("\n"));
1075 /* Get the host Utf16 data and convert it to Linux Utf16. */
1076 rc = vboxClipboardReadHostData(VBOX_SHARED_CLIPBOARD_FMT_UNICODETEXT,
1077 reinterpret_cast<void **>(&pu16HostText), &cbHostText);
1078 if ((rc != VINF_SUCCESS) || cbHostText == 0)
1079 {
1080 Log (("vboxClipboardConvertUtf16: vboxClipboardReadHostData returned %Vrc, %d bytes of data\n", rc, cbGuestText));
1081 g_ctx.hostFormats = 0;
1082 LogFlowFunc(("rc = false\n"));
1083 return false;
1084 }
1085 cwHostText = cbHostText / 2;
1086 Log2 (("vboxClipboardConvertUtf8: original text is %.*ls\n", cwHostText, pu16HostText));
1087 cwGuestText = vboxClipboardUtf16GetLinSize(pu16HostText, cwHostText);
1088 pu16GuestText = reinterpret_cast<PRTUTF16>(RTMemAlloc(cwGuestText * 2));
1089 if (pu16GuestText == 0)
1090 {
1091 RTMemFree(reinterpret_cast<char *>(pu16HostText));
1092 LogFlowFunc(("rc = false\n"));
1093 return false;
1094 }
1095 rc = vboxClipboardUtf16WinToLin(pu16HostText, cwHostText, pu16GuestText, cwGuestText);
1096 RTMemFree(reinterpret_cast<char *>(pu16HostText));
1097 if (rc != VINF_SUCCESS)
1098 {
1099 Log2(("vboxClipboardConvertUtf16: vboxClipboardUtf16WinToLin returned %Vrc\n", rc));
1100 RTMemFree(reinterpret_cast<char *>(pu16GuestText));
1101 LogFlowFunc(("rc = false\n"));
1102 return false;
1103 }
1104 /* Now convert the Utf16 Linux text to Utf8 */
1105 cbGuestText = cwGuestText * 3; /* Should always be enough. */
1106 pcGuestText = XtMalloc(cbGuestText);
1107 if (pcGuestText == 0)
1108 {
1109 RTMemFree(reinterpret_cast<char *>(pu16GuestText));
1110 LogFlowFunc(("rc = false\n"));
1111 return false;
1112 }
1113 /* Our runtime can't cope with endian markers. */
1114 rc = RTUtf16ToUtf8Ex(pu16GuestText + 1, cwGuestText - 1, &pcGuestText, cbGuestText, 0);
1115 RTMemFree(reinterpret_cast<char *>(pu16GuestText));
1116 if (rc != VINF_SUCCESS)
1117 {
1118 XtFree(pcGuestText);
1119 LogFlowFunc(("rc = false\n"));
1120 return false;
1121 }
1122 Log2 (("vboxClipboardConvertUtf8: converted text is %.*s\n", cbGuestText, pcGuestText));
1123 *atomTypeReturn = g_ctx.atomUtf8;
1124 *pValReturn = reinterpret_cast<XtPointer>(pcGuestText);
1125 *pcLenReturn = cbGuestText;
1126 *piFormatReturn = 8;
1127 LogFlowFunc(("rc = true\n"));
1128 return true;
1129}
1130
1131/**
1132 * Satisfy a request from the guest to convert the clipboard text to compound text.
1133 *
1134 * @returns true if we successfully convert the data to the format requested, false otherwise.
1135 *
1136 * @param atomTypeReturn The type of the data we are returning
1137 * @param pValReturn A pointer to the data we are returning. This should be to memory
1138 * allocated by XtMalloc, which will be freed by the toolkit later
1139 * @param pcLenReturn The length of the data we are returning
1140 * @param piFormatReturn The format (8bit, 16bit, 32bit) of the data we are returning
1141 */
1142static Boolean vboxClipboardConvertCText(Atom *atomTypeReturn, XtPointer *pValReturn,
1143 unsigned long *pcLenReturn, int *piFormatReturn)
1144{
1145 PRTUTF16 pu16GuestText, pu16HostText;
1146 char *pcUtf8Text;
1147 unsigned cbHostText, cwHostText, cwGuestText, cbUtf8Text;
1148 XTextProperty property;
1149 int rc;
1150
1151 LogFlowFunc(("\n"));
1152 /* Get the host Utf16 data and convert it to Linux Utf16. */
1153 rc = vboxClipboardReadHostData(VBOX_SHARED_CLIPBOARD_FMT_UNICODETEXT,
1154 reinterpret_cast<void **>(&pu16HostText), &cbHostText);
1155 if ((rc != VINF_SUCCESS) || cbHostText == 0)
1156 {
1157 Log (("vboxClipboardConvertCText: vboxClipboardReadHostData returned %Vrc, %d bytes of data\n", rc, cbHostText));
1158 g_ctx.hostFormats = 0;
1159 LogFlowFunc(("rc = false\n"));
1160 return false;
1161 }
1162 Log2 (("vboxClipboardConvertCText: original text is %.*ls\n", cwHostText, pu16HostText));
1163 cwHostText = cbHostText / 2;
1164 cwGuestText = vboxClipboardUtf16GetLinSize(pu16HostText, cwHostText);
1165 pu16GuestText = reinterpret_cast<PRTUTF16>(RTMemAlloc(cwGuestText * 2));
1166 if (pu16GuestText == 0)
1167 {
1168 Log(("vboxClipboardConvertCText: out of memory\n"));
1169 RTMemFree(reinterpret_cast<char *>(pu16HostText));
1170 LogFlowFunc(("rc = false\n"));
1171 return false;
1172 }
1173 rc = vboxClipboardUtf16WinToLin(pu16HostText, cwHostText, pu16GuestText, cwGuestText);
1174 RTMemFree(reinterpret_cast<char *>(pu16HostText));
1175 if (rc != VINF_SUCCESS)
1176 {
1177 Log2(("vboxClipboardConvertUtf16: vboxClipboardUtf16WinToLin returned %Vrc\n", rc));
1178 RTMemFree(reinterpret_cast<char *>(pu16GuestText));
1179 LogFlowFunc(("rc = false\n"));
1180 return false;
1181 }
1182 /* Now convert the Utf16 Linux text to Utf8 */
1183 cbUtf8Text = cwGuestText * 3; /* Should always be enough. */
1184 pcUtf8Text = reinterpret_cast<char *>(RTMemAlloc(cbUtf8Text));
1185 if (pcUtf8Text == 0)
1186 {
1187 Log(("vboxClipboardConvertCText: out of memory\n"));
1188 RTMemFree(reinterpret_cast<char *>(pu16GuestText));
1189 LogFlowFunc(("rc = false\n"));
1190 return false;
1191 }
1192 /* Our runtime can't cope with endian markers. */
1193 rc = RTUtf16ToUtf8Ex(pu16GuestText + 1, cwGuestText - 1, &pcUtf8Text, cbUtf8Text, 0);
1194 RTMemFree(reinterpret_cast<char *>(pu16GuestText));
1195 if (rc != VINF_SUCCESS)
1196 {
1197 Log(("vboxClipboardConvertCText: RTUtf16ToUtf8Ex failed: rc=%Vrc\n", rc));
1198 XtFree(pcUtf8Text);
1199 LogFlowFunc(("rc = false\n"));
1200 return false;
1201 }
1202 /* And finally (!) convert the Utf8 text to compound text. */
1203 rc = Xutf8TextListToTextProperty(XtDisplay(g_ctx.widget), &pcUtf8Text, 1,
1204 XCompoundTextStyle, &property);
1205 RTMemFree(pcUtf8Text);
1206 if (rc < 0)
1207 {
1208 const char *pcReason;
1209 switch(rc)
1210 {
1211 case XNoMemory:
1212 pcReason = "out of memory";
1213 break;
1214 case XLocaleNotSupported:
1215 pcReason = "locale (Utf8) not supported";
1216 break;
1217 case XConverterNotFound:
1218 pcReason = "converter not found";
1219 break;
1220 default:
1221 pcReason = "unknown error";
1222 }
1223 LogRel(("vboxClipboardConvertCText: Xutf8TextListToTextProperty failed. Reason: %s\n",
1224 pcReason));
1225 XFree(property.value);
1226 LogFlowFunc(("rc = false\n"));
1227 return false;
1228 }
1229 Log2 (("vboxClipboardConvertCText: converted text is %s\n", property.value));
1230 *atomTypeReturn = property.encoding;
1231 *pValReturn = reinterpret_cast<XtPointer>(property.value);
1232 *pcLenReturn = property.nitems;
1233 *piFormatReturn = property.format;
1234 LogFlowFunc(("rc = true\n"));
1235 return true;
1236}
1237
1238/**
1239 * Callback to convert the hosts clipboard data for an application on the guest. Called by the
1240 * X Toolkit.
1241 * @returns true if we successfully convert the data to the format requested, false otherwise.
1242 *
1243 * @param atomSelection The selection which is being requested. We only handle the clipboard.
1244 * @param atomTarget The format we should convert the data to
1245 * @param atomTypeReturn The type of the data we are returning
1246 * @param pValReturn A pointer to the data we are returning. This should be to memory
1247 * allocated by XtMalloc, which will be freed by the toolkit later
1248 * @param pcLenReturn The length of the data we are returning
1249 * @param piFormatReturn The format (8bit, 16bit, 32bit) of the data we are returning
1250 */
1251static Boolean vboxClipboardConvertProc(Widget, Atom *atomSelection, Atom *atomTarget,
1252 Atom *atomTypeReturn, XtPointer *pValReturn,
1253 unsigned long *pcLenReturn, int *piFormatReturn)
1254{
1255 g_eClipboardFormats eFormat = INVALID;
1256 int rc;
1257
1258 LogFlowFunc(("\n"));
1259 if (*atomSelection != g_ctx.atomClipboard)
1260 {
1261 LogFlowFunc(("rc = false\n"));
1262 return false;
1263 }
1264#ifdef DEBUG
1265 char *szAtomName = XGetAtomName(XtDisplay(g_ctx.widget), *atomTarget);
1266 if (szAtomName != 0)
1267 {
1268 Log2 (("vboxClipboardConvertProc: request for format %s\n", szAtomName));
1269 XFree(szAtomName);
1270 }
1271 else
1272 {
1273 Log(("vboxClipboardConvertProc: request for invalid target atom %d!\n", *atomTarget));
1274 }
1275#endif
1276 if (*atomTarget == g_ctx.atomTargets)
1277 {
1278 eFormat = TARGETS;
1279 }
1280 else
1281 {
1282 for (unsigned i = 0; i != g_ctx.formatList.size(); ++i)
1283 {
1284 if (g_ctx.formatList[i].atom == *atomTarget)
1285 {
1286 eFormat = g_ctx.formatList[i].format;
1287 break;
1288 }
1289 }
1290 }
1291 switch (eFormat)
1292 {
1293 case TARGETS:
1294 rc = vboxClipboardConvertTargets(atomTypeReturn, pValReturn, pcLenReturn, piFormatReturn);
1295 LogFlowFunc(("rc=%d\n", rc));
1296 return rc;
1297 case UTF16:
1298 rc = vboxClipboardConvertUtf16(atomTypeReturn, pValReturn, pcLenReturn, piFormatReturn);
1299 LogFlowFunc(("rc=%d\n", rc));
1300 return rc;
1301 case UTF8:
1302 rc = vboxClipboardConvertUtf8(atomTypeReturn, pValReturn, pcLenReturn, piFormatReturn);
1303 LogFlowFunc(("rc=%d\n", rc));
1304 return rc;
1305 case CTEXT:
1306 rc = vboxClipboardConvertCText(atomTypeReturn, pValReturn, pcLenReturn, piFormatReturn);
1307 LogFlowFunc(("rc=%d\n", rc));
1308 return rc;
1309 default:
1310 Log(("vboxClipboardConvertProc: bad format\n"));
1311 LogFlowFunc(("rc = false\n"));
1312 return false;
1313 }
1314}
1315
1316static void vboxClipboardLoseProc(Widget, Atom *)
1317{
1318 LogFlowFunc(("giving the guest clipboard ownership\n"));
1319 g_ctx.eOwner = GUEST;
1320 g_ctx.notifyHost = true;
1321 LogFlowFunc(("returning\n"));
1322}
1323
1324/**
1325 * The host is taking possession of the shared clipboard. Called by the HGCM clipboard
1326 * subsystem.
1327 *
1328 * @param u32Formats Clipboard formats the the guest is offering
1329 */
1330void vboxClipboardFormatAnnounce (uint32_t u32Formats)
1331{
1332 g_ctx.hostFormats = u32Formats;
1333 LogFlowFunc(("u32Formats = %d\n", u32Formats));
1334 if (u32Formats == 0)
1335 {
1336 /* This is just an automatism, not a genuine anouncement */
1337 LogFlowFunc(("returning\n"));
1338 return;
1339 }
1340 Log2 (("vboxClipboardFormatAnnounce: giving the host clipboard ownership\n"));
1341 g_ctx.eOwner = HOST;
1342 g_ctx.guestTextFormat = INVALID;
1343 g_ctx.guestBitmapFormat = INVALID;
1344 if (XtOwnSelection(g_ctx.widget, g_ctx.atomClipboard, CurrentTime, vboxClipboardConvertProc,
1345 vboxClipboardLoseProc, 0) != True)
1346 {
1347 Log2 (("vboxClipboardFormatAnnounce: returning clipboard ownership to the guest\n"));
1348 g_ctx.notifyHost = true;
1349 g_ctx.eOwner = GUEST;
1350 }
1351 LogFlowFunc(("returning\n"));
1352}
1353
1354/**
1355 * Called when the host wants to read the guest clipboard.
1356 *
1357 * @param u32Format The format that the host would like to receive the data in
1358 */
1359int vboxClipboardReadGuestData (uint32_t u32Format)
1360{
1361 LogFlowFunc(("u32Format = %d\n", u32Format));
1362
1363 /*
1364 * The guest wants to read data in the given format.
1365 */
1366 if (u32Format & VBOX_SHARED_CLIPBOARD_FMT_UNICODETEXT)
1367 {
1368 if (g_ctx.guestTextFormat == INVALID)
1369 {
1370 /* No data available. */
1371 vboxClipboardSendData (0, NULL, 0);
1372 LogFlowFunc(("sent empty data, returned VINF_SUCCESS\n"));
1373 return VINF_SUCCESS;
1374 }
1375 g_ctx.requestGuestFormat = g_ctx.guestTextFormat;
1376 /* Send out a request for the data to the current clipboard owner */
1377 XtGetSelectionValue(g_ctx.widget, g_ctx.atomClipboard, g_ctx.atomGuestTextFormat,
1378 vboxClipboardGetProc, 0, CurrentTime);
1379 /* When the data arrives, the vboxClipboardGetProc callback will be called. The
1380 callback will signal the event semaphore when it has processed the data for us. */
1381 }
1382 else
1383 {
1384 vboxClipboardSendData (0, NULL, 0);
1385 LogFlowFunc(("sent empty data, returned VERR_NOT_IMPLEMENTED\n"));
1386 return VERR_NOT_IMPLEMENTED;
1387 }
1388 LogFlowFunc(("returned VINF_SUCCESS\n"));
1389 return VINF_SUCCESS;
1390}
1391
1392/** Terminate the guest side of the shared clipboard. */
1393void vboxClipboardDestroy (void)
1394{
1395 LogFlowFunc(("\n"));
1396
1397 /* Set the termination flag. */
1398 XtAppSetExitFlag(g_ctx.appContext);
1399 LogFlowFunc(("returning\n"));
1400}
1401
1402/**
1403 * The main loop of our clipboard reader.
1404 */
1405static int vboxClipboardThread(RTTHREAD /* ThreadSelf */, void * /* pvUser */)
1406{
1407 int rc = VINF_SUCCESS;
1408 LogFlowFunc(("Starting clipboard thread\n"));
1409
1410 for (;;)
1411 {
1412 VBoxClipboardGetHostMsg parms;
1413 VBOX_INIT_CALL(&parms, VBOX_SHARED_CLIPBOARD_FN_GET_HOST_MSG, g_ctx.client);
1414
1415 VBoxHGCMParmUInt32Set (&parms.msg, 0);
1416 VBoxHGCMParmUInt32Set (&parms.formats, 0);
1417 if (ioctl(g_ctx.receiveDevice, IOCTL_VBOXGUEST_HGCM_CALL, (void*)&parms) < 0)
1418 {
1419 Log(("Failed to call the driver for host message.\n"));
1420
1421 /* Wait a bit before retrying. */
1422 if (RTSemEventWait(g_ctx.terminating, 1000) == VINF_SUCCESS)
1423 break;
1424 continue;
1425 }
1426 rc = parms.hdr.result;
1427 if (VBOX_SUCCESS (rc))
1428 {
1429 uint32_t u32Msg;
1430 uint32_t u32Formats;
1431
1432 rc = VBoxHGCMParmUInt32Get (&parms.msg, &u32Msg);
1433
1434 if (VBOX_SUCCESS (rc))
1435 {
1436 rc = VBoxHGCMParmUInt32Get (&parms.formats, &u32Formats);
1437
1438 if (VBOX_SUCCESS (rc))
1439 {
1440 Log(("vboxClipboardHostEvent u32Msg %d, u32Formats %d\n", u32Msg, u32Formats));
1441
1442 switch (u32Msg)
1443 {
1444 case VBOX_SHARED_CLIPBOARD_HOST_MSG_FORMATS:
1445 {
1446 /* The host has announced available clipboard formats.
1447 * Save the information so that it is available for
1448 * future requests from guest applications.
1449 */
1450 vboxClipboardFormatAnnounce(u32Formats);
1451 } break;
1452 case VBOX_SHARED_CLIPBOARD_HOST_MSG_READ_DATA:
1453 {
1454 /* The host needs data in the specified format.
1455 */
1456 vboxClipboardReadGuestData(u32Formats);
1457 } break;
1458 case VBOX_SHARED_CLIPBOARD_HOST_MSG_QUIT:
1459 {
1460 /* The host is terminating.
1461 */
1462 vboxClipboardDestroy();
1463 rc = VERR_INTERRUPTED;
1464 } break;
1465 default:
1466 {
1467 Log(("Unsupported message from host!!!\n"));
1468 }
1469 }
1470 }
1471 }
1472 }
1473 if (rc == VERR_INTERRUPTED)
1474 {
1475 /* Wait for termination event. */
1476 RTSemEventWait(g_ctx.terminating, RT_INDEFINITE_WAIT);
1477 break;
1478 }
1479 if (VBOX_FAILURE (rc))
1480 {
1481 /* Wait a bit before retrying. */
1482 if (RTSemEventWait(g_ctx.terminating, 1000) == VINF_SUCCESS)
1483 break;
1484 continue;
1485 }
1486
1487 Log(("processed host event rc = %d\n", rc));
1488 }
1489 LogFlowFunc(("rc=%d\n", rc));
1490 return rc;
1491}
1492
1493/**
1494 * Disconnect the guest clipboard from the host.
1495 */
1496void vboxClipboardDisconnect (void)
1497{
1498#if 0
1499 VMMDevHGCMDisconnect request;
1500#endif
1501 LogFlowFunc(("\n"));
1502
1503 AssertReturn(g_ctx.client != 0, (void) 0);
1504#if 0
1505 /* Currently, disconnecting is not needed, as the new "connect clipboard"
1506 ioctl in the Guest Additions kernel module disconnects the last
1507 connection made automatically. The reason for this change was that
1508 currently only one clipboard connection is allowed, and that if the
1509 client holding that connection was terminated too abruptly, the
1510 information needed to disconnect that connection was lost. If the
1511 subsystem is ever changed to allow several connections, this will have
1512 to be rethought. */
1513 vmmdevInitRequest((VMMDevRequestHeader*)&request, VMMDevReq_HGCMDisconnect);
1514 request.u32ClientID = g_ctx.client;
1515 ioctl(g_ctx.sendDevice, IOCTL_VBOXGUEST_VMMREQUEST, (void*)&request);
1516#endif
1517 LogFlowFunc(("returning\n"));
1518}
1519
1520
1521/**
1522 * Connect the guest clipboard to the host.
1523 *
1524 * @returns RT status code
1525 */
1526int vboxClipboardConnect (void)
1527{
1528 LogFlowFunc(("\n"));
1529 int rc;
1530 /* Only one client is supported for now */
1531 AssertReturn(g_ctx.client == 0, VERR_NOT_SUPPORTED);
1532
1533 /* Initialise the termination semaphore. */
1534 RTSemEventCreate(&g_ctx.terminating);
1535 /* Open a connection to the driver for sending requests. */
1536 g_ctx.sendDevice = open(VBOXGUEST_DEVICE_NAME, O_RDWR, 0);
1537 if (g_ctx.sendDevice < 0)
1538 {
1539 int err = errno;
1540
1541 Log(("Error opening kernel module! errno = %d\n", err));
1542 cout << "Failed to open the VirtualBox device in the guest." << endl;
1543 LogFlowFunc(("returning 1\n"));
1544 return RTErrConvertFromErrno(err);
1545 }
1546 /* Open a connection to the driver for polling for host requests. */
1547 g_ctx.receiveDevice = open(VBOXGUEST_DEVICE_NAME, O_RDWR, 0);
1548 if (g_ctx.receiveDevice < 0)
1549 {
1550 int err = errno;
1551
1552 Log(("Error opening kernel module! rc = %d\n", err));
1553 cout << "Failed to open the VirtualBox device in the guest" << endl;
1554 LogFlowFunc(("returning 1\n"));
1555 return RTErrConvertFromErrno(err);
1556 }
1557
1558 rc = ioctl(g_ctx.sendDevice, IOCTL_VBOXGUEST_CLIPBOARD_CONNECT, (void*)&g_ctx.client);
1559 if (rc >= 0)
1560 {
1561 if (g_ctx.client == 0)
1562 {
1563 cout << "We got an invalid client ID of 0!" << endl;
1564 return VERR_NOT_SUPPORTED;
1565 }
1566 g_ctx.eOwner = HOST;
1567 }
1568 else
1569 {
1570 Log(("Error connecting to host. rc = %d (%s)\n", rc, strerror(-rc)));
1571 cout << "Unable to connect to the host system." << endl;
1572 LogFlowFunc(("returned VERR_NOT_SUPPORTED\n"));
1573 return VERR_NOT_SUPPORTED;
1574 }
1575 LogFlowFunc(("returned VINF_SUCCESS\n"));
1576 return VINF_SUCCESS;
1577}
1578
1579/** We store information about the target formats we can handle in a global vector for internal
1580 use. */
1581static void vboxClipboardAddFormat(const char *pszName, g_eClipboardFormats eFormat,
1582 unsigned hostFormat)
1583{
1584 VBOXCLIPBOARDFORMAT sFormat;
1585 LogFlowFunc(("pszName=%s, eFormat=%d, hostFormat=%u\n", pszName, eFormat, hostFormat));
1586 /* Get an atom from the X server for that target format */
1587 Atom atomFormat = XInternAtom(XtDisplay(g_ctx.widget), pszName, false);
1588 Log2(("vboxClipboardAddFormat: atomFormat=%d\n", atomFormat));
1589 if (atomFormat == 0)
1590 {
1591 LogFlowFunc(("atomFormat=0! returning...\n"));
1592 return;
1593 }
1594 sFormat.atom = atomFormat;
1595 sFormat.format = eFormat;
1596 sFormat.hostFormat = hostFormat;
1597 g_ctx.formatList.push_back(sFormat);
1598 LogFlowFunc (("returning\n"));
1599}
1600
1601/** Create the X11 window which we use to interact with the guest clipboard */
1602static int vboxClipboardCreateWindow(void)
1603{
1604 /* Create a window and make it a clipboard viewer. */
1605 int cArgc = 0;
1606 char *pcArgv = 0;
1607 String szFallbackResources[] = { (char*)"*.width: 1", (char*)"*.height: 1", 0 };
1608
1609 /* Set up the Clipbard application context and main window. */
1610 g_ctx.widget = XtOpenApplication(&g_ctx.appContext, "VBoxClipboard", 0, 0, &cArgc, &pcArgv,
1611 szFallbackResources, applicationShellWidgetClass, 0, 0);
1612 AssertMsgReturn(g_ctx.widget != 0, ("Failed to construct the X clipboard window\n"),
1613 VERR_ACCESS_DENIED);
1614 XtSetMappedWhenManaged(g_ctx.widget, false);
1615 XtRealizeWidget(g_ctx.widget);
1616
1617 /* Get hold of the atoms which we need */
1618 g_ctx.atomClipboard = XInternAtom(XtDisplay(g_ctx.widget), "CLIPBOARD", false /* only_if_exists */);
1619 g_ctx.atomTargets = XInternAtom(XtDisplay(g_ctx.widget), "TARGETS", false);
1620 g_ctx.atomMultiple = XInternAtom(XtDisplay(g_ctx.widget), "MULTIPLE", false);
1621 g_ctx.atomTimestamp = XInternAtom(XtDisplay(g_ctx.widget), "TIMESTAMP", false);
1622 g_ctx.atomUtf16 = XInternAtom(XtDisplay(g_ctx.widget),
1623 "text/plain;charset=ISO-10646-UCS-2", false);
1624 g_ctx.atomUtf8 = XInternAtom(XtDisplay(g_ctx.widget), "UTF_STRING", false);
1625 g_ctx.atomCText = XInternAtom(XtDisplay(g_ctx.widget), "COMPOUND_TEXT", false);
1626 /* And build up the vector of supported formats */
1627#ifdef USE_UTF16
1628 vboxClipboardAddFormat("text/plain;charset=ISO-10646-UCS-2", UTF16,
1629 VBOX_SHARED_CLIPBOARD_FMT_UNICODETEXT);
1630#endif
1631#ifdef USE_UTF8
1632 vboxClipboardAddFormat("UTF8_STRING", UTF8,
1633 VBOX_SHARED_CLIPBOARD_FMT_UNICODETEXT);
1634 vboxClipboardAddFormat("text/plain;charset=UTF-8", UTF8,
1635 VBOX_SHARED_CLIPBOARD_FMT_UNICODETEXT);
1636 vboxClipboardAddFormat("text/plain;charset=utf-8", UTF8,
1637 VBOX_SHARED_CLIPBOARD_FMT_UNICODETEXT);
1638 vboxClipboardAddFormat("STRING", UTF8,
1639 VBOX_SHARED_CLIPBOARD_FMT_UNICODETEXT);
1640 vboxClipboardAddFormat("TEXT", UTF8,
1641 VBOX_SHARED_CLIPBOARD_FMT_UNICODETEXT);
1642 vboxClipboardAddFormat("text/plain", UTF8,
1643 VBOX_SHARED_CLIPBOARD_FMT_UNICODETEXT);
1644#endif
1645#ifdef USE_CTEXT
1646 vboxClipboardAddFormat("COMPOUND_TEXT", CTEXT,
1647 VBOX_SHARED_CLIPBOARD_FMT_UNICODETEXT);
1648#endif
1649 return VINF_SUCCESS;
1650}
1651
1652/** Initialise the guest side of the shared clipboard. */
1653int vboxClipboardMain (void)
1654{
1655 int rc;
1656 LogFlowFunc(("\n"));
1657
1658 rc = vboxClipboardCreateWindow();
1659 if (VBOX_FAILURE(rc))
1660 {
1661 return rc;
1662 }
1663
1664 rc = RTThreadCreate(&g_ctx.thread, vboxClipboardThread, 0, 0, RTTHREADTYPE_IO,
1665 RTTHREADFLAGS_WAITABLE, "SHCLIP");
1666 AssertRCReturn(rc, rc);
1667 /* Set up a timer to poll the host clipboard */
1668 XtAppAddTimeOut(g_ctx.appContext, 200 /* ms */, vboxClipboardTimerProc, 0);
1669
1670 XtAppMainLoop(g_ctx.appContext);
1671 g_ctx.formatList.clear();
1672 XtDestroyApplicationContext(g_ctx.appContext);
1673 /* Set the termination signal. */
1674 RTSemEventSignal(g_ctx.terminating);
1675 LogFlowFunc(("returning %d\n", rc));
1676 return rc;
1677}
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