VirtualBox

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

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

The Big Sun Rebranding Header Change

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