VirtualBox

source: vbox/trunk/src/VBox/HostServices/SharedClipboard/linux.cpp@ 7410

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

Better cleanup on shared clipboard initialization failure.

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