VirtualBox

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

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

Only while LOG_TO_BACKDOOR is defined for ring-3 guest apps.

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