VirtualBox

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

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

Additions/x11/clipboard: fix handling of middle-button pasting and the issue with the guest clipboard not working immediately on startup

  • Property svn:eol-style set to native
  • Property svn:keywords set to Id
File size: 54.4 KB
Line 
1/** $Id: clipboard.cpp 19150 2009-04-23 19:01:05Z 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 /* First convert the compound text to Utf8 */
393 property.value = reinterpret_cast<unsigned char *>(pValue);
394 property.encoding = g_ctx.atomCText;
395 property.format = 8;
396 property.nitems = cbSourceLen;
397#ifdef RT_OS_SOLARIS
398 rc = XmbTextPropertyToTextList(XtDisplay(g_ctx.widget), &property, &ppu8SourceText, &cProps);
399#else
400 rc = Xutf8TextPropertyToTextList(XtDisplay(g_ctx.widget), &property, &ppu8SourceText, &cProps);
401#endif
402 XtFree(reinterpret_cast<char *>(pValue));
403 if (rc < 0)
404 {
405 const char *pcReason;
406 switch(rc)
407 {
408 case XNoMemory:
409 pcReason = "out of memory";
410 break;
411 case XLocaleNotSupported:
412 pcReason = "locale (Utf8) not supported";
413 break;
414 case XConverterNotFound:
415 pcReason = "converter not found";
416 break;
417 default:
418 pcReason = "unknown error";
419 }
420 XFreeStringList(ppu8SourceText);
421 LogRel(("vboxClipboardGetCText: Xutf8TextPropertyToTextList failed. Reason: %s\n", pcReason));
422 vboxClipboardSendData(0, NULL, 0);
423 LogFlowFunc(("sending empty data and returning\n"));
424 return;
425 }
426 /* Next convert the UTF8 to UTF16 */
427 rc = RTStrToUtf16Ex(*ppu8SourceText, cbSourceLen, &pu16SourceText, 0, &cwSourceLen);
428 XFreeStringList(ppu8SourceText);
429 if (rc != VINF_SUCCESS)
430 {
431 vboxClipboardSendData(0, NULL, 0);
432 LogFlowFunc(("sending empty data and returning\n"));
433 return;
434 }
435 rc = vboxClipboardUtf16LinToWin(pu16SourceText, cwSourceLen, &pu16DestText, &cwDestLen);
436 RTMemFree(reinterpret_cast<void *>(pu16SourceText));
437 if (rc != VINF_SUCCESS)
438 {
439 vboxClipboardSendData(0, NULL, 0);
440 LogFlowFunc(("sending empty data and returning\n"));
441 return;
442 }
443 LogFlow(("vboxClipboardGetCText: converted string is %.*ls\n", cwDestLen, pu16DestText));
444 vboxClipboardSendData(VBOX_SHARED_CLIPBOARD_FMT_UNICODETEXT,
445 reinterpret_cast<void *>(pu16DestText), cwDestLen * 2);
446 RTMemFree(reinterpret_cast<void *>(pu16DestText));
447 LogFlowFunc(("returning\n"));
448}
449
450
451/**
452 * Convert the Latin1 text returned from the guest X11 clipboard to UTF-16LE with Windows EOLs
453 * and send it to the host.
454 *
455 * @param pValue Source Latin1 text
456 * @param cbSourceLen Length in 8-bit bytes of the source text
457 */
458static void vboxClipboardGetLatin1(XtPointer pValue, size_t cbSourceLen)
459{
460 enum { LINEFEED = 0xa, CARRIAGERETURN = 0xd };
461 /* Leave space for an additional null character at the end of the destination text. */
462 size_t cwDestLen = cbSourceLen + 1, cwDestPos;
463 char *pu8SourceText = reinterpret_cast<char *>(pValue);
464 PRTUTF16 pu16DestText;
465
466 LogFlowFunc(("converting Latin1 to Utf-16LE. Original is %.*s\n", cbSourceLen,
467 pu8SourceText));
468 /* Find the size of the destination text */
469 for (size_t i = 0; i < cbSourceLen; i++)
470 {
471 if (pu8SourceText[i] == LINEFEED)
472 ++cwDestLen;
473 }
474 pu16DestText = reinterpret_cast<PRTUTF16>(RTMemAlloc(cwDestLen * 2));
475 if (pu16DestText == 0)
476 {
477 XtFree(reinterpret_cast<char *>(pValue));
478 LogFlow(("vboxClipboardGetLatin1: failed to allocate %d bytes!\n", cwDestLen * 2));
479 vboxClipboardSendData(0, NULL, 0);
480 LogFlowFunc(("sending empty data and returning\n"));
481 return;
482 }
483 /* Copy the original X clipboard string to the destination, replacing Linux EOLs with
484 Windows ones */
485 cwDestPos = 0;
486 for (size_t i = 0; i < cbSourceLen; ++i, ++cwDestPos)
487 {
488 if (pu8SourceText[i] == LINEFEED)
489 {
490 pu16DestText[cwDestPos] = CARRIAGERETURN;
491 ++cwDestPos;
492 }
493 /* latin1 < utf-16LE */
494 pu16DestText[cwDestPos] = pu8SourceText[i];
495 if (pu8SourceText[i] == 0)
496 break;
497 }
498 pu16DestText[cwDestPos] = 0;
499 LogFlow(("vboxClipboardGetLatin1: converted text is %.*ls\n", cwDestPos, pu16DestText));
500 vboxClipboardSendData(VBOX_SHARED_CLIPBOARD_FMT_UNICODETEXT,
501 reinterpret_cast<void *>(pu16DestText), cwDestPos * 2);
502 RTMemFree(reinterpret_cast<void *>(pu16DestText));
503 XtFree(reinterpret_cast<char *>(pValue));
504 LogFlowFunc(("returning\n"));
505}
506
507
508/** Convert the clipboard text from the current format to Utf-16 with Windows line breaks.
509 We are reading the guest clipboard to make it available to the host. */
510static void vboxClipboardGetProc(Widget, XtPointer /* pClientData */, Atom * /* selection */,
511 Atom *atomType, XtPointer pValue, long unsigned int *pcLen,
512 int *piFormat)
513{
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 /* The X Toolkit may have failed to get the clipboard selection for us. */
517 if (*atomType == XT_CONVERT_FAIL)
518 {
519 vboxClipboardSendData(0, NULL, 0);
520 LogFlowFunc(("Xt failed to convert the data. Sending empty data and returning\n"));
521 return;
522 }
523 if (NULL == pValue)
524 {
525 vboxClipboardSendData(0, NULL, 0);
526 LogFlowFunc(("The clipboard contents changed while we were requesting them. Sending empty data and returning\n"));
527 return;
528 }
529 /* In which format did we request the clipboard data? */
530 unsigned cTextLen = (*pcLen) * (*piFormat) / 8;
531 switch (g_ctx.requestGuestFormat)
532 {
533 case UTF16:
534 vboxClipboardGetUtf16(pValue, cTextLen / 2);
535 break;
536 case CTEXT:
537 vboxClipboardGetCText(pValue, cTextLen);
538 break;
539 case UTF8:
540 {
541 /* If we are given broken Utf-8, we treat it as Latin1. Is this acceptable? */
542 size_t cStringLen;
543 char *pu8SourceText = reinterpret_cast<char *>(pValue);
544
545 if (RTStrUniLenEx(pu8SourceText, *pcLen, &cStringLen) == VINF_SUCCESS)
546 vboxClipboardGetUtf8(pValue, cTextLen);
547 else
548 vboxClipboardGetLatin1(pValue, cTextLen);
549 break;
550 }
551 default:
552 {
553 XtFree(reinterpret_cast<char *>(pValue));
554 Log(("vboxClipboardGetProc: bad target format\n"));
555 vboxClipboardSendData(0, NULL, 0);
556 LogFlowFunc(("sending empty data and returning\n"));
557 return;
558 }
559 }
560 g_ctx.notifyHost = true;
561 LogFlowFunc(("returning\n"));
562}
563
564
565/**
566 * Tell the host that new clipboard formats are available.
567 *
568 * @returns VBox status code.
569 * @param u32Formats The formats to advertise
570 */
571static int vboxClipboardReportFormats(uint32_t u32Formats)
572{
573 int rc;
574 LogFlowFunc(("u32Formats=%d\n", u32Formats));
575 rc = VbglR3ClipboardReportFormats(g_ctx.client, u32Formats);
576 LogFlowFunc(("rc=%Rrc\n", rc));
577 return rc;
578}
579
580
581/** Callback to handle a reply to a request for the targets the current clipboard holder can
582 handle. We are reading the guest clipboard to make it available to the host. */
583static void vboxClipboardTargetsProc(Widget, XtPointer, Atom * /* selection */, Atom *atomType,
584 XtPointer pValue, long unsigned int *pcLen, int *piFormat)
585{
586 static int cCalls = 0;
587 Atom *atomTargets = reinterpret_cast<Atom *>(pValue);
588 /* The number of format atoms the clipboard holder is offering us */
589 unsigned cAtoms = *pcLen;
590 /* The best clipboard format we have found so far */
591 g_eClipboardFormat eBestTarget = INVALID;
592 /* The atom corresponding to our best clipboard format found */
593 Atom atomBestTarget = None;
594
595 if ((cCalls % 10) == 0)
596 Log3(("vboxClipboardTargetsProc called, cAtoms=%d\n", cAtoms));
597 if (*atomType == XT_CONVERT_FAIL)
598 {
599 Log(("vboxClipboardTargetsProc: reading clipboard from guest, X toolkit failed to convert the selection\n"));
600 LogFlowFunc(("returning\n"));
601 return;
602 }
603 /* Run through the atoms offered to us to see if we recognise them. If we find the atom for
604 a "better" format than the best we have found so far, we remember it as our new "best"
605 format. */
606 for (unsigned i = 0; i < cAtoms; ++i)
607 {
608 for (unsigned j = 0; j != g_ctx.formatList.size(); ++j)
609 if (g_ctx.formatList[j].atom == atomTargets[i])
610 {
611 if (eBestTarget < g_ctx.formatList[j].format)
612 {
613 eBestTarget = g_ctx.formatList[j].format;
614 atomBestTarget = g_ctx.formatList[j].atom;
615 }
616 break;
617 }
618#ifdef DEBUG
619 char *szAtomName = XGetAtomName(XtDisplay(g_ctx.widget), atomTargets[i]);
620 if (szAtomName != 0)
621 {
622 if ((cCalls % 10) == 0)
623 Log3(("vboxClipboardTargetsProc: the guest offers target %s\n", szAtomName));
624 XFree(szAtomName);
625 }
626 else
627 {
628 if ((cCalls % 10) == 0)
629 Log3(("vboxClipboardTargetsProc: the guest returned a bad atom: %d\n",
630 atomTargets[i]));
631 }
632#endif
633 }
634 g_ctx.atomGuestTextFormat = atomBestTarget;
635 /* If the available formats as seen by the host have changed, or if we suspect that
636 the host has cached the clipboard data (which can change without our noticing it),
637 then tell the host that new clipboard contents are available. */
638 if ((eBestTarget != g_ctx.guestTextFormat) || (g_ctx.notifyHost == true))
639 {
640 uint32_t u32Formats = 0;
641#ifdef DEBUG
642 if (atomBestTarget != None)
643 {
644 char *szAtomName = XGetAtomName(XtDisplay(g_ctx.widget), atomBestTarget);
645 LogFlow(("vboxClipboardTargetsProc: switching to guest text target %s. Available targets are:\n", szAtomName));
646 XFree(szAtomName);
647 }
648 else
649 LogFlow(("vboxClipboardTargetsProc: no supported host text target found. Available targets are:\n"));
650
651 for (unsigned i = 0; i < cAtoms; ++i)
652 {
653 char *szAtomName = XGetAtomName(XtDisplay(g_ctx.widget), atomTargets[i]);
654 if (szAtomName != 0)
655 {
656 LogFlow(("vboxClipboardTargetsProc: %s\n", szAtomName));
657 XFree(szAtomName);
658 }
659 }
660#endif
661 g_ctx.guestTextFormat = eBestTarget;
662 if (eBestTarget != INVALID)
663 u32Formats |= VBOX_SHARED_CLIPBOARD_FMT_UNICODETEXT;
664 vboxClipboardReportFormats(u32Formats);
665 g_ctx.notifyHost = false;
666 }
667 XtFree(reinterpret_cast<char *>(pValue));
668 ++cCalls;
669}
670
671
672/**
673 * This callback is called every 200ms to check the contents of the guest clipboard.
674 */
675static void vboxClipboardTimerProc(XtPointer /* pUserData */, XtIntervalId * /* hTimerId */)
676{
677 static int cCalls = 0;
678 /* Get the current clipboard contents */
679 if (g_ctx.eOwner == GUEST)
680 {
681 if ((cCalls % 10) == 0)
682 Log3(("vboxClipboardTimerProc: requesting the targets that the guest clipboard offers\n"));
683 XtGetSelectionValue(g_ctx.widget, g_ctx.atomClipboard, g_ctx.atomTargets,
684 vboxClipboardTargetsProc, 0, CurrentTime);
685 }
686 /* Re-arm our timer */
687 XtAppAddTimeOut(g_ctx.appContext, 200 /* ms */, vboxClipboardTimerProc, 0);
688 ++cCalls;
689}
690
691
692/**
693 * Satisfy a request from the guest for available clipboard targets.
694 *
695 * @returns true if we successfully convert the data to the format requested, false otherwise.
696 *
697 * @param atomTypeReturn The type of the data we are returning
698 * @param pValReturn A pointer to the data we are returning. This should be to memory
699 * allocated by XtMalloc, which will be freed by the toolkit later
700 * @param pcLenReturn The length of the data we are returning
701 * @param piFormatReturn The format (8bit, 16bit, 32bit) of the data we are returning
702 */
703static Boolean vboxClipboardConvertTargets(Atom *atomTypeReturn, XtPointer *pValReturn,
704 unsigned long *pcLenReturn, int *piFormatReturn)
705{
706 unsigned uListSize = g_ctx.formatList.size();
707 Atom *atomTargets = reinterpret_cast<Atom *>(XtMalloc((uListSize + 3) * sizeof(Atom)));
708 unsigned cTargets = 0;
709
710 LogFlowFunc(("\n"));
711 LogFlow(("vboxClipboardConvertTargets: uListSize=%u\n", uListSize));
712 for (unsigned i = 0; i < uListSize; ++i)
713 {
714 if ( ((g_ctx.hostFormats & VBOX_SHARED_CLIPBOARD_FMT_UNICODETEXT) != 0)
715 && (g_ctx.formatList[i].hostFormat == VBOX_SHARED_CLIPBOARD_FMT_UNICODETEXT))
716 {
717 atomTargets[cTargets] = g_ctx.formatList[i].atom;
718 ++cTargets;
719 }
720 }
721 atomTargets[cTargets] = g_ctx.atomTargets;
722 atomTargets[cTargets + 1] = g_ctx.atomMultiple;
723 atomTargets[cTargets + 2] = g_ctx.atomTimestamp;
724#ifdef DEBUG
725 for (unsigned i = 0; i < cTargets + 3; i++)
726 {
727 char *szAtomName = XGetAtomName(XtDisplay(g_ctx.widget), atomTargets[i]);
728 if (szAtomName != 0)
729 {
730 LogFlow(("vboxClipboardConvertTargets: returning target %s\n", szAtomName));
731 XFree(szAtomName);
732 }
733 else
734 Log(("vboxClipboardConvertTargets: invalid atom %d in the list!\n", atomTargets[i]));
735 }
736#endif
737 *atomTypeReturn = XA_ATOM;
738 *pValReturn = reinterpret_cast<XtPointer>(atomTargets);
739 *pcLenReturn = cTargets + 3;
740 *piFormatReturn = 32;
741 LogFlowFunc(("returning true\n"));
742 return true;
743}
744
745
746/**
747 * Get the size of the buffer needed to hold a zero-terminated Utf16 string with Linux EOLs
748 * converted from a Utf16 string with Windows EOLs.
749 *
750 * @returns The size of the buffer needed in bytes
751 *
752 * @param pu16Src The source Utf16 string
753 * @param cwSrc The length in 16 bit words of the source string
754 */
755static int vboxClipboardUtf16GetLinSize(PRTUTF16 pu16Src, size_t cwSrc)
756{
757 enum { LINEFEED = 0xa, CARRIAGERETURN = 0xd };
758 size_t cwDest;
759
760 LogFlowFunc(("pu16Src=%.*ls, cwSrc=%u\n", cwSrc, pu16Src, cwSrc));
761 AssertReturn(pu16Src != 0, VERR_INVALID_PARAMETER);
762 /* We only take little endian Utf16 */
763 AssertReturn(pu16Src[0] != 0xfffe, VERR_INVALID_PARAMETER);
764 if (cwSrc == 0)
765 {
766 LogFlowFunc(("returning 0\n"));
767 return 0;
768 }
769 /* Calculate the size of the destination text string. */
770 /* Is this Utf16 or Utf16-LE? */
771 if (pu16Src[0] == 0xfeff)
772 cwDest = 0;
773 else
774 cwDest = 1;
775 for (size_t i = 0; i < cwSrc; ++i, ++cwDest)
776 {
777 if ( (i + 1 < cwSrc)
778 && (pu16Src[i] == CARRIAGERETURN)
779 && (pu16Src[i + 1] == LINEFEED))
780 ++i;
781 if (pu16Src[i] == 0)
782 break;
783 }
784 /* The terminating null */
785 ++cwDest;
786 LogFlowFunc(("returning %d\n", cwDest * 2));
787 return cwDest * 2;
788}
789
790
791/**
792 * Convert Utf16-LE text with Windows EOLs to Utf16 with Linux EOLs. This function does not
793 * verify that the Utf16 is valid.
794 *
795 * @returns VBox status code
796 *
797 * @param pu16Src Text to convert
798 * @param cwSrc Size of the source text in 16 bit words
799 * @param pu16Dest The buffer to store the converted text to
800 * @param cwDest The size of the buffer for the destination text
801 */
802static int vboxClipboardUtf16WinToLin(PRTUTF16 pu16Src, size_t cwSrc, PRTUTF16 pu16Dest,
803 size_t cwDest)
804{
805 enum { LINEFEED = 0xa, CARRIAGERETURN = 0xd };
806 size_t cwDestPos;
807
808 LogFlowFunc(("pu16Src=%.*ls, cwSrc=%u, pu16Dest=%p, cwDest=%u\n",
809 cwSrc, pu16Src, cwSrc, pu16Dest, cwDest));
810 /* A buffer of size 0 may not be an error, but it is not a good idea either. */
811 Assert(cwDest > 0);
812 AssertReturn(VALID_PTR(pu16Src), VERR_INVALID_PARAMETER);
813 /* We only take little endian Utf16 */
814 AssertReturn(pu16Src[0] != 0xfffe, VERR_INVALID_PARAMETER);
815 if (cwSrc == 0)
816 {
817 if (cwDest != 0)
818 pu16Dest[0] = 0;
819 LogFlowFunc(("set empty string. Returning VINF_SUCCESS\n"));
820 return VINF_SUCCESS;
821 }
822 /* Prepend the Utf16 byte order marker if it is missing. */
823 if (pu16Src[0] == 0xfeff)
824 cwDestPos = 0;
825 else
826 {
827 if (cwDest == 0)
828 {
829 LogFlowFunc(("returning VERR_BUFFER_OVERFLOW\n"));
830 return VERR_BUFFER_OVERFLOW;
831 }
832 pu16Dest[0] = 0xfeff;
833 cwDestPos = 1;
834 }
835 for (size_t i = 0; i < cwSrc; ++i, ++cwDestPos)
836 {
837 if (cwDestPos == cwDest)
838 {
839 LogFlowFunc(("returning VERR_BUFFER_OVERFLOW\n"));
840 return VERR_BUFFER_OVERFLOW;
841 }
842 if ( (i + 1 < cwSrc)
843 && (pu16Src[i] == CARRIAGERETURN)
844 && (pu16Src[i + 1] == LINEFEED))
845 ++i;
846
847 pu16Dest[cwDestPos] = pu16Src[i];
848 if (pu16Src[i] == 0)
849 break;
850 }
851 if (cwDestPos == cwDest)
852 {
853 LogFlowFunc(("returning VERR_BUFFER_OVERFLOW\n"));
854 return VERR_BUFFER_OVERFLOW;
855 }
856 pu16Dest[cwDestPos] = 0;
857 LogFlowFunc(("set string %ls. Returning\n", pu16Dest + 1));
858 return VINF_SUCCESS;
859}
860
861
862/**
863 * Satisfy a request from the guest to convert the clipboard text to Utf16.
864 *
865 * @returns true if we successfully convert the data to the format requested, false otherwise.
866 *
867 * @param atomTypeReturn The type of the data we are returning
868 * @param pValReturn A pointer to the data we are returning. This should be to memory
869 * allocated by XtMalloc, which will be freed by the toolkit later
870 * @param pcLenReturn The length of the data we are returning
871 * @param piFormatReturn The format (8bit, 16bit, 32bit) of the data we are returning
872 */
873static Boolean vboxClipboardConvertUtf16(Atom *atomTypeReturn, XtPointer *pValReturn,
874 unsigned long *pcLenReturn, int *piFormatReturn)
875{
876 PRTUTF16 pu16GuestText, pu16HostText;
877 unsigned cbHostText, cwHostText, cwGuestText;
878 int rc;
879
880 LogFlowFunc(("\n"));
881 rc = vboxClipboardReadHostData(VBOX_SHARED_CLIPBOARD_FMT_UNICODETEXT,
882 reinterpret_cast<void **>(&pu16HostText), &cbHostText);
883 if ((rc != VINF_SUCCESS) || cbHostText == 0)
884 {
885 LogFlow(("vboxClipboardConvertUtf16: vboxClipboardReadHostData returned %Rrc, %d bytes of data\n", rc, cbHostText));
886 g_ctx.hostFormats = 0;
887 LogFlowFunc(("rc = false\n"));
888 return false;
889 }
890 cwHostText = cbHostText / 2;
891 cwGuestText = vboxClipboardUtf16GetLinSize(pu16HostText, cwHostText);
892 pu16GuestText = reinterpret_cast<PRTUTF16>(XtMalloc(cwGuestText * 2));
893 if (pu16GuestText == 0)
894 {
895 RTMemFree(reinterpret_cast<void *>(pu16HostText));
896 LogFlowFunc(("rc = false\n"));
897 return false;
898 }
899 rc = vboxClipboardUtf16WinToLin(pu16HostText, cwHostText, pu16GuestText, cwGuestText);
900 if (rc != VINF_SUCCESS)
901 {
902 LogFlow(("vboxClipboardConvertUtf16: vboxClipboardUtf16WinToLin returned %Rrc\n", rc));
903 RTMemFree(reinterpret_cast<void *>(pu16HostText));
904 XtFree(reinterpret_cast<char *>(pu16GuestText));
905 LogFlowFunc(("rc = false\n"));
906 return false;
907 }
908 LogFlow(("vboxClipboardConvertUtf16: returning Unicode, original text is %.*ls\n", cwHostText, pu16HostText));
909 LogFlow(("vboxClipboardConvertUtf16: converted text is %.*ls\n", cwGuestText - 1, pu16GuestText + 1));
910 RTMemFree(reinterpret_cast<void *>(pu16HostText));
911 *atomTypeReturn = g_ctx.atomUtf16;
912 *pValReturn = reinterpret_cast<XtPointer>(pu16GuestText);
913 *pcLenReturn = cwGuestText * 2;
914 *piFormatReturn = 8;
915 LogFlowFunc(("rc = true\n"));
916 return true;
917}
918
919
920/**
921 * Satisfy a request from the guest to convert the clipboard text to Utf8.
922 *
923 * @returns true if we successfully convert the data to the format requested, false otherwise.
924 *
925 * @param atomTypeReturn The type of the data we are returning
926 * @param pValReturn A pointer to the data we are returning. This should be to memory
927 * allocated by XtMalloc, which will be freed by the toolkit later
928 * @param pcLenReturn The length of the data we are returning
929 * @param piFormatReturn The format (8bit, 16bit, 32bit) of the data we are returning
930 */
931static Boolean vboxClipboardConvertUtf8(Atom *atomTypeReturn, XtPointer *pValReturn,
932 unsigned long *pcLenReturn, int *piFormatReturn)
933{
934 PRTUTF16 pu16GuestText, pu16HostText;
935 char *pcGuestText;
936 unsigned cbHostText, cwHostText, cwGuestText, cbGuestTextBuffer;
937 size_t cbGuestTextActual;
938 int rc;
939
940 LogFlowFunc(("\n"));
941 /* Get the host Utf16 data and convert it to Linux Utf16. */
942 rc = vboxClipboardReadHostData(VBOX_SHARED_CLIPBOARD_FMT_UNICODETEXT,
943 reinterpret_cast<void **>(&pu16HostText), &cbHostText);
944 if ((rc != VINF_SUCCESS) || cbHostText == 0)
945 {
946 LogFlow(("vboxClipboardConvertUtf16: vboxClipboardReadHostData returned %Rrc, %d bytes of data\n", rc, cbHostText));
947 g_ctx.hostFormats = 0;
948 LogFlowFunc(("rc = false\n"));
949 return false;
950 }
951 cwHostText = cbHostText / 2;
952 LogFlow(("vboxClipboardConvertUtf8: original text is %.*ls\n", cwHostText, pu16HostText));
953 cwGuestText = vboxClipboardUtf16GetLinSize(pu16HostText, cwHostText);
954 pu16GuestText = reinterpret_cast<PRTUTF16>(RTMemAlloc(cwGuestText * 2));
955 if (pu16GuestText == 0)
956 {
957 RTMemFree(reinterpret_cast<char *>(pu16HostText));
958 LogFlowFunc(("rc = false\n"));
959 return false;
960 }
961 rc = vboxClipboardUtf16WinToLin(pu16HostText, cwHostText, pu16GuestText, cwGuestText);
962 RTMemFree(reinterpret_cast<char *>(pu16HostText));
963 if (rc != VINF_SUCCESS)
964 {
965 LogFlow(("vboxClipboardConvertUtf16: vboxClipboardUtf16WinToLin returned %Rrc\n", rc));
966 RTMemFree(reinterpret_cast<char *>(pu16GuestText));
967 LogFlowFunc(("rc = false\n"));
968 return false;
969 }
970 /* Now convert the Utf16 Linux text to Utf8 */
971 cbGuestTextBuffer = cwGuestText * 3; /* Should always be enough. */
972 pcGuestText = XtMalloc(cbGuestTextBuffer);
973 if (pcGuestText == 0)
974 {
975 RTMemFree(reinterpret_cast<char *>(pu16GuestText));
976 LogFlowFunc(("rc = false\n"));
977 return false;
978 }
979 /* Our runtime can't cope with endian markers. */
980 cbGuestTextActual = 0;
981 rc = RTUtf16ToUtf8Ex(pu16GuestText + 1, cwGuestText - 1, &pcGuestText, cbGuestTextBuffer, &cbGuestTextActual);
982 RTMemFree(reinterpret_cast<char *>(pu16GuestText));
983 if (rc != VINF_SUCCESS)
984 {
985 XtFree(pcGuestText);
986 LogFlowFunc(("rc = false\n"));
987 return false;
988 }
989 LogFlow(("vboxClipboardConvertUtf8: converted text is %.*s\n", cbGuestTextActual, pcGuestText));
990 *atomTypeReturn = g_ctx.atomUtf8;
991 *pValReturn = reinterpret_cast<XtPointer>(pcGuestText);
992 *pcLenReturn = (unsigned long)cbGuestTextActual;
993 *piFormatReturn = 8;
994 LogFlowFunc(("rc = true\n"));
995 return true;
996}
997
998
999/**
1000 * Satisfy a request from the guest to convert the clipboard text to compound text.
1001 *
1002 * @returns true if we successfully convert the data to the format requested, false otherwise.
1003 *
1004 * @param atomTypeReturn The type of the data we are returning
1005 * @param pValReturn A pointer to the data we are returning. This should be to memory
1006 * allocated by XtMalloc, which will be freed by the toolkit later
1007 * @param pcLenReturn The length of the data we are returning
1008 * @param piFormatReturn The format (8bit, 16bit, 32bit) of the data we are returning
1009 */
1010static Boolean vboxClipboardConvertCText(Atom *atomTypeReturn, XtPointer *pValReturn,
1011 unsigned long *pcLenReturn, int *piFormatReturn)
1012{
1013 PRTUTF16 pu16GuestText, pu16HostText;
1014 char *pcUtf8Text;
1015 unsigned cbHostText, cwHostText, cwGuestText, cbUtf8Text;
1016 XTextProperty property;
1017 int rc;
1018
1019 LogFlowFunc(("\n"));
1020 /* Get the host Utf16 data and convert it to Linux Utf16. */
1021 rc = vboxClipboardReadHostData(VBOX_SHARED_CLIPBOARD_FMT_UNICODETEXT,
1022 reinterpret_cast<void **>(&pu16HostText), &cbHostText);
1023 if ((rc != VINF_SUCCESS) || cbHostText == 0)
1024 {
1025 LogFlow(("vboxClipboardConvertCText: vboxClipboardReadHostData returned %Rrc, %d bytes of data\n", rc, cbHostText));
1026 g_ctx.hostFormats = 0;
1027 LogFlowFunc(("rc = false\n"));
1028 return false;
1029 }
1030 LogFlow(("vboxClipboardConvertCText: original text is %.*ls\n", cwHostText, pu16HostText));
1031 cwHostText = cbHostText / 2;
1032 cwGuestText = vboxClipboardUtf16GetLinSize(pu16HostText, cwHostText);
1033 pu16GuestText = reinterpret_cast<PRTUTF16>(RTMemAlloc(cwGuestText * 2));
1034 if (pu16GuestText == 0)
1035 {
1036 Log(("vboxClipboardConvertCText: out of memory\n"));
1037 RTMemFree(reinterpret_cast<char *>(pu16HostText));
1038 LogFlowFunc(("rc = false\n"));
1039 return false;
1040 }
1041 rc = vboxClipboardUtf16WinToLin(pu16HostText, cwHostText, pu16GuestText, cwGuestText);
1042 RTMemFree(reinterpret_cast<char *>(pu16HostText));
1043 if (rc != VINF_SUCCESS)
1044 {
1045 LogFlow(("vboxClipboardConvertUtf16: vboxClipboardUtf16WinToLin returned %Rrc\n", rc));
1046 RTMemFree(reinterpret_cast<char *>(pu16GuestText));
1047 LogFlowFunc(("rc = false\n"));
1048 return false;
1049 }
1050 /* Now convert the Utf16 Linux text to Utf8 */
1051 cbUtf8Text = cwGuestText * 3; /* Should always be enough. */
1052 pcUtf8Text = reinterpret_cast<char *>(RTMemAlloc(cbUtf8Text));
1053 if (pcUtf8Text == 0)
1054 {
1055 Log(("vboxClipboardConvertCText: out of memory\n"));
1056 RTMemFree(reinterpret_cast<char *>(pu16GuestText));
1057 LogFlowFunc(("rc = false\n"));
1058 return false;
1059 }
1060 /* Our runtime can't cope with endian markers. */
1061 rc = RTUtf16ToUtf8Ex(pu16GuestText + 1, cwGuestText - 1, &pcUtf8Text, cbUtf8Text, 0);
1062 RTMemFree(reinterpret_cast<char *>(pu16GuestText));
1063 if (rc != VINF_SUCCESS)
1064 {
1065 Log(("vboxClipboardConvertCText: RTUtf16ToUtf8Ex failed: rc=%Rrc\n", rc));
1066 XtFree(pcUtf8Text);
1067 LogFlowFunc(("rc = false\n"));
1068 return false;
1069 }
1070 /* And finally (!) convert the Utf8 text to compound text. */
1071#ifdef RT_OS_SOLARIS
1072 rc = XmbTextListToTextProperty(XtDisplay(g_ctx.widget), &pcUtf8Text, 1,
1073 XCompoundTextStyle, &property);
1074#else
1075 rc = Xutf8TextListToTextProperty(XtDisplay(g_ctx.widget), &pcUtf8Text, 1,
1076 XCompoundTextStyle, &property);
1077#endif
1078 RTMemFree(pcUtf8Text);
1079 if (rc < 0)
1080 {
1081 const char *pcReason;
1082 switch(rc)
1083 {
1084 case XNoMemory:
1085 pcReason = "out of memory";
1086 break;
1087 case XLocaleNotSupported:
1088 pcReason = "locale (Utf8) not supported";
1089 break;
1090 case XConverterNotFound:
1091 pcReason = "converter not found";
1092 break;
1093 default:
1094 pcReason = "unknown error";
1095 }
1096 LogRel(("vboxClipboardConvertCText: Xutf8TextListToTextProperty failed. Reason: %s\n",
1097 pcReason));
1098 LogFlowFunc(("rc = false\n"));
1099 return false;
1100 }
1101 LogFlow(("vboxClipboardConvertCText: converted text is %s\n", property.value));
1102 *atomTypeReturn = property.encoding;
1103 *pValReturn = reinterpret_cast<XtPointer>(property.value);
1104 *pcLenReturn = property.nitems;
1105 *piFormatReturn = property.format;
1106 LogFlowFunc(("rc = true\n"));
1107 return true;
1108}
1109
1110
1111/**
1112 * Callback to convert the hosts clipboard data for an application on the guest. Called by the
1113 * X Toolkit.
1114 * @returns true if we successfully convert the data to the format requested, false otherwise.
1115 *
1116 * @param atomSelection The selection which is being requested. We only handle the clipboard.
1117 * @param atomTarget The format we should convert the data to
1118 * @param atomTypeReturn The type of the data we are returning
1119 * @param pValReturn A pointer to the data we are returning. This should be to memory
1120 * allocated by XtMalloc, which will be freed by the toolkit later
1121 * @param pcLenReturn The length of the data we are returning
1122 * @param piFormatReturn The format (8bit, 16bit, 32bit) of the data we are returning
1123 */
1124static Boolean vboxClipboardConvertProc(Widget, Atom *atomSelection, Atom *atomTarget,
1125 Atom *atomTypeReturn, XtPointer *pValReturn,
1126 unsigned long *pcLenReturn, int *piFormatReturn)
1127{
1128 g_eClipboardFormat eFormat = INVALID;
1129 int rc;
1130
1131 LogFlowFunc(("\n"));
1132 if ( (*atomSelection != g_ctx.atomClipboard)
1133 && (*atomSelection != g_ctx.atomPrimary)
1134 )
1135 {
1136 LogFlowFunc(("rc = false\n"));
1137 return false;
1138 }
1139#ifdef DEBUG
1140 char *szAtomName = XGetAtomName(XtDisplay(g_ctx.widget), *atomTarget);
1141 if (szAtomName != 0)
1142 {
1143 LogFlow(("vboxClipboardConvertProc: request for format %s\n", szAtomName));
1144 XFree(szAtomName);
1145 }
1146 else
1147 Log(("vboxClipboardConvertProc: request for invalid target atom %d!\n", *atomTarget));
1148#endif
1149 if (*atomTarget == g_ctx.atomTargets)
1150 eFormat = TARGETS;
1151 else
1152 {
1153 for (unsigned i = 0; i != g_ctx.formatList.size(); ++i)
1154 {
1155 if (g_ctx.formatList[i].atom == *atomTarget)
1156 {
1157 eFormat = g_ctx.formatList[i].format;
1158 break;
1159 }
1160 }
1161 }
1162 switch (eFormat)
1163 {
1164 case TARGETS:
1165 rc = vboxClipboardConvertTargets(atomTypeReturn, pValReturn, pcLenReturn, piFormatReturn);
1166 LogFlowFunc(("TARGETS rc=%d\n", rc));
1167 return rc;
1168 case UTF16:
1169 rc = vboxClipboardConvertUtf16(atomTypeReturn, pValReturn, pcLenReturn, piFormatReturn);
1170 LogFlowFunc(("UTF16 rc=%d\n", rc));
1171 return rc;
1172 case UTF8:
1173 rc = vboxClipboardConvertUtf8(atomTypeReturn, pValReturn, pcLenReturn, piFormatReturn);
1174 LogFlowFunc(("UTF8 rc=%d\n", rc));
1175 return rc;
1176 case CTEXT:
1177 rc = vboxClipboardConvertCText(atomTypeReturn, pValReturn, pcLenReturn, piFormatReturn);
1178 LogFlowFunc(("CTEXT rc=%d\n", rc));
1179 return rc;
1180 default:
1181 Log(("vboxClipboardConvertProc: bad format\n"));
1182 LogFlowFunc(("rc = false\n"));
1183 return false;
1184 }
1185}
1186
1187
1188static void vboxClipboardLoseProc(Widget, Atom *)
1189{
1190 LogFlowFunc(("giving the guest clipboard ownership\n"));
1191 g_ctx.eOwner = GUEST;
1192 g_ctx.notifyHost = true;
1193 LogFlowFunc(("returning\n"));
1194}
1195
1196
1197/**
1198 * The host is taking possession of the shared clipboard. Called by the HGCM clipboard
1199 * subsystem.
1200 *
1201 * @param u32Formats Clipboard formats the guest is offering
1202 */
1203void vboxClipboardFormatAnnounce (uint32_t u32Formats)
1204{
1205 g_ctx.hostFormats = u32Formats;
1206 LogFlowFunc(("u32Formats = %d\n", u32Formats));
1207 if (u32Formats == 0)
1208 {
1209 /* This is just an automatism, not a genuine announcement */
1210 LogFlowFunc(("returning\n"));
1211 return;
1212 }
1213 LogFlow(("vboxClipboardFormatAnnounce: giving the host clipboard ownership\n"));
1214 g_ctx.eOwner = HOST;
1215 g_ctx.guestTextFormat = INVALID;
1216 g_ctx.guestBitmapFormat = INVALID;
1217 if (XtOwnSelection(g_ctx.widget, g_ctx.atomClipboard, CurrentTime,
1218 vboxClipboardConvertProc, vboxClipboardLoseProc, 0)
1219 == True)
1220 XtOwnSelection(g_ctx.widget, g_ctx.atomPrimary, CurrentTime,
1221 vboxClipboardConvertProc, NULL, 0);
1222 else
1223 {
1224 LogFlow(("vboxClipboardFormatAnnounce: returning clipboard ownership to the guest\n"));
1225 g_ctx.notifyHost = true;
1226 g_ctx.eOwner = GUEST;
1227 }
1228 LogFlowFunc(("returning\n"));
1229}
1230
1231
1232/**
1233 * Called when the host wants to read the guest clipboard.
1234 *
1235 * @param u32Format The format that the host would like to receive the data in
1236 */
1237int vboxClipboardReadGuestData (uint32_t u32Format)
1238{
1239 LogFlowFunc(("u32Format = %d\n", u32Format));
1240
1241 /*
1242 * The guest wants to read data in the given format.
1243 */
1244 if (u32Format & VBOX_SHARED_CLIPBOARD_FMT_UNICODETEXT)
1245 {
1246 if (g_ctx.guestTextFormat == INVALID)
1247 {
1248 /* No data available. */
1249 vboxClipboardSendData(0, NULL, 0);
1250 LogFlowFunc(("sent empty data, returned VINF_SUCCESS\n"));
1251 return VINF_SUCCESS;
1252 }
1253 g_ctx.requestGuestFormat = g_ctx.guestTextFormat;
1254 /* Send out a request for the data to the current clipboard owner */
1255 XtGetSelectionValue(g_ctx.widget, g_ctx.atomClipboard, g_ctx.atomGuestTextFormat,
1256 vboxClipboardGetProc, 0, CurrentTime);
1257 /* When the data arrives, the vboxClipboardGetProc callback will be called. The
1258 callback will signal the event semaphore when it has processed the data for us. */
1259 }
1260 else
1261 {
1262 vboxClipboardSendData(0, NULL, 0);
1263 LogFlowFunc(("sent empty data, returned VERR_NOT_IMPLEMENTED\n"));
1264 return VERR_NOT_IMPLEMENTED;
1265 }
1266 LogFlowFunc(("returned VINF_SUCCESS\n"));
1267 return VINF_SUCCESS;
1268}
1269
1270
1271/** Terminate the guest side of the shared clipboard. */
1272void vboxClipboardDestroy (void)
1273{
1274 LogFlowFunc(("\n"));
1275
1276 /* Set the termination flag. */
1277 XtAppSetExitFlag(g_ctx.appContext);
1278 LogFlowFunc(("returning\n"));
1279}
1280
1281
1282/**
1283 * The main loop of our clipboard reader.
1284 */
1285static int vboxClipboardThread(RTTHREAD /* ThreadSelf */, void * /* pvUser */)
1286{
1287 int rc;
1288 LogFlowFunc(("Starting clipboard thread\n"));
1289
1290 for (;;)
1291 {
1292 uint32_t Msg;
1293 uint32_t fFormats;
1294 rc = VbglR3ClipboardGetHostMsg(g_ctx.client, &Msg, &fFormats);
1295 if (RT_SUCCESS(rc))
1296 {
1297 switch (Msg)
1298 {
1299 case VBOX_SHARED_CLIPBOARD_HOST_MSG_FORMATS:
1300 {
1301 /* The host has announced available clipboard formats.
1302 * Save the information so that it is available for
1303 * future requests from guest applications.
1304 */
1305 LogFlowFunc(("VBOX_SHARED_CLIPBOARD_HOST_MSG_FORMATS fFormats=%x\n", fFormats));
1306 vboxClipboardFormatAnnounce(fFormats);
1307 break;
1308 }
1309
1310 case VBOX_SHARED_CLIPBOARD_HOST_MSG_READ_DATA:
1311 {
1312 /* The host needs data in the specified format. */
1313 LogFlowFunc(("VBOX_SHARED_CLIPBOARD_HOST_MSG_READ_DATA fFormats=%x\n", fFormats));
1314 vboxClipboardReadGuestData(fFormats);
1315 break;
1316 }
1317
1318 case VBOX_SHARED_CLIPBOARD_HOST_MSG_QUIT:
1319 {
1320 /* The host is terminating. */
1321 LogFlowFunc(("VBOX_SHARED_CLIPBOARD_HOST_MSG_QUIT\n"));
1322 vboxClipboardDestroy();
1323 rc = VERR_INTERRUPTED;
1324 break;
1325 }
1326
1327 default:
1328 Log(("Unsupported message from host!!!\n"));
1329 }
1330 }
1331 if (rc == VERR_INTERRUPTED)
1332 {
1333 /* Wait for termination event. */
1334 RTSemEventWait(g_ctx.terminating, RT_INDEFINITE_WAIT);
1335 break;
1336 }
1337 if (RT_FAILURE(rc))
1338 {
1339 /* Wait a bit before retrying. */
1340 if (RTSemEventWait(g_ctx.terminating, 1000) == VINF_SUCCESS)
1341 break;
1342 continue;
1343 }
1344
1345 LogFlow(("processed host event rc = %d\n", rc));
1346 }
1347 LogFlowFunc(("rc=%d\n", rc));
1348 return rc;
1349}
1350
1351
1352/**
1353 * Disconnect the guest clipboard from the host.
1354 */
1355void vboxClipboardDisconnect(void)
1356{
1357#if 0
1358 VMMDevHGCMDisconnect request;
1359#endif
1360 LogFlowFunc(("\n"));
1361
1362 AssertReturn(g_ctx.client != 0, (void) 0);
1363 VbglR3ClipboardDisconnect(g_ctx.client);
1364 LogFlowFunc(("returning\n"));
1365}
1366
1367/**
1368 * Connect the guest clipboard to the host.
1369 *
1370 * @returns VBox status code
1371 */
1372int vboxClipboardConnect(void)
1373{
1374 int rc;
1375 LogFlowFunc(("\n"));
1376
1377 /* Only one client is supported for now */
1378 AssertReturn(g_ctx.client == 0, VERR_NOT_SUPPORTED);
1379
1380 /* Initialise the termination semaphore. */
1381 RTSemEventCreate(&g_ctx.terminating);
1382
1383 /* Initialise threading in X11 and in Xt. */
1384 if (!XInitThreads() || !XtToolkitThreadInitialize())
1385 {
1386 LogRel(("VBoxClient: error initialising threads in X11, exiting.\n"));
1387 return VERR_NOT_SUPPORTED;
1388 }
1389
1390 rc = VbglR3ClipboardConnect(&g_ctx.client);
1391 if (RT_FAILURE(rc))
1392 {
1393 LogRel(("Error connecting to host. rc=%Rrc\n", rc));
1394 return rc;
1395 }
1396 if (!g_ctx.client)
1397 {
1398 LogRel(("Invalid client ID of 0\n"));
1399 return VERR_NOT_SUPPORTED;
1400 }
1401 /* Assume that if the guest clipboard already contains data then the
1402 * user put it there for a reason. */
1403 g_ctx.eOwner = GUEST;
1404 LogFlowFunc(("g_ctx.client=%u rc=%Rrc\n", g_ctx.client, rc));
1405 return rc;
1406}
1407
1408
1409/** We store information about the target formats we can handle in a global vector for internal
1410 use. */
1411static void vboxClipboardAddFormat(const char *pszName, g_eClipboardFormat eFormat, unsigned hostFormat)
1412{
1413 VBOXCLIPBOARDFORMAT sFormat;
1414 LogFlowFunc(("pszName=%s, eFormat=%d, hostFormat=%u\n", pszName, eFormat, hostFormat));
1415 /* Get an atom from the X server for that target format */
1416 Atom atomFormat = XInternAtom(XtDisplay(g_ctx.widget), pszName, false);
1417 LogFlow(("vboxClipboardAddFormat: atomFormat=%d\n", atomFormat));
1418 if (atomFormat == 0)
1419 {
1420 LogFlowFunc(("atomFormat=0! returning...\n"));
1421 return;
1422 }
1423 sFormat.atom = atomFormat;
1424 sFormat.format = eFormat;
1425 sFormat.hostFormat = hostFormat;
1426 g_ctx.formatList.push_back(sFormat);
1427 LogFlowFunc(("returning\n"));
1428}
1429
1430
1431/** Create the X11 window which we use to interact with the guest clipboard */
1432static int vboxClipboardCreateWindow(void)
1433{
1434 /* Create a window and make it a clipboard viewer. */
1435 int cArgc = 0;
1436 char *pcArgv = 0;
1437 String szFallbackResources[] = { (char*)"*.width: 1", (char*)"*.height: 1", 0 };
1438
1439 /* Set up the Clipbard application context and main window. */
1440 g_ctx.widget = XtOpenApplication(&g_ctx.appContext, "VBoxClipboard", 0, 0, &cArgc, &pcArgv,
1441 szFallbackResources, applicationShellWidgetClass, 0, 0);
1442 AssertMsgReturn(g_ctx.widget != 0, ("Failed to construct the X clipboard window\n"),
1443 VERR_ACCESS_DENIED);
1444 XtSetMappedWhenManaged(g_ctx.widget, false);
1445 XtRealizeWidget(g_ctx.widget);
1446
1447 /* Get hold of the atoms which we need */
1448 g_ctx.atomClipboard = XInternAtom(XtDisplay(g_ctx.widget), "CLIPBOARD", false /* only_if_exists */);
1449 g_ctx.atomPrimary = XInternAtom(XtDisplay(g_ctx.widget), "PRIMARY", false);
1450 g_ctx.atomTargets = XInternAtom(XtDisplay(g_ctx.widget), "TARGETS", false);
1451 g_ctx.atomMultiple = XInternAtom(XtDisplay(g_ctx.widget), "MULTIPLE", false);
1452 g_ctx.atomTimestamp = XInternAtom(XtDisplay(g_ctx.widget), "TIMESTAMP", false);
1453 g_ctx.atomUtf16 = XInternAtom(XtDisplay(g_ctx.widget),
1454 "text/plain;charset=ISO-10646-UCS-2", false);
1455 g_ctx.atomUtf8 = XInternAtom(XtDisplay(g_ctx.widget), "UTF_STRING", false);
1456 g_ctx.atomCText = XInternAtom(XtDisplay(g_ctx.widget), "COMPOUND_TEXT", false);
1457 /* And build up the vector of supported formats */
1458#ifdef USE_UTF16
1459 vboxClipboardAddFormat("text/plain;charset=ISO-10646-UCS-2", UTF16, VBOX_SHARED_CLIPBOARD_FMT_UNICODETEXT);
1460#endif
1461#ifdef USE_UTF8
1462 vboxClipboardAddFormat("UTF8_STRING", UTF8, VBOX_SHARED_CLIPBOARD_FMT_UNICODETEXT);
1463 vboxClipboardAddFormat("text/plain;charset=UTF-8", UTF8, VBOX_SHARED_CLIPBOARD_FMT_UNICODETEXT);
1464 vboxClipboardAddFormat("text/plain;charset=utf-8", UTF8, VBOX_SHARED_CLIPBOARD_FMT_UNICODETEXT);
1465 vboxClipboardAddFormat("STRING", UTF8, VBOX_SHARED_CLIPBOARD_FMT_UNICODETEXT);
1466 vboxClipboardAddFormat("TEXT", UTF8, VBOX_SHARED_CLIPBOARD_FMT_UNICODETEXT);
1467 vboxClipboardAddFormat("text/plain", UTF8, VBOX_SHARED_CLIPBOARD_FMT_UNICODETEXT);
1468#endif
1469#ifdef USE_CTEXT
1470 vboxClipboardAddFormat("COMPOUND_TEXT", CTEXT, VBOX_SHARED_CLIPBOARD_FMT_UNICODETEXT);
1471#endif
1472 return VINF_SUCCESS;
1473}
1474
1475/** Initialise the guest side of the shared clipboard. */
1476int vboxClipboardMain(void)
1477{
1478 int rc;
1479 LogFlowFunc(("\n"));
1480
1481 rc = vboxClipboardCreateWindow();
1482 if (RT_FAILURE(rc))
1483 return rc;
1484
1485 rc = RTThreadCreate(&g_ctx.thread, vboxClipboardThread, 0, 0, RTTHREADTYPE_IO,
1486 RTTHREADFLAGS_WAITABLE, "SHCLIP");
1487 AssertRCReturn(rc, rc);
1488 /* Set up a timer to poll the host clipboard */
1489 XtAppAddTimeOut(g_ctx.appContext, 200 /* ms */, vboxClipboardTimerProc, 0);
1490
1491 XtAppMainLoop(g_ctx.appContext);
1492 g_ctx.formatList.clear();
1493 XtDestroyApplicationContext(g_ctx.appContext);
1494 /* Set the termination signal. */
1495 RTSemEventSignal(g_ctx.terminating);
1496 LogFlowFunc(("returning %d\n", rc));
1497 return rc;
1498}
1499
1500class ClipboardService : public VBoxClient::Service
1501{
1502public:
1503 virtual const char *getPidFilePath()
1504 {
1505 return ".vboxclient-clipboard.pid";
1506 }
1507 virtual int run()
1508 {
1509 int rc = vboxClipboardConnect();
1510 if (RT_SUCCESS(rc))
1511 rc = vboxClipboardMain();
1512 return rc;
1513 }
1514 virtual void cleanup()
1515 {
1516 /* Nothing to do. */
1517 }
1518};
1519
1520VBoxClient::Service *VBoxClient::GetClipboardService()
1521{
1522 return new ClipboardService;
1523}
Note: See TracBrowser for help on using the repository browser.

© 2025 Oracle Support Privacy / Do Not Sell My Info Terms of Use Trademark Policy Automated Access Etiquette