summaryrefslogtreecommitdiffstats
path: root/src/plugins/platforms/xcb
diff options
context:
space:
mode:
authorLars Knoll <lars.knoll@nokia.com>2011-06-02 23:20:47 +0200
committerLars Knoll <lars.knoll@nokia.com>2011-06-07 15:50:53 +0200
commit30b7c6512cde501e0e5da6e2a6dd2d8113092269 (patch)
tree057a44051a0c34a6b2e72c4a2a006d0609a9d31e /src/plugins/platforms/xcb
parent92edbd20603dfe2c6b68e0023b76aa9bbab89688 (diff)
X11 DnD implementation
Initial code for DnD on X11. Only Xdnd based, Motif DnD is being ignored. The code is currently limited to dropping stuff onto the application. Starting drags is not yet implemented. Reviewed-by: Samuel
Diffstat (limited to 'src/plugins/platforms/xcb')
-rw-r--r--src/plugins/platforms/xcb/qxcbclipboard.cpp33
-rw-r--r--src/plugins/platforms/xcb/qxcbclipboard.h2
-rw-r--r--src/plugins/platforms/xcb/qxcbconnection.cpp4
-rw-r--r--src/plugins/platforms/xcb/qxcbconnection.h11
-rw-r--r--src/plugins/platforms/xcb/qxcbdrag.cpp1708
-rw-r--r--src/plugins/platforms/xcb/qxcbdrag.h122
-rw-r--r--src/plugins/platforms/xcb/qxcbintegration.cpp6
-rw-r--r--src/plugins/platforms/xcb/qxcbintegration.h2
-rw-r--r--src/plugins/platforms/xcb/qxcbwindow.cpp18
-rw-r--r--src/plugins/platforms/xcb/xcb.pro3
10 files changed, 1883 insertions, 26 deletions
diff --git a/src/plugins/platforms/xcb/qxcbclipboard.cpp b/src/plugins/platforms/xcb/qxcbclipboard.cpp
index 79d564a000..8857f280db 100644
--- a/src/plugins/platforms/xcb/qxcbclipboard.cpp
+++ b/src/plugins/platforms/xcb/qxcbclipboard.cpp
@@ -498,7 +498,6 @@ bool QXcbClipboard::clipboardReadProperty(xcb_window_t win, xcb_atom_t property,
ulong bytes_left; // bytes_after
xcb_atom_t dummy_type;
int dummy_format;
- int r;
if (!type) // allow null args
type = &dummy_type;
@@ -640,25 +639,25 @@ namespace
};
}
-static xcb_generic_event_t *waitForClipboardEvent(QXcbConnection *connection, xcb_window_t win, int type, int timeout)
+xcb_generic_event_t *QXcbClipboard::waitForClipboardEvent(xcb_window_t win, int type, int timeout)
{
QElapsedTimer timer;
timer.start();
do {
Notify notify(win, type);
- xcb_generic_event_t *e = connection->checkEvent(notify);
+ xcb_generic_event_t *e = m_connection->checkEvent(notify);
if (e)
return e;
// process other clipboard events, since someone is probably requesting data from us
- ClipboardEvent clipboard(connection);
- e = connection->checkEvent(clipboard);
+ ClipboardEvent clipboard(m_connection);
+ e = m_connection->checkEvent(clipboard);
if (e) {
- connection->handleXcbEvent(e);
+ m_connection->handleXcbEvent(e);
free(e);
}
- connection->flush();
+ m_connection->flush();
// sleep 50 ms, so we don't use up CPU cycles all the time.
struct timeval usleep_tv;
@@ -688,7 +687,7 @@ QByteArray QXcbClipboard::clipboardReadIncrementalProperty(xcb_window_t win, xcb
for (;;) {
m_connection->flush();
- xcb_generic_event_t *ge = ::waitForClipboardEvent(m_connection, win, XCB_PROPERTY_NOTIFY, clipboard_timeout);
+ xcb_generic_event_t *ge = waitForClipboardEvent(win, XCB_PROPERTY_NOTIFY, clipboard_timeout);
if (!ge)
break;
@@ -737,16 +736,22 @@ QByteArray QXcbClipboard::getDataInFormat(xcb_atom_t modeAtom, xcb_atom_t fmtAto
xcb_window_t win = requestor();
// qDebug() << "getDataInFormat" << m_connection->atomName(modeAtom) << m_connection->atomName(fmtAtom) << win;
+ return getSelection(win, modeAtom, fmtAtom, m_connection->atom(QXcbAtom::_QT_SELECTION));
+}
+
+QByteArray QXcbClipboard::getSelection(xcb_window_t win, xcb_atom_t selection, xcb_atom_t target, xcb_atom_t property)
+{
+ QByteArray buf;
+
uint32_t mask = XCB_EVENT_MASK_NO_EVENT;
xcb_change_window_attributes(m_connection->xcb_connection(), win, XCB_CW_EVENT_MASK, &mask);
- xcb_delete_property(m_connection->xcb_connection(), win, m_connection->atom(QXcbAtom::_QT_SELECTION));
- xcb_convert_selection(m_connection->xcb_connection(), win, modeAtom, fmtAtom,
- m_connection->atom(QXcbAtom::_QT_SELECTION), XCB_CURRENT_TIME);
+ xcb_delete_property(m_connection->xcb_connection(), win, property);
+ xcb_convert_selection(m_connection->xcb_connection(), win, selection, target, property, XCB_CURRENT_TIME);
m_connection->sync();
- xcb_generic_event_t *ge = waitForClipboardEvent(m_connection, win, XCB_SELECTION_NOTIFY, clipboard_timeout);
+ xcb_generic_event_t *ge = waitForClipboardEvent(win, XCB_SELECTION_NOTIFY, clipboard_timeout);
bool no_selection = !ge || ((xcb_selection_notify_event_t *)ge)->property == XCB_NONE;
free(ge);
@@ -757,11 +762,11 @@ QByteArray QXcbClipboard::getDataInFormat(xcb_atom_t modeAtom, xcb_atom_t fmtAto
xcb_change_window_attributes(m_connection->xcb_connection(), win, XCB_CW_EVENT_MASK, &mask);
xcb_atom_t type;
- if (clipboardReadProperty(win, m_connection->atom(QXcbAtom::_QT_SELECTION), true, &buf, 0, &type, 0)) {
+ if (clipboardReadProperty(win, property, true, &buf, 0, &type, 0)) {
if (type == m_connection->atom(QXcbAtom::INCR)) {
qDebug() << "INCR";
int nbytes = buf.size() >= 4 ? *((int*)buf.data()) : 0;
- buf = clipboardReadIncrementalProperty(win, m_connection->atom(QXcbAtom::_QT_SELECTION), nbytes, false);
+ buf = clipboardReadIncrementalProperty(win, property, nbytes, false);
}
}
diff --git a/src/plugins/platforms/xcb/qxcbclipboard.h b/src/plugins/platforms/xcb/qxcbclipboard.h
index 57900f4b54..c3207cfcd0 100644
--- a/src/plugins/platforms/xcb/qxcbclipboard.h
+++ b/src/plugins/platforms/xcb/qxcbclipboard.h
@@ -75,10 +75,12 @@ public:
QByteArray getDataInFormat(xcb_atom_t modeAtom, xcb_atom_t fmtatom);
xcb_window_t getSelectionOwner(xcb_atom_t atom) const;
+ QByteArray getSelection(xcb_window_t win, xcb_atom_t selection, xcb_atom_t target, xcb_atom_t property);
private:
void setOwner(xcb_window_t window);
+ xcb_generic_event_t *waitForClipboardEvent(xcb_window_t win, int type, int timeout);
xcb_atom_t sendTargetsSelection(QMimeData *d, xcb_window_t window, xcb_atom_t property);
xcb_atom_t sendSelection(QMimeData *d, xcb_atom_t target, xcb_window_t window, xcb_atom_t property);
diff --git a/src/plugins/platforms/xcb/qxcbconnection.cpp b/src/plugins/platforms/xcb/qxcbconnection.cpp
index 885ec0c067..b719f9d7ed 100644
--- a/src/plugins/platforms/xcb/qxcbconnection.cpp
+++ b/src/plugins/platforms/xcb/qxcbconnection.cpp
@@ -44,6 +44,7 @@
#include "qxcbscreen.h"
#include "qxcbwindow.h"
#include "qxcbclipboard.h"
+#include "qxcbdrag.h"
#include <QtAlgorithms>
#include <QSocketNotifier>
@@ -118,6 +119,7 @@ QXcbConnection::QXcbConnection(const char *displayName)
m_keyboard = new QXcbKeyboard(this);
m_clipboard = new QXcbClipboard(this);
+ m_drag = new QXcbDrag(this);
#ifdef XCB_USE_DRI2
initializeDri2();
@@ -753,7 +755,7 @@ xcb_atom_t QXcbConnection::internAtom(const char *name)
QByteArray QXcbConnection::atomName(xcb_atom_t atom)
{
- xcb_get_atom_name_cookie_t cookie = xcb_get_atom_name_unchecked(xcb_connection(), atom);
+ xcb_get_atom_name_cookie_t cookie = Q_XCB_CALL(xcb_get_atom_name_unchecked(xcb_connection(), atom));
xcb_get_atom_name_reply_t *reply = xcb_get_atom_name_reply(xcb_connection(), cookie, 0);
if (reply) {
QByteArray result(xcb_get_atom_name_name(reply), xcb_get_atom_name_name_length(reply));
diff --git a/src/plugins/platforms/xcb/qxcbconnection.h b/src/plugins/platforms/xcb/qxcbconnection.h
index 51aa09fcef..136fbca8f8 100644
--- a/src/plugins/platforms/xcb/qxcbconnection.h
+++ b/src/plugins/platforms/xcb/qxcbconnection.h
@@ -53,6 +53,9 @@
class QXcbScreen;
class QXcbWindow;
+class QXcbDrag;
+class QXcbKeyboard;
+class QXcbClipboard;
typedef QHash<xcb_window_t, QXcbWindow *> WindowMapper;
@@ -63,6 +66,7 @@ namespace QXcbAtom {
static const xcb_atom_t XA_PIXMAP = 20;
static const xcb_atom_t XA_BITMAP = 5;
static const xcb_atom_t XA_STRING = 32;
+ static const xcb_atom_t XA_WINDOW = 33;
enum Atom {
// window-manager <-> client protocols
@@ -225,9 +229,6 @@ namespace QXcbAtom {
};
}
-class QXcbKeyboard;
-class QXcbClipboard;
-
class QXcbConnection : public QObject
{
Q_OBJECT
@@ -253,6 +254,7 @@ public:
QXcbKeyboard *keyboard() const { return m_keyboard; }
QXcbClipboard *clipboard() const { return m_clipboard; }
+ QXcbDrag *drag() const { return m_drag; }
#ifdef XCB_USE_XLIB
void *xlib_display() const { return m_xlib_display; }
@@ -277,6 +279,7 @@ public:
void addWindow(xcb_window_t id, QXcbWindow *window);
void removeWindow(xcb_window_t id);
+ QXcbWindow *platformWindowFromId(xcb_window_t id);
xcb_generic_event_t *checkEvent(int type);
template<typename T>
@@ -296,6 +299,7 @@ private:
#ifdef XCB_USE_DRI2
void initializeDri2();
#endif
+
xcb_connection_t *m_connection;
const xcb_setup_t *m_setup;
@@ -308,6 +312,7 @@ private:
QXcbKeyboard *m_keyboard;
QXcbClipboard *m_clipboard;
+ QXcbDrag *m_drag;
#if defined(XCB_USE_XLIB)
void *m_xlib_display;
diff --git a/src/plugins/platforms/xcb/qxcbdrag.cpp b/src/plugins/platforms/xcb/qxcbdrag.cpp
new file mode 100644
index 0000000000..bb515c37ad
--- /dev/null
+++ b/src/plugins/platforms/xcb/qxcbdrag.cpp
@@ -0,0 +1,1708 @@
+/****************************************************************************
+**
+** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
+** All rights reserved.
+** Contact: Nokia Corporation (qt-info@nokia.com)
+**
+** This file is part of the QtGui module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** GNU Lesser General Public License Usage
+** This file may be used under the terms of the GNU Lesser General Public
+** License version 2.1 as published by the Free Software Foundation and
+** appearing in the file LICENSE.LGPL included in the packaging of this
+** file. Please review the following information to ensure the GNU Lesser
+** General Public License version 2.1 requirements will be met:
+** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU General
+** Public License version 3.0 as published by the Free Software Foundation
+** and appearing in the file LICENSE.GPL included in the packaging of this
+** file. Please review the following information to ensure the GNU General
+** Public License version 3.0 requirements will be met:
+** http://www.gnu.org/copyleft/gpl.html.
+**
+** Other Usage
+** Alternatively, this file may be used in accordance with the terms and
+** conditions contained in a signed written agreement between you and Nokia.
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "qxcbdrag.h"
+#include <xcb/xcb.h>
+#include "qxcbconnection.h"
+#include "qxcbclipboard.h"
+#include "qxcbmime.h"
+#include "qxcbwindow.h"
+#include "qwindow.h"
+#include <private/qdnd_p.h>
+#include <qdebug.h>
+#include <qevent.h>
+#include <qguiapplication.h>
+#include <qrect.h>
+
+QT_BEGIN_NAMESPACE
+
+#define DND_DEBUG
+#ifdef DND_DEBUG
+#define DEBUG qDebug
+#else
+#define DEBUG if(0) qDebug
+#endif
+
+#ifdef DND_DEBUG
+#define DNDDEBUG qDebug()
+#else
+#define DNDDEBUG if(0) qDebug()
+#endif
+
+const int xdnd_version = 5;
+
+static inline xcb_window_t xcb_window(QWindow *w)
+{
+ return static_cast<QXcbWindow *>(w->handle())->xcb_window();
+}
+
+class QDropData : public QXcbMime
+{
+public:
+ QDropData(QXcbDrag *d);
+ ~QDropData();
+
+protected:
+ bool hasFormat_sys(const QString &mimeType) const;
+ QStringList formats_sys() const;
+ QVariant retrieveData_sys(const QString &mimeType, QVariant::Type type) const;
+
+ QVariant xdndObtainData(const QByteArray &format, QVariant::Type requestedType) const;
+
+ QXcbDrag *drag;
+};
+
+
+QXcbDrag::QXcbDrag(QXcbConnection *c)
+{
+ m_connection = c;
+ dropData = new QDropData(this);
+
+ startDrag(); // init variables
+}
+
+QXcbDrag::~QXcbDrag()
+{
+ delete dropData;
+}
+
+
+QMimeData *QXcbDrag::platformDropData()
+{
+ return dropData;
+}
+
+void QXcbDrag::startDrag()
+{
+ currentWindow.clear();
+
+ xdnd_dragsource = XCB_NONE;
+ last_target_accepted_action = Qt::IgnoreAction;
+
+ waiting_for_status = false;
+ current_proxy_target = XCB_NONE;
+}
+
+void QXcbDrag::cancel()
+{
+ //###
+}
+
+void QXcbDrag::move(const QMouseEvent *me)
+{
+ // ###
+}
+
+void QXcbDrag::drop(const QMouseEvent *me)
+{
+ // ###
+}
+
+#define ATOM(x) connection()->atom(QXcbAtom::x)
+
+Qt::DropAction QXcbDrag::toDropAction(xcb_atom_t atom) const
+{
+ if (atom == ATOM(XdndActionCopy) || atom == 0)
+ return Qt::CopyAction;
+ if (atom == ATOM(XdndActionLink))
+ return Qt::LinkAction;
+ if (atom == ATOM(XdndActionMove))
+ return Qt::MoveAction;
+ return Qt::CopyAction;
+}
+
+xcb_atom_t QXcbDrag::toXdndAction(Qt::DropAction a) const
+{
+ switch (a) {
+ case Qt::CopyAction:
+ return ATOM(XdndActionCopy);
+ case Qt::LinkAction:
+ return ATOM(XdndActionLink);
+ case Qt::MoveAction:
+ case Qt::TargetMoveAction:
+ return ATOM(XdndActionMove);
+ case Qt::IgnoreAction:
+ return XCB_NONE;
+ default:
+ return ATOM(XdndActionCopy);
+ }
+}
+
+#undef ATOM
+
+#if 0
+static int findXdndDropTransactionByWindow(Window window)
+{
+ int at = -1;
+ for (int i = 0; i < X11->dndDropTransactions.count(); ++i) {
+ const QXdndDropTransaction &t = X11->dndDropTransactions.at(i);
+ if (t.target == window || t.proxy_target == window) {
+ at = i;
+ break;
+ }
+ }
+ return at;
+}
+
+static int findXdndDropTransactionByTime(Time timestamp)
+{
+ int at = -1;
+ for (int i = 0; i < X11->dndDropTransactions.count(); ++i) {
+ const QXdndDropTransaction &t = X11->dndDropTransactions.at(i);
+ if (t.timestamp == timestamp) {
+ at = i;
+ break;
+ }
+ }
+ return at;
+}
+
+// timer used to discard old XdndDrop transactions
+static int transaction_expiry_timer = -1;
+enum { XdndDropTransactionTimeout = 5000 }; // 5 seconds
+
+static void restartXdndDropExpiryTimer()
+{
+ if (transaction_expiry_timer != -1)
+ QDragManager::self()->killTimer(transaction_expiry_timer);
+ transaction_expiry_timer = QDragManager::self()->startTimer(XdndDropTransactionTimeout);
+}
+
+
+// find an ancestor with XdndAware on it
+static Window findXdndAwareParent(Window window)
+{
+ Window target = 0;
+ forever {
+ // check if window has XdndAware
+ Atom type = 0;
+ int f;
+ unsigned long n, a;
+ unsigned char *data = 0;
+ if (XGetWindowProperty(X11->display, window, ATOM(XdndAware), 0, 0, False,
+ AnyPropertyType, &type, &f,&n,&a,&data) == Success) {
+ if (data)
+ XFree(data);
+ if (type) {
+ target = window;
+ break;
+ }
+ }
+
+ // try window's parent
+ Window root;
+ Window parent;
+ Window *children;
+ uint unused;
+ if (!XQueryTree(X11->display, window, &root, &parent, &children, &unused))
+ break;
+ if (children)
+ XFree(children);
+ if (window == root)
+ break;
+ window = parent;
+ }
+ return target;
+}
+
+
+
+
+// clean up the stuff used.
+static void qt_xdnd_cleanup();
+
+static void qt_xdnd_send_leave();
+
+
+// timer used when target wants "continuous" move messages (eg. scroll)
+static int heartbeat = -1;
+// top-level window we sent position to last.
+static Window qt_xdnd_current_target;
+// window to send events to (always valid if qt_xdnd_current_target)
+static Window current_proxy_target;
+
+// widget we forwarded position to last, and local position
+static QPointer<QWidget> currentWindow;
+static QPoint currentPosition;
+// timestamp from the XdndPosition and XdndDrop
+static Time target_time;
+// screen number containing the pointer... -1 means default
+static int qt_xdnd_current_screen = -1;
+// state of dragging... true if dragging, false if not
+bool qt_xdnd_dragging = false;
+
+static bool waiting_for_status = false;
+
+// used to preset each new QDragMoveEvent
+
+// Shift/Ctrl handling, and final drop status
+static Qt::DropAction global_accepted_action = Qt::CopyAction;
+static Qt::DropActions possible_actions = Qt::IgnoreAction;
+
+// for embedding only
+static QWidget* current_embedding_widget = 0;
+static xcb_client_message_event_t last_enter_event;
+
+// cursors
+static QCursor *noDropCursor = 0;
+static QCursor *moveCursor = 0;
+static QCursor *copyCursor = 0;
+static QCursor *linkCursor = 0;
+
+
+class QExtraWidget : public QWidget
+{
+ Q_DECLARE_PRIVATE(QWidget)
+public:
+ inline QWExtra* extraData();
+ inline QTLWExtra* topData();
+};
+
+inline QWExtra* QExtraWidget::extraData() { return d_func()->extraData(); }
+inline QTLWExtra* QExtraWidget::topData() { return d_func()->topData(); }
+
+
+
+void QX11Data::xdndSetup() {
+ QCursorData::initialize();
+ qAddPostRoutine(qt_xdnd_cleanup);
+}
+
+
+void qt_xdnd_cleanup()
+{
+ delete noDropCursor;
+ noDropCursor = 0;
+ delete copyCursor;
+ copyCursor = 0;
+ delete moveCursor;
+ moveCursor = 0;
+ delete linkCursor;
+ linkCursor = 0;
+ delete defaultPm;
+ defaultPm = 0;
+ delete xdnd_data.desktop_proxy;
+ xdnd_data.desktop_proxy = 0;
+ delete xdnd_data.deco;
+ xdnd_data.deco = 0;
+}
+
+
+static QWidget *find_child(QWidget *tlw, QPoint & p)
+{
+ QWidget *widget = tlw;
+
+ p = widget->mapFromGlobal(p);
+ bool done = false;
+ while (!done) {
+ done = true;
+ if (((QExtraWidget*)widget)->extraData() &&
+ ((QExtraWidget*)widget)->extraData()->xDndProxy != 0)
+ break; // stop searching for widgets under the mouse cursor if found widget is a proxy.
+ QObjectList children = widget->children();
+ if (!children.isEmpty()) {
+ for(int i = children.size(); i > 0;) {
+ --i;
+ QWidget *w = qobject_cast<QWidget *>(children.at(i));
+ if (!w)
+ continue;
+ if (w->testAttribute(Qt::WA_TransparentForMouseEvents))
+ continue;
+ if (w->isVisible() &&
+ w->geometry().contains(p) &&
+ !w->isWindow()) {
+ widget = w;
+ done = false;
+ p = widget->mapFromParent(p);
+ break;
+ }
+ }
+ }
+ }
+ return widget;
+}
+
+
+static bool checkEmbedded(QWidget* w, const XEvent* xe)
+{
+ if (!w)
+ return false;
+
+ if (current_embedding_widget != 0 && current_embedding_widget != w) {
+ qt_xdnd_current_target = ((QExtraWidget*)current_embedding_widget)->extraData()->xDndProxy;
+ current_proxy_target = qt_xdnd_current_target;
+ qt_xdnd_send_leave();
+ qt_xdnd_current_target = 0;
+ current_proxy_target = 0;
+ current_embedding_widget = 0;
+ }
+
+ QWExtra* extra = ((QExtraWidget*)w)->extraData();
+ if (extra && extra->xDndProxy != 0) {
+
+ if (current_embedding_widget != w) {
+
+ last_enter_event.xany.window = extra->xDndProxy;
+ XSendEvent(X11->display, extra->xDndProxy, False, NoEventMask, &last_enter_event);
+ current_embedding_widget = w;
+ }
+
+ ((XEvent*)xe)->xany.window = extra->xDndProxy;
+ XSendEvent(X11->display, extra->xDndProxy, False, NoEventMask, (XEvent*)xe);
+ if (currentWindow != w) {
+ currentWindow = w;
+ }
+ return true;
+ }
+ current_embedding_widget = 0;
+ return false;
+}
+#endif
+
+
+void QXcbDrag::handleEnter(QWindow *window, const xcb_client_message_event_t *event)
+{
+ Q_UNUSED(window);
+ DEBUG() << "handleEnter" << window;
+
+// motifdnd_active = false;
+// last_enter_event.xclient = xe->xclient;
+
+ int version = (int)(event->data.data32[1] >> 24);
+ if (version > xdnd_version)
+ return;
+
+ xdnd_dragsource = event->data.data32[0];
+
+ if (event->data.data32[1] & 1) {
+ // get the types from XdndTypeList
+ xcb_get_property_cookie_t cookie = xcb_get_property(connection()->xcb_connection(), false, xdnd_dragsource,
+ connection()->atom(QXcbAtom::XdndTypelist), QXcbAtom::XA_ATOM,
+ 0, xdnd_max_type);
+ xcb_get_property_reply_t *reply = xcb_get_property_reply(connection()->xcb_connection(), cookie, 0);
+ if (reply && reply->type != XCB_NONE && reply->format == 32) {
+ int length = xcb_get_property_value_length(reply) / 4;
+ if (length > xdnd_max_type)
+ length = xdnd_max_type;
+
+ xcb_atom_t *atoms = (xcb_atom_t *)xcb_get_property_value(reply);
+ for (int i = 0; i < length; ++i)
+ xdnd_types.append(atoms[i]);
+ }
+ free(reply);
+ } else {
+ // get the types from the message
+ for(int i = 2; i < 5; i++) {
+ if (event->data.data32[i])
+ xdnd_types.append(event->data.data32[i]);
+ }
+ }
+ for(int i = 0; i < xdnd_types.length(); ++i)
+ DEBUG() << " " << connection()->atomName(xdnd_types.at(i));
+}
+
+void QXcbDrag::handle_xdnd_position(QWindow *w, const xcb_client_message_event_t *e, bool passive)
+{
+ QPoint p((e->data.data32[2] & 0xffff0000) >> 16, e->data.data32[2] & 0x0000ffff);
+ Q_ASSERT(w);
+ QRect geometry = w->geometry();
+
+ p -= geometry.topLeft();
+
+ // ####
+// if (!passive && checkEmbedded(w, e))
+// return;
+
+ if (!w || (/*!w->acceptDrops() &&*/ (w->windowType() == Qt::Desktop)))
+ return;
+
+ if (e->data.data32[0] != xdnd_dragsource) {
+ DEBUG("xdnd drag position from unexpected source (%x not %x)", e->data.data32[0], xdnd_dragsource);
+ return;
+ }
+
+ // timestamp from the source
+ if (e->data.data32[3] != 0)
+ target_time /*= X11->userTime*/ = e->data.data32[3];
+
+ QDragManager *manager = QDragManager::self();
+ QMimeData *dropData = manager->dropData();
+
+ xcb_client_message_event_t response;
+ response.response_type = XCB_CLIENT_MESSAGE;
+ response.window = xdnd_dragsource;
+ response.format = 32;
+ response.type = connection()->atom(QXcbAtom::XdndStatus);
+ response.data.data32[0] = xcb_window(w);
+ response.data.data32[1] = 0; // flags
+ response.data.data32[2] = 0; // x, y
+ response.data.data32[3] = 0; // w, h
+ response.data.data32[4] = 0; // action
+
+ if (!passive) { // otherwise just reject
+ QRect answerRect(p + geometry.topLeft(), QSize(1,1));
+
+ if (manager->object) {
+ manager->possible_actions = manager->dragPrivate()->possible_actions;
+ } else {
+ manager->possible_actions = Qt::DropActions(toDropAction(e->data.data32[4]));
+// possible_actions |= Qt::CopyAction;
+ }
+ QDragMoveEvent me(p, manager->possible_actions, dropData,
+ QGuiApplication::mouseButtons(), QGuiApplication::keyboardModifiers());
+
+ Qt::DropAction accepted_action = Qt::IgnoreAction;
+
+ currentPosition = p;
+
+ if (w != currentWindow.data()) {
+ if (currentWindow) {
+ QDragLeaveEvent e;
+ QGuiApplication::sendEvent(currentWindow.data(), &e);
+ }
+ currentWindow = w;
+
+ last_target_accepted_action = Qt::IgnoreAction;
+ QDragEnterEvent de(p, manager->possible_actions, dropData,
+ QGuiApplication::mouseButtons(), QGuiApplication::keyboardModifiers());
+ QGuiApplication::sendEvent(w, &de);
+ if (de.isAccepted() && de.dropAction() != Qt::IgnoreAction)
+ last_target_accepted_action = de.dropAction();
+ }
+
+ DEBUG() << "qt_handle_xdnd_position action=" << connection()->atomName(e->data.data32[4]);
+
+ if (last_target_accepted_action != Qt::IgnoreAction) {
+ me.setDropAction(last_target_accepted_action);
+ me.accept();
+ }
+ QGuiApplication::sendEvent(w, &me);
+ if (me.isAccepted()) {
+ response.data.data32[1] = 1; // yes
+ accepted_action = me.dropAction();
+ last_target_accepted_action = accepted_action;
+ } else {
+ response.data.data32[0] = 0;
+ last_target_accepted_action = Qt::IgnoreAction;
+ }
+ answerRect = me.answerRect().translated(geometry.topLeft()).intersected(geometry);
+
+ if (answerRect.left() < 0)
+ answerRect.setLeft(0);
+ if (answerRect.right() > 4096)
+ answerRect.setRight(4096);
+ if (answerRect.top() < 0)
+ answerRect.setTop(0);
+ if (answerRect.bottom() > 4096)
+ answerRect.setBottom(4096);
+ if (answerRect.width() < 0)
+ answerRect.setWidth(0);
+ if (answerRect.height() < 0)
+ answerRect.setHeight(0);
+
+ response.data.data32[2] = (answerRect.x() << 16) + answerRect.y();
+ response.data.data32[3] = (answerRect.width() << 16) + answerRect.height();
+ response.data.data32[4] = toXdndAction(accepted_action);
+ }
+
+ // reset
+ target_time = XCB_CURRENT_TIME;
+
+ QXcbWindow *source = connection()->platformWindowFromId(xdnd_dragsource);
+ if (source && (source->window()->windowType() == Qt::Desktop) /*&& !source->acceptDrops()*/)
+ source = 0;
+
+ DEBUG() << "sending XdndStatus";
+ if (source)
+ handle_xdnd_status(source->window(), &response, passive);
+ else
+ Q_XCB_CALL(xcb_send_event(connection()->xcb_connection(), false, xdnd_dragsource,
+ XCB_EVENT_MASK_NO_EVENT, (const char *)&response));
+}
+
+namespace
+{
+ class ClientMessageScanner {
+ public:
+ ClientMessageScanner(xcb_atom_t a) : atom(a) {}
+ xcb_atom_t atom;
+ bool check(xcb_generic_event_t *event) const {
+ if (!event)
+ return false;
+ if ((event->response_type & 0x7f) != XCB_CLIENT_MESSAGE)
+ return false;
+ return ((xcb_client_message_event_t *)event)->type == atom;
+ }
+ };
+}
+
+void QXcbDrag::handlePosition(QWindow * w, const xcb_client_message_event_t *event, bool passive)
+{
+ xcb_client_message_event_t *lastEvent = const_cast<xcb_client_message_event_t *>(event);
+ xcb_generic_event_t *nextEvent;
+ ClientMessageScanner scanner(connection()->atom(QXcbAtom::XdndPosition));
+ while ((nextEvent = connection()->checkEvent(scanner))) {
+ if (lastEvent != event)
+ free(lastEvent);
+ lastEvent = (xcb_client_message_event_t *)nextEvent;
+ }
+
+ handle_xdnd_position(w, lastEvent, passive);
+ if (lastEvent != event)
+ free(lastEvent);
+}
+
+void QXcbDrag::handle_xdnd_status(QWindow *, const xcb_client_message_event_t *event, bool)
+{
+ // ignore late status messages
+ if (event->data.data32[0] && event->data.data32[0] != current_proxy_target)
+ return;
+
+ Qt::DropAction newAction = (event->data.data32[1] & 0x1) ? toDropAction(event->data.data32[4]) : Qt::IgnoreAction;
+
+ if ((event->data.data32[1] & 2) == 0) {
+ QPoint p((event->data.data32[2] & 0xffff0000) >> 16, event->data.data32[2] & 0x0000ffff);
+ QSize s((event->data.data32[3] & 0xffff0000) >> 16, event->data.data32[3] & 0x0000ffff);
+ source_sameanswer = QRect(p, s);
+ } else {
+ source_sameanswer = QRect();
+ }
+ QDragManager *manager = QDragManager::self();
+ manager->willDrop = (event->data.data32[1] & 0x1);
+ if (manager->global_accepted_action != newAction) {
+ manager->global_accepted_action = newAction;
+ manager->emitActionChanged(newAction);
+ }
+ manager->updateCursor();
+ waiting_for_status = false;
+}
+
+void QXcbDrag::handleStatus(QWindow *w, const xcb_client_message_event_t *event, bool passive)
+{
+ DEBUG("xdndHandleStatus");
+ xcb_client_message_event_t *lastEvent = const_cast<xcb_client_message_event_t *>(event);
+ xcb_generic_event_t *nextEvent;
+ ClientMessageScanner scanner(connection()->atom(QXcbAtom::XdndStatus));
+ while ((nextEvent = connection()->checkEvent(scanner))) {
+ if (lastEvent != event)
+ free(lastEvent);
+ lastEvent = (xcb_client_message_event_t *)nextEvent;
+ }
+
+ handle_xdnd_status(w, lastEvent, passive);
+ if (lastEvent != event)
+ free(lastEvent);
+ DEBUG("xdndHandleStatus end");
+}
+
+void QXcbDrag::handleLeave(QWindow *w, const xcb_client_message_event_t *event, bool /*passive*/)
+{
+ DEBUG("xdnd leave");
+ if (!currentWindow || w != currentWindow.data())
+ return; // sanity
+
+ // ###
+// if (checkEmbedded(current_embedding_widget, event)) {
+// current_embedding_widget = 0;
+// currentWindow.clear();
+// return;
+// }
+
+ if (event->data.data32[0] != xdnd_dragsource) {
+ // This often happens - leave other-process window quickly
+ DEBUG("xdnd drag leave from unexpected source (%x not %x", event->data.data32[0], xdnd_dragsource);
+ }
+
+ QDragLeaveEvent e;
+ QGuiApplication::sendEvent(currentWindow.data(), &e);
+
+ xdnd_dragsource = 0;
+ xdnd_types.clear();
+ currentWindow.clear();
+}
+
+#if 0
+void qt_xdnd_send_leave()
+{
+ if (!qt_xdnd_current_target)
+ return;
+
+ QDragManager *manager = QDragManager::self();
+
+ XClientMessageEvent leave;
+ leave.type = ClientMessage;
+ leave.window = qt_xdnd_current_target;
+ leave.format = 32;
+ leave.message_type = ATOM(XdndLeave);
+ leave.data.l[0] = manager->dragPrivate()->source->effectiveWinId();
+ leave.data.l[1] = 0; // flags
+ leave.data.l[2] = 0; // x, y
+ leave.data.l[3] = 0; // w, h
+ leave.data.l[4] = 0; // just null
+
+ QWidget * w = QWidget::find(current_proxy_target);
+
+ if (w && (w->windowType() == Qt::Desktop) && !w->acceptDrops())
+ w = 0;
+
+ if (w)
+ X11->xdndHandleLeave(w, (const XEvent *)&leave, false);
+ else
+ XSendEvent(X11->display, current_proxy_target, False,
+ NoEventMask, (XEvent*)&leave);
+
+ // reset the drag manager state
+ manager->willDrop = false;
+ if (global_accepted_action != Qt::IgnoreAction)
+ manager->emitActionChanged(Qt::IgnoreAction);
+ global_accepted_action = Qt::IgnoreAction;
+ manager->updateCursor();
+ qt_xdnd_current_target = 0;
+ current_proxy_target = 0;
+ qt_xdnd_source_current_time = 0;
+ waiting_for_status = false;
+}
+
+// TODO: remove and use QApplication::currentKeyboardModifiers() in Qt 4.8.
+static Qt::KeyboardModifiers currentKeyboardModifiers()
+{
+ Window root;
+ Window child;
+ int root_x, root_y, win_x, win_y;
+ uint keybstate;
+ for (int i = 0; i < ScreenCount(X11->display); ++i) {
+ if (XQueryPointer(X11->display, QX11Info::appRootWindow(i), &root, &child,
+ &root_x, &root_y, &win_x, &win_y, &keybstate))
+ return X11->translateModifiers(keybstate & 0x00ff);
+ }
+ return 0;
+}
+#endif
+
+void QXcbDrag::handleDrop(QWindow *, const xcb_client_message_event_t *event, bool passive)
+{
+ DEBUG("xdndHandleDrop");
+ if (!currentWindow) {
+ xdnd_dragsource = 0;
+ return; // sanity
+ }
+
+ // ###
+// if (!passive && checkEmbedded(currentWindow, xe)){
+// current_embedding_widget = 0;
+// xdnd_dragsource = 0;
+// currentWindow = 0;
+// return;
+// }
+ const uint32_t *l = event->data.data32;
+
+ QDragManager *manager = QDragManager::self();
+ DEBUG("xdnd drop");
+
+ if (l[0] != xdnd_dragsource) {
+ DEBUG("xdnd drop from unexpected source (%x not %x", l[0], xdnd_dragsource);
+ return;
+ }
+
+ // update the "user time" from the timestamp in the event.
+ if (l[2] != 0)
+ target_time = /*X11->userTime =*/ l[2];
+
+ if (!passive) {
+ // this could be a same-application drop, just proxied due to
+ // some XEMBEDding, so try to find the real QMimeData used
+ // based on the timestamp for this drop.
+ QMimeData *dropData = 0;
+ // ###
+// int at = findXdndDropTransactionByTime(target_time);
+// if (at != -1)
+// dropData = QDragManager::dragPrivate(X11->dndDropTransactions.at(at).object)->data;
+ // if we can't find it, then use the data in the drag manager
+ if (!dropData)
+ dropData = manager->dropData();
+
+ // Drop coming from another app? Update keyboard modifiers.
+// if (!qt_xdnd_dragging) {
+// QApplicationPrivate::modifier_buttons = currentKeyboardModifiers();
+// }
+
+ QDropEvent de(currentPosition, manager->possible_actions, dropData,
+ QGuiApplication::mouseButtons(), QGuiApplication::keyboardModifiers());
+ QGuiApplication::sendEvent(currentWindow.data(), &de);
+ if (!de.isAccepted()) {
+ // Ignore a failed drag
+ manager->global_accepted_action = Qt::IgnoreAction;
+ } else {
+ manager->global_accepted_action = de.dropAction();
+ }
+ xcb_client_message_event_t finished;
+ finished.type = XCB_CLIENT_MESSAGE;
+ finished.window = xdnd_dragsource;
+ finished.format = 32;
+ finished.type = connection()->atom(QXcbAtom::XdndFinished);
+ DNDDEBUG << "xdndHandleDrop"
+ << "currentWindow" << currentWindow
+ << (currentWindow ? xcb_window(currentWindow.data()) : 0);
+ finished.data.data32[0] = currentWindow ? xcb_window(currentWindow.data()) : XCB_NONE;
+ finished.data.data32[1] = de.isAccepted() ? 1 : 0; // flags
+ finished.data.data32[2] = toXdndAction(manager->global_accepted_action);
+ Q_XCB_CALL(xcb_send_event(connection()->xcb_connection(), false, xdnd_dragsource,
+ XCB_EVENT_MASK_NO_EVENT, (char *)&finished));
+ } else {
+ QDragLeaveEvent e;
+ QGuiApplication::sendEvent(currentWindow.data(), &e);
+ }
+ xdnd_dragsource = 0;
+ currentWindow.clear();
+ waiting_for_status = false;
+
+ // reset
+ target_time = XCB_CURRENT_TIME;
+}
+
+#if 0
+
+void QX11Data::xdndHandleFinished(QWidget *, const XEvent * xe, bool passive)
+{
+ DEBUG("xdndHandleFinished");
+ const unsigned long *l = (const unsigned long *)xe->xclient.data.l;
+
+ DNDDEBUG << "xdndHandleFinished, l[0]" << l[0]
+ << "qt_xdnd_current_target" << qt_xdnd_current_target
+ << "qt_xdnd_current_proxy_targe" << current_proxy_target;
+
+ if (l[0]) {
+ int at = findXdndDropTransactionByWindow(l[0]);
+ if (at != -1) {
+ restartXdndDropExpiryTimer();
+
+ QXdndDropTransaction t = X11->dndDropTransactions.takeAt(at);
+ QDragManager *manager = QDragManager::self();
+
+ Window target = qt_xdnd_current_target;
+ Window proxy_target = current_proxy_target;
+ QWidget *embedding_widget = current_embedding_widget;
+ QDrag *currentObject = manager->object;
+
+ qt_xdnd_current_target = t.target;
+ current_proxy_target = t.proxy_target;
+ current_embedding_widget = t.embedding_widget;
+ manager->object = t.object;
+
+ if (!passive)
+ (void) checkEmbedded(currentWindow, xe);
+
+ current_embedding_widget = 0;
+ qt_xdnd_current_target = 0;
+ current_proxy_target = 0;
+
+ if (t.object)
+ t.object->deleteLater();
+
+ qt_xdnd_current_target = target;
+ current_proxy_target = proxy_target;
+ current_embedding_widget = embedding_widget;
+ manager->object = currentObject;
+ }
+ }
+ waiting_for_status = false;
+}
+
+
+void QDragManager::timerEvent(QTimerEvent* e)
+{
+ if (e->timerId() == heartbeat && source_sameanswer.isNull()) {
+ move(QCursor::pos());
+ } else if (e->timerId() == transaction_expiry_timer) {
+ for (int i = 0; i < X11->dndDropTransactions.count(); ++i) {
+ const QXdndDropTransaction &t = X11->dndDropTransactions.at(i);
+ if (t.targetWidget) {
+ // dnd within the same process, don't delete these
+ continue;
+ }
+ t.object->deleteLater();
+ X11->dndDropTransactions.removeAt(i--);
+ }
+
+ killTimer(transaction_expiry_timer);
+ transaction_expiry_timer = -1;
+ }
+}
+
+bool QDragManager::eventFilter(QObject * o, QEvent * e)
+{
+ if (beingCancelled) {
+ if (e->type() == QEvent::KeyRelease && ((QKeyEvent*)e)->key() == Qt::Key_Escape) {
+ qApp->removeEventFilter(this);
+ Q_ASSERT(object == 0);
+ beingCancelled = false;
+ eventLoop->exit();
+ return true; // block the key release
+ }
+ return false;
+ }
+
+ Q_ASSERT(object != 0);
+
+ if (!o->isWidgetType())
+ return false;
+
+ if (e->type() == QEvent::MouseMove) {
+ QMouseEvent* me = (QMouseEvent *)e;
+ move(me->globalPos());
+ return true;
+ } else if (e->type() == QEvent::MouseButtonRelease) {
+ DEBUG("pre drop");
+ qApp->removeEventFilter(this);
+ if (willDrop)
+ drop();
+ else
+ cancel();
+ DEBUG("drop, resetting object");
+ beingCancelled = false;
+ eventLoop->exit();
+ return true;
+ }
+
+ if (e->type() == QEvent::ShortcutOverride) {
+ // prevent accelerators from firing while dragging
+ e->accept();
+ return true;
+ }
+
+ if (e->type() == QEvent::KeyPress || e->type() == QEvent::KeyRelease) {
+ QKeyEvent *ke = ((QKeyEvent*)e);
+ if (ke->key() == Qt::Key_Escape && e->type() == QEvent::KeyPress) {
+ cancel();
+ qApp->removeEventFilter(this);
+ beingCancelled = false;
+ eventLoop->exit();
+ } else {
+ source_sameanswer = QRect(); // force move
+ move(QCursor::pos());
+ }
+ return true; // Eat all key events
+ }
+
+ // ### We bind modality to widgets, so we have to do this
+ // ### "manually".
+ // DnD is modal - eat all other interactive events
+ switch (e->type()) {
+ case QEvent::MouseButtonPress:
+ case QEvent::MouseButtonRelease:
+ case QEvent::MouseButtonDblClick:
+ case QEvent::MouseMove:
+ case QEvent::KeyPress:
+ case QEvent::KeyRelease:
+ case QEvent::Wheel:
+ case QEvent::ShortcutOverride:
+ return true;
+ default:
+ return false;
+ }
+}
+
+void QDragManager::updateCursor()
+{
+ if (!noDropCursor) {
+#ifndef QT_NO_CURSOR
+ noDropCursor = new QCursor(Qt::ForbiddenCursor);
+ moveCursor = new QCursor(Qt::DragMoveCursor);
+ copyCursor = new QCursor(Qt::DragCopyCursor);
+ linkCursor = new QCursor(Qt::DragLinkCursor);
+#endif
+ }
+
+ QCursor *c;
+ if (willDrop) {
+ if (global_accepted_action == Qt::CopyAction) {
+ c = copyCursor;
+ } else if (global_accepted_action == Qt::LinkAction) {
+ c = linkCursor;
+ } else {
+ c = moveCursor;
+ }
+ if (xdnd_data.deco) {
+ xdnd_data.deco->show();
+ xdnd_data.deco->raise();
+ }
+ } else {
+ c = noDropCursor;
+ //if (qt_xdnd_deco)
+ // qt_xdnd_deco->hide();
+ }
+#ifndef QT_NO_CURSOR
+ if (c)
+ qApp->changeOverrideCursor(*c);
+#endif
+}
+
+
+void QDragManager::cancel(bool deleteSource)
+{
+ DEBUG("QDragManager::cancel");
+ Q_ASSERT(heartbeat != -1);
+ killTimer(heartbeat);
+ heartbeat = -1;
+ beingCancelled = true;
+ qt_xdnd_dragging = false;
+
+ if (qt_xdnd_current_target)
+ qt_xdnd_send_leave();
+
+#ifndef QT_NO_CURSOR
+ if (restoreCursor) {
+ QApplication::restoreOverrideCursor();
+ restoreCursor = false;
+ }
+#endif
+
+ if (deleteSource && object)
+ object->deleteLater();
+ object = 0;
+ qDeleteInEventHandler(xdnd_data.deco);
+ xdnd_data.deco = 0;
+
+ global_accepted_action = Qt::IgnoreAction;
+}
+
+static
+Window findRealWindow(const QPoint & pos, Window w, int md)
+{
+ if (xdnd_data.deco && w == xdnd_data.deco->effectiveWinId())
+ return 0;
+
+ if (md) {
+ X11->ignoreBadwindow();
+ XWindowAttributes attr;
+ XGetWindowAttributes(X11->display, w, &attr);
+ if (X11->badwindow())
+ return 0;
+
+ if (attr.map_state == IsViewable
+ && QRect(attr.x,attr.y,attr.width,attr.height).contains(pos)) {
+ {
+ Atom type = XNone;
+ int f;
+ unsigned long n, a;
+ unsigned char *data;
+
+ XGetWindowProperty(X11->display, w, ATOM(XdndAware), 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(X11->display, w, &r, &p, &c, &nc)) {
+ r=0;
+ for (uint i=nc; !r && i--;) {
+ r = findRealWindow(pos-QPoint(attr.x,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;
+}
+
+void QDragManager::move(const QPoint & globalPos)
+{
+#ifdef QT_NO_CURSOR
+ Q_UNUSED(globalPos);
+ return;
+#else
+ DEBUG() << "QDragManager::move enter";
+ if (!object) {
+ // perhaps the target crashed?
+ return;
+ }
+
+ int screen = QCursor::x11Screen();
+ if ((qt_xdnd_current_screen == -1 && screen != X11->defaultScreen) || (screen != qt_xdnd_current_screen)) {
+ // recreate the pixmap on the new screen...
+ delete xdnd_data.deco;
+ QWidget* parent = object->source()->window()->x11Info().screen() == screen
+ ? object->source()->window() : QApplication::desktop()->screen(screen);
+ xdnd_data.deco = new QShapedPixmapWidget(parent);
+ if (!QWidget::mouseGrabber()) {
+ updatePixmap();
+ xdnd_data.deco->grabMouse();
+ }
+ }
+ xdnd_data.deco->move(QCursor::pos() - xdnd_data.deco->pm_hot);
+
+ if (source_sameanswer.contains(globalPos) && source_sameanswer.isValid())
+ return;
+
+ qt_xdnd_current_screen = screen;
+ Window rootwin = QX11Info::appRootWindow(qt_xdnd_current_screen);
+ Window target = 0;
+ int lx = 0, ly = 0;
+ if (!XTranslateCoordinates(X11->display, rootwin, rootwin, globalPos.x(), globalPos.y(), &lx, &ly, &target))
+ // some weird error...
+ return;
+
+ if (target == rootwin) {
+ // Ok.
+ } else if (target) {
+ //me
+ Window src = rootwin;
+ while (target != 0) {
+ DNDDEBUG << "checking target for XdndAware" << QWidget::find(target) << target;
+ int lx2, ly2;
+ Window t;
+ // translate coordinates
+ if (!XTranslateCoordinates(X11->display, src, target, lx, ly, &lx2, &ly2, &t)) {
+ target = 0;
+ break;
+ }
+ lx = lx2;
+ ly = ly2;
+ src = target;
+
+ // check if it has XdndAware
+ Atom type = 0;
+ int f;
+ unsigned long n, a;
+ unsigned char *data = 0;
+ XGetWindowProperty(X11->display, target, ATOM(XdndAware), 0, 0, False,
+ AnyPropertyType, &type, &f,&n,&a,&data);
+ if (data)
+ XFree(data);
+ if (type) {
+ DNDDEBUG << "Found XdndAware on " << QWidget::find(target) << target;
+ break;
+ }
+
+ // find child at the coordinates
+ if (!XTranslateCoordinates(X11->display, src, src, lx, ly, &lx2, &ly2, &target)) {
+ target = 0;
+ break;
+ }
+ }
+ if (xdnd_data.deco && (!target || target == xdnd_data.deco->effectiveWinId())) {
+ DNDDEBUG << "need to find real window";
+ target = findRealWindow(globalPos, rootwin, 6);
+ DNDDEBUG << "real window found" << QWidget::find(target) << target;
+ }
+ }
+
+ QWidget* w;
+ if (target) {
+ w = QWidget::find((WId)target);
+ if (w && (w->windowType() == Qt::Desktop) && !w->acceptDrops())
+ w = 0;
+ } else {
+ w = 0;
+ target = rootwin;
+ }
+
+ DNDDEBUG << "and the final target is " << QWidget::find(target) << target;
+ DNDDEBUG << "the widget w is" << w;
+
+ WId proxy_target = xdndProxy(target);
+ if (!proxy_target)
+ proxy_target = target;
+ int target_version = 1;
+
+ if (proxy_target) {
+ Atom type = XNone;
+ int r, f;
+ unsigned long n, a;
+ unsigned char *retval;
+ X11->ignoreBadwindow();
+ r = XGetWindowProperty(X11->display, proxy_target, ATOM(XdndAware), 0,
+ 1, False, AnyPropertyType, &type, &f,&n,&a,&retval);
+ int *tv = (int *)retval;
+ if (r != Success || X11->badwindow()) {
+ target = 0;
+ } else {
+ target_version = qMin(xdnd_version,tv ? *tv : 1);
+ if (tv)
+ XFree(tv);
+// if (!(!X11->badwindow() && type))
+// target = 0;
+ }
+ }
+
+ if (target != qt_xdnd_current_target) {
+ if (qt_xdnd_current_target)
+ qt_xdnd_send_leave();
+
+ qt_xdnd_current_target = target;
+ current_proxy_target = proxy_target;
+ if (target) {
+ QVector<Atom> types;
+ int flags = target_version << 24;
+ QStringList fmts = QInternalMimeData::formatsHelper(dragPrivate()->data);
+ for (int i = 0; i < fmts.size(); ++i) {
+ QList<Atom> atoms = X11->xdndMimeAtomsForFormat(fmts.at(i));
+ for (int j = 0; j < atoms.size(); ++j) {
+ if (!types.contains(atoms.at(j)))
+ types.append(atoms.at(j));
+ }
+ }
+ if (types.size() > 3) {
+ XChangeProperty(X11->display,
+ dragPrivate()->source->effectiveWinId(), ATOM(XdndTypelist),
+ XA_ATOM, 32, PropModeReplace,
+ (unsigned char *)types.data(),
+ types.size());
+ flags |= 0x0001;
+ }
+ XClientMessageEvent enter;
+ enter.type = ClientMessage;
+ enter.window = target;
+ enter.format = 32;
+ enter.message_type = ATOM(XdndEnter);
+ enter.data.l[0] = dragPrivate()->source->effectiveWinId();
+ enter.data.l[1] = flags;
+ enter.data.l[2] = types.size()>0 ? types.at(0) : 0;
+ enter.data.l[3] = types.size()>1 ? types.at(1) : 0;
+ enter.data.l[4] = types.size()>2 ? types.at(2) : 0;
+ // provisionally set the rectangle to 5x5 pixels...
+ source_sameanswer = QRect(globalPos.x() - 2,
+ globalPos.y() -2 , 5, 5);
+
+ DEBUG("sending Xdnd enter");
+ if (w)
+ X11->xdndHandleEnter(w, (const XEvent *)&enter, false);
+ else if (target)
+ XSendEvent(X11->display, proxy_target, False, NoEventMask, (XEvent*)&enter);
+ waiting_for_status = false;
+ }
+ }
+ if (waiting_for_status)
+ return;
+
+ if (target) {
+ waiting_for_status = true;
+
+ XClientMessageEvent move;
+ move.type = ClientMessage;
+ move.window = target;
+ move.format = 32;
+ move.message_type = ATOM(XdndPosition);
+ move.window = target;
+ move.data.l[0] = dragPrivate()->source->effectiveWinId();
+ move.data.l[1] = 0; // flags
+ move.data.l[2] = (globalPos.x() << 16) + globalPos.y();
+ move.data.l[3] = X11->time;
+ move.data.l[4] = qtaction_to_xdndaction(defaultAction(dragPrivate()->possible_actions, QApplication::keyboardModifiers()));
+ DEBUG("sending Xdnd position");
+
+ qt_xdnd_source_current_time = X11->time;
+
+ if (w)
+ handle_xdnd_position(w, (const XEvent *)&move, false);
+ else
+ XSendEvent(X11->display, proxy_target, False, NoEventMask,
+ (XEvent*)&move);
+ } else {
+ if (willDrop) {
+ willDrop = false;
+ updateCursor();
+ }
+ }
+ DEBUG() << "QDragManager::move leave";
+#endif
+}
+
+
+void QDragManager::drop()
+{
+ Q_ASSERT(heartbeat != -1);
+ killTimer(heartbeat);
+ heartbeat = -1;
+ qt_xdnd_dragging = false;
+
+ if (!qt_xdnd_current_target)
+ return;
+
+ qDeleteInEventHandler(xdnd_data.deco);
+ xdnd_data.deco = 0;
+
+ XClientMessageEvent drop;
+ drop.type = ClientMessage;
+ drop.window = qt_xdnd_current_target;
+ drop.format = 32;
+ drop.message_type = ATOM(XdndDrop);
+ drop.data.l[0] = dragPrivate()->source->effectiveWinId();
+ drop.data.l[1] = 0; // flags
+ drop.data.l[2] = X11->time;
+
+ drop.data.l[3] = 0;
+ drop.data.l[4] = 0;
+
+ QWidget * w = QWidget::find(current_proxy_target);
+
+ if (w && (w->windowType() == Qt::Desktop) && !w->acceptDrops())
+ w = 0;
+
+ QXdndDropTransaction t = {
+ X11->time,
+ qt_xdnd_current_target,
+ current_proxy_target,
+ w,
+ current_embedding_widget,
+ object
+ };
+ X11->dndDropTransactions.append(t);
+ restartXdndDropExpiryTimer();
+
+ if (w)
+ X11->xdndHandleDrop(w, (const XEvent *)&drop, false);
+ else
+ XSendEvent(X11->display, current_proxy_target, False,
+ NoEventMask, (XEvent*)&drop);
+
+ qt_xdnd_current_target = 0;
+ current_proxy_target = 0;
+ qt_xdnd_source_current_time = 0;
+ current_embedding_widget = 0;
+ object = 0;
+
+#ifndef QT_NO_CURSOR
+ if (restoreCursor) {
+ QApplication::restoreOverrideCursor();
+ restoreCursor = false;
+ }
+#endif
+}
+
+
+
+bool QX11Data::xdndHandleBadwindow()
+{
+ if (qt_xdnd_current_target) {
+ QDragManager *manager = QDragManager::self();
+ if (manager->object) {
+ qt_xdnd_current_target = 0;
+ current_proxy_target = 0;
+ manager->object->deleteLater();
+ manager->object = 0;
+ delete xdnd_data.deco;
+ xdnd_data.deco = 0;
+ return true;
+ }
+ }
+ if (xdnd_dragsource) {
+ xdnd_dragsource = 0;
+ if (currentWindow) {
+ QApplication::postEvent(currentWindow, new QDragLeaveEvent);
+ currentWindow = 0;
+ }
+ return true;
+ }
+ return false;
+}
+
+void QX11Data::xdndHandleSelectionRequest(const XSelectionRequestEvent * req)
+{
+ if (!req)
+ return;
+ XEvent evt;
+ evt.xselection.type = SelectionNotify;
+ evt.xselection.display = req->display;
+ evt.xselection.requestor = req->requestor;
+ evt.xselection.selection = req->selection;
+ evt.xselection.target = XNone;
+ evt.xselection.property = XNone;
+ evt.xselection.time = req->time;
+
+ QDragManager *manager = QDragManager::self();
+ QDrag *currentObject = manager->object;
+
+ // which transaction do we use? (note: -2 means use current manager->object)
+ int at = -1;
+
+ // figure out which data the requestor is really interested in
+ if (manager->object && req->time == qt_xdnd_source_current_time) {
+ // requestor wants the current drag data
+ at = -2;
+ } else {
+ // if someone has requested data in response to XdndDrop, find the corresponding transaction. the
+ // spec says to call XConvertSelection() using the timestamp from the XdndDrop
+ at = findXdndDropTransactionByTime(req->time);
+ if (at == -1) {
+ // no dice, perhaps the client was nice enough to use the same window id in XConvertSelection()
+ // that we sent the XdndDrop event to.
+ at = findXdndDropTransactionByWindow(req->requestor);
+ }
+ if (at == -1 && req->time == CurrentTime) {
+ // previous Qt versions always requested the data on a child of the target window
+ // using CurrentTime... but it could be asking for either drop data or the current drag's data
+ Window target = findXdndAwareParent(req->requestor);
+ if (target) {
+ if (qt_xdnd_current_target && qt_xdnd_current_target == target)
+ at = -2;
+ else
+ at = findXdndDropTransactionByWindow(target);
+ }
+ }
+ }
+ if (at >= 0) {
+ restartXdndDropExpiryTimer();
+
+ // use the drag object from an XdndDrop tansaction
+ manager->object = X11->dndDropTransactions.at(at).object;
+ } else if (at != -2) {
+ // no transaction found, we'll have to reject the request
+ manager->object = 0;
+ }
+ if (manager->object) {
+ Atom atomFormat = req->target;
+ int dataFormat = 0;
+ QByteArray data;
+ if (X11->xdndMimeDataForAtom(req->target, manager->dragPrivate()->data,
+ &data, &atomFormat, &dataFormat)) {
+ int dataSize = data.size() / (dataFormat / 8);
+ XChangeProperty (X11->display, req->requestor, req->property,
+ atomFormat, dataFormat, PropModeReplace,
+ (unsigned char *)data.data(), dataSize);
+ evt.xselection.property = req->property;
+ evt.xselection.target = atomFormat;
+ }
+ }
+
+ // reset manager->object in case we modified it above
+ manager->object = currentObject;
+
+ // ### this can die if req->requestor crashes at the wrong
+ // ### moment
+ XSendEvent(X11->display, req->requestor, False, 0, &evt);
+}
+
+
+
+/*
+ Enable drag and drop for widget w by installing the proper
+ properties on w's toplevel widget.
+*/
+bool QX11Data::dndEnable(QWidget* w, bool on)
+{
+ w = w->window();
+
+ if (bool(((QExtraWidget*)w)->topData()->dnd) == on)
+ return true; // been there, done that
+ ((QExtraWidget*)w)->topData()->dnd = on ? 1 : 0;
+
+ motifdndEnable(w, on);
+ return xdndEnable(w, on);
+}
+
+Qt::DropAction QDragManager::drag(QDrag * o)
+{
+ if (object == o || !o || !o->d_func()->source)
+ return Qt::IgnoreAction;
+
+ if (object) {
+ cancel();
+ qApp->removeEventFilter(this);
+ beingCancelled = false;
+ }
+
+ if (object) {
+ // the last drag and drop operation hasn't finished, so we are going to wait
+ // for one second to see if it does... if the finish message comes after this,
+ // then we could still have problems, but this is highly unlikely
+ QApplication::flush();
+
+ QElapsedTimer timer;
+ timer.start();
+ do {
+ XEvent event;
+ if (XCheckTypedEvent(X11->display, ClientMessage, &event))
+ qApp->x11ProcessEvent(&event);
+
+ // sleep 50 ms, so we don't use up CPU cycles all the time.
+ struct timeval usleep_tv;
+ usleep_tv.tv_sec = 0;
+ usleep_tv.tv_usec = 50000;
+ select(0, 0, 0, 0, &usleep_tv);
+ } while (object && timer.hasExpired(1000));
+ }
+
+ object = o;
+ object->d_func()->target = 0;
+ xdnd_data.deco = new QShapedPixmapWidget(object->source()->window());
+
+ willDrop = false;
+
+ updatePixmap();
+
+ qApp->installEventFilter(this);
+ XSetSelectionOwner(X11->display, ATOM(XdndSelection), dragPrivate()->source->window()->internalWinId(), X11->time);
+ global_accepted_action = Qt::CopyAction;
+ source_sameanswer = QRect();
+#ifndef QT_NO_CURSOR
+ // set the override cursor (must be done here, since it is updated
+ // in the call to move() below)
+ qApp->setOverrideCursor(Qt::ArrowCursor);
+ restoreCursor = true;
+#endif
+ move(QCursor::pos());
+ heartbeat = startTimer(200);
+
+ qt_xdnd_dragging = true;
+
+ if (!QWidget::mouseGrabber())
+ xdnd_data.deco->grabMouse();
+
+ eventLoop = new QEventLoop;
+ (void) eventLoop->exec();
+ delete eventLoop;
+ eventLoop = 0;
+
+#ifndef QT_NO_CURSOR
+ if (restoreCursor) {
+ qApp->restoreOverrideCursor();
+ restoreCursor = false;
+ }
+#endif
+
+ // delete cursors as they may be different next drag.
+ delete noDropCursor;
+ noDropCursor = 0;
+ delete copyCursor;
+ copyCursor = 0;
+ delete moveCursor;
+ moveCursor = 0;
+ delete linkCursor;
+ linkCursor = 0;
+
+ delete xdnd_data.deco;
+ xdnd_data.deco = 0;
+ if (heartbeat != -1)
+ killTimer(heartbeat);
+ heartbeat = -1;
+ qt_xdnd_current_screen = -1;
+ qt_xdnd_dragging = false;
+
+ return global_accepted_action;
+ // object persists until we get an xdnd_finish message
+}
+
+#endif
+
+
+static xcb_window_t xdndProxy(QXcbConnection *c, xcb_window_t w)
+{
+ xcb_window_t proxy = XCB_NONE;
+
+ xcb_get_property_cookie_t cookie = xcb_get_property(c->xcb_connection(), false, w, c->atom(QXcbAtom::XdndProxy),
+ QXcbAtom::XA_WINDOW, 0, 1);
+ xcb_get_property_reply_t *reply = xcb_get_property_reply(c->xcb_connection(), cookie, 0);
+
+ if (reply && reply->type == QXcbAtom::XA_WINDOW)
+ proxy = *((xcb_window_t *)xcb_get_property_value(reply));
+ free(reply);
+
+ if (proxy == XCB_NONE)
+ return proxy;
+
+ // exists and is real?
+ cookie = xcb_get_property(c->xcb_connection(), false, proxy, c->atom(QXcbAtom::XdndProxy),
+ QXcbAtom::XA_WINDOW, 0, 1);
+ reply = xcb_get_property_reply(c->xcb_connection(), cookie, 0);
+
+ if (reply && reply->type == QXcbAtom::XA_WINDOW) {
+ xcb_window_t p = *((xcb_window_t *)xcb_get_property_value(reply));
+ if (proxy != p)
+ proxy = 0;
+ } else {
+ proxy = 0;
+ }
+
+ free(reply);
+
+ return proxy;
+}
+
+bool QXcbDrag::dndEnable(QXcbWindow *w, bool on)
+{
+ DNDDEBUG << "xdndEnable" << w << on;
+ if (on) {
+ QXcbWindow *xdnd_widget = 0;
+ if ((w->window()->windowType() == Qt::Desktop)) {
+ if (desktop_proxy) // *WE* already have one.
+ return false;
+
+ xcb_grab_server(connection()->xcb_connection());
+
+ // As per Xdnd4, use XdndProxy
+ xcb_window_t proxy_id = xdndProxy(connection(), w->xcb_window());
+
+ if (!proxy_id) {
+ desktop_proxy = new QWindow;
+ xdnd_widget = static_cast<QXcbWindow *>(desktop_proxy->handle());
+ proxy_id = xdnd_widget->xcb_window();
+ xcb_atom_t xdnd_proxy = connection()->atom(QXcbAtom::XdndProxy);
+ xcb_change_property(connection()->xcb_connection(), XCB_PROP_MODE_REPLACE, w->xcb_window(), xdnd_proxy,
+ QXcbAtom::XA_WINDOW, 32, 1, &proxy_id);
+ xcb_change_property(connection()->xcb_connection(), XCB_PROP_MODE_REPLACE, proxy_id, xdnd_proxy,
+ QXcbAtom::XA_WINDOW, 32, 1, &proxy_id);
+ }
+
+ xcb_ungrab_server(connection()->xcb_connection());
+ } else {
+ xdnd_widget = w;
+ }
+ if (xdnd_widget) {
+ DNDDEBUG << "setting XdndAware for" << xdnd_widget << xdnd_widget->xcb_window();
+ xcb_atom_t atm = xdnd_version;
+ xcb_change_property(connection()->xcb_connection(), XCB_PROP_MODE_REPLACE, xdnd_widget->xcb_window(),
+ connection()->atom(QXcbAtom::XdndAware), QXcbAtom::XA_ATOM, 32, 1, &atm);
+ return true;
+ } else {
+ return false;
+ }
+ } else {
+ if ((w->window()->windowType() == Qt::Desktop)) {
+ xcb_delete_property(connection()->xcb_connection(), w->xcb_window(), connection()->atom(QXcbAtom::XdndProxy));
+ delete desktop_proxy;
+ desktop_proxy = 0;
+ } else {
+ DNDDEBUG << "not deleting XDndAware";
+ }
+ return true;
+ }
+}
+
+
+
+
+QDropData::QDropData(QXcbDrag *d)
+ : QXcbMime(),
+ drag(d)
+{
+}
+
+QDropData::~QDropData()
+{
+}
+
+QVariant QDropData::retrieveData_sys(const QString &mimetype, QVariant::Type requestedType) const
+{
+ QByteArray mime = mimetype.toLatin1();
+ QVariant data = /*X11->motifdnd_active
+ ? X11->motifdndObtainData(mime)
+ :*/ xdndObtainData(mime, requestedType);
+ return data;
+}
+
+QVariant QDropData::xdndObtainData(const QByteArray &format, QVariant::Type requestedType) const
+{
+ QByteArray result;
+
+ QDragManager *manager = QDragManager::self();
+ QXcbConnection *c = drag->connection();
+ QXcbWindow *xcb_window = c->platformWindowFromId(drag->xdnd_dragsource);
+ if (xcb_window && manager->object && xcb_window->window()->windowType() != Qt::Desktop) {
+ QDragPrivate *o = manager->dragPrivate();
+ if (o->data->hasFormat(QLatin1String(format)))
+ result = o->data->data(QLatin1String(format));
+ return result;
+ }
+
+ QList<xcb_atom_t> atoms = drag->xdnd_types;
+ QByteArray encoding;
+ xcb_atom_t a = mimeAtomForFormat(c, QLatin1String(format), requestedType, atoms, &encoding);
+ if (a == XCB_NONE)
+ return result;
+
+ if (c->clipboard()->getSelectionOwner(drag->connection()->atom(QXcbAtom::XdndSelection)) == XCB_NONE)
+ return result; // should never happen?
+
+ QWindow* tw = drag->currentWindow.data();
+ if (!drag->currentWindow || (drag->currentWindow.data()->windowType() == Qt::Desktop))
+ tw = new QWindow;
+ xcb_window_t win = ::xcb_window(tw);
+
+ xcb_atom_t xdnd_selection = c->atom(QXcbAtom::XdndSelection);
+ result = c->clipboard()->getSelection(win, xdnd_selection, a, xdnd_selection);
+
+ if (!drag->currentWindow || (drag->currentWindow.data()->windowType() == Qt::Desktop))
+ delete tw;
+
+ return mimeConvertToFormat(c, a, result, QLatin1String(format), requestedType, encoding);
+}
+
+
+bool QDropData::hasFormat_sys(const QString &format) const
+{
+ return formats().contains(format);
+}
+
+QStringList QDropData::formats_sys() const
+{
+ QStringList formats;
+// if (X11->motifdnd_active) {
+// int i = 0;
+// QByteArray fmt;
+// while (!(fmt = X11->motifdndFormat(i)).isEmpty()) {
+// formats.append(QLatin1String(fmt));
+// ++i;
+// }
+// } else {
+ for (int i = 0; i < drag->xdnd_types.size(); ++i) {
+ QString f = mimeAtomToString(drag->connection(), drag->xdnd_types.at(i));
+ if (!formats.contains(f))
+ formats.append(f);
+ }
+// }
+ return formats;
+}
+
+QT_END_NAMESPACE
diff --git a/src/plugins/platforms/xcb/qxcbdrag.h b/src/plugins/platforms/xcb/qxcbdrag.h
new file mode 100644
index 0000000000..4aab35b7b1
--- /dev/null
+++ b/src/plugins/platforms/xcb/qxcbdrag.h
@@ -0,0 +1,122 @@
+/****************************************************************************
+**
+** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
+** All rights reserved.
+** Contact: Nokia Corporation (qt-info@nokia.com)
+**
+** This file is part of the QtGui module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** GNU Lesser General Public License Usage
+** This file may be used under the terms of the GNU Lesser General Public
+** License version 2.1 as published by the Free Software Foundation and
+** appearing in the file LICENSE.LGPL included in the packaging of this
+** file. Please review the following information to ensure the GNU Lesser
+** General Public License version 2.1 requirements will be met:
+** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU General
+** Public License version 3.0 as published by the Free Software Foundation
+** and appearing in the file LICENSE.GPL included in the packaging of this
+** file. Please review the following information to ensure the GNU General
+** Public License version 3.0 requirements will be met:
+** http://www.gnu.org/copyleft/gpl.html.
+**
+** Other Usage
+** Alternatively, this file may be used in accordance with the terms and
+** conditions contained in a signed written agreement between you and Nokia.
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QXCBDRAG_H
+#define QXCBDRAG_H
+
+#include <qlist.h>
+#include <qplatformdrag_qpa.h>
+#include <qnamespace.h>
+#include <xcb/xcb.h>
+#include <qpoint.h>
+#include <qrect.h>
+#include <qsharedpointer.h>
+
+QT_BEGIN_NAMESPACE
+
+class QMouseEvent;
+class QWindow;
+class QXcbConnection;
+class QXcbWindow;
+class QDropData;
+
+class QXcbDrag : public QPlatformDrag
+{
+public:
+ QXcbDrag(QXcbConnection *c);
+ ~QXcbDrag();
+
+ virtual QMimeData *platformDropData();
+
+// virtual Qt::DropAction drag(QDrag *);
+
+ virtual void startDrag();
+ virtual void cancel();
+ virtual void move(const QMouseEvent *me);
+ virtual void drop(const QMouseEvent *me);
+
+ void handleEnter(QWindow *window, const xcb_client_message_event_t *event);
+ void handlePosition(QWindow *w, const xcb_client_message_event_t *event, bool passive);
+ void handleStatus(QWindow *w, const xcb_client_message_event_t *event, bool passive);
+ void handleLeave(QWindow *w, const xcb_client_message_event_t *event, bool /*passive*/);
+ void handleDrop(QWindow *, const xcb_client_message_event_t *event, bool passive);
+
+ bool dndEnable(QXcbWindow *win, bool on);
+
+ QXcbConnection *connection() const { return m_connection; }
+
+private:
+ friend class QDropData;
+
+ void handle_xdnd_position(QWindow *w, const xcb_client_message_event_t *event, bool passive);
+ void handle_xdnd_status(QWindow *, const xcb_client_message_event_t *event, bool);
+
+ Qt::DropAction toDropAction(xcb_atom_t atom) const;
+ xcb_atom_t toXdndAction(Qt::DropAction a) const;
+
+ QWeakPointer<QWindow> currentWindow;
+ QPoint currentPosition;
+
+ QXcbConnection *m_connection;
+ QDropData *dropData;
+
+ QWindow *desktop_proxy;
+
+ xcb_atom_t xdnd_dragsource;
+
+ // the types in this drop. 100 is no good, but at least it's big.
+ enum { xdnd_max_type = 100 };
+ QList<xcb_atom_t> xdnd_types;
+
+ xcb_timestamp_t target_time;
+ Qt::DropAction last_target_accepted_action;
+
+ // rectangle in which the answer will be the same
+ QRect source_sameanswer;
+ bool waiting_for_status;
+
+ // window to send events to (always valid if current_target)
+ xcb_window_t current_proxy_target;
+};
+
+QT_END_NAMESPACE
+
+#endif
diff --git a/src/plugins/platforms/xcb/qxcbintegration.cpp b/src/plugins/platforms/xcb/qxcbintegration.cpp
index c63a29b675..a7d88e7741 100644
--- a/src/plugins/platforms/xcb/qxcbintegration.cpp
+++ b/src/plugins/platforms/xcb/qxcbintegration.cpp
@@ -46,7 +46,7 @@
#include "qxcbwindowsurface.h"
#include "qxcbnativeinterface.h"
#include "qxcbclipboard.h"
-#include <qsimpledrag.h>
+#include "qxcbdrag.h"
#include <qgenericunixprintersupport.h>
@@ -70,13 +70,11 @@ QXcbIntegration::QXcbIntegration()
m_fontDatabase = new QGenericUnixFontDatabase();
m_nativeInterface = new QXcbNativeInterface;
- m_drag = new QSimpleDrag;
}
QXcbIntegration::~QXcbIntegration()
{
delete m_connection;
- delete m_drag;
}
bool QXcbIntegration::hasCapability(QPlatformIntegration::Capability cap) const
@@ -371,5 +369,5 @@ QPlatformClipboard *QXcbIntegration::clipboard() const
QPlatformDrag *QXcbIntegration::drag() const
{
- return m_drag;
+ return m_connection->drag();
}
diff --git a/src/plugins/platforms/xcb/qxcbintegration.h b/src/plugins/platforms/xcb/qxcbintegration.h
index 2ddbe9fa49..1c1ca24094 100644
--- a/src/plugins/platforms/xcb/qxcbintegration.h
+++ b/src/plugins/platforms/xcb/qxcbintegration.h
@@ -48,7 +48,6 @@
QT_BEGIN_NAMESPACE
class QXcbConnection;
-class QSimpleDrag;
class QXcbIntegration : public QPlatformIntegration
{
@@ -82,7 +81,6 @@ private:
QPlatformFontDatabase *m_fontDatabase;
QPlatformNativeInterface *m_nativeInterface;
QPlatformPrinterSupport *m_printerSupport;
- QSimpleDrag *m_drag;
};
QT_END_NAMESPACE
diff --git a/src/plugins/platforms/xcb/qxcbwindow.cpp b/src/plugins/platforms/xcb/qxcbwindow.cpp
index ca86cbdb56..b254fd5f13 100644
--- a/src/plugins/platforms/xcb/qxcbwindow.cpp
+++ b/src/plugins/platforms/xcb/qxcbwindow.cpp
@@ -45,6 +45,9 @@
#include "qxcbconnection.h"
#include "qxcbscreen.h"
+#include "qxcbdrag.h"
+
+
#ifdef XCB_USE_DRI2
#include "qdri2context.h"
#endif
@@ -268,6 +271,8 @@ void QXcbWindow::create()
if (wasCreated)
setWindowFlags(window()->windowFlags());
+
+ connection()->drag()->dndEnable(this, true);
}
QXcbWindow::~QXcbWindow()
@@ -933,7 +938,10 @@ void QXcbWindow::handleExposeEvent(const xcb_expose_event_t *event)
void QXcbWindow::handleClientMessageEvent(const xcb_client_message_event_t *event)
{
- if (event->format == 32 && event->type == atom(QXcbAtom::WM_PROTOCOLS)) {
+ if (event->format != 32)
+ return;
+
+ if (event->type == atom(QXcbAtom::WM_PROTOCOLS)) {
if (event->data.data32[0] == atom(QXcbAtom::WM_DELETE_WINDOW)) {
QWindowSystemInterface::handleCloseEvent(window());
} else if (event->data.data32[0] == atom(QXcbAtom::_NET_WM_PING)) {
@@ -952,6 +960,14 @@ void QXcbWindow::handleClientMessageEvent(const xcb_client_message_event_t *even
m_syncValue.lo = event->data.data32[2];
m_syncValue.hi = event->data.data32[3];
}
+ } else if (event->type == atom(QXcbAtom::XdndEnter)) {
+ connection()->drag()->handleEnter(window(), event);
+ } else if (event->type == atom(QXcbAtom::XdndPosition)) {
+ connection()->drag()->handlePosition(window(), event, false);
+ } else if (event->type == atom(QXcbAtom::XdndLeave)) {
+ connection()->drag()->handleLeave(window(), event, false);
+ } else if (event->type == atom(QXcbAtom::XdndDrop)) {
+ connection()->drag()->handleDrop(window(), event, false);
}
}
diff --git a/src/plugins/platforms/xcb/xcb.pro b/src/plugins/platforms/xcb/xcb.pro
index 5d48ff9b50..e7fee04094 100644
--- a/src/plugins/platforms/xcb/xcb.pro
+++ b/src/plugins/platforms/xcb/xcb.pro
@@ -11,6 +11,7 @@ SOURCES = \
qxcbintegration.cpp \
qxcbkeyboard.cpp \
qxcbmime.cpp \
+ qxcbdrag.cpp \
qxcbscreen.cpp \
qxcbwindow.cpp \
qxcbwindowsurface.cpp \
@@ -22,6 +23,7 @@ HEADERS = \
qxcbconnection.h \
qxcbintegration.h \
qxcbkeyboard.h \
+ qxcbdrag.h \
qxcbmime.h \
qxcbobject.h \
qxcbscreen.h \
@@ -72,7 +74,6 @@ QMAKE_CXXFLAGS += $$QMAKE_CFLAGS_XCB
load(qpa/fontdatabases/genericunix)
load(qpa/printersupport/genericunix)
-load(qpa/dnd/simple)
target.path += $$[QT_INSTALL_PLUGINS]/platforms
INSTALLS += target