VirtualBox

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

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

Additions/x11: removed accidentally added log statements

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