VirtualBox

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

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

export sharedfolders and xclient to OSE

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