summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-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