Qt
Internal/Contributor docs for the Qt SDK. Note: These are NOT official API docs; those are found at https://doc.qt.io/
Loading...
Searching...
No Matches
qwldatadevicemanager.cpp
Go to the documentation of this file.
1// Copyright (C) 2017 The Qt Company Ltd.
2// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
3// Qt-Security score:critical reason:network-protocol
4
6
7#include <QtWaylandCompositor/QWaylandCompositor>
8
9#include <QtWaylandCompositor/private/qwaylandcompositor_p.h>
10#include <QtWaylandCompositor/private/qwaylandseat_p.h>
13#include "qwldataoffer_p.h"
14#include "qwaylandmimehelper_p.h"
15
16#include <QtCore/QDebug>
17#include <QtCore/QSocketNotifier>
18#include <fcntl.h>
19#include <QtCore/private/qcore_unix_p.h>
20#include <QtCore/QFile>
21
22QT_BEGIN_NAMESPACE
23
24namespace QtWayland {
25
26DataDeviceManager::DataDeviceManager(QWaylandCompositor *compositor)
27 : wl_data_device_manager(compositor->display(), 1)
28 , m_compositor(compositor)
29{
30}
31
33{
34 if (m_current_selection_source && source
35 && m_current_selection_source->time() > source->time()) {
36 qDebug() << "Trying to set older selection";
37 return;
38 }
39
40 m_compositorOwnsSelection = false;
41
42 finishReadFromClient();
43
44 m_current_selection_source = source;
45 if (source)
46 source->setManager(this);
47
48 // When retained selection is enabled, the compositor will query all the data from the client.
49 // This makes it possible to
50 // 1. supply the selection after the offering client is gone
51 // 2. make it possible for the compositor to participate in copy-paste
52 // The downside is decreased performance, therefore this mode has to be enabled
53 // explicitly in the compositors.
54 if (source && m_compositor->retainedSelectionEnabled()) {
55 m_retainedData.clear();
56 m_retainedReadIndex = 0;
57 retain();
58 }
59}
60
62{
63 if (m_current_selection_source == source) {
64 finishReadFromClient();
65 m_current_selection_source = nullptr;
66 }
67}
68
69void DataDeviceManager::retain()
70{
71 QList<QString> offers = m_current_selection_source->mimeTypes();
72 finishReadFromClient();
73 if (m_retainedReadIndex >= offers.size()) {
74 QWaylandCompositorPrivate::get(m_compositor)->feedRetainedSelectionData(&m_retainedData);
75 return;
76 }
77 QString mimeType = offers.at(m_retainedReadIndex);
78 m_retainedReadBuf.clear();
79 int fd[2];
80 if (pipe(fd) == -1) {
81 qWarning("Clipboard: Failed to create pipe");
82 return;
83 }
84 fcntl(fd[0], F_SETFL, fcntl(fd[0], F_GETFL, 0) | O_NONBLOCK);
85 m_current_selection_source->send(mimeType, fd[1]);
86 m_retainedReadNotifier = new QSocketNotifier(fd[0], QSocketNotifier::Read, this);
87 connect(m_retainedReadNotifier, &QSocketNotifier::activated, this, &DataDeviceManager::readFromClient);
88}
89
90void DataDeviceManager::finishReadFromClient(bool exhausted)
91{
92 Q_UNUSED(exhausted);
93 if (m_retainedReadNotifier) {
94 if (exhausted) {
95 int fd = m_retainedReadNotifier->socket();
96 delete m_retainedReadNotifier;
97 close(fd);
98 } else {
99 // Do not close the handle or destroy the read notifier here
100 // or else clients may SIGPIPE.
101 m_obsoleteRetainedReadNotifiers.append(m_retainedReadNotifier);
102 }
103 m_retainedReadNotifier = nullptr;
104 }
105}
106
107void DataDeviceManager::readFromClient(int fd)
108{
109 static char buf[4096];
110 int obsCount = m_obsoleteRetainedReadNotifiers.size();
111 for (int i = 0; i < obsCount; ++i) {
112 QSocketNotifier *sn = m_obsoleteRetainedReadNotifiers.at(i);
113 if (sn->socket() == fd) {
114 // Read and drop the data, stopping to read and closing the handle
115 // is not yet safe because that could kill the client with SIGPIPE
116 // when it still tries to write.
117 int n;
118 do {
119 n = QT_READ(fd, buf, sizeof buf);
120 } while (n > 0);
121 if (n != -1 || (errno != EAGAIN && errno != EWOULDBLOCK)) {
122 m_obsoleteRetainedReadNotifiers.removeAt(i);
123 delete sn;
124 close(fd);
125 }
126 return;
127 }
128 }
129 int n = QT_READ(fd, buf, sizeof buf);
130 if (n <= 0) {
131 if (n != -1 || (errno != EAGAIN && errno != EWOULDBLOCK)) {
132 finishReadFromClient(true);
133 QList<QString> offers = m_current_selection_source->mimeTypes();
134 QString mimeType = offers.at(m_retainedReadIndex);
135 m_retainedData.setData(mimeType, m_retainedReadBuf);
136 ++m_retainedReadIndex;
137 retain();
138 }
139 } else {
140 m_retainedReadBuf.append(buf, n);
141 }
142}
143
145{
146 return m_current_selection_source;
147}
148
149struct wl_display *DataDeviceManager::display() const
150{
151 return m_compositor->display();
152}
153
154void DataDeviceManager::overrideSelection(const QMimeData &mimeData)
155{
156 const QStringList formats = mimeData.formats();
157 if (formats.isEmpty())
158 return;
159
160 m_retainedData.clear();
161 for (const QString &format : formats)
162 m_retainedData.setData(format, mimeData.data(format));
163
164 QWaylandCompositorPrivate::get(m_compositor)->feedRetainedSelectionData(&m_retainedData);
165
166 m_compositorOwnsSelection = true;
167
168 if (m_current_selection_source) {
169 finishReadFromClient();
170 m_current_selection_source->cancel();
171 // wl_data_source::cancelled will destroy it and
172 // it will make m_current_selection_source as nullptr.
173 // But it will immediately affect the selection here.
174 m_current_selection_source = nullptr;
175 }
176
177 QWaylandSeat *dev = m_compositor->defaultSeat();
178 QWaylandSurface *focusSurface = dev->keyboardFocus();
179 if (focusSurface)
180 offerFromCompositorToClient(
181 QWaylandSeatPrivate::get(dev)->dataDevice()->resourceMap().value(focusSurface->waylandClient())->handle);
182}
183
184bool DataDeviceManager::offerFromCompositorToClient(wl_resource *clientDataDeviceResource)
185{
186 if (!m_compositorOwnsSelection)
187 return false;
188
189 wl_client *client = wl_resource_get_client(clientDataDeviceResource);
190 //qDebug("compositor offers %d types to %p", int(m_retainedData.formats().count()), client);
191
192 struct wl_resource *selectionOffer =
193 wl_resource_create(client, &wl_data_offer_interface, -1, 0);
194 wl_resource_set_implementation(selectionOffer, &compositor_offer_interface, this, nullptr);
195 wl_data_device_send_data_offer(clientDataDeviceResource, selectionOffer);
196 const auto formats = m_retainedData.formats();
197 for (const QString &format : formats) {
198 QByteArray ba = format.toLatin1();
199 wl_data_offer_send_offer(selectionOffer, ba.constData());
200 }
201 wl_data_device_send_selection(clientDataDeviceResource, selectionOffer);
202
203 return true;
204}
205
206void DataDeviceManager::offerRetainedSelection(wl_resource *clientDataDeviceResource)
207{
208 if (m_retainedData.formats().isEmpty())
209 return;
210
211 m_compositorOwnsSelection = true;
212 offerFromCompositorToClient(clientDataDeviceResource);
213}
214
215void DataDeviceManager::data_device_manager_create_data_source(Resource *resource, uint32_t id)
216{
217 new DataSource(resource->client(), id, m_compositor->currentTimeMsecs());
218}
219
220void DataDeviceManager::data_device_manager_get_data_device(Resource *resource, uint32_t id, struct ::wl_resource *seat)
221{
222 QWaylandSeat *input_device = QWaylandSeat::fromSeatResource(seat);
223 QWaylandSeatPrivate::get(input_device)->clientRequestedDataDevice(this, resource->client(), id);
224}
225
226void DataDeviceManager::comp_accept(wl_client *, wl_resource *, uint32_t, const char *)
227{
228}
229
230void DataDeviceManager::comp_receive(wl_client *client, wl_resource *resource, const char *mime_type, int32_t fd)
231{
232 Q_UNUSED(client);
233 DataDeviceManager *self = static_cast<DataDeviceManager *>(wl_resource_get_user_data(resource));
234 //qDebug("client %p wants data for type %s from compositor", client, mime_type);
235 QByteArray content = QWaylandMimeHelper::getByteArray(&self->m_retainedData, QString::fromLatin1(mime_type));
236 if (!content.isEmpty()) {
237 QFile f;
238 if (f.open(fd, QIODevice::WriteOnly))
239 f.write(content);
240 }
241 close(fd);
242}
243
244void DataDeviceManager::comp_destroy(wl_client *, wl_resource *)
245{
246}
247
248QT_WARNING_DISABLE_GCC("-Wmissing-field-initializers")
249QT_WARNING_DISABLE_CLANG("-Wmissing-field-initializers")
250
255};
256
257} //namespace
258
259QT_END_NAMESPACE
260
261#include "moc_qwldatadevicemanager_p.cpp"
struct wl_display * display() const
void overrideSelection(const QMimeData &mimeData)
void sourceDestroyed(DataSource *source)
void setCurrentSelectionSource(DataSource *source)
void data_device_manager_create_data_source(Resource *resource, uint32_t id) override
void setManager(DataDeviceManager *mgr)