Drag and drop optimization (update)

Wilco Greven kde-optimize@mail.kde.org
Thu, 30 Jan 2003 12:05:35 +0100


--Boundary-00=_/bQO+2N2ptAig+e
Content-Type: text/plain;
  charset="iso-8859-1"
Content-Transfer-Encoding: 7bit
Content-Disposition: inline

Here's an updated patch + testprogram. It uses XGetGeometry instead of 
XGetWindowAttributes now. And the clumsy do..while has been replaced by a for 
loop.
--Boundary-00=_/bQO+2N2ptAig+e
Content-Type: text/x-diff;
  charset="iso-8859-1";
  name="qdnd_x11.cpp.diff"
Content-Transfer-Encoding: 7bit
Content-Disposition: attachment; filename="qdnd_x11.cpp.diff"

Index: qdnd_x11.cpp
===================================================================
RCS file: /home/kde/qt-copy/src/kernel/qdnd_x11.cpp,v
retrieving revision 1.54
diff -u -p -b -r1.54 qdnd_x11.cpp
--- qdnd_x11.cpp	29 Jan 2003 13:54:56 -0000	1.54
+++ qdnd_x11.cpp	30 Jan 2003 10:59:36 -0000
@@ -245,6 +245,9 @@ static const char* const default_pm[] = 
 "X X X X X X X"
 };
 
+// Use NETWM for drag and drop.
+Atom qt_net_wm_client_list_stacking;
+
 class QShapedPixmapWidget : public QWidget {
 public:
     QShapedPixmapWidget(int screen = -1) :
@@ -419,6 +422,9 @@ void qt_xdnd_setup() {
     qt_x11_intern_atom( "QT_SELECTION", &qt_selection_property );
     qt_x11_intern_atom( "INCR", &qt_incr_atom );
 
+    qt_x11_intern_atom("_NET_CLIENT_LIST_STACKING",
+		                      &qt_net_wm_client_list_stacking);
+
     qAddPostRoutine( qt_xdnd_cleanup );
 }
 
@@ -1096,6 +1102,55 @@ Window findRealWindow( const QPoint & po
     return 0;
 }
 
+static Window findRealWindowNETWM( const QPoint& pos, Window rootWindow, int )
+{
+    Display* const dpy = QPaintDevice::x11AppDisplay();
+
+    Window* head;
+
+    Atom type_ret;
+    int format_ret;
+    unsigned long nitems_ret;
+    unsigned long unused;
+    unsigned char *data_ret = 0;
+
+    if (XGetWindowProperty(dpy, rootWindow, qt_net_wm_client_list_stacking,
+                       0, 1024, False, XA_WINDOW, &type_ret,
+                       &format_ret, &nitems_ret, &unused, &data_ret) 
+          == Success) {
+        if (type_ret == XA_WINDOW && format_ret == 32)
+            head = (Window*) data_ret;
+
+        if (data_ret)
+            XFree(data_ret);
+    }
+    else
+        return 0;
+
+    Window* win;
+    for (win = head + nitems_ret - 1; win != head; --win) {
+        Window root_win;
+        int win_x, win_y;
+        unsigned int win_width, win_height;
+        unsigned int border_width_ret;
+        unsigned int depth_ret;
+        XGetGeometry(dpy, *win, &root_win, &win_x, &win_y, 
+                     &win_width, &win_height, &border_width_ret,
+                     &depth_ret);
+
+        int win_glob_x, win_glob_y;
+        Window child;
+        XTranslateCoordinates(dpy, *win, root_win, win_x, win_y, 
+                              &win_glob_x, &win_glob_y, &child);
+
+        if (pos.x() >= win_glob_x && pos.x() < win_glob_x + win_width
+         && pos.y() >= win_glob_y && pos.y() < win_glob_y + win_height)
+            break;
+    }
+
+    return *win;
+}
+
 void QDragManager::move( const QPoint & globalPos )
 {
     Q_ASSERT( object != 0 ); // ### remove in Qt 4.0 (object should never be 0 here)
@@ -1131,7 +1186,8 @@ void QDragManager::move( const QPoint & 
 	if (targetW)
 	    target = targetW;
 	if ( qt_xdnd_deco && (!target || target == qt_xdnd_deco->winId()) ) {
-	    target = findRealWindow(globalPos,rootwin,6);
+	    // target = findRealWindowM(globalPos,rootwin,6);
+	    target = findRealWindowNETWM(globalPos,rootwin,6);
 	}
     }
 

--Boundary-00=_/bQO+2N2ptAig+e
Content-Type: text/x-c++src;
  charset="iso-8859-1";
  name="findclient.cpp"
Content-Transfer-Encoding: 7bit
Content-Disposition: attachment; filename="findclient.cpp"

// Build using:
// g++ -g findclient.cpp -o findclient -I/usr/local/kde/include -L/usr/X11R6/lib -L/usr/local/kde/lib -lX11 -lqt-mt

#include <qapplication.h>
#include <qdatetime.h>
#include <qwidget.h>

extern "C" {
#include <X11/Xatom.h>
#include <X11/Xlib.h>
}

#include "kcounterprof.h"

static Display* dpy;
static Window supportWindow;
static Window rootWindow;

static Atom wm_state;
static Atom net_client_list_stacking;

static int count;


static Window findRealWindow( int x, int y, Window w, int md )
{
    count++;

    if ( md ) {
        XWindowAttributes attr;
        XGetWindowAttributes(dpy, w, &attr);

        if ( attr.map_state != IsUnmapped
          && x >= attr.x && x < attr.x + attr.width
          && y >= attr.y && y < attr.y + attr.height)
        {
            {
                Atom   type = None;
                int f;
                unsigned long n, a;
                unsigned char *data;

                XGetWindowProperty( dpy, w, wm_state, 0,
                    0, False, AnyPropertyType, &type, &f,&n,&a,&data );
                if ( data ) XFree(data);
                if ( type ) return w;
            }

            Window r, p;
            Window* c;
            uint nc;
            if ( XQueryTree( dpy, w, &r, &p, &c, &nc ) ) {
                r=0;
                for (uint i=nc; !r && i--; ) {
                    r = findRealWindow( x-attr.x, y-attr.y, c[i], md-1 );
                }
                XFree(c);
                if ( r )
                    return r;

                // We didn't find a client window!  Just use the
                // innermost window.
            }

            // No children!
            return w;
        }
    }
    return 0;
}

static Window findRealWindowNETWM( int x, int y, Window, int)
{
    Window* head;

    Atom type_ret;
    int format_ret;
    unsigned long nitems_ret;
    unsigned long unused;
    unsigned char *data_ret = 0;

    if (XGetWindowProperty(dpy, rootWindow, net_client_list_stacking,
                       0, 1024, False, XA_WINDOW, &type_ret,
                       &format_ret, &nitems_ret, &unused, &data_ret) 
          == Success) {
        if (type_ret == XA_WINDOW && format_ret == 32)
            head = (Window*) data_ret;

        if (data_ret)
            XFree(data_ret);
    }
    else
        return 0;

    Window* win;
    for (win = head + nitems_ret - 1; win != head; --win) {
        Window root_win;
        int win_x, win_y;
        unsigned int win_width, win_height;
        unsigned int border_width_ret;
        unsigned int depth_ret;
        XGetGeometry(dpy, *win, &root_win, &win_x, &win_y, 
                     &win_width, &win_height, &border_width_ret,
                     &depth_ret);

        int win_glob_x, win_glob_y;
        Window child_ret;
        XTranslateCoordinates(dpy, *win, root_win, win_x, win_y, 
                              &win_glob_x, &win_glob_y, &child_ret);

        if (x >= win_glob_x && x < win_glob_x + win_width
         && y >= win_glob_y && y < win_glob_y + win_height)
            break;
    }

    return *win;
}

void test(int x, int y)
{
    Window result1 = findRealWindow(x, y, rootWindow, 6);
    Window result2 = findRealWindowNETWM(x, y, rootWindow, 6);

    if (result1 != result2) {
        qDebug("Different results for position (%i, %i): ", x, y);
        qDebug("  findRealWindow:      %x", result1);
        qDebug("  findRealWindowNETWM: %x", result2);
    }
}

int main(int argc, char** argv)
{
    QApplication app(argc, argv);
    supportWindow = (new QWidget())->winId();
    srand48(QTime::currentTime().second()+QTime::currentTime().msec());

    dpy = QPaintDevice::x11AppDisplay();
    wm_state = XInternAtom(dpy, "WM_STATE", False);
    net_client_list_stacking 
             = XInternAtom(dpy, "_NET_CLIENT_LIST_STACKING", False);
    count = 0;

    rootWindow = RootWindow(dpy, DefaultScreen(dpy));

    int x, y;

    const int width = DisplayWidth(dpy, DefaultScreen(dpy));
    const int height = DisplayHeight(dpy, DefaultScreen(dpy));

    /*
    for (;;) {
        x = int(drand48() * width);
        y = int(drand48() * height);
	test(x, y);
    }
    */

    x = int(drand48() * width);
    y = int(drand48() * height);

    KCounterProf::begin("findRealWindow");
    Window result1 = findRealWindow(x, y, rootWindow, 6);
    KCounterProf::end("findRealWindow");
    qDebug("findRealWindow was called %u times", count);
    qDebug("Window returned: %x", result1);

    KCounterProf::begin("findRealWindowNETWM");
    Window result2 = findRealWindowNETWM(x, y, rootWindow, 6);
    KCounterProf::end("findRealWindowNETWM");
    qDebug("Window returned: %x", result2);

    return 0;
}



--Boundary-00=_/bQO+2N2ptAig+e--