VirtualBox

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

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

Linux clipboard fix for X11 64bit weirdness

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