VirtualBox

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

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

SharedClipboard/x11.cpp: Handle failure of the worker thread better and added a r=bird @todo on the subject of the short timeout. We cannot return failure if the thread is still running because that will lead to a SIGSEGV after dlclose.

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