Changeset 19152 in vbox for trunk/src/VBox/HostServices
- Timestamp:
- Apr 23, 2009 9:23:45 PM (16 years ago)
- File:
-
- 1 edited
Legend:
- Unmodified
- Added
- Removed
-
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.