VirtualBox

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

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

HostServices/x11: Increased timeout to 2 minutes for trusted Xorg.

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