VirtualBox

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

Last change on this file was 104316, checked in by vboxsync, 5 weeks ago

Shared Clipboard/X11: Fixed leak in testcase found by ASAN.

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

© 2023 Oracle
ContactPrivacy policyTerms of Use