VirtualBox

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

Last change on this file since 3321 was 3321, checked in by vboxsync, 18 years ago

Linux host clipboard fixes and added compound text to the Linux guest clipboard

File size: 65.6 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 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) * (*piFormat) / sizeof(Atom) / 8;
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 XtFree(reinterpret_cast<char *>(pValue));
772 g_ctx.atomGuestTextFormat = atomBestTarget;
773 /* If the available formats as seen by the host have changed, or if we suspect that
774 the host has cached the clipboard data (which can change without our noticing it),
775 then tell the host that new clipboard contents are available. */
776 if ((eBestTarget != g_ctx.guestTextFormat) || (g_ctx.notifyHost == true))
777 {
778 uint32_t u32Formats = 0;
779#ifdef DEBUG
780 if (atomBestTarget != None)
781 {
782 char *szAtomName = XGetAtomName(XtDisplay(g_ctx.widget), atomBestTarget);
783 Log2 (("vboxClipboardTargetsProc: switching to guest text target %s\n", szAtomName));
784 XFree(szAtomName);
785 }
786 else
787 {
788 Log2(("vboxClipboardTargetsProc: no supported host text target found.\n"));
789 }
790#endif
791 g_ctx.guestTextFormat = eBestTarget;
792 if (eBestTarget != INVALID)
793 u32Formats |= VBOX_SHARED_CLIPBOARD_FMT_UNICODETEXT;
794 vboxClipboardReportFormats(u32Formats);
795 g_ctx.notifyHost = false;
796 }
797 ++cCalls;
798}
799
800/**
801 * This callback is called every 200ms to check the contents of the guest clipboard.
802 */
803static void vboxClipboardTimerProc(XtPointer /* pUserData */, XtIntervalId * /* hTimerId */)
804{
805 static int cCalls = 0;
806 Log3 (("vboxClipboardTimerProc called\n"));
807 /* Get the current clipboard contents */
808 if (g_ctx.eOwner == GUEST)
809 {
810 if ((cCalls % 10) == 0)
811 Log3 (("vboxClipboardTimerProc: requesting the targets that the guest clipboard offers\n"));
812 XtGetSelectionValue(g_ctx.widget, g_ctx.atomClipboard, g_ctx.atomTargets,
813 vboxClipboardTargetsProc, 0, CurrentTime);
814 }
815 /* Re-arm our timer */
816 XtAppAddTimeOut(g_ctx.appContext, 200 /* ms */, vboxClipboardTimerProc, 0);
817 ++cCalls;
818}
819
820/**
821 * Satisfy a request from the guest for available clipboard targets.
822 *
823 * @returns true if we successfully convert the data to the format requested, false otherwise.
824 *
825 * @param atomTypeReturn The type of the data we are returning
826 * @param pValReturn A pointer to the data we are returning. This should be to memory
827 * allocated by XtMalloc, which will be freed by the toolkit later
828 * @param pcLenReturn The length of the data we are returning
829 * @param piFormatReturn The format (8bit, 16bit, 32bit) of the data we are returning
830 */
831static Boolean vboxClipboardConvertTargets(Atom *atomTypeReturn, XtPointer *pValReturn,
832 unsigned long *pcLenReturn, int *piFormatReturn)
833{
834 unsigned uListSize = g_ctx.formatList.size();
835 Atom *atomTargets = reinterpret_cast<Atom *>(XtMalloc((uListSize + 3) * sizeof(Atom)));
836 unsigned cTargets = 0;
837
838 LogFlowFunc(("\n"));
839 Log2(("vboxClipboardConvertTargets: uListSize=%u\n", uListSize));
840 for (unsigned i = 0; i < uListSize; ++i)
841 {
842 if ( ((g_ctx.hostFormats & VBOX_SHARED_CLIPBOARD_FMT_UNICODETEXT) != 0)
843 && (g_ctx.formatList[i].hostFormat == VBOX_SHARED_CLIPBOARD_FMT_UNICODETEXT))
844 {
845 atomTargets[cTargets] = g_ctx.formatList[i].atom;
846 ++cTargets;
847 }
848 }
849 atomTargets[cTargets] = g_ctx.atomTargets;
850 atomTargets[cTargets + 1] = g_ctx.atomMultiple;
851 atomTargets[cTargets + 2] = g_ctx.atomTimestamp;
852#ifdef DEBUG
853 for (unsigned i = 0; i < cTargets + 3; i++)
854 {
855 char *szAtomName = XGetAtomName(XtDisplay(g_ctx.widget), atomTargets[i]);
856 if (szAtomName != 0)
857 {
858 Log2 (("vboxClipboardConvertTargets: returning target %s\n", szAtomName));
859 XFree(szAtomName);
860 }
861 else
862 {
863 Log(("vboxClipboardConvertTargets: invalid atom %d in the list!\n", atomTargets[i]));
864 }
865 }
866#endif
867 *atomTypeReturn = XA_ATOM;
868 *pValReturn = reinterpret_cast<XtPointer>(atomTargets);
869 *pcLenReturn = cTargets + 3;
870 *piFormatReturn = sizeof(Atom) * 8;
871 LogFlowFunc(("returning true\n"));
872 return true;
873}
874
875/**
876 * Get the size of the buffer needed to hold a zero-terminated Utf16 string with Linux EOLs
877 * converted from a Utf16 string with Windows EOLs.
878 *
879 * @returns The size of the buffer needed in bytes
880 *
881 * @param pu16Src The source Utf16 string
882 * @param cwSrc The length in 16 bit words of the source string
883 */
884static int vboxClipboardUtf16GetLinSize(PRTUTF16 pu16Src, size_t cwSrc)
885{
886 enum { LINEFEED = 0xa, CARRIAGERETURN = 0xd };
887 size_t cwDest;
888
889 LogFlowFunc(("pu16Src=%.*ls, cwSrc=%u\n", cwSrc, pu16Src, cwSrc));
890 AssertReturn(pu16Src != 0, VERR_INVALID_PARAMETER);
891 /* We only take little endian Utf16 */
892 AssertReturn(pu16Src[0] != 0xfffe, VERR_INVALID_PARAMETER);
893 if (cwSrc == 0)
894 {
895 LogFlowFunc(("returning 0\n"));
896 return 0;
897 }
898 /* Calculate the size of the destination text string. */
899 /* Is this Utf16 or Utf16-LE? */
900 if (pu16Src[0] == 0xfeff)
901 cwDest = 0;
902 else
903 cwDest = 1;
904 for (size_t i = 0; i < cwSrc; ++i, ++cwDest)
905 {
906 if ( (i + 1 < cwSrc)
907 && (pu16Src[i] == CARRIAGERETURN)
908 && (pu16Src[i + 1] == LINEFEED))
909 ++i;
910 if (pu16Src[i] == 0)
911 {
912 break;
913 }
914 }
915 /* The terminating null */
916 ++cwDest;
917 LogFlowFunc(("returning %d\n", cwDest * 2));
918 return cwDest * 2;
919}
920
921/**
922 * Convert Utf16-LE text with Windows EOLs to Utf16 with Linux EOLs. This function does not
923 * verify that the Utf16 is valid.
924 *
925 * @returns VBox status code
926 *
927 * @param pu16Src Text to convert
928 * @param cwSrc Size of the source text in 16 bit words
929 * @param pu16Dest The buffer to store the converted text to
930 * @param cwDest The size of the buffer for the destination text
931 */
932static int vboxClipboardUtf16WinToLin(PRTUTF16 pu16Src, size_t cwSrc, PRTUTF16 pu16Dest,
933 size_t cwDest)
934{
935 enum { LINEFEED = 0xa, CARRIAGERETURN = 0xd };
936 size_t cwDestPos;
937
938 LogFlowFunc(("pu16Src=%.*ls, cwSrc=%u, pu16Dest=%p, cwDest=%u\n",
939 cwSrc, pu16Src, cwSrc, pu16Dest, cwDest));
940 /* A buffer of size 0 may not be an error, but it is not a good idea either. */
941 Assert(cwDest > 0);
942 AssertReturn(VALID_PTR(pu16Src), VERR_INVALID_PARAMETER);
943 /* We only take little endian Utf16 */
944 AssertReturn(pu16Src[0] != 0xfffe, VERR_INVALID_PARAMETER);
945 if (cwSrc == 0)
946 {
947 if (cwDest != 0)
948 pu16Dest[0] = 0;
949 LogFlowFunc(("set empty string. Returning VINF_SUCCESS\n"));
950 return VINF_SUCCESS;
951 }
952 /* Prepend the Utf16 byte order marker if it is missing. */
953 if (pu16Src[0] == 0xfeff)
954 {
955 cwDestPos = 0;
956 }
957 else
958 {
959 if (cwDest == 0)
960 {
961 LogFlowFunc(("returning VERR_BUFFER_OVERFLOW\n"));
962 return VERR_BUFFER_OVERFLOW;
963 }
964 pu16Dest[0] = 0xfeff;
965 cwDestPos = 1;
966 }
967 for (size_t i = 0; i < cwSrc; ++i, ++cwDestPos)
968 {
969 if (cwDestPos == cwDest)
970 {
971 LogFlowFunc(("returning VERR_BUFFER_OVERFLOW\n"));
972 return VERR_BUFFER_OVERFLOW;
973 }
974 if ( (i + 1 < cwSrc)
975 && (pu16Src[i] == CARRIAGERETURN)
976 && (pu16Src[i + 1] == LINEFEED))
977 {
978 ++i;
979 }
980 pu16Dest[cwDestPos] = pu16Src[i];
981 if (pu16Src[i] == 0)
982 {
983 break;
984 }
985 }
986 if (cwDestPos == cwDest)
987 {
988 LogFlowFunc(("returning VERR_BUFFER_OVERFLOW\n"));
989 return VERR_BUFFER_OVERFLOW;
990 }
991 pu16Dest[cwDestPos] = 0;
992 LogFlowFunc(("set string %ls. Returning\n", pu16Dest + 1));
993 return VINF_SUCCESS;
994}
995
996/**
997 * Satisfy a request from the guest to convert the clipboard text to Utf16.
998 *
999 * @returns true if we successfully convert the data to the format requested, false otherwise.
1000 *
1001 * @param atomTypeReturn The type of the data we are returning
1002 * @param pValReturn A pointer to the data we are returning. This should be to memory
1003 * allocated by XtMalloc, which will be freed by the toolkit later
1004 * @param pcLenReturn The length of the data we are returning
1005 * @param piFormatReturn The format (8bit, 16bit, 32bit) of the data we are returning
1006 */
1007static Boolean vboxClipboardConvertUtf16(Atom *atomTypeReturn, XtPointer *pValReturn,
1008 unsigned long *pcLenReturn, int *piFormatReturn)
1009{
1010 PRTUTF16 pu16GuestText, pu16HostText;
1011 unsigned cbHostText, cwHostText, cwGuestText;
1012 int rc;
1013
1014 LogFlowFunc(("\n"));
1015 rc = vboxClipboardReadHostData(VBOX_SHARED_CLIPBOARD_FMT_UNICODETEXT,
1016 reinterpret_cast<void **>(&pu16HostText), &cbHostText);
1017 if ((rc != VINF_SUCCESS) || cbHostText == 0)
1018 {
1019 Log (("vboxClipboardConvertUtf16: vboxClipboardReadHostData returned %Vrc, %d bytes of data\n", rc, cbHostText));
1020 g_ctx.hostFormats = 0;
1021 LogFlowFunc(("rc = false\n"));
1022 return false;
1023 }
1024 cwHostText = cbHostText / 2;
1025 cwGuestText = vboxClipboardUtf16GetLinSize(pu16HostText, cwHostText);
1026 pu16GuestText = reinterpret_cast<PRTUTF16>(XtMalloc(cwGuestText * 2));
1027 if (pu16GuestText == 0)
1028 {
1029 RTMemFree(reinterpret_cast<void *>(pu16HostText));
1030 LogFlowFunc(("rc = false\n"));
1031 return false;
1032 }
1033 rc = vboxClipboardUtf16WinToLin(pu16HostText, cwHostText, pu16GuestText, cwGuestText);
1034 if (rc != VINF_SUCCESS)
1035 {
1036 Log2(("vboxClipboardConvertUtf16: vboxClipboardUtf16WinToLin returned %Vrc\n", rc));
1037 RTMemFree(reinterpret_cast<void *>(pu16HostText));
1038 XtFree(reinterpret_cast<char *>(pu16GuestText));
1039 LogFlowFunc(("rc = false\n"));
1040 return false;
1041 }
1042 Log2 (("vboxClipboardConvertUtf16: returning Unicode, original text is %.*ls\n",
1043 cwHostText, pu16HostText));
1044 Log2 (("vboxClipboardConvertUtf16: converted text is %.*ls\n", cwGuestText - 1,
1045 pu16GuestText + 1));
1046 RTMemFree(reinterpret_cast<void *>(pu16HostText));
1047 *atomTypeReturn = g_ctx.atomUtf16;
1048 *pValReturn = reinterpret_cast<XtPointer>(pu16GuestText);
1049 *pcLenReturn = cwGuestText * 2;
1050 *piFormatReturn = 8;
1051 LogFlowFunc(("rc = true\n"));
1052 return true;
1053}
1054
1055/**
1056 * Satisfy a request from the guest to convert the clipboard text to Utf8.
1057 *
1058 * @returns true if we successfully convert the data to the format requested, false otherwise.
1059 *
1060 * @param atomTypeReturn The type of the data we are returning
1061 * @param pValReturn A pointer to the data we are returning. This should be to memory
1062 * allocated by XtMalloc, which will be freed by the toolkit later
1063 * @param pcLenReturn The length of the data we are returning
1064 * @param piFormatReturn The format (8bit, 16bit, 32bit) of the data we are returning
1065 */
1066static Boolean vboxClipboardConvertUtf8(Atom *atomTypeReturn, XtPointer *pValReturn,
1067 unsigned long *pcLenReturn, int *piFormatReturn)
1068{
1069 PRTUTF16 pu16GuestText, pu16HostText;
1070 char *pcGuestText;
1071 unsigned cbHostText, cwHostText, cwGuestText, cbGuestText;
1072 int rc;
1073
1074 LogFlowFunc(("\n"));
1075 /* Get the host Utf16 data and convert it to Linux Utf16. */
1076 rc = vboxClipboardReadHostData(VBOX_SHARED_CLIPBOARD_FMT_UNICODETEXT,
1077 reinterpret_cast<void **>(&pu16HostText), &cbHostText);
1078 if ((rc != VINF_SUCCESS) || cbHostText == 0)
1079 {
1080 Log (("vboxClipboardConvertUtf16: vboxClipboardReadHostData returned %Vrc, %d bytes of data\n", rc, cbGuestText));
1081 g_ctx.hostFormats = 0;
1082 LogFlowFunc(("rc = false\n"));
1083 return false;
1084 }
1085 cwHostText = cbHostText / 2;
1086 Log2 (("vboxClipboardConvertUtf8: original text is %.*ls\n", cwHostText, pu16HostText));
1087 cwGuestText = vboxClipboardUtf16GetLinSize(pu16HostText, cwHostText);
1088 pu16GuestText = reinterpret_cast<PRTUTF16>(RTMemAlloc(cwGuestText * 2));
1089 if (pu16GuestText == 0)
1090 {
1091 RTMemFree(reinterpret_cast<char *>(pu16HostText));
1092 LogFlowFunc(("rc = false\n"));
1093 return false;
1094 }
1095 rc = vboxClipboardUtf16WinToLin(pu16HostText, cwHostText, pu16GuestText, cwGuestText);
1096 RTMemFree(reinterpret_cast<char *>(pu16HostText));
1097 if (rc != VINF_SUCCESS)
1098 {
1099 Log2(("vboxClipboardConvertUtf16: vboxClipboardUtf16WinToLin returned %Vrc\n", rc));
1100 RTMemFree(reinterpret_cast<char *>(pu16GuestText));
1101 LogFlowFunc(("rc = false\n"));
1102 return false;
1103 }
1104 /* Now convert the Utf16 Linux text to Utf8 */
1105 cbGuestText = cwGuestText * 3; /* Should always be enough. */
1106 pcGuestText = XtMalloc(cbGuestText);
1107 if (pcGuestText == 0)
1108 {
1109 RTMemFree(reinterpret_cast<char *>(pu16GuestText));
1110 LogFlowFunc(("rc = false\n"));
1111 return false;
1112 }
1113 /* Our runtime can't cope with endian markers. */
1114 rc = RTUtf16ToUtf8Ex(pu16GuestText + 1, cwGuestText - 1, &pcGuestText, cbGuestText, 0);
1115 RTMemFree(reinterpret_cast<char *>(pu16GuestText));
1116 if (rc != VINF_SUCCESS)
1117 {
1118 XtFree(pcGuestText);
1119 LogFlowFunc(("rc = false\n"));
1120 return false;
1121 }
1122 Log2 (("vboxClipboardConvertUtf8: converted text is %.*s\n", cbGuestText, pcGuestText));
1123 *atomTypeReturn = g_ctx.atomUtf8;
1124 *pValReturn = reinterpret_cast<XtPointer>(pcGuestText);
1125 *pcLenReturn = cbGuestText;
1126 *piFormatReturn = 8;
1127 LogFlowFunc(("rc = true\n"));
1128 return true;
1129}
1130
1131/**
1132 * Satisfy a request from the guest to convert the clipboard text to compound text.
1133 *
1134 * @returns true if we successfully convert the data to the format requested, false otherwise.
1135 *
1136 * @param atomTypeReturn The type of the data we are returning
1137 * @param pValReturn A pointer to the data we are returning. This should be to memory
1138 * allocated by XtMalloc, which will be freed by the toolkit later
1139 * @param pcLenReturn The length of the data we are returning
1140 * @param piFormatReturn The format (8bit, 16bit, 32bit) of the data we are returning
1141 */
1142static Boolean vboxClipboardConvertCText(Atom *atomTypeReturn, XtPointer *pValReturn,
1143 unsigned long *pcLenReturn, int *piFormatReturn)
1144{
1145 PRTUTF16 pu16GuestText, pu16HostText;
1146 char *pcUtf8Text;
1147 unsigned cbHostText, cwHostText, cwGuestText, cbUtf8Text;
1148 XTextProperty property;
1149 int rc;
1150
1151 LogFlowFunc(("\n"));
1152 /* Get the host Utf16 data and convert it to Linux Utf16. */
1153 rc = vboxClipboardReadHostData(VBOX_SHARED_CLIPBOARD_FMT_UNICODETEXT,
1154 reinterpret_cast<void **>(&pu16HostText), &cbHostText);
1155 if ((rc != VINF_SUCCESS) || cbHostText == 0)
1156 {
1157 Log (("vboxClipboardConvertCText: vboxClipboardReadHostData returned %Vrc, %d bytes of data\n", rc, cbHostText));
1158 g_ctx.hostFormats = 0;
1159 LogFlowFunc(("rc = false\n"));
1160 return false;
1161 }
1162 Log2 (("vboxClipboardConvertCText: original text is %.*ls\n", cwHostText, pu16HostText));
1163 cwHostText = cbHostText / 2;
1164 cwGuestText = vboxClipboardUtf16GetLinSize(pu16HostText, cwHostText);
1165 pu16GuestText = reinterpret_cast<PRTUTF16>(RTMemAlloc(cwGuestText * 2));
1166 if (pu16GuestText == 0)
1167 {
1168 Log(("vboxClipboardConvertCText: out of memory\n"));
1169 RTMemFree(reinterpret_cast<char *>(pu16HostText));
1170 LogFlowFunc(("rc = false\n"));
1171 return false;
1172 }
1173 rc = vboxClipboardUtf16WinToLin(pu16HostText, cwHostText, pu16GuestText, cwGuestText);
1174 RTMemFree(reinterpret_cast<char *>(pu16HostText));
1175 if (rc != VINF_SUCCESS)
1176 {
1177 Log2(("vboxClipboardConvertUtf16: vboxClipboardUtf16WinToLin returned %Vrc\n", rc));
1178 RTMemFree(reinterpret_cast<char *>(pu16GuestText));
1179 LogFlowFunc(("rc = false\n"));
1180 return false;
1181 }
1182 /* Now convert the Utf16 Linux text to Utf8 */
1183 cbUtf8Text = cwGuestText * 3; /* Should always be enough. */
1184 pcUtf8Text = reinterpret_cast<char *>(RTMemAlloc(cbUtf8Text));
1185 if (pcUtf8Text == 0)
1186 {
1187 Log(("vboxClipboardConvertCText: out of memory\n"));
1188 RTMemFree(reinterpret_cast<char *>(pu16GuestText));
1189 LogFlowFunc(("rc = false\n"));
1190 return false;
1191 }
1192 /* Our runtime can't cope with endian markers. */
1193 rc = RTUtf16ToUtf8Ex(pu16GuestText + 1, cwGuestText - 1, &pcUtf8Text, cbUtf8Text, 0);
1194 RTMemFree(reinterpret_cast<char *>(pu16GuestText));
1195 if (rc != VINF_SUCCESS)
1196 {
1197 Log(("vboxClipboardConvertCText: RTUtf16ToUtf8Ex failed: rc=%Vrc\n", rc));
1198 XtFree(pcUtf8Text);
1199 LogFlowFunc(("rc = false\n"));
1200 return false;
1201 }
1202 /* And finally (!) convert the Utf8 text to compound text. */
1203 rc = Xutf8TextListToTextProperty(XtDisplay(g_ctx.widget), &pcUtf8Text, 1,
1204 XCompoundTextStyle, &property);
1205 RTMemFree(pcUtf8Text);
1206 if (rc < 0)
1207 {
1208 char *pcReason;
1209 switch(rc)
1210 {
1211 case XNoMemory:
1212 pcReason = "out of memory";
1213 break;
1214 case XLocaleNotSupported:
1215 pcReason = "locale (Utf8) not supported";
1216 break;
1217 case XConverterNotFound:
1218 pcReason = "converter not found";
1219 break;
1220 default:
1221 pcReason = "unknown error";
1222 }
1223 LogRel(("vboxClipboardConvertCText: Xutf8TextListToTextProperty failed. Reason: %s\n",
1224 pcReason));
1225 XFree(property.value);
1226 LogFlowFunc(("rc = false\n"));
1227 return false;
1228 }
1229 Log2 (("vboxClipboardConvertCText: converted text is %s\n", property.value));
1230 *atomTypeReturn = property.encoding;
1231 *pValReturn = reinterpret_cast<XtPointer>(property.value);
1232 *pcLenReturn = property.nitems;
1233 *piFormatReturn = property.format;
1234 LogFlowFunc(("rc = true\n"));
1235 return true;
1236}
1237
1238/**
1239 * Satisfy a request from the guest to convert the clipboard text to Latin1.
1240 *
1241 * @returns true if we successfully convert the data to the format requested, false otherwise.
1242 *
1243 * @param atomTypeReturn The type of the data we are returning
1244 * @param pValReturn A pointer to the data we are returning. This should be to memory
1245 * allocated by XtMalloc, which will be freed by the toolkit later
1246 * @param pcLenReturn The length of the data we are returning
1247 * @param piFormatReturn The format (8bit, 16bit, 32bit) of the data we are returning
1248 */
1249static Boolean vboxClipboardConvertLatin1(Atom *atomTypeReturn, XtPointer *pValReturn,
1250 unsigned long *pcLenReturn, int *piFormatReturn)
1251{
1252 enum { LINEFEED = 0xa, CARRIAGERETURN = 0xd };
1253 PRTUTF16 pu16HostText;
1254 unsigned cbHostText, cwHostText, cbGuestPos = 0, cbGuestText;
1255 unsigned char *pcGuestText;
1256 int rc;
1257
1258 LogFlowFunc(("\n"));
1259 /* Get the host UTF16 data */
1260 rc = vboxClipboardReadHostData(VBOX_SHARED_CLIPBOARD_FMT_UNICODETEXT,
1261 reinterpret_cast<void **>(&pu16HostText), &cbHostText);
1262 if ((rc != VINF_SUCCESS) || cbHostText == 0)
1263 {
1264 Log (("vboxClipboardConvertUtf16: vboxClipboardReadHostData returned %Vrc, %d bytes of data\n", rc, cbGuestText));
1265 g_ctx.hostFormats = 0;
1266 LogFlowFunc(("rc = false\n"));
1267 return false;
1268 }
1269 cwHostText = cbHostText / 2;
1270 cbGuestText = cwHostText;
1271 pcGuestText = reinterpret_cast<unsigned char *>(XtMalloc(cbGuestText));
1272 if (pcGuestText == 0)
1273 {
1274 RTMemFree(reinterpret_cast<void *>(pu16HostText));
1275 LogFlowFunc(("rc = false\n"));
1276 return false;
1277 }
1278 for (unsigned i = 0; i < cwHostText; ++i, ++cbGuestPos)
1279 {
1280 if ( (i + 1 < cwHostText)
1281 && (pu16HostText[i] == CARRIAGERETURN)
1282 && (pu16HostText[i + 1] == LINEFEED))
1283 ++i;
1284 if (pu16HostText[i] < 256)
1285 pcGuestText[cbGuestPos] = pu16HostText[i];
1286 else
1287 /* Any better ideas as to how to do this? */
1288 pcGuestText[cbGuestPos] = '.';
1289 }
1290 Log (("vboxClipboardConvertLatin1: returning Latin-1, original text is %.*ls\n", cwHostText,
1291 pu16HostText));
1292 Log (("vboxClipboardConvertLatin1: converted text is %.*s\n", cbGuestPos,
1293 pcGuestText));
1294 RTMemFree(reinterpret_cast<void *>(pu16HostText));
1295 *atomTypeReturn = XA_STRING;
1296 *pValReturn = reinterpret_cast<XtPointer>(pcGuestText);
1297 *pcLenReturn = cbGuestPos;
1298 *piFormatReturn = 8;
1299 LogFlowFunc(("rc = true\n"));
1300 return true;
1301}
1302
1303/**
1304 * Callback to convert the hosts clipboard data for an application on the guest. Called by the
1305 * X Toolkit.
1306 * @returns true if we successfully convert the data to the format requested, false otherwise.
1307 *
1308 * @param atomSelection The selection which is being requested. We only handle the clipboard.
1309 * @param atomTarget The format we should convert the data to
1310 * @param atomTypeReturn The type of the data we are returning
1311 * @param pValReturn A pointer to the data we are returning. This should be to memory
1312 * allocated by XtMalloc, which will be freed by the toolkit later
1313 * @param pcLenReturn The length of the data we are returning
1314 * @param piFormatReturn The format (8bit, 16bit, 32bit) of the data we are returning
1315 */
1316static Boolean vboxClipboardConvertProc(Widget, Atom *atomSelection, Atom *atomTarget,
1317 Atom *atomTypeReturn, XtPointer *pValReturn,
1318 unsigned long *pcLenReturn, int *piFormatReturn)
1319{
1320 g_eClipboardFormats eFormat = INVALID;
1321 int rc;
1322
1323 LogFlowFunc(("\n"));
1324 if (*atomSelection != g_ctx.atomClipboard)
1325 {
1326 LogFlowFunc(("rc = false\n"));
1327 return false;
1328 }
1329#ifdef DEBUG
1330 char *szAtomName = XGetAtomName(XtDisplay(g_ctx.widget), *atomTarget);
1331 if (szAtomName != 0)
1332 {
1333 Log2 (("vboxClipboardConvertProc: request for format %s\n", szAtomName));
1334 XFree(szAtomName);
1335 }
1336 else
1337 {
1338 Log(("vboxClipboardConvertProc: request for invalid target atom %d!\n", *atomTarget));
1339 }
1340#endif
1341 if (*atomTarget == g_ctx.atomTargets)
1342 {
1343 eFormat = TARGETS;
1344 }
1345 else
1346 {
1347 for (unsigned i = 0; i != g_ctx.formatList.size(); ++i)
1348 {
1349 if (g_ctx.formatList[i].atom == *atomTarget)
1350 {
1351 eFormat = g_ctx.formatList[i].format;
1352 break;
1353 }
1354 }
1355 }
1356 switch (eFormat)
1357 {
1358 case TARGETS:
1359 rc = vboxClipboardConvertTargets(atomTypeReturn, pValReturn, pcLenReturn, piFormatReturn);
1360 LogFlowFunc(("rc=%d\n", rc));
1361 return rc;
1362 case UTF16:
1363 rc = vboxClipboardConvertUtf16(atomTypeReturn, pValReturn, pcLenReturn, piFormatReturn);
1364 LogFlowFunc(("rc=%d\n", rc));
1365 return rc;
1366 case UTF8:
1367 rc = vboxClipboardConvertUtf8(atomTypeReturn, pValReturn, pcLenReturn, piFormatReturn);
1368 LogFlowFunc(("rc=%d\n", rc));
1369 return rc;
1370 case CTEXT:
1371 rc = vboxClipboardConvertCText(atomTypeReturn, pValReturn, pcLenReturn, piFormatReturn);
1372 LogFlowFunc(("rc=%d\n", rc));
1373 return rc;
1374 case LATIN1:
1375 rc = vboxClipboardConvertLatin1(atomTypeReturn, pValReturn, pcLenReturn, piFormatReturn);
1376 LogFlowFunc(("rc=%d\n", rc));
1377 return rc;
1378 default:
1379 Log(("vboxClipboardConvertProc: bad format\n"));
1380 LogFlowFunc(("rc = false\n"));
1381 return false;
1382 }
1383}
1384
1385static void vboxClipboardLoseProc(Widget, Atom *)
1386{
1387 LogFlowFunc(("giving the guest clipboard ownership\n"));
1388 g_ctx.eOwner = GUEST;
1389 g_ctx.notifyHost = true;
1390 LogFlowFunc(("returning\n"));
1391}
1392
1393/**
1394 * The host is taking possession of the shared clipboard. Called by the HGCM clipboard
1395 * subsystem.
1396 *
1397 * @param u32Formats Clipboard formats the the guest is offering
1398 */
1399void vboxClipboardFormatAnnounce (uint32_t u32Formats)
1400{
1401 g_ctx.hostFormats = u32Formats;
1402 LogFlowFunc(("u32Formats = %d\n", u32Formats));
1403 if (u32Formats == 0)
1404 {
1405 /* This is just an automatism, not a genuine anouncement */
1406 LogFlowFunc(("returning\n"));
1407 return;
1408 }
1409 Log2 (("vboxClipboardFormatAnnounce: giving the host clipboard ownership\n"));
1410 g_ctx.eOwner = HOST;
1411 g_ctx.guestTextFormat = INVALID;
1412 g_ctx.guestBitmapFormat = INVALID;
1413 if (XtOwnSelection(g_ctx.widget, g_ctx.atomClipboard, CurrentTime, vboxClipboardConvertProc,
1414 vboxClipboardLoseProc, 0) != True)
1415 {
1416 Log2 (("vboxClipboardFormatAnnounce: returning clipboard ownership to the guest\n"));
1417 g_ctx.notifyHost = true;
1418 g_ctx.eOwner = GUEST;
1419 }
1420 LogFlowFunc(("returning\n"));
1421}
1422
1423/**
1424 * Called when the host wants to read the guest clipboard.
1425 *
1426 * @param u32Format The format that the host would like to receive the data in
1427 */
1428int vboxClipboardReadGuestData (uint32_t u32Format)
1429{
1430 LogFlowFunc(("u32Format = %d\n", u32Format));
1431
1432 /*
1433 * The guest wants to read data in the given format.
1434 */
1435 if (u32Format & VBOX_SHARED_CLIPBOARD_FMT_UNICODETEXT)
1436 {
1437 if (g_ctx.guestTextFormat == INVALID)
1438 {
1439 /* No data available. */
1440 vboxClipboardSendData (0, NULL, 0);
1441 LogFlowFunc(("sent empty data, returned VINF_SUCCESS\n"));
1442 return VINF_SUCCESS;
1443 }
1444 g_ctx.requestGuestFormat = g_ctx.guestTextFormat;
1445 /* Send out a request for the data to the current clipboard owner */
1446 XtGetSelectionValue(g_ctx.widget, g_ctx.atomClipboard, g_ctx.atomGuestTextFormat,
1447 vboxClipboardGetProc, 0, CurrentTime);
1448 /* When the data arrives, the vboxClipboardGetProc callback will be called. The
1449 callback will signal the event semaphore when it has processed the data for us. */
1450 }
1451 else
1452 {
1453 vboxClipboardSendData (0, NULL, 0);
1454 LogFlowFunc(("sent empty data, returned VERR_NOT_IMPLEMENTED\n"));
1455 return VERR_NOT_IMPLEMENTED;
1456 }
1457 LogFlowFunc(("returned VINF_SUCCESS\n"));
1458 return VINF_SUCCESS;
1459}
1460
1461/** Terminate the guest side of the shared clipboard. */
1462void vboxClipboardDestroy (void)
1463{
1464 LogFlowFunc(("\n"));
1465
1466 /* Set the termination flag. */
1467 XtAppSetExitFlag(g_ctx.appContext);
1468 LogFlowFunc(("returning\n"));
1469}
1470
1471/**
1472 * The main loop of our clipboard reader.
1473 */
1474static int vboxClipboardThread(RTTHREAD /* ThreadSelf */, void * /* pvUser */)
1475{
1476 int rc = VINF_SUCCESS;
1477 LogFlowFunc(("Starting clipboard thread\n"));
1478
1479 for (;;)
1480 {
1481 VBoxClipboardGetHostMsg parms;
1482 VBOX_INIT_CALL(&parms, VBOX_SHARED_CLIPBOARD_FN_GET_HOST_MSG, g_ctx.client);
1483
1484 VBoxHGCMParmUInt32Set (&parms.msg, 0);
1485 VBoxHGCMParmUInt32Set (&parms.formats, 0);
1486 if (ioctl(g_ctx.receiveDevice, IOCTL_VBOXGUEST_HGCM_CALL, (void*)&parms) < 0)
1487 {
1488 Log(("Failed to call the driver for host message.\n"));
1489
1490 /* Wait a bit before retrying. */
1491 if (RTSemEventWait(g_ctx.terminating, 1000) == VINF_SUCCESS)
1492 break;
1493 continue;
1494 }
1495 rc = parms.hdr.result;
1496 if (VBOX_SUCCESS (rc))
1497 {
1498 uint32_t u32Msg;
1499 uint32_t u32Formats;
1500
1501 rc = VBoxHGCMParmUInt32Get (&parms.msg, &u32Msg);
1502
1503 if (VBOX_SUCCESS (rc))
1504 {
1505 rc = VBoxHGCMParmUInt32Get (&parms.formats, &u32Formats);
1506
1507 if (VBOX_SUCCESS (rc))
1508 {
1509 Log(("vboxClipboardHostEvent u32Msg %d, u32Formats %d\n", u32Msg, u32Formats));
1510
1511 switch (u32Msg)
1512 {
1513 case VBOX_SHARED_CLIPBOARD_HOST_MSG_FORMATS:
1514 {
1515 /* The host has announced available clipboard formats.
1516 * Save the information so that it is available for
1517 * future requests from guest applications.
1518 */
1519 vboxClipboardFormatAnnounce(u32Formats);
1520 } break;
1521 case VBOX_SHARED_CLIPBOARD_HOST_MSG_READ_DATA:
1522 {
1523 /* The host needs data in the specified format.
1524 */
1525 vboxClipboardReadGuestData(u32Formats);
1526 } break;
1527 case VBOX_SHARED_CLIPBOARD_HOST_MSG_QUIT:
1528 {
1529 /* The host is terminating.
1530 */
1531 vboxClipboardDestroy();
1532 rc = VERR_INTERRUPTED;
1533 } break;
1534 default:
1535 {
1536 Log(("Unsupported message from host!!!\n"));
1537 }
1538 }
1539 }
1540 }
1541 }
1542 if (rc == VERR_INTERRUPTED)
1543 {
1544 /* Wait for termination event. */
1545 RTSemEventWait(g_ctx.terminating, RT_INDEFINITE_WAIT);
1546 break;
1547 }
1548 if (VBOX_FAILURE (rc))
1549 {
1550 /* Wait a bit before retrying. */
1551 if (RTSemEventWait(g_ctx.terminating, 1000) == VINF_SUCCESS)
1552 break;
1553 continue;
1554 }
1555
1556 Log(("processed host event rc = %d\n", rc));
1557 }
1558 LogFlowFunc(("rc=%d\n", rc));
1559 return rc;
1560}
1561
1562/**
1563 * Disconnect the guest clipboard from the host.
1564 */
1565void vboxClipboardDisconnect (void)
1566{
1567#if 0
1568 VMMDevHGCMDisconnect request;
1569#endif
1570 LogFlowFunc(("\n"));
1571
1572 AssertReturn(g_ctx.client != 0, (void) 0);
1573#if 0
1574 /* Currently, disconnecting is not needed, as the new "connect clipboard"
1575 ioctl in the Guest Additions kernel module disconnects the last
1576 connection made automatically. The reason for this change was that
1577 currently only one clipboard connection is allowed, and that if the
1578 client holding that connection was terminated too abruptly, the
1579 information needed to disconnect that connection was lost. If the
1580 subsystem is ever changed to allow several connections, this will have
1581 to be rethought. */
1582 vmmdevInitRequest((VMMDevRequestHeader*)&request, VMMDevReq_HGCMDisconnect);
1583 request.u32ClientID = g_ctx.client;
1584 ioctl(g_ctx.sendDevice, IOCTL_VBOXGUEST_VMMREQUEST, (void*)&request);
1585#endif
1586 LogFlowFunc(("returning\n"));
1587}
1588
1589int vboxClipboardXLibErrorHandler(Display *pDisplay, XErrorEvent *pError)
1590{
1591 char errorText[1024];
1592
1593 LogFlowFunc(("\n"));
1594 if (pError->error_code == BadAtom)
1595 {
1596 /* This can be triggered in debug builds if a guest application passes a bad atom
1597 in its list of supported clipboard formats. As such it is harmless. */
1598 LogFlowFunc(("ignoring BadAtom error and returning\n"));
1599 return 0;
1600 }
1601 vboxClipboardDisconnect();
1602 XGetErrorText(pDisplay, pError->error_code, errorText, sizeof(errorText));
1603 if (g_ctx.daemonise == 0)
1604 {
1605 cout << "An X Window protocol error occurred: " << errorText << endl
1606 << " Request code: " << int(pError->request_code) << endl
1607 << " Minor code: " << int(pError->minor_code) << endl
1608 << " Serial number of the failed request: " << int(pError->serial) << endl;
1609 }
1610 Log(("%s: an X Window protocol error occurred: %s. Request code: %d, minor code: %d, serial number: %d\n",
1611 __PRETTY_FUNCTION__, pError->error_code, pError->request_code, pError->minor_code,
1612 pError->serial));
1613 LogFlowFunc(("exiting\n"));
1614 exit(1);
1615}
1616
1617
1618/**
1619 * Connect the guest clipboard to the host.
1620 *
1621 * @returns RT status code
1622 */
1623int vboxClipboardConnect (void)
1624{
1625 LogFlowFunc(("\n"));
1626 int rc;
1627 /* Only one client is supported for now */
1628 AssertReturn(g_ctx.client == 0, VERR_NOT_SUPPORTED);
1629
1630 rc = ioctl(g_ctx.sendDevice, IOCTL_VBOXGUEST_CLIPBOARD_CONNECT, (void*)&g_ctx.client);
1631 if (rc >= 0)
1632 {
1633 if (g_ctx.client == 0)
1634 {
1635 cout << "We got an invalid client ID of 0!" << endl;
1636 return VERR_NOT_SUPPORTED;
1637 }
1638 g_ctx.eOwner = HOST;
1639 }
1640 else
1641 {
1642 Log(("Error connecting to host. rc = %d (%s)\n", rc, strerror(-rc)));
1643 cout << "Unable to connect to the host system." << endl;
1644 LogFlowFunc(("returned VERR_NOT_SUPPORTED\n"));
1645 return VERR_NOT_SUPPORTED;
1646 }
1647 /* Set an X11 error handler, so that we don't die when we get BadAtom errors. */
1648 XSetErrorHandler(vboxClipboardXLibErrorHandler);
1649 LogFlowFunc(("returned VINF_SUCCESS\n"));
1650 return VINF_SUCCESS;
1651}
1652
1653/** We store information about the target formats we can handle in a global vector for internal
1654 use. */
1655static void vboxClipboardAddFormat(const char *pszName, g_eClipboardFormats eFormat,
1656 unsigned hostFormat)
1657{
1658 VBOXCLIPBOARDFORMAT sFormat;
1659 LogFlowFunc(("pszName=%s, eFormat=%d, hostFormat=%u\n", pszName, eFormat, hostFormat));
1660 /* Get an atom from the X server for that target format */
1661 Atom atomFormat = XInternAtom(XtDisplay(g_ctx.widget), pszName, false);
1662 Log2(("vboxClipboardAddFormat: atomFormat=%d\n", atomFormat));
1663 if (atomFormat == 0)
1664 {
1665 LogFlowFunc(("atomFormat=0! returning...\n"));
1666 return;
1667 }
1668 sFormat.atom = atomFormat;
1669 sFormat.format = eFormat;
1670 sFormat.hostFormat = hostFormat;
1671 g_ctx.formatList.push_back(sFormat);
1672 LogFlowFunc (("returning\n"));
1673}
1674
1675/** Create the X11 window which we use to interact with the guest clipboard */
1676static int vboxClipboardCreateWindow(void)
1677{
1678 /* Create a window and make it a clipboard viewer. */
1679 int cArgc = 0;
1680 char *pcArgv = 0;
1681 String szFallbackResources[] = { "*.width: 1", "*.height: 1", 0 };
1682
1683 /* Set up the Clipbard application context and main window. */
1684 g_ctx.widget = XtOpenApplication(&g_ctx.appContext, "VBoxClipboard", 0, 0, &cArgc, &pcArgv,
1685 szFallbackResources, applicationShellWidgetClass, 0, 0);
1686 AssertMsgReturn(g_ctx.widget != 0, ("Failed to construct the X clipboard window\n"),
1687 VERR_ACCESS_DENIED);
1688 XtSetMappedWhenManaged(g_ctx.widget, false);
1689 XtRealizeWidget(g_ctx.widget);
1690
1691 /* Get hold of the atoms which we need */
1692 g_ctx.atomClipboard = XInternAtom(XtDisplay(g_ctx.widget), "CLIPBOARD", false /* only_if_exists */);
1693 g_ctx.atomTargets = XInternAtom(XtDisplay(g_ctx.widget), "TARGETS", false);
1694 g_ctx.atomMultiple = XInternAtom(XtDisplay(g_ctx.widget), "MULTIPLE", false);
1695 g_ctx.atomTimestamp = XInternAtom(XtDisplay(g_ctx.widget), "TIMESTAMP", false);
1696 g_ctx.atomUtf16 = XInternAtom(XtDisplay(g_ctx.widget),
1697 "text/plain;charset=ISO-10646-UCS-2", false);
1698 g_ctx.atomUtf8 = XInternAtom(XtDisplay(g_ctx.widget), "UTF_STRING", false);
1699 g_ctx.atomCText = XInternAtom(XtDisplay(g_ctx.widget), "COMPOUND_TEXT", false);
1700 /* And build up the vector of supported formats */
1701#ifdef USE_UTF16
1702 vboxClipboardAddFormat("text/plain;charset=ISO-10646-UCS-2", UTF16,
1703 VBOX_SHARED_CLIPBOARD_FMT_UNICODETEXT);
1704#endif
1705#ifdef USE_UTF8
1706 vboxClipboardAddFormat("UTF8_STRING", UTF8,
1707 VBOX_SHARED_CLIPBOARD_FMT_UNICODETEXT);
1708 vboxClipboardAddFormat("text/plain;charset=UTF-8", UTF8,
1709 VBOX_SHARED_CLIPBOARD_FMT_UNICODETEXT);
1710 vboxClipboardAddFormat("text/plain;charset=utf-8", UTF8,
1711 VBOX_SHARED_CLIPBOARD_FMT_UNICODETEXT);
1712#endif
1713#ifdef USE_CTEXT
1714 vboxClipboardAddFormat("COMPOUND_TEXT", CTEXT,
1715 VBOX_SHARED_CLIPBOARD_FMT_UNICODETEXT);
1716#endif
1717#ifdef USE_LATIN1
1718 vboxClipboardAddFormat("STRING", LATIN1,
1719 VBOX_SHARED_CLIPBOARD_FMT_UNICODETEXT);
1720 vboxClipboardAddFormat("TEXT", LATIN1,
1721 VBOX_SHARED_CLIPBOARD_FMT_UNICODETEXT);
1722 vboxClipboardAddFormat("text/plain", LATIN1,
1723 VBOX_SHARED_CLIPBOARD_FMT_UNICODETEXT);
1724#endif
1725 return VINF_SUCCESS;
1726}
1727
1728/** Initialise the guest side of the shared clipboard. */
1729int vboxClipboardMain (void)
1730{
1731 int rc;
1732 LogFlowFunc(("\n"));
1733
1734 rc = vboxClipboardCreateWindow();
1735 if (VBOX_FAILURE(rc))
1736 {
1737 return rc;
1738 }
1739
1740 rc = RTThreadCreate(&g_ctx.thread, vboxClipboardThread, 0, 0, RTTHREADTYPE_IO,
1741 RTTHREADFLAGS_WAITABLE, "SHCLIP");
1742 AssertRCReturn(rc, rc);
1743 /* Set up a timer to poll the host clipboard */
1744 XtAppAddTimeOut(g_ctx.appContext, 200 /* ms */, vboxClipboardTimerProc, 0);
1745
1746 XtAppMainLoop(g_ctx.appContext);
1747 g_ctx.formatList.clear();
1748 XtDestroyApplicationContext(g_ctx.appContext);
1749 /* Set the termination signal. */
1750 RTSemEventSignal(g_ctx.terminating);
1751 LogFlowFunc(("returning %d\n", rc));
1752 return rc;
1753}
1754
1755
1756/**
1757 * Become a daemon process
1758 */
1759void vboxDaemonise(void)
1760{
1761 /* First fork and exit the parent process, so that we are sure we are not session leader. */
1762 if (fork() != 0)
1763 {
1764 exit(0);
1765 }
1766 /* Detach from the controlling terminal by creating our own session. */
1767 setsid();
1768 /* And change to the root directory to avoid holding the one we were started in open. */
1769 chdir("/");
1770 /* Close the standard files. */
1771 close(0);
1772 close(1);
1773 close(2);
1774}
1775
1776int main(int argc, char *argv[])
1777{
1778 int rc;
1779
1780 /* Parse our option(s) */
1781 g_ctx.daemonise = 1;
1782 while (1)
1783 {
1784 static struct option sOpts[] =
1785 {
1786 {"nodaemon", 0, 0, 'd'},
1787 {0, 0, 0, 0}
1788 };
1789 int cOpt = getopt_long(argc, argv, "", sOpts, 0);
1790 if (cOpt == -1)
1791 {
1792 if (optind < argc)
1793 {
1794 cout << "Unrecognized command line argument: " << argv[argc] << endl;
1795 exit(1);
1796 }
1797 break;
1798 }
1799 switch(cOpt)
1800 {
1801 case 'd':
1802 g_ctx.daemonise = 0;
1803 break;
1804 default:
1805 cout << "Unrecognized command line option: " << static_cast<char>(cOpt) << endl;
1806 case '?':
1807 exit(1);
1808 }
1809 }
1810 /* Initialise our runtime before all else. */
1811 RTR3Init(false);
1812 LogFlowFunc(("\n"));
1813 /* Initialise threading in Xt before we start any new threads. */
1814 XtToolkitThreadInitialize();
1815 /* Initialise the termination semaphore. */
1816 RTSemEventCreate(&g_ctx.terminating);
1817 /* Open a connection to the driver for sending requests. */
1818 g_ctx.sendDevice = open(VBOXGUEST_DEVICE_NAME, O_RDWR, 0);
1819 if (g_ctx.sendDevice < 0)
1820 {
1821 Log(("Error opening kernel module! errno = %d\n", errno));
1822 cout << "Failed to open the VirtualBox device in the guest." << endl;
1823 LogFlowFunc(("returning 1\n"));
1824 return 1;
1825 }
1826 /* Open a connection to the driver for polling for host requests. */
1827 g_ctx.receiveDevice = open(VBOXGUEST_DEVICE_NAME, O_RDWR, 0);
1828 if (g_ctx.receiveDevice < 0)
1829 {
1830 Log(("Error opening kernel module! rc = %d\n", errno));
1831 cout << "Failed to open the VirtualBox device in the guest" << endl;
1832 LogFlowFunc(("returning 1\n"));
1833 return 1;
1834 }
1835 /* Connect to the host clipboard. */
1836 rc = vboxClipboardConnect();
1837 if (rc != VINF_SUCCESS)
1838 {
1839 Log(("vboxClipboardConnect failed with rc = %d\n", rc));
1840 cout << "Failed to connect to the host clipboard." << endl;
1841 LogFlowFunc(("returning 1\n"));
1842 return 1;
1843 }
1844 if (g_ctx.daemonise == 1)
1845 {
1846 vboxDaemonise();
1847 }
1848 vboxClipboardMain();
1849 vboxClipboardDisconnect();
1850 LogFlowFunc(("returning 0\n"));
1851 return 0;
1852}
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