VirtualBox

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

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

Updated the Linux shared clipboard code to provide absolutely minimal COMPOUND_TEXT support

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