VirtualBox

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

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

The Giant CDDL Dual-License Header Change.

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 63.9 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 rc = Xutf8TextPropertyToTextList(XtDisplay(g_ctx.widget), &property, &ppu8SrcText, &cProps);
631 XtFree(reinterpret_cast<char *>(pValue));
632 if (rc < 0)
633 {
634 const char *pcReason;
635 switch(rc)
636 {
637 case XNoMemory:
638 pcReason = "out of memory";
639 break;
640 case XLocaleNotSupported:
641 pcReason = "locale (Utf8) not supported";
642 break;
643 case XConverterNotFound:
644 pcReason = "converter not found";
645 break;
646 default:
647 pcReason = "unknown error";
648 }
649 XFreeStringList(ppu8SrcText);
650 LogRel(("vboxClipboardGetCText: Xutf8TextPropertyToTextList failed. Reason: %s\n",
651 pcReason));
652 *pcbActual = 0;
653 RTSemEventSignal(g_ctx.waitForData);
654 return;
655 }
656 /* Now convert the UTF8 to UTF16 */
657 rc = RTStrToUtf16Ex(*ppu8SrcText, cbSrcLen, &pu16SrcText, 0, &cwSrcLen);
658 XFreeStringList(ppu8SrcText);
659 if (RT_FAILURE(rc))
660 {
661 LogRel(("vboxClipboardGetCText: clipboard conversion failed. RTStrToUtf16Ex returned %Vrc. Abandoning.\n", rc));
662 *pcbActual = 0;
663 RTSemEventSignal(g_ctx.waitForData);
664 return;
665 }
666 /* Check how much longer will the converted text will be. */
667 rc = vboxClipboardUtf16GetWinSize(pu16SrcText, cwSrcLen, &cwDestLen);
668 if (RT_FAILURE(rc))
669 {
670 LogRel(("vboxClipboardGetCText: clipboard conversion failed. vboxClipboardUtf16GetLinSize returned %Vrc. Abandoning.\n", rc));
671 LogFlowFunc (("guest buffer too small: size %d bytes, needed %d. Returning.\n",
672 cb, cwDestLen * 2));
673 RTUtf16Free(pu16SrcText);
674 *pcbActual = cwDestLen * 2;
675 RTSemEventSignal(g_ctx.waitForData);
676 AssertReturnVoid(RT_SUCCESS(rc));
677 }
678 if (cb < cwDestLen * 2)
679 {
680 RTUtf16Free(pu16SrcText);
681 /* Report the amount of buffer space needed for the transfer */
682 LogFlowFunc (("guest buffer too small: size %d bytes, needed %d. Returning.\n",
683 cb, cwDestLen * 2));
684 *pcbActual = cwDestLen * 2;
685 RTSemEventSignal(g_ctx.waitForData);
686 return;
687 }
688 /* Convert the text. */
689 rc = vboxClipboardUtf16LinToWin(pu16SrcText, cwSrcLen, pu16DestText, cb / 2);
690 RTUtf16Free(pu16SrcText);
691 if (RT_FAILURE(rc))
692 {
693 LogRel(("vboxClipboardGetCText: clipboard conversion failed. vboxClipboardUtf16LinToWin returned %Vrc. Abandoning.\n", rc));
694 *pcbActual = 0;
695 RTSemEventSignal(g_ctx.waitForData);
696 return;
697 }
698 LogFlowFunc (("converted string is %.*ls. Returning.\n", cwDestLen, pu16DestText));
699 *pcbActual = cwDestLen * 2;
700 RTSemEventSignal(g_ctx.waitForData);
701}
702
703/**
704 * Convert the Latin1 text returned from the X11 clipboard to UTF-16LE with Windows EOLS
705 * and place it in the global g_pcClipboardText variable. We are reading the host clipboard to
706 * make it available to the guest.
707 *
708 * @param pValue Source Latin1 text
709 * @param cbSourceLen Length in 8-bit bytes of the source text
710 * @param pv Where to store the converted data
711 * @param cb Length in bytes of the buffer pointed to by cb
712 * @param pcbActual Where to store the size of the converted data
713 * @param pClient Pointer to the client context structure
714 */
715static void vboxClipboardGetLatin1(XtPointer pValue, unsigned cbSourceLen, void *pv, unsigned cb,
716 uint32_t *pcbActual)
717{
718 unsigned cwDestLen = cbSourceLen + 1;
719 char *pu8SourceText = reinterpret_cast<char *>(pValue);
720 PRTUTF16 pu16DestText = reinterpret_cast<PRTUTF16>(pv);
721
722 LogFlow (("vboxClipboardGetLatin1: converting Latin1 to Utf-16LE. Original is %.*s\n",
723 cbSourceLen, pu8SourceText));
724 for (unsigned i = 0; i < cbSourceLen; i++)
725 if (pu8SourceText[i] == LINEFEED)
726 ++cwDestLen;
727 if (cb < cwDestLen * 2)
728 {
729 XtFree(reinterpret_cast<char *>(pValue));
730 /* Report the amount of buffer space needed for the transfer */
731 Log2 (("vboxClipboardGetLatin1: guest buffer too small: size %d bytes\n", cb));
732 *pcbActual = cwDestLen * 2;
733 RTSemEventSignal(g_ctx.waitForData);
734 return;
735 }
736 for (unsigned i = 0, j = 0; i < cbSourceLen; ++i, ++j)
737 if (pu8SourceText[i] != LINEFEED)
738 pu16DestText[j] = pu8SourceText[i]; /* latin1 < utf-16LE */
739 else
740 {
741 pu16DestText[j] = CARRIAGERETURN;
742 ++j;
743 pu16DestText[j] = LINEFEED;
744 }
745 pu16DestText[cwDestLen - 1] = 0;
746 *pcbActual = cwDestLen * 2;
747 Log2 (("vboxClipboardGetLatin1: converted text is %.*ls\n", cwDestLen, pu16DestText));
748 XtFree(reinterpret_cast<char *>(pValue));
749 RTSemEventSignal(g_ctx.waitForData);
750}
751
752/** Convert the clipboard text from the current format to Utf-16 with Windows line breaks.
753 We are reading the host clipboard to make it available to the guest. */
754static void vboxClipboardGetProc(Widget, XtPointer pClientData, Atom * /* selection */,
755 Atom *atomType, XtPointer pValue, long unsigned int *pcLen,
756 int *piFormat)
757{
758 LogFlowFunc(("pClientData=%p, *pcLen=%lu, *piFormat=%d\n", pClientData, *pcLen, *piFormat));
759 LogFlowFunc(("g_ctx.requestHostFormat=%d, g_ctx.requestBufferSize=%d\n",
760 g_ctx.requestHostFormat, g_ctx.requestBufferSize));
761 unsigned cTextLen = (*pcLen) * (*piFormat) / 8;
762 /* The X Toolkit may have failed to get the clipboard selection for us. */
763 if (*atomType == XT_CONVERT_FAIL)
764 return;
765 /* We grab this mutex whenever an asynchronous clipboard operation completes and while
766 disconnecting a client from the clipboard to stop these operations colliding. */
767 RTSemMutexRequest(g_ctx.asyncMutex, RT_INDEFINITE_WAIT);
768 if (reinterpret_cast<VBOXCLIPBOARDCLIENTDATA *>(pClientData) != g_ctx.pClient)
769 {
770 /* If the client is no longer connected, just return. */
771 XtFree(reinterpret_cast<char *>(pValue));
772 LogFlowFunc(("client is no longer connected, returning\n"));
773 RTSemMutexRelease(g_ctx.asyncMutex);
774 return;
775 }
776
777 /* In which format did we request the clipboard data? */
778 switch (g_ctx.requestHostFormat)
779 {
780 case UTF16:
781 vboxClipboardGetUtf16(pValue, cTextLen / 2, g_ctx.requestBuffer, g_ctx.requestBufferSize,
782 g_ctx.requestActualSize);
783 break;
784 case CTEXT:
785 vboxClipboardGetCText(pValue, cTextLen, g_ctx.requestBuffer, g_ctx.requestBufferSize,
786 g_ctx.requestActualSize);
787 break;
788 case UTF8:
789 {
790 /* If we are given broken Utf-8, we treat it as Latin1. Is this acceptable? */
791 size_t cStringLen;
792 char *pu8SourceText = reinterpret_cast<char *>(pValue);
793
794 if ((g_ctx.requestHostFormat == UTF8)
795 && (RTStrUniLenEx(pu8SourceText, *pcLen, &cStringLen) == VINF_SUCCESS))
796 {
797 vboxClipboardGetUtf8(pValue, cTextLen, g_ctx.requestBuffer, g_ctx.requestBufferSize,
798 g_ctx.requestActualSize);
799 break;
800 }
801 else
802 {
803 vboxClipboardGetLatin1(pValue, cTextLen, g_ctx.requestBuffer, g_ctx.requestBufferSize,
804 g_ctx.requestActualSize);
805 break;
806 }
807 }
808 default:
809 Log (("vboxClipboardGetProc: bad target format\n"));
810 XtFree(reinterpret_cast<char *>(pValue));
811 RTSemMutexRelease(g_ctx.asyncMutex);
812 return;
813 }
814 g_ctx.notifyGuest = true;
815 RTSemMutexRelease(g_ctx.asyncMutex);
816}
817
818/** Callback to handle a reply to a request for the targets the current clipboard holder can
819 handle. We are reading the host clipboard to make it available to the guest. */
820static void vboxClipboardTargetsProc(Widget, XtPointer pClientData, Atom * /* selection */,
821 Atom *atomType, XtPointer pValue, long unsigned int *pcLen,
822 int *piFormat)
823{
824 Atom *atomTargets = reinterpret_cast<Atom *>(pValue);
825 unsigned cAtoms = *pcLen;
826 g_eClipboardFormats eBestTarget = INVALID;
827 Atom atomBestTarget = None;
828
829 Log3 (("vboxClipboardTargetsProc called\n"));
830 if (*atomType == XT_CONVERT_FAIL)
831 {
832 Log (("vboxClipboardTargetsProc: reading clipboard from host, X toolkit failed to convert the selection\n"));
833 return;
834 }
835 /* We grab this mutex whenever an asynchronous clipboard operation completes and while
836 disconnecting a client from the clipboard to stop these operations colliding. */
837 RTSemMutexRequest(g_ctx.asyncMutex, RT_INDEFINITE_WAIT);
838 if (reinterpret_cast<VBOXCLIPBOARDCLIENTDATA *>(pClientData) != g_ctx.pClient)
839 {
840 /* If the client is no longer connected, just return. */
841 LogFlowFunc(("client is no longer connected, returning\n"));
842 RTSemMutexRelease(g_ctx.asyncMutex);
843 return;
844 }
845
846 for (unsigned i = 0; i < cAtoms; ++i)
847 {
848 for (unsigned j = 0; j != g_ctx.formatList.size(); ++j)
849 if (g_ctx.formatList[j].atom == atomTargets[i])
850 {
851 if (eBestTarget < g_ctx.formatList[j].format)
852 {
853 eBestTarget = g_ctx.formatList[j].format;
854 atomBestTarget = g_ctx.formatList[j].atom;
855 }
856 break;
857 }
858#ifdef DEBUG
859 char *szAtomName = XGetAtomName(XtDisplay(g_ctx.widget), atomTargets[i]);
860 if (szAtomName != 0)
861 {
862 Log3 (("vboxClipboardTargetsProc: the host offers target %s\n", szAtomName));
863 XFree(szAtomName);
864 }
865#endif
866 }
867 g_ctx.atomHostTextFormat = atomBestTarget;
868 if ((eBestTarget != g_ctx.hostTextFormat) || (g_ctx.notifyGuest == true))
869 {
870 uint32_t u32Formats = 0;
871#ifdef DEBUG
872 if (atomBestTarget != None)
873 {
874 char *szAtomName = XGetAtomName(XtDisplay(g_ctx.widget), atomBestTarget);
875 Log2 (("vboxClipboardTargetsProc: switching to host text target %s. Available targets are:\n",
876 szAtomName));
877 XFree(szAtomName);
878 }
879 else
880 {
881 Log2(("vboxClipboardTargetsProc: no supported host text target found. Available targets are:\n"));
882 }
883 for (unsigned i = 0; i < cAtoms; ++i)
884 {
885 char *szAtomName = XGetAtomName(XtDisplay(g_ctx.widget), atomTargets[i]);
886 if (szAtomName != 0)
887 {
888 Log2 (("vboxClipboardTargetsProc: %s\n", szAtomName));
889 XFree(szAtomName);
890 }
891 }
892#endif
893 g_ctx.hostTextFormat = eBestTarget;
894 if (eBestTarget != INVALID)
895 u32Formats |= VBOX_SHARED_CLIPBOARD_FMT_UNICODETEXT;
896 vboxSvcClipboardReportMsg (g_ctx.pClient, VBOX_SHARED_CLIPBOARD_HOST_MSG_FORMATS,
897 u32Formats);
898 g_ctx.notifyGuest = false;
899 }
900 XtFree(reinterpret_cast<char *>(pValue));
901 RTSemMutexRelease(g_ctx.asyncMutex);
902}
903
904/**
905 * This callback is called every 200ms to check the contents of the host clipboard.
906 */
907static void vboxClipboardTimerProc(XtPointer /* pUserData */, XtIntervalId * /* hTimerId */)
908{
909 Log3 (("vboxClipboardTimerProc called\n"));
910 /* Get the current clipboard contents */
911 if (g_ctx.eOwner == HOST && g_ctx.pClient != 0)
912 {
913 Log3 (("vboxClipboardTimerProc: requesting the targets that the host clipboard offers\n"));
914 XtGetSelectionValue(g_ctx.widget, g_ctx.atomClipboard, g_ctx.atomTargets,
915 vboxClipboardTargetsProc, reinterpret_cast<XtPointer>(g_ctx.pClient),
916 CurrentTime);
917 }
918 /* Re-arm our timer */
919 XtAppAddTimeOut(g_ctx.appContext, 200 /* ms */, vboxClipboardTimerProc, 0);
920}
921
922/** We store information about the target formats we can handle in a global vector for internal
923 use. */
924static void vboxClipboardAddFormat(const char *pszName, g_eClipboardFormats eFormat,
925 unsigned guestFormat)
926{
927 VBOXCLIPBOARDFORMAT sFormat;
928 /* Get an atom from the X server for that target format */
929 Atom atomFormat = XInternAtom(XtDisplay(g_ctx.widget), pszName, false);
930 sFormat.atom = atomFormat;
931 sFormat.format = eFormat;
932 sFormat.guestFormat = guestFormat;
933 g_ctx.formatList.push_back(sFormat);
934 LogFlow (("vboxClipboardAddFormat: added format %s (%d)\n", pszName, eFormat));
935}
936
937/**
938 * The main loop of our clipboard reader.
939 */
940static int vboxClipboardThread(RTTHREAD self, void * /* pvUser */)
941{
942 /* Create a window and make it a clipboard viewer. */
943 int cArgc = 0;
944 char *pcArgv = 0;
945 int rc = VINF_SUCCESS;
946 String szFallbackResources[] = { (char*)"*.width: 1", (char*)"*.height: 1", 0 };
947 Display *pDisplay;
948 LogRel (("vboxClipboardThread: starting clipboard thread\n"));
949
950 /* Make sure we are thread safe */
951 XtToolkitThreadInitialize();
952 /* Set up the Clipbard application context and main window. We call all these functions
953 directly instead of calling XtOpenApplication() so that we can fail gracefully if we
954 can't get an X11 display. */
955 XtToolkitInitialize();
956 g_ctx.appContext = XtCreateApplicationContext();
957 XtAppSetFallbackResources(g_ctx.appContext, szFallbackResources);
958 pDisplay = XtOpenDisplay(g_ctx.appContext, 0, 0, "VBoxClipboard", 0, 0, &cArgc, &pcArgv);
959 if (pDisplay == 0)
960 {
961 LogRel(("vboxClipboardThread: failed to connect to the host clipboard - the window system may not be running.\n"));
962 return VERR_NOT_SUPPORTED;
963 }
964 g_ctx.widget = XtAppCreateShell(0, "VBoxClipboard", applicationShellWidgetClass, pDisplay,
965 0, 0);
966 if (g_ctx.widget == 0)
967 {
968 LogRel(("vboxClipboardThread: failed to construct the X11 window for the clipboard manager.\n"));
969 AssertReturn(g_ctx.widget != 0, VERR_ACCESS_DENIED);
970 }
971 RTThreadUserSignal(self);
972 XtSetMappedWhenManaged(g_ctx.widget, false);
973 XtRealizeWidget(g_ctx.widget);
974
975 /* Get hold of the atoms which we need */
976 g_ctx.atomClipboard = XInternAtom(XtDisplay(g_ctx.widget), "CLIPBOARD", false /* only_if_exists */);
977 g_ctx.atomTargets = XInternAtom(XtDisplay(g_ctx.widget), "TARGETS", false);
978 g_ctx.atomMultiple = XInternAtom(XtDisplay(g_ctx.widget), "MULTIPLE", false);
979 g_ctx.atomTimestamp = XInternAtom(XtDisplay(g_ctx.widget), "TIMESTAMP", false);
980 g_ctx.atomUtf16 = XInternAtom(XtDisplay(g_ctx.widget),
981 "text/plain;charset=ISO-10646-UCS-2", false);
982 g_ctx.atomUtf8 = XInternAtom(XtDisplay(g_ctx.widget), "UTF_STRING", false);
983 /* And build up the vector of supported formats */
984 g_ctx.atomCText = XInternAtom(XtDisplay(g_ctx.widget), "COMPOUND_TEXT", false);
985 /* And build up the vector of supported formats */
986#ifdef USE_UTF16
987 vboxClipboardAddFormat("text/plain;charset=ISO-10646-UCS-2", UTF16,
988 VBOX_SHARED_CLIPBOARD_FMT_UNICODETEXT);
989#endif
990#ifdef USE_UTF8
991 vboxClipboardAddFormat("UTF8_STRING", UTF8,
992 VBOX_SHARED_CLIPBOARD_FMT_UNICODETEXT);
993 vboxClipboardAddFormat("text/plain;charset=UTF-8", UTF8,
994 VBOX_SHARED_CLIPBOARD_FMT_UNICODETEXT);
995 vboxClipboardAddFormat("text/plain;charset=utf-8", UTF8,
996 VBOX_SHARED_CLIPBOARD_FMT_UNICODETEXT);
997 vboxClipboardAddFormat("STRING", UTF8,
998 VBOX_SHARED_CLIPBOARD_FMT_UNICODETEXT);
999 vboxClipboardAddFormat("TEXT", UTF8,
1000 VBOX_SHARED_CLIPBOARD_FMT_UNICODETEXT);
1001 vboxClipboardAddFormat("text/plain", UTF8,
1002 VBOX_SHARED_CLIPBOARD_FMT_UNICODETEXT);
1003#endif
1004#ifdef USE_CTEXT
1005 vboxClipboardAddFormat("COMPOUND_TEXT", CTEXT,
1006 VBOX_SHARED_CLIPBOARD_FMT_UNICODETEXT);
1007#endif
1008
1009 /* Set up a timer to poll the host clipboard */
1010 XtAppAddTimeOut(g_ctx.appContext, 200 /* ms */, vboxClipboardTimerProc, 0);
1011
1012 XtAppMainLoop(g_ctx.appContext);
1013 g_ctx.formatList.clear();
1014 RTSemEventDestroy(g_ctx.waitForData);
1015 RTSemMutexDestroy(g_ctx.asyncMutex);
1016 XtCloseDisplay(pDisplay);
1017 LogRel (("vboxClipboardThread: clipboard thread terminated successfully with return code %Vrc\n", rc));
1018 return rc;
1019}
1020
1021/** Initialise the host side of the shared clipboard - called by the hgcm layer. */
1022int vboxClipboardInit (void)
1023{
1024 int rc;
1025
1026 LogRel(("vboxClipboardInit: initializing host clipboard\n"));
1027 RTSemEventCreate(&g_ctx.waitForData);
1028 RTSemMutexCreate(&g_ctx.asyncMutex);
1029 rc = RTThreadCreate(&g_ctx.thread, vboxClipboardThread, 0, 0, RTTHREADTYPE_IO,
1030 RTTHREADFLAGS_WAITABLE, "SHCLIP");
1031 if (RT_FAILURE(rc))
1032 {
1033 LogRel(("vboxClipboardInit: failed to create the clipboard thread.\n"));
1034 AssertRCReturn(rc, rc);
1035 }
1036 return RTThreadUserWait(g_ctx.thread, 1000);
1037}
1038
1039/** Terminate the host side of the shared clipboard - called by the hgcm layer. */
1040void vboxClipboardDestroy (void)
1041{
1042 LogRel(("vboxClipboardDestroy: shutting down host clipboard\n"));
1043 int rc, rcThread;
1044 XEvent ev;
1045
1046 /* Set the termination flag. */
1047 XtAppSetExitFlag(g_ctx.appContext);
1048 /* Wake up the event loop */
1049 memset(&ev, 0, sizeof(ev));
1050 ev.xclient.type = ClientMessage;
1051 ev.xclient.format = 8;
1052 XSendEvent(XtDisplay(g_ctx.widget), XtWindow(g_ctx.widget), false, 0, &ev);
1053 XFlush(XtDisplay(g_ctx.widget));
1054 rc = RTThreadWait(g_ctx.thread, 2000, &rcThread);
1055 AssertRC(rc);
1056 AssertRC(rcThread);
1057 LogFlowFunc(("returning.\n"));
1058}
1059
1060/**
1061 * Enable the shared clipboard - called by the hgcm clipboard subsystem.
1062 *
1063 * @param pClient Structure containing context information about the guest system
1064 * @returns RT status code
1065 */
1066int vboxClipboardConnect (VBOXCLIPBOARDCLIENTDATA *pClient)
1067{
1068 LogFlow(("vboxClipboardConnect\n"));
1069
1070 /* Only one client is supported for now */
1071 if (g_ctx.pClient != 0)
1072 {
1073 LogRel(("vboxClipboardConnect: attempted to connect, but a client appears to be already running.\n"));
1074 AssertReturn(g_ctx.pClient == 0, VERR_NOT_SUPPORTED);
1075 }
1076
1077 pClient->pCtx = &g_ctx;
1078 pClient->pCtx->pClient = pClient;
1079 g_ctx.eOwner = HOST;
1080 g_ctx.notifyGuest = true;
1081 return VINF_SUCCESS;
1082}
1083
1084/**
1085 * Synchronise the contents of the host clipboard with the guest, called by the HGCM layer
1086 * after a save and restore of the guest.
1087 */
1088int vboxClipboardSync (VBOXCLIPBOARDCLIENTDATA *pClient)
1089{
1090 /* On a Linux host, the guest should never synchronise/cache its clipboard contents, as
1091 we have no way of reliably telling when the host clipboard data changes. So instead
1092 of synchronising, we tell the guest to empty its clipboard, and we set the cached
1093 flag so that we report formats to the guest next time we poll for them. */
1094 vboxSvcClipboardReportMsg (g_ctx.pClient, VBOX_SHARED_CLIPBOARD_HOST_MSG_FORMATS, 0);
1095 g_ctx.notifyGuest = true;
1096
1097 return VINF_SUCCESS;
1098}
1099
1100/**
1101 * Shut down the shared clipboard subsystem and "disconnect" the guest.
1102 */
1103void vboxClipboardDisconnect (VBOXCLIPBOARDCLIENTDATA *pClient)
1104{
1105 LogFlow(("vboxClipboardDisconnect\n"));
1106
1107 RTSemMutexRequest(g_ctx.asyncMutex, RT_INDEFINITE_WAIT);
1108 g_ctx.pClient = NULL;
1109 g_ctx.eOwner = NONE;
1110 g_ctx.hostTextFormat = INVALID;
1111 g_ctx.hostBitmapFormat = INVALID;
1112 RTSemMutexRelease(g_ctx.asyncMutex);
1113}
1114
1115/**
1116 * Satisfy a request from the host for available clipboard targets.
1117 *
1118 * @returns true if we successfully convert the data to the format requested, false otherwise.
1119 *
1120 * @param atomTypeReturn The type of the data we are returning
1121 * @param pValReturn A pointer to the data we are returning. This should be to memory
1122 * allocated by XtMalloc, which will be freed by the toolkit later
1123 * @param pcLenReturn The length of the data we are returning
1124 * @param piFormatReturn The format (8bit, 16bit, 32bit) of the data we are returning
1125 */
1126static Boolean vboxClipboardConvertTargets(Atom *atomTypeReturn, XtPointer *pValReturn,
1127 unsigned long *pcLenReturn, int *piFormatReturn)
1128{
1129 unsigned uListSize = g_ctx.formatList.size();
1130 Atom *atomTargets = reinterpret_cast<Atom *>(XtMalloc((uListSize + 3) * sizeof(Atom)));
1131 unsigned cTargets = 0;
1132
1133 LogFlow (("vboxClipboardConvertTargets called\n"));
1134 for (unsigned i = 0; i < uListSize; ++i)
1135 {
1136 if ( ((g_ctx.guestFormats & VBOX_SHARED_CLIPBOARD_FMT_UNICODETEXT) != 0)
1137 && (g_ctx.formatList[i].guestFormat == VBOX_SHARED_CLIPBOARD_FMT_UNICODETEXT))
1138 {
1139 atomTargets[cTargets] = g_ctx.formatList[i].atom;
1140 ++cTargets;
1141 }
1142 }
1143 atomTargets[cTargets] = g_ctx.atomTargets;
1144 atomTargets[cTargets + 1] = g_ctx.atomMultiple;
1145 atomTargets[cTargets + 2] = g_ctx.atomTimestamp;
1146#ifdef DEBUG
1147 for (unsigned i = 0; i < cTargets + 3; i++)
1148 {
1149 char *szAtomName = XGetAtomName(XtDisplay(g_ctx.widget), atomTargets[i]);
1150 if (szAtomName != 0)
1151 {
1152 Log2 (("vboxClipboardConvertTargets: returning target %s\n", szAtomName));
1153 XFree(szAtomName);
1154 }
1155 else
1156 {
1157 Log(("vboxClipboardConvertTargets: invalid atom %d in the list!\n", atomTargets[i]));
1158 }
1159 }
1160#endif
1161 *atomTypeReturn = XA_ATOM;
1162 *pValReturn = reinterpret_cast<XtPointer>(atomTargets);
1163 *pcLenReturn = cTargets + 3;
1164 *piFormatReturn = 32;
1165 return true;
1166}
1167
1168/**
1169 * Reset the contents of the buffer used to pass clipboard data from the guest to the host.
1170 * This must be done after every clipboard transfer.
1171 */
1172static void vboxClipboardEmptyGuestBuffer(void)
1173{
1174 if (g_ctx.pClient->data.pv != 0)
1175 RTMemFree(g_ctx.pClient->data.pv);
1176 g_ctx.pClient->data.pv = 0;
1177 g_ctx.pClient->data.cb = 0;
1178 g_ctx.pClient->data.u32Format = 0;
1179}
1180
1181/**
1182 * Satisfy a request from the host to convert the clipboard text to Utf16. We return non-zero
1183 * terminated text.
1184 *
1185 * @returns true if we successfully convert the data to the format requested, false otherwise.
1186 *
1187 * @retval atomTypeReturn The type of the data we are returning
1188 * @retval pValReturn A pointer to the data we are returning. This should be to memory
1189 * allocated by XtMalloc, which will be freed by the toolkit later
1190 * @retval pcLenReturn The length of the data we are returning
1191 * @retval piFormatReturn The format (8bit, 16bit, 32bit) of the data we are returning
1192 */
1193static Boolean vboxClipboardConvertUtf16(Atom *atomTypeReturn, XtPointer *pValReturn,
1194 unsigned long *pcLenReturn, int *piFormatReturn)
1195{
1196 PRTUTF16 pu16SrcText, pu16DestText;
1197 size_t cwSrcLen, cwDestLen;
1198 int rc;
1199
1200 LogFlowFunc (("called\n"));
1201 rc = vboxClipboardReadDataFromClient(&g_ctx, VBOX_SHARED_CLIPBOARD_FMT_UNICODETEXT);
1202 if ((rc != VINF_SUCCESS) || (g_ctx.pClient->data.cb == 0))
1203 {
1204 LogRel (("vboxClipboardConvertUtf16: vboxClipboardReadDataFromClient returned %Vrc, %d bytes of data\n", rc, g_ctx.pClient->data.cb));
1205 vboxClipboardEmptyGuestBuffer();
1206 return false;
1207 }
1208 pu16SrcText = reinterpret_cast<PRTUTF16>(g_ctx.pClient->data.pv);
1209 cwSrcLen = g_ctx.pClient->data.cb / 2;
1210 /* How long will the converted text be? */
1211 rc = vboxClipboardUtf16GetLinSize(pu16SrcText, cwSrcLen, &cwDestLen);
1212 if (RT_FAILURE(rc))
1213 {
1214 LogRel(("vboxClipboardConvertUtf16: clipboard conversion failed. vboxClipboardUtf16GetLinSize returned %Vrc. Abandoning.\n", rc));
1215 vboxClipboardEmptyGuestBuffer();
1216 AssertRCReturn(rc, false);
1217 }
1218 if (cwDestLen == 0)
1219 {
1220 LogFlowFunc(("received empty clipboard data from the guest, returning false.\n"));
1221 vboxClipboardEmptyGuestBuffer();
1222 return false;
1223 }
1224 pu16DestText = reinterpret_cast<PRTUTF16>(XtMalloc(cwDestLen * 2));
1225 if (pu16DestText == 0)
1226 {
1227 LogRel (("vboxClipboardConvertUtf16: failed to allocate %d bytes\n", cwDestLen * 2));
1228 vboxClipboardEmptyGuestBuffer();
1229 return false;
1230 }
1231 /* Convert the text. */
1232 rc = vboxClipboardUtf16WinToLin(pu16SrcText, cwSrcLen, pu16DestText, cwDestLen);
1233 if (RT_FAILURE(rc))
1234 {
1235 LogRel(("vboxClipboardConvertUtf16: clipboard conversion failed. vboxClipboardUtf16WinToLin returned %Vrc. Abandoning.\n", rc));
1236 XtFree(reinterpret_cast<char *>(pu16DestText));
1237 vboxClipboardEmptyGuestBuffer();
1238 return false;
1239 }
1240 LogFlowFunc (("converted string is %.*ls. Returning.\n", cwDestLen, pu16DestText));
1241 vboxClipboardEmptyGuestBuffer();
1242 *atomTypeReturn = g_ctx.atomUtf16;
1243 *pValReturn = reinterpret_cast<XtPointer>(pu16DestText);
1244 *pcLenReturn = cwDestLen;
1245 *piFormatReturn = 16;
1246 return true;
1247}
1248
1249/**
1250 * Satisfy a request from the host to convert the clipboard text to Utf8.
1251 *
1252 * @returns true if we successfully convert the data to the format requested, false otherwise.
1253 *
1254 * @param atomTypeReturn The type of the data we are returning
1255 * @param pValReturn A pointer to the data we are returning. This should be to memory
1256 * allocated by XtMalloc, which will be freed by the toolkit later
1257 * @param pcLenReturn The length of the data we are returning
1258 * @param piFormatReturn The format (8bit, 16bit, 32bit) of the data we are returning
1259 */
1260static Boolean vboxClipboardConvertUtf8(Atom *atomTypeReturn, XtPointer *pValReturn,
1261 unsigned long *pcLenReturn, int *piFormatReturn)
1262{
1263 PRTUTF16 pu16SrcText, pu16DestText;
1264 char *pu8DestText;
1265 size_t cwSrcLen, cwDestLen, cbDestLen;
1266 int rc;
1267
1268 LogFlowFunc (("called\n"));
1269 /* Read the clipboard data from the guest. */
1270 rc = vboxClipboardReadDataFromClient(&g_ctx, VBOX_SHARED_CLIPBOARD_FMT_UNICODETEXT);
1271 if ((rc != VINF_SUCCESS) || (g_ctx.pClient->data.cb == 0))
1272 {
1273 LogRel (("vboxClipboardConvertUtf8: vboxClipboardReadDataFromClient returned %Vrc, %d bytes of data\n", rc, g_ctx.pClient->data.cb));
1274 vboxClipboardEmptyGuestBuffer();
1275 return false;
1276 }
1277 pu16SrcText = reinterpret_cast<PRTUTF16>(g_ctx.pClient->data.pv);
1278 cwSrcLen = g_ctx.pClient->data.cb / 2;
1279 /* How long will the converted text be? */
1280 rc = vboxClipboardUtf16GetLinSize(pu16SrcText, cwSrcLen, &cwDestLen);
1281 if (RT_FAILURE(rc))
1282 {
1283 LogRel(("vboxClipboardConvertUtf8: clipboard conversion failed. vboxClipboardUtf16GetLinSize returned %Vrc. Abandoning.\n", rc));
1284 vboxClipboardEmptyGuestBuffer();
1285 AssertRCReturn(rc, false);
1286 }
1287 if (cwDestLen == 0)
1288 {
1289 LogFlowFunc(("received empty clipboard data from the guest, returning false.\n"));
1290 vboxClipboardEmptyGuestBuffer();
1291 return false;
1292 }
1293 pu16DestText = reinterpret_cast<PRTUTF16>(RTMemAlloc(cwDestLen * 2));
1294 if (pu16DestText == 0)
1295 {
1296 LogRel (("vboxClipboardConvertUtf8: failed to allocate %d bytes\n", cwDestLen * 2));
1297 vboxClipboardEmptyGuestBuffer();
1298 return false;
1299 }
1300 /* Convert the text. */
1301 rc = vboxClipboardUtf16WinToLin(pu16SrcText, cwSrcLen, pu16DestText, cwDestLen);
1302 if (RT_FAILURE(rc))
1303 {
1304 LogRel(("vboxClipboardConvertUtf8: clipboard conversion failed. vboxClipboardUtf16WinToLin() returned %Vrc. Abandoning.\n", rc));
1305 RTMemFree(reinterpret_cast<void *>(pu16DestText));
1306 vboxClipboardEmptyGuestBuffer();
1307 return rc;
1308 }
1309 /* Allocate enough space, as RTUtf16ToUtf8Ex may fail if the
1310 space is too tightly calculated. */
1311 pu8DestText = XtMalloc(cwDestLen * 4);
1312 if (pu8DestText == 0)
1313 {
1314 LogRel (("vboxClipboardConvertUtf8: failed to allocate %d bytes\n", cwDestLen * 4));
1315 RTMemFree(reinterpret_cast<void *>(pu16DestText));
1316 vboxClipboardEmptyGuestBuffer();
1317 return false;
1318 }
1319 /* Convert the Utf16 string to Utf8. */
1320 rc = RTUtf16ToUtf8Ex(pu16DestText + 1, cwDestLen - 1, &pu8DestText, cwDestLen * 4,
1321 &cbDestLen);
1322 RTMemFree(reinterpret_cast<void *>(pu16DestText));
1323 if (RT_FAILURE(rc))
1324 {
1325 LogRel(("vboxClipboardConvertUtf8: clipboard conversion failed. RTUtf16ToUtf8Ex() returned %Vrc. Abandoning.\n", rc));
1326 XtFree(pu8DestText);
1327 vboxClipboardEmptyGuestBuffer();
1328 return rc;
1329 }
1330 LogFlowFunc (("converted string is %.*s. Returning.\n", cbDestLen, pu8DestText));
1331 vboxClipboardEmptyGuestBuffer();
1332 *atomTypeReturn = g_ctx.atomUtf8;
1333 *pValReturn = reinterpret_cast<XtPointer>(pu8DestText);
1334 *pcLenReturn = cbDestLen;
1335 *piFormatReturn = 8;
1336 return true;
1337}
1338
1339/**
1340 * Satisfy a request from the host to convert the clipboard text to COMPOUND_TEXT.
1341 *
1342 * @returns true if we successfully convert the data to the format requested, false otherwise.
1343 *
1344 * @param atomTypeReturn The type of the data we are returning
1345 * @param pValReturn A pointer to the data we are returning. This should be to memory
1346 * allocated by XtMalloc, which will be freed by the toolkit later
1347 * @param pcLenReturn The length of the data we are returning
1348 * @param piFormatReturn The format (8bit, 16bit, 32bit) of the data we are returning
1349 */
1350static Boolean vboxClipboardConvertCText(Atom *atomTypeReturn, XtPointer *pValReturn,
1351 unsigned long *pcLenReturn, int *piFormatReturn)
1352{
1353 PRTUTF16 pu16SrcText, pu16DestText;
1354 char *pu8DestText = 0;
1355 size_t cwSrcLen, cwDestLen, cbDestLen;
1356 XTextProperty property;
1357 int rc;
1358
1359 LogFlowFunc (("called\n"));
1360 /* Read the clipboard data from the guest. */
1361 rc = vboxClipboardReadDataFromClient(&g_ctx, VBOX_SHARED_CLIPBOARD_FMT_UNICODETEXT);
1362 if ((rc != VINF_SUCCESS) || (g_ctx.pClient->data.cb == 0))
1363 {
1364 LogRel (("vboxClipboardConvertCText: vboxClipboardReadDataFromClient returned %Vrc, %d bytes of data\n", rc, g_ctx.pClient->data.cb));
1365 vboxClipboardEmptyGuestBuffer();
1366 return false;
1367 }
1368 pu16SrcText = reinterpret_cast<PRTUTF16>(g_ctx.pClient->data.pv);
1369 cwSrcLen = g_ctx.pClient->data.cb / 2;
1370 /* How long will the converted text be? */
1371 rc = vboxClipboardUtf16GetLinSize(pu16SrcText, cwSrcLen, &cwDestLen);
1372 if (RT_FAILURE(rc))
1373 {
1374 LogRel(("vboxClipboardConvertCText: clipboard conversion failed. vboxClipboardUtf16GetLinSize returned %Vrc. Abandoning.\n", rc));
1375 vboxClipboardEmptyGuestBuffer();
1376 AssertRCReturn(rc, false);
1377 }
1378 if (cwDestLen == 0)
1379 {
1380 LogFlowFunc(("received empty clipboard data from the guest, returning false.\n"));
1381 vboxClipboardEmptyGuestBuffer();
1382 return false;
1383 }
1384 pu16DestText = reinterpret_cast<PRTUTF16>(RTMemAlloc(cwDestLen * 2));
1385 if (pu16DestText == 0)
1386 {
1387 LogRel (("vboxClipboardConvertCText: failed to allocate %d bytes\n", cwDestLen * 2));
1388 vboxClipboardEmptyGuestBuffer();
1389 return false;
1390 }
1391 /* Convert the text. */
1392 rc = vboxClipboardUtf16WinToLin(pu16SrcText, cwSrcLen, pu16DestText, cwDestLen);
1393 if (RT_FAILURE(rc))
1394 {
1395 LogRel(("vboxClipboardConvertCText: clipboard conversion failed. vboxClipboardUtf16WinToLin() returned %Vrc. Abandoning.\n", rc));
1396 RTMemFree(reinterpret_cast<void *>(pu16DestText));
1397 vboxClipboardEmptyGuestBuffer();
1398 return false;
1399 }
1400 /* Convert the Utf16 string to Utf8. */
1401 rc = RTUtf16ToUtf8Ex(pu16DestText + 1, cwDestLen - 1, &pu8DestText, 0, &cbDestLen);
1402 RTMemFree(reinterpret_cast<void *>(pu16DestText));
1403 if (RT_FAILURE(rc))
1404 {
1405 LogRel(("vboxClipboardConvertCText: clipboard conversion failed. RTUtf16ToUtf8Ex() returned %Vrc. Abandoning.\n", rc));
1406 vboxClipboardEmptyGuestBuffer();
1407 return false;
1408 }
1409 /* And finally (!) convert the Utf8 text to compound text. */
1410 rc = Xutf8TextListToTextProperty(XtDisplay(g_ctx.widget), &pu8DestText, 1,
1411 XCompoundTextStyle, &property);
1412 RTMemFree(pu8DestText);
1413 if (rc < 0)
1414 {
1415 const char *pcReason;
1416 switch(rc)
1417 {
1418 case XNoMemory:
1419 pcReason = "out of memory";
1420 break;
1421 case XLocaleNotSupported:
1422 pcReason = "locale (Utf8) not supported";
1423 break;
1424 case XConverterNotFound:
1425 pcReason = "converter not found";
1426 break;
1427 default:
1428 pcReason = "unknown error";
1429 }
1430 LogRel(("vboxClipboardConvertCText: Xutf8TextListToTextProperty failed. Reason: %s\n",
1431 pcReason));
1432 XFree(property.value);
1433 vboxClipboardEmptyGuestBuffer();
1434 return false;
1435 }
1436 LogFlowFunc (("converted string is %s. Returning.\n", property.value));
1437 vboxClipboardEmptyGuestBuffer();
1438 *atomTypeReturn = property.encoding;
1439 *pValReturn = reinterpret_cast<XtPointer>(property.value);
1440 *pcLenReturn = property.nitems;
1441 *piFormatReturn = property.format;
1442 return true;
1443}
1444
1445/**
1446 * Callback to convert the guests clipboard data for an application on the host. Called by the
1447 * X Toolkit.
1448 * @returns true if we successfully convert the data to the format requested, false otherwise.
1449 *
1450 * @param atomSelection The selection which is being requested. We only handle the clipboard.
1451 * @param atomTarget The format we should convert the data to
1452 * @param atomTypeReturn The type of the data we are returning
1453 * @param pValReturn A pointer to the data we are returning. This should be to memory
1454 * allocated by XtMalloc, which will be freed by the toolkit later
1455 * @param pcLenReturn The length of the data we are returning
1456 * @param piFormatReturn The format (8bit, 16bit, 32bit) of the data we are returning
1457 */
1458static Boolean vboxClipboardConvertProc(Widget, Atom *atomSelection, Atom *atomTarget,
1459 Atom *atomTypeReturn, XtPointer *pValReturn,
1460 unsigned long *pcLenReturn, int *piFormatReturn)
1461{
1462 g_eClipboardFormats eFormat = INVALID;
1463
1464 LogFlowFunc(("\n"));
1465 if (*atomSelection != g_ctx.atomClipboard)
1466 {
1467 LogFlowFunc(("rc = false\n"));
1468 return false;
1469 }
1470#ifdef DEBUG
1471 char *szAtomName = XGetAtomName(XtDisplay(g_ctx.widget), *atomTarget);
1472 if (szAtomName != 0)
1473 {
1474 Log2 (("vboxClipboardConvertProc: request for format %s\n", szAtomName));
1475 XFree(szAtomName);
1476 }
1477 else
1478 {
1479 Log(("vboxClipboardConvertProc: request for invalid target atom %d!\n", *atomTarget));
1480 }
1481#endif
1482 if (*atomTarget == g_ctx.atomTargets)
1483 {
1484 eFormat = TARGETS;
1485 }
1486 else
1487 {
1488 for (unsigned i = 0; i != g_ctx.formatList.size(); ++i)
1489 {
1490 if (g_ctx.formatList[i].atom == *atomTarget)
1491 {
1492 eFormat = g_ctx.formatList[i].format;
1493 break;
1494 }
1495 }
1496 }
1497 switch (eFormat)
1498 {
1499 case TARGETS:
1500 return vboxClipboardConvertTargets(atomTypeReturn, pValReturn, pcLenReturn,
1501 piFormatReturn);
1502 case UTF16:
1503 return vboxClipboardConvertUtf16(atomTypeReturn, pValReturn, pcLenReturn,
1504 piFormatReturn);
1505 case UTF8:
1506 return vboxClipboardConvertUtf8(atomTypeReturn, pValReturn, pcLenReturn,
1507 piFormatReturn);
1508 case CTEXT:
1509 return vboxClipboardConvertCText(atomTypeReturn, pValReturn, pcLenReturn,
1510 piFormatReturn);
1511 default:
1512 Log(("vboxClipboardConvertProc: bad format\n"));
1513 return false;
1514 }
1515}
1516
1517static void vboxClipboardLoseProc(Widget, Atom *)
1518{
1519 LogFlow (("vboxClipboardLoseProc: called, giving the host clipboard ownership\n"));
1520 g_ctx.eOwner = HOST;
1521 g_ctx.notifyGuest = true;
1522}
1523
1524/**
1525 * The guest is taking possession of the shared clipboard. Called by the HGCM clipboard
1526 * subsystem.
1527 *
1528 * @param pClient Context data for the guest system
1529 * @param u32Formats Clipboard formats the the guest is offering
1530 */
1531void vboxClipboardFormatAnnounce (VBOXCLIPBOARDCLIENTDATA *pClient, uint32_t u32Formats)
1532{
1533 pClient->pCtx->guestFormats = u32Formats;
1534 LogFlowFunc (("u32Formats=%d\n", u32Formats));
1535 if (u32Formats == 0)
1536 {
1537 /* This is just an automatism, not a genuine anouncement */
1538 LogFlowFunc(("returning\n"));
1539 return;
1540 }
1541 if (g_ctx.eOwner == GUEST)
1542 {
1543 /* We already own the clipboard, so no need to grab it, especially as that can lead
1544 to races due to the asynchronous nature of the X11 clipboard. This event may also
1545 have been sent out by the guest to invalidate the Windows clipboard cache. */
1546 LogFlowFunc(("returning\n"));
1547 return;
1548 }
1549 Log2 (("vboxClipboardFormatAnnounce: giving the guest clipboard ownership\n"));
1550 g_ctx.eOwner = GUEST;
1551 g_ctx.hostTextFormat = INVALID;
1552 g_ctx.hostBitmapFormat = INVALID;
1553 if (XtOwnSelection(g_ctx.widget, g_ctx.atomClipboard, CurrentTime, vboxClipboardConvertProc,
1554 vboxClipboardLoseProc, 0) != True)
1555 {
1556 Log2 (("vboxClipboardFormatAnnounce: returning clipboard ownership to the host\n"));
1557 /* We set this so that the guest gets notified when we take the clipboard, even if no
1558 guest formats are found which we understand. */
1559 g_ctx.notifyGuest = true;
1560 g_ctx.eOwner = HOST;
1561 }
1562 LogFlowFunc(("returning\n"));
1563
1564}
1565
1566/**
1567 * Called by the HGCM clipboard subsystem when the guest wants to read the host clipboard.
1568 *
1569 * @param pClient Context information about the guest VM
1570 * @param u32Format The format that the guest would like to receive the data in
1571 * @param pv Where to write the data to
1572 * @param cb The size of the buffer to write the data to
1573 * @param pcbActual Where to write the actual size of the written data
1574 */
1575int vboxClipboardReadData (VBOXCLIPBOARDCLIENTDATA *pClient, uint32_t u32Format, void *pv,
1576 uint32_t cb, uint32_t *pcbActual)
1577{
1578 LogFlow(("vboxClipboardReadData: u32Format = %d, cb = %d\n", u32Format, cb));
1579
1580 /*
1581 * The guest wants to read data in the given format.
1582 */
1583 if (u32Format & VBOX_SHARED_CLIPBOARD_FMT_UNICODETEXT)
1584 {
1585 if (g_ctx.hostTextFormat == INVALID)
1586 {
1587 /* No data available. */
1588 *pcbActual = 0;
1589 return VINF_SUCCESS;
1590 }
1591 /* Only one of the host and the guest should ever be waiting. */
1592 if (RT_FAILURE(ASMAtomicCmpXchgU32(&g_ctx.waiter, 1, 0)))
1593 {
1594 LogRel(("vboxClipboardReadData: detected a deadlock situation - the host and the guest are waiting for each other.\n"));
1595 return VERR_DEADLOCK;
1596 }
1597 g_ctx.requestHostFormat = g_ctx.hostTextFormat;
1598 g_ctx.requestBuffer = pv;
1599 g_ctx.requestBufferSize = cb;
1600 g_ctx.requestActualSize = pcbActual;
1601 /* Send out a request for the data to the current clipboard owner */
1602 XtGetSelectionValue(g_ctx.widget, g_ctx.atomClipboard, g_ctx.atomHostTextFormat,
1603 vboxClipboardGetProc, reinterpret_cast<XtPointer>(g_ctx.pClient),
1604 CurrentTime);
1605 /* When the data arrives, the vboxClipboardGetProc callback will be called. The
1606 callback will signal the event semaphore when it has processed the data for us. */
1607 if (RTSemEventWait(g_ctx.waitForData, CLIPBOARDTIMEOUT) != VINF_SUCCESS)
1608 {
1609 LogRel (("vboxClipboardReadDataFromClient: XtGetSelectionValue failed to complete within %d milliseconds\n", CLIPBOARDTIMEOUT));
1610 g_ctx.hostTextFormat = INVALID;
1611 g_ctx.hostBitmapFormat = INVALID;
1612 g_ctx.waiter = 0;
1613 return VERR_TIMEOUT;
1614 }
1615 g_ctx.waiter = 0;
1616 }
1617 else
1618 {
1619 return VERR_NOT_IMPLEMENTED;
1620 }
1621 return VINF_SUCCESS;
1622}
1623
1624/**
1625 * Called by the HGCM clipboard subsystem when we have requested data and that data arrives.
1626 *
1627 * @param pClient Context information about the guest VM
1628 * @param pv Buffer to which the data was written
1629 * @param cb The size of the data written
1630 * @param u32Format The format of the data written
1631 */
1632void vboxClipboardWriteData (VBOXCLIPBOARDCLIENTDATA *pClient, void *pv, uint32_t cb, uint32_t u32Format)
1633{
1634 LogFlow(("vboxClipboardWriteData\n"));
1635
1636 /*
1637 * The guest returns data that was requested in the WM_RENDERFORMAT handler.
1638 */
1639 if (!( pClient->data.pv == NULL
1640 && pClient->data.cb == 0
1641 && pClient->data.u32Format == 0))
1642 {
1643 LogRel(("vboxClipboardWriteData: clipboard data has arrived from the guest, but another transfer is in process or has not cleaned up properly.\n"));
1644 AssertMsgFailed(("vboxClipboardWriteData: clipboard data has arrived from the guest, but another transfer is in process or has not cleaned up properly.\n"));
1645 }
1646
1647 if (cb > 0)
1648 {
1649 pClient->data.pv = RTMemAlloc (cb);
1650
1651 if (pClient->data.pv)
1652 {
1653 memcpy (pClient->data.pv, pv, cb);
1654 pClient->data.cb = cb;
1655 pClient->data.u32Format = u32Format;
1656 }
1657 }
1658
1659 RTSemEventSignal(g_ctx.waitForData);
1660}
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