xcb: Fix DnD for separate X screens

Recreate QShapedPixmapWindow when the cursor goes to
another X screen.

Change-Id: Ifd4c4281971b23abc45a9f6c0509832a45c31521
Reviewed-by: Lars Knoll <lars.knoll@theqtcompany.com>
This commit is contained in:
Alexander Volkov 2015-10-07 16:58:09 +03:00
parent de70798859
commit c55a36cb90
11 changed files with 75 additions and 70 deletions

View File

@ -38,8 +38,8 @@
QT_BEGIN_NAMESPACE QT_BEGIN_NAMESPACE
QShapedPixmapWindow::QShapedPixmapWindow() QShapedPixmapWindow::QShapedPixmapWindow(QScreen *screen)
: QWindow(), : QWindow(screen),
m_backingStore(0) m_backingStore(0)
{ {
QSurfaceFormat format; QSurfaceFormat format;

View File

@ -55,7 +55,7 @@ class QShapedPixmapWindow : public QWindow
{ {
Q_OBJECT Q_OBJECT
public: public:
QShapedPixmapWindow(); explicit QShapedPixmapWindow(QScreen *screen = 0);
~QShapedPixmapWindow(); ~QShapedPixmapWindow();
void render(); void render();

View File

@ -203,25 +203,15 @@ void QBasicDrag::restoreCursor()
void QBasicDrag::startDrag() void QBasicDrag::startDrag()
{ {
// ### TODO Check if its really necessary to have m_drag_icon_window QPoint pos;
// 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());
#ifndef QT_NO_CURSOR #ifndef QT_NO_CURSOR
QPoint pos = QCursor::pos(); pos = QCursor::pos();
if (pos.x() == int(qInf())) { if (pos.x() == int(qInf())) {
// ### fixme: no mouse pos registered. Get pos from touch... // ### fixme: no mouse pos registered. Get pos from touch...
pos = QPoint(); pos = QPoint();
} }
m_drag_icon_window->updateGeometry(pos);
#endif #endif
recreateShapedPixmapWindow(Q_NULLPTR, pos);
m_drag_icon_window->setVisible(true);
enableEventFilter(); 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() void QBasicDrag::cancel()
{ {
disableEventFilter(); disableEventFilter();

View File

@ -58,6 +58,7 @@ class QWindow;
class QEventLoop; class QEventLoop;
class QDropData; class QDropData;
class QShapedPixmapWindow; class QShapedPixmapWindow;
class QScreen;
class Q_GUI_EXPORT QBasicDrag : public QPlatformDrag, public QObject class Q_GUI_EXPORT QBasicDrag : public QPlatformDrag, public QObject
{ {
@ -80,6 +81,7 @@ protected:
void moveShapedPixmapWindow(const QPoint &deviceIndependentPosition); void moveShapedPixmapWindow(const QPoint &deviceIndependentPosition);
QShapedPixmapWindow *shapedPixmapWindow() const { return m_drag_icon_window; } QShapedPixmapWindow *shapedPixmapWindow() const { return m_drag_icon_window; }
void recreateShapedPixmapWindow(QScreen *screen, const QPoint &pos);
void updateCursor(Qt::DropAction action); void updateCursor(Qt::DropAction action);
bool canDrop() const { return m_can_drop; } bool canDrop() const { return m_can_drop; }

View File

@ -376,8 +376,10 @@ public:
QXcbConnection *connection() const { return const_cast<QXcbConnection *>(this); } QXcbConnection *connection() const { return const_cast<QXcbConnection *>(this); }
const QList<QXcbVirtualDesktop *> &virtualDesktops() const { return m_virtualDesktops; }
const QList<QXcbScreen *> &screens() const { return m_screens; } const QList<QXcbScreen *> &screens() const { return m_screens; }
int primaryScreenNumber() const { return m_primaryScreenNumber; } int primaryScreenNumber() const { return m_primaryScreenNumber; }
QXcbVirtualDesktop *primaryVirtualDesktop() const { return m_virtualDesktops.value(m_primaryScreenNumber); }
QXcbScreen *primaryScreen() const; QXcbScreen *primaryScreen() const;
inline xcb_atom_t atom(QXcbAtom::Atom atom) const { return m_allAtoms[atom]; } inline xcb_atom_t atom(QXcbAtom::Atom atom) const { return m_allAtoms[atom]; }

View File

@ -607,30 +607,33 @@ xcb_cursor_t QXcbCursor::createBitmapCursor(QCursor *cursor)
} }
#endif #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) if (pos)
*pos = QPoint(); *pos = QPoint();
xcb_screen_iterator_t it = xcb_setup_roots_iterator(c->setup());
while (it.rem) { xcb_window_t root = c->primaryVirtualDesktop()->root();
xcb_window_t root = it.data->root; xcb_query_pointer_cookie_t cookie = xcb_query_pointer(c->xcb_connection(), root);
xcb_query_pointer_cookie_t cookie = xcb_query_pointer(c->xcb_connection(), root); xcb_generic_error_t *err = 0;
xcb_generic_error_t *err = 0; xcb_query_pointer_reply_t *reply = xcb_query_pointer_reply(c->xcb_connection(), cookie, &err);
xcb_query_pointer_reply_t *reply = xcb_query_pointer_reply(c->xcb_connection(), cookie, &err); if (!err && reply) {
if (!err && reply) { if (virtualDesktop) {
if (pos) foreach (QXcbVirtualDesktop *vd, c->virtualDesktops()) {
*pos = QPoint(reply->root_x, reply->root_y); if (vd->root() == reply->root) {
if (rootWin) *virtualDesktop = vd;
*rootWin = root; break;
if (keybMask) }
*keybMask = reply->mask; }
free(reply);
return;
} }
free(err); if (pos)
*pos = QPoint(reply->root_x, reply->root_y);
if (keybMask)
*keybMask = reply->mask;
free(reply); free(reply);
xcb_screen_next(&it); return;
} }
free(err);
free(reply);
} }
QPoint QXcbCursor::pos() const QPoint QXcbCursor::pos() const
@ -642,9 +645,9 @@ QPoint QXcbCursor::pos() const
void QXcbCursor::setPos(const QPoint &pos) void QXcbCursor::setPos(const QPoint &pos)
{ {
xcb_window_t root = 0; QXcbVirtualDesktop *virtualDesktop = Q_NULLPTR;
queryPointer(connection(), &root, 0); queryPointer(connection(), &virtualDesktop, 0);
xcb_warp_pointer(xcb_connection(), XCB_NONE, root, 0, 0, 0, 0, pos.x(), pos.y()); xcb_warp_pointer(xcb_connection(), XCB_NONE, virtualDesktop->root(), 0, 0, 0, 0, pos.x(), pos.y());
xcb_flush(xcb_connection()); xcb_flush(xcb_connection());
} }

View File

@ -75,7 +75,7 @@ public:
QPoint pos() const Q_DECL_OVERRIDE; QPoint pos() const Q_DECL_OVERRIDE;
void setPos(const QPoint &pos) 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: private:
#ifndef QT_NO_CURSOR #ifndef QT_NO_CURSOR

View File

@ -39,6 +39,7 @@
#include "qxcbwindow.h" #include "qxcbwindow.h"
#include "qxcbscreen.h" #include "qxcbscreen.h"
#include "qwindow.h" #include "qwindow.h"
#include "qxcbcursor.h"
#include <private/qdnd_p.h> #include <private/qdnd_p.h>
#include <qdebug.h> #include <qdebug.h>
#include <qevent.h> #include <qevent.h>
@ -160,7 +161,7 @@ void QXcbDrag::init()
source_time = XCB_CURRENT_TIME; source_time = XCB_CURRENT_TIME;
target_time = XCB_CURRENT_TIME; target_time = XCB_CURRENT_TIME;
current_screen = 0; QXcbCursor::queryPointer(connection(), &current_virtual_desktop, 0);
drag_types.clear(); drag_types.clear();
} }
@ -308,38 +309,20 @@ void QXcbDrag::move(const QPoint &globalPos)
if (source_sameanswer.contains(globalPos) && source_sameanswer.isValid()) if (source_sameanswer.contains(globalPos) && source_sameanswer.isValid())
return; return;
const QList<QXcbScreen *> &screens = connection()->screens(); QXcbVirtualDesktop *virtualDesktop = Q_NULLPTR;
QXcbScreen *screen = connection()->primaryScreen(); QPoint cursorPos;
for (int i = 0; i < screens.size(); ++i) { QXcbCursor::queryPointer(connection(), &virtualDesktop, &cursorPos);
if (screens.at(i)->geometry().contains(globalPos)) { QXcbScreen *screen = virtualDesktop->screenAt(cursorPos);
screen = screens.at(i); QPoint deviceIndependentPos = QHighDpiScaling::mapPositionFromNative(globalPos, screen);
break;
} if (virtualDesktop != current_virtual_desktop) {
recreateShapedPixmapWindow(static_cast<QPlatformScreen*>(screen)->screen(), deviceIndependentPos);
current_virtual_desktop = virtualDesktop;
} else {
QBasicDrag::moveShapedPixmapWindow(deviceIndependentPos);
} }
QBasicDrag::moveShapedPixmapWindow(QHighDpiScaling::mapPositionFromNative(globalPos, screen)); xcb_window_t rootwin = current_virtual_desktop->root();
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_translate_coordinates_reply_t *translate = xcb_translate_coordinates_reply_t *translate =
::translateCoordinates(connection(), rootwin, rootwin, globalPos.x(), globalPos.y()); ::translateCoordinates(connection(), rootwin, rootwin, globalPos.x(), globalPos.y());
if (!translate) if (!translate)

View File

@ -133,7 +133,7 @@ private:
// window to send events to (always valid if current_target) // window to send events to (always valid if current_target)
xcb_window_t current_proxy_target; xcb_window_t current_proxy_target;
QXcbScreen *current_screen; QXcbVirtualDesktop *current_virtual_desktop;
// 10 minute timer used to discard old XdndDrop transactions // 10 minute timer used to discard old XdndDrop transactions
enum { XdndDropTransactionTimeout = 600000 }; enum { XdndDropTransactionTimeout = 600000 };

View File

@ -61,6 +61,15 @@ QXcbVirtualDesktop::~QXcbVirtualDesktop()
delete m_xSettings; 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 QXcbXSettings *QXcbVirtualDesktop::xSettings() const
{ {
if (!m_xSettings) { if (!m_xSettings) {

View File

@ -64,6 +64,8 @@ public:
int number() const { return m_number; } int number() const { return m_number; }
QSize size() const { return QSize(m_screen->width_in_pixels, m_screen->height_in_pixels); } 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); } 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; QXcbXSettings *xSettings() const;
@ -104,6 +106,7 @@ public:
void setVirtualSiblings(QList<QPlatformScreen *> sl) { m_siblings = sl; } void setVirtualSiblings(QList<QPlatformScreen *> sl) { m_siblings = sl; }
void removeVirtualSibling(QPlatformScreen *s) { m_siblings.removeOne(s); } void removeVirtualSibling(QPlatformScreen *s) { m_siblings.removeOne(s); }
void addVirtualSibling(QPlatformScreen *s) { ((QXcbScreen *) s)->isPrimary() ? m_siblings.prepend(s) : m_siblings.append(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; } void setPrimary(bool primary) { m_primary = primary; }
bool isPrimary() const { return m_primary; } bool isPrimary() const { return m_primary; }