VirtualBox

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

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

Additions/x11/VBoxClient: run a separate process for each service and create pidfiles to ensure uniqueness

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