VirtualBox

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

Last change on this file since 91933 was 91933, checked in by vboxsync, 3 years ago

X11: Shared Clipboard: handle case when libXt reports empty clipboard buffer, bugref:10117.

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

© 2023 Oracle
ContactPrivacy policyTerms of Use