From c55a36cb9015cf1eebd49eaa5b1b4f4ec9b28451 Mon Sep 17 00:00:00 2001 From: Alexander Volkov Date: Wed, 7 Oct 2015 16:58:09 +0300 Subject: [PATCH] xcb: Fix DnD for separate X screens Recreate QShapedPixmapWindow when the cursor goes to another X screen. Change-Id: Ifd4c4281971b23abc45a9f6c0509832a45c31521 Reviewed-by: Lars Knoll --- src/gui/kernel/qshapedpixmapdndwindow.cpp | 4 +- src/gui/kernel/qshapedpixmapdndwindow_p.h | 2 +- src/gui/kernel/qsimpledrag.cpp | 29 +++++++------- src/gui/kernel/qsimpledrag_p.h | 2 + src/plugins/platforms/xcb/qxcbconnection.h | 2 + src/plugins/platforms/xcb/qxcbcursor.cpp | 45 ++++++++++++---------- src/plugins/platforms/xcb/qxcbcursor.h | 2 +- src/plugins/platforms/xcb/qxcbdrag.cpp | 45 +++++++--------------- src/plugins/platforms/xcb/qxcbdrag.h | 2 +- src/plugins/platforms/xcb/qxcbscreen.cpp | 9 +++++ src/plugins/platforms/xcb/qxcbscreen.h | 3 ++ 11 files changed, 75 insertions(+), 70 deletions(-) diff --git a/src/gui/kernel/qshapedpixmapdndwindow.cpp b/src/gui/kernel/qshapedpixmapdndwindow.cpp index 8f80789fb0..5736c41e25 100644 --- a/src/gui/kernel/qshapedpixmapdndwindow.cpp +++ b/src/gui/kernel/qshapedpixmapdndwindow.cpp @@ -38,8 +38,8 @@ QT_BEGIN_NAMESPACE -QShapedPixmapWindow::QShapedPixmapWindow() - : QWindow(), +QShapedPixmapWindow::QShapedPixmapWindow(QScreen *screen) + : QWindow(screen), m_backingStore(0) { QSurfaceFormat format; diff --git a/src/gui/kernel/qshapedpixmapdndwindow_p.h b/src/gui/kernel/qshapedpixmapdndwindow_p.h index fc311cff92..7536c09165 100644 --- a/src/gui/kernel/qshapedpixmapdndwindow_p.h +++ b/src/gui/kernel/qshapedpixmapdndwindow_p.h @@ -55,7 +55,7 @@ class QShapedPixmapWindow : public QWindow { Q_OBJECT public: - QShapedPixmapWindow(); + explicit QShapedPixmapWindow(QScreen *screen = 0); ~QShapedPixmapWindow(); void render(); diff --git a/src/gui/kernel/qsimpledrag.cpp b/src/gui/kernel/qsimpledrag.cpp index b02f1dd8bd..6acac4cade 100644 --- a/src/gui/kernel/qsimpledrag.cpp +++ b/src/gui/kernel/qsimpledrag.cpp @@ -203,25 +203,15 @@ void QBasicDrag::restoreCursor() void QBasicDrag::startDrag() { - // ### TODO Check if its really necessary to have m_drag_icon_window - // when QDrag is used without a pixmap - QDrag::setPixmap() - if (!m_drag_icon_window) - m_drag_icon_window = new QShapedPixmapWindow(); - - m_drag_icon_window->setPixmap(m_drag->pixmap()); - m_drag_icon_window->setHotspot(m_drag->hotSpot()); - + QPoint pos; #ifndef QT_NO_CURSOR - QPoint pos = QCursor::pos(); + pos = QCursor::pos(); if (pos.x() == int(qInf())) { // ### fixme: no mouse pos registered. Get pos from touch... pos = QPoint(); } - m_drag_icon_window->updateGeometry(pos); #endif - - m_drag_icon_window->setVisible(true); - + recreateShapedPixmapWindow(Q_NULLPTR, pos); enableEventFilter(); } @@ -229,6 +219,19 @@ void QBasicDrag::endDrag() { } +void QBasicDrag::recreateShapedPixmapWindow(QScreen *screen, const QPoint &pos) +{ + delete m_drag_icon_window; + // ### TODO Check if its really necessary to have m_drag_icon_window + // when QDrag is used without a pixmap - QDrag::setPixmap() + m_drag_icon_window = new QShapedPixmapWindow(screen); + + m_drag_icon_window->setPixmap(m_drag->pixmap()); + m_drag_icon_window->setHotspot(m_drag->hotSpot()); + m_drag_icon_window->updateGeometry(pos); + m_drag_icon_window->setVisible(true); +} + void QBasicDrag::cancel() { disableEventFilter(); diff --git a/src/gui/kernel/qsimpledrag_p.h b/src/gui/kernel/qsimpledrag_p.h index a011475381..4c9edbae05 100644 --- a/src/gui/kernel/qsimpledrag_p.h +++ b/src/gui/kernel/qsimpledrag_p.h @@ -58,6 +58,7 @@ class QWindow; class QEventLoop; class QDropData; class QShapedPixmapWindow; +class QScreen; class Q_GUI_EXPORT QBasicDrag : public QPlatformDrag, public QObject { @@ -80,6 +81,7 @@ protected: void moveShapedPixmapWindow(const QPoint &deviceIndependentPosition); QShapedPixmapWindow *shapedPixmapWindow() const { return m_drag_icon_window; } + void recreateShapedPixmapWindow(QScreen *screen, const QPoint &pos); void updateCursor(Qt::DropAction action); bool canDrop() const { return m_can_drop; } diff --git a/src/plugins/platforms/xcb/qxcbconnection.h b/src/plugins/platforms/xcb/qxcbconnection.h index 6ee32bf600..fb5b941fff 100644 --- a/src/plugins/platforms/xcb/qxcbconnection.h +++ b/src/plugins/platforms/xcb/qxcbconnection.h @@ -376,8 +376,10 @@ public: QXcbConnection *connection() const { return const_cast(this); } + const QList &virtualDesktops() const { return m_virtualDesktops; } const QList &screens() const { return m_screens; } int primaryScreenNumber() const { return m_primaryScreenNumber; } + QXcbVirtualDesktop *primaryVirtualDesktop() const { return m_virtualDesktops.value(m_primaryScreenNumber); } QXcbScreen *primaryScreen() const; inline xcb_atom_t atom(QXcbAtom::Atom atom) const { return m_allAtoms[atom]; } diff --git a/src/plugins/platforms/xcb/qxcbcursor.cpp b/src/plugins/platforms/xcb/qxcbcursor.cpp index 0cd9159052..b321ed95dc 100644 --- a/src/plugins/platforms/xcb/qxcbcursor.cpp +++ b/src/plugins/platforms/xcb/qxcbcursor.cpp @@ -607,30 +607,33 @@ xcb_cursor_t QXcbCursor::createBitmapCursor(QCursor *cursor) } #endif -void QXcbCursor::queryPointer(QXcbConnection *c, xcb_window_t *rootWin, QPoint *pos, int *keybMask) +void QXcbCursor::queryPointer(QXcbConnection *c, QXcbVirtualDesktop **virtualDesktop, QPoint *pos, int *keybMask) { if (pos) *pos = QPoint(); - xcb_screen_iterator_t it = xcb_setup_roots_iterator(c->setup()); - while (it.rem) { - xcb_window_t root = it.data->root; - xcb_query_pointer_cookie_t cookie = xcb_query_pointer(c->xcb_connection(), root); - xcb_generic_error_t *err = 0; - xcb_query_pointer_reply_t *reply = xcb_query_pointer_reply(c->xcb_connection(), cookie, &err); - if (!err && reply) { - if (pos) - *pos = QPoint(reply->root_x, reply->root_y); - if (rootWin) - *rootWin = root; - if (keybMask) - *keybMask = reply->mask; - free(reply); - return; + + xcb_window_t root = c->primaryVirtualDesktop()->root(); + xcb_query_pointer_cookie_t cookie = xcb_query_pointer(c->xcb_connection(), root); + xcb_generic_error_t *err = 0; + xcb_query_pointer_reply_t *reply = xcb_query_pointer_reply(c->xcb_connection(), cookie, &err); + if (!err && reply) { + if (virtualDesktop) { + foreach (QXcbVirtualDesktop *vd, c->virtualDesktops()) { + if (vd->root() == reply->root) { + *virtualDesktop = vd; + break; + } + } } - free(err); + if (pos) + *pos = QPoint(reply->root_x, reply->root_y); + if (keybMask) + *keybMask = reply->mask; free(reply); - xcb_screen_next(&it); + return; } + free(err); + free(reply); } QPoint QXcbCursor::pos() const @@ -642,9 +645,9 @@ QPoint QXcbCursor::pos() const void QXcbCursor::setPos(const QPoint &pos) { - xcb_window_t root = 0; - queryPointer(connection(), &root, 0); - xcb_warp_pointer(xcb_connection(), XCB_NONE, root, 0, 0, 0, 0, pos.x(), pos.y()); + QXcbVirtualDesktop *virtualDesktop = Q_NULLPTR; + queryPointer(connection(), &virtualDesktop, 0); + xcb_warp_pointer(xcb_connection(), XCB_NONE, virtualDesktop->root(), 0, 0, 0, 0, pos.x(), pos.y()); xcb_flush(xcb_connection()); } diff --git a/src/plugins/platforms/xcb/qxcbcursor.h b/src/plugins/platforms/xcb/qxcbcursor.h index f4f6e61706..3c6dece1f2 100644 --- a/src/plugins/platforms/xcb/qxcbcursor.h +++ b/src/plugins/platforms/xcb/qxcbcursor.h @@ -75,7 +75,7 @@ public: QPoint pos() const Q_DECL_OVERRIDE; void setPos(const QPoint &pos) Q_DECL_OVERRIDE; - static void queryPointer(QXcbConnection *c, xcb_window_t *rootWin, QPoint *pos, int *keybMask = 0); + static void queryPointer(QXcbConnection *c, QXcbVirtualDesktop **virtualDesktop, QPoint *pos, int *keybMask = 0); private: #ifndef QT_NO_CURSOR diff --git a/src/plugins/platforms/xcb/qxcbdrag.cpp b/src/plugins/platforms/xcb/qxcbdrag.cpp index de23c59a63..f9c3aa7fed 100644 --- a/src/plugins/platforms/xcb/qxcbdrag.cpp +++ b/src/plugins/platforms/xcb/qxcbdrag.cpp @@ -39,6 +39,7 @@ #include "qxcbwindow.h" #include "qxcbscreen.h" #include "qwindow.h" +#include "qxcbcursor.h" #include #include #include @@ -160,7 +161,7 @@ void QXcbDrag::init() source_time = XCB_CURRENT_TIME; target_time = XCB_CURRENT_TIME; - current_screen = 0; + QXcbCursor::queryPointer(connection(), ¤t_virtual_desktop, 0); drag_types.clear(); } @@ -308,38 +309,20 @@ void QXcbDrag::move(const QPoint &globalPos) if (source_sameanswer.contains(globalPos) && source_sameanswer.isValid()) return; - const QList &screens = connection()->screens(); - QXcbScreen *screen = connection()->primaryScreen(); - for (int i = 0; i < screens.size(); ++i) { - if (screens.at(i)->geometry().contains(globalPos)) { - screen = screens.at(i); - break; - } + QXcbVirtualDesktop *virtualDesktop = Q_NULLPTR; + QPoint cursorPos; + QXcbCursor::queryPointer(connection(), &virtualDesktop, &cursorPos); + QXcbScreen *screen = virtualDesktop->screenAt(cursorPos); + QPoint deviceIndependentPos = QHighDpiScaling::mapPositionFromNative(globalPos, screen); + + if (virtualDesktop != current_virtual_desktop) { + recreateShapedPixmapWindow(static_cast(screen)->screen(), deviceIndependentPos); + current_virtual_desktop = virtualDesktop; + } else { + QBasicDrag::moveShapedPixmapWindow(deviceIndependentPos); } - QBasicDrag::moveShapedPixmapWindow(QHighDpiScaling::mapPositionFromNative(globalPos, screen)); - - if (screen != current_screen) { - // ### need to recreate the shaped pixmap window? -// 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); - current_screen = screen; - } - - -// qt_xdnd_current_screen = screen; - xcb_window_t rootwin = current_screen->root(); + xcb_window_t rootwin = current_virtual_desktop->root(); xcb_translate_coordinates_reply_t *translate = ::translateCoordinates(connection(), rootwin, rootwin, globalPos.x(), globalPos.y()); if (!translate) diff --git a/src/plugins/platforms/xcb/qxcbdrag.h b/src/plugins/platforms/xcb/qxcbdrag.h index 9584c04238..c9d257906f 100644 --- a/src/plugins/platforms/xcb/qxcbdrag.h +++ b/src/plugins/platforms/xcb/qxcbdrag.h @@ -133,7 +133,7 @@ private: // window to send events to (always valid if current_target) xcb_window_t current_proxy_target; - QXcbScreen *current_screen; + QXcbVirtualDesktop *current_virtual_desktop; // 10 minute timer used to discard old XdndDrop transactions enum { XdndDropTransactionTimeout = 600000 }; diff --git a/src/plugins/platforms/xcb/qxcbscreen.cpp b/src/plugins/platforms/xcb/qxcbscreen.cpp index c6e48dc8c4..2e0c55eea8 100644 --- a/src/plugins/platforms/xcb/qxcbscreen.cpp +++ b/src/plugins/platforms/xcb/qxcbscreen.cpp @@ -61,6 +61,15 @@ QXcbVirtualDesktop::~QXcbVirtualDesktop() delete m_xSettings; } +QXcbScreen *QXcbVirtualDesktop::screenAt(const QPoint &pos) const +{ + foreach (QXcbScreen *screen, connection()->screens()) { + if (screen->virtualDesktop() == this && screen->nativeGeometry().contains(pos)) + return screen; + } + return Q_NULLPTR; +} + QXcbXSettings *QXcbVirtualDesktop::xSettings() const { if (!m_xSettings) { diff --git a/src/plugins/platforms/xcb/qxcbscreen.h b/src/plugins/platforms/xcb/qxcbscreen.h index cbb6307d6e..d8d63608e7 100644 --- a/src/plugins/platforms/xcb/qxcbscreen.h +++ b/src/plugins/platforms/xcb/qxcbscreen.h @@ -64,6 +64,8 @@ public: int number() const { return m_number; } QSize size() const { return QSize(m_screen->width_in_pixels, m_screen->height_in_pixels); } QSize physicalSize() const { return QSize(m_screen->width_in_millimeters, m_screen->height_in_millimeters); } + xcb_window_t root() const { return m_screen->root; } + QXcbScreen *screenAt(const QPoint &pos) const; QXcbXSettings *xSettings() const; @@ -104,6 +106,7 @@ public: void setVirtualSiblings(QList sl) { m_siblings = sl; } void removeVirtualSibling(QPlatformScreen *s) { m_siblings.removeOne(s); } void addVirtualSibling(QPlatformScreen *s) { ((QXcbScreen *) s)->isPrimary() ? m_siblings.prepend(s) : m_siblings.append(s); } + QXcbVirtualDesktop *virtualDesktop() const { return m_virtualDesktop; } void setPrimary(bool primary) { m_primary = primary; } bool isPrimary() const { return m_primary; }