Changeset 19754 in vbox for trunk/src/VBox/GuestHost/SharedClipboard
- Timestamp:
- May 15, 2009 10:41:36 PM (16 years ago)
- svn:sync-xref-src-repo-rev:
- 47375
- File:
-
- 1 edited
Legend:
- Unmodified
- Added
- Removed
-
trunk/src/VBox/GuestHost/SharedClipboard/x11-clipboard.cpp
r19704 r19754 139 139 * Use NIL_CLIPX11FORMAT to start the enumeration. 140 140 */ 141 static CLIPX11FORMAT clip NextX11Format(uint32_t u32VBoxFormat,142 CLIPX11FORMAT lastFormat)141 static CLIPX11FORMAT clipEnumX11Formats(uint32_t u32VBoxFormats, 142 CLIPX11FORMAT lastFormat) 143 143 { 144 144 for (unsigned i = lastFormat + 1; i < RT_ELEMENTS(g_aFormats); ++i) 145 if ( clipVBoxFormatForX11Format(i) == u32VBoxFormat)145 if (u32VBoxFormats & clipVBoxFormatForX11Format(i)) 146 146 return i; 147 147 return NIL_CLIPX11FORMAT; … … 279 279 } 280 280 281 /** 282 * Send a message to VBox that new data is available to prevent the other 283 * side of the shared clipboard from caching the data we send to it. We need 284 * to do this because we can't always tell ourselves when the clipboard 285 * contents change on our side. 286 */ 287 static void clipInvalidateVBoxCache(CLIPBACKEND *pCtx) 288 { 289 pCtx->notifyVBox = true; 290 } 291 292 /** 293 * Forget any information about valid data in the X11 clipboard. To be used 294 * when the X11 clipboard contents go away, or if an error occurs accessing 295 * them. 296 */ 297 static void clipInvalidateX11Contents(CLIPBACKEND *pCtx) 298 { 299 pCtx->X11TextFormat = 0; 300 pCtx->X11BitmapFormat = 0; 281 static void clipQueueToEventThread(XtAppContext app_context, 282 XtTimerCallbackProc proc, 283 XtPointer client_data); 284 #ifndef TESTCASE 285 void clipQueueToEventThread(XtAppContext app_context, 286 XtTimerCallbackProc proc, 287 XtPointer client_data) 288 { 289 XtAppAddTimeOut(app_context, 0, proc, client_data); 290 } 291 #endif 292 293 /** 294 * Report the formats currently supported by the X11 clipboard to VBox. 295 */ 296 static void clipReportFormatsToVBox(CLIPBACKEND *pCtx) 297 { 298 uint32_t u32VBoxFormats = clipVBoxFormatForX11Format(pCtx->X11TextFormat); 299 VBoxX11ClipboardReportX11Formats(pCtx->pFrontend, u32VBoxFormats); 300 } 301 302 /** 303 * Forget which formats were previously in the X11 clipboard. Should be 304 * called when we grab the clipboard, so that when we lose it again the poller 305 * will notify us when new formats become available. */ 306 static void clipResetX11Formats(CLIPBACKEND *pCtx) 307 { 308 pCtx->X11TextFormat = INVALID; 309 pCtx->X11BitmapFormat = INVALID; 310 } 311 312 /** Tell VBox that X11 currently has nothing in its clipboard. */ 313 static void clipReportEmptyX11CB(CLIPBACKEND *pCtx) 314 { 315 clipResetX11Formats(pCtx); 316 clipReportFormatsToVBox(pCtx); 317 } 318 319 /** 320 * Go through an array of X11 clipboard targets to see if we can support any 321 * of them and if relevant to choose the ones we prefer (e.g. we like Utf8 322 * better than compound text). 323 * @param pCtx the clipboard backend context structure 324 * @param pTargets the list of targets 325 * @param cTargets the size of the list in @a pTargets 326 * @param pChanged This is set to true if the formats available have changed 327 * from VBox's point of view, and to false otherwise. 328 * Somehow this didn't feel right as a return value. 329 */ 330 void clipGetFormatsFromTargets(CLIPBACKEND *pCtx, Atom *pTargets, 331 size_t cTargets, bool *pChanged) 332 { 333 bool changed = false; 334 CLIPX11FORMAT bestTextFormat = NIL_CLIPX11FORMAT; 335 AssertPtrReturnVoid(pCtx); 336 AssertPtrReturnVoid(pTargets); 337 for (unsigned i = 0; i < cTargets; ++i) 338 { 339 CLIPFORMAT enmBestTextTarget = INVALID; 340 CLIPX11FORMAT format = clipFindX11FormatByAtom(pCtx->widget, 341 pTargets[i]); 342 if (format != NIL_CLIPX11FORMAT) 343 { 344 if ( (clipVBoxFormatForX11Format(format) 345 == VBOX_SHARED_CLIPBOARD_FMT_UNICODETEXT) 346 && enmBestTextTarget < clipRealFormatForX11Format(format)) 347 { 348 enmBestTextTarget = clipRealFormatForX11Format(format); 349 bestTextFormat = format; 350 } 351 } 352 } 353 if (pCtx->X11TextFormat != bestTextFormat) 354 { 355 changed = true; 356 pCtx->X11TextFormat = bestTextFormat; 357 } 358 pCtx->X11BitmapFormat = INVALID; /* not yet supported */ 359 if (pChanged) 360 *pChanged = changed; 361 } 362 363 /** 364 * Notify the VBox clipboard about available data formats, based on the 365 * "targets" information obtained from the X11 clipboard. 366 * @note callback for XtGetSelectionValue, called on a polling loop 367 */ 368 static void clipConvertX11Targets(Widget, XtPointer pClientData, 369 Atom * /* selection */, Atom *atomType, 370 XtPointer pValue, long unsigned int *pcLen, 371 int *piFormat) 372 { 373 CLIPBACKEND *pCtx = 374 reinterpret_cast<CLIPBACKEND *>(pClientData); 375 Atom *pTargets = reinterpret_cast<Atom *>(pValue); 376 size_t cTargets = *pcLen; 377 bool changed = true; 378 379 Log3 (("%s: called\n", __PRETTY_FUNCTION__)); 380 if (pCtx->fOwnsClipboard) 381 /* VBox raced us and we lost. So we don't want to report anything. */ 382 changed = false; 383 else if (*atomType == XT_CONVERT_FAIL) /* timeout */ 384 clipResetX11Formats(pCtx); 385 else 386 clipGetFormatsFromTargets(pCtx, pTargets, cTargets, &changed); 387 if (changed) 388 clipReportFormatsToVBox(pCtx); 389 XtFree(reinterpret_cast<char *>(pValue)); 390 } 391 392 enum { TIMER_FREQ = 200 /* ms */ }; 393 394 static void clipPollX11CBFormats(XtPointer pUserData, 395 XtIntervalId * /* hTimerId */); 396 static void clipSchedulePoller(CLIPBACKEND *pCtx, 397 XtTimerCallbackProc proc); 398 399 #ifndef TESTCASE 400 void clipSchedulePoller(CLIPBACKEND *pCtx, 401 XtTimerCallbackProc proc) 402 { 403 XtAppAddTimeOut(pCtx->appContext, TIMER_FREQ, proc, pCtx); 404 } 405 #endif 406 407 /** 408 * This timer callback is called every 200ms to check the contents of the X11 409 * clipboard. 410 * @note X11 backend code, callback for XtAppAddTimeOut, recursively 411 * re-armed. 412 * @todo Use the XFIXES extension to check for new clipboard data when 413 * available. 414 */ 415 void clipPollX11CBFormats(XtPointer pUserData, XtIntervalId * /* hTimerId */) 416 { 417 CLIPBACKEND *pCtx = (CLIPBACKEND *)pUserData; 418 Log3 (("%s: called\n", __PRETTY_FUNCTION__)); 419 /* Get the current clipboard contents if we don't own it ourselves */ 420 if (!pCtx->fOwnsClipboard) 421 { 422 Log3 (("%s: requesting the targets that the X11 clipboard offers\n", 423 __PRETTY_FUNCTION__)); 424 XtGetSelectionValue(pCtx->widget, 425 clipGetAtom(pCtx->widget, "CLIPBOARD"), 426 clipGetAtom(pCtx->widget, "TARGETS"), 427 clipConvertX11Targets, pCtx, 428 CurrentTime); 429 } 430 /* Re-arm our timer */ 431 clipSchedulePoller(pCtx, clipPollX11CBFormats); 432 } 433 434 #ifndef TESTCASE 435 /** 436 * The main loop of our clipboard reader. 437 * @note X11 backend code. 438 */ 439 static int clipEventThread(RTTHREAD self, void *pvUser) 440 { 441 LogRel(("Shared clipboard: starting shared clipboard thread\n")); 442 443 CLIPBACKEND *pCtx = (CLIPBACKEND *)pvUser; 444 while (XtAppGetExitFlag(pCtx->appContext) == FALSE) 445 XtAppProcessEvent(pCtx->appContext, XtIMAll); 446 LogRel(("Shared clipboard: shared clipboard thread terminated successfully\n")); 447 return VINF_SUCCESS; 448 } 449 #endif 450 451 /** X11 specific uninitialisation for the shared clipboard. 452 * @note X11 backend code. 453 */ 454 static void clipUninit(CLIPBACKEND *pCtx) 455 { 456 AssertPtrReturnVoid(pCtx); 457 if (pCtx->widget) 458 { 459 /* Valid widget + invalid appcontext = bug. But don't return yet. */ 460 AssertPtr(pCtx->appContext); 461 clipUnregisterContext(pCtx); 462 XtDestroyWidget(pCtx->widget); 463 } 464 pCtx->widget = NULL; 465 if (pCtx->appContext) 466 XtDestroyApplicationContext(pCtx->appContext); 467 pCtx->appContext = NULL; 468 if (pCtx->wakeupPipeRead != 0) 469 close(pCtx->wakeupPipeRead); 470 if (pCtx->wakeupPipeWrite != 0) 471 close(pCtx->wakeupPipeWrite); 472 pCtx->wakeupPipeRead = 0; 473 pCtx->wakeupPipeWrite = 0; 474 } 475 476 /** Worker function for stopping the clipboard which runs on the event 477 * thread. */ 478 static void clipStopEventThreadWorker(XtPointer pUserData, int * /* source */, 479 XtInputId * /* id */) 480 { 481 482 CLIPBACKEND *pCtx = (CLIPBACKEND *)pUserData; 483 484 /* This might mean that we are getting stopped twice. */ 485 Assert(pCtx->widget != NULL); 486 487 /* Set the termination flag to tell the Xt event loop to exit. We 488 * reiterate that any outstanding requests from the X11 event loop to 489 * the VBox part *must* have returned before we do this. */ 490 XtAppSetExitFlag(pCtx->appContext); 491 } 492 493 /** X11 specific initialisation for the shared clipboard. 494 * @note X11 backend code. 495 */ 496 static int clipInit(CLIPBACKEND *pCtx) 497 { 498 /* Create a window and make it a clipboard viewer. */ 499 int cArgc = 0; 500 char *pcArgv = 0; 501 int rc = VINF_SUCCESS; 502 Display *pDisplay; 503 504 /* Make sure we are thread safe */ 505 XtToolkitThreadInitialize(); 506 /* Set up the Clipbard application context and main window. We call all 507 * these functions directly instead of calling XtOpenApplication() so 508 * that we can fail gracefully if we can't get an X11 display. */ 509 XtToolkitInitialize(); 510 pCtx->appContext = XtCreateApplicationContext(); 511 pDisplay = XtOpenDisplay(pCtx->appContext, 0, 0, "VBoxClipboard", 0, 0, &cArgc, &pcArgv); 512 if (NULL == pDisplay) 513 { 514 LogRel(("Shared clipboard: failed to connect to the X11 clipboard - the window system may not be running.\n")); 515 rc = VERR_NOT_SUPPORTED; 516 } 517 if (RT_SUCCESS(rc)) 518 { 519 pCtx->widget = XtVaAppCreateShell(0, "VBoxClipboard", 520 applicationShellWidgetClass, 521 pDisplay, XtNwidth, 1, XtNheight, 522 1, NULL); 523 if (NULL == pCtx->widget) 524 { 525 LogRel(("Shared clipboard: failed to construct the X11 window for the shared clipboard manager.\n")); 526 rc = VERR_NO_MEMORY; 527 } 528 else 529 rc = clipRegisterContext(pCtx); 530 } 531 if (RT_SUCCESS(rc)) 532 { 533 XtSetMappedWhenManaged(pCtx->widget, false); 534 XtRealizeWidget(pCtx->widget); 535 /* Set up a timer to poll the X11 clipboard */ 536 clipSchedulePoller(pCtx, clipPollX11CBFormats); 537 } 538 /* Create the pipes */ 539 int pipes[2]; 540 if (!pipe(pipes)) 541 { 542 pCtx->wakeupPipeRead = pipes[0]; 543 pCtx->wakeupPipeWrite = pipes[1]; 544 if (!XtAppAddInput(pCtx->appContext, pCtx->wakeupPipeRead, 545 (XtPointer) XtInputReadMask, 546 clipStopEventThreadWorker, (XtPointer) pCtx)) 547 rc = VERR_NO_MEMORY; /* What failure means is not doc'ed. */ 548 } 549 else 550 rc = RTErrConvertFromErrno(errno); 551 if (RT_FAILURE(rc)) 552 clipUninit(pCtx); 553 return rc; 554 } 555 556 /** 557 * Construct the X11 backend of the shared clipboard. 558 * @note X11 backend code 559 */ 560 CLIPBACKEND *VBoxX11ClipboardConstructX11(VBOXCLIPBOARDCONTEXT *pFrontend) 561 { 562 int rc; 563 564 CLIPBACKEND *pCtx = (CLIPBACKEND *) 565 RTMemAllocZ(sizeof(CLIPBACKEND)); 566 if (pCtx && !RTEnvGet("DISPLAY")) 567 { 568 /* 569 * If we don't find the DISPLAY environment variable we assume that 570 * we are not connected to an X11 server. Don't actually try to do 571 * this then, just fail silently and report success on every call. 572 * This is important for VBoxHeadless. 573 */ 574 LogRelFunc(("X11 DISPLAY variable not set -- disabling shared clipboard\n")); 575 pCtx->fHaveX11 = false; 576 return pCtx; 577 } 578 579 pCtx->fHaveX11 = true; 580 581 LogRel(("Initializing X11 clipboard backend\n")); 582 if (pCtx) 583 pCtx->pFrontend = pFrontend; 584 return pCtx; 585 } 586 587 /** 588 * Destruct the shared clipboard X11 backend. 589 * @note X11 backend code 590 */ 591 void VBoxX11ClipboardDestructX11(CLIPBACKEND *pCtx) 592 { 593 /* 594 * Immediately return if we are not connected to the X server. 595 */ 596 if (!pCtx->fHaveX11) 597 return; 598 599 /* We set this to NULL when the event thread exits. It really should 600 * have exited at this point, when we are about to unload the code from 601 * memory. */ 602 Assert(pCtx->widget == NULL); 603 } 604 605 /** 606 * Announce to the X11 backend that we are ready to start. 607 */ 608 int VBoxX11ClipboardStartX11(CLIPBACKEND *pCtx) 609 { 610 int rc = VINF_SUCCESS; 611 LogFlowFunc(("\n")); 612 /* 613 * Immediately return if we are not connected to the X server. 614 */ 615 if (!pCtx->fHaveX11) 616 return VINF_SUCCESS; 617 618 rc = clipInit(pCtx); 619 #ifndef TESTCASE 620 if (RT_SUCCESS(rc)) 621 { 622 rc = RTThreadCreate(&pCtx->thread, clipEventThread, pCtx, 0, 623 RTTHREADTYPE_IO, RTTHREADFLAGS_WAITABLE, "SHCLIP"); 624 if (RT_FAILURE(rc)) 625 LogRel(("Failed to initialise the shared clipboard X11 backend.\n")); 626 } 627 #endif 628 if (RT_SUCCESS(rc)) 629 { 630 pCtx->fOwnsClipboard = false; 631 clipResetX11Formats(pCtx); 632 } 633 return rc; 634 } 635 636 /** String written to the wakeup pipe. */ 637 #define WAKE_UP_STRING "WakeUp!" 638 /** Length of the string written. */ 639 #define WAKE_UP_STRING_LEN ( sizeof(WAKE_UP_STRING) - 1 ) 640 641 /** 642 * Shut down the shared clipboard X11 backend. 643 * @note X11 backend code 644 * @note Any requests from this object to get clipboard data from VBox 645 * *must* have completed or aborted before we are called, as 646 * otherwise the X11 event loop will still be waiting for the request 647 * to return and will not be able to terminate. 648 */ 649 int VBoxX11ClipboardStopX11(CLIPBACKEND *pCtx) 650 { 651 int rc, rcThread; 652 unsigned count = 0; 653 /* 654 * Immediately return if we are not connected to the X server. 655 */ 656 if (!pCtx->fHaveX11) 657 return VINF_SUCCESS; 658 659 LogRelFunc(("stopping the shared clipboard X11 backend\n")); 660 /* Write to the "stop" pipe */ 661 rc = write(pCtx->wakeupPipeWrite, WAKE_UP_STRING, WAKE_UP_STRING_LEN); 662 do 663 { 664 rc = RTThreadWait(pCtx->thread, 1000, &rcThread); 665 ++count; 666 Assert(RT_SUCCESS(rc) || ((VERR_TIMEOUT == rc) && (count != 5))); 667 } while ((VERR_TIMEOUT == rc) && (count < 300)); 668 if (RT_SUCCESS(rc)) 669 AssertRC(rcThread); 670 else 671 LogRelFunc(("rc=%Rrc\n", rc)); 672 clipUninit(pCtx); 673 LogFlowFunc(("returning %Rrc.\n", rc)); 674 return rc; 675 } 676 677 /** 678 * Satisfy a request from X11 for clipboard targets supported by VBox. 679 * 680 * @returns iprt status code 681 * @param atomTypeReturn The type of the data we are returning 682 * @param pValReturn A pointer to the data we are returning. This 683 * should be set to memory allocated by XtMalloc, 684 * which will be freed later by the Xt toolkit. 685 * @param pcLenReturn The length of the data we are returning 686 * @param piFormatReturn The format (8bit, 16bit, 32bit) of the data we are 687 * returning 688 * @note X11 backend code, called by the XtOwnSelection callback. 689 */ 690 static int clipCreateX11Targets(CLIPBACKEND *pCtx, Atom *atomTypeReturn, 691 XtPointer *pValReturn, 692 unsigned long *pcLenReturn, 693 int *piFormatReturn) 694 { 695 Atom *atomTargets = (Atom *)XtMalloc( (MAX_CLIP_X11_FORMATS + 3) 696 * sizeof(Atom)); 697 unsigned cTargets = 0; 698 LogFlowFunc (("called\n")); 699 CLIPX11FORMAT format = NIL_CLIPX11FORMAT; 700 do 701 { 702 format = clipEnumX11Formats(pCtx->vboxFormats, format); 703 if (format != NIL_CLIPX11FORMAT) 704 { 705 atomTargets[cTargets] = clipAtomForX11Format(pCtx->widget, 706 format); 707 ++cTargets; 708 } 709 } while (format != NIL_CLIPX11FORMAT); 710 /* We always offer these */ 711 atomTargets[cTargets] = clipGetAtom(pCtx->widget, "TARGETS"); 712 atomTargets[cTargets + 1] = clipGetAtom(pCtx->widget, "MULTIPLE"); 713 atomTargets[cTargets + 2] = clipGetAtom(pCtx->widget, "TIMESTAMP"); 714 *atomTypeReturn = XA_ATOM; 715 *pValReturn = (XtPointer)atomTargets; 716 *pcLenReturn = cTargets + 3; 717 *piFormatReturn = 32; 718 return VINF_SUCCESS; 719 } 720 721 /** This is a wrapper around VBoxX11ClipboardReadVBoxData that will cache the 722 * data returned. This is unfortunately necessary, because if the other side 723 * of the shared clipboard is also an X11 system, it may send a format 724 * announcement message every time its clipboard is read, for reasons that 725 * are explained elsewhere. Unfortunately, some applications on our side 726 * like to read the clipboard several times in short succession in different 727 * formats. This can fail if it collides with a format announcement message. 728 * @todo any ideas about how to do this better are welcome. 729 */ 730 static int clipReadVBoxClipboard(CLIPBACKEND *pCtx, uint32_t u32Format, 731 void **ppv, uint32_t *pcb) 732 { 733 int rc = VINF_SUCCESS; 734 LogFlowFunc(("pCtx=%p, u32Format=%02X, ppv=%p, pcb=%p\n", pCtx, 735 u32Format, ppv, pcb)); 736 if (u32Format == VBOX_SHARED_CLIPBOARD_FMT_UNICODETEXT) 737 { 738 if (pCtx->pvUnicodeCache == NULL) 739 rc = VBoxX11ClipboardReadVBoxData(pCtx->pFrontend, u32Format, 740 &pCtx->pvUnicodeCache, 741 &pCtx->cbUnicodeCache); 742 if (RT_SUCCESS(rc)) 743 { 744 *ppv = RTMemDup(pCtx->pvUnicodeCache, pCtx->cbUnicodeCache); 745 *pcb = pCtx->cbUnicodeCache; 746 if (*ppv == NULL) 747 rc = VERR_NO_MEMORY; 748 } 749 } 750 else 751 rc = VBoxX11ClipboardReadVBoxData(pCtx->pFrontend, u32Format, 752 ppv, pcb); 753 LogFlowFunc(("returning %Rrc\n", rc)); 754 if (RT_SUCCESS(rc)) 755 LogFlowFunc(("*ppv=%.*ls, *pcb=%u\n", *pcb, *ppv, *pcb)); 756 return rc; 757 } 758 759 /** 760 * Calculate a buffer size large enough to hold the source Windows format 761 * text converted into Unix Utf8, including the null terminator 762 * @returns iprt status code 763 * @param pwsz the source text in UCS-2 with Windows EOLs 764 * @param cwc the size in USC-2 elements of the source text, with or 765 * without the terminator 766 * @param pcbActual where to store the buffer size needed 767 */ 768 static int clipWinTxtBufSizeForUtf8(PRTUTF16 pwsz, size_t cwc, 769 size_t *pcbActual) 770 { 771 size_t cbRet = 0; 772 int rc = RTUtf16CalcUtf8LenEx(pwsz, cwc, &cbRet); 773 if (RT_SUCCESS(rc)) 774 *pcbActual = cbRet + 1; /* null terminator */ 775 return rc; 776 } 777 778 /** 779 * Convert text from Windows format (UCS-2 with CRLF line endings) to standard 780 * Utf-8. 781 * 782 * @returns iprt status code 783 * 784 * @param pwszSrc the text to be converted 785 * @param cbSrc the length of @a pwszSrc in bytes 786 * @param pszBuf where to write the converted string 787 * @param cbBuf the size of the buffer pointed to by @a pszBuf 788 * @param pcbActual where to store the size of the converted string. 789 * optional. 790 */ 791 static int clipWinTxtToUtf8(PRTUTF16 pwszSrc, size_t cbSrc, char *pszBuf, 792 size_t cbBuf, size_t *pcbActual) 793 { 794 PRTUTF16 pwszTmp = NULL; 795 size_t cwSrc = cbSrc / 2, cwTmp = 0, cbDest = 0; 796 int rc = VINF_SUCCESS; 797 798 LogFlowFunc (("pwszSrc=%.*ls, cbSrc=%u\n", cbSrc, pwszSrc, cbSrc)); 799 /* How long will the converted text be? */ 800 AssertPtr(pwszSrc); 801 AssertPtr(pszBuf); 802 rc = vboxClipboardUtf16GetLinSize(pwszSrc, cwSrc, &cwTmp); 803 if (RT_SUCCESS(rc) && cwTmp == 0) 804 rc = VERR_NO_DATA; 805 if (RT_SUCCESS(rc)) 806 pwszTmp = (PRTUTF16)RTMemAlloc(cwTmp * 2); 807 if (!pwszTmp) 808 rc = VERR_NO_MEMORY; 809 /* Convert the text. */ 810 if (RT_SUCCESS(rc)) 811 rc = vboxClipboardUtf16WinToLin(pwszSrc, cwSrc, pwszTmp, cwTmp); 812 if (RT_SUCCESS(rc)) 813 /* Convert the Utf16 string to Utf8. */ 814 rc = RTUtf16ToUtf8Ex(pwszTmp + 1, cwTmp - 1, &pszBuf, cbBuf, 815 &cbDest); 816 RTMemFree(reinterpret_cast<void *>(pwszTmp)); 817 if (pcbActual) 818 *pcbActual = cbDest + 1; 819 LogFlowFunc(("returning %Rrc\n", rc)); 820 if (RT_SUCCESS(rc)) 821 LogFlowFunc (("converted string is %.*s. Returning.\n", cbDest, 822 pszBuf)); 823 return rc; 824 } 825 826 /** 827 * Satisfy a request from X11 to convert the clipboard text to Utf-8. We 828 * return null-terminated text, but can cope with non-null-terminated input. 829 * 830 * @returns iprt status code 831 * @param pDisplay an X11 display structure, needed for conversions 832 * performed by Xlib 833 * @param pv the text to be converted (UCS-2 with Windows EOLs) 834 * @param cb the length of the text in @cb in bytes 835 * @param atomTypeReturn where to store the atom for the type of the data 836 * we are returning 837 * @param pValReturn where to store the pointer to the data we are 838 * returning. This should be to memory allocated by 839 * XtMalloc, which will be freed by the Xt toolkit 840 * later. 841 * @param pcLenReturn where to store the length of the data we are 842 * returning 843 * @param piFormatReturn where to store the bit width (8, 16, 32) of the 844 * data we are returning 845 */ 846 static int clipWinTxtToUtf8ForX11CB(Display *pDisplay, PRTUTF16 pwszSrc, 847 size_t cbSrc, Atom *atomTarget, 848 Atom *atomTypeReturn, 849 XtPointer *pValReturn, 850 unsigned long *pcLenReturn, 851 int *piFormatReturn) 852 { 853 /* This may slightly overestimate the space needed. */ 854 size_t cbDest = 0; 855 int rc = clipWinTxtBufSizeForUtf8(pwszSrc, cbSrc / 2, &cbDest); 856 if (RT_SUCCESS(rc)) 857 { 858 char *pszDest = (char *)XtMalloc(cbDest); 859 size_t cbActual = 0; 860 if (pszDest) 861 rc = clipWinTxtToUtf8(pwszSrc, cbSrc, pszDest, cbDest, 862 &cbActual); 863 if (RT_SUCCESS(rc)) 864 { 865 *atomTypeReturn = *atomTarget; 866 *pValReturn = (XtPointer)pszDest; 867 *pcLenReturn = cbActual; 868 *piFormatReturn = 8; 869 } 870 } 871 return rc; 872 } 873 874 /** 875 * Satisfy a request from X11 to convert the clipboard text to 876 * COMPOUND_TEXT. We return null-terminated text, but can cope with non-null- 877 * terminated input. 878 * 879 * @returns iprt status code 880 * @param pDisplay an X11 display structure, needed for conversions 881 * performed by Xlib 882 * @param pv the text to be converted (UCS-2 with Windows EOLs) 883 * @param cb the length of the text in @cb in bytes 884 * @param atomTypeReturn where to store the atom for the type of the data 885 * we are returning 886 * @param pValReturn where to store the pointer to the data we are 887 * returning. This should be to memory allocated by 888 * XtMalloc, which will be freed by the Xt toolkit 889 * later. 890 * @param pcLenReturn where to store the length of the data we are 891 * returning 892 * @param piFormatReturn where to store the bit width (8, 16, 32) of the 893 * data we are returning 894 */ 895 static int clipWinTxtToCTextForX11CB(Display *pDisplay, PRTUTF16 pwszSrc, 896 size_t cbSrc, Atom *atomTypeReturn, 897 XtPointer *pValReturn, 898 unsigned long *pcLenReturn, 899 int *piFormatReturn) 900 { 901 char *pszTmp = NULL; 902 size_t cbTmp = 0, cbActual = 0; 903 XTextProperty property; 904 int rc = VINF_SUCCESS, xrc = 0; 905 906 LogFlowFunc(("pwszSrc=%.*ls, cbSrc=%u\n", cbSrc / 2, pwszSrc, cbSrc)); 907 AssertPtrReturn(pDisplay, false); 908 AssertPtrReturn(pwszSrc, false); 909 rc = clipWinTxtBufSizeForUtf8(pwszSrc, cbSrc / 2, &cbTmp); 910 if (RT_SUCCESS(rc)) 911 { 912 pszTmp = (char *)RTMemAlloc(cbTmp); 913 if (!pszTmp) 914 rc = VERR_NO_MEMORY; 915 } 916 if (RT_SUCCESS(rc)) 917 rc = clipWinTxtToUtf8(pwszSrc, cbSrc, pszTmp, cbTmp + 1, 918 &cbActual); 919 /* And finally (!) convert the Utf8 text to compound text. */ 920 #ifdef X_HAVE_UTF8_STRING 921 if (RT_SUCCESS(rc)) 922 xrc = Xutf8TextListToTextProperty(pDisplay, &pszTmp, 1, 923 XCompoundTextStyle, &property); 924 #else 925 if (RT_SUCCESS(rc)) 926 xrc = XmbTextListToTextProperty(pDisplay, &pszTmp, 1, 927 XCompoundTextStyle, &property); 928 #endif 929 if (RT_SUCCESS(rc) && xrc < 0) 930 rc = ( xrc == XNoMemory ? VERR_NO_MEMORY 931 : xrc == XLocaleNotSupported ? VERR_NOT_SUPPORTED 932 : xrc == XConverterNotFound ? VERR_NOT_SUPPORTED 933 : VERR_UNRESOLVED_ERROR); 934 RTMemFree(pszTmp); 935 *atomTypeReturn = property.encoding; 936 *pValReturn = reinterpret_cast<XtPointer>(property.value); 937 *pcLenReturn = property.nitems + 1; 938 *piFormatReturn = property.format; 939 LogFlowFunc(("returning %Rrc\n", rc)); 940 if (RT_SUCCESS(rc)) 941 LogFlowFunc (("converted string is %s\n", property.value)); 942 return rc; 943 } 944 945 /** 946 * Does this atom correspond to one of the two selection types we support? 947 * @param widget a valid Xt widget 948 * @param selType the atom in question 949 */ 950 static bool clipIsSupportedSelectionType(Widget widget, Atom selType) 951 { 952 return( (selType == clipGetAtom(widget, "CLIPBOARD")) 953 || (selType == clipGetAtom(widget, "PRIMARY"))); 954 } 955 956 static int clipConvertVBoxCBForX11(CLIPBACKEND *pCtx, Atom *atomTarget, 957 Atom *atomTypeReturn, 958 XtPointer *pValReturn, 959 unsigned long *pcLenReturn, 960 int *piFormatReturn) 961 { 962 int rc = VINF_SUCCESS; 963 CLIPX11FORMAT x11Format = clipFindX11FormatByAtom(pCtx->widget, 964 *atomTarget); 965 CLIPFORMAT format = clipRealFormatForX11Format(x11Format); 966 if ((format == UTF8) || (format == CTEXT)) 967 { 968 void *pv = NULL; 969 uint32_t cb = 0; 970 rc = clipReadVBoxClipboard(pCtx, 971 VBOX_SHARED_CLIPBOARD_FMT_UNICODETEXT, 972 &pv, &cb); 973 if (RT_SUCCESS(rc) && (cb == 0)) 974 rc = VERR_NO_DATA; 975 if (RT_SUCCESS(rc) && (format == UTF8)) 976 rc = clipWinTxtToUtf8ForX11CB(XtDisplay(pCtx->widget), 977 (PRTUTF16)pv, cb, atomTarget, 978 atomTypeReturn, pValReturn, 979 pcLenReturn, piFormatReturn); 980 else if (RT_SUCCESS(rc) && (format == CTEXT)) 981 rc = clipWinTxtToCTextForX11CB(XtDisplay(pCtx->widget), 982 (PRTUTF16)pv, cb, 983 atomTypeReturn, pValReturn, 984 pcLenReturn, piFormatReturn); 985 RTMemFree(pv); 986 } 987 else 988 rc = VERR_NOT_SUPPORTED; 989 return rc; 990 } 991 992 /** 993 * Return VBox's clipboard data for an X11 client. 994 * @note X11 backend code, callback for XtOwnSelection 995 */ 996 static Boolean clipXtConvertSelectionProc(Widget widget, Atom *atomSelection, 997 Atom *atomTarget, 998 Atom *atomTypeReturn, 999 XtPointer *pValReturn, 1000 unsigned long *pcLenReturn, 1001 int *piFormatReturn) 1002 { 1003 CLIPBACKEND *pCtx = clipLookupContext(widget); 1004 int rc = VINF_SUCCESS; 1005 1006 LogFlowFunc(("\n")); 1007 if ( !pCtx->fOwnsClipboard /* Drop requests we receive too late. */ 1008 || !clipIsSupportedSelectionType(pCtx->widget, *atomSelection)) 1009 return false; 1010 if (*atomTarget == clipGetAtom(pCtx->widget, "TARGETS")) 1011 rc = clipCreateX11Targets(pCtx, atomTypeReturn, pValReturn, 1012 pcLenReturn, piFormatReturn); 1013 else 1014 rc = clipConvertVBoxCBForX11(pCtx, atomTarget, atomTypeReturn, 1015 pValReturn, pcLenReturn, piFormatReturn); 1016 LogFlowFunc(("returning, internal status code %Rrc\n", rc)); 1017 return RT_SUCCESS(rc); 1018 } 1019 1020 /** 1021 * This is called by the X toolkit intrinsics to let us know that another 1022 * X11 client has taken the clipboard. In this case we notify VBox that 1023 * we want ownership of the clipboard. 1024 * @note X11 backend code, callback for XtOwnSelection 1025 */ 1026 static void clipXtLoseSelectionProc(Widget widget, Atom *) 1027 { 1028 CLIPBACKEND *pCtx = clipLookupContext(widget); 1029 LogFlowFunc (("\n")); 1030 /* These should be set to the right values as soon as we start polling */ 1031 clipResetX11Formats(pCtx); 1032 pCtx->fOwnsClipboard = false; 1033 } 1034 1035 /** Structure used to pass information about formats that VBox supports */ 1036 typedef struct _CLIPNEWVBOXFORMATS 1037 { 1038 /** Context information for the X11 clipboard */ 1039 CLIPBACKEND *pCtx; 1040 /** Formats supported by VBox */ 1041 uint32_t formats; 1042 } CLIPNEWVBOXFORMATS; 1043 1044 /** Invalidates the local cache of the data in the VBox clipboard. */ 1045 static void clipInvalidateVBoxCBCache(CLIPBACKEND *pCtx) 1046 { 1047 if (pCtx->pvUnicodeCache != NULL) 1048 { 1049 RTMemFree(pCtx->pvUnicodeCache); 1050 pCtx->pvUnicodeCache = NULL; 1051 } 1052 } 1053 1054 /** Gives up ownership of the X11 clipboard */ 1055 static void clipGiveUpX11CB(CLIPBACKEND *pCtx) 1056 { 1057 XtDisownSelection(pCtx->widget, clipGetAtom(pCtx->widget, "CLIPBOARD"), 1058 CurrentTime); 1059 XtDisownSelection(pCtx->widget, clipGetAtom(pCtx->widget, "PRIMARY"), 1060 CurrentTime); 1061 pCtx->fOwnsClipboard = false; 1062 pCtx->vboxFormats = 0; 1063 } 1064 1065 /** 1066 * Take possession of the X11 clipboard (and middle-button selection). 1067 */ 1068 static void clipGrabX11CB(CLIPBACKEND *pCtx, uint32_t u32Formats) 1069 { 1070 if (XtOwnSelection(pCtx->widget, clipGetAtom(pCtx->widget, "CLIPBOARD"), 1071 CurrentTime, clipXtConvertSelectionProc, 1072 clipXtLoseSelectionProc, 0)) 1073 { 1074 pCtx->fOwnsClipboard = true; 1075 pCtx->vboxFormats = u32Formats; 1076 /* Grab the middle-button paste selection too. */ 1077 XtOwnSelection(pCtx->widget, clipGetAtom(pCtx->widget, "PRIMARY"), 1078 CurrentTime, clipXtConvertSelectionProc, NULL, 0); 1079 } 1080 else 1081 /* Someone raced us to get the clipboard and they won. */ 1082 pCtx->fOwnsClipboard = false; 1083 } 1084 1085 /** 1086 * Worker function for VBoxX11ClipboardAnnounceVBoxFormat which runs on the 1087 * event thread. 1088 * @param pUserData Pointer to a CLIPNEWVBOXFORMATS structure containing 1089 * information about the VBox formats available and the 1090 * clipboard context data. Must be freed by the worker. 1091 */ 1092 static void clipNewVBoxFormatsWorker(XtPointer pUserData, 1093 XtIntervalId * /* interval */) 1094 { 1095 CLIPNEWVBOXFORMATS *pFormats = (CLIPNEWVBOXFORMATS *)pUserData; 1096 CLIPBACKEND *pCtx = pFormats->pCtx; 1097 uint32_t u32Formats = pFormats->formats; 1098 RTMemFree(pFormats); 1099 LogFlowFunc (("u32Formats=%d\n", u32Formats)); 1100 clipInvalidateVBoxCBCache(pCtx); 1101 if (u32Formats == 0) 1102 clipGiveUpX11CB(pCtx); 1103 else 1104 clipGrabX11CB(pCtx, u32Formats); 1105 clipResetX11Formats(pCtx); 1106 LogFlowFunc(("returning\n")); 1107 } 1108 1109 /** 1110 * VBox is taking possession of the shared clipboard. 1111 * 1112 * @param u32Formats Clipboard formats that VBox is offering 1113 * @note X11 backend code 1114 */ 1115 void VBoxX11ClipboardAnnounceVBoxFormat(CLIPBACKEND *pCtx, 1116 uint32_t u32Formats) 1117 { 1118 /* 1119 * Immediately return if we are not connected to the X server. 1120 */ 1121 if (!pCtx->fHaveX11) 1122 return; 1123 /* This must be freed by the worker callback */ 1124 CLIPNEWVBOXFORMATS *pFormats = 1125 (CLIPNEWVBOXFORMATS *) RTMemAlloc(sizeof(CLIPNEWVBOXFORMATS)); 1126 if (pFormats != NULL) /* if it is we will soon have other problems */ 1127 { 1128 pFormats->pCtx = pCtx; 1129 pFormats->formats = u32Formats; 1130 clipQueueToEventThread(pCtx->appContext, clipNewVBoxFormatsWorker, 1131 (XtPointer) pFormats); 1132 } 301 1133 } 302 1134 … … 517 1349 /** A structure containing information about where to store a request 518 1350 * for the X11 clipboard contents. */ 519 struct _CLIP X11CLIPBOARDREQ1351 struct _CLIPREADX11CBREQ 520 1352 { 521 1353 /** The buffer to write X11 clipboard data to (valid during a request … … 540 1372 }; 541 1373 542 typedef struct _CLIP X11CLIPBOARDREQ CLIPX11CLIPBOARDREQ;1374 typedef struct _CLIPREADX11CBREQ CLIPREADX11CBREQ; 543 1375 544 1376 /** … … 553 1385 int *piFormat) 554 1386 { 555 CLIP X11CLIPBOARDREQ *pReq = (CLIPX11CLIPBOARDREQ *) pClientData;1387 CLIPREADX11CBREQ *pReq = (CLIPREADX11CBREQ *) pClientData; 556 1388 LogFlowFunc(("pReq->mBuffer=%p, pReq->mSize=%u, pReq->mFormat=%02X, pReq->mTextFormat=%u, pReq->mCtx=%p\n", 557 1389 pReq->mBuffer, pReq->mSize, pReq->mFormat, … … 600 1432 else 601 1433 rc = VERR_NOT_IMPLEMENTED; 602 if (RT_SUCCESS(rc))603 /* The other end may cache the data, so invalidate it again. */604 clipInvalidateVBoxCache(pCtx);605 else606 /* We failed to retrieve the X11 clipboard contents, mark them as607 * invalid. */608 clipInvalidateX11Contents(pCtx);609 1434 XtFree((char *)pvSrc); 610 1435 pReq->mRC = rc; 611 1436 RTSemEventSignal(pReq->mSem); 1437 /* Don't try to call VBox until after we have released the inbound call 1438 * from VBox! */ 1439 /** @todo make this safer */ 1440 if (RT_SUCCESS(rc)) 1441 /* The other end may want to cache the data, so pretend we have new 1442 * data, as we have no way of telling when new data really does 1443 * arrive. */ 1444 clipReportFormatsToVBox(pCtx); 1445 else 1446 clipReportEmptyX11CB(pCtx); 612 1447 LogFlowFunc(("rc=%Rrc\n", rc)); 613 }614 615 /**616 * Notify the host clipboard about the data formats we support, based on the617 * "targets" (available data formats) information obtained from the X11618 * clipboard.619 * @note X11 backend code, callback for XtGetSelectionValue, called when we620 * poll for available targets.621 */622 static void clipConvertX11Targets(Widget, XtPointer pClientData,623 Atom * /* selection */, Atom *atomType,624 XtPointer pValue, long unsigned int *pcLen,625 int *piFormat)626 {627 CLIPBACKEND *pCtx =628 reinterpret_cast<CLIPBACKEND *>(pClientData);629 Atom *atomTargets = reinterpret_cast<Atom *>(pValue);630 unsigned cAtoms = *pcLen;631 CLIPFORMAT enmBestTarget = INVALID;632 CLIPX11FORMAT bestFormat = NIL_CLIPX11FORMAT;633 634 Log3 (("%s: called\n", __PRETTY_FUNCTION__));635 if ( (*atomType == XT_CONVERT_FAIL) /* timeout */636 || (pCtx->fOwnsClipboard == true) /* VBox currently owns the637 * clipboard */638 )639 {640 clipInvalidateX11Contents(pCtx);641 return;642 }643 644 for (unsigned i = 0; i < cAtoms; ++i)645 {646 CLIPX11FORMAT format = clipFindX11FormatByAtom(pCtx->widget,647 atomTargets[i]);648 if (format != NIL_CLIPX11FORMAT)649 {650 if (enmBestTarget < clipRealFormatForX11Format(format))651 {652 enmBestTarget = clipRealFormatForX11Format(format);653 bestFormat = format;654 }655 }656 }657 uint32_t u32VBoxFormat = clipVBoxFormatForX11Format(bestFormat);658 uint32_t u32OldVBoxFormat659 = clipVBoxFormatForX11Format(pCtx->X11TextFormat);660 /* Notify VBox if something has changed or if a forced notification was661 * scheduled. */662 if ((u32VBoxFormat != u32OldVBoxFormat) || (pCtx->notifyVBox))663 {664 VBoxX11ClipboardReportX11Formats(pCtx->pFrontend, u32VBoxFormat);665 pCtx->notifyVBox = false;666 }667 pCtx->X11TextFormat = bestFormat;668 XtFree(reinterpret_cast<char *>(pValue));669 }670 671 enum { TIMER_FREQ = 200 /* ms */ };672 673 static void clipPollX11CBFormats(XtPointer pUserData,674 XtIntervalId * /* hTimerId */);675 static void clipSchedulePoller(CLIPBACKEND *pCtx,676 XtTimerCallbackProc proc);677 678 #ifndef TESTCASE679 void clipSchedulePoller(CLIPBACKEND *pCtx,680 XtTimerCallbackProc proc)681 {682 XtAppAddTimeOut(pCtx->appContext, TIMER_FREQ, proc, pCtx);683 }684 #endif685 686 /**687 * This timer callback is called every 200ms to check the contents of the X11688 * clipboard.689 * @note X11 backend code, callback for XtAppAddTimeOut, recursively690 * re-armed.691 * @todo Use the XFIXES extension to check for new clipboard data when692 * available.693 */694 void clipPollX11CBFormats(XtPointer pUserData, XtIntervalId * /* hTimerId */)695 {696 CLIPBACKEND *pCtx = (CLIPBACKEND *)pUserData;697 Log3 (("%s: called\n", __PRETTY_FUNCTION__));698 /* Get the current clipboard contents if we don't own it ourselves */699 if (!pCtx->fOwnsClipboard)700 {701 Log3 (("%s: requesting the targets that the host clipboard offers\n",702 __PRETTY_FUNCTION__));703 XtGetSelectionValue(pCtx->widget,704 clipGetAtom(pCtx->widget, "CLIPBOARD"),705 clipGetAtom(pCtx->widget, "TARGETS"),706 clipConvertX11Targets, pCtx,707 CurrentTime);708 }709 /* Re-arm our timer */710 clipSchedulePoller(pCtx, clipPollX11CBFormats);711 }712 713 #ifndef TESTCASE714 /**715 * The main loop of our clipboard reader.716 * @note X11 backend code.717 */718 static int clipEventThread(RTTHREAD self, void *pvUser)719 {720 LogRel(("Shared clipboard: starting host clipboard thread\n"));721 722 CLIPBACKEND *pCtx = (CLIPBACKEND *)pvUser;723 while (XtAppGetExitFlag(pCtx->appContext) == FALSE)724 XtAppProcessEvent(pCtx->appContext, XtIMAll);725 LogRel(("Shared clipboard: host clipboard thread terminated successfully\n"));726 return VINF_SUCCESS;727 }728 #endif729 730 /** X11 specific uninitialisation for the shared clipboard.731 * @note X11 backend code.732 */733 static void clipUninit(CLIPBACKEND *pCtx)734 {735 AssertPtrReturnVoid(pCtx);736 if (pCtx->widget)737 {738 /* Valid widget + invalid appcontext = bug. But don't return yet. */739 AssertPtr(pCtx->appContext);740 clipUnregisterContext(pCtx);741 XtDestroyWidget(pCtx->widget);742 }743 pCtx->widget = NULL;744 if (pCtx->appContext)745 XtDestroyApplicationContext(pCtx->appContext);746 pCtx->appContext = NULL;747 if (pCtx->wakeupPipeRead != 0)748 close(pCtx->wakeupPipeRead);749 if (pCtx->wakeupPipeWrite != 0)750 close(pCtx->wakeupPipeWrite);751 pCtx->wakeupPipeRead = 0;752 pCtx->wakeupPipeWrite = 0;753 }754 755 /** Worker function for stopping the clipboard which runs on the event756 * thread. */757 static void clipStopEventThreadWorker(XtPointer pUserData, int * /* source */,758 XtInputId * /* id */)759 {760 761 CLIPBACKEND *pCtx = (CLIPBACKEND *)pUserData;762 763 /* This might mean that we are getting stopped twice. */764 Assert(pCtx->widget != NULL);765 766 /* Set the termination flag to tell the Xt event loop to exit. We767 * reiterate that any outstanding requests from the X11 event loop to768 * the VBox part *must* have returned before we do this. */769 XtAppSetExitFlag(pCtx->appContext);770 pCtx->fOwnsClipboard = false;771 clipInvalidateX11Contents(pCtx);772 }773 774 /** X11 specific initialisation for the shared clipboard.775 * @note X11 backend code.776 */777 static int clipInit(CLIPBACKEND *pCtx)778 {779 /* Create a window and make it a clipboard viewer. */780 int cArgc = 0;781 char *pcArgv = 0;782 int rc = VINF_SUCCESS;783 Display *pDisplay;784 785 /* Make sure we are thread safe */786 XtToolkitThreadInitialize();787 /* Set up the Clipbard application context and main window. We call all788 * these functions directly instead of calling XtOpenApplication() so789 * that we can fail gracefully if we can't get an X11 display. */790 XtToolkitInitialize();791 pCtx->appContext = XtCreateApplicationContext();792 pDisplay = XtOpenDisplay(pCtx->appContext, 0, 0, "VBoxClipboard", 0, 0, &cArgc, &pcArgv);793 if (NULL == pDisplay)794 {795 LogRel(("Shared clipboard: failed to connect to the host clipboard - the window system may not be running.\n"));796 rc = VERR_NOT_SUPPORTED;797 }798 if (RT_SUCCESS(rc))799 {800 pCtx->widget = XtVaAppCreateShell(0, "VBoxClipboard",801 applicationShellWidgetClass,802 pDisplay, XtNwidth, 1, XtNheight,803 1, NULL);804 if (NULL == pCtx->widget)805 {806 LogRel(("Shared clipboard: failed to construct the X11 window for the host clipboard manager.\n"));807 rc = VERR_NO_MEMORY;808 }809 else810 rc = clipRegisterContext(pCtx);811 }812 if (RT_SUCCESS(rc))813 {814 XtSetMappedWhenManaged(pCtx->widget, false);815 XtRealizeWidget(pCtx->widget);816 /* Set up a timer to poll the host clipboard */817 clipSchedulePoller(pCtx, clipPollX11CBFormats);818 }819 /* Create the pipes */820 int pipes[2];821 if (!pipe(pipes))822 {823 pCtx->wakeupPipeRead = pipes[0];824 pCtx->wakeupPipeWrite = pipes[1];825 if (!XtAppAddInput(pCtx->appContext, pCtx->wakeupPipeRead,826 (XtPointer) XtInputReadMask,827 clipStopEventThreadWorker, (XtPointer) pCtx))828 rc = VERR_NO_MEMORY; /* What failure means is not doc'ed. */829 }830 else831 rc = RTErrConvertFromErrno(errno);832 if (RT_FAILURE(rc))833 clipUninit(pCtx);834 return rc;835 }836 837 /**838 * Construct the X11 backend of the shared clipboard.839 * @note X11 backend code840 */841 CLIPBACKEND *VBoxX11ClipboardConstructX11(VBOXCLIPBOARDCONTEXT *pFrontend)842 {843 int rc;844 845 CLIPBACKEND *pCtx = (CLIPBACKEND *)846 RTMemAllocZ(sizeof(CLIPBACKEND));847 if (pCtx && !RTEnvGet("DISPLAY"))848 {849 /*850 * If we don't find the DISPLAY environment variable we assume that851 * we are not connected to an X11 server. Don't actually try to do852 * this then, just fail silently and report success on every call.853 * This is important for VBoxHeadless.854 */855 LogRelFunc(("X11 DISPLAY variable not set -- disabling shared clipboard\n"));856 pCtx->fHaveX11 = false;857 return pCtx;858 }859 860 pCtx->fHaveX11 = true;861 862 LogRel(("Initializing X11 clipboard backend\n"));863 if (pCtx)864 pCtx->pFrontend = pFrontend;865 return pCtx;866 }867 868 /**869 * Destruct the shared clipboard X11 backend.870 * @note X11 backend code871 */872 void VBoxX11ClipboardDestructX11(CLIPBACKEND *pCtx)873 {874 /*875 * Immediately return if we are not connected to the host X server.876 */877 if (!pCtx->fHaveX11)878 return;879 880 /* We set this to NULL when the event thread exits. It really should881 * have exited at this point, when we are about to unload the code from882 * memory. */883 Assert(pCtx->widget == NULL);884 }885 886 /**887 * Announce to the X11 backend that we are ready to start.888 */889 int VBoxX11ClipboardStartX11(CLIPBACKEND *pCtx)890 {891 int rc = VINF_SUCCESS;892 LogFlowFunc(("\n"));893 /*894 * Immediately return if we are not connected to the host X server.895 */896 if (!pCtx->fHaveX11)897 return VINF_SUCCESS;898 899 rc = clipInit(pCtx);900 #ifndef TESTCASE901 if (RT_SUCCESS(rc))902 {903 rc = RTThreadCreate(&pCtx->thread, clipEventThread, pCtx, 0,904 RTTHREADTYPE_IO, RTTHREADFLAGS_WAITABLE, "SHCLIP");905 if (RT_FAILURE(rc))906 LogRel(("Failed to initialise the shared clipboard X11 backend.\n"));907 }908 #endif909 if (RT_SUCCESS(rc))910 {911 pCtx->fOwnsClipboard = false;912 clipInvalidateVBoxCache(pCtx);913 }914 return rc;915 }916 917 /** String written to the wakeup pipe. */918 #define WAKE_UP_STRING "WakeUp!"919 /** Length of the string written. */920 #define WAKE_UP_STRING_LEN ( sizeof(WAKE_UP_STRING) - 1 )921 922 /**923 * Shut down the shared clipboard X11 backend.924 * @note X11 backend code925 * @note Any requests from this object to get clipboard data from VBox926 * *must* have completed or aborted before we are called, as927 * otherwise the X11 event loop will still be waiting for the request928 * to return and will not be able to terminate.929 */930 int VBoxX11ClipboardStopX11(CLIPBACKEND *pCtx)931 {932 int rc, rcThread;933 unsigned count = 0;934 /*935 * Immediately return if we are not connected to the host X server.936 */937 if (!pCtx->fHaveX11)938 return VINF_SUCCESS;939 940 LogRelFunc(("stopping the shared clipboard X11 backend\n"));941 /* Write to the "stop" pipe */942 rc = write(pCtx->wakeupPipeWrite, WAKE_UP_STRING, WAKE_UP_STRING_LEN);943 do944 {945 rc = RTThreadWait(pCtx->thread, 1000, &rcThread);946 ++count;947 Assert(RT_SUCCESS(rc) || ((VERR_TIMEOUT == rc) && (count != 5)));948 } while ((VERR_TIMEOUT == rc) && (count < 300));949 if (RT_SUCCESS(rc))950 AssertRC(rcThread);951 else952 LogRelFunc(("rc=%Rrc\n", rc));953 clipUninit(pCtx);954 LogFlowFunc(("returning %Rrc.\n", rc));955 return rc;956 }957 958 /**959 * Satisfy a request from X11 for clipboard targets supported by VBox.960 *961 * @returns true if we successfully convert the data to the format962 * requested, false otherwise.963 *964 * @param atomTypeReturn The type of the data we are returning965 * @param pValReturn A pointer to the data we are returning. This966 * should be set to memory allocated by XtMalloc,967 * which will be freed later by the Xt toolkit.968 * @param pcLenReturn The length of the data we are returning969 * @param piFormatReturn The format (8bit, 16bit, 32bit) of the data we are970 * returning971 * @note X11 backend code, called by the XtOwnSelection callback.972 */973 static Boolean clipCreateX11Targets(CLIPBACKEND *pCtx, Atom *atomTypeReturn,974 XtPointer *pValReturn,975 unsigned long *pcLenReturn,976 int *piFormatReturn)977 {978 Atom *atomTargets = (Atom *)XtMalloc( (MAX_CLIP_X11_FORMATS + 3)979 * sizeof(Atom));980 unsigned cTargets = 0;981 LogFlowFunc (("called\n"));982 CLIPX11FORMAT format = NIL_CLIPX11FORMAT;983 do984 {985 format = clipNextX11Format(VBOX_SHARED_CLIPBOARD_FMT_UNICODETEXT,986 format);987 if (format != NIL_CLIPX11FORMAT)988 {989 atomTargets[cTargets] = clipAtomForX11Format(pCtx->widget,990 format);991 ++cTargets;992 }993 } while (format != NIL_CLIPX11FORMAT);994 /* We always offer these */995 atomTargets[cTargets] = clipGetAtom(pCtx->widget, "TARGETS");996 atomTargets[cTargets + 1] = clipGetAtom(pCtx->widget, "MULTIPLE");997 atomTargets[cTargets + 2] = clipGetAtom(pCtx->widget, "TIMESTAMP");998 *atomTypeReturn = XA_ATOM;999 *pValReturn = (XtPointer)atomTargets;1000 *pcLenReturn = cTargets + 3;1001 *piFormatReturn = 32;1002 return true;1003 }1004 1005 /** This is a wrapper around VBoxX11ClipboardReadVBoxData that will cache the1006 * data returned. This is unfortunately necessary, because if the other side1007 * of the shared clipboard is also an X11 system, it may send a format1008 * announcement message every time its clipboard is read, for reasons that1009 * are explained elsewhere. Unfortunately, some applications on our side1010 * like to read the clipboard several times in short succession in different1011 * formats. This can fail if it collides with a format announcement message.1012 * @todo any ideas about how to do this better are welcome.1013 */1014 static int clipReadVBoxClipboard(CLIPBACKEND *pCtx, uint32_t u32Format,1015 void **ppv, uint32_t *pcb)1016 {1017 int rc = VINF_SUCCESS;1018 LogFlowFunc(("pCtx=%p, u32Format=%02X, ppv=%p, pcb=%p\n", pCtx,1019 u32Format, ppv, pcb));1020 if (u32Format == VBOX_SHARED_CLIPBOARD_FMT_UNICODETEXT)1021 {1022 if (pCtx->pvUnicodeCache == NULL)1023 rc = VBoxX11ClipboardReadVBoxData(pCtx->pFrontend, u32Format,1024 &pCtx->pvUnicodeCache,1025 &pCtx->cbUnicodeCache);1026 if (RT_SUCCESS(rc))1027 {1028 *ppv = RTMemDup(pCtx->pvUnicodeCache, pCtx->cbUnicodeCache);1029 *pcb = pCtx->cbUnicodeCache;1030 if (*ppv == NULL)1031 rc = VERR_NO_MEMORY;1032 }1033 }1034 else1035 rc = VBoxX11ClipboardReadVBoxData(pCtx->pFrontend, u32Format,1036 ppv, pcb);1037 LogFlowFunc(("returning %Rrc\n", rc));1038 if (RT_SUCCESS(rc))1039 LogFlowFunc(("*ppv=%.*ls, *pcb=%u\n", *pcb, *ppv, *pcb));1040 return rc;1041 }1042 1043 1044 /**1045 * Convert text from Windows format (UCS-2 with CRLF line endings) to standard1046 * Utf-8.1047 *1048 * @returns iprt status code1049 *1050 * @param pwszSrc the text to be converted1051 * @param cbSrc the length of @a pwszSrc in bytes1052 * @param pszBuf where to write the converted string1053 * @param cbBuf the size of the buffer pointed to by @a pszBuf1054 * @param pcbActual where to store the size of the converted string.1055 * optional.1056 */1057 static int clipWinTxtToUtf8(PRTUTF16 pwszSrc, size_t cbSrc, char *pszBuf,1058 size_t cbBuf, size_t *pcbActual)1059 {1060 PRTUTF16 pwszTmp = NULL;1061 size_t cwSrc = cbSrc / 2, cwTmp = 0, cbDest = 0;1062 int rc = VINF_SUCCESS;1063 1064 LogFlowFunc (("pwszSrc=%.*ls, cbSrc=%u\n", cbSrc, pwszSrc, cbSrc));1065 /* How long will the converted text be? */1066 AssertPtr(pwszSrc);1067 AssertPtr(pszBuf);1068 rc = vboxClipboardUtf16GetLinSize(pwszSrc, cwSrc, &cwTmp);1069 if (RT_SUCCESS(rc) && cwTmp == 0)1070 rc = VERR_NO_DATA;1071 if (RT_SUCCESS(rc))1072 pwszTmp = (PRTUTF16)RTMemAlloc(cwTmp * 2);1073 if (!pwszTmp)1074 rc = VERR_NO_MEMORY;1075 /* Convert the text. */1076 if (RT_SUCCESS(rc))1077 rc = vboxClipboardUtf16WinToLin(pwszSrc, cwSrc, pwszTmp, cwTmp);1078 if (RT_SUCCESS(rc))1079 /* Convert the Utf16 string to Utf8. */1080 rc = RTUtf16ToUtf8Ex(pwszTmp + 1, cwTmp - 1, &pszBuf, cbBuf,1081 &cbDest);1082 RTMemFree(reinterpret_cast<void *>(pwszTmp));1083 if (pcbActual)1084 *pcbActual = cbDest + 1;1085 LogFlowFunc(("returning %Rrc\n", rc));1086 if (RT_SUCCESS(rc))1087 LogFlowFunc (("converted string is %.*s. Returning.\n", cbDest,1088 pszBuf));1089 return rc;1090 }1091 1092 /**1093 * Satisfy a request from X11 to convert the clipboard text to1094 * COMPOUND_TEXT. We return null-terminated text, but can cope with non-null-1095 * terminated input.1096 *1097 * @returns true if we successfully convert the data to the format1098 * requested, false otherwise.1099 *1100 * @param pDisplay an X11 display structure, needed for conversions1101 * performed by Xlib1102 * @param pv the text to be converted (UCS-2 with Windows EOLs)1103 * @param cb the length of the text in @cb in bytes1104 * @param atomTypeReturn where to store the atom for the type of the data1105 * we are returning1106 * @param pValReturn where to store the pointer to the data we are1107 * returning. This should be to memory allocated by1108 * XtMalloc, which will be freed by the Xt toolkit1109 * later.1110 * @param pcLenReturn where to store the length of the data we are1111 * returning1112 * @param piFormatReturn where to store the bit width (8, 16, 32) of the1113 * data we are returning1114 */1115 static Boolean clipConvertToCTextForX11(Display *pDisplay, PRTUTF16 pwszSrc,1116 size_t cbSrc, Atom *atomTypeReturn,1117 XtPointer *pValReturn,1118 unsigned long *pcLenReturn,1119 int *piFormatReturn)1120 {1121 char *pszTmp = NULL;1122 size_t cbTmp = 0, cbActual = 0;1123 XTextProperty property;1124 int rc = VINF_SUCCESS, xrc = 0;1125 1126 /* This may slightly overestimate the space needed. */1127 rc = RTUtf16CalcUtf8LenEx(pwszSrc, cbSrc / 2, &cbTmp);1128 ++cbTmp; /* Null terminator */1129 if (RT_SUCCESS(rc))1130 {1131 pszTmp = (char *)RTMemAlloc(cbTmp);1132 if (!pszTmp)1133 rc = VERR_NO_MEMORY;1134 }1135 if (RT_SUCCESS(rc))1136 rc = clipWinTxtToUtf8(pwszSrc, cbSrc, pszTmp, cbTmp + 1,1137 &cbActual);1138 /* And finally (!) convert the Utf8 text to compound text. */1139 #ifdef X_HAVE_UTF8_STRING1140 if (RT_SUCCESS(rc))1141 xrc = Xutf8TextListToTextProperty(pDisplay, &pszTmp, 1,1142 XCompoundTextStyle, &property);1143 #else1144 if (RT_SUCCESS(rc))1145 xrc = XmbTextListToTextProperty(pDisplay, &pszTmp, 1,1146 XCompoundTextStyle, &property);1147 #endif1148 if (RT_SUCCESS(rc) && xrc < 0)1149 rc = ( xrc == XNoMemory ? VERR_NO_MEMORY1150 : xrc == XLocaleNotSupported ? VERR_NOT_SUPPORTED1151 : xrc == XConverterNotFound ? VERR_NOT_SUPPORTED1152 : VERR_UNRESOLVED_ERROR);1153 RTMemFree(pszTmp);1154 *atomTypeReturn = property.encoding;1155 *pValReturn = reinterpret_cast<XtPointer>(property.value);1156 *pcLenReturn = property.nitems + 1;1157 *piFormatReturn = property.format;1158 LogFlowFunc(("returning, internal rc=%Rrc\n"));1159 if (RT_SUCCESS(rc))1160 LogFlowFunc (("converted string is %s\n", property.value));1161 return RT_SUCCESS(rc);1162 }1163 1164 /**1165 * Return VBox's clipboard data for an X11 client.1166 * @note X11 backend code, callback for XtOwnSelection1167 */1168 static Boolean vboxClipboardConvertForX11(Widget widget, Atom *atomSelection,1169 Atom *atomTarget,1170 Atom *atomTypeReturn,1171 XtPointer *pValReturn,1172 unsigned long *pcLenReturn,1173 int *piFormatReturn)1174 {1175 CLIPFORMAT enmFormat = INVALID;1176 CLIPBACKEND *pCtx = clipLookupContext(widget);1177 int rc = VINF_SUCCESS;1178 bool retval = false;1179 1180 LogFlowFunc(("\n"));1181 /* Drop requests that we receive too late. */1182 if (!pCtx->fOwnsClipboard)1183 return false;1184 if ( (*atomSelection != clipGetAtom(pCtx->widget, "CLIPBOARD"))1185 && (*atomSelection != clipGetAtom(pCtx->widget, "PRIMARY"))1186 )1187 {1188 LogFlowFunc(("rc = false\n"));1189 return false;1190 }1191 if (*atomTarget == clipGetAtom(pCtx->widget, "TARGETS"))1192 {1193 enmFormat = TARGETS;1194 }1195 else1196 {1197 for (unsigned i = 0; i < RT_ELEMENTS(g_aFormats); ++i)1198 {1199 if (*atomTarget == clipGetAtom(pCtx->widget,1200 g_aFormats[i].pcszAtom))1201 {1202 enmFormat = g_aFormats[i].enmFormat;1203 break;1204 }1205 }1206 }1207 switch (enmFormat)1208 {1209 case TARGETS:1210 return clipCreateX11Targets(pCtx, atomTypeReturn, pValReturn,1211 pcLenReturn, piFormatReturn);1212 case UTF8:1213 {1214 /* Read the clipboard data from the guest. */1215 void *pv = NULL;1216 uint32_t cb = 0;1217 rc = clipReadVBoxClipboard(pCtx,1218 VBOX_SHARED_CLIPBOARD_FMT_UNICODETEXT,1219 &pv, &cb);1220 if (RT_SUCCESS(rc) && (cb != 0))1221 {1222 /* This may slightly overestimate the space needed. */1223 size_t cbDest = 0;1224 rc = RTUtf16CalcUtf8LenEx((PRTUTF16)pv, cb / 2, &cbDest);1225 if (RT_SUCCESS(rc))1226 {1227 char *pszDest = (char *)XtMalloc(cbDest + 1);1228 size_t cbActual = 0;1229 if (pszDest)1230 rc = clipWinTxtToUtf8((PRTUTF16)pv, cb, pszDest,1231 cbDest + 1, &cbActual);1232 if (RT_SUCCESS(rc))1233 {1234 *atomTypeReturn = *atomTarget;1235 *pValReturn = (XtPointer)pszDest;1236 *pcLenReturn = cbActual;1237 *piFormatReturn = 8;1238 retval = true;1239 }1240 }1241 }1242 RTMemFree(pv);1243 break;1244 }1245 case CTEXT:1246 {1247 /* Read the clipboard data from the guest. */1248 void *pv = NULL;1249 uint32_t cb = 0;1250 rc = clipReadVBoxClipboard(pCtx,1251 VBOX_SHARED_CLIPBOARD_FMT_UNICODETEXT,1252 &pv, &cb);1253 if (RT_SUCCESS(rc) && (cb != 0))1254 retval = clipConvertToCTextForX11(XtDisplay(pCtx->widget),1255 (PRTUTF16)pv, cb,1256 atomTypeReturn, pValReturn,1257 pcLenReturn, piFormatReturn);1258 RTMemFree(pv);1259 break;1260 }1261 default:1262 LogFunc (("bad format\n"));1263 return false;1264 }1265 return retval;1266 }1267 1268 /**1269 * This is called by the X toolkit intrinsics to let us know that another1270 * X11 client has taken the clipboard. In this case we notify VBox that1271 * we want ownership of the clipboard.1272 * @note X11 backend code, callback for XtOwnSelection1273 */1274 static void vboxClipboardReturnToX11(Widget widget, Atom *)1275 {1276 CLIPBACKEND *pCtx = clipLookupContext(widget);1277 LogFlowFunc (("called, giving X11 clipboard ownership\n"));1278 /* These should be set to the right values as soon as we start polling */1279 clipInvalidateX11Contents(pCtx);1280 pCtx->fOwnsClipboard = false;1281 clipInvalidateVBoxCache(pCtx);1282 }1283 1284 static void clipSchedule(XtAppContext app_context, XtTimerCallbackProc proc,1285 XtPointer client_data);1286 #ifndef TESTCASE1287 void clipSchedule(XtAppContext app_context, XtTimerCallbackProc proc,1288 XtPointer client_data)1289 {1290 XtAppAddTimeOut(app_context, 0, proc, client_data);1291 }1292 #endif1293 1294 /** Structure used to pass information about formats that VBox supports */1295 typedef struct _VBOXCLIPBOARDFORMATS1296 {1297 /** Context information for the X11 clipboard */1298 CLIPBACKEND *pCtx;1299 /** Formats supported by VBox */1300 uint32_t formats;1301 } VBOXCLIPBOARDFORMATS;1302 1303 /** Worker function for VBoxX11ClipboardAnnounceVBoxFormat which runs on the1304 * event thread. */1305 static void vboxClipboardAnnounceWorker(XtPointer pUserData,1306 XtIntervalId * /* interval */)1307 {1308 /* Extract and free the user data */1309 VBOXCLIPBOARDFORMATS *pFormats = (VBOXCLIPBOARDFORMATS *)pUserData;1310 CLIPBACKEND *pCtx = pFormats->pCtx;1311 uint32_t u32Formats = pFormats->formats;1312 RTMemFree(pFormats);1313 LogFlowFunc (("u32Formats=%d\n", u32Formats));1314 pCtx->vboxFormats = u32Formats;1315 /* Invalidate the clipboard cache */1316 if (pCtx->pvUnicodeCache != NULL)1317 {1318 RTMemFree(pCtx->pvUnicodeCache);1319 pCtx->pvUnicodeCache = NULL;1320 }1321 if (u32Formats == 0)1322 {1323 /* This is just an automatism, not a genuine anouncement */1324 XtDisownSelection(pCtx->widget, clipGetAtom(pCtx->widget, "CLIPBOARD"), CurrentTime);1325 pCtx->fOwnsClipboard = false;1326 LogFlowFunc(("returning\n"));1327 return;1328 }1329 Log2 (("%s: giving the guest clipboard ownership\n", __PRETTY_FUNCTION__));1330 if (XtOwnSelection(pCtx->widget, clipGetAtom(pCtx->widget, "CLIPBOARD"), CurrentTime,1331 vboxClipboardConvertForX11, vboxClipboardReturnToX11,1332 0) == True)1333 {1334 pCtx->fOwnsClipboard = true;1335 /* Grab the middle-button paste selection too. */1336 XtOwnSelection(pCtx->widget, clipGetAtom(pCtx->widget, "PRIMARY"), CurrentTime,1337 vboxClipboardConvertForX11, NULL, 0);1338 }1339 else1340 {1341 /* Another X11 client claimed the clipboard just after us, so let it1342 * go again. */1343 Log2 (("%s: returning clipboard ownership to the X11\n",1344 __PRETTY_FUNCTION__));1345 /* VBox thinks it currently owns the clipboard, so we must notify it1346 * as soon as we know what formats X11 has to offer. */1347 clipInvalidateVBoxCache(pCtx);1348 pCtx->fOwnsClipboard = false;1349 }1350 LogFlowFunc(("returning\n"));1351 }1352 1353 /**1354 * VBox is taking possession of the shared clipboard.1355 *1356 * @param u32Formats Clipboard formats the guest is offering1357 * @note X11 backend code1358 */1359 void VBoxX11ClipboardAnnounceVBoxFormat(CLIPBACKEND *pCtx,1360 uint32_t u32Formats)1361 {1362 /*1363 * Immediately return if we are not connected to the host X server.1364 */1365 if (!pCtx->fHaveX11)1366 return;1367 /* This must be freed by the worker callback */1368 VBOXCLIPBOARDFORMATS *pFormats =1369 (VBOXCLIPBOARDFORMATS *) RTMemAlloc(sizeof(VBOXCLIPBOARDFORMATS));1370 if (pFormats != NULL) /* if it is we will soon have other problems */1371 {1372 pFormats->pCtx = pCtx;1373 pFormats->formats = u32Formats;1374 clipSchedule(pCtx->appContext, vboxClipboardAnnounceWorker,1375 (XtPointer) pFormats);1376 }1377 1448 } 1378 1449 … … 1382 1453 XtIntervalId * /* interval */) 1383 1454 { 1384 CLIP X11CLIPBOARDREQ *pReq = (CLIPX11CLIPBOARDREQ *)pUserData;1455 CLIPREADX11CBREQ *pReq = (CLIPREADX11CBREQ *)pUserData; 1385 1456 CLIPBACKEND *pCtx = pReq->mCtx; 1386 1457 LogFlowFunc (("pReq->mFormat = %02X, pReq->mSize = %d\n", pReq->mFormat, … … 1401 1472 { 1402 1473 pReq->mTextFormat = pCtx->X11TextFormat; 1403 if (pReq->mTextFormat == 0)1474 if (pReq->mTextFormat == INVALID) 1404 1475 /* VBox thinks we have data and we don't */ 1405 1476 rc = VERR_NO_DATA; … … 1408 1479 * owner */ 1409 1480 XtGetSelectionValue(pCtx->widget, clipGetAtom(pCtx->widget, "CLIPBOARD"), 1410 clip GetAtom(pCtx->widget,1411 g_aFormats[pCtx->X11TextFormat].pcszAtom),1481 clipAtomForX11Format(pCtx->widget, 1482 pCtx->X11TextFormat), 1412 1483 clipConvertX11CB, 1413 1484 reinterpret_cast<XtPointer>(pReq), … … 1430 1501 * Called when VBox wants to read the X11 clipboard. 1431 1502 * 1432 * @param pClient Context information about the guest VM 1433 * @param u32Format The format that the guest would like to receive the data in 1503 * @param pCtx Context data for the clipboard backend 1504 * @param u32Format The format that the VBox would like to receive the data 1505 * in 1434 1506 * @param pv Where to write the data to 1435 1507 * @param cb The size of the buffer to write the data to … … 1446 1518 1447 1519 /* 1448 * Immediately return if we are not connected to the hostX server.1520 * Immediately return if we are not connected to the X server. 1449 1521 */ 1450 1522 if (!pCtx->fHaveX11) 1451 1523 return VINF_SUCCESS; 1452 1524 1453 CLIP X11CLIPBOARDREQ req = { 0 };1525 CLIPREADX11CBREQ req = { 0 }; 1454 1526 req.mRC = VERR_WRONG_ORDER; 1455 1527 req.mBuffer = pv; … … 1463 1535 { 1464 1536 /* We use this to schedule a worker function on the event thread. */ 1465 clip Schedule(pCtx->appContext, vboxClipboardReadX11Worker,1466 (XtPointer) &req);1537 clipQueueToEventThread(pCtx->appContext, vboxClipboardReadX11Worker, 1538 (XtPointer) &req); 1467 1539 rc = RTSemEventWait(req.mSem, RT_INDEFINITE_WAIT); 1468 1540 if (RT_SUCCESS(rc)) … … 1506 1578 /* For the purpose of the test case, we just execute the procedure to be 1507 1579 * scheduled, as we are running single threaded. */ 1508 void clipSchedule(XtAppContext app_context, XtTimerCallbackProc proc, 1509 XtPointer client_data) 1580 void clipQueueToEventThread(XtAppContext app_context, 1581 XtTimerCallbackProc proc, 1582 XtPointer client_data) 1510 1583 { 1511 1584 proc(client_data, NULL); … … 1850 1923 g_pfnSelLose(TEST_WIDGET, &clipAtom); 1851 1924 g_ownsSel = false; 1852 g_fX11Formats = 0;1853 1925 } 1854 1926 … … 2201 2273 2202 2274 /*** request for an invalid VBox format from X11 ***/ 2203 RTPrintf(TEST_NAME ": TESTING a request for an invalid hostformat from X11\n");2275 RTPrintf(TEST_NAME ": TESTING a request for an invalid VBox format from X11\n"); 2204 2276 rc = VBoxX11ClipboardReadX11Data(pCtx, 0xffff, (void *) pc, 2205 2277 sizeof(pc), &cbActual);
Note:
See TracChangeset
for help on using the changeset viewer.