VirtualBox

source: vbox/trunk/src/VBox/GuestHost/SharedClipboard/clipboard-x11.cpp@ 99663

最後變更 在這個檔案從99663是 99564,由 vboxsync 提交於 23 月 前

Guest Additions/VBoxClient: Initial commit for bugref:10427. Xinerama stuff untested.

  • 屬性 svn:eol-style 設為 native
  • 屬性 svn:keywords 設為 Author Date Id Revision
檔案大小: 83.6 KB
 
1/** @file
2 * Shared Clipboard: Common X11 code.
3 */
4
5/*
6 * Copyright (C) 2006-2023 Oracle and/or its affiliates.
7 *
8 * This file is part of VirtualBox base platform packages, as
9 * available from https://www.alldomusa.eu.org.
10 *
11 * This program is free software; you can redistribute it and/or
12 * modify it under the terms of the GNU General Public License
13 * as published by the Free Software Foundation, in version 3 of the
14 * License.
15 *
16 * This program is distributed in the hope that it will be useful, but
17 * WITHOUT ANY WARRANTY; without even the implied warranty of
18 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
19 * General Public License for more details.
20 *
21 * You should have received a copy of the GNU General Public License
22 * along with this program; if not, see <https://www.gnu.org/licenses>.
23 *
24 * SPDX-License-Identifier: GPL-3.0-only
25 */
26
27/* Note: to automatically run regression tests on the Shared Clipboard,
28 * execute the tstClipboardGH-X11 testcase. If you often make changes to the
29 * clipboard code, adding the line
30 *
31 * OTHERS += $(PATH_tstClipboardGH-X11)/tstClipboardGH-X11.run
32 *
33 * to LocalConfig.kmk will cause the tests to be run every time the code is
34 * changed.
35 */
36
37
38/*********************************************************************************************************************************
39* Header Files *
40*********************************************************************************************************************************/
41#define LOG_GROUP LOG_GROUP_SHARED_CLIPBOARD
42
43#include <errno.h>
44
45#include <dlfcn.h>
46#include <fcntl.h>
47#include <unistd.h>
48
49#ifdef RT_OS_SOLARIS
50#include <tsol/label.h>
51#endif
52
53#include <X11/Xlib.h>
54#include <X11/Xatom.h>
55#include <X11/Intrinsic.h>
56#include <X11/Shell.h>
57#include <X11/Xproto.h>
58#include <X11/StringDefs.h>
59
60#include <iprt/assert.h>
61#include <iprt/types.h>
62#include <iprt/mem.h>
63#include <iprt/semaphore.h>
64#include <iprt/thread.h>
65#include <iprt/utf16.h>
66#include <iprt/uri.h>
67
68#ifdef VBOX_WITH_SHARED_CLIPBOARD_TRANSFERS
69# include <iprt/cpp/list.h>
70# include <iprt/cpp/ministring.h>
71# include <VBox/GuestHost/SharedClipboard-transfers.h>
72#endif
73
74#include <VBox/log.h>
75#include <VBox/version.h>
76
77#include <VBox/GuestHost/SharedClipboard.h>
78#include <VBox/GuestHost/SharedClipboard-x11.h>
79#include <VBox/GuestHost/clipboard-helper.h>
80#include <VBox/HostServices/VBoxClipboardSvc.h>
81
82/** Own macro for declaring function visibility / linkage based on whether this
83 * code runs as part of test cases or not. */
84#ifdef TESTCASE
85# define SHCL_X11_DECL(x) x
86#else
87# define SHCL_X11_DECL(x) static x
88#endif
89
90
91/*********************************************************************************************************************************
92* Externals *
93*********************************************************************************************************************************/
94#ifdef TESTCASE
95extern void tstThreadScheduleCall(void (*proc)(void *, void *), void *client_data);
96extern void tstClipRequestData(SHCLX11CTX* pCtx, SHCLX11FMTIDX target, void *closure);
97extern void tstRequestTargets(SHCLX11CTX* pCtx);
98#endif
99
100
101/*********************************************************************************************************************************
102* Prototypes *
103*********************************************************************************************************************************/
104class formats;
105SHCL_X11_DECL(Atom) clipGetAtom(PSHCLX11CTX pCtx, const char *pszName);
106SHCL_X11_DECL(void) clipQueryX11Targets(PSHCLX11CTX pCtx);
107
108static int clipInitInternal(PSHCLX11CTX pCtx);
109static void clipUninitInternal(PSHCLX11CTX pCtx);
110
111
112/*********************************************************************************************************************************
113* Global Variables *
114*********************************************************************************************************************************/
115
116/**
117 * The table maps X11 names to data formats
118 * and to the corresponding VBox clipboard formats.
119 */
120SHCL_X11_DECL(SHCLX11FMTTABLE) g_aFormats[] =
121{
122 { "INVALID", SHCLX11FMT_INVALID, VBOX_SHCL_FMT_NONE },
123
124 { "UTF8_STRING", SHCLX11FMT_UTF8, VBOX_SHCL_FMT_UNICODETEXT },
125 { "text/plain;charset=UTF-8", SHCLX11FMT_UTF8, VBOX_SHCL_FMT_UNICODETEXT },
126 { "text/plain;charset=utf-8", SHCLX11FMT_UTF8, VBOX_SHCL_FMT_UNICODETEXT },
127 { "STRING", SHCLX11FMT_TEXT, VBOX_SHCL_FMT_UNICODETEXT },
128 { "TEXT", SHCLX11FMT_TEXT, VBOX_SHCL_FMT_UNICODETEXT },
129 { "text/plain", SHCLX11FMT_TEXT, VBOX_SHCL_FMT_UNICODETEXT },
130
131 { "text/html", SHCLX11FMT_HTML, VBOX_SHCL_FMT_HTML },
132 { "text/html;charset=utf-8", SHCLX11FMT_HTML, VBOX_SHCL_FMT_HTML },
133 { "application/x-moz-nativehtml", SHCLX11FMT_HTML, VBOX_SHCL_FMT_HTML },
134
135 { "image/bmp", SHCLX11FMT_BMP, VBOX_SHCL_FMT_BITMAP },
136 { "image/x-bmp", SHCLX11FMT_BMP, VBOX_SHCL_FMT_BITMAP },
137 { "image/x-MS-bmp", SHCLX11FMT_BMP, VBOX_SHCL_FMT_BITMAP },
138 /** @todo Inkscape exports image/png but not bmp... */
139
140#ifdef VBOX_WITH_SHARED_CLIPBOARD_TRANSFERS
141 { "text/uri-list", SHCLX11FMT_URI_LIST, VBOX_SHCL_FMT_URI_LIST },
142 { "x-special/gnome-copied-files", SHCLX11FMT_URI_LIST, VBOX_SHCL_FMT_URI_LIST },
143 { "x-special/nautilus-clipboard", SHCLX11FMT_URI_LIST, VBOX_SHCL_FMT_URI_LIST },
144 { "application/x-kde-cutselection", SHCLX11FMT_URI_LIST, VBOX_SHCL_FMT_URI_LIST },
145 /** @todo Anything else we need to add here? */
146 /** @todo Add Wayland / Weston support. */
147#endif
148};
149
150
151#ifdef TESTCASE
152# ifdef RT_OS_SOLARIS_10
153char XtStrings [] = "";
154WidgetClassRec* applicationShellWidgetClass;
155char XtShellStrings [] = "";
156int XmbTextPropertyToTextList(
157 Display* /* display */,
158 XTextProperty* /* text_prop */,
159 char*** /* list_return */,
160 int* /* count_return */
161)
162{
163 return 0;
164}
165# else /* !RT_OS_SOLARIS_10 */
166const char XtStrings [] = "";
167_WidgetClassRec* applicationShellWidgetClass;
168const char XtShellStrings [] = "";
169# endif /* RT_OS_SOLARIS_10 */
170#else /* !TESTCASE */
171# ifdef VBOX_WITH_VBOXCLIENT_LAZY_LOAD
172/* Defines needed for lazy loading global data from the shared objects (.so).
173 * See r157060. */
174DECLASM(WidgetClass * ) LazyGetPtr_applicationShellWidgetClass(void);
175#define applicationShellWidgetClass (*LazyGetPtr_applicationShellWidgetClass())
176DECLASM(const char *) LazyGetPtr_XtStrings(void);
177#define XtStrings (LazyGetPtr_XtStrings())
178# endif
179#endif /* TESTCASE */
180
181
182/*********************************************************************************************************************************
183* Defines *
184*********************************************************************************************************************************/
185
186#define SHCL_MAX_X11_FORMATS RT_ELEMENTS(g_aFormats)
187
188
189/*********************************************************************************************************************************
190* Internal structures *
191*********************************************************************************************************************************/
192
193/**
194 * A structure containing information about where to store a request
195 * for the X11 clipboard contents.
196 */
197typedef struct _CLIPREADX11CBREQ
198{
199 /** The format VBox would like the data in. */
200 SHCLFORMAT uFmtVBox;
201 /** The format we requested from X11. */
202 SHCLX11FMTIDX idxFmtX11;
203 /** The clipboard context this request is associated with. */
204 SHCLX11CTX *pCtx;
205 /** The request structure passed in from the backend. */
206 CLIPREADCBREQ *pReq;
207} CLIPREADX11CBREQ;
208
209
210
211#ifdef TESTCASE
212/**
213 * Return the max. number of elements in the X11 format table.
214 * Used by the testing code in tstClipboardGH-X11.cpp
215 * which cannot use RT_ELEMENTS(g_aFormats) directly.
216 *
217 * @return size_t The number of elements in the g_aFormats array.
218 */
219SHCL_X11_DECL(size_t) clipReportMaxX11Formats(void)
220{
221 return (RT_ELEMENTS(g_aFormats));
222}
223#endif
224
225/**
226 * Returns the atom corresponding to a supported X11 format.
227 *
228 * @returns Found atom to the corresponding X11 format.
229 * @param pCtx The X11 clipboard context to use.
230 * @param uFmtIdx Format index to look up atom for.
231 */
232static Atom clipAtomForX11Format(PSHCLX11CTX pCtx, SHCLX11FMTIDX uFmtIdx)
233{
234 AssertReturn(uFmtIdx < RT_ELEMENTS(g_aFormats), 0);
235 return clipGetAtom(pCtx, g_aFormats[uFmtIdx].pcszAtom);
236}
237
238/**
239 * Returns the SHCLX11FMT corresponding to a supported X11 format.
240 *
241 * @return SHCLX11FMT for a specific format index.
242 * @param uFmtIdx Format index to look up SHCLX11CLIPFMT for.
243 */
244SHCL_X11_DECL(SHCLX11FMT) clipRealFormatForX11Format(SHCLX11FMTIDX uFmtIdx)
245{
246 AssertReturn(uFmtIdx < RT_ELEMENTS(g_aFormats), SHCLX11FMT_INVALID);
247 return g_aFormats[uFmtIdx].enmFmtX11;
248}
249
250/**
251 * Returns the VBox format corresponding to a supported X11 format.
252 *
253 * @return SHCLFORMAT for a specific format index.
254 * @param uFmtIdx Format index to look up VBox format for.
255 */
256static SHCLFORMAT clipVBoxFormatForX11Format(SHCLX11FMTIDX uFmtIdx)
257{
258 AssertReturn(uFmtIdx < RT_ELEMENTS(g_aFormats), VBOX_SHCL_FMT_NONE);
259 return g_aFormats[uFmtIdx].uFmtVBox;
260}
261
262/**
263 * Looks up the X11 format matching a given X11 atom.
264 *
265 * @returns The format on success, NIL_CLIPX11FORMAT on failure.
266 * @param pCtx The X11 clipboard context to use.
267 * @param atomFormat Atom to look up X11 format for.
268 */
269static SHCLX11FMTIDX clipFindX11FormatByAtom(PSHCLX11CTX pCtx, Atom atomFormat)
270{
271 for (unsigned i = 0; i < RT_ELEMENTS(g_aFormats); ++i)
272 if (clipAtomForX11Format(pCtx, i) == atomFormat)
273 {
274 LogFlowFunc(("Returning index %u for atom '%s'\n", i, g_aFormats[i].pcszAtom));
275 return i;
276 }
277 return NIL_CLIPX11FORMAT;
278}
279
280/**
281 * Enumerates supported X11 clipboard formats corresponding to given VBox formats.
282 *
283 * @returns The next matching X11 format index in the list, or NIL_CLIPX11FORMAT if there are no more.
284 * @param uFormatsVBox VBox formats to enumerate supported X11 clipboard formats for.
285 * @param lastFmtIdx The value returned from the last call of this function.
286 * Use NIL_CLIPX11FORMAT to start the enumeration.
287 */
288static SHCLX11FMTIDX clipEnumX11Formats(SHCLFORMATS uFormatsVBox,
289 SHCLX11FMTIDX lastFmtIdx)
290{
291 for (unsigned i = lastFmtIdx + 1; i < RT_ELEMENTS(g_aFormats); ++i)
292 {
293 if (uFormatsVBox & clipVBoxFormatForX11Format(i))
294 return i;
295 }
296
297 return NIL_CLIPX11FORMAT;
298}
299
300/**
301 * Array of structures for mapping Xt widgets to context pointers. We
302 * need this because the widget clipboard callbacks do not pass user data.
303 */
304static struct
305{
306 /** Pointer to widget we want to associate the context with. */
307 Widget pWidget;
308 /** Pointer to X11 context associated with the widget. */
309 PSHCLX11CTX pCtx;
310} g_aContexts[VBOX_SHARED_CLIPBOARD_X11_CONNECTIONS_MAX];
311
312/**
313 * Registers a new X11 clipboard context.
314 *
315 * @returns VBox status code.
316 * @param pCtx The X11 clipboard context to use.
317 */
318static int clipRegisterContext(PSHCLX11CTX pCtx)
319{
320 AssertPtrReturn(pCtx, VERR_INVALID_PARAMETER);
321
322 bool fFound = false;
323
324 Widget pWidget = pCtx->pWidget;
325 AssertReturn(pWidget != NULL, VERR_INVALID_PARAMETER);
326
327 for (unsigned i = 0; i < RT_ELEMENTS(g_aContexts); ++i)
328 {
329 AssertReturn( (g_aContexts[i].pWidget != pWidget)
330 && (g_aContexts[i].pCtx != pCtx), VERR_WRONG_ORDER);
331 if (g_aContexts[i].pWidget == NULL && !fFound)
332 {
333 AssertReturn(g_aContexts[i].pCtx == NULL, VERR_INTERNAL_ERROR);
334 g_aContexts[i].pWidget = pWidget;
335 g_aContexts[i].pCtx = pCtx;
336 fFound = true;
337 }
338 }
339
340 return fFound ? VINF_SUCCESS : VERR_OUT_OF_RESOURCES;
341}
342
343/**
344 * Unregister an X11 clipboard context.
345 *
346 * @param pCtx The X11 clipboard context to use.
347 */
348static void clipUnregisterContext(PSHCLX11CTX pCtx)
349{
350 AssertPtrReturnVoid(pCtx);
351
352 Widget pWidget = pCtx->pWidget;
353 AssertPtrReturnVoid(pWidget);
354
355 bool fFound = false;
356 for (unsigned i = 0; i < RT_ELEMENTS(g_aContexts); ++i)
357 {
358 Assert(!fFound || g_aContexts[i].pWidget != pWidget);
359 if (g_aContexts[i].pWidget == pWidget)
360 {
361 Assert(g_aContexts[i].pCtx != NULL);
362 g_aContexts[i].pWidget = NULL;
363 g_aContexts[i].pCtx = NULL;
364 fFound = true;
365 }
366 }
367}
368
369/**
370 * Finds a X11 clipboard context for a specific X11 widget.
371 *
372 * @returns Pointer to associated X11 clipboard context if found, or NULL if not found.
373 * @param pWidget X11 widget to return X11 clipboard context for.
374 */
375static PSHCLX11CTX clipLookupContext(Widget pWidget)
376{
377 AssertPtrReturn(pWidget, NULL);
378
379 for (unsigned i = 0; i < RT_ELEMENTS(g_aContexts); ++i)
380 {
381 if (g_aContexts[i].pWidget == pWidget)
382 {
383 Assert(g_aContexts[i].pCtx != NULL);
384 return g_aContexts[i].pCtx;
385 }
386 }
387
388 return NULL;
389}
390
391/**
392 * Converts an atom name string to an X11 atom, looking it up in a cache before asking the server.
393 *
394 * @returns Found X11 atom.
395 * @param pCtx The X11 clipboard context to use.
396 * @param pcszName Name of atom to return atom for.
397 */
398SHCL_X11_DECL(Atom) clipGetAtom(PSHCLX11CTX pCtx, const char *pcszName)
399{
400 AssertPtrReturn(pcszName, None);
401 return XInternAtom(XtDisplay(pCtx->pWidget), pcszName, False);
402}
403
404/** String written to the wakeup pipe. */
405#define WAKE_UP_STRING "WakeUp!"
406/** Length of the string written. */
407#define WAKE_UP_STRING_LEN ( sizeof(WAKE_UP_STRING) - 1 )
408
409/**
410 * Schedules a function call to run on the Xt event thread by passing it to
411 * the application context as a 0ms timeout and waking up the event loop by
412 * writing to the wakeup pipe which it monitors.
413 */
414static int clipThreadScheduleCall(PSHCLX11CTX pCtx,
415 void (*proc)(void *, void *),
416 void *client_data)
417{
418 LogFlowFunc(("proc=%p, client_data=%p\n", proc, client_data));
419
420#ifndef TESTCASE
421 AssertReturn(pCtx, VERR_INVALID_POINTER);
422 AssertReturn(pCtx->pAppContext, VERR_INVALID_POINTER);
423
424 XtAppAddTimeOut(pCtx->pAppContext, 0, (XtTimerCallbackProc)proc,
425 (XtPointer)client_data);
426 ssize_t cbWritten = write(pCtx->wakeupPipeWrite, WAKE_UP_STRING, WAKE_UP_STRING_LEN);
427 Assert(cbWritten == WAKE_UP_STRING_LEN);
428 RT_NOREF(cbWritten);
429#else
430 RT_NOREF(pCtx);
431 tstThreadScheduleCall(proc, client_data);
432#endif
433
434 LogFlowFuncLeaveRC(VINF_SUCCESS);
435 return VINF_SUCCESS;
436}
437
438/**
439 * Reports the formats currently supported by the X11 clipboard to VBox.
440 *
441 * @note Runs in Xt event thread.
442 *
443 * @param pCtx The X11 clipboard context to use.
444 */
445static void clipReportFormatsToVBox(PSHCLX11CTX pCtx)
446{
447 SHCLFORMATS vboxFmt = clipVBoxFormatForX11Format(pCtx->idxFmtText);
448 vboxFmt |= clipVBoxFormatForX11Format(pCtx->idxFmtBmp);
449 vboxFmt |= clipVBoxFormatForX11Format(pCtx->idxFmtHTML);
450#ifdef VBOX_WITH_SHARED_CLIPBOARD_TRANSFERS
451 vboxFmt |= clipVBoxFormatForX11Format(pCtx->idxFmtURI);
452#endif
453
454 LogFlowFunc(("idxFmtText=%u ('%s'), idxFmtBmp=%u ('%s'), idxFmtHTML=%u ('%s')",
455 pCtx->idxFmtText, g_aFormats[pCtx->idxFmtText].pcszAtom,
456 pCtx->idxFmtBmp, g_aFormats[pCtx->idxFmtBmp].pcszAtom,
457 pCtx->idxFmtHTML, g_aFormats[pCtx->idxFmtHTML].pcszAtom));
458#ifdef VBOX_WITH_SHARED_CLIPBOARD_TRANSFERS
459 LogFlowFunc((", idxFmtURI=%u ('%s')", pCtx->idxFmtURI, g_aFormats[pCtx->idxFmtURI].pcszAtom));
460#endif
461 LogFlow((" -> vboxFmt=%#x\n", vboxFmt));
462
463#ifdef LOG_ENABLED
464 char *pszFmts = ShClFormatsToStrA(vboxFmt);
465 AssertPtrReturnVoid(pszFmts);
466 LogRel2(("Shared Clipboard: X11 reported available VBox formats '%s'\n", pszFmts));
467 RTStrFree(pszFmts);
468#endif
469
470 pCtx->Callbacks.pfnReportFormats(pCtx->pFrontend, vboxFmt, NULL /* pvUser */);
471}
472
473/**
474 * Forgets which formats were previously in the X11 clipboard. Called when we
475 * grab the clipboard.
476 *
477 * @param pCtx The X11 clipboard context to use.
478 */
479static void clipResetX11Formats(PSHCLX11CTX pCtx)
480{
481 LogFlowFuncEnter();
482
483 pCtx->idxFmtText = 0;
484 pCtx->idxFmtBmp = 0;
485 pCtx->idxFmtHTML = 0;
486#ifdef VBOX_WITH_SHARED_CLIPBOARD_TRANSFERS
487 pCtx->idxFmtURI = 0;
488#endif
489}
490
491/**
492 * Tells VBox that X11 currently has nothing in its clipboard.
493 *
494 * @param pCtx The X11 clipboard context to use.
495 */
496SHCL_X11_DECL(void) clipReportEmpty(PSHCLX11CTX pCtx)
497{
498 clipResetX11Formats(pCtx);
499 clipReportFormatsToVBox(pCtx);
500}
501
502/**
503 * Go through an array of X11 clipboard targets to see if they contain a text
504 * format we can support, and if so choose the ones we prefer (e.g. we like
505 * UTF-8 better than plain text).
506 *
507 * @return Index to supported X clipboard format.
508 * @param pCtx The X11 clipboard context to use.
509 * @param paIdxFmtTargets The list of targets.
510 * @param cTargets The size of the list in @a pTargets.
511 */
512SHCL_X11_DECL(SHCLX11FMTIDX) clipGetTextFormatFromTargets(PSHCLX11CTX pCtx,
513 SHCLX11FMTIDX *paIdxFmtTargets,
514 size_t cTargets)
515{
516 AssertPtrReturn(pCtx, NIL_CLIPX11FORMAT);
517 AssertReturn(RT_VALID_PTR(paIdxFmtTargets) || cTargets == 0, NIL_CLIPX11FORMAT);
518
519 SHCLX11FMTIDX idxFmtText = NIL_CLIPX11FORMAT;
520 SHCLX11FMT fmtTextX11 = SHCLX11FMT_INVALID;
521 for (unsigned i = 0; i < cTargets; ++i)
522 {
523 SHCLX11FMTIDX idxFmt = paIdxFmtTargets[i];
524 if (idxFmt != NIL_CLIPX11FORMAT)
525 {
526 if ( (clipVBoxFormatForX11Format(idxFmt) == VBOX_SHCL_FMT_UNICODETEXT)
527 && fmtTextX11 < clipRealFormatForX11Format(idxFmt))
528 {
529 fmtTextX11 = clipRealFormatForX11Format(idxFmt);
530 idxFmtText = idxFmt;
531 }
532 }
533 }
534 return idxFmtText;
535}
536
537/**
538 * Goes through an array of X11 clipboard targets to see if they contain a bitmap
539 * format we can support, and if so choose the ones we prefer (e.g. we like
540 * BMP better than PNG because we don't have to convert).
541 *
542 * @return Supported X clipboard format.
543 * @param pCtx The X11 clipboard context to use.
544 * @param paIdxFmtTargets The list of targets.
545 * @param cTargets The size of the list in @a pTargets.
546 */
547static SHCLX11FMTIDX clipGetBitmapFormatFromTargets(PSHCLX11CTX pCtx,
548 SHCLX11FMTIDX *paIdxFmtTargets,
549 size_t cTargets)
550{
551 AssertPtrReturn(pCtx, NIL_CLIPX11FORMAT);
552 AssertReturn(RT_VALID_PTR(paIdxFmtTargets) || cTargets == 0, NIL_CLIPX11FORMAT);
553
554 SHCLX11FMTIDX idxFmtBmp = NIL_CLIPX11FORMAT;
555 SHCLX11FMT fmtBmpX11 = SHCLX11FMT_INVALID;
556 for (unsigned i = 0; i < cTargets; ++i)
557 {
558 SHCLX11FMTIDX idxFmt = paIdxFmtTargets[i];
559 if (idxFmt != NIL_CLIPX11FORMAT)
560 {
561 if ( (clipVBoxFormatForX11Format(idxFmt) == VBOX_SHCL_FMT_BITMAP)
562 && fmtBmpX11 < clipRealFormatForX11Format(idxFmt))
563 {
564 fmtBmpX11 = clipRealFormatForX11Format(idxFmt);
565 idxFmtBmp = idxFmt;
566 }
567 }
568 }
569 return idxFmtBmp;
570}
571
572/**
573 * Goes through an array of X11 clipboard targets to see if they contain a HTML
574 * format we can support, and if so choose the ones we prefer.
575 *
576 * @return Supported X clipboard format.
577 * @param pCtx The X11 clipboard context to use.
578 * @param paIdxFmtTargets The list of targets.
579 * @param cTargets The size of the list in @a pTargets.
580 */
581static SHCLX11FMTIDX clipGetHtmlFormatFromTargets(PSHCLX11CTX pCtx,
582 SHCLX11FMTIDX *paIdxFmtTargets,
583 size_t cTargets)
584{
585 AssertPtrReturn(pCtx, NIL_CLIPX11FORMAT);
586 AssertReturn(RT_VALID_PTR(paIdxFmtTargets) || cTargets == 0, NIL_CLIPX11FORMAT);
587
588 SHCLX11FMTIDX idxFmtHTML = NIL_CLIPX11FORMAT;
589 SHCLX11FMT fmxHTMLX11 = SHCLX11FMT_INVALID;
590 for (unsigned i = 0; i < cTargets; ++i)
591 {
592 SHCLX11FMTIDX idxFmt = paIdxFmtTargets[i];
593 if (idxFmt != NIL_CLIPX11FORMAT)
594 {
595 if ( (clipVBoxFormatForX11Format(idxFmt) == VBOX_SHCL_FMT_HTML)
596 && fmxHTMLX11 < clipRealFormatForX11Format(idxFmt))
597 {
598 fmxHTMLX11 = clipRealFormatForX11Format(idxFmt);
599 idxFmtHTML = idxFmt;
600 }
601 }
602 }
603 return idxFmtHTML;
604}
605
606# ifdef VBOX_WITH_SHARED_CLIPBOARD_TRANSFERS
607/**
608 * Goes through an array of X11 clipboard targets to see if they contain an URI list
609 * format we can support, and if so choose the ones we prefer.
610 *
611 * @return Supported X clipboard format.
612 * @param pCtx The X11 clipboard context to use.
613 * @param paIdxFmtTargets The list of targets.
614 * @param cTargets The size of the list in @a pTargets.
615 */
616static SHCLX11FMTIDX clipGetURIListFormatFromTargets(PSHCLX11CTX pCtx,
617 SHCLX11FMTIDX *paIdxFmtTargets,
618 size_t cTargets)
619{
620 AssertPtrReturn(pCtx, NIL_CLIPX11FORMAT);
621 AssertReturn(RT_VALID_PTR(paIdxFmtTargets) || cTargets == 0, NIL_CLIPX11FORMAT);
622
623 SHCLX11FMTIDX idxFmtURI = NIL_CLIPX11FORMAT;
624 SHCLX11FMT fmtURIX11 = SHCLX11FMT_INVALID;
625 for (unsigned i = 0; i < cTargets; ++i)
626 {
627 SHCLX11FMTIDX idxFmt = paIdxFmtTargets[i];
628 if (idxFmt != NIL_CLIPX11FORMAT)
629 {
630 if ( (clipVBoxFormatForX11Format(idxFmt) == VBOX_SHCL_FMT_URI_LIST)
631 && fmtURIX11 < clipRealFormatForX11Format(idxFmt))
632 {
633 fmtURIX11 = clipRealFormatForX11Format(idxFmt);
634 idxFmtURI = idxFmt;
635 }
636 }
637 }
638 return idxFmtURI;
639}
640# endif /* VBOX_WITH_SHARED_CLIPBOARD_TRANSFERS */
641
642/**
643 * Goes through an array of X11 clipboard targets to see if we can support any
644 * of them and if relevant to choose the ones we prefer (e.g. we like Utf8
645 * better than plain text).
646 *
647 * @param pCtx The X11 clipboard context to use.
648 * @param paIdxFmtTargets The list of targets.
649 * @param cTargets The size of the list in @a pTargets.
650 */
651static void clipGetFormatsFromTargets(PSHCLX11CTX pCtx,
652 SHCLX11FMTIDX *paIdxFmtTargets, size_t cTargets)
653{
654 AssertPtrReturnVoid(pCtx);
655 AssertPtrReturnVoid(paIdxFmtTargets);
656
657 SHCLX11FMTIDX idxFmtText = clipGetTextFormatFromTargets(pCtx, paIdxFmtTargets, cTargets);
658 if (pCtx->idxFmtText != idxFmtText)
659 pCtx->idxFmtText = idxFmtText;
660
661 pCtx->idxFmtBmp = SHCLX11FMT_INVALID; /* not yet supported */ /** @todo r=andy Check this. */
662 SHCLX11FMTIDX idxFmtBmp = clipGetBitmapFormatFromTargets(pCtx, paIdxFmtTargets, cTargets);
663 if (pCtx->idxFmtBmp != idxFmtBmp)
664 pCtx->idxFmtBmp = idxFmtBmp;
665
666 SHCLX11FMTIDX idxFmtHTML = clipGetHtmlFormatFromTargets(pCtx, paIdxFmtTargets, cTargets);
667 if (pCtx->idxFmtHTML != idxFmtHTML)
668 pCtx->idxFmtHTML = idxFmtHTML;
669
670#ifdef VBOX_WITH_SHARED_CLIPBOARD_TRANSFERS
671 SHCLX11FMTIDX idxFmtURI = clipGetURIListFormatFromTargets(pCtx, paIdxFmtTargets, cTargets);
672 if (pCtx->idxFmtURI != idxFmtURI)
673 pCtx->idxFmtURI = idxFmtURI;
674#endif
675}
676
677#ifdef VBOX_WITH_SHARED_CLIPBOARD_XT_BUSY
678DECLINLINE(bool) clipGetXtBusy(PSHCLX11CTX pCtx)
679{
680 LogFlowFunc(("fXtBusy=%RTbool, fXtNeedsUpdate=%RTbool\n", pCtx->fXtBusy, pCtx->fXtNeedsUpdate));
681 return pCtx->fXtBusy;
682}
683
684DECLINLINE(bool) clipGetXtNeedsUpdate(PSHCLX11CTX pCtx)
685{
686 LogFlowFunc(("fXtBusy=%RTbool, fXtNeedsUpdate=%RTbool\n", pCtx->fXtBusy, pCtx->fXtNeedsUpdate));
687 return pCtx->fXtNeedsUpdate;
688}
689
690DECLINLINE(bool) clipSetXtBusy(PSHCLX11CTX pCtx, bool fBusy)
691{
692 pCtx->fXtBusy = fBusy;
693 LogFlowFunc(("fXtBusy=%RTbool, fXtNeedsUpdate=%RTbool\n", pCtx->fXtBusy, pCtx->fXtNeedsUpdate));
694 return pCtx->fXtBusy;
695}
696
697DECLINLINE(bool) clipSetXtNeedsUpdate(PSHCLX11CTX pCtx, bool fNeedsUpdate)
698{
699 pCtx->fXtNeedsUpdate = fNeedsUpdate;
700 LogFlowFunc(("fXtBusy=%RTbool, fXtNeedsUpdate=%RTbool\n", pCtx->fXtBusy, pCtx->fXtNeedsUpdate));
701 return pCtx->fXtNeedsUpdate;
702}
703#endif /* VBOX_WITH_SHARED_CLIPBOARD_XT_BUSY */
704
705/**
706 * Updates the context's information about targets currently supported by X11,
707 * based on an array of X11 atoms.
708 *
709 * @param pCtx The X11 clipboard context to use.
710 * @param pTargets The array of atoms describing the targets supported.
711 * @param cTargets The size of the array @a pTargets.
712 */
713SHCL_X11_DECL(void) clipUpdateX11Targets(PSHCLX11CTX pCtx, SHCLX11FMTIDX *paIdxFmtTargets, size_t cTargets)
714{
715 LogFlowFuncEnter();
716
717#ifdef VBOX_WITH_SHARED_CLIPBOARD_XT_BUSY
718 clipSetXtBusy(pCtx, false);
719 if (clipGetXtNeedsUpdate(pCtx))
720 {
721 /* We may already be out of date. */
722 clipSetXtNeedsUpdate(pCtx, false);
723 clipQueryX11Targets(pCtx);
724 return;
725 }
726#endif
727
728 if (paIdxFmtTargets == NULL)
729 {
730 /* No data available */
731 clipReportEmpty(pCtx);
732 return;
733 }
734
735 clipGetFormatsFromTargets(pCtx, paIdxFmtTargets, cTargets);
736 clipReportFormatsToVBox(pCtx);
737}
738
739/**
740 * Notifies the VBox clipboard about available data formats ("targets" on X11),
741 * based on the information obtained from the X11 clipboard.
742 *
743 * @note Callback installed by clipQueryX11Targets() for XtGetSelectionValue().
744 * @note This function is treated as API glue, and as such is not part of any
745 * unit test. So keep it simple, be paranoid and log everything.
746 */
747SHCL_X11_DECL(void) clipQueryX11TargetsCallback(Widget widget, XtPointer pClient,
748 Atom * /* selection */, Atom *atomType,
749 XtPointer pValue, long unsigned int *pcLen,
750 int *piFormat)
751{
752 RT_NOREF(piFormat);
753
754 PSHCLX11CTX pCtx = reinterpret_cast<SHCLX11CTX *>(pClient);
755
756 LogFlowFunc(("pValue=%p, *pcLen=%u, *atomType=%d%s\n",
757 pValue, *pcLen, *atomType, *atomType == XT_CONVERT_FAIL ? " (XT_CONVERT_FAIL)" : ""));
758
759 Atom *pAtoms = (Atom *)pValue;
760
761 unsigned cFormats = *pcLen;
762
763 LogRel2(("Shared Clipboard: Querying X11 formats ...\n"));
764 LogRel2(("Shared Clipboard: %u X11 formats were found\n", cFormats));
765
766 SHCLX11FMTIDX *paIdxFmt = NULL;
767 if ( cFormats
768 && pValue
769 && (*atomType != XT_CONVERT_FAIL /* time out */))
770 {
771 /* Allocated array to hold the format indices. */
772 paIdxFmt = (SHCLX11FMTIDX *)RTMemAllocZ(cFormats * sizeof(SHCLX11FMTIDX));
773 }
774
775#if !defined(TESTCASE)
776 if (pValue)
777 {
778 for (unsigned i = 0; i < cFormats; ++i)
779 {
780 if (pAtoms[i])
781 {
782 char *pszName = XGetAtomName(XtDisplay(widget), pAtoms[i]);
783 LogRel2(("Shared Clipboard: Found X11 format '%s'\n", pszName));
784 XFree(pszName);
785 }
786 else
787 LogFunc(("Found empty target\n"));
788 }
789 }
790#endif
791
792 if (paIdxFmt)
793 {
794 for (unsigned i = 0; i < cFormats; ++i)
795 {
796 for (unsigned j = 0; j < RT_ELEMENTS(g_aFormats); ++j)
797 {
798 Atom target = XInternAtom(XtDisplay(widget),
799 g_aFormats[j].pcszAtom, False);
800 if (*(pAtoms + i) == target)
801 paIdxFmt[i] = j;
802 }
803#if !defined(TESTCASE)
804 if (paIdxFmt[i] != SHCLX11FMT_INVALID)
805 LogRel2(("Shared Clipboard: Reporting X11 format '%s'\n", g_aFormats[paIdxFmt[i]].pcszAtom));
806#endif
807 }
808 }
809 else
810 LogFunc(("Reporting empty targets (none reported or allocation failure)\n"));
811
812 clipUpdateX11Targets(pCtx, paIdxFmt, cFormats);
813 RTMemFree(paIdxFmt);
814
815 XtFree(reinterpret_cast<char *>(pValue));
816}
817
818/**
819 * Queries the current formats ("targets") of the X11 clipboard ("CLIPBOARD").
820 *
821 * @param pCtx The X11 clipboard context to use.
822 */
823SHCL_X11_DECL(void) clipQueryX11Targets(PSHCLX11CTX pCtx)
824{
825#ifndef TESTCASE
826
827# ifdef VBOX_WITH_SHARED_CLIPBOARD_XT_BUSY
828 if (clipGetXtBusy(pCtx))
829 {
830 clipSetXtNeedsUpdate(pCtx, true);
831 return;
832 }
833 clipSetXtBusy(pCtx, true);
834# endif
835
836 XtGetSelectionValue(pCtx->pWidget,
837 clipGetAtom(pCtx, "CLIPBOARD"),
838 clipGetAtom(pCtx, "TARGETS"),
839 clipQueryX11TargetsCallback, pCtx,
840 CurrentTime);
841#else
842 tstRequestTargets(pCtx);
843#endif
844}
845
846typedef struct
847{
848 int type; /* event base */
849 unsigned long serial;
850 Bool send_event;
851 Display *display;
852 Window window;
853 int subtype;
854 Window owner;
855 Atom selection;
856 Time timestamp;
857 Time selection_timestamp;
858} XFixesSelectionNotifyEvent;
859
860#ifndef TESTCASE
861/**
862 * Waits until an event arrives and handle it if it is an XFIXES selection
863 * event, which Xt doesn't know about.
864 *
865 * @param pCtx The X11 clipboard context to use.
866 */
867static void clipPeekEventAndDoXFixesHandling(PSHCLX11CTX pCtx)
868{
869 union
870 {
871 XEvent event;
872 XFixesSelectionNotifyEvent fixes;
873 } event = { { 0 } };
874
875 if (XtAppPeekEvent(pCtx->pAppContext, &event.event))
876 {
877 if ( (event.event.type == pCtx->fixesEventBase)
878 && (event.fixes.owner != XtWindow(pCtx->pWidget)))
879 {
880 if ( (event.fixes.subtype == 0 /* XFixesSetSelectionOwnerNotify */)
881 && (event.fixes.owner != 0))
882 clipQueryX11Targets(pCtx);
883 else
884 clipReportEmpty(pCtx);
885 }
886 }
887}
888
889/**
890 * The main loop of our X11 event thread.
891 *
892 * @returns VBox status code.
893 * @param hThreadSelf Associated thread handle.
894 * @param pvUser Pointer to the X11 clipboard context to use.
895 */
896static DECLCALLBACK(int) clipThreadMain(RTTHREAD hThreadSelf, void *pvUser)
897{
898 PSHCLX11CTX pCtx = (PSHCLX11CTX)pvUser;
899 AssertPtr(pCtx);
900
901 LogFlowFunc(("pCtx=%p\n", pCtx));
902
903 bool fSignalled = false; /* Whether we have signalled the parent already or not. */
904
905 int rc = clipInitInternal(pCtx);
906 if (RT_SUCCESS(rc))
907 {
908 rc = clipRegisterContext(pCtx);
909 if (RT_SUCCESS(rc))
910 {
911 if (pCtx->fGrabClipboardOnStart)
912 clipQueryX11Targets(pCtx);
913
914 pCtx->fThreadStarted = true;
915
916 /* We're now ready to run, tell parent. */
917 int rc2 = RTThreadUserSignal(hThreadSelf);
918 AssertRC(rc2);
919
920 fSignalled = true;
921
922 while (XtAppGetExitFlag(pCtx->pAppContext) == FALSE)
923 {
924 clipPeekEventAndDoXFixesHandling(pCtx);
925 XtAppProcessEvent(pCtx->pAppContext, XtIMAll);
926 }
927
928 LogRel(("Shared Clipboard: X11 event thread exiting\n"));
929
930 clipUnregisterContext(pCtx);
931 }
932 else
933 {
934 LogRel(("Shared Clipboard: unable to register clip context: %Rrc\n", rc));
935 }
936
937 clipUninitInternal(pCtx);
938 }
939
940 if (!fSignalled) /* Signal parent if we didn't do so yet. */
941 {
942 int rc2 = RTThreadUserSignal(hThreadSelf);
943 AssertRC(rc2);
944 }
945
946 LogFlowFuncLeaveRC(rc);
947 return rc;
948}
949
950/**
951 * Worker function for stopping the clipboard which runs on the event
952 * thread.
953 *
954 * @param pvUserData Pointer to the X11 clipboard context to use.
955 */
956static void clipThreadSignalStop(void *pvUserData, void *)
957{
958 PSHCLX11CTX pCtx = (PSHCLX11CTX)pvUserData;
959
960 /* This might mean that we are getting stopped twice. */
961 Assert(pCtx->pWidget != NULL);
962
963 /* Set the termination flag to tell the Xt event loop to exit. We
964 * reiterate that any outstanding requests from the X11 event loop to
965 * the VBox part *must* have returned before we do this. */
966 XtAppSetExitFlag(pCtx->pAppContext);
967}
968
969/**
970 * Sets up the XFixes library and load the XFixesSelectSelectionInput symbol.
971 */
972static int clipLoadXFixes(Display *pDisplay, PSHCLX11CTX pCtx)
973{
974 int rc;
975
976 void *hFixesLib = dlopen("libXfixes.so.1", RTLD_LAZY);
977 if (!hFixesLib)
978 hFixesLib = dlopen("libXfixes.so.2", RTLD_LAZY);
979 if (!hFixesLib)
980 hFixesLib = dlopen("libXfixes.so.3", RTLD_LAZY);
981 if (!hFixesLib)
982 hFixesLib = dlopen("libXfixes.so.4", RTLD_LAZY);
983 if (hFixesLib)
984 {
985 /* For us, a NULL function pointer is a failure */
986 pCtx->fixesSelectInput = (void (*)(Display *, Window, Atom, long unsigned int))
987 (uintptr_t)dlsym(hFixesLib, "XFixesSelectSelectionInput");
988 if (pCtx->fixesSelectInput)
989 {
990 int dummy1 = 0;
991 int dummy2 = 0;
992 if (XQueryExtension(pDisplay, "XFIXES", &dummy1, &pCtx->fixesEventBase, &dummy2) != 0)
993 {
994 if (pCtx->fixesEventBase >= 0)
995 {
996 rc = VINF_SUCCESS;
997 }
998 else
999 {
1000 LogRel(("Shared Clipboard: fixesEventBase is less than zero: %d\n", pCtx->fixesEventBase));
1001 rc = VERR_NOT_SUPPORTED;
1002 }
1003 }
1004 else
1005 {
1006 LogRel(("Shared Clipboard: XQueryExtension failed\n"));
1007 rc = VERR_NOT_SUPPORTED;
1008 }
1009 }
1010 else
1011 {
1012 LogRel(("Shared Clipboard: Symbol XFixesSelectSelectionInput not found!\n"));
1013 rc = VERR_NOT_SUPPORTED;
1014 }
1015 }
1016 else
1017 {
1018 LogRel(("Shared Clipboard: libxFixes.so.* not found!\n"));
1019 rc = VERR_NOT_SUPPORTED;
1020 }
1021 return rc;
1022}
1023
1024/**
1025 * This is the callback which is scheduled when data is available on the
1026 * wakeup pipe. It simply reads all data from the pipe.
1027 *
1028 * @param pvUserData Pointer to the X11 clipboard context to use.
1029 */
1030static void clipThreadDrainWakeupPipe(XtPointer pvUserData, int *, XtInputId *)
1031{
1032 LogFlowFuncEnter();
1033
1034 PSHCLX11CTX pCtx = (PSHCLX11CTX)pvUserData;
1035 char acBuf[WAKE_UP_STRING_LEN];
1036
1037 while (read(pCtx->wakeupPipeRead, acBuf, sizeof(acBuf)) > 0) {}
1038}
1039#endif /* !TESTCASE */
1040
1041/**
1042 * X11-specific initialisation for the Shared Clipboard.
1043 *
1044 * Note: Must be called from the thread serving the Xt stuff.
1045 *
1046 * @returns VBox status code.
1047 * @param pCtx The X11 clipboard context to init.
1048 */
1049static int clipInitInternal(PSHCLX11CTX pCtx)
1050{
1051 LogFlowFunc(("pCtx=%p\n", pCtx));
1052
1053 /* Make sure we are thread safe. */
1054 XtToolkitThreadInitialize();
1055
1056 /*
1057 * Set up the Clipboard application context and main window. We call all
1058 * these functions directly instead of calling XtOpenApplication() so
1059 * that we can fail gracefully if we can't get an X11 display.
1060 */
1061 XtToolkitInitialize();
1062
1063 int rc = VINF_SUCCESS;
1064
1065 Assert(pCtx->pAppContext == NULL); /* No nested initialization. */
1066 pCtx->pAppContext = XtCreateApplicationContext();
1067 if (pCtx->pAppContext == NULL)
1068 {
1069 LogRel(("Shared Clipboard: Failed to create Xt application context\n"));
1070 return VERR_NOT_SUPPORTED; /** @todo Fudge! */
1071 }
1072
1073 /* Create a window and make it a clipboard viewer. */
1074 int cArgc = 0;
1075 char *pcArgv = 0;
1076 Display *pDisplay = XtOpenDisplay(pCtx->pAppContext, 0, 0, "VBoxShCl", 0, 0, &cArgc, &pcArgv);
1077 if (pDisplay == NULL)
1078 {
1079 LogRel(("Shared Clipboard: Failed to connect to the X11 clipboard - the window system may not be running\n"));
1080 rc = VERR_NOT_SUPPORTED;
1081 }
1082
1083#ifndef TESTCASE
1084 if (RT_SUCCESS(rc))
1085 {
1086 rc = clipLoadXFixes(pDisplay, pCtx);
1087 if (RT_FAILURE(rc))
1088 LogRel(("Shared Clipboard: Failed to load the XFIXES extension\n"));
1089 }
1090#endif
1091
1092 if (RT_SUCCESS(rc))
1093 {
1094 pCtx->pWidget = XtVaAppCreateShell(0, "VBoxShCl",
1095 applicationShellWidgetClass,
1096 pDisplay,
1097 XtNwidth, 1, XtNheight, 1,
1098 NULL);
1099 if (pCtx->pWidget == NULL)
1100 {
1101 LogRel(("Shared Clipboard: Failed to create Xt app shell\n"));
1102 rc = VERR_NO_MEMORY; /** @todo r=andy Improve this. */
1103 }
1104 else
1105 {
1106#ifndef TESTCASE
1107 if (!XtAppAddInput(pCtx->pAppContext, pCtx->wakeupPipeRead,
1108 (XtPointer) XtInputReadMask,
1109 clipThreadDrainWakeupPipe, (XtPointer) pCtx))
1110 {
1111 LogRel(("Shared Clipboard: Failed to add input to Xt app context\n"));
1112 rc = VERR_ACCESS_DENIED; /** @todo r=andy Improve this. */
1113 }
1114#endif
1115 }
1116 }
1117
1118 if (RT_SUCCESS(rc))
1119 {
1120 XtSetMappedWhenManaged(pCtx->pWidget, false);
1121 XtRealizeWidget(pCtx->pWidget);
1122
1123#ifndef TESTCASE
1124 /* Enable clipboard update notification. */
1125 pCtx->fixesSelectInput(pDisplay, XtWindow(pCtx->pWidget),
1126 clipGetAtom(pCtx, "CLIPBOARD"),
1127 7 /* All XFixes*Selection*NotifyMask flags */);
1128#endif
1129 }
1130
1131 if (RT_FAILURE(rc))
1132 {
1133 LogRel(("Shared Clipboard: Initialisation failed: %Rrc\n", rc));
1134 clipUninitInternal(pCtx);
1135 }
1136
1137 LogFlowFuncLeaveRC(rc);
1138 return rc;
1139}
1140
1141/**
1142 * X11-specific uninitialisation for the Shared Clipboard.
1143 *
1144 * Note: Must be called from the thread serving the Xt stuff.
1145 *
1146 * @param pCtx The X11 clipboard context to uninit.
1147 */
1148static void clipUninitInternal(PSHCLX11CTX pCtx)
1149{
1150 AssertPtrReturnVoid(pCtx);
1151
1152 LogFlowFunc(("pCtx=%p\n", pCtx));
1153
1154 if (pCtx->pWidget)
1155 {
1156 /* Valid widget + invalid appcontext = bug. But don't return yet. */
1157 AssertPtr(pCtx->pAppContext);
1158
1159 XtDestroyWidget(pCtx->pWidget);
1160 pCtx->pWidget = NULL;
1161 }
1162
1163 if (pCtx->pAppContext)
1164 {
1165 XtDestroyApplicationContext(pCtx->pAppContext);
1166 pCtx->pAppContext = NULL;
1167 }
1168
1169 LogFlowFuncLeaveRC(VINF_SUCCESS);
1170}
1171
1172/**
1173 * Sets the callback table, internal version.
1174 *
1175 * @param pCtx The clipboard context.
1176 * @param pCallbacks Callback table to set. If NULL, the current callback table will be cleared.
1177 */
1178static void shClX11SetCallbacksInternal(PSHCLX11CTX pCtx, PSHCLCALLBACKS pCallbacks)
1179{
1180 if (pCallbacks)
1181 {
1182 memcpy(&pCtx->Callbacks, pCallbacks, sizeof(SHCLCALLBACKS));
1183 }
1184 else
1185 RT_ZERO(pCtx->Callbacks);
1186}
1187
1188/**
1189 * Sets the callback table.
1190 *
1191 * @param pCtx The clipboard context.
1192 * @param pCallbacks Callback table to set. If NULL, the current callback table will be cleared.
1193 */
1194void ShClX11SetCallbacks(PSHCLX11CTX pCtx, PSHCLCALLBACKS pCallbacks)
1195{
1196 shClX11SetCallbacksInternal(pCtx, pCallbacks);
1197}
1198
1199/**
1200 * Initializes a X11 context of the Shared Clipboard.
1201 *
1202 * @returns VBox status code.
1203 * @param pCtx The clipboard context to initialize.
1204 * @param pCallbacks Callback table to use.
1205 * @param pParent Parent context to use.
1206 * @param fHeadless Whether the code runs in a headless environment or not.
1207 */
1208int ShClX11Init(PSHCLX11CTX pCtx, PSHCLCALLBACKS pCallbacks, PSHCLCONTEXT pParent, bool fHeadless)
1209{
1210 AssertPtrReturn(pCtx, VERR_INVALID_POINTER);
1211
1212 LogFlowFunc(("pCtx=%p\n", pCtx));
1213
1214 int rc = VINF_SUCCESS;
1215
1216 RT_BZERO(pCtx, sizeof(SHCLX11CTX));
1217
1218 if (fHeadless)
1219 {
1220 /*
1221 * If we don't find the DISPLAY environment variable we assume that
1222 * we are not connected to an X11 server. Don't actually try to do
1223 * this then, just fail silently and report success on every call.
1224 * This is important for VBoxHeadless.
1225 */
1226 LogRel(("Shared Clipboard: X11 DISPLAY variable not set -- disabling clipboard sharing\n"));
1227 }
1228
1229 /* Install given callbacks. */
1230 shClX11SetCallbacksInternal(pCtx, pCallbacks);
1231
1232 pCtx->fHaveX11 = !fHeadless;
1233 pCtx->pFrontend = pParent;
1234
1235#ifdef VBOX_WITH_SHARED_CLIPBOARD_XT_BUSY
1236 pCtx->fXtBusy = false;
1237 pCtx->fXtNeedsUpdate = false;
1238#endif
1239
1240#ifdef VBOX_WITH_SHARED_CLIPBOARD_TRANSFERS_HTTP
1241 ShClTransferHttpServerInit(&pCtx->HttpCtx.HttpServer);
1242#endif
1243
1244#ifdef TESTCASE
1245 if (RT_SUCCESS(rc))
1246 {
1247 /** @todo The testcases currently do not utilize the threading code. So init stuff here. */
1248 rc = clipInitInternal(pCtx);
1249 if (RT_SUCCESS(rc))
1250 rc = clipRegisterContext(pCtx);
1251 }
1252#endif
1253
1254 LogFlowFuncLeaveRC(rc);
1255 return rc;
1256}
1257
1258/**
1259 * Destroys a Shared Clipboard X11 context.
1260 *
1261 * @param pCtx The X11 clipboard context to destroy.
1262 */
1263void ShClX11Destroy(PSHCLX11CTX pCtx)
1264{
1265 if (!pCtx)
1266 return;
1267
1268 LogFlowFunc(("pCtx=%p\n", pCtx));
1269
1270#ifdef TESTCASE
1271 /** @todo The testcases currently do not utilize the threading code. So uninit stuff here. */
1272 clipUnregisterContext(pCtx);
1273 clipUninitInternal(pCtx);
1274#endif
1275
1276 if (pCtx->fHaveX11)
1277 {
1278 /* We set this to NULL when the event thread exits. It really should
1279 * have exited at this point, when we are about to unload the code from
1280 * memory. */
1281 Assert(pCtx->pWidget == NULL);
1282 }
1283}
1284
1285#ifndef TESTCASE
1286/**
1287 * Starts our own Xt even thread for handling Shared Clipboard messages, extended version.
1288 *
1289 * @returns VBox status code.
1290 * @param pCtx The X11 clipboard context to use.
1291 * @param pszName Thread name to use.
1292 * @param fGrab Whether we should try to grab the shared clipboard at once.
1293 */
1294int ShClX11ThreadStartEx(PSHCLX11CTX pCtx, const char *pszName, bool fGrab)
1295{
1296 AssertPtrReturn(pCtx, VERR_INVALID_POINTER);
1297
1298 /*
1299 * Immediately return if we are not connected to the X server.
1300 */
1301 if (!pCtx->fHaveX11)
1302 return VINF_SUCCESS;
1303
1304 pCtx->fGrabClipboardOnStart = fGrab;
1305
1306 clipResetX11Formats(pCtx);
1307
1308 int rc;
1309
1310 /*
1311 * Create the pipes.
1312 ** @todo r=andy Replace this with RTPipe API.
1313 */
1314 int pipes[2];
1315 if (!pipe(pipes))
1316 {
1317 pCtx->wakeupPipeRead = pipes[0];
1318 pCtx->wakeupPipeWrite = pipes[1];
1319
1320 if (!fcntl(pCtx->wakeupPipeRead, F_SETFL, O_NONBLOCK))
1321 {
1322 rc = VINF_SUCCESS;
1323 }
1324 else
1325 rc = RTErrConvertFromErrno(errno);
1326 }
1327 else
1328 rc = RTErrConvertFromErrno(errno);
1329
1330 if (RT_SUCCESS(rc))
1331 {
1332 LogRel2(("Shared Clipboard: Starting X11 event thread ...\n"));
1333
1334 rc = RTThreadCreate(&pCtx->Thread, clipThreadMain, pCtx, 0,
1335 RTTHREADTYPE_IO, RTTHREADFLAGS_WAITABLE, pszName);
1336 if (RT_SUCCESS(rc))
1337 rc = RTThreadUserWait(pCtx->Thread, RT_MS_30SEC /* msTimeout */);
1338
1339 if (RT_FAILURE(rc))
1340 {
1341 LogRel(("Shared Clipboard: Failed to start the X11 event thread with %Rrc\n", rc));
1342 clipUninitInternal(pCtx);
1343 }
1344 else
1345 {
1346 if (!pCtx->fThreadStarted)
1347 {
1348 LogRel(("Shared Clipboard: X11 event thread reported an error while starting\n"));
1349 }
1350 else
1351 LogRel2(("Shared Clipboard: X11 event thread started\n"));
1352 }
1353 }
1354
1355 LogFlowFuncLeaveRC(rc);
1356 return rc;
1357}
1358
1359/**
1360 * Starts our own Xt even thread for handling Shared Clipboard messages.
1361 *
1362 * @returns VBox status code.
1363 * @param pCtx The X11 clipboard context to use.
1364 * @param fGrab Whether we should try to grab the shared clipboard at once.
1365 */
1366int ShClX11ThreadStart(PSHCLX11CTX pCtx, bool fGrab)
1367{
1368 return ShClX11ThreadStartEx(pCtx, "SHCLX11", fGrab);
1369}
1370
1371/**
1372 * Stops the Shared Clipboard Xt even thread.
1373 *
1374 * @note Any requests from this object to get clipboard data from VBox
1375 * *must* have completed or aborted before we are called, as
1376 * otherwise the X11 event loop will still be waiting for the request
1377 * to return and will not be able to terminate.
1378 *
1379 * @returns VBox status code.
1380 * @param pCtx The X11 clipboard context to use.
1381 */
1382int ShClX11ThreadStop(PSHCLX11CTX pCtx)
1383{
1384 int rc;
1385 /*
1386 * Immediately return if we are not connected to the X server.
1387 */
1388 if (!pCtx->fHaveX11)
1389 return VINF_SUCCESS;
1390
1391 LogRel2(("Shared Clipboard: Signalling the X11 event thread to stop\n"));
1392
1393 /* Write to the "stop" pipe. */
1394 rc = clipThreadScheduleCall(pCtx, clipThreadSignalStop, (XtPointer)pCtx);
1395 if (RT_FAILURE(rc))
1396 {
1397 LogRel(("Shared Clipboard: cannot notify X11 event thread on shutdown with %Rrc\n", rc));
1398 return rc;
1399 }
1400
1401 LogRel2(("Shared Clipboard: Waiting for X11 event thread to stop ...\n"));
1402
1403 int rcThread;
1404 rc = RTThreadWait(pCtx->Thread, RT_MS_30SEC /* msTimeout */, &rcThread);
1405 if (RT_SUCCESS(rc))
1406 rc = rcThread;
1407 if (RT_SUCCESS(rc))
1408 {
1409 if (pCtx->wakeupPipeRead != 0)
1410 {
1411 close(pCtx->wakeupPipeRead);
1412 pCtx->wakeupPipeRead = 0;
1413 }
1414
1415 if (pCtx->wakeupPipeWrite != 0)
1416 {
1417 close(pCtx->wakeupPipeWrite);
1418 pCtx->wakeupPipeWrite = 0;
1419 }
1420 }
1421
1422 if (RT_SUCCESS(rc))
1423 {
1424 LogRel2(("Shared Clipboard: X11 event thread stopped successfully\n"));
1425 }
1426 else
1427 LogRel(("Shared Clipboard: Stopping X11 event thread failed with %Rrc\n", rc));
1428
1429 LogFlowFuncLeaveRC(rc);
1430 return rc;
1431}
1432#endif /* !TESTCASE */
1433
1434/**
1435 * Returns the targets supported by VBox.
1436 *
1437 * This will return a list of atoms which tells the caller
1438 * what kind of clipboard formats we support.
1439 *
1440 * @returns VBox status code.
1441 * @param pCtx The X11 clipboard context to use.
1442 * @param atomTypeReturn The type of the data we are returning.
1443 * @param pValReturn A pointer to the data we are returning. This
1444 * should be set to memory allocated by XtMalloc,
1445 * which will be freed later by the Xt toolkit.
1446 * @param pcLenReturn The length of the data we are returning.
1447 * @param piFormatReturn The format (8bit, 16bit, 32bit) of the data we are
1448 * returning.
1449 * @note X11 backend code, called by the XtOwnSelection callback.
1450 */
1451static int clipCreateX11Targets(PSHCLX11CTX pCtx, Atom *atomTypeReturn,
1452 XtPointer *pValReturn,
1453 unsigned long *pcLenReturn,
1454 int *piFormatReturn)
1455{
1456 const unsigned cFixedTargets = 3; /* See below. */
1457
1458 Atom *pAtomTargets = (Atom *)XtMalloc((SHCL_MAX_X11_FORMATS + cFixedTargets) * sizeof(Atom));
1459 if (!pAtomTargets)
1460 return VERR_NO_MEMORY;
1461
1462 unsigned cTargets = 0;
1463 SHCLX11FMTIDX idxFmt = NIL_CLIPX11FORMAT;
1464 do
1465 {
1466 idxFmt = clipEnumX11Formats(pCtx->vboxFormats, idxFmt);
1467 if (idxFmt != NIL_CLIPX11FORMAT)
1468 {
1469 pAtomTargets[cTargets] = clipAtomForX11Format(pCtx, idxFmt);
1470 ++cTargets;
1471 }
1472 } while (idxFmt != NIL_CLIPX11FORMAT);
1473
1474 /* We always offer these fixed targets. */
1475 pAtomTargets[cTargets] = clipGetAtom(pCtx, "TARGETS");
1476 pAtomTargets[cTargets + 1] = clipGetAtom(pCtx, "MULTIPLE");
1477 pAtomTargets[cTargets + 2] = clipGetAtom(pCtx, "TIMESTAMP");
1478
1479 *atomTypeReturn = XA_ATOM;
1480 *pValReturn = (XtPointer)pAtomTargets;
1481 *pcLenReturn = cTargets + cFixedTargets;
1482 *piFormatReturn = 32;
1483
1484 LogFlowFunc(("cTargets=%u\n", cTargets + cFixedTargets));
1485
1486 return VINF_SUCCESS;
1487}
1488
1489/**
1490 * Helper for ShClX11RequestDataForX11Callback() that will cache the data returned.
1491 *
1492 * @returns VBox status code. VERR_NO_DATA if no data available.
1493 * @param pCtx The X11 clipboard context to use.
1494 * @param uFmt Clipboard format to read data in.
1495 * @param ppv Returns an allocated buffer with data read on success.
1496 * Needs to be free'd with RTMemFree() by the caller.
1497 * @param pcb Returns the amount of data read (in bytes) on success.
1498 */
1499static int shClX11RequestDataForX11CallbackHelper(PSHCLX11CTX pCtx, SHCLFORMAT uFmt,
1500 void **ppv, uint32_t *pcb)
1501{
1502 AssertPtrReturn(pCtx, VERR_INVALID_POINTER);
1503 AssertPtrReturn(ppv, VERR_INVALID_POINTER);
1504 AssertPtrReturn(pcb, VERR_INVALID_POINTER);
1505
1506 LogFlowFunc(("pCtx=%p, uFmt=%#x\n", pCtx, uFmt));
1507
1508 int rc = VINF_SUCCESS;
1509
1510 void *pv = NULL;
1511 uint32_t cb = 0;
1512
1513 if (uFmt == VBOX_SHCL_FMT_UNICODETEXT)
1514 {
1515 if (pCtx->pvUnicodeCache == NULL) /** @todo r=andy Using string cache here? */
1516 rc = pCtx->Callbacks.pfnOnRequestDataFromSource(pCtx->pFrontend, uFmt, &pCtx->pvUnicodeCache, &pCtx->cbUnicodeCache,
1517 NULL /* pvUser */);
1518 if ( RT_SUCCESS(rc)
1519 /* Catch misbehaving callbacks. */
1520 && pCtx->pvUnicodeCache
1521 && pCtx->cbUnicodeCache)
1522 {
1523 pv = RTMemDup(pCtx->pvUnicodeCache, pCtx->cbUnicodeCache);
1524 if (pv)
1525 cb = pCtx->cbUnicodeCache;
1526 else
1527 rc = VERR_NO_MEMORY;
1528 }
1529 }
1530 else
1531 rc = pCtx->Callbacks.pfnOnRequestDataFromSource(pCtx->pFrontend, uFmt, &pv, &cb, NULL /* pvUser */);
1532
1533
1534 /* Safey net in case the callbacks above misbehave
1535 * (must return VERR_NO_DATA if no data available). */
1536 if ( RT_SUCCESS(rc)
1537 && (pv == NULL || cb == 0))
1538 rc = VERR_NO_DATA;
1539
1540 if (RT_SUCCESS(rc))
1541 {
1542 *ppv = pv;
1543 *pcb = cb;
1544 }
1545
1546 LogFlowFunc(("Returning pv=%p, cb=%RU32, rc=%Rrc\n", pv, cb, rc));
1547 return rc;
1548}
1549
1550/**
1551 * Satisfies a request from X11 to convert the clipboard text to UTF-8 LF.
1552 *
1553 * @returns VBox status code. VERR_NO_DATA if no data was converted.
1554 * @param pDisplay An X11 display structure, needed for conversions
1555 * performed by Xlib.
1556 * @param pv The text to be converted (UCS-2 with Windows EOLs).
1557 * @param cb The length of the text in @cb in bytes.
1558 * @param atomTypeReturn Where to store the atom for the type of the data
1559 * we are returning.
1560 * @param pValReturn Where to store the pointer to the data we are
1561 * returning. This should be to memory allocated by
1562 * XtMalloc, which will be freed by the Xt toolkit
1563 * later.
1564 * @param pcLenReturn Where to store the length of the data we are
1565 * returning.
1566 * @param piFormatReturn Where to store the bit width (8, 16, 32) of the
1567 * data we are returning.
1568 */
1569static int clipConvertUtf16ToX11Data(Display *pDisplay, PRTUTF16 pwszSrc,
1570 size_t cbSrc, Atom *atomTarget,
1571 Atom *atomTypeReturn,
1572 XtPointer *pValReturn,
1573 unsigned long *pcLenReturn,
1574 int *piFormatReturn)
1575{
1576 RT_NOREF(pDisplay);
1577 AssertReturn(cbSrc % sizeof(RTUTF16) == 0, VERR_INVALID_PARAMETER);
1578
1579 const size_t cwcSrc = cbSrc / sizeof(RTUTF16);
1580 if (!cwcSrc)
1581 return VERR_NO_DATA;
1582
1583 /* This may slightly overestimate the space needed. */
1584 size_t chDst = 0;
1585 int rc = ShClUtf16LenUtf8(pwszSrc, cwcSrc, &chDst);
1586 if (RT_SUCCESS(rc))
1587 {
1588 chDst++; /* Add space for terminator. */
1589
1590 char *pszDst = (char *)XtMalloc(chDst);
1591 if (pszDst)
1592 {
1593 size_t cbActual = 0;
1594 rc = ShClConvUtf16CRLFToUtf8LF(pwszSrc, cwcSrc, pszDst, chDst, &cbActual);
1595 if (RT_SUCCESS(rc))
1596 {
1597 *atomTypeReturn = *atomTarget;
1598 *pValReturn = (XtPointer)pszDst;
1599 *pcLenReturn = cbActual + 1 /* Include terminator */;
1600 *piFormatReturn = 8;
1601 }
1602 }
1603 else
1604 rc = VERR_NO_MEMORY;
1605 }
1606
1607 LogFlowFuncLeaveRC(rc);
1608 return rc;
1609}
1610
1611/**
1612 * Satisfies a request from X11 to convert the clipboard HTML fragment to UTF-8. We
1613 * return null-terminated text, but can cope with non-null-terminated input.
1614 *
1615 * @returns VBox status code.
1616 * @param pDisplay An X11 display structure, needed for conversions
1617 * performed by Xlib.
1618 * @param pv The text to be converted (UTF8 with Windows EOLs).
1619 * @param cb The length of the text in @cb in bytes.
1620 * @param atomTypeReturn Where to store the atom for the type of the data
1621 * we are returning.
1622 * @param pValReturn Where to store the pointer to the data we are
1623 * returning. This should be to memory allocated by
1624 * XtMalloc, which will be freed by the Xt toolkit later.
1625 * @param pcLenReturn Where to store the length of the data we are returning.
1626 * @param piFormatReturn Where to store the bit width (8, 16, 32) of the
1627 * data we are returning.
1628 */
1629static int clipConvertHtmlToX11Data(Display *pDisplay, const char *pszSrc,
1630 size_t cbSrc, Atom *atomTarget,
1631 Atom *atomTypeReturn,
1632 XtPointer *pValReturn,
1633 unsigned long *pcLenReturn,
1634 int *piFormatReturn)
1635{
1636 RT_NOREF(pDisplay, pValReturn);
1637
1638 /* This may slightly overestimate the space needed. */
1639 LogFlowFunc(("Source: %s", pszSrc));
1640
1641 char *pszDest = (char *)XtMalloc(cbSrc);
1642 if (pszDest == NULL)
1643 return VERR_NO_MEMORY;
1644
1645 memcpy(pszDest, pszSrc, cbSrc);
1646
1647 *atomTypeReturn = *atomTarget;
1648 *pValReturn = (XtPointer)pszDest;
1649 *pcLenReturn = cbSrc;
1650 *piFormatReturn = 8;
1651
1652 return VINF_SUCCESS;
1653}
1654
1655
1656/**
1657 * Does this atom correspond to one of the two selection types we support?
1658 *
1659 * @param pCtx The X11 clipboard context to use.
1660 * @param selType The atom in question.
1661 */
1662static bool clipIsSupportedSelectionType(PSHCLX11CTX pCtx, Atom selType)
1663{
1664 return( (selType == clipGetAtom(pCtx, "CLIPBOARD"))
1665 || (selType == clipGetAtom(pCtx, "PRIMARY")));
1666}
1667
1668/**
1669 * Removes a trailing nul character from a string by adjusting the string
1670 * length. Some X11 applications don't like zero-terminated text...
1671 *
1672 * @param pText The text in question.
1673 * @param pcText The length of the text, adjusted on return.
1674 * @param format The format of the text.
1675 */
1676static void clipTrimTrailingNul(XtPointer pText, unsigned long *pcText,
1677 SHCLX11FMT format)
1678{
1679 AssertPtrReturnVoid(pText);
1680 AssertPtrReturnVoid(pcText);
1681 AssertReturnVoid((format == SHCLX11FMT_UTF8) || (format == SHCLX11FMT_TEXT) || (format == SHCLX11FMT_HTML));
1682
1683 if (((char *)pText)[*pcText - 1] == '\0')
1684 --(*pcText);
1685}
1686
1687static int clipConvertToX11Data(PSHCLX11CTX pCtx, Atom *atomTarget,
1688 Atom *atomTypeReturn,
1689 XtPointer *pValReturn,
1690 unsigned long *pcLenReturn,
1691 int *piFormatReturn)
1692{
1693 int rc = VERR_NOT_SUPPORTED; /* Play safe by default. */
1694
1695 SHCLX11FMTIDX idxFmtX11 = clipFindX11FormatByAtom(pCtx, *atomTarget);
1696 SHCLX11FMT fmtX11 = clipRealFormatForX11Format(idxFmtX11);
1697
1698 LogFlowFunc(("vboxFormats=0x%x, idxFmtX11=%u ('%s'), fmtX11=%u\n",
1699 pCtx->vboxFormats, idxFmtX11, g_aFormats[idxFmtX11].pcszAtom, fmtX11));
1700
1701 char *pszFmts = ShClFormatsToStrA(pCtx->vboxFormats);
1702 AssertPtrReturn(pszFmts, VERR_NO_MEMORY);
1703 LogRel2(("Shared Clipboard: Converting VBox formats '%s' to '%s' for X11\n",
1704 pszFmts, fmtX11 == SHCLX11FMT_INVALID ? "<invalid>" : g_aFormats[idxFmtX11].pcszAtom));
1705 RTStrFree(pszFmts);
1706
1707 void *pv = NULL;
1708 uint32_t cb = 0;
1709
1710 if ( ( (fmtX11 == SHCLX11FMT_UTF8)
1711 || (fmtX11 == SHCLX11FMT_TEXT)
1712 )
1713 && (pCtx->vboxFormats & VBOX_SHCL_FMT_UNICODETEXT))
1714 {
1715 rc = shClX11RequestDataForX11CallbackHelper(pCtx, VBOX_SHCL_FMT_UNICODETEXT, &pv, &cb);
1716 if ( RT_SUCCESS(rc)
1717 && ( (fmtX11 == SHCLX11FMT_UTF8)
1718 || (fmtX11 == SHCLX11FMT_TEXT)))
1719 {
1720 rc = clipConvertUtf16ToX11Data(XtDisplay(pCtx->pWidget),
1721 (PRTUTF16)pv, cb, atomTarget,
1722 atomTypeReturn, pValReturn,
1723 pcLenReturn, piFormatReturn);
1724 }
1725
1726 if (RT_SUCCESS(rc))
1727 clipTrimTrailingNul(*(XtPointer *)pValReturn, pcLenReturn, fmtX11);
1728
1729 RTMemFree(pv);
1730 }
1731 else if ( (fmtX11 == SHCLX11FMT_BMP)
1732 && (pCtx->vboxFormats & VBOX_SHCL_FMT_BITMAP))
1733 {
1734 rc = shClX11RequestDataForX11CallbackHelper(pCtx, VBOX_SHCL_FMT_BITMAP, &pv, &cb);
1735 if ( RT_SUCCESS(rc)
1736 && (fmtX11 == SHCLX11FMT_BMP))
1737 {
1738 /* Create a full BMP from it. */
1739 rc = ShClDibToBmp(pv, cb, (void **)pValReturn,
1740 (size_t *)pcLenReturn);
1741 }
1742
1743 if (RT_SUCCESS(rc))
1744 {
1745 *atomTypeReturn = *atomTarget;
1746 *piFormatReturn = 8;
1747 }
1748
1749 RTMemFree(pv);
1750 }
1751 else if ( (fmtX11 == SHCLX11FMT_HTML)
1752 && (pCtx->vboxFormats & VBOX_SHCL_FMT_HTML))
1753 {
1754 rc = shClX11RequestDataForX11CallbackHelper(pCtx, VBOX_SHCL_FMT_HTML, &pv, &cb);
1755 if (RT_SUCCESS(rc))
1756 {
1757 /**
1758 * The common VBox HTML encoding will be UTF-8.
1759 * Before sending it to the X11 clipboard we have to convert it to UTF-8 first.
1760 *
1761 * Strange that we get UTF-16 from the X11 clipboard, but
1762 * in same time we send UTF-8 to X11 clipboard and it works.
1763 ** @todo r=andy Verify this.
1764 */
1765 rc = clipConvertHtmlToX11Data(XtDisplay(pCtx->pWidget),
1766 (const char*)pv, cb, atomTarget,
1767 atomTypeReturn, pValReturn,
1768 pcLenReturn, piFormatReturn);
1769 if (RT_SUCCESS(rc))
1770 clipTrimTrailingNul(*(XtPointer *)pValReturn, pcLenReturn, fmtX11);
1771
1772 RTMemFree(pv);
1773 }
1774 }
1775#ifdef VBOX_WITH_SHARED_CLIPBOARD_TRANSFERS
1776 else if (fmtX11 == SHCLX11FMT_URI_LIST)
1777 {
1778 if (pCtx->vboxFormats & VBOX_SHCL_FMT_URI_LIST)
1779 {
1780 rc = shClX11RequestDataForX11CallbackHelper(pCtx, VBOX_SHCL_FMT_URI_LIST, &pv, &cb);
1781 if (RT_SUCCESS(rc))
1782 {
1783 void *pvDst = (void *)XtMalloc(cb);
1784 if (pvDst)
1785 {
1786 memcpy(pvDst, pv, cb);
1787
1788 *atomTypeReturn = *atomTarget;
1789 *pValReturn = (XtPointer)pvDst;
1790 *pcLenReturn = cb;
1791 *piFormatReturn = 8;
1792 }
1793 else
1794 rc = VERR_NO_MEMORY;
1795 }
1796 }
1797 /* else not supported yet. */
1798 }
1799#endif
1800 else
1801 {
1802 *atomTypeReturn = XT_CONVERT_FAIL;
1803 *pValReturn = (XtPointer)NULL;
1804 *pcLenReturn = 0;
1805 *piFormatReturn = 0;
1806 }
1807
1808 if (RT_FAILURE(rc))
1809 {
1810 char *pszFmts2 = ShClFormatsToStrA(pCtx->vboxFormats);
1811 char *pszAtomName = XGetAtomName(XtDisplay(pCtx->pWidget), *atomTarget);
1812
1813 LogRel(("Shared Clipboard: Converting VBox formats '%s' to '%s' for X11 (idxFmtX11=%u, fmtX11=%u, atomTarget='%s') failed, rc=%Rrc\n",
1814 pszFmts2 ? pszFmts2 : "unknown", g_aFormats[idxFmtX11].pcszAtom, idxFmtX11, fmtX11, pszAtomName ? pszAtomName : "unknown", rc));
1815
1816 if (pszFmts2)
1817 RTStrFree(pszFmts2);
1818 if (pszAtomName)
1819 XFree(pszAtomName);
1820 }
1821
1822 LogFlowFuncLeaveRC(rc);
1823 return rc;
1824}
1825
1826/**
1827 * Returns VBox's clipboard data for an X11 client.
1828 *
1829 * @note Callback for XtOwnSelection.
1830 */
1831static Boolean clipXtConvertSelectionProc(Widget widget, Atom *atomSelection,
1832 Atom *atomTarget,
1833 Atom *atomTypeReturn,
1834 XtPointer *pValReturn,
1835 unsigned long *pcLenReturn,
1836 int *piFormatReturn)
1837{
1838 LogFlowFuncEnter();
1839
1840 PSHCLX11CTX pCtx = clipLookupContext(widget);
1841 if (!pCtx)
1842 return False;
1843
1844 /* Is this the rigt selection (clipboard) we were asked for? */
1845 if (!clipIsSupportedSelectionType(pCtx, *atomSelection))
1846 return False;
1847
1848 int rc;
1849 if (*atomTarget == clipGetAtom(pCtx, "TARGETS"))
1850 rc = clipCreateX11Targets(pCtx, atomTypeReturn, pValReturn,
1851 pcLenReturn, piFormatReturn);
1852 else
1853 rc = clipConvertToX11Data(pCtx, atomTarget, atomTypeReturn,
1854 pValReturn, pcLenReturn, piFormatReturn);
1855
1856#if 0 /** @todo Disabled -- crashes when running with tstClipboardGH-X11. */
1857 XSelectionRequestEvent* pReq =
1858 XtGetSelectionRequest(widget, *atomSelection, (XtRequestId)NULL);
1859 LogFlowFunc(("returning pVBoxWnd=%#x, ownerWnd=%#x, reqWnd=%#x, %RTbool, rc=%Rrc\n",
1860 XtWindow(pCtx->pWidget), pReq->owner, pReq->requestor, RT_SUCCESS(rc), rc));
1861#endif
1862 return RT_SUCCESS(rc) ? True : False;
1863}
1864
1865static void clipXtConvertSelectionProcLose(Widget widget, Atom *atomSelection)
1866{
1867 RT_NOREF(widget, atomSelection);
1868 LogFlowFuncEnter();
1869}
1870
1871static void clipXtConvertSelectionProcDone(Widget widget, Atom *atomSelection, Atom *atomTarget)
1872{
1873 RT_NOREF(widget, atomSelection, atomTarget);
1874 LogFlowFuncEnter();
1875}
1876
1877/**
1878 * Structure used to pass information about formats that VBox supports.
1879 */
1880typedef struct _CLIPNEWVBOXFORMATS
1881{
1882 /** Context information for the X11 clipboard. */
1883 PSHCLX11CTX pCtx;
1884 /** Formats supported by VBox. */
1885 SHCLFORMATS Formats;
1886} CLIPNEWVBOXFORMATS, *PCLIPNEWVBOXFORMATS;
1887
1888
1889
1890/**
1891 * Invalidates the local cache of the data in the VBox clipboard.
1892 *
1893 * @param pCtx The X11 clipboard context to use.
1894 */
1895static void clipInvalidateClipboardCache(PSHCLX11CTX pCtx)
1896{
1897 if (pCtx->pvUnicodeCache != NULL)
1898 {
1899 RTMemFree(pCtx->pvUnicodeCache);
1900 pCtx->pvUnicodeCache = NULL;
1901 }
1902}
1903
1904/**
1905 * Takes possession of the X11 clipboard (and middle-button selection).
1906 *
1907 * @param pCtx The X11 clipboard context to use.
1908 * @param uFormats Clipboard formats to set.
1909 */
1910static void clipGrabX11Clipboard(PSHCLX11CTX pCtx, SHCLFORMATS uFormats)
1911{
1912 LogFlowFuncEnter();
1913
1914 /** @ŧodo r=andy The docs say: "the value CurrentTime is not acceptable" here!? */
1915 if (XtOwnSelection(pCtx->pWidget, clipGetAtom(pCtx, "CLIPBOARD"),
1916 CurrentTime,
1917 clipXtConvertSelectionProc, clipXtConvertSelectionProcLose, clipXtConvertSelectionProcDone))
1918 {
1919 pCtx->vboxFormats = uFormats;
1920
1921 /* Grab the middle-button paste selection too. */
1922 XtOwnSelection(pCtx->pWidget, clipGetAtom(pCtx, "PRIMARY"),
1923 CurrentTime, clipXtConvertSelectionProc, NULL, 0);
1924#ifndef TESTCASE
1925 /* Xt suppresses these if we already own the clipboard, so send them
1926 * ourselves. */
1927 XSetSelectionOwner(XtDisplay(pCtx->pWidget),
1928 clipGetAtom(pCtx, "CLIPBOARD"),
1929 XtWindow(pCtx->pWidget), CurrentTime);
1930 XSetSelectionOwner(XtDisplay(pCtx->pWidget),
1931 clipGetAtom(pCtx, "PRIMARY"),
1932 XtWindow(pCtx->pWidget), CurrentTime);
1933#endif
1934 }
1935}
1936
1937/**
1938 * Worker function for ShClX11ReportFormatsToX11 which runs on the
1939 * event thread.
1940 *
1941 * @param pvUserData Pointer to a CLIPNEWVBOXFORMATS structure containing
1942 * information about the VBox formats available and the
1943 * clipboard context data. Must be freed by the worker.
1944 */
1945static void ShClX11ReportFormatsToX11Worker(void *pvUserData, void * /* interval */)
1946{
1947 AssertPtrReturnVoid(pvUserData);
1948
1949 CLIPNEWVBOXFORMATS *pFormats = (CLIPNEWVBOXFORMATS *)pvUserData;
1950
1951 PSHCLX11CTX pCtx = pFormats->pCtx;
1952 SHCLFORMATS fFormats = pFormats->Formats;
1953
1954 RTMemFree(pFormats);
1955
1956#ifdef LOG_ENABLED
1957 char *pszFmts = ShClFormatsToStrA(fFormats);
1958 AssertPtrReturnVoid(pszFmts);
1959 LogRel2(("Shared Clipboard: Reported available VBox formats %s to X11\n", pszFmts));
1960 RTStrFree(pszFmts);
1961#endif
1962
1963 clipInvalidateClipboardCache(pCtx);
1964 clipGrabX11Clipboard(pCtx, fFormats);
1965 clipResetX11Formats(pCtx);
1966
1967 LogFlowFuncLeave();
1968}
1969
1970/**
1971 * Announces new clipboard formats to the X11 clipboard.
1972 *
1973 * @returns VBox status code.
1974 * @param pCtx Context data for the clipboard backend.
1975 * @param uFormats Clipboard formats offered.
1976 */
1977int ShClX11ReportFormatsToX11(PSHCLX11CTX pCtx, SHCLFORMATS uFormats)
1978{
1979 /*
1980 * Immediately return if we are not connected to the X server.
1981 */
1982 if (!pCtx->fHaveX11)
1983 return VINF_SUCCESS;
1984
1985 int rc;
1986
1987 /* This must be free'd by the worker callback. */
1988 PCLIPNEWVBOXFORMATS pFormats = (PCLIPNEWVBOXFORMATS)RTMemAlloc(sizeof(CLIPNEWVBOXFORMATS));
1989 if (pFormats)
1990 {
1991 pFormats->pCtx = pCtx;
1992 pFormats->Formats = uFormats;
1993 rc = clipThreadScheduleCall(pCtx, ShClX11ReportFormatsToX11Worker,
1994 (XtPointer)pFormats);
1995 if (RT_FAILURE(rc))
1996 RTMemFree(pFormats);
1997 }
1998 else
1999 rc = VERR_NO_MEMORY;
2000
2001 LogFlowFuncLeaveRC(rc);
2002 return rc;
2003}
2004
2005/**
2006 * Converts the data obtained from the X11 clipboard to the required format,
2007 * place it in the buffer supplied and signal that data has arrived.
2008 *
2009 * Converts the text obtained UTF-16LE with Windows EOLs.
2010 * Converts full BMP data to DIB format.
2011 */
2012SHCL_X11_DECL(void) clipConvertDataFromX11Worker(void *pClient, void *pvSrc, unsigned cbSrc)
2013{
2014 CLIPREADX11CBREQ *pReq = (CLIPREADX11CBREQ *)pClient;
2015 AssertPtrReturnVoid(pReq);
2016
2017 LogFlowFunc(("uFmtVBox=%#x, idxFmtX11=%u, pvSrc=%p, cbSrc=%u\n", pReq->uFmtVBox, pReq->idxFmtX11, pvSrc, cbSrc));
2018
2019 LogRel2(("Shared Clipboard: Converting X11 format '%s' to VBox format %#x\n",
2020 g_aFormats[pReq->idxFmtX11].pcszAtom, pReq->uFmtVBox));
2021
2022 AssertPtr(pReq->pCtx);
2023 Assert(pReq->uFmtVBox != VBOX_SHCL_FMT_NONE); /* Sanity. */
2024
2025 int rc = VINF_SUCCESS;
2026
2027 void *pvDst = NULL;
2028 size_t cbDst = 0;
2029
2030 PSHCLX11CTX pCtx = pReq->pCtx;
2031 AssertPtr(pReq->pCtx);
2032
2033#ifdef VBOX_WITH_SHARED_CLIPBOARD_XT_BUSY
2034 clipSetXtBusy(pCtx, false);
2035 if (clipGetXtNeedsUpdate(pCtx))
2036 clipQueryX11Targets(pCtx);
2037#endif
2038
2039 /* If X11 clipboard buffer has no data, libXt can pass to XtGetSelectionValue()
2040 * callback an empty string, in this case cbSrc is 0. */
2041 if (pvSrc == NULL || cbSrc == 0)
2042 {
2043 /* The clipboard selection may have changed before we could get it. */
2044 rc = VERR_NO_DATA;
2045 }
2046 else if (pReq->uFmtVBox == VBOX_SHCL_FMT_UNICODETEXT)
2047 {
2048 /* In which format is the clipboard data? */
2049 switch (clipRealFormatForX11Format(pReq->idxFmtX11))
2050 {
2051 case SHCLX11FMT_UTF8:
2052 RT_FALL_THROUGH();
2053 case SHCLX11FMT_TEXT:
2054 {
2055 size_t cwDst;
2056 /* If we are given broken UTF-8, we treat it as Latin1. */ /** @todo BUGBUG Is this acceptable? */
2057 if (RT_SUCCESS(RTStrValidateEncodingEx((char *)pvSrc, cbSrc, 0)))
2058 rc = ShClConvUtf8LFToUtf16CRLF((const char *)pvSrc, cbSrc,
2059 (PRTUTF16 *)&pvDst, &cwDst);
2060 else
2061 rc = ShClConvLatin1LFToUtf16CRLF((char *)pvSrc, cbSrc,
2062 (PRTUTF16 *)&pvDst, &cwDst);
2063 if (RT_SUCCESS(rc))
2064 {
2065 cwDst += 1 /* Include terminator */;
2066 cbDst = cwDst * sizeof(RTUTF16); /* Convert RTUTF16 units to bytes. */
2067 }
2068 break;
2069 }
2070
2071 default:
2072 {
2073 rc = VERR_INVALID_PARAMETER;
2074 break;
2075 }
2076 }
2077 }
2078 else if (pReq->uFmtVBox == VBOX_SHCL_FMT_BITMAP)
2079 {
2080 /* In which format is the clipboard data? */
2081 switch (clipRealFormatForX11Format(pReq->idxFmtX11))
2082 {
2083 case SHCLX11FMT_BMP:
2084 {
2085 const void *pDib;
2086 size_t cbDibSize;
2087 rc = ShClBmpGetDib((const void *)pvSrc, cbSrc,
2088 &pDib, &cbDibSize);
2089 if (RT_SUCCESS(rc))
2090 {
2091 pvDst = RTMemAlloc(cbDibSize);
2092 if (!pvDst)
2093 rc = VERR_NO_MEMORY;
2094 else
2095 {
2096 memcpy(pvDst, pDib, cbDibSize);
2097 cbDst = cbDibSize;
2098 }
2099 }
2100 break;
2101 }
2102
2103 default:
2104 {
2105 rc = VERR_INVALID_PARAMETER;
2106 break;
2107 }
2108 }
2109 }
2110 else if (pReq->uFmtVBox == VBOX_SHCL_FMT_HTML)
2111 {
2112 /* In which format is the clipboard data? */
2113 switch (clipRealFormatForX11Format(pReq->idxFmtX11))
2114 {
2115 case SHCLX11FMT_HTML:
2116 {
2117 /*
2118 * The common VBox HTML encoding will be - UTF-8
2119 * because it more general for HTML formats then UTF-16
2120 * X11 clipboard returns UTF-16, so before sending it we should
2121 * convert it to UTF-8.
2122 */
2123 pvDst = NULL;
2124 cbDst = 0;
2125
2126 /*
2127 * Some applications sends data in UTF-16, some in UTF-8,
2128 * without indication it in MIME.
2129 *
2130 * In case of UTF-16, at least [Open|Libre] Office adds an byte order mark (0xfeff)
2131 * at the start of the clipboard data.
2132 */
2133 if ( cbSrc >= sizeof(RTUTF16)
2134 && *(PRTUTF16)pvSrc == VBOX_SHCL_UTF16LEMARKER)
2135 {
2136 rc = ShClConvUtf16ToUtf8HTML((PRTUTF16)pvSrc, cbSrc / sizeof(RTUTF16), (char**)&pvDst, &cbDst);
2137 if (RT_SUCCESS(rc))
2138 {
2139 LogFlowFunc(("UTF-16 Unicode source (%u bytes):\n%ls\n\n", cbSrc, pvSrc));
2140 LogFlowFunc(("Byte Order Mark = %hx", ((PRTUTF16)pvSrc)[0]));
2141 LogFlowFunc(("UTF-8 Unicode dest (%u bytes):\n%s\n\n", cbDst, pvDst));
2142 }
2143 else
2144 LogRel(("Shared Clipboard: Converting UTF-16 Unicode failed with %Rrc\n", rc));
2145 }
2146 else /* Raw data. */
2147 {
2148 pvDst = RTMemAllocZ(cbSrc + 1 /* '\0' */);
2149 if(pvDst)
2150 {
2151 memcpy(pvDst, pvSrc, cbSrc);
2152 cbDst = cbSrc + 1 /* '\0' */;
2153 }
2154 else
2155 {
2156 rc = VERR_NO_MEMORY;
2157 break;
2158 }
2159 }
2160
2161 rc = VINF_SUCCESS;
2162 break;
2163 }
2164
2165 default:
2166 {
2167 rc = VERR_INVALID_PARAMETER;
2168 break;
2169 }
2170 }
2171 }
2172# ifdef VBOX_WITH_SHARED_CLIPBOARD_TRANSFERS
2173 else if (pReq->uFmtVBox == VBOX_SHCL_FMT_URI_LIST)
2174 {
2175 /* In which format is the clipboard data? */
2176 switch (clipRealFormatForX11Format(pReq->idxFmtX11))
2177 {
2178 case SHCLX11FMT_URI_LIST:
2179 {
2180 /* For URI lists we only accept valid UTF-8 encodings. */
2181 if (RT_SUCCESS(RTStrValidateEncodingEx((char *)pvSrc, cbSrc, 0)))
2182 {
2183 /* URI lists on X are strings separated with "\r\n". */
2184 RTCList<RTCString> lstRootEntries = RTCString((char *)pvSrc, cbSrc).split("\r\n");
2185 for (size_t i = 0; i < lstRootEntries.size(); ++i)
2186 {
2187 char *pszEntry = RTUriFilePath(lstRootEntries.at(i).c_str());
2188 AssertPtrBreakStmt(pszEntry, VERR_INVALID_PARAMETER);
2189
2190 rc = RTStrAAppend((char **)&pvDst, "http://localhost");
2191 AssertRCBreakStmt(rc, VERR_NO_MEMORY);
2192 cbDst += (uint32_t)strlen(pszEntry);
2193
2194
2195
2196 /** @todo BUGBUG Fix port! */
2197 /** @todo Add port + UUID (virtual path). */
2198
2199 rc = RTStrAAppend((char **)&pvDst, pszEntry);
2200 AssertRCBreakStmt(rc, VERR_NO_MEMORY);
2201 cbDst += (uint32_t)strlen(pszEntry);
2202
2203 LogFlowFunc(("URI list entry '%s'\n", (char *)pvDst));
2204
2205 rc = RTStrAAppend((char **)&pvDst, "\r\n");
2206 AssertRCBreakStmt(rc, VERR_NO_MEMORY);
2207 cbDst += (uint32_t)strlen("\r\n");
2208
2209 RTStrFree(pszEntry);
2210 }
2211
2212 if (cbDst)
2213 cbDst++; /* Include final (zero) termination. */
2214
2215 LogFlowFunc(("URI list: cbDst=%RU32\n", cbDst));
2216 }
2217 else
2218 rc = VERR_INVALID_PARAMETER;
2219 break;
2220 }
2221
2222 default:
2223 {
2224 rc = VERR_INVALID_PARAMETER;
2225 break;
2226 }
2227 }
2228 }
2229# endif /* VBOX_WITH_SHARED_CLIPBOARD_TRANSFERS */
2230 else
2231 rc = VERR_NOT_SUPPORTED;
2232
2233 if (RT_FAILURE(rc))
2234 LogRel(("Shared Clipboard: Converting X11 format '%s' (idxFmtX11=%u) to VBox format %#x failed, rc=%Rrc\n",
2235 g_aFormats[pReq->idxFmtX11].pcszAtom, pReq->idxFmtX11, pReq->uFmtVBox, rc));
2236
2237 SHCLX11READDATAREQ SendData;
2238 RT_ZERO(SendData);
2239 SendData.pReq = pReq->pReq;
2240 SendData.rcCompletion = rc;
2241
2242 pCtx->Callbacks.pfnOnSendDataToDest(pReq->pCtx->pFrontend, pvDst, cbDst, &SendData);
2243
2244 RTMemFree(pvDst);
2245 RTMemFree(pReq);
2246
2247 LogFlowFuncLeaveRC(rc);
2248}
2249
2250/**
2251 * Converts the data obtained from the X11 clipboard to the required format,
2252 * place it in the buffer supplied and signal that data has arrived.
2253 *
2254 * Converts the text obtained UTF-16LE with Windows EOLs.
2255 * Converts full BMP data to DIB format.
2256 */
2257SHCL_X11_DECL(void) clipConvertDataFromX11(Widget widget, XtPointer pClient,
2258 Atom * /* selection */, Atom *atomType,
2259 XtPointer pvSrc, long unsigned int *pcLen,
2260 int *piFormat)
2261{
2262 RT_NOREF(widget);
2263
2264 int rc = VINF_SUCCESS;
2265
2266 CLIPREADX11CBREQ *pReq = (CLIPREADX11CBREQ *)pClient;
2267 if (pReq) /* Give some more clues, if available. */
2268 {
2269 char *pszFmts = ShClFormatsToStrA(pReq->uFmtVBox);
2270 AssertPtrReturnVoid(pszFmts);
2271 AssertReturnVoid(pReq->idxFmtX11 <= SHCL_MAX_X11_FORMATS); /* Paranoia, should be checked already by the caller. */
2272 LogRel2(("Shared Clipboard: Converting X11 format '%s' -> VBox format(s) '%s'\n", g_aFormats[pReq->idxFmtX11].pcszAtom, pszFmts));
2273 RTStrFree(pszFmts);
2274 }
2275
2276 if (*atomType == XT_CONVERT_FAIL) /* Xt timeout */
2277 {
2278 LogRel(("Shared Clipboard: Reading clipboard data from X11 timed out\n"));
2279 rc = VERR_TIMEOUT;
2280 }
2281 else
2282 {
2283 if ( pReq
2284 && pReq->pCtx->Callbacks.pfnOnClipboardRead)
2285 {
2286 void *pvData = NULL;
2287 size_t cbData = 0;
2288 rc = pReq->pCtx->Callbacks.pfnOnClipboardRead(pReq->pCtx->pFrontend, pReq->uFmtVBox, &pvData, &cbData, NULL);
2289 if (RT_SUCCESS(rc))
2290 {
2291 /* Feed to conversion worker. */
2292 clipConvertDataFromX11Worker(pClient, pvData, cbData);
2293 RTMemFree(pvData);
2294 }
2295 }
2296 else /* Call with current data provided by X (default). */
2297 clipConvertDataFromX11Worker(pClient, pvSrc, (*pcLen) * (*piFormat) / 8);
2298 }
2299
2300 if (RT_FAILURE(rc))
2301 {
2302 LogRel(("Shared Clipboard: Reading clipboard data from X11 failed with %Rrc\n", rc));
2303 /* Make sure to complete the request in any case. */
2304 clipConvertDataFromX11Worker(pClient, NULL, 0);
2305 }
2306
2307 XtFree((char *)pvSrc);
2308}
2309
2310/**
2311 * Requests the current clipboard data from a specific selection.
2312 *
2313 * @returns VBox status code.
2314 * @param pCtx The X11 clipboard context to use.
2315 * @param pszWhere Clipboard selection to request the data from.
2316 * @param idxFmt The X11 format to request the data in.
2317 * @param pReq Where to store the requested data on success.
2318 */
2319static int clipGetSelectionValueEx(PSHCLX11CTX pCtx, const char *pszWhere, SHCLX11FMTIDX idxFmt,
2320 CLIPREADX11CBREQ *pReq)
2321{
2322 AssertPtrReturn(pszWhere, VERR_INVALID_POINTER);
2323 AssertReturn(idxFmt <= SHCL_MAX_X11_FORMATS, VERR_INVALID_PARAMETER);
2324 AssertReturn(clipIsSupportedSelectionType(pCtx, clipGetAtom(pCtx, pszWhere)), VERR_INVALID_PARAMETER);
2325 AssertPtrReturn(pReq, VERR_INVALID_POINTER);
2326
2327 LogRel2(("Shared Clipboard: Requesting X11 selection value in %s for format '%s'\n", pszWhere, g_aFormats[idxFmt].pcszAtom));
2328
2329#ifndef TESTCASE
2330 XtGetSelectionValue(pCtx->pWidget, clipGetAtom(pCtx, pszWhere),
2331 clipAtomForX11Format(pCtx, idxFmt),
2332 clipConvertDataFromX11,
2333 reinterpret_cast<XtPointer>(pReq),
2334 CurrentTime);
2335#else
2336 tstClipRequestData(pCtx, idxFmt, (void *)pReq);
2337#endif
2338
2339 return VINF_SUCCESS; /** @todo Return real rc. */
2340}
2341
2342/**
2343 * Requests the current clipboard data from the CLIPBOARD selection.
2344 *
2345 * @returns VBox status code.
2346 * @param pCtx The X11 clipboard context to use.
2347 * @param idxFmt The X11 format to request the data in.
2348 * @param pReq Where to store the requested data on success.
2349 *
2350 * @sa clipGetSelectionValueEx() for requesting data for a specific selection.
2351 */
2352static int clipGetSelectionValue(PSHCLX11CTX pCtx, SHCLX11FMTIDX idxFmt, CLIPREADX11CBREQ *pReq)
2353{
2354 return clipGetSelectionValueEx(pCtx, "CLIPBOARD", idxFmt, pReq);
2355}
2356
2357/**
2358 * Worker function for ShClX11ReadDataFromX11 which runs on the event thread.
2359 *
2360 * @param pvUserData Pointer to a CLIPREADX11CBREQ structure containing
2361 * information about the clipboard read request.
2362 * Must be free'd by the worker.
2363 */
2364static void ShClX11ReadDataFromX11Worker(void *pvUserData, void * /* interval */)
2365{
2366 AssertPtrReturnVoid(pvUserData);
2367
2368 CLIPREADX11CBREQ *pReq = (CLIPREADX11CBREQ *)pvUserData;
2369 SHCLX11CTX *pCtx = pReq->pCtx;
2370 AssertPtrReturnVoid(pCtx);
2371
2372 LogFlowFunc(("pReq->uFmtVBox=%#x, idxFmtX11=%#x\n", pReq->uFmtVBox, pReq->idxFmtX11));
2373
2374 int rc = VERR_NO_DATA; /* VBox thinks we have data and we don't. */
2375
2376#ifdef VBOX_WITH_SHARED_CLIPBOARD_XT_BUSY
2377 const bool fXtBusy = clipGetXtBusy(pCtx);
2378 clipSetXtBusy(pCtx, true);
2379 if (fXtBusy)
2380 {
2381 /* If the clipboard is busy just fend off the request. */
2382 rc = VERR_TRY_AGAIN;
2383 }
2384 else
2385#endif
2386 if (pReq->uFmtVBox & VBOX_SHCL_FMT_UNICODETEXT)
2387 {
2388 pReq->idxFmtX11 = pCtx->idxFmtText;
2389 if (pReq->idxFmtX11 != SHCLX11FMT_INVALID)
2390 {
2391 /* Send out a request for the data to the current clipboard owner. */
2392 rc = clipGetSelectionValue(pCtx, pCtx->idxFmtText, pReq);
2393 }
2394 }
2395 else if (pReq->uFmtVBox & VBOX_SHCL_FMT_BITMAP)
2396 {
2397 pReq->idxFmtX11 = pCtx->idxFmtBmp;
2398 if (pReq->idxFmtX11 != SHCLX11FMT_INVALID)
2399 {
2400 /* Send out a request for the data to the current clipboard owner. */
2401 rc = clipGetSelectionValue(pCtx, pCtx->idxFmtBmp, pReq);
2402 }
2403 }
2404 else if (pReq->uFmtVBox & VBOX_SHCL_FMT_HTML)
2405 {
2406 pReq->idxFmtX11 = pCtx->idxFmtHTML;
2407 if (pReq->idxFmtX11 != SHCLX11FMT_INVALID)
2408 {
2409 /* Send out a request for the data to the current clipboard owner. */
2410 rc = clipGetSelectionValue(pCtx, pCtx->idxFmtHTML, pReq);
2411 }
2412 }
2413#ifdef VBOX_WITH_SHARED_CLIPBOARD_TRANSFERS
2414 else if (pReq->uFmtVBox & VBOX_SHCL_FMT_URI_LIST)
2415 {
2416 pReq->idxFmtX11 = pCtx->idxFmtURI;
2417 if (pReq->idxFmtX11 != SHCLX11FMT_INVALID)
2418 {
2419 /* Send out a request for the data to the current clipboard owner. */
2420 rc = clipGetSelectionValue(pCtx, pCtx->idxFmtURI, pReq);
2421 }
2422 }
2423#endif
2424 else
2425 {
2426#ifdef VBOX_WITH_SHARED_CLIPBOARD_XT_BUSY
2427 clipSetXtBusy(pCtx, false);
2428#endif
2429 rc = VERR_NOT_IMPLEMENTED;
2430 }
2431
2432 if (RT_FAILURE(rc))
2433 {
2434 /* The clipboard callback was never scheduled, so we must signal
2435 * that the request processing is finished and clean up ourselves. */
2436 SHCLX11READDATAREQ SendData;
2437 RT_ZERO(SendData);
2438 SendData.pReq = pReq->pReq;
2439 SendData.rcCompletion = rc;
2440
2441 pCtx->Callbacks.pfnOnSendDataToDest(pReq->pCtx->pFrontend, NULL /* pv */ ,0 /* cb */, &SendData);
2442 RTMemFree(pReq);
2443 }
2444
2445 LogFlowFuncLeaveRC(rc);
2446}
2447
2448/**
2449 * Called when VBox wants to read the X11 clipboard.
2450 *
2451 * @returns VBox status code.
2452 * @retval VERR_NO_DATA if format is supported but no data is available currently.
2453 * @retval VERR_NOT_IMPLEMENTED if the format is not implemented.
2454 * @param pCtx Context data for the clipboard backend.
2455 * @param uFmt The format that the VBox would like to receive the data in.
2456 * @param pReq Read callback request to use. Will be free'd in the callback on success.
2457 * Otherwise the caller has to free this again on error.
2458 *
2459 * @note We allocate a request structure which must be freed by the worker.
2460 */
2461int ShClX11ReadDataFromX11(PSHCLX11CTX pCtx, SHCLFORMAT uFmt, CLIPREADCBREQ *pReq)
2462{
2463 AssertPtrReturn(pReq, VERR_INVALID_POINTER);
2464 /*
2465 * Immediately return if we are not connected to the X server.
2466 */
2467 if (!pCtx->fHaveX11)
2468 return VERR_NO_DATA;
2469
2470 int rc = VINF_SUCCESS;
2471
2472 CLIPREADX11CBREQ *pX11Req = (CLIPREADX11CBREQ *)RTMemAllocZ(sizeof(CLIPREADX11CBREQ));
2473 if (pX11Req)
2474 {
2475 pX11Req->pCtx = pCtx;
2476 pX11Req->uFmtVBox = uFmt;
2477 pX11Req->pReq = pReq;
2478
2479 /* We use this to schedule a worker function on the event thread. */
2480 rc = clipThreadScheduleCall(pCtx, ShClX11ReadDataFromX11Worker, (XtPointer)pX11Req);
2481 if (RT_FAILURE(rc))
2482 RTMemFree(pX11Req);
2483 }
2484 else
2485 rc = VERR_NO_MEMORY;
2486
2487 LogFlowFuncLeaveRC(rc);
2488 return rc;
2489}
注意: 瀏覽 TracBrowser 來幫助您使用儲存庫瀏覽器

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