VirtualBox

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

Last change on this file since 92833 was 92833, checked in by vboxsync, 2 years ago

X11: Shared Clipboard: extend list of mime types, bugref:10160.

Add Firefox specific format application/x-moz-nativehtml to the
list of supported formats.

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

© 2023 Oracle
ContactPrivacy policyTerms of Use