VirtualBox

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

Last change on this file since 4052 was 3895, checked in by vboxsync, 18 years ago

Removed support for Latin1 from the Linux clipboard code, except as a fallback for bad Utf8. Plain text now means Utf8.

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

© 2025 Oracle Support Privacy / Do Not Sell My Info Terms of Use Trademark Policy Automated Access Etiquette