VirtualBox

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

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

Solaris: Host-side Shared clipboard lib, some fixes to clipboard-new and
more flow logging. The previous problem of invalid client Id is fixed but
seems there's more work here to be fixed, most possibly in the r3 guest
clipboard side.

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