Implement XDnD in the xcb plugin

Ported most of the code to support dragging
from qdnd_x11.cpp to xcb. Some features are still
not working 100% correct, but it's becoming
usable.

Reviewed-by: Samuel
This commit is contained in:
Lars Knoll 2011-06-05 23:29:26 +02:00
parent 30b7c6512c
commit c3f9de6296
8 changed files with 656 additions and 752 deletions

View File

@ -143,6 +143,7 @@ QDragManager::QDragManager()
#ifdef Q_WS_X11
xdndMimeTransferedPixmapIndex = 0;
#endif
shapedPixmapWindow = 0;
possible_actions = Qt::IgnoreAction;
@ -298,50 +299,9 @@ static const char *const default_pm[] = {
static Qt::KeyboardModifiers oldstate;
class QShapedPixmapWindow : public QWindow {
QPixmap pixmap;
public:
QShapedPixmapWindow() :
QWindow(0)
{
setWindowFlags(Qt::Tool | Qt::FramelessWindowHint | Qt::X11BypassWindowManagerHint);
// ### Should we set the surface type to raster?
// ### FIXME
// setAttribute(Qt::WA_TransparentForMouseEvents);
}
void move(const QPoint &p) {
QRect g = geometry();
g.setTopLeft(p);
setGeometry(g);
}
void setPixmap(QPixmap pm)
{
pixmap = pm;
// ###
// if (!pixmap.mask().isNull()) {
// setMask(pixmap.mask());
// } else {
// clearMask();
// }
setGeometry(QRect(geometry().topLeft(), pm.size()));
}
// ### Get it painted again!
// void paintEvent(QPaintEvent*)
// {
// QPainter p(this);
// p.drawPixmap(0,0,pixmap);
// }
};
static QShapedPixmapWindow *qt_qws_dnd_deco = 0;
void QDragManager::updatePixmap()
{
if (qt_qws_dnd_deco) {
if (shapedPixmapWindow) {
QPixmap pm;
QPoint pm_hot(default_pm_hotx,default_pm_hoty);
if (object) {
@ -354,12 +314,12 @@ void QDragManager::updatePixmap()
defaultPm = new QPixmap(default_pm);
pm = *defaultPm;
}
qt_qws_dnd_deco->setPixmap(pm);
qt_qws_dnd_deco->move(QCursor::pos()-pm_hot);
shapedPixmapWindow->setPixmap(pm);
shapedPixmapWindow->move(QCursor::pos()-pm_hot);
if (willDrop) {
qt_qws_dnd_deco->show();
shapedPixmapWindow->show();
} else {
qt_qws_dnd_deco->hide();
shapedPixmapWindow->hide();
}
}
}
@ -368,8 +328,8 @@ void QDragManager::updateCursor()
{
#ifndef QT_NO_CURSOR
if (willDrop) {
if (qt_qws_dnd_deco)
qt_qws_dnd_deco->show();
if (shapedPixmapWindow)
shapedPixmapWindow->show();
if (currentActionForOverrideCursor != global_accepted_action) {
QGuiApplication::changeOverrideCursor(QCursor(dragCursor(global_accepted_action), 0, 0));
currentActionForOverrideCursor = global_accepted_action;
@ -380,8 +340,8 @@ void QDragManager::updateCursor()
QGuiApplication::changeOverrideCursor(QCursor(Qt::ForbiddenCursor));
currentActionForOverrideCursor = Qt::IgnoreAction;
}
if (qt_qws_dnd_deco)
qt_qws_dnd_deco->hide();
if (shapedPixmapWindow)
shapedPixmapWindow->hide();
}
#endif
}
@ -468,8 +428,8 @@ Qt::DropAction QDragManager::drag(QDrag *o)
}
object = o;
if (!qt_qws_dnd_deco)
qt_qws_dnd_deco = new QShapedPixmapWindow();
if (!shapedPixmapWindow)
shapedPixmapWindow = new QShapedPixmapWindow();
oldstate = Qt::NoModifier; // #### Should use state that caused the drag
// drag_mode = mode;
@ -494,8 +454,8 @@ Qt::DropAction QDragManager::drag(QDrag *o)
delete eventLoop;
eventLoop = 0;
delete qt_qws_dnd_deco;
qt_qws_dnd_deco = 0;
delete shapedPixmapWindow;
shapedPixmapWindow = 0;
return global_accepted_action;
}

View File

@ -59,6 +59,7 @@
#include "QtGui/qdrag.h"
#include "QtGui/qpixmap.h"
#include "QtGui/qcursor.h"
#include "QtGui/qwindow.h"
#include "QtCore/qpoint.h"
#include "private/qobject_p.h"
@ -108,6 +109,44 @@ public:
Qt::DropAction defaultDropAction;
};
class QShapedPixmapWindow : public QWindow {
QPixmap pixmap;
public:
QShapedPixmapWindow() :
QWindow(0)
{
setWindowFlags(Qt::Tool | Qt::FramelessWindowHint | Qt::X11BypassWindowManagerHint);
// ### Should we set the surface type to raster?
// ### FIXME
// setAttribute(Qt::WA_TransparentForMouseEvents);
}
void move(const QPoint &p) {
QRect g = geometry();
g.setTopLeft(p);
setGeometry(g);
}
void setPixmap(QPixmap pm)
{
pixmap = pm;
// ###
// if (!pixmap.mask().isNull()) {
// setMask(pixmap.mask());
// } else {
// clearMask();
// }
setGeometry(QRect(geometry().topLeft(), pm.size()));
}
// ### Get it painted again!
// void paintEvent(QPaintEvent*)
// {
// QPainter p(this);
// p.drawPixmap(0,0,pixmap);
// }
};
class Q_GUI_EXPORT QDragManager : public QObject {
Q_OBJECT
@ -159,6 +198,8 @@ public:
// Shift/Ctrl handling, and final drop status
Qt::DropAction global_accepted_action;
QShapedPixmapWindow *shapedPixmapWindow;
private:
QMimeData *platformDropData;

View File

@ -228,7 +228,7 @@ void QXcbClipboard::setMimeData(QMimeData *data, QClipboard::Mode mode)
*d = data;
}
xcb_set_selection_owner(m_connection->xcb_connection(), newOwner, modeAtom, XCB_CURRENT_TIME);
xcb_set_selection_owner(m_connection->xcb_connection(), newOwner, modeAtom, m_connection->time());
if (getSelectionOwner(modeAtom) != newOwner) {
qWarning("QClipboard::setData: Cannot set X11 selection owner");
@ -390,7 +390,7 @@ void QXcbClipboard::handleSelectionRequest(xcb_selection_request_event_t *req)
} else if (req->selection == m_connection->atom(QXcbAtom::CLIPBOARD)) {
d = m_clientClipboard;
} else {
qWarning("QClipboard: Unknown selection '%lx'", (long)req->selection);
qWarning() << "QClipboard: Unknown selection" << m_connection->atomName(req->selection);
xcb_send_event(m_connection->xcb_connection(), false, req->requestor, XCB_EVENT_MASK_NO_EVENT, (const char *)&event);
return;
}
@ -505,7 +505,7 @@ bool QXcbClipboard::clipboardReadProperty(xcb_window_t win, xcb_atom_t property,
format = &dummy_format;
// Don't read anything, just get the size of the property data
xcb_get_property_cookie_t cookie = xcb_get_property(m_connection->xcb_connection(), false, win, property, XCB_GET_PROPERTY_TYPE_ANY, 0, 0);
xcb_get_property_cookie_t cookie = Q_XCB_CALL(xcb_get_property(m_connection->xcb_connection(), false, win, property, XCB_GET_PROPERTY_TYPE_ANY, 0, 0));
xcb_get_property_reply_t *reply = xcb_get_property_reply(m_connection->xcb_connection(), cookie, 0);
if (!reply || reply->type == XCB_NONE) {
buffer->resize(0);
@ -546,7 +546,7 @@ bool QXcbClipboard::clipboardReadProperty(xcb_window_t win, xcb_atom_t property,
while (bytes_left) {
// more to read...
xcb_get_property_cookie_t cookie = xcb_get_property(m_connection->xcb_connection(), false, win, property, XCB_GET_PROPERTY_TYPE_ANY, offset, maxsize/4);
xcb_get_property_cookie_t cookie = Q_XCB_CALL(xcb_get_property(m_connection->xcb_connection(), false, win, property, XCB_GET_PROPERTY_TYPE_ANY, offset, maxsize/4));
reply = xcb_get_property_reply(m_connection->xcb_connection(), cookie, 0);
if (!reply || reply->type == XCB_NONE) {
free(reply);

View File

@ -109,6 +109,8 @@ QXcbConnection::QXcbConnection(const char *displayName)
initializeAllAtoms();
m_time = XCB_CURRENT_TIME;
xcb_screen_iterator_t it = xcb_setup_roots_iterator(m_setup);
int screenNumber = 0;
@ -450,7 +452,7 @@ void QXcbConnection::handleXcbEvent(xcb_generic_event_t *event)
case XCB_UNMAP_NOTIFY:
HANDLE_PLATFORM_WINDOW_EVENT(xcb_unmap_notify_event_t, event, handleUnmapNotifyEvent);
case XCB_CLIENT_MESSAGE:
HANDLE_PLATFORM_WINDOW_EVENT(xcb_client_message_event_t, window, handleClientMessageEvent);
handleClientMessageEvent((xcb_client_message_event_t *)event);
case XCB_ENTER_NOTIFY:
HANDLE_PLATFORM_WINDOW_EVENT(xcb_enter_notify_event_t, event, handleEnterNotifyEvent);
case XCB_LEAVE_NOTIFY:
@ -467,9 +469,16 @@ void QXcbConnection::handleXcbEvent(xcb_generic_event_t *event)
m_keyboard->handleMappingNotifyEvent((xcb_mapping_notify_event_t *)event);
break;
case XCB_SELECTION_REQUEST:
m_clipboard->handleSelectionRequest((xcb_selection_request_event_t *)event);
{
xcb_selection_request_event_t *sr = (xcb_selection_request_event_t *)event;
if (sr->selection == atom(QXcbAtom::XdndSelection))
m_drag->handleSelectionRequest(sr);
else
m_clipboard->handleSelectionRequest(sr);
break;
}
case XCB_SELECTION_CLEAR:
setTime(((xcb_selection_clear_event_t *)event)->time);
qDebug() << "XCB_SELECTION_CLEAR";
handled = false;
break;
@ -477,6 +486,11 @@ void QXcbConnection::handleXcbEvent(xcb_generic_event_t *event)
qDebug() << "XCB_SELECTION_NOTIFY";
handled = false;
break;
case XCB_PROPERTY_NOTIFY:
setTime(((xcb_property_notify_event_t *)event)->time);
// qDebug() << "XCB_PROPERTY_NOTIFY";
handled = false;
break;
default:
handled = false;
break;
@ -533,6 +547,25 @@ void QXcbConnection::processXcbEvents()
xcb_flush(xcb_connection());
}
void QXcbConnection::handleClientMessageEvent(const xcb_client_message_event_t *event)
{
if (event->format != 32)
return;
if (event->type == atom(QXcbAtom::XdndStatus)) {
drag()->handleStatus(event, false);
} else if (event->type == atom(QXcbAtom::XdndFinished)) {
drag()->handleFinished(event, false);
}
QXcbWindow *window = platformWindowFromId(event->window);
if (!window)
return;
window->handleClientMessageEvent(event);
}
xcb_generic_event_t *QXcbConnection::checkEvent(int type)
{
while (xcb_generic_event_t *event = xcb_poll_for_event(xcb_connection()))

View File

@ -238,7 +238,7 @@ public:
QXcbConnection *connection() const { return const_cast<QXcbConnection *>(this); }
QList<QXcbScreen *> screens() const { return m_screens; }
const QList<QXcbScreen *> &screens() const { return m_screens; }
int primaryScreen() const { return m_primaryScreen; }
xcb_atom_t atom(QXcbAtom::Atom atom);
@ -290,6 +290,9 @@ public:
typedef bool (*PeekFunc)(xcb_generic_event_t *);
void addPeekFunc(PeekFunc f);
inline xcb_timestamp_t time() const { return m_time; }
inline void setTime(xcb_timestamp_t t) { if (t > m_time) m_time = t; }
private slots:
void processXcbEvents();
@ -299,6 +302,7 @@ private:
#ifdef XCB_USE_DRI2
void initializeDri2();
#endif
void handleClientMessageEvent(const xcb_client_message_event_t *event);
xcb_connection_t *m_connection;
const xcb_setup_t *m_setup;
@ -308,6 +312,8 @@ private:
xcb_atom_t m_allAtoms[QXcbAtom::NAtoms];
xcb_timestamp_t m_time;
QByteArray m_displayName;
QXcbKeyboard *m_keyboard;

File diff suppressed because it is too large Load Diff

View File

@ -49,6 +49,7 @@
#include <qpoint.h>
#include <qrect.h>
#include <qsharedpointer.h>
#include <qvector.h>
QT_BEGIN_NAMESPACE
@ -57,8 +58,10 @@ class QWindow;
class QXcbConnection;
class QXcbWindow;
class QDropData;
class QXcbScreen;
class QDrag;
class QXcbDrag : public QPlatformDrag
class QXcbDrag : public QObject, public QPlatformDrag
{
public:
QXcbDrag(QXcbConnection *c);
@ -72,22 +75,32 @@ public:
virtual void cancel();
virtual void move(const QMouseEvent *me);
virtual void drop(const QMouseEvent *me);
void endDrag();
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);
void handleStatus(const xcb_client_message_event_t *event, bool passive);
void handleSelectionRequest(const xcb_selection_request_event_t *event);
void handleFinished(const xcb_client_message_event_t *event, bool passive);
bool dndEnable(QXcbWindow *win, bool on);
QXcbConnection *connection() const { return m_connection; }
protected:
void timerEvent(QTimerEvent* e);
private:
friend class QDropData;
void init();
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);
void handle_xdnd_status(const xcb_client_message_event_t *event, bool);
void send_leave();
Qt::DropAction toDropAction(xcb_atom_t atom) const;
xcb_atom_t toXdndAction(Qt::DropAction a) const;
@ -107,14 +120,40 @@ private:
QList<xcb_atom_t> xdnd_types;
xcb_timestamp_t target_time;
xcb_timestamp_t source_time;
Qt::DropAction last_target_accepted_action;
// rectangle in which the answer will be the same
QRect source_sameanswer;
bool waiting_for_status;
// top-level window we sent position to last.
xcb_window_t current_target;
// window to send events to (always valid if current_target)
xcb_window_t current_proxy_target;
QXcbScreen *current_screen;
int heartbeat;
bool xdnd_dragging;
QVector<xcb_atom_t> drag_types;
struct Transaction
{
xcb_timestamp_t timestamp;
xcb_window_t target;
xcb_window_t proxy_target;
QWindow *targetWindow;
// QWidget *embedding_widget;
QDrag *object;
};
QList<Transaction> transactions;
int transaction_expiry_timer;
void restartDropExpiryTimer();
int findTransactionByWindow(xcb_window_t window);
int findTransactionByTime(xcb_timestamp_t timestamp);
};
QT_END_NAMESPACE

View File

@ -944,6 +944,9 @@ void QXcbWindow::handleClientMessageEvent(const xcb_client_message_event_t *even
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::WM_TAKE_FOCUS)) {
connection()->setTime(event->data.data32[1]);
// ### handle take focus!
} else if (event->data.data32[0] == atom(QXcbAtom::_NET_WM_PING)) {
xcb_client_message_event_t reply = *event;
@ -953,6 +956,7 @@ void QXcbWindow::handleClientMessageEvent(const xcb_client_message_event_t *even
xcb_send_event(xcb_connection(), 0, m_screen->root(), XCB_EVENT_MASK_STRUCTURE_NOTIFY | XCB_EVENT_MASK_SUBSTRUCTURE_REDIRECT, (const char *)&reply);
xcb_flush(xcb_connection());
} else if (event->data.data32[0] == atom(QXcbAtom::_NET_WM_SYNC_REQUEST)) {
connection()->setTime(event->data.data32[1]);
if (!m_hasReceivedSyncRequest) {
m_hasReceivedSyncRequest = true;
printf("Window manager supports _NET_WM_SYNC_REQUEST, syncing resizes\n");
@ -1077,6 +1081,8 @@ void QXcbWindow::handleMotionNotifyEvent(const xcb_motion_notify_event_t *event)
void QXcbWindow::handleMouseEvent(xcb_button_t detail, uint16_t state, xcb_timestamp_t time, const QPoint &local, const QPoint &global)
{
connection()->setTime(time);
Qt::MouseButtons buttons = translateMouseButtons(state);
Qt::MouseButton button = translateMouseButton(detail);
@ -1087,6 +1093,8 @@ void QXcbWindow::handleMouseEvent(xcb_button_t detail, uint16_t state, xcb_times
void QXcbWindow::handleEnterNotifyEvent(const xcb_enter_notify_event_t *event)
{
connection()->setTime(event->time);
if ((event->mode != XCB_NOTIFY_MODE_NORMAL && event->mode != XCB_NOTIFY_MODE_UNGRAB)
|| event->detail == XCB_NOTIFY_DETAIL_VIRTUAL
|| event->detail == XCB_NOTIFY_DETAIL_NONLINEAR_VIRTUAL)
@ -1099,6 +1107,8 @@ void QXcbWindow::handleEnterNotifyEvent(const xcb_enter_notify_event_t *event)
void QXcbWindow::handleLeaveNotifyEvent(const xcb_leave_notify_event_t *event)
{
connection()->setTime(event->time);
if ((event->mode != XCB_NOTIFY_MODE_NORMAL && event->mode != XCB_NOTIFY_MODE_UNGRAB)
|| event->detail == XCB_NOTIFY_DETAIL_INFERIOR)
{