Changeset 20552 in vbox for trunk/src/VBox
- Timestamp:
- Jun 14, 2009 4:34:30 PM (16 years ago)
- Location:
- trunk/src/VBox/Additions/x11/VBoxClient
- Files:
-
- 2 edited
Legend:
- Unmodified
- Added
- Removed
-
trunk/src/VBox/Additions/x11/VBoxClient/Makefile.kmk
r18360 r20552 34 34 main.cpp 35 35 VBoxClient_SOURCES += \ 36 $(PATH_ROOT)/src/VBox/GuestHost/SharedClipboard/clipboard-helper.cpp \ 37 $(PATH_ROOT)/src/VBox/GuestHost/SharedClipboard/x11-clipboard.cpp \ 36 38 clipboard.cpp 37 39 VBoxClient_LIBPATH = \ -
trunk/src/VBox/Additions/x11/VBoxClient/clipboard.cpp
r19800 r20552 23 23 * Header Files * 24 24 ****************************************************************************/ 25 #include <VBox/HostServices/VBoxClipboardSvc.h>26 #include <VBox/log.h>27 25 #include <iprt/alloc.h> 28 26 #include <iprt/asm.h> … … 31 29 #include <iprt/mem.h> 32 30 #include <iprt/string.h> 33 #include <iprt/thread.h>34 31 #include <iprt/process.h> 35 32 #include <iprt/semaphore.h> 36 33 37 #include <X11/Xlib.h> 38 #include <X11/Xatom.h> 39 #include <X11/Intrinsic.h> 40 #include <X11/Shell.h> 41 #include <X11/X.h> 42 #include <vector> 34 #include <VBox/log.h> 35 #include <VBox/HostServices/VBoxClipboardSvc.h> 36 #include <VBox/GuestHost/SharedClipboard.h> 43 37 44 38 #include "VBoxClient.h" 45 39 #include "clipboard.h" 46 47 /** The formats which we support in the guest. These can be deactivated in order to test specific code paths. */48 #define USE_UTF1649 #define USE_UTF850 #define USE_CTEXT51 40 52 41 /**************************************************************************** 53 42 * Global Variables * 54 43 ****************************************************************************/ 55 /** The different clipboard formats which we support. */56 enum g_eClipboardFormat57 {58 INVALID = 0,59 TARGETS,60 CTEXT,61 UTF8,62 UTF1663 };64 65 /** The X11 clipboard uses several names for the same format. This structure maps an X11 name to a format. */66 typedef struct67 {68 Atom atom;69 g_eClipboardFormat format;70 unsigned hostFormat;71 } VBOXCLIPBOARDFORMAT;72 73 /** Does the host or the guest currently own the clipboard? */74 enum g_eClipboardOwner75 {76 NONE = 0,77 HOST,78 GUEST79 };80 44 81 45 /** 82 46 * Global clipboard context information. 83 47 */ 84 typedef struct 85 { 86 /** The Xt application context structure */ 87 XtAppContext appContext; 88 89 /** We have a separate thread to wait for Window and Clipboard events */ 90 RTTHREAD thread; 91 /** The Xt widget which we use as our clipboard client. It is never made visible. */ 92 Widget widget; 93 94 /** X11 atom refering to the clipboard: CLIPBOARD */ 95 Atom atomClipboard; 96 /** X11 atom refering to the selection: PRIMARY */ 97 Atom atomPrimary; 98 /** X11 atom refering to the clipboard: TARGETS */ 99 Atom atomTargets; 100 /** X11 atom refering to the clipboard: MULTIPLE */ 101 Atom atomMultiple; 102 /** X11 atom refering to the clipboard: TIMESTAMP */ 103 Atom atomTimestamp; 104 /** X11 atom refering to the clipboard utf16 text format: text/plain;charset=ISO-10646-UCS-2 */ 105 Atom atomUtf16; 106 /** X11 atom refering to the clipboard utf8 text format: UTF8_STRING */ 107 Atom atomUtf8; 108 /** X11 atom refering to the native X11 clipboard text format: COMPOUND_TEXT */ 109 Atom atomCText; 110 111 /** A list of the X11 formats which we support, mapped to our identifier for them, in the order 112 we prefer to have them in. */ 113 std::vector<VBOXCLIPBOARDFORMAT> formatList; 114 115 /** Does the host or the guest currently own the clipboard? */ 116 volatile enum g_eClipboardOwner eOwner; 117 118 /** What is the best text format the guest has to offer? INVALID for none. */ 119 g_eClipboardFormat guestTextFormat; 120 /** Atom corresponding to the guest text format */ 121 Atom atomGuestTextFormat; 122 /** What is the best bitmap format the guest has to offer? INVALID for none. */ 123 g_eClipboardFormat guestBitmapFormat; 124 /** Atom corresponding to the guest Bitmap format */ 125 Atom atomGuestBitmapFormat; 126 /** What formats does the host have on offer? */ 127 int hostFormats; 128 /** Windows caches the clipboard data it receives. Since we have no way of knowing whether 129 that data is still valid, we always send a "data changed" message after a successful 130 transfer to invalidate the cache. */ 131 bool notifyHost; 132 133 /** Since the clipboard data moves asynchronously, we use an event semaphore to wait for it. */ 134 RTSEMEVENT terminating; 135 136 /** Format which we are reading from the guest clipboard (valid during a request for the 137 guest clipboard) */ 138 g_eClipboardFormat requestGuestFormat; 139 /** The guest buffer to write guest clipboard data to (valid during a request for the host 140 clipboard) */ 141 void *requestBuffer; 142 /** The size of the host buffer to write guest clipboard data to (valid during a request for 143 the guest clipboard) */ 144 unsigned requestBufferSize; 145 /** The size of the guest clipboard data written to the host buffer (valid during a request 146 for the guest clipboard) */ 147 uint32_t *requestActualSize; 148 48 struct _VBOXCLIPBOARDCONTEXT 49 { 149 50 /** Client ID for the clipboard subsystem */ 150 51 uint32_t client; 151 } VBOXCLIPBOARDCONTEXT; 52 53 /** Pointer to the X11 clipboard backend */ 54 CLIPBACKEND *pBackend; 55 }; 152 56 153 57 /** Only one client is supported. There seems to be no need for more clients. */ … … 183 87 * returned 184 88 */ 185 static int vboxClipboardReadHostData(uint32_t u32Format, void **ppv, uint32_t *pcb) 89 int ClipRequestDataForX11(VBOXCLIPBOARDCONTEXT *pCtx, uint32_t u32Format, 90 void **ppv, uint32_t *pcb) 186 91 { 187 92 int rc = VINF_SUCCESS; … … 229 134 } 230 135 231 232 /** 233 * Convert a Utf16 text with Linux EOLs to zero-terminated Utf16-LE with Windows EOLs, allocating 234 * memory for the converted text. Does no checking for validity. 235 * 236 * @returns VBox status code 237 * 238 * @param pu16Src Source Utf16 text to convert 239 * @param cwSrc Size of the source text in 16 bit words 240 * @retval ppu16Dest Where to store the pointer to the converted text. Only valid on success 241 * and if pcwDest is greater than 0. 242 * @retval pcwDest Size of the converted text in 16 bit words, including the trailing null 243 * if present 244 */ 245 static int vboxClipboardUtf16LinToWin(PRTUTF16 pu16Src, size_t cwSrc, PRTUTF16 *ppu16Dest, 246 size_t *pcwDest) 247 { 248 enum { LINEFEED = 0xa, CARRIAGERETURN = 0xd }; 249 PRTUTF16 pu16Dest; 250 size_t cwDest, i, j; 251 LogFlowFunc(("pu16Src=%.*ls, cwSrc=%u\n", cwSrc, pu16Src, cwSrc)); 252 if (cwSrc == 0) 253 { 254 *ppu16Dest = 0; 255 *pcwDest = 0; 256 LogFlowFunc(("*ppu16Dest=0, *pcwDest=0, rc=VINF_SUCCESS\n")); 257 return VINF_SUCCESS; 258 } 259 AssertReturn(VALID_PTR(pu16Src), VERR_INVALID_PARAMETER); 260 cwDest = 0; 261 for (i = 0; i < cwSrc; ++i, ++cwDest) 262 { 263 if (pu16Src[i] == LINEFEED) 264 ++cwDest; 265 if (pu16Src[i] == 0) 266 break; 267 } 268 /* Leave space for a trailing null */ 269 ++cwDest; 270 pu16Dest = reinterpret_cast<PRTUTF16>(RTMemAlloc(cwDest * 2)); 271 if (pu16Dest == 0) 272 { 273 LogFlowFunc(("rc=VERR_NO_MEMORY\n")); 274 return VERR_NO_MEMORY; 275 } 276 for (i = (pu16Src[0] == 0xfeff ? 1 : 0), j = 0; i < cwSrc; ++i, ++j) 277 { 278 if (pu16Src[i] == LINEFEED) 279 { 280 pu16Dest[j] = CARRIAGERETURN; 281 ++j; 282 } 283 if (pu16Src[i] == 0) 284 break; 285 pu16Dest[j] = pu16Src[i]; 286 } 287 /* The trailing null */ 288 pu16Dest[j] = 0; 289 *ppu16Dest = pu16Dest; 290 *pcwDest = cwDest; 291 LogFlowFunc(("*ppu16Dest=%p, *pcwDest=%d, rc=VINF_SUCCESS\n", pu16Dest, cwDest)); 292 return VINF_SUCCESS; 293 } 294 295 296 /** 297 * Convert the UTF-16 text returned from the guest X11 clipboard to UTF-16LE with Windows EOLs 298 * and send it to the host. 299 * 300 * @param pValue Source UTF-16 text 301 * @param cwSourceLen Length in 16-bit words of the source text 302 */ 303 static void vboxClipboardGetUtf16(XtPointer pValue, size_t cwSourceLen) 304 { 305 size_t cwDestLen; 306 PRTUTF16 pu16DestText; 307 PRTUTF16 pu16SourceText = reinterpret_cast<PRTUTF16>(pValue); 308 int rc; 309 310 LogFlowFunc(("converting Utf-16 to Utf-16LE. Original is %.*ls\n", 311 cwSourceLen - 1, pu16SourceText + 1)); 312 rc = vboxClipboardUtf16LinToWin(pu16SourceText, cwSourceLen, &pu16DestText, &cwDestLen); 313 if (rc != VINF_SUCCESS) 314 { 315 XtFree(reinterpret_cast<char *>(pValue)); 316 vboxClipboardSendData(0, NULL, 0); 317 LogFlowFunc(("sending empty data and returning\n")); 318 return; 319 } 320 LogFlow(("vboxClipboardGetUtf16: converted string is %.*ls\n", cwDestLen, pu16DestText)); 321 vboxClipboardSendData(VBOX_SHARED_CLIPBOARD_FMT_UNICODETEXT, 322 reinterpret_cast<void *>(pu16DestText), cwDestLen * 2); 323 RTMemFree(reinterpret_cast<void *>(pu16DestText)); 324 XtFree(reinterpret_cast<char *>(pValue)); 325 LogFlowFunc(("returning\n")); 326 } 327 328 329 /** 330 * Convert the UTF-8 text returned from the guest X11 clipboard to UTF-16LE with Windows EOLs 331 * and send it to the host. 332 * 333 * @param pValue Source UTF-8 text 334 * @param cbSourceLen Length in 8-bit bytes of the source text 335 */ 336 static void vboxClipboardGetUtf8(XtPointer pValue, size_t cbSourceLen) 337 { 338 size_t cwSourceLen, cwDestLen; 339 char *pu8SourceText = reinterpret_cast<char *>(pValue); 340 PRTUTF16 pu16SourceText = 0, pu16DestText; 341 int rc; 342 343 LogFlowFunc(("\n")); 344 LogFlow(("vboxClipboardGetUtf8: converting Utf-8 to Utf-16LE. Original is %.*s\n", cbSourceLen, 345 pu8SourceText)); 346 /* First convert the UTF8 to UTF16 */ 347 rc = RTStrToUtf16Ex(pu8SourceText, cbSourceLen, &pu16SourceText, 0, &cwSourceLen); 348 if (rc != VINF_SUCCESS) 349 { 350 XtFree(reinterpret_cast<char *>(pValue)); 351 vboxClipboardSendData(0, NULL, 0); 352 LogFlowFunc(("sending empty data and returning\n")); 353 return; 354 } 355 rc = vboxClipboardUtf16LinToWin(pu16SourceText, cwSourceLen, &pu16DestText, &cwDestLen); 356 if (rc != VINF_SUCCESS) 357 { 358 RTMemFree(reinterpret_cast<void *>(pu16SourceText)); 359 XtFree(reinterpret_cast<char *>(pValue)); 360 vboxClipboardSendData(0, NULL, 0); 361 LogFlowFunc(("sending empty data and returning\n")); 362 return; 363 } 364 LogFlow(("vboxClipboardGetUtf8: converted string is %.*ls\n", cwDestLen, pu16DestText)); 365 vboxClipboardSendData(VBOX_SHARED_CLIPBOARD_FMT_UNICODETEXT, 366 reinterpret_cast<void *>(pu16DestText), cwDestLen * 2); 367 RTMemFree(reinterpret_cast<void *>(pu16SourceText)); 368 RTMemFree(reinterpret_cast<void *>(pu16DestText)); 369 XtFree(reinterpret_cast<char *>(pValue)); 370 LogFlowFunc(("returning\n")); 371 } 372 373 374 /** 375 * Convert the compound text returned from the guest X11 clipboard to UTF-16LE with Windows EOLs 376 * and send it to the host. 377 * 378 * @param pValue Source compound text 379 * @param cbSourceLen Length in 8-bit bytes of the source text 380 */ 381 static void vboxClipboardGetCText(XtPointer pValue, size_t cbSourceLen) 382 { 383 size_t cwSourceLen, cwDestLen; 384 char **ppu8SourceText = 0; 385 PRTUTF16 pu16SourceText = 0, pu16DestText; 386 XTextProperty property; 387 int rc, cProps; 388 389 LogFlowFunc(("\n")); 390 LogFlow(("vboxClipboardGetCText: converting compound text to Utf-16LE. Original is %.*s\n", 391 cbSourceLen, pValue)); 392 /* Quick fix for 2.2. */ 393 if (cbSourceLen == 0) 394 { 395 XtFree(reinterpret_cast<char *>(pValue)); 396 vboxClipboardSendData(VBOX_SHARED_CLIPBOARD_FMT_UNICODETEXT, 397 NULL, 0); 398 return; 399 } 400 /* First convert the compound text to Utf8 */ 401 property.value = reinterpret_cast<unsigned char *>(pValue); 402 property.encoding = g_ctx.atomCText; 403 property.format = 8; 404 property.nitems = cbSourceLen; 405 #ifdef RT_OS_SOLARIS 406 rc = XmbTextPropertyToTextList(XtDisplay(g_ctx.widget), &property, &ppu8SourceText, &cProps); 407 #else 408 rc = Xutf8TextPropertyToTextList(XtDisplay(g_ctx.widget), &property, &ppu8SourceText, &cProps); 409 #endif 410 XtFree(reinterpret_cast<char *>(pValue)); 411 if (rc < 0) 412 { 413 const char *pcReason; 414 switch(rc) 415 { 416 case XNoMemory: 417 pcReason = "out of memory"; 418 break; 419 case XLocaleNotSupported: 420 pcReason = "locale (Utf8) not supported"; 421 break; 422 case XConverterNotFound: 423 pcReason = "converter not found"; 424 break; 425 default: 426 pcReason = "unknown error"; 427 } 428 XFreeStringList(ppu8SourceText); 429 LogRel(("vboxClipboardGetCText: Xutf8TextPropertyToTextList failed. Reason: %s\n", pcReason)); 430 vboxClipboardSendData(0, NULL, 0); 431 LogFlowFunc(("sending empty data and returning\n")); 432 return; 433 } 434 /* Next convert the UTF8 to UTF16 */ 435 rc = RTStrToUtf16Ex(*ppu8SourceText, cbSourceLen, &pu16SourceText, 0, &cwSourceLen); 436 XFreeStringList(ppu8SourceText); 437 if (rc != VINF_SUCCESS) 438 { 439 vboxClipboardSendData(0, NULL, 0); 440 LogFlowFunc(("sending empty data and returning\n")); 441 return; 442 } 443 rc = vboxClipboardUtf16LinToWin(pu16SourceText, cwSourceLen, &pu16DestText, &cwDestLen); 444 RTMemFree(reinterpret_cast<void *>(pu16SourceText)); 445 if (rc != VINF_SUCCESS) 446 { 447 vboxClipboardSendData(0, NULL, 0); 448 LogFlowFunc(("sending empty data and returning\n")); 449 return; 450 } 451 LogFlow(("vboxClipboardGetCText: converted string is %.*ls\n", cwDestLen, pu16DestText)); 452 vboxClipboardSendData(VBOX_SHARED_CLIPBOARD_FMT_UNICODETEXT, 453 reinterpret_cast<void *>(pu16DestText), cwDestLen * 2); 454 RTMemFree(reinterpret_cast<void *>(pu16DestText)); 455 LogFlowFunc(("returning\n")); 456 } 457 458 459 /** 460 * Convert the Latin1 text returned from the guest X11 clipboard to UTF-16LE with Windows EOLs 461 * and send it to the host. 462 * 463 * @param pValue Source Latin1 text 464 * @param cbSourceLen Length in 8-bit bytes of the source text 465 */ 466 static void vboxClipboardGetLatin1(XtPointer pValue, size_t cbSourceLen) 467 { 468 enum { LINEFEED = 0xa, CARRIAGERETURN = 0xd }; 469 /* Leave space for an additional null character at the end of the destination text. */ 470 size_t cwDestLen = cbSourceLen + 1, cwDestPos; 471 char *pu8SourceText = reinterpret_cast<char *>(pValue); 472 PRTUTF16 pu16DestText; 473 474 LogFlowFunc(("converting Latin1 to Utf-16LE. Original is %.*s\n", cbSourceLen, 475 pu8SourceText)); 476 /* Find the size of the destination text */ 477 for (size_t i = 0; i < cbSourceLen; i++) 478 { 479 if (pu8SourceText[i] == LINEFEED) 480 ++cwDestLen; 481 } 482 pu16DestText = reinterpret_cast<PRTUTF16>(RTMemAlloc(cwDestLen * 2)); 483 if (pu16DestText == 0) 484 { 485 XtFree(reinterpret_cast<char *>(pValue)); 486 LogFlow(("vboxClipboardGetLatin1: failed to allocate %d bytes!\n", cwDestLen * 2)); 487 vboxClipboardSendData(0, NULL, 0); 488 LogFlowFunc(("sending empty data and returning\n")); 489 return; 490 } 491 /* Copy the original X clipboard string to the destination, replacing Linux EOLs with 492 Windows ones */ 493 cwDestPos = 0; 494 for (size_t i = 0; i < cbSourceLen; ++i, ++cwDestPos) 495 { 496 if (pu8SourceText[i] == LINEFEED) 497 { 498 pu16DestText[cwDestPos] = CARRIAGERETURN; 499 ++cwDestPos; 500 } 501 /* latin1 < utf-16LE */ 502 pu16DestText[cwDestPos] = pu8SourceText[i]; 503 if (pu8SourceText[i] == 0) 504 break; 505 } 506 pu16DestText[cwDestPos] = 0; 507 LogFlow(("vboxClipboardGetLatin1: converted text is %.*ls\n", cwDestPos, pu16DestText)); 508 vboxClipboardSendData(VBOX_SHARED_CLIPBOARD_FMT_UNICODETEXT, 509 reinterpret_cast<void *>(pu16DestText), cwDestPos * 2); 510 RTMemFree(reinterpret_cast<void *>(pu16DestText)); 511 XtFree(reinterpret_cast<char *>(pValue)); 512 LogFlowFunc(("returning\n")); 513 } 514 515 516 /** Convert the clipboard text from the current format to Utf-16 with Windows line breaks. 517 We are reading the guest clipboard to make it available to the host. */ 518 static void vboxClipboardGetProc(Widget, XtPointer /* pClientData */, Atom * /* selection */, 519 Atom *atomType, XtPointer pValue, long unsigned int *pcLen, 520 int *piFormat) 521 { 522 LogFlowFunc(("*pcLen=%lu, *piFormat=%d, requested target format: %d, g_ctx.requestBufferSize=%d\n", 523 *pcLen, *piFormat, g_ctx.requestGuestFormat, g_ctx.requestBufferSize)); 524 /* The X Toolkit may have failed to get the clipboard selection for us. */ 525 if (*atomType == XT_CONVERT_FAIL) 526 { 527 vboxClipboardSendData(0, NULL, 0); 528 LogFlowFunc(("Xt failed to convert the data. Sending empty data and returning\n")); 529 return; 530 } 531 if (NULL == pValue) 532 { 533 vboxClipboardSendData(0, NULL, 0); 534 LogFlowFunc(("The clipboard contents changed while we were requesting them. Sending empty data and returning\n")); 535 return; 536 } 537 /* In which format did we request the clipboard data? */ 538 unsigned cTextLen = (*pcLen) * (*piFormat) / 8; 539 switch (g_ctx.requestGuestFormat) 540 { 541 case UTF16: 542 vboxClipboardGetUtf16(pValue, cTextLen / 2); 543 break; 544 case CTEXT: 545 vboxClipboardGetCText(pValue, cTextLen); 546 break; 547 case UTF8: 548 { 549 /* If we are given broken Utf-8, we treat it as Latin1. Is this acceptable? */ 550 size_t cStringLen; 551 char *pu8SourceText = reinterpret_cast<char *>(pValue); 552 553 if (RTStrUniLenEx(pu8SourceText, *pcLen, &cStringLen) == VINF_SUCCESS) 554 vboxClipboardGetUtf8(pValue, cTextLen); 555 else 556 vboxClipboardGetLatin1(pValue, cTextLen); 557 break; 558 } 559 default: 560 { 561 XtFree(reinterpret_cast<char *>(pValue)); 562 Log(("vboxClipboardGetProc: bad target format\n")); 563 vboxClipboardSendData(0, NULL, 0); 564 LogFlowFunc(("sending empty data and returning\n")); 565 return; 566 } 567 } 568 g_ctx.notifyHost = true; 569 LogFlowFunc(("returning\n")); 570 } 571 136 /** Opaque data structure describing a request from the host for clipboard 137 * data, passed in when the request is forwarded to the X11 backend so that 138 * it can be completed correctly. */ 139 struct _CLIPREADCBREQ 140 { 141 /** The data format that was requested. */ 142 uint32_t u32Format; 143 }; 572 144 573 145 /** 574 146 * Tell the host that new clipboard formats are available. 575 147 * 576 * @returns VBox status code.577 148 * @param u32Formats The formats to advertise 578 149 */ 579 static int vboxClipboardReportFormats(uint32_t u32Formats)150 void ClipReportX11Formats(VBOXCLIPBOARDCONTEXT *pCtx, uint32_t u32Formats) 580 151 { 581 152 int rc; … … 583 154 rc = VbglR3ClipboardReportFormats(g_ctx.client, u32Formats); 584 155 LogFlowFunc(("rc=%Rrc\n", rc)); 156 } 157 158 /** This is called by the backend to tell us that a request for data from 159 * X11 has completed. 160 * @param pCtx Our context information 161 * @param rc the iprt result code of the request 162 * @param pReq the request structure that we passed in when we started 163 * the request. We RTMemFree() this in this function. 164 * @param pv the clipboard data returned from X11 if the request 165 * succeeded (see @a rc) 166 * @param cb the size of the data in @a pv 167 */ 168 void ClipCompleteDataRequestFromX11(VBOXCLIPBOARDCONTEXT *pCtx, int rc, 169 CLIPREADCBREQ *pReq, void *pv, 170 uint32_t cb) 171 { 172 if (RT_SUCCESS(rc)) 173 vboxClipboardSendData(pReq->u32Format, pv, cb); 174 else 175 vboxClipboardSendData(0, NULL, 0); 176 RTMemFree(pReq); 177 } 178 179 /** 180 * Connect the guest clipboard to the host. 181 * 182 * @returns VBox status code 183 */ 184 int vboxClipboardConnect(void) 185 { 186 int rc; 187 LogFlowFunc(("\n")); 188 189 /* Sanity */ 190 AssertReturn(g_ctx.client == 0, VERR_WRONG_ORDER); 191 g_ctx.pBackend = ClipConstructX11(&g_ctx); 192 if (!g_ctx.pBackend) 193 rc = VERR_NO_MEMORY; 194 if (RT_SUCCESS(rc)) 195 rc = ClipStartX11(g_ctx.pBackend); 196 if (RT_SUCCESS(rc)) 197 { 198 rc = VbglR3ClipboardConnect(&g_ctx.client); 199 if (RT_FAILURE(rc)) 200 LogRel(("Error connecting to host. rc=%Rrc\n", rc)); 201 else if (!g_ctx.client) 202 { 203 LogRel(("Invalid client ID of 0\n")); 204 rc = VERR_NOT_SUPPORTED; 205 } 206 } 207 208 if (RT_FAILURE(rc) && g_ctx.pBackend) 209 ClipDestructX11(g_ctx.pBackend); 210 LogFlowFunc(("g_ctx.client=%u rc=%Rrc\n", g_ctx.client, rc)); 585 211 return rc; 586 212 } 587 213 588 589 /** Callback to handle a reply to a request for the targets the current clipboard holder can 590 handle. We are reading the guest clipboard to make it available to the host. */ 591 static void vboxClipboardTargetsProc(Widget, XtPointer, Atom * /* selection */, Atom *atomType, 592 XtPointer pValue, long unsigned int *pcLen, int *piFormat) 593 { 594 static int cCalls = 0; 595 Atom *atomTargets = reinterpret_cast<Atom *>(pValue); 596 /* The number of format atoms the clipboard holder is offering us */ 597 unsigned cAtoms = *pcLen; 598 /* The best clipboard format we have found so far */ 599 g_eClipboardFormat eBestTarget = INVALID; 600 /* The atom corresponding to our best clipboard format found */ 601 Atom atomBestTarget = None; 602 603 if ((cCalls % 10) == 0) 604 Log3(("vboxClipboardTargetsProc called, cAtoms=%d\n", cAtoms)); 605 if (*atomType == XT_CONVERT_FAIL) 606 { 607 Log(("vboxClipboardTargetsProc: reading clipboard from guest, X toolkit failed to convert the selection\n")); 608 LogFlowFunc(("returning\n")); 609 return; 610 } 611 /* Run through the atoms offered to us to see if we recognise them. If we find the atom for 612 a "better" format than the best we have found so far, we remember it as our new "best" 613 format. */ 614 for (unsigned i = 0; i < cAtoms; ++i) 615 { 616 for (unsigned j = 0; j != g_ctx.formatList.size(); ++j) 617 if (g_ctx.formatList[j].atom == atomTargets[i]) 618 { 619 if (eBestTarget < g_ctx.formatList[j].format) 620 { 621 eBestTarget = g_ctx.formatList[j].format; 622 atomBestTarget = g_ctx.formatList[j].atom; 623 } 624 break; 625 } 626 #ifdef DEBUG 627 char *szAtomName = XGetAtomName(XtDisplay(g_ctx.widget), atomTargets[i]); 628 if (szAtomName != 0) 629 { 630 if ((cCalls % 10) == 0) 631 Log3(("vboxClipboardTargetsProc: the guest offers target %s\n", szAtomName)); 632 XFree(szAtomName); 633 } 634 else 635 { 636 if ((cCalls % 10) == 0) 637 Log3(("vboxClipboardTargetsProc: the guest returned a bad atom: %d\n", 638 atomTargets[i])); 639 } 640 #endif 641 } 642 g_ctx.atomGuestTextFormat = atomBestTarget; 643 /* If the available formats as seen by the host have changed, or if we suspect that 644 the host has cached the clipboard data (which can change without our noticing it), 645 then tell the host that new clipboard contents are available. */ 646 if ((eBestTarget != g_ctx.guestTextFormat) || (g_ctx.notifyHost == true)) 647 { 648 uint32_t u32Formats = 0; 649 #ifdef DEBUG 650 if (atomBestTarget != None) 651 { 652 char *szAtomName = XGetAtomName(XtDisplay(g_ctx.widget), atomBestTarget); 653 LogFlow(("vboxClipboardTargetsProc: switching to guest text target %s. Available targets are:\n", szAtomName)); 654 XFree(szAtomName); 655 } 656 else 657 LogFlow(("vboxClipboardTargetsProc: no supported host text target found. Available targets are:\n")); 658 659 for (unsigned i = 0; i < cAtoms; ++i) 660 { 661 char *szAtomName = XGetAtomName(XtDisplay(g_ctx.widget), atomTargets[i]); 662 if (szAtomName != 0) 663 { 664 LogFlow(("vboxClipboardTargetsProc: %s\n", szAtomName)); 665 XFree(szAtomName); 666 } 667 } 668 #endif 669 g_ctx.guestTextFormat = eBestTarget; 670 if (eBestTarget != INVALID) 671 u32Formats |= VBOX_SHARED_CLIPBOARD_FMT_UNICODETEXT; 672 vboxClipboardReportFormats(u32Formats); 673 g_ctx.notifyHost = false; 674 } 675 XtFree(reinterpret_cast<char *>(pValue)); 676 ++cCalls; 677 } 678 679 680 /** 681 * This callback is called every 200ms to check the contents of the guest clipboard. 682 */ 683 static void vboxClipboardTimerProc(XtPointer /* pUserData */, XtIntervalId * /* hTimerId */) 684 { 685 static int cCalls = 0; 686 /* Get the current clipboard contents */ 687 if (g_ctx.eOwner == GUEST) 688 { 689 if ((cCalls % 10) == 0) 690 Log3(("vboxClipboardTimerProc: requesting the targets that the guest clipboard offers\n")); 691 XtGetSelectionValue(g_ctx.widget, g_ctx.atomClipboard, g_ctx.atomTargets, 692 vboxClipboardTargetsProc, 0, CurrentTime); 693 } 694 /* Re-arm our timer */ 695 XtAppAddTimeOut(g_ctx.appContext, 200 /* ms */, vboxClipboardTimerProc, 0); 696 ++cCalls; 697 } 698 699 700 /** 701 * Satisfy a request from the guest for available clipboard targets. 702 * 703 * @returns true if we successfully convert the data to the format requested, false otherwise. 704 * 705 * @param atomTypeReturn The type of the data we are returning 706 * @param pValReturn A pointer to the data we are returning. This should be to memory 707 * allocated by XtMalloc, which will be freed by the toolkit later 708 * @param pcLenReturn The length of the data we are returning 709 * @param piFormatReturn The format (8bit, 16bit, 32bit) of the data we are returning 710 */ 711 static Boolean vboxClipboardConvertTargets(Atom *atomTypeReturn, XtPointer *pValReturn, 712 unsigned long *pcLenReturn, int *piFormatReturn) 713 { 714 unsigned uListSize = g_ctx.formatList.size(); 715 Atom *atomTargets = reinterpret_cast<Atom *>(XtMalloc((uListSize + 3) * sizeof(Atom))); 716 unsigned cTargets = 0; 717 718 LogFlowFunc(("\n")); 719 LogFlow(("vboxClipboardConvertTargets: uListSize=%u\n", uListSize)); 720 for (unsigned i = 0; i < uListSize; ++i) 721 { 722 if ( ((g_ctx.hostFormats & VBOX_SHARED_CLIPBOARD_FMT_UNICODETEXT) != 0) 723 && (g_ctx.formatList[i].hostFormat == VBOX_SHARED_CLIPBOARD_FMT_UNICODETEXT)) 724 { 725 atomTargets[cTargets] = g_ctx.formatList[i].atom; 726 ++cTargets; 727 } 728 } 729 atomTargets[cTargets] = g_ctx.atomTargets; 730 atomTargets[cTargets + 1] = g_ctx.atomMultiple; 731 atomTargets[cTargets + 2] = g_ctx.atomTimestamp; 732 #ifdef DEBUG 733 for (unsigned i = 0; i < cTargets + 3; i++) 734 { 735 char *szAtomName = XGetAtomName(XtDisplay(g_ctx.widget), atomTargets[i]); 736 if (szAtomName != 0) 737 { 738 LogFlow(("vboxClipboardConvertTargets: returning target %s\n", szAtomName)); 739 XFree(szAtomName); 740 } 741 else 742 Log(("vboxClipboardConvertTargets: invalid atom %d in the list!\n", atomTargets[i])); 743 } 744 #endif 745 *atomTypeReturn = XA_ATOM; 746 *pValReturn = reinterpret_cast<XtPointer>(atomTargets); 747 *pcLenReturn = cTargets + 3; 748 *piFormatReturn = 32; 749 LogFlowFunc(("returning true\n")); 750 return true; 751 } 752 753 754 /** 755 * Get the size of the buffer needed to hold a zero-terminated Utf16 string with Linux EOLs 756 * converted from a Utf16 string with Windows EOLs. 757 * 758 * @returns The size of the buffer needed in bytes 759 * 760 * @param pu16Src The source Utf16 string 761 * @param cwSrc The length in 16 bit words of the source string 762 */ 763 static int vboxClipboardUtf16GetLinSize(PRTUTF16 pu16Src, size_t cwSrc) 764 { 765 enum { LINEFEED = 0xa, CARRIAGERETURN = 0xd }; 766 size_t cwDest; 767 768 LogFlowFunc(("pu16Src=%.*ls, cwSrc=%u\n", cwSrc, pu16Src, cwSrc)); 769 AssertReturn(pu16Src != 0, VERR_INVALID_PARAMETER); 770 /* We only take little endian Utf16 */ 771 AssertReturn(pu16Src[0] != 0xfffe, VERR_INVALID_PARAMETER); 772 if (cwSrc == 0) 773 { 774 LogFlowFunc(("returning 0\n")); 775 return 0; 776 } 777 /* Calculate the size of the destination text string. */ 778 /* Is this Utf16 or Utf16-LE? */ 779 if (pu16Src[0] == 0xfeff) 780 cwDest = 0; 781 else 782 cwDest = 1; 783 for (size_t i = 0; i < cwSrc; ++i, ++cwDest) 784 { 785 if ( (i + 1 < cwSrc) 786 && (pu16Src[i] == CARRIAGERETURN) 787 && (pu16Src[i + 1] == LINEFEED)) 788 ++i; 789 if (pu16Src[i] == 0) 790 break; 791 } 792 /* The terminating null */ 793 ++cwDest; 794 LogFlowFunc(("returning %d\n", cwDest * 2)); 795 return cwDest * 2; 796 } 797 798 799 /** 800 * Convert Utf16-LE text with Windows EOLs to Utf16 with Linux EOLs. This function does not 801 * verify that the Utf16 is valid. 802 * 803 * @returns VBox status code 804 * 805 * @param pu16Src Text to convert 806 * @param cwSrc Size of the source text in 16 bit words 807 * @param pu16Dest The buffer to store the converted text to 808 * @param cwDest The size of the buffer for the destination text 809 */ 810 static int vboxClipboardUtf16WinToLin(PRTUTF16 pu16Src, size_t cwSrc, PRTUTF16 pu16Dest, 811 size_t cwDest) 812 { 813 enum { LINEFEED = 0xa, CARRIAGERETURN = 0xd }; 814 size_t cwDestPos; 815 816 LogFlowFunc(("pu16Src=%.*ls, cwSrc=%u, pu16Dest=%p, cwDest=%u\n", 817 cwSrc, pu16Src, cwSrc, pu16Dest, cwDest)); 818 /* A buffer of size 0 may not be an error, but it is not a good idea either. */ 819 Assert(cwDest > 0); 820 AssertReturn(VALID_PTR(pu16Src), VERR_INVALID_PARAMETER); 821 /* We only take little endian Utf16 */ 822 AssertReturn(pu16Src[0] != 0xfffe, VERR_INVALID_PARAMETER); 823 if (cwSrc == 0) 824 { 825 if (cwDest != 0) 826 pu16Dest[0] = 0; 827 LogFlowFunc(("set empty string. Returning VINF_SUCCESS\n")); 828 return VINF_SUCCESS; 829 } 830 /* Prepend the Utf16 byte order marker if it is missing. */ 831 if (pu16Src[0] == 0xfeff) 832 cwDestPos = 0; 833 else 834 { 835 if (cwDest == 0) 836 { 837 LogFlowFunc(("returning VERR_BUFFER_OVERFLOW\n")); 838 return VERR_BUFFER_OVERFLOW; 839 } 840 pu16Dest[0] = 0xfeff; 841 cwDestPos = 1; 842 } 843 for (size_t i = 0; i < cwSrc; ++i, ++cwDestPos) 844 { 845 if (cwDestPos == cwDest) 846 { 847 LogFlowFunc(("returning VERR_BUFFER_OVERFLOW\n")); 848 return VERR_BUFFER_OVERFLOW; 849 } 850 if ( (i + 1 < cwSrc) 851 && (pu16Src[i] == CARRIAGERETURN) 852 && (pu16Src[i + 1] == LINEFEED)) 853 ++i; 854 855 pu16Dest[cwDestPos] = pu16Src[i]; 856 if (pu16Src[i] == 0) 857 break; 858 } 859 if (cwDestPos == cwDest) 860 { 861 LogFlowFunc(("returning VERR_BUFFER_OVERFLOW\n")); 862 return VERR_BUFFER_OVERFLOW; 863 } 864 pu16Dest[cwDestPos] = 0; 865 LogFlowFunc(("set string %ls. Returning\n", pu16Dest + 1)); 866 return VINF_SUCCESS; 867 } 868 869 870 /** 871 * Satisfy a request from the guest to convert the clipboard text to Utf16. 872 * 873 * @returns true if we successfully convert the data to the format requested, false otherwise. 874 * 875 * @param atomTypeReturn The type of the data we are returning 876 * @param pValReturn A pointer to the data we are returning. This should be to memory 877 * allocated by XtMalloc, which will be freed by the toolkit later 878 * @param pcLenReturn The length of the data we are returning 879 * @param piFormatReturn The format (8bit, 16bit, 32bit) of the data we are returning 880 */ 881 static Boolean vboxClipboardConvertUtf16(Atom *atomTypeReturn, XtPointer *pValReturn, 882 unsigned long *pcLenReturn, int *piFormatReturn) 883 { 884 PRTUTF16 pu16GuestText, pu16HostText; 885 unsigned cbHostText, cwHostText, cwGuestText; 214 /** 215 * The main loop of our clipboard reader. 216 */ 217 int vboxClipboardMain(void) 218 { 886 219 int rc; 887 888 LogFlowFunc(("\n")); 889 rc = vboxClipboardReadHostData(VBOX_SHARED_CLIPBOARD_FMT_UNICODETEXT, 890 reinterpret_cast<void **>(&pu16HostText), &cbHostText); 891 if ((rc != VINF_SUCCESS) || cbHostText == 0) 892 { 893 LogFlow(("vboxClipboardConvertUtf16: vboxClipboardReadHostData returned %Rrc, %d bytes of data\n", rc, cbHostText)); 894 g_ctx.hostFormats = 0; 895 LogFlowFunc(("rc = false\n")); 896 return false; 897 } 898 cwHostText = cbHostText / 2; 899 cwGuestText = vboxClipboardUtf16GetLinSize(pu16HostText, cwHostText); 900 pu16GuestText = reinterpret_cast<PRTUTF16>(XtMalloc(cwGuestText * 2)); 901 if (pu16GuestText == 0) 902 { 903 RTMemFree(reinterpret_cast<void *>(pu16HostText)); 904 LogFlowFunc(("rc = false\n")); 905 return false; 906 } 907 rc = vboxClipboardUtf16WinToLin(pu16HostText, cwHostText, pu16GuestText, cwGuestText); 908 if (rc != VINF_SUCCESS) 909 { 910 LogFlow(("vboxClipboardConvertUtf16: vboxClipboardUtf16WinToLin returned %Rrc\n", rc)); 911 RTMemFree(reinterpret_cast<void *>(pu16HostText)); 912 XtFree(reinterpret_cast<char *>(pu16GuestText)); 913 LogFlowFunc(("rc = false\n")); 914 return false; 915 } 916 LogFlow(("vboxClipboardConvertUtf16: returning Unicode, original text is %.*ls\n", cwHostText, pu16HostText)); 917 LogFlow(("vboxClipboardConvertUtf16: converted text is %.*ls\n", cwGuestText - 1, pu16GuestText + 1)); 918 RTMemFree(reinterpret_cast<void *>(pu16HostText)); 919 *atomTypeReturn = g_ctx.atomUtf16; 920 *pValReturn = reinterpret_cast<XtPointer>(pu16GuestText); 921 *pcLenReturn = cwGuestText * 2; 922 *piFormatReturn = 8; 923 LogFlowFunc(("rc = true\n")); 924 return true; 925 } 926 927 928 /** 929 * Satisfy a request from the guest to convert the clipboard text to Utf8. 930 * 931 * @returns true if we successfully convert the data to the format requested, false otherwise. 932 * 933 * @param atomTypeReturn The type of the data we are returning 934 * @param pValReturn A pointer to the data we are returning. This should be to memory 935 * allocated by XtMalloc, which will be freed by the toolkit later 936 * @param pcLenReturn The length of the data we are returning 937 * @param piFormatReturn The format (8bit, 16bit, 32bit) of the data we are returning 938 */ 939 static Boolean vboxClipboardConvertUtf8(Atom *atomTypeReturn, XtPointer *pValReturn, 940 unsigned long *pcLenReturn, int *piFormatReturn) 941 { 942 PRTUTF16 pu16GuestText, pu16HostText; 943 char *pcGuestText; 944 unsigned cbHostText, cwHostText, cwGuestText, cbGuestTextBuffer; 945 size_t cbGuestTextActual; 946 int rc; 947 948 LogFlowFunc(("\n")); 949 /* Get the host Utf16 data and convert it to Linux Utf16. */ 950 rc = vboxClipboardReadHostData(VBOX_SHARED_CLIPBOARD_FMT_UNICODETEXT, 951 reinterpret_cast<void **>(&pu16HostText), &cbHostText); 952 if ((rc != VINF_SUCCESS) || cbHostText == 0) 953 { 954 LogFlow(("vboxClipboardConvertUtf16: vboxClipboardReadHostData returned %Rrc, %d bytes of data\n", rc, cbHostText)); 955 g_ctx.hostFormats = 0; 956 LogFlowFunc(("rc = false\n")); 957 return false; 958 } 959 cwHostText = cbHostText / 2; 960 LogFlow(("vboxClipboardConvertUtf8: original text is %.*ls\n", cwHostText, pu16HostText)); 961 cwGuestText = vboxClipboardUtf16GetLinSize(pu16HostText, cwHostText); 962 pu16GuestText = reinterpret_cast<PRTUTF16>(RTMemAlloc(cwGuestText * 2)); 963 if (pu16GuestText == 0) 964 { 965 RTMemFree(reinterpret_cast<char *>(pu16HostText)); 966 LogFlowFunc(("rc = false\n")); 967 return false; 968 } 969 rc = vboxClipboardUtf16WinToLin(pu16HostText, cwHostText, pu16GuestText, cwGuestText); 970 RTMemFree(reinterpret_cast<char *>(pu16HostText)); 971 if (rc != VINF_SUCCESS) 972 { 973 LogFlow(("vboxClipboardConvertUtf16: vboxClipboardUtf16WinToLin returned %Rrc\n", rc)); 974 RTMemFree(reinterpret_cast<char *>(pu16GuestText)); 975 LogFlowFunc(("rc = false\n")); 976 return false; 977 } 978 /* Now convert the Utf16 Linux text to Utf8 */ 979 cbGuestTextBuffer = cwGuestText * 3; /* Should always be enough. */ 980 pcGuestText = XtMalloc(cbGuestTextBuffer); 981 if (pcGuestText == 0) 982 { 983 RTMemFree(reinterpret_cast<char *>(pu16GuestText)); 984 LogFlowFunc(("rc = false\n")); 985 return false; 986 } 987 /* Our runtime can't cope with endian markers. */ 988 cbGuestTextActual = 0; 989 rc = RTUtf16ToUtf8Ex(pu16GuestText + 1, cwGuestText - 1, &pcGuestText, cbGuestTextBuffer, &cbGuestTextActual); 990 RTMemFree(reinterpret_cast<char *>(pu16GuestText)); 991 if (rc != VINF_SUCCESS) 992 { 993 XtFree(pcGuestText); 994 LogFlowFunc(("rc = false\n")); 995 return false; 996 } 997 LogFlow(("vboxClipboardConvertUtf8: converted text is %.*s\n", cbGuestTextActual, pcGuestText)); 998 *atomTypeReturn = g_ctx.atomUtf8; 999 *pValReturn = reinterpret_cast<XtPointer>(pcGuestText); 1000 *pcLenReturn = (unsigned long)cbGuestTextActual; 1001 *piFormatReturn = 8; 1002 LogFlowFunc(("rc = true\n")); 1003 return true; 1004 } 1005 1006 1007 /** 1008 * Satisfy a request from the guest to convert the clipboard text to compound text. 1009 * 1010 * @returns true if we successfully convert the data to the format requested, false otherwise. 1011 * 1012 * @param atomTypeReturn The type of the data we are returning 1013 * @param pValReturn A pointer to the data we are returning. This should be to memory 1014 * allocated by XtMalloc, which will be freed by the toolkit later 1015 * @param pcLenReturn The length of the data we are returning 1016 * @param piFormatReturn The format (8bit, 16bit, 32bit) of the data we are returning 1017 */ 1018 static Boolean vboxClipboardConvertCText(Atom *atomTypeReturn, XtPointer *pValReturn, 1019 unsigned long *pcLenReturn, int *piFormatReturn) 1020 { 1021 PRTUTF16 pu16GuestText, pu16HostText; 1022 char *pcUtf8Text; 1023 unsigned cbHostText, cwHostText, cwGuestText, cbUtf8Text; 1024 XTextProperty property; 1025 int rc; 1026 1027 LogFlowFunc(("\n")); 1028 /* Get the host Utf16 data and convert it to Linux Utf16. */ 1029 rc = vboxClipboardReadHostData(VBOX_SHARED_CLIPBOARD_FMT_UNICODETEXT, 1030 reinterpret_cast<void **>(&pu16HostText), &cbHostText); 1031 if ((rc != VINF_SUCCESS) || cbHostText == 0) 1032 { 1033 LogFlow(("vboxClipboardConvertCText: vboxClipboardReadHostData returned %Rrc, %d bytes of data\n", rc, cbHostText)); 1034 g_ctx.hostFormats = 0; 1035 LogFlowFunc(("rc = false\n")); 1036 return false; 1037 } 1038 cwHostText = cbHostText / 2; 1039 LogFlow(("vboxClipboardConvertCText: original text is %.*ls\n", cwHostText, pu16HostText)); 1040 cwGuestText = vboxClipboardUtf16GetLinSize(pu16HostText, cwHostText); 1041 pu16GuestText = reinterpret_cast<PRTUTF16>(RTMemAlloc(cwGuestText * 2)); 1042 if (pu16GuestText == 0) 1043 { 1044 Log(("vboxClipboardConvertCText: out of memory\n")); 1045 RTMemFree(reinterpret_cast<char *>(pu16HostText)); 1046 LogFlowFunc(("rc = false\n")); 1047 return false; 1048 } 1049 rc = vboxClipboardUtf16WinToLin(pu16HostText, cwHostText, pu16GuestText, cwGuestText); 1050 RTMemFree(reinterpret_cast<char *>(pu16HostText)); 1051 if (rc != VINF_SUCCESS) 1052 { 1053 LogFlow(("vboxClipboardConvertUtf16: vboxClipboardUtf16WinToLin returned %Rrc\n", rc)); 1054 RTMemFree(reinterpret_cast<char *>(pu16GuestText)); 1055 LogFlowFunc(("rc = false\n")); 1056 return false; 1057 } 1058 /* Now convert the Utf16 Linux text to Utf8 */ 1059 cbUtf8Text = cwGuestText * 3; /* Should always be enough. */ 1060 pcUtf8Text = reinterpret_cast<char *>(RTMemAlloc(cbUtf8Text)); 1061 if (pcUtf8Text == 0) 1062 { 1063 Log(("vboxClipboardConvertCText: out of memory\n")); 1064 RTMemFree(reinterpret_cast<char *>(pu16GuestText)); 1065 LogFlowFunc(("rc = false\n")); 1066 return false; 1067 } 1068 /* Our runtime can't cope with endian markers. */ 1069 rc = RTUtf16ToUtf8Ex(pu16GuestText + 1, cwGuestText - 1, &pcUtf8Text, cbUtf8Text, 0); 1070 RTMemFree(reinterpret_cast<char *>(pu16GuestText)); 1071 if (rc != VINF_SUCCESS) 1072 { 1073 Log(("vboxClipboardConvertCText: RTUtf16ToUtf8Ex failed: rc=%Rrc\n", rc)); 1074 XtFree(pcUtf8Text); 1075 LogFlowFunc(("rc = false\n")); 1076 return false; 1077 } 1078 /* And finally (!) convert the Utf8 text to compound text. */ 1079 #ifdef RT_OS_SOLARIS 1080 rc = XmbTextListToTextProperty(XtDisplay(g_ctx.widget), &pcUtf8Text, 1, 1081 XCompoundTextStyle, &property); 1082 #else 1083 rc = Xutf8TextListToTextProperty(XtDisplay(g_ctx.widget), &pcUtf8Text, 1, 1084 XCompoundTextStyle, &property); 1085 #endif 1086 RTMemFree(pcUtf8Text); 1087 if (rc < 0) 1088 { 1089 const char *pcReason; 1090 switch(rc) 1091 { 1092 case XNoMemory: 1093 pcReason = "out of memory"; 1094 break; 1095 case XLocaleNotSupported: 1096 pcReason = "locale (Utf8) not supported"; 1097 break; 1098 case XConverterNotFound: 1099 pcReason = "converter not found"; 1100 break; 1101 default: 1102 pcReason = "unknown error"; 1103 } 1104 LogRel(("vboxClipboardConvertCText: Xutf8TextListToTextProperty failed. Reason: %s\n", 1105 pcReason)); 1106 LogFlowFunc(("rc = false\n")); 1107 return false; 1108 } 1109 LogFlow(("vboxClipboardConvertCText: converted text is %s\n", property.value)); 1110 *atomTypeReturn = property.encoding; 1111 *pValReturn = reinterpret_cast<XtPointer>(property.value); 1112 *pcLenReturn = property.nitems; 1113 *piFormatReturn = property.format; 1114 LogFlowFunc(("rc = true\n")); 1115 return true; 1116 } 1117 1118 1119 /** 1120 * Callback to convert the hosts clipboard data for an application on the guest. Called by the 1121 * X Toolkit. 1122 * @returns true if we successfully convert the data to the format requested, false otherwise. 1123 * 1124 * @param atomSelection The selection which is being requested. We only handle the clipboard. 1125 * @param atomTarget The format we should convert the data to 1126 * @param atomTypeReturn The type of the data we are returning 1127 * @param pValReturn A pointer to the data we are returning. This should be to memory 1128 * allocated by XtMalloc, which will be freed by the toolkit later 1129 * @param pcLenReturn The length of the data we are returning 1130 * @param piFormatReturn The format (8bit, 16bit, 32bit) of the data we are returning 1131 */ 1132 static Boolean vboxClipboardConvertProc(Widget, Atom *atomSelection, Atom *atomTarget, 1133 Atom *atomTypeReturn, XtPointer *pValReturn, 1134 unsigned long *pcLenReturn, int *piFormatReturn) 1135 { 1136 g_eClipboardFormat eFormat = INVALID; 1137 int rc; 1138 1139 LogFlowFunc(("\n")); 1140 if ( (*atomSelection != g_ctx.atomClipboard) 1141 && (*atomSelection != g_ctx.atomPrimary) 1142 ) 1143 { 1144 LogFlowFunc(("rc = false\n")); 1145 return false; 1146 } 1147 #ifdef DEBUG 1148 char *szAtomName = XGetAtomName(XtDisplay(g_ctx.widget), *atomTarget); 1149 if (szAtomName != 0) 1150 { 1151 LogFlow(("vboxClipboardConvertProc: request for format %s\n", szAtomName)); 1152 XFree(szAtomName); 1153 } 1154 else 1155 Log(("vboxClipboardConvertProc: request for invalid target atom %d!\n", *atomTarget)); 1156 #endif 1157 if (*atomTarget == g_ctx.atomTargets) 1158 eFormat = TARGETS; 1159 else 1160 { 1161 for (unsigned i = 0; i != g_ctx.formatList.size(); ++i) 1162 { 1163 if (g_ctx.formatList[i].atom == *atomTarget) 1164 { 1165 eFormat = g_ctx.formatList[i].format; 1166 break; 1167 } 1168 } 1169 } 1170 switch (eFormat) 1171 { 1172 case TARGETS: 1173 rc = vboxClipboardConvertTargets(atomTypeReturn, pValReturn, pcLenReturn, piFormatReturn); 1174 LogFlowFunc(("TARGETS rc=%d\n", rc)); 1175 return rc; 1176 case UTF16: 1177 rc = vboxClipboardConvertUtf16(atomTypeReturn, pValReturn, pcLenReturn, piFormatReturn); 1178 LogFlowFunc(("UTF16 rc=%d\n", rc)); 1179 return rc; 1180 case UTF8: 1181 rc = vboxClipboardConvertUtf8(atomTypeReturn, pValReturn, pcLenReturn, piFormatReturn); 1182 LogFlowFunc(("UTF8 rc=%d\n", rc)); 1183 return rc; 1184 case CTEXT: 1185 rc = vboxClipboardConvertCText(atomTypeReturn, pValReturn, pcLenReturn, piFormatReturn); 1186 LogFlowFunc(("CTEXT rc=%d\n", rc)); 1187 return rc; 1188 default: 1189 Log(("vboxClipboardConvertProc: bad format\n")); 1190 LogFlowFunc(("rc = false\n")); 1191 return false; 1192 } 1193 } 1194 1195 1196 static void vboxClipboardLoseProc(Widget, Atom *) 1197 { 1198 LogFlowFunc(("giving the guest clipboard ownership\n")); 1199 g_ctx.eOwner = GUEST; 1200 g_ctx.notifyHost = true; 1201 LogFlowFunc(("returning\n")); 1202 } 1203 1204 1205 /** 1206 * The host is taking possession of the shared clipboard. Called by the HGCM clipboard 1207 * subsystem. 1208 * 1209 * @param u32Formats Clipboard formats the guest is offering 1210 */ 1211 void vboxClipboardFormatAnnounce (uint32_t u32Formats) 1212 { 1213 g_ctx.hostFormats = u32Formats; 1214 LogFlowFunc(("u32Formats = %d\n", u32Formats)); 1215 if (u32Formats == 0) 1216 { 1217 /* This is just an automatism, not a genuine announcement */ 1218 LogFlowFunc(("returning\n")); 1219 return; 1220 } 1221 LogFlow(("vboxClipboardFormatAnnounce: giving the host clipboard ownership\n")); 1222 g_ctx.eOwner = HOST; 1223 g_ctx.guestTextFormat = INVALID; 1224 g_ctx.guestBitmapFormat = INVALID; 1225 if (XtOwnSelection(g_ctx.widget, g_ctx.atomClipboard, CurrentTime, 1226 vboxClipboardConvertProc, vboxClipboardLoseProc, 0) 1227 == True) 1228 XtOwnSelection(g_ctx.widget, g_ctx.atomPrimary, CurrentTime, 1229 vboxClipboardConvertProc, NULL, 0); 1230 else 1231 { 1232 LogFlow(("vboxClipboardFormatAnnounce: returning clipboard ownership to the guest\n")); 1233 g_ctx.notifyHost = true; 1234 g_ctx.eOwner = GUEST; 1235 } 1236 LogFlowFunc(("returning\n")); 1237 } 1238 1239 1240 /** 1241 * Called when the host wants to read the guest clipboard. 1242 * 1243 * @param u32Format The format that the host would like to receive the data in 1244 */ 1245 int vboxClipboardReadGuestData (uint32_t u32Format) 1246 { 1247 LogFlowFunc(("u32Format = %d\n", u32Format)); 1248 1249 /* 1250 * The guest wants to read data in the given format. 1251 */ 1252 if (u32Format & VBOX_SHARED_CLIPBOARD_FMT_UNICODETEXT) 1253 { 1254 if (g_ctx.guestTextFormat == INVALID) 1255 { 1256 /* No data available. */ 1257 vboxClipboardSendData(0, NULL, 0); 1258 LogFlowFunc(("sent empty data, returned VINF_SUCCESS\n")); 1259 return VINF_SUCCESS; 1260 } 1261 g_ctx.requestGuestFormat = g_ctx.guestTextFormat; 1262 /* Send out a request for the data to the current clipboard owner */ 1263 XtGetSelectionValue(g_ctx.widget, g_ctx.atomClipboard, g_ctx.atomGuestTextFormat, 1264 vboxClipboardGetProc, 0, CurrentTime); 1265 /* When the data arrives, the vboxClipboardGetProc callback will be called. The 1266 callback will signal the event semaphore when it has processed the data for us. */ 1267 } 1268 else 1269 { 1270 vboxClipboardSendData(0, NULL, 0); 1271 LogFlowFunc(("sent empty data, returned VERR_NOT_IMPLEMENTED\n")); 1272 return VERR_NOT_IMPLEMENTED; 1273 } 1274 LogFlowFunc(("returned VINF_SUCCESS\n")); 1275 return VINF_SUCCESS; 1276 } 1277 1278 1279 /** Terminate the guest side of the shared clipboard. */ 1280 void vboxClipboardDestroy (void) 1281 { 1282 LogFlowFunc(("\n")); 1283 1284 /* Set the termination flag. */ 1285 XtAppSetExitFlag(g_ctx.appContext); 1286 LogFlowFunc(("returning\n")); 1287 } 1288 1289 1290 /** 1291 * The main loop of our clipboard reader. 1292 */ 1293 static int vboxClipboardThread(RTTHREAD /* ThreadSelf */, void * /* pvUser */) 1294 { 1295 int rc; 1296 LogFlowFunc(("Starting clipboard thread\n")); 1297 1298 for (;;) 220 LogFlowFunc(("Starting guest clipboard service\n")); 221 bool fExiting = false; 222 223 while (!fExiting) 1299 224 { 1300 225 uint32_t Msg; … … 1312 237 */ 1313 238 LogFlowFunc(("VBOX_SHARED_CLIPBOARD_HOST_MSG_FORMATS fFormats=%x\n", fFormats)); 1314 vboxClipboardFormatAnnounce(fFormats);239 ClipAnnounceFormatToX11(g_ctx.pBackend, fFormats); 1315 240 break; 1316 241 } … … 1320 245 /* The host needs data in the specified format. */ 1321 246 LogFlowFunc(("VBOX_SHARED_CLIPBOARD_HOST_MSG_READ_DATA fFormats=%x\n", fFormats)); 1322 vboxClipboardReadGuestData(fFormats); 247 CLIPREADCBREQ *pReq; 248 pReq = (CLIPREADCBREQ *)RTMemAllocZ(sizeof(*pReq)); 249 if (!pReq) 250 { 251 rc = VERR_NO_MEMORY; 252 fExiting = true; 253 } 254 else 255 { 256 pReq->u32Format = fFormats; 257 ClipRequestDataFromX11(g_ctx.pBackend, fFormats, 258 pReq); 259 } 1323 260 break; 1324 261 } … … 1328 265 /* The host is terminating. */ 1329 266 LogFlowFunc(("VBOX_SHARED_CLIPBOARD_HOST_MSG_QUIT\n")); 1330 vboxClipboardDestroy(); 1331 rc = VERR_INTERRUPTED; 267 if (RT_SUCCESS(ClipStopX11(g_ctx.pBackend))) 268 ClipDestructX11(g_ctx.pBackend); 269 fExiting = true; 1332 270 break; 1333 271 } … … 1337 275 } 1338 276 } 1339 if (rc == VERR_INTERRUPTED)1340 {1341 /* Wait for termination event. */1342 RTSemEventWait(g_ctx.terminating, RT_INDEFINITE_WAIT);1343 break;1344 }1345 if (RT_FAILURE(rc))1346 {1347 /* Wait a bit before retrying. */1348 if (RTSemEventWait(g_ctx.terminating, 1000) == VINF_SUCCESS)1349 break;1350 continue;1351 }1352 277 1353 278 LogFlow(("processed host event rc = %d\n", rc)); 1354 279 } 1355 280 LogFlowFunc(("rc=%d\n", rc)); 1356 return rc;1357 }1358 1359 1360 /**1361 * Disconnect the guest clipboard from the host.1362 */1363 void vboxClipboardDisconnect(void)1364 {1365 #if 01366 VMMDevHGCMDisconnect request;1367 #endif1368 LogFlowFunc(("\n"));1369 1370 AssertReturn(g_ctx.client != 0, (void) 0);1371 VbglR3ClipboardDisconnect(g_ctx.client);1372 LogFlowFunc(("returning\n"));1373 }1374 1375 /**1376 * Connect the guest clipboard to the host.1377 *1378 * @returns VBox status code1379 */1380 int vboxClipboardConnect(void)1381 {1382 int rc;1383 LogFlowFunc(("\n"));1384 1385 /* Only one client is supported for now */1386 AssertReturn(g_ctx.client == 0, VERR_NOT_SUPPORTED);1387 1388 /* Initialise the termination semaphore. */1389 RTSemEventCreate(&g_ctx.terminating);1390 1391 /* Initialise threading in X11 and in Xt. */1392 if (!XInitThreads() || !XtToolkitThreadInitialize())1393 {1394 LogRel(("VBoxClient: error initialising threads in X11, exiting.\n"));1395 return VERR_NOT_SUPPORTED;1396 }1397 1398 rc = VbglR3ClipboardConnect(&g_ctx.client);1399 if (RT_FAILURE(rc))1400 {1401 LogRel(("Error connecting to host. rc=%Rrc\n", rc));1402 return rc;1403 }1404 if (!g_ctx.client)1405 {1406 LogRel(("Invalid client ID of 0\n"));1407 return VERR_NOT_SUPPORTED;1408 }1409 /* Assume that if the guest clipboard already contains data then the1410 * user put it there for a reason. */1411 g_ctx.eOwner = GUEST;1412 LogFlowFunc(("g_ctx.client=%u rc=%Rrc\n", g_ctx.client, rc));1413 return rc;1414 }1415 1416 1417 /** We store information about the target formats we can handle in a global vector for internal1418 use. */1419 static void vboxClipboardAddFormat(const char *pszName, g_eClipboardFormat eFormat, unsigned hostFormat)1420 {1421 VBOXCLIPBOARDFORMAT sFormat;1422 LogFlowFunc(("pszName=%s, eFormat=%d, hostFormat=%u\n", pszName, eFormat, hostFormat));1423 /* Get an atom from the X server for that target format */1424 Atom atomFormat = XInternAtom(XtDisplay(g_ctx.widget), pszName, false);1425 LogFlow(("vboxClipboardAddFormat: atomFormat=%d\n", atomFormat));1426 if (atomFormat == 0)1427 {1428 LogFlowFunc(("atomFormat=0! returning...\n"));1429 return;1430 }1431 sFormat.atom = atomFormat;1432 sFormat.format = eFormat;1433 sFormat.hostFormat = hostFormat;1434 g_ctx.formatList.push_back(sFormat);1435 LogFlowFunc(("returning\n"));1436 }1437 1438 1439 /** Create the X11 window which we use to interact with the guest clipboard */1440 static int vboxClipboardCreateWindow(void)1441 {1442 /* Create a window and make it a clipboard viewer. */1443 int cArgc = 0;1444 char *pcArgv = 0;1445 String szFallbackResources[] = { (char*)"*.width: 1", (char*)"*.height: 1", 0 };1446 1447 /* Set up the Clipbard application context and main window. */1448 g_ctx.widget = XtOpenApplication(&g_ctx.appContext, "VBoxClipboard", 0, 0, &cArgc, &pcArgv,1449 szFallbackResources, applicationShellWidgetClass, 0, 0);1450 AssertMsgReturn(g_ctx.widget != 0, ("Failed to construct the X clipboard window\n"),1451 VERR_ACCESS_DENIED);1452 XtSetMappedWhenManaged(g_ctx.widget, false);1453 XtRealizeWidget(g_ctx.widget);1454 1455 /* Get hold of the atoms which we need */1456 g_ctx.atomClipboard = XInternAtom(XtDisplay(g_ctx.widget), "CLIPBOARD", false /* only_if_exists */);1457 g_ctx.atomPrimary = XInternAtom(XtDisplay(g_ctx.widget), "PRIMARY", false);1458 g_ctx.atomTargets = XInternAtom(XtDisplay(g_ctx.widget), "TARGETS", false);1459 g_ctx.atomMultiple = XInternAtom(XtDisplay(g_ctx.widget), "MULTIPLE", false);1460 g_ctx.atomTimestamp = XInternAtom(XtDisplay(g_ctx.widget), "TIMESTAMP", false);1461 g_ctx.atomUtf16 = XInternAtom(XtDisplay(g_ctx.widget),1462 "text/plain;charset=ISO-10646-UCS-2", false);1463 g_ctx.atomUtf8 = XInternAtom(XtDisplay(g_ctx.widget), "UTF_STRING", false);1464 g_ctx.atomCText = XInternAtom(XtDisplay(g_ctx.widget), "COMPOUND_TEXT", false);1465 /* And build up the vector of supported formats */1466 #ifdef USE_UTF161467 vboxClipboardAddFormat("text/plain;charset=ISO-10646-UCS-2", UTF16, VBOX_SHARED_CLIPBOARD_FMT_UNICODETEXT);1468 #endif1469 #ifdef USE_UTF81470 vboxClipboardAddFormat("UTF8_STRING", UTF8, VBOX_SHARED_CLIPBOARD_FMT_UNICODETEXT);1471 vboxClipboardAddFormat("text/plain;charset=UTF-8", UTF8, VBOX_SHARED_CLIPBOARD_FMT_UNICODETEXT);1472 vboxClipboardAddFormat("text/plain;charset=utf-8", UTF8, VBOX_SHARED_CLIPBOARD_FMT_UNICODETEXT);1473 vboxClipboardAddFormat("STRING", UTF8, VBOX_SHARED_CLIPBOARD_FMT_UNICODETEXT);1474 vboxClipboardAddFormat("TEXT", UTF8, VBOX_SHARED_CLIPBOARD_FMT_UNICODETEXT);1475 vboxClipboardAddFormat("text/plain", UTF8, VBOX_SHARED_CLIPBOARD_FMT_UNICODETEXT);1476 #endif1477 #ifdef USE_CTEXT1478 vboxClipboardAddFormat("COMPOUND_TEXT", CTEXT, VBOX_SHARED_CLIPBOARD_FMT_UNICODETEXT);1479 #endif1480 return VINF_SUCCESS;1481 }1482 1483 /** Initialise the guest side of the shared clipboard. */1484 int vboxClipboardMain(void)1485 {1486 int rc;1487 LogFlowFunc(("\n"));1488 1489 rc = vboxClipboardCreateWindow();1490 if (RT_FAILURE(rc))1491 return rc;1492 1493 rc = RTThreadCreate(&g_ctx.thread, vboxClipboardThread, 0, 0, RTTHREADTYPE_IO,1494 RTTHREADFLAGS_WAITABLE, "SHCLIP");1495 AssertRCReturn(rc, rc);1496 /* Set up a timer to poll the host clipboard */1497 XtAppAddTimeOut(g_ctx.appContext, 200 /* ms */, vboxClipboardTimerProc, 0);1498 1499 XtAppMainLoop(g_ctx.appContext);1500 g_ctx.formatList.clear();1501 XtDestroyApplicationContext(g_ctx.appContext);1502 /* Set the termination signal. */1503 RTSemEventSignal(g_ctx.terminating);1504 LogFlowFunc(("returning %d\n", rc));1505 281 return rc; 1506 282 } … … 1518 294 if (RT_SUCCESS(rc)) 1519 295 rc = vboxClipboardMain(); 296 if (RT_FAILURE(rc)) 297 LogFunc(("guest clipboard service terminated annormally: return code %Rrc\n", rc)); 1520 298 return rc; 1521 299 }
Note:
See TracChangeset
for help on using the changeset viewer.