VirtualBox

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

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

Test workaround for badly behaving applications for the Linux clipboard

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

© 2025 Oracle Support Privacy / Do Not Sell My Info Terms of Use Trademark Policy Automated Access Etiquette