VirtualBox

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

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

typos

  • Property svn:eol-style set to native
  • Property svn:keywords set to Id
File size: 53.4 KB
Line 
1/** $Id: clipboard.cpp 11648 2008-08-26 09:49:28Z 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=%Vrc\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=%Vrc\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 %Vrc, %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 %Vrc\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, cbGuestText;
936 int rc;
937
938 LogFlowFunc(("\n"));
939 /* Get the host Utf16 data and convert it to Linux Utf16. */
940 rc = vboxClipboardReadHostData(VBOX_SHARED_CLIPBOARD_FMT_UNICODETEXT,
941 reinterpret_cast<void **>(&pu16HostText), &cbHostText);
942 if ((rc != VINF_SUCCESS) || cbHostText == 0)
943 {
944 LogFlow(("vboxClipboardConvertUtf16: vboxClipboardReadHostData returned %Vrc, %d bytes of data\n", rc, cbGuestText));
945 g_ctx.hostFormats = 0;
946 LogFlowFunc(("rc = false\n"));
947 return false;
948 }
949 cwHostText = cbHostText / 2;
950 LogFlow(("vboxClipboardConvertUtf8: original text is %.*ls\n", cwHostText, pu16HostText));
951 cwGuestText = vboxClipboardUtf16GetLinSize(pu16HostText, cwHostText);
952 pu16GuestText = reinterpret_cast<PRTUTF16>(RTMemAlloc(cwGuestText * 2));
953 if (pu16GuestText == 0)
954 {
955 RTMemFree(reinterpret_cast<char *>(pu16HostText));
956 LogFlowFunc(("rc = false\n"));
957 return false;
958 }
959 rc = vboxClipboardUtf16WinToLin(pu16HostText, cwHostText, pu16GuestText, cwGuestText);
960 RTMemFree(reinterpret_cast<char *>(pu16HostText));
961 if (rc != VINF_SUCCESS)
962 {
963 LogFlow(("vboxClipboardConvertUtf16: vboxClipboardUtf16WinToLin returned %Vrc\n", rc));
964 RTMemFree(reinterpret_cast<char *>(pu16GuestText));
965 LogFlowFunc(("rc = false\n"));
966 return false;
967 }
968 /* Now convert the Utf16 Linux text to Utf8 */
969 cbGuestText = cwGuestText * 3; /* Should always be enough. */
970 pcGuestText = XtMalloc(cbGuestText);
971 if (pcGuestText == 0)
972 {
973 RTMemFree(reinterpret_cast<char *>(pu16GuestText));
974 LogFlowFunc(("rc = false\n"));
975 return false;
976 }
977 /* Our runtime can't cope with endian markers. */
978 rc = RTUtf16ToUtf8Ex(pu16GuestText + 1, cwGuestText - 1, &pcGuestText, cbGuestText, 0);
979 RTMemFree(reinterpret_cast<char *>(pu16GuestText));
980 if (rc != VINF_SUCCESS)
981 {
982 XtFree(pcGuestText);
983 LogFlowFunc(("rc = false\n"));
984 return false;
985 }
986 LogFlow(("vboxClipboardConvertUtf8: converted text is %.*s\n", cbGuestText, pcGuestText));
987 *atomTypeReturn = g_ctx.atomUtf8;
988 *pValReturn = reinterpret_cast<XtPointer>(pcGuestText);
989 *pcLenReturn = cbGuestText;
990 *piFormatReturn = 8;
991 LogFlowFunc(("rc = true\n"));
992 return true;
993}
994
995
996/**
997 * Satisfy a request from the guest to convert the clipboard text to compound text.
998 *
999 * @returns true if we successfully convert the data to the format requested, false otherwise.
1000 *
1001 * @param atomTypeReturn The type of the data we are returning
1002 * @param pValReturn A pointer to the data we are returning. This should be to memory
1003 * allocated by XtMalloc, which will be freed by the toolkit later
1004 * @param pcLenReturn The length of the data we are returning
1005 * @param piFormatReturn The format (8bit, 16bit, 32bit) of the data we are returning
1006 */
1007static Boolean vboxClipboardConvertCText(Atom *atomTypeReturn, XtPointer *pValReturn,
1008 unsigned long *pcLenReturn, int *piFormatReturn)
1009{
1010 PRTUTF16 pu16GuestText, pu16HostText;
1011 char *pcUtf8Text;
1012 unsigned cbHostText, cwHostText, cwGuestText, cbUtf8Text;
1013 XTextProperty property;
1014 int rc;
1015
1016 LogFlowFunc(("\n"));
1017 /* Get the host Utf16 data and convert it to Linux Utf16. */
1018 rc = vboxClipboardReadHostData(VBOX_SHARED_CLIPBOARD_FMT_UNICODETEXT,
1019 reinterpret_cast<void **>(&pu16HostText), &cbHostText);
1020 if ((rc != VINF_SUCCESS) || cbHostText == 0)
1021 {
1022 LogFlow(("vboxClipboardConvertCText: vboxClipboardReadHostData returned %Vrc, %d bytes of data\n", rc, cbHostText));
1023 g_ctx.hostFormats = 0;
1024 LogFlowFunc(("rc = false\n"));
1025 return false;
1026 }
1027 LogFlow(("vboxClipboardConvertCText: original text is %.*ls\n", cwHostText, pu16HostText));
1028 cwHostText = cbHostText / 2;
1029 cwGuestText = vboxClipboardUtf16GetLinSize(pu16HostText, cwHostText);
1030 pu16GuestText = reinterpret_cast<PRTUTF16>(RTMemAlloc(cwGuestText * 2));
1031 if (pu16GuestText == 0)
1032 {
1033 Log(("vboxClipboardConvertCText: out of memory\n"));
1034 RTMemFree(reinterpret_cast<char *>(pu16HostText));
1035 LogFlowFunc(("rc = false\n"));
1036 return false;
1037 }
1038 rc = vboxClipboardUtf16WinToLin(pu16HostText, cwHostText, pu16GuestText, cwGuestText);
1039 RTMemFree(reinterpret_cast<char *>(pu16HostText));
1040 if (rc != VINF_SUCCESS)
1041 {
1042 LogFlow(("vboxClipboardConvertUtf16: vboxClipboardUtf16WinToLin returned %Vrc\n", rc));
1043 RTMemFree(reinterpret_cast<char *>(pu16GuestText));
1044 LogFlowFunc(("rc = false\n"));
1045 return false;
1046 }
1047 /* Now convert the Utf16 Linux text to Utf8 */
1048 cbUtf8Text = cwGuestText * 3; /* Should always be enough. */
1049 pcUtf8Text = reinterpret_cast<char *>(RTMemAlloc(cbUtf8Text));
1050 if (pcUtf8Text == 0)
1051 {
1052 Log(("vboxClipboardConvertCText: out of memory\n"));
1053 RTMemFree(reinterpret_cast<char *>(pu16GuestText));
1054 LogFlowFunc(("rc = false\n"));
1055 return false;
1056 }
1057 /* Our runtime can't cope with endian markers. */
1058 rc = RTUtf16ToUtf8Ex(pu16GuestText + 1, cwGuestText - 1, &pcUtf8Text, cbUtf8Text, 0);
1059 RTMemFree(reinterpret_cast<char *>(pu16GuestText));
1060 if (rc != VINF_SUCCESS)
1061 {
1062 Log(("vboxClipboardConvertCText: RTUtf16ToUtf8Ex failed: rc=%Vrc\n", rc));
1063 XtFree(pcUtf8Text);
1064 LogFlowFunc(("rc = false\n"));
1065 return false;
1066 }
1067 /* And finally (!) convert the Utf8 text to compound text. */
1068#ifdef RT_OS_SOLARIS
1069 rc = XmbTextListToTextProperty(XtDisplay(g_ctx.widget), &pcUtf8Text, 1,
1070 XCompoundTextStyle, &property);
1071#else
1072 rc = Xutf8TextListToTextProperty(XtDisplay(g_ctx.widget), &pcUtf8Text, 1,
1073 XCompoundTextStyle, &property);
1074#endif
1075 RTMemFree(pcUtf8Text);
1076 if (rc < 0)
1077 {
1078 const char *pcReason;
1079 switch(rc)
1080 {
1081 case XNoMemory:
1082 pcReason = "out of memory";
1083 break;
1084 case XLocaleNotSupported:
1085 pcReason = "locale (Utf8) not supported";
1086 break;
1087 case XConverterNotFound:
1088 pcReason = "converter not found";
1089 break;
1090 default:
1091 pcReason = "unknown error";
1092 }
1093 LogRel(("vboxClipboardConvertCText: Xutf8TextListToTextProperty failed. Reason: %s\n",
1094 pcReason));
1095 XFree(property.value);
1096 LogFlowFunc(("rc = false\n"));
1097 return false;
1098 }
1099 LogFlow(("vboxClipboardConvertCText: converted text is %s\n", property.value));
1100 *atomTypeReturn = property.encoding;
1101 *pValReturn = reinterpret_cast<XtPointer>(property.value);
1102 *pcLenReturn = property.nitems;
1103 *piFormatReturn = property.format;
1104 LogFlowFunc(("rc = true\n"));
1105 return true;
1106}
1107
1108
1109/**
1110 * Callback to convert the hosts clipboard data for an application on the guest. Called by the
1111 * X Toolkit.
1112 * @returns true if we successfully convert the data to the format requested, false otherwise.
1113 *
1114 * @param atomSelection The selection which is being requested. We only handle the clipboard.
1115 * @param atomTarget The format we should convert the data to
1116 * @param atomTypeReturn The type of the data we are returning
1117 * @param pValReturn A pointer to the data we are returning. This should be to memory
1118 * allocated by XtMalloc, which will be freed by the toolkit later
1119 * @param pcLenReturn The length of the data we are returning
1120 * @param piFormatReturn The format (8bit, 16bit, 32bit) of the data we are returning
1121 */
1122static Boolean vboxClipboardConvertProc(Widget, Atom *atomSelection, Atom *atomTarget,
1123 Atom *atomTypeReturn, XtPointer *pValReturn,
1124 unsigned long *pcLenReturn, int *piFormatReturn)
1125{
1126 g_eClipboardFormat eFormat = INVALID;
1127 int rc;
1128
1129 LogFlowFunc(("\n"));
1130 if ( (*atomSelection != g_ctx.atomClipboard)
1131 && (*atomSelection != g_ctx.atomPrimary)
1132 )
1133 {
1134 LogFlowFunc(("rc = false\n"));
1135 return false;
1136 }
1137#ifdef DEBUG
1138 char *szAtomName = XGetAtomName(XtDisplay(g_ctx.widget), *atomTarget);
1139 if (szAtomName != 0)
1140 {
1141 LogFlow(("vboxClipboardConvertProc: request for format %s\n", szAtomName));
1142 XFree(szAtomName);
1143 }
1144 else
1145 Log(("vboxClipboardConvertProc: request for invalid target atom %d!\n", *atomTarget));
1146#endif
1147 if (*atomTarget == g_ctx.atomTargets)
1148 eFormat = TARGETS;
1149 else
1150 {
1151 for (unsigned i = 0; i != g_ctx.formatList.size(); ++i)
1152 {
1153 if (g_ctx.formatList[i].atom == *atomTarget)
1154 {
1155 eFormat = g_ctx.formatList[i].format;
1156 break;
1157 }
1158 }
1159 }
1160 switch (eFormat)
1161 {
1162 case TARGETS:
1163 rc = vboxClipboardConvertTargets(atomTypeReturn, pValReturn, pcLenReturn, piFormatReturn);
1164 LogFlowFunc(("TARGETS rc=%d\n", rc));
1165 return rc;
1166 case UTF16:
1167 rc = vboxClipboardConvertUtf16(atomTypeReturn, pValReturn, pcLenReturn, piFormatReturn);
1168 LogFlowFunc(("UTF16 rc=%d\n", rc));
1169 return rc;
1170 case UTF8:
1171 rc = vboxClipboardConvertUtf8(atomTypeReturn, pValReturn, pcLenReturn, piFormatReturn);
1172 LogFlowFunc(("UTF8 rc=%d\n", rc));
1173 return rc;
1174 case CTEXT:
1175 rc = vboxClipboardConvertCText(atomTypeReturn, pValReturn, pcLenReturn, piFormatReturn);
1176 LogFlowFunc(("CTEXT rc=%d\n", rc));
1177 return rc;
1178 default:
1179 Log(("vboxClipboardConvertProc: bad format\n"));
1180 LogFlowFunc(("rc = false\n"));
1181 return false;
1182 }
1183}
1184
1185
1186static void vboxClipboardLoseProc(Widget, Atom *)
1187{
1188 LogFlowFunc(("giving the guest clipboard ownership\n"));
1189 g_ctx.eOwner = GUEST;
1190 g_ctx.notifyHost = true;
1191 LogFlowFunc(("returning\n"));
1192}
1193
1194
1195/**
1196 * The host is taking possession of the shared clipboard. Called by the HGCM clipboard
1197 * subsystem.
1198 *
1199 * @param u32Formats Clipboard formats the the guest is offering
1200 */
1201void vboxClipboardFormatAnnounce (uint32_t u32Formats)
1202{
1203 g_ctx.hostFormats = u32Formats;
1204 LogFlowFunc(("u32Formats = %d\n", u32Formats));
1205 if (u32Formats == 0)
1206 {
1207 /* This is just an automatism, not a genuine anouncement */
1208 LogFlowFunc(("returning\n"));
1209 return;
1210 }
1211 LogFlow(("vboxClipboardFormatAnnounce: giving the host clipboard ownership\n"));
1212 g_ctx.eOwner = HOST;
1213 g_ctx.guestTextFormat = INVALID;
1214 g_ctx.guestBitmapFormat = INVALID;
1215 if (XtOwnSelection(g_ctx.widget, g_ctx.atomClipboard, CurrentTime, vboxClipboardConvertProc,
1216 vboxClipboardLoseProc, 0) != True)
1217 {
1218 LogFlow(("vboxClipboardFormatAnnounce: returning clipboard ownership to the guest\n"));
1219 g_ctx.notifyHost = true;
1220 g_ctx.eOwner = GUEST;
1221 }
1222 XtOwnSelection(g_ctx.widget, g_ctx.atomPrimary, CurrentTime, vboxClipboardConvertProc,
1223 NULL, 0);
1224 LogFlowFunc(("returning\n"));
1225}
1226
1227
1228/**
1229 * Called when the host wants to read the guest clipboard.
1230 *
1231 * @param u32Format The format that the host would like to receive the data in
1232 */
1233int vboxClipboardReadGuestData (uint32_t u32Format)
1234{
1235 LogFlowFunc(("u32Format = %d\n", u32Format));
1236
1237 /*
1238 * The guest wants to read data in the given format.
1239 */
1240 if (u32Format & VBOX_SHARED_CLIPBOARD_FMT_UNICODETEXT)
1241 {
1242 if (g_ctx.guestTextFormat == INVALID)
1243 {
1244 /* No data available. */
1245 vboxClipboardSendData(0, NULL, 0);
1246 LogFlowFunc(("sent empty data, returned VINF_SUCCESS\n"));
1247 return VINF_SUCCESS;
1248 }
1249 g_ctx.requestGuestFormat = g_ctx.guestTextFormat;
1250 /* Send out a request for the data to the current clipboard owner */
1251 XtGetSelectionValue(g_ctx.widget, g_ctx.atomClipboard, g_ctx.atomGuestTextFormat,
1252 vboxClipboardGetProc, 0, CurrentTime);
1253 /* When the data arrives, the vboxClipboardGetProc callback will be called. The
1254 callback will signal the event semaphore when it has processed the data for us. */
1255 }
1256 else
1257 {
1258 vboxClipboardSendData(0, NULL, 0);
1259 LogFlowFunc(("sent empty data, returned VERR_NOT_IMPLEMENTED\n"));
1260 return VERR_NOT_IMPLEMENTED;
1261 }
1262 LogFlowFunc(("returned VINF_SUCCESS\n"));
1263 return VINF_SUCCESS;
1264}
1265
1266
1267/** Terminate the guest side of the shared clipboard. */
1268void vboxClipboardDestroy (void)
1269{
1270 LogFlowFunc(("\n"));
1271
1272 /* Set the termination flag. */
1273 XtAppSetExitFlag(g_ctx.appContext);
1274 LogFlowFunc(("returning\n"));
1275}
1276
1277
1278/**
1279 * The main loop of our clipboard reader.
1280 */
1281static int vboxClipboardThread(RTTHREAD /* ThreadSelf */, void * /* pvUser */)
1282{
1283 int rc;
1284 LogFlowFunc(("Starting clipboard thread\n"));
1285
1286 for (;;)
1287 {
1288 uint32_t Msg;
1289 uint32_t fFormats;
1290 rc = VbglR3ClipboardGetHostMsg(g_ctx.client, &Msg, &fFormats);
1291 if (RT_SUCCESS(rc))
1292 {
1293 switch (Msg)
1294 {
1295 case VBOX_SHARED_CLIPBOARD_HOST_MSG_FORMATS:
1296 {
1297 /* The host has announced available clipboard formats.
1298 * Save the information so that it is available for
1299 * future requests from guest applications.
1300 */
1301 LogFlowFunc(("VBOX_SHARED_CLIPBOARD_HOST_MSG_FORMATS fFormats=%x\n", fFormats));
1302 vboxClipboardFormatAnnounce(fFormats);
1303 break;
1304 }
1305
1306 case VBOX_SHARED_CLIPBOARD_HOST_MSG_READ_DATA:
1307 {
1308 /* The host needs data in the specified format. */
1309 LogFlowFunc(("VBOX_SHARED_CLIPBOARD_HOST_MSG_READ_DATA fFormats=%x\n", fFormats));
1310 vboxClipboardReadGuestData(fFormats);
1311 break;
1312 }
1313
1314 case VBOX_SHARED_CLIPBOARD_HOST_MSG_QUIT:
1315 {
1316 /* The host is terminating. */
1317 LogFlowFunc(("VBOX_SHARED_CLIPBOARD_HOST_MSG_QUIT\n"));
1318 vboxClipboardDestroy();
1319 rc = VERR_INTERRUPTED;
1320 break;
1321 }
1322
1323 default:
1324 Log(("Unsupported message from host!!!\n"));
1325 }
1326 }
1327 if (rc == VERR_INTERRUPTED)
1328 {
1329 /* Wait for termination event. */
1330 RTSemEventWait(g_ctx.terminating, RT_INDEFINITE_WAIT);
1331 break;
1332 }
1333 if (VBOX_FAILURE(rc))
1334 {
1335 /* Wait a bit before retrying. */
1336 if (RTSemEventWait(g_ctx.terminating, 1000) == VINF_SUCCESS)
1337 break;
1338 continue;
1339 }
1340
1341 LogFlow(("processed host event rc = %d\n", rc));
1342 }
1343 LogFlowFunc(("rc=%d\n", rc));
1344 return rc;
1345}
1346
1347
1348/**
1349 * Disconnect the guest clipboard from the host.
1350 */
1351void vboxClipboardDisconnect(void)
1352{
1353#if 0
1354 VMMDevHGCMDisconnect request;
1355#endif
1356 LogFlowFunc(("\n"));
1357
1358 AssertReturn(g_ctx.client != 0, (void) 0);
1359 VbglR3ClipboardDisconnect(g_ctx.client);
1360 LogFlowFunc(("returning\n"));
1361}
1362
1363
1364/**
1365 * Connect the guest clipboard to the host.
1366 *
1367 * @returns VBox status code
1368 */
1369int vboxClipboardConnect(void)
1370{
1371 int rc;
1372 LogFlowFunc(("\n"));
1373
1374 /* Only one client is supported for now */
1375 AssertReturn(g_ctx.client == 0, VERR_NOT_SUPPORTED);
1376
1377 /* Initialise the termination semaphore. */
1378 RTSemEventCreate(&g_ctx.terminating);
1379
1380 rc = VbglR3ClipboardConnect(&g_ctx.client);
1381 if (VBOX_FAILURE(rc))
1382 {
1383 LogRel(("Error connecting to host. rc=%Vrc\n", rc));
1384 return rc;
1385 }
1386 if (!g_ctx.client)
1387 {
1388 LogRel(("Invalid client ID of 0\n"));
1389 return VERR_NOT_SUPPORTED;
1390 }
1391 g_ctx.eOwner = HOST;
1392 LogFlowFunc(("g_ctx.client=%u rc=%Vrc\n", g_ctx.client, rc));
1393 return rc;
1394}
1395
1396
1397/** We store information about the target formats we can handle in a global vector for internal
1398 use. */
1399static void vboxClipboardAddFormat(const char *pszName, g_eClipboardFormat eFormat, unsigned hostFormat)
1400{
1401 VBOXCLIPBOARDFORMAT sFormat;
1402 LogFlowFunc(("pszName=%s, eFormat=%d, hostFormat=%u\n", pszName, eFormat, hostFormat));
1403 /* Get an atom from the X server for that target format */
1404 Atom atomFormat = XInternAtom(XtDisplay(g_ctx.widget), pszName, false);
1405 LogFlow(("vboxClipboardAddFormat: atomFormat=%d\n", atomFormat));
1406 if (atomFormat == 0)
1407 {
1408 LogFlowFunc(("atomFormat=0! returning...\n"));
1409 return;
1410 }
1411 sFormat.atom = atomFormat;
1412 sFormat.format = eFormat;
1413 sFormat.hostFormat = hostFormat;
1414 g_ctx.formatList.push_back(sFormat);
1415 LogFlowFunc(("returning\n"));
1416}
1417
1418
1419/** Create the X11 window which we use to interact with the guest clipboard */
1420static int vboxClipboardCreateWindow(void)
1421{
1422 /* Create a window and make it a clipboard viewer. */
1423 int cArgc = 0;
1424 char *pcArgv = 0;
1425 String szFallbackResources[] = { (char*)"*.width: 1", (char*)"*.height: 1", 0 };
1426
1427 /* Set up the Clipbard application context and main window. */
1428 g_ctx.widget = XtOpenApplication(&g_ctx.appContext, "VBoxClipboard", 0, 0, &cArgc, &pcArgv,
1429 szFallbackResources, applicationShellWidgetClass, 0, 0);
1430 AssertMsgReturn(g_ctx.widget != 0, ("Failed to construct the X clipboard window\n"),
1431 VERR_ACCESS_DENIED);
1432 XtSetMappedWhenManaged(g_ctx.widget, false);
1433 XtRealizeWidget(g_ctx.widget);
1434
1435 /* Get hold of the atoms which we need */
1436 g_ctx.atomClipboard = XInternAtom(XtDisplay(g_ctx.widget), "CLIPBOARD", false /* only_if_exists */);
1437 g_ctx.atomPrimary = XInternAtom(XtDisplay(g_ctx.widget), "PRIMARY", false);
1438 g_ctx.atomTargets = XInternAtom(XtDisplay(g_ctx.widget), "TARGETS", false);
1439 g_ctx.atomMultiple = XInternAtom(XtDisplay(g_ctx.widget), "MULTIPLE", false);
1440 g_ctx.atomTimestamp = XInternAtom(XtDisplay(g_ctx.widget), "TIMESTAMP", false);
1441 g_ctx.atomUtf16 = XInternAtom(XtDisplay(g_ctx.widget),
1442 "text/plain;charset=ISO-10646-UCS-2", false);
1443 g_ctx.atomUtf8 = XInternAtom(XtDisplay(g_ctx.widget), "UTF_STRING", false);
1444 g_ctx.atomCText = XInternAtom(XtDisplay(g_ctx.widget), "COMPOUND_TEXT", false);
1445 /* And build up the vector of supported formats */
1446#ifdef USE_UTF16
1447 vboxClipboardAddFormat("text/plain;charset=ISO-10646-UCS-2", UTF16, VBOX_SHARED_CLIPBOARD_FMT_UNICODETEXT);
1448#endif
1449#ifdef USE_UTF8
1450 vboxClipboardAddFormat("UTF8_STRING", UTF8, VBOX_SHARED_CLIPBOARD_FMT_UNICODETEXT);
1451 vboxClipboardAddFormat("text/plain;charset=UTF-8", UTF8, VBOX_SHARED_CLIPBOARD_FMT_UNICODETEXT);
1452 vboxClipboardAddFormat("text/plain;charset=utf-8", UTF8, VBOX_SHARED_CLIPBOARD_FMT_UNICODETEXT);
1453 vboxClipboardAddFormat("STRING", UTF8, VBOX_SHARED_CLIPBOARD_FMT_UNICODETEXT);
1454 vboxClipboardAddFormat("TEXT", UTF8, VBOX_SHARED_CLIPBOARD_FMT_UNICODETEXT);
1455 vboxClipboardAddFormat("text/plain", UTF8, VBOX_SHARED_CLIPBOARD_FMT_UNICODETEXT);
1456#endif
1457#ifdef USE_CTEXT
1458 vboxClipboardAddFormat("COMPOUND_TEXT", CTEXT, VBOX_SHARED_CLIPBOARD_FMT_UNICODETEXT);
1459#endif
1460 return VINF_SUCCESS;
1461}
1462
1463/** Initialise the guest side of the shared clipboard. */
1464int vboxClipboardMain(void)
1465{
1466 int rc;
1467 LogFlowFunc(("\n"));
1468
1469 rc = vboxClipboardCreateWindow();
1470 if (VBOX_FAILURE(rc))
1471 return rc;
1472
1473 rc = RTThreadCreate(&g_ctx.thread, vboxClipboardThread, 0, 0, RTTHREADTYPE_IO,
1474 RTTHREADFLAGS_WAITABLE, "SHCLIP");
1475 AssertRCReturn(rc, rc);
1476 /* Set up a timer to poll the host clipboard */
1477 XtAppAddTimeOut(g_ctx.appContext, 200 /* ms */, vboxClipboardTimerProc, 0);
1478
1479 XtAppMainLoop(g_ctx.appContext);
1480 g_ctx.formatList.clear();
1481 XtDestroyApplicationContext(g_ctx.appContext);
1482 /* Set the termination signal. */
1483 RTSemEventSignal(g_ctx.terminating);
1484 LogFlowFunc(("returning %d\n", rc));
1485 return rc;
1486}
1487
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