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 <shawn.rutledge@qt.io>bb10
parent
6a28a2cc34
commit
47aebe3587
|
|
@ -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
|
|||
else
|
||||
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)
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
|
|
|
|||
|
|
@ -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();
|
||||
}
|
||||
|
||||
|
|
|
|||
Loading…
Reference in New Issue