KWin
Loading...
Searching...
No Matches
clipboard.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
7 SPDX-License-Identifier: GPL-2.0-or-later
8*/
9#include "clipboard.h"
10
11#include "datasource.h"
12#include "selection_source.h"
13
14#include "wayland/seat.h"
15#include "wayland_server.h"
16#include "workspace.h"
17#include "x11window.h"
18
19#include <xcb/xcb_event.h>
20#include <xcb/xfixes.h>
21
22#include <xwayland_logging.h>
23
24namespace KWin
25{
26namespace Xwl
27{
28
29Clipboard::Clipboard(xcb_atom_t atom, QObject *parent)
30 : Selection(atom, parent)
31{
32 xcb_connection_t *xcbConn = kwinApp()->x11Connection();
33
34 const uint32_t clipboardValues[] = {XCB_EVENT_MASK_SUBSTRUCTURE_NOTIFY | XCB_EVENT_MASK_PROPERTY_CHANGE};
35 xcb_create_window(xcbConn,
36 XCB_COPY_FROM_PARENT,
37 window(),
38 kwinApp()->x11RootWindow(),
39 0, 0,
40 10, 10,
41 0,
42 XCB_WINDOW_CLASS_INPUT_OUTPUT,
43 XCB_COPY_FROM_PARENT,
44 XCB_CW_EVENT_MASK,
45 clipboardValues);
47 xcb_flush(xcbConn);
48
50 this, &Clipboard::wlSelectionChanged);
51}
52
53void Clipboard::wlSelectionChanged(AbstractDataSource *dsi)
54{
55 if (m_waitingForTargets) {
56 return;
57 }
58
59 if (!ownsSelection(dsi)) {
60 // Wayland native window provides new selection
61 if (!m_checkConnection) {
62 m_checkConnection = connect(workspace(), &Workspace::windowActivated,
63 this, &Clipboard::checkWlSource);
64 }
65 // remove previous source so checkWlSource() can create a new one
66 setWlSource(nullptr);
67 }
68 checkWlSource();
69}
70
71bool Clipboard::ownsSelection(AbstractDataSource *dsi) const
72{
73 return dsi && dsi == m_selectionSource.get();
74}
75
76void Clipboard::checkWlSource()
77{
78 if (m_waitingForTargets) {
79 return;
80 }
81
82 auto dsi = waylandServer()->seat()->selection();
83 auto removeSource = [this] {
84 if (wlSource()) {
85 setWlSource(nullptr);
86 ownSelection(false);
87 }
88 };
89
90 // Wayland source gets created when:
91 // - the Wl selection exists,
92 // - its source is not Xwayland,
93 // - a window is active,
94 // - this window is an Xwayland one.
95 //
96 // Otherwise the Wayland source gets destroyed to shield
97 // against snooping X windows.
98
99 if (!dsi || ownsSelection(dsi)) {
100 // Xwayland source or no source
101 disconnect(m_checkConnection);
102 m_checkConnection = QMetaObject::Connection();
103 removeSource();
104 return;
105 }
106 if (!qobject_cast<KWin::X11Window *>(workspace()->activeWindow())) {
107 // no active window or active window is Wayland native
108 removeSource();
109 return;
110 }
111 // Xwayland window is active and we need a Wayland source
112 if (wlSource()) {
113 // source already exists, nothing more to do
114 return;
115 }
116 auto *wls = new WlSource(this);
117 setWlSource(wls);
118 if (dsi) {
119 wls->setDataSourceIface(dsi);
120 }
121 ownSelection(true);
122}
123
124void Clipboard::doHandleXfixesNotify(xcb_xfixes_selection_notify_event_t *event)
125{
126 const Window *window = workspace()->activeWindow();
127 if (!qobject_cast<const X11Window *>(window)) {
128 // clipboard is only allowed to be acquired when Xwayland has focus
129 // TODO: can we make this stronger (window id comparison)?
130 createX11Source(nullptr);
131 return;
132 }
133
134 createX11Source(event);
135
136 if (X11Source *source = x11Source()) {
137 source->getTargets();
138 m_waitingForTargets = true;
139 } else {
140 qCWarning(KWIN_XWL) << "Could not create a source from" << event << Qt::hex << (event ? event->owner : -1);
141 }
142}
143
144void Clipboard::x11OfferLost()
145{
146 m_selectionSource.reset();
147}
148
149void Clipboard::x11OffersChanged(const QStringList &added, const QStringList &removed)
150{
151 m_waitingForTargets = false;
152 X11Source *source = x11Source();
153 if (!source) {
154 qCWarning(KWIN_XWL) << "offers changed when not having an X11Source!?";
155 return;
156 }
157
158 const Mimes offers = source->offers();
159
160 if (!offers.isEmpty()) {
161 QStringList mimeTypes;
162 mimeTypes.reserve(offers.size());
163 std::transform(offers.begin(), offers.end(), std::back_inserter(mimeTypes), [](const Mimes::value_type &pair) {
164 return pair.first;
165 });
166 auto newSelection = std::make_unique<XwlDataSource>();
167 newSelection->setMimeTypes(mimeTypes);
168 connect(newSelection.get(), &XwlDataSource::dataRequested, source, &X11Source::startTransfer);
169 // we keep the old selection around because setSelection needs it to be still alive
170 std::swap(m_selectionSource, newSelection);
171 waylandServer()->seat()->setSelection(m_selectionSource.get());
172 } else {
173 AbstractDataSource *currentSelection = waylandServer()->seat()->selection();
174 if (!ownsSelection(currentSelection)) {
175 waylandServer()->seat()->setSelection(nullptr);
176 m_selectionSource.reset();
177 }
178 }
179}
180
181} // namespace Xwl
182} // namespace KWin
183
184#include "moc_clipboard.cpp"
The AbstractDataSource class abstracts the data that can be transferred to another client.
void setSelection(AbstractDataSource *selection)
Definition seat.cpp:1283
void selectionChanged(KWin::AbstractDataSource *)
AbstractDataSource * selection() const
Definition seat.cpp:1278
SeatInterface * seat() const
Window * activeWindow() const
Definition workspace.h:767
void windowActivated(KWin::Window *)
Clipboard(xcb_atom_t atom, QObject *parent)
Definition clipboard.cpp:29
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
void startTransfer(const QString &mimeName, qint32 fd)
void dataRequested(const QString &mimeType, qint32 fd)
QList< QPair< QString, xcb_atom_t > > Mimes
Definition drag_x.h:31
WaylandServer * waylandServer()
Workspace * workspace()
Definition workspace.h:830