VirtualBox

source: vbox/trunk/src/VBox/Additions/x11/xclient/clipboard.cpp@ 7023

Last change on this file since 7023 was 6949, checked in by vboxsync, 17 years ago

Additions/x11: made some logging optional in the clipboard client

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