VirtualBox

source: vbox/trunk/src/VBox/HostServices/SharedClipboard/VBoxSharedClipboardSvc-x11.cpp@ 78619

Last change on this file since 78619 was 78619, checked in by vboxsync, 6 years ago

Shared Clipboard/URI: Build fix.

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 20.4 KB
Line 
1/* $Id: VBoxSharedClipboardSvc-x11.cpp 78619 2019-05-21 08:42:35Z vboxsync $ */
2/** @file
3 * Shared Clipboard Service - Linux host.
4 */
5
6/*
7 * Copyright (C) 2006-2019 Oracle Corporation
8 *
9 * This file is part of VirtualBox Open Source Edition (OSE), as
10 * available from http://www.virtualbox.org. This file is free software;
11 * you can redistribute it and/or modify it under the terms of the GNU
12 * General Public License (GPL) as published by the Free Software
13 * Foundation, in version 2 as it comes in the "COPYING" file of the
14 * VirtualBox OSE distribution. VirtualBox OSE is distributed in the
15 * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
16 */
17
18
19/*********************************************************************************************************************************
20* Header Files *
21*********************************************************************************************************************************/
22#define LOG_GROUP LOG_GROUP_SHARED_CLIPBOARD
23#include <iprt/assert.h>
24#include <iprt/critsect.h>
25#include <iprt/env.h>
26#include <iprt/mem.h>
27#include <iprt/semaphore.h>
28#include <iprt/string.h>
29
30#include <VBox/GuestHost/SharedClipboard.h>
31#include <VBox/HostServices/VBoxClipboardSvc.h>
32#include <VBox/err.h>
33
34#include "VBoxSharedClipboardSvc-internal.h"
35
36
37/*********************************************************************************************************************************
38* Structures and Typedefs *
39*********************************************************************************************************************************/
40struct _VBOXCLIPBOARDREQFROMVBOX;
41typedef struct _VBOXCLIPBOARDREQFROMVBOX VBOXCLIPBOARDREQFROMVBOX;
42
43/** Global context information used by the host glue for the X11 clipboard
44 * backend */
45struct _VBOXCLIPBOARDCONTEXT
46{
47 /** This mutex is grabbed during any critical operations on the clipboard
48 * which might clash with others. */
49 RTCRITSECT clipboardMutex;
50 /** The currently pending request for data from VBox. NULL if there is
51 * no request pending. The protocol for completing a request is to grab
52 * the critical section, check that @a pReq is not NULL, fill in the data
53 * fields and set @a pReq to NULL. The protocol for cancelling a pending
54 * request is to grab the critical section and set pReq to NULL.
55 * It is an error if a request arrives while another one is pending, and
56 * the backend is responsible for ensuring that this does not happen. */
57 VBOXCLIPBOARDREQFROMVBOX *pReq;
58 /** Pointer to the opaque X11 backend structure */
59 CLIPBACKEND *pBackend;
60 /** Pointer to the VBox host client data structure. */
61 PVBOXCLIPBOARDCLIENTDATA pClientData;
62 /** We set this when we start shutting down as a hint not to post any new
63 * requests. */
64 bool fShuttingDown;
65};
66
67
68
69/**
70 * Report formats available in the X11 clipboard to VBox.
71 * @param pCtx Opaque context pointer for the glue code
72 * @param u32Formats The formats available
73 * @note Host glue code
74 */
75void ClipReportX11Formats(VBOXCLIPBOARDCONTEXT *pCtx, uint32_t u32Formats)
76{
77 LogFlowFunc(("pCtx=%p, u32Formats=%02X\n", pCtx, u32Formats));
78
79 vboxSvcClipboardReportMsg(pCtx->pClientData, VBOX_SHARED_CLIPBOARD_HOST_MSG_REPORT_FORMATS, u32Formats);
80}
81
82/**
83 * Initialise the host side of the shared clipboard.
84 * @note Host glue code
85 */
86int VBoxClipboardSvcImplInit(void)
87{
88 return VINF_SUCCESS;
89}
90
91/**
92 * Terminate the host side of the shared clipboard.
93 * @note host glue code
94 */
95void VBoxClipboardSvcImplDestroy(void)
96{
97
98}
99
100/**
101 * Connect a guest to the shared clipboard.
102 * @note host glue code
103 * @note on the host, we assume that some other application already owns
104 * the clipboard and leave ownership to X11.
105 */
106int VBoxClipboardSvcImplConnect(PVBOXCLIPBOARDCLIENTDATA pClientData, bool fHeadless)
107{
108 int rc = VINF_SUCCESS;
109
110 LogRel(("Starting host clipboard service\n"));
111
112 PVBOXCLIPBOARDCONTEXT pCtx = (PVBOXCLIPBOARDCONTEXT)RTMemAllocZ(sizeof(VBOXCLIPBOARDCONTEXT));
113 if (!pCtx)
114 {
115 rc = VERR_NO_MEMORY;
116 }
117 else
118 {
119 RTCritSectInit(&pCtx->clipboardMutex);
120 CLIPBACKEND *pBackend = ClipConstructX11(pCtx, fHeadless);
121 if (!pBackend)
122 {
123 rc = VERR_NO_MEMORY;
124 }
125 else
126 {
127 pCtx->pBackend = pBackend;
128 pClientData->State.pCtx = pCtx;
129 pCtx->pClientData = pClientData;
130
131 rc = ClipStartX11(pBackend, true /* grab shared clipboard */);
132 if (RT_FAILURE(rc))
133 ClipDestructX11(pBackend);
134 }
135
136 if (RT_FAILURE(rc))
137 {
138 RTCritSectDelete(&pCtx->clipboardMutex);
139 RTMemFree(pCtx);
140 }
141 }
142
143 if (RT_FAILURE(rc))
144 LogRel(("Failed to initialize the Shared Clipboard host service, rc=%Rrc\n", rc));
145
146 LogFlowFunc(("returning %Rrc\n", rc));
147 return rc;
148}
149
150/**
151 * Synchronise the contents of the host clipboard with the guest, called
152 * after a save and restore of the guest.
153 * @note Host glue code
154 */
155int VBoxClipboardSvcImplSync(PVBOXCLIPBOARDCLIENTDATA pClientData)
156{
157 /* Tell the guest we have no data in case X11 is not available. If
158 * there is data in the host clipboard it will automatically be sent to
159 * the guest when the clipboard starts up. */
160 vboxSvcClipboardReportMsg(pClientData, VBOX_SHARED_CLIPBOARD_HOST_MSG_REPORT_FORMATS, 0);
161
162 return VINF_SUCCESS; /** @todo r=andy Check rc code. */
163}
164
165/**
166 * Shut down the shared clipboard service and "disconnect" the guest.
167 * @note Host glue code
168 */
169void VBoxClipboardSvcImplDisconnect(PVBOXCLIPBOARDCLIENTDATA pClientData)
170{
171 LogFlowFuncEnter();
172
173 LogRel(("Stopping the host clipboard service\n"));
174
175 PVBOXCLIPBOARDCONTEXT pCtx = pClientData->State.pCtx;
176
177 /* Drop the reference to the client, in case it is still there. This
178 * will cause any outstanding clipboard data requests from X11 to fail
179 * immediately. */
180 pCtx->fShuttingDown = true;
181
182 /* If there is a currently pending request, release it immediately. */
183 VBoxClipboardSvcImplWriteData(pClientData, NULL, 0, 0);
184
185 int rc = ClipStopX11(pCtx->pBackend);
186 /** @todo handle this slightly more reasonably, or be really sure
187 * it won't go wrong. */
188 AssertRC(rc);
189
190 if (RT_SUCCESS(rc)) /* And if not? */
191 {
192 ClipDestructX11(pCtx->pBackend);
193 RTCritSectDelete(&pCtx->clipboardMutex);
194 RTMemFree(pCtx);
195 }
196}
197
198/**
199 * VBox is taking possession of the shared clipboard.
200 *
201 * @param pClientData Context data for the guest system
202 * @param u32Formats Clipboard formats the guest is offering
203 * @note Host glue code
204 */
205void VBoxClipboardSvcImplFormatAnnounce(PVBOXCLIPBOARDCLIENTDATA pClientData, uint32_t u32Formats)
206{
207 LogFlowFunc(("pClientData=%p, u32Formats=%02X\n", pClientData, u32Formats));
208
209 ClipAnnounceFormatToX11(pClientData->State.pCtx->pBackend, u32Formats);
210}
211
212/** Structure describing a request for clipoard data from the guest. */
213struct _CLIPREADCBREQ
214{
215 /** Where to write the returned data to. */
216 void *pv;
217 /** The size of the buffer in pv */
218 uint32_t cb;
219 /** The actual size of the data written */
220 uint32_t *pcbActual;
221};
222
223/**
224 * Called when VBox wants to read the X11 clipboard.
225 *
226 * @returns VINF_SUCCESS on successful completion
227 * @returns VINF_HGCM_ASYNC_EXECUTE if the operation will complete
228 * asynchronously
229 * @returns iprt status code on failure
230 * @param pClientData Context information about the guest VM
231 * @param u32Format The format that the guest would like to receive the data in
232 * @param pv Where to write the data to
233 * @param cb The size of the buffer to write the data to
234 * @param pcbActual Where to write the actual size of the written data
235 *
236 * @note We always fail or complete asynchronously
237 * @note On success allocates a CLIPREADCBREQ structure which must be
238 * freed in ClipCompleteDataRequestFromX11 when it is called back from
239 * the backend code.
240 *
241 */
242int VBoxClipboardSvcImplReadData(PVBOXCLIPBOARDCLIENTDATA pClientData,
243 uint32_t u32Format, void *pv, uint32_t cb, uint32_t *pcbActual)
244{
245 LogFlowFunc(("pClientData=%p, u32Format=%02X, pv=%p, cb=%u, pcbActual=%p\n",
246 pClientData, u32Format, pv, cb, pcbActual));
247
248 int rc = VINF_SUCCESS;
249 CLIPREADCBREQ *pReq = (CLIPREADCBREQ *)RTMemAlloc(sizeof(CLIPREADCBREQ));
250 if (!pReq)
251 {
252 rc = VERR_NO_MEMORY;
253 }
254 else
255 {
256 pReq->pv = pv;
257 pReq->cb = cb;
258 pReq->pcbActual = pcbActual;
259 rc = ClipRequestDataFromX11(pClientData->State.pCtx->pBackend, u32Format, pReq);
260 if (RT_SUCCESS(rc))
261 rc = VINF_HGCM_ASYNC_EXECUTE;
262 }
263
264 LogFlowFuncLeaveRC(rc);
265 return rc;
266}
267
268/**
269 * Complete a request from VBox for the X11 clipboard data. The data should
270 * be written to the buffer provided in the initial request.
271 * @param pCtx request context information
272 * @param rc the completion status of the request
273 * @param pReq request
274 * @param pv address
275 * @param cb size
276 *
277 * @todo change this to deal with the buffer issues rather than offloading
278 * them onto the caller
279 */
280void ClipCompleteDataRequestFromX11(VBOXCLIPBOARDCONTEXT *pCtx, int rc,
281 CLIPREADCBREQ *pReq, void *pv, uint32_t cb)
282{
283 if (cb <= pReq->cb && cb != 0)
284 memcpy(pReq->pv, pv, cb);
285
286 RTMemFree(pReq);
287
288 vboxSvcClipboardCompleteReadData(pCtx->pClientData, rc, cb);
289}
290
291/** A request for clipboard data from VBox */
292struct _VBOXCLIPBOARDREQFROMVBOX
293{
294 /** Data received */
295 void *pv;
296 /** The size of the data */
297 uint32_t cb;
298 /** Format of the data */
299 uint32_t format;
300 /** A semaphore for waiting for the data */
301 RTSEMEVENT finished;
302};
303
304/** Wait for clipboard data requested from VBox to arrive. */
305static int clipWaitForDataFromVBox(VBOXCLIPBOARDCONTEXT *pCtx,
306 VBOXCLIPBOARDREQFROMVBOX *pReq,
307 uint32_t u32Format)
308{
309 int rc = VINF_SUCCESS;
310
311 LogFlowFunc(("pCtx=%p, pReq=%p, u32Format=%02X\n", pCtx, pReq, u32Format));
312
313 /* Request data from VBox */
314 vboxSvcClipboardReportMsg(pCtx->pClientData,
315 VBOX_SHARED_CLIPBOARD_HOST_MSG_READ_DATA,
316 u32Format);
317 /* Which will signal us when it is ready. We use a timeout here
318 * because we can't be sure that the guest will behave correctly.
319 */
320 rc = RTSemEventWait(pReq->finished, CLIPBOARD_TIMEOUT);
321
322 /* If the request hasn't yet completed then we cancel it. We use
323 * the critical section to prevent these operations colliding. */
324 RTCritSectEnter(&pCtx->clipboardMutex);
325
326 /* The data may have arrived between the semaphore timing out and
327 * our grabbing the mutex. */
328 if (rc == VERR_TIMEOUT && pReq->pv != NULL)
329 rc = VINF_SUCCESS;
330 if (pCtx->pReq == pReq)
331 pCtx->pReq = NULL;
332 Assert(pCtx->pReq == NULL);
333
334 RTCritSectLeave(&pCtx->clipboardMutex);
335
336 if (RT_SUCCESS(rc) && (pReq->pv == NULL))
337 rc = VERR_NO_DATA;
338
339 LogFlowFuncLeaveRC(rc);
340 return rc;
341}
342
343/** Post a request for clipboard data to VBox/the guest and wait for it to be
344 * completed. */
345static int clipRequestDataFromVBox(VBOXCLIPBOARDCONTEXT *pCtx, VBOXCLIPBOARDREQFROMVBOX *pReq, uint32_t u32Format)
346{
347 int rc = VINF_SUCCESS;
348
349 LogFlowFunc(("pCtx=%p, pReq=%p, u32Format=%02X\n", pCtx, pReq, u32Format));
350
351 /* Start by "posting" the request for the next invocation of
352 * vboxClipboardWriteData. */
353 RTCritSectEnter(&pCtx->clipboardMutex);
354
355 if (pCtx->pReq != NULL)
356 {
357 /* This would be a violation of the protocol, see the comments in the
358 * context structure definition. */
359 Assert(false);
360 rc = VERR_WRONG_ORDER;
361 }
362 else
363 pCtx->pReq = pReq;
364
365 RTCritSectLeave(&pCtx->clipboardMutex);
366
367 if (RT_SUCCESS(rc))
368 rc = clipWaitForDataFromVBox(pCtx, pReq, u32Format);
369
370 LogFlowFuncLeaveRC(rc);
371 return rc;
372}
373
374/**
375 * Send a request to VBox to transfer the contents of its clipboard to X11.
376 *
377 * @param pCtx Pointer to the host clipboard structure
378 * @param u32Format The format in which the data should be transferred
379 * @param ppv On success and if pcb > 0, this will point to a buffer
380 * to be freed with RTMemFree containing the data read.
381 * @param pcb On success, this contains the number of bytes of data
382 * returned
383 * @note Host glue code.
384 */
385int ClipRequestDataForX11(VBOXCLIPBOARDCONTEXT *pCtx, uint32_t u32Format, void **ppv, uint32_t *pcb)
386{
387 VBOXCLIPBOARDREQFROMVBOX request = { NULL, 0, 0, NIL_RTSEMEVENT };
388
389 LogFlowFunc(("pCtx=%p, u32Format=%02X, ppv=%p, pcb=%p\n", pCtx, u32Format, ppv, pcb));
390
391 if (pCtx->fShuttingDown)
392 {
393 /* The shared clipboard is disconnecting. */
394 LogRel(("Clipboard: Host requested guest clipboard data after guest had disconnected\n"));
395 return VERR_WRONG_ORDER;
396 }
397
398 int rc = RTSemEventCreate(&request.finished);
399 if (RT_SUCCESS(rc))
400 {
401 rc = clipRequestDataFromVBox(pCtx, &request, u32Format);
402 RTSemEventDestroy(request.finished);
403 }
404
405 if (RT_SUCCESS(rc))
406 {
407 *ppv = request.pv;
408 *pcb = request.cb;
409 }
410
411 LogFlowFuncLeaveRC(rc);
412 return rc;
413}
414
415/**
416 * Called when we have requested data from VBox and that data has arrived.
417 *
418 * @param pClientData Context information about the guest VM
419 * @param pv Buffer to which the data was written
420 * @param cb The size of the data written
421 * @param u32Format The format of the data written
422 * @note Host glue code
423 */
424void VBoxClipboardSvcImplWriteData(PVBOXCLIPBOARDCLIENTDATA pClientData,
425 void *pv, uint32_t cb, uint32_t u32Format)
426{
427 LogFlowFunc(("pClientData=%p, pv=%p (%.*ls), cb=%u, u32Format=%02X\n", pClientData, pv, cb / 2, pv, cb, u32Format));
428
429 VBOXCLIPBOARDCONTEXT *pCtx = pClientData->State.pCtx;
430
431 /* Grab the mutex and check whether there is a pending request for data. */
432 RTCritSectEnter(&pCtx->clipboardMutex);
433
434 VBOXCLIPBOARDREQFROMVBOX *pReq = pCtx->pReq;
435 if (pReq != NULL)
436 {
437 if (cb > 0)
438 {
439 pReq->pv = RTMemDup(pv, cb);
440 if (pReq->pv != NULL) /* NULL may also mean no memory... */
441 {
442 pReq->cb = cb;
443 pReq->format = u32Format;
444 }
445 }
446 /* Signal that the request has been completed. */
447 RTSemEventSignal(pReq->finished);
448 pCtx->pReq = NULL;
449 }
450
451 RTCritSectLeave(&pCtx->clipboardMutex);
452}
453
454#ifdef TESTCASE
455# include <iprt/initterm.h>
456# include <iprt/stream.h>
457
458# define TEST_NAME "tstClipboardX11-2"
459
460struct _CLIPBACKEND
461{
462 uint32_t formats;
463 struct _READDATA
464 {
465 uint32_t format;
466 int rc;
467 CLIPREADCBREQ *pReq;
468 } readData;
469 struct _COMPLETEREAD
470 {
471 int rc;
472 uint32_t cbActual;
473 } completeRead;
474 struct _WRITEDATA
475 {
476 void *pv;
477 uint32_t cb;
478 uint32_t format;
479 bool timeout;
480 } writeData;
481 struct _REPORTDATA
482 {
483 uint32_t format;
484 } reportData;
485};
486
487void vboxSvcClipboardReportMsg(PVBOXCLIPBOARDCLIENTDATA pClientData, uint32_t u32Msg, uint32_t u32Formats)
488{
489 RT_NOREF(u32Formats);
490 CLIPBACKEND *pBackend = pClientData->State.pCtx->pBackend;
491
492 if ((u32Msg == VBOX_SHARED_CLIPBOARD_HOST_MSG_READ_DATA)
493 && !pBackend->writeData.timeout)
494 VBoxClipboardSvcImplWriteData(pClientData, pBackend->writeData.pv,
495 pBackend->writeData.cb,
496 pBackend->writeData.format);
497 else
498 return;
499}
500
501void vboxSvcClipboardCompleteReadData(PVBOXCLIPBOARDCLIENTDATA pClientData, int rc, uint32_t cbActual)
502{
503 CLIPBACKEND *pBackend = pClientData->State.pCtx->pBackend;
504 pBackend->completeRead.rc = rc;
505 pBackend->completeRead.cbActual = cbActual;
506}
507
508CLIPBACKEND* ClipConstructX11(VBOXCLIPBOARDCONTEXT *pFrontend, bool)
509{
510 RT_NOREF(pFrontend);
511 return (CLIPBACKEND *)RTMemAllocZ(sizeof(CLIPBACKEND));
512}
513
514void ClipDestructX11(CLIPBACKEND *pBackend)
515{
516 RTMemFree(pBackend);
517}
518
519int ClipStartX11(CLIPBACKEND *pBackend, bool)
520{
521 RT_NOREF(pBackend);
522 return VINF_SUCCESS;
523}
524
525int ClipStopX11(CLIPBACKEND *pBackend)
526{
527 RT_NOREF1(pBackend);
528 return VINF_SUCCESS;
529}
530
531int ClipAnnounceFormatToX11(CLIPBACKEND *pBackend, VBOXCLIPBOARDFORMATS vboxFormats)
532{
533 pBackend->formats = vboxFormats;
534 return VINF_SUCCESS;
535}
536
537extern int ClipRequestDataFromX11(CLIPBACKEND *pBackend, VBOXCLIPBOARDFORMAT vboxFormat,
538 CLIPREADCBREQ *pReq)
539{
540 pBackend->readData.format = vboxFormat;
541 pBackend->readData.pReq = pReq;
542 return pBackend->readData.rc;
543}
544
545int main()
546{
547 VBOXCLIPBOARDCLIENTDATA client;
548 unsigned cErrors = 0;
549 int rc = RTR3InitExeNoArguments(0);
550 RTPrintf(TEST_NAME ": TESTING\n");
551 AssertRCReturn(rc, 1);
552 rc = VBoxClipboardSvcImplConnect(&client, false);
553 CLIPBACKEND *pBackend = client.State.pCtx->pBackend;
554 AssertRCReturn(rc, 1);
555 VBoxClipboardSvcImplFormatAnnounce(&client,
556 VBOX_SHARED_CLIPBOARD_FMT_UNICODETEXT);
557 if (pBackend->formats != VBOX_SHARED_CLIPBOARD_FMT_UNICODETEXT)
558 {
559 RTPrintf(TEST_NAME ": vboxClipboardFormatAnnounce failed with VBOX_SHARED_CLIPBOARD_FMT_UNICODETEXT\n");
560 ++cErrors;
561 }
562 pBackend->readData.rc = VINF_SUCCESS;
563 client.State.asyncRead.callHandle = (VBOXHGCMCALLHANDLE)pBackend;
564 client.State.asyncRead.paParms = (VBOXHGCMSVCPARM *)&client;
565 uint32_t u32Dummy;
566 rc = VBoxClipboardSvcImplReadData(&client, VBOX_SHARED_CLIPBOARD_FMT_UNICODETEXT,
567 &u32Dummy, 42, &u32Dummy);
568 if (rc != VINF_HGCM_ASYNC_EXECUTE)
569 {
570 RTPrintf(TEST_NAME ": vboxClipboardReadData returned %Rrc\n", rc);
571 ++cErrors;
572 }
573 else
574 {
575 if ( pBackend->readData.format != VBOX_SHARED_CLIPBOARD_FMT_UNICODETEXT
576 || pBackend->readData.pReq->pv != &u32Dummy
577 || pBackend->readData.pReq->cb != 42
578 || pBackend->readData.pReq->pcbActual != &u32Dummy)
579 {
580 RTPrintf(TEST_NAME ": format=%u, pReq->pv=%p, pReq->cb=%u, pReq->pcbActual=%p\n",
581 pBackend->readData.format, pBackend->readData.pReq->pv,
582 pBackend->readData.pReq->cb,
583 pBackend->readData.pReq->pcbActual);
584 ++cErrors;
585 }
586 else
587 {
588 ClipCompleteDataRequestFromX11(client.State.pCtx, VERR_NO_DATA,
589 pBackend->readData.pReq, NULL, 43);
590 if ( pBackend->completeRead.rc != VERR_NO_DATA
591 || pBackend->completeRead.cbActual != 43)
592 {
593 RTPrintf(TEST_NAME ": rc=%Rrc, cbActual=%u\n",
594 pBackend->completeRead.rc,
595 pBackend->completeRead.cbActual);
596 ++cErrors;
597 }
598 }
599 }
600 void *pv;
601 uint32_t cb;
602 pBackend->writeData.pv = (void *)"testing";
603 pBackend->writeData.cb = sizeof("testing");
604 pBackend->writeData.format = 1234;
605 pBackend->reportData.format = 4321; /* XX this should be handled! */
606 rc = ClipRequestDataForX11(client.State.pCtx, 23, &pv, &cb);
607 if ( rc != VINF_SUCCESS
608 || strcmp((const char *)pv, "testing") != 0
609 || cb != sizeof("testing"))
610 {
611 RTPrintf("rc=%Rrc, pv=%p, cb=%u\n", rc, pv, cb);
612 ++cErrors;
613 }
614 else
615 RTMemFree(pv);
616 pBackend->writeData.timeout = true;
617 rc = ClipRequestDataForX11(client.State.pCtx, 23, &pv, &cb);
618 if (rc != VERR_TIMEOUT)
619 {
620 RTPrintf("rc=%Rrc, expected VERR_TIMEOUT\n", rc);
621 ++cErrors;
622 }
623 pBackend->writeData.pv = NULL;
624 pBackend->writeData.cb = 0;
625 pBackend->writeData.timeout = false;
626 rc = ClipRequestDataForX11(client.State.pCtx, 23, &pv, &cb);
627 if (rc != VERR_NO_DATA)
628 {
629 RTPrintf("rc=%Rrc, expected VERR_NO_DATA\n", rc);
630 ++cErrors;
631 }
632 /* Data arriving after a timeout should *not* cause any segfaults or
633 * memory leaks. Check with Valgrind! */
634 VBoxClipboardSvcImplWriteData(&client, (void *)"tested", sizeof("tested"), 999);
635 VBoxClipboardSvcImplDisconnect(&client);
636 if (cErrors > 0)
637 RTPrintf(TEST_NAME ": errors: %u\n", cErrors);
638 return cErrors > 0 ? 1 : 0;
639}
640#endif /* TESTCASE */
641
Note: See TracBrowser for help on using the repository browser.

© 2024 Oracle Support Privacy / Do Not Sell My Info Terms of Use Trademark Policy Automated Access Etiquette