qt-bugs@ issue : 16115
applied: no
author: Lubos Lunak <l.lunak@kde.org>

See http://lists.kde.org/?t=104388858900001&r=1&w=2



--- src/kernel/qdnd_x11.cpp
+++ src/kernel/qdnd_x11.cpp
@@ -49,13 +49,15 @@
 #include "qdragobject.h"
 #include "qobjectlist.h"
 #include "qcursor.h"
+#include "qbitmap.h"
+#include "qpainter.h"
 
 #include "qt_x11_p.h"
 
 // conflict resolution
 
-// unused, may be used again later: const int XKeyPress = KeyPress;
-// unused, may be used again later: const int XKeyRelease = KeyRelease;
+const int XKeyPress = KeyPress;
+const int XKeyRelease = KeyRelease;
 #undef KeyPress
 #undef KeyRelease
 
@@ -253,20 +255,47 @@ class QShapedPixmapWidget : public QWidg
 public:
     QShapedPixmapWidget(int screen = -1) :
 	QWidget(QApplication::desktop()->screen( screen ),
-		0, WStyle_Customize | WStyle_Tool | WStyle_NoBorder | WX11BypassWM )
+		0, WStyle_Customize | WStyle_Tool | WStyle_NoBorder | WX11BypassWM ), oldpmser( 0 ), oldbmser( 0 )
     {
     }
 
-    void setPixmap(QPixmap pm)
+    void setPixmap(QPixmap pm, QPoint hot)
     {
-	if ( pm.mask() ) {
+	int bmser = pm.mask() ? pm.mask()->serialNumber() : 0;
+	if( oldpmser == pm.serialNumber() && oldbmser == bmser
+	    && oldhot == hot )
+	    return;
+	oldpmser = pm.serialNumber();
+	oldbmser = bmser;
+	oldhot = hot;
+	bool hotspot_in = !(hot.x() < 0 || hot.y() < 0 || hot.x() >= pm.width() || hot.y() >= pm.height());
+// if the pixmap has hotspot in its area, make a "hole" in it at that position
+// this will allow XTranslateCoordinates() to find directly the window below the cursor instead
+// of finding this pixmap, and therefore there won't be needed any (slow) search for the window
+// using findRealWindow()
+	if( hotspot_in ) {
+	    QBitmap mask = pm.mask() ? *pm.mask() : QBitmap( pm.width(), pm.height());
+	    if( !pm.mask())
+		mask.fill( Qt::color1 );
+	    QPainter p( &mask );
+	    p.setPen( Qt::color0 );
+	    p.drawPoint( hot.x(), hot.y());
+	    p.end();
+    	    pm.setMask( mask );
+    	    setMask( mask );
+	} else if ( pm.mask() ) {
 	    setMask( *pm.mask() );
 	} else {
 	    clearMask();
 	}
 	resize(pm.width(),pm.height());
 	setErasePixmap(pm);
+	erase();
     }
+private:
+    int oldpmser;
+    int oldbmser;
+    QPoint oldhot;
 };
 
 static QShapedPixmapWidget * qt_xdnd_deco = 0;
@@ -872,6 +901,45 @@ void QDragManager::timerEvent( QTimerEve
 	move( QCursor::pos() );
 }
 
+static bool qt_xdnd_was_move = false;
+static bool qt_xdnd_found = false;
+// check whole incoming X queue for move events
+// checking whole queue is done by always returning False in the predicate
+// if there's another move event in the queue, and there's not a mouse button
+// or keyboard or ClientMessage event before it, the current move event
+// may be safely discarded
+// this helps avoiding being overloaded by being flooded from many events
+// from the XServer
+static
+Bool qt_xdnd_predicate( Display*, XEvent* ev, XPointer )
+{
+    if( qt_xdnd_found )
+	return False;
+    if( ev->type == MotionNotify )
+    {
+	qt_xdnd_was_move = true;
+	qt_xdnd_found = true;
+    }
+    if( ev->type == ButtonPress || ev->type == ButtonRelease
+	|| ev->type == XKeyPress || ev->type == XKeyRelease
+	|| ev->type == ClientMessage )
+    {
+	qt_xdnd_was_move = false;
+	qt_xdnd_found = true;
+    }
+    return False;
+}
+
+static
+bool qt_xdnd_another_movement()
+{
+    qt_xdnd_was_move = false;
+    qt_xdnd_found = false;
+    XEvent dummy;
+    XCheckIfEvent( qt_xdisplay(), &dummy, qt_xdnd_predicate, NULL );
+    return qt_xdnd_was_move;
+}
+
 bool QDragManager::eventFilter( QObject * o, QEvent * e)
 {
     if ( beingCancelled ) {
@@ -894,8 +962,10 @@ bool QDragManager::eventFilter( QObject 
 
     if ( e->type() == QEvent::MouseMove ) {
 	QMouseEvent* me = (QMouseEvent *)e;
-	updateMode(me->stateAfter());
-	move( me->globalPos() );
+	if( !qt_xdnd_another_movement()) {
+	    updateMode(me->stateAfter());
+	    move( me->globalPos() );
+	}
 	return TRUE;
     } else if ( e->type() == QEvent::MouseButtonRelease ) {
 	qApp->removeEventFilter( this );
@@ -1136,7 +1206,7 @@ void QDragManager::move( const QPoint & 
 	    qt_xdnd_deco->grabMouse();
 	}
     }
-    updatePixmap();
+    updatePixmap( globalPos );
 
     if ( qt_xdnd_source_sameanswer.contains( globalPos ) &&
 	 qt_xdnd_source_sameanswer.isValid() ) {
@@ -1729,7 +1799,7 @@ bool QDragManager::drag( QDragObject * o
     // qt_xdnd_source_object persists until we get an xdnd_finish message
 }
 
-void QDragManager::updatePixmap()
+void QDragManager::updatePixmap( const QPoint& cursorPos )
 {
     if ( qt_xdnd_deco ) {
 	QPixmap pm;
@@ -1744,9 +1814,8 @@ void QDragManager::updatePixmap()
 		defaultPm = new QPixmap(default_pm);
 	    pm = *defaultPm;
 	}
-	qt_xdnd_deco->setPixmap(pm);
-	qt_xdnd_deco->move(QCursor::pos()-pm_hot);
-	qt_xdnd_deco->repaint(FALSE);
+	qt_xdnd_deco->setPixmap(pm, pm_hot);
+	qt_xdnd_deco->move(cursorPos-pm_hot);
 	    //if ( willDrop ) {
 	    qt_xdnd_deco->show();
 	    //} else {
@@ -1755,4 +1824,9 @@ void QDragManager::updatePixmap()
     }
 }
 
+void QDragManager::updatePixmap()
+{
+    updatePixmap( QCursor::pos());
+}
+
 #endif // QT_NO_DRAGANDDROP
--- src/kernel/qdragobject.h
+++ src/kernel/qdragobject.h
@@ -245,6 +245,7 @@ private:
     void move( const QPoint & );
     void drop();
     void updatePixmap();
+    void updatePixmap( const QPoint& cursorPos );
 
 private:
     QDragObject * object;
