Changeset 18613 in vbox for trunk/src/VBox/GuestHost/SharedClipboard
- Timestamp:
- Apr 1, 2009 6:17:54 PM (16 years ago)
- svn:sync-xref-src-repo-rev:
- 45519
- File:
-
- 1 copied
Legend:
- Unmodified
- Added
- Removed
-
trunk/src/VBox/GuestHost/SharedClipboard/x11-clipboard.cpp
r18582 r18613 2 2 * 3 3 * Shared Clipboard: 4 * Linux host.4 * X11 backend code. 5 5 */ 6 6 … … 21 21 */ 22 22 23 #include <string.h> 24 #include <stdio.h> 25 #include <stdint.h> 23 /** @todo create a clipboard log group */ 24 #define LOG_GROUP LOG_GROUP_HGCM 25 26 #include <vector> 26 27 27 28 #ifdef RT_OS_SOLARIS 28 29 #include <tsol/label.h> 29 30 #endif 30 31 #include <vector>32 31 33 32 #include <X11/Xlib.h> … … 38 37 #include <X11/StringDefs.h> 39 38 40 #include <iprt/alloc.h>41 #include <iprt/asm.h> /* For atomic operations */42 #include <iprt/assert.h>43 39 #include <iprt/env.h> 44 40 #include <iprt/mem.h> 45 #include <iprt/s tring.h>41 #include <iprt/semaphore.h> 46 42 #include <iprt/thread.h> 47 #include <iprt/process.h> 48 #include <iprt/semaphore.h> 49 43 44 #include <VBox/log.h> 45 46 #include <VBox/GuestHost/SharedClipboard.h> 50 47 #include <VBox/GuestHost/clipboard-helper.h> 51 48 #include <VBox/HostServices/VBoxClipboardSvc.h> 52 53 #include "VBoxClipboard.h"54 49 55 50 /** Do we want to test Utf16 by disabling other text formats? */ … … 72 67 }; 73 68 74 /** The X11 clipboard uses several names for the same format. This structure maps an X1175 69 /** The X11 clipboard uses several names for the same format. This 70 * structure maps an X11 name to a format. */ 76 71 typedef struct { 77 72 Atom atom; … … 79 74 unsigned guestFormat; 80 75 } VBOXCLIPBOARDFORMAT; 81 82 /** Does X11 or VBox currently own the clipboard? */83 enum g_eOwner { NONE = 0, X11, VB };84 85 typedef struct {86 /** BMP file type marker - must always contain 'BM' */87 uint16_t bfType;88 /** The size of the BMP file in bytes (the MS docs say that this is not reliable) */89 uint32_t bfSize;90 /** Reserved, must always be zero */91 uint16_t bfReserved1;92 /** Reserved, must always be zero */93 uint16_t bfReserved2;94 /** Offset from the beginning of this header to the actual image bits */95 } VBOXBITMAPFILEHEADER;96 97 /** Global context information used by the host glue for the X11 clipboard98 * backend */99 struct _VBOXCLIPBOARDCONTEXT100 {101 /** Since the clipboard data moves asynchronously, we use an event102 * semaphore to wait for it. When a function issues a request for103 * clipboard data it must wait for this semaphore, which is triggered104 * when the data arrives. */105 RTSEMEVENT waitForData;106 /** Who (if anyone) is currently waiting for data? Used for sanity107 * checks when data arrives. */108 volatile uint32_t waiter;109 /** This mutex is grabbed during any critical operations on the clipboard110 * which might clash with others. */111 RTSEMMUTEX clipboardMutex;112 113 /** Pointer to the client data structure */114 VBOXCLIPBOARDCLIENTDATA *pClient;115 };116 76 117 77 /** Global context information used by the X11 clipboard backend */ … … 175 135 }; 176 136 177 typedef struct _VBOXCLIPBOARDCONTEXTX11 VBOXCLIPBOARDCONTEXTX11;178 179 /** A structure containing information about where to store a request180 * for the X11 clipboard contents. */181 struct _VBOXCLIPBOARDREQUEST182 {183 /** The buffer to write X11 clipboard data to (valid during a request184 * for the clipboard contents) */185 void *pv;186 /** The size of the buffer to write X11 clipboard data to (valid during187 * a request for the clipboard contents) */188 unsigned cb;189 /** The size of the X11 clipboard data written to the buffer (valid190 * during a request for the clipboard contents) */191 uint32_t *pcbActual;192 };193 194 typedef struct _VBOXCLIPBOARDREQUEST VBOXCLIPBOARDREQUEST;195 196 137 /* Only one client is supported. There seems to be no need for more clients. 197 138 */ 198 static VBOXCLIPBOARDCONTEXT g_ctxHost;199 139 static VBOXCLIPBOARDCONTEXTX11 g_ctxX11; 200 140 201 141 /* Are we actually connected to the X server? */ 202 142 static bool g_fHaveX11; 203 204 /** @todo this is a temporary declaration. */205 static void vboxClipboardFormatAnnounceBackend (uint32_t u32Formats);206 207 /**208 * Send a request to VBox to transfer the contents of its clipboard to X11.209 *210 * @param pCtx Pointer to the host clipboard structure211 * @param u32Format The format in which the data should be transfered212 * @param ppv On success and if pcb > 0, this will point to a buffer213 * to be freed with RTMemFree containing the data read.214 * @param pcb On success, this contains the number of bytes of data215 * returned216 * @note Host glue code.217 */218 static int vboxClipboardReadDataFromVBox (VBOXCLIPBOARDCONTEXT *pCtx, uint32_t u32Format, void **ppv, uint32_t *pcb)219 {220 volatile VBOXCLIPBOARDCLIENTDATA *pClient = pCtx->pClient;221 222 LogFlowFunc(("u32Format=%02X\n", u32Format));223 if (pClient == NULL)224 {225 /* This can legitimately happen if we disconnect during a request for226 * data from X11. */227 LogFunc(("host requested guest clipboard data after guest had disconnected.\n"));228 vboxClipboardFormatAnnounceBackend(0);229 pCtx->waiter = NONE;230 return VERR_TIMEOUT;231 }232 /* Assert that no other transfer is in process (requests are serialised)233 * and that the last transfer cleaned up properly. */234 AssertLogRelReturn( pClient->data.pv == NULL235 && pClient->data.cb == 0236 && pClient->data.u32Format == 0,237 VERR_WRONG_ORDER238 );239 /* No one else (X11 or VBox) should currently be waiting. The first because240 * requests from X11 are serialised and the second because VBox previously241 * grabbed the clipboard, so it should not be waiting for data from us. */242 AssertLogRelReturn (ASMAtomicCmpXchgU32(&pCtx->waiter, X11, NONE), VERR_DEADLOCK);243 /* Request data from VBox */244 vboxSvcClipboardReportMsg(pCtx->pClient,245 VBOX_SHARED_CLIPBOARD_HOST_MSG_READ_DATA,246 u32Format);247 /* Which will signal us when it is ready. We use a timeout here because248 * we can't be sure that the guest will behave correctly. */249 int rc = RTSemEventWait(pCtx->waitForData, CLIPBOARDTIMEOUT);250 if (rc == VERR_TIMEOUT)251 rc = VINF_SUCCESS; /* Timeout handling follows. */252 /* Now we have a potential race between the HGCM thread delivering the data253 * and our setting waiter to NONE to say that we are no longer waiting for254 * it. We solve this as follows: both of these operations are done under255 * the clipboard mutex. The HGCM thread will only deliver the data if we256 * are still waiting after it acquires the mutex. After we release the257 * mutex, we finally do our check to see whether the data was delivered. */258 RTSemMutexRequest(g_ctxHost.clipboardMutex, RT_INDEFINITE_WAIT);259 pCtx->waiter = NONE;260 RTSemMutexRelease(g_ctxHost.clipboardMutex);261 AssertLogRelRCSuccess(rc);262 if (RT_FAILURE(rc))263 {264 /* I believe this should not happen. Wait until the assertions arrive265 * to prove the contrary. */266 RTMemFree(pClient->data.pv);267 g_ctxHost.pClient->data.pv = 0;268 g_ctxHost.pClient->data.cb = 0;269 g_ctxHost.pClient->data.u32Format = 0;270 vboxClipboardFormatAnnounce(NULL, 0);271 return rc;272 }273 if (pClient->data.pv == NULL)274 return VERR_TIMEOUT;275 LogFlowFunc(("wait completed. Returning.\n"));276 *ppv = pClient->data.pv;277 *pcb = pClient->data.cb;278 g_ctxHost.pClient->data.pv = 0;279 g_ctxHost.pClient->data.cb = 0;280 g_ctxHost.pClient->data.u32Format = 0;281 return VINF_SUCCESS;282 }283 143 284 144 /** … … 588 448 589 449 /** 590 * Report formats available in the X11 clipboard to VBox.591 * @param pCtx Opaque context pointer for the glue code592 * @param u32Formats The formats available593 * @note Host glue code594 */595 void VBoxX11ClipboardReportX11Formats(VBOXCLIPBOARDCONTEXT *pCtx,596 uint32_t u32Formats)597 {598 vboxSvcClipboardReportMsg(pCtx->pClient,599 VBOX_SHARED_CLIPBOARD_HOST_MSG_FORMATS,600 u32Formats);601 }602 603 604 /**605 450 * Notify the host clipboard about the data formats we support, based on the 606 451 * "targets" (available data formats) information obtained from the X11 … … 750 595 * @note X11 backend code. 751 596 */ 752 int vboxClipboardInitX11 (void)597 static int vboxClipboardInitX11 (void) 753 598 { 754 599 /* Create a window and make it a clipboard viewer. */ … … 829 674 * @note X11 backend code 830 675 */ 831 int vboxClipboardInitBackend (VBOXCLIPBOARDCONTEXT *pFrontend) 676 int VBoxX11ClipboardInitX11(VBOXCLIPBOARDCONTEXT *pFrontend, 677 VBOXCLIPBOARDCONTEXTX11 **ppBackend) 832 678 { 833 679 int rc; … … 885 731 886 732 /** 887 * Initialise the host side of the shared clipboard.888 * @note Host glue code889 */890 int vboxClipboardInit (void)891 {892 int rc = VINF_SUCCESS;893 LogRel(("Initializing host clipboard service\n"));894 RTSemEventCreate(&g_ctxHost.waitForData);895 RTSemMutexCreate(&g_ctxHost.clipboardMutex);896 rc = vboxClipboardInitBackend(&g_ctxHost);897 if (RT_FAILURE(rc))898 {899 RTSemEventDestroy(g_ctxHost.waitForData);900 RTSemMutexDestroy(g_ctxHost.clipboardMutex);901 LogRel(("Failed to start the host shared clipboard service.\n"));902 }903 return rc;904 }905 906 /**907 733 * Terminate the shared clipboard X11 backend. 908 734 * @note X11 backend code 909 735 */ 910 int vboxClipboardDestroyBackend (void)736 int VBoxX11ClipboardTermX11(VBOXCLIPBOARDCONTEXTX11 *pBackend) 911 737 { 912 738 int rc, rcThread; … … 953 779 954 780 /** 955 * Terminate the host side of the shared clipboard.956 * @note host glue code957 */958 void vboxClipboardDestroy (void)959 {960 int rc = VINF_SUCCESS;961 LogRelFunc(("shutting down host clipboard\n"));962 /* Drop the reference to the client, in case it is still there. This963 * will cause any outstanding clipboard data requests from X11 to fail964 * immediately. */965 g_ctxHost.pClient = NULL;966 /* The backend may be waiting for data from VBox. At this point it is no967 * longer going to arrive, and we must release it to allow the event968 * loop to terminate. In this case the buffer where VBox would have969 * written the clipboard data will still be empty and we will just970 * return "no data" to the backend. Any subsequent attempts to get the971 * data from VBox will fail immediately as the client reference is gone.972 */973 /** @note This has been made unconditional, as it should do no harm974 * even if we are not waiting. */975 RTSemEventSignal(g_ctxHost.waitForData);976 rc = vboxClipboardDestroyBackend();977 if (RT_SUCCESS(rc))978 {979 /* We can safely destroy these as the backend has exited980 * successfully and no other calls from the host code should be981 * forthcoming. */982 /** @todo can the backend fail to exit successfully? What then? */983 RTSemEventDestroy(g_ctxHost.waitForData);984 RTSemMutexDestroy(g_ctxHost.clipboardMutex);985 }986 }987 988 /**989 781 * Announce to the X11 backend that we are ready to start. 990 782 * @param owner who is the initial clipboard owner 991 783 */ 992 int vboxClipboardConnectBackend (enum g_eOwner owner) 784 int VBoxX11ClipboardStartX11(VBOXCLIPBOARDCONTEXTX11 *pBackend, 785 enum g_eOwner owner) 993 786 { 994 787 LogFlowFunc(("\n")); … … 1006 799 /** @todo Check whether the guest gets a format announcement at 1007 800 * startup. */ 1008 vboxClipboardFormatAnnounceBackend(0);801 VBoxX11ClipboardAnnounceVBoxFormat(pBackend, 0); 1009 802 } 1010 803 return VINF_SUCCESS; 1011 }1012 1013 /**1014 * Connect a guest to the shared clipboard.1015 * @note host glue code1016 * @note on the host, we assume that some other application already owns1017 * the clipboard and leave ownership to X11.1018 */1019 int vboxClipboardConnect (VBOXCLIPBOARDCLIENTDATA *pClient)1020 {1021 int rc = VINF_SUCCESS;1022 LogFlowFunc(("\n"));1023 /* Only one client is supported for now */1024 AssertLogRelReturn(g_ctxHost.pClient == 0, VERR_NOT_SUPPORTED);1025 pClient->pCtx = &g_ctxHost;1026 pClient->pCtx->pClient = pClient;1027 /** The pClient pointer is a dummy anyway, as we only support a single1028 * client at a time. */1029 rc = vboxClipboardConnectBackend(X11 /* initial owner */);1030 return rc;1031 804 } 1032 805 … … 1035 808 * @note X11 backend code 1036 809 */ 1037 void vboxClipboardRequestSync (void)810 void VBoxX11ClipboardRequestSyncX11(VBOXCLIPBOARDCONTEXTX11 *pBackend) 1038 811 { 1039 812 /* … … 1046 819 1047 820 /** 1048 * Synchronise the contents of the host clipboard with the guest, called1049 * after a save and restore of the guest.1050 * @note Host glue code1051 */1052 int vboxClipboardSync (VBOXCLIPBOARDCLIENTDATA *pClient)1053 {1054 1055 /* On a Linux host, the guest should never synchronise/cache its1056 * clipboard contents, as we have no way of reliably telling when the1057 * host clipboard data changes. So instead of synchronising, we tell1058 * the guest to empty its clipboard, and we set the cached flag so that1059 * we report formats to the guest next time we poll for them. */1060 /** @note This has been changed so that the message is sent even if1061 * X11 is not available. */1062 vboxSvcClipboardReportMsg (g_ctxHost.pClient,1063 VBOX_SHARED_CLIPBOARD_HOST_MSG_FORMATS, 0);1064 vboxClipboardRequestSync();1065 1066 return VINF_SUCCESS;1067 }1068 1069 /**1070 821 * Shut down the shared clipboard X11 backend. 1071 822 * @note X11 backend code 1072 823 */ 1073 void vboxClipboardDisconnectBackend (void)824 void VBoxX11ClipboardStopX11(VBOXCLIPBOARDCONTEXTX11 *pBackend) 1074 825 { 1075 826 /* … … 1082 833 g_ctxX11.X11TextFormat = INVALID; 1083 834 g_ctxX11.X11BitmapFormat = INVALID; 1084 }1085 1086 /**1087 * Shut down the shared clipboard service and "disconnect" the guest.1088 * @note Host glue code1089 */1090 void vboxClipboardDisconnect (VBOXCLIPBOARDCLIENTDATA *)1091 {1092 LogFlow(("vboxClipboardDisconnect\n"));1093 1094 RTSemMutexRequest(g_ctxHost.clipboardMutex, RT_INDEFINITE_WAIT);1095 g_ctxHost.pClient = NULL;1096 vboxClipboardDisconnectBackend();1097 RTSemMutexRelease(g_ctxHost.clipboardMutex);1098 835 } 1099 836 … … 1193 930 1194 931 LogFlowFunc (("called\n")); 1195 rc = vboxClipboardReadDataFromVBox(g_ctxX11.pFrontend, VBOX_SHARED_CLIPBOARD_FMT_UNICODETEXT, &pvVBox, &cbVBox);932 rc = VBoxX11ClipboardReadVBoxData(g_ctxX11.pFrontend, VBOX_SHARED_CLIPBOARD_FMT_UNICODETEXT, &pvVBox, &cbVBox); 1196 933 if ((RT_FAILURE(rc)) || (cbVBox == 0)) 1197 934 { 1198 /* If vboxClipboardReadDataFromVBoxfails then pClient may be invalid */1199 LogRelFunc ((" vboxClipboardReadDataFromVBoxreturned %Rrc%s\n", rc,935 /* If VBoxX11ClipboardReadVBoxData fails then pClient may be invalid */ 936 LogRelFunc (("VBoxX11ClipboardReadVBoxData returned %Rrc%s\n", rc, 1200 937 RT_SUCCESS(rc) ? ", cbVBox == 0" : "")); 1201 938 RTMemFree(pvVBox); … … 1278 1015 LogFlowFunc (("called\n")); 1279 1016 /* Read the clipboard data from the guest. */ 1280 rc = vboxClipboardReadDataFromVBox(g_ctxX11.pFrontend, VBOX_SHARED_CLIPBOARD_FMT_UNICODETEXT, &pvVBox, &cbVBox);1017 rc = VBoxX11ClipboardReadVBoxData(g_ctxX11.pFrontend, VBOX_SHARED_CLIPBOARD_FMT_UNICODETEXT, &pvVBox, &cbVBox); 1281 1018 if ((rc != VINF_SUCCESS) || (cbVBox == 0)) 1282 1019 { 1283 /* If vboxClipboardReadDataFromVBoxfails then pClient may be invalid */1284 LogRelFunc ((" vboxClipboardReadDataFromVBoxreturned %Rrc%s\n", rc,1020 /* If VBoxX11ClipboardReadVBoxData fails then pClient may be invalid */ 1021 LogRelFunc (("VBoxX11ClipboardReadVBoxData returned %Rrc%s\n", rc, 1285 1022 RT_SUCCESS(rc) ? ", cbVBox == 0" : "")); 1286 1023 RTMemFree(pvVBox); … … 1385 1122 LogFlowFunc (("called\n")); 1386 1123 /* Read the clipboard data from the guest. */ 1387 rc = vboxClipboardReadDataFromVBox(g_ctxX11.pFrontend, VBOX_SHARED_CLIPBOARD_FMT_UNICODETEXT, &pvVBox, &cbVBox);1124 rc = VBoxX11ClipboardReadVBoxData(g_ctxX11.pFrontend, VBOX_SHARED_CLIPBOARD_FMT_UNICODETEXT, &pvVBox, &cbVBox); 1388 1125 if ((rc != VINF_SUCCESS) || (cbVBox == 0)) 1389 1126 { 1390 /* If vboxClipboardReadDataFromVBoxfails then pClient may be invalid */1391 LogRelFunc ((" vboxClipboardReadDataFromVBoxreturned %Rrc%s\n", rc,1127 /* If VBoxX11ClipboardReadVBoxData fails then pClient may be invalid */ 1128 LogRelFunc (("VBoxX11ClipboardReadVBoxData returned %Rrc%s\n", rc, 1392 1129 RT_SUCCESS(rc) ? ", cbVBox == 0" : "")); 1393 1130 RTMemFree(pvVBox); … … 1566 1303 * @note X11 backend code 1567 1304 */ 1568 void vboxClipboardFormatAnnounceBackend (uint32_t u32Formats) 1305 void VBoxX11ClipboardAnnounceVBoxFormat(VBOXCLIPBOARDCONTEXTX11 *pBackend, 1306 uint32_t u32Formats) 1569 1307 { 1570 1308 /* … … 1610 1348 1611 1349 /** 1612 * VBox is taking possession of the shared clipboard.1613 *1614 * @param pClient Context data for the guest system1615 * @param u32Formats Clipboard formats the guest is offering1616 * @note Host glue code1617 */1618 void vboxClipboardFormatAnnounce (VBOXCLIPBOARDCLIENTDATA *pClient, uint32_t u32Formats)1619 {1620 vboxClipboardFormatAnnounceBackend (u32Formats);1621 }1622 1623 /**1624 1350 * Called when VBox wants to read the X11 clipboard. 1625 1351 * … … 1631 1357 * @note X11 backend code 1632 1358 */ 1633 int vboxClipboardReadDataBackend (uint32_t u32Format, 1634 VBOXCLIPBOARDREQUEST *pRequest) 1359 int VBoxX11ClipboardReadX11Data(VBOXCLIPBOARDCONTEXTX11 *pBackend, 1360 uint32_t u32Format, 1361 VBOXCLIPBOARDREQUEST *pRequest) 1635 1362 { 1636 1363 /* … … 1680 1407 } 1681 1408 1682 /**1683 * Called when VBox wants to read the X11 clipboard.1684 *1685 * @param pClient Context information about the guest VM1686 * @param u32Format The format that the guest would like to receive the data in1687 * @param pv Where to write the data to1688 * @param cb The size of the buffer to write the data to1689 * @param pcbActual Where to write the actual size of the written data1690 * @note Host glue code1691 */1692 int vboxClipboardReadData (VBOXCLIPBOARDCLIENTDATA *pClient,1693 uint32_t u32Format, void *pv, uint32_t cb,1694 uint32_t *pcbActual)1695 {1696 int rc = VINF_SUCCESS;1697 VBOXCLIPBOARDREQUEST request;1698 /* No one else (VBox or X11) should currently be waiting. The first1699 * because requests from VBox are serialised and the second because X111700 * previously grabbed the clipboard, so it should not be waiting for1701 * data from us. */1702 AssertLogRelReturn (ASMAtomicCmpXchgU32(&g_ctxHost.waiter, VB, NONE),1703 VERR_DEADLOCK);1704 request.pv = pv;1705 request.cb = cb;1706 request.pcbActual = pcbActual;1707 rc = vboxClipboardReadDataBackend(u32Format, &request);1708 g_ctxHost.waiter = NONE;1709 return rc;1710 }1711 1712 /**1713 * Called when we have requested data from VBox and that data has arrived.1714 *1715 * @param pClient Context information about the guest VM1716 * @param pv Buffer to which the data was written1717 * @param cb The size of the data written1718 * @param u32Format The format of the data written1719 * @note Host glue code1720 */1721 void vboxClipboardWriteData (VBOXCLIPBOARDCLIENTDATA *pClient, void *pv, uint32_t cb, uint32_t u32Format)1722 {1723 if (!g_fHaveX11)1724 return;1725 1726 LogFlowFunc (("called\n"));1727 1728 /* Assert that no other transfer is in process (requests are serialised)1729 * or has not cleaned up properly. */1730 AssertLogRelReturnVoid ( pClient->data.pv == NULL1731 && pClient->data.cb == 01732 && pClient->data.u32Format == 0);1733 1734 /* Grab the mutex and check that X11 is still waiting for the data before1735 * delivering it. See the explanation in vboxClipboardReadDataFromVBox. */1736 RTSemMutexRequest(g_ctxHost.clipboardMutex, RT_INDEFINITE_WAIT);1737 if (g_ctxHost.waiter == X11 && cb > 0)1738 {1739 pClient->data.pv = RTMemAlloc (cb);1740 1741 if (pClient->data.pv)1742 {1743 memcpy (pClient->data.pv, pv, cb);1744 pClient->data.cb = cb;1745 pClient->data.u32Format = u32Format;1746 }1747 }1748 RTSemMutexRelease(g_ctxHost.clipboardMutex);1749 1750 RTSemEventSignal(g_ctxHost.waitForData);1751 }1752
Note:
See TracChangeset
for help on using the changeset viewer.