VirtualBox

source: vbox/trunk/src/VBox/Additions/x11/VBoxClient/clipboard.cpp@ 19979

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

clipboard: typo

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