Quellcodebibliothek Statistik Leitseite products/Sources/formale Sprachen/C/LibreOffice/vcl/unx/generic/dtrans/   (Office von Apache Version 25.8.3.2©)  Datei vom 5.10.2025 mit Größe 152 kB image not shown  

Quelle  X11_selection.cxx   Sprache: C

 
/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
/*
 * This file is part of the LibreOffice project.
 *
 * This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/.
 *
 * This file incorporates work covered by the following license notice:
 *
 *   Licensed to the Apache Software Foundation (ASF) under one or more
 *   contributor license agreements. See the NOTICE file distributed
 *   with this work for additional information regarding copyright
 *   ownership. The ASF licenses this file to you under the Apache
 *   License, Version 2.0 (the "License"); you may not use this file
 *   except in compliance with the License. You may obtain a copy of
 *   the License at http://www.apache.org/licenses/LICENSE-2.0 .
 */


#include <sal/config.h>
#include <sal/log.hxx>

#include <cstdlib>
#include <thread>

#include <unx/saldisp.hxx>

#include <unistd.h>
#include <string.h>
#include <sys/time.h>

#include <X11/Xlib.h>
#include <X11/Xutil.h>
#include <X11/XKBlib.h>
#include <X11/Xatom.h>
#include <X11/keysym.h>

#include <poll.h>

#include <sal/macros.h>

#include "X11_selection.hxx"
#include "X11_clipboard.hxx"
#include "X11_transferable.hxx"
#include "X11_dndcontext.hxx"
#include "bmp.hxx"

#include <vcl/svapp.hxx>
#include <o3tl/string_view.hxx>

// pointer bitmaps
#include "copydata_curs.h"
#include "copydata_mask.h"
#include "movedata_curs.h"
#include "movedata_mask.h"
#include "linkdata_curs.h"
#include "linkdata_mask.h"
#include "nodrop_curs.h"
#include "nodrop_mask.h"
#include <com/sun/star/datatransfer/dnd/DNDConstants.hpp>
#include <com/sun/star/awt/MouseEvent.hpp>
#include <com/sun/star/awt/MouseButton.hpp>
#include <com/sun/star/frame/Desktop.hpp>
#include <rtl/tencinfo.h>
#include <rtl/ustrbuf.hxx>

#include <comphelper/processfactory.hxx>
#include <comphelper/solarmutex.hxx>

#include <cppuhelper/supportsservice.hxx>
#include <algorithm>

constexpr auto DRAG_EVENT_MASK = ButtonPressMask |
                              ButtonReleaseMask |
                              PointerMotionMask |
                              EnterWindowMask |
                              LeaveWindowMask;

using namespace com::sun::star::datatransfer;
using namespace com::sun::star::datatransfer::dnd;
using namespace com::sun::star::lang;
using namespace com::sun::star::awt;
using namespace com::sun::star::uno;
using namespace com::sun::star::frame;
using namespace cppu;

using namespace x11;

// stubs to satisfy solaris compiler's rather rigid linking warning
extern "C"
{
    static void call_SelectionManager_run( void * pMgr )
    {
        SelectionManager::run( pMgr );
    }

    static void call_SelectionManager_runDragExecute( void * pMgr )
    {
        osl_setThreadName("SelectionManager::runDragExecute()");
        SelectionManager::runDragExecute( pMgr );
    }
}

const tools::Long nXdndProtocolRevision = 5;

namespace {

// mapping between mime types (or what the office thinks of mime types)
// and X convention types
struct NativeTypeEntry
{
    Atom            nAtom;
    const char*     pType;              // Mime encoding on our side
    const char*     pNativeType;        // string corresponding to nAtom for the case of nAtom being uninitialized
    int             nFormat;            // the corresponding format
};

}

// the convention for Xdnd is mime types as specified by the corresponding
// RFC's with the addition that text/plain without charset tag contains iso8859-1
// sadly some applications (e.g. gtk) do not honor the mimetype only rule,
// so for compatibility add UTF8_STRING
static NativeTypeEntry aXdndConversionTab[] =
{
    { 0, "text/plain;charset=iso8859-1""text/plain", 8 },
    { 0, "text/plain;charset=utf-8""UTF8_STRING", 8 }
};

// for clipboard and primary selections there is only a convention for text
// that the encoding name of the text is taken as type in all capitalized letters
static NativeTypeEntry aNativeConversionTab[] =
{
    { 0, "text/plain;charset=utf-16""ISO10646-1", 16 },
    { 0, "text/plain;charset=utf-8""UTF8_STRING", 8 },
    { 0, "text/plain;charset=utf-8""UTF-8", 8 },
    { 0, "text/plain;charset=utf-8""text/plain;charset=UTF-8", 8 },
    // ISO encodings
    { 0, "text/plain;charset=iso8859-2""ISO8859-2", 8 },
    { 0, "text/plain;charset=iso8859-3""ISO8859-3", 8 },
    { 0, "text/plain;charset=iso8859-4""ISO8859-4", 8 },
    { 0, "text/plain;charset=iso8859-5""ISO8859-5", 8 },
    { 0, "text/plain;charset=iso8859-6""ISO8859-6", 8 },
    { 0, "text/plain;charset=iso8859-7""ISO8859-7", 8 },
    { 0, "text/plain;charset=iso8859-8""ISO8859-8", 8 },
    { 0, "text/plain;charset=iso8859-9""ISO8859-9", 8 },
    { 0, "text/plain;charset=iso8859-10""ISO8859-10", 8 },
    { 0, "text/plain;charset=iso8859-13""ISO8859-13", 8 },
    { 0, "text/plain;charset=iso8859-14""ISO8859-14", 8 },
    { 0, "text/plain;charset=iso8859-15""ISO8859-15", 8 },
    // asian encodings
    { 0, "text/plain;charset=jisx0201.1976-0""JISX0201.1976-0", 8 },
    { 0, "text/plain;charset=jisx0208.1983-0""JISX0208.1983-0", 8 },
    { 0, "text/plain;charset=jisx0208.1990-0""JISX0208.1990-0", 8 },
    { 0, "text/plain;charset=jisx0212.1990-0""JISX0212.1990-0", 8 },
    { 0, "text/plain;charset=gb2312.1980-0""GB2312.1980-0", 8 },
    { 0, "text/plain;charset=ksc5601.1992-0""KSC5601.1992-0", 8 },
    // eastern european encodings
    { 0, "text/plain;charset=koi8-r""KOI8-R", 8 },
    { 0, "text/plain;charset=koi8-u""KOI8-U", 8 },
    // String (== iso8859-1)
    { XA_STRING, "text/plain;charset=iso8859-1""STRING", 8 },
    // special for compound text
    { 0, "text/plain;charset=compound_text""COMPOUND_TEXT", 8 },

    // PIXMAP
    { XA_PIXMAP, "image/bmp""PIXMAP", 32 }
};

rtl_TextEncoding x11::getTextPlainEncoding( const OUString& rMimeType )
{
    rtl_TextEncoding aEncoding = RTL_TEXTENCODING_DONTKNOW;
    OUString aMimeType( rMimeType.toAsciiLowerCase() );
    sal_Int32 nIndex = 0;
    if( o3tl::getToken(aMimeType, 0, ';', nIndex ) == u"text/plain" )
    {
        if( aMimeType.getLength() == 10 ) // only "text/plain"
            aEncoding = RTL_TEXTENCODING_ISO_8859_1;
        else
        {
            while( nIndex != -1 )
            {
                OUString aToken = aMimeType.getToken( 0, ';', nIndex );
                sal_Int32 nPos = 0;
                if( o3tl::getToken(aToken, 0, '=', nPos ) == u"charset" )
                {
                    OString aEncToken = OUStringToOString( o3tl::getToken(aToken, 0, '=', nPos ), RTL_TEXTENCODING_ISO_8859_1 );
                    aEncoding = rtl_getTextEncodingFromUnixCharset( aEncToken.getStr() );
                    if( aEncoding == RTL_TEXTENCODING_DONTKNOW )
                    {
                        if( aEncToken.equalsIgnoreAsciiCase( "utf-8" ) )
                            aEncoding = RTL_TEXTENCODING_UTF8;
                    }
                    if( aEncoding != RTL_TEXTENCODING_DONTKNOW )
                        break;
                }
            }
        }
    }
#if OSL_DEBUG_LEVEL > 1
    SAL_WARN_IF(aEncoding == RTL_TEXTENCODING_DONTKNOW,
            "vcl.unx.dtrans""getTextPlainEncoding( "
            << rMimeType << " ) failed.");
#endif
    return aEncoding;
}

std::unordered_map< OUString, SelectionManager* >& SelectionManager::getInstances()
{
    static std::unordered_map< OUString, SelectionManager* > aInstances;
    return aInstances;
}

SelectionManager::SelectionManager() :
        m_nIncrementalThreshold( 15*1024 ),
        m_pDisplay( nullptr ),
        m_aThread( nullptr ),
        m_aDragExecuteThread( nullptr ),
        m_aWindow( None ),
        m_nSelectionTimeout( 0 ),
        m_nSelectionTimestamp( CurrentTime ),
        m_bDropEnterSent( true ),
        m_aCurrentDropWindow( None ),
        m_nDropTime( None ),
        m_nLastDropAction( 0 ),
        m_nLastX( 0 ),
        m_nLastY( 0 ),
        m_bDropWaitingForCompletion( false ),
        m_aDropWindow( None ),
        m_aDropProxy( None ),
        m_aDragSourceWindow( None ),
        m_nLastDragX( 0 ),
        m_nLastDragY( 0 ),
        m_nNoPosX( 0 ),
        m_nNoPosY( 0 ),
        m_nNoPosWidth( 0 ),
        m_nNoPosHeight( 0 ),
        m_nDragButton( 0 ),
        m_nUserDragAction( 0 ),
        m_nTargetAcceptAction( 0 ),
        m_nSourceActions( 0 ),
        m_bLastDropAccepted( false ),
        m_bDropSuccess( false ),
        m_bDropSent( false ),
        m_nDropTimeout( 0 ),
        m_bWaitingForPrimaryConversion( false ),
        m_aMoveCursor( None ),
        m_aCopyCursor( None ),
        m_aLinkCursor( None ),
        m_aNoneCursor( None ),
        m_aCurrentCursor( None ),
        m_nCurrentProtocolVersion( nXdndProtocolRevision ),
        m_nTARGETSAtom( None ),
        m_nTIMESTAMPAtom( None ),
        m_nTEXTAtom( None ),
        m_nINCRAtom( None ),
        m_nCOMPOUNDAtom( None ),
        m_nMULTIPLEAtom( None ),
        m_nImageBmpAtom( None ),
        m_nXdndAware( None ),
        m_nXdndEnter( None ),
        m_nXdndLeave( None ),
        m_nXdndPosition( None ),
        m_nXdndStatus( None ),
        m_nXdndDrop( None ),
        m_nXdndFinished( None ),
        m_nXdndSelection( None ),
        m_nXdndTypeList( None ),
        m_nXdndProxy( None ),
        m_nXdndActionCopy( None ),
        m_nXdndActionMove( None ),
        m_nXdndActionLink( None ),
        m_nXdndActionAsk( None ),
        m_bShutDown( false )
{
    memset(&m_aDropEnterEvent, 0, sizeof(m_aDropEnterEvent));
    m_EndThreadPipe[0] = 0;
    m_EndThreadPipe[1] = 0;
    m_aDragRunning.reset();
}

Cursor SelectionManager::createCursor( const unsigned char* pPointerData, const unsigned char* pMaskData, int width, int height, int hotX, int hotY )
{
    Pixmap aPointer;
    Pixmap aMask;
    XColor aBlack, aWhite;

    aBlack.pixel = BlackPixel( m_pDisplay, 0 );
    aBlack.red = aBlack.green = aBlack.blue = 0;
    aBlack.flags = DoRed | DoGreen | DoBlue;

    aWhite.pixel = WhitePixel( m_pDisplay, 0 );
    aWhite.red = aWhite.green = aWhite.blue = 0xffff;
    aWhite.flags = DoRed | DoGreen | DoBlue;

    aPointer =
        XCreateBitmapFromData( m_pDisplay,
                               m_aWindow,
                               reinterpret_cast<const char*>(pPointerData),
                               width,
                               height );
    aMask
        = XCreateBitmapFromData( m_pDisplay,
                                 m_aWindow,
                                 reinterpret_cast<const char*>(pMaskData),
                                 width,
                                 height );
    Cursor aCursor =
        XCreatePixmapCursor( m_pDisplay, aPointer, aMask,
                             &aBlack, &aWhite,
                             hotX,
                             hotY );
    XFreePixmap( m_pDisplay, aPointer );
    XFreePixmap( m_pDisplay, aMask );

    return aCursor;
}

void SelectionManager::initialize( const Sequence< Any >& arguments )
{
    osl::MutexGuard aGuard(m_aMutex);

    if( ! m_xDisplayConnection.is() )
    {
        /*
         *  first argument must be a css::awt::XDisplayConnection
         *  from this we will get the XEvents of the vcl event loop by
         *  registering us as XEventHandler on it.
         *
         *  implementor's note:
         *  FIXME:
         *  finally the clipboard and XDND service is back in the module it belongs
         *  now cleanup and sharing of resources with the normal vcl event loop
         *  needs to be added. The display used would be that of the normal event loop
         *  and synchronization should be done via the SolarMutex.
         */

        if( arguments.hasElements() )
            arguments.getConstArray()[0] >>= m_xDisplayConnection;
        if( ! m_xDisplayConnection.is() )
        {
        }
        else
            m_xDisplayConnection->addEventHandler( Any(), this, ~0 );
    }

    if( m_pDisplay )
        return;

    OUString aUDisplay;
    if( m_xDisplayConnection.is() )
    {
        Any aIdentifier = m_xDisplayConnection->getIdentifier();
        aIdentifier >>= aUDisplay;
    }

    OString aDisplayName( OUStringToOString( aUDisplay, RTL_TEXTENCODING_ISO_8859_1 ) );

    m_pDisplay = XOpenDisplay( aDisplayName.isEmpty() ? nullptr : aDisplayName.getStr());

    if( !m_pDisplay )
        return;

#ifdef SYNCHRONIZE
    XSynchronize( m_pDisplay, True );
#endif
    // special targets
    m_nTARGETSAtom      = getAtom( u"TARGETS"_ustr );
    m_nTIMESTAMPAtom    = getAtom( u"TIMESTAMP"_ustr );
    m_nTEXTAtom         = getAtom( u"TEXT"_ustr );
    m_nINCRAtom         = getAtom( u"INCR"_ustr );
    m_nCOMPOUNDAtom     = getAtom( u"COMPOUND_TEXT"_ustr );
    m_nMULTIPLEAtom     = getAtom( u"MULTIPLE"_ustr );
    m_nImageBmpAtom     = getAtom( u"image/bmp"_ustr );

    // Atoms for Xdnd protocol
    m_nXdndAware        = getAtom( u"XdndAware"_ustr );
    m_nXdndEnter        = getAtom( u"XdndEnter"_ustr );
    m_nXdndLeave        = getAtom( u"XdndLeave"_ustr );
    m_nXdndPosition     = getAtom( u"XdndPosition"_ustr );
    m_nXdndStatus       = getAtom( u"XdndStatus"_ustr );
    m_nXdndDrop         = getAtom( u"XdndDrop"_ustr );
    m_nXdndFinished     = getAtom( u"XdndFinished"_ustr );
    m_nXdndSelection    = getAtom( u"XdndSelection"_ustr );
    m_nXdndTypeList     = getAtom( u"XdndTypeList"_ustr );
    m_nXdndProxy        = getAtom( u"XdndProxy"_ustr );
    m_nXdndActionCopy   = getAtom( u"XdndActionCopy"_ustr );
    m_nXdndActionMove   = getAtom( u"XdndActionMove"_ustr );
    m_nXdndActionLink   = getAtom( u"XdndActionLink"_ustr );
    m_nXdndActionAsk    = getAtom( u"XdndActionAsk"_ustr );

    // initialize map with member none
    m_aAtomToString[ 0 ]= "None";
    m_aAtomToString[ XA_PRIMARY ] = "PRIMARY";

    // create a (invisible) message window
    m_aWindow = XCreateSimpleWindow( m_pDisplay, DefaultRootWindow( m_pDisplay ),
                                     10, 10, 10, 10, 0, 0, 1 );

    // initialize threshold for incremental transfers
    // ICCCM says it should be smaller that the max request size
    // which in turn is guaranteed to be at least 16k bytes
    m_nIncrementalThreshold = XMaxRequestSize( m_pDisplay ) - 1024;

    if( !m_aWindow )
        return;

    // initialize default cursors
    m_aMoveCursor = createCursor( movedata_curs_bits,
                                  movedata_mask_bits,
                                  movedata_curs_width,
                                  movedata_curs_height,
                                  movedata_curs_x_hot,
                                  movedata_curs_y_hot );
    m_aCopyCursor = createCursor( copydata_curs_bits,
                                  copydata_mask_bits,
                                  copydata_curs_width,
                                  copydata_curs_height,
                                  copydata_curs_x_hot,
                                  copydata_curs_y_hot );
    m_aLinkCursor = createCursor( linkdata_curs_bits,
                                  linkdata_mask_bits,
                                  linkdata_curs_width,
                                  linkdata_curs_height,
                                  linkdata_curs_x_hot,
                                  linkdata_curs_y_hot );
    m_aNoneCursor = createCursor( nodrop_curs_bits,
                                  nodrop_mask_bits,
                                  nodrop_curs_width,
                                  nodrop_curs_height,
                                  nodrop_curs_x_hot,
                                  nodrop_curs_y_hot );

    // just interested in SelectionClear/Notify/Request and PropertyChange
    XSelectInput( m_pDisplay, m_aWindow, PropertyChangeMask );
    // create the transferable for Drag operations
    m_xDropTransferable = new X11Transferable( *this, m_nXdndSelection );
    registerHandler( m_nXdndSelection, *this );

    m_aThread = osl_createSuspendedThread( call_SelectionManager_run, this );
    if( m_aThread )
        osl_resumeThread( m_aThread );
#if OSL_DEBUG_LEVEL > 1
    else
        SAL_WARN("vcl.unx.dtrans""SelectionManager::initialize: "
                << "creation of dispatch thread failed !.");
#endif

    if (pipe(m_EndThreadPipe) != 0) {
#if OSL_DEBUG_LEVEL > 1
        SAL_WARN("vcl.unx.dtrans""Failed to create endThreadPipe.");
#endif
        m_EndThreadPipe[0] = m_EndThreadPipe[1] = 0;
    }
}

SelectionManager::~SelectionManager()
{
#if OSL_DEBUG_LEVEL > 1
    SAL_INFO("vcl.unx.dtrans""SelectionManager::~SelectionManager ("
            << (m_pDisplay ? DisplayString(m_pDisplay) : "no display")
            << ").");
#endif
    {
        osl::MutexGuard aGuard( *osl::Mutex::getGlobalMutex() );

        auto it = std::find_if(getInstances().begin(), getInstances().end(),
            [&](const std::pair< OUString, SelectionManager* >& rInstance) { return rInstance.second == this; });
        if( it != getInstances().end() )
            getInstances().erase( it );
    }

    if( m_aThread )
    {
        osl_terminateThread( m_aThread );
        osl_joinWithThread( m_aThread );
        osl_destroyThread( m_aThread );
    }

    if( m_aDragExecuteThread )
    {
        osl_terminateThread( m_aDragExecuteThread );
        osl_joinWithThread( m_aDragExecuteThread );
        m_aDragExecuteThread = nullptr;
        // thread handle is freed in dragDoDispatch()
    }

    osl::MutexGuard aGuard(m_aMutex);

#if OSL_DEBUG_LEVEL > 1
    SAL_INFO("vcl.unx.dtrans""shutting down SelectionManager.");
#endif

    if( !m_pDisplay )
        return;

    deregisterHandler( m_nXdndSelection );
    // destroy message window
    if( m_aWindow )
        XDestroyWindow( m_pDisplay, m_aWindow );
    // release cursors
    if (m_aMoveCursor != None)
        XFreeCursor(m_pDisplay, m_aMoveCursor);
    if (m_aCopyCursor != None)
        XFreeCursor(m_pDisplay, m_aCopyCursor);
    if (m_aLinkCursor != None)
        XFreeCursor(m_pDisplay, m_aLinkCursor);
    if (m_aNoneCursor != None)
        XFreeCursor(m_pDisplay, m_aNoneCursor);

    // paranoia setting, the drag thread should have
    // done that already
    XUngrabPointer( m_pDisplay, CurrentTime );
    XUngrabKeyboard( m_pDisplay, CurrentTime );

    XCloseDisplay( m_pDisplay );
}

SelectionAdaptor* SelectionManager::getAdaptor( Atom selection )
{
    std::unordered_map< Atom, Selection* >::iterator it =
          m_aSelections.find( selection );
    return it != m_aSelections.end() ? it->second->m_pAdaptor : nullptr;
}

OUString SelectionManager::convertFromCompound( const char* pText, int nLen )
{
    osl::MutexGuard aGuard( m_aMutex );
    OUStringBuffer aRet;
    if( nLen < 0 )
        nLen = strlen( pText );

    char** pTextList = nullptr;
    int nTexts = 0;

    XTextProperty aProp;
    aProp.value     = reinterpret_cast<unsigned char *>(const_cast<char *>(pText));
    aProp.encoding  = m_nCOMPOUNDAtom;
    aProp.format    = 8;
    aProp.nitems    = nLen;
    XmbTextPropertyToTextList( m_pDisplay,
                               &aProp,
                               &pTextList,
                               &nTexts );
    rtl_TextEncoding aEncoding = osl_getThreadTextEncoding();
    forint i = 0; i < nTexts; i++ )
        aRet.append(OStringToOUString( pTextList[i], aEncoding ));

    if( pTextList )
        XFreeStringList( pTextList );

    return aRet.makeStringAndClear();
}

OString SelectionManager::convertToCompound( const OUString& rText )
{
    osl::MutexGuard aGuard( m_aMutex );
    XTextProperty aProp;
    aProp.value = nullptr;
    aProp.encoding = XA_STRING;
    aProp.format = 8;
    aProp.nitems = 0;

    OString aRet( rText.getStr(), rText.getLength(), osl_getThreadTextEncoding() );
    char* pT = const_cast<char*>(aRet.getStr());

    XmbTextListToTextProperty( m_pDisplay,
                               &pT,
                               1,
                               XCompoundTextStyle,
                               &aProp );
    if( aProp.value )
    {
        aRet = reinterpret_cast<char*>(aProp.value);
        XFree( aProp.value );
#ifdef __sun
        /*
         *  for currently unknown reasons XmbTextListToTextProperty on Solaris returns
         *  no data in ISO8859-n encodings (at least for n = 1, 15)
         *  in these encodings the directly converted text does the
         *  trick, also.
         */

        if( aRet.isEmpty() && !rText.isEmpty() )
            aRet = OUStringToOString( rText, osl_getThreadTextEncoding() );
#endif
    }
    else
        aRet.clear();

    return aRet;
}

bool SelectionManager::convertData(
                                   const css::uno::Reference< XTransferable >& xTransferable,
                                   Atom nType,
                                   Atom nSelection,
                                   int& rFormat,
                                   Sequence< sal_Int8 >& rData )
{
    bool bSuccess = false;

    if( ! xTransferable.is() )
        return bSuccess;

    try
    {

        DataFlavor aFlavor;
        aFlavor.MimeType = convertTypeFromNative( nType, nSelection, rFormat );

        sal_Int32 nIndex = 0;
        if( o3tl::getToken(aFlavor.MimeType, 0, ';', nIndex ) == u"text/plain" )
        {
            if( o3tl::getToken(aFlavor.MimeType, 0, ';', nIndex ) == u"charset=utf-16" )
                aFlavor.DataType = cppu::UnoType<OUString>::get();
            else
                aFlavor.DataType = cppu::UnoType<Sequence< sal_Int8 >>::get();
        }
        else
            aFlavor.DataType = cppu::UnoType<Sequence< sal_Int8 >>::get();

        if( xTransferable->isDataFlavorSupported( aFlavor ) )
        {
            Any aValue( xTransferable->getTransferData( aFlavor ) );
            if( aValue.getValueTypeClass() == TypeClass_STRING )
            {
                OUString aString;
                aValue >>= aString;
                rData = Sequence< sal_Int8 >( reinterpret_cast<sal_Int8 const *>(aString.getStr()), aString.getLength() * sizeof( sal_Unicode ) );
                bSuccess = true;
            }
            else if( aValue.getValueType() == cppu::UnoType<Sequence< sal_Int8 >>::get() )
            {
                aValue >>= rData;
                bSuccess = true;
            }
        }
        else if( aFlavor.MimeType.startsWith("text/plain") )
        {
            rtl_TextEncoding aEncoding = RTL_TEXTENCODING_DONTKNOW;
            bool bCompoundText = false;
            if( nType == m_nCOMPOUNDAtom )
                bCompoundText = true;
            else
                aEncoding = getTextPlainEncoding( aFlavor.MimeType );
            if( aEncoding != RTL_TEXTENCODING_DONTKNOW || bCompoundText )
            {
                aFlavor.MimeType = "text/plain;charset=utf-16";
                aFlavor.DataType = cppu::UnoType<OUString>::get();
                if( xTransferable->isDataFlavorSupported( aFlavor ) )
                {
                    Any aValue( xTransferable->getTransferData( aFlavor ) );
                    OUString aString;
                    aValue >>= aString;
                    OString aByteString( bCompoundText ? convertToCompound( aString ) : OUStringToOString( aString, aEncoding ) );
                    rData = Sequence< sal_Int8 >( reinterpret_cast<sal_Int8 const *>(aByteString.getStr()), aByteString.getLength() * sizeofchar ) );
                    bSuccess = true;
                }
            }
        }
    }
    // various exceptions possible ... which all lead to a failed conversion
    // so simplify here to a catch all
    catch(...)
    {
    }

    return bSuccess;
}

SelectionManager& SelectionManager::get()
{
    osl::MutexGuard aGuard( *osl::Mutex::getGlobalMutex() );

    OUString aDisplayName;
    if (auto const env = getenv( "DISPLAY" ))
        aDisplayName = OStringToOUString( env, RTL_TEXTENCODING_ISO_8859_1 );
    SelectionManager* pInstance = nullptr;

    std::unordered_map< OUString, SelectionManager* >::iterator it = getInstances().find( aDisplayName );
    if( it != getInstances().end() )
        pInstance = it->second;
    else pInstance = getInstances()[ aDisplayName ] = new SelectionManager();

    return *pInstance;
}

OUString SelectionManager::getString( Atom aAtom )
{
    osl::MutexGuard aGuard(m_aMutex);

    if( m_aAtomToString.find( aAtom ) == m_aAtomToString.end() )
    {
        char* pAtom = m_pDisplay ? XGetAtomName( m_pDisplay, aAtom ) : nullptr;
        if( ! pAtom )
            return OUString();
        OUString aString( OStringToOUString( pAtom, RTL_TEXTENCODING_ISO_8859_1 ) );
        XFree( pAtom );
        m_aStringToAtom[ aString ] = aAtom;
        m_aAtomToString[ aAtom ] = aString;
    }
    return m_aAtomToString[ aAtom ];
}

Atom SelectionManager::getAtom( const OUString& rString )
{
    osl::MutexGuard aGuard(m_aMutex);

    if( m_aStringToAtom.find( rString ) == m_aStringToAtom.end() )
    {
        static Atom nNoDisplayAtoms = 1;
        Atom aAtom = m_pDisplay ? XInternAtom( m_pDisplay, OUStringToOString( rString, RTL_TEXTENCODING_ISO_8859_1 ).getStr(), False ) : nNoDisplayAtoms++;
        m_aStringToAtom[ rString ] = aAtom;
        m_aAtomToString[ aAtom ] = rString;
    }
    return m_aStringToAtom[ rString ];
}

bool SelectionManager::requestOwnership( Atom selection )
{
    bool bSuccess = false;
    if( m_pDisplay && m_aWindow )
    {
        osl::MutexGuard aGuard(m_aMutex);

        SelectionAdaptor* pAdaptor = getAdaptor( selection );
        if( pAdaptor )
        {
            XSetSelectionOwner( m_pDisplay, selection, m_aWindow, CurrentTime );
            if( XGetSelectionOwner( m_pDisplay, selection ) == m_aWindow )
                bSuccess = true;

#if OSL_DEBUG_LEVEL > 1
            SAL_INFO("vcl.unx.dtrans",
                    (bSuccess ? "acquired" : "failed to acquire")
                    << " ownership for selection "
                    << getString( selection ));
#endif

            Selection* pSel = m_aSelections[ selection ];
            pSel->m_bOwner = bSuccess;
            delete pSel->m_pPixmap;
            pSel->m_pPixmap = nullptr;
            pSel->m_nOrigTimestamp = m_nSelectionTimestamp;
        }
#if OSL_DEBUG_LEVEL > 1
        else
            SAL_WARN("vcl.unx.dtrans""no adaptor for selection "
                    << getString( selection ));

        if( pAdaptor->getTransferable().is() )
        {
            Sequence< DataFlavor > aTypes = pAdaptor->getTransferable()->getTransferDataFlavors();
            forint i = 0; i < aTypes.getLength(); i++ )
            {
                SAL_INFO("vcl.unx.dtrans"" " << aTypes.getConstArray()[i].MimeType);
            }
        }
#endif
    }
    return bSuccess;
}

void SelectionManager::convertTypeToNative( const OUString& rType, Atom selection, int& rFormat, ::std::list< Atom >& rConversions, bool bPushFront )
{
    NativeTypeEntry* pTab = selection == m_nXdndSelection ? aXdndConversionTab : aNativeConversionTab;
    int nTabEntries = selection == m_nXdndSelection ? SAL_N_ELEMENTS(aXdndConversionTab) : SAL_N_ELEMENTS(aNativeConversionTab);

    OString aType( OUStringToOString( rType, RTL_TEXTENCODING_ISO_8859_1 ) );
    SAL_INFO( "vcl.unx.dtrans""convertTypeToNative " << aType );
    rFormat = 0;
    forint i = 0; i < nTabEntries; i++ )
    {
        if( aType.equalsIgnoreAsciiCase( pTab[i].pType ) )
        {
            if( ! pTab[i].nAtom )
                pTab[i].nAtom = getAtom( OStringToOUString( pTab[i].pNativeType, RTL_TEXTENCODING_ISO_8859_1 ) );
            rFormat = pTab[i].nFormat;
            if( bPushFront )
                rConversions.push_front( pTab[i].nAtom );
            else
                rConversions.push_back( pTab[i].nAtom );
            if( pTab[i].nFormat == XA_PIXMAP )
            {
                if( bPushFront )
                {
                    rConversions.push_front( XA_VISUALID );
                    rConversions.push_front( XA_COLORMAP );
                }
                else
                {
                    rConversions.push_back( XA_VISUALID );
                    rConversions.push_back( XA_COLORMAP );
                }
            }
        }
    }
    if( ! rFormat )
        rFormat = 8; // byte buffer
    if( bPushFront )
        rConversions.push_front( getAtom( rType ) );
    else
        rConversions.push_back( getAtom( rType ) );
};

void SelectionManager::getNativeTypeList( const Sequence< DataFlavor >& rTypes, std::list< Atom >& rOutTypeList, Atom targetselection )
{
    rOutTypeList.clear();

    int nFormat;
    bool bHaveText = false;
    forconst auto& rFlavor : rTypes )
    {
        if( rFlavor.MimeType.startsWith("text/plain"))
            bHaveText = true;
        else
            convertTypeToNative( rFlavor.MimeType, targetselection, nFormat, rOutTypeList );
    }
    if( bHaveText )
    {
        if( targetselection != m_nXdndSelection )
        {
            // only mimetypes should go into Xdnd type list
            rOutTypeList.push_front( XA_STRING );
            rOutTypeList.push_front( m_nCOMPOUNDAtom );
        }
        convertTypeToNative( u"text/plain;charset=utf-8"_ustr, targetselection, nFormat, rOutTypeList, true );
    }
    if( targetselection != m_nXdndSelection )
        rOutTypeList.push_back( m_nMULTIPLEAtom );
}

OUString SelectionManager::convertTypeFromNative( Atom nType, Atom selection, intrFormat )
{
    NativeTypeEntry* pTab = (selection == m_nXdndSelection) ? aXdndConversionTab : aNativeConversionTab;
    int nTabEntries = (selection == m_nXdndSelection) ? SAL_N_ELEMENTS(aXdndConversionTab) : SAL_N_ELEMENTS(aNativeConversionTab);

    forint i = 0; i < nTabEntries; i++ )
    {
        if( ! pTab[i].nAtom )
            pTab[i].nAtom = getAtom( OStringToOUString( pTab[i].pNativeType, RTL_TEXTENCODING_ISO_8859_1 ) );
        if( nType == pTab[i].nAtom )
        {
            rFormat = pTab[i].nFormat;
            return OStringToOUString( pTab[i].pType, RTL_TEXTENCODING_ISO_8859_1 );
        }
    }
    rFormat = 8;
    return getString( nType );
}

bool SelectionManager::getPasteData( Atom selection, Atom type, Sequence< sal_Int8 >& rData )
{
    osl::ResettableMutexGuard aGuard(m_aMutex);
    std::unordered_map< Atom, Selection* >::iterator it;
    bool bSuccess = false;

#if OSL_DEBUG_LEVEL > 1
    SAL_INFO("vcl.unx.dtrans""getPasteData( " << getString( selection )
            << ", native: " << getString( type ) << " ).");
#endif

    if( ! m_pDisplay )
        return false;

    it = m_aSelections.find( selection );
    if( it == m_aSelections.end() )
        return false;

    ::Window aSelectionOwner = XGetSelectionOwner( m_pDisplay, selection );
    if( aSelectionOwner == None )
        return false;
    if( aSelectionOwner == m_aWindow )
    {
        // probably bad timing led us here
#if OSL_DEBUG_LEVEL > 1
        SAL_WARN("vcl.unx.dtrans""Innere Nabelschau.");
#endif
        return false;
    }

    // ICCCM recommends to destroy property before convert request unless
    // parameters are transported; we do only in case of MULTIPLE,
    // so destroy property unless target is MULTIPLE
    if( type != m_nMULTIPLEAtom )
        XDeleteProperty( m_pDisplay, m_aWindow, selection );

    XConvertSelection( m_pDisplay, selection, type, selection, m_aWindow, selection == m_nXdndSelection ? m_nDropTime : CurrentTime );
    it->second->m_eState            = Selection::WaitingForResponse;
    it->second->m_aRequestedType    = type;
    it->second->m_aData             = Sequence< sal_Int8 >();
    it->second->m_aDataArrived.reset();
    // really start the request; if we don't flush the
    // queue the request won't leave it because there are no more
    // X calls after this until the data arrived or timeout
    XFlush( m_pDisplay );

    // do a reschedule
    struct timeval tv_last, tv_current;
    gettimeofday( &tv_last, nullptr );
    tv_current = tv_last;

    XEvent aEvent;
    do
    {
        bool bAdjustTime = false;
        {
            bool bHandle = false;

            if( XCheckTypedEvent( m_pDisplay,
                                  PropertyNotify,
                                  &aEvent
                                  ) )
            {
                bHandle = true;
                if( aEvent.xproperty.window == m_aWindow
                    && aEvent.xproperty.atom == selection )
                    bAdjustTime = true;
            }
            else if( XCheckTypedEvent( m_pDisplay,
                                  SelectionClear,
                                  &aEvent
                                  ) )
            {
                bHandle = true;
            }
            else if( XCheckTypedEvent( m_pDisplay,
                                  SelectionRequest,
                                  &aEvent
                                  ) )
            {
                bHandle = true;
            }
            else if( XCheckTypedEvent( m_pDisplay,
                                  SelectionNotify,
                                  &aEvent
                                  ) )
            {
                bHandle = true;
                if( aEvent.xselection.selection == selection
                    && ( aEvent.xselection.requestor == m_aWindow ||
                         aEvent.xselection.requestor == m_aCurrentDropWindow )
                    )
                    bAdjustTime = true;
            }
            else
            {
                aGuard.clear();
                std::this_thread::sleep_for(std::chrono::milliseconds(100));
                aGuard.reset();
            }
            if( bHandle )
            {
                aGuard.clear();
                handleXEvent( aEvent );
                aGuard.reset();
            }
        }
        gettimeofday( &tv_current, nullptr );
        if( bAdjustTime )
            tv_last = tv_current;
    } while( ! it->second->m_aDataArrived.check() && (tv_current.tv_sec - tv_last.tv_sec) < getSelectionTimeout() );

#if OSL_DEBUG_LEVEL > 1
    SAL_WARN_IF((tv_current.tv_sec - tv_last.tv_sec) > getSelectionTimeout(),
            "vcl.unx.dtrans""timed out.");
#endif

    if( it->second->m_aDataArrived.check() &&
        it->second->m_aData.getLength() )
    {
        rData = it->second->m_aData;
        bSuccess = true;
    }
#if OSL_DEBUG_LEVEL > 1
    else
        SAL_WARN("vcl.unx.dtrans""conversion unsuccessful.");
#endif
    return bSuccess;
}

bool SelectionManager::getPasteData( Atom selection, const OUString& rType, Sequence< sal_Int8 >& rData )
{
    bool bSuccess = false;

    std::unordered_map< Atom, Selection* >::iterator it;
    {
        osl::MutexGuard aGuard(m_aMutex);

        it = m_aSelections.find( selection );
        if( it == m_aSelections.end() )
            return false;
    }

    if( it->second->m_aTypes.getLength() == 0 )
    {
        Sequence< DataFlavor > aFlavors;
        getPasteDataTypes( selection, aFlavors );
        if( it->second->m_aTypes.getLength() == 0 )
            return false;
    }

    const Sequence< DataFlavor >& rTypes( it->second->m_aTypes );
    const std::vector< Atom >& rNativeTypes( it->second->m_aNativeTypes );

#if OSL_DEBUG_LEVEL > 1
    SAL_INFO("vcl.unx.dtrans""getPasteData( \""
            << getString( selection )
            << "\", \""
            << rType << "\" ).");
#endif

    if( rType == "text/plain;charset=utf-16" )
    {
        // let's see if we have UTF16 else try to find something convertible
        if( it->second->m_aTypes.getLength() && ! it->second->m_bHaveUTF16 )
        {
            Sequence< sal_Int8 > aData;
            if( it->second->m_aUTF8Type != None &&
                getPasteData( selection,
                              it->second->m_aUTF8Type,
                              aData )
                )
            {
              OUString aRet( reinterpret_cast<const char*>(aData.getConstArray()), aData.getLength(), RTL_TEXTENCODING_UTF8 );
              rData = Sequence< sal_Int8 >( reinterpret_cast<sal_Int8 const *>(aRet.getStr()), (aRet.getLength()+1)*sizeof( sal_Unicode ) );
              bSuccess = true;
            }
            else if( it->second->m_bHaveCompound &&
                getPasteData( selection,
                              m_nCOMPOUNDAtom,
                              aData )
                )
            {
                OUString aRet( convertFromCompound( reinterpret_cast<const char*>(aData.getConstArray()), aData.getLength() ) );
                rData = Sequence< sal_Int8 >( reinterpret_cast<sal_Int8 const *>(aRet.getStr()), (aRet.getLength()+1)*sizeof( sal_Unicode ) );
                bSuccess = true;
            }
            else
            {
                forint i = 0; i < rTypes.getLength(); i++ )
                {
                    rtl_TextEncoding aEncoding = getTextPlainEncoding( rTypes.getConstArray()[i].MimeType );
                    if( aEncoding != RTL_TEXTENCODING_DONTKNOW  &&
                        aEncoding != RTL_TEXTENCODING_UNICODE   &&
                        getPasteData( selection,
                                      rNativeTypes[i],
                                      aData )
                        )
                    {
#if OSL_DEBUG_LEVEL > 1
                        SAL_INFO("vcl.unx.dtrans""using \""
                                << rTypes.getConstArray()[i].MimeType
                                << "\" instead of \""
                                << rType
                                << "\".");
#endif

                        OString aConvert( reinterpret_cast<char const *>(aData.getConstArray()), aData.getLength() );
                        OUString aUTF( OStringToOUString( aConvert, aEncoding ) );
                        rData = Sequence< sal_Int8 >( reinterpret_cast<sal_Int8 const *>(aUTF.getStr()), (aUTF.getLength()+1)*sizeof( sal_Unicode ) );
                        bSuccess = true;
                        break;
                    }
                }
            }
        }
    }
    else if( rType == "image/bmp" )
    {
        // #i83376# try if someone has the data in image/bmp already before
        // doing the PIXMAP stuff (e.g. the Gimp has this)
        bSuccess = getPasteData( selection, m_nImageBmpAtom, rData );
#if OSL_DEBUG_LEVEL > 1
        SAL_INFO_IF(bSuccess, "vcl.unx.dtrans",
                "got " << (int) rData.getLength() << " bytes of image/bmp.");
#endif
        if( ! bSuccess )
        {
            Pixmap aPixmap = None;
            Colormap aColormap = None;

            // prepare property for MULTIPLE request
            Sequence< sal_Int8 > aData;
            Atom const pTypes[4] = { XA_PIXMAP, XA_PIXMAP, XA_COLORMAP, XA_COLORMAP };
            {
                osl::MutexGuard aGuard(m_aMutex);

                XChangeProperty( m_pDisplay,
                    m_aWindow,
                    selection,
                    XA_ATOM,
                    32,
                    PropModeReplace,
                    reinterpret_cast<unsigned char const *>(pTypes),
                    4 );
            }

            // try MULTIPLE request
            if( getPasteData( selection, m_nMULTIPLEAtom, aData ) )
            {
                Atom* pReturnedTypes = reinterpret_cast<Atom*>(aData.getArray());
                if( pReturnedTypes[0] == XA_PIXMAP && pReturnedTypes[1] == XA_PIXMAP )
                {
                    osl::MutexGuard aGuard(m_aMutex);

                    Atom type = None;
                    int format = 0;
                    unsigned long nItems = 0;
                    unsigned long nBytes = 0;
                    unsigned char* pReturn = nullptr;
                    XGetWindowProperty( m_pDisplay, m_aWindow, XA_PIXMAP, 0, 1, True, XA_PIXMAP, &type, &format, &nItems,&nbsp;&nBytes, &pReturn );
                    if( pReturn )
                    {
                        if( type == XA_PIXMAP )
                            aPixmap = *reinterpret_cast<Pixmap*>(pReturn);
                        XFree( pReturn );
                        pReturn = nullptr;
                        if( pReturnedTypes[2] == XA_COLORMAP && pReturnedTypes[3] == XA_COLORMAP )
                        {
                            XGetWindowProperty( m_pDisplay, m_aWindow, XA_COLORMAP, 0, 1, True, XA_COLORMAP, &type, &format,&nbsp;&nItems, &nBytes, &pReturn );
                            if( pReturn )
                            {
                                if( type == XA_COLORMAP )
                                    aColormap = *reinterpret_cast<Colormap*>(pReturn);
                                XFree( pReturn );
                            }
                        }
                    }
#if OSL_DEBUG_LEVEL > 1
                    else
                    {
                        SAL_WARN("vcl.unx.dtrans""could not get PIXMAP property: type="
                                << getString( type )
                                << ", format=" << format
                                << ", items=" << nItems
                                << ", bytes=" << nBytes
                                << ", ret=0x" << pReturn);
                    }
#endif
                }
            }

            if( aPixmap == None )
            {
                // perhaps two normal requests will work
                if( getPasteData( selection, XA_PIXMAP, aData ) )
                {
                    aPixmap = *reinterpret_cast<Pixmap*>(aData.getArray());
                    if( aColormap == None && getPasteData( selection, XA_COLORMAP, aData ) )
                        aColormap = *reinterpret_cast<Colormap*>(aData.getArray());
                }
            }

            // convert data if possible
            if( aPixmap != None )
            {
                osl::MutexGuard aGuard(m_aMutex);

                sal_Int32 nOutSize = 0;
                sal_uInt8* pBytes = X11_getBmpFromPixmap( m_pDisplay, aPixmap, aColormap, nOutSize );
                if( pBytes )
                {
                    if( nOutSize )
                    {
                        rData = Sequence< sal_Int8 >( nOutSize );
                        memcpy( rData.getArray(), pBytes, nOutSize );
                        bSuccess = true;
                    }
                    std::free( pBytes );
                }
            }
        }
    }

    if( ! bSuccess )
    {
        int nFormat;
        ::std::list< Atom > aTypes;
        convertTypeToNative( rType, selection, nFormat, aTypes );
        Atom nSelectedType = None;
        for (auto const& type : aTypes)
        {
            forauto const & nativeType: rNativeTypes )
                if(nativeType == type)
                {
                    nSelectedType = type;
                    if (nSelectedType != None)
                        break;
                }
        }
        if( nSelectedType != None )
            bSuccess = getPasteData( selection, nSelectedType, rData );
    }
#if OSL_DEBUG_LEVEL > 1
    SAL_INFO("vcl.unx.dtrans""getPasteData for selection "
            << getString( selection )
            << " and data type "
            << rType
            << " returns "
            << ( bSuccess ? "true" : "false")
            << ", returned sequence has length "
            << rData.getLength() <<  ".");
#endif
    return bSuccess;
}

bool SelectionManager::getPasteDataTypes( Atom selection, Sequence< DataFlavor >& rTypes )
{
    std::unordered_map< Atom, Selection* >::iterator it;
    {
        osl::MutexGuard aGuard(m_aMutex);

        it = m_aSelections.find( selection );
        if( it != m_aSelections.end()                           &&
            it->second->m_aTypes.getLength()                    &&
            std::abs( it->second->m_nLastTimestamp - time( nullptr ) ) < 2
            )
        {
            rTypes = it->second->m_aTypes;
            return true;
        }
    }

    bool bSuccess = false;
    bool bHaveUTF16 = false;
    Atom aUTF8Type = None;
    bool bHaveCompound = false;
    Sequence< sal_Int8 > aAtoms;

    if( selection == m_nXdndSelection )
    {
        // xdnd sends first three types with XdndEnter
        // if more than three types are supported then the XDndTypeList
        // property on the source window is used
        if( m_aDropEnterEvent.data.l[0] && m_aCurrentDropWindow )
        {
            if( m_aDropEnterEvent.data.l[1] & 1 )
            {
                const unsigned int atomcount = 256;
                // more than three types; look in property
                osl::MutexGuard aGuard(m_aMutex);

                Atom nType;
                int nFormat;
                unsigned long nItems, nBytes;
                unsigned char* pBytes = nullptr;

                XGetWindowProperty( m_pDisplay, m_aDropEnterEvent.data.l[0],
                                    m_nXdndTypeList, 0, atomcount, False,
                                    XA_ATOM,
                                    &nType, &nFormat, &nItems, &nBytes, &pBytes );
#if OSL_DEBUG_LEVEL > 1
                SAL_INFO("vcl.unx.dtrans""have "
                        << nItems
                        << " data types in XdndTypeList.");
#endif
                if( nItems == atomcount && nBytes > 0 )
                {
                    // wow ... more than 256 types !
                    aAtoms.realloc( sizeof( Atom )*atomcount+nBytes );
                    memcpy( aAtoms.getArray(), pBytes, sizeof( Atom )*atomcount );
                    XFree( pBytes );
                    pBytes = nullptr;
                    XGetWindowProperty( m_pDisplay, m_aDropEnterEvent.data.l[0],
                                        m_nXdndTypeList, atomcount, nBytes/sizeof(Atom),
                                        False, XA_ATOM,
                                        &nType, &nFormat, &nItems, &nBytes, &pBytes );
                    {
                        memcpy( aAtoms.getArray()+atomcount*sizeof(Atom), pBytes, nItems*sizeof(Atom) );
                        XFree( pBytes );
                    }
                }
                else
                {
                    aAtoms.realloc( sizeof(Atom)*nItems );
                    memcpy( aAtoms.getArray(), pBytes, nItems*sizeof(Atom) );
                    XFree( pBytes );
                }
            }
            else
            {
                // one to three types
                int n = 0, i;
                for( i = 0; i < 3; i++ )
                    if( m_aDropEnterEvent.data.l[2+i] )
                        n++;
#if OSL_DEBUG_LEVEL > 1
                SAL_INFO("vcl.unx.dtrans""have "
                        << n
                        << " data types in XdndEnter.");
#endif
                aAtoms.realloc( sizeof(Atom)*n );
                for( i = 0, n = 0; i < 3; i++ )
                    if( m_aDropEnterEvent.data.l[2+i] )
                        reinterpret_cast<Atom*>(aAtoms.getArray())[n++] = m_aDropEnterEvent.data.l[2+i];
            }
        }
    }
    // get data of type TARGETS
    else if( ! getPasteData( selection, m_nTARGETSAtom, aAtoms ) )
        aAtoms = Sequence< sal_Int8 >();

    std::vector< Atom > aNativeTypes;
    if( aAtoms.hasElements() )
    {
        sal_Int32 nAtoms = aAtoms.getLength() / sizeof(Atom);
        Atom* pAtoms = reinterpret_cast<Atom*>(aAtoms.getArray());
        rTypes.realloc( nAtoms );
        aNativeTypes.resize( nAtoms );
        DataFlavor* pFlavors = rTypes.getArray();
        sal_Int32 nNativeTypesIndex = 0;
        bool bHaveText = false;
        while( nAtoms-- )
        {
            SAL_INFO_IF(*pAtoms && *pAtoms < 0x01000000, "vcl.unx.dtrans",
                "getPasteDataTypes: available: \"" << getString(*pAtoms) << "\"");
            if( *pAtoms == m_nCOMPOUNDAtom )
                bHaveText = bHaveCompound = true;
            else if( *pAtoms && *pAtoms < 0x01000000 )
            {
                int nFormat;
                pFlavors->MimeType = convertTypeFromNative( *pAtoms, selection, nFormat );
                pFlavors->DataType = cppu::UnoType<Sequence< sal_Int8 >>::get();
                sal_Int32 nIndex = 0;
                if( o3tl::getToken(pFlavors->MimeType, 0, ';', nIndex ) == u"text/plain" )
                {
                    std::u16string_view aToken(o3tl::getToken(pFlavors->MimeType, 0, ';', nIndex ));
                    // omit text/plain;charset=unicode since it is not well defined
                    if( aToken == u"charset=unicode" )
                    {
                        pAtoms++;
                        continue;
                    }
                    bHaveText = true;
                    if( aToken == u"charset=utf-16" )
                    {
                        bHaveUTF16 = true;
                        pFlavors->DataType = cppu::UnoType<OUString>::get();
                    }
                    else if( aToken == u"charset=utf-8" )
                    {
                        aUTF8Type = *pAtoms;
                    }
                }
                pFlavors++;
                aNativeTypes[ nNativeTypesIndex ] = *pAtoms;
                nNativeTypesIndex++;
            }
            pAtoms++;
        }
        if( (pFlavors - rTypes.getArray()) < rTypes.getLength() )
            rTypes.realloc(pFlavors - rTypes.getArray());
        bSuccess = rTypes.hasElements();
        if( bHaveText && ! bHaveUTF16 )
        {
            int i = 0;

            int nNewFlavors = rTypes.getLength()+1;
            Sequence< DataFlavor > aTemp( nNewFlavors );
            for( i = 0; i < nNewFlavors-1; i++ )
                aTemp.getArray()[i+1] = rTypes.getConstArray()[i];
            aTemp.getArray()[0].MimeType = "text/plain;charset=utf-16";
            aTemp.getArray()[0].DataType = cppu::UnoType<OUString>::get();
            rTypes = std::move(aTemp);

            std::vector< Atom > aNativeTemp( nNewFlavors );
            for( i = 0; i < nNewFlavors-1; i++ )
                aNativeTemp[ i + 1 ] = aNativeTypes[ i ];
            aNativeTemp[0] = None;
            aNativeTypes = std::move(aNativeTemp);
        }
    }

    {
        osl::MutexGuard aGuard(m_aMutex);

        it = m_aSelections.find( selection );
        if( it != m_aSelections.end() )
        {
            if( bSuccess )
            {
                it->second->m_aTypes            = rTypes;
                it->second->m_aNativeTypes      = std::move(aNativeTypes);
                it->second->m_nLastTimestamp    = time( nullptr );
                it->second->m_bHaveUTF16        = bHaveUTF16;
                it->second->m_aUTF8Type         = aUTF8Type;
                it->second->m_bHaveCompound     = bHaveCompound;
            }
            else
            {
                it->second->m_aTypes            = Sequence< DataFlavor >();
                it->second->m_aNativeTypes      = std::vector< Atom >();
                it->second->m_nLastTimestamp    = 0;
                it->second->m_bHaveUTF16        = false;
                it->second->m_aUTF8Type         = None;
                it->second->m_bHaveCompound     = false;
            }
        }
    }

#if OSL_DEBUG_LEVEL > 1
    {
        SAL_INFO("vcl.unx.dtrans""SelectionManager::getPasteDataTypes( "
                << getString( selection )
                << " ) = "
                << (bSuccess ? "true" : "false"));
        forint i = 0; i < rTypes.getLength(); i++ )
            SAL_INFO("vcl.unx.dtrans""type: " << rTypes.getConstArray()[i].MimeType);
    }
#endif

    return bSuccess;
}

PixmapHolder* SelectionManager::getPixmapHolder( Atom selection )
{
    std::unordered_map< Atom, Selection* >::const_iterator it = m_aSelections.find( selection );
    if( it == m_aSelections.end() )
        return nullptr;
    if( ! it->second->m_pPixmap )
        it->second->m_pPixmap = new PixmapHolder( m_pDisplay );
    return it->second->m_pPixmap;
}

static std::size_t GetTrueFormatSize(int nFormat)
{
    // http://mail.gnome.org/archives/wm-spec-list/2003-March/msg00067.html
    return nFormat == 32 ? sizeof(long) : nFormat/8;
}

bool SelectionManager::sendData( SelectionAdaptor* pAdaptor,
                                 ::Window requestor,
                                 Atom target,
                                 Atom property,
                                 Atom selection )
{
    osl::ResettableMutexGuard aGuard( m_aMutex );

    // handle targets related to image/bmp
    if( target == XA_COLORMAP || target == XA_PIXMAP || target == XA_BITMAP || target == XA_VISUALID )
    {
        PixmapHolder* pPixmap = getPixmapHolder( selection );
        if( ! pPixmap ) return false;
        XID nValue = None;

        // handle colormap request
        if( target == XA_COLORMAP )
            nValue = static_cast<XID>(pPixmap->getColormap());
        else if( target == XA_VISUALID )
            nValue = static_cast<XID>(pPixmap->getVisualID());
        else if( target == XA_PIXMAP || target == XA_BITMAP )
        {
            nValue = static_cast<XID>(pPixmap->getPixmap());
            if( nValue == None )
            {
                // first conversion
                Sequence< sal_Int8 > aData;
                int nFormat;
                aGuard.clear();
                bool bConverted = convertData( pAdaptor->getTransferable(), target, selection, nFormat, aData );
                aGuard.reset();
                if( bConverted )
                {
                    // get pixmap again since clearing the guard could have invalidated
                    // the pixmap in another thread
                    pPixmap = getPixmapHolder( selection );
                    // conversion succeeded, so aData contains image/bmp now
                    if( pPixmap->needsConversion( reinterpret_cast<const sal_uInt8*>(aData.getConstArray()) ) )
                    {
                        SAL_INFO( "vcl.unx.dtrans""trying bitmap conversion" );
                        int depth = pPixmap->getDepth();
                        aGuard.clear();
                        aData = convertBitmapDepth(aData, depth);
                        aGuard.reset();
                    }
                    // get pixmap again since clearing the guard could have invalidated
                    // the pixmap in another thread
                    pPixmap = getPixmapHolder( selection );
                    nValue = static_cast<XID>(pPixmap->setBitmapData( reinterpret_cast<const sal_uInt8*>(aData.getConstArray()) ));
                }
                if( nValue == None )
                    return false;
            }
            if( target == XA_BITMAP )
                nValue = static_cast<XID>(pPixmap->getBitmap());
        }

        XChangeProperty( m_pDisplay,
                         requestor,
                         property,
                         target,
                         32,
                         PropModeReplace,
                         reinterpret_cast<const unsigned char*>(&nValue),
                         1);
        return true;
    }

    /*
     *  special target TEXT allows us to transfer
     *  the data in an encoding of our choice
     *  COMPOUND_TEXT will work with most applications
     */

    if( target == m_nTEXTAtom )
        target = m_nCOMPOUNDAtom;

    Sequence< sal_Int8 > aData;
    int nFormat;
    aGuard.clear();
    bool bConverted = convertData( pAdaptor->getTransferable(), target, selection, nFormat, aData );
    aGuard.reset();
    if( bConverted )
    {
        // conversion succeeded
        if( aData.getLength() > m_nIncrementalThreshold )
        {
#if OSL_DEBUG_LEVEL > 1
            SAL_INFO("vcl.unx.dtrans""using INCR protocol.");
            std::unordered_map< ::Window, std::unordered_map< Atom, IncrementalTransfer > >::const_iterator win_it = m_aIncrementals.find( requestor );
            if( win_it != m_aIncrementals.end() )
            {
                std::unordered_map< Atom, IncrementalTransfer >::const_iterator inc_it = win_it->second.find( property );
                if( inc_it != win_it->second.end() )
                {
                    const IncrementalTransfer& rInc = inc_it->second;
                    SAL_INFO("vcl.unx.dtrans""premature end and new start for INCR transfer for window "
                            << std::showbase << std::hex
                            << rInc.m_aRequestor
                            << ", property "
                            << getString( rInc.m_aProperty )
                            << ", type "
                            << getString( rInc.m_aTarget ));
                }
            }
#endif

            // insert IncrementalTransfer
            IncrementalTransfer& rInc   = m_aIncrementals[ requestor ][ property ];
            rInc.m_aData                = std::move(aData);
            rInc.m_nBufferPos           = 0;
            rInc.m_aRequestor           = requestor;
            rInc.m_aProperty            = property;
            rInc.m_aTarget              = target;
            rInc.m_nFormat              = nFormat;
            rInc.m_nTransferStartTime   = time( nullptr );

            // use incr protocol, signal start to requestor
            tools::Long nMinSize = m_nIncrementalThreshold;
            XSelectInput( m_pDisplay, requestor, PropertyChangeMask );
            XChangeProperty( m_pDisplay, requestor, property,
                             m_nINCRAtom, 32,  PropModeReplace, reinterpret_cast<unsigned char*>(&nMinSize), 1 );
            XFlush( m_pDisplay );
        }
        else
        {
            std::size_t nUnitSize = GetTrueFormatSize(nFormat);
            XChangeProperty( m_pDisplay,
                             requestor,
                             property,
                             target,
                             nFormat,
                             PropModeReplace,
                             reinterpret_cast<const unsigned char*>(aData.getConstArray()),
                             aData.getLength()/nUnitSize );
            }
    }
#if OSL_DEBUG_LEVEL > 1
    else
        SAL_WARN("vcl.unx.dtrans""convertData failed for type: "
                << convertTypeFromNative( target, selection, nFormat ));
#endif
    return bConverted;
}

bool SelectionManager::handleSelectionRequest( XSelectionRequestEvent& rRequest )
{
    osl::ResettableMutexGuard aGuard( m_aMutex );

#if OSL_DEBUG_LEVEL > 1
    SAL_INFO("vcl.unx.dtrans""handleSelectionRequest for selection "
            << getString( rRequest.selection )
            << " and target "
            << getString( rRequest.target ));
#endif

    XEvent aNotify;

    aNotify.type                  = SelectionNotify;
    aNotify.xselection.display    = rRequest.display;
    aNotify.xselection.send_event = True;
    aNotify.xselection.requestor  = rRequest.requestor;
    aNotify.xselection.selection  = rRequest.selection;
    aNotify.xselection.time       = rRequest.time;
    aNotify.xselection.target     = rRequest.target;
    aNotify.xselection.property   = None;

    SelectionAdaptor* pAdaptor = getAdaptor( rRequest.selection );
    // ensure that we still own that selection
    if( pAdaptor &&
        XGetSelectionOwner( m_pDisplay, rRequest.selection ) == m_aWindow )
    {
        css::uno::Reference< XTransferable > xTrans( pAdaptor->getTransferable() );
        if( rRequest.target == m_nTARGETSAtom )
        {
            // someone requests our types
            if( xTrans.is() )
            {
                aGuard.clear();
                Sequence< DataFlavor > aFlavors = xTrans->getTransferDataFlavors();
                aGuard.reset();

                ::std::list< Atom > aConversions;
                getNativeTypeList( aFlavors, aConversions, rRequest.selection );

                int i, nTypes = aConversions.size();
                Atom* pTypes = static_cast<Atom*>(alloca( nTypes * sizeof( Atom ) ));
                std::list< Atom >::const_iterator it;
                for( i = 0, it = aConversions.begin(); i < nTypes; i++, ++it )
                    pTypes[i] = *it;
                XChangeProperty( m_pDisplay, rRequest.requestor, rRequest.property,
                                 XA_ATOM, 32, PropModeReplace, reinterpret_cast<unsigned char*>(pTypes), nTypes );
                aNotify.xselection.property = rRequest.property;
#if OSL_DEBUG_LEVEL > 1
                SAL_INFO("vcl.unx.dtrans""sending type list:");
                forint k = 0; k < nTypes; k++ )
                    SAL_INFO("vcl.unx.dtrans"" "
                            << (pTypes[k] ? XGetAtomName( m_pDisplay, pTypes[k] ) :
                                ""));
#endif
            }
        }
        else if( rRequest.target == m_nTIMESTAMPAtom )
        {
            tools::Long nTimeStamp = static_cast<tools::Long>(m_aSelections[rRequest.selection]->m_nOrigTimestamp);
            XChangeProperty( m_pDisplay, rRequest.requestor, rRequest.property,
                             XA_INTEGER, 32, PropModeReplace, reinterpret_cast<unsigned char*>(&nTimeStamp), 1 );
            aNotify.xselection.property = rRequest.property;
#if OSL_DEBUG_LEVEL > 1
            SAL_INFO("vcl.unx.dtrans""sending timestamp: " << (int)nTimeStamp);
#endif
        }
        else
        {
            bool bEventSuccess = false;
            if( rRequest.target == m_nMULTIPLEAtom )
            {
                // get all targets
                Atom nType = None;
                int nFormat = 0;
                unsigned long nItems = 0, nBytes = 0;
                unsigned char* pData = nullptr;

                // get number of atoms
                XGetWindowProperty( m_pDisplay,
                                    rRequest.requestor,
                                    rRequest.property,
                                    0, 0,
                                    False,
                                    AnyPropertyType,
                                    &nType, &nFormat,
                                    &nItems, &nBytes,
                                    &pData );
                if( nFormat == 32 && nBytes/4 )
                {
                    if( pData ) // ?? should not happen
                    {
                        XFree( pData );
                        pData = nullptr;
                    }
                    XGetWindowProperty( m_pDisplay,
                                        rRequest.requestor,
                                        rRequest.property,
                                        0, nBytes/4,
                                        False,
                                        nType,
                                        &nType, &nFormat,
                                        &nItems, &nBytes,
                                        &pData );
                    if( pData && nItems )
                    {
#if OSL_DEBUG_LEVEL > 1
                        SAL_INFO("vcl.unx.dtrans""found "
                                << nItems
                                << " atoms in MULTIPLE request.");
#endif
                        bEventSuccess = true;
                        bool bResetAtoms = false;
                        Atom* pAtoms = reinterpret_cast<Atom*>(pData);
                        aGuard.clear();
                        forunsigned long i = 0; i < nItems; i += 2 )
                        {
#if OSL_DEBUG_LEVEL > 1
                            std::ostringstream oss;
                            oss << " "
                                << getString( pAtoms[i] )
                                << " => "
                                << getString( pAtoms[i+1] )
                                << ": ";
#endif

                            bool bSuccess = sendData( pAdaptor, rRequest.requestor, pAtoms[i], pAtoms[i+1], rRequest.selection );
#if OSL_DEBUG_LEVEL > 1
                            oss << (bSuccess ? "succeeded" : "failed");
                            SAL_INFO("vcl.unx.dtrans", oss.str());
#endif
                            if( ! bSuccess )
                            {
                                pAtoms[i] = None;
                                bResetAtoms = true;
                            }
                        }
                        aGuard.reset();
                        if( bResetAtoms )
                            XChangeProperty( m_pDisplay,
                                             rRequest.requestor,
                                             rRequest.property,
                                             XA_ATOM,
                                             32,
                                             PropModeReplace,
                                             pData,
                                             nBytes/4 );
                    }
                    if( pData )
                        XFree( pData );
                }
#if OSL_DEBUG_LEVEL > 1
                else
                {
                    std::ostringstream oss;
                    oss << "could not get type list from \""
                        << getString( rRequest.property )
                        << "\" of type \""
                        << getString( nType )
                        << "\" on requestor "
                        << std::showbase << std::hex
                        << rRequest.requestor
                        << ", requestor has properties:";

--> --------------------

--> maximum size reached

--> --------------------

Messung V0.5
C=94 H=95 G=94

¤ Dauer der Verarbeitung: 0.19 Sekunden  ¤

*© Formatika GbR, Deutschland






Wurzel

Suchen

Beweissystem der NASA

Beweissystem Isabelle

NIST Cobol Testsuite

Cephes Mathematical Library

Wiener Entwicklungsmethode

Haftungshinweis

Die Informationen auf dieser Webseite wurden nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit, noch Qualität der bereit gestellten Informationen zugesichert.

Bemerkung:

Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.