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:
parent
30b7c6512c
commit
c3f9de6296
@ -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;
|
||||
}
|
||||
|
@ -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;
|
||||
|
||||
|
@ -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);
|
||||
|
@ -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()))
|
||||
|
@ -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
@ -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
|
||||
|
@ -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)
|
||||
{
|
||||
|
Loading…
Reference in New Issue
Block a user