VirtualBox

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

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

Additions/x11: added flow logging statements to the VBoxClient code

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

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