VirtualBox

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

Last change on this file since 3806 was 3338, checked in by vboxsync, 18 years ago

Export HostServices

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