KWin
Loading...
Searching...
No Matches
dnd.cpp
Go to the documentation of this file.
1/*
2 KWin - the KDE window manager
3 This file is part of the KDE project.
4
5 SPDX-FileCopyrightText: 2019 Roman Gilg <subdiff@gmail.com>
6 SPDX-FileCopyrightText: 2021 David Edmundson <davidedmundson@kde.org>
7 SPDX-FileCopyrightText: 2021 David Redondo <kde@david-redondo.de>
8 SPDX-License-Identifier: GPL-2.0-or-later
9*/
10#include "dnd.h"
11
12#include "databridge.h"
13#include "drag_wl.h"
14#include "drag_x.h"
15#include "selection_source.h"
16
17#include "atoms.h"
18#include "wayland/compositor.h"
19#include "wayland/datasource.h"
20#include "wayland/seat.h"
21#include "wayland_server.h"
22#include "window.h"
23#include "workspace.h"
24#include "xwayland.h"
25#include "xwldrophandler.h"
26
27#include <QMouseEvent>
28
29#include <xcb/xcb.h>
30
31namespace KWin
32{
33namespace Xwl
34{
35
36// version of DnD support in X
37const static uint32_t s_version = 5;
38uint32_t Dnd::version()
39{
40 return s_version;
41}
42
44{
45 return m_dropHandler;
46}
47
48Dnd::Dnd(xcb_atom_t atom, QObject *parent)
49 : Selection(atom, parent)
50 , m_dropHandler(new XwlDropHandler(this))
51{
52 xcb_connection_t *xcbConn = kwinApp()->x11Connection();
53
54 const uint32_t dndValues[] = {XCB_EVENT_MASK_SUBSTRUCTURE_NOTIFY | XCB_EVENT_MASK_PROPERTY_CHANGE};
55 xcb_create_window(xcbConn,
56 XCB_COPY_FROM_PARENT,
57 window(),
58 kwinApp()->x11RootWindow(),
59 0, 0,
60 8192, 8192, // TODO: get current screen size and connect to changes
61 0,
62 XCB_WINDOW_CLASS_INPUT_OUTPUT,
63 XCB_COPY_FROM_PARENT,
64 XCB_CW_EVENT_MASK,
65 dndValues);
67
68 xcb_change_property(xcbConn,
69 XCB_PROP_MODE_REPLACE,
70 window(),
72 XCB_ATOM_ATOM,
73 32, 1, &s_version);
74 xcb_flush(xcbConn);
75
76 connect(waylandServer()->seat(), &SeatInterface::dragStarted, this, &Dnd::startDrag);
77 connect(waylandServer()->seat(), &SeatInterface::dragEnded, this, &Dnd::endDrag);
78}
79
80void Dnd::doHandleXfixesNotify(xcb_xfixes_selection_notify_event_t *event)
81{
82 if (qobject_cast<XToWlDrag *>(m_currentDrag)) {
83 // X drag is in progress, rogue X client took over the selection.
84 return;
85 }
86 if (m_currentDrag) {
87 // Wl drag is in progress - don't overwrite by rogue X client,
88 // get it back instead!
89 ownSelection(true);
90 return;
91 }
92 createX11Source(nullptr);
93 const auto *seat = waylandServer()->seat();
94 auto *originSurface = seat->focusedPointerSurface();
95 if (!originSurface) {
96 return;
97 }
98 if (originSurface->client() != waylandServer()->xWaylandConnection()) {
99 // focused surface client is not Xwayland - do not allow drag to start
100 // TODO: can we make this stronger (window id comparison)?
101 return;
102 }
103 if (!seat->isPointerButtonPressed(Qt::LeftButton)) {
104 // we only allow drags to be started on (left) pointer button being
105 // pressed for now
106 return;
107 }
108 createX11Source(event);
109 X11Source *source = x11Source();
110 if (!source) {
111 return;
112 }
113 m_currentDrag = new XToWlDrag(source, this);
114}
115
117{
118}
119
120void Dnd::x11OffersChanged(const QStringList &added, const QStringList &removed)
121{
122}
123
124bool Dnd::handleClientMessage(xcb_client_message_event_t *event)
125{
126 for (Drag *drag : std::as_const(m_oldDrags)) {
127 if (drag->handleClientMessage(event)) {
128 return true;
129 }
130 }
131 if (m_currentDrag && m_currentDrag->handleClientMessage(event)) {
132 return true;
133 }
134 return false;
135}
136
138{
139 Q_ASSERT(m_currentDrag);
140 return m_currentDrag->moveFilter(target);
141}
142
143void Dnd::startDrag()
144{
145 auto dragSource = waylandServer()->seat()->dragSource();
146 if (qobject_cast<XwlDataSource *>(dragSource)) {
147 return;
148 }
149
150 // There can only ever be one Wl native drag at the same time.
151 Q_ASSERT(!m_currentDrag);
152
153 // New Wl to X drag, init drag and Wl source.
154 m_currentDrag = new WlToXDrag(this);
155 auto source = new WlSource(this);
156 source->setDataSourceIface(dragSource);
157 connect(dragSource, &AbstractDataSource::aboutToBeDestroyed, this, [this, source] {
158 if (source == wlSource()) {
159 setWlSource(nullptr);
160 }
161 });
162 setWlSource(source);
163 ownSelection(true);
164}
165
166void Dnd::endDrag()
167{
168 Q_ASSERT(m_currentDrag);
169
170 connect(m_currentDrag, &Drag::finish, this, &Dnd::clearOldDrag);
171 m_oldDrags << m_currentDrag;
172
173 m_currentDrag = nullptr;
174}
175
176void Dnd::clearOldDrag(Drag *drag)
177{
178 m_oldDrags.removeOne(drag);
179 delete drag;
180}
181
183using DnDActions = DataDeviceManagerInterface::DnDActions;
184
186{
187 if (atom == atoms->xdnd_action_copy) {
188 return DnDAction::Copy;
189 } else if (atom == atoms->xdnd_action_move) {
190 return DnDAction::Move;
191 } else if (atom == atoms->xdnd_action_ask) {
192 // we currently do not support it - need some test client first
193 return DnDAction::None;
194 // return DnDAction::Ask;
195 }
196 return DnDAction::None;
197}
198
200{
201 if (action == DnDAction::Copy) {
202 return atoms->xdnd_action_copy;
203 } else if (action == DnDAction::Move) {
204 return atoms->xdnd_action_move;
205 } else if (action == DnDAction::Ask) {
206 // we currently do not support it - need some test client first
207 return XCB_ATOM_NONE;
208 // return atoms->xdnd_action_ask;
209 }
210 return XCB_ATOM_NONE;
211}
212
213} // namespace Xwl
214} // namespace KWin
215
216#include "moc_dnd.cpp"
Xcb::Atom xdnd_action_copy
Definition atoms.h:48
Xcb::Atom xdnd_aware
Definition atoms.h:43
Xcb::Atom xdnd_action_ask
Definition atoms.h:50
Xcb::Atom xdnd_action_move
Definition atoms.h:49
AbstractDataSource * dragSource() const
Definition seat.cpp:1218
SeatInterface * seat() const
static uint32_t version()
Definition dnd.cpp:38
static DnDAction atomToClientAction(xcb_atom_t atom)
Definition dnd.cpp:185
Dnd(xcb_atom_t atom, QObject *parent)
Definition dnd.cpp:48
void doHandleXfixesNotify(xcb_xfixes_selection_notify_event_t *event) override
Definition dnd.cpp:80
XwlDropHandler * dropHandler() const
Definition dnd.cpp:43
DragEventReply dragMoveFilter(Window *target)
Definition dnd.cpp:137
void x11OfferLost() override
Definition dnd.cpp:116
bool handleClientMessage(xcb_client_message_event_t *event) override
Definition dnd.cpp:124
static xcb_atom_t clientActionToAtom(DnDAction action)
Definition dnd.cpp:199
void x11OffersChanged(const QStringList &added, const QStringList &removed) override
Definition dnd.cpp:120
void finish(Drag *self)
virtual DragEventReply moveFilter(Window *target)=0
virtual bool handleClientMessage(xcb_client_message_event_t *event)=0
xcb_atom_t atom() const
Definition selection.h:59
void createX11Source(xcb_xfixes_selection_notify_event_t *event)
void setWlSource(WlSource *source)
void ownSelection(bool own)
xcb_window_t window() const
Definition selection.h:63
WlSource * wlSource() const
Definition selection.h:86
X11Source * x11Source() const
Definition selection.h:91
DataDeviceManagerInterface::DnDActions DnDActions
Definition dnd.cpp:183
WaylandServer * waylandServer()
KWIN_EXPORT Atoms * atoms
Definition main.cpp:74