VirtualBox

source: vbox/trunk/src/VBox/HostServices/SharedClipboard/x11-clipboard.cpp@ 76188

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

HGCM,Main,SharedFolder,SharedClipboard,GuestProperties: Added HGCM service helpers for statistics and dbg info registration/deregistration. A PUVM is passed to HGCMService (where the helpers are implemented) when the service is loaded. Since this drags in both dbg.h and stam.h, LOG_GROUP defines now have to be at the top of the include list as everywhere else (i.e. hgcmsvc.h will define LOG_GROUP default by dragging in log.h). Added generic statistics of HGCM message processing and function level statistics to the shared folder service. [missing files, ++]

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