VirtualBox

source: vbox/trunk/src/VBox/HostServices/SharedClipboard/x11.cpp@ 17822

Last change on this file since 17822 was 17004, checked in by vboxsync, 16 years ago

Additions and HostServices/SharedClipboard: removed an XFree() call when the allocating function failed

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 61.1 KB
Line 
1/** @file
2 *
3 * Shared Clipboard:
4 * Linux host.
5 */
6
7/*
8 * Copyright (C) 2006-2007 Sun Microsystems, Inc.
9 *
10 * This file is part of VirtualBox Open Source Edition (OSE), as
11 * available from http://www.virtualbox.org. This file is free software;
12 * you can redistribute it and/or modify it under the terms of the GNU
13 * General Public License (GPL) as published by the Free Software
14 * Foundation, in version 2 as it comes in the "COPYING" file of the
15 * VirtualBox OSE distribution. VirtualBox OSE is distributed in the
16 * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
17 *
18 * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa
19 * Clara, CA 95054 USA or visit http://www.sun.com if you need
20 * additional information or have any questions.
21 */
22
23#include <vector>
24
25#include <VBox/HostServices/VBoxClipboardSvc.h>
26
27#include <iprt/alloc.h>
28#include <iprt/asm.h> /* For atomic operations */
29#include <iprt/assert.h>
30#include <iprt/env.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#include <string.h>
37#include <stdio.h>
38#include <stdint.h>
39
40#include "VBoxClipboard.h"
41#include "clipboard-helper.h"
42
43#include <X11/Xlib.h>
44#include <X11/Xatom.h>
45#include <X11/Intrinsic.h>
46#include <X11/Shell.h>
47#include <X11/Xproto.h>
48#include <X11/StringDefs.h>
49
50#ifdef RT_OS_SOLARIS
51#include <tsol/label.h>
52#endif
53
54/** Do we want to test Utf16 by disabling other text formats? */
55static bool g_testUtf16 = false;
56/** Do we want to test Utf8 by disabling other text formats? */
57static bool g_testUtf8 = false;
58/** Do we want to test compount text by disabling other text formats? */
59static bool g_testCText = false;
60/** Are we currently debugging the clipboard code? */
61static bool g_debugClipboard = false;
62
63/** The different clipboard formats which we support. */
64enum g_eClipboardFormats
65{
66 INVALID = 0,
67 TARGETS,
68 CTEXT,
69 UTF8,
70 UTF16
71};
72
73/** The X11 clipboard uses several names for the same format. This structure maps an X11
74 name to a format. */
75typedef struct {
76 Atom atom;
77 g_eClipboardFormats format;
78 unsigned guestFormat;
79} VBOXCLIPBOARDFORMAT;
80
81/** Does X11 or VBox currently own the clipboard? */
82enum g_eOwner { NONE = 0, X11, VB };
83
84typedef struct {
85 /** BMP file type marker - must always contain 'BM' */
86 uint16_t bfType;
87 /** The size of the BMP file in bytes (the MS docs say that this is not reliable) */
88 uint32_t bfSize;
89 /** Reserved, must always be zero */
90 uint16_t bfReserved1;
91 /** Reserved, must always be zero */
92 uint16_t bfReserved2;
93 /** Offset from the beginning of this header to the actual image bits */
94} VBOXBITMAPFILEHEADER;
95
96/** Global clipboard context information */
97struct _VBOXCLIPBOARDCONTEXT
98{
99 /** The X Toolkit application context structure */
100 XtAppContext appContext;
101
102 /** We have a separate thread to wait for Window and Clipboard events */
103 RTTHREAD thread;
104 /** The X Toolkit widget which we use as our clipboard client. It is never made visible. */
105 Widget widget;
106
107 /** X11 atom refering to the clipboard: CLIPBOARD */
108 Atom atomClipboard;
109 /** X11 atom refering to the selection: PRIMARY */
110 Atom atomPrimary;
111 /** X11 atom refering to the clipboard targets: TARGETS */
112 Atom atomTargets;
113 /** X11 atom refering to the clipboard multiple target: MULTIPLE */
114 Atom atomMultiple;
115 /** X11 atom refering to the clipboard timestamp target: TIMESTAMP */
116 Atom atomTimestamp;
117 /** X11 atom refering to the clipboard utf16 text format: text/plain;charset=ISO-10646-UCS-2 */
118 Atom atomUtf16;
119 /** X11 atom refering to the clipboard utf8 text format: UTF8_STRING */
120 Atom atomUtf8;
121 /** X11 atom refering to the clipboard compound text format: COMPOUND_TEXT */
122 Atom atomCText;
123
124 /** A list of the X11 formats which we support, mapped to our identifier for them, in the
125 order we prefer to have them in. */
126 std::vector<VBOXCLIPBOARDFORMAT> formatList;
127
128 /** Does the host or the guest currently own the clipboard? */
129 volatile enum g_eOwner eOwner;
130
131 /** What is the best text format the host has to offer? INVALID for none. */
132 g_eClipboardFormats hostTextFormat;
133 /** Atom corresponding to the host text format */
134 Atom atomHostTextFormat;
135 /** What is the best bitmap format the host has to offer? INVALID for none. */
136 g_eClipboardFormats hostBitmapFormat;
137 /** Atom corresponding to the host Bitmap format */
138 Atom atomHostBitmapFormat;
139 /** What formats does the guest have on offer? */
140 int guestFormats;
141 /** Windows caches the clipboard data it receives. Since we have no way of knowing whether
142 that data is still valid, we always send a "data changed" message after a successful
143 transfer to invalidate the cache. */
144 bool notifyGuest;
145
146 /** Since the clipboard data moves asynchronously, we use an event semaphore to wait for
147 it. When a function issues a request for clipboard data it must wait for this
148 semaphore, which is triggered when the data arrives. */
149 RTSEMEVENT waitForData;
150 /** Who (if anyone) is currently waiting for data? Used for sanity checks
151 * when data arrives. */
152 volatile uint32_t waiter;
153 /** This mutex is grabbed during any critical operations on the clipboard
154 * which might clash with others. */
155 RTSEMMUTEX clipboardMutex;
156
157 /** Format which we are reading from the host clipboard (valid during a request for the
158 host clipboard) */
159 g_eClipboardFormats requestHostFormat;
160 /** The guest buffer to write host clipboard data to (valid during a request for the host
161 clipboard) */
162 void *requestBuffer;
163 /** The size of the guest buffer to write host clipboard data to (valid during a request for
164 the host clipboard) */
165 unsigned requestBufferSize;
166 /** The size of the host clipboard data written to the guest buffer (valid during a request
167 for the host clipboard) */
168 uint32_t *requestActualSize;
169
170 /** Pointer to the client data structure */
171 VBOXCLIPBOARDCLIENTDATA *pClient;
172};
173
174/* Only one client is supported. There seems to be no need for more clients. */
175static VBOXCLIPBOARDCONTEXT g_ctx;
176
177/* Are we actually connected to the X11 servicer? */
178static bool g_fHaveX11;
179
180/**
181 * Reset the contents of the buffer used to pass clipboard data from VBox to X11.
182 * This must be done after every clipboard transfer.
183 */
184static void vboxClipboardEmptyGuestBuffer(void)
185{
186 if (g_ctx.pClient->data.pv != 0)
187 RTMemFree(g_ctx.pClient->data.pv);
188 g_ctx.pClient->data.pv = 0;
189 g_ctx.pClient->data.cb = 0;
190 g_ctx.pClient->data.u32Format = 0;
191}
192
193/**
194 * Send a request to VBox to transfer the contents of its clipboard to X11.
195 *
196 * @param pCtx Pointer to the host clipboard structure
197 * @param u32Format The format in which the data should be transfered
198 * @thread clipboard X11 event thread
199 * @note called by vboxClipboardConvert*
200 */
201static int vboxClipboardReadDataFromVBox (VBOXCLIPBOARDCONTEXT *pCtx, uint32_t u32Format)
202{
203 volatile VBOXCLIPBOARDCLIENTDATA *pClient = pCtx->pClient;
204
205 LogFlowFunc(("u32Format=%02X\n", u32Format));
206 if (pClient == NULL)
207 {
208 /* This can legitimately happen if we disconnect during a request for
209 * data from X11. */
210 LogFunc(("host requested guest clipboard data after guest had disconnected.\n"));
211 pCtx->guestFormats = 0;
212 pCtx->waiter = NONE;
213 return VERR_TIMEOUT;
214 }
215 /* Assert that no other transfer is in process (requests are serialised)
216 * and that the last transfer cleaned up properly. */
217 AssertLogRelReturn( pClient->data.pv == NULL
218 && pClient->data.cb == 0
219 && pClient->data.u32Format == 0,
220 VERR_WRONG_ORDER
221 );
222 /* No one else (X11 or VBox) should currently be waiting. The first because
223 * requests from X11 are serialised and the second because VBox previously
224 * grabbed the clipboard, so it should not be waiting for data from us. */
225 AssertLogRelReturn (ASMAtomicCmpXchgU32(&pCtx->waiter, X11, NONE), VERR_DEADLOCK);
226 /* Request data from VBox */
227 vboxSvcClipboardReportMsg(pCtx->pClient,
228 VBOX_SHARED_CLIPBOARD_HOST_MSG_READ_DATA,
229 u32Format);
230 /* Which will signal us when it is ready. We use a timeout here because
231 * we can't be sure that the guest will behave correctly. */
232 int rc = RTSemEventWait(pCtx->waitForData, CLIPBOARDTIMEOUT);
233 if (rc == VERR_TIMEOUT)
234 rc = VINF_SUCCESS; /* Timeout handling follows. */
235 /* Now we have a potential race between the HGCM thread delivering the data
236 * and our setting waiter to NONE to say that we are no longer waiting for
237 * it. We solve this as follows: both of these operations are done under
238 * the clipboard mutex. The HGCM thread will only deliver the data if we
239 * are still waiting after it acquires the mutex. After we release the
240 * mutex, we finally do our check to see whether the data was delivered. */
241 RTSemMutexRequest(g_ctx.clipboardMutex, RT_INDEFINITE_WAIT);
242 pCtx->waiter = NONE;
243 RTSemMutexRelease(g_ctx.clipboardMutex);
244 AssertLogRelRCSuccess(rc);
245 if (RT_FAILURE(rc))
246 {
247 /* I believe this should not happen. Wait until the assertions arrive
248 * to prove the contrary. */
249 vboxClipboardEmptyGuestBuffer();
250 pCtx->guestFormats = 0;
251 return rc;
252 }
253 if (pClient->data.pv == NULL)
254 return VERR_TIMEOUT;
255 LogFlowFunc(("wait completed. Returning.\n"));
256 return VINF_SUCCESS;
257}
258
259/**
260 * Convert the UTF-16 text returned from the X11 clipboard to UTF-16LE with Windows EOLs
261 * and place it in the global g_pcClipboardText variable. We are reading the host clipboard to
262 * make it available to the guest.
263 *
264 * @param pValue Source UTF-16 text
265 * @param cwSourceLen Length in 16-bit words of the source text
266 * @param pv Where to store the converted data
267 * @param cb Length in bytes of the buffer pointed to by cb
268 * @param pcbActual Where to store the size of the converted data
269 * @param pClient Pointer to the client context structure
270 */
271static void vboxClipboardGetUtf16(XtPointer pValue, unsigned cwSrcLen, void *pv, unsigned cb,
272 uint32_t *pcbActual)
273{
274 size_t cwDestLen;
275 PRTUTF16 pu16SrcText = reinterpret_cast<PRTUTF16>(pValue);
276 PRTUTF16 pu16DestText = reinterpret_cast<PRTUTF16>(pv);
277
278 LogFlowFunc (("converting Utf-16 to Utf-16LE. cwSrcLen=%d, cb=%d, pu16SrcText+1=%.*ls\n",
279 cwSrcLen, cb, cwSrcLen - 1, pu16SrcText + 1));
280 *pcbActual = 0; /* Only set this to the right value on success. */
281 /* How long will the converted text be? */
282 int rc = vboxClipboardUtf16GetWinSize(pu16SrcText, cwSrcLen, &cwDestLen);
283 if (RT_SUCCESS(rc) && (cb < cwDestLen * 2))
284 {
285 /* Not enough buffer space provided - report the amount needed. */
286 LogFlowFunc (("guest buffer too small: size %d bytes, needed %d. Returning.\n",
287 cb, cwDestLen * 2));
288 *pcbActual = cwDestLen * 2;
289 rc = VERR_BUFFER_OVERFLOW;
290 }
291 /* Convert the text. */
292 if (RT_SUCCESS(rc))
293 rc = vboxClipboardUtf16LinToWin(pu16SrcText, cwSrcLen, pu16DestText, cb / 2);
294 if (RT_SUCCESS(rc))
295 {
296 LogFlowFunc (("converted string is %.*ls\n", cwDestLen, pu16DestText));
297 *pcbActual = cwDestLen * 2;
298 }
299 /* We need to do this whether we succeed or fail. */
300 XtFree(reinterpret_cast<char *>(pValue));
301 RTSemEventSignal(g_ctx.waitForData);
302 LogFlowFunc(("Returning. Status is %Rrc\n", rc));
303}
304
305/**
306 * Convert the UTF-8 text returned from the X11 clipboard to UTF-16LE with Windows EOLS.
307 * We are reading the X11 clipboard to make it available to VBox.
308 *
309 * @param pValue Source UTF-8 text
310 * @param cbSourceLen Length in 8-bit bytes of the source text
311 * @param pv Where to store the converted data
312 * @param cb Length in bytes of the buffer pointed to by pv
313 * @param pcbActual Where to store the size of the converted data
314 * @param pClient Pointer to the client context structure
315 * @thread clipboard X11 event thread
316 * @note called by vboxClipboardGetDataFromX11
317 */
318static void vboxClipboardGetUtf8FromX11(XtPointer pValue, unsigned cbSrcLen,
319 void *pv, unsigned cb,
320 uint32_t *pcbActual)
321{
322 size_t cwSrcLen, cwDestLen;
323 char *pu8SrcText = reinterpret_cast<char *>(pValue);
324 PRTUTF16 pu16SrcText = NULL;
325 PRTUTF16 pu16DestText = reinterpret_cast<PRTUTF16>(pv);
326
327 LogFlowFunc (("converting Utf-8 to Utf-16LE. cbSrcLen=%d, cb=%d, pu8SrcText=%.*s\n",
328 cbSrcLen, cb, cbSrcLen, pu8SrcText));
329 *pcbActual = 0; /* Only set this to the right value on success. */
330 /* First convert the UTF8 to UTF16 */
331 int rc = RTStrToUtf16Ex(pu8SrcText, cbSrcLen, &pu16SrcText, 0, &cwSrcLen);
332 /* Check how much longer will the converted text will be. */
333 if (RT_SUCCESS(rc))
334 rc = vboxClipboardUtf16GetWinSize(pu16SrcText, cwSrcLen, &cwDestLen);
335 if (RT_SUCCESS(rc) && (cb < cwDestLen * 2))
336 {
337 /* Not enough buffer space provided - report the amount needed. */
338 LogFlowFunc (("guest buffer too small: size %d bytes, needed %d. Returning.\n",
339 cb, cwDestLen * 2));
340 *pcbActual = cwDestLen * 2;
341 rc = VERR_BUFFER_OVERFLOW;
342 }
343 /* Convert the text. */
344 if (RT_SUCCESS(rc))
345 rc = vboxClipboardUtf16LinToWin(pu16SrcText, cwSrcLen, pu16DestText, cb / 2);
346 if (RT_SUCCESS(rc))
347 {
348 LogFlowFunc (("converted string is %.*ls.\n", cwDestLen, pu16DestText));
349 *pcbActual = cwDestLen * 2;
350 }
351 XtFree(reinterpret_cast<char *>(pValue));
352 RTUtf16Free(pu16SrcText);
353 RTSemEventSignal(g_ctx.waitForData);
354 LogFlowFunc(("Returning. Status is %Rrc", rc));
355}
356
357/**
358 * Convert the COMPOUND_TEXT text returned from the X11 clipboard to UTF-16LE with Windows
359 * EOLS. We are reading the X11 clipboard to make it available to VBox.
360 *
361 * @param pValue Source COMPOUND_TEXT text
362 * @param cbSourceLen Length in 8-bit bytes of the source text
363 * @param pv Where to store the converted data
364 * @param cb Length in bytes of the buffer pointed to by pv
365 * @param pcbActual Where to store the size of the converted data
366 * @param pClient Pointer to the client context structure
367 * @thread clipboard X11 event thread
368 * @note called by vboxClipboardGetDataFromX11
369 */
370static void vboxClipboardGetCTextFromX11(XtPointer pValue, unsigned cbSrcLen,
371 void *pv, unsigned cb,
372 uint32_t *pcbActual)
373{
374 size_t cwSrcLen, cwDestLen;
375 char **ppu8SrcText = NULL;
376 PRTUTF16 pu16SrcText = NULL;
377 PRTUTF16 pu16DestText = reinterpret_cast<PRTUTF16>(pv);
378 XTextProperty property;
379 int rc = VINF_SUCCESS;
380 int cProps;
381
382 LogFlowFunc (("converting COMPOUND TEXT to Utf-16LE. cbSrcLen=%d, cb=%d, pu8SrcText=%.*s\n",
383 cbSrcLen, cb, cbSrcLen, reinterpret_cast<char *>(pValue)));
384 *pcbActual = 0; /* Only set this to the right value on success. */
385 /* First convert the compound text to Utf8 */
386 property.value = reinterpret_cast<unsigned char *>(pValue);
387 property.encoding = g_ctx.atomCText;
388 property.format = 8;
389 property.nitems = cbSrcLen;
390#ifdef RT_OS_SOLARIS
391 int xrc = XmbTextPropertyToTextList(XtDisplay(g_ctx.widget), &property, &ppu8SrcText, &cProps);
392#else
393 int xrc = Xutf8TextPropertyToTextList(XtDisplay(g_ctx.widget), &property, &ppu8SrcText, &cProps);
394#endif
395 XtFree(reinterpret_cast<char *>(pValue));
396 if (xrc < 0)
397 switch(xrc)
398 {
399 case XNoMemory:
400 rc = VERR_NO_MEMORY;
401 break;
402 case XLocaleNotSupported:
403 case XConverterNotFound:
404 rc = VERR_NOT_SUPPORTED;
405 break;
406 default:
407 rc = VERR_UNRESOLVED_ERROR;
408 }
409 /* Now convert the UTF8 to UTF16 */
410 if (RT_SUCCESS(rc))
411 rc = RTStrToUtf16Ex(*ppu8SrcText, cbSrcLen, &pu16SrcText, 0, &cwSrcLen);
412 /* Check how much longer will the converted text will be. */
413 if (RT_SUCCESS(rc))
414 rc = vboxClipboardUtf16GetWinSize(pu16SrcText, cwSrcLen, &cwDestLen);
415 if (RT_SUCCESS(rc) && (cb < cwDestLen * 2))
416 {
417 /* Not enough buffer space provided - report the amount needed. */
418 LogFlowFunc (("guest buffer too small: size %d bytes, needed %d. Returning.\n",
419 cb, cwDestLen * 2));
420 *pcbActual = cwDestLen * 2;
421 rc = VERR_BUFFER_OVERFLOW;
422 }
423 /* Convert the text. */
424 if (RT_SUCCESS(rc))
425 rc = vboxClipboardUtf16LinToWin(pu16SrcText, cwSrcLen, pu16DestText, cb / 2);
426 if (RT_SUCCESS(rc))
427 {
428 LogFlowFunc (("converted string is %.*ls\n", cwDestLen, pu16DestText));
429 *pcbActual = cwDestLen * 2;
430 }
431 if (ppu8SrcText != NULL)
432 XFreeStringList(ppu8SrcText);
433 RTUtf16Free(pu16SrcText);
434 LogFlowFunc(("Returning. Status is %Rrc\n", rc));
435 RTSemEventSignal(g_ctx.waitForData);
436}
437
438/**
439 * Convert the Latin1 text returned from the X11 clipboard to UTF-16LE with Windows EOLS
440 * and place it in the global g_pcClipboardText variable. We are reading the X11 clipboard to
441 * make it available to VBox.
442 *
443 * @param pValue Source Latin1 text
444 * @param cbSourceLen Length in 8-bit bytes of the source text
445 * @param pv Where to store the converted data
446 * @param cb Length in bytes of the buffer pointed to by cb
447 * @param pcbActual Where to store the size of the converted data
448 * @param pClient Pointer to the client context structure
449 * @thread clipboard X11 event thread
450 * @note called by vboxClipboardGetDataFromX11
451 */
452static void vboxClipboardGetLatin1FromX11(XtPointer pValue, unsigned cbSourceLen, void *pv, unsigned cb,
453 uint32_t *pcbActual)
454{
455 unsigned cwDestLen = cbSourceLen + 1;
456 char *pu8SourceText = reinterpret_cast<char *>(pValue);
457 PRTUTF16 pu16DestText = reinterpret_cast<PRTUTF16>(pv);
458 int rc = VINF_SUCCESS;
459
460 LogFlowFunc (("converting Latin1 to Utf-16LE. Original is %.*s\n",
461 cbSourceLen, pu8SourceText));
462 *pcbActual = 0; /* Only set this to the right value on success. */
463 for (unsigned i = 0; i < cbSourceLen; i++)
464 if (pu8SourceText[i] == LINEFEED)
465 ++cwDestLen;
466 if (cb < cwDestLen * 2)
467 {
468 /* Not enough buffer space provided - report the amount needed. */
469 LogFlowFunc (("guest buffer too small: size %d bytes\n", cb));
470 *pcbActual = cwDestLen * 2;
471 rc = VERR_BUFFER_OVERFLOW;
472 }
473 if (RT_SUCCESS(rc))
474 {
475 for (unsigned i = 0, j = 0; i < cbSourceLen; ++i, ++j)
476 if (pu8SourceText[i] != LINEFEED)
477 pu16DestText[j] = pu8SourceText[i]; /* latin1 < utf-16LE */
478 else
479 {
480 pu16DestText[j] = CARRIAGERETURN;
481 ++j;
482 pu16DestText[j] = LINEFEED;
483 }
484 pu16DestText[cwDestLen - 1] = 0;
485 *pcbActual = cwDestLen * 2;
486 LogFlowFunc (("converted text is %.*ls\n", cwDestLen, pu16DestText));
487 }
488 XtFree(reinterpret_cast<char *>(pValue));
489 RTSemEventSignal(g_ctx.waitForData);
490 LogFlowFunc(("Returning. Status is %Rrc\n", rc));
491}
492
493/**
494 * Convert the clipboard text from the current format to Utf-16 with Windows line breaks.
495 * We are reading the X11 clipboard to make it available to VBox.
496 * @thread clipboard X11 event thread
497 * @note Callback for XtGetSelectionValue, called from vboxClipboardReadData
498 */
499static void vboxClipboardGetDataFromX11(Widget, XtPointer pClientData,
500 Atom * /* selection */, Atom *atomType,
501 XtPointer pValue,
502 long unsigned int *pcLen,
503 int *piFormat)
504{
505 LogFlowFunc(("pClientData=%p, *pcLen=%lu, *piFormat=%d\n", pClientData, *pcLen, *piFormat));
506 LogFlowFunc(("g_ctx.requestHostFormat=%d, g_ctx.requestBufferSize=%d\n",
507 g_ctx.requestHostFormat, g_ctx.requestBufferSize));
508 unsigned cTextLen = (*pcLen) * (*piFormat) / 8;
509 /* The X Toolkit may have failed to get the clipboard selection for us. */
510 if (*atomType == XT_CONVERT_FAIL)
511 return;
512 /* The clipboard selection may have changed before we could get it. */
513 if (NULL == pValue)
514 return;
515 /* We grab this mutex whenever an asynchronous clipboard operation completes and while
516 disconnecting a client from the clipboard to stop these operations colliding. */
517 RTSemMutexRequest(g_ctx.clipboardMutex, RT_INDEFINITE_WAIT);
518 if (reinterpret_cast<VBOXCLIPBOARDCLIENTDATA *>(pClientData) != g_ctx.pClient)
519 {
520 /* If the client is no longer connected, just return. */
521 XtFree(reinterpret_cast<char *>(pValue));
522 LogFlowFunc(("client is no longer connected, returning\n"));
523 RTSemMutexRelease(g_ctx.clipboardMutex);
524 return;
525 }
526
527 /* In which format did we request the clipboard data? */
528 switch (g_ctx.requestHostFormat)
529 {
530 case UTF16:
531 vboxClipboardGetUtf16(pValue, cTextLen / 2, g_ctx.requestBuffer, g_ctx.requestBufferSize,
532 g_ctx.requestActualSize);
533 break;
534 case CTEXT:
535 vboxClipboardGetCTextFromX11(pValue, cTextLen, g_ctx.requestBuffer, g_ctx.requestBufferSize,
536 g_ctx.requestActualSize);
537 break;
538 case UTF8:
539 {
540 /* If we are given broken Utf-8, we treat it as Latin1. Is this acceptable? */
541 size_t cStringLen;
542 char *pu8SourceText = reinterpret_cast<char *>(pValue);
543
544 if ((g_ctx.requestHostFormat == UTF8)
545 && (RTStrUniLenEx(pu8SourceText, *pcLen, &cStringLen) == VINF_SUCCESS))
546 {
547 vboxClipboardGetUtf8FromX11(pValue, cTextLen, g_ctx.requestBuffer, g_ctx.requestBufferSize,
548 g_ctx.requestActualSize);
549 break;
550 }
551 else
552 {
553 vboxClipboardGetLatin1FromX11(pValue, cTextLen, g_ctx.requestBuffer, g_ctx.requestBufferSize,
554 g_ctx.requestActualSize);
555 break;
556 }
557 }
558 default:
559 LogFunc (("bad target format\n"));
560 XtFree(reinterpret_cast<char *>(pValue));
561 RTSemMutexRelease(g_ctx.clipboardMutex);
562 return;
563 }
564 g_ctx.notifyGuest = true;
565 RTSemMutexRelease(g_ctx.clipboardMutex);
566}
567
568/**
569 * Find out what targets the current X11 clipboard holder can handle. We are
570 * reading the X11 clipboard to make it available to VBox.
571 * @thread clipboard X11 event thread
572 * @note Callback for XtGetSelectionValue, called from vboxClipboardPollX11ForTargets
573 */
574static void vboxClipboardGetTargetsFromX11(Widget, XtPointer pClientData,
575 Atom * /* selection */,
576 Atom *atomType,
577 XtPointer pValue,
578 long unsigned int *pcLen,
579 int *piFormat)
580{
581 Atom *atomTargets = reinterpret_cast<Atom *>(pValue);
582 unsigned cAtoms = *pcLen;
583 g_eClipboardFormats eBestTarget = INVALID;
584 Atom atomBestTarget = None;
585
586 Log3 (("%s: called\n", __PRETTY_FUNCTION__));
587 if (*atomType == XT_CONVERT_FAIL)
588 {
589 LogFunc (("reading clipboard from host, X toolkit failed to convert the selection\n"));
590 return;
591 }
592 /* We grab this mutex whenever an asynchronous clipboard operation completes and while
593 disconnecting a client from the clipboard to stop these operations colliding. */
594 RTSemMutexRequest(g_ctx.clipboardMutex, RT_INDEFINITE_WAIT);
595 if (reinterpret_cast<VBOXCLIPBOARDCLIENTDATA *>(pClientData) != g_ctx.pClient)
596 {
597 /* If the client is no longer connected, just return. */
598 LogFlowFunc(("client is no longer connected, returning\n"));
599 RTSemMutexRelease(g_ctx.clipboardMutex);
600 return;
601 }
602
603 for (unsigned i = 0; i < cAtoms; ++i)
604 {
605 for (unsigned j = 0; j != g_ctx.formatList.size(); ++j)
606 if (g_ctx.formatList[j].atom == atomTargets[i])
607 {
608 if (eBestTarget < g_ctx.formatList[j].format)
609 {
610 eBestTarget = g_ctx.formatList[j].format;
611 atomBestTarget = g_ctx.formatList[j].atom;
612 }
613 break;
614 }
615 if (g_debugClipboard)
616 {
617 char *szAtomName = XGetAtomName(XtDisplay(g_ctx.widget), atomTargets[i]);
618 if (szAtomName != 0)
619 {
620 Log2 (("%s: the host offers target %s\n", __PRETTY_FUNCTION__,
621 szAtomName));
622 XFree(szAtomName);
623 }
624 }
625 }
626 g_ctx.atomHostTextFormat = atomBestTarget;
627 if ((eBestTarget != g_ctx.hostTextFormat) || (g_ctx.notifyGuest == true))
628 {
629 uint32_t u32Formats = 0;
630 if (g_debugClipboard)
631 {
632 if (atomBestTarget != None)
633 {
634 char *szAtomName = XGetAtomName(XtDisplay(g_ctx.widget), atomBestTarget);
635 Log2 (("%s: switching to host text target %s. Available targets are:\n",
636 __PRETTY_FUNCTION__, szAtomName));
637 XFree(szAtomName);
638 }
639 else
640 Log2(("%s: no supported host text target found. Available targets are:\n",
641 __PRETTY_FUNCTION__));
642 for (unsigned i = 0; i < cAtoms; ++i)
643 {
644 char *szAtomName = XGetAtomName(XtDisplay(g_ctx.widget), atomTargets[i]);
645 if (szAtomName != 0)
646 {
647 Log2 (("%s: %s\n", __PRETTY_FUNCTION__, szAtomName));
648 XFree(szAtomName);
649 }
650 }
651 }
652 g_ctx.hostTextFormat = eBestTarget;
653 if (eBestTarget != INVALID)
654 u32Formats |= VBOX_SHARED_CLIPBOARD_FMT_UNICODETEXT;
655 vboxSvcClipboardReportMsg (g_ctx.pClient, VBOX_SHARED_CLIPBOARD_HOST_MSG_FORMATS,
656 u32Formats);
657 g_ctx.notifyGuest = false;
658 }
659 XtFree(reinterpret_cast<char *>(pValue));
660 RTSemMutexRelease(g_ctx.clipboardMutex);
661}
662
663/**
664 * This callback is called every 200ms to check the contents of the X11 clipboard.
665 * @thread clipboard X11 event thread
666 * @note Callback for XtAppAddTimeOut, called from vboxClipboardThread and
667 * recursively retriggered
668 */
669static void vboxClipboardPollX11ForTargets(XtPointer /* pUserData */, XtIntervalId * /* hTimerId */)
670{
671 Log3 (("%s: called\n", __PRETTY_FUNCTION__));
672 /* Get the current clipboard contents */
673 if (g_ctx.eOwner == X11 && g_ctx.pClient != 0)
674 {
675 Log3 (("%s: requesting the targets that the host clipboard offers\n",
676 __PRETTY_FUNCTION__));
677 XtGetSelectionValue(g_ctx.widget, g_ctx.atomClipboard, g_ctx.atomTargets,
678 vboxClipboardGetTargetsFromX11, reinterpret_cast<XtPointer>(g_ctx.pClient),
679 CurrentTime);
680 }
681 /* Re-arm our timer */
682 XtAppAddTimeOut(g_ctx.appContext, 200 /* ms */, vboxClipboardPollX11ForTargets, 0);
683}
684
685/** We store information about the target formats we can handle in a global vector for internal
686 use. */
687static void vboxClipboardAddFormat(const char *pszName, g_eClipboardFormats eFormat,
688 unsigned guestFormat)
689{
690 VBOXCLIPBOARDFORMAT sFormat;
691 /* Get an atom from the X server for that target format */
692 Atom atomFormat = XInternAtom(XtDisplay(g_ctx.widget), pszName, false);
693 sFormat.atom = atomFormat;
694 sFormat.format = eFormat;
695 sFormat.guestFormat = guestFormat;
696 g_ctx.formatList.push_back(sFormat);
697 LogFlow (("vboxClipboardAddFormat: added format %s (%d)\n", pszName, eFormat));
698}
699
700/**
701 * The main loop of our clipboard reader.
702 * @thread clipboard X11 event thread
703 */
704static int vboxClipboardThread(RTTHREAD self, void * /* pvUser */)
705{
706 LogRel(("Shared clipboard: starting host clipboard thread\n"));
707
708 /* Set up a timer to poll the host clipboard */
709 XtAppAddTimeOut(g_ctx.appContext, 200 /* ms */, vboxClipboardPollX11ForTargets, 0);
710
711 XtAppMainLoop(g_ctx.appContext);
712 g_ctx.formatList.clear();
713 LogRel(("Shared clipboard: host clipboard thread terminated successfully\n"));
714 return VINF_SUCCESS;
715}
716
717/** X11 specific initialisation for the shared clipboard. */
718int vboxClipboardInitX11 (void)
719{
720 /* Create a window and make it a clipboard viewer. */
721 int cArgc = 0;
722 char *pcArgv = 0;
723 int rc = VINF_SUCCESS;
724 // static String szFallbackResources[] = { (char*)"*.width: 1", (char*)"*.height: 1", NULL };
725 Display *pDisplay;
726
727 /* Make sure we are thread safe */
728 XtToolkitThreadInitialize();
729 /* Set up the Clipbard application context and main window. We call all these functions
730 directly instead of calling XtOpenApplication() so that we can fail gracefully if we
731 can't get an X11 display. */
732 XtToolkitInitialize();
733 g_ctx.appContext = XtCreateApplicationContext();
734 // XtAppSetFallbackResources(g_ctx.appContext, szFallbackResources);
735 pDisplay = XtOpenDisplay(g_ctx.appContext, 0, 0, "VBoxClipboard", 0, 0, &cArgc, &pcArgv);
736 if (NULL == pDisplay)
737 {
738 LogRel(("Shared clipboard: failed to connect to the host clipboard - the window system may not be running.\n"));
739 rc = VERR_NOT_SUPPORTED;
740 }
741 if (RT_SUCCESS(rc))
742 {
743 g_ctx.widget = XtVaAppCreateShell(0, "VBoxClipboard", applicationShellWidgetClass, pDisplay,
744 XtNwidth, 1, XtNheight, 1, NULL);
745 if (NULL == g_ctx.widget)
746 {
747 LogRel(("Shared clipboard: failed to construct the X11 window for the host clipboard manager.\n"));
748 rc = VERR_NO_MEMORY;
749 }
750 }
751 if (RT_SUCCESS(rc))
752 {
753 XtSetMappedWhenManaged(g_ctx.widget, false);
754 XtRealizeWidget(g_ctx.widget);
755
756 /* Get hold of the atoms which we need */
757 g_ctx.atomClipboard = XInternAtom(XtDisplay(g_ctx.widget), "CLIPBOARD", false /* only_if_exists */);
758 g_ctx.atomPrimary = XInternAtom(XtDisplay(g_ctx.widget), "PRIMARY", false);
759 g_ctx.atomTargets = XInternAtom(XtDisplay(g_ctx.widget), "TARGETS", false);
760 g_ctx.atomMultiple = XInternAtom(XtDisplay(g_ctx.widget), "MULTIPLE", false);
761 g_ctx.atomTimestamp = XInternAtom(XtDisplay(g_ctx.widget), "TIMESTAMP", false);
762 g_ctx.atomUtf16 = XInternAtom(XtDisplay(g_ctx.widget),
763 "text/plain;charset=ISO-10646-UCS-2", false);
764 g_ctx.atomUtf8 = XInternAtom(XtDisplay(g_ctx.widget), "UTF_STRING", false);
765 /* And build up the vector of supported formats */
766 g_ctx.atomCText = XInternAtom(XtDisplay(g_ctx.widget), "COMPOUND_TEXT", false);
767 /* And build up the vector of supported formats */
768 if (!g_testUtf8 && !g_testCText)
769 vboxClipboardAddFormat("text/plain;charset=ISO-10646-UCS-2", UTF16,
770 VBOX_SHARED_CLIPBOARD_FMT_UNICODETEXT);
771 if (!g_testUtf16 && !g_testCText)
772 {
773 vboxClipboardAddFormat("UTF8_STRING", UTF8,
774 VBOX_SHARED_CLIPBOARD_FMT_UNICODETEXT);
775 vboxClipboardAddFormat("text/plain;charset=UTF-8", UTF8,
776 VBOX_SHARED_CLIPBOARD_FMT_UNICODETEXT);
777 vboxClipboardAddFormat("text/plain;charset=utf-8", UTF8,
778 VBOX_SHARED_CLIPBOARD_FMT_UNICODETEXT);
779 vboxClipboardAddFormat("STRING", UTF8,
780 VBOX_SHARED_CLIPBOARD_FMT_UNICODETEXT);
781 vboxClipboardAddFormat("TEXT", UTF8,
782 VBOX_SHARED_CLIPBOARD_FMT_UNICODETEXT);
783 vboxClipboardAddFormat("text/plain", UTF8,
784 VBOX_SHARED_CLIPBOARD_FMT_UNICODETEXT);
785}
786 if (!g_testUtf16 && !g_testUtf8)
787 vboxClipboardAddFormat("COMPOUND_TEXT", CTEXT,
788 VBOX_SHARED_CLIPBOARD_FMT_UNICODETEXT);
789 }
790 return rc;
791}
792
793/**
794 * Initialise the host side of the shared clipboard.
795 * @note Called by the HGCM clipboard service
796 * @thread HGCM clipboard service thread
797 */
798int vboxClipboardInit (void)
799{
800 int rc;
801
802 if (!RTEnvGet("DISPLAY"))
803 {
804 /*
805 * If we don't find the DISPLAY environment variable we assume that we are not
806 * connected to an X11 server. Don't actually try to do this then, just fail
807 * silently and report success on every call. This is important for VBoxHeadless.
808 */
809 LogRelFunc(("no X11 detected -- host clipboard disabled\n"));
810 g_fHaveX11 = false;
811 return VINF_SUCCESS;
812 }
813
814 if (RTEnvGet("VBOX_CBTEST_UTF16"))
815 {
816 g_testUtf16 = true;
817 LogRel(("Host clipboard: testing Utf16\n"));
818 }
819 else if (RTEnvGet("VBOX_CBTEST_UTF8"))
820 {
821 g_testUtf8 = true;
822 LogRel(("Host clipboard: testing Utf8\n"));
823 }
824 else if (RTEnvGet("VBOX_CBTEST_CTEXT"))
825 {
826 g_testCText = true;
827 LogRel(("Host clipboard: testing compound text\n"));
828 }
829 else if (RTEnvGet("VBOX_CBDEBUG"))
830 {
831 g_debugClipboard = true;
832 LogRel(("Host clipboard: enabling additional debugging output\n"));
833 }
834
835 g_fHaveX11 = true;
836
837 LogRel(("Initializing host clipboard service\n"));
838 RTSemEventCreate(&g_ctx.waitForData);
839 RTSemMutexCreate(&g_ctx.clipboardMutex);
840 rc = vboxClipboardInitX11();
841 if (RT_SUCCESS(rc))
842 {
843 rc = RTThreadCreate(&g_ctx.thread, vboxClipboardThread, 0, 0,
844 RTTHREADTYPE_IO, RTTHREADFLAGS_WAITABLE, "SHCLIP");
845 if (RT_FAILURE(rc))
846 LogRel(("Failed to start the host shared clipboard thread.\n"));
847 }
848 if (RT_FAILURE(rc))
849 {
850 RTSemEventDestroy(g_ctx.waitForData);
851 RTSemMutexDestroy(g_ctx.clipboardMutex);
852 }
853 return rc;
854}
855
856/**
857 * Terminate the host side of the shared clipboard.
858 * @note Called by the HGCM clipboard service
859 * @thread HGCM clipboard service thread
860 */
861void vboxClipboardDestroy (void)
862{
863 int rc, rcThread;
864 unsigned count = 0;
865 XEvent ev;
866
867 /*
868 * Immediately return if we are not connected to the host X server.
869 */
870 if (!g_fHaveX11)
871 return;
872
873 LogRel(("vboxClipboardDestroy: shutting down host clipboard\n"));
874
875 /* Drop the reference to the client, in case it is still there. This will
876 * cause any outstanding clipboard data requests from X11 to fail
877 * immediately. */
878 g_ctx.pClient = NULL;
879 if (g_ctx.eOwner == VB)
880 /* X11 may be waiting for data from VBox. At this point it is no
881 * longer going to arrive, and we must release it to allow the event
882 * loop to terminate. In this case the buffer where VBox would have
883 * written the clipboard data will still be empty and we will just
884 * return "no data" to X11. Any subsequent attempts to get the data
885 * from VBox will fail immediately as the client reference is gone. */
886 RTSemEventSignal(g_ctx.waitForData);
887 /* Set the termination flag. This has been observed to block if it was set
888 * during a request for clipboard data coming from X11, so only we do it
889 * after releasing any such requests. */
890 XtAppSetExitFlag(g_ctx.appContext);
891 /* Wake up the event loop */
892 memset(&ev, 0, sizeof(ev));
893 ev.xclient.type = ClientMessage;
894 ev.xclient.format = 8;
895 XSendEvent(XtDisplay(g_ctx.widget), XtWindow(g_ctx.widget), false, 0, &ev);
896 XFlush(XtDisplay(g_ctx.widget));
897 do
898 {
899 rc = RTThreadWait(g_ctx.thread, 1000, &rcThread);
900 ++count;
901 Assert(RT_SUCCESS(rc) || ((VERR_TIMEOUT == rc) && (count != 5)));
902 } while ((VERR_TIMEOUT == rc) && (count < 300));
903 if (RT_SUCCESS(rc))
904 {
905 /*
906 * No one should be waiting on this by now. Justification:
907 * - Case 1: VBox is waiting for data from X11:
908 * Not possible, as it would be waiting on this thread.
909 * - Case 2: X11 is waiting for data from VBox:
910 * Not possible, as we checked that the X11 event thread exited
911 * successfully.
912 */
913 RTSemEventDestroy(g_ctx.waitForData);
914 RTSemMutexDestroy(g_ctx.clipboardMutex);
915 AssertRC(rcThread);
916 }
917 else
918 LogRel(("vboxClipboardDestroy: rc=%Rrc\n", rc));
919 XtCloseDisplay(XtDisplay(g_ctx.widget));
920 LogFlowFunc(("returning.\n"));
921}
922
923/**
924 * Connect a guest the shared clipboard.
925 *
926 * @param pClient Structure containing context information about the guest system
927 * @returns RT status code
928 * @note Called by the HGCM clipboard service
929 * @thread HGCM clipboard service thread
930 */
931int vboxClipboardConnect (VBOXCLIPBOARDCLIENTDATA *pClient)
932{
933 /*
934 * Immediately return if we are not connected to the host X server.
935 */
936 if (!g_fHaveX11)
937 return VINF_SUCCESS;
938
939 LogFlow(("vboxClipboardConnect\n"));
940
941 /* Only one client is supported for now */
942 AssertLogRelReturn(g_ctx.pClient == 0, VERR_NOT_SUPPORTED);
943
944 pClient->pCtx = &g_ctx;
945 pClient->pCtx->pClient = pClient;
946 g_ctx.eOwner = X11;
947 g_ctx.notifyGuest = true;
948 return VINF_SUCCESS;
949}
950
951/**
952 * Synchronise the contents of the host clipboard with the guest, called
953 * after a save and restore of the guest.
954 * @note Called by the HGCM clipboard service
955 * @thread HGCM clipboard service thread
956 */
957int vboxClipboardSync (VBOXCLIPBOARDCLIENTDATA *pClient)
958{
959 /*
960 * Immediately return if we are not connected to the host X server.
961 */
962 if (!g_fHaveX11)
963 return VINF_SUCCESS;
964
965 /* On a Linux host, the guest should never synchronise/cache its clipboard contents, as
966 we have no way of reliably telling when the host clipboard data changes. So instead
967 of synchronising, we tell the guest to empty its clipboard, and we set the cached
968 flag so that we report formats to the guest next time we poll for them. */
969 vboxSvcClipboardReportMsg (g_ctx.pClient, VBOX_SHARED_CLIPBOARD_HOST_MSG_FORMATS, 0);
970 g_ctx.notifyGuest = true;
971
972 return VINF_SUCCESS;
973}
974
975/**
976 * Shut down the shared clipboard service and "disconnect" the guest.
977 * @note Called by the HGCM clipboard service
978 * @thread HGCM clipboard service thread
979 */
980void vboxClipboardDisconnect (VBOXCLIPBOARDCLIENTDATA *)
981{
982 /*
983 * Immediately return if we are not connected to the host X server.
984 */
985 if (!g_fHaveX11)
986 return;
987
988 LogFlow(("vboxClipboardDisconnect\n"));
989
990 RTSemMutexRequest(g_ctx.clipboardMutex, RT_INDEFINITE_WAIT);
991 g_ctx.pClient = NULL;
992 g_ctx.eOwner = NONE;
993 g_ctx.hostTextFormat = INVALID;
994 g_ctx.hostBitmapFormat = INVALID;
995 RTSemMutexRelease(g_ctx.clipboardMutex);
996}
997
998/**
999 * Satisfy a request from X11 for clipboard targets supported by VBox.
1000 *
1001 * @returns true if we successfully convert the data to the format requested, false otherwise.
1002 *
1003 * @param atomTypeReturn The type of the data we are returning
1004 * @param pValReturn A pointer to the data we are returning. This should be to memory
1005 * allocated by XtMalloc, which will be freed by the toolkit later
1006 * @param pcLenReturn The length of the data we are returning
1007 * @param piFormatReturn The format (8bit, 16bit, 32bit) of the data we are returning
1008 * @thread clipboard X11 event thread
1009 * @note called by vboxClipboardConvertForX11
1010 */
1011static Boolean vboxClipboardConvertTargetsForX11(Atom *atomTypeReturn, XtPointer *pValReturn,
1012 unsigned long *pcLenReturn, int *piFormatReturn)
1013{
1014 unsigned uListSize = g_ctx.formatList.size();
1015 Atom *atomTargets = reinterpret_cast<Atom *>(XtMalloc((uListSize + 3) * sizeof(Atom)));
1016 unsigned cTargets = 0;
1017
1018 LogFlowFunc (("called\n"));
1019 for (unsigned i = 0; i < uListSize; ++i)
1020 {
1021 if ( ((g_ctx.guestFormats & VBOX_SHARED_CLIPBOARD_FMT_UNICODETEXT) != 0)
1022 && (g_ctx.formatList[i].guestFormat == VBOX_SHARED_CLIPBOARD_FMT_UNICODETEXT))
1023 {
1024 atomTargets[cTargets] = g_ctx.formatList[i].atom;
1025 ++cTargets;
1026 }
1027 }
1028 atomTargets[cTargets] = g_ctx.atomTargets;
1029 atomTargets[cTargets + 1] = g_ctx.atomMultiple;
1030 atomTargets[cTargets + 2] = g_ctx.atomTimestamp;
1031 if (g_debugClipboard)
1032 {
1033 for (unsigned i = 0; i < cTargets + 3; i++)
1034 {
1035 char *szAtomName = XGetAtomName(XtDisplay(g_ctx.widget), atomTargets[i]);
1036 if (szAtomName != 0)
1037 {
1038 Log2 (("%s: returning target %s\n", __PRETTY_FUNCTION__,
1039 szAtomName));
1040 XFree(szAtomName);
1041 }
1042 else
1043 {
1044 Log(("%s: invalid atom %d in the list!\n", __PRETTY_FUNCTION__,
1045 atomTargets[i]));
1046 }
1047 }
1048 }
1049 *atomTypeReturn = XA_ATOM;
1050 *pValReturn = reinterpret_cast<XtPointer>(atomTargets);
1051 *pcLenReturn = cTargets + 3;
1052 *piFormatReturn = 32;
1053 return true;
1054}
1055
1056/**
1057 * Satisfy a request from the host to convert the clipboard text to Utf16. We return non-zero
1058 * terminated text.
1059 *
1060 * @returns true if we successfully convert the data to the format requested, false otherwise.
1061 *
1062 * @retval atomTypeReturn The type of the data we are returning
1063 * @retval pValReturn A pointer to the data we are returning. This should be to memory
1064 * allocated by XtMalloc, which will be freed by the toolkit later
1065 * @retval pcLenReturn The length of the data we are returning
1066 * @retval piFormatReturn The format (8bit, 16bit, 32bit) of the data we are returning
1067 */
1068static Boolean vboxClipboardConvertUtf16(Atom *atomTypeReturn, XtPointer *pValReturn,
1069 unsigned long *pcLenReturn, int *piFormatReturn)
1070{
1071 PRTUTF16 pu16SrcText, pu16DestText;
1072 size_t cwSrcLen, cwDestLen;
1073 int rc;
1074
1075 LogFlowFunc (("called\n"));
1076 rc = vboxClipboardReadDataFromVBox(&g_ctx, VBOX_SHARED_CLIPBOARD_FMT_UNICODETEXT);
1077 if ((RT_FAILURE(rc)) || (g_ctx.pClient->data.cb == 0))
1078 {
1079 /* If vboxClipboardReadDataFromVBox fails then pClient may be invalid */
1080 LogRelFunc (("vboxClipboardReadDataFromVBox returned %Rrc%s\n", rc,
1081 RT_SUCCESS(rc) ? ", g_ctx.pClient->data.cb == 0" : ""));
1082 vboxClipboardEmptyGuestBuffer();
1083 return false;
1084 }
1085 pu16SrcText = reinterpret_cast<PRTUTF16>(g_ctx.pClient->data.pv);
1086 cwSrcLen = g_ctx.pClient->data.cb / 2;
1087 /* How long will the converted text be? */
1088 rc = vboxClipboardUtf16GetLinSize(pu16SrcText, cwSrcLen, &cwDestLen);
1089 if (RT_FAILURE(rc))
1090 {
1091 LogRel(("vboxClipboardConvertUtf16: clipboard conversion failed. vboxClipboardUtf16GetLinSize returned %Rrc. Abandoning.\n", rc));
1092 vboxClipboardEmptyGuestBuffer();
1093 AssertRCReturn(rc, false);
1094 }
1095 if (cwDestLen == 0)
1096 {
1097 LogFlowFunc(("received empty clipboard data from the guest, returning false.\n"));
1098 vboxClipboardEmptyGuestBuffer();
1099 return false;
1100 }
1101 pu16DestText = reinterpret_cast<PRTUTF16>(XtMalloc(cwDestLen * 2));
1102 if (pu16DestText == 0)
1103 {
1104 LogRel(("vboxClipboardConvertUtf16: failed to allocate %d bytes\n", cwDestLen * 2));
1105 vboxClipboardEmptyGuestBuffer();
1106 return false;
1107 }
1108 /* Convert the text. */
1109 rc = vboxClipboardUtf16WinToLin(pu16SrcText, cwSrcLen, pu16DestText, cwDestLen);
1110 if (RT_FAILURE(rc))
1111 {
1112 LogRel(("vboxClipboardConvertUtf16: clipboard conversion failed. vboxClipboardUtf16WinToLin returned %Rrc. Abandoning.\n", rc));
1113 XtFree(reinterpret_cast<char *>(pu16DestText));
1114 vboxClipboardEmptyGuestBuffer();
1115 return false;
1116 }
1117 LogFlowFunc (("converted string is %.*ls. Returning.\n", cwDestLen, pu16DestText));
1118 vboxClipboardEmptyGuestBuffer();
1119 *atomTypeReturn = g_ctx.atomUtf16;
1120 *pValReturn = reinterpret_cast<XtPointer>(pu16DestText);
1121 *pcLenReturn = cwDestLen;
1122 *piFormatReturn = 16;
1123 return true;
1124}
1125
1126/**
1127 * Satisfy a request from the host to convert the clipboard text to Utf8.
1128 *
1129 * @returns true if we successfully convert the data to the format requested, false otherwise.
1130 *
1131 * @param atomTypeReturn The type of the data we are returning
1132 * @param pValReturn A pointer to the data we are returning. This should be to memory
1133 * allocated by XtMalloc, which will be freed by the toolkit later
1134 * @param pcLenReturn The length of the data we are returning
1135 * @param piFormatReturn The format (8bit, 16bit, 32bit) of the data we are returning
1136 * @thread clipboard X11 event thread
1137 * @note called by vboxClipboardConvertForX11
1138 */
1139static Boolean vboxClipboardConvertToUtf8ForX11(Atom *atomTypeReturn,
1140 XtPointer *pValReturn,
1141 unsigned long *pcLenReturn,
1142 int *piFormatReturn)
1143{
1144 PRTUTF16 pu16SrcText, pu16DestText;
1145 char *pu8DestText;
1146 size_t cwSrcLen, cwDestLen, cbDestLen;
1147 int rc;
1148
1149 LogFlowFunc (("called\n"));
1150 /* Read the clipboard data from the guest. */
1151 rc = vboxClipboardReadDataFromVBox(&g_ctx, VBOX_SHARED_CLIPBOARD_FMT_UNICODETEXT);
1152 if ((rc != VINF_SUCCESS) || (g_ctx.pClient->data.cb == 0))
1153 {
1154 /* If vboxClipboardReadDataFromVBox fails then pClient may be invalid */
1155 LogRelFunc (("vboxClipboardReadDataFromVBox returned %Rrc%s\n", rc,
1156 RT_SUCCESS(rc) ? ", g_ctx.pClient->data.cb == 0" : ""));
1157 vboxClipboardEmptyGuestBuffer();
1158 return false;
1159 }
1160 pu16SrcText = reinterpret_cast<PRTUTF16>(g_ctx.pClient->data.pv);
1161 cwSrcLen = g_ctx.pClient->data.cb / 2;
1162 /* How long will the converted text be? */
1163 rc = vboxClipboardUtf16GetLinSize(pu16SrcText, cwSrcLen, &cwDestLen);
1164 if (RT_FAILURE(rc))
1165 {
1166 LogRelFunc (("clipboard conversion failed. vboxClipboardUtf16GetLinSize returned %Rrc. Abandoning.\n", rc));
1167 vboxClipboardEmptyGuestBuffer();
1168 AssertRCReturn(rc, false);
1169 }
1170 if (cwDestLen == 0)
1171 {
1172 LogFlowFunc(("received empty clipboard data from the guest, returning false.\n"));
1173 vboxClipboardEmptyGuestBuffer();
1174 return false;
1175 }
1176 pu16DestText = reinterpret_cast<PRTUTF16>(RTMemAlloc(cwDestLen * 2));
1177 if (pu16DestText == 0)
1178 {
1179 LogRelFunc (("failed to allocate %d bytes\n", cwDestLen * 2));
1180 vboxClipboardEmptyGuestBuffer();
1181 return false;
1182 }
1183 /* Convert the text. */
1184 rc = vboxClipboardUtf16WinToLin(pu16SrcText, cwSrcLen, pu16DestText, cwDestLen);
1185 if (RT_FAILURE(rc))
1186 {
1187 LogRelFunc (("clipboard conversion failed. vboxClipboardUtf16WinToLin() returned %Rrc. Abandoning.\n", rc));
1188 RTMemFree(reinterpret_cast<void *>(pu16DestText));
1189 vboxClipboardEmptyGuestBuffer();
1190 return false;
1191 }
1192 /* Allocate enough space, as RTUtf16ToUtf8Ex may fail if the
1193 space is too tightly calculated. */
1194 pu8DestText = XtMalloc(cwDestLen * 4);
1195 if (pu8DestText == 0)
1196 {
1197 LogRelFunc (("failed to allocate %d bytes\n", cwDestLen * 4));
1198 RTMemFree(reinterpret_cast<void *>(pu16DestText));
1199 vboxClipboardEmptyGuestBuffer();
1200 return false;
1201 }
1202 /* Convert the Utf16 string to Utf8. */
1203 rc = RTUtf16ToUtf8Ex(pu16DestText + 1, cwDestLen - 1, &pu8DestText, cwDestLen * 4,
1204 &cbDestLen);
1205 RTMemFree(reinterpret_cast<void *>(pu16DestText));
1206 if (RT_FAILURE(rc))
1207 {
1208 LogRelFunc (("clipboard conversion failed. RTUtf16ToUtf8Ex() returned %Rrc. Abandoning.\n", rc));
1209 XtFree(pu8DestText);
1210 vboxClipboardEmptyGuestBuffer();
1211 return false;
1212 }
1213 LogFlowFunc (("converted string is %.*s. Returning.\n", cbDestLen, pu8DestText));
1214 vboxClipboardEmptyGuestBuffer();
1215 *atomTypeReturn = g_ctx.atomUtf8;
1216 *pValReturn = reinterpret_cast<XtPointer>(pu8DestText);
1217 *pcLenReturn = cbDestLen;
1218 *piFormatReturn = 8;
1219 return true;
1220}
1221
1222/**
1223 * Satisfy a request from the host to convert the clipboard text to COMPOUND_TEXT.
1224 *
1225 * @returns true if we successfully convert the data to the format requested, false otherwise.
1226 *
1227 * @param atomTypeReturn The type of the data we are returning
1228 * @param pValReturn A pointer to the data we are returning. This should be to memory
1229 * allocated by XtMalloc, which will be freed by the toolkit later
1230 * @param pcLenReturn The length of the data we are returning
1231 * @param piFormatReturn The format (8bit, 16bit, 32bit) of the data we are returning
1232 * @thread clipboard X11 event thread
1233 * @note called by vboxClipboardConvertForX11
1234 */
1235static Boolean vboxClipboardConvertToCTextForX11(Atom *atomTypeReturn,
1236 XtPointer *pValReturn,
1237 unsigned long *pcLenReturn,
1238 int *piFormatReturn)
1239{
1240 PRTUTF16 pu16SrcText, pu16DestText;
1241 char *pu8DestText = 0;
1242 size_t cwSrcLen, cwDestLen, cbDestLen;
1243 XTextProperty property;
1244 int rc;
1245
1246 LogFlowFunc (("called\n"));
1247 /* Read the clipboard data from the guest. */
1248 rc = vboxClipboardReadDataFromVBox(&g_ctx, VBOX_SHARED_CLIPBOARD_FMT_UNICODETEXT);
1249 if ((rc != VINF_SUCCESS) || (g_ctx.pClient->data.cb == 0))
1250 {
1251 /* If vboxClipboardReadDataFromVBox fails then pClient may be invalid */
1252 LogRelFunc (("vboxClipboardReadDataFromVBox returned %Rrc%s\n", rc,
1253 RT_SUCCESS(rc) ? ", g_ctx.pClient->data.cb == 0" : ""));
1254 vboxClipboardEmptyGuestBuffer();
1255 return false;
1256 }
1257 pu16SrcText = reinterpret_cast<PRTUTF16>(g_ctx.pClient->data.pv);
1258 cwSrcLen = g_ctx.pClient->data.cb / 2;
1259 /* How long will the converted text be? */
1260 rc = vboxClipboardUtf16GetLinSize(pu16SrcText, cwSrcLen, &cwDestLen);
1261 if (RT_FAILURE(rc))
1262 {
1263 LogRelFunc (("clipboard conversion failed. vboxClipboardUtf16GetLinSize returned %Rrc. Abandoning.\n", rc));
1264 vboxClipboardEmptyGuestBuffer();
1265 AssertRCReturn(rc, false);
1266 }
1267 if (cwDestLen == 0)
1268 {
1269 LogFlowFunc(("received empty clipboard data from the guest, returning false.\n"));
1270 vboxClipboardEmptyGuestBuffer();
1271 return false;
1272 }
1273 pu16DestText = reinterpret_cast<PRTUTF16>(RTMemAlloc(cwDestLen * 2));
1274 if (pu16DestText == 0)
1275 {
1276 LogRelFunc (("failed to allocate %d bytes\n", cwDestLen * 2));
1277 vboxClipboardEmptyGuestBuffer();
1278 return false;
1279 }
1280 /* Convert the text. */
1281 rc = vboxClipboardUtf16WinToLin(pu16SrcText, cwSrcLen, pu16DestText, cwDestLen);
1282 if (RT_FAILURE(rc))
1283 {
1284 LogRelFunc (("clipboard conversion failed. vboxClipboardUtf16WinToLin() returned %Rrc. Abandoning.\n", rc));
1285 RTMemFree(reinterpret_cast<void *>(pu16DestText));
1286 vboxClipboardEmptyGuestBuffer();
1287 return false;
1288 }
1289 /* Convert the Utf16 string to Utf8. */
1290 rc = RTUtf16ToUtf8Ex(pu16DestText + 1, cwDestLen - 1, &pu8DestText, 0, &cbDestLen);
1291 RTMemFree(reinterpret_cast<void *>(pu16DestText));
1292 if (RT_FAILURE(rc))
1293 {
1294 LogRelFunc (("clipboard conversion failed. RTUtf16ToUtf8Ex() returned %Rrc. Abandoning.\n", rc));
1295 vboxClipboardEmptyGuestBuffer();
1296 return false;
1297 }
1298 /* And finally (!) convert the Utf8 text to compound text. */
1299#ifdef RT_OS_SOLARIS
1300 rc = XmbTextListToTextProperty(XtDisplay(g_ctx.widget), &pu8DestText, 1,
1301 XCompoundTextStyle, &property);
1302#else
1303 rc = Xutf8TextListToTextProperty(XtDisplay(g_ctx.widget), &pu8DestText, 1,
1304 XCompoundTextStyle, &property);
1305#endif
1306 RTMemFree(pu8DestText);
1307 if (rc < 0)
1308 {
1309 const char *pcReason;
1310 switch(rc)
1311 {
1312 case XNoMemory:
1313 pcReason = "out of memory";
1314 break;
1315 case XLocaleNotSupported:
1316 pcReason = "locale (Utf8) not supported";
1317 break;
1318 case XConverterNotFound:
1319 pcReason = "converter not found";
1320 break;
1321 default:
1322 pcReason = "unknown error";
1323 }
1324 LogRelFunc (("Xutf8TextListToTextProperty failed. Reason: %s\n",
1325 pcReason));
1326 vboxClipboardEmptyGuestBuffer();
1327 return false;
1328 }
1329 LogFlowFunc (("converted string is %s. Returning.\n", property.value));
1330 vboxClipboardEmptyGuestBuffer();
1331 *atomTypeReturn = property.encoding;
1332 *pValReturn = reinterpret_cast<XtPointer>(property.value);
1333 *pcLenReturn = property.nitems;
1334 *piFormatReturn = property.format;
1335 return true;
1336}
1337
1338/**
1339 * Callback to request VBox's clipboard data for an X11 client. Called by the
1340 * X Toolkit.
1341 * @thread clipboard X11 event thread
1342 * @note callback for XtOwnSelection, called by vboxClipboardFormatAnnounce
1343 */
1344static Boolean vboxClipboardConvertForX11(Widget, Atom *atomSelection,
1345 Atom *atomTarget,
1346 Atom *atomTypeReturn,
1347 XtPointer *pValReturn,
1348 unsigned long *pcLenReturn,
1349 int *piFormatReturn)
1350{
1351 g_eClipboardFormats eFormat = INVALID;
1352
1353 LogFlowFunc(("\n"));
1354 /* Drop requests that we receive too late. */
1355 if (g_ctx.eOwner != VB)
1356 return false;
1357 if ( (*atomSelection != g_ctx.atomClipboard)
1358 && (*atomSelection != g_ctx.atomPrimary)
1359 )
1360 {
1361 LogFlowFunc(("rc = false\n"));
1362 return false;
1363 }
1364 if (g_debugClipboard)
1365 {
1366 char *szAtomName = XGetAtomName(XtDisplay(g_ctx.widget), *atomTarget);
1367 if (szAtomName != 0)
1368 {
1369 Log2 (("%s: request for format %s\n", __PRETTY_FUNCTION__, szAtomName));
1370 XFree(szAtomName);
1371 }
1372 else
1373 {
1374 LogFunc (("request for invalid target atom %d!\n", *atomTarget));
1375 }
1376 }
1377 if (*atomTarget == g_ctx.atomTargets)
1378 {
1379 eFormat = TARGETS;
1380 }
1381 else
1382 {
1383 for (unsigned i = 0; i != g_ctx.formatList.size(); ++i)
1384 {
1385 if (g_ctx.formatList[i].atom == *atomTarget)
1386 {
1387 eFormat = g_ctx.formatList[i].format;
1388 break;
1389 }
1390 }
1391 }
1392 switch (eFormat)
1393 {
1394 case TARGETS:
1395 return vboxClipboardConvertTargetsForX11(atomTypeReturn, pValReturn,
1396 pcLenReturn, piFormatReturn);
1397 case UTF16:
1398 return vboxClipboardConvertUtf16(atomTypeReturn, pValReturn, pcLenReturn,
1399 piFormatReturn);
1400 case UTF8:
1401 return vboxClipboardConvertToUtf8ForX11(atomTypeReturn, pValReturn,
1402 pcLenReturn, piFormatReturn);
1403 case CTEXT:
1404 return vboxClipboardConvertToCTextForX11(atomTypeReturn, pValReturn,
1405 pcLenReturn, piFormatReturn);
1406 default:
1407 LogFunc (("bad format\n"));
1408 return false;
1409 }
1410}
1411
1412/**
1413 * This is called by the X toolkit intrinsics to let us know that another
1414 * X11 client has taken the clipboard.
1415 * @note callback for XtOwnSelection, called from vboxClipboardFormatAnnounce
1416 * @thread clipboard X11 event thread
1417 */
1418static void vboxClipboardReturnToX11(Widget, Atom *)
1419{
1420 LogFlowFunc (("called, giving VBox clipboard ownership\n"));
1421 g_ctx.eOwner = X11;
1422 g_ctx.notifyGuest = true;
1423}
1424
1425/**
1426 * VBox is taking possession of the shared clipboard.
1427 *
1428 * @param pClient Context data for the guest system
1429 * @param u32Formats Clipboard formats the guest is offering
1430 * @note Called by the HGCM clipboard service
1431 * @thread HGCM clipboard service thread
1432 */
1433void vboxClipboardFormatAnnounce (VBOXCLIPBOARDCLIENTDATA *pClient, uint32_t u32Formats)
1434{
1435 /*
1436 * Immediately return if we are not connected to the host X server.
1437 */
1438 if (!g_fHaveX11)
1439 return;
1440
1441 pClient->pCtx->guestFormats = u32Formats;
1442 LogFlowFunc (("u32Formats=%d\n", u32Formats));
1443 if (u32Formats == 0)
1444 {
1445 /* This is just an automatism, not a genuine anouncement */
1446 LogFlowFunc(("returning\n"));
1447 return;
1448 }
1449 if (g_ctx.eOwner == VB)
1450 {
1451 /* We already own the clipboard, so no need to grab it, especially as that can lead
1452 to races due to the asynchronous nature of the X11 clipboard. This event may also
1453 have been sent out by the guest to invalidate the Windows clipboard cache. */
1454 LogFlowFunc(("returning\n"));
1455 return;
1456 }
1457 Log2 (("%s: giving the guest clipboard ownership\n", __PRETTY_FUNCTION__));
1458 g_ctx.eOwner = VB;
1459 g_ctx.hostTextFormat = INVALID;
1460 g_ctx.hostBitmapFormat = INVALID;
1461 if (XtOwnSelection(g_ctx.widget, g_ctx.atomClipboard, CurrentTime, vboxClipboardConvertForX11,
1462 vboxClipboardReturnToX11, 0) != True)
1463 {
1464 Log2 (("%s: returning clipboard ownership to the host\n", __PRETTY_FUNCTION__));
1465 /* We set this so that the guest gets notified when we take the clipboard, even if no
1466 guest formats are found which we understand. */
1467 g_ctx.notifyGuest = true;
1468 g_ctx.eOwner = X11;
1469 }
1470 XtOwnSelection(g_ctx.widget, g_ctx.atomPrimary, CurrentTime, vboxClipboardConvertForX11,
1471 NULL, 0);
1472 LogFlowFunc(("returning\n"));
1473
1474}
1475
1476/**
1477 * Called when VBox wants to read the X11 clipboard.
1478 *
1479 * @param pClient Context information about the guest VM
1480 * @param u32Format The format that the guest would like to receive the data in
1481 * @param pv Where to write the data to
1482 * @param cb The size of the buffer to write the data to
1483 * @param pcbActual Where to write the actual size of the written data
1484 * @note Called by the HGCM clipboard service
1485 * @thread HGCM clipboard service thread
1486 */
1487int vboxClipboardReadData (VBOXCLIPBOARDCLIENTDATA *pClient, uint32_t u32Format, void *pv,
1488 uint32_t cb, uint32_t *pcbActual)
1489{
1490 /*
1491 * Immediately return if we are not connected to the host X server.
1492 */
1493 if (!g_fHaveX11)
1494 {
1495 /* no data available */
1496 *pcbActual = 0;
1497 return VINF_SUCCESS;
1498 }
1499
1500 LogFlowFunc (("u32Format = %d, cb = %d\n", u32Format, cb));
1501
1502 /*
1503 * The guest wants to read data in the given format.
1504 */
1505 if (u32Format & VBOX_SHARED_CLIPBOARD_FMT_UNICODETEXT)
1506 {
1507 if (g_ctx.hostTextFormat == INVALID)
1508 {
1509 /* No data available. */
1510 *pcbActual = 0;
1511 return VERR_NO_DATA; /* The guest thinks we have data and we don't */
1512 }
1513 /* No one else (VBox or X11) should currently be waiting. The first because
1514 * requests from VBox are serialised and the second because X11 previously
1515 * grabbed the clipboard, so it should not be waiting for data from us. */
1516 AssertLogRelReturn (ASMAtomicCmpXchgU32(&g_ctx.waiter, VB, NONE), VERR_DEADLOCK);
1517 g_ctx.requestHostFormat = g_ctx.hostTextFormat;
1518 g_ctx.requestBuffer = pv;
1519 g_ctx.requestBufferSize = cb;
1520 g_ctx.requestActualSize = pcbActual;
1521 /* Initially set the size of the data read to zero in case we fail
1522 * somewhere. */
1523 *pcbActual = 0;
1524 /* Send out a request for the data to the current clipboard owner */
1525 XtGetSelectionValue(g_ctx.widget, g_ctx.atomClipboard, g_ctx.atomHostTextFormat,
1526 vboxClipboardGetDataFromX11, reinterpret_cast<XtPointer>(g_ctx.pClient),
1527 CurrentTime);
1528 /* When the data arrives, the vboxClipboardGetDataFromX11 callback will be called. The
1529 callback will signal the event semaphore when it has processed the data for us. */
1530
1531 int rc = RTSemEventWait(g_ctx.waitForData, RT_INDEFINITE_WAIT);
1532 if (RT_FAILURE(rc))
1533 {
1534 g_ctx.waiter = NONE;
1535 return rc;
1536 }
1537 g_ctx.waiter = NONE;
1538 }
1539 else
1540 {
1541 return VERR_NOT_IMPLEMENTED;
1542 }
1543 return VINF_SUCCESS;
1544}
1545
1546/**
1547 * Called when we have requested data from VBox and that data has arrived.
1548 *
1549 * @param pClient Context information about the guest VM
1550 * @param pv Buffer to which the data was written
1551 * @param cb The size of the data written
1552 * @param u32Format The format of the data written
1553 * @note Called by the HGCM clipboard service
1554 * @thread HGCM clipboard service thread
1555 */
1556void vboxClipboardWriteData (VBOXCLIPBOARDCLIENTDATA *pClient, void *pv, uint32_t cb, uint32_t u32Format)
1557{
1558 if (!g_fHaveX11)
1559 return;
1560
1561 LogFlowFunc (("called\n"));
1562
1563 /* Assert that no other transfer is in process (requests are serialised)
1564 * or has not cleaned up properly. */
1565 AssertLogRelReturnVoid ( pClient->data.pv == NULL
1566 && pClient->data.cb == 0
1567 && pClient->data.u32Format == 0);
1568
1569 /* Grab the mutex and check that X11 is still waiting for the data before
1570 * delivering it. See the explanation in vboxClipboardReadDataFromVBox. */
1571 RTSemMutexRequest(g_ctx.clipboardMutex, RT_INDEFINITE_WAIT);
1572 if (g_ctx.waiter == X11 && cb > 0)
1573 {
1574 pClient->data.pv = RTMemAlloc (cb);
1575
1576 if (pClient->data.pv)
1577 {
1578 memcpy (pClient->data.pv, pv, cb);
1579 pClient->data.cb = cb;
1580 pClient->data.u32Format = u32Format;
1581 }
1582 }
1583 RTSemMutexRelease(g_ctx.clipboardMutex);
1584
1585 RTSemEventSignal(g_ctx.waitForData);
1586}
1587
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