summaryrefslogtreecommitdiffstats
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/plugins/platforms/xcb/qxcbatom.cpp1
-rw-r--r--src/plugins/platforms/xcb/qxcbatom.h1
-rw-r--r--src/plugins/platforms/xcb/qxcbdrag.cpp108
-rw-r--r--src/plugins/platforms/xcb/qxcbdrag.h15
4 files changed, 118 insertions, 7 deletions
diff --git a/src/plugins/platforms/xcb/qxcbatom.cpp b/src/plugins/platforms/xcb/qxcbatom.cpp
index d366564dd6..a73d28319d 100644
--- a/src/plugins/platforms/xcb/qxcbatom.cpp
+++ b/src/plugins/platforms/xcb/qxcbatom.cpp
@@ -182,6 +182,7 @@ static const char *xcb_atomnames = {
"XdndActionCopy\0"
"XdndActionLink\0"
"XdndActionMove\0"
+ "XdndActionAsk\0"
"XdndActionPrivate\0"
// Xkb
diff --git a/src/plugins/platforms/xcb/qxcbatom.h b/src/plugins/platforms/xcb/qxcbatom.h
index 80b5887395..9cf93ec314 100644
--- a/src/plugins/platforms/xcb/qxcbatom.h
+++ b/src/plugins/platforms/xcb/qxcbatom.h
@@ -183,6 +183,7 @@ public:
XdndActionCopy,
XdndActionLink,
XdndActionMove,
+ XdndActionAsk,
XdndActionPrivate,
// Xkb
diff --git a/src/plugins/platforms/xcb/qxcbdrag.cpp b/src/plugins/platforms/xcb/qxcbdrag.cpp
index 3d525598ca..d82129c6bc 100644
--- a/src/plugins/platforms/xcb/qxcbdrag.cpp
+++ b/src/plugins/platforms/xcb/qxcbdrag.cpp
@@ -216,6 +216,22 @@ void QXcbDrag::endDrag()
initiatorWindow.clear();
}
+Qt::DropAction QXcbDrag::defaultAction(Qt::DropActions possibleActions, Qt::KeyboardModifiers modifiers) const
+{
+ if (currentDrag() || drop_actions.isEmpty())
+ return QBasicDrag::defaultAction(possibleActions, modifiers);
+
+ return toDropAction(drop_actions.first());
+}
+
+void QXcbDrag::handlePropertyNotifyEvent(const xcb_property_notify_event_t *event)
+{
+ if (event->window != xdnd_dragsource || event->atom != atom(QXcbAtom::XdndActionList))
+ return;
+
+ readActionList();
+}
+
static
bool windowInteractsWithPosition(xcb_connection_t *connection, const QPoint & pos, xcb_window_t w, xcb_shape_sk_t shapeType)
{
@@ -470,16 +486,20 @@ void QXcbDrag::move(const QPoint &globalPos, Qt::MouseButtons b, Qt::KeyboardMod
move.data.data32[1] = 0; // flags
move.data.data32[2] = (globalPos.x() << 16) + globalPos.y();
move.data.data32[3] = connection()->time();
- move.data.data32[4] = toXdndAction(defaultAction(currentDrag()->supportedActions(), mods));
+ const auto supportedActions = currentDrag()->supportedActions();
+ const auto requestedAction = defaultAction(supportedActions, mods);
+ move.data.data32[4] = toXdndAction(requestedAction);
qCDebug(lcQpaXDnd) << "sending XdndPosition to target:" << target;
source_time = connection()->time();
- if (w)
+ if (w) {
handle_xdnd_position(w, &move, b, mods);
- else
+ } else {
+ setActionList(requestedAction, supportedActions);
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";
@@ -560,6 +580,16 @@ Qt::DropAction QXcbDrag::toDropAction(xcb_atom_t a) const
return Qt::CopyAction;
}
+Qt::DropActions QXcbDrag::toDropActions(const QVector<xcb_atom_t> &atoms) const
+{
+ Qt::DropActions actions;
+ for (const auto actionAtom : atoms) {
+ if (actionAtom != atom(QXcbAtom::XdndActionAsk))
+ actions |= toDropAction(actionAtom);
+ }
+ return actions;
+}
+
xcb_atom_t QXcbDrag::toXdndAction(Qt::DropAction a) const
{
switch (a) {
@@ -577,6 +607,60 @@ xcb_atom_t QXcbDrag::toXdndAction(Qt::DropAction a) const
}
}
+void QXcbDrag::readActionList()
+{
+ drop_actions.clear();
+ auto reply = Q_XCB_REPLY(xcb_get_property, xcb_connection(), false, xdnd_dragsource,
+ atom(QXcbAtom::XdndActionList), XCB_ATOM_ATOM,
+ 0, 1024);
+ if (reply && reply->type != XCB_NONE && reply->format == 32) {
+ int length = xcb_get_property_value_length(reply.get()) / 4;
+
+ xcb_atom_t *atoms = (xcb_atom_t *)xcb_get_property_value(reply.get());
+ for (int i = 0; i < length; ++i)
+ drop_actions.append(atoms[i]);
+ }
+}
+
+void QXcbDrag::setActionList(Qt::DropAction requestedAction, Qt::DropActions supportedActions)
+{
+#ifndef QT_NO_CLIPBOARD
+ QVector<xcb_atom_t> actions;
+ if (requestedAction != Qt::IgnoreAction)
+ actions.append(toXdndAction(requestedAction));
+
+ auto checkAppend = [this, requestedAction, supportedActions, &actions](Qt::DropAction action) {
+ if (requestedAction != action && supportedActions & action)
+ actions.append(toXdndAction(action));
+ };
+
+ checkAppend(Qt::CopyAction);
+ checkAppend(Qt::MoveAction);
+ checkAppend(Qt::LinkAction);
+
+ if (current_actions != actions) {
+ xcb_change_property(xcb_connection(), XCB_PROP_MODE_REPLACE, connection()->clipboard()->owner(),
+ atom(QXcbAtom::XdndActionList),
+ XCB_ATOM_ATOM, 32, actions.size(), actions.constData());
+ current_actions = actions;
+ }
+#endif
+}
+
+void QXcbDrag::startListeningForActionListChanges()
+{
+ connection()->addWindowEventListener(xdnd_dragsource, this);
+ const uint32_t event_mask[] = { XCB_EVENT_MASK_PROPERTY_CHANGE };
+ xcb_change_window_attributes(xcb_connection(), xdnd_dragsource, XCB_CW_EVENT_MASK, event_mask);
+}
+
+void QXcbDrag::stopListeningForActionListChanges()
+{
+ const uint32_t event_mask[] = { XCB_EVENT_MASK_NO_EVENT };
+ xcb_change_window_attributes(xcb_connection(), xdnd_dragsource, XCB_CW_EVENT_MASK, event_mask);
+ connection()->removeWindowEventListener(xdnd_dragsource);
+}
+
int QXcbDrag::findTransactionByWindow(xcb_window_t window)
{
int at = -1;
@@ -657,6 +741,9 @@ void QXcbDrag::handleEnter(QPlatformWindow *, const xcb_client_message_event_t *
return;
xdnd_dragsource = event->data.data32[0];
+ startListeningForActionListChanges();
+ readActionList();
+
if (!proxy)
proxy = xdndProxy(connection(), xdnd_dragsource);
current_proxy_target = proxy ? proxy : xdnd_dragsource;
@@ -723,7 +810,9 @@ void QXcbDrag::handle_xdnd_position(QPlatformWindow *w, const xcb_client_message
supported_actions = currentDrag()->supportedActions();
} else {
dropData = m_dropData;
- supported_actions = Qt::DropActions(toDropAction(e->data.data32[4]));
+ supported_actions = toDropActions(drop_actions);
+ if (e->data.data32[4] != atom(QXcbAtom::XdndActionAsk))
+ supported_actions |= Qt::DropActions(toDropAction(e->data.data32[4]));
}
auto buttons = currentDrag() ? b : connection()->queryMouseButtons();
@@ -867,8 +956,10 @@ void QXcbDrag::handleLeave(QPlatformWindow *w, const xcb_client_message_event_t
// If the target receives XdndLeave, it frees any cached data and forgets the whole incident.
qCDebug(lcQpaXDnd) << "target:" << event->window << "received XdndLeave";
- if (!currentWindow || w != currentWindow.data()->handle())
+ if (!currentWindow || w != currentWindow.data()->handle()) {
+ stopListeningForActionListChanges();
return; // sanity
+ }
// ###
// if (checkEmbedded(current_embedding_widget, event)) {
@@ -883,6 +974,8 @@ void QXcbDrag::handleLeave(QPlatformWindow *w, const xcb_client_message_event_t
event->data.data32[0], xdnd_dragsource);
}
+ stopListeningForActionListChanges();
+
QWindowSystemInterface::handleDrag(w->window(), nullptr, QPoint(), Qt::IgnoreAction, 0, 0);
}
@@ -929,6 +1022,7 @@ void QXcbDrag::handleDrop(QPlatformWindow *, const xcb_client_message_event_t *e
qCDebug(lcQpaXDnd) << "target:" << event->window << "received XdndDrop";
if (!currentWindow) {
+ stopListeningForActionListChanges();
xdnd_dragsource = 0;
return; // sanity
}
@@ -951,7 +1045,7 @@ void QXcbDrag::handleDrop(QPlatformWindow *, const xcb_client_message_event_t *e
supported_drop_actions = Qt::DropActions(l[4]);
} else {
dropData = m_dropData;
- supported_drop_actions = accepted_drop_action;
+ supported_drop_actions = accepted_drop_action | toDropActions(drop_actions);
}
if (!dropData)
@@ -986,6 +1080,8 @@ void QXcbDrag::handleDrop(QPlatformWindow *, const xcb_client_message_event_t *e
xcb_send_event(xcb_connection(), false, current_proxy_target,
XCB_EVENT_MASK_NO_EVENT, (char *)&finished);
+ stopListeningForActionListChanges();
+
dropped = true;
}
diff --git a/src/plugins/platforms/xcb/qxcbdrag.h b/src/plugins/platforms/xcb/qxcbdrag.h
index 1388e68acc..b6371041e6 100644
--- a/src/plugins/platforms/xcb/qxcbdrag.h
+++ b/src/plugins/platforms/xcb/qxcbdrag.h
@@ -68,7 +68,7 @@ class QXcbScreen;
class QDrag;
class QShapedPixmapWindow;
-class QXcbDrag : public QXcbObject, public QBasicDrag
+class QXcbDrag : public QXcbObject, public QBasicDrag, public QXcbWindowEventListener
{
public:
QXcbDrag(QXcbConnection *c);
@@ -82,6 +82,10 @@ public:
void drop(const QPoint &globalPos, Qt::MouseButtons b, Qt::KeyboardModifiers mods) override;
void endDrag() override;
+ Qt::DropAction defaultAction(Qt::DropActions possibleActions, Qt::KeyboardModifiers modifiers) const override;
+
+ void handlePropertyNotifyEvent(const xcb_property_notify_event_t *event) override;
+
void handleEnter(QPlatformWindow *window, const xcb_client_message_event_t *event, xcb_window_t proxy = 0);
void handlePosition(QPlatformWindow *w, const xcb_client_message_event_t *event);
void handleLeave(QPlatformWindow *w, const xcb_client_message_event_t *event);
@@ -114,8 +118,14 @@ private:
void send_leave();
Qt::DropAction toDropAction(xcb_atom_t atom) const;
+ Qt::DropActions toDropActions(const QVector<xcb_atom_t> &atoms) const;
xcb_atom_t toXdndAction(Qt::DropAction a) const;
+ void readActionList();
+ void setActionList(Qt::DropAction requestedAction, Qt::DropActions supportedActions);
+ void startListeningForActionListChanges();
+ void stopListeningForActionListChanges();
+
QPointer<QWindow> initiatorWindow;
QPointer<QWindow> currentWindow;
QPoint currentPosition;
@@ -159,6 +169,9 @@ private:
QVector<xcb_atom_t> drag_types;
+ QVector<xcb_atom_t> current_actions;
+ QVector<xcb_atom_t> drop_actions;
+
struct Transaction
{
xcb_timestamp_t timestamp;