VirtualBox

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

Last change on this file since 8237 was 8155, checked in by vboxsync, 17 years ago

The Big Sun Rebranding Header Change

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