VirtualBox

source: vbox/trunk/src/VBox/Additions/x11/xclient/clipboard.cpp@ 15776

Last change on this file since 15776 was 14297, checked in by vboxsync, 16 years ago

Corrected a couple of grammos.

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