Changeset 19152 in vbox for trunk/src/VBox
- Timestamp:
- Apr 23, 2009 9:23:45 PM (16 years ago)
- Location:
- trunk/src/VBox
- Files:
-
- 2 edited
Legend:
- Unmodified
- Added
- Removed
-
trunk/src/VBox/GuestHost/SharedClipboard/x11-clipboard.cpp
r19027 r19152 23 23 #define LOG_GROUP LOG_GROUP_SHARED_CLIPBOARD 24 24 25 #include <errno.h> 25 26 #include <vector> 27 28 #include <unistd.h> 26 29 27 30 #ifdef RT_OS_SOLARIS … … 103 106 std::vector<VBOXCLIPBOARDFORMAT> formatList; 104 107 105 /** Does VBox or X11 currently own the clipboard? */ 106 volatile enum g_eOwner eOwner; 108 /** Does VBox currently own the clipboard? If so, we don't need to poll 109 * X11 for supported formats. */ 110 bool fOwnsClipboard; 107 111 108 112 /** What is the best text format X11 has to offer? INVALID for none. */ … … 110 114 /** Atom corresponding to the X11 text format */ 111 115 Atom atomX11TextFormat; 112 /** What is the best bitmap format X11 has to offer? INVALID for none. */ 116 /** What is the best bitmap format X11 has to offer? INVALID for none. 117 */ 113 118 g_eClipboardFormats X11BitmapFormat; 114 119 /** Atom corresponding to the X11 Bitmap format */ … … 121 126 * to invalidate it. */ 122 127 bool notifyVBox; 123 124 /** Since the clipboard data moves asynchronously, we use an event 125 * semaphore to wait for it. When a function issues a request for 126 * clipboard data it must wait for this semaphore, which is triggered 127 * when the data arrives. */ 128 RTSEMEVENT waitForData; 128 /** Cache of the last unicode data that we received */ 129 void *pvUnicodeCache; 130 /** Size of the unicode data in the cache */ 131 uint32_t cbUnicodeCache; 132 /** When we wish the clipboard to exit, we have to wake up the event 133 * loop. We do this by writing into a pipe. This end of the pipe is 134 * the end that another thread can write to. */ 135 int wakeupPipeWrite; 136 /** The reader end of the pipe */ 137 int wakeupPipeRead; 129 138 }; 130 139 … … 248 257 * the X11 clipboard. 249 258 */ 250 static voidvboxClipboardGetUtf8FromX11(VBOXCLIPBOARDCONTEXTX11 *pCtx,251 252 253 259 static int vboxClipboardGetUtf8FromX11(VBOXCLIPBOARDCONTEXTX11 *pCtx, 260 XtPointer pValue, unsigned cbSrcLen, 261 void *pv, unsigned cb, 262 uint32_t *pcbActual) 254 263 { 255 264 size_t cwSrcLen; … … 267 276 XtFree(reinterpret_cast<char *>(pValue)); 268 277 RTUtf16Free(pu16SrcText); 269 RTSemEventSignal(pCtx->waitForData);270 LogFlowFunc(("Returning. Status is %Rrc\n", rc));278 LogFlowFunc(("Returning %Rrc\n", rc)); 279 return rc; 271 280 } 272 281 … … 285 294 * the X11 clipboard. 286 295 */ 287 static voidvboxClipboardGetCTextFromX11(VBOXCLIPBOARDCONTEXTX11 *pCtx,288 289 290 296 static int vboxClipboardGetCTextFromX11(VBOXCLIPBOARDCONTEXTX11 *pCtx, 297 XtPointer pValue, unsigned cbSrcLen, 298 void *pv, unsigned cb, 299 uint32_t *pcbActual) 291 300 { 292 301 size_t cwSrcLen; … … 335 344 XFreeStringList(ppu8SrcText); 336 345 RTUtf16Free(pu16SrcText); 337 LogFlowFunc(("Returning . Status is%Rrc\n", rc));338 RTSemEventSignal(pCtx->waitForData);346 LogFlowFunc(("Returning %Rrc\n", rc)); 347 return rc; 339 348 } 340 349 … … 353 362 * the X11 clipboard. 354 363 */ 355 static voidvboxClipboardGetLatin1FromX11(VBOXCLIPBOARDCONTEXTX11 *pCtx,356 357 358 364 static int vboxClipboardGetLatin1FromX11(VBOXCLIPBOARDCONTEXTX11 *pCtx, 365 XtPointer pValue, 366 unsigned cbSourceLen, void *pv, 367 unsigned cb, uint32_t *pcbActual) 359 368 { 360 369 unsigned cwDestLen = cbSourceLen + 1; … … 392 401 } 393 402 XtFree(reinterpret_cast<char *>(pValue)); 394 RTSemEventSignal(pCtx->waitForData);395 LogFlowFunc(("Returning. Status is %Rrc\n", rc));403 LogFlowFunc(("Returning %Rrc\n", rc)); 404 return rc; 396 405 } 397 406 … … 412 421 = reinterpret_cast<VBOXCLIPBOARDREQUEST *>(pClientData); 413 422 VBOXCLIPBOARDCONTEXTX11 *pCtx = pRequest->pCtx; 423 if (pCtx->fOwnsClipboard == true) 424 { 425 /* We don't want to request data from ourselves! */ 426 pRequest->rc = VERR_NO_DATA; 427 RTSemEventSignal(pRequest->finished); 428 return; 429 } 414 430 pRequest->rc = VINF_SUCCESS; 415 431 LogFlowFunc(("pClientData=%p, *pcLen=%lu, *piFormat=%d\n", pClientData, … … 422 438 { 423 439 pRequest->rc = VERR_TIMEOUT; 424 RTSemEventSignal(p Ctx->waitForData);440 RTSemEventSignal(pRequest->finished); 425 441 return; 426 442 } … … 429 445 { 430 446 pRequest->rc = VERR_NO_DATA; 431 RTSemEventSignal(p Ctx->waitForData);447 RTSemEventSignal(pRequest->finished); 432 448 return; 433 449 } … … 436 452 { 437 453 case CTEXT: 438 vboxClipboardGetCTextFromX11(pCtx, pValue, cTextLen, pRequest->pv, 439 pRequest->cb, pRequest->pcbActual); 454 pRequest->rc = vboxClipboardGetCTextFromX11(pCtx, pValue, cTextLen, 455 pRequest->pv, 456 pRequest->cb, 457 pRequest->pcbActual); 458 RTSemEventSignal(pRequest->finished); 440 459 break; 441 460 case UTF8: … … 448 467 && (RTStrUniLenEx(pu8SourceText, *pcLen, &cStringLen) == VINF_SUCCESS)) 449 468 { 450 vboxClipboardGetUtf8FromX11(pCtx, pValue, cTextLen, pRequest->pv, 451 pRequest->cb, pRequest->pcbActual); 469 pRequest->rc = vboxClipboardGetUtf8FromX11(pCtx, pValue, 470 cTextLen, 471 pRequest->pv, 472 pRequest->cb, 473 pRequest->pcbActual); 474 RTSemEventSignal(pRequest->finished); 452 475 break; 453 476 } 454 477 else 455 478 { 456 vboxClipboardGetLatin1FromX11(pCtx, pValue, cTextLen, 457 pRequest->pv, pRequest->cb, 458 pRequest->pcbActual); 479 pRequest->rc = vboxClipboardGetLatin1FromX11(pCtx, pValue, 480 cTextLen, 481 pRequest->pv, 482 pRequest->cb, 483 pRequest->pcbActual); 484 RTSemEventSignal(pRequest->finished); 459 485 break; 460 486 } … … 464 490 XtFree(reinterpret_cast<char *>(pValue)); 465 491 pRequest->rc = VERR_INVALID_PARAMETER; 466 RTSemEventSignal(p Ctx->waitForData);492 RTSemEventSignal(pRequest->finished); 467 493 return; 468 494 } … … 493 519 494 520 Log3 (("%s: called\n", __PRETTY_FUNCTION__)); 495 if (*atomType == XT_CONVERT_FAIL) /* timeout */ 496 { 497 LogFunc (("reading clipboard from host, X toolkit failed to convert the selection\n")); 521 if ( (*atomType == XT_CONVERT_FAIL) /* timeout */ 522 || (pCtx->fOwnsClipboard == true) /* VBox currently owns the 523 * clipboard */ 524 ) 525 { 498 526 pCtx->atomX11TextFormat = None; 499 527 pCtx->X11TextFormat = INVALID; … … 576 604 reinterpret_cast<VBOXCLIPBOARDCONTEXTX11 *>(pUserData); 577 605 Log3 (("%s: called\n", __PRETTY_FUNCTION__)); 578 /* Get the current clipboard contents */579 if ( (pCtx->eOwner != VB))606 /* Get the current clipboard contents if we don't own it ourselves */ 607 if (!pCtx->fOwnsClipboard) 580 608 { 581 609 Log3 (("%s: requesting the targets that the host clipboard offers\n", … … 642 670 vboxClipboardRemoveContext(pCtx); 643 671 XtDestroyWidget(pCtx->widget); 644 pCtx->widget = NULL;645 }672 } 673 pCtx->widget = NULL; 646 674 if (pCtx->appContext) 647 {648 675 XtDestroyApplicationContext(pCtx->appContext); 649 pCtx->appContext = NULL; 650 } 676 pCtx->appContext = NULL; 677 if (pCtx->wakeupPipeRead != 0) 678 close(pCtx->wakeupPipeRead); 679 if (pCtx->wakeupPipeWrite != 0) 680 close(pCtx->wakeupPipeWrite); 681 pCtx->wakeupPipeRead = 0; 682 pCtx->wakeupPipeWrite = 0; 683 } 684 685 /** Worker function for stopping the clipboard which runs on the event 686 * thread. */ 687 static void vboxClipboardStopWorker(XtPointer pUserData, int * /* source */, 688 XtInputId * /* id */) 689 { 690 691 VBOXCLIPBOARDCONTEXTX11 *pCtx = (VBOXCLIPBOARDCONTEXTX11 *)pUserData; 692 693 /* This might mean that we are getting stopped twice. */ 694 Assert(pCtx->widget != NULL); 695 696 /* Set the termination flag to tell the Xt event loop to exit. We 697 * reiterate that any outstanding requests from the X11 event loop to 698 * the VBox part *must* have returned before we do this. */ 699 XtAppSetExitFlag(pCtx->appContext); 700 pCtx->fOwnsClipboard = false; 701 pCtx->X11TextFormat = INVALID; 702 pCtx->X11BitmapFormat = INVALID; 651 703 } 652 704 … … 721 773 VBOX_SHARED_CLIPBOARD_FMT_UNICODETEXT); 722 774 } 775 /* Create the pipes */ 776 int pipes[2]; 777 if (!pipe(pipes)) 778 { 779 pCtx->wakeupPipeRead = pipes[0]; 780 pCtx->wakeupPipeWrite = pipes[1]; 781 XtAppAddInput(pCtx->appContext, pCtx->wakeupPipeRead, 782 (XtPointer) XtInputReadMask, vboxClipboardStopWorker, 783 (XtPointer) pCtx); 784 } 785 else 786 rc = RTErrConvertFromErrno(errno); 723 787 if (RT_FAILURE(rc)) 724 788 vboxClipboardUninitX11(pCtx); … … 770 834 LogRel(("Initializing X11 clipboard backend\n")); 771 835 if (pCtx) 772 {773 836 pCtx->pFrontend = pFrontend; 774 RTSemEventCreate(&pCtx->waitForData);775 }776 837 return pCtx; 777 838 } … … 793 854 * memory. */ 794 855 Assert(pCtx->widget == NULL); 795 RTSemEventDestroy(pCtx->waitForData);796 856 } 797 857 798 858 /** 799 859 * Announce to the X11 backend that we are ready to start. 800 * @param owner who is the initial clipboard owner 801 */ 802 int VBoxX11ClipboardStartX11(VBOXCLIPBOARDCONTEXTX11 *pCtx, 803 bool fOwnsClipboard) 860 */ 861 int VBoxX11ClipboardStartX11(VBOXCLIPBOARDCONTEXTX11 *pCtx) 804 862 { 805 863 int rc = VINF_SUCCESS; … … 821 879 if (RT_SUCCESS(rc)) 822 880 { 823 if (fOwnsClipboard) 824 { 825 pCtx->eOwner = X11; 826 pCtx->notifyVBox = true; 827 } 828 else 829 { 830 /** @todo Check whether the guest gets a format announcement at 831 * startup. */ 832 pCtx->eOwner = NONE; 833 VBoxX11ClipboardAnnounceVBoxFormat(pCtx, 0); 834 } 881 pCtx->fOwnsClipboard = false; 882 pCtx->notifyVBox = true; 835 883 } 836 884 return rc; 837 885 } 838 886 839 /** 840 * Called when the VBox may have fallen out of sync with the backend. 841 * @note X11 backend code 842 */ 843 void VBoxX11ClipboardRequestSyncX11(VBOXCLIPBOARDCONTEXTX11 *pCtx) 844 { 845 /* 846 * Immediately return if we are not connected to the host X server. 847 */ 848 if (!g_fHaveX11) 849 return; 850 pCtx->notifyVBox = true; 851 } 887 /** String written to the wakeup pipe. */ 888 #define WAKE_UP_STRING "WakeUp!" 889 /** Length of the string written. */ 890 #define WAKE_UP_STRING_LEN ( sizeof(WAKE_UP_STRING) - 1 ) 852 891 853 892 /** … … 863 902 int rc, rcThread; 864 903 unsigned count = 0; 865 XEvent ev;866 904 /* 867 905 * Immediately return if we are not connected to the host X server. … … 870 908 return VINF_SUCCESS; 871 909 872 /* This might mean that we are getting stopped twice. */873 AssertReturn(pCtx->widget != NULL, VERR_WRONG_ORDER);874 910 LogRelFunc(("stopping the shared clipboard X11 backend\n")); 875 876 /* Set the termination flag to tell the Xt event loop to exit. We 877 * reiterate that any outstanding requests from the X11 event loop to 878 * the VBox part *must* have returned before we do this. */ 879 XtAppSetExitFlag(pCtx->appContext); 880 /* Wake up the event loop */ 881 memset(&ev, 0, sizeof(ev)); 882 ev.xclient.type = ClientMessage; 883 ev.xclient.format = 8; 884 XSendEvent(XtDisplay(pCtx->widget), XtWindow(pCtx->widget), false, 0, &ev); 885 XFlush(XtDisplay(pCtx->widget)); 886 pCtx->eOwner = NONE; 887 pCtx->X11TextFormat = INVALID; 888 pCtx->X11BitmapFormat = INVALID; 911 /* Write to the "stop" pipe */ 912 rc = write(pCtx->wakeupPipeWrite, WAKE_UP_STRING, WAKE_UP_STRING_LEN); 889 913 do 890 914 { … … 965 989 *piFormatReturn = 32; 966 990 return true; 991 } 992 993 /** This is a wrapper around VBoxX11ClipboardReadVBoxData that will cache the 994 * data returned. This is unfortunately necessary, because if the other side 995 * of the shared clipboard is also an X11 system, it may send a format 996 * announcement message every time its clipboard is read, for reasons that 997 * are explained elsewhere. Unfortunately, some applications on our side 998 * like to read the clipboard several times in short succession in different 999 * formats. This can fail if it collides with a format announcement message. 1000 * @todo any ideas about how to do this better are welcome. 1001 */ 1002 static int vboxClipboardReadVBoxData (VBOXCLIPBOARDCONTEXTX11 *pCtx, 1003 uint32_t u32Format, void **ppv, 1004 uint32_t *pcb) 1005 { 1006 int rc = VINF_SUCCESS; 1007 LogFlowFunc(("pCtx=%p, u32Format=%02X, ppv=%p, pcb=%p\n", pCtx, 1008 u32Format, ppv, pcb)); 1009 if (u32Format == VBOX_SHARED_CLIPBOARD_FMT_UNICODETEXT) 1010 { 1011 if (pCtx->pvUnicodeCache == NULL) 1012 rc = VBoxX11ClipboardReadVBoxData(pCtx->pFrontend, u32Format, 1013 &pCtx->pvUnicodeCache, 1014 &pCtx->cbUnicodeCache); 1015 if (RT_SUCCESS(rc)) 1016 { 1017 *ppv = RTMemDup(pCtx->pvUnicodeCache, pCtx->cbUnicodeCache); 1018 *pcb = pCtx->cbUnicodeCache; 1019 if (*ppv == NULL) 1020 rc = VERR_NO_MEMORY; 1021 } 1022 } 1023 else 1024 rc = VBoxX11ClipboardReadVBoxData(pCtx->pFrontend, u32Format, 1025 ppv, pcb); 1026 LogFlowFunc(("returning %Rrc\n", rc)); 1027 if (RT_SUCCESS(rc)) 1028 LogFlowFunc(("*ppv=%.*ls, *pcb=%u\n", *pcb, *ppv, *pcb)); 1029 return rc; 967 1030 } 968 1031 … … 1004 1067 LogFlowFunc (("called\n")); 1005 1068 /* Read the clipboard data from the guest. */ 1006 rc = VBoxX11ClipboardReadVBoxData(pCtx->pFrontend, VBOX_SHARED_CLIPBOARD_FMT_UNICODETEXT, &pvVBox, &cbVBox); 1069 rc = vboxClipboardReadVBoxData(pCtx, 1070 VBOX_SHARED_CLIPBOARD_FMT_UNICODETEXT, 1071 &pvVBox, &cbVBox); 1007 1072 if ((rc != VINF_SUCCESS) || (cbVBox == 0)) 1008 1073 { 1009 /* If VBoxX11ClipboardReadVBoxData fails then pClient may be invalid*/1010 LogRelFunc ((" VBoxX11ClipboardReadVBoxData returned %Rrc%s\n", rc,1074 /* If vboxClipboardReadVBoxData fails then we may be terminating */ 1075 LogRelFunc (("vboxClipboardReadVBoxData returned %Rrc%s\n", rc, 1011 1076 RT_SUCCESS(rc) ? ", cbVBox == 0" : "")); 1012 1077 RTMemFree(pvVBox); … … 1113 1178 LogFlowFunc (("called\n")); 1114 1179 /* Read the clipboard data from the guest. */ 1115 rc = VBoxX11ClipboardReadVBoxData(pCtx->pFrontend, VBOX_SHARED_CLIPBOARD_FMT_UNICODETEXT, &pvVBox, &cbVBox); 1180 rc = vboxClipboardReadVBoxData(pCtx, 1181 VBOX_SHARED_CLIPBOARD_FMT_UNICODETEXT, 1182 &pvVBox, &cbVBox); 1116 1183 if ((rc != VINF_SUCCESS) || (cbVBox == 0)) 1117 1184 { 1118 /* If VBoxX11ClipboardReadVBoxData fails then pClient may be invalid*/1119 LogRelFunc ((" VBoxX11ClipboardReadVBoxData returned %Rrc%s\n", rc,1185 /* If vboxClipboardReadVBoxData fails then we may be terminating */ 1186 LogRelFunc (("vboxClipboardReadVBoxData returned %Rrc%s\n", rc, 1120 1187 RT_SUCCESS(rc) ? ", cbVBox == 0" : "")); 1121 1188 RTMemFree(pvVBox); … … 1219 1286 LogFlowFunc(("\n")); 1220 1287 /* Drop requests that we receive too late. */ 1221 if ( pCtx->eOwner != VB)1288 if (!pCtx->fOwnsClipboard) 1222 1289 return false; 1223 1290 if ( (*atomSelection != pCtx->atomClipboard) … … 1285 1352 { 1286 1353 VBOXCLIPBOARDCONTEXTX11 *pCtx = vboxClipboardFindContext(widget); 1287 LogFlowFunc (("called, giving VBox clipboard ownership\n")); 1288 pCtx->eOwner = X11; 1354 LogFlowFunc (("called, giving X11 clipboard ownership\n")); 1355 /* These should be set to the right values as soon as we start polling */ 1356 pCtx->X11TextFormat = INVALID; 1357 pCtx->X11BitmapFormat = INVALID; 1358 pCtx->fOwnsClipboard = false; 1289 1359 pCtx->notifyVBox = true; 1360 } 1361 1362 /** Structure used to pass information about formats that VBox supports */ 1363 typedef struct _VBOXCLIPBOARDFORMATS 1364 { 1365 /** Context information for the X11 clipboard */ 1366 VBOXCLIPBOARDCONTEXTX11 *pCtx; 1367 /** Formats supported by VBox */ 1368 uint32_t formats; 1369 } VBOXCLIPBOARDFORMATS; 1370 1371 /** Worker function for VBoxX11ClipboardAnnounceVBoxFormat which runs on the 1372 * event thread. */ 1373 static void vboxClipboardAnnounceWorker(XtPointer pUserData, 1374 XtIntervalId * /* interval */) 1375 { 1376 /* Extract and free the user data */ 1377 VBOXCLIPBOARDFORMATS *pFormats = (VBOXCLIPBOARDFORMATS *)pUserData; 1378 VBOXCLIPBOARDCONTEXTX11 *pCtx = pFormats->pCtx; 1379 uint32_t u32Formats = pFormats->formats; 1380 RTMemFree(pFormats); 1381 LogFlowFunc (("u32Formats=%d\n", u32Formats)); 1382 pCtx->vboxFormats = u32Formats; 1383 /* Invalidate the clipboard cache */ 1384 if (pCtx->pvUnicodeCache != NULL) 1385 { 1386 RTMemFree(pCtx->pvUnicodeCache); 1387 pCtx->pvUnicodeCache = NULL; 1388 } 1389 if (u32Formats == 0) 1390 { 1391 /* This is just an automatism, not a genuine anouncement */ 1392 XtDisownSelection(pCtx->widget, pCtx->atomClipboard, CurrentTime); 1393 pCtx->fOwnsClipboard = false; 1394 LogFlowFunc(("returning\n")); 1395 return; 1396 } 1397 Log2 (("%s: giving the guest clipboard ownership\n", __PRETTY_FUNCTION__)); 1398 if (XtOwnSelection(pCtx->widget, pCtx->atomClipboard, CurrentTime, 1399 vboxClipboardConvertForX11, vboxClipboardReturnToX11, 1400 0) == True) 1401 { 1402 pCtx->fOwnsClipboard = true; 1403 /* Grab the middle-button paste selection too. */ 1404 XtOwnSelection(pCtx->widget, pCtx->atomPrimary, CurrentTime, 1405 vboxClipboardConvertForX11, NULL, 0); 1406 } 1407 else 1408 { 1409 /* Another X11 client claimed the clipboard just after us, so let it 1410 * go again. */ 1411 Log2 (("%s: returning clipboard ownership to the X11\n", 1412 __PRETTY_FUNCTION__)); 1413 /* VBox thinks it currently owns the clipboard, so we must notify it 1414 * as soon as we know what formats X11 has to offer. */ 1415 pCtx->notifyVBox = true; 1416 pCtx->fOwnsClipboard = false; 1417 } 1418 LogFlowFunc(("returning\n")); 1290 1419 } 1291 1420 … … 1304 1433 if (!g_fHaveX11) 1305 1434 return; 1306 1307 LogFlowFunc (("u32Formats=%d\n", u32Formats)); 1308 pCtx->vboxFormats = u32Formats; 1309 if (u32Formats == 0) 1310 { 1311 /* This is just an automatism, not a genuine anouncement */ 1312 XtDisownSelection(pCtx->widget, pCtx->atomClipboard, CurrentTime); 1313 pCtx->eOwner = NONE; 1314 LogFlowFunc(("returning\n")); 1315 return; 1316 } 1317 Log2 (("%s: giving the guest clipboard ownership\n", __PRETTY_FUNCTION__)); 1318 pCtx->eOwner = VB; 1319 pCtx->X11TextFormat = INVALID; 1320 pCtx->X11BitmapFormat = INVALID; 1321 if (XtOwnSelection(pCtx->widget, pCtx->atomClipboard, CurrentTime, 1322 vboxClipboardConvertForX11, vboxClipboardReturnToX11, 1323 0) != True) 1324 { 1325 Log2 (("%s: returning clipboard ownership to the host\n", __PRETTY_FUNCTION__)); 1326 /* We set this so that the guest gets notified when we take the clipboard, even if no 1327 guest formats are found which we understand. */ 1328 pCtx->notifyVBox = true; 1329 pCtx->eOwner = X11; 1330 } 1331 XtOwnSelection(pCtx->widget, pCtx->atomPrimary, CurrentTime, vboxClipboardConvertForX11, 1332 NULL, 0); 1333 LogFlowFunc(("returning\n")); 1334 1435 /* This must be freed by the worker callback */ 1436 VBOXCLIPBOARDFORMATS *pFormats = 1437 (VBOXCLIPBOARDFORMATS *) RTMemAlloc(sizeof(VBOXCLIPBOARDFORMATS)); 1438 if (pFormats != NULL) /* if it is we will soon have other problems */ 1439 { 1440 pFormats->pCtx = pCtx; 1441 pFormats->formats = u32Formats; 1442 XtAppAddTimeOut(pCtx->appContext, 0, vboxClipboardAnnounceWorker, 1443 (XtPointer) pFormats); 1444 } 1445 } 1446 1447 /** Worker function for VBoxX11ClipboardReadX11Data which runs on the event 1448 * thread. */ 1449 static void vboxClipboardReadX11Worker(XtPointer pUserData, 1450 XtIntervalId * /* interval */) 1451 { 1452 VBOXCLIPBOARDREQUEST *pRequest = (VBOXCLIPBOARDREQUEST *)pUserData; 1453 VBOXCLIPBOARDCONTEXTX11 *pCtx = pRequest->pCtx; 1454 LogFlowFunc (("u32Format = %d, cb = %d\n", pRequest->format, 1455 pRequest->cb)); 1456 1457 int rc = VINF_SUCCESS; 1458 /* Set this to start with, just in case */ 1459 *pRequest->pcbActual = 0; 1460 /* Do not continue if we already own the clipboard */ 1461 if (pCtx->fOwnsClipboard == true) 1462 rc = VERR_NO_DATA; /** @todo should this be VINF? */ 1463 else 1464 { 1465 /* 1466 * VBox wants to read data in the given format. 1467 */ 1468 if (pRequest->format == VBOX_SHARED_CLIPBOARD_FMT_UNICODETEXT) 1469 { 1470 if (pCtx->atomX11TextFormat == None) 1471 /* VBox thinks we have data and we don't */ 1472 rc = VERR_NO_DATA; 1473 else 1474 /* Send out a request for the data to the current clipboard 1475 * owner */ 1476 XtGetSelectionValue(pCtx->widget, pCtx->atomClipboard, 1477 pCtx->atomX11TextFormat, 1478 vboxClipboardGetDataFromX11, 1479 reinterpret_cast<XtPointer>(pRequest), 1480 CurrentTime); 1481 } 1482 else 1483 rc = VERR_NOT_IMPLEMENTED; 1484 } 1485 if (RT_FAILURE(rc)) 1486 { 1487 pRequest->rc = rc; 1488 /* The clipboard callback was never scheduled, so we must signal 1489 * that the request processing is finished ourselves. */ 1490 RTSemEventSignal(pRequest->finished); 1491 } 1492 LogFlowFunc(("status %Rrc\n", rc)); 1335 1493 } 1336 1494 … … 1346 1504 */ 1347 1505 int VBoxX11ClipboardReadX11Data(VBOXCLIPBOARDCONTEXTX11 *pCtx, 1348 uint32_t u32Format, 1349 VBOXCLIPBOARDREQUEST *pRequest) 1350 { 1506 uint32_t u32Format, void *pv, uint32_t cb, 1507 uint32_t *pcbActual) 1508 { 1509 /* Initially set the size of the data read to zero in case we fail 1510 * somewhere or X11 is not available. */ 1511 *pcbActual = 0; 1512 1351 1513 /* 1352 1514 * Immediately return if we are not connected to the host X server. 1353 1515 */ 1354 1516 if (!g_fHaveX11) 1355 {1356 /* no data available */1357 *pRequest->pcbActual = 0;1358 pRequest->rc = VINF_SUCCESS;1359 1517 return VINF_SUCCESS; 1360 } 1361 1362 LogFlowFunc (("u32Format = %d, cb = %d\n", u32Format, pRequest->cb)); 1363 pRequest->rc = VERR_WRONG_ORDER; 1364 1365 /* 1366 * VBox wants to read data in the given format. 1367 */ 1368 if (u32Format == VBOX_SHARED_CLIPBOARD_FMT_UNICODETEXT) 1369 { 1370 if (pCtx->X11TextFormat == INVALID) 1371 { 1372 /* No data available. */ 1373 *pRequest->pcbActual = 0; 1374 return VERR_NO_DATA; /* VBox thinks we have data and we don't */ 1375 } 1376 /* Initially set the size of the data read to zero in case we fail 1377 * somewhere. */ 1378 *pRequest->pcbActual = 0; 1379 /* Send out a request for the data to the current clipboard owner */ 1380 XtGetSelectionValue(pCtx->widget, pCtx->atomClipboard, 1381 pCtx->atomX11TextFormat, 1382 vboxClipboardGetDataFromX11, 1383 reinterpret_cast<XtPointer>(pRequest), 1384 CurrentTime); 1385 /* When the data arrives, the vboxClipboardGetDataFromX11 callback 1386 * will be called. The callback will signal the event semaphore when 1387 * it has processed the data for us. */ 1388 1389 int rc = RTSemEventWait(pCtx->waitForData, RT_INDEFINITE_WAIT); 1390 if (RT_FAILURE(rc)) 1391 return rc; 1392 } 1393 else 1394 { 1395 return VERR_NOT_IMPLEMENTED; 1396 } 1397 LogFlowFunc(("returning %Rrc\n", pRequest->rc)); 1398 return pRequest->rc; 1518 1519 VBOXCLIPBOARDREQUEST request; 1520 request.rc = VERR_WRONG_ORDER; 1521 request.pv = pv; 1522 request.cb = cb; 1523 request.pcbActual = pcbActual; 1524 request.format = u32Format; 1525 request.pCtx = pCtx; 1526 /* The worker function will signal this when it has finished. */ 1527 int rc = RTSemEventCreate(&request.finished); 1528 if (RT_SUCCESS(rc)) 1529 { 1530 /* We use this to schedule a worker function on the event thread. */ 1531 XtAppAddTimeOut(pCtx->appContext, 0, vboxClipboardReadX11Worker, 1532 (XtPointer) &request); 1533 rc = RTSemEventWait(request.finished, RT_INDEFINITE_WAIT); 1534 if (RT_SUCCESS(rc)) 1535 rc = request.rc; 1536 RTSemEventDestroy(request.finished); 1537 } 1538 return rc; 1399 1539 } 1400 1540 … … 1529 1669 char pc[256]; 1530 1670 uint32_t cbActual; 1531 VBOXCLIPBOARDREQUEST request = {(void *) pc, sizeof(pc),1532 &cbActual, VINF_SUCCESS, g_pCtxTestX11};1533 1671 RTPrintf("tstClipboardX11: TESTING a request for an invalid data format\n"); 1534 int rc = VBoxX11ClipboardReadX11Data(g_pCtxTestX11, 1535 0xffff, &request);1672 int rc = VBoxX11ClipboardReadX11Data(g_pCtxTestX11, 0xffff, (void *) pc, 1673 sizeof(pc), &cbActual); 1536 1674 RTPrintf("Returned %Rrc - %s\n", rc, 1537 1675 rc == VERR_NOT_IMPLEMENTED ? "SUCCESS" : "FAILURE"); … … 1539 1677 fSuccess = false; 1540 1678 RTPrintf("tstClipboardX11: TESTING a request for data from an empty clipboard\n"); 1679 VBoxX11ClipboardAnnounceVBoxFormat(g_pCtxTestX11, 1680 VBOX_SHARED_CLIPBOARD_FMT_UNICODETEXT); 1681 VBoxX11ClipboardAnnounceVBoxFormat(g_pCtxTestX11, 1682 0); 1541 1683 rc = VBoxX11ClipboardReadX11Data(g_pCtxTestX11, 1542 VBOX_SHARED_CLIPBOARD_FMT_UNICODETEXT, &request); 1684 VBOX_SHARED_CLIPBOARD_FMT_UNICODETEXT, 1685 (void *) pc, sizeof(pc), &cbActual); 1543 1686 RTPrintf("Returned %Rrc - %s\n", rc, 1544 1687 rc == VERR_NO_DATA ? "SUCCESS" : "FAILURE"); … … 1557 1700 char pc[256]; 1558 1701 uint32_t cbActual; 1559 VBOXCLIPBOARDREQUEST request = {(void *) pc, pData->cchBuffer,1560 &cbActual, VINF_SUCCESS, g_pCtxTestX11};1561 1702 for (int i = 0; (i < MAX_ATTEMPTS) && !fSuccess; ++i) 1562 1703 { 1704 VBoxX11ClipboardAnnounceVBoxFormat(g_pCtxTestVBox, 1705 VBOX_SHARED_CLIPBOARD_FMT_UNICODETEXT); 1563 1706 int rc = VBoxX11ClipboardReadX11Data(g_pCtxTestX11, 1564 VBOX_SHARED_CLIPBOARD_FMT_UNICODETEXT, &request); 1565 AssertReturn(RT_SUCCESS(rc), 1); 1707 VBOX_SHARED_CLIPBOARD_FMT_UNICODETEXT, 1708 (void *) pc, sizeof(pc), 1709 &cbActual); 1710 AssertRCReturn(rc, 1); 1566 1711 /* Did we expect and get an overflow? */ 1567 1712 if ( RT_SUCCESS(rc) … … 1596 1741 int rc = VINF_SUCCESS; 1597 1742 int status = 0; 1598 /* We can't reasonably test anything without an X session, so just1599 * continue. */1743 /* We can't test anything without an X session, so just return success 1744 * in that case. */ 1600 1745 if (!RTEnvGet("DISPLAY")) 1601 1746 return 0; 1602 g_debugClipboard = true;1603 1747 RTR3Init(); 1604 1748 g_pCtxTestX11 = VBoxX11ClipboardConstructX11(NULL); 1605 1749 g_pCtxTestVBox = 1606 1750 VBoxX11ClipboardConstructX11((VBOXCLIPBOARDCONTEXT *)g_pCtxTestX11); 1607 rc = VBoxX11ClipboardStartX11(g_pCtxTestVBox , false);1751 rc = VBoxX11ClipboardStartX11(g_pCtxTestVBox); 1608 1752 AssertRCReturn(rc, 1); 1609 1753 bool fSuccess = false; … … 1612 1756 for (unsigned i = 0; (i < MAX_ATTEMPTS) && !fSuccess; ++i) 1613 1757 { 1614 rc = VBoxX11ClipboardStartX11(g_pCtxTestX11 , false);1758 rc = VBoxX11ClipboardStartX11(g_pCtxTestX11); 1615 1759 AssertRCReturn(rc, 1); 1616 1760 fSuccess = testInvalid(); … … 1624 1768 if (!fSuccess) 1625 1769 status = 1; 1626 rc = VBoxX11ClipboardStartX11(g_pCtxTestX11 , true);1770 rc = VBoxX11ClipboardStartX11(g_pCtxTestX11); 1627 1771 AssertRCReturn(rc, 1); 1628 1772 /* Claim the clipboard and make sure we get it */ … … 1657 1801 char pc[256]; 1658 1802 uint32_t cbActual; 1659 VBOXCLIPBOARDREQUEST request = {(void *) pc, sizeof(pc), 1660 &cbActual, VINF_SUCCESS, 1661 g_pCtxTestX11}; 1803 VBoxX11ClipboardAnnounceVBoxFormat(g_pCtxTestVBox, 1804 VBOX_SHARED_CLIPBOARD_FMT_UNICODETEXT); 1662 1805 rc = VBoxX11ClipboardReadX11Data(g_pCtxTestX11, 1663 VBOX_SHARED_CLIPBOARD_FMT_UNICODETEXT, &request); 1806 VBOX_SHARED_CLIPBOARD_FMT_UNICODETEXT, 1807 (void *) pc, sizeof(pc), &cbActual); 1664 1808 AssertMsg( RT_SUCCESS(rc) 1665 1809 || (rc == VERR_TIMEOUT) -
trunk/src/VBox/HostServices/SharedClipboard/x11-clipboard.cpp
r18886 r19152 21 21 */ 22 22 23 #define LOG_GROUP LOG_GROUP_SHARED_CLIPBOARD 24 23 25 #include <string.h> 24 26 25 #include <iprt/asm.h> /* For atomic operations */26 27 #include <iprt/assert.h> 28 #include <iprt/critsect.h> 27 29 #include <iprt/mem.h> 28 30 #include <iprt/semaphore.h> … … 32 34 33 35 #include "VBoxClipboard.h" 36 37 /** A request for clipboard data from VBox */ 38 typedef struct _VBOXCLIPBOARDREQFROMVBOX 39 { 40 /** Data received */ 41 void *pv; 42 /** The size of the data */ 43 uint32_t cb; 44 /** Format of the data */ 45 uint32_t format; 46 /** A semaphore for waiting for the data */ 47 RTSEMEVENT finished; 48 } VBOXCLIPBOARDREQFROMVBOX; 34 49 35 50 /** Global context information used by the host glue for the X11 clipboard … … 37 52 struct _VBOXCLIPBOARDCONTEXT 38 53 { 39 /** Since the clipboard data moves asynchronously, we use an event40 * semaphore to wait for it. When a function issues a request for41 * clipboard data it must wait for this semaphore, which is triggered42 * when the data arrives. */43 RTSEMEVENT waitForData;44 /** Who (if anyone) is currently waiting for data? Used for sanity45 * checks when data arrives. */46 volatile uint32_t waiter;47 54 /** This mutex is grabbed during any critical operations on the clipboard 48 55 * which might clash with others. */ 49 RTSEMMUTEX clipboardMutex; 50 56 RTCRITSECT clipboardMutex; 57 /** The currently pending request for data from VBox. NULL if there is 58 * no request pending. The protocol for completing a request is to grab 59 * the critical section, check that @a pReq is not NULL, fill in the data 60 * fields and set @a pReq to NULL. The protocol for cancelling a pending 61 * request is to grab the critical section and set pReq to NULL. 62 * It is an error if a request arrives while another one is pending, and 63 * the backend is responsible for ensuring that this does not happen. */ 64 VBOXCLIPBOARDREQFROMVBOX *pReq; 65 51 66 /** Pointer to the opaque X11 backend structure */ 52 67 VBOXCLIPBOARDCONTEXTX11 *pBackend; 53 /** Pointer to the client data structure*/68 /** Pointer to the VBox host client data structure. */ 54 69 VBOXCLIPBOARDCLIENTDATA *pClient; 70 /** We set this when we start shutting down as a hint not to post any new 71 * requests. */ 72 bool fShuttingDown; 55 73 }; 56 74 57 /* Only one client is supported. There seems to be no need for more clients. 58 */ 59 static VBOXCLIPBOARDCONTEXT g_ctxHost; 75 static int vboxClipboardWaitForReq(VBOXCLIPBOARDCONTEXT *pCtx, 76 VBOXCLIPBOARDREQFROMVBOX *pReq, 77 uint32_t u32Format) 78 { 79 int rc = VINF_SUCCESS; 80 LogFlowFunc(("pCtx=%p, pReq=%p, u32Format=%02X\n", pCtx, pReq, u32Format)); 81 /* Request data from VBox */ 82 vboxSvcClipboardReportMsg(pCtx->pClient, 83 VBOX_SHARED_CLIPBOARD_HOST_MSG_READ_DATA, 84 u32Format); 85 /* Which will signal us when it is ready. We use a timeout here 86 * because we can't be sure that the guest will behave correctly. 87 */ 88 rc = RTSemEventWait(pReq->finished, CLIPBOARD_TIMEOUT); 89 /* If the request hasn't yet completed then we cancel it. We use 90 * the critical section to prevent these operations colliding. */ 91 RTCritSectEnter(&pCtx->clipboardMutex); 92 /* The data may have arrived between the semaphore timing out and 93 * our grabbing the mutex. */ 94 if (rc == VERR_TIMEOUT && pReq->pv != NULL) 95 rc = VINF_SUCCESS; 96 if (pCtx->pReq == pReq) 97 pCtx->pReq = NULL; 98 Assert(pCtx->pReq == NULL); 99 RTCritSectLeave(&pCtx->clipboardMutex); 100 LogFlowFunc(("returning %Rrc\n", rc)); 101 return rc; 102 } 103 104 /** Post a request for clipboard data to VBox/the guest and wait for it to be 105 * completed. */ 106 static int vboxClipboardPostAndWaitForReq(VBOXCLIPBOARDCONTEXT *pCtx, 107 VBOXCLIPBOARDREQFROMVBOX *pReq, 108 uint32_t u32Format) 109 { 110 int rc = VINF_SUCCESS; 111 LogFlowFunc(("pCtx=%p, pReq=%p, u32Format=%02X\n", pCtx, pReq, 112 u32Format)); 113 /* Start by "posting" the request for the next invocation of 114 * vboxClipboardWriteData. */ 115 RTCritSectEnter(&pCtx->clipboardMutex); 116 if (pCtx->pReq != NULL) 117 { 118 /* This would be a violation of the protocol, see the comments in the 119 * context structure definition. */ 120 Assert(false); 121 rc = VERR_WRONG_ORDER; 122 } 123 else 124 pCtx->pReq = pReq; 125 RTCritSectLeave(&pCtx->clipboardMutex); 126 if (RT_SUCCESS(rc)) 127 rc = vboxClipboardWaitForReq(pCtx, pReq, u32Format); 128 LogFlowFunc(("returning %Rrc\n", rc)); 129 return rc; 130 } 60 131 61 132 /** … … 74 145 uint32_t *pcb) 75 146 { 76 volatile VBOXCLIPBOARDCLIENTDATA *pClient = pCtx->pClient; 77 78 LogFlowFunc(("u32Format=%02X\n", u32Format)); 79 /* Zero our output parameters */ 80 *ppv = NULL; 81 *pcb = 0; 82 if (pClient == NULL) 83 { 84 /* This can legitimately happen if we disconnect during a request for 85 * data from X11. */ 147 VBOXCLIPBOARDREQFROMVBOX request = { NULL }; 148 149 LogFlowFunc(("pCtx=%p, u32Format=%02X, ppv=%p, pcb=%p\n", pCtx, 150 u32Format, ppv, pcb)); 151 if (pCtx->fShuttingDown) 152 { 153 /* The shared clipboard is disconnecting. */ 86 154 LogFunc(("host requested guest clipboard data after guest had disconnected.\n")); 87 VBoxX11ClipboardAnnounceVBoxFormat(pCtx->pBackend, 0); 88 pCtx->waiter = NONE; 89 return VERR_TIMEOUT; 90 } 91 /* Assert that no other transfer is in process (requests are serialised) 92 * and that the last transfer cleaned up properly. */ 93 AssertLogRelReturn( pClient->data.pv == NULL 94 && pClient->data.cb == 0 95 && pClient->data.u32Format == 0, 96 VERR_WRONG_ORDER 97 ); 98 /* No one else (X11 or VBox) should currently be waiting. The first because 99 * requests from X11 are serialised and the second because VBox previously 100 * grabbed the clipboard, so it should not be waiting for data from us. */ 101 AssertLogRelReturn (ASMAtomicCmpXchgU32(&pCtx->waiter, X11, NONE), VERR_DEADLOCK); 102 /* Request data from VBox */ 103 vboxSvcClipboardReportMsg(pCtx->pClient, 104 VBOX_SHARED_CLIPBOARD_HOST_MSG_READ_DATA, 105 u32Format); 106 /* Which will signal us when it is ready. We use a timeout here because 107 * we can't be sure that the guest will behave correctly. */ 108 int rc = RTSemEventWait(pCtx->waitForData, CLIPBOARDTIMEOUT); 109 if (rc == VERR_TIMEOUT) 110 rc = VINF_SUCCESS; /* Timeout handling follows. */ 111 /* Now we have a potential race between the HGCM thread delivering the data 112 * and our setting waiter to NONE to say that we are no longer waiting for 113 * it. We solve this as follows: both of these operations are done under 114 * the clipboard mutex. The HGCM thread will only deliver the data if we 115 * are still waiting after it acquires the mutex. After we release the 116 * mutex, we finally do our check to see whether the data was delivered. */ 117 RTSemMutexRequest(g_ctxHost.clipboardMutex, RT_INDEFINITE_WAIT); 118 pCtx->waiter = NONE; 119 RTSemMutexRelease(g_ctxHost.clipboardMutex); 120 AssertLogRelRCSuccess(rc); 121 if (RT_FAILURE(rc)) 122 { 123 /* I believe this should not happen. Wait until the assertions arrive 124 * to prove the contrary. */ 125 RTMemFree(pClient->data.pv); 126 g_ctxHost.pClient->data.pv = 0; 127 g_ctxHost.pClient->data.cb = 0; 128 g_ctxHost.pClient->data.u32Format = 0; 129 vboxClipboardFormatAnnounce(NULL, 0); 130 return rc; 131 } 132 if (pClient->data.pv == NULL) 133 return VERR_TIMEOUT; 134 LogFlowFunc(("wait completed. Returning.\n")); 135 *ppv = pClient->data.pv; 136 *pcb = pClient->data.cb; 137 g_ctxHost.pClient->data.pv = 0; 138 g_ctxHost.pClient->data.cb = 0; 139 g_ctxHost.pClient->data.u32Format = 0; 140 return VINF_SUCCESS; 155 return VERR_WRONG_ORDER; 156 } 157 int rc = RTSemEventCreate(&request.finished); 158 if (RT_SUCCESS(rc)) 159 { 160 rc = vboxClipboardPostAndWaitForReq(pCtx, &request, 161 u32Format); 162 RTSemEventDestroy(request.finished); 163 } 164 if (RT_SUCCESS(rc)) 165 { 166 *ppv = request.pv; 167 *pcb = request.cb; 168 } 169 LogFlowFunc(("returning %Rrc\n", rc)); 170 if (RT_SUCCESS(rc)) 171 LogFlowFunc(("*ppv=%.*ls, *pcb=%u\n", *pcb / 2, *ppv, *pcb)); 172 return rc; 141 173 } 142 174 … … 150 182 uint32_t u32Formats) 151 183 { 184 LogFlowFunc(("called. pCtx=%p, u32Formats=%02X\n", pCtx, u32Formats)); 152 185 vboxSvcClipboardReportMsg(pCtx->pClient, 153 186 VBOX_SHARED_CLIPBOARD_HOST_MSG_FORMATS, … … 161 194 int vboxClipboardInit (void) 162 195 { 163 int rc = VINF_SUCCESS; 164 VBOXCLIPBOARDCONTEXTX11 *pBackend = NULL; 165 166 LogRel(("Initializing host clipboard service\n")); 167 RTSemEventCreate(&g_ctxHost.waitForData); 168 RTSemMutexCreate(&g_ctxHost.clipboardMutex); 169 pBackend = VBoxX11ClipboardConstructX11(&g_ctxHost); 170 if (pBackend == NULL) 171 { 172 RTSemEventDestroy(g_ctxHost.waitForData); 173 RTSemMutexDestroy(g_ctxHost.clipboardMutex); 174 LogRel(("Failed to start the host shared clipboard service, out of memory.\n")); 175 rc = VERR_NO_MEMORY; 176 } 177 else 178 g_ctxHost.pBackend = pBackend; 179 return rc; 196 return VINF_SUCCESS; 180 197 } 181 198 … … 186 203 void vboxClipboardDestroy (void) 187 204 { 188 int rc = VINF_SUCCESS; 189 LogRelFunc(("shutting down host clipboard\n")); 190 VBoxX11ClipboardDestructX11(g_ctxHost.pBackend); 191 /* We can safely destroy these as the backend has exited 192 * successfully and no other calls from the host code should be 193 * forthcoming. */ 194 RTSemEventDestroy(g_ctxHost.waitForData); 195 RTSemMutexDestroy(g_ctxHost.clipboardMutex); 205 196 206 } 197 207 … … 205 215 { 206 216 int rc = VINF_SUCCESS; 207 LogFlowFunc(("\n")); 208 /* Only one client is supported for now */ 209 AssertLogRelReturn(g_ctxHost.pClient == 0, VERR_NOT_SUPPORTED); 210 pClient->pCtx = &g_ctxHost; 211 pClient->pCtx->pClient = pClient; 212 /** The pClient pointer is a dummy anyway, as we only support a single 213 * client at a time. */ 214 rc = VBoxX11ClipboardStartX11(g_ctxHost.pBackend, 215 true /* fOwnClipboard */); 217 VBOXCLIPBOARDCONTEXTX11 *pBackend = NULL; 218 219 LogRel(("Starting host clipboard service\n")); 220 VBOXCLIPBOARDCONTEXT *pCtx = 221 (VBOXCLIPBOARDCONTEXT *) RTMemAllocZ(sizeof(VBOXCLIPBOARDCONTEXT)); 222 if (!pCtx) 223 rc = VERR_NO_MEMORY; 224 else 225 { 226 RTCritSectInit(&pCtx->clipboardMutex); 227 pBackend = VBoxX11ClipboardConstructX11(pCtx); 228 if (pBackend == NULL) 229 rc = VERR_NO_MEMORY; 230 else 231 { 232 pCtx->pBackend = pBackend; 233 pClient->pCtx = pCtx; 234 pCtx->pClient = pClient; 235 rc = VBoxX11ClipboardStartX11(pBackend); 236 } 237 if (RT_FAILURE(rc) && pBackend) 238 VBoxX11ClipboardStopX11(pCtx->pBackend); 239 if (RT_FAILURE(rc)) 240 RTCritSectDelete(&pCtx->clipboardMutex); 241 } 242 if (RT_FAILURE(rc)) 243 { 244 RTMemFree(pCtx); 245 LogRel(("Failed to initialise the shared clipboard\n")); 246 } 247 LogFlowFunc(("returning %Rrc\n", rc)); 216 248 return rc; 217 249 } … … 224 256 int vboxClipboardSync (VBOXCLIPBOARDCLIENTDATA *pClient) 225 257 { 226 227 /* On a Linux host, the guest should never synchronise/cache its 228 * clipboard contents, as we have no way of reliably telling when the 229 * host clipboard data changes. So instead of synchronising, we tell 230 * the guest to empty its clipboard, and we set the cached flag so that 231 * we report formats to the guest next time we poll for them. */ 232 /** @note This has been changed so that the message is sent even if 233 * X11 is not available. */ 234 vboxSvcClipboardReportMsg (g_ctxHost.pClient, 258 /* Tell the guest we have no data in case X11 is not available. If 259 * there is data in the host clipboard it will automatically be sent to 260 * the guest when the clipboard starts up. */ 261 vboxSvcClipboardReportMsg (pClient, 235 262 VBOX_SHARED_CLIPBOARD_HOST_MSG_FORMATS, 0); 236 VBoxX11ClipboardRequestSyncX11(g_ctxHost.pBackend);237 238 263 return VINF_SUCCESS; 239 264 } … … 243 268 * @note Host glue code 244 269 */ 245 void vboxClipboardDisconnect (VBOXCLIPBOARDCLIENTDATA * )270 void vboxClipboardDisconnect (VBOXCLIPBOARDCLIENTDATA *pClient) 246 271 { 247 272 LogFlow(("vboxClipboardDisconnect\n")); 248 273 249 RTSemMutexRequest(g_ctxHost.clipboardMutex, RT_INDEFINITE_WAIT); 274 LogRel(("Stopping the host clipboard service\n")); 275 VBOXCLIPBOARDCONTEXT *pCtx = pClient->pCtx; 250 276 /* Drop the reference to the client, in case it is still there. This 251 277 * will cause any outstanding clipboard data requests from X11 to fail 252 278 * immediately. */ 253 g_ctxHost.pClient = NULL; 254 /* The backend may be waiting for data from VBox. At this point it is no 255 * longer going to arrive, and we must release it to allow the event 256 * loop to terminate. In this case the buffer where VBox would have 257 * written the clipboard data will still be empty and we will just 258 * return "no data" to the backend. Any subsequent attempts to get the 259 * data from VBox will fail immediately as the client reference is gone. 260 */ 261 /** @note This has been made unconditional, as it should do no harm 262 * even if we are not waiting. */ 263 RTSemEventSignal(g_ctxHost.waitForData); 264 int rc = VBoxX11ClipboardStopX11(g_ctxHost.pBackend); 279 pCtx->fShuttingDown = true; 280 /* If there is a currently pending request, release it immediately. */ 281 vboxClipboardWriteData(pClient, NULL, 0, 0); 282 int rc = VBoxX11ClipboardStopX11(pCtx->pBackend); 265 283 /** @todo handle this slightly more reasonably, or be really sure 266 284 * it won't go wrong. */ 267 285 AssertRC(rc); 268 RTSemMutexRelease(g_ctxHost.clipboardMutex); 286 if (RT_SUCCESS(rc)) /* And if not? */ 287 { 288 VBoxX11ClipboardDestructX11(pCtx->pBackend); 289 RTCritSectDelete(&pCtx->clipboardMutex); 290 RTMemFree(pCtx); 291 } 269 292 } 270 293 … … 276 299 * @note Host glue code 277 300 */ 278 void vboxClipboardFormatAnnounce (VBOXCLIPBOARDCLIENTDATA *pClient, uint32_t u32Formats) 279 { 280 VBoxX11ClipboardAnnounceVBoxFormat (g_ctxHost.pBackend, u32Formats); 301 void vboxClipboardFormatAnnounce (VBOXCLIPBOARDCLIENTDATA *pClient, 302 uint32_t u32Formats) 303 { 304 LogFlowFunc(("called. pClient=%p, u32Formats=%02X\n", pClient, 305 u32Formats)); 306 VBoxX11ClipboardAnnounceVBoxFormat (pClient->pCtx->pBackend, u32Formats); 281 307 } 282 308 … … 295 321 uint32_t *pcbActual) 296 322 { 297 int rc = VINF_SUCCESS; 298 VBOXCLIPBOARDREQUEST request; 299 /* No one else (VBox or X11) should currently be waiting. The first 300 * because requests from VBox are serialised and the second because X11 301 * previously grabbed the clipboard, so it should not be waiting for 302 * data from us. */ 303 AssertLogRelReturn (ASMAtomicCmpXchgU32(&g_ctxHost.waiter, VB, NONE), 304 VERR_DEADLOCK); 305 request.pv = pv; 306 request.cb = cb; 307 request.pcbActual = pcbActual; 308 request.pCtx = g_ctxHost.pBackend; 309 rc = VBoxX11ClipboardReadX11Data(g_ctxHost.pBackend, u32Format, &request); 310 g_ctxHost.waiter = NONE; 323 LogFlowFunc(("pClient=%p, u32Format=%02X, pv=%p, cb=%u, pcbActual=%p", 324 pClient, u32Format, pv, cb, pcbActual)); 325 int rc = VBoxX11ClipboardReadX11Data(pClient->pCtx->pBackend, u32Format, 326 pv, cb, pcbActual); 327 LogFlowFunc(("returning %Rrc\n", rc)); 328 if (RT_SUCCESS(rc)) 329 LogFlowFunc(("*pv=%.*ls, *pcbActual=%u\n", *pcbActual / 2, pv, 330 *pcbActual)); 311 331 return rc; 312 332 } … … 324 344 void *pv, uint32_t cb, uint32_t u32Format) 325 345 { 326 /* Assume that if this gets called at all then the X11 backend is running. */ 327 #if 0 328 if (!g_fHaveX11) 329 return; 330 #endif 331 332 LogFlowFunc (("called\n")); 333 334 /* Assert that no other transfer is in process (requests are serialised) 335 * or has not cleaned up properly. */ 336 AssertLogRelReturnVoid ( pClient->data.pv == NULL 337 && pClient->data.cb == 0 338 && pClient->data.u32Format == 0); 339 340 /* Grab the mutex and check that X11 is still waiting for the data before 341 * delivering it. See the explanation in VBoxX11ClipboardReadVBoxData. */ 342 RTSemMutexRequest(g_ctxHost.clipboardMutex, RT_INDEFINITE_WAIT); 343 if (g_ctxHost.waiter == X11 && cb > 0) 344 { 345 pClient->data.pv = RTMemAlloc (cb); 346 347 if (pClient->data.pv) 346 LogFlowFunc (("called. pClient=%p, pv=%p (%.*ls), cb=%u, u32Format=%02X\n", 347 pClient, pv, cb / 2, pv, cb, u32Format)); 348 349 VBOXCLIPBOARDCONTEXT *pCtx = pClient->pCtx; 350 /* Grab the mutex and check whether there is a pending request for data. 351 */ 352 RTCritSectEnter(&pCtx->clipboardMutex); 353 VBOXCLIPBOARDREQFROMVBOX *pReq = pCtx->pReq; 354 if (pReq != NULL) 355 { 356 if (cb > 0) 348 357 { 349 memcpy (pClient->data.pv, pv, cb); 350 pClient->data.cb = cb; 351 pClient->data.u32Format = u32Format; 358 pReq->pv = RTMemDup(pv, cb); 359 if (pReq->pv != NULL) /* NULL may also mean no memory... */ 360 { 361 pReq->cb = cb; 362 pReq->format = u32Format; 363 } 352 364 } 353 }354 RTSemMutexRelease(g_ctxHost.clipboardMutex);355 356 RTSemEventSignal(g_ctxHost.waitForData);357 } 358 365 /* Signal that the request has been completed. */ 366 RTSemEventSignal(pReq->finished); 367 } 368 pCtx->pReq = NULL; 369 RTCritSectLeave(&pCtx->clipboardMutex); 370 }
Note:
See TracChangeset
for help on using the changeset viewer.