nss: upgrade to release 3.73
[LibreOffice.git] / vcl / unx / generic / dtrans / X11_selection.cxx
blobce98fcbd09f5193b22ad986be9f1a3c55e3625f3
1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2 /*
3 * This file is part of the LibreOffice project.
5 * This Source Code Form is subject to the terms of the Mozilla Public
6 * License, v. 2.0. If a copy of the MPL was not distributed with this
7 * file, You can obtain one at http://mozilla.org/MPL/2.0/.
9 * This file incorporates work covered by the following license notice:
11 * Licensed to the Apache Software Foundation (ASF) under one or more
12 * contributor license agreements. See the NOTICE file distributed
13 * with this work for additional information regarding copyright
14 * ownership. The ASF licenses this file to you under the Apache
15 * License, Version 2.0 (the "License"); you may not use this file
16 * except in compliance with the License. You may obtain a copy of
17 * the License at http://www.apache.org/licenses/LICENSE-2.0 .
20 #include <sal/config.h>
21 #include <sal/log.hxx>
23 #include <cstdlib>
25 #include <unx/saldisp.hxx>
27 #include <unistd.h>
28 #include <string.h>
29 #include <sys/time.h>
31 #include <X11/Xlib.h>
32 #include <X11/Xutil.h>
33 #include <X11/XKBlib.h>
34 #include <X11/Xatom.h>
35 #include <X11/keysym.h>
37 #if defined(NETBSD) || defined (FREEBSD) || defined(OPENBSD)
38 #include <sys/poll.h>
39 #else
40 #include <poll.h>
41 #endif
43 #include <sal/macros.h>
45 #include "X11_selection.hxx"
46 #include "X11_clipboard.hxx"
47 #include "X11_transferable.hxx"
48 #include "X11_dndcontext.hxx"
49 #include "bmp.hxx"
51 #include <vcl/svapp.hxx>
53 // pointer bitmaps
54 #include "copydata_curs.h"
55 #include "copydata_mask.h"
56 #include "movedata_curs.h"
57 #include "movedata_mask.h"
58 #include "linkdata_curs.h"
59 #include "linkdata_mask.h"
60 #include "nodrop_curs.h"
61 #include "nodrop_mask.h"
62 #include <com/sun/star/datatransfer/dnd/DNDConstants.hpp>
63 #include <com/sun/star/awt/MouseEvent.hpp>
64 #include <com/sun/star/awt/MouseButton.hpp>
65 #include <com/sun/star/frame/Desktop.hpp>
66 #include <rtl/tencinfo.h>
67 #include <rtl/ustrbuf.hxx>
69 #include <comphelper/processfactory.hxx>
70 #include <comphelper/solarmutex.hxx>
72 #include <cppuhelper/supportsservice.hxx>
73 #include <algorithm>
75 constexpr auto DRAG_EVENT_MASK = ButtonPressMask |
76 ButtonReleaseMask |
77 PointerMotionMask |
78 EnterWindowMask |
79 LeaveWindowMask;
81 using namespace com::sun::star::datatransfer;
82 using namespace com::sun::star::datatransfer::dnd;
83 using namespace com::sun::star::lang;
84 using namespace com::sun::star::awt;
85 using namespace com::sun::star::uno;
86 using namespace com::sun::star::frame;
87 using namespace cppu;
89 using namespace x11;
91 // stubs to satisfy solaris compiler's rather rigid linking warning
92 extern "C"
94 static void call_SelectionManager_run( void * pMgr )
96 SelectionManager::run( pMgr );
99 static void call_SelectionManager_runDragExecute( void * pMgr )
101 osl_setThreadName("SelectionManager::runDragExecute()");
102 SelectionManager::runDragExecute( pMgr );
106 const tools::Long nXdndProtocolRevision = 5;
108 namespace {
110 // mapping between mime types (or what the office thinks of mime types)
111 // and X convention types
112 struct NativeTypeEntry
114 Atom nAtom;
115 const char* pType; // Mime encoding on our side
116 const char* pNativeType; // string corresponding to nAtom for the case of nAtom being uninitialized
117 int nFormat; // the corresponding format
122 // the convention for Xdnd is mime types as specified by the corresponding
123 // RFC's with the addition that text/plain without charset tag contains iso8859-1
124 // sadly some applications (e.g. gtk) do not honor the mimetype only rule,
125 // so for compatibility add UTF8_STRING
126 static NativeTypeEntry aXdndConversionTab[] =
128 { 0, "text/plain;charset=iso8859-1", "text/plain", 8 },
129 { 0, "text/plain;charset=utf-8", "UTF8_STRING", 8 }
132 // for clipboard and primary selections there is only a convention for text
133 // that the encoding name of the text is taken as type in all capitalized letters
134 static NativeTypeEntry aNativeConversionTab[] =
136 { 0, "text/plain;charset=utf-16", "ISO10646-1", 16 },
137 { 0, "text/plain;charset=utf-8", "UTF8_STRING", 8 },
138 { 0, "text/plain;charset=utf-8", "UTF-8", 8 },
139 { 0, "text/plain;charset=utf-8", "text/plain;charset=UTF-8", 8 },
140 // ISO encodings
141 { 0, "text/plain;charset=iso8859-2", "ISO8859-2", 8 },
142 { 0, "text/plain;charset=iso8859-3", "ISO8859-3", 8 },
143 { 0, "text/plain;charset=iso8859-4", "ISO8859-4", 8 },
144 { 0, "text/plain;charset=iso8859-5", "ISO8859-5", 8 },
145 { 0, "text/plain;charset=iso8859-6", "ISO8859-6", 8 },
146 { 0, "text/plain;charset=iso8859-7", "ISO8859-7", 8 },
147 { 0, "text/plain;charset=iso8859-8", "ISO8859-8", 8 },
148 { 0, "text/plain;charset=iso8859-9", "ISO8859-9", 8 },
149 { 0, "text/plain;charset=iso8859-10", "ISO8859-10", 8 },
150 { 0, "text/plain;charset=iso8859-13", "ISO8859-13", 8 },
151 { 0, "text/plain;charset=iso8859-14", "ISO8859-14", 8 },
152 { 0, "text/plain;charset=iso8859-15", "ISO8859-15", 8 },
153 // asian encodings
154 { 0, "text/plain;charset=jisx0201.1976-0", "JISX0201.1976-0", 8 },
155 { 0, "text/plain;charset=jisx0208.1983-0", "JISX0208.1983-0", 8 },
156 { 0, "text/plain;charset=jisx0208.1990-0", "JISX0208.1990-0", 8 },
157 { 0, "text/plain;charset=jisx0212.1990-0", "JISX0212.1990-0", 8 },
158 { 0, "text/plain;charset=gb2312.1980-0", "GB2312.1980-0", 8 },
159 { 0, "text/plain;charset=ksc5601.1992-0", "KSC5601.1992-0", 8 },
160 // eastern european encodings
161 { 0, "text/plain;charset=koi8-r", "KOI8-R", 8 },
162 { 0, "text/plain;charset=koi8-u", "KOI8-U", 8 },
163 // String (== iso8859-1)
164 { XA_STRING, "text/plain;charset=iso8859-1", "STRING", 8 },
165 // special for compound text
166 { 0, "text/plain;charset=compound_text", "COMPOUND_TEXT", 8 },
168 // PIXMAP
169 { XA_PIXMAP, "image/bmp", "PIXMAP", 32 }
172 rtl_TextEncoding x11::getTextPlainEncoding( const OUString& rMimeType )
174 rtl_TextEncoding aEncoding = RTL_TEXTENCODING_DONTKNOW;
175 OUString aMimeType( rMimeType.toAsciiLowerCase() );
176 sal_Int32 nIndex = 0;
177 if( aMimeType.getToken( 0, ';', nIndex ) == "text/plain" )
179 if( aMimeType.getLength() == 10 ) // only "text/plain"
180 aEncoding = RTL_TEXTENCODING_ISO_8859_1;
181 else
183 while( nIndex != -1 )
185 OUString aToken = aMimeType.getToken( 0, ';', nIndex );
186 sal_Int32 nPos = 0;
187 if( aToken.getToken( 0, '=', nPos ) == "charset" )
189 OString aEncToken = OUStringToOString( aToken.getToken( 0, '=', nPos ), RTL_TEXTENCODING_ISO_8859_1 );
190 aEncoding = rtl_getTextEncodingFromUnixCharset( aEncToken.getStr() );
191 if( aEncoding == RTL_TEXTENCODING_DONTKNOW )
193 if( aEncToken.equalsIgnoreAsciiCase( "utf-8" ) )
194 aEncoding = RTL_TEXTENCODING_UTF8;
196 if( aEncoding != RTL_TEXTENCODING_DONTKNOW )
197 break;
202 #if OSL_DEBUG_LEVEL > 1
203 SAL_WARN_IF(aEncoding == RTL_TEXTENCODING_DONTKNOW,
204 "vcl.unx.dtrans", "getTextPlainEncoding( "
205 << rMimeType << " ) failed.");
206 #endif
207 return aEncoding;
210 std::unordered_map< OUString, SelectionManager* >& SelectionManager::getInstances()
212 static std::unordered_map< OUString, SelectionManager* > aInstances;
213 return aInstances;
216 SelectionManager::SelectionManager() :
217 m_nIncrementalThreshold( 15*1024 ),
218 m_pDisplay( nullptr ),
219 m_aThread( nullptr ),
220 m_aDragExecuteThread( nullptr ),
221 m_aWindow( None ),
222 m_nSelectionTimeout( 0 ),
223 m_nSelectionTimestamp( CurrentTime ),
224 m_bDropEnterSent( true ),
225 m_aCurrentDropWindow( None ),
226 m_nDropTime( None ),
227 m_nLastDropAction( 0 ),
228 m_nLastX( 0 ),
229 m_nLastY( 0 ),
230 m_bDropWaitingForCompletion( false ),
231 m_aDropWindow( None ),
232 m_aDropProxy( None ),
233 m_aDragSourceWindow( None ),
234 m_nLastDragX( 0 ),
235 m_nLastDragY( 0 ),
236 m_nNoPosX( 0 ),
237 m_nNoPosY( 0 ),
238 m_nNoPosWidth( 0 ),
239 m_nNoPosHeight( 0 ),
240 m_nDragButton( 0 ),
241 m_nUserDragAction( 0 ),
242 m_nTargetAcceptAction( 0 ),
243 m_nSourceActions( 0 ),
244 m_bLastDropAccepted( false ),
245 m_bDropSuccess( false ),
246 m_bDropSent( false ),
247 m_nDropTimeout( 0 ),
248 m_bWaitingForPrimaryConversion( false ),
249 m_aMoveCursor( None ),
250 m_aCopyCursor( None ),
251 m_aLinkCursor( None ),
252 m_aNoneCursor( None ),
253 m_aCurrentCursor( None ),
254 m_nCurrentProtocolVersion( nXdndProtocolRevision ),
255 m_nTARGETSAtom( None ),
256 m_nTIMESTAMPAtom( None ),
257 m_nTEXTAtom( None ),
258 m_nINCRAtom( None ),
259 m_nCOMPOUNDAtom( None ),
260 m_nMULTIPLEAtom( None ),
261 m_nImageBmpAtom( None ),
262 m_nXdndAware( None ),
263 m_nXdndEnter( None ),
264 m_nXdndLeave( None ),
265 m_nXdndPosition( None ),
266 m_nXdndStatus( None ),
267 m_nXdndDrop( None ),
268 m_nXdndFinished( None ),
269 m_nXdndSelection( None ),
270 m_nXdndTypeList( None ),
271 m_nXdndProxy( None ),
272 m_nXdndActionCopy( None ),
273 m_nXdndActionMove( None ),
274 m_nXdndActionLink( None ),
275 m_nXdndActionAsk( None ),
276 m_bShutDown( false )
278 memset(&m_aDropEnterEvent, 0, sizeof(m_aDropEnterEvent));
279 m_EndThreadPipe[0] = 0;
280 m_EndThreadPipe[1] = 0;
281 m_aDragRunning.reset();
284 Cursor SelectionManager::createCursor( const unsigned char* pPointerData, const unsigned char* pMaskData, int width, int height, int hotX, int hotY )
286 Pixmap aPointer;
287 Pixmap aMask;
288 XColor aBlack, aWhite;
290 aBlack.pixel = BlackPixel( m_pDisplay, 0 );
291 aBlack.red = aBlack.green = aBlack.blue = 0;
292 aBlack.flags = DoRed | DoGreen | DoBlue;
294 aWhite.pixel = WhitePixel( m_pDisplay, 0 );
295 aWhite.red = aWhite.green = aWhite.blue = 0xffff;
296 aWhite.flags = DoRed | DoGreen | DoBlue;
298 aPointer =
299 XCreateBitmapFromData( m_pDisplay,
300 m_aWindow,
301 reinterpret_cast<const char*>(pPointerData),
302 width,
303 height );
304 aMask
305 = XCreateBitmapFromData( m_pDisplay,
306 m_aWindow,
307 reinterpret_cast<const char*>(pMaskData),
308 width,
309 height );
310 Cursor aCursor =
311 XCreatePixmapCursor( m_pDisplay, aPointer, aMask,
312 &aBlack, &aWhite,
313 hotX,
314 hotY );
315 XFreePixmap( m_pDisplay, aPointer );
316 XFreePixmap( m_pDisplay, aMask );
318 return aCursor;
321 void SelectionManager::initialize( const Sequence< Any >& arguments )
323 osl::MutexGuard aGuard(m_aMutex);
325 if( ! m_xDisplayConnection.is() )
328 * first argument must be a css::awt::XDisplayConnection
329 * from this we will get the XEvents of the vcl event loop by
330 * registering us as XEventHandler on it.
332 * implementor's note:
333 * FIXME:
334 * finally the clipboard and XDND service is back in the module it belongs
335 * now cleanup and sharing of resources with the normal vcl event loop
336 * needs to be added. The display used would be that of the normal event loop
337 * and synchronization should be done via the SolarMutex.
339 if( arguments.hasElements() )
340 arguments.getConstArray()[0] >>= m_xDisplayConnection;
341 if( ! m_xDisplayConnection.is() )
344 else
345 m_xDisplayConnection->addEventHandler( Any(), this, ~0 );
348 if( m_pDisplay )
349 return;
351 OUString aUDisplay;
352 if( m_xDisplayConnection.is() )
354 Any aIdentifier = m_xDisplayConnection->getIdentifier();
355 aIdentifier >>= aUDisplay;
358 OString aDisplayName( OUStringToOString( aUDisplay, RTL_TEXTENCODING_ISO_8859_1 ) );
360 m_pDisplay = XOpenDisplay( aDisplayName.isEmpty() ? nullptr : aDisplayName.getStr());
362 if( !m_pDisplay )
363 return;
365 #ifdef SYNCHRONIZE
366 XSynchronize( m_pDisplay, True );
367 #endif
368 // special targets
369 m_nTARGETSAtom = getAtom( "TARGETS" );
370 m_nTIMESTAMPAtom = getAtom( "TIMESTAMP" );
371 m_nTEXTAtom = getAtom( "TEXT" );
372 m_nINCRAtom = getAtom( "INCR" );
373 m_nCOMPOUNDAtom = getAtom( "COMPOUND_TEXT" );
374 m_nMULTIPLEAtom = getAtom( "MULTIPLE" );
375 m_nImageBmpAtom = getAtom( "image/bmp" );
377 // Atoms for Xdnd protocol
378 m_nXdndAware = getAtom( "XdndAware" );
379 m_nXdndEnter = getAtom( "XdndEnter" );
380 m_nXdndLeave = getAtom( "XdndLeave" );
381 m_nXdndPosition = getAtom( "XdndPosition" );
382 m_nXdndStatus = getAtom( "XdndStatus" );
383 m_nXdndDrop = getAtom( "XdndDrop" );
384 m_nXdndFinished = getAtom( "XdndFinished" );
385 m_nXdndSelection = getAtom( "XdndSelection" );
386 m_nXdndTypeList = getAtom( "XdndTypeList" );
387 m_nXdndProxy = getAtom( "XdndProxy" );
388 m_nXdndActionCopy = getAtom( "XdndActionCopy" );
389 m_nXdndActionMove = getAtom( "XdndActionMove" );
390 m_nXdndActionLink = getAtom( "XdndActionLink" );
391 m_nXdndActionAsk = getAtom( "XdndActionAsk" );
393 // initialize map with member none
394 m_aAtomToString[ 0 ]= "None";
395 m_aAtomToString[ XA_PRIMARY ] = "PRIMARY";
397 // create a (invisible) message window
398 m_aWindow = XCreateSimpleWindow( m_pDisplay, DefaultRootWindow( m_pDisplay ),
399 10, 10, 10, 10, 0, 0, 1 );
401 // initialize threshold for incremental transfers
402 // ICCCM says it should be smaller that the max request size
403 // which in turn is guaranteed to be at least 16k bytes
404 m_nIncrementalThreshold = XMaxRequestSize( m_pDisplay ) - 1024;
406 if( !m_aWindow )
407 return;
409 // initialize default cursors
410 m_aMoveCursor = createCursor( movedata_curs_bits,
411 movedata_mask_bits,
412 movedata_curs_width,
413 movedata_curs_height,
414 movedata_curs_x_hot,
415 movedata_curs_y_hot );
416 m_aCopyCursor = createCursor( copydata_curs_bits,
417 copydata_mask_bits,
418 copydata_curs_width,
419 copydata_curs_height,
420 copydata_curs_x_hot,
421 copydata_curs_y_hot );
422 m_aLinkCursor = createCursor( linkdata_curs_bits,
423 linkdata_mask_bits,
424 linkdata_curs_width,
425 linkdata_curs_height,
426 linkdata_curs_x_hot,
427 linkdata_curs_y_hot );
428 m_aNoneCursor = createCursor( nodrop_curs_bits,
429 nodrop_mask_bits,
430 nodrop_curs_width,
431 nodrop_curs_height,
432 nodrop_curs_x_hot,
433 nodrop_curs_y_hot );
435 // just interested in SelectionClear/Notify/Request and PropertyChange
436 XSelectInput( m_pDisplay, m_aWindow, PropertyChangeMask );
437 // create the transferable for Drag operations
438 m_xDropTransferable = new X11Transferable( *this, m_nXdndSelection );
439 registerHandler( m_nXdndSelection, *this );
441 m_aThread = osl_createSuspendedThread( call_SelectionManager_run, this );
442 if( m_aThread )
443 osl_resumeThread( m_aThread );
444 #if OSL_DEBUG_LEVEL > 1
445 else
446 SAL_WARN("vcl.unx.dtrans", "SelectionManager::initialize: "
447 << "creation of dispatch thread failed !.");
448 #endif
450 if (pipe(m_EndThreadPipe) != 0) {
451 #if OSL_DEBUG_LEVEL > 1
452 SAL_WARN("vcl.unx.dtrans", "Failed to create endThreadPipe.");
453 #endif
454 m_EndThreadPipe[0] = m_EndThreadPipe[1] = 0;
458 SelectionManager::~SelectionManager()
460 #if OSL_DEBUG_LEVEL > 1
461 SAL_INFO("vcl.unx.dtrans", "SelectionManager::~SelectionManager ("
462 << (m_pDisplay ? DisplayString(m_pDisplay) : "no display")
463 << ").");
464 #endif
466 osl::MutexGuard aGuard( *osl::Mutex::getGlobalMutex() );
468 auto it = std::find_if(getInstances().begin(), getInstances().end(),
469 [&](const std::pair< OUString, SelectionManager* >& rInstance) { return rInstance.second == this; });
470 if( it != getInstances().end() )
471 getInstances().erase( it );
474 if( m_aThread )
476 osl_terminateThread( m_aThread );
477 osl_joinWithThread( m_aThread );
478 osl_destroyThread( m_aThread );
481 if( m_aDragExecuteThread )
483 osl_terminateThread( m_aDragExecuteThread );
484 osl_joinWithThread( m_aDragExecuteThread );
485 m_aDragExecuteThread = nullptr;
486 // thread handle is freed in dragDoDispatch()
489 osl::MutexGuard aGuard(m_aMutex);
491 #if OSL_DEBUG_LEVEL > 1
492 SAL_INFO("vcl.unx.dtrans", "shutting down SelectionManager.");
493 #endif
495 if( !m_pDisplay )
496 return;
498 deregisterHandler( m_nXdndSelection );
499 // destroy message window
500 if( m_aWindow )
501 XDestroyWindow( m_pDisplay, m_aWindow );
502 // release cursors
503 if (m_aMoveCursor != None)
504 XFreeCursor(m_pDisplay, m_aMoveCursor);
505 if (m_aCopyCursor != None)
506 XFreeCursor(m_pDisplay, m_aCopyCursor);
507 if (m_aLinkCursor != None)
508 XFreeCursor(m_pDisplay, m_aLinkCursor);
509 if (m_aNoneCursor != None)
510 XFreeCursor(m_pDisplay, m_aNoneCursor);
512 // paranoia setting, the drag thread should have
513 // done that already
514 XUngrabPointer( m_pDisplay, CurrentTime );
515 XUngrabKeyboard( m_pDisplay, CurrentTime );
517 XCloseDisplay( m_pDisplay );
520 SelectionAdaptor* SelectionManager::getAdaptor( Atom selection )
522 std::unordered_map< Atom, Selection* >::iterator it =
523 m_aSelections.find( selection );
524 return it != m_aSelections.end() ? it->second->m_pAdaptor : nullptr;
527 OUString SelectionManager::convertFromCompound( const char* pText, int nLen )
529 osl::MutexGuard aGuard( m_aMutex );
530 OUStringBuffer aRet;
531 if( nLen < 0 )
532 nLen = strlen( pText );
534 char** pTextList = nullptr;
535 int nTexts = 0;
537 XTextProperty aProp;
538 aProp.value = reinterpret_cast<unsigned char *>(const_cast<char *>(pText));
539 aProp.encoding = m_nCOMPOUNDAtom;
540 aProp.format = 8;
541 aProp.nitems = nLen;
542 XmbTextPropertyToTextList( m_pDisplay,
543 &aProp,
544 &pTextList,
545 &nTexts );
546 rtl_TextEncoding aEncoding = osl_getThreadTextEncoding();
547 for( int i = 0; i < nTexts; i++ )
548 aRet.append(OStringToOUString( pTextList[i], aEncoding ));
550 if( pTextList )
551 XFreeStringList( pTextList );
553 return aRet.makeStringAndClear();
556 OString SelectionManager::convertToCompound( const OUString& rText )
558 osl::MutexGuard aGuard( m_aMutex );
559 XTextProperty aProp;
560 aProp.value = nullptr;
561 aProp.encoding = XA_STRING;
562 aProp.format = 8;
563 aProp.nitems = 0;
565 OString aRet( rText.getStr(), rText.getLength(), osl_getThreadTextEncoding() );
566 char* pT = const_cast<char*>(aRet.getStr());
568 XmbTextListToTextProperty( m_pDisplay,
569 &pT,
571 XCompoundTextStyle,
572 &aProp );
573 if( aProp.value )
575 aRet = reinterpret_cast<char*>(aProp.value);
576 XFree( aProp.value );
577 #ifdef __sun
579 * for currently unknown reasons XmbTextListToTextProperty on Solaris returns
580 * no data in ISO8859-n encodings (at least for n = 1, 15)
581 * in these encodings the directly converted text does the
582 * trick, also.
584 if( aRet.isEmpty() && !rText.isEmpty() )
585 aRet = OUStringToOString( rText, osl_getThreadTextEncoding() );
586 #endif
588 else
589 aRet.clear();
591 return aRet;
594 bool SelectionManager::convertData(
595 const css::uno::Reference< XTransferable >& xTransferable,
596 Atom nType,
597 Atom nSelection,
598 int& rFormat,
599 Sequence< sal_Int8 >& rData )
601 bool bSuccess = false;
603 if( ! xTransferable.is() )
604 return bSuccess;
609 DataFlavor aFlavor;
610 aFlavor.MimeType = convertTypeFromNative( nType, nSelection, rFormat );
612 sal_Int32 nIndex = 0;
613 if( aFlavor.MimeType.getToken( 0, ';', nIndex ) == "text/plain" )
615 if( aFlavor.MimeType.getToken( 0, ';', nIndex ) == "charset=utf-16" )
616 aFlavor.DataType = cppu::UnoType<OUString>::get();
617 else
618 aFlavor.DataType = cppu::UnoType<Sequence< sal_Int8 >>::get();
620 else
621 aFlavor.DataType = cppu::UnoType<Sequence< sal_Int8 >>::get();
623 if( xTransferable->isDataFlavorSupported( aFlavor ) )
625 Any aValue( xTransferable->getTransferData( aFlavor ) );
626 if( aValue.getValueTypeClass() == TypeClass_STRING )
628 OUString aString;
629 aValue >>= aString;
630 rData = Sequence< sal_Int8 >( reinterpret_cast<sal_Int8 const *>(aString.getStr()), aString.getLength() * sizeof( sal_Unicode ) );
631 bSuccess = true;
633 else if( aValue.getValueType() == cppu::UnoType<Sequence< sal_Int8 >>::get() )
635 aValue >>= rData;
636 bSuccess = true;
639 else if( aFlavor.MimeType.startsWith("text/plain") )
641 rtl_TextEncoding aEncoding = RTL_TEXTENCODING_DONTKNOW;
642 bool bCompoundText = false;
643 if( nType == m_nCOMPOUNDAtom )
644 bCompoundText = true;
645 else
646 aEncoding = getTextPlainEncoding( aFlavor.MimeType );
647 if( aEncoding != RTL_TEXTENCODING_DONTKNOW || bCompoundText )
649 aFlavor.MimeType = "text/plain;charset=utf-16";
650 aFlavor.DataType = cppu::UnoType<OUString>::get();
651 if( xTransferable->isDataFlavorSupported( aFlavor ) )
653 Any aValue( xTransferable->getTransferData( aFlavor ) );
654 OUString aString;
655 aValue >>= aString;
656 OString aByteString( bCompoundText ? convertToCompound( aString ) : OUStringToOString( aString, aEncoding ) );
657 rData = Sequence< sal_Int8 >( reinterpret_cast<sal_Int8 const *>(aByteString.getStr()), aByteString.getLength() * sizeof( char ) );
658 bSuccess = true;
663 // various exceptions possible ... which all lead to a failed conversion
664 // so simplify here to a catch all
665 catch(...)
669 return bSuccess;
672 SelectionManager& SelectionManager::get( const OUString& rDisplayName )
674 osl::MutexGuard aGuard( *osl::Mutex::getGlobalMutex() );
676 OUString aDisplayName( rDisplayName );
677 if( aDisplayName.isEmpty() )
678 aDisplayName = OStringToOUString( getenv( "DISPLAY" ), RTL_TEXTENCODING_ISO_8859_1 );
679 SelectionManager* pInstance = nullptr;
681 std::unordered_map< OUString, SelectionManager* >::iterator it = getInstances().find( aDisplayName );
682 if( it != getInstances().end() )
683 pInstance = it->second;
684 else pInstance = getInstances()[ aDisplayName ] = new SelectionManager();
686 return *pInstance;
689 OUString SelectionManager::getString( Atom aAtom )
691 osl::MutexGuard aGuard(m_aMutex);
693 if( m_aAtomToString.find( aAtom ) == m_aAtomToString.end() )
695 char* pAtom = m_pDisplay ? XGetAtomName( m_pDisplay, aAtom ) : nullptr;
696 if( ! pAtom )
697 return OUString();
698 OUString aString( OStringToOUString( pAtom, RTL_TEXTENCODING_ISO_8859_1 ) );
699 XFree( pAtom );
700 m_aStringToAtom[ aString ] = aAtom;
701 m_aAtomToString[ aAtom ] = aString;
703 return m_aAtomToString[ aAtom ];
706 Atom SelectionManager::getAtom( const OUString& rString )
708 osl::MutexGuard aGuard(m_aMutex);
710 if( m_aStringToAtom.find( rString ) == m_aStringToAtom.end() )
712 static Atom nNoDisplayAtoms = 1;
713 Atom aAtom = m_pDisplay ? XInternAtom( m_pDisplay, OUStringToOString( rString, RTL_TEXTENCODING_ISO_8859_1 ).getStr(), False ) : nNoDisplayAtoms++;
714 m_aStringToAtom[ rString ] = aAtom;
715 m_aAtomToString[ aAtom ] = rString;
717 return m_aStringToAtom[ rString ];
720 bool SelectionManager::requestOwnership( Atom selection )
722 bool bSuccess = false;
723 if( m_pDisplay && m_aWindow )
725 osl::MutexGuard aGuard(m_aMutex);
727 SelectionAdaptor* pAdaptor = getAdaptor( selection );
728 if( pAdaptor )
730 XSetSelectionOwner( m_pDisplay, selection, m_aWindow, CurrentTime );
731 if( XGetSelectionOwner( m_pDisplay, selection ) == m_aWindow )
732 bSuccess = true;
734 #if OSL_DEBUG_LEVEL > 1
735 SAL_INFO("vcl.unx.dtrans",
736 (bSuccess ? "acquired" : "failed to acquire")
737 << " ownership for selection "
738 << getString( selection ));
739 #endif
741 Selection* pSel = m_aSelections[ selection ];
742 pSel->m_bOwner = bSuccess;
743 delete pSel->m_pPixmap;
744 pSel->m_pPixmap = nullptr;
745 pSel->m_nOrigTimestamp = m_nSelectionTimestamp;
747 #if OSL_DEBUG_LEVEL > 1
748 else
749 SAL_WARN("vcl.unx.dtrans", "no adaptor for selection "
750 << getString( selection ));
752 if( pAdaptor->getTransferable().is() )
754 Sequence< DataFlavor > aTypes = pAdaptor->getTransferable()->getTransferDataFlavors();
755 for( int i = 0; i < aTypes.getLength(); i++ )
757 SAL_INFO("vcl.unx.dtrans", " " << aTypes.getConstArray()[i].MimeType);
760 #endif
762 return bSuccess;
765 void SelectionManager::convertTypeToNative( const OUString& rType, Atom selection, int& rFormat, ::std::list< Atom >& rConversions, bool bPushFront )
767 NativeTypeEntry* pTab = selection == m_nXdndSelection ? aXdndConversionTab : aNativeConversionTab;
768 int nTabEntries = selection == m_nXdndSelection ? SAL_N_ELEMENTS(aXdndConversionTab) : SAL_N_ELEMENTS(aNativeConversionTab);
770 OString aType( OUStringToOString( rType, RTL_TEXTENCODING_ISO_8859_1 ) );
771 SAL_INFO( "vcl.unx.dtrans", "convertTypeToNative " << aType );
772 rFormat = 0;
773 for( int i = 0; i < nTabEntries; i++ )
775 if( aType.equalsIgnoreAsciiCase( pTab[i].pType ) )
777 if( ! pTab[i].nAtom )
778 pTab[i].nAtom = getAtom( OStringToOUString( pTab[i].pNativeType, RTL_TEXTENCODING_ISO_8859_1 ) );
779 rFormat = pTab[i].nFormat;
780 if( bPushFront )
781 rConversions.push_front( pTab[i].nAtom );
782 else
783 rConversions.push_back( pTab[i].nAtom );
784 if( pTab[i].nFormat == XA_PIXMAP )
786 if( bPushFront )
788 rConversions.push_front( XA_VISUALID );
789 rConversions.push_front( XA_COLORMAP );
791 else
793 rConversions.push_back( XA_VISUALID );
794 rConversions.push_back( XA_COLORMAP );
799 if( ! rFormat )
800 rFormat = 8; // byte buffer
801 if( bPushFront )
802 rConversions.push_front( getAtom( rType ) );
803 else
804 rConversions.push_back( getAtom( rType ) );
807 void SelectionManager::getNativeTypeList( const Sequence< DataFlavor >& rTypes, std::list< Atom >& rOutTypeList, Atom targetselection )
809 rOutTypeList.clear();
811 int nFormat;
812 bool bHaveText = false;
813 for( const auto& rFlavor : rTypes )
815 if( rFlavor.MimeType.startsWith("text/plain"))
816 bHaveText = true;
817 else
818 convertTypeToNative( rFlavor.MimeType, targetselection, nFormat, rOutTypeList );
820 if( bHaveText )
822 if( targetselection != m_nXdndSelection )
824 // only mimetypes should go into Xdnd type list
825 rOutTypeList.push_front( XA_STRING );
826 rOutTypeList.push_front( m_nCOMPOUNDAtom );
828 convertTypeToNative( "text/plain;charset=utf-8", targetselection, nFormat, rOutTypeList, true );
830 if( targetselection != m_nXdndSelection )
831 rOutTypeList.push_back( m_nMULTIPLEAtom );
834 OUString SelectionManager::convertTypeFromNative( Atom nType, Atom selection, int& rFormat )
836 NativeTypeEntry* pTab = (selection == m_nXdndSelection) ? aXdndConversionTab : aNativeConversionTab;
837 int nTabEntries = (selection == m_nXdndSelection) ? SAL_N_ELEMENTS(aXdndConversionTab) : SAL_N_ELEMENTS(aNativeConversionTab);
839 for( int i = 0; i < nTabEntries; i++ )
841 if( ! pTab[i].nAtom )
842 pTab[i].nAtom = getAtom( OStringToOUString( pTab[i].pNativeType, RTL_TEXTENCODING_ISO_8859_1 ) );
843 if( nType == pTab[i].nAtom )
845 rFormat = pTab[i].nFormat;
846 return OStringToOUString( pTab[i].pType, RTL_TEXTENCODING_ISO_8859_1 );
849 rFormat = 8;
850 return getString( nType );
853 bool SelectionManager::getPasteData( Atom selection, Atom type, Sequence< sal_Int8 >& rData )
855 osl::ResettableMutexGuard aGuard(m_aMutex);
856 std::unordered_map< Atom, Selection* >::iterator it;
857 bool bSuccess = false;
859 #if OSL_DEBUG_LEVEL > 1
860 SAL_INFO("vcl.unx.dtrans", "getPasteData( " << getString( selection )
861 << ", native: " << getString( type ) << " ).");
862 #endif
864 if( ! m_pDisplay )
865 return false;
867 it = m_aSelections.find( selection );
868 if( it == m_aSelections.end() )
869 return false;
871 ::Window aSelectionOwner = XGetSelectionOwner( m_pDisplay, selection );
872 if( aSelectionOwner == None )
873 return false;
874 if( aSelectionOwner == m_aWindow )
876 // probably bad timing led us here
877 #if OSL_DEBUG_LEVEL > 1
878 SAL_WARN("vcl.unx.dtrans", "Innere Nabelschau.");
879 #endif
880 return false;
883 // ICCCM recommends to destroy property before convert request unless
884 // parameters are transported; we do only in case of MULTIPLE,
885 // so destroy property unless target is MULTIPLE
886 if( type != m_nMULTIPLEAtom )
887 XDeleteProperty( m_pDisplay, m_aWindow, selection );
889 XConvertSelection( m_pDisplay, selection, type, selection, m_aWindow, selection == m_nXdndSelection ? m_nDropTime : CurrentTime );
890 it->second->m_eState = Selection::WaitingForResponse;
891 it->second->m_aRequestedType = type;
892 it->second->m_aData = Sequence< sal_Int8 >();
893 it->second->m_aDataArrived.reset();
894 // really start the request; if we don't flush the
895 // queue the request won't leave it because there are no more
896 // X calls after this until the data arrived or timeout
897 XFlush( m_pDisplay );
899 // do a reschedule
900 struct timeval tv_last, tv_current;
901 gettimeofday( &tv_last, nullptr );
902 tv_current = tv_last;
904 XEvent aEvent;
907 bool bAdjustTime = false;
909 bool bHandle = false;
911 if( XCheckTypedEvent( m_pDisplay,
912 PropertyNotify,
913 &aEvent
916 bHandle = true;
917 if( aEvent.xproperty.window == m_aWindow
918 && aEvent.xproperty.atom == selection )
919 bAdjustTime = true;
921 else if( XCheckTypedEvent( m_pDisplay,
922 SelectionClear,
923 &aEvent
926 bHandle = true;
928 else if( XCheckTypedEvent( m_pDisplay,
929 SelectionRequest,
930 &aEvent
933 bHandle = true;
935 else if( XCheckTypedEvent( m_pDisplay,
936 SelectionNotify,
937 &aEvent
940 bHandle = true;
941 if( aEvent.xselection.selection == selection
942 && ( aEvent.xselection.requestor == m_aWindow ||
943 aEvent.xselection.requestor == m_aCurrentDropWindow )
945 bAdjustTime = true;
947 else
949 aGuard.clear();
950 osl::Thread::wait(std::chrono::milliseconds(100));
951 aGuard.reset();
953 if( bHandle )
955 aGuard.clear();
956 handleXEvent( aEvent );
957 aGuard.reset();
960 gettimeofday( &tv_current, nullptr );
961 if( bAdjustTime )
962 tv_last = tv_current;
963 } while( ! it->second->m_aDataArrived.check() && (tv_current.tv_sec - tv_last.tv_sec) < getSelectionTimeout() );
965 #if OSL_DEBUG_LEVEL > 1
966 SAL_WARN_IF((tv_current.tv_sec - tv_last.tv_sec) > getSelectionTimeout(),
967 "vcl.unx.dtrans", "timed out.");
968 #endif
970 if( it->second->m_aDataArrived.check() &&
971 it->second->m_aData.getLength() )
973 rData = it->second->m_aData;
974 bSuccess = true;
976 #if OSL_DEBUG_LEVEL > 1
977 else
978 SAL_WARN("vcl.unx.dtrans", "conversion unsuccessful.");
979 #endif
980 return bSuccess;
983 bool SelectionManager::getPasteData( Atom selection, const OUString& rType, Sequence< sal_Int8 >& rData )
985 bool bSuccess = false;
987 std::unordered_map< Atom, Selection* >::iterator it;
989 osl::MutexGuard aGuard(m_aMutex);
991 it = m_aSelections.find( selection );
992 if( it == m_aSelections.end() )
993 return false;
996 if( it->second->m_aTypes.getLength() == 0 )
998 Sequence< DataFlavor > aFlavors;
999 getPasteDataTypes( selection, aFlavors );
1000 if( it->second->m_aTypes.getLength() == 0 )
1001 return false;
1004 const Sequence< DataFlavor >& rTypes( it->second->m_aTypes );
1005 const std::vector< Atom >& rNativeTypes( it->second->m_aNativeTypes );
1007 #if OSL_DEBUG_LEVEL > 1
1008 SAL_INFO("vcl.unx.dtrans", "getPasteData( \""
1009 << getString( selection )
1010 << "\", \""
1011 << rType << "\" ).");
1012 #endif
1014 if( rType == "text/plain;charset=utf-16" )
1016 // lets see if we have UTF16 else try to find something convertible
1017 if( it->second->m_aTypes.getLength() && ! it->second->m_bHaveUTF16 )
1019 Sequence< sal_Int8 > aData;
1020 if( it->second->m_aUTF8Type != None &&
1021 getPasteData( selection,
1022 it->second->m_aUTF8Type,
1023 aData )
1026 OUString aRet( reinterpret_cast<const char*>(aData.getConstArray()), aData.getLength(), RTL_TEXTENCODING_UTF8 );
1027 rData = Sequence< sal_Int8 >( reinterpret_cast<sal_Int8 const *>(aRet.getStr()), (aRet.getLength()+1)*sizeof( sal_Unicode ) );
1028 bSuccess = true;
1030 else if( it->second->m_bHaveCompound &&
1031 getPasteData( selection,
1032 m_nCOMPOUNDAtom,
1033 aData )
1036 OUString aRet( convertFromCompound( reinterpret_cast<const char*>(aData.getConstArray()), aData.getLength() ) );
1037 rData = Sequence< sal_Int8 >( reinterpret_cast<sal_Int8 const *>(aRet.getStr()), (aRet.getLength()+1)*sizeof( sal_Unicode ) );
1038 bSuccess = true;
1040 else
1042 for( int i = 0; i < rTypes.getLength(); i++ )
1044 rtl_TextEncoding aEncoding = getTextPlainEncoding( rTypes.getConstArray()[i].MimeType );
1045 if( aEncoding != RTL_TEXTENCODING_DONTKNOW &&
1046 aEncoding != RTL_TEXTENCODING_UNICODE &&
1047 getPasteData( selection,
1048 rNativeTypes[i],
1049 aData )
1052 #if OSL_DEBUG_LEVEL > 1
1053 SAL_INFO("vcl.unx.dtrans", "using \""
1054 << rTypes.getConstArray()[i].MimeType
1055 << "\" instead of \""
1056 << rType
1057 << "\".");
1058 #endif
1060 OString aConvert( reinterpret_cast<char const *>(aData.getConstArray()), aData.getLength() );
1061 OUString aUTF( OStringToOUString( aConvert, aEncoding ) );
1062 rData = Sequence< sal_Int8 >( reinterpret_cast<sal_Int8 const *>(aUTF.getStr()), (aUTF.getLength()+1)*sizeof( sal_Unicode ) );
1063 bSuccess = true;
1064 break;
1070 else if( rType == "image/bmp" )
1072 // #i83376# try if someone has the data in image/bmp already before
1073 // doing the PIXMAP stuff (e.g. the Gimp has this)
1074 bSuccess = getPasteData( selection, m_nImageBmpAtom, rData );
1075 #if OSL_DEBUG_LEVEL > 1
1076 SAL_INFO_IF(bSuccess, "vcl.unx.dtrans",
1077 "got " << (int) rData.getLength() << " bytes of image/bmp.");
1078 #endif
1079 if( ! bSuccess )
1081 Pixmap aPixmap = None;
1082 Colormap aColormap = None;
1084 // prepare property for MULTIPLE request
1085 Sequence< sal_Int8 > aData;
1086 Atom const pTypes[4] = { XA_PIXMAP, XA_PIXMAP, XA_COLORMAP, XA_COLORMAP };
1088 osl::MutexGuard aGuard(m_aMutex);
1090 XChangeProperty( m_pDisplay,
1091 m_aWindow,
1092 selection,
1093 XA_ATOM,
1095 PropModeReplace,
1096 reinterpret_cast<unsigned char const *>(pTypes),
1097 4 );
1100 // try MULTIPLE request
1101 if( getPasteData( selection, m_nMULTIPLEAtom, aData ) )
1103 Atom* pReturnedTypes = reinterpret_cast<Atom*>(aData.getArray());
1104 if( pReturnedTypes[0] == XA_PIXMAP && pReturnedTypes[1] == XA_PIXMAP )
1106 osl::MutexGuard aGuard(m_aMutex);
1108 Atom type = None;
1109 int format = 0;
1110 unsigned long nItems = 0;
1111 unsigned long nBytes = 0;
1112 unsigned char* pReturn = nullptr;
1113 XGetWindowProperty( m_pDisplay, m_aWindow, XA_PIXMAP, 0, 1, True, XA_PIXMAP, &type, &format, &nItems, &nBytes, &pReturn );
1114 if( pReturn )
1116 if( type == XA_PIXMAP )
1117 aPixmap = *reinterpret_cast<Pixmap*>(pReturn);
1118 XFree( pReturn );
1119 pReturn = nullptr;
1120 if( pReturnedTypes[2] == XA_COLORMAP && pReturnedTypes[3] == XA_COLORMAP )
1122 XGetWindowProperty( m_pDisplay, m_aWindow, XA_COLORMAP, 0, 1, True, XA_COLORMAP, &type, &format, &nItems, &nBytes, &pReturn );
1123 if( pReturn )
1125 if( type == XA_COLORMAP )
1126 aColormap = *reinterpret_cast<Colormap*>(pReturn);
1127 XFree( pReturn );
1131 #if OSL_DEBUG_LEVEL > 1
1132 else
1134 SAL_WARN("vcl.unx.dtrans", "could not get PIXMAP property: type="
1135 << getString( type )
1136 << ", format=" << format
1137 << ", items=" << nItems
1138 << ", bytes=" << nBytes
1139 << ", ret=0x" << pReturn);
1141 #endif
1145 if( aPixmap == None )
1147 // perhaps two normal requests will work
1148 if( getPasteData( selection, XA_PIXMAP, aData ) )
1150 aPixmap = *reinterpret_cast<Pixmap*>(aData.getArray());
1151 if( aColormap == None && getPasteData( selection, XA_COLORMAP, aData ) )
1152 aColormap = *reinterpret_cast<Colormap*>(aData.getArray());
1156 // convert data if possible
1157 if( aPixmap != None )
1159 osl::MutexGuard aGuard(m_aMutex);
1161 sal_Int32 nOutSize = 0;
1162 sal_uInt8* pBytes = X11_getBmpFromPixmap( m_pDisplay, aPixmap, aColormap, nOutSize );
1163 if( pBytes )
1165 if( nOutSize )
1167 rData = Sequence< sal_Int8 >( nOutSize );
1168 memcpy( rData.getArray(), pBytes, nOutSize );
1169 bSuccess = true;
1171 std::free( pBytes );
1177 if( ! bSuccess )
1179 int nFormat;
1180 ::std::list< Atom > aTypes;
1181 convertTypeToNative( rType, selection, nFormat, aTypes );
1182 Atom nSelectedType = None;
1183 for (auto const& type : aTypes)
1185 for( auto const & nativeType: rNativeTypes )
1186 if(nativeType == type)
1188 nSelectedType = type;
1189 if (nSelectedType != None)
1190 break;
1193 if( nSelectedType != None )
1194 bSuccess = getPasteData( selection, nSelectedType, rData );
1196 #if OSL_DEBUG_LEVEL > 1
1197 SAL_INFO("vcl.unx.dtrans", "getPasteData for selection "
1198 << getString( selection )
1199 << " and data type "
1200 << rType
1201 << " returns "
1202 << ( bSuccess ? "true" : "false")
1203 << ", returned sequence has length "
1204 << rData.getLength() << ".");
1205 #endif
1206 return bSuccess;
1209 bool SelectionManager::getPasteDataTypes( Atom selection, Sequence< DataFlavor >& rTypes )
1211 std::unordered_map< Atom, Selection* >::iterator it;
1213 osl::MutexGuard aGuard(m_aMutex);
1215 it = m_aSelections.find( selection );
1216 if( it != m_aSelections.end() &&
1217 it->second->m_aTypes.getLength() &&
1218 std::abs( it->second->m_nLastTimestamp - time( nullptr ) ) < 2
1221 rTypes = it->second->m_aTypes;
1222 return true;
1226 bool bSuccess = false;
1227 bool bHaveUTF16 = false;
1228 Atom aUTF8Type = None;
1229 bool bHaveCompound = false;
1230 Sequence< sal_Int8 > aAtoms;
1232 if( selection == m_nXdndSelection )
1234 // xdnd sends first three types with XdndEnter
1235 // if more than three types are supported then the XDndTypeList
1236 // property on the source window is used
1237 if( m_aDropEnterEvent.data.l[0] && m_aCurrentDropWindow )
1239 if( m_aDropEnterEvent.data.l[1] & 1 )
1241 const unsigned int atomcount = 256;
1242 // more than three types; look in property
1243 osl::MutexGuard aGuard(m_aMutex);
1245 Atom nType;
1246 int nFormat;
1247 unsigned long nItems, nBytes;
1248 unsigned char* pBytes = nullptr;
1250 XGetWindowProperty( m_pDisplay, m_aDropEnterEvent.data.l[0],
1251 m_nXdndTypeList, 0, atomcount, False,
1252 XA_ATOM,
1253 &nType, &nFormat, &nItems, &nBytes, &pBytes );
1254 #if OSL_DEBUG_LEVEL > 1
1255 SAL_INFO("vcl.unx.dtrans", "have "
1256 << nItems
1257 << " data types in XdndTypeList.");
1258 #endif
1259 if( nItems == atomcount && nBytes > 0 )
1261 // wow ... more than 256 types !
1262 aAtoms.realloc( sizeof( Atom )*atomcount+nBytes );
1263 memcpy( aAtoms.getArray(), pBytes, sizeof( Atom )*atomcount );
1264 XFree( pBytes );
1265 pBytes = nullptr;
1266 XGetWindowProperty( m_pDisplay, m_aDropEnterEvent.data.l[0],
1267 m_nXdndTypeList, atomcount, nBytes/sizeof(Atom),
1268 False, XA_ATOM,
1269 &nType, &nFormat, &nItems, &nBytes, &pBytes );
1271 memcpy( aAtoms.getArray()+atomcount*sizeof(Atom), pBytes, nItems*sizeof(Atom) );
1272 XFree( pBytes );
1275 else
1277 aAtoms.realloc( sizeof(Atom)*nItems );
1278 memcpy( aAtoms.getArray(), pBytes, nItems*sizeof(Atom) );
1279 XFree( pBytes );
1282 else
1284 // one to three types
1285 int n = 0, i;
1286 for( i = 0; i < 3; i++ )
1287 if( m_aDropEnterEvent.data.l[2+i] )
1288 n++;
1289 #if OSL_DEBUG_LEVEL > 1
1290 SAL_INFO("vcl.unx.dtrans", "have "
1291 << n
1292 << " data types in XdndEnter.");
1293 #endif
1294 aAtoms.realloc( sizeof(Atom)*n );
1295 for( i = 0, n = 0; i < 3; i++ )
1296 if( m_aDropEnterEvent.data.l[2+i] )
1297 reinterpret_cast<Atom*>(aAtoms.getArray())[n++] = m_aDropEnterEvent.data.l[2+i];
1301 // get data of type TARGETS
1302 else if( ! getPasteData( selection, m_nTARGETSAtom, aAtoms ) )
1303 aAtoms = Sequence< sal_Int8 >();
1305 std::vector< Atom > aNativeTypes;
1306 if( aAtoms.hasElements() )
1308 sal_Int32 nAtoms = aAtoms.getLength() / sizeof(Atom);
1309 Atom* pAtoms = reinterpret_cast<Atom*>(aAtoms.getArray());
1310 rTypes.realloc( nAtoms );
1311 aNativeTypes.resize( nAtoms );
1312 DataFlavor* pFlavors = rTypes.getArray();
1313 sal_Int32 nNativeTypesIndex = 0;
1314 bool bHaveText = false;
1315 while( nAtoms-- )
1317 SAL_INFO_IF(*pAtoms && *pAtoms < 0x01000000, "vcl.unx.dtrans",
1318 "getPasteDataTypes: available: \"" << getString(*pAtoms) << "\"");
1319 if( *pAtoms == m_nCOMPOUNDAtom )
1320 bHaveText = bHaveCompound = true;
1321 else if( *pAtoms && *pAtoms < 0x01000000 )
1323 int nFormat;
1324 pFlavors->MimeType = convertTypeFromNative( *pAtoms, selection, nFormat );
1325 pFlavors->DataType = cppu::UnoType<Sequence< sal_Int8 >>::get();
1326 sal_Int32 nIndex = 0;
1327 if( pFlavors->MimeType.getToken( 0, ';', nIndex ) == "text/plain" )
1329 OUString aToken(pFlavors->MimeType.getToken( 0, ';', nIndex ));
1330 // omit text/plain;charset=unicode since it is not well defined
1331 if( aToken == "charset=unicode" )
1333 pAtoms++;
1334 continue;
1336 bHaveText = true;
1337 if( aToken == "charset=utf-16" )
1339 bHaveUTF16 = true;
1340 pFlavors->DataType = cppu::UnoType<OUString>::get();
1342 else if( aToken == "charset=utf-8" )
1344 aUTF8Type = *pAtoms;
1347 pFlavors++;
1348 aNativeTypes[ nNativeTypesIndex ] = *pAtoms;
1349 nNativeTypesIndex++;
1351 pAtoms++;
1353 if( (pFlavors - rTypes.getArray()) < rTypes.getLength() )
1354 rTypes.realloc(pFlavors - rTypes.getArray());
1355 bSuccess = rTypes.hasElements();
1356 if( bHaveText && ! bHaveUTF16 )
1358 int i = 0;
1360 int nNewFlavors = rTypes.getLength()+1;
1361 Sequence< DataFlavor > aTemp( nNewFlavors );
1362 for( i = 0; i < nNewFlavors-1; i++ )
1363 aTemp.getArray()[i+1] = rTypes.getConstArray()[i];
1364 aTemp.getArray()[0].MimeType = "text/plain;charset=utf-16";
1365 aTemp.getArray()[0].DataType = cppu::UnoType<OUString>::get();
1366 rTypes = aTemp;
1368 std::vector< Atom > aNativeTemp( nNewFlavors );
1369 for( i = 0; i < nNewFlavors-1; i++ )
1370 aNativeTemp[ i + 1 ] = aNativeTypes[ i ];
1371 aNativeTemp[0] = None;
1372 aNativeTypes = aNativeTemp;
1377 osl::MutexGuard aGuard(m_aMutex);
1379 it = m_aSelections.find( selection );
1380 if( it != m_aSelections.end() )
1382 if( bSuccess )
1384 it->second->m_aTypes = rTypes;
1385 it->second->m_aNativeTypes = aNativeTypes;
1386 it->second->m_nLastTimestamp = time( nullptr );
1387 it->second->m_bHaveUTF16 = bHaveUTF16;
1388 it->second->m_aUTF8Type = aUTF8Type;
1389 it->second->m_bHaveCompound = bHaveCompound;
1391 else
1393 it->second->m_aTypes = Sequence< DataFlavor >();
1394 it->second->m_aNativeTypes = std::vector< Atom >();
1395 it->second->m_nLastTimestamp = 0;
1396 it->second->m_bHaveUTF16 = false;
1397 it->second->m_aUTF8Type = None;
1398 it->second->m_bHaveCompound = false;
1403 #if OSL_DEBUG_LEVEL > 1
1405 SAL_INFO("vcl.unx.dtrans", "SelectionManager::getPasteDataTypes( "
1406 << getString( selection )
1407 << " ) = "
1408 << (bSuccess ? "true" : "false"));
1409 for( int i = 0; i < rTypes.getLength(); i++ )
1410 SAL_INFO("vcl.unx.dtrans", "type: " << rTypes.getConstArray()[i].MimeType);
1412 #endif
1414 return bSuccess;
1417 PixmapHolder* SelectionManager::getPixmapHolder( Atom selection )
1419 std::unordered_map< Atom, Selection* >::const_iterator it = m_aSelections.find( selection );
1420 if( it == m_aSelections.end() )
1421 return nullptr;
1422 if( ! it->second->m_pPixmap )
1423 it->second->m_pPixmap = new PixmapHolder( m_pDisplay );
1424 return it->second->m_pPixmap;
1427 static std::size_t GetTrueFormatSize(int nFormat)
1429 // http://mail.gnome.org/archives/wm-spec-list/2003-March/msg00067.html
1430 return nFormat == 32 ? sizeof(long) : nFormat/8;
1433 bool SelectionManager::sendData( SelectionAdaptor* pAdaptor,
1434 ::Window requestor,
1435 Atom target,
1436 Atom property,
1437 Atom selection )
1439 osl::ResettableMutexGuard aGuard( m_aMutex );
1441 // handle targets related to image/bmp
1442 if( target == XA_COLORMAP || target == XA_PIXMAP || target == XA_BITMAP || target == XA_VISUALID )
1444 PixmapHolder* pPixmap = getPixmapHolder( selection );
1445 if( ! pPixmap ) return false;
1446 XID nValue = None;
1448 // handle colormap request
1449 if( target == XA_COLORMAP )
1450 nValue = static_cast<XID>(pPixmap->getColormap());
1451 else if( target == XA_VISUALID )
1452 nValue = static_cast<XID>(pPixmap->getVisualID());
1453 else if( target == XA_PIXMAP || target == XA_BITMAP )
1455 nValue = static_cast<XID>(pPixmap->getPixmap());
1456 if( nValue == None )
1458 // first conversion
1459 Sequence< sal_Int8 > aData;
1460 int nFormat;
1461 aGuard.clear();
1462 bool bConverted = convertData( pAdaptor->getTransferable(), target, selection, nFormat, aData );
1463 aGuard.reset();
1464 if( bConverted )
1466 // get pixmap again since clearing the guard could have invalidated
1467 // the pixmap in another thread
1468 pPixmap = getPixmapHolder( selection );
1469 // conversion succeeded, so aData contains image/bmp now
1470 if( pPixmap->needsConversion( reinterpret_cast<const sal_uInt8*>(aData.getConstArray()) ) )
1472 SAL_INFO( "vcl.unx.dtrans", "trying bitmap conversion" );
1473 int depth = pPixmap->getDepth();
1474 aGuard.clear();
1475 aData = convertBitmapDepth(aData, depth);
1476 aGuard.reset();
1478 // get pixmap again since clearing the guard could have invalidated
1479 // the pixmap in another thread
1480 pPixmap = getPixmapHolder( selection );
1481 nValue = static_cast<XID>(pPixmap->setBitmapData( reinterpret_cast<const sal_uInt8*>(aData.getConstArray()) ));
1483 if( nValue == None )
1484 return false;
1486 if( target == XA_BITMAP )
1487 nValue = static_cast<XID>(pPixmap->getBitmap());
1490 XChangeProperty( m_pDisplay,
1491 requestor,
1492 property,
1493 target,
1495 PropModeReplace,
1496 reinterpret_cast<const unsigned char*>(&nValue),
1498 return true;
1502 * special target TEXT allows us to transfer
1503 * the data in an encoding of our choice
1504 * COMPOUND_TEXT will work with most applications
1506 if( target == m_nTEXTAtom )
1507 target = m_nCOMPOUNDAtom;
1509 Sequence< sal_Int8 > aData;
1510 int nFormat;
1511 aGuard.clear();
1512 bool bConverted = convertData( pAdaptor->getTransferable(), target, selection, nFormat, aData );
1513 aGuard.reset();
1514 if( bConverted )
1516 // conversion succeeded
1517 if( aData.getLength() > m_nIncrementalThreshold )
1519 #if OSL_DEBUG_LEVEL > 1
1520 SAL_INFO("vcl.unx.dtrans", "using INCR protocol.");
1521 std::unordered_map< ::Window, std::unordered_map< Atom, IncrementalTransfer > >::const_iterator win_it = m_aIncrementals.find( requestor );
1522 if( win_it != m_aIncrementals.end() )
1524 std::unordered_map< Atom, IncrementalTransfer >::const_iterator inc_it = win_it->second.find( property );
1525 if( inc_it != win_it->second.end() )
1527 const IncrementalTransfer& rInc = inc_it->second;
1528 SAL_INFO("vcl.unx.dtrans", "premature end and new start for INCR transfer for window "
1529 << std::showbase << std::hex
1530 << rInc.m_aRequestor
1531 << ", property "
1532 << getString( rInc.m_aProperty )
1533 << ", type "
1534 << getString( rInc.m_aTarget ));
1537 #endif
1539 // insert IncrementalTransfer
1540 IncrementalTransfer& rInc = m_aIncrementals[ requestor ][ property ];
1541 rInc.m_aData = aData;
1542 rInc.m_nBufferPos = 0;
1543 rInc.m_aRequestor = requestor;
1544 rInc.m_aProperty = property;
1545 rInc.m_aTarget = target;
1546 rInc.m_nFormat = nFormat;
1547 rInc.m_nTransferStartTime = time( nullptr );
1549 // use incr protocol, signal start to requestor
1550 tools::Long nMinSize = m_nIncrementalThreshold;
1551 XSelectInput( m_pDisplay, requestor, PropertyChangeMask );
1552 XChangeProperty( m_pDisplay, requestor, property,
1553 m_nINCRAtom, 32, PropModeReplace, reinterpret_cast<unsigned char*>(&nMinSize), 1 );
1554 XFlush( m_pDisplay );
1556 else
1558 std::size_t nUnitSize = GetTrueFormatSize(nFormat);
1559 XChangeProperty( m_pDisplay,
1560 requestor,
1561 property,
1562 target,
1563 nFormat,
1564 PropModeReplace,
1565 reinterpret_cast<const unsigned char*>(aData.getConstArray()),
1566 aData.getLength()/nUnitSize );
1569 #if OSL_DEBUG_LEVEL > 1
1570 else
1571 SAL_WARN("vcl.unx.dtrans", "convertData failed for type: "
1572 << convertTypeFromNative( target, selection, nFormat ));
1573 #endif
1574 return bConverted;
1577 bool SelectionManager::handleSelectionRequest( XSelectionRequestEvent& rRequest )
1579 osl::ResettableMutexGuard aGuard( m_aMutex );
1581 #if OSL_DEBUG_LEVEL > 1
1582 SAL_INFO("vcl.unx.dtrans", "handleSelectionRequest for selection "
1583 << getString( rRequest.selection )
1584 << " and target "
1585 << getString( rRequest.target ));
1586 #endif
1588 XEvent aNotify;
1590 aNotify.type = SelectionNotify;
1591 aNotify.xselection.display = rRequest.display;
1592 aNotify.xselection.send_event = True;
1593 aNotify.xselection.requestor = rRequest.requestor;
1594 aNotify.xselection.selection = rRequest.selection;
1595 aNotify.xselection.time = rRequest.time;
1596 aNotify.xselection.target = rRequest.target;
1597 aNotify.xselection.property = None;
1599 SelectionAdaptor* pAdaptor = getAdaptor( rRequest.selection );
1600 // ensure that we still own that selection
1601 if( pAdaptor &&
1602 XGetSelectionOwner( m_pDisplay, rRequest.selection ) == m_aWindow )
1604 css::uno::Reference< XTransferable > xTrans( pAdaptor->getTransferable() );
1605 if( rRequest.target == m_nTARGETSAtom )
1607 // someone requests our types
1608 if( xTrans.is() )
1610 aGuard.clear();
1611 Sequence< DataFlavor > aFlavors = xTrans->getTransferDataFlavors();
1612 aGuard.reset();
1614 ::std::list< Atom > aConversions;
1615 getNativeTypeList( aFlavors, aConversions, rRequest.selection );
1617 int i, nTypes = aConversions.size();
1618 Atom* pTypes = static_cast<Atom*>(alloca( nTypes * sizeof( Atom ) ));
1619 std::list< Atom >::const_iterator it;
1620 for( i = 0, it = aConversions.begin(); i < nTypes; i++, ++it )
1621 pTypes[i] = *it;
1622 XChangeProperty( m_pDisplay, rRequest.requestor, rRequest.property,
1623 XA_ATOM, 32, PropModeReplace, reinterpret_cast<unsigned char*>(pTypes), nTypes );
1624 aNotify.xselection.property = rRequest.property;
1625 #if OSL_DEBUG_LEVEL > 1
1626 SAL_INFO("vcl.unx.dtrans", "sending type list:");
1627 for( int k = 0; k < nTypes; k++ )
1628 SAL_INFO("vcl.unx.dtrans", " "
1629 << (pTypes[k] ? XGetAtomName( m_pDisplay, pTypes[k] ) :
1630 "<None>"));
1631 #endif
1634 else if( rRequest.target == m_nTIMESTAMPAtom )
1636 tools::Long nTimeStamp = static_cast<tools::Long>(m_aSelections[rRequest.selection]->m_nOrigTimestamp);
1637 XChangeProperty( m_pDisplay, rRequest.requestor, rRequest.property,
1638 XA_INTEGER, 32, PropModeReplace, reinterpret_cast<unsigned char*>(&nTimeStamp), 1 );
1639 aNotify.xselection.property = rRequest.property;
1640 #if OSL_DEBUG_LEVEL > 1
1641 SAL_INFO("vcl.unx.dtrans", "sending timestamp: " << (int)nTimeStamp);
1642 #endif
1644 else
1646 bool bEventSuccess = false;
1647 if( rRequest.target == m_nMULTIPLEAtom )
1649 // get all targets
1650 Atom nType = None;
1651 int nFormat = 0;
1652 unsigned long nItems = 0, nBytes = 0;
1653 unsigned char* pData = nullptr;
1655 // get number of atoms
1656 XGetWindowProperty( m_pDisplay,
1657 rRequest.requestor,
1658 rRequest.property,
1659 0, 0,
1660 False,
1661 AnyPropertyType,
1662 &nType, &nFormat,
1663 &nItems, &nBytes,
1664 &pData );
1665 if( nFormat == 32 && nBytes/4 )
1667 if( pData ) // ?? should not happen
1669 XFree( pData );
1670 pData = nullptr;
1672 XGetWindowProperty( m_pDisplay,
1673 rRequest.requestor,
1674 rRequest.property,
1675 0, nBytes/4,
1676 False,
1677 nType,
1678 &nType, &nFormat,
1679 &nItems, &nBytes,
1680 &pData );
1681 if( pData && nItems )
1683 #if OSL_DEBUG_LEVEL > 1
1684 SAL_INFO("vcl.unx.dtrans", "found "
1685 << nItems
1686 << " atoms in MULTIPLE request.");
1687 #endif
1688 bEventSuccess = true;
1689 bool bResetAtoms = false;
1690 Atom* pAtoms = reinterpret_cast<Atom*>(pData);
1691 aGuard.clear();
1692 for( unsigned long i = 0; i < nItems; i += 2 )
1694 #if OSL_DEBUG_LEVEL > 1
1695 std::ostringstream oss;
1696 oss << " "
1697 << getString( pAtoms[i] )
1698 << " => "
1699 << getString( pAtoms[i+1] )
1700 << ": ";
1701 #endif
1703 bool bSuccess = sendData( pAdaptor, rRequest.requestor, pAtoms[i], pAtoms[i+1], rRequest.selection );
1704 #if OSL_DEBUG_LEVEL > 1
1705 oss << (bSuccess ? "succeeded" : "failed");
1706 SAL_INFO("vcl.unx.dtrans", oss.str());
1707 #endif
1708 if( ! bSuccess )
1710 pAtoms[i] = None;
1711 bResetAtoms = true;
1714 aGuard.reset();
1715 if( bResetAtoms )
1716 XChangeProperty( m_pDisplay,
1717 rRequest.requestor,
1718 rRequest.property,
1719 XA_ATOM,
1721 PropModeReplace,
1722 pData,
1723 nBytes/4 );
1725 if( pData )
1726 XFree( pData );
1728 #if OSL_DEBUG_LEVEL > 1
1729 else
1731 std::ostringstream oss;
1732 oss << "could not get type list from \""
1733 << getString( rRequest.property )
1734 << "\" of type \""
1735 << getString( nType )
1736 << "\" on requestor "
1737 << std::showbase << std::hex
1738 << rRequest.requestor
1739 << ", requestor has properties:";
1741 int nProps = 0;
1742 Atom* pProps = XListProperties( m_pDisplay, rRequest.requestor, &nProps );
1743 if( pProps )
1745 for( int i = 0; i < nProps; i++ )
1746 oss << " \"" << getString( pProps[i]) << "\"";
1747 XFree( pProps );
1749 SAL_INFO("vcl.unx.dtrans", oss.str());
1751 #endif
1753 else
1755 aGuard.clear();
1756 bEventSuccess = sendData( pAdaptor, rRequest.requestor, rRequest.target, rRequest.property, rRequest.selection );
1757 aGuard.reset();
1759 if( bEventSuccess )
1761 aNotify.xselection.target = rRequest.target;
1762 aNotify.xselection.property = rRequest.property;
1765 aGuard.clear();
1766 xTrans.clear();
1767 aGuard.reset();
1769 XSendEvent( m_pDisplay, rRequest.requestor, False, 0, &aNotify );
1771 if( rRequest.selection == XA_PRIMARY &&
1772 m_bWaitingForPrimaryConversion &&
1773 m_xDragSourceListener.is() )
1775 DragSourceDropEvent dsde;
1776 dsde.Source = static_cast< OWeakObject* >(this);
1777 dsde.DragSourceContext = new DragSourceContext( m_aDropWindow, *this );
1778 dsde.DragSource = static_cast< XDragSource* >(this);
1779 if( aNotify.xselection.property != None )
1781 dsde.DropAction = DNDConstants::ACTION_COPY;
1782 dsde.DropSuccess = true;
1784 else
1786 dsde.DropAction = DNDConstants::ACTION_NONE;
1787 dsde.DropSuccess = false;
1789 css::uno::Reference< XDragSourceListener > xListener( m_xDragSourceListener );
1790 m_xDragSourceListener.clear();
1791 aGuard.clear();
1792 if( xListener.is() )
1793 xListener->dragDropEnd( dsde );
1796 // we handled the event in any case by answering
1797 return true;
1800 bool SelectionManager::handleReceivePropertyNotify( XPropertyEvent const & rNotify )
1802 osl::MutexGuard aGuard( m_aMutex );
1803 // data we requested arrived
1804 #if OSL_DEBUG_LEVEL > 1
1805 SAL_INFO("vcl.unx.dtrans", "handleReceivePropertyNotify for property "
1806 << getString( rNotify.atom ));
1807 #endif
1808 bool bHandled = false;
1810 std::unordered_map< Atom, Selection* >::iterator it =
1811 m_aSelections.find( rNotify.atom );
1812 if( it != m_aSelections.end() &&
1813 rNotify.state == PropertyNewValue &&
1814 ( it->second->m_eState == Selection::WaitingForResponse ||
1815 it->second->m_eState == Selection::WaitingForData ||
1816 it->second->m_eState == Selection::IncrementalTransfer
1820 // MULTIPLE requests are only complete after selection notify
1821 if( it->second->m_aRequestedType == m_nMULTIPLEAtom &&
1822 ( it->second->m_eState == Selection::WaitingForResponse ||
1823 it->second->m_eState == Selection::WaitingForData ) )
1824 return false;
1826 bHandled = true;
1828 Atom nType = None;
1829 int nFormat = 0;
1830 unsigned long nItems = 0, nBytes = 0;
1831 unsigned char* pData = nullptr;
1833 // get type and length
1834 XGetWindowProperty( m_pDisplay,
1835 rNotify.window,
1836 rNotify.atom,
1837 0, 0,
1838 False,
1839 AnyPropertyType,
1840 &nType, &nFormat,
1841 &nItems, &nBytes,
1842 &pData );
1843 #if OSL_DEBUG_LEVEL > 1
1844 SAL_INFO("vcl.unx.dtrans", "found "
1845 << nBytes
1846 << " bytes data of type "
1847 << getString( nType )
1848 << " and format "
1849 << nFormat
1850 << ", items = "
1851 << nItems);
1852 #endif
1853 if( pData )
1855 XFree( pData );
1856 pData = nullptr;
1859 if( nType == m_nINCRAtom )
1861 // start data transfer
1862 XDeleteProperty( m_pDisplay, rNotify.window, rNotify.atom );
1863 it->second->m_eState = Selection::IncrementalTransfer;
1865 else if( nType != None )
1867 XGetWindowProperty( m_pDisplay,
1868 rNotify.window,
1869 rNotify.atom,
1870 0, nBytes/4 +1,
1871 True,
1872 nType,
1873 &nType, &nFormat,
1874 &nItems, &nBytes,
1875 &pData );
1877 #if OSL_DEBUG_LEVEL > 1
1878 SAL_INFO("vcl.unx.dtrans", "read "
1879 << nItems
1880 << " items data of type "
1881 << getString( nType )
1882 << " and format "
1883 << nFormat
1884 << ", "
1885 << nBytes
1886 << " bytes left in property.");
1887 #endif
1889 std::size_t nUnitSize = GetTrueFormatSize(nFormat);
1891 if( it->second->m_eState == Selection::WaitingForData ||
1892 it->second->m_eState == Selection::WaitingForResponse )
1894 // copy data
1895 it->second->m_aData = Sequence< sal_Int8 >( reinterpret_cast<sal_Int8*>(pData), nItems*nUnitSize );
1896 it->second->m_eState = Selection::Inactive;
1897 it->second->m_aDataArrived.set();
1899 else if( it->second->m_eState == Selection::IncrementalTransfer )
1901 if( nItems )
1903 // append data
1904 Sequence< sal_Int8 > aData( it->second->m_aData.getLength() + nItems*nUnitSize );
1905 memcpy( aData.getArray(), it->second->m_aData.getArray(), it->second->m_aData.getLength() );
1906 memcpy( aData.getArray() + it->second->m_aData.getLength(), pData, nItems*nUnitSize );
1907 it->second->m_aData = aData;
1909 else
1911 it->second->m_eState = Selection::Inactive;
1912 it->second->m_aDataArrived.set();
1915 if( pData )
1916 XFree( pData );
1918 else if( it->second->m_eState == Selection::IncrementalTransfer )
1920 it->second->m_eState = Selection::Inactive;
1921 it->second->m_aDataArrived.set();
1924 return bHandled;
1927 bool SelectionManager::handleSendPropertyNotify( XPropertyEvent const & rNotify )
1929 osl::MutexGuard aGuard( m_aMutex );
1931 // ready for next part of an IncrementalTransfer
1932 #if OSL_DEBUG_LEVEL > 1
1933 SAL_INFO("vcl.unx.dtrans", "handleSendPropertyNotify for property "
1934 << getString( rNotify.atom )
1935 << " ("
1936 << (rNotify.state == PropertyNewValue ?
1937 "new value" :
1938 (rNotify.state == PropertyDelete ?
1939 "deleted" :
1940 "unknown"))
1941 << ").");
1942 #endif
1944 bool bHandled = false;
1945 // feed incrementals
1946 if( rNotify.state == PropertyDelete )
1948 auto it = m_aIncrementals.find( rNotify.window );
1949 if( it != m_aIncrementals.end() )
1951 bHandled = true;
1952 int nCurrentTime = time( nullptr );
1953 // throw out aborted transfers
1954 std::vector< Atom > aTimeouts;
1955 for (auto const& incrementalTransfer : it->second)
1957 if( (nCurrentTime - incrementalTransfer.second.m_nTransferStartTime) > (getSelectionTimeout()+2) )
1959 aTimeouts.push_back( incrementalTransfer.first );
1960 #if OSL_DEBUG_LEVEL > 1
1961 const IncrementalTransfer& rInc = incrementalTransfer.second;
1962 SAL_INFO("vcl.unx.dtrans",
1963 "timeout on INCR transfer for window "
1964 << std::showbase << std::hex
1965 << rInc.m_aRequestor
1966 << ", property "
1967 << getString( rInc.m_aProperty )
1968 << ", type "
1969 << getString( rInc.m_aTarget ));
1970 #endif
1974 for (auto const& timeout : aTimeouts)
1976 // transfer broken, might even be a new client with the
1977 // same window id
1978 it->second.erase( timeout );
1980 aTimeouts.clear();
1982 auto inc_it = it->second.find( rNotify.atom );
1983 if( inc_it != it->second.end() )
1985 IncrementalTransfer& rInc = inc_it->second;
1987 int nBytes = rInc.m_aData.getLength() - rInc.m_nBufferPos;
1988 nBytes = std::min(nBytes, m_nIncrementalThreshold);
1989 if( nBytes < 0 ) // sanity check
1990 nBytes = 0;
1991 #if OSL_DEBUG_LEVEL > 1
1992 SAL_INFO("vcl.unx.dtrans", "pushing "
1993 << nBytes
1994 << " bytes: \""
1995 << std::setw(std::min(nBytes, 32))
1996 << ((const unsigned char*)
1997 rInc.m_aData.getConstArray()+rInc.m_nBufferPos)
1998 << "\"...");
1999 #endif
2000 std::size_t nUnitSize = GetTrueFormatSize(rInc.m_nFormat);
2002 XChangeProperty( m_pDisplay,
2003 rInc.m_aRequestor,
2004 rInc.m_aProperty,
2005 rInc.m_aTarget,
2006 rInc.m_nFormat,
2007 PropModeReplace,
2008 reinterpret_cast<const unsigned char*>(rInc.m_aData.getConstArray())+rInc.m_nBufferPos,
2009 nBytes/nUnitSize );
2010 rInc.m_nBufferPos += nBytes;
2011 rInc.m_nTransferStartTime = nCurrentTime;
2013 if( nBytes == 0 ) // transfer finished
2015 #if OSL_DEBUG_LEVEL > 1
2016 SAL_INFO("vcl.unx.dtrans", "finished INCR transfer for "
2017 << "window "
2018 << std::showbase << std::hex
2019 << rInc.m_aRequestor
2020 << ", property "
2021 << getString( rInc.m_aProperty )
2022 << ", type "
2023 << getString( rInc.m_aTarget ));
2024 #endif
2025 it->second.erase( inc_it );
2029 // eventually clean up the hash map
2030 if( it->second.empty() )
2031 m_aIncrementals.erase( it );
2034 return bHandled;
2037 bool SelectionManager::handleSelectionNotify( XSelectionEvent const & rNotify )
2039 osl::MutexGuard aGuard( m_aMutex );
2041 bool bHandled = false;
2043 // notification about success/failure of one of our conversion requests
2044 #if OSL_DEBUG_LEVEL > 1
2045 SAL_INFO("vcl.unx.dtrans", "handleSelectionNotify for selection "
2046 << getString( rNotify.selection )
2047 << " and property "
2048 << (rNotify.property ? getString( rNotify.property ) : "None")
2049 << " ("
2050 << std::showbase << std::hex
2051 << rNotify.property
2052 << ").");
2053 SAL_WARN_IF(rNotify.requestor != m_aWindow &&
2054 rNotify.requestor != m_aCurrentDropWindow,
2055 "vcl.unx.dtrans", "selection notify for unknown window "
2056 << std::showbase << std::hex
2057 << rNotify.requestor);
2058 #endif
2059 std::unordered_map< Atom, Selection* >::iterator it =
2060 m_aSelections.find( rNotify.selection );
2061 if (
2062 (rNotify.requestor == m_aWindow || rNotify.requestor == m_aCurrentDropWindow) &&
2063 it != m_aSelections.end() &&
2065 (it->second->m_eState == Selection::WaitingForResponse) ||
2066 (it->second->m_eState == Selection::WaitingForData)
2070 bHandled = true;
2071 if( it->second->m_aRequestedType == m_nMULTIPLEAtom )
2073 Atom nType = None;
2074 int nFormat = 0;
2075 unsigned long nItems = 0, nBytes = 0;
2076 unsigned char* pData = nullptr;
2078 // get type and length
2079 XGetWindowProperty( m_pDisplay,
2080 rNotify.requestor,
2081 rNotify.property,
2082 0, 256,
2083 False,
2084 AnyPropertyType,
2085 &nType, &nFormat,
2086 &nItems, &nBytes,
2087 &pData );
2088 if( nBytes ) // HUGE request !!!
2090 if( pData )
2091 XFree( pData );
2092 XGetWindowProperty( m_pDisplay,
2093 rNotify.requestor,
2094 rNotify.property,
2095 0, 256+(nBytes+3)/4,
2096 False,
2097 AnyPropertyType,
2098 &nType, &nFormat,
2099 &nItems, &nBytes,
2100 &pData );
2102 it->second->m_eState = Selection::Inactive;
2103 std::size_t nUnitSize = GetTrueFormatSize(nFormat);
2104 it->second->m_aData = Sequence< sal_Int8 >(reinterpret_cast<sal_Int8*>(pData), nItems * nUnitSize);
2105 it->second->m_aDataArrived.set();
2106 if( pData )
2107 XFree( pData );
2109 // WaitingForData can actually happen; some
2110 // applications (e.g. cmdtool on Solaris) first send
2111 // a success and then cancel it. Weird !
2112 else if( rNotify.property == None )
2114 // conversion failed, stop transfer
2115 it->second->m_eState = Selection::Inactive;
2116 it->second->m_aData = Sequence< sal_Int8 >();
2117 it->second->m_aDataArrived.set();
2119 // get the bytes, by INCR if necessary
2120 else
2121 it->second->m_eState = Selection::WaitingForData;
2123 #if OSL_DEBUG_LEVEL > 1
2124 else if( it != m_aSelections.end() )
2125 SAL_WARN("vcl.unx.dtrans", "selection in state " << it->second->m_eState);
2126 #endif
2127 return bHandled;
2130 bool SelectionManager::handleDropEvent( XClientMessageEvent const & rMessage )
2132 osl::ResettableMutexGuard aGuard(m_aMutex);
2134 // handle drop related events
2135 ::Window aSource = rMessage.data.l[0];
2136 ::Window aTarget = rMessage.window;
2138 bool bHandled = false;
2140 std::unordered_map< ::Window, DropTargetEntry >::iterator it =
2141 m_aDropTargets.find( aTarget );
2143 #if OSL_DEBUG_LEVEL > 1
2144 if( rMessage.message_type == m_nXdndEnter ||
2145 rMessage.message_type == m_nXdndLeave ||
2146 rMessage.message_type == m_nXdndDrop ||
2147 rMessage.message_type == m_nXdndPosition )
2149 std::ostringstream oss;
2150 oss << "got drop event "
2151 << getString( rMessage.message_type )
2152 << ", ";
2154 if( it == m_aDropTargets.end() )
2155 oss << "but no target found.";
2156 else if( ! it->second.m_pTarget->m_bActive )
2157 oss << "but target is inactive.";
2158 else if( m_aDropEnterEvent.data.l[0] != None && (::Window)m_aDropEnterEvent.data.l[0] != aSource )
2159 oss << "but source "
2160 << std::showbase << std::hex
2161 << aSource
2162 << " is unknown (expected "
2163 << m_aDropEnterEvent.data.l[0]
2164 << " or 0).";
2165 else
2166 oss << "processing.";
2167 SAL_INFO("vcl.unx.dtrans", oss.str());
2169 #endif
2171 if( it != m_aDropTargets.end() && it->second.m_pTarget->m_bActive &&
2172 m_bDropWaitingForCompletion && m_aDropEnterEvent.data.l[0] )
2174 bHandled = true;
2175 OSL_FAIL( "someone forgot to call dropComplete ?" );
2176 // some listener forgot to call dropComplete in the last operation
2177 // let us end it now and accept the new enter event
2178 aGuard.clear();
2179 dropComplete( false, m_aCurrentDropWindow );
2180 aGuard.reset();
2183 if( it != m_aDropTargets.end() &&
2184 it->second.m_pTarget->m_bActive &&
2185 ( m_aDropEnterEvent.data.l[0] == None || ::Window(m_aDropEnterEvent.data.l[0]) == aSource )
2188 if( rMessage.message_type == m_nXdndEnter )
2190 bHandled = true;
2191 m_aDropEnterEvent = rMessage;
2192 m_bDropEnterSent = false;
2193 m_aCurrentDropWindow = aTarget;
2194 m_nCurrentProtocolVersion = m_aDropEnterEvent.data.l[1] >> 24;
2195 #if OSL_DEBUG_LEVEL > 1
2196 SAL_INFO("vcl.unx.dtrans", "received XdndEnter on "
2197 << std::showbase << std::hex
2198 << aTarget);
2199 #endif
2201 else if(
2202 rMessage.message_type == m_nXdndPosition &&
2203 aSource == ::Window(m_aDropEnterEvent.data.l[0])
2206 bHandled = true;
2207 m_nDropTime = m_nCurrentProtocolVersion > 0 ? rMessage.data.l[3] : CurrentTime;
2209 ::Window aChild;
2210 XTranslateCoordinates( m_pDisplay,
2211 it->second.m_aRootWindow,
2212 it->first,
2213 rMessage.data.l[2] >> 16,
2214 rMessage.data.l[2] & 0xffff,
2215 &m_nLastX, &m_nLastY,
2216 &aChild );
2217 #if OSL_DEBUG_LEVEL > 1
2218 SAL_INFO("vcl.unx.dtrans", "received XdndPosition on "
2219 << std::showbase << std::hex
2220 << aTarget
2221 << " ("
2222 << std::dec
2223 << m_nLastX
2224 << ", "
2225 << m_nLastY
2226 << ").");
2227 #endif
2228 DropTargetDragEnterEvent aEvent;
2229 aEvent.Source = static_cast< XDropTarget* >(it->second.m_pTarget);
2230 aEvent.Context = new DropTargetDragContext( m_aCurrentDropWindow, *this );
2231 aEvent.LocationX = m_nLastX;
2232 aEvent.LocationY = m_nLastY;
2233 aEvent.SourceActions = m_nSourceActions;
2234 if( m_nCurrentProtocolVersion < 2 )
2235 aEvent.DropAction = DNDConstants::ACTION_COPY;
2236 else if( Atom(rMessage.data.l[4]) == m_nXdndActionCopy )
2237 aEvent.DropAction = DNDConstants::ACTION_COPY;
2238 else if( Atom(rMessage.data.l[4]) == m_nXdndActionMove )
2239 aEvent.DropAction = DNDConstants::ACTION_MOVE;
2240 else if( Atom(rMessage.data.l[4]) == m_nXdndActionLink )
2241 aEvent.DropAction = DNDConstants::ACTION_LINK;
2242 else if( Atom(rMessage.data.l[4]) == m_nXdndActionAsk )
2243 // currently no interface to implement ask
2244 aEvent.DropAction = ~0;
2245 else
2246 aEvent.DropAction = DNDConstants::ACTION_NONE;
2248 m_nLastDropAction = aEvent.DropAction;
2249 if( ! m_bDropEnterSent )
2251 m_bDropEnterSent = true;
2252 aEvent.SupportedDataFlavors = m_xDropTransferable->getTransferDataFlavors();
2253 aGuard.clear();
2254 it->second->dragEnter( aEvent );
2256 else
2258 aGuard.clear();
2259 it->second->dragOver( aEvent );
2262 else if(
2263 rMessage.message_type == m_nXdndLeave &&
2264 aSource == ::Window(m_aDropEnterEvent.data.l[0])
2267 bHandled = true;
2268 #if OSL_DEBUG_LEVEL > 1
2269 SAL_INFO("vcl.unx.dtrans", "received XdndLeave on "
2270 << std::showbase << std::hex
2271 << aTarget);
2272 #endif
2273 DropTargetEvent aEvent;
2274 aEvent.Source = static_cast< XDropTarget* >(it->second.m_pTarget);
2275 m_aDropEnterEvent.data.l[0] = None;
2276 if( m_aCurrentDropWindow == aTarget )
2277 m_aCurrentDropWindow = None;
2278 m_nCurrentProtocolVersion = nXdndProtocolRevision;
2279 aGuard.clear();
2280 it->second->dragExit( aEvent );
2282 else if(
2283 rMessage.message_type == m_nXdndDrop &&
2284 aSource == ::Window(m_aDropEnterEvent.data.l[0])
2287 bHandled = true;
2288 m_nDropTime = m_nCurrentProtocolVersion > 0 ? rMessage.data.l[2] : CurrentTime;
2289 #if OSL_DEBUG_LEVEL > 1
2290 SAL_INFO("vcl.unx.dtrans", "received XdndDrop on "
2291 << std::showbase << std::hex
2292 << aTarget
2293 << " ("
2294 << m_nLastX
2295 << ", "
2296 << m_nLastY
2297 << ").");
2298 #endif
2299 if( m_bLastDropAccepted )
2301 DropTargetDropEvent aEvent;
2302 aEvent.Source = static_cast< XDropTarget* >(it->second.m_pTarget);
2303 aEvent.Context = new DropTargetDropContext( m_aCurrentDropWindow, *this );
2304 aEvent.LocationX = m_nLastX;
2305 aEvent.LocationY = m_nLastY;
2306 aEvent.DropAction = m_nLastDropAction;
2307 // there is nothing corresponding to source supported actions
2308 // every source can do link, copy and move
2309 aEvent.SourceActions= m_nLastDropAction;
2310 aEvent.Transferable = m_xDropTransferable;
2312 m_bDropWaitingForCompletion = true;
2313 aGuard.clear();
2314 it->second->drop( aEvent );
2316 else
2318 #if OSL_DEBUG_LEVEL > 1
2319 SAL_INFO("vcl.unx.dtrans", "XdndDrop canceled due to "
2320 << "m_bLastDropAccepted = false." );
2321 #endif
2322 DropTargetEvent aEvent;
2323 aEvent.Source = static_cast< XDropTarget* >(it->second.m_pTarget);
2324 aGuard.clear();
2325 it->second->dragExit( aEvent );
2326 // reset the drop status, notify source
2327 dropComplete( false, m_aCurrentDropWindow );
2331 return bHandled;
2335 * methods for XDropTargetDropContext
2338 void SelectionManager::dropComplete( bool bSuccess, ::Window aDropWindow )
2340 osl::ClearableMutexGuard aGuard(m_aMutex);
2342 if( aDropWindow == m_aCurrentDropWindow )
2344 if( m_xDragSourceListener.is() )
2346 DragSourceDropEvent dsde;
2347 dsde.Source = static_cast< OWeakObject* >(this);
2348 dsde.DragSourceContext = new DragSourceContext( m_aDropWindow, *this );
2349 dsde.DragSource = static_cast< XDragSource* >(this);
2350 dsde.DropAction = getUserDragAction();
2351 dsde.DropSuccess = bSuccess;
2352 css::uno::Reference< XDragSourceListener > xListener = m_xDragSourceListener;
2353 m_xDragSourceListener.clear();
2355 aGuard.clear();
2356 xListener->dragDropEnd( dsde );
2358 else if( m_aDropEnterEvent.data.l[0] && m_aCurrentDropWindow )
2360 XEvent aEvent;
2361 aEvent.xclient.type = ClientMessage;
2362 aEvent.xclient.display = m_pDisplay;
2363 aEvent.xclient.window = m_aDropEnterEvent.data.l[0];
2364 aEvent.xclient.message_type = m_nXdndFinished;
2365 aEvent.xclient.format = 32;
2366 aEvent.xclient.data.l[0] = m_aCurrentDropWindow;
2367 aEvent.xclient.data.l[1] = bSuccess ? 1 : 0;
2368 aEvent.xclient.data.l[2] = 0;
2369 aEvent.xclient.data.l[3] = 0;
2370 aEvent.xclient.data.l[4] = 0;
2371 if( bSuccess )
2373 if( m_nLastDropAction & DNDConstants::ACTION_MOVE )
2374 aEvent.xclient.data.l[2] = m_nXdndActionMove;
2375 else if( m_nLastDropAction & DNDConstants::ACTION_COPY )
2376 aEvent.xclient.data.l[2] = m_nXdndActionCopy;
2377 else if( m_nLastDropAction & DNDConstants::ACTION_LINK )
2378 aEvent.xclient.data.l[2] = m_nXdndActionLink;
2380 #if OSL_DEBUG_LEVEL > 1
2381 SAL_INFO("vcl.unx.dtrans", "Sending XdndFinished to "
2382 << std::showbase << std::hex
2383 << m_aDropEnterEvent.data.l[0]);
2384 #endif
2385 XSendEvent( m_pDisplay, m_aDropEnterEvent.data.l[0],
2386 False, NoEventMask, & aEvent );
2388 m_aDropEnterEvent.data.l[0] = None;
2389 m_aCurrentDropWindow = None;
2390 m_nCurrentProtocolVersion = nXdndProtocolRevision;
2392 m_bDropWaitingForCompletion = false;
2394 else
2395 OSL_FAIL( "dropComplete from invalid DropTargetDropContext" );
2399 * methods for XDropTargetDragContext
2402 void SelectionManager::sendDragStatus( Atom nDropAction )
2404 osl::ClearableMutexGuard aGuard(m_aMutex);
2406 if( m_xDragSourceListener.is() )
2408 sal_Int8 nNewDragAction;
2409 if( nDropAction == m_nXdndActionMove )
2410 nNewDragAction = DNDConstants::ACTION_MOVE;
2411 else if( nDropAction == m_nXdndActionCopy )
2412 nNewDragAction = DNDConstants::ACTION_COPY;
2413 else if( nDropAction == m_nXdndActionLink )
2414 nNewDragAction = DNDConstants::ACTION_LINK;
2415 else
2416 nNewDragAction = DNDConstants::ACTION_NONE;
2417 nNewDragAction &= m_nSourceActions;
2419 if( nNewDragAction != m_nTargetAcceptAction )
2421 setCursor( getDefaultCursor( nNewDragAction ), m_aDropWindow );
2422 m_nTargetAcceptAction = nNewDragAction;
2425 DragSourceDragEvent dsde;
2426 dsde.Source = static_cast< OWeakObject* >(this);
2427 dsde.DragSourceContext = new DragSourceContext( m_aDropWindow, *this );
2428 dsde.DragSource = static_cast< XDragSource* >(this);
2429 dsde.DropAction = m_nSourceActions;
2430 dsde.UserAction = getUserDragAction();
2432 css::uno::Reference< XDragSourceListener > xListener( m_xDragSourceListener );
2433 // caution: do not change anything after this
2434 aGuard.clear();
2435 if( xListener.is() )
2436 xListener->dragOver( dsde );
2438 else if( m_aDropEnterEvent.data.l[0] && m_aCurrentDropWindow )
2440 XEvent aEvent;
2441 aEvent.xclient.type = ClientMessage;
2442 aEvent.xclient.display = m_pDisplay;
2443 aEvent.xclient.window = m_aDropEnterEvent.data.l[0];
2444 aEvent.xclient.message_type = m_nXdndStatus;
2445 aEvent.xclient.format = 32;
2446 aEvent.xclient.data.l[0] = m_aCurrentDropWindow;
2447 aEvent.xclient.data.l[1] = 2;
2448 if( nDropAction == m_nXdndActionMove ||
2449 nDropAction == m_nXdndActionLink ||
2450 nDropAction == m_nXdndActionCopy )
2451 aEvent.xclient.data.l[1] |= 1;
2452 aEvent.xclient.data.l[2] = 0;
2453 aEvent.xclient.data.l[3] = 0;
2454 aEvent.xclient.data.l[4] = m_nCurrentProtocolVersion > 1 ? nDropAction : 0;
2456 #if OSL_DEBUG_LEVEL > 1
2457 SAL_INFO("vcl.unx.dtrans", "Sending XdndStatus to "
2458 << std::showbase << std::hex
2459 << m_aDropEnterEvent.data.l[0]
2460 << " with action "
2461 << getString( nDropAction ));
2462 #endif
2464 XSendEvent( m_pDisplay, m_aDropEnterEvent.data.l[0],
2465 False, NoEventMask, & aEvent );
2466 XFlush( m_pDisplay );
2470 sal_Int8 SelectionManager::getUserDragAction() const
2472 return (m_nTargetAcceptAction != DNDConstants::ACTION_DEFAULT) ? m_nTargetAcceptAction : m_nUserDragAction;
2475 bool SelectionManager::updateDragAction( int modifierState )
2477 bool bRet = false;
2479 sal_Int8 nNewDropAction = DNDConstants::ACTION_MOVE;
2480 if( ( modifierState & ShiftMask ) && ! ( modifierState & ControlMask ) )
2481 nNewDropAction = DNDConstants::ACTION_MOVE;
2482 else if( ( modifierState & ControlMask ) && ! ( modifierState & ShiftMask ) )
2483 nNewDropAction = DNDConstants::ACTION_COPY;
2484 else if( ( modifierState & ShiftMask ) && ( modifierState & ControlMask ) )
2485 nNewDropAction = DNDConstants::ACTION_LINK;
2486 if( m_nCurrentProtocolVersion < 0 && m_aDropWindow != None )
2487 nNewDropAction = DNDConstants::ACTION_COPY;
2488 nNewDropAction &= m_nSourceActions;
2490 if( ! ( modifierState & ( ControlMask | ShiftMask ) ) )
2492 if( ! nNewDropAction )
2494 // default to an action so the user does not have to press
2495 // keys explicitly
2496 if( m_nSourceActions & DNDConstants::ACTION_MOVE )
2497 nNewDropAction = DNDConstants::ACTION_MOVE;
2498 else if( m_nSourceActions & DNDConstants::ACTION_COPY )
2499 nNewDropAction = DNDConstants::ACTION_COPY;
2500 else if( m_nSourceActions & DNDConstants::ACTION_LINK )
2501 nNewDropAction = DNDConstants::ACTION_LINK;
2503 nNewDropAction |= DNDConstants::ACTION_DEFAULT;
2506 if( nNewDropAction != m_nUserDragAction || m_nTargetAcceptAction != DNDConstants::ACTION_DEFAULT )
2508 #if OSL_DEBUG_LEVEL > 1
2509 SAL_INFO("vcl.unx.dtrans", "updateDragAction: "
2510 << std::hex
2511 << (int)m_nUserDragAction
2512 << " -> "
2513 << (int)nNewDropAction);
2514 #endif
2515 bRet = true;
2516 m_nUserDragAction = nNewDropAction;
2518 DragSourceDragEvent dsde;
2519 dsde.Source = static_cast< OWeakObject* >(this);
2520 dsde.DragSourceContext = new DragSourceContext( m_aDropWindow, *this );
2521 dsde.DragSource = static_cast< XDragSource* >(this);
2522 dsde.DropAction = m_nUserDragAction;
2523 dsde.UserAction = m_nUserDragAction;
2524 m_nTargetAcceptAction = DNDConstants::ACTION_DEFAULT; // invalidate last accept
2525 m_xDragSourceListener->dropActionChanged( dsde );
2527 return bRet;
2530 void SelectionManager::sendDropPosition( bool bForce, Time eventTime )
2532 osl::ClearableMutexGuard aGuard(m_aMutex);
2534 if( m_bDropSent )
2535 return;
2537 std::unordered_map< ::Window, DropTargetEntry >::const_iterator it =
2538 m_aDropTargets.find( m_aDropWindow );
2539 if( it != m_aDropTargets.end() )
2541 if( it->second.m_pTarget->m_bActive )
2543 int x, y;
2544 ::Window aChild;
2545 XTranslateCoordinates( m_pDisplay, it->second.m_aRootWindow, m_aDropWindow, m_nLastDragX, m_nLastDragY, &x, &y, &aChild );
2546 DropTargetDragEvent dtde;
2547 dtde.Source = static_cast< OWeakObject* >(it->second.m_pTarget );
2548 dtde.Context = new DropTargetDragContext( m_aCurrentDropWindow, *this );
2549 dtde.LocationX = x;
2550 dtde.LocationY = y;
2551 dtde.DropAction = getUserDragAction();
2552 dtde.SourceActions = m_nSourceActions;
2553 aGuard.clear();
2554 it->second->dragOver( dtde );
2557 else if( bForce ||
2559 m_nLastDragX < m_nNoPosX || m_nLastDragX >= m_nNoPosX+m_nNoPosWidth ||
2560 m_nLastDragY < m_nNoPosY || m_nLastDragY >= m_nNoPosY+m_nNoPosHeight
2563 // send XdndPosition
2564 XEvent aEvent;
2565 aEvent.type = ClientMessage;
2566 aEvent.xclient.display = m_pDisplay;
2567 aEvent.xclient.format = 32;
2568 aEvent.xclient.message_type = m_nXdndPosition;
2569 aEvent.xclient.window = m_aDropWindow;
2570 aEvent.xclient.data.l[0] = m_aWindow;
2571 aEvent.xclient.data.l[1] = 0;
2572 aEvent.xclient.data.l[2] = m_nLastDragX << 16 | (m_nLastDragY&0xffff);
2573 aEvent.xclient.data.l[3] = eventTime;
2575 if( m_nUserDragAction & DNDConstants::ACTION_COPY )
2576 aEvent.xclient.data.l[4]=m_nXdndActionCopy;
2577 else if( m_nUserDragAction & DNDConstants::ACTION_MOVE )
2578 aEvent.xclient.data.l[4]=m_nXdndActionMove;
2579 else if( m_nUserDragAction & DNDConstants::ACTION_LINK )
2580 aEvent.xclient.data.l[4]=m_nXdndActionLink;
2581 else
2582 aEvent.xclient.data.l[4]=m_nXdndActionCopy;
2583 XSendEvent( m_pDisplay, m_aDropProxy, False, NoEventMask, &aEvent );
2584 m_nNoPosX = m_nNoPosY = m_nNoPosWidth = m_nNoPosHeight = 0;
2588 bool SelectionManager::handleDragEvent( XEvent const & rMessage )
2590 if( ! m_xDragSourceListener.is() )
2591 return false;
2593 osl::ResettableMutexGuard aGuard(m_aMutex);
2595 bool bHandled = false;
2597 // for shortcut
2598 std::unordered_map< ::Window, DropTargetEntry >::const_iterator it =
2599 m_aDropTargets.find( m_aDropWindow );
2601 #if OSL_DEBUG_LEVEL > 1
2602 switch( rMessage.type )
2604 case ClientMessage:
2605 SAL_INFO("vcl.unx.dtrans", "handleDragEvent: "
2606 << getString( rMessage.xclient.message_type ));
2607 break;
2608 case MotionNotify:
2609 break;
2610 case EnterNotify:
2611 SAL_INFO("vcl.unx.dtrans", "handleDragEvent: EnterNotify.");
2612 break;
2613 case LeaveNotify:
2614 SAL_INFO("vcl.unx.dtrans", "handleDragEvent: LeaveNotify.");
2615 break;
2616 case ButtonPress:
2617 SAL_INFO("vcl.unx.dtrans", "handleDragEvent: ButtonPress "
2618 << rMessage.xbutton.button
2619 << " (m_nDragButton = "
2620 << m_nDragButton
2621 << ").");
2622 break;
2623 case ButtonRelease:
2624 SAL_INFO("vcl.unx.dtrans", "handleDragEvent: ButtonRelease "
2625 << rMessage.xbutton.button
2626 << " (m_nDragButton = "
2627 << m_nDragButton
2628 << ").");
2629 break;
2630 case KeyPress:
2631 SAL_INFO("vcl.unx.dtrans", "handleDragEvent: KeyPress.");
2632 break;
2633 case KeyRelease:
2634 SAL_INFO("vcl.unx.dtrans", "handleDragEvent: KeyRelease.");
2635 break;
2636 default:
2637 SAL_INFO("vcl.unx.dtrans", "handleDragEvent: <unknown type "
2638 << rMessage.type
2639 << ">.");
2640 break;
2642 #endif
2644 // handle drag related events
2645 if( rMessage.type == ClientMessage )
2647 if( rMessage.xclient.message_type == m_nXdndStatus && Atom(rMessage.xclient.data.l[0]) == m_aDropWindow )
2649 bHandled = true;
2650 DragSourceDragEvent dsde;
2651 dsde.Source = static_cast< OWeakObject* >(this);
2652 dsde.DragSourceContext = new DragSourceContext( m_aDropWindow, *this );
2653 dsde.DragSource = static_cast< XDragSource* >( this );
2654 dsde.UserAction = getUserDragAction();
2655 dsde.DropAction = DNDConstants::ACTION_NONE;
2656 m_bDropSuccess = (rMessage.xclient.data.l[1] & 1) != 0;
2657 #if OSL_DEBUG_LEVEL > 1
2658 SAL_INFO("vcl.unx.dtrans", "status drop action: accept = "
2659 << (m_bDropSuccess ? "true" : "false")
2660 << ", "
2661 << getString( rMessage.xclient.data.l[4] ));
2662 #endif
2663 if( rMessage.xclient.data.l[1] & 1 )
2665 if( m_nCurrentProtocolVersion > 1 )
2667 if( Atom(rMessage.xclient.data.l[4]) == m_nXdndActionCopy )
2668 dsde.DropAction = DNDConstants::ACTION_COPY;
2669 else if( Atom(rMessage.xclient.data.l[4]) == m_nXdndActionMove )
2670 dsde.DropAction = DNDConstants::ACTION_MOVE;
2671 else if( Atom(rMessage.xclient.data.l[4]) == m_nXdndActionLink )
2672 dsde.DropAction = DNDConstants::ACTION_LINK;
2674 else
2675 dsde.DropAction = DNDConstants::ACTION_COPY;
2677 m_nTargetAcceptAction = dsde.DropAction;
2679 if( ! ( rMessage.xclient.data.l[1] & 2 ) )
2681 m_nNoPosX = rMessage.xclient.data.l[2] >> 16;
2682 m_nNoPosY = rMessage.xclient.data.l[2] & 0xffff;
2683 m_nNoPosWidth = rMessage.xclient.data.l[3] >> 16;
2684 m_nNoPosHeight = rMessage.xclient.data.l[3] & 0xffff;
2686 else
2687 m_nNoPosX = m_nNoPosY = m_nNoPosWidth = m_nNoPosHeight = 0;
2689 setCursor( getDefaultCursor( dsde.DropAction ), m_aDropWindow );
2690 aGuard.clear();
2691 m_xDragSourceListener->dragOver( dsde );
2693 else if( rMessage.xclient.message_type == m_nXdndFinished && m_aDropWindow == Atom(rMessage.xclient.data.l[0]) )
2695 bHandled = true;
2696 // notify the listener
2697 DragSourceDropEvent dsde;
2698 dsde.Source = static_cast< OWeakObject* >(this);
2699 dsde.DragSourceContext = new DragSourceContext( m_aDropWindow, *this );
2700 dsde.DragSource = static_cast< XDragSource* >(this);
2701 dsde.DropAction = m_nTargetAcceptAction;
2702 dsde.DropSuccess = m_bDropSuccess;
2703 css::uno::Reference< XDragSourceListener > xListener( m_xDragSourceListener );
2704 m_xDragSourceListener.clear();
2705 aGuard.clear();
2706 xListener->dragDropEnd( dsde );
2709 else if( rMessage.type == MotionNotify ||
2710 rMessage.type == EnterNotify || rMessage.type == LeaveNotify
2713 bHandled = true;
2714 bool bForce = false;
2715 int root_x = rMessage.type == MotionNotify ? rMessage.xmotion.x_root : rMessage.xcrossing.x_root;
2716 int root_y = rMessage.type == MotionNotify ? rMessage.xmotion.y_root : rMessage.xcrossing.y_root;
2717 ::Window root = rMessage.type == MotionNotify ? rMessage.xmotion.root : rMessage.xcrossing.root;
2719 aGuard.clear();
2720 if( rMessage.type == MotionNotify )
2722 bForce = updateDragAction( rMessage.xmotion.state );
2724 updateDragWindow( root_x, root_y, root );
2725 aGuard.reset();
2727 if( m_nCurrentProtocolVersion >= 0 && m_aDropProxy != None )
2729 aGuard.clear();
2730 sendDropPosition( bForce, rMessage.type == MotionNotify ? rMessage.xmotion.time : rMessage.xcrossing.time );
2733 else if( rMessage.type == KeyPress || rMessage.type == KeyRelease )
2735 bHandled = true;
2736 KeySym aKey = XkbKeycodeToKeysym( m_pDisplay, rMessage.xkey.keycode, 0, 0 );
2737 if( aKey == XK_Escape )
2739 // abort drag
2740 if( it != m_aDropTargets.end() )
2742 DropTargetEvent dte;
2743 dte.Source = static_cast< OWeakObject* >( it->second.m_pTarget );
2744 aGuard.clear();
2745 it->second.m_pTarget->dragExit( dte );
2746 aGuard.reset();
2748 else if( m_aDropProxy != None && m_nCurrentProtocolVersion >= 0 )
2750 // send XdndLeave
2751 XEvent aEvent;
2752 aEvent.type = ClientMessage;
2753 aEvent.xclient.display = m_pDisplay;
2754 aEvent.xclient.format = 32;
2755 aEvent.xclient.message_type = m_nXdndLeave;
2756 aEvent.xclient.window = m_aDropWindow;
2757 aEvent.xclient.data.l[0] = m_aWindow;
2758 memset( aEvent.xclient.data.l+1, 0, sizeof(long)*4);
2759 m_aDropWindow = m_aDropProxy = None;
2760 XSendEvent( m_pDisplay, m_aDropProxy, False, NoEventMask, &aEvent );
2762 // notify the listener
2763 DragSourceDropEvent dsde;
2764 dsde.Source = static_cast< OWeakObject* >(this);
2765 dsde.DragSourceContext = new DragSourceContext( m_aDropWindow, *this );
2766 dsde.DragSource = static_cast< XDragSource* >(this);
2767 dsde.DropAction = DNDConstants::ACTION_NONE;
2768 dsde.DropSuccess = false;
2769 css::uno::Reference< XDragSourceListener > xListener( m_xDragSourceListener );
2770 m_xDragSourceListener.clear();
2771 aGuard.clear();
2772 xListener->dragDropEnd( dsde );
2774 else
2777 * man page says: state is state immediate PRIOR to the
2778 * event. It would seem that this is a somewhat arguable
2779 * design decision.
2781 int nState = rMessage.xkey.state;
2782 int nNewState = 0;
2783 switch( aKey )
2785 case XK_Shift_R:
2786 case XK_Shift_L: nNewState = ShiftMask;break;
2787 case XK_Control_R:
2788 case XK_Control_L: nNewState = ControlMask;break;
2789 // just interested in shift and ctrl for dnd
2791 if( rMessage.type == KeyPress )
2792 nState |= nNewState;
2793 else
2794 nState &= ~nNewState;
2795 aGuard.clear();
2796 if( updateDragAction( nState ) )
2797 sendDropPosition( true, rMessage.xkey.time );
2800 else if(
2801 ( rMessage.type == ButtonPress || rMessage.type == ButtonRelease ) &&
2802 rMessage.xbutton.button == m_nDragButton )
2804 bool bCancel = true;
2805 if( m_aDropWindow != None )
2807 if( it != m_aDropTargets.end() )
2809 if( it->second.m_pTarget->m_bActive && m_nUserDragAction != DNDConstants::ACTION_NONE && m_bLastDropAccepted )
2811 bHandled = true;
2812 int x, y;
2813 ::Window aChild;
2814 XTranslateCoordinates( m_pDisplay, rMessage.xbutton.root, m_aDropWindow, rMessage.xbutton.x_root, rMessage.xbutton.y_root, &x, &y, &aChild );
2815 DropTargetDropEvent dtde;
2816 dtde.Source = static_cast< OWeakObject* >(it->second.m_pTarget );
2817 dtde.Context = new DropTargetDropContext( m_aCurrentDropWindow, *this );
2818 dtde.LocationX = x;
2819 dtde.LocationY = y;
2820 dtde.DropAction = m_nUserDragAction;
2821 dtde.SourceActions = m_nSourceActions;
2822 dtde.Transferable = m_xDragSourceTransferable;
2823 m_bDropSent = true;
2824 m_nDropTimeout = time( nullptr );
2825 m_bDropWaitingForCompletion = true;
2826 aGuard.clear();
2827 it->second->drop( dtde );
2828 bCancel = false;
2830 else bCancel = true;
2832 else if( m_nCurrentProtocolVersion >= 0 )
2834 bHandled = true;
2836 XEvent aEvent;
2837 aEvent.type = ClientMessage;
2838 aEvent.xclient.display = m_pDisplay;
2839 aEvent.xclient.format = 32;
2840 aEvent.xclient.message_type = m_nXdndDrop;
2841 aEvent.xclient.window = m_aDropWindow;
2842 aEvent.xclient.data.l[0] = m_aWindow;
2843 aEvent.xclient.data.l[1] = 0;
2844 aEvent.xclient.data.l[2] = rMessage.xbutton.time;
2845 aEvent.xclient.data.l[3] = 0;
2846 aEvent.xclient.data.l[4] = 0;
2848 m_bDropSent = true;
2849 m_nDropTimeout = time( nullptr );
2850 XSendEvent( m_pDisplay, m_aDropProxy, False, NoEventMask, &aEvent );
2851 bCancel = false;
2853 else
2855 // dropping on non XdndWindows: acquire ownership of
2856 // PRIMARY and send a middle mouse button click down/up to
2857 // target window
2858 SelectionAdaptor* pAdaptor = getAdaptor( XA_PRIMARY );
2859 if( pAdaptor )
2861 bHandled = true;
2863 ::Window aDummy;
2864 XEvent aEvent;
2865 aEvent.type = ButtonPress;
2866 aEvent.xbutton.display = m_pDisplay;
2867 aEvent.xbutton.window = m_aDropWindow;
2868 aEvent.xbutton.root = rMessage.xbutton.root;
2869 aEvent.xbutton.subwindow = m_aDropWindow;
2870 aEvent.xbutton.time = rMessage.xbutton.time+1;
2871 aEvent.xbutton.x_root = rMessage.xbutton.x_root;
2872 aEvent.xbutton.y_root = rMessage.xbutton.y_root;
2873 aEvent.xbutton.state = rMessage.xbutton.state;
2874 aEvent.xbutton.button = Button2;
2875 aEvent.xbutton.same_screen = True;
2876 XTranslateCoordinates( m_pDisplay,
2877 rMessage.xbutton.root, m_aDropWindow,
2878 rMessage.xbutton.x_root, rMessage.xbutton.y_root,
2879 &aEvent.xbutton.x, &aEvent.xbutton.y,
2880 &aDummy );
2881 XSendEvent( m_pDisplay, m_aDropWindow, False, ButtonPressMask, &aEvent );
2882 aEvent.xbutton.type = ButtonRelease;
2883 aEvent.xbutton.time++;
2884 aEvent.xbutton.state |= Button2Mask;
2885 XSendEvent( m_pDisplay, m_aDropWindow, False, ButtonReleaseMask, &aEvent );
2887 m_bDropSent = true;
2888 m_nDropTimeout = time( nullptr );
2889 XSendEvent( m_pDisplay, m_aDropProxy, False, NoEventMask, &aEvent );
2890 m_bWaitingForPrimaryConversion = true;
2891 m_bDropSent = true;
2892 m_nDropTimeout = time( nullptr );
2893 // HACK :-)
2894 aGuard.clear();
2895 static_cast< X11Clipboard* >( pAdaptor )->setContents( m_xDragSourceTransferable, css::uno::Reference< css::datatransfer::clipboard::XClipboardOwner >() );
2896 aGuard.reset();
2897 bCancel = false;
2901 if( bCancel )
2903 // cancel drag
2904 DragSourceDropEvent dsde;
2905 dsde.Source = static_cast< OWeakObject* >(this);
2906 dsde.DragSourceContext = new DragSourceContext( m_aDropWindow, *this );
2907 dsde.DragSource = static_cast< XDragSource* >(this);
2908 dsde.DropAction = DNDConstants::ACTION_NONE;
2909 dsde.DropSuccess = false;
2910 css::uno::Reference< XDragSourceListener > xListener( m_xDragSourceListener );
2911 m_xDragSourceListener.clear();
2912 aGuard.clear();
2913 xListener->dragDropEnd( dsde );
2914 bHandled = true;
2917 return bHandled;
2920 void SelectionManager::accept( sal_Int8 dragOperation, ::Window aDropWindow )
2922 if( aDropWindow != m_aCurrentDropWindow )
2923 return;
2925 #if OSL_DEBUG_LEVEL > 1
2926 SAL_INFO("vcl.unx.dtrans", "accept: " << std::hex << dragOperation);
2927 #endif
2928 Atom nAction = None;
2929 dragOperation &= (DNDConstants::ACTION_MOVE | DNDConstants::ACTION_COPY | DNDConstants::ACTION_LINK);
2930 if( dragOperation & DNDConstants::ACTION_MOVE )
2931 nAction = m_nXdndActionMove;
2932 else if( dragOperation & DNDConstants::ACTION_COPY )
2933 nAction = m_nXdndActionCopy;
2934 else if( dragOperation & DNDConstants::ACTION_LINK )
2935 nAction = m_nXdndActionLink;
2936 m_bLastDropAccepted = true;
2937 sendDragStatus( nAction );
2940 void SelectionManager::reject( ::Window aDropWindow )
2942 if( aDropWindow != m_aCurrentDropWindow )
2943 return;
2945 #if OSL_DEBUG_LEVEL > 1
2946 SAL_INFO("vcl.unx.dtrans", "reject.");
2947 #endif
2948 m_bLastDropAccepted = false;
2949 sendDragStatus( None );
2950 if( m_bDropSent && m_xDragSourceListener.is() )
2952 DragSourceDropEvent dsde;
2953 dsde.Source = static_cast< OWeakObject* >(this);
2954 dsde.DragSourceContext = new DragSourceContext( m_aDropWindow, *this );
2955 dsde.DragSource = static_cast< XDragSource* >(this);
2956 dsde.DropAction = DNDConstants::ACTION_NONE;
2957 dsde.DropSuccess = false;
2958 m_xDragSourceListener->dragDropEnd( dsde );
2959 m_xDragSourceListener.clear();
2964 * XDragSource
2967 sal_Bool SelectionManager::isDragImageSupported()
2969 return false;
2972 sal_Int32 SelectionManager::getDefaultCursor( sal_Int8 dragAction )
2974 Cursor aCursor = m_aNoneCursor;
2975 if( dragAction & DNDConstants::ACTION_MOVE )
2976 aCursor = m_aMoveCursor;
2977 else if( dragAction & DNDConstants::ACTION_COPY )
2978 aCursor = m_aCopyCursor;
2979 else if( dragAction & DNDConstants::ACTION_LINK )
2980 aCursor = m_aLinkCursor;
2981 return aCursor;
2984 int SelectionManager::getXdndVersion( ::Window aWindow, ::Window& rProxy )
2986 Atom* pProperties = nullptr;
2987 int nProperties = 0;
2988 Atom nType;
2989 int nFormat;
2990 unsigned long nItems, nBytes;
2991 unsigned char* pBytes = nullptr;
2993 int nVersion = -1;
2994 rProxy = None;
2997 * XListProperties is used here to avoid unnecessary XGetWindowProperty calls
2998 * and therefore reducing latency penalty
3000 pProperties = XListProperties( m_pDisplay, aWindow, &nProperties );
3001 // first look for proxy
3002 int i;
3003 for( i = 0; i < nProperties; i++ )
3005 if( pProperties[i] == m_nXdndProxy )
3007 XGetWindowProperty( m_pDisplay, aWindow, m_nXdndProxy, 0, 1, False, XA_WINDOW,
3008 &nType, &nFormat, &nItems, &nBytes, &pBytes );
3009 if( pBytes )
3011 if( nType == XA_WINDOW )
3012 rProxy = *reinterpret_cast< ::Window* >(pBytes);
3013 XFree( pBytes );
3014 pBytes = nullptr;
3015 if( rProxy != None )
3017 // now check proxy whether it points to itself
3018 XGetWindowProperty( m_pDisplay, rProxy, m_nXdndProxy, 0, 1, False, XA_WINDOW,
3019 &nType, &nFormat, &nItems, &nBytes, &pBytes );
3020 if( pBytes )
3022 if( nType == XA_WINDOW && *reinterpret_cast< ::Window* >(pBytes) != rProxy )
3023 rProxy = None;
3024 XFree( pBytes );
3025 pBytes = nullptr;
3027 else
3028 rProxy = None;
3031 break;
3034 if ( pProperties )
3035 XFree (pProperties);
3037 ::Window aAwareWindow = rProxy != None ? rProxy : aWindow;
3039 XGetWindowProperty( m_pDisplay, aAwareWindow, m_nXdndAware, 0, 1, False, XA_ATOM,
3040 &nType, &nFormat, &nItems, &nBytes, &pBytes );
3041 if( pBytes )
3043 if( nType == XA_ATOM )
3044 nVersion = *reinterpret_cast<Atom*>(pBytes);
3045 XFree( pBytes );
3048 nVersion = std::min<int>(nVersion, nXdndProtocolRevision);
3050 return nVersion;
3053 void SelectionManager::updateDragWindow( int nX, int nY, ::Window aRoot )
3055 osl::ResettableMutexGuard aGuard( m_aMutex );
3057 css::uno::Reference< XDragSourceListener > xListener( m_xDragSourceListener );
3059 m_nLastDragX = nX;
3060 m_nLastDragY = nY;
3062 ::Window aParent = aRoot;
3063 ::Window aChild;
3064 ::Window aNewProxy = None, aNewCurrentWindow = None;
3065 int nNewProtocolVersion = -1;
3066 int nWinX, nWinY;
3068 // find the first XdndAware window or check if root window is
3069 // XdndAware or has XdndProxy
3072 XTranslateCoordinates( m_pDisplay, aRoot, aParent, nX, nY, &nWinX, &nWinY, &aChild );
3073 if( aChild != None )
3075 if( aChild == m_aCurrentDropWindow && aChild != aRoot && m_nCurrentProtocolVersion >= 0 )
3077 aParent = aChild;
3078 break;
3080 nNewProtocolVersion = getXdndVersion( aChild, aNewProxy );
3081 aParent = aChild;
3083 } while( aChild != None && nNewProtocolVersion < 0 );
3085 aNewCurrentWindow = aParent;
3086 if( aNewCurrentWindow == aRoot )
3088 // no children, try root drop
3089 nNewProtocolVersion = getXdndVersion( aNewCurrentWindow, aNewProxy );
3090 if( nNewProtocolVersion < 3 )
3092 aNewCurrentWindow = aNewProxy = None;
3093 nNewProtocolVersion = nXdndProtocolRevision;
3097 DragSourceDragEvent dsde;
3098 dsde.Source = static_cast< OWeakObject* >(this);
3099 dsde.DragSourceContext = new DragSourceContext( m_aDropWindow, *this );
3100 dsde.DragSource = static_cast< XDragSource* >(this);
3101 dsde.DropAction = nNewProtocolVersion >= 0 ? m_nUserDragAction : DNDConstants::ACTION_COPY;
3102 dsde.UserAction = nNewProtocolVersion >= 0 ? m_nUserDragAction : DNDConstants::ACTION_COPY;
3104 std::unordered_map< ::Window, DropTargetEntry >::const_iterator it;
3105 if( aNewCurrentWindow != m_aDropWindow )
3107 #if OSL_DEBUG_LEVEL > 1
3108 SAL_INFO("vcl.unx.dtrans", "drag left window "
3109 << std::showbase << std::hex
3110 << m_aDropWindow
3111 << std::dec
3112 << " (rev. "
3113 << m_nCurrentProtocolVersion
3114 << "), entered window "
3115 << std::showbase << std::hex
3116 << aNewCurrentWindow
3117 << " (rev "
3118 << std::dec
3119 << nNewProtocolVersion
3120 << ").");
3121 #endif
3122 if( m_aDropWindow != None )
3124 it = m_aDropTargets.find( m_aDropWindow );
3125 if( it != m_aDropTargets.end() )
3126 // shortcut for own drop targets
3128 DropTargetEvent dte;
3129 dte.Source = static_cast< OWeakObject* >( it->second.m_pTarget );
3130 aGuard.clear();
3131 it->second.m_pTarget->dragExit( dte );
3132 aGuard.reset();
3134 else
3136 // send old drop target a XdndLeave
3137 XEvent aEvent;
3138 aEvent.type = ClientMessage;
3139 aEvent.xclient.display = m_pDisplay;
3140 aEvent.xclient.format = 32;
3141 aEvent.xclient.message_type = m_nXdndLeave;
3142 aEvent.xclient.window = m_aDropWindow;
3143 aEvent.xclient.data.l[0] = m_aWindow;
3144 aEvent.xclient.data.l[1] = 0;
3145 XSendEvent( m_pDisplay, m_aDropProxy, False, NoEventMask, &aEvent );
3147 if( xListener.is() )
3149 aGuard.clear();
3150 xListener->dragExit( dsde );
3151 aGuard.reset();
3155 m_nCurrentProtocolVersion = nNewProtocolVersion;
3156 m_aDropWindow = aNewCurrentWindow;
3157 m_aDropProxy = aNewProxy != None ? aNewProxy : m_aDropWindow;
3159 it = m_aDropTargets.find( m_aDropWindow );
3160 if( it != m_aDropTargets.end() && ! it->second.m_pTarget->m_bActive )
3161 m_aDropProxy = None;
3163 if( m_aDropProxy != None && xListener.is() )
3165 aGuard.clear();
3166 xListener->dragEnter( dsde );
3167 aGuard.reset();
3169 // send XdndEnter
3170 if( m_aDropProxy != None && m_nCurrentProtocolVersion >= 0 )
3172 it = m_aDropTargets.find( m_aDropWindow );
3173 if( it != m_aDropTargets.end() )
3175 XTranslateCoordinates( m_pDisplay, aRoot, m_aDropWindow, nX, nY, &nWinX, &nWinY, &aChild );
3176 DropTargetDragEnterEvent dtde;
3177 dtde.Source = static_cast< OWeakObject* >( it->second.m_pTarget );
3178 dtde.Context = new DropTargetDragContext( m_aCurrentDropWindow, *this );
3179 dtde.LocationX = nWinX;
3180 dtde.LocationY = nWinY;
3181 dtde.DropAction = m_nUserDragAction;
3182 dtde.SourceActions = m_nSourceActions;
3183 dtde.SupportedDataFlavors = m_xDragSourceTransferable->getTransferDataFlavors();
3184 aGuard.clear();
3185 it->second.m_pTarget->dragEnter( dtde );
3186 aGuard.reset();
3188 else
3190 XEvent aEvent;
3191 aEvent.type = ClientMessage;
3192 aEvent.xclient.display = m_pDisplay;
3193 aEvent.xclient.format = 32;
3194 aEvent.xclient.message_type = m_nXdndEnter;
3195 aEvent.xclient.window = m_aDropWindow;
3196 aEvent.xclient.data.l[0] = m_aWindow;
3197 aEvent.xclient.data.l[1] = m_nCurrentProtocolVersion << 24;
3198 memset( aEvent.xclient.data.l + 2, 0, sizeof( long )*3 );
3199 // fill in data types
3200 ::std::list< Atom > aConversions;
3201 getNativeTypeList( m_aDragFlavors, aConversions, m_nXdndSelection );
3202 if( aConversions.size() > 3 )
3203 aEvent.xclient.data.l[1] |= 1;
3204 ::std::list< Atom >::const_iterator type_it = aConversions.begin();
3205 for( int i = 0; type_it != aConversions.end() && i < 3; i++, ++type_it )
3206 aEvent.xclient.data.l[i+2] = *type_it;
3207 XSendEvent( m_pDisplay, m_aDropProxy, False, NoEventMask, &aEvent );
3210 m_nNoPosX = m_nNoPosY = m_nNoPosWidth = m_nNoPosHeight = 0;
3212 else if( m_aDropProxy != None && xListener.is() )
3214 aGuard.clear();
3215 // drag over for XdndAware windows comes when receiving XdndStatus
3216 xListener->dragOver( dsde );
3220 void SelectionManager::startDrag(
3221 const DragGestureEvent& trigger,
3222 sal_Int8 sourceActions,
3223 sal_Int32,
3224 sal_Int32,
3225 const css::uno::Reference< XTransferable >& transferable,
3226 const css::uno::Reference< XDragSourceListener >& listener
3229 #if OSL_DEBUG_LEVEL > 1
3230 SAL_INFO("vcl.unx.dtrans", "startDrag( sourceActions = "
3231 << std::hex
3232 << (int)sourceActions
3233 << " ).");
3234 #endif
3235 DragSourceDropEvent aDragFailedEvent;
3236 aDragFailedEvent.Source = static_cast< OWeakObject* >(this);
3237 aDragFailedEvent.DragSource = static_cast< XDragSource* >(this);
3238 aDragFailedEvent.DragSourceContext = new DragSourceContext( None, *this );
3239 aDragFailedEvent.DropAction = DNDConstants::ACTION_NONE;
3240 aDragFailedEvent.DropSuccess = false;
3242 if( m_aDragRunning.check() )
3244 if( listener.is() )
3245 listener->dragDropEnd( aDragFailedEvent );
3247 #if OSL_DEBUG_LEVEL > 1
3248 SAL_WARN("vcl.unx.dtrans",
3249 "*** ERROR *** second drag and drop started.");
3250 if( m_xDragSourceListener.is() )
3251 SAL_WARN("vcl.unx.dtrans",
3252 "*** ERROR *** drag source listener already set.");
3253 else
3254 SAL_WARN("vcl.unx.dtrans",
3255 "*** ERROR *** drag thread already running.");
3256 #endif
3257 return;
3260 SalFrame* pCaptureFrame = nullptr;
3263 osl::ClearableMutexGuard aGuard(m_aMutex);
3265 // first get the current pointer position and the window that
3266 // the pointer is located in. since said window should be one
3267 // of our DropTargets at the time of executeDrag we can use
3268 // them for a start
3269 ::Window aRoot, aParent, aChild;
3270 int root_x(0), root_y(0), win_x(0), win_y(0);
3271 unsigned int mask(0);
3273 bool bPointerFound = false;
3274 for (auto const& dropTarget : m_aDropTargets)
3276 if( XQueryPointer( m_pDisplay, dropTarget.second.m_aRootWindow,
3277 &aRoot, &aParent,
3278 &root_x, &root_y,
3279 &win_x, &win_y,
3280 &mask ) )
3282 aParent = dropTarget.second.m_aRootWindow;
3283 aRoot = aParent;
3284 bPointerFound = true;
3285 break;
3289 // don't start DnD if there is none of our windows on the same screen as
3290 // the pointer or if no mouse button is pressed
3291 if( !bPointerFound || (mask & (Button1Mask|Button2Mask|Button3Mask)) == 0 )
3293 aGuard.clear();
3294 if( listener.is() )
3295 listener->dragDropEnd( aDragFailedEvent );
3296 return;
3299 // try to find which of our drop targets is the drag source
3300 // if that drop target is deregistered we should stop executing
3301 // the drag (actually this is a poor substitute for an "endDrag"
3302 // method ).
3303 m_aDragSourceWindow = None;
3306 XTranslateCoordinates( m_pDisplay, aRoot, aParent, root_x, root_y, &win_x, &win_y, &aChild );
3307 if( aChild != None && m_aDropTargets.find( aChild ) != m_aDropTargets.end() )
3309 m_aDragSourceWindow = aChild;
3310 #if OSL_DEBUG_LEVEL > 1
3311 SAL_INFO("vcl.unx.dtrans", "found drag source window "
3312 << std::showbase << std::hex
3313 << m_aDragSourceWindow);
3314 #endif
3315 break;
3317 aParent = aChild;
3318 } while( aChild != None );
3320 #if OSL_DEBUG_LEVEL > 1
3321 SAL_INFO("vcl.unx.dtrans", "try to grab pointer ...");
3322 #endif
3323 int nPointerGrabSuccess =
3324 XGrabPointer( m_pDisplay, aRoot, True,
3325 DRAG_EVENT_MASK,
3326 GrabModeAsync, GrabModeAsync,
3327 None,
3328 None,
3329 CurrentTime );
3330 /* if we could not grab the pointer here, there is a chance
3331 that the pointer is grabbed by the other vcl display (the main loop)
3332 so let's break that grab and reset it later
3334 remark: this whole code should really be molten into normal vcl so only
3335 one display is used...
3337 if( nPointerGrabSuccess != GrabSuccess )
3339 comphelper::SolarMutex& rSolarMutex( Application::GetSolarMutex() );
3340 if( rSolarMutex.tryToAcquire() )
3342 pCaptureFrame = vcl_sal::getSalDisplay(GetGenericUnixSalData())->GetCaptureFrame();
3343 if( pCaptureFrame )
3345 vcl_sal::getSalDisplay(GetGenericUnixSalData())->CaptureMouse( nullptr );
3346 nPointerGrabSuccess =
3347 XGrabPointer( m_pDisplay, aRoot, True,
3348 DRAG_EVENT_MASK,
3349 GrabModeAsync, GrabModeAsync,
3350 None,
3351 None,
3352 CurrentTime );
3356 #if OSL_DEBUG_LEVEL > 1
3357 SAL_INFO("vcl.unx.dtrans", "... grabbed pointer: "
3358 << nPointerGrabSuccess);
3359 SAL_INFO("vcl.unx.dtrans", "try to grab keyboard ...");
3360 #endif
3361 int nKeyboardGrabSuccess =
3362 XGrabKeyboard( m_pDisplay, aRoot, True,
3363 GrabModeAsync, GrabModeAsync, CurrentTime );
3364 #if OSL_DEBUG_LEVEL > 1
3365 SAL_INFO("vcl.unx.dtrans", "... grabbed keyboard: "
3366 << nKeyboardGrabSuccess);
3367 #endif
3368 if( nPointerGrabSuccess != GrabSuccess || nKeyboardGrabSuccess != GrabSuccess )
3370 if( nPointerGrabSuccess == GrabSuccess )
3371 XUngrabPointer( m_pDisplay, CurrentTime );
3372 if( nKeyboardGrabSuccess == GrabSuccess )
3373 XUngrabKeyboard( m_pDisplay, CurrentTime );
3374 XFlush( m_pDisplay );
3375 aGuard.clear();
3376 if( listener.is() )
3377 listener->dragDropEnd( aDragFailedEvent );
3378 if( pCaptureFrame )
3380 comphelper::SolarMutex& rSolarMutex( Application::GetSolarMutex() );
3381 if( rSolarMutex.tryToAcquire() )
3382 vcl_sal::getSalDisplay(GetGenericUnixSalData())->CaptureMouse( pCaptureFrame );
3383 #if OSL_DEBUG_LEVEL > 0
3384 else
3385 OSL_FAIL( "failed to acquire SolarMutex to reset capture frame" );
3386 #endif
3388 return;
3391 m_xDragSourceTransferable = transferable;
3392 m_xDragSourceListener = listener;
3393 m_aDragFlavors = transferable->getTransferDataFlavors();
3394 m_aCurrentCursor = None;
3396 requestOwnership( m_nXdndSelection );
3398 ::std::list< Atom > aConversions;
3399 getNativeTypeList( m_aDragFlavors, aConversions, m_nXdndSelection );
3401 Atom* pTypes = static_cast<Atom*>(alloca( sizeof(Atom)*aConversions.size() ));
3402 int nTypes = 0;
3403 for (auto const& conversion : aConversions)
3404 pTypes[nTypes++] = conversion;
3406 XChangeProperty( m_pDisplay, m_aWindow, m_nXdndTypeList, XA_ATOM, 32, PropModeReplace, reinterpret_cast<unsigned char*>(pTypes), nTypes );
3408 m_nSourceActions = sourceActions | DNDConstants::ACTION_DEFAULT;
3409 m_nUserDragAction = DNDConstants::ACTION_MOVE & m_nSourceActions;
3410 if( ! m_nUserDragAction )
3411 m_nUserDragAction = DNDConstants::ACTION_COPY & m_nSourceActions;
3412 if( ! m_nUserDragAction )
3413 m_nUserDragAction = DNDConstants::ACTION_LINK & m_nSourceActions;
3414 m_nTargetAcceptAction = DNDConstants::ACTION_DEFAULT;
3415 m_bDropSent = false;
3416 m_bDropSuccess = false;
3417 m_bWaitingForPrimaryConversion = false;
3418 m_nDragButton = Button1; // default to left button
3419 css::awt::MouseEvent aEvent;
3420 if( trigger.Event >>= aEvent )
3422 if( aEvent.Buttons & MouseButton::LEFT )
3423 m_nDragButton = Button1;
3424 else if( aEvent.Buttons & MouseButton::RIGHT )
3425 m_nDragButton = Button3;
3426 else if( aEvent.Buttons & MouseButton::MIDDLE )
3427 m_nDragButton = Button2;
3429 #if OSL_DEBUG_LEVEL > 1
3430 SAL_INFO("vcl.unx.dtrans", "m_nUserDragAction = "
3431 << std::hex
3432 << (int)m_nUserDragAction);
3433 #endif
3434 updateDragWindow( root_x, root_y, aRoot );
3435 m_nUserDragAction = ~0;
3436 updateDragAction( mask );
3439 m_aDragRunning.set();
3440 m_aDragExecuteThread = osl_createSuspendedThread( call_SelectionManager_runDragExecute, this );
3441 if( m_aDragExecuteThread )
3442 osl_resumeThread( m_aDragExecuteThread );
3443 else
3445 #if OSL_DEBUG_LEVEL > 1
3446 SAL_INFO("vcl.unx.dtrans", "osl_createSuspendedThread failed "
3447 << "for drag execute.");
3448 #endif
3449 m_xDragSourceListener.clear();
3450 m_xDragSourceTransferable.clear();
3452 m_bDropSent = false;
3453 m_bDropSuccess = false;
3454 m_bWaitingForPrimaryConversion = false;
3455 m_aDropWindow = None;
3456 m_aDropProxy = None;
3457 m_nCurrentProtocolVersion = nXdndProtocolRevision;
3458 m_nNoPosX = 0;
3459 m_nNoPosY = 0;
3460 m_nNoPosWidth = 0;
3461 m_nNoPosHeight = 0;
3462 m_aCurrentCursor = None;
3464 XUngrabPointer( m_pDisplay, CurrentTime );
3465 XUngrabKeyboard( m_pDisplay, CurrentTime );
3466 XFlush( m_pDisplay );
3468 if( pCaptureFrame )
3470 comphelper::SolarMutex& rSolarMutex( Application::GetSolarMutex() );
3471 if( rSolarMutex.tryToAcquire() )
3472 vcl_sal::getSalDisplay(GetGenericUnixSalData())->CaptureMouse( pCaptureFrame );
3473 #if OSL_DEBUG_LEVEL > 0
3474 else
3475 OSL_FAIL( "failed to acquire SolarMutex to reset capture frame" );
3476 #endif
3479 m_aDragRunning.reset();
3481 if( listener.is() )
3482 listener->dragDropEnd( aDragFailedEvent );
3486 void SelectionManager::runDragExecute( void* pThis )
3488 SelectionManager* This = static_cast<SelectionManager*>(pThis);
3489 This->dragDoDispatch();
3492 void SelectionManager::dragDoDispatch()
3495 // do drag
3496 // m_xDragSourceListener will be cleared on finished drop
3497 #if OSL_DEBUG_LEVEL > 1
3498 SAL_INFO("vcl.unx.dtrans", "begin executeDrag dispatching.");
3499 #endif
3500 oslThread aThread = m_aDragExecuteThread;
3501 while( m_xDragSourceListener.is() && ( ! m_bDropSent || time(nullptr)-m_nDropTimeout < 5 ) && osl_scheduleThread( aThread ) )
3503 // let the thread in the run method do the dispatching
3504 // just look occasionally here whether drop timed out or is completed
3505 osl::Thread::wait(std::chrono::milliseconds(200));
3507 #if OSL_DEBUG_LEVEL > 1
3508 SAL_INFO("vcl.unx.dtrans", "end executeDrag dispatching.");
3509 #endif
3511 osl::ClearableMutexGuard aGuard(m_aMutex);
3513 css::uno::Reference< XDragSourceListener > xListener( m_xDragSourceListener );
3514 css::uno::Reference< XTransferable > xTransferable( m_xDragSourceTransferable );
3515 m_xDragSourceListener.clear();
3516 m_xDragSourceTransferable.clear();
3518 DragSourceDropEvent dsde;
3519 dsde.Source = static_cast< OWeakObject* >(this);
3520 dsde.DragSourceContext = new DragSourceContext( m_aDropWindow, *this );
3521 dsde.DragSource = static_cast< XDragSource* >(this);
3522 dsde.DropAction = DNDConstants::ACTION_NONE;
3523 dsde.DropSuccess = false;
3525 // cleanup after drag
3526 if( m_bWaitingForPrimaryConversion )
3528 SelectionAdaptor* pAdaptor = getAdaptor( XA_PRIMARY );
3529 if (pAdaptor)
3530 pAdaptor->clearTransferable();
3533 m_bDropSent = false;
3534 m_bDropSuccess = false;
3535 m_bWaitingForPrimaryConversion = false;
3536 m_aDropWindow = None;
3537 m_aDropProxy = None;
3538 m_nCurrentProtocolVersion = nXdndProtocolRevision;
3539 m_nNoPosX = 0;
3540 m_nNoPosY = 0;
3541 m_nNoPosWidth = 0;
3542 m_nNoPosHeight = 0;
3543 m_aCurrentCursor = None;
3545 XUngrabPointer( m_pDisplay, CurrentTime );
3546 XUngrabKeyboard( m_pDisplay, CurrentTime );
3547 XFlush( m_pDisplay );
3549 m_aDragExecuteThread = nullptr;
3550 m_aDragRunning.reset();
3552 aGuard.clear();
3553 if( xListener.is() )
3555 xTransferable.clear();
3556 xListener->dragDropEnd( dsde );
3559 osl_destroyThread( aThread );
3563 * XDragSourceContext
3567 void SelectionManager::setCursor( sal_Int32 cursor, ::Window aDropWindow )
3569 osl::MutexGuard aGuard( m_aMutex );
3570 if( aDropWindow == m_aDropWindow && Cursor(cursor) != m_aCurrentCursor )
3572 if( m_xDragSourceListener.is() && ! m_bDropSent )
3574 m_aCurrentCursor = cursor;
3575 XChangeActivePointerGrab( m_pDisplay, DRAG_EVENT_MASK, cursor, CurrentTime );
3576 XFlush( m_pDisplay );
3581 void SelectionManager::transferablesFlavorsChanged()
3583 osl::MutexGuard aGuard(m_aMutex);
3585 m_aDragFlavors = m_xDragSourceTransferable->getTransferDataFlavors();
3587 std::list< Atom > aConversions;
3589 getNativeTypeList( m_aDragFlavors, aConversions, m_nXdndSelection );
3591 Atom* pTypes = static_cast<Atom*>(alloca( sizeof(Atom)*aConversions.size() ));
3592 int nTypes = 0;
3593 for (auto const& conversion : aConversions)
3594 pTypes[nTypes++] = conversion;
3595 XChangeProperty( m_pDisplay, m_aWindow, m_nXdndTypeList, XA_ATOM, 32, PropModeReplace, reinterpret_cast<unsigned char*>(pTypes), nTypes );
3597 if( m_aCurrentDropWindow == None || m_nCurrentProtocolVersion < 0 )
3598 return;
3600 // send synthetic leave and enter events
3602 XEvent aEvent;
3604 aEvent.type = ClientMessage;
3605 aEvent.xclient.display = m_pDisplay;
3606 aEvent.xclient.format = 32;
3607 aEvent.xclient.window = m_aDropWindow;
3608 aEvent.xclient.data.l[0] = m_aWindow;
3610 aEvent.xclient.message_type = m_nXdndLeave;
3611 aEvent.xclient.data.l[1] = 0;
3612 XSendEvent( m_pDisplay, m_aDropProxy, False, NoEventMask, &aEvent );
3614 aEvent.xclient.message_type = m_nXdndEnter;
3615 aEvent.xclient.data.l[1] = m_nCurrentProtocolVersion << 24;
3616 memset( aEvent.xclient.data.l + 2, 0, sizeof( long )*3 );
3617 // fill in data types
3618 if( nTypes > 3 )
3619 aEvent.xclient.data.l[1] |= 1;
3620 for( int j = 0; j < nTypes && j < 3; j++ )
3621 aEvent.xclient.data.l[j+2] = pTypes[j];
3623 XSendEvent( m_pDisplay, m_aDropProxy, False, NoEventMask, &aEvent );
3628 * dispatch loop
3631 bool SelectionManager::handleXEvent( XEvent& rEvent )
3634 * since we are XConnectionListener to a second X display
3635 * to get client messages it is essential not to dispatch
3636 * events twice that we get on both connections
3638 * between dispatching ButtonPress and startDrag
3639 * the user can already have released the mouse. The ButtonRelease
3640 * will then be dispatched in VCLs queue and never turn up here.
3641 * Which is not so good, since startDrag will XGrabPointer and
3642 * XGrabKeyboard -> solid lock.
3644 if( rEvent.xany.display != m_pDisplay
3645 && rEvent.type != ClientMessage
3646 && rEvent.type != ButtonPress
3647 && rEvent.type != ButtonRelease
3649 return false;
3651 bool bHandled = false;
3652 switch (rEvent.type)
3654 case SelectionClear:
3656 osl::ClearableMutexGuard aGuard(m_aMutex);
3657 #if OSL_DEBUG_LEVEL > 1
3658 SAL_INFO("vcl.unx.dtrans", "SelectionClear for selection "
3659 << getString( rEvent.xselectionclear.selection ));
3660 #endif
3661 SelectionAdaptor* pAdaptor = getAdaptor( rEvent.xselectionclear.selection );
3662 std::unordered_map< Atom, Selection* >::iterator it( m_aSelections.find( rEvent.xselectionclear.selection ) );
3663 if( it != m_aSelections.end() )
3664 it->second->m_bOwner = false;
3665 aGuard.clear();
3666 if ( pAdaptor )
3667 pAdaptor->clearTransferable();
3669 break;
3671 case SelectionRequest:
3672 bHandled = handleSelectionRequest( rEvent.xselectionrequest );
3673 break;
3674 case PropertyNotify:
3675 if( rEvent.xproperty.window == m_aWindow ||
3676 rEvent.xproperty.window == m_aCurrentDropWindow
3678 bHandled = handleReceivePropertyNotify( rEvent.xproperty );
3679 else
3680 bHandled = handleSendPropertyNotify( rEvent.xproperty );
3681 break;
3682 case SelectionNotify:
3683 bHandled = handleSelectionNotify( rEvent.xselection );
3684 break;
3685 case ClientMessage:
3686 // messages from drag target
3687 if( rEvent.xclient.message_type == m_nXdndStatus ||
3688 rEvent.xclient.message_type == m_nXdndFinished )
3689 bHandled = handleDragEvent( rEvent );
3690 // messages from drag source
3691 else if(
3692 rEvent.xclient.message_type == m_nXdndEnter ||
3693 rEvent.xclient.message_type == m_nXdndLeave ||
3694 rEvent.xclient.message_type == m_nXdndPosition ||
3695 rEvent.xclient.message_type == m_nXdndDrop
3697 bHandled = handleDropEvent( rEvent.xclient );
3698 break;
3699 case EnterNotify:
3700 case LeaveNotify:
3701 case MotionNotify:
3702 case ButtonPress:
3703 case ButtonRelease:
3704 case KeyPress:
3705 case KeyRelease:
3706 bHandled = handleDragEvent( rEvent );
3707 break;
3708 default:
3711 return bHandled;
3714 void SelectionManager::dispatchEvent( int millisec )
3716 // acquire the mutex to prevent other threads
3717 // from using the same X connection
3718 osl::ResettableMutexGuard aGuard(m_aMutex);
3720 if( !XPending( m_pDisplay ))
3722 int nfds = 1;
3723 // wait for any events if none are already queued
3724 pollfd aPollFD[2];
3725 aPollFD[0].fd = XConnectionNumber( m_pDisplay );
3726 aPollFD[0].events = POLLIN;
3727 aPollFD[0].revents = 0;
3729 // on infinite timeout we need endthreadpipe monitoring too
3730 if (millisec < 0)
3732 aPollFD[1].fd = m_EndThreadPipe[0];
3733 aPollFD[1].events = POLLIN | POLLERR;
3734 aPollFD[1].revents = 0;
3735 nfds = 2;
3738 // release mutex for the time of waiting for possible data
3739 aGuard.clear();
3740 if( poll( aPollFD, nfds, millisec ) <= 0 )
3741 return;
3742 aGuard.reset();
3744 while( XPending( m_pDisplay ))
3746 XEvent event;
3747 XNextEvent( m_pDisplay, &event );
3748 aGuard.clear();
3749 handleXEvent( event );
3750 aGuard.reset();
3754 void SelectionManager::run( void* pThis )
3756 #if OSL_DEBUG_LEVEL > 1
3757 SAL_INFO("vcl.unx.dtrans", "SelectionManager::run.");
3758 #endif
3759 osl::Thread::setName("SelectionManager");
3760 // dispatch until the cows come home
3762 SelectionManager* This = static_cast<SelectionManager*>(pThis);
3764 timeval aLast;
3765 gettimeofday( &aLast, nullptr );
3767 css::uno::Reference< XComponentContext > xContext( ::comphelper::getProcessComponentContext() );
3768 This->m_xDesktop.set( Desktop::create(xContext) );
3769 This->m_xDesktop->addTerminateListener(This);
3771 // if end thread pipe properly initialized, allow infinite wait in poll
3772 // otherwise, fallback on 1 sec timeout
3773 const int timeout = (This->m_EndThreadPipe[0] != This->m_EndThreadPipe[1]) ? -1 : 1000;
3775 while( osl_scheduleThread(This->m_aThread) )
3777 This->dispatchEvent( timeout );
3779 timeval aNow;
3780 gettimeofday( &aNow, nullptr );
3782 if( (aNow.tv_sec - aLast.tv_sec) > 0 )
3784 osl::ClearableMutexGuard aGuard(This->m_aMutex);
3785 std::vector< std::pair< SelectionAdaptor*, css::uno::Reference< XInterface > > > aChangeVector;
3787 for (auto const& selection : This->m_aSelections)
3789 if( selection.first != This->m_nXdndSelection && ! selection.second->m_bOwner )
3791 ::Window aOwner = XGetSelectionOwner( This->m_pDisplay, selection.first );
3792 if( aOwner != selection.second->m_aLastOwner )
3794 selection.second->m_aLastOwner = aOwner;
3795 std::pair< SelectionAdaptor*, css::uno::Reference< XInterface > >
3796 aKeep( selection.second->m_pAdaptor, selection.second->m_pAdaptor->getReference() );
3797 aChangeVector.push_back( aKeep );
3801 aGuard.clear();
3802 for (auto const& change : aChangeVector)
3804 change.first->fireContentsChanged();
3806 aLast = aNow;
3809 // close write end on exit so write() fails and other thread does not block
3810 // forever
3811 close(This->m_EndThreadPipe[1]);
3812 close(This->m_EndThreadPipe[0]);
3813 #if OSL_DEBUG_LEVEL > 1
3814 SAL_INFO("vcl.unx.dtrans", "SelectionManager::run end.");
3815 #endif
3818 void SelectionManager::shutdown() throw()
3820 #if OSL_DEBUG_LEVEL > 1
3821 SAL_INFO("vcl.unx.dtrans", "SelectionManager got app termination event.");
3822 #endif
3824 osl::ResettableMutexGuard aGuard(m_aMutex);
3826 if( m_bShutDown )
3827 return;
3828 m_bShutDown = true;
3830 if ( m_xDesktop.is() )
3831 m_xDesktop->removeTerminateListener(this);
3833 if( m_xDisplayConnection.is() )
3834 m_xDisplayConnection->removeEventHandler(Any(), this);
3836 // stop dispatching
3837 if( m_aThread )
3839 osl_terminateThread( m_aThread );
3841 * Allow thread to finish before app exits to avoid pulling the carpet
3842 * out from under it if pasting is occurring during shutdown
3844 * a) allow it to have the Mutex and
3845 * b) reschedule to allow it to complete callbacks to any
3846 * Application::GetSolarMutex protected regions, etc. e.g.
3847 * TransferableHelper::getTransferDataFlavors (via
3848 * SelectionManager::handleSelectionRequest) which it might
3849 * currently be trying to enter.
3851 * Otherwise the thread may be left still waiting on a GlobalMutex
3852 * when that gets destroyed, letting the thread blow up and die
3853 * when enters the section in a now dead OOo instance.
3855 aGuard.clear();
3856 while (osl_isThreadRunning(m_aThread))
3858 { // drop mutex before write - otherwise may deadlock
3859 SolarMutexGuard guard2;
3860 Application::Reschedule();
3862 // trigger poll()'s wait end by writing a dummy value
3863 char dummy=0;
3864 dummy = write(m_EndThreadPipe[1], &dummy, 1);
3866 osl_joinWithThread( m_aThread );
3867 osl_destroyThread( m_aThread );
3868 m_aThread = nullptr;
3869 aGuard.reset();
3871 m_xDesktop.clear();
3872 m_xDisplayConnection.clear();
3873 m_xDropTransferable.clear();
3876 sal_Bool SelectionManager::handleEvent(const Any& event)
3878 Sequence< sal_Int8 > aSeq;
3879 if( event >>= aSeq )
3881 XEvent* pEvent = reinterpret_cast<XEvent*>(aSeq.getArray());
3882 Time nTimestamp = CurrentTime;
3883 if( pEvent->type == ButtonPress || pEvent->type == ButtonRelease )
3884 nTimestamp = pEvent->xbutton.time;
3885 else if( pEvent->type == KeyPress || pEvent->type == KeyRelease )
3886 nTimestamp = pEvent->xkey.time;
3887 else if( pEvent->type == MotionNotify )
3888 nTimestamp = pEvent->xmotion.time;
3889 else if( pEvent->type == PropertyNotify )
3890 nTimestamp = pEvent->xproperty.time;
3892 if( nTimestamp != CurrentTime )
3894 osl::MutexGuard aGuard(m_aMutex);
3896 m_nSelectionTimestamp = nTimestamp;
3899 return handleXEvent( *pEvent );
3901 else
3903 #if OSL_DEBUG_LEVEL > 1
3904 SAL_INFO("vcl.unx.dtrans", "SelectionManager got downing event.");
3905 #endif
3906 shutdown();
3908 return true;
3911 void SAL_CALL SelectionManager::disposing( const css::lang::EventObject& rEvt )
3913 if (rEvt.Source == m_xDesktop || rEvt.Source == m_xDisplayConnection)
3914 shutdown();
3917 void SAL_CALL SelectionManager::queryTermination( const css::lang::EventObject& )
3922 * To be safe, shutdown needs to be called before the ~SfxApplication is called, waiting until
3923 * the downing event can be too late if paste are requested during shutdown and ~SfxApplication
3924 * has been called before vcl is shutdown
3926 void SAL_CALL SelectionManager::notifyTermination( const css::lang::EventObject& rEvent )
3928 disposing(rEvent);
3931 void SelectionManager::registerHandler( Atom selection, SelectionAdaptor& rAdaptor )
3933 osl::MutexGuard aGuard(m_aMutex);
3935 Selection* pNewSelection = new Selection();
3936 pNewSelection->m_pAdaptor = &rAdaptor;
3937 m_aSelections[ selection ] = pNewSelection;
3940 void SelectionManager::deregisterHandler( Atom selection )
3942 osl::MutexGuard aGuard(m_aMutex);
3944 std::unordered_map< Atom, Selection* >::iterator it =
3945 m_aSelections.find( selection );
3946 if( it != m_aSelections.end() )
3948 delete it->second->m_pPixmap;
3949 delete it->second;
3950 m_aSelections.erase( it );
3954 static bool bWasError = false;
3956 extern "C"
3958 static int local_xerror_handler(Display* , XErrorEvent*)
3960 bWasError = true;
3961 return 0;
3963 typedef int(*xerror_hdl_t)(Display*,XErrorEvent*);
3966 void SelectionManager::registerDropTarget( ::Window aWindow, DropTarget* pTarget )
3968 osl::MutexGuard aGuard(m_aMutex);
3970 // sanity check
3971 std::unordered_map< ::Window, DropTargetEntry >::const_iterator it =
3972 m_aDropTargets.find( aWindow );
3973 if( it != m_aDropTargets.end() )
3974 OSL_FAIL( "attempt to register window as drop target twice" );
3975 else if( aWindow && m_pDisplay )
3977 DropTargetEntry aEntry( pTarget );
3978 bWasError=false;
3979 /* #i100000# ugly workaround: gtk sets its own XErrorHandler which is not suitable for us
3980 unfortunately XErrorHandler is not per display, so this is just and ugly hack
3981 Need to remove separate display and integrate clipboard/dnd into vcl's unx code ASAP
3983 xerror_hdl_t pOldHandler = XSetErrorHandler( local_xerror_handler );
3984 XSelectInput( m_pDisplay, aWindow, PropertyChangeMask );
3985 if( ! bWasError )
3987 // set XdndAware
3988 XChangeProperty( m_pDisplay, aWindow, m_nXdndAware, XA_ATOM, 32, PropModeReplace, reinterpret_cast<unsigned char const *>(&nXdndProtocolRevision), 1 );
3989 if( ! bWasError )
3991 // get root window of window (in 99.999% of all cases this will be
3992 // DefaultRootWindow( m_pDisplay )
3993 int x, y;
3994 unsigned int w, h, bw, d;
3995 XGetGeometry( m_pDisplay, aWindow, &aEntry.m_aRootWindow,
3996 &x, &y, &w, &h, &bw, &d );
3999 XSetErrorHandler( pOldHandler );
4000 if(bWasError)
4001 return;
4002 m_aDropTargets[ aWindow ] = aEntry;
4004 else
4005 OSL_FAIL( "attempt to register None as drop target" );
4008 void SelectionManager::deregisterDropTarget( ::Window aWindow )
4010 osl::ClearableMutexGuard aGuard(m_aMutex);
4012 m_aDropTargets.erase( aWindow );
4013 if( aWindow != m_aDragSourceWindow || !m_aDragRunning.check() )
4014 return;
4016 // abort drag
4017 std::unordered_map< ::Window, DropTargetEntry >::const_iterator it =
4018 m_aDropTargets.find( m_aDropWindow );
4019 if( it != m_aDropTargets.end() )
4021 DropTargetEvent dte;
4022 dte.Source = static_cast< OWeakObject* >( it->second.m_pTarget );
4023 aGuard.clear();
4024 it->second.m_pTarget->dragExit( dte );
4026 else if( m_aDropProxy != None && m_nCurrentProtocolVersion >= 0 )
4028 // send XdndLeave
4029 XEvent aEvent;
4030 aEvent.type = ClientMessage;
4031 aEvent.xclient.display = m_pDisplay;
4032 aEvent.xclient.format = 32;
4033 aEvent.xclient.message_type = m_nXdndLeave;
4034 aEvent.xclient.window = m_aDropWindow;
4035 aEvent.xclient.data.l[0] = m_aWindow;
4036 memset( aEvent.xclient.data.l+1, 0, sizeof(long)*4);
4037 m_aDropWindow = m_aDropProxy = None;
4038 XSendEvent( m_pDisplay, m_aDropProxy, False, NoEventMask, &aEvent );
4040 // notify the listener
4041 DragSourceDropEvent dsde;
4042 dsde.Source = static_cast< OWeakObject* >(this);
4043 dsde.DragSourceContext = new DragSourceContext( m_aDropWindow, *this );
4044 dsde.DragSource = static_cast< XDragSource* >(this);
4045 dsde.DropAction = DNDConstants::ACTION_NONE;
4046 dsde.DropSuccess = false;
4047 css::uno::Reference< XDragSourceListener > xListener( m_xDragSourceListener );
4048 m_xDragSourceListener.clear();
4049 aGuard.clear();
4050 xListener->dragDropEnd( dsde );
4055 * SelectionAdaptor
4058 css::uno::Reference< XTransferable > SelectionManager::getTransferable() throw()
4060 return m_xDragSourceTransferable;
4063 void SelectionManager::clearTransferable() throw()
4065 m_xDragSourceTransferable.clear();
4068 void SelectionManager::fireContentsChanged() throw()
4072 css::uno::Reference< XInterface > SelectionManager::getReference() throw()
4074 return css::uno::Reference< XInterface >( static_cast<OWeakObject*>(this) );
4078 * SelectionManagerHolder
4081 SelectionManagerHolder::SelectionManagerHolder() :
4082 ::cppu::WeakComponentImplHelper<
4083 XDragSource,
4084 XInitialization,
4085 XServiceInfo > (m_aMutex)
4089 SelectionManagerHolder::~SelectionManagerHolder()
4093 void SelectionManagerHolder::initialize( const Sequence< Any >& arguments )
4095 OUString aDisplayName;
4097 if( arguments.hasElements() )
4099 css::uno::Reference< XDisplayConnection > xConn;
4100 arguments.getConstArray()[0] >>= xConn;
4101 if( xConn.is() )
4103 Any aIdentifier;
4104 aIdentifier >>= aDisplayName;
4108 SelectionManager& rManager = SelectionManager::get( aDisplayName );
4109 rManager.initialize( arguments );
4110 m_xRealDragSource = static_cast< XDragSource* >(&rManager);
4114 * XDragSource
4117 sal_Bool SelectionManagerHolder::isDragImageSupported()
4119 return m_xRealDragSource.is() && m_xRealDragSource->isDragImageSupported();
4122 sal_Int32 SelectionManagerHolder::getDefaultCursor( sal_Int8 dragAction )
4124 return m_xRealDragSource.is() ? m_xRealDragSource->getDefaultCursor( dragAction ) : 0;
4127 void SelectionManagerHolder::startDrag(
4128 const css::datatransfer::dnd::DragGestureEvent& trigger,
4129 sal_Int8 sourceActions, sal_Int32 cursor, sal_Int32 image,
4130 const css::uno::Reference< css::datatransfer::XTransferable >& transferable,
4131 const css::uno::Reference< css::datatransfer::dnd::XDragSourceListener >& listener
4134 if( m_xRealDragSource.is() )
4135 m_xRealDragSource->startDrag( trigger, sourceActions, cursor, image, transferable, listener );
4139 * XServiceInfo
4142 OUString SelectionManagerHolder::getImplementationName()
4144 return "com.sun.star.datatransfer.dnd.XdndSupport";
4147 sal_Bool SelectionManagerHolder::supportsService( const OUString& ServiceName )
4149 return cppu::supportsService(this, ServiceName);
4152 Sequence< OUString > SelectionManagerHolder::getSupportedServiceNames()
4154 return Xdnd_getSupportedServiceNames();
4157 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */