Changeset 22181 in vbox for trunk/src/VBox/GuestHost/SharedClipboard
- Timestamp:
- Aug 11, 2009 4:41:38 PM (16 years ago)
- svn:sync-xref-src-repo-rev:
- 50961
- File:
-
- 1 edited
Legend:
- Unmodified
- Added
- Removed
-
trunk/src/VBox/GuestHost/SharedClipboard/x11-clipboard.cpp
r21651 r22181 21 21 */ 22 22 23 /* Note: to automatically run regression tests on the shared clipboard, set 24 * the make variable VBOX_RUN_X11_CLIPBOARD_TEST=1 while building. If you 25 * often make changes to the clipboard code, setting this variable in 26 * LocalConfig.kmk will cause the tests to be run every time the code is 23 /* Note: to automatically run regression tests on the shared clipboard, 24 * execute the tstClipboardX11 testcase. If you often make changes to the 25 * clipboard code, adding the line 26 * OTHERS += $(PATH_tstClipboardX11)/tstClipboardX11.run 27 * to LocalConfig.kmk will cause the tests to be run every time the code is 27 28 * changed. */ 28 29 … … 31 32 #include <errno.h> 32 33 34 #include <dlfcn.h> 35 #include <fcntl.h> 33 36 #include <unistd.h> 34 37 … … 44 47 #include <X11/StringDefs.h> 45 48 49 #include <iprt/types.h> 46 50 #include <iprt/env.h> 47 51 #include <iprt/mem.h> … … 164 168 Widget widget; 165 169 166 /** Does VBox currently own the clipboard? If so, we don't need to poll 167 * X11 for supported formats. */ 170 /** Does VBox currently own the clipboard? */ 168 171 bool fOwnsClipboard; 172 /** Should we try to grab the clipboard on startup? */ 173 bool fGrabClipboardOnStart; 169 174 170 175 /** The best text format X11 has to offer, as an index into the formats … … 191 196 /** The reader end of the pipe */ 192 197 int wakeupPipeRead; 198 /** A pointer to the XFixesSelectSelectionInput function */ 199 void (*fixesSelectInput)(Display *, Window, Atom, unsigned long); 200 /** The first XFixes event number */ 201 int fixesEventBase; 193 202 }; 194 203 … … 280 289 } 281 290 282 static void clipQueueToEventThread( XtAppContext app_context,291 static void clipQueueToEventThread(CLIPBACKEND *pCtx, 283 292 XtTimerCallbackProc proc, 284 293 XtPointer client_data); 294 295 /** String written to the wakeup pipe. */ 296 #define WAKE_UP_STRING "WakeUp!" 297 /** Length of the string written. */ 298 #define WAKE_UP_STRING_LEN ( sizeof(WAKE_UP_STRING) - 1 ) 299 285 300 #ifndef TESTCASE 286 void clipQueueToEventThread(XtAppContext app_context, 301 /** Schedule a function call to run on the Xt event thread by passing it to 302 * the application context as a 0ms timeout and waking up the event loop by 303 * writing to the wakeup pipe which it monitors. */ 304 void clipQueueToEventThread(CLIPBACKEND *pCtx, 287 305 XtTimerCallbackProc proc, 288 306 XtPointer client_data) 289 307 { 290 XtAppAddTimeOut(app_context, 0, proc, client_data); 308 LogRel2(("clipQueueToEventThread: proc=%p, client_data=%p\n", 309 proc, client_data)); 310 XtAppAddTimeOut(pCtx->appContext, 0, proc, client_data); 311 write(pCtx->wakeupPipeWrite, WAKE_UP_STRING, WAKE_UP_STRING_LEN); 291 312 } 292 313 #endif … … 302 323 303 324 /** 304 * Forget which formats were previously in the X11 clipboard. Should be 305 * called when we grab the clipboard, so that when we lose it again the poller 306 * will notify us when new formats become available. */ 325 * Forget which formats were previously in the X11 clipboard. Called when we 326 * grab the clipboard. */ 307 327 static void clipResetX11Formats(CLIPBACKEND *pCtx) 308 328 { … … 425 445 bool changed = false; 426 446 427 LogRel 3(("%s: called\n", __PRETTY_FUNCTION__));447 LogRel2 (("%s: called\n", __PRETTY_FUNCTION__)); 428 448 if (pCtx->fOwnsClipboard) 429 449 /* VBox raced us and we lost. So we don't want to report anything. */ 430 450 return; 431 451 clipGetFormatsFromTargets(pCtx, pTargets, cTargets, &changed); 432 if (changed) 433 clipReportFormatsToVBox(pCtx); 452 clipReportFormatsToVBox(pCtx); 434 453 } 435 454 … … 460 479 * Notify the VBox clipboard about available data formats, based on the 461 480 * "targets" information obtained from the X11 clipboard. 462 * @note callback for XtGetSelectionValue , called on a polling loop481 * @note callback for XtGetSelectionValue 463 482 */ 464 483 static void clipConvertX11Targets(Widget, XtPointer pClientData, … … 472 491 : (Atom *)pValue; 473 492 size_t cTargets = pTargets ? *pcLen : 0; 493 LogRel2(("clipConvertX11Targets: pValue=%p, *pcLen=%u, *atomType=%d, XT_CONVERT_FAIL=%d\n", 494 pValue, *pcLen, *atomType, XT_CONVERT_FAIL)); 474 495 clipUpdateX11Targets(pCtx, pTargets, cTargets); 475 496 XtFree(reinterpret_cast<char *>(pValue)); 476 497 } 477 498 478 enum { TIMER_FREQ = 200 /* ms */ }; 479 480 static void clipPollX11CBFormats(XtPointer pUserData, 481 XtIntervalId * /* hTimerId */); 482 static void clipSchedulePoller(CLIPBACKEND *pCtx, 483 XtTimerCallbackProc proc); 484 485 #ifndef TESTCASE 486 void clipSchedulePoller(CLIPBACKEND *pCtx, 487 XtTimerCallbackProc proc) 488 { 489 XtAppAddTimeOut(pCtx->appContext, TIMER_FREQ, proc, pCtx); 490 } 491 #endif 492 493 /** 494 * This timer callback is called every 200ms to check the contents of the X11 495 * clipboard. 496 * @note X11 backend code, callback for XtAppAddTimeOut, recursively 497 * re-armed. 498 * @todo Use the XFIXES extension to check for new clipboard data when 499 * available. 500 */ 501 void clipPollX11CBFormats(XtPointer pUserData, XtIntervalId * /* hTimerId */) 502 { 503 CLIPBACKEND *pCtx = (CLIPBACKEND *)pUserData; 504 LogRel3 (("%s: called\n", __PRETTY_FUNCTION__)); 499 /** 500 * Callback to notify us when the contents of the X11 clipboard change. 501 */ 502 void clipQueryX11CBFormats(CLIPBACKEND *pCtx) 503 { 504 LogRel2 (("%s: called\n", __PRETTY_FUNCTION__)); 505 505 /* Get the current clipboard contents if we don't own it ourselves */ 506 506 if (!pCtx->fOwnsClipboard) 507 507 { 508 LogRel 3(("%s: requesting the targets that the X11 clipboard offers\n",508 LogRel2 (("%s: requesting the targets that the X11 clipboard offers\n", 509 509 __PRETTY_FUNCTION__)); 510 510 XtGetSelectionValue(pCtx->widget, … … 514 514 CurrentTime); 515 515 } 516 /* Re-arm our timer */517 clipSchedulePoller(pCtx, clipPollX11CBFormats);518 516 } 519 517 520 518 #ifndef TESTCASE 519 /** 520 * Wait until an event arrives and handle it if it is an XFIXES selection 521 * event, which Xt doesn't know about. 522 */ 523 void clipPeekEventAndDoXFixesHandling(CLIPBACKEND *pCtx) 524 { 525 XEvent event = { 0 }; 526 527 if (XtAppPeekEvent(pCtx->appContext, &event)) 528 if (event.type == pCtx->fixesEventBase) 529 clipQueryX11CBFormats(pCtx); 530 } 531 521 532 /** 522 533 * The main loop of our clipboard reader. … … 528 539 529 540 CLIPBACKEND *pCtx = (CLIPBACKEND *)pvUser; 541 542 if (pCtx->fGrabClipboardOnStart) 543 clipQueryX11CBFormats(pCtx); 530 544 while (XtAppGetExitFlag(pCtx->appContext) == FALSE) 545 { 546 clipPeekEventAndDoXFixesHandling(pCtx); 531 547 XtAppProcessEvent(pCtx->appContext, XtIMAll); 548 } 532 549 LogRel(("Shared clipboard: shared clipboard thread terminated successfully\n")); 533 550 return VINF_SUCCESS; … … 562 579 /** Worker function for stopping the clipboard which runs on the event 563 580 * thread. */ 564 static void clipStopEventThreadWorker(XtPointer pUserData, int * /* source */, 565 XtInputId * /* id */) 581 static void clipStopEventThreadWorker(XtPointer pUserData, XtIntervalId *) 566 582 { 567 583 … … 575 591 * the VBox part *must* have returned before we do this. */ 576 592 XtAppSetExitFlag(pCtx->appContext); 593 } 594 595 #ifndef TESTCASE 596 /** Setup the XFixes library and load the XFixesSelectSelectionInput symbol */ 597 static int clipLoadXFixes(Display *pDisplay, CLIPBACKEND *pCtx) 598 { 599 int dummy1 = 0, dummy2 = 0, rc = VINF_SUCCESS; 600 void *hFixesLib; 601 602 hFixesLib = dlopen("libXfixes.so.1", RTLD_LAZY); 603 if (!hFixesLib) 604 hFixesLib = dlopen("libXfixes.so.2", RTLD_LAZY); 605 if (!hFixesLib) 606 hFixesLib = dlopen("libXfixes.so.3", RTLD_LAZY); 607 if (hFixesLib) 608 pCtx->fixesSelectInput = 609 (void (*)(Display *, Window, Atom, long unsigned int)) 610 (uintptr_t)dlsym(hFixesLib, "XFixesSelectSelectionInput"); 611 /* For us, a NULL function pointer is a failure */ 612 if (!hFixesLib || !pCtx->fixesSelectInput) 613 rc = VERR_NOT_SUPPORTED; 614 if ( RT_SUCCESS(rc) 615 && !XQueryExtension(pDisplay, "XFIXES", &dummy1, 616 &pCtx->fixesEventBase, &dummy2)) 617 rc = VERR_NOT_SUPPORTED; 618 if (RT_SUCCESS(rc) && pCtx->fixesEventBase < 0) 619 rc = VERR_NOT_SUPPORTED; 620 return rc; 621 } 622 #endif 623 624 /** This is the callback which is scheduled when data is available on the 625 * wakeup pipe. It simply reads all data from the pipe. */ 626 static void clipDrainWakeupPipe(XtPointer pUserData, int *, XtInputId *) 627 { 628 CLIPBACKEND *pCtx = (CLIPBACKEND *)pUserData; 629 char acBuf[WAKE_UP_STRING_LEN]; 630 631 LogRel2(("clipDrainWakeupPipe: called\n")); 632 while (read(pCtx->wakeupPipeRead, acBuf, sizeof(acBuf)) > 0) {} 577 633 } 578 634 … … 601 657 rc = VERR_NOT_SUPPORTED; 602 658 } 659 #ifndef TESTCASE 660 if (RT_SUCCESS(rc)) 661 rc = clipLoadXFixes(pDisplay, pCtx); 662 #endif 603 663 if (RT_SUCCESS(rc)) 604 664 { … … 617 677 if (RT_SUCCESS(rc)) 618 678 { 679 EventMask mask = 0; 680 619 681 XtSetMappedWhenManaged(pCtx->widget, false); 620 682 XtRealizeWidget(pCtx->widget); 621 /* Set up a timer to poll the X11 clipboard */ 622 clipSchedulePoller(pCtx, clipPollX11CBFormats); 683 #ifndef TESTCASE 684 /* Enable clipboard update notification */ 685 pCtx->fixesSelectInput(pDisplay, XtWindow(pCtx->widget), 686 clipGetAtom(pCtx->widget, "CLIPBOARD"), 687 7 /* All XFixes*Selection*NotifyMask flags */); 688 #endif 623 689 } 624 690 /* Create the pipes */ … … 630 696 if (!XtAppAddInput(pCtx->appContext, pCtx->wakeupPipeRead, 631 697 (XtPointer) XtInputReadMask, 632 clip StopEventThreadWorker, (XtPointer) pCtx))698 clipDrainWakeupPipe, (XtPointer) pCtx)) 633 699 rc = VERR_NO_MEMORY; /* What failure means is not doc'ed. */ 700 if ( RT_SUCCESS(rc) 701 && (fcntl(pCtx->wakeupPipeRead, F_SETFL, O_NONBLOCK) != 0)) 702 rc = RTErrConvertFromErrno(errno); 634 703 } 635 704 else … … 691 760 /** 692 761 * Announce to the X11 backend that we are ready to start. 693 */ 694 int ClipStartX11(CLIPBACKEND *pCtx) 762 * @param grab whether we should try to grab the shared clipboard at once 763 */ 764 int ClipStartX11(CLIPBACKEND *pCtx, bool grab) 695 765 { 696 766 int rc = VINF_SUCCESS; … … 703 773 704 774 rc = clipInit(pCtx); 775 if (RT_SUCCESS(rc)) 776 { 777 pCtx->fOwnsClipboard = false; 778 clipResetX11Formats(pCtx); 779 pCtx->fGrabClipboardOnStart = grab; 780 } 705 781 #ifndef TESTCASE 706 782 if (RT_SUCCESS(rc)) … … 712 788 } 713 789 #endif 714 if (RT_SUCCESS(rc))715 {716 pCtx->fOwnsClipboard = false;717 clipResetX11Formats(pCtx);718 }719 790 return rc; 720 791 } 721 722 /** String written to the wakeup pipe. */723 #define WAKE_UP_STRING "WakeUp!"724 /** Length of the string written. */725 #define WAKE_UP_STRING_LEN ( sizeof(WAKE_UP_STRING) - 1 )726 792 727 793 /** … … 745 811 LogRelFunc(("stopping the shared clipboard X11 backend\n")); 746 812 /* Write to the "stop" pipe */ 747 rc = write(pCtx->wakeupPipeWrite, WAKE_UP_STRING, WAKE_UP_STRING_LEN); 813 clipQueueToEventThread(pCtx, clipStopEventThreadWorker, (XtPointer) pCtx); 814 #ifndef TESTCASE 748 815 do 749 816 { … … 752 819 Assert(RT_SUCCESS(rc) || ((VERR_TIMEOUT == rc) && (count != 5))); 753 820 } while ((VERR_TIMEOUT == rc) && (count < 300)); 821 #else 822 rc = VINF_SUCCESS; 823 rcThread = VINF_SUCCESS; 824 #endif 754 825 if (RT_SUCCESS(rc)) 755 826 AssertRC(rcThread); … … 1090 1161 rc = clipCreateX11Targets(pCtx, atomTypeReturn, pValReturn, 1091 1162 pcLenReturn, piFormatReturn); 1092 else1093 1163 rc = clipConvertVBoxCBForX11(pCtx, atomTarget, atomTypeReturn, 1094 1164 pValReturn, pcLenReturn, piFormatReturn); … … 1103 1173 { 1104 1174 LogRelFlowFunc (("\n")); 1105 /* The formats should be set to the right values as soon as we start1106 * polling*/1175 /* The formats should be set to the right values as soon as X11 clipboard 1176 * data becomes available. */ 1107 1177 clipReportEmptyX11CB(pCtx); 1108 1178 pCtx->fOwnsClipboard = false; … … 1203 1273 pFormats->pCtx = pCtx; 1204 1274 pFormats->formats = u32Formats; 1205 clipQueueToEventThread(pCtx ->appContext, clipNewVBoxFormatsWorker,1275 clipQueueToEventThread(pCtx, clipNewVBoxFormatsWorker, 1206 1276 (XtPointer) pFormats); 1207 1277 } … … 1497 1567 RTMemFree(pvDest); 1498 1568 RTMemFree(pReq); 1499 if (RT_SUCCESS(rc))1500 /* The other end may want to cache the data, so pretend we have new1501 * data, as we have no way of telling when new data really does1502 * arrive. */1503 clipReportFormatsToVBox(pCtx);1504 // else1505 // clipReportEmptyX11CB(pCtx);1506 1569 LogRelFlowFunc(("rc=%Rrc\n", rc)); 1507 1570 } … … 1586 1649 pX11Req->mReq = pReq; 1587 1650 /* We use this to schedule a worker function on the event thread. */ 1588 clipQueueToEventThread(pCtx ->appContext, vboxClipboardReadX11Worker,1651 clipQueueToEventThread(pCtx, vboxClipboardReadX11Worker, 1589 1652 (XtPointer) pX11Req); 1590 1653 } … … 1607 1670 #define TEST_WIDGET (Widget)0xffff 1608 1671 1609 /* Our X11 clipboard target poller */1610 static XtTimerCallbackProc g_pfnPoller = NULL;1611 /* User data for the poller function. */1612 static XtPointer g_pPollerData = NULL;1613 1614 /* For the testcase, we install the poller function in a global variable1615 * which is called when the testcase updates the X11 targets. */1616 void clipSchedulePoller(CLIPBACKEND *pCtx,1617 XtTimerCallbackProc proc)1618 {1619 g_pfnPoller = proc;1620 g_pPollerData = (XtPointer)pCtx;1621 }1622 1623 1672 /* For the purpose of the test case, we just execute the procedure to be 1624 1673 * scheduled, as we are running single threaded. */ 1625 void clipQueueToEventThread( XtAppContext app_context,1674 void clipQueueToEventThread(CLIPBACKEND *pCtx, 1626 1675 XtTimerCallbackProc proc, 1627 1676 XtPointer client_data) … … 2570 2619 if (!testStringFromVBoxFailed(pCtx, "UTF8_STRING")) 2571 2620 ++cErrs; 2621 rc = ClipStopX11(pCtx); 2622 AssertRCReturn(rc, 1); 2623 ClipDestructX11(pCtx); 2572 2624 2573 2625 if (cErrs > 0)
Note:
See TracChangeset
for help on using the changeset viewer.