VirtualBox

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

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

gcc warning

  • 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 as published by the Free Software Foundation,
14 * in version 2 as it comes in the "COPYING" file of the VirtualBox OSE
15 * distribution. VirtualBox OSE is distributed in the hope that it will
16 * 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 rc = RTThreadWait(g_ctx.thread, 2000, &rcThread);
1054 AssertRC(rc);
1055 AssertRC(rcThread);
1056 LogFlowFunc(("returning.\n"));
1057}
1058
1059/**
1060 * Enable the shared clipboard - called by the hgcm clipboard subsystem.
1061 *
1062 * @param pClient Structure containing context information about the guest system
1063 * @returns RT status code
1064 */
1065int vboxClipboardConnect (VBOXCLIPBOARDCLIENTDATA *pClient)
1066{
1067 LogFlow(("vboxClipboardConnect\n"));
1068
1069 /* Only one client is supported for now */
1070 if (g_ctx.pClient != 0)
1071 {
1072 LogRel(("vboxClipboardConnect: attempted to connect, but a client appears to be already running.\n"));
1073 AssertReturn(g_ctx.pClient == 0, VERR_NOT_SUPPORTED);
1074 }
1075
1076 pClient->pCtx = &g_ctx;
1077 pClient->pCtx->pClient = pClient;
1078 g_ctx.eOwner = HOST;
1079 g_ctx.notifyGuest = true;
1080 return VINF_SUCCESS;
1081}
1082
1083/**
1084 * Synchronise the contents of the host clipboard with the guest, called by the HGCM layer
1085 * after a save and restore of the guest.
1086 */
1087int vboxClipboardSync (VBOXCLIPBOARDCLIENTDATA *pClient)
1088{
1089 /* On a Linux host, the guest should never synchronise/cache its clipboard contents, as
1090 we have no way of reliably telling when the host clipboard data changes. So instead
1091 of synchronising, we tell the guest to empty its clipboard, and we set the cached
1092 flag so that we report formats to the guest next time we poll for them. */
1093 vboxSvcClipboardReportMsg (g_ctx.pClient, VBOX_SHARED_CLIPBOARD_HOST_MSG_FORMATS, 0);
1094 g_ctx.notifyGuest = true;
1095
1096 return VINF_SUCCESS;
1097}
1098
1099/**
1100 * Shut down the shared clipboard subsystem and "disconnect" the guest.
1101 */
1102void vboxClipboardDisconnect (VBOXCLIPBOARDCLIENTDATA *pClient)
1103{
1104 LogFlow(("vboxClipboardDisconnect\n"));
1105
1106 RTSemMutexRequest(g_ctx.asyncMutex, RT_INDEFINITE_WAIT);
1107 g_ctx.pClient = NULL;
1108 g_ctx.eOwner = NONE;
1109 g_ctx.hostTextFormat = INVALID;
1110 g_ctx.hostBitmapFormat = INVALID;
1111 RTSemMutexRelease(g_ctx.asyncMutex);
1112}
1113
1114/**
1115 * Satisfy a request from the host for available clipboard targets.
1116 *
1117 * @returns true if we successfully convert the data to the format requested, false otherwise.
1118 *
1119 * @param atomTypeReturn The type of the data we are returning
1120 * @param pValReturn A pointer to the data we are returning. This should be to memory
1121 * allocated by XtMalloc, which will be freed by the toolkit later
1122 * @param pcLenReturn The length of the data we are returning
1123 * @param piFormatReturn The format (8bit, 16bit, 32bit) of the data we are returning
1124 */
1125static Boolean vboxClipboardConvertTargets(Atom *atomTypeReturn, XtPointer *pValReturn,
1126 unsigned long *pcLenReturn, int *piFormatReturn)
1127{
1128 unsigned uListSize = g_ctx.formatList.size();
1129 Atom *atomTargets = reinterpret_cast<Atom *>(XtMalloc((uListSize + 3) * sizeof(Atom)));
1130 unsigned cTargets = 0;
1131
1132 LogFlow (("vboxClipboardConvertTargets called\n"));
1133 for (unsigned i = 0; i < uListSize; ++i)
1134 {
1135 if ( ((g_ctx.guestFormats & VBOX_SHARED_CLIPBOARD_FMT_UNICODETEXT) != 0)
1136 && (g_ctx.formatList[i].guestFormat == VBOX_SHARED_CLIPBOARD_FMT_UNICODETEXT))
1137 {
1138 atomTargets[cTargets] = g_ctx.formatList[i].atom;
1139 ++cTargets;
1140 }
1141 }
1142 atomTargets[cTargets] = g_ctx.atomTargets;
1143 atomTargets[cTargets + 1] = g_ctx.atomMultiple;
1144 atomTargets[cTargets + 2] = g_ctx.atomTimestamp;
1145#ifdef DEBUG
1146 for (unsigned i = 0; i < cTargets + 3; i++)
1147 {
1148 char *szAtomName = XGetAtomName(XtDisplay(g_ctx.widget), atomTargets[i]);
1149 if (szAtomName != 0)
1150 {
1151 Log2 (("vboxClipboardConvertTargets: returning target %s\n", szAtomName));
1152 XFree(szAtomName);
1153 }
1154 else
1155 {
1156 Log(("vboxClipboardConvertTargets: invalid atom %d in the list!\n", atomTargets[i]));
1157 }
1158 }
1159#endif
1160 *atomTypeReturn = XA_ATOM;
1161 *pValReturn = reinterpret_cast<XtPointer>(atomTargets);
1162 *pcLenReturn = cTargets + 3;
1163 *piFormatReturn = 32;
1164 return true;
1165}
1166
1167/**
1168 * Reset the contents of the buffer used to pass clipboard data from the guest to the host.
1169 * This must be done after every clipboard transfer.
1170 */
1171static void vboxClipboardEmptyGuestBuffer(void)
1172{
1173 if (g_ctx.pClient->data.pv != 0)
1174 RTMemFree(g_ctx.pClient->data.pv);
1175 g_ctx.pClient->data.pv = 0;
1176 g_ctx.pClient->data.cb = 0;
1177 g_ctx.pClient->data.u32Format = 0;
1178}
1179
1180/**
1181 * Satisfy a request from the host to convert the clipboard text to Utf16. We return non-zero
1182 * terminated text.
1183 *
1184 * @returns true if we successfully convert the data to the format requested, false otherwise.
1185 *
1186 * @retval atomTypeReturn The type of the data we are returning
1187 * @retval pValReturn A pointer to the data we are returning. This should be to memory
1188 * allocated by XtMalloc, which will be freed by the toolkit later
1189 * @retval pcLenReturn The length of the data we are returning
1190 * @retval piFormatReturn The format (8bit, 16bit, 32bit) of the data we are returning
1191 */
1192static Boolean vboxClipboardConvertUtf16(Atom *atomTypeReturn, XtPointer *pValReturn,
1193 unsigned long *pcLenReturn, int *piFormatReturn)
1194{
1195 PRTUTF16 pu16SrcText, pu16DestText;
1196 size_t cwSrcLen, cwDestLen;
1197 int rc;
1198
1199 LogFlowFunc (("called\n"));
1200 rc = vboxClipboardReadDataFromClient(&g_ctx, VBOX_SHARED_CLIPBOARD_FMT_UNICODETEXT);
1201 if ((rc != VINF_SUCCESS) || (g_ctx.pClient->data.cb == 0))
1202 {
1203 LogRel (("vboxClipboardConvertUtf16: vboxClipboardReadDataFromClient returned %Vrc, %d bytes of data\n", rc, g_ctx.pClient->data.cb));
1204 vboxClipboardEmptyGuestBuffer();
1205 return false;
1206 }
1207 pu16SrcText = reinterpret_cast<PRTUTF16>(g_ctx.pClient->data.pv);
1208 cwSrcLen = g_ctx.pClient->data.cb / 2;
1209 /* How long will the converted text be? */
1210 rc = vboxClipboardUtf16GetLinSize(pu16SrcText, cwSrcLen, &cwDestLen);
1211 if (RT_FAILURE(rc))
1212 {
1213 LogRel(("vboxClipboardConvertUtf16: clipboard conversion failed. vboxClipboardUtf16GetLinSize returned %Vrc. Abandoning.\n", rc));
1214 vboxClipboardEmptyGuestBuffer();
1215 AssertRCReturn(rc, false);
1216 }
1217 if (cwDestLen == 0)
1218 {
1219 LogFlowFunc(("received empty clipboard data from the guest, returning false.\n"));
1220 vboxClipboardEmptyGuestBuffer();
1221 return false;
1222 }
1223 pu16DestText = reinterpret_cast<PRTUTF16>(XtMalloc(cwDestLen * 2));
1224 if (pu16DestText == 0)
1225 {
1226 LogRel (("vboxClipboardConvertUtf16: failed to allocate %d bytes\n", cwDestLen * 2));
1227 vboxClipboardEmptyGuestBuffer();
1228 return false;
1229 }
1230 /* Convert the text. */
1231 rc = vboxClipboardUtf16WinToLin(pu16SrcText, cwSrcLen, pu16DestText, cwDestLen);
1232 if (RT_FAILURE(rc))
1233 {
1234 LogRel(("vboxClipboardConvertUtf16: clipboard conversion failed. vboxClipboardUtf16WinToLin returned %Vrc. Abandoning.\n", rc));
1235 XtFree(reinterpret_cast<char *>(pu16DestText));
1236 vboxClipboardEmptyGuestBuffer();
1237 return false;
1238 }
1239 LogFlowFunc (("converted string is %.*ls. Returning.\n", cwDestLen, pu16DestText));
1240 vboxClipboardEmptyGuestBuffer();
1241 *atomTypeReturn = g_ctx.atomUtf16;
1242 *pValReturn = reinterpret_cast<XtPointer>(pu16DestText);
1243 *pcLenReturn = cwDestLen;
1244 *piFormatReturn = 16;
1245 return true;
1246}
1247
1248/**
1249 * Satisfy a request from the host to convert the clipboard text to Utf8.
1250 *
1251 * @returns true if we successfully convert the data to the format requested, false otherwise.
1252 *
1253 * @param atomTypeReturn The type of the data we are returning
1254 * @param pValReturn A pointer to the data we are returning. This should be to memory
1255 * allocated by XtMalloc, which will be freed by the toolkit later
1256 * @param pcLenReturn The length of the data we are returning
1257 * @param piFormatReturn The format (8bit, 16bit, 32bit) of the data we are returning
1258 */
1259static Boolean vboxClipboardConvertUtf8(Atom *atomTypeReturn, XtPointer *pValReturn,
1260 unsigned long *pcLenReturn, int *piFormatReturn)
1261{
1262 PRTUTF16 pu16SrcText, pu16DestText;
1263 char *pu8DestText;
1264 size_t cwSrcLen, cwDestLen, cbDestLen;
1265 int rc;
1266
1267 LogFlowFunc (("called\n"));
1268 /* Read the clipboard data from the guest. */
1269 rc = vboxClipboardReadDataFromClient(&g_ctx, VBOX_SHARED_CLIPBOARD_FMT_UNICODETEXT);
1270 if ((rc != VINF_SUCCESS) || (g_ctx.pClient->data.cb == 0))
1271 {
1272 LogRel (("vboxClipboardConvertUtf8: vboxClipboardReadDataFromClient returned %Vrc, %d bytes of data\n", rc, g_ctx.pClient->data.cb));
1273 vboxClipboardEmptyGuestBuffer();
1274 return false;
1275 }
1276 pu16SrcText = reinterpret_cast<PRTUTF16>(g_ctx.pClient->data.pv);
1277 cwSrcLen = g_ctx.pClient->data.cb / 2;
1278 /* How long will the converted text be? */
1279 rc = vboxClipboardUtf16GetLinSize(pu16SrcText, cwSrcLen, &cwDestLen);
1280 if (RT_FAILURE(rc))
1281 {
1282 LogRel(("vboxClipboardConvertUtf8: clipboard conversion failed. vboxClipboardUtf16GetLinSize returned %Vrc. Abandoning.\n", rc));
1283 vboxClipboardEmptyGuestBuffer();
1284 AssertRCReturn(rc, false);
1285 }
1286 if (cwDestLen == 0)
1287 {
1288 LogFlowFunc(("received empty clipboard data from the guest, returning false.\n"));
1289 vboxClipboardEmptyGuestBuffer();
1290 return false;
1291 }
1292 pu16DestText = reinterpret_cast<PRTUTF16>(RTMemAlloc(cwDestLen * 2));
1293 if (pu16DestText == 0)
1294 {
1295 LogRel (("vboxClipboardConvertUtf8: failed to allocate %d bytes\n", cwDestLen * 2));
1296 vboxClipboardEmptyGuestBuffer();
1297 return false;
1298 }
1299 /* Convert the text. */
1300 rc = vboxClipboardUtf16WinToLin(pu16SrcText, cwSrcLen, pu16DestText, cwDestLen);
1301 if (RT_FAILURE(rc))
1302 {
1303 LogRel(("vboxClipboardConvertUtf8: clipboard conversion failed. vboxClipboardUtf16WinToLin() returned %Vrc. Abandoning.\n", rc));
1304 RTMemFree(reinterpret_cast<void *>(pu16DestText));
1305 vboxClipboardEmptyGuestBuffer();
1306 return rc;
1307 }
1308 /* Allocate as much space as the Utf16 string takes up, as RTUtf16ToUtf8Ex may fail if the
1309 space is too tightly calculated. */
1310 pu8DestText = XtMalloc(cwDestLen * 2);
1311 if (pu8DestText == 0)
1312 {
1313 LogRel (("vboxClipboardConvertUtf8: failed to allocate %d bytes\n", cwDestLen * 2));
1314 RTMemFree(reinterpret_cast<void *>(pu16DestText));
1315 vboxClipboardEmptyGuestBuffer();
1316 return false;
1317 }
1318 /* Convert the Utf16 string to Utf8. */
1319 rc = RTUtf16ToUtf8Ex(pu16DestText + 1, cwDestLen - 1, &pu8DestText, cwDestLen * 2,
1320 &cbDestLen);
1321 RTMemFree(reinterpret_cast<void *>(pu16DestText));
1322 if (RT_FAILURE(rc))
1323 {
1324 LogRel(("vboxClipboardConvertUtf8: clipboard conversion failed. RTUtf16ToUtf8Ex() returned %Vrc. Abandoning.\n", rc));
1325 XtFree(pu8DestText);
1326 vboxClipboardEmptyGuestBuffer();
1327 return rc;
1328 }
1329 LogFlowFunc (("converted string is %.*s. Returning.\n", cbDestLen, pu8DestText));
1330 vboxClipboardEmptyGuestBuffer();
1331 *atomTypeReturn = g_ctx.atomUtf8;
1332 *pValReturn = reinterpret_cast<XtPointer>(pu8DestText);
1333 *pcLenReturn = cbDestLen;
1334 *piFormatReturn = 8;
1335 return true;
1336}
1337
1338/**
1339 * Satisfy a request from the host to convert the clipboard text to COMPOUND_TEXT.
1340 *
1341 * @returns true if we successfully convert the data to the format requested, false otherwise.
1342 *
1343 * @param atomTypeReturn The type of the data we are returning
1344 * @param pValReturn A pointer to the data we are returning. This should be to memory
1345 * allocated by XtMalloc, which will be freed by the toolkit later
1346 * @param pcLenReturn The length of the data we are returning
1347 * @param piFormatReturn The format (8bit, 16bit, 32bit) of the data we are returning
1348 */
1349static Boolean vboxClipboardConvertCText(Atom *atomTypeReturn, XtPointer *pValReturn,
1350 unsigned long *pcLenReturn, int *piFormatReturn)
1351{
1352 PRTUTF16 pu16SrcText, pu16DestText;
1353 char *pu8DestText = 0;
1354 size_t cwSrcLen, cwDestLen, cbDestLen;
1355 XTextProperty property;
1356 int rc;
1357
1358 LogFlowFunc (("called\n"));
1359 /* Read the clipboard data from the guest. */
1360 rc = vboxClipboardReadDataFromClient(&g_ctx, VBOX_SHARED_CLIPBOARD_FMT_UNICODETEXT);
1361 if ((rc != VINF_SUCCESS) || (g_ctx.pClient->data.cb == 0))
1362 {
1363 LogRel (("vboxClipboardConvertCText: vboxClipboardReadDataFromClient returned %Vrc, %d bytes of data\n", rc, g_ctx.pClient->data.cb));
1364 vboxClipboardEmptyGuestBuffer();
1365 return false;
1366 }
1367 pu16SrcText = reinterpret_cast<PRTUTF16>(g_ctx.pClient->data.pv);
1368 cwSrcLen = g_ctx.pClient->data.cb / 2;
1369 /* How long will the converted text be? */
1370 rc = vboxClipboardUtf16GetLinSize(pu16SrcText, cwSrcLen, &cwDestLen);
1371 if (RT_FAILURE(rc))
1372 {
1373 LogRel(("vboxClipboardConvertCText: clipboard conversion failed. vboxClipboardUtf16GetLinSize returned %Vrc. Abandoning.\n", rc));
1374 vboxClipboardEmptyGuestBuffer();
1375 AssertRCReturn(rc, false);
1376 }
1377 if (cwDestLen == 0)
1378 {
1379 LogFlowFunc(("received empty clipboard data from the guest, returning false.\n"));
1380 vboxClipboardEmptyGuestBuffer();
1381 return false;
1382 }
1383 pu16DestText = reinterpret_cast<PRTUTF16>(RTMemAlloc(cwDestLen * 2));
1384 if (pu16DestText == 0)
1385 {
1386 LogRel (("vboxClipboardConvertCText: failed to allocate %d bytes\n", cwDestLen * 2));
1387 vboxClipboardEmptyGuestBuffer();
1388 return false;
1389 }
1390 /* Convert the text. */
1391 rc = vboxClipboardUtf16WinToLin(pu16SrcText, cwSrcLen, pu16DestText, cwDestLen);
1392 if (RT_FAILURE(rc))
1393 {
1394 LogRel(("vboxClipboardConvertCText: clipboard conversion failed. vboxClipboardUtf16WinToLin() returned %Vrc. Abandoning.\n", rc));
1395 RTMemFree(reinterpret_cast<void *>(pu16DestText));
1396 vboxClipboardEmptyGuestBuffer();
1397 return false;
1398 }
1399 /* Convert the Utf16 string to Utf8. */
1400 rc = RTUtf16ToUtf8Ex(pu16DestText + 1, cwDestLen - 1, &pu8DestText, 0, &cbDestLen);
1401 RTMemFree(reinterpret_cast<void *>(pu16DestText));
1402 if (RT_FAILURE(rc))
1403 {
1404 LogRel(("vboxClipboardConvertCText: clipboard conversion failed. RTUtf16ToUtf8Ex() returned %Vrc. Abandoning.\n", rc));
1405 vboxClipboardEmptyGuestBuffer();
1406 return false;
1407 }
1408 /* And finally (!) convert the Utf8 text to compound text. */
1409 rc = Xutf8TextListToTextProperty(XtDisplay(g_ctx.widget), &pu8DestText, 1,
1410 XCompoundTextStyle, &property);
1411 RTMemFree(pu8DestText);
1412 if (rc < 0)
1413 {
1414 const char *pcReason;
1415 switch(rc)
1416 {
1417 case XNoMemory:
1418 pcReason = "out of memory";
1419 break;
1420 case XLocaleNotSupported:
1421 pcReason = "locale (Utf8) not supported";
1422 break;
1423 case XConverterNotFound:
1424 pcReason = "converter not found";
1425 break;
1426 default:
1427 pcReason = "unknown error";
1428 }
1429 LogRel(("vboxClipboardConvertCText: Xutf8TextListToTextProperty failed. Reason: %s\n",
1430 pcReason));
1431 XFree(property.value);
1432 vboxClipboardEmptyGuestBuffer();
1433 return false;
1434 }
1435 LogFlowFunc (("converted string is %s. Returning.\n", property.value));
1436 vboxClipboardEmptyGuestBuffer();
1437 *atomTypeReturn = property.encoding;
1438 *pValReturn = reinterpret_cast<XtPointer>(property.value);
1439 *pcLenReturn = property.nitems;
1440 *piFormatReturn = property.format;
1441 return true;
1442}
1443
1444/**
1445 * Callback to convert the guests clipboard data for an application on the host. Called by the
1446 * X Toolkit.
1447 * @returns true if we successfully convert the data to the format requested, false otherwise.
1448 *
1449 * @param atomSelection The selection which is being requested. We only handle the clipboard.
1450 * @param atomTarget The format we should convert the data to
1451 * @param atomTypeReturn The type of the data we are returning
1452 * @param pValReturn A pointer to the data we are returning. This should be to memory
1453 * allocated by XtMalloc, which will be freed by the toolkit later
1454 * @param pcLenReturn The length of the data we are returning
1455 * @param piFormatReturn The format (8bit, 16bit, 32bit) of the data we are returning
1456 */
1457static Boolean vboxClipboardConvertProc(Widget, Atom *atomSelection, Atom *atomTarget,
1458 Atom *atomTypeReturn, XtPointer *pValReturn,
1459 unsigned long *pcLenReturn, int *piFormatReturn)
1460{
1461 g_eClipboardFormats eFormat = INVALID;
1462
1463 LogFlowFunc(("\n"));
1464 if (*atomSelection != g_ctx.atomClipboard)
1465 {
1466 LogFlowFunc(("rc = false\n"));
1467 return false;
1468 }
1469#ifdef DEBUG
1470 char *szAtomName = XGetAtomName(XtDisplay(g_ctx.widget), *atomTarget);
1471 if (szAtomName != 0)
1472 {
1473 Log2 (("vboxClipboardConvertProc: request for format %s\n", szAtomName));
1474 XFree(szAtomName);
1475 }
1476 else
1477 {
1478 Log(("vboxClipboardConvertProc: request for invalid target atom %d!\n", *atomTarget));
1479 }
1480#endif
1481 if (*atomTarget == g_ctx.atomTargets)
1482 {
1483 eFormat = TARGETS;
1484 }
1485 else
1486 {
1487 for (unsigned i = 0; i != g_ctx.formatList.size(); ++i)
1488 {
1489 if (g_ctx.formatList[i].atom == *atomTarget)
1490 {
1491 eFormat = g_ctx.formatList[i].format;
1492 break;
1493 }
1494 }
1495 }
1496 switch (eFormat)
1497 {
1498 case TARGETS:
1499 return vboxClipboardConvertTargets(atomTypeReturn, pValReturn, pcLenReturn,
1500 piFormatReturn);
1501 case UTF16:
1502 return vboxClipboardConvertUtf16(atomTypeReturn, pValReturn, pcLenReturn,
1503 piFormatReturn);
1504 case UTF8:
1505 return vboxClipboardConvertUtf8(atomTypeReturn, pValReturn, pcLenReturn,
1506 piFormatReturn);
1507 case CTEXT:
1508 return vboxClipboardConvertCText(atomTypeReturn, pValReturn, pcLenReturn,
1509 piFormatReturn);
1510 default:
1511 Log(("vboxClipboardConvertProc: bad format\n"));
1512 return false;
1513 }
1514}
1515
1516static void vboxClipboardLoseProc(Widget, Atom *)
1517{
1518 LogFlow (("vboxClipboardLoseProc: called, giving the host clipboard ownership\n"));
1519 g_ctx.eOwner = HOST;
1520 g_ctx.notifyGuest = true;
1521}
1522
1523/**
1524 * The guest is taking possession of the shared clipboard. Called by the HGCM clipboard
1525 * subsystem.
1526 *
1527 * @param pClient Context data for the guest system
1528 * @param u32Formats Clipboard formats the the guest is offering
1529 */
1530void vboxClipboardFormatAnnounce (VBOXCLIPBOARDCLIENTDATA *pClient, uint32_t u32Formats)
1531{
1532 pClient->pCtx->guestFormats = u32Formats;
1533 LogFlowFunc (("u32Formats=%d\n", u32Formats));
1534 if (u32Formats == 0)
1535 {
1536 /* This is just an automatism, not a genuine anouncement */
1537 LogFlowFunc(("returning\n"));
1538 return;
1539 }
1540 if (g_ctx.eOwner == GUEST)
1541 {
1542 /* We already own the clipboard, so no need to grab it, especially as that can lead
1543 to races due to the asynchronous nature of the X11 clipboard. This event may also
1544 have been sent out by the guest to invalidate the Windows clipboard cache. */
1545 LogFlowFunc(("returning\n"));
1546 return;
1547 }
1548 Log2 (("vboxClipboardFormatAnnounce: giving the guest clipboard ownership\n"));
1549 g_ctx.eOwner = GUEST;
1550 g_ctx.hostTextFormat = INVALID;
1551 g_ctx.hostBitmapFormat = INVALID;
1552 if (XtOwnSelection(g_ctx.widget, g_ctx.atomClipboard, CurrentTime, vboxClipboardConvertProc,
1553 vboxClipboardLoseProc, 0) != True)
1554 {
1555 Log2 (("vboxClipboardFormatAnnounce: returning clipboard ownership to the host\n"));
1556 /* We set this so that the guest gets notified when we take the clipboard, even if no
1557 guest formats are found which we understand. */
1558 g_ctx.notifyGuest = true;
1559 g_ctx.eOwner = HOST;
1560 }
1561 LogFlowFunc(("returning\n"));
1562
1563}
1564
1565/**
1566 * Called by the HGCM clipboard subsystem when the guest wants to read the host clipboard.
1567 *
1568 * @param pClient Context information about the guest VM
1569 * @param u32Format The format that the guest would like to receive the data in
1570 * @param pv Where to write the data to
1571 * @param cb The size of the buffer to write the data to
1572 * @param pcbActual Where to write the actual size of the written data
1573 */
1574int vboxClipboardReadData (VBOXCLIPBOARDCLIENTDATA *pClient, uint32_t u32Format, void *pv,
1575 uint32_t cb, uint32_t *pcbActual)
1576{
1577 LogFlow(("vboxClipboardReadData: u32Format = %d, cb = %d\n", u32Format, cb));
1578
1579 /*
1580 * The guest wants to read data in the given format.
1581 */
1582 if (u32Format & VBOX_SHARED_CLIPBOARD_FMT_UNICODETEXT)
1583 {
1584 if (g_ctx.hostTextFormat == INVALID)
1585 {
1586 /* No data available. */
1587 *pcbActual = 0;
1588 return VINF_SUCCESS;
1589 }
1590 /* Only one of the host and the guest should ever be waiting. */
1591 if (RT_FAILURE(ASMAtomicCmpXchgU32(&g_ctx.waiter, 1, 0)))
1592 {
1593 LogRel(("vboxClipboardReadData: detected a deadlock situation - the host and the guest are waiting for each other.\n"));
1594 return VERR_DEADLOCK;
1595 }
1596 g_ctx.requestHostFormat = g_ctx.hostTextFormat;
1597 g_ctx.requestBuffer = pv;
1598 g_ctx.requestBufferSize = cb;
1599 g_ctx.requestActualSize = pcbActual;
1600 /* Send out a request for the data to the current clipboard owner */
1601 XtGetSelectionValue(g_ctx.widget, g_ctx.atomClipboard, g_ctx.atomHostTextFormat,
1602 vboxClipboardGetProc, reinterpret_cast<XtPointer>(g_ctx.pClient),
1603 CurrentTime);
1604 /* When the data arrives, the vboxClipboardGetProc callback will be called. The
1605 callback will signal the event semaphore when it has processed the data for us. */
1606 if (RTSemEventWait(g_ctx.waitForData, CLIPBOARDTIMEOUT) != VINF_SUCCESS)
1607 {
1608 LogRel (("vboxClipboardReadDataFromClient: XtGetSelectionValue failed to complete within %d milliseconds\n", CLIPBOARDTIMEOUT));
1609 g_ctx.hostTextFormat = INVALID;
1610 g_ctx.hostBitmapFormat = INVALID;
1611 g_ctx.waiter = 0;
1612 return VERR_TIMEOUT;
1613 }
1614 g_ctx.waiter = 0;
1615 }
1616 else
1617 {
1618 return VERR_NOT_IMPLEMENTED;
1619 }
1620 return VINF_SUCCESS;
1621}
1622
1623/**
1624 * Called by the HGCM clipboard subsystem when we have requested data and that data arrives.
1625 *
1626 * @param pClient Context information about the guest VM
1627 * @param pv Buffer to which the data was written
1628 * @param cb The size of the data written
1629 * @param u32Format The format of the data written
1630 */
1631void vboxClipboardWriteData (VBOXCLIPBOARDCLIENTDATA *pClient, void *pv, uint32_t cb, uint32_t u32Format)
1632{
1633 LogFlow(("vboxClipboardWriteData\n"));
1634
1635 /*
1636 * The guest returns data that was requested in the WM_RENDERFORMAT handler.
1637 */
1638 if (!( pClient->data.pv == NULL
1639 && pClient->data.cb == 0
1640 && pClient->data.u32Format == 0))
1641 {
1642 LogRel(("vboxClipboardWriteData: clipboard data has arrived from the guest, but another transfer is in process or has not cleaned up properly.\n"));
1643 AssertMsgFailed(("vboxClipboardWriteData: clipboard data has arrived from the guest, but another transfer is in process or has not cleaned up properly.\n"));
1644 }
1645
1646 if (cb > 0)
1647 {
1648 pClient->data.pv = RTMemAlloc (cb);
1649
1650 if (pClient->data.pv)
1651 {
1652 memcpy (pClient->data.pv, pv, cb);
1653 pClient->data.cb = cb;
1654 pClient->data.u32Format = u32Format;
1655 }
1656 }
1657
1658 RTSemEventSignal(g_ctx.waitForData);
1659}
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