VirtualBox

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

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

HostServices/SharedClipboard: another attempt to fix a segfault in the X11 clipboard

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