x11-clipboard.cpp revision 355e8f335c21967a66a2151d4b8571c0ba3b35ec
af062818b47340eef15700d2f0211576ba3506eevboxsync/** @file
589fd26cedb2b4ebbed14f2964cad03cc8ebbca2vboxsync *
af062818b47340eef15700d2f0211576ba3506eevboxsync * Shared Clipboard:
af062818b47340eef15700d2f0211576ba3506eevboxsync * Linux host.
af062818b47340eef15700d2f0211576ba3506eevboxsync */
af062818b47340eef15700d2f0211576ba3506eevboxsync
af062818b47340eef15700d2f0211576ba3506eevboxsync/*
af062818b47340eef15700d2f0211576ba3506eevboxsync * Copyright (C) 2006-2007 Sun Microsystems, Inc.
af062818b47340eef15700d2f0211576ba3506eevboxsync *
af062818b47340eef15700d2f0211576ba3506eevboxsync * This file is part of VirtualBox Open Source Edition (OSE), as
af062818b47340eef15700d2f0211576ba3506eevboxsync * available from http://www.virtualbox.org. This file is free software;
af062818b47340eef15700d2f0211576ba3506eevboxsync * you can redistribute it and/or modify it under the terms of the GNU
af062818b47340eef15700d2f0211576ba3506eevboxsync * General Public License (GPL) as published by the Free Software
af062818b47340eef15700d2f0211576ba3506eevboxsync * Foundation, in version 2 as it comes in the "COPYING" file of the
af062818b47340eef15700d2f0211576ba3506eevboxsync * VirtualBox OSE distribution. VirtualBox OSE is distributed in the
af062818b47340eef15700d2f0211576ba3506eevboxsync * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
af062818b47340eef15700d2f0211576ba3506eevboxsync *
af062818b47340eef15700d2f0211576ba3506eevboxsync * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa
af062818b47340eef15700d2f0211576ba3506eevboxsync * Clara, CA 95054 USA or visit http://www.sun.com if you need
af062818b47340eef15700d2f0211576ba3506eevboxsync * additional information or have any questions.
af062818b47340eef15700d2f0211576ba3506eevboxsync */
af062818b47340eef15700d2f0211576ba3506eevboxsync
af062818b47340eef15700d2f0211576ba3506eevboxsync#include <string.h>
af062818b47340eef15700d2f0211576ba3506eevboxsync#include <stdio.h>
af062818b47340eef15700d2f0211576ba3506eevboxsync#include <stdint.h>
af062818b47340eef15700d2f0211576ba3506eevboxsync
af062818b47340eef15700d2f0211576ba3506eevboxsync#ifdef RT_OS_SOLARIS
af062818b47340eef15700d2f0211576ba3506eevboxsync#include <tsol/label.h>
af062818b47340eef15700d2f0211576ba3506eevboxsync#endif
af062818b47340eef15700d2f0211576ba3506eevboxsync
af062818b47340eef15700d2f0211576ba3506eevboxsync#include <vector>
af062818b47340eef15700d2f0211576ba3506eevboxsync
af062818b47340eef15700d2f0211576ba3506eevboxsync#include <X11/Xlib.h>
af062818b47340eef15700d2f0211576ba3506eevboxsync#include <X11/Xatom.h>
af062818b47340eef15700d2f0211576ba3506eevboxsync#include <X11/Intrinsic.h>
af062818b47340eef15700d2f0211576ba3506eevboxsync#include <X11/Shell.h>
af062818b47340eef15700d2f0211576ba3506eevboxsync#include <X11/Xproto.h>
af062818b47340eef15700d2f0211576ba3506eevboxsync#include <X11/StringDefs.h>
af062818b47340eef15700d2f0211576ba3506eevboxsync
af062818b47340eef15700d2f0211576ba3506eevboxsync#include <iprt/alloc.h>
af062818b47340eef15700d2f0211576ba3506eevboxsync#include <iprt/asm.h> /* For atomic operations */
af062818b47340eef15700d2f0211576ba3506eevboxsync#include <iprt/assert.h>
589fd26cedb2b4ebbed14f2964cad03cc8ebbca2vboxsync#include <iprt/env.h>
af062818b47340eef15700d2f0211576ba3506eevboxsync#include <iprt/mem.h>
af062818b47340eef15700d2f0211576ba3506eevboxsync#include <iprt/string.h>
af062818b47340eef15700d2f0211576ba3506eevboxsync#include <iprt/thread.h>
af062818b47340eef15700d2f0211576ba3506eevboxsync#include <iprt/process.h>
af062818b47340eef15700d2f0211576ba3506eevboxsync#include <iprt/semaphore.h>
af062818b47340eef15700d2f0211576ba3506eevboxsync
af062818b47340eef15700d2f0211576ba3506eevboxsync#include <VBox/GuestHost/clipboard-helper.h>
af062818b47340eef15700d2f0211576ba3506eevboxsync#include <VBox/HostServices/VBoxClipboardSvc.h>
af062818b47340eef15700d2f0211576ba3506eevboxsync
af062818b47340eef15700d2f0211576ba3506eevboxsync#include "VBoxClipboard.h"
af062818b47340eef15700d2f0211576ba3506eevboxsync
af062818b47340eef15700d2f0211576ba3506eevboxsync/** Do we want to test Utf16 by disabling other text formats? */
af062818b47340eef15700d2f0211576ba3506eevboxsyncstatic bool g_testUtf16 = false;
af062818b47340eef15700d2f0211576ba3506eevboxsync/** Do we want to test Utf8 by disabling other text formats? */
af062818b47340eef15700d2f0211576ba3506eevboxsyncstatic bool g_testUtf8 = false;
af062818b47340eef15700d2f0211576ba3506eevboxsync/** Do we want to test compount text by disabling other text formats? */
af062818b47340eef15700d2f0211576ba3506eevboxsyncstatic bool g_testCText = false;
af062818b47340eef15700d2f0211576ba3506eevboxsync/** Are we currently debugging the clipboard code? */
af062818b47340eef15700d2f0211576ba3506eevboxsyncstatic bool g_debugClipboard = false;
af062818b47340eef15700d2f0211576ba3506eevboxsync
af062818b47340eef15700d2f0211576ba3506eevboxsync/** The different clipboard formats which we support. */
af062818b47340eef15700d2f0211576ba3506eevboxsyncenum g_eClipboardFormats
af062818b47340eef15700d2f0211576ba3506eevboxsync{
af062818b47340eef15700d2f0211576ba3506eevboxsync INVALID = 0,
af062818b47340eef15700d2f0211576ba3506eevboxsync TARGETS,
af062818b47340eef15700d2f0211576ba3506eevboxsync CTEXT,
af062818b47340eef15700d2f0211576ba3506eevboxsync UTF8,
af062818b47340eef15700d2f0211576ba3506eevboxsync UTF16
589fd26cedb2b4ebbed14f2964cad03cc8ebbca2vboxsync};
af062818b47340eef15700d2f0211576ba3506eevboxsync
af062818b47340eef15700d2f0211576ba3506eevboxsync/** The X11 clipboard uses several names for the same format. This structure maps an X11
af062818b47340eef15700d2f0211576ba3506eevboxsync name to a format. */
589fd26cedb2b4ebbed14f2964cad03cc8ebbca2vboxsynctypedef struct {
af062818b47340eef15700d2f0211576ba3506eevboxsync Atom atom;
af062818b47340eef15700d2f0211576ba3506eevboxsync g_eClipboardFormats format;
af062818b47340eef15700d2f0211576ba3506eevboxsync unsigned guestFormat;
af062818b47340eef15700d2f0211576ba3506eevboxsync} VBOXCLIPBOARDFORMAT;
af062818b47340eef15700d2f0211576ba3506eevboxsync
af062818b47340eef15700d2f0211576ba3506eevboxsync/** Does X11 or VBox currently own the clipboard? */
af062818b47340eef15700d2f0211576ba3506eevboxsyncenum g_eOwner { NONE = 0, X11, VB };
af062818b47340eef15700d2f0211576ba3506eevboxsync
af062818b47340eef15700d2f0211576ba3506eevboxsynctypedef struct {
af062818b47340eef15700d2f0211576ba3506eevboxsync /** BMP file type marker - must always contain 'BM' */
af062818b47340eef15700d2f0211576ba3506eevboxsync uint16_t bfType;
af062818b47340eef15700d2f0211576ba3506eevboxsync /** The size of the BMP file in bytes (the MS docs say that this is not reliable) */
af062818b47340eef15700d2f0211576ba3506eevboxsync uint32_t bfSize;
af062818b47340eef15700d2f0211576ba3506eevboxsync /** Reserved, must always be zero */
af062818b47340eef15700d2f0211576ba3506eevboxsync uint16_t bfReserved1;
af062818b47340eef15700d2f0211576ba3506eevboxsync /** Reserved, must always be zero */
af062818b47340eef15700d2f0211576ba3506eevboxsync uint16_t bfReserved2;
589fd26cedb2b4ebbed14f2964cad03cc8ebbca2vboxsync /** Offset from the beginning of this header to the actual image bits */
af062818b47340eef15700d2f0211576ba3506eevboxsync} VBOXBITMAPFILEHEADER;
af062818b47340eef15700d2f0211576ba3506eevboxsync
589fd26cedb2b4ebbed14f2964cad03cc8ebbca2vboxsync/** Global context information used by the host glue for the X11 clipboard
af062818b47340eef15700d2f0211576ba3506eevboxsync * backend */
af062818b47340eef15700d2f0211576ba3506eevboxsyncstruct _VBOXCLIPBOARDCONTEXT
af062818b47340eef15700d2f0211576ba3506eevboxsync{
af062818b47340eef15700d2f0211576ba3506eevboxsync /** Since the clipboard data moves asynchronously, we use an event
af062818b47340eef15700d2f0211576ba3506eevboxsync * semaphore to wait for it. When a function issues a request for
af062818b47340eef15700d2f0211576ba3506eevboxsync * clipboard data it must wait for this semaphore, which is triggered
af062818b47340eef15700d2f0211576ba3506eevboxsync * when the data arrives. */
589fd26cedb2b4ebbed14f2964cad03cc8ebbca2vboxsync RTSEMEVENT waitForData;
589fd26cedb2b4ebbed14f2964cad03cc8ebbca2vboxsync /** Who (if anyone) is currently waiting for data? Used for sanity
589fd26cedb2b4ebbed14f2964cad03cc8ebbca2vboxsync * checks when data arrives. */
af062818b47340eef15700d2f0211576ba3506eevboxsync volatile uint32_t waiter;
589fd26cedb2b4ebbed14f2964cad03cc8ebbca2vboxsync /** This mutex is grabbed during any critical operations on the clipboard
589fd26cedb2b4ebbed14f2964cad03cc8ebbca2vboxsync * which might clash with others. */
af062818b47340eef15700d2f0211576ba3506eevboxsync RTSEMMUTEX clipboardMutex;
589fd26cedb2b4ebbed14f2964cad03cc8ebbca2vboxsync
589fd26cedb2b4ebbed14f2964cad03cc8ebbca2vboxsync /** Pointer to the client data structure */
589fd26cedb2b4ebbed14f2964cad03cc8ebbca2vboxsync VBOXCLIPBOARDCLIENTDATA *pClient;
589fd26cedb2b4ebbed14f2964cad03cc8ebbca2vboxsync};
589fd26cedb2b4ebbed14f2964cad03cc8ebbca2vboxsync
589fd26cedb2b4ebbed14f2964cad03cc8ebbca2vboxsync/** Global context information used by the X11 clipboard backend */
589fd26cedb2b4ebbed14f2964cad03cc8ebbca2vboxsyncstruct _VBOXCLIPBOARDCONTEXTX11
af062818b47340eef15700d2f0211576ba3506eevboxsync{
af062818b47340eef15700d2f0211576ba3506eevboxsync /** The X Toolkit application context structure */
af062818b47340eef15700d2f0211576ba3506eevboxsync XtAppContext appContext;
af062818b47340eef15700d2f0211576ba3506eevboxsync
af062818b47340eef15700d2f0211576ba3506eevboxsync /** We have a separate thread to wait for Window and Clipboard events */
af062818b47340eef15700d2f0211576ba3506eevboxsync RTTHREAD thread;
af062818b47340eef15700d2f0211576ba3506eevboxsync /** The X Toolkit widget which we use as our clipboard client. It is never made visible. */
af062818b47340eef15700d2f0211576ba3506eevboxsync Widget widget;
af062818b47340eef15700d2f0211576ba3506eevboxsync
af062818b47340eef15700d2f0211576ba3506eevboxsync /** X11 atom refering to the clipboard: CLIPBOARD */
af062818b47340eef15700d2f0211576ba3506eevboxsync Atom atomClipboard;
af062818b47340eef15700d2f0211576ba3506eevboxsync /** X11 atom refering to the selection: PRIMARY */
af062818b47340eef15700d2f0211576ba3506eevboxsync Atom atomPrimary;
af062818b47340eef15700d2f0211576ba3506eevboxsync /** X11 atom refering to the clipboard targets: TARGETS */
af062818b47340eef15700d2f0211576ba3506eevboxsync Atom atomTargets;
589fd26cedb2b4ebbed14f2964cad03cc8ebbca2vboxsync /** X11 atom refering to the clipboard multiple target: MULTIPLE */
589fd26cedb2b4ebbed14f2964cad03cc8ebbca2vboxsync Atom atomMultiple;
589fd26cedb2b4ebbed14f2964cad03cc8ebbca2vboxsync /** X11 atom refering to the clipboard timestamp target: TIMESTAMP */
af062818b47340eef15700d2f0211576ba3506eevboxsync Atom atomTimestamp;
af062818b47340eef15700d2f0211576ba3506eevboxsync /** X11 atom refering to the clipboard utf16 text format: text/plain;charset=ISO-10646-UCS-2 */
589fd26cedb2b4ebbed14f2964cad03cc8ebbca2vboxsync Atom atomUtf16;
589fd26cedb2b4ebbed14f2964cad03cc8ebbca2vboxsync /** X11 atom refering to the clipboard utf8 text format: UTF8_STRING */
af062818b47340eef15700d2f0211576ba3506eevboxsync Atom atomUtf8;
af062818b47340eef15700d2f0211576ba3506eevboxsync /** X11 atom refering to the clipboard compound text format: COMPOUND_TEXT */
af062818b47340eef15700d2f0211576ba3506eevboxsync Atom atomCText;
af062818b47340eef15700d2f0211576ba3506eevboxsync
589fd26cedb2b4ebbed14f2964cad03cc8ebbca2vboxsync /** A list of the X11 formats which we support, mapped to our identifier for them, in the
589fd26cedb2b4ebbed14f2964cad03cc8ebbca2vboxsync order we prefer to have them in. */
af062818b47340eef15700d2f0211576ba3506eevboxsync std::vector<VBOXCLIPBOARDFORMAT> formatList;
af062818b47340eef15700d2f0211576ba3506eevboxsync
af062818b47340eef15700d2f0211576ba3506eevboxsync /** Does VBox or X11 currently own the clipboard? */
af062818b47340eef15700d2f0211576ba3506eevboxsync volatile enum g_eOwner eOwner;
589fd26cedb2b4ebbed14f2964cad03cc8ebbca2vboxsync
589fd26cedb2b4ebbed14f2964cad03cc8ebbca2vboxsync /** What is the best text format X11 has to offer? INVALID for none. */
589fd26cedb2b4ebbed14f2964cad03cc8ebbca2vboxsync g_eClipboardFormats X11TextFormat;
589fd26cedb2b4ebbed14f2964cad03cc8ebbca2vboxsync /** Atom corresponding to the X11 text format */
589fd26cedb2b4ebbed14f2964cad03cc8ebbca2vboxsync Atom atomX11TextFormat;
589fd26cedb2b4ebbed14f2964cad03cc8ebbca2vboxsync /** What is the best bitmap format X11 has to offer? INVALID for none. */
589fd26cedb2b4ebbed14f2964cad03cc8ebbca2vboxsync g_eClipboardFormats X11BitmapFormat;
589fd26cedb2b4ebbed14f2964cad03cc8ebbca2vboxsync /** Atom corresponding to the X11 Bitmap format */
589fd26cedb2b4ebbed14f2964cad03cc8ebbca2vboxsync Atom atomX11BitmapFormat;
589fd26cedb2b4ebbed14f2964cad03cc8ebbca2vboxsync /** What formats does VBox have on offer? */
af062818b47340eef15700d2f0211576ba3506eevboxsync int vboxFormats;
af062818b47340eef15700d2f0211576ba3506eevboxsync /** Windows hosts and guests cache the clipboard data they receive.
af062818b47340eef15700d2f0211576ba3506eevboxsync * Since we have no way of knowing whether their cache is still valid,
af062818b47340eef15700d2f0211576ba3506eevboxsync * we always send a "data changed" message after a successful transfer
af062818b47340eef15700d2f0211576ba3506eevboxsync * to invalidate it. */
af062818b47340eef15700d2f0211576ba3506eevboxsync bool notifyVBox;
af062818b47340eef15700d2f0211576ba3506eevboxsync
af062818b47340eef15700d2f0211576ba3506eevboxsync /** Since the clipboard data moves asynchronously, we use an event
af062818b47340eef15700d2f0211576ba3506eevboxsync * semaphore to wait for it. When a function issues a request for
af062818b47340eef15700d2f0211576ba3506eevboxsync * clipboard data it must wait for this semaphore, which is triggered
af062818b47340eef15700d2f0211576ba3506eevboxsync * when the data arrives. */
af062818b47340eef15700d2f0211576ba3506eevboxsync RTSEMEVENT waitForData;
af062818b47340eef15700d2f0211576ba3506eevboxsync};
af062818b47340eef15700d2f0211576ba3506eevboxsync
af062818b47340eef15700d2f0211576ba3506eevboxsynctypedef struct _VBOXCLIPBOARDCONTEXTX11 VBOXCLIPBOARDCONTEXTX11;
af062818b47340eef15700d2f0211576ba3506eevboxsync
589fd26cedb2b4ebbed14f2964cad03cc8ebbca2vboxsync/** A structure containing information about where to store a request
af062818b47340eef15700d2f0211576ba3506eevboxsync * for the X11 clipboard contents. */
af062818b47340eef15700d2f0211576ba3506eevboxsyncstruct _VBOXCLIPBOARDREQUEST
af062818b47340eef15700d2f0211576ba3506eevboxsync{
af062818b47340eef15700d2f0211576ba3506eevboxsync /** The buffer to write X11 clipboard data to (valid during a request
af062818b47340eef15700d2f0211576ba3506eevboxsync * for the clipboard contents) */
af062818b47340eef15700d2f0211576ba3506eevboxsync void *pv;
af062818b47340eef15700d2f0211576ba3506eevboxsync /** The size of the buffer to write X11 clipboard data to (valid during
af062818b47340eef15700d2f0211576ba3506eevboxsync * a request for the clipboard contents) */
af062818b47340eef15700d2f0211576ba3506eevboxsync unsigned cb;
af062818b47340eef15700d2f0211576ba3506eevboxsync /** The size of the X11 clipboard data written to the buffer (valid
af062818b47340eef15700d2f0211576ba3506eevboxsync * during a request for the clipboard contents) */
af062818b47340eef15700d2f0211576ba3506eevboxsync uint32_t *pcbActual;
af062818b47340eef15700d2f0211576ba3506eevboxsync};
af062818b47340eef15700d2f0211576ba3506eevboxsync
af062818b47340eef15700d2f0211576ba3506eevboxsynctypedef struct _VBOXCLIPBOARDREQUEST VBOXCLIPBOARDREQUEST;
af062818b47340eef15700d2f0211576ba3506eevboxsync
af062818b47340eef15700d2f0211576ba3506eevboxsync/* Only one client is supported. There seems to be no need for more clients.
af062818b47340eef15700d2f0211576ba3506eevboxsync */
af062818b47340eef15700d2f0211576ba3506eevboxsyncstatic VBOXCLIPBOARDCONTEXT g_ctxHost;
af062818b47340eef15700d2f0211576ba3506eevboxsyncstatic VBOXCLIPBOARDCONTEXTX11 g_ctxX11;
af062818b47340eef15700d2f0211576ba3506eevboxsync
af062818b47340eef15700d2f0211576ba3506eevboxsync/* Are we actually connected to the X server? */
af062818b47340eef15700d2f0211576ba3506eevboxsyncstatic bool g_fHaveX11;
589fd26cedb2b4ebbed14f2964cad03cc8ebbca2vboxsync
af062818b47340eef15700d2f0211576ba3506eevboxsync
af062818b47340eef15700d2f0211576ba3506eevboxsync/**
af062818b47340eef15700d2f0211576ba3506eevboxsync * Send a request to VBox to transfer the contents of its clipboard to X11.
af062818b47340eef15700d2f0211576ba3506eevboxsync *
af062818b47340eef15700d2f0211576ba3506eevboxsync * @param pCtx Pointer to the host clipboard structure
af062818b47340eef15700d2f0211576ba3506eevboxsync * @param u32Format The format in which the data should be transfered
af062818b47340eef15700d2f0211576ba3506eevboxsync * @param ppv On success and if pcb > 0, this will point to a buffer
af062818b47340eef15700d2f0211576ba3506eevboxsync * to be freed with RTMemFree containing the data read.
589fd26cedb2b4ebbed14f2964cad03cc8ebbca2vboxsync * @param pcb On success, this contains the number of bytes of data
af062818b47340eef15700d2f0211576ba3506eevboxsync * returned
af062818b47340eef15700d2f0211576ba3506eevboxsync * @note Host glue code.
af062818b47340eef15700d2f0211576ba3506eevboxsync */
af062818b47340eef15700d2f0211576ba3506eevboxsyncstatic int vboxClipboardReadDataFromVBox (VBOXCLIPBOARDCONTEXT *pCtx, uint32_t u32Format, void **ppv, uint32_t *pcb)
af062818b47340eef15700d2f0211576ba3506eevboxsync{
af062818b47340eef15700d2f0211576ba3506eevboxsync volatile VBOXCLIPBOARDCLIENTDATA *pClient = pCtx->pClient;
af062818b47340eef15700d2f0211576ba3506eevboxsync
af062818b47340eef15700d2f0211576ba3506eevboxsync LogFlowFunc(("u32Format=%02X\n", u32Format));
af062818b47340eef15700d2f0211576ba3506eevboxsync if (pClient == NULL)
af062818b47340eef15700d2f0211576ba3506eevboxsync {
af062818b47340eef15700d2f0211576ba3506eevboxsync /* This can legitimately happen if we disconnect during a request for
af062818b47340eef15700d2f0211576ba3506eevboxsync * data from X11. */
af062818b47340eef15700d2f0211576ba3506eevboxsync LogFunc(("host requested guest clipboard data after guest had disconnected.\n"));
af062818b47340eef15700d2f0211576ba3506eevboxsync vboxClipboardFormatAnnounce(NULL, 0);
af062818b47340eef15700d2f0211576ba3506eevboxsync pCtx->waiter = NONE;
af062818b47340eef15700d2f0211576ba3506eevboxsync return VERR_TIMEOUT;
af062818b47340eef15700d2f0211576ba3506eevboxsync }
589fd26cedb2b4ebbed14f2964cad03cc8ebbca2vboxsync /* Assert that no other transfer is in process (requests are serialised)
af062818b47340eef15700d2f0211576ba3506eevboxsync * and that the last transfer cleaned up properly. */
af062818b47340eef15700d2f0211576ba3506eevboxsync AssertLogRelReturn( pClient->data.pv == NULL
af062818b47340eef15700d2f0211576ba3506eevboxsync && pClient->data.cb == 0
af062818b47340eef15700d2f0211576ba3506eevboxsync && pClient->data.u32Format == 0,
af062818b47340eef15700d2f0211576ba3506eevboxsync VERR_WRONG_ORDER
af062818b47340eef15700d2f0211576ba3506eevboxsync );
af062818b47340eef15700d2f0211576ba3506eevboxsync /* No one else (X11 or VBox) should currently be waiting. The first because
af062818b47340eef15700d2f0211576ba3506eevboxsync * requests from X11 are serialised and the second because VBox previously
af062818b47340eef15700d2f0211576ba3506eevboxsync * grabbed the clipboard, so it should not be waiting for data from us. */
af062818b47340eef15700d2f0211576ba3506eevboxsync AssertLogRelReturn (ASMAtomicCmpXchgU32(&pCtx->waiter, X11, NONE), VERR_DEADLOCK);
af062818b47340eef15700d2f0211576ba3506eevboxsync /* Request data from VBox */
af062818b47340eef15700d2f0211576ba3506eevboxsync vboxSvcClipboardReportMsg(pCtx->pClient,
af062818b47340eef15700d2f0211576ba3506eevboxsync VBOX_SHARED_CLIPBOARD_HOST_MSG_READ_DATA,
af062818b47340eef15700d2f0211576ba3506eevboxsync u32Format);
589fd26cedb2b4ebbed14f2964cad03cc8ebbca2vboxsync /* Which will signal us when it is ready. We use a timeout here because
af062818b47340eef15700d2f0211576ba3506eevboxsync * we can't be sure that the guest will behave correctly. */
af062818b47340eef15700d2f0211576ba3506eevboxsync int rc = RTSemEventWait(pCtx->waitForData, CLIPBOARDTIMEOUT);
af062818b47340eef15700d2f0211576ba3506eevboxsync if (rc == VERR_TIMEOUT)
af062818b47340eef15700d2f0211576ba3506eevboxsync rc = VINF_SUCCESS; /* Timeout handling follows. */
af062818b47340eef15700d2f0211576ba3506eevboxsync /* Now we have a potential race between the HGCM thread delivering the data
af062818b47340eef15700d2f0211576ba3506eevboxsync * and our setting waiter to NONE to say that we are no longer waiting for
af062818b47340eef15700d2f0211576ba3506eevboxsync * it. We solve this as follows: both of these operations are done under
af062818b47340eef15700d2f0211576ba3506eevboxsync * the clipboard mutex. The HGCM thread will only deliver the data if we
af062818b47340eef15700d2f0211576ba3506eevboxsync * are still waiting after it acquires the mutex. After we release the
af062818b47340eef15700d2f0211576ba3506eevboxsync * mutex, we finally do our check to see whether the data was delivered. */
af062818b47340eef15700d2f0211576ba3506eevboxsync RTSemMutexRequest(g_ctxHost.clipboardMutex, RT_INDEFINITE_WAIT);
af062818b47340eef15700d2f0211576ba3506eevboxsync pCtx->waiter = NONE;
af062818b47340eef15700d2f0211576ba3506eevboxsync RTSemMutexRelease(g_ctxHost.clipboardMutex);
589fd26cedb2b4ebbed14f2964cad03cc8ebbca2vboxsync AssertLogRelRCSuccess(rc);
af062818b47340eef15700d2f0211576ba3506eevboxsync if (RT_FAILURE(rc))
af062818b47340eef15700d2f0211576ba3506eevboxsync {
af062818b47340eef15700d2f0211576ba3506eevboxsync /* I believe this should not happen. Wait until the assertions arrive
af062818b47340eef15700d2f0211576ba3506eevboxsync * to prove the contrary. */
af062818b47340eef15700d2f0211576ba3506eevboxsync RTMemFree(pClient->data.pv);
af062818b47340eef15700d2f0211576ba3506eevboxsync g_ctxHost.pClient->data.pv = 0;
af062818b47340eef15700d2f0211576ba3506eevboxsync g_ctxHost.pClient->data.cb = 0;
af062818b47340eef15700d2f0211576ba3506eevboxsync g_ctxHost.pClient->data.u32Format = 0;
af062818b47340eef15700d2f0211576ba3506eevboxsync vboxClipboardFormatAnnounce(NULL, 0);
af062818b47340eef15700d2f0211576ba3506eevboxsync return rc;
af062818b47340eef15700d2f0211576ba3506eevboxsync }
af062818b47340eef15700d2f0211576ba3506eevboxsync if (pClient->data.pv == NULL)
af062818b47340eef15700d2f0211576ba3506eevboxsync return VERR_TIMEOUT;
af062818b47340eef15700d2f0211576ba3506eevboxsync LogFlowFunc(("wait completed. Returning.\n"));
af062818b47340eef15700d2f0211576ba3506eevboxsync *ppv = pClient->data.pv;
af062818b47340eef15700d2f0211576ba3506eevboxsync *pcb = pClient->data.cb;
af062818b47340eef15700d2f0211576ba3506eevboxsync g_ctxHost.pClient->data.pv = 0;
af062818b47340eef15700d2f0211576ba3506eevboxsync g_ctxHost.pClient->data.cb = 0;
589fd26cedb2b4ebbed14f2964cad03cc8ebbca2vboxsync g_ctxHost.pClient->data.u32Format = 0;
589fd26cedb2b4ebbed14f2964cad03cc8ebbca2vboxsync return VINF_SUCCESS;
af062818b47340eef15700d2f0211576ba3506eevboxsync}
589fd26cedb2b4ebbed14f2964cad03cc8ebbca2vboxsync
589fd26cedb2b4ebbed14f2964cad03cc8ebbca2vboxsync/**
589fd26cedb2b4ebbed14f2964cad03cc8ebbca2vboxsync * Convert the UTF-16 text obtained from the X11 clipboard to UTF-16LE with
af062818b47340eef15700d2f0211576ba3506eevboxsync * Windows EOLs, place it in the buffer supplied and signal that data has
589fd26cedb2b4ebbed14f2964cad03cc8ebbca2vboxsync * arrived.
589fd26cedb2b4ebbed14f2964cad03cc8ebbca2vboxsync *
589fd26cedb2b4ebbed14f2964cad03cc8ebbca2vboxsync * @param pValue Source UTF-16 text
af062818b47340eef15700d2f0211576ba3506eevboxsync * @param cwSourceLen Length in 16-bit words of the source text
af062818b47340eef15700d2f0211576ba3506eevboxsync * @param pv Where to store the converted data
af062818b47340eef15700d2f0211576ba3506eevboxsync * @param cb Length in bytes of the buffer pointed to by cb
af062818b47340eef15700d2f0211576ba3506eevboxsync * @param pcbActual Where to store the size of the converted data
af062818b47340eef15700d2f0211576ba3506eevboxsync * @param pClient Pointer to the client context structure
589fd26cedb2b4ebbed14f2964cad03cc8ebbca2vboxsync * @note X11 backend code, called from the Xt callback when we wish to read
589fd26cedb2b4ebbed14f2964cad03cc8ebbca2vboxsync * the X11 clipboard.
589fd26cedb2b4ebbed14f2964cad03cc8ebbca2vboxsync */
589fd26cedb2b4ebbed14f2964cad03cc8ebbca2vboxsyncstatic void vboxClipboardGetUtf16(XtPointer pValue, unsigned cwSrcLen,
af062818b47340eef15700d2f0211576ba3506eevboxsync void *pv, unsigned cb,
af062818b47340eef15700d2f0211576ba3506eevboxsync uint32_t *pcbActual)
589fd26cedb2b4ebbed14f2964cad03cc8ebbca2vboxsync{
589fd26cedb2b4ebbed14f2964cad03cc8ebbca2vboxsync size_t cwDestLen;
af062818b47340eef15700d2f0211576ba3506eevboxsync PRTUTF16 pu16SrcText = reinterpret_cast<PRTUTF16>(pValue);
af062818b47340eef15700d2f0211576ba3506eevboxsync PRTUTF16 pu16DestText = reinterpret_cast<PRTUTF16>(pv);
af062818b47340eef15700d2f0211576ba3506eevboxsync
af062818b47340eef15700d2f0211576ba3506eevboxsync LogFlowFunc (("converting Utf-16 to Utf-16LE. cwSrcLen=%d, cb=%d, pu16SrcText+1=%.*ls\n",
af062818b47340eef15700d2f0211576ba3506eevboxsync cwSrcLen, cb, cwSrcLen - 1, pu16SrcText + 1));
af062818b47340eef15700d2f0211576ba3506eevboxsync *pcbActual = 0; /* Only set this to the right value on success. */
af062818b47340eef15700d2f0211576ba3506eevboxsync /* How long will the converted text be? */
af062818b47340eef15700d2f0211576ba3506eevboxsync int rc = vboxClipboardUtf16GetWinSize(pu16SrcText, cwSrcLen, &cwDestLen);
af062818b47340eef15700d2f0211576ba3506eevboxsync if (RT_SUCCESS(rc) && (cb < cwDestLen * 2))
af062818b47340eef15700d2f0211576ba3506eevboxsync {
589fd26cedb2b4ebbed14f2964cad03cc8ebbca2vboxsync /* Not enough buffer space provided - report the amount needed. */
589fd26cedb2b4ebbed14f2964cad03cc8ebbca2vboxsync LogFlowFunc (("guest buffer too small: size %d bytes, needed %d. Returning.\n",
589fd26cedb2b4ebbed14f2964cad03cc8ebbca2vboxsync cb, cwDestLen * 2));
589fd26cedb2b4ebbed14f2964cad03cc8ebbca2vboxsync *pcbActual = cwDestLen * 2;
589fd26cedb2b4ebbed14f2964cad03cc8ebbca2vboxsync rc = VERR_BUFFER_OVERFLOW;
589fd26cedb2b4ebbed14f2964cad03cc8ebbca2vboxsync }
589fd26cedb2b4ebbed14f2964cad03cc8ebbca2vboxsync /* Convert the text. */
589fd26cedb2b4ebbed14f2964cad03cc8ebbca2vboxsync if (RT_SUCCESS(rc))
af062818b47340eef15700d2f0211576ba3506eevboxsync rc = vboxClipboardUtf16LinToWin(pu16SrcText, cwSrcLen, pu16DestText, cb / 2);
af062818b47340eef15700d2f0211576ba3506eevboxsync if (RT_SUCCESS(rc))
af062818b47340eef15700d2f0211576ba3506eevboxsync {
af062818b47340eef15700d2f0211576ba3506eevboxsync LogFlowFunc (("converted string is %.*ls\n", cwDestLen, pu16DestText));
af062818b47340eef15700d2f0211576ba3506eevboxsync *pcbActual = cwDestLen * 2;
af062818b47340eef15700d2f0211576ba3506eevboxsync }
af062818b47340eef15700d2f0211576ba3506eevboxsync /* We need to do this whether we succeed or fail. */
af062818b47340eef15700d2f0211576ba3506eevboxsync XtFree(reinterpret_cast<char *>(pValue));
af062818b47340eef15700d2f0211576ba3506eevboxsync RTSemEventSignal(g_ctxX11.waitForData);
af062818b47340eef15700d2f0211576ba3506eevboxsync LogFlowFunc(("Returning. Status is %Rrc\n", rc));
af062818b47340eef15700d2f0211576ba3506eevboxsync}
589fd26cedb2b4ebbed14f2964cad03cc8ebbca2vboxsync
af062818b47340eef15700d2f0211576ba3506eevboxsync/**
af062818b47340eef15700d2f0211576ba3506eevboxsync * Convert the UTF-8 text obtained from the X11 clipboard to UTF-16LE with
589fd26cedb2b4ebbed14f2964cad03cc8ebbca2vboxsync * Windows EOLs, place it in the buffer supplied and signal that data has
af062818b47340eef15700d2f0211576ba3506eevboxsync * arrived.
af062818b47340eef15700d2f0211576ba3506eevboxsync *
af062818b47340eef15700d2f0211576ba3506eevboxsync * @param pValue Source UTF-8 text
af062818b47340eef15700d2f0211576ba3506eevboxsync * @param cbSourceLen Length in 8-bit bytes of the source text
af062818b47340eef15700d2f0211576ba3506eevboxsync * @param pv Where to store the converted data
af062818b47340eef15700d2f0211576ba3506eevboxsync * @param cb Length in bytes of the buffer pointed to by pv
af062818b47340eef15700d2f0211576ba3506eevboxsync * @param pcbActual Where to store the size of the converted data
af062818b47340eef15700d2f0211576ba3506eevboxsync * @param pClient Pointer to the client context structure
af062818b47340eef15700d2f0211576ba3506eevboxsync * @note X11 backend code, called from the Xt callback when we wish to read
af062818b47340eef15700d2f0211576ba3506eevboxsync * the X11 clipboard.
589fd26cedb2b4ebbed14f2964cad03cc8ebbca2vboxsync */
589fd26cedb2b4ebbed14f2964cad03cc8ebbca2vboxsyncstatic void vboxClipboardGetUtf8FromX11(XtPointer pValue, unsigned cbSrcLen,
af062818b47340eef15700d2f0211576ba3506eevboxsync void *pv, unsigned cb,
589fd26cedb2b4ebbed14f2964cad03cc8ebbca2vboxsync uint32_t *pcbActual)
589fd26cedb2b4ebbed14f2964cad03cc8ebbca2vboxsync{
589fd26cedb2b4ebbed14f2964cad03cc8ebbca2vboxsync size_t cwSrcLen, cwDestLen;
af062818b47340eef15700d2f0211576ba3506eevboxsync char *pu8SrcText = reinterpret_cast<char *>(pValue);
589fd26cedb2b4ebbed14f2964cad03cc8ebbca2vboxsync PRTUTF16 pu16SrcText = NULL;
589fd26cedb2b4ebbed14f2964cad03cc8ebbca2vboxsync PRTUTF16 pu16DestText = reinterpret_cast<PRTUTF16>(pv);
af062818b47340eef15700d2f0211576ba3506eevboxsync
af062818b47340eef15700d2f0211576ba3506eevboxsync LogFlowFunc (("converting Utf-8 to Utf-16LE. cbSrcLen=%d, cb=%d, pu8SrcText=%.*s\n",
589fd26cedb2b4ebbed14f2964cad03cc8ebbca2vboxsync cbSrcLen, cb, cbSrcLen, pu8SrcText));
589fd26cedb2b4ebbed14f2964cad03cc8ebbca2vboxsync *pcbActual = 0; /* Only set this to the right value on success. */
589fd26cedb2b4ebbed14f2964cad03cc8ebbca2vboxsync /* First convert the UTF8 to UTF16 */
589fd26cedb2b4ebbed14f2964cad03cc8ebbca2vboxsync int rc = RTStrToUtf16Ex(pu8SrcText, cbSrcLen, &pu16SrcText, 0, &cwSrcLen);
589fd26cedb2b4ebbed14f2964cad03cc8ebbca2vboxsync /* Check how much longer will the converted text will be. */
589fd26cedb2b4ebbed14f2964cad03cc8ebbca2vboxsync if (RT_SUCCESS(rc))
589fd26cedb2b4ebbed14f2964cad03cc8ebbca2vboxsync rc = vboxClipboardUtf16GetWinSize(pu16SrcText, cwSrcLen, &cwDestLen);
589fd26cedb2b4ebbed14f2964cad03cc8ebbca2vboxsync if (RT_SUCCESS(rc) && (cb < cwDestLen * 2))
589fd26cedb2b4ebbed14f2964cad03cc8ebbca2vboxsync {
589fd26cedb2b4ebbed14f2964cad03cc8ebbca2vboxsync /* Not enough buffer space provided - report the amount needed. */
589fd26cedb2b4ebbed14f2964cad03cc8ebbca2vboxsync LogFlowFunc (("guest buffer too small: size %d bytes, needed %d. Returning.\n",
589fd26cedb2b4ebbed14f2964cad03cc8ebbca2vboxsync cb, cwDestLen * 2));
589fd26cedb2b4ebbed14f2964cad03cc8ebbca2vboxsync *pcbActual = cwDestLen * 2;
589fd26cedb2b4ebbed14f2964cad03cc8ebbca2vboxsync rc = VERR_BUFFER_OVERFLOW;
589fd26cedb2b4ebbed14f2964cad03cc8ebbca2vboxsync }
589fd26cedb2b4ebbed14f2964cad03cc8ebbca2vboxsync /* Convert the text. */
589fd26cedb2b4ebbed14f2964cad03cc8ebbca2vboxsync if (RT_SUCCESS(rc))
589fd26cedb2b4ebbed14f2964cad03cc8ebbca2vboxsync rc = vboxClipboardUtf16LinToWin(pu16SrcText, cwSrcLen, pu16DestText, cb / 2);
589fd26cedb2b4ebbed14f2964cad03cc8ebbca2vboxsync if (RT_SUCCESS(rc))
589fd26cedb2b4ebbed14f2964cad03cc8ebbca2vboxsync {
589fd26cedb2b4ebbed14f2964cad03cc8ebbca2vboxsync LogFlowFunc (("converted string is %.*ls.\n", cwDestLen, pu16DestText));
589fd26cedb2b4ebbed14f2964cad03cc8ebbca2vboxsync *pcbActual = cwDestLen * 2;
589fd26cedb2b4ebbed14f2964cad03cc8ebbca2vboxsync }
589fd26cedb2b4ebbed14f2964cad03cc8ebbca2vboxsync XtFree(reinterpret_cast<char *>(pValue));
589fd26cedb2b4ebbed14f2964cad03cc8ebbca2vboxsync RTUtf16Free(pu16SrcText);
589fd26cedb2b4ebbed14f2964cad03cc8ebbca2vboxsync RTSemEventSignal(g_ctxX11.waitForData);
589fd26cedb2b4ebbed14f2964cad03cc8ebbca2vboxsync LogFlowFunc(("Returning. Status is %Rrc", rc));
589fd26cedb2b4ebbed14f2964cad03cc8ebbca2vboxsync}
589fd26cedb2b4ebbed14f2964cad03cc8ebbca2vboxsync
af062818b47340eef15700d2f0211576ba3506eevboxsync/**
af062818b47340eef15700d2f0211576ba3506eevboxsync * Convert the COMPOUND_TEXT obtained from the X11 clipboard to UTF-16LE with
af062818b47340eef15700d2f0211576ba3506eevboxsync * Windows EOLs, place it in the buffer supplied and signal that data has
af062818b47340eef15700d2f0211576ba3506eevboxsync * arrived.
af062818b47340eef15700d2f0211576ba3506eevboxsync *
af062818b47340eef15700d2f0211576ba3506eevboxsync * @param pValue Source COMPOUND_TEXT
af062818b47340eef15700d2f0211576ba3506eevboxsync * @param cbSourceLen Length in 8-bit bytes of the source text
af062818b47340eef15700d2f0211576ba3506eevboxsync * @param pv Where to store the converted data
af062818b47340eef15700d2f0211576ba3506eevboxsync * @param cb Length in bytes of the buffer pointed to by pv
af062818b47340eef15700d2f0211576ba3506eevboxsync * @param pcbActual Where to store the size of the converted data
af062818b47340eef15700d2f0211576ba3506eevboxsync * @param pClient Pointer to the client context structure
af062818b47340eef15700d2f0211576ba3506eevboxsync * @note X11 backend code, called from the Xt callback when we wish to read
af062818b47340eef15700d2f0211576ba3506eevboxsync * the X11 clipboard.
af062818b47340eef15700d2f0211576ba3506eevboxsync */
af062818b47340eef15700d2f0211576ba3506eevboxsyncstatic void vboxClipboardGetCTextFromX11(XtPointer pValue, unsigned cbSrcLen,
af062818b47340eef15700d2f0211576ba3506eevboxsync void *pv, unsigned cb,
af062818b47340eef15700d2f0211576ba3506eevboxsync uint32_t *pcbActual)
af062818b47340eef15700d2f0211576ba3506eevboxsync{
af062818b47340eef15700d2f0211576ba3506eevboxsync size_t cwSrcLen, cwDestLen;
af062818b47340eef15700d2f0211576ba3506eevboxsync char **ppu8SrcText = NULL;
af062818b47340eef15700d2f0211576ba3506eevboxsync PRTUTF16 pu16SrcText = NULL;
af062818b47340eef15700d2f0211576ba3506eevboxsync PRTUTF16 pu16DestText = reinterpret_cast<PRTUTF16>(pv);
af062818b47340eef15700d2f0211576ba3506eevboxsync XTextProperty property;
af062818b47340eef15700d2f0211576ba3506eevboxsync int rc = VINF_SUCCESS;
af062818b47340eef15700d2f0211576ba3506eevboxsync int cProps;
af062818b47340eef15700d2f0211576ba3506eevboxsync
589fd26cedb2b4ebbed14f2964cad03cc8ebbca2vboxsync LogFlowFunc (("converting COMPOUND TEXT to Utf-16LE. cbSrcLen=%d, cb=%d, pu8SrcText=%.*s\n",
589fd26cedb2b4ebbed14f2964cad03cc8ebbca2vboxsync cbSrcLen, cb, cbSrcLen, reinterpret_cast<char *>(pValue)));
589fd26cedb2b4ebbed14f2964cad03cc8ebbca2vboxsync *pcbActual = 0; /* Only set this to the right value on success. */
589fd26cedb2b4ebbed14f2964cad03cc8ebbca2vboxsync /* First convert the compound text to Utf8 */
589fd26cedb2b4ebbed14f2964cad03cc8ebbca2vboxsync property.value = reinterpret_cast<unsigned char *>(pValue);
589fd26cedb2b4ebbed14f2964cad03cc8ebbca2vboxsync property.encoding = g_ctxX11.atomCText;
589fd26cedb2b4ebbed14f2964cad03cc8ebbca2vboxsync property.format = 8;
589fd26cedb2b4ebbed14f2964cad03cc8ebbca2vboxsync property.nitems = cbSrcLen;
589fd26cedb2b4ebbed14f2964cad03cc8ebbca2vboxsync#ifdef RT_OS_SOLARIS
589fd26cedb2b4ebbed14f2964cad03cc8ebbca2vboxsync int xrc = XmbTextPropertyToTextList(XtDisplay(g_ctxX11.widget), &property, &ppu8SrcText, &cProps);
589fd26cedb2b4ebbed14f2964cad03cc8ebbca2vboxsync#else
589fd26cedb2b4ebbed14f2964cad03cc8ebbca2vboxsync int xrc = Xutf8TextPropertyToTextList(XtDisplay(g_ctxX11.widget), &property, &ppu8SrcText, &cProps);
589fd26cedb2b4ebbed14f2964cad03cc8ebbca2vboxsync#endif
589fd26cedb2b4ebbed14f2964cad03cc8ebbca2vboxsync XtFree(reinterpret_cast<char *>(pValue));
589fd26cedb2b4ebbed14f2964cad03cc8ebbca2vboxsync if (xrc < 0)
589fd26cedb2b4ebbed14f2964cad03cc8ebbca2vboxsync switch(xrc)
589fd26cedb2b4ebbed14f2964cad03cc8ebbca2vboxsync {
589fd26cedb2b4ebbed14f2964cad03cc8ebbca2vboxsync case XNoMemory:
589fd26cedb2b4ebbed14f2964cad03cc8ebbca2vboxsync rc = VERR_NO_MEMORY;
589fd26cedb2b4ebbed14f2964cad03cc8ebbca2vboxsync break;
589fd26cedb2b4ebbed14f2964cad03cc8ebbca2vboxsync case XLocaleNotSupported:
589fd26cedb2b4ebbed14f2964cad03cc8ebbca2vboxsync case XConverterNotFound:
589fd26cedb2b4ebbed14f2964cad03cc8ebbca2vboxsync rc = VERR_NOT_SUPPORTED;
589fd26cedb2b4ebbed14f2964cad03cc8ebbca2vboxsync break;
589fd26cedb2b4ebbed14f2964cad03cc8ebbca2vboxsync default:
589fd26cedb2b4ebbed14f2964cad03cc8ebbca2vboxsync rc = VERR_UNRESOLVED_ERROR;
589fd26cedb2b4ebbed14f2964cad03cc8ebbca2vboxsync }
589fd26cedb2b4ebbed14f2964cad03cc8ebbca2vboxsync /* Now convert the UTF8 to UTF16 */
589fd26cedb2b4ebbed14f2964cad03cc8ebbca2vboxsync if (RT_SUCCESS(rc))
af062818b47340eef15700d2f0211576ba3506eevboxsync rc = RTStrToUtf16Ex(*ppu8SrcText, cbSrcLen, &pu16SrcText, 0, &cwSrcLen);
af062818b47340eef15700d2f0211576ba3506eevboxsync /* Check how much longer will the converted text will be. */
af062818b47340eef15700d2f0211576ba3506eevboxsync if (RT_SUCCESS(rc))
af062818b47340eef15700d2f0211576ba3506eevboxsync rc = vboxClipboardUtf16GetWinSize(pu16SrcText, cwSrcLen, &cwDestLen);
589fd26cedb2b4ebbed14f2964cad03cc8ebbca2vboxsync if (RT_SUCCESS(rc) && (cb < cwDestLen * 2))
af062818b47340eef15700d2f0211576ba3506eevboxsync {
af062818b47340eef15700d2f0211576ba3506eevboxsync /* Not enough buffer space provided - report the amount needed. */
af062818b47340eef15700d2f0211576ba3506eevboxsync LogFlowFunc (("guest buffer too small: size %d bytes, needed %d. Returning.\n",
af062818b47340eef15700d2f0211576ba3506eevboxsync cb, cwDestLen * 2));
af062818b47340eef15700d2f0211576ba3506eevboxsync *pcbActual = cwDestLen * 2;
af062818b47340eef15700d2f0211576ba3506eevboxsync rc = VERR_BUFFER_OVERFLOW;
af062818b47340eef15700d2f0211576ba3506eevboxsync }
589fd26cedb2b4ebbed14f2964cad03cc8ebbca2vboxsync /* Convert the text. */
589fd26cedb2b4ebbed14f2964cad03cc8ebbca2vboxsync if (RT_SUCCESS(rc))
af062818b47340eef15700d2f0211576ba3506eevboxsync rc = vboxClipboardUtf16LinToWin(pu16SrcText, cwSrcLen, pu16DestText, cb / 2);
af062818b47340eef15700d2f0211576ba3506eevboxsync if (RT_SUCCESS(rc))
af062818b47340eef15700d2f0211576ba3506eevboxsync {
589fd26cedb2b4ebbed14f2964cad03cc8ebbca2vboxsync LogFlowFunc (("converted string is %.*ls\n", cwDestLen, pu16DestText));
589fd26cedb2b4ebbed14f2964cad03cc8ebbca2vboxsync *pcbActual = cwDestLen * 2;
af062818b47340eef15700d2f0211576ba3506eevboxsync }
af062818b47340eef15700d2f0211576ba3506eevboxsync if (ppu8SrcText != NULL)
af062818b47340eef15700d2f0211576ba3506eevboxsync XFreeStringList(ppu8SrcText);
af062818b47340eef15700d2f0211576ba3506eevboxsync RTUtf16Free(pu16SrcText);
af062818b47340eef15700d2f0211576ba3506eevboxsync LogFlowFunc(("Returning. Status is %Rrc\n", rc));
af062818b47340eef15700d2f0211576ba3506eevboxsync RTSemEventSignal(g_ctxX11.waitForData);
af062818b47340eef15700d2f0211576ba3506eevboxsync}
af062818b47340eef15700d2f0211576ba3506eevboxsync
af062818b47340eef15700d2f0211576ba3506eevboxsync/**
af062818b47340eef15700d2f0211576ba3506eevboxsync * Convert the Latin1 text obtained from the X11 clipboard to UTF-16LE with
af062818b47340eef15700d2f0211576ba3506eevboxsync * Windows EOLs, place it in the buffer supplied and signal that data has
af062818b47340eef15700d2f0211576ba3506eevboxsync * arrived.
af062818b47340eef15700d2f0211576ba3506eevboxsync *
af062818b47340eef15700d2f0211576ba3506eevboxsync * @param pValue Source Latin1 text
af062818b47340eef15700d2f0211576ba3506eevboxsync * @param cbSourceLen Length in 8-bit bytes of the source text
af062818b47340eef15700d2f0211576ba3506eevboxsync * @param pv Where to store the converted data
af062818b47340eef15700d2f0211576ba3506eevboxsync * @param cb Length in bytes of the buffer pointed to by cb
af062818b47340eef15700d2f0211576ba3506eevboxsync * @param pcbActual Where to store the size of the converted data
589fd26cedb2b4ebbed14f2964cad03cc8ebbca2vboxsync * @param pClient Pointer to the client context structure
af062818b47340eef15700d2f0211576ba3506eevboxsync * @note X11 backend code, called from the Xt callback when we wish to read
af062818b47340eef15700d2f0211576ba3506eevboxsync * the X11 clipboard.
af062818b47340eef15700d2f0211576ba3506eevboxsync */
af062818b47340eef15700d2f0211576ba3506eevboxsyncstatic void vboxClipboardGetLatin1FromX11(XtPointer pValue,
af062818b47340eef15700d2f0211576ba3506eevboxsync unsigned cbSourceLen, void *pv,
af062818b47340eef15700d2f0211576ba3506eevboxsync unsigned cb, uint32_t *pcbActual)
af062818b47340eef15700d2f0211576ba3506eevboxsync{
af062818b47340eef15700d2f0211576ba3506eevboxsync unsigned cwDestLen = cbSourceLen + 1;
af062818b47340eef15700d2f0211576ba3506eevboxsync char *pu8SourceText = reinterpret_cast<char *>(pValue);
af062818b47340eef15700d2f0211576ba3506eevboxsync PRTUTF16 pu16DestText = reinterpret_cast<PRTUTF16>(pv);
af062818b47340eef15700d2f0211576ba3506eevboxsync int rc = VINF_SUCCESS;
589fd26cedb2b4ebbed14f2964cad03cc8ebbca2vboxsync
af062818b47340eef15700d2f0211576ba3506eevboxsync LogFlowFunc (("converting Latin1 to Utf-16LE. Original is %.*s\n",
af062818b47340eef15700d2f0211576ba3506eevboxsync cbSourceLen, pu8SourceText));
af062818b47340eef15700d2f0211576ba3506eevboxsync *pcbActual = 0; /* Only set this to the right value on success. */
af062818b47340eef15700d2f0211576ba3506eevboxsync for (unsigned i = 0; i < cbSourceLen; i++)
af062818b47340eef15700d2f0211576ba3506eevboxsync if (pu8SourceText[i] == LINEFEED)
af062818b47340eef15700d2f0211576ba3506eevboxsync ++cwDestLen;
af062818b47340eef15700d2f0211576ba3506eevboxsync if (cb < cwDestLen * 2)
af062818b47340eef15700d2f0211576ba3506eevboxsync {
af062818b47340eef15700d2f0211576ba3506eevboxsync /* Not enough buffer space provided - report the amount needed. */
af062818b47340eef15700d2f0211576ba3506eevboxsync LogFlowFunc (("guest buffer too small: size %d bytes\n", cb));
af062818b47340eef15700d2f0211576ba3506eevboxsync *pcbActual = cwDestLen * 2;
af062818b47340eef15700d2f0211576ba3506eevboxsync rc = VERR_BUFFER_OVERFLOW;
af062818b47340eef15700d2f0211576ba3506eevboxsync }
af062818b47340eef15700d2f0211576ba3506eevboxsync if (RT_SUCCESS(rc))
af062818b47340eef15700d2f0211576ba3506eevboxsync {
af062818b47340eef15700d2f0211576ba3506eevboxsync for (unsigned i = 0, j = 0; i < cbSourceLen; ++i, ++j)
af062818b47340eef15700d2f0211576ba3506eevboxsync if (pu8SourceText[i] != LINEFEED)
af062818b47340eef15700d2f0211576ba3506eevboxsync pu16DestText[j] = pu8SourceText[i]; /* latin1 < utf-16LE */
af062818b47340eef15700d2f0211576ba3506eevboxsync else
af062818b47340eef15700d2f0211576ba3506eevboxsync {
589fd26cedb2b4ebbed14f2964cad03cc8ebbca2vboxsync pu16DestText[j] = CARRIAGERETURN;
589fd26cedb2b4ebbed14f2964cad03cc8ebbca2vboxsync ++j;
589fd26cedb2b4ebbed14f2964cad03cc8ebbca2vboxsync pu16DestText[j] = LINEFEED;
589fd26cedb2b4ebbed14f2964cad03cc8ebbca2vboxsync }
589fd26cedb2b4ebbed14f2964cad03cc8ebbca2vboxsync pu16DestText[cwDestLen - 1] = 0;
589fd26cedb2b4ebbed14f2964cad03cc8ebbca2vboxsync *pcbActual = cwDestLen * 2;
589fd26cedb2b4ebbed14f2964cad03cc8ebbca2vboxsync LogFlowFunc (("converted text is %.*ls\n", cwDestLen, pu16DestText));
589fd26cedb2b4ebbed14f2964cad03cc8ebbca2vboxsync }
589fd26cedb2b4ebbed14f2964cad03cc8ebbca2vboxsync XtFree(reinterpret_cast<char *>(pValue));
af062818b47340eef15700d2f0211576ba3506eevboxsync RTSemEventSignal(g_ctxX11.waitForData);
af062818b47340eef15700d2f0211576ba3506eevboxsync LogFlowFunc(("Returning. Status is %Rrc\n", rc));
af062818b47340eef15700d2f0211576ba3506eevboxsync}
af062818b47340eef15700d2f0211576ba3506eevboxsync
af062818b47340eef15700d2f0211576ba3506eevboxsync/**
af062818b47340eef15700d2f0211576ba3506eevboxsync * Convert the text obtained from the X11 clipboard to UTF-16LE with Windows
af062818b47340eef15700d2f0211576ba3506eevboxsync * EOLs, place it in the buffer supplied and signal that data has arrived.
af062818b47340eef15700d2f0211576ba3506eevboxsync * @note X11 backend code, callback for XtGetSelectionValue, for use when
af062818b47340eef15700d2f0211576ba3506eevboxsync * the X11 clipboard contains a text format we understand.
af062818b47340eef15700d2f0211576ba3506eevboxsync */
af062818b47340eef15700d2f0211576ba3506eevboxsyncstatic void vboxClipboardGetDataFromX11(Widget, XtPointer pClientData,
af062818b47340eef15700d2f0211576ba3506eevboxsync Atom * /* selection */,
589fd26cedb2b4ebbed14f2964cad03cc8ebbca2vboxsync Atom *atomType,
589fd26cedb2b4ebbed14f2964cad03cc8ebbca2vboxsync XtPointer pValue,
589fd26cedb2b4ebbed14f2964cad03cc8ebbca2vboxsync long unsigned int *pcLen,
589fd26cedb2b4ebbed14f2964cad03cc8ebbca2vboxsync int *piFormat)
589fd26cedb2b4ebbed14f2964cad03cc8ebbca2vboxsync{
589fd26cedb2b4ebbed14f2964cad03cc8ebbca2vboxsync VBOXCLIPBOARDREQUEST *pRequest
589fd26cedb2b4ebbed14f2964cad03cc8ebbca2vboxsync = reinterpret_cast<VBOXCLIPBOARDREQUEST *>(pClientData);
589fd26cedb2b4ebbed14f2964cad03cc8ebbca2vboxsync LogFlowFunc(("pClientData=%p, *pcLen=%lu, *piFormat=%d\n", pClientData,
589fd26cedb2b4ebbed14f2964cad03cc8ebbca2vboxsync *pcLen, *piFormat));
589fd26cedb2b4ebbed14f2964cad03cc8ebbca2vboxsync LogFlowFunc(("g_ctxX11.X11TextFormat=%d, pRequest->cb=%d\n",
589fd26cedb2b4ebbed14f2964cad03cc8ebbca2vboxsync g_ctxX11.X11TextFormat, pRequest->cb));
589fd26cedb2b4ebbed14f2964cad03cc8ebbca2vboxsync unsigned cTextLen = (*pcLen) * (*piFormat) / 8;
af062818b47340eef15700d2f0211576ba3506eevboxsync /* The X Toolkit may have failed to get the clipboard selection for us. */
af062818b47340eef15700d2f0211576ba3506eevboxsync if (*atomType == XT_CONVERT_FAIL)
af062818b47340eef15700d2f0211576ba3506eevboxsync return;
af062818b47340eef15700d2f0211576ba3506eevboxsync /* The clipboard selection may have changed before we could get it. */
af062818b47340eef15700d2f0211576ba3506eevboxsync if (NULL == pValue)
af062818b47340eef15700d2f0211576ba3506eevboxsync return;
af062818b47340eef15700d2f0211576ba3506eevboxsync /* In which format is the clipboard data? */
af062818b47340eef15700d2f0211576ba3506eevboxsync switch (g_ctxX11.X11TextFormat)
af062818b47340eef15700d2f0211576ba3506eevboxsync {
af062818b47340eef15700d2f0211576ba3506eevboxsync case UTF16:
af062818b47340eef15700d2f0211576ba3506eevboxsync vboxClipboardGetUtf16(pValue, cTextLen / 2, pRequest->pv,
af062818b47340eef15700d2f0211576ba3506eevboxsync pRequest->cb, pRequest->pcbActual);
af062818b47340eef15700d2f0211576ba3506eevboxsync break;
af062818b47340eef15700d2f0211576ba3506eevboxsync case CTEXT:
af062818b47340eef15700d2f0211576ba3506eevboxsync vboxClipboardGetCTextFromX11(pValue, cTextLen, pRequest->pv,
af062818b47340eef15700d2f0211576ba3506eevboxsync pRequest->cb, pRequest->pcbActual);
af062818b47340eef15700d2f0211576ba3506eevboxsync break;
af062818b47340eef15700d2f0211576ba3506eevboxsync case UTF8:
af062818b47340eef15700d2f0211576ba3506eevboxsync {
af062818b47340eef15700d2f0211576ba3506eevboxsync /* If we are given broken Utf-8, we treat it as Latin1. Is this acceptable? */
af062818b47340eef15700d2f0211576ba3506eevboxsync size_t cStringLen;
af062818b47340eef15700d2f0211576ba3506eevboxsync char *pu8SourceText = reinterpret_cast<char *>(pValue);
af062818b47340eef15700d2f0211576ba3506eevboxsync
af062818b47340eef15700d2f0211576ba3506eevboxsync if ((g_ctxX11.X11TextFormat == UTF8)
af062818b47340eef15700d2f0211576ba3506eevboxsync && (RTStrUniLenEx(pu8SourceText, *pcLen, &cStringLen) == VINF_SUCCESS))
af062818b47340eef15700d2f0211576ba3506eevboxsync {
af062818b47340eef15700d2f0211576ba3506eevboxsync vboxClipboardGetUtf8FromX11(pValue, cTextLen, pRequest->pv,
af062818b47340eef15700d2f0211576ba3506eevboxsync pRequest->cb, pRequest->pcbActual);
af062818b47340eef15700d2f0211576ba3506eevboxsync break;
589fd26cedb2b4ebbed14f2964cad03cc8ebbca2vboxsync }
589fd26cedb2b4ebbed14f2964cad03cc8ebbca2vboxsync else
af062818b47340eef15700d2f0211576ba3506eevboxsync {
af062818b47340eef15700d2f0211576ba3506eevboxsync vboxClipboardGetLatin1FromX11(pValue, cTextLen, pRequest->pv,
af062818b47340eef15700d2f0211576ba3506eevboxsync pRequest->cb, pRequest->pcbActual);
af062818b47340eef15700d2f0211576ba3506eevboxsync break;
af062818b47340eef15700d2f0211576ba3506eevboxsync }
af062818b47340eef15700d2f0211576ba3506eevboxsync }
af062818b47340eef15700d2f0211576ba3506eevboxsync default:
af062818b47340eef15700d2f0211576ba3506eevboxsync LogFunc (("bad target format\n"));
af062818b47340eef15700d2f0211576ba3506eevboxsync XtFree(reinterpret_cast<char *>(pValue));
af062818b47340eef15700d2f0211576ba3506eevboxsync return;
af062818b47340eef15700d2f0211576ba3506eevboxsync }
af062818b47340eef15700d2f0211576ba3506eevboxsync g_ctxX11.notifyVBox = true;
af062818b47340eef15700d2f0211576ba3506eevboxsync}
af062818b47340eef15700d2f0211576ba3506eevboxsync
af062818b47340eef15700d2f0211576ba3506eevboxsync/**
af062818b47340eef15700d2f0211576ba3506eevboxsync * Notify the host clipboard about the data formats we support, based on the
af062818b47340eef15700d2f0211576ba3506eevboxsync * "targets" (available data formats) information obtained from the X11
af062818b47340eef15700d2f0211576ba3506eevboxsync * clipboard.
af062818b47340eef15700d2f0211576ba3506eevboxsync * @note X11 backend code, callback for XtGetSelectionValue, called when we
af062818b47340eef15700d2f0211576ba3506eevboxsync * poll for available targets.
af062818b47340eef15700d2f0211576ba3506eevboxsync * @todo This function still references host-specific data and calls host
af062818b47340eef15700d2f0211576ba3506eevboxsync * frontend APIs. Fix.
af062818b47340eef15700d2f0211576ba3506eevboxsync */
af062818b47340eef15700d2f0211576ba3506eevboxsyncstatic void vboxClipboardGetTargetsFromX11(Widget,
af062818b47340eef15700d2f0211576ba3506eevboxsync XtPointer /* pClientData */,
af062818b47340eef15700d2f0211576ba3506eevboxsync Atom * /* selection */,
af062818b47340eef15700d2f0211576ba3506eevboxsync Atom *atomType,
af062818b47340eef15700d2f0211576ba3506eevboxsync XtPointer pValue,
af062818b47340eef15700d2f0211576ba3506eevboxsync long unsigned int *pcLen,
af062818b47340eef15700d2f0211576ba3506eevboxsync int *piFormat)
af062818b47340eef15700d2f0211576ba3506eevboxsync{
af062818b47340eef15700d2f0211576ba3506eevboxsync Atom *atomTargets = reinterpret_cast<Atom *>(pValue);
af062818b47340eef15700d2f0211576ba3506eevboxsync unsigned cAtoms = *pcLen;
af062818b47340eef15700d2f0211576ba3506eevboxsync g_eClipboardFormats eBestTarget = INVALID;
af062818b47340eef15700d2f0211576ba3506eevboxsync Atom atomBestTarget = None;
af062818b47340eef15700d2f0211576ba3506eevboxsync
af062818b47340eef15700d2f0211576ba3506eevboxsync Log3 (("%s: called\n", __PRETTY_FUNCTION__));
af062818b47340eef15700d2f0211576ba3506eevboxsync if (*atomType == XT_CONVERT_FAIL)
af062818b47340eef15700d2f0211576ba3506eevboxsync {
589fd26cedb2b4ebbed14f2964cad03cc8ebbca2vboxsync LogFunc (("reading clipboard from host, X toolkit failed to convert the selection\n"));
589fd26cedb2b4ebbed14f2964cad03cc8ebbca2vboxsync return;
589fd26cedb2b4ebbed14f2964cad03cc8ebbca2vboxsync }
589fd26cedb2b4ebbed14f2964cad03cc8ebbca2vboxsync
589fd26cedb2b4ebbed14f2964cad03cc8ebbca2vboxsync for (unsigned i = 0; i < cAtoms; ++i)
589fd26cedb2b4ebbed14f2964cad03cc8ebbca2vboxsync {
589fd26cedb2b4ebbed14f2964cad03cc8ebbca2vboxsync for (unsigned j = 0; j != g_ctxX11.formatList.size(); ++j)
589fd26cedb2b4ebbed14f2964cad03cc8ebbca2vboxsync if (g_ctxX11.formatList[j].atom == atomTargets[i])
589fd26cedb2b4ebbed14f2964cad03cc8ebbca2vboxsync {
589fd26cedb2b4ebbed14f2964cad03cc8ebbca2vboxsync if (eBestTarget < g_ctxX11.formatList[j].format)
589fd26cedb2b4ebbed14f2964cad03cc8ebbca2vboxsync {
589fd26cedb2b4ebbed14f2964cad03cc8ebbca2vboxsync eBestTarget = g_ctxX11.formatList[j].format;
589fd26cedb2b4ebbed14f2964cad03cc8ebbca2vboxsync atomBestTarget = g_ctxX11.formatList[j].atom;
589fd26cedb2b4ebbed14f2964cad03cc8ebbca2vboxsync }
589fd26cedb2b4ebbed14f2964cad03cc8ebbca2vboxsync break;
589fd26cedb2b4ebbed14f2964cad03cc8ebbca2vboxsync }
589fd26cedb2b4ebbed14f2964cad03cc8ebbca2vboxsync if (g_debugClipboard)
589fd26cedb2b4ebbed14f2964cad03cc8ebbca2vboxsync {
589fd26cedb2b4ebbed14f2964cad03cc8ebbca2vboxsync char *szAtomName = XGetAtomName(XtDisplay(g_ctxX11.widget), atomTargets[i]);
589fd26cedb2b4ebbed14f2964cad03cc8ebbca2vboxsync if (szAtomName != 0)
589fd26cedb2b4ebbed14f2964cad03cc8ebbca2vboxsync {
589fd26cedb2b4ebbed14f2964cad03cc8ebbca2vboxsync Log2 (("%s: the host offers target %s\n", __PRETTY_FUNCTION__,
589fd26cedb2b4ebbed14f2964cad03cc8ebbca2vboxsync szAtomName));
589fd26cedb2b4ebbed14f2964cad03cc8ebbca2vboxsync XFree(szAtomName);
589fd26cedb2b4ebbed14f2964cad03cc8ebbca2vboxsync }
589fd26cedb2b4ebbed14f2964cad03cc8ebbca2vboxsync }
589fd26cedb2b4ebbed14f2964cad03cc8ebbca2vboxsync }
589fd26cedb2b4ebbed14f2964cad03cc8ebbca2vboxsync g_ctxX11.atomX11TextFormat = atomBestTarget;
589fd26cedb2b4ebbed14f2964cad03cc8ebbca2vboxsync if ((eBestTarget != g_ctxX11.X11TextFormat) || (g_ctxX11.notifyVBox == true))
589fd26cedb2b4ebbed14f2964cad03cc8ebbca2vboxsync {
589fd26cedb2b4ebbed14f2964cad03cc8ebbca2vboxsync uint32_t u32Formats = 0;
589fd26cedb2b4ebbed14f2964cad03cc8ebbca2vboxsync if (g_debugClipboard)
af062818b47340eef15700d2f0211576ba3506eevboxsync {
af062818b47340eef15700d2f0211576ba3506eevboxsync if (atomBestTarget != None)
af062818b47340eef15700d2f0211576ba3506eevboxsync {
af062818b47340eef15700d2f0211576ba3506eevboxsync char *szAtomName = XGetAtomName(XtDisplay(g_ctxX11.widget), atomBestTarget);
af062818b47340eef15700d2f0211576ba3506eevboxsync Log2 (("%s: switching to host text target %s. Available targets are:\n",
af062818b47340eef15700d2f0211576ba3506eevboxsync __PRETTY_FUNCTION__, szAtomName));
af062818b47340eef15700d2f0211576ba3506eevboxsync XFree(szAtomName);
af062818b47340eef15700d2f0211576ba3506eevboxsync }
af062818b47340eef15700d2f0211576ba3506eevboxsync else
af062818b47340eef15700d2f0211576ba3506eevboxsync Log2(("%s: no supported host text target found. Available targets are:\n",
__PRETTY_FUNCTION__));
for (unsigned i = 0; i < cAtoms; ++i)
{
char *szAtomName = XGetAtomName(XtDisplay(g_ctxX11.widget), atomTargets[i]);
if (szAtomName != 0)
{
Log2 (("%s: %s\n", __PRETTY_FUNCTION__, szAtomName));
XFree(szAtomName);
}
}
}
g_ctxX11.X11TextFormat = eBestTarget;
if (eBestTarget != INVALID)
u32Formats |= VBOX_SHARED_CLIPBOARD_FMT_UNICODETEXT;
vboxSvcClipboardReportMsg (g_ctxHost.pClient, VBOX_SHARED_CLIPBOARD_HOST_MSG_FORMATS,
u32Formats);
g_ctxX11.notifyVBox = false;
}
XtFree(reinterpret_cast<char *>(pValue));
}
/**
* This timer callback is called every 200ms to check the contents of the X11
* clipboard.
* @note X11 backend code, callback for XtAppAddTimeOut, recursively
* re-armed.
* @todo This function still references host-specific data. Fix.
* @todo Use the XFIXES extension to check for new clipboard data when
* available.
*/
static void vboxClipboardPollX11ForTargets(XtPointer /* pUserData */, XtIntervalId * /* hTimerId */)
{
Log3 (("%s: called\n", __PRETTY_FUNCTION__));
/* Get the current clipboard contents */
if (g_ctxX11.eOwner == X11 && g_ctxHost.pClient != 0)
{
Log3 (("%s: requesting the targets that the host clipboard offers\n",
__PRETTY_FUNCTION__));
XtGetSelectionValue(g_ctxX11.widget, g_ctxX11.atomClipboard, g_ctxX11.atomTargets,
vboxClipboardGetTargetsFromX11, reinterpret_cast<XtPointer>(g_ctxHost.pClient),
CurrentTime);
}
/* Re-arm our timer */
XtAppAddTimeOut(g_ctxX11.appContext, 200 /* ms */, vboxClipboardPollX11ForTargets, 0);
}
/** We store information about the target formats we can handle in a global
* vector for internal use.
* @note X11 backend code.
*/
static void vboxClipboardAddFormat(const char *pszName, g_eClipboardFormats eFormat,
unsigned guestFormat)
{
VBOXCLIPBOARDFORMAT sFormat;
/* Get an atom from the X server for that target format */
Atom atomFormat = XInternAtom(XtDisplay(g_ctxX11.widget), pszName, false);
sFormat.atom = atomFormat;
sFormat.format = eFormat;
sFormat.guestFormat = guestFormat;
g_ctxX11.formatList.push_back(sFormat);
LogFlow (("vboxClipboardAddFormat: added format %s (%d)\n", pszName, eFormat));
}
/**
* The main loop of our clipboard reader.
* @note X11 backend code.
*/
static int vboxClipboardThread(RTTHREAD self, void * /* pvUser */)
{
LogRel(("Shared clipboard: starting host clipboard thread\n"));
/* Set up a timer to poll the host clipboard */
XtAppAddTimeOut(g_ctxX11.appContext, 200 /* ms */, vboxClipboardPollX11ForTargets, 0);
XtAppMainLoop(g_ctxX11.appContext);
g_ctxX11.formatList.clear();
LogRel(("Shared clipboard: host clipboard thread terminated successfully\n"));
return VINF_SUCCESS;
}
/** X11 specific initialisation for the shared clipboard.
* @note X11 backend code.
*/
int vboxClipboardInitX11 (void)
{
/* Create a window and make it a clipboard viewer. */
int cArgc = 0;
char *pcArgv = 0;
int rc = VINF_SUCCESS;
// static String szFallbackResources[] = { (char*)"*.width: 1", (char*)"*.height: 1", NULL };
Display *pDisplay;
/* Make sure we are thread safe */
XtToolkitThreadInitialize();
/* Set up the Clipbard application context and main window. We call all these functions
directly instead of calling XtOpenApplication() so that we can fail gracefully if we
can't get an X11 display. */
XtToolkitInitialize();
g_ctxX11.appContext = XtCreateApplicationContext();
// XtAppSetFallbackResources(g_ctxX11.appContext, szFallbackResources);
pDisplay = XtOpenDisplay(g_ctxX11.appContext, 0, 0, "VBoxClipboard", 0, 0, &cArgc, &pcArgv);
if (NULL == pDisplay)
{
LogRel(("Shared clipboard: failed to connect to the host clipboard - the window system may not be running.\n"));
rc = VERR_NOT_SUPPORTED;
}
if (RT_SUCCESS(rc))
{
g_ctxX11.widget = XtVaAppCreateShell(0, "VBoxClipboard", applicationShellWidgetClass, pDisplay,
XtNwidth, 1, XtNheight, 1, NULL);
if (NULL == g_ctxX11.widget)
{
LogRel(("Shared clipboard: failed to construct the X11 window for the host clipboard manager.\n"));
rc = VERR_NO_MEMORY;
}
}
if (RT_SUCCESS(rc))
{
XtSetMappedWhenManaged(g_ctxX11.widget, false);
XtRealizeWidget(g_ctxX11.widget);
/* Get hold of the atoms which we need */
g_ctxX11.atomClipboard = XInternAtom(XtDisplay(g_ctxX11.widget), "CLIPBOARD", false /* only_if_exists */);
g_ctxX11.atomPrimary = XInternAtom(XtDisplay(g_ctxX11.widget), "PRIMARY", false);
g_ctxX11.atomTargets = XInternAtom(XtDisplay(g_ctxX11.widget), "TARGETS", false);
g_ctxX11.atomMultiple = XInternAtom(XtDisplay(g_ctxX11.widget), "MULTIPLE", false);
g_ctxX11.atomTimestamp = XInternAtom(XtDisplay(g_ctxX11.widget), "TIMESTAMP", false);
g_ctxX11.atomUtf16 = XInternAtom(XtDisplay(g_ctxX11.widget),
"text/plain;charset=ISO-10646-UCS-2", false);
g_ctxX11.atomUtf8 = XInternAtom(XtDisplay(g_ctxX11.widget), "UTF_STRING", false);
/* And build up the vector of supported formats */
g_ctxX11.atomCText = XInternAtom(XtDisplay(g_ctxX11.widget), "COMPOUND_TEXT", false);
/* And build up the vector of supported formats */
if (!g_testUtf8 && !g_testCText)
vboxClipboardAddFormat("text/plain;charset=ISO-10646-UCS-2", UTF16,
VBOX_SHARED_CLIPBOARD_FMT_UNICODETEXT);
if (!g_testUtf16 && !g_testCText)
{
vboxClipboardAddFormat("UTF8_STRING", UTF8,
VBOX_SHARED_CLIPBOARD_FMT_UNICODETEXT);
vboxClipboardAddFormat("text/plain;charset=UTF-8", UTF8,
VBOX_SHARED_CLIPBOARD_FMT_UNICODETEXT);
vboxClipboardAddFormat("text/plain;charset=utf-8", UTF8,
VBOX_SHARED_CLIPBOARD_FMT_UNICODETEXT);
vboxClipboardAddFormat("STRING", UTF8,
VBOX_SHARED_CLIPBOARD_FMT_UNICODETEXT);
vboxClipboardAddFormat("TEXT", UTF8,
VBOX_SHARED_CLIPBOARD_FMT_UNICODETEXT);
vboxClipboardAddFormat("text/plain", UTF8,
VBOX_SHARED_CLIPBOARD_FMT_UNICODETEXT);
}
if (!g_testUtf16 && !g_testUtf8)
vboxClipboardAddFormat("COMPOUND_TEXT", CTEXT,
VBOX_SHARED_CLIPBOARD_FMT_UNICODETEXT);
}
return rc;
}
/**
* Initialise the X11 backend of the shared clipboard.
* @note X11 backend code
*/
int vboxClipboardInitBackend (void)
{
int rc;
if (!RTEnvGet("DISPLAY"))
{
/*
* If we don't find the DISPLAY environment variable we assume that we are not
* connected to an X11 server. Don't actually try to do this then, just fail
* silently and report success on every call. This is important for VBoxHeadless.
*/
LogRelFunc(("X11 DISPLAY variable not set -- disabling shared clipboard\n"));
g_fHaveX11 = false;
return VINF_SUCCESS;
}
if (RTEnvGet("VBOX_CBTEST_UTF16"))
{
g_testUtf16 = true;
LogRel(("Host clipboard: testing Utf16\n"));
}
else if (RTEnvGet("VBOX_CBTEST_UTF8"))
{
g_testUtf8 = true;
LogRel(("Host clipboard: testing Utf8\n"));
}
else if (RTEnvGet("VBOX_CBTEST_CTEXT"))
{
g_testCText = true;
LogRel(("Host clipboard: testing compound text\n"));
}
else if (RTEnvGet("VBOX_CBDEBUG"))
{
g_debugClipboard = true;
LogRel(("Host clipboard: enabling additional debugging output\n"));
}
g_fHaveX11 = true;
LogRel(("Initializing X11 clipboard backend\n"));
RTSemEventCreate(&g_ctxX11.waitForData);
rc = vboxClipboardInitX11();
if (RT_SUCCESS(rc))
{
rc = RTThreadCreate(&g_ctxX11.thread, vboxClipboardThread, 0, 0,
RTTHREADTYPE_IO, RTTHREADFLAGS_WAITABLE, "SHCLIP");
if (RT_FAILURE(rc))
LogRel(("Failed to initialise the shared clipboard X11 backend.\n"));
}
if (RT_FAILURE(rc))
RTSemEventDestroy(g_ctxX11.waitForData);
return rc;
}
/**
* Initialise the host side of the shared clipboard.
* @note Host glue code
*/
int vboxClipboardInit (void)
{
int rc = VINF_SUCCESS;
LogRel(("Initializing host clipboard service\n"));
RTSemEventCreate(&g_ctxHost.waitForData);
RTSemMutexCreate(&g_ctxHost.clipboardMutex);
rc = vboxClipboardInitBackend();
if (RT_FAILURE(rc))
{
RTSemEventDestroy(g_ctxHost.waitForData);
RTSemMutexDestroy(g_ctxHost.clipboardMutex);
LogRel(("Failed to start the host shared clipboard service.\n"));
}
return rc;
}
/**
* Terminate the shared clipboard X11 backend.
* @note X11 backend code
*/
int vboxClipboardDestroyBackend (void)
{
int rc, rcThread;
unsigned count = 0;
XEvent ev;
/*
* Immediately return if we are not connected to the host X server.
*/
if (!g_fHaveX11)
return VINF_SUCCESS;
LogRelFunc(("shutting down the shared clipboard X11 backend\n"));
/* Set the termination flag. This has been observed to block if it was set
* during a request for clipboard data coming from X11, so only we do it
* after releasing any such requests. */
XtAppSetExitFlag(g_ctxX11.appContext);
/* Wake up the event loop */
memset(&ev, 0, sizeof(ev));
ev.xclient.type = ClientMessage;
ev.xclient.format = 8;
XSendEvent(XtDisplay(g_ctxX11.widget), XtWindow(g_ctxX11.widget), false, 0, &ev);
XFlush(XtDisplay(g_ctxX11.widget));
do
{
rc = RTThreadWait(g_ctxX11.thread, 1000, &rcThread);
++count;
Assert(RT_SUCCESS(rc) || ((VERR_TIMEOUT == rc) && (count != 5)));
} while ((VERR_TIMEOUT == rc) && (count < 300));
if (RT_SUCCESS(rc))
{
/* We can safely destroy this now, as only this thread ever waits
* for it. */
RTSemEventDestroy(g_ctxX11.waitForData);
AssertRC(rcThread);
}
else
LogRel(("vboxClipboardDestroy: rc=%Rrc\n", rc));
XtCloseDisplay(XtDisplay(g_ctxX11.widget));
LogFlowFunc(("returning %Rrc.\n", rc));
return rc;
}
/**
* Terminate the host side of the shared clipboard.
* @note host glue code
*/
void vboxClipboardDestroy (void)
{
int rc = VINF_SUCCESS;
LogRelFunc(("shutting down host clipboard\n"));
/* Drop the reference to the client, in case it is still there. This
* will cause any outstanding clipboard data requests from X11 to fail
* immediately. */
g_ctxHost.pClient = NULL;
/* The backend may be waiting for data from VBox. At this point it is no
* longer going to arrive, and we must release it to allow the event
* loop to terminate. In this case the buffer where VBox would have
* written the clipboard data will still be empty and we will just
* return "no data" to the backend. Any subsequent attempts to get the
* data from VBox will fail immediately as the client reference is gone.
*/
/** @note This has been made unconditional, as it should do no harm
* even if we are not waiting. */
RTSemEventSignal(g_ctxHost.waitForData);
rc = vboxClipboardDestroyBackend();
if (RT_SUCCESS(rc))
{
/* We can safely destroy these as the backend has exited
* successfully and no other calls from the host code should be
* forthcoming. */
/** @todo move these two into a frontend proxy object that the
* backend can call. */
/** @todo can the backend fail to exit successfully? What then? */
RTSemEventDestroy(g_ctxHost.waitForData);
RTSemMutexDestroy(g_ctxHost.clipboardMutex);
}
}
/**
* Announce to the X11 backend that we are ready to start.
* @param owner who is the initial clipboard owner
*/
int vboxClipboardConnectBackend (enum g_eOwner owner)
{
LogFlowFunc(("\n"));
/*
* Immediately return if we are not connected to the host X server.
*/
if (!g_fHaveX11)
return VINF_SUCCESS;
g_ctxX11.eOwner = owner;
if (owner == X11)
g_ctxX11.notifyVBox = true;
else
{
/** @todo take ownership of the X11 clipboard. */
}
return VINF_SUCCESS;
}
/**
* Connect a guest to the shared clipboard.
* @note host glue code
* @note on the host, we assume that some other application already owns
* the clipboard and leave ownership to X11.
*/
int vboxClipboardConnect (VBOXCLIPBOARDCLIENTDATA *pClient)
{
int rc = VINF_SUCCESS;
LogFlowFunc(("\n"));
/* Only one client is supported for now */
AssertLogRelReturn(g_ctxHost.pClient == 0, VERR_NOT_SUPPORTED);
pClient->pCtx = &g_ctxHost;
pClient->pCtx->pClient = pClient;
/** The pClient pointer is a dummy anyway, as we only support a single
* client at a time. */
rc = vboxClipboardConnectBackend(X11 /* initial owner */);
return rc;
}
/**
* Called when the VBox may have fallen out of sync with the backend.
* @note X11 backend code
*/
void vboxClipboardRequestSync (void)
{
/*
* Immediately return if we are not connected to the host X server.
*/
if (!g_fHaveX11)
return;
g_ctxX11.notifyVBox = true;
}
/**
* Synchronise the contents of the host clipboard with the guest, called
* after a save and restore of the guest.
* @note Host glue code
*/
int vboxClipboardSync (VBOXCLIPBOARDCLIENTDATA *pClient)
{
/* On a Linux host, the guest should never synchronise/cache its
* clipboard contents, as we have no way of reliably telling when the
* host clipboard data changes. So instead of synchronising, we tell
* the guest to empty its clipboard, and we set the cached flag so that
* we report formats to the guest next time we poll for them. */
/** @note This has been changed so that the message is sent even if
* X11 is not available. */
vboxSvcClipboardReportMsg (g_ctxHost.pClient,
VBOX_SHARED_CLIPBOARD_HOST_MSG_FORMATS, 0);
vboxClipboardRequestSync();
return VINF_SUCCESS;
}
/**
* Shut down the shared clipboard service and "disconnect" the guest.
* @todo This mixes host glue and X11 backend code, separate into two
* functions.
*/
void vboxClipboardDisconnectBackend (void)
{
/*
* Immediately return if we are not connected to the host X server.
*/
if (!g_fHaveX11)
return;
g_ctxX11.eOwner = NONE;
g_ctxX11.X11TextFormat = INVALID;
g_ctxX11.X11BitmapFormat = INVALID;
}
/**
* Shut down the shared clipboard service and "disconnect" the guest.
* @todo This mixes host glue and X11 backend code, separate into two
* functions.
*/
void vboxClipboardDisconnect (VBOXCLIPBOARDCLIENTDATA *)
{
LogFlow(("vboxClipboardDisconnect\n"));
RTSemMutexRequest(g_ctxHost.clipboardMutex, RT_INDEFINITE_WAIT);
g_ctxHost.pClient = NULL;
vboxClipboardDisconnectBackend();
RTSemMutexRelease(g_ctxHost.clipboardMutex);
}
/**
* Satisfy a request from X11 for clipboard targets supported by VBox.
*
* @returns true if we successfully convert the data to the format
* requested, false otherwise.
*
* @param atomTypeReturn The type of the data we are returning
* @param pValReturn A pointer to the data we are returning. This
* should be set to memory allocated by XtMalloc,
* which will be freed later by the Xt toolkit.
* @param pcLenReturn The length of the data we are returning
* @param piFormatReturn The format (8bit, 16bit, 32bit) of the data we are
* returning
* @note X11 backend code, called by the XtOwnSelection callback.
*/
static Boolean vboxClipboardConvertTargetsForX11(Atom *atomTypeReturn,
XtPointer *pValReturn,
unsigned long *pcLenReturn,
int *piFormatReturn)
{
unsigned uListSize = g_ctxX11.formatList.size();
Atom *atomTargets = reinterpret_cast<Atom *>(XtMalloc((uListSize + 3) * sizeof(Atom)));
unsigned cTargets = 0;
LogFlowFunc (("called\n"));
for (unsigned i = 0; i < uListSize; ++i)
{
if ( ((g_ctxX11.vboxFormats & VBOX_SHARED_CLIPBOARD_FMT_UNICODETEXT) != 0)
&& (g_ctxX11.formatList[i].guestFormat == VBOX_SHARED_CLIPBOARD_FMT_UNICODETEXT))
{
atomTargets[cTargets] = g_ctxX11.formatList[i].atom;
++cTargets;
}
}
atomTargets[cTargets] = g_ctxX11.atomTargets;
atomTargets[cTargets + 1] = g_ctxX11.atomMultiple;
atomTargets[cTargets + 2] = g_ctxX11.atomTimestamp;
if (g_debugClipboard)
{
for (unsigned i = 0; i < cTargets + 3; i++)
{
char *szAtomName = XGetAtomName(XtDisplay(g_ctxX11.widget), atomTargets[i]);
if (szAtomName != 0)
{
Log2 (("%s: returning target %s\n", __PRETTY_FUNCTION__,
szAtomName));
XFree(szAtomName);
}
else
{
Log(("%s: invalid atom %d in the list!\n", __PRETTY_FUNCTION__,
atomTargets[i]));
}
}
}
*atomTypeReturn = XA_ATOM;
*pValReturn = reinterpret_cast<XtPointer>(atomTargets);
*pcLenReturn = cTargets + 3;
*piFormatReturn = 32;
return true;
}
/**
* Satisfy a request from X11 to convert the clipboard text to Utf16. We
* return non-zero terminated text.
* @todo that works, but it is bad. Change it to return zero-terminated
* text.
*
* @returns true if we successfully convert the data to the format
* requested, false otherwise.
*
* @param atomTypeReturn Where to store the atom for the type of the data
* we are returning
* @param pValReturn Where to store the pointer to the data we are
* returning. This should be to memory allocated by
* XtMalloc, which will be freed by the Xt toolkit
* later.
* @param pcLenReturn Where to store the length of the data we are
* returning
* @param piFormatReturn Where to store the bit width (8, 16, 32) of the
* data we are returning
* @note X11 backend code, called by the callback for XtOwnSelection.
* @todo this function uses host-specific data and APIs. Fix.
*/
static Boolean vboxClipboardConvertUtf16(Atom *atomTypeReturn,
XtPointer *pValReturn,
unsigned long *pcLenReturn,
int *piFormatReturn)
{
PRTUTF16 pu16SrcText, pu16DestText;
void *pvVBox;
uint32_t cbVBox;
size_t cwSrcLen, cwDestLen;
int rc;
LogFlowFunc (("called\n"));
rc = vboxClipboardReadDataFromVBox(&g_ctxHost, VBOX_SHARED_CLIPBOARD_FMT_UNICODETEXT, &pvVBox, &cbVBox);
if ((RT_FAILURE(rc)) || (cbVBox == 0))
{
/* If vboxClipboardReadDataFromVBox fails then pClient may be invalid */
LogRelFunc (("vboxClipboardReadDataFromVBox returned %Rrc%s\n", rc,
RT_SUCCESS(rc) ? ", cbVBox == 0" : ""));
RTMemFree(pvVBox);
return false;
}
pu16SrcText = reinterpret_cast<PRTUTF16>(pvVBox);
cwSrcLen = cbVBox / 2;
/* How long will the converted text be? */
rc = vboxClipboardUtf16GetLinSize(pu16SrcText, cwSrcLen, &cwDestLen);
if (RT_FAILURE(rc))
{
LogRel(("vboxClipboardConvertUtf16: clipboard conversion failed. vboxClipboardUtf16GetLinSize returned %Rrc. Abandoning.\n", rc));
RTMemFree(pvVBox);
AssertRCReturn(rc, false);
}
if (cwDestLen == 0)
{
LogFlowFunc(("received empty clipboard data from the guest, returning false.\n"));
RTMemFree(pvVBox);
return false;
}
pu16DestText = reinterpret_cast<PRTUTF16>(XtMalloc(cwDestLen * 2));
if (pu16DestText == 0)
{
LogRel(("vboxClipboardConvertUtf16: failed to allocate %d bytes\n", cwDestLen * 2));
RTMemFree(pvVBox);
return false;
}
/* Convert the text. */
rc = vboxClipboardUtf16WinToLin(pu16SrcText, cwSrcLen, pu16DestText, cwDestLen);
if (RT_FAILURE(rc))
{
LogRel(("vboxClipboardConvertUtf16: clipboard conversion failed. vboxClipboardUtf16WinToLin returned %Rrc. Abandoning.\n", rc));
XtFree(reinterpret_cast<char *>(pu16DestText));
RTMemFree(pvVBox);
return false;
}
LogFlowFunc (("converted string is %.*ls. Returning.\n", cwDestLen, pu16DestText));
RTMemFree(pvVBox);
*atomTypeReturn = g_ctxX11.atomUtf16;
*pValReturn = reinterpret_cast<XtPointer>(pu16DestText);
*pcLenReturn = cwDestLen;
*piFormatReturn = 16;
return true;
}
/**
* Satisfy a request from X11 to convert the clipboard text to Utf8. We
* return non-zero terminated text.
* @todo that works, but it is bad. Change it to return zero-terminated
* text.
*
* @returns true if we successfully convert the data to the format
* requested, false otherwise.
*
* @param atomTypeReturn Where to store the atom for the type of the data
* we are returning
* @param pValReturn Where to store the pointer to the data we are
* returning. This should be to memory allocated by
* XtMalloc, which will be freed by the Xt toolkit
* later.
* @param pcLenReturn Where to store the length of the data we are
* returning
* @param piFormatReturn Where to store the bit width (8, 16, 32) of the
* data we are returning
* @note X11 backend code, called by the callback for XtOwnSelection.
* @todo this function uses host-specific data and APIs. Fix.
*/
static Boolean vboxClipboardConvertToUtf8ForX11(Atom *atomTypeReturn,
XtPointer *pValReturn,
unsigned long *pcLenReturn,
int *piFormatReturn)
{
PRTUTF16 pu16SrcText, pu16DestText;
char *pu8DestText;
void *pvVBox;
uint32_t cbVBox;
size_t cwSrcLen, cwDestLen, cbDestLen;
int rc;
LogFlowFunc (("called\n"));
/* Read the clipboard data from the guest. */
rc = vboxClipboardReadDataFromVBox(&g_ctxHost, VBOX_SHARED_CLIPBOARD_FMT_UNICODETEXT, &pvVBox, &cbVBox);
if ((rc != VINF_SUCCESS) || (cbVBox == 0))
{
/* If vboxClipboardReadDataFromVBox fails then pClient may be invalid */
LogRelFunc (("vboxClipboardReadDataFromVBox returned %Rrc%s\n", rc,
RT_SUCCESS(rc) ? ", cbVBox == 0" : ""));
RTMemFree(pvVBox);
return false;
}
pu16SrcText = reinterpret_cast<PRTUTF16>(pvVBox);
cwSrcLen = cbVBox / 2;
/* How long will the converted text be? */
rc = vboxClipboardUtf16GetLinSize(pu16SrcText, cwSrcLen, &cwDestLen);
if (RT_FAILURE(rc))
{
LogRelFunc (("clipboard conversion failed. vboxClipboardUtf16GetLinSize returned %Rrc. Abandoning.\n", rc));
RTMemFree(pvVBox);
AssertRCReturn(rc, false);
}
if (cwDestLen == 0)
{
LogFlowFunc(("received empty clipboard data from the guest, returning false.\n"));
RTMemFree(pvVBox);
return false;
}
pu16DestText = reinterpret_cast<PRTUTF16>(RTMemAlloc(cwDestLen * 2));
if (pu16DestText == 0)
{
LogRelFunc (("failed to allocate %d bytes\n", cwDestLen * 2));
RTMemFree(pvVBox);
return false;
}
/* Convert the text. */
rc = vboxClipboardUtf16WinToLin(pu16SrcText, cwSrcLen, pu16DestText, cwDestLen);
if (RT_FAILURE(rc))
{
LogRelFunc (("clipboard conversion failed. vboxClipboardUtf16WinToLin() returned %Rrc. Abandoning.\n", rc));
RTMemFree(reinterpret_cast<void *>(pu16DestText));
RTMemFree(pvVBox);
return false;
}
/* Allocate enough space, as RTUtf16ToUtf8Ex may fail if the
space is too tightly calculated. */
pu8DestText = XtMalloc(cwDestLen * 4);
if (pu8DestText == 0)
{
LogRelFunc (("failed to allocate %d bytes\n", cwDestLen * 4));
RTMemFree(reinterpret_cast<void *>(pu16DestText));
RTMemFree(pvVBox);
return false;
}
/* Convert the Utf16 string to Utf8. */
rc = RTUtf16ToUtf8Ex(pu16DestText + 1, cwDestLen - 1, &pu8DestText, cwDestLen * 4,
&cbDestLen);
RTMemFree(reinterpret_cast<void *>(pu16DestText));
if (RT_FAILURE(rc))
{
LogRelFunc (("clipboard conversion failed. RTUtf16ToUtf8Ex() returned %Rrc. Abandoning.\n", rc));
XtFree(pu8DestText);
RTMemFree(pvVBox);
return false;
}
LogFlowFunc (("converted string is %.*s. Returning.\n", cbDestLen, pu8DestText));
RTMemFree(pvVBox);
*atomTypeReturn = g_ctxX11.atomUtf8;
*pValReturn = reinterpret_cast<XtPointer>(pu8DestText);
*pcLenReturn = cbDestLen;
*piFormatReturn = 8;
return true;
}
/**
* Satisfy a request from X11 to convert the clipboard text to
* COMPOUND_TEXT. We return non-zero terminated text.
* @todo that works, but it is bad. Change it to return zero-terminated
* text.
*
* @returns true if we successfully convert the data to the format
* requested, false otherwise.
*
* @param atomTypeReturn Where to store the atom for the type of the data
* we are returning
* @param pValReturn Where to store the pointer to the data we are
* returning. This should be to memory allocated by
* XtMalloc, which will be freed by the Xt toolkit
* later.
* @param pcLenReturn Where to store the length of the data we are
* returning
* @param piFormatReturn Where to store the bit width (8, 16, 32) of the
* data we are returning
* @note X11 backend code, called by the callback for XtOwnSelection.
* @todo this function uses host-specific data and APIs. Fix.
*/
static Boolean vboxClipboardConvertToCTextForX11(Atom *atomTypeReturn,
XtPointer *pValReturn,
unsigned long *pcLenReturn,
int *piFormatReturn)
{
PRTUTF16 pu16SrcText, pu16DestText;
void *pvVBox;
uint32_t cbVBox;
char *pu8DestText = 0;
size_t cwSrcLen, cwDestLen, cbDestLen;
XTextProperty property;
int rc;
LogFlowFunc (("called\n"));
/* Read the clipboard data from the guest. */
rc = vboxClipboardReadDataFromVBox(&g_ctxHost, VBOX_SHARED_CLIPBOARD_FMT_UNICODETEXT, &pvVBox, &cbVBox);
if ((rc != VINF_SUCCESS) || (cbVBox == 0))
{
/* If vboxClipboardReadDataFromVBox fails then pClient may be invalid */
LogRelFunc (("vboxClipboardReadDataFromVBox returned %Rrc%s\n", rc,
RT_SUCCESS(rc) ? ", cbVBox == 0" : ""));
RTMemFree(pvVBox);
return false;
}
pu16SrcText = reinterpret_cast<PRTUTF16>(pvVBox);
cwSrcLen = cbVBox / 2;
/* How long will the converted text be? */
rc = vboxClipboardUtf16GetLinSize(pu16SrcText, cwSrcLen, &cwDestLen);
if (RT_FAILURE(rc))
{
LogRelFunc (("clipboard conversion failed. vboxClipboardUtf16GetLinSize returned %Rrc. Abandoning.\n", rc));
RTMemFree(pvVBox);
AssertRCReturn(rc, false);
}
if (cwDestLen == 0)
{
LogFlowFunc(("received empty clipboard data from the guest, returning false.\n"));
RTMemFree(pvVBox);
return false;
}
pu16DestText = reinterpret_cast<PRTUTF16>(RTMemAlloc(cwDestLen * 2));
if (pu16DestText == 0)
{
LogRelFunc (("failed to allocate %d bytes\n", cwDestLen * 2));
RTMemFree(pvVBox);
return false;
}
/* Convert the text. */
rc = vboxClipboardUtf16WinToLin(pu16SrcText, cwSrcLen, pu16DestText, cwDestLen);
if (RT_FAILURE(rc))
{
LogRelFunc (("clipboard conversion failed. vboxClipboardUtf16WinToLin() returned %Rrc. Abandoning.\n", rc));
RTMemFree(reinterpret_cast<void *>(pu16DestText));
RTMemFree(pvVBox);
return false;
}
/* Convert the Utf16 string to Utf8. */
rc = RTUtf16ToUtf8Ex(pu16DestText + 1, cwDestLen - 1, &pu8DestText, 0, &cbDestLen);
RTMemFree(reinterpret_cast<void *>(pu16DestText));
if (RT_FAILURE(rc))
{
LogRelFunc (("clipboard conversion failed. RTUtf16ToUtf8Ex() returned %Rrc. Abandoning.\n", rc));
RTMemFree(pvVBox);
return false;
}
/* And finally (!) convert the Utf8 text to compound text. */
#ifdef RT_OS_SOLARIS
rc = XmbTextListToTextProperty(XtDisplay(g_ctxX11.widget), &pu8DestText, 1,
XCompoundTextStyle, &property);
#else
rc = Xutf8TextListToTextProperty(XtDisplay(g_ctxX11.widget), &pu8DestText, 1,
XCompoundTextStyle, &property);
#endif
RTMemFree(pu8DestText);
if (rc < 0)
{
const char *pcReason;
switch(rc)
{
case XNoMemory:
pcReason = "out of memory";
break;
case XLocaleNotSupported:
pcReason = "locale (Utf8) not supported";
break;
case XConverterNotFound:
pcReason = "converter not found";
break;
default:
pcReason = "unknown error";
}
LogRelFunc (("Xutf8TextListToTextProperty failed. Reason: %s\n",
pcReason));
RTMemFree(pvVBox);
return false;
}
LogFlowFunc (("converted string is %s. Returning.\n", property.value));
RTMemFree(pvVBox);
*atomTypeReturn = property.encoding;
*pValReturn = reinterpret_cast<XtPointer>(property.value);
*pcLenReturn = property.nitems;
*piFormatReturn = property.format;
return true;
}
/**
* Return VBox's clipboard data for an X11 client.
* @note X11 backend code, callback for XtOwnSelection
*/
static Boolean vboxClipboardConvertForX11(Widget, Atom *atomSelection,
Atom *atomTarget,
Atom *atomTypeReturn,
XtPointer *pValReturn,
unsigned long *pcLenReturn,
int *piFormatReturn)
{
g_eClipboardFormats eFormat = INVALID;
LogFlowFunc(("\n"));
/* Drop requests that we receive too late. */
if (g_ctxX11.eOwner != VB)
return false;
if ( (*atomSelection != g_ctxX11.atomClipboard)
&& (*atomSelection != g_ctxX11.atomPrimary)
)
{
LogFlowFunc(("rc = false\n"));
return false;
}
if (g_debugClipboard)
{
char *szAtomName = XGetAtomName(XtDisplay(g_ctxX11.widget), *atomTarget);
if (szAtomName != 0)
{
Log2 (("%s: request for format %s\n", __PRETTY_FUNCTION__, szAtomName));
XFree(szAtomName);
}
else
{
LogFunc (("request for invalid target atom %d!\n", *atomTarget));
}
}
if (*atomTarget == g_ctxX11.atomTargets)
{
eFormat = TARGETS;
}
else
{
for (unsigned i = 0; i != g_ctxX11.formatList.size(); ++i)
{
if (g_ctxX11.formatList[i].atom == *atomTarget)
{
eFormat = g_ctxX11.formatList[i].format;
break;
}
}
}
switch (eFormat)
{
case TARGETS:
return vboxClipboardConvertTargetsForX11(atomTypeReturn, pValReturn,
pcLenReturn, piFormatReturn);
case UTF16:
return vboxClipboardConvertUtf16(atomTypeReturn, pValReturn, pcLenReturn,
piFormatReturn);
case UTF8:
return vboxClipboardConvertToUtf8ForX11(atomTypeReturn, pValReturn,
pcLenReturn, piFormatReturn);
case CTEXT:
return vboxClipboardConvertToCTextForX11(atomTypeReturn, pValReturn,
pcLenReturn, piFormatReturn);
default:
LogFunc (("bad format\n"));
return false;
}
}
/**
* This is called by the X toolkit intrinsics to let us know that another
* X11 client has taken the clipboard. In this case we notify VBox that
* we want ownership of the clipboard.
* @note X11 backend code, callback for XtOwnSelection
*/
static void vboxClipboardReturnToX11(Widget, Atom *)
{
LogFlowFunc (("called, giving VBox clipboard ownership\n"));
g_ctxX11.eOwner = X11;
g_ctxX11.notifyVBox = true;
}
/**
* VBox is taking possession of the shared clipboard.
*
* @param u32Formats Clipboard formats the guest is offering
* @note X11 backend code
*/
void vboxClipboardFormatAnnounceBackend (uint32_t u32Formats)
{
/*
* Immediately return if we are not connected to the host X server.
*/
if (!g_fHaveX11)
return;
g_ctxX11.vboxFormats = u32Formats;
LogFlowFunc (("u32Formats=%d\n", u32Formats));
if (u32Formats == 0)
{
/* This is just an automatism, not a genuine anouncement */
LogFlowFunc(("returning\n"));
return;
}
if (g_ctxX11.eOwner == VB)
{
/* We already own the clipboard, so no need to grab it, especially as that can lead
to races due to the asynchronous nature of the X11 clipboard. This event may also
have been sent out by the guest to invalidate the Windows clipboard cache. */
LogFlowFunc(("returning\n"));
return;
}
Log2 (("%s: giving the guest clipboard ownership\n", __PRETTY_FUNCTION__));
g_ctxX11.eOwner = VB;
g_ctxX11.X11TextFormat = INVALID;
g_ctxX11.X11BitmapFormat = INVALID;
if (XtOwnSelection(g_ctxX11.widget, g_ctxX11.atomClipboard, CurrentTime, vboxClipboardConvertForX11,
vboxClipboardReturnToX11, 0) != True)
{
Log2 (("%s: returning clipboard ownership to the host\n", __PRETTY_FUNCTION__));
/* We set this so that the guest gets notified when we take the clipboard, even if no
guest formats are found which we understand. */
g_ctxX11.notifyVBox = true;
g_ctxX11.eOwner = X11;
}
XtOwnSelection(g_ctxX11.widget, g_ctxX11.atomPrimary, CurrentTime, vboxClipboardConvertForX11,
NULL, 0);
LogFlowFunc(("returning\n"));
}
/**
* VBox is taking possession of the shared clipboard.
*
* @param pClient Context data for the guest system
* @param u32Formats Clipboard formats the guest is offering
* @note Host glue code
*/
void vboxClipboardFormatAnnounce (VBOXCLIPBOARDCLIENTDATA *pClient, uint32_t u32Formats)
{
vboxClipboardFormatAnnounceBackend (u32Formats);
}
/**
* Called when VBox wants to read the X11 clipboard.
*
* @param pClient Context information about the guest VM
* @param u32Format The format that the guest would like to receive the data in
* @param pv Where to write the data to
* @param cb The size of the buffer to write the data to
* @param pcbActual Where to write the actual size of the written data
* @note X11 backend code
*/
int vboxClipboardReadDataBackend (uint32_t u32Format,
VBOXCLIPBOARDREQUEST *pRequest)
{
/*
* Immediately return if we are not connected to the host X server.
*/
if (!g_fHaveX11)
{
/* no data available */
*pRequest->pcbActual = 0;
return VINF_SUCCESS;
}
LogFlowFunc (("u32Format = %d, cb = %d\n", u32Format, pRequest->cb));
/*
* The guest wants to read data in the given format.
*/
if (u32Format & VBOX_SHARED_CLIPBOARD_FMT_UNICODETEXT)
{
if (g_ctxX11.X11TextFormat == INVALID)
{
/* No data available. */
*pRequest->pcbActual = 0;
return VERR_NO_DATA; /* The guest thinks we have data and we don't */
}
/* Initially set the size of the data read to zero in case we fail
* somewhere. */
*pRequest->pcbActual = 0;
/* Send out a request for the data to the current clipboard owner */
XtGetSelectionValue(g_ctxX11.widget, g_ctxX11.atomClipboard,
g_ctxX11.atomX11TextFormat,
vboxClipboardGetDataFromX11,
reinterpret_cast<XtPointer>(pRequest),
CurrentTime);
/* When the data arrives, the vboxClipboardGetDataFromX11 callback will be called. The
callback will signal the event semaphore when it has processed the data for us. */
int rc = RTSemEventWait(g_ctxX11.waitForData, RT_INDEFINITE_WAIT);
if (RT_FAILURE(rc))
return rc;
}
else
{
return VERR_NOT_IMPLEMENTED;
}
return VINF_SUCCESS;
}
/**
* Called when VBox wants to read the X11 clipboard.
*
* @param pClient Context information about the guest VM
* @param u32Format The format that the guest would like to receive the data in
* @param pv Where to write the data to
* @param cb The size of the buffer to write the data to
* @param pcbActual Where to write the actual size of the written data
* @note Host glue code
*/
int vboxClipboardReadData (VBOXCLIPBOARDCLIENTDATA *pClient,
uint32_t u32Format, void *pv, uint32_t cb,
uint32_t *pcbActual)
{
int rc = VINF_SUCCESS;
VBOXCLIPBOARDREQUEST request;
/* No one else (VBox or X11) should currently be waiting. The first
* because requests from VBox are serialised and the second because X11
* previously grabbed the clipboard, so it should not be waiting for
* data from us. */
AssertLogRelReturn (ASMAtomicCmpXchgU32(&g_ctxHost.waiter, VB, NONE),
VERR_DEADLOCK);
request.pv = pv;
request.cb = cb;
request.pcbActual = pcbActual;
rc = vboxClipboardReadDataBackend(u32Format, &request);
g_ctxHost.waiter = NONE;
return rc;
}
/**
* Called when we have requested data from VBox and that data has arrived.
*
* @param pClient Context information about the guest VM
* @param pv Buffer to which the data was written
* @param cb The size of the data written
* @param u32Format The format of the data written
* @note Host glue code
*/
void vboxClipboardWriteData (VBOXCLIPBOARDCLIENTDATA *pClient, void *pv, uint32_t cb, uint32_t u32Format)
{
if (!g_fHaveX11)
return;
LogFlowFunc (("called\n"));
/* Assert that no other transfer is in process (requests are serialised)
* or has not cleaned up properly. */
AssertLogRelReturnVoid ( pClient->data.pv == NULL
&& pClient->data.cb == 0
&& pClient->data.u32Format == 0);
/* Grab the mutex and check that X11 is still waiting for the data before
* delivering it. See the explanation in vboxClipboardReadDataFromVBox. */
RTSemMutexRequest(g_ctxHost.clipboardMutex, RT_INDEFINITE_WAIT);
if (g_ctxHost.waiter == X11 && cb > 0)
{
pClient->data.pv = RTMemAlloc (cb);
if (pClient->data.pv)
{
memcpy (pClient->data.pv, pv, cb);
pClient->data.cb = cb;
pClient->data.u32Format = u32Format;
}
}
RTSemMutexRelease(g_ctxHost.clipboardMutex);
RTSemEventSignal(g_ctxHost.waitForData);
}