path: root/src/plugins/platforms/xcb
diff options
authorGatis Paeglis <>2018-05-05 20:51:15 +0200
committerGatis Paeglis <>2018-06-22 06:09:36 +0000
commit47aebe3587c7be28ed4fde23b77848657482ed66 (patch)
tree99f7dee2e21482ef6bf3c326996e144c225f3ab5 /src/plugins/platforms/xcb
parent6a28a2cc34d938417affc96d2495a7e3d2e2b869 (diff)
xcb: account for misbehaving Unity DnD Manager
... which may cause dnd data to be lost. As soon as Unity sees that dnd operation has started (it monitors for changes on XdndSelection) it creates an invisible window named XdndCollectionWindowImp that fill entire screen and starts to act as DnD target. Once it has fetched the mime data it moves XdndCollectionWindowImp away without sending any DnD termination events. XdndCollectionWindowImp does not respect the XDnD protocol. Only when its gone we can start a normal dnd operation - looking for real DnD target. We ask windows if they are XdndAware on the initial mouse press and subsequent mouse move events. This patch sets a cursor to Qt::ForbiddenCursor while DnD is interfered by XdndCollectionWindowImp. A user will see the real DnD action (reflected by cursor) only after the next mouse move when XdndCollectionWindowImp has stopped interfering. We also setCanDrop(false) while DnD target is XdndCollectionWindowImp. Temporary seeing ForbiddenCursor is better than losing DnD data. Dropping in this state means that drop will simply be ignored. It is unclear what Unity developers expected DnD source window to do when user releases mouse while XdndCollectionWindowImp is stealing the data. Looking at Unity code, it appears that they were hoping to be quick enough that it would never happen. Task-number: QTBUG-49464 Change-Id: I10880073f6d843572be44fe9a3c4f78194466299 Reviewed-by: Shawn Rutledge <>
Diffstat (limited to 'src/plugins/platforms/xcb')
3 files changed, 25 insertions, 0 deletions
diff --git a/src/plugins/platforms/xcb/qxcbdrag.cpp b/src/plugins/platforms/xcb/qxcbdrag.cpp
index dce0cccf87..999ae16897 100644
--- a/src/plugins/platforms/xcb/qxcbdrag.cpp
+++ b/src/plugins/platforms/xcb/qxcbdrag.cpp
@@ -155,6 +155,8 @@ void QXcbDrag::init()
dropped = false;
canceled = false;
+ source_sameanswer = QRect();
bool QXcbDrag::eventFilter(QObject *o, QEvent *e)
@@ -471,6 +473,17 @@ void QXcbDrag::move(const QPoint &globalPos, Qt::MouseButtons b, Qt::KeyboardMod
xcb_send_event(xcb_connection(), false, proxy_target, XCB_EVENT_MASK_NO_EVENT, (const char *)&move);
+ static const bool isUnity = qgetenv("XDG_CURRENT_DESKTOP").toLower() == "unity";
+ if (isUnity && xdndCollectionWindow == XCB_NONE) {
+ QString name = QXcbWindow::windowTitle(connection(), target);
+ if (name == QStringLiteral("XdndCollectionWindowImp"))
+ xdndCollectionWindow = target;
+ }
+ if (target == xdndCollectionWindow) {
+ setCanDrop(false);
+ updateCursor(Qt::IgnoreAction);
+ }
void QXcbDrag::drop(const QPoint &globalPos, Qt::MouseButtons b, Qt::KeyboardModifiers mods)
diff --git a/src/plugins/platforms/xcb/qxcbdrag.h b/src/plugins/platforms/xcb/qxcbdrag.h
index bbdf75f170..c19008c04b 100644
--- a/src/plugins/platforms/xcb/qxcbdrag.h
+++ b/src/plugins/platforms/xcb/qxcbdrag.h
@@ -143,6 +143,9 @@ private:
bool dropped;
bool canceled;
+ // A window from Unity DnD Manager, which does not respect the XDnD spec
+ xcb_window_t xdndCollectionWindow = XCB_NONE;
// top-level window we sent position to last.
xcb_window_t current_target;
// window to send events to (always valid if current_target)
diff --git a/src/plugins/platforms/xcb/qxcbwindow.cpp b/src/plugins/platforms/xcb/qxcbwindow.cpp
index 97dac4cfc1..4988ed9657 100644
--- a/src/plugins/platforms/xcb/qxcbwindow.cpp
+++ b/src/plugins/platforms/xcb/qxcbwindow.cpp
@@ -2839,6 +2839,15 @@ QString QXcbWindow::windowTitle(const QXcbConnection *conn, xcb_window_t window)
const char *name = reinterpret_cast<const char *>(xcb_get_property_value(reply.get()));
return QString::fromUtf8(name, xcb_get_property_value_length(reply.get()));
+ reply = Q_XCB_REPLY_UNCHECKED(xcb_get_property, conn->xcb_connection(),
+ false, window, conn->atom(QXcbAtom::WM_NAME),
+ XCB_ATOM_STRING, 0, 1024);
+ if (reply && reply->format == 8 && reply->type == XCB_ATOM_STRING) {
+ const char *name = reinterpret_cast<const char *>(xcb_get_property_value(reply.get()));
+ return QString::fromLatin1(name, xcb_get_property_value_length(reply.get()));
+ }
return QString();