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
This commit is contained in:
Lars Knoll 2011-06-02 23:20:47 +02:00
parent 92edbd2060
commit 30b7c6512c
10 changed files with 1883 additions and 26 deletions

View File

@ -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);
}
}

View File

@ -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);

View File

@ -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));

View File

@ -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;

File diff suppressed because it is too large Load Diff

View File

@ -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

View File

@ -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();
}

View File

@ -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

View File

@ -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);
}
}

View File

@ -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