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