VirtualBox

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

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

Corrected a couple of typos in comments. No code changes.

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 58.3 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 the host or the guest currently own the clipboard? */
82enum g_eClipboardOwner { NONE = 0, HOST, GUEST };
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_eClipboardOwner 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 /** And because it would not do for the guest to be waiting for the host while the host
151 is waiting for the guest, we set a flag and assert horribly if we spot a deadlock. */
152 uint32_t waiter;
153 /** This mutex is held while an asynchronous operation completes (i.e. the host clipboard is
154 being queried) to make sure that the clipboard is not disconnected during that time. It
155 is also grabbed when the clipboard client disconnects. When an asynchronous operation
156 starts completing, it checks that the same client is still connected immediately after
157 grabbing the mutex. */
158 RTSEMMUTEX asyncMutex;
159
160 /** Format which we are reading from the host clipboard (valid during a request for the
161 host clipboard) */
162 g_eClipboardFormats requestHostFormat;
163 /** The guest buffer to write host clipboard data to (valid during a request for the host
164 clipboard) */
165 void *requestBuffer;
166 /** The size of the guest buffer to write host clipboard data to (valid during a request for
167 the host clipboard) */
168 unsigned requestBufferSize;
169 /** The size of the host clipboard data written to the guest buffer (valid during a request
170 for the host clipboard) */
171 uint32_t *requestActualSize;
172
173 /** Pointer to the client data structure */
174 VBOXCLIPBOARDCLIENTDATA *pClient;
175};
176
177/* Only one client is supported. There seems to be no need for more clients. */
178static VBOXCLIPBOARDCONTEXT g_ctx;
179
180/* Are we actually connected to the X11 servicer? */
181static bool g_fHaveX11;
182
183/**
184 * Send a request to the guest to transfer the contents of its clipboard to the host.
185 *
186 * @param pCtx Pointer to the host clipboard structure
187 * @param u32Format The format in which the data should be transfered
188 */
189static int vboxClipboardReadDataFromClient (VBOXCLIPBOARDCONTEXT *pCtx, uint32_t u32Format)
190{
191 VBOXCLIPBOARDCLIENTDATA *pClient = pCtx->pClient;
192
193 LogFlowFunc(("u32Format=%02X\n", u32Format));
194 if (pClient == 0)
195 {
196 Log(("vboxClipboardReadDataFromClient: host requested guest clipboard data after guest had disconnected.\n"));
197 pCtx->guestFormats = 0;
198 pCtx->waiter = 0;
199 return VERR_TIMEOUT;
200 }
201 if (!( pCtx->pClient->data.pv == NULL
202 && pCtx->pClient->data.cb == 0
203 && pCtx->pClient->data.u32Format == 0))
204 {
205 LogRel(("vboxClipboardReadDataFromClient: a guest to host clipboard transfer has been requested, but another is in progress, or has not cleaned up properly.\n"));
206 AssertMsgFailed(("A guest to host clipboard transfer has been requested, but another is in progress, or has not cleaned up properly.\n"));
207 }
208
209 /* Only one of the guest and the host should be waiting at any one time */
210 if (!ASMAtomicCmpXchgU32(&pCtx->waiter, 1, 0))
211 {
212 LogRel(("vboxClipboardReadDataFromClient: deadlock situation - the host and the guest are both waiting for data from the other.\n"));
213 return VERR_DEADLOCK;
214 }
215 /* Request data from the guest */
216 vboxSvcClipboardReportMsg (pCtx->pClient, VBOX_SHARED_CLIPBOARD_HOST_MSG_READ_DATA, u32Format);
217 /* Which will signal us when it is ready. */
218 if (RTSemEventWait(pCtx->waitForData, CLIPBOARDTIMEOUT) != VINF_SUCCESS)
219 {
220 LogRel(("vboxClipboardReadDataFromClient: vboxSvcClipboardReportMsg failed to complete within %d milliseconds\n", CLIPBOARDTIMEOUT));
221 pCtx->guestFormats = 0;
222 pCtx->waiter = 0;
223 return VERR_TIMEOUT;
224 }
225 pCtx->waiter = 0;
226 LogFlowFunc(("wait completed. Returning.\n"));
227 return VINF_SUCCESS;
228}
229
230/**
231 * Convert the UTF-16 text returned from the X11 clipboard to UTF-16LE with Windows EOLs
232 * and place it in the global g_pcClipboardText variable. We are reading the host clipboard to
233 * make it available to the guest.
234 *
235 * @param pValue Source UTF-16 text
236 * @param cwSourceLen Length in 16-bit words of the source text
237 * @param pv Where to store the converted data
238 * @param cb Length in bytes of the buffer pointed to by cb
239 * @param pcbActual Where to store the size of the converted data
240 * @param pClient Pointer to the client context structure
241 */
242static void vboxClipboardGetUtf16(XtPointer pValue, unsigned cwSrcLen, void *pv, unsigned cb,
243 uint32_t *pcbActual)
244{
245 size_t cwDestLen;
246 PRTUTF16 pu16SrcText = reinterpret_cast<PRTUTF16>(pValue);
247 PRTUTF16 pu16DestText = reinterpret_cast<PRTUTF16>(pv);
248
249 LogFlowFunc (("converting Utf-16 to Utf-16LE. cwSrcLen=%d, cb=%d, pu16SrcText+1=%.*ls\n",
250 cwSrcLen, cb, cwSrcLen - 1, pu16SrcText + 1));
251 *pcbActual = 0; /* Only set this to the right value on success. */
252 /* How long will the converted text be? */
253 int rc = vboxClipboardUtf16GetWinSize(pu16SrcText, cwSrcLen, &cwDestLen);
254 if (RT_SUCCESS(rc) && (cb < cwDestLen * 2))
255 {
256 /* Not enough buffer space provided - report the amount needed. */
257 LogFlowFunc (("guest buffer too small: size %d bytes, needed %d. Returning.\n",
258 cb, cwDestLen * 2));
259 *pcbActual = cwDestLen * 2;
260 rc = VERR_BUFFER_OVERFLOW;
261 }
262 /* Convert the text. */
263 if (RT_SUCCESS(rc))
264 rc = vboxClipboardUtf16LinToWin(pu16SrcText, cwSrcLen, pu16DestText, cb / 2);
265 if (RT_SUCCESS(rc))
266 {
267 LogFlowFunc (("converted string is %.*ls\n", cwDestLen, pu16DestText));
268 *pcbActual = cwDestLen * 2;
269 }
270 /* We need to do this whether we succeed or fail. */
271 XtFree(reinterpret_cast<char *>(pValue));
272 RTSemEventSignal(g_ctx.waitForData);
273 LogFlowFunc(("Returning. Status is %Rrc\n", rc));
274}
275
276/**
277 * Convert the UTF-8 text returned from the X11 clipboard to UTF-16LE with Windows EOLS.
278 * We are reading the host clipboard to make it available to the guest.
279 *
280 * @param pValue Source UTF-8 text
281 * @param cbSourceLen Length in 8-bit bytes of the source text
282 * @param pv Where to store the converted data
283 * @param cb Length in bytes of the buffer pointed to by pv
284 * @param pcbActual Where to store the size of the converted data
285 * @param pClient Pointer to the client context structure
286 */
287static void vboxClipboardGetUtf8(XtPointer pValue, unsigned cbSrcLen, void *pv, unsigned cb,
288 uint32_t *pcbActual)
289{
290 size_t cwSrcLen, cwDestLen;
291 char *pu8SrcText = reinterpret_cast<char *>(pValue);
292 PRTUTF16 pu16SrcText = NULL;
293 PRTUTF16 pu16DestText = reinterpret_cast<PRTUTF16>(pv);
294
295 LogFlowFunc (("converting Utf-8 to Utf-16LE. cbSrcLen=%d, cb=%d, pu8SrcText=%.*s\n",
296 cbSrcLen, cb, cbSrcLen, pu8SrcText));
297 *pcbActual = 0; /* Only set this to the right value on success. */
298 /* First convert the UTF8 to UTF16 */
299 int rc = RTStrToUtf16Ex(pu8SrcText, cbSrcLen, &pu16SrcText, 0, &cwSrcLen);
300 /* Check how much longer will the converted text will be. */
301 if (RT_SUCCESS(rc))
302 rc = vboxClipboardUtf16GetWinSize(pu16SrcText, cwSrcLen, &cwDestLen);
303 if (RT_SUCCESS(rc) && (cb < cwDestLen * 2))
304 {
305 /* Not enough buffer space provided - report the amount needed. */
306 LogFlowFunc (("guest buffer too small: size %d bytes, needed %d. Returning.\n",
307 cb, cwDestLen * 2));
308 *pcbActual = cwDestLen * 2;
309 rc = VERR_BUFFER_OVERFLOW;
310 }
311 /* Convert the text. */
312 if (RT_SUCCESS(rc))
313 rc = vboxClipboardUtf16LinToWin(pu16SrcText, cwSrcLen, pu16DestText, cb / 2);
314 if (RT_SUCCESS(rc))
315 {
316 LogFlowFunc (("converted string is %.*ls.\n", cwDestLen, pu16DestText));
317 *pcbActual = cwDestLen * 2;
318 }
319 XtFree(reinterpret_cast<char *>(pValue));
320 RTUtf16Free(pu16SrcText);
321 RTSemEventSignal(g_ctx.waitForData);
322 LogFlowFunc(("Returning. Status is %Rrc", rc));
323}
324
325/**
326 * Convert the COMPOUND_TEXT text returned from the X11 clipboard to UTF-16LE with Windows
327 * EOLS. We are reading the host clipboard to make it available to the guest.
328 *
329 * @param pValue Source COMPOUND_TEXT text
330 * @param cbSourceLen Length in 8-bit bytes of the source text
331 * @param pv Where to store the converted data
332 * @param cb Length in bytes of the buffer pointed to by pv
333 * @param pcbActual Where to store the size of the converted data
334 * @param pClient Pointer to the client context structure
335 */
336static void vboxClipboardGetCText(XtPointer pValue, unsigned cbSrcLen, void *pv, unsigned cb,
337 uint32_t *pcbActual)
338{
339 size_t cwSrcLen, cwDestLen;
340 char **ppu8SrcText = NULL;
341 PRTUTF16 pu16SrcText = NULL;
342 PRTUTF16 pu16DestText = reinterpret_cast<PRTUTF16>(pv);
343 XTextProperty property;
344 int rc = VINF_SUCCESS;
345 int cProps;
346
347 LogFlowFunc (("converting COMPOUND TEXT to Utf-16LE. cbSrcLen=%d, cb=%d, pu8SrcText=%.*s\n",
348 cbSrcLen, cb, cbSrcLen, reinterpret_cast<char *>(pValue)));
349 *pcbActual = 0; /* Only set this to the right value on success. */
350 /* First convert the compound text to Utf8 */
351 property.value = reinterpret_cast<unsigned char *>(pValue);
352 property.encoding = g_ctx.atomCText;
353 property.format = 8;
354 property.nitems = cbSrcLen;
355#ifdef RT_OS_SOLARIS
356 int xrc = XmbTextPropertyToTextList(XtDisplay(g_ctx.widget), &property, &ppu8SrcText, &cProps);
357#else
358 int xrc = Xutf8TextPropertyToTextList(XtDisplay(g_ctx.widget), &property, &ppu8SrcText, &cProps);
359#endif
360 XtFree(reinterpret_cast<char *>(pValue));
361 if (xrc < 0)
362 switch(xrc)
363 {
364 case XNoMemory:
365 rc = VERR_NO_MEMORY;
366 break;
367 case XLocaleNotSupported:
368 case XConverterNotFound:
369 rc = VERR_NOT_SUPPORTED;
370 break;
371 default:
372 rc = VERR_UNRESOLVED_ERROR;
373 }
374 /* Now convert the UTF8 to UTF16 */
375 if (RT_SUCCESS(rc))
376 rc = RTStrToUtf16Ex(*ppu8SrcText, cbSrcLen, &pu16SrcText, 0, &cwSrcLen);
377 /* Check how much longer will the converted text will be. */
378 if (RT_SUCCESS(rc))
379 rc = vboxClipboardUtf16GetWinSize(pu16SrcText, cwSrcLen, &cwDestLen);
380 if (RT_SUCCESS(rc) && (cb < cwDestLen * 2))
381 {
382 /* Not enough buffer space provided - report the amount needed. */
383 LogFlowFunc (("guest buffer too small: size %d bytes, needed %d. Returning.\n",
384 cb, cwDestLen * 2));
385 *pcbActual = cwDestLen * 2;
386 rc = VERR_BUFFER_OVERFLOW;
387 }
388 /* Convert the text. */
389 if (RT_SUCCESS(rc))
390 rc = vboxClipboardUtf16LinToWin(pu16SrcText, cwSrcLen, pu16DestText, cb / 2);
391 if (RT_SUCCESS(rc))
392 {
393 LogFlowFunc (("converted string is %.*ls\n", cwDestLen, pu16DestText));
394 *pcbActual = cwDestLen * 2;
395 }
396 if (ppu8SrcText != NULL)
397 XFreeStringList(ppu8SrcText);
398 RTUtf16Free(pu16SrcText);
399 LogFlowFunc(("Returning. Status is %Rrc\n", rc));
400 RTSemEventSignal(g_ctx.waitForData);
401}
402
403/**
404 * Convert the Latin1 text returned from the X11 clipboard to UTF-16LE with Windows EOLS
405 * and place it in the global g_pcClipboardText variable. We are reading the host clipboard to
406 * make it available to the guest.
407 *
408 * @param pValue Source Latin1 text
409 * @param cbSourceLen Length in 8-bit bytes of the source text
410 * @param pv Where to store the converted data
411 * @param cb Length in bytes of the buffer pointed to by cb
412 * @param pcbActual Where to store the size of the converted data
413 * @param pClient Pointer to the client context structure
414 */
415static void vboxClipboardGetLatin1(XtPointer pValue, unsigned cbSourceLen, void *pv, unsigned cb,
416 uint32_t *pcbActual)
417{
418 unsigned cwDestLen = cbSourceLen + 1;
419 char *pu8SourceText = reinterpret_cast<char *>(pValue);
420 PRTUTF16 pu16DestText = reinterpret_cast<PRTUTF16>(pv);
421 int rc = VINF_SUCCESS;
422
423 LogFlow (("vboxClipboardGetLatin1: converting Latin1 to Utf-16LE. Original is %.*s\n",
424 cbSourceLen, pu8SourceText));
425 *pcbActual = 0; /* Only set this to the right value on success. */
426 for (unsigned i = 0; i < cbSourceLen; i++)
427 if (pu8SourceText[i] == LINEFEED)
428 ++cwDestLen;
429 if (cb < cwDestLen * 2)
430 {
431 /* Not enough buffer space provided - report the amount needed. */
432 LogFlowFunc (("guest buffer too small: size %d bytes\n", cb));
433 *pcbActual = cwDestLen * 2;
434 rc = VERR_BUFFER_OVERFLOW;
435 }
436 if (RT_SUCCESS(rc))
437 {
438 for (unsigned i = 0, j = 0; i < cbSourceLen; ++i, ++j)
439 if (pu8SourceText[i] != LINEFEED)
440 pu16DestText[j] = pu8SourceText[i]; /* latin1 < utf-16LE */
441 else
442 {
443 pu16DestText[j] = CARRIAGERETURN;
444 ++j;
445 pu16DestText[j] = LINEFEED;
446 }
447 pu16DestText[cwDestLen - 1] = 0;
448 *pcbActual = cwDestLen * 2;
449 LogFlowFunc (("converted text is %.*ls\n", cwDestLen, pu16DestText));
450 }
451 XtFree(reinterpret_cast<char *>(pValue));
452 RTSemEventSignal(g_ctx.waitForData);
453 LogFlowFunc(("Returning. Status is %Rrc\n", rc));
454}
455
456/** Convert the clipboard text from the current format to Utf-16 with Windows line breaks.
457 We are reading the host clipboard to make it available to the guest. */
458static void vboxClipboardGetProc(Widget, XtPointer pClientData, Atom * /* selection */,
459 Atom *atomType, XtPointer pValue, long unsigned int *pcLen,
460 int *piFormat)
461{
462 LogFlowFunc(("pClientData=%p, *pcLen=%lu, *piFormat=%d\n", pClientData, *pcLen, *piFormat));
463 LogFlowFunc(("g_ctx.requestHostFormat=%d, g_ctx.requestBufferSize=%d\n",
464 g_ctx.requestHostFormat, g_ctx.requestBufferSize));
465 unsigned cTextLen = (*pcLen) * (*piFormat) / 8;
466 /* The X Toolkit may have failed to get the clipboard selection for us. */
467 if (*atomType == XT_CONVERT_FAIL)
468 return;
469 /* The clipboard selection may have changed before we could get it. */
470 if (NULL == pValue)
471 return;
472 /* We grab this mutex whenever an asynchronous clipboard operation completes and while
473 disconnecting a client from the clipboard to stop these operations colliding. */
474 RTSemMutexRequest(g_ctx.asyncMutex, RT_INDEFINITE_WAIT);
475 if (reinterpret_cast<VBOXCLIPBOARDCLIENTDATA *>(pClientData) != g_ctx.pClient)
476 {
477 /* If the client is no longer connected, just return. */
478 XtFree(reinterpret_cast<char *>(pValue));
479 LogFlowFunc(("client is no longer connected, returning\n"));
480 RTSemMutexRelease(g_ctx.asyncMutex);
481 return;
482 }
483
484 /* In which format did we request the clipboard data? */
485 switch (g_ctx.requestHostFormat)
486 {
487 case UTF16:
488 vboxClipboardGetUtf16(pValue, cTextLen / 2, g_ctx.requestBuffer, g_ctx.requestBufferSize,
489 g_ctx.requestActualSize);
490 break;
491 case CTEXT:
492 vboxClipboardGetCText(pValue, cTextLen, g_ctx.requestBuffer, g_ctx.requestBufferSize,
493 g_ctx.requestActualSize);
494 break;
495 case UTF8:
496 {
497 /* If we are given broken Utf-8, we treat it as Latin1. Is this acceptable? */
498 size_t cStringLen;
499 char *pu8SourceText = reinterpret_cast<char *>(pValue);
500
501 if ((g_ctx.requestHostFormat == UTF8)
502 && (RTStrUniLenEx(pu8SourceText, *pcLen, &cStringLen) == VINF_SUCCESS))
503 {
504 vboxClipboardGetUtf8(pValue, cTextLen, g_ctx.requestBuffer, g_ctx.requestBufferSize,
505 g_ctx.requestActualSize);
506 break;
507 }
508 else
509 {
510 vboxClipboardGetLatin1(pValue, cTextLen, g_ctx.requestBuffer, g_ctx.requestBufferSize,
511 g_ctx.requestActualSize);
512 break;
513 }
514 }
515 default:
516 Log (("vboxClipboardGetProc: bad target format\n"));
517 XtFree(reinterpret_cast<char *>(pValue));
518 RTSemMutexRelease(g_ctx.asyncMutex);
519 return;
520 }
521 g_ctx.notifyGuest = true;
522 RTSemMutexRelease(g_ctx.asyncMutex);
523}
524
525/** Callback to handle a reply to a request for the targets the current clipboard holder can
526 handle. We are reading the host clipboard to make it available to the guest. */
527static void vboxClipboardTargetsProc(Widget, XtPointer pClientData, Atom * /* selection */,
528 Atom *atomType, XtPointer pValue, long unsigned int *pcLen,
529 int *piFormat)
530{
531 Atom *atomTargets = reinterpret_cast<Atom *>(pValue);
532 unsigned cAtoms = *pcLen;
533 g_eClipboardFormats eBestTarget = INVALID;
534 Atom atomBestTarget = None;
535
536 Log3 (("vboxClipboardTargetsProc called\n"));
537 if (*atomType == XT_CONVERT_FAIL)
538 {
539 Log (("vboxClipboardTargetsProc: reading clipboard from host, X toolkit failed to convert the selection\n"));
540 return;
541 }
542 /* We grab this mutex whenever an asynchronous clipboard operation completes and while
543 disconnecting a client from the clipboard to stop these operations colliding. */
544 RTSemMutexRequest(g_ctx.asyncMutex, RT_INDEFINITE_WAIT);
545 if (reinterpret_cast<VBOXCLIPBOARDCLIENTDATA *>(pClientData) != g_ctx.pClient)
546 {
547 /* If the client is no longer connected, just return. */
548 LogFlowFunc(("client is no longer connected, returning\n"));
549 RTSemMutexRelease(g_ctx.asyncMutex);
550 return;
551 }
552
553 for (unsigned i = 0; i < cAtoms; ++i)
554 {
555 for (unsigned j = 0; j != g_ctx.formatList.size(); ++j)
556 if (g_ctx.formatList[j].atom == atomTargets[i])
557 {
558 if (eBestTarget < g_ctx.formatList[j].format)
559 {
560 eBestTarget = g_ctx.formatList[j].format;
561 atomBestTarget = g_ctx.formatList[j].atom;
562 }
563 break;
564 }
565 if (g_debugClipboard)
566 {
567 char *szAtomName = XGetAtomName(XtDisplay(g_ctx.widget), atomTargets[i]);
568 if (szAtomName != 0)
569 {
570 Log2 (("vboxClipboardTargetsProc: the host offers target %s\n", szAtomName));
571 XFree(szAtomName);
572 }
573 }
574 }
575 g_ctx.atomHostTextFormat = atomBestTarget;
576 if ((eBestTarget != g_ctx.hostTextFormat) || (g_ctx.notifyGuest == true))
577 {
578 uint32_t u32Formats = 0;
579 if (g_debugClipboard)
580 {
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 }
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 else if (RTEnvGet("VBOX_CBDEBUG"))
772 {
773 g_debugClipboard = true;
774 LogRel(("Host clipboard: enabling additional debugging output\n"));
775 }
776
777 g_fHaveX11 = true;
778
779 LogRel(("Initializing host clipboard service\n"));
780 RTSemEventCreate(&g_ctx.waitForData);
781 RTSemMutexCreate(&g_ctx.asyncMutex);
782 rc = vboxClipboardInitX11();
783 if (RT_SUCCESS(rc))
784 {
785 rc = RTThreadCreate(&g_ctx.thread, vboxClipboardThread, 0, 0,
786 RTTHREADTYPE_IO, RTTHREADFLAGS_WAITABLE, "SHCLIP");
787 if (RT_FAILURE(rc))
788 LogRel(("Failed to start the host shared clipboard thread.\n"));
789 }
790 if (RT_FAILURE(rc))
791 {
792 RTSemEventDestroy(g_ctx.waitForData);
793 RTSemMutexDestroy(g_ctx.asyncMutex);
794 }
795 return rc;
796}
797
798/** Terminate the host side of the shared clipboard - called by the hgcm layer. */
799void vboxClipboardDestroy (void)
800{
801 int rc, rcThread;
802 unsigned count = 0;
803 XEvent ev;
804
805 /*
806 * Immetiately return if we are not connected to the host X server.
807 */
808 if (!g_fHaveX11)
809 return;
810
811 LogRel(("vboxClipboardDestroy: shutting down host clipboard\n"));
812
813 /* Set the termination flag. */
814 XtAppSetExitFlag(g_ctx.appContext);
815 /* Wake up the event loop */
816 memset(&ev, 0, sizeof(ev));
817 ev.xclient.type = ClientMessage;
818 ev.xclient.format = 8;
819 XSendEvent(XtDisplay(g_ctx.widget), XtWindow(g_ctx.widget), false, 0, &ev);
820 XFlush(XtDisplay(g_ctx.widget));
821 do
822 {
823 rc = RTThreadWait(g_ctx.thread, 1000, &rcThread);
824 ++count;
825 Assert(RT_SUCCESS(rc) || ((VERR_TIMEOUT == rc) && (count != 5)));
826 } while ((VERR_TIMEOUT == rc) && (count < 300));
827 if (RT_SUCCESS(rc))
828 AssertRC(rcThread);
829 else
830 LogRel(("vboxClipboardDestroy: rc=%Rrc\n", rc));
831 XtCloseDisplay(XtDisplay(g_ctx.widget));
832 LogFlowFunc(("returning.\n"));
833}
834
835/**
836 * Enable the shared clipboard - called by the hgcm clipboard subsystem.
837 *
838 * @param pClient Structure containing context information about the guest system
839 * @returns RT status code
840 */
841int vboxClipboardConnect (VBOXCLIPBOARDCLIENTDATA *pClient)
842{
843 /*
844 * Immetiately return if we are not connected to the host X server.
845 */
846 if (!g_fHaveX11)
847 return VINF_SUCCESS;
848
849 LogFlow(("vboxClipboardConnect\n"));
850
851 /* Only one client is supported for now */
852 if (g_ctx.pClient != 0)
853 {
854 LogRel(("vboxClipboardConnect: attempted to connect, but a client appears to be already running.\n"));
855 AssertReturn(g_ctx.pClient == 0, VERR_NOT_SUPPORTED);
856 }
857
858 pClient->pCtx = &g_ctx;
859 pClient->pCtx->pClient = pClient;
860 g_ctx.eOwner = HOST;
861 g_ctx.notifyGuest = true;
862 return VINF_SUCCESS;
863}
864
865/**
866 * Synchronise the contents of the host clipboard with the guest, called by the HGCM layer
867 * after a save and restore of the guest.
868 */
869int vboxClipboardSync (VBOXCLIPBOARDCLIENTDATA *pClient)
870{
871 /*
872 * Immetiately return if we are not connected to the host X server.
873 */
874 if (!g_fHaveX11)
875 return VINF_SUCCESS;
876
877 /* On a Linux host, the guest should never synchronise/cache its clipboard contents, as
878 we have no way of reliably telling when the host clipboard data changes. So instead
879 of synchronising, we tell the guest to empty its clipboard, and we set the cached
880 flag so that we report formats to the guest next time we poll for them. */
881 vboxSvcClipboardReportMsg (g_ctx.pClient, VBOX_SHARED_CLIPBOARD_HOST_MSG_FORMATS, 0);
882 g_ctx.notifyGuest = true;
883
884 return VINF_SUCCESS;
885}
886
887/**
888 * Shut down the shared clipboard subsystem and "disconnect" the guest.
889 */
890void vboxClipboardDisconnect (VBOXCLIPBOARDCLIENTDATA *pClient)
891{
892 /*
893 * Immetiately return if we are not connected to the host X server.
894 */
895 if (!g_fHaveX11)
896 return;
897
898 LogFlow(("vboxClipboardDisconnect\n"));
899
900 RTSemMutexRequest(g_ctx.asyncMutex, RT_INDEFINITE_WAIT);
901 g_ctx.pClient = NULL;
902 g_ctx.eOwner = NONE;
903 g_ctx.hostTextFormat = INVALID;
904 g_ctx.hostBitmapFormat = INVALID;
905 RTSemMutexRelease(g_ctx.asyncMutex);
906}
907
908/**
909 * Satisfy a request from the host for available clipboard targets.
910 *
911 * @returns true if we successfully convert the data to the format requested, false otherwise.
912 *
913 * @param atomTypeReturn The type of the data we are returning
914 * @param pValReturn A pointer to the data we are returning. This should be to memory
915 * allocated by XtMalloc, which will be freed by the toolkit later
916 * @param pcLenReturn The length of the data we are returning
917 * @param piFormatReturn The format (8bit, 16bit, 32bit) of the data we are returning
918 */
919static Boolean vboxClipboardConvertTargets(Atom *atomTypeReturn, XtPointer *pValReturn,
920 unsigned long *pcLenReturn, int *piFormatReturn)
921{
922 unsigned uListSize = g_ctx.formatList.size();
923 Atom *atomTargets = reinterpret_cast<Atom *>(XtMalloc((uListSize + 3) * sizeof(Atom)));
924 unsigned cTargets = 0;
925
926 LogFlow (("vboxClipboardConvertTargets called\n"));
927 for (unsigned i = 0; i < uListSize; ++i)
928 {
929 if ( ((g_ctx.guestFormats & VBOX_SHARED_CLIPBOARD_FMT_UNICODETEXT) != 0)
930 && (g_ctx.formatList[i].guestFormat == VBOX_SHARED_CLIPBOARD_FMT_UNICODETEXT))
931 {
932 atomTargets[cTargets] = g_ctx.formatList[i].atom;
933 ++cTargets;
934 }
935 }
936 atomTargets[cTargets] = g_ctx.atomTargets;
937 atomTargets[cTargets + 1] = g_ctx.atomMultiple;
938 atomTargets[cTargets + 2] = g_ctx.atomTimestamp;
939 if (g_debugClipboard)
940 {
941 for (unsigned i = 0; i < cTargets + 3; i++)
942 {
943 char *szAtomName = XGetAtomName(XtDisplay(g_ctx.widget), atomTargets[i]);
944 if (szAtomName != 0)
945 {
946 Log2 (("vboxClipboardConvertTargets: returning target %s\n", szAtomName));
947 XFree(szAtomName);
948 }
949 else
950 {
951 Log(("vboxClipboardConvertTargets: invalid atom %d in the list!\n", atomTargets[i]));
952 }
953 }
954 }
955 *atomTypeReturn = XA_ATOM;
956 *pValReturn = reinterpret_cast<XtPointer>(atomTargets);
957 *pcLenReturn = cTargets + 3;
958 *piFormatReturn = 32;
959 return true;
960}
961
962/**
963 * Reset the contents of the buffer used to pass clipboard data from the guest to the host.
964 * This must be done after every clipboard transfer.
965 */
966static void vboxClipboardEmptyGuestBuffer(void)
967{
968 if (g_ctx.pClient->data.pv != 0)
969 RTMemFree(g_ctx.pClient->data.pv);
970 g_ctx.pClient->data.pv = 0;
971 g_ctx.pClient->data.cb = 0;
972 g_ctx.pClient->data.u32Format = 0;
973}
974
975/**
976 * Satisfy a request from the host to convert the clipboard text to Utf16. We return non-zero
977 * terminated text.
978 *
979 * @returns true if we successfully convert the data to the format requested, false otherwise.
980 *
981 * @retval atomTypeReturn The type of the data we are returning
982 * @retval pValReturn A pointer to the data we are returning. This should be to memory
983 * allocated by XtMalloc, which will be freed by the toolkit later
984 * @retval pcLenReturn The length of the data we are returning
985 * @retval piFormatReturn The format (8bit, 16bit, 32bit) of the data we are returning
986 */
987static Boolean vboxClipboardConvertUtf16(Atom *atomTypeReturn, XtPointer *pValReturn,
988 unsigned long *pcLenReturn, int *piFormatReturn)
989{
990 PRTUTF16 pu16SrcText, pu16DestText;
991 size_t cwSrcLen, cwDestLen;
992 int rc;
993
994 LogFlowFunc (("called\n"));
995 rc = vboxClipboardReadDataFromClient(&g_ctx, VBOX_SHARED_CLIPBOARD_FMT_UNICODETEXT);
996 if ((RT_FAILURE(rc)) || (g_ctx.pClient->data.cb == 0))
997 {
998 /* If vboxClipboardReadDataFromClient fails then pClient may be invalid */
999 LogRelFunc (("vboxClipboardReadDataFromClient returned %Rrc%s\n", rc,
1000 RT_SUCCESS(rc) ? ", g_ctx.pClient->data.cb == 0" : ""));
1001 vboxClipboardEmptyGuestBuffer();
1002 return false;
1003 }
1004 pu16SrcText = reinterpret_cast<PRTUTF16>(g_ctx.pClient->data.pv);
1005 cwSrcLen = g_ctx.pClient->data.cb / 2;
1006 /* How long will the converted text be? */
1007 rc = vboxClipboardUtf16GetLinSize(pu16SrcText, cwSrcLen, &cwDestLen);
1008 if (RT_FAILURE(rc))
1009 {
1010 LogRel(("vboxClipboardConvertUtf16: clipboard conversion failed. vboxClipboardUtf16GetLinSize returned %Rrc. Abandoning.\n", rc));
1011 vboxClipboardEmptyGuestBuffer();
1012 AssertRCReturn(rc, false);
1013 }
1014 if (cwDestLen == 0)
1015 {
1016 LogFlowFunc(("received empty clipboard data from the guest, returning false.\n"));
1017 vboxClipboardEmptyGuestBuffer();
1018 return false;
1019 }
1020 pu16DestText = reinterpret_cast<PRTUTF16>(XtMalloc(cwDestLen * 2));
1021 if (pu16DestText == 0)
1022 {
1023 LogRel(("vboxClipboardConvertUtf16: failed to allocate %d bytes\n", cwDestLen * 2));
1024 vboxClipboardEmptyGuestBuffer();
1025 return false;
1026 }
1027 /* Convert the text. */
1028 rc = vboxClipboardUtf16WinToLin(pu16SrcText, cwSrcLen, pu16DestText, cwDestLen);
1029 if (RT_FAILURE(rc))
1030 {
1031 LogRel(("vboxClipboardConvertUtf16: clipboard conversion failed. vboxClipboardUtf16WinToLin returned %Rrc. Abandoning.\n", rc));
1032 XtFree(reinterpret_cast<char *>(pu16DestText));
1033 vboxClipboardEmptyGuestBuffer();
1034 return false;
1035 }
1036 LogFlowFunc (("converted string is %.*ls. Returning.\n", cwDestLen, pu16DestText));
1037 vboxClipboardEmptyGuestBuffer();
1038 *atomTypeReturn = g_ctx.atomUtf16;
1039 *pValReturn = reinterpret_cast<XtPointer>(pu16DestText);
1040 *pcLenReturn = cwDestLen;
1041 *piFormatReturn = 16;
1042 return true;
1043}
1044
1045/**
1046 * Satisfy a request from the host to convert the clipboard text to Utf8.
1047 *
1048 * @returns true if we successfully convert the data to the format requested, false otherwise.
1049 *
1050 * @param atomTypeReturn The type of the data we are returning
1051 * @param pValReturn A pointer to the data we are returning. This should be to memory
1052 * allocated by XtMalloc, which will be freed by the toolkit later
1053 * @param pcLenReturn The length of the data we are returning
1054 * @param piFormatReturn The format (8bit, 16bit, 32bit) of the data we are returning
1055 */
1056static Boolean vboxClipboardConvertUtf8(Atom *atomTypeReturn, XtPointer *pValReturn,
1057 unsigned long *pcLenReturn, int *piFormatReturn)
1058{
1059 PRTUTF16 pu16SrcText, pu16DestText;
1060 char *pu8DestText;
1061 size_t cwSrcLen, cwDestLen, cbDestLen;
1062 int rc;
1063
1064 LogFlowFunc (("called\n"));
1065 /* Read the clipboard data from the guest. */
1066 rc = vboxClipboardReadDataFromClient(&g_ctx, VBOX_SHARED_CLIPBOARD_FMT_UNICODETEXT);
1067 if ((rc != VINF_SUCCESS) || (g_ctx.pClient->data.cb == 0))
1068 {
1069 /* If vboxClipboardReadDataFromClient fails then pClient may be invalid */
1070 LogRelFunc (("vboxClipboardReadDataFromClient returned %Rrc%s\n", rc,
1071 RT_SUCCESS(rc) ? ", g_ctx.pClient->data.cb == 0" : ""));
1072 vboxClipboardEmptyGuestBuffer();
1073 return false;
1074 }
1075 pu16SrcText = reinterpret_cast<PRTUTF16>(g_ctx.pClient->data.pv);
1076 cwSrcLen = g_ctx.pClient->data.cb / 2;
1077 /* How long will the converted text be? */
1078 rc = vboxClipboardUtf16GetLinSize(pu16SrcText, cwSrcLen, &cwDestLen);
1079 if (RT_FAILURE(rc))
1080 {
1081 LogRel(("vboxClipboardConvertUtf8: clipboard conversion failed. vboxClipboardUtf16GetLinSize returned %Rrc. Abandoning.\n", rc));
1082 vboxClipboardEmptyGuestBuffer();
1083 AssertRCReturn(rc, false);
1084 }
1085 if (cwDestLen == 0)
1086 {
1087 LogFlowFunc(("received empty clipboard data from the guest, returning false.\n"));
1088 vboxClipboardEmptyGuestBuffer();
1089 return false;
1090 }
1091 pu16DestText = reinterpret_cast<PRTUTF16>(RTMemAlloc(cwDestLen * 2));
1092 if (pu16DestText == 0)
1093 {
1094 LogRel(("vboxClipboardConvertUtf8: failed to allocate %d bytes\n", cwDestLen * 2));
1095 vboxClipboardEmptyGuestBuffer();
1096 return false;
1097 }
1098 /* Convert the text. */
1099 rc = vboxClipboardUtf16WinToLin(pu16SrcText, cwSrcLen, pu16DestText, cwDestLen);
1100 if (RT_FAILURE(rc))
1101 {
1102 LogRel(("vboxClipboardConvertUtf8: clipboard conversion failed. vboxClipboardUtf16WinToLin() returned %Rrc. Abandoning.\n", rc));
1103 RTMemFree(reinterpret_cast<void *>(pu16DestText));
1104 vboxClipboardEmptyGuestBuffer();
1105 return false;
1106 }
1107 /* Allocate enough space, as RTUtf16ToUtf8Ex may fail if the
1108 space is too tightly calculated. */
1109 pu8DestText = XtMalloc(cwDestLen * 4);
1110 if (pu8DestText == 0)
1111 {
1112 LogRel(("vboxClipboardConvertUtf8: failed to allocate %d bytes\n", cwDestLen * 4));
1113 RTMemFree(reinterpret_cast<void *>(pu16DestText));
1114 vboxClipboardEmptyGuestBuffer();
1115 return false;
1116 }
1117 /* Convert the Utf16 string to Utf8. */
1118 rc = RTUtf16ToUtf8Ex(pu16DestText + 1, cwDestLen - 1, &pu8DestText, cwDestLen * 4,
1119 &cbDestLen);
1120 RTMemFree(reinterpret_cast<void *>(pu16DestText));
1121 if (RT_FAILURE(rc))
1122 {
1123 LogRel(("vboxClipboardConvertUtf8: clipboard conversion failed. RTUtf16ToUtf8Ex() returned %Rrc. Abandoning.\n", rc));
1124 XtFree(pu8DestText);
1125 vboxClipboardEmptyGuestBuffer();
1126 return false;
1127 }
1128 LogFlowFunc (("converted string is %.*s. Returning.\n", cbDestLen, pu8DestText));
1129 vboxClipboardEmptyGuestBuffer();
1130 *atomTypeReturn = g_ctx.atomUtf8;
1131 *pValReturn = reinterpret_cast<XtPointer>(pu8DestText);
1132 *pcLenReturn = cbDestLen;
1133 *piFormatReturn = 8;
1134 return true;
1135}
1136
1137/**
1138 * Satisfy a request from the host to convert the clipboard text to COMPOUND_TEXT.
1139 *
1140 * @returns true if we successfully convert the data to the format requested, false otherwise.
1141 *
1142 * @param atomTypeReturn The type of the data we are returning
1143 * @param pValReturn A pointer to the data we are returning. This should be to memory
1144 * allocated by XtMalloc, which will be freed by the toolkit later
1145 * @param pcLenReturn The length of the data we are returning
1146 * @param piFormatReturn The format (8bit, 16bit, 32bit) of the data we are returning
1147 */
1148static Boolean vboxClipboardConvertCText(Atom *atomTypeReturn, XtPointer *pValReturn,
1149 unsigned long *pcLenReturn, int *piFormatReturn)
1150{
1151 PRTUTF16 pu16SrcText, pu16DestText;
1152 char *pu8DestText = 0;
1153 size_t cwSrcLen, cwDestLen, cbDestLen;
1154 XTextProperty property;
1155 int rc;
1156
1157 LogFlowFunc (("called\n"));
1158 /* Read the clipboard data from the guest. */
1159 rc = vboxClipboardReadDataFromClient(&g_ctx, VBOX_SHARED_CLIPBOARD_FMT_UNICODETEXT);
1160 if ((rc != VINF_SUCCESS) || (g_ctx.pClient->data.cb == 0))
1161 {
1162 /* If vboxClipboardReadDataFromClient fails then pClient may be invalid */
1163 LogRelFunc (("vboxClipboardReadDataFromClient returned %Rrc%s\n", rc,
1164 RT_SUCCESS(rc) ? ", g_ctx.pClient->data.cb == 0" : ""));
1165 vboxClipboardEmptyGuestBuffer();
1166 return false;
1167 }
1168 pu16SrcText = reinterpret_cast<PRTUTF16>(g_ctx.pClient->data.pv);
1169 cwSrcLen = g_ctx.pClient->data.cb / 2;
1170 /* How long will the converted text be? */
1171 rc = vboxClipboardUtf16GetLinSize(pu16SrcText, cwSrcLen, &cwDestLen);
1172 if (RT_FAILURE(rc))
1173 {
1174 LogRel(("vboxClipboardConvertCText: clipboard conversion failed. vboxClipboardUtf16GetLinSize returned %Rrc. Abandoning.\n", rc));
1175 vboxClipboardEmptyGuestBuffer();
1176 AssertRCReturn(rc, false);
1177 }
1178 if (cwDestLen == 0)
1179 {
1180 LogFlowFunc(("received empty clipboard data from the guest, returning false.\n"));
1181 vboxClipboardEmptyGuestBuffer();
1182 return false;
1183 }
1184 pu16DestText = reinterpret_cast<PRTUTF16>(RTMemAlloc(cwDestLen * 2));
1185 if (pu16DestText == 0)
1186 {
1187 LogRel(("vboxClipboardConvertCText: failed to allocate %d bytes\n", cwDestLen * 2));
1188 vboxClipboardEmptyGuestBuffer();
1189 return false;
1190 }
1191 /* Convert the text. */
1192 rc = vboxClipboardUtf16WinToLin(pu16SrcText, cwSrcLen, pu16DestText, cwDestLen);
1193 if (RT_FAILURE(rc))
1194 {
1195 LogRel(("vboxClipboardConvertCText: clipboard conversion failed. vboxClipboardUtf16WinToLin() returned %Rrc. Abandoning.\n", rc));
1196 RTMemFree(reinterpret_cast<void *>(pu16DestText));
1197 vboxClipboardEmptyGuestBuffer();
1198 return false;
1199 }
1200 /* Convert the Utf16 string to Utf8. */
1201 rc = RTUtf16ToUtf8Ex(pu16DestText + 1, cwDestLen - 1, &pu8DestText, 0, &cbDestLen);
1202 RTMemFree(reinterpret_cast<void *>(pu16DestText));
1203 if (RT_FAILURE(rc))
1204 {
1205 LogRel(("vboxClipboardConvertCText: clipboard conversion failed. RTUtf16ToUtf8Ex() returned %Rrc. Abandoning.\n", rc));
1206 vboxClipboardEmptyGuestBuffer();
1207 return false;
1208 }
1209 /* And finally (!) convert the Utf8 text to compound text. */
1210#ifdef RT_OS_SOLARIS
1211 rc = XmbTextListToTextProperty(XtDisplay(g_ctx.widget), &pu8DestText, 1,
1212 XCompoundTextStyle, &property);
1213#else
1214 rc = Xutf8TextListToTextProperty(XtDisplay(g_ctx.widget), &pu8DestText, 1,
1215 XCompoundTextStyle, &property);
1216#endif
1217 RTMemFree(pu8DestText);
1218 if (rc < 0)
1219 {
1220 const char *pcReason;
1221 switch(rc)
1222 {
1223 case XNoMemory:
1224 pcReason = "out of memory";
1225 break;
1226 case XLocaleNotSupported:
1227 pcReason = "locale (Utf8) not supported";
1228 break;
1229 case XConverterNotFound:
1230 pcReason = "converter not found";
1231 break;
1232 default:
1233 pcReason = "unknown error";
1234 }
1235 LogRel(("vboxClipboardConvertCText: Xutf8TextListToTextProperty failed. Reason: %s\n",
1236 pcReason));
1237 XFree(property.value);
1238 vboxClipboardEmptyGuestBuffer();
1239 return false;
1240 }
1241 LogFlowFunc (("converted string is %s. Returning.\n", property.value));
1242 vboxClipboardEmptyGuestBuffer();
1243 *atomTypeReturn = property.encoding;
1244 *pValReturn = reinterpret_cast<XtPointer>(property.value);
1245 *pcLenReturn = property.nitems;
1246 *piFormatReturn = property.format;
1247 return true;
1248}
1249
1250/**
1251 * Callback to convert the guests clipboard data for an application on the host. Called by the
1252 * X Toolkit.
1253 * @returns true if we successfully convert the data to the format requested, false otherwise.
1254 *
1255 * @param atomSelection The selection which is being requested. We only handle the clipboard.
1256 * @param atomTarget The format we should convert the data to
1257 * @param atomTypeReturn The type of the data we are returning
1258 * @param pValReturn A pointer to the data we are returning. This should be to memory
1259 * allocated by XtMalloc, which will be freed by the toolkit later
1260 * @param pcLenReturn The length of the data we are returning
1261 * @param piFormatReturn The format (8bit, 16bit, 32bit) of the data we are returning
1262 */
1263static Boolean vboxClipboardConvertProc(Widget, Atom *atomSelection, Atom *atomTarget,
1264 Atom *atomTypeReturn, XtPointer *pValReturn,
1265 unsigned long *pcLenReturn, int *piFormatReturn)
1266{
1267 g_eClipboardFormats eFormat = INVALID;
1268
1269 LogFlowFunc(("\n"));
1270 if ( (*atomSelection != g_ctx.atomClipboard)
1271 && (*atomSelection != g_ctx.atomPrimary)
1272 )
1273 {
1274 LogFlowFunc(("rc = false\n"));
1275 return false;
1276 }
1277 if (g_debugClipboard)
1278 {
1279 char *szAtomName = XGetAtomName(XtDisplay(g_ctx.widget), *atomTarget);
1280 if (szAtomName != 0)
1281 {
1282 Log2 (("vboxClipboardConvertProc: request for format %s\n", szAtomName));
1283 XFree(szAtomName);
1284 }
1285 else
1286 {
1287 Log(("vboxClipboardConvertProc: request for invalid target atom %d!\n", *atomTarget));
1288 }
1289 }
1290 if (*atomTarget == g_ctx.atomTargets)
1291 {
1292 eFormat = TARGETS;
1293 }
1294 else
1295 {
1296 for (unsigned i = 0; i != g_ctx.formatList.size(); ++i)
1297 {
1298 if (g_ctx.formatList[i].atom == *atomTarget)
1299 {
1300 eFormat = g_ctx.formatList[i].format;
1301 break;
1302 }
1303 }
1304 }
1305 switch (eFormat)
1306 {
1307 case TARGETS:
1308 return vboxClipboardConvertTargets(atomTypeReturn, pValReturn, pcLenReturn,
1309 piFormatReturn);
1310 case UTF16:
1311 return vboxClipboardConvertUtf16(atomTypeReturn, pValReturn, pcLenReturn,
1312 piFormatReturn);
1313 case UTF8:
1314 return vboxClipboardConvertUtf8(atomTypeReturn, pValReturn, pcLenReturn,
1315 piFormatReturn);
1316 case CTEXT:
1317 return vboxClipboardConvertCText(atomTypeReturn, pValReturn, pcLenReturn,
1318 piFormatReturn);
1319 default:
1320 Log(("vboxClipboardConvertProc: bad format\n"));
1321 return false;
1322 }
1323}
1324
1325static void vboxClipboardLoseProc(Widget, Atom *)
1326{
1327 LogFlow (("vboxClipboardLoseProc: called, giving the host clipboard ownership\n"));
1328 g_ctx.eOwner = HOST;
1329 g_ctx.notifyGuest = true;
1330}
1331
1332/**
1333 * The guest is taking possession of the shared clipboard. Called by the HGCM clipboard
1334 * subsystem.
1335 *
1336 * @param pClient Context data for the guest system
1337 * @param u32Formats Clipboard formats the guest is offering
1338 */
1339void vboxClipboardFormatAnnounce (VBOXCLIPBOARDCLIENTDATA *pClient, uint32_t u32Formats)
1340{
1341 /*
1342 * Immetiately return if we are not connected to the host X server.
1343 */
1344 if (!g_fHaveX11)
1345 return;
1346
1347 pClient->pCtx->guestFormats = u32Formats;
1348 LogFlowFunc (("u32Formats=%d\n", u32Formats));
1349 if (u32Formats == 0)
1350 {
1351 /* This is just an automatism, not a genuine anouncement */
1352 LogFlowFunc(("returning\n"));
1353 return;
1354 }
1355 if (g_ctx.eOwner == GUEST)
1356 {
1357 /* We already own the clipboard, so no need to grab it, especially as that can lead
1358 to races due to the asynchronous nature of the X11 clipboard. This event may also
1359 have been sent out by the guest to invalidate the Windows clipboard cache. */
1360 LogFlowFunc(("returning\n"));
1361 return;
1362 }
1363 Log2 (("vboxClipboardFormatAnnounce: giving the guest clipboard ownership\n"));
1364 g_ctx.eOwner = GUEST;
1365 g_ctx.hostTextFormat = INVALID;
1366 g_ctx.hostBitmapFormat = INVALID;
1367 if (XtOwnSelection(g_ctx.widget, g_ctx.atomClipboard, CurrentTime, vboxClipboardConvertProc,
1368 vboxClipboardLoseProc, 0) != True)
1369 {
1370 Log2 (("vboxClipboardFormatAnnounce: returning clipboard ownership to the host\n"));
1371 /* We set this so that the guest gets notified when we take the clipboard, even if no
1372 guest formats are found which we understand. */
1373 g_ctx.notifyGuest = true;
1374 g_ctx.eOwner = HOST;
1375 }
1376 XtOwnSelection(g_ctx.widget, g_ctx.atomPrimary, CurrentTime, vboxClipboardConvertProc,
1377 NULL, 0);
1378 LogFlowFunc(("returning\n"));
1379
1380}
1381
1382/**
1383 * Called by the HGCM clipboard subsystem when the guest wants to read the host clipboard.
1384 *
1385 * @param pClient Context information about the guest VM
1386 * @param u32Format The format that the guest would like to receive the data in
1387 * @param pv Where to write the data to
1388 * @param cb The size of the buffer to write the data to
1389 * @param pcbActual Where to write the actual size of the written data
1390 */
1391int vboxClipboardReadData (VBOXCLIPBOARDCLIENTDATA *pClient, uint32_t u32Format, void *pv,
1392 uint32_t cb, uint32_t *pcbActual)
1393{
1394 /*
1395 * Immetiately return if we are not connected to the host X server.
1396 */
1397 if (!g_fHaveX11)
1398 {
1399 /* no data available */
1400 *pcbActual = 0;
1401 return VINF_SUCCESS;
1402 }
1403
1404 LogFlow(("vboxClipboardReadData: u32Format = %d, cb = %d\n", u32Format, cb));
1405
1406 /*
1407 * The guest wants to read data in the given format.
1408 */
1409 if (u32Format & VBOX_SHARED_CLIPBOARD_FMT_UNICODETEXT)
1410 {
1411 if (g_ctx.hostTextFormat == INVALID)
1412 {
1413 /* No data available. */
1414 *pcbActual = 0;
1415 return VERR_NO_DATA; /* The guest thinks we have data and we don't */
1416 }
1417 /* Only one of the host and the guest should ever be waiting. */
1418 if (!ASMAtomicCmpXchgU32(&g_ctx.waiter, 1, 0))
1419 {
1420 LogRel(("vboxClipboardReadData: detected a deadlock situation - the host and the guest are waiting for each other.\n"));
1421 return VERR_DEADLOCK;
1422 }
1423 g_ctx.requestHostFormat = g_ctx.hostTextFormat;
1424 g_ctx.requestBuffer = pv;
1425 g_ctx.requestBufferSize = cb;
1426 g_ctx.requestActualSize = pcbActual;
1427 /* Initially set the size of the data read to zero in case we fail
1428 * somewhere. */
1429 *pcbActual = 0;
1430 /* Send out a request for the data to the current clipboard owner */
1431 XtGetSelectionValue(g_ctx.widget, g_ctx.atomClipboard, g_ctx.atomHostTextFormat,
1432 vboxClipboardGetProc, reinterpret_cast<XtPointer>(g_ctx.pClient),
1433 CurrentTime);
1434 /* When the data arrives, the vboxClipboardGetProc callback will be called. The
1435 callback will signal the event semaphore when it has processed the data for us. */
1436
1437#ifdef RT_OS_SOLARIS
1438 /*
1439 * Trusted Xorg requires a bigger timeout.
1440 */
1441 unsigned long cTimeoutMillies = CLIPBOARDTIMEOUT;
1442 if (is_system_labeled())
1443 cTimeoutMillies = 1000 * 120;
1444
1445 if (RTSemEventWait(g_ctx.waitForData, cTimeoutMillies) != VINF_SUCCESS)
1446#else
1447 if (RTSemEventWait(g_ctx.waitForData, CLIPBOARDTIMEOUT) != VINF_SUCCESS)
1448#endif
1449 {
1450 /* No need to polute the release log for this. */
1451 // LogRel(("vboxClipboardReadDataFromClient: XtGetSelectionValue failed to complete within %d milliseconds\n", CLIPBOARDTIMEOUT));
1452 /* A time out can legitimately occur if a client is temporarily too busy to answer fast */
1453 // g_ctx.hostTextFormat = INVALID;
1454 // g_ctx.hostBitmapFormat = INVALID;
1455 g_ctx.waiter = 0;
1456 return VERR_TIMEOUT;
1457 }
1458 g_ctx.waiter = 0;
1459 }
1460 else
1461 {
1462 return VERR_NOT_IMPLEMENTED;
1463 }
1464 return VINF_SUCCESS;
1465}
1466
1467/**
1468 * Called by the HGCM clipboard subsystem when we have requested data and that data arrives.
1469 *
1470 * @param pClient Context information about the guest VM
1471 * @param pv Buffer to which the data was written
1472 * @param cb The size of the data written
1473 * @param u32Format The format of the data written
1474 */
1475void vboxClipboardWriteData (VBOXCLIPBOARDCLIENTDATA *pClient, void *pv, uint32_t cb, uint32_t u32Format)
1476{
1477 if (!g_fHaveX11)
1478 return;
1479
1480 LogFlow(("vboxClipboardWriteData\n"));
1481
1482 /*
1483 * The guest returns data that was requested in the WM_RENDERFORMAT handler.
1484 */
1485 if (!( pClient->data.pv == NULL
1486 && pClient->data.cb == 0
1487 && pClient->data.u32Format == 0))
1488 {
1489 LogRel(("vboxClipboardWriteData: clipboard data has arrived from the guest, but another transfer is in process or has not cleaned up properly.\n"));
1490 AssertMsgFailed(("vboxClipboardWriteData: clipboard data has arrived from the guest, but another transfer is in process or has not cleaned up properly.\n"));
1491 }
1492
1493 if (cb > 0)
1494 {
1495 pClient->data.pv = RTMemAlloc (cb);
1496
1497 if (pClient->data.pv)
1498 {
1499 memcpy (pClient->data.pv, pv, cb);
1500 pClient->data.cb = cb;
1501 pClient->data.u32Format = u32Format;
1502 }
1503 }
1504
1505 RTSemEventSignal(g_ctx.waitForData);
1506}
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