xcb: Add support for XdndActionList property
This allows to pass and receive possible drop actions from other processes, including GTK applications. Fixes: QTBUG-75744 Change-Id: I944edc6fa00f8801a25912e70eb104a647a9fc0e Reviewed-by: JiDe Zhang <zccrs@live.com> Reviewed-by: Liang Qi <liang.qi@qt.io>bb10
parent
cb509f3aee
commit
cc1d891b8e
|
|
@ -182,6 +182,7 @@ static const char *xcb_atomnames = {
|
|||
"XdndActionCopy\0"
|
||||
"XdndActionLink\0"
|
||||
"XdndActionMove\0"
|
||||
"XdndActionAsk\0"
|
||||
"XdndActionPrivate\0"
|
||||
|
||||
// Xkb
|
||||
|
|
|
|||
|
|
@ -183,6 +183,7 @@ public:
|
|||
XdndActionCopy,
|
||||
XdndActionLink,
|
||||
XdndActionMove,
|
||||
XdndActionAsk,
|
||||
XdndActionPrivate,
|
||||
|
||||
// Xkb
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
Loading…
Reference in New Issue