1 /****************************************************************************
2 **
3 ** Copyright (C) 2017 The Qt Company Ltd.
4 ** Contact: https://www.qt.io/licensing/
5 **
6 ** This file is part of the QtWaylandCompositor module of the Qt Toolkit.
7 **
8 ** $QT_BEGIN_LICENSE:GPL$
9 ** Commercial License Usage
10 ** Licensees holding valid commercial Qt licenses may use this file in
11 ** accordance with the commercial license agreement provided with the
12 ** Software or, alternatively, in accordance with the terms contained in
13 ** a written agreement between you and The Qt Company. For licensing terms
14 ** and conditions see https://www.qt.io/terms-conditions. For further
15 ** information use the contact form at https://www.qt.io/contact-us.
16 **
17 ** GNU General Public License Usage
18 ** Alternatively, this file may be used under the terms of the GNU
19 ** General Public License version 3 or (at your option) any later version
20 ** approved by the KDE Free Qt Foundation. The licenses are as published by
21 ** the Free Software Foundation and appearing in the file LICENSE.GPL3
22 ** included in the packaging of this file. Please review the following
23 ** information to ensure the GNU General Public License requirements will
24 ** be met: https://www.gnu.org/licenses/gpl-3.0.html.
25 **
26 ** $QT_END_LICENSE$
27 **
28 ****************************************************************************/
29 
30 #include "qwldatadevicemanager_p.h"
31 
32 #include <QtWaylandCompositor/QWaylandCompositor>
33 
34 #include <QtWaylandCompositor/private/qwaylandcompositor_p.h>
35 #include <QtWaylandCompositor/private/qwaylandseat_p.h>
36 #include "qwldatadevice_p.h"
37 #include "qwldatasource_p.h"
38 #include "qwldataoffer_p.h"
39 #include "qwaylandmimehelper_p.h"
40 
41 #include <QtCore/QDebug>
42 #include <QtCore/QSocketNotifier>
43 #include <fcntl.h>
44 #include <QtCore/private/qcore_unix_p.h>
45 #include <QtCore/QFile>
46 
47 QT_BEGIN_NAMESPACE
48 
49 namespace QtWayland {
50 
DataDeviceManager(QWaylandCompositor * compositor)51 DataDeviceManager::DataDeviceManager(QWaylandCompositor *compositor)
52     : wl_data_device_manager(compositor->display(), 1)
53     , m_compositor(compositor)
54 {
55 }
56 
setCurrentSelectionSource(DataSource * source)57 void DataDeviceManager::setCurrentSelectionSource(DataSource *source)
58 {
59     if (m_current_selection_source && source
60             && m_current_selection_source->time() > source->time()) {
61         qDebug() << "Trying to set older selection";
62         return;
63     }
64 
65     m_compositorOwnsSelection = false;
66 
67     finishReadFromClient();
68 
69     m_current_selection_source = source;
70     if (source)
71         source->setManager(this);
72 
73     // When retained selection is enabled, the compositor will query all the data from the client.
74     // This makes it possible to
75     //    1. supply the selection after the offering client is gone
76     //    2. make it possible for the compositor to participate in copy-paste
77     // The downside is decreased performance, therefore this mode has to be enabled
78     // explicitly in the compositors.
79     if (source && m_compositor->retainedSelectionEnabled()) {
80         m_retainedData.clear();
81         m_retainedReadIndex = 0;
82         retain();
83     }
84 }
85 
sourceDestroyed(DataSource * source)86 void DataDeviceManager::sourceDestroyed(DataSource *source)
87 {
88     if (m_current_selection_source == source)
89         finishReadFromClient();
90 }
91 
retain()92 void DataDeviceManager::retain()
93 {
94     QList<QString> offers = m_current_selection_source->mimeTypes();
95     finishReadFromClient();
96     if (m_retainedReadIndex >= offers.count()) {
97         QWaylandCompositorPrivate::get(m_compositor)->feedRetainedSelectionData(&m_retainedData);
98         return;
99     }
100     QString mimeType = offers.at(m_retainedReadIndex);
101     m_retainedReadBuf.clear();
102     int fd[2];
103     if (pipe(fd) == -1) {
104         qWarning("Clipboard: Failed to create pipe");
105         return;
106     }
107     fcntl(fd[0], F_SETFL, fcntl(fd[0], F_GETFL, 0) | O_NONBLOCK);
108     m_current_selection_source->send(mimeType, fd[1]);
109     m_retainedReadNotifier = new QSocketNotifier(fd[0], QSocketNotifier::Read, this);
110     connect(m_retainedReadNotifier, &QSocketNotifier::activated, this, &DataDeviceManager::readFromClient);
111 }
112 
finishReadFromClient(bool exhausted)113 void DataDeviceManager::finishReadFromClient(bool exhausted)
114 {
115     Q_UNUSED(exhausted);
116     if (m_retainedReadNotifier) {
117         if (exhausted) {
118             int fd = m_retainedReadNotifier->socket();
119             delete m_retainedReadNotifier;
120             close(fd);
121         } else {
122             // Do not close the handle or destroy the read notifier here
123             // or else clients may SIGPIPE.
124             m_obsoleteRetainedReadNotifiers.append(m_retainedReadNotifier);
125         }
126         m_retainedReadNotifier = nullptr;
127     }
128 }
129 
readFromClient(int fd)130 void DataDeviceManager::readFromClient(int fd)
131 {
132     static char buf[4096];
133     int obsCount = m_obsoleteRetainedReadNotifiers.count();
134     for (int i = 0; i < obsCount; ++i) {
135         QSocketNotifier *sn = m_obsoleteRetainedReadNotifiers.at(i);
136         if (sn->socket() == fd) {
137             // Read and drop the data, stopping to read and closing the handle
138             // is not yet safe because that could kill the client with SIGPIPE
139             // when it still tries to write.
140             int n;
141             do {
142                 n = QT_READ(fd, buf, sizeof buf);
143             } while (n > 0);
144             if (n != -1 || (errno != EAGAIN && errno != EWOULDBLOCK)) {
145                 m_obsoleteRetainedReadNotifiers.removeAt(i);
146                 delete sn;
147                 close(fd);
148             }
149             return;
150         }
151     }
152     int n = QT_READ(fd, buf, sizeof buf);
153     if (n <= 0) {
154         if (n != -1 || (errno != EAGAIN && errno != EWOULDBLOCK)) {
155             finishReadFromClient(true);
156             QList<QString> offers = m_current_selection_source->mimeTypes();
157             QString mimeType = offers.at(m_retainedReadIndex);
158             m_retainedData.setData(mimeType, m_retainedReadBuf);
159             ++m_retainedReadIndex;
160             retain();
161         }
162     } else {
163         m_retainedReadBuf.append(buf, n);
164     }
165 }
166 
currentSelectionSource()167 DataSource *DataDeviceManager::currentSelectionSource()
168 {
169     return m_current_selection_source;
170 }
171 
display() const172 struct wl_display *DataDeviceManager::display() const
173 {
174     return m_compositor->display();
175 }
176 
overrideSelection(const QMimeData & mimeData)177 void DataDeviceManager::overrideSelection(const QMimeData &mimeData)
178 {
179     const QStringList formats = mimeData.formats();
180     if (formats.isEmpty())
181         return;
182 
183     m_retainedData.clear();
184     for (const QString &format : formats)
185         m_retainedData.setData(format, mimeData.data(format));
186 
187     QWaylandCompositorPrivate::get(m_compositor)->feedRetainedSelectionData(&m_retainedData);
188 
189     m_compositorOwnsSelection = true;
190 
191     QWaylandSeat *dev = m_compositor->defaultSeat();
192     QWaylandSurface *focusSurface = dev->keyboardFocus();
193     if (focusSurface)
194         offerFromCompositorToClient(
195                     QWaylandSeatPrivate::get(dev)->dataDevice()->resourceMap().value(focusSurface->waylandClient())->handle);
196 }
197 
offerFromCompositorToClient(wl_resource * clientDataDeviceResource)198 bool DataDeviceManager::offerFromCompositorToClient(wl_resource *clientDataDeviceResource)
199 {
200     if (!m_compositorOwnsSelection)
201         return false;
202 
203     wl_client *client = wl_resource_get_client(clientDataDeviceResource);
204     //qDebug("compositor offers %d types to %p", m_retainedData.formats().count(), client);
205 
206     struct wl_resource *selectionOffer =
207              wl_resource_create(client, &wl_data_offer_interface, -1, 0);
208     wl_resource_set_implementation(selectionOffer, &compositor_offer_interface, this, nullptr);
209     wl_data_device_send_data_offer(clientDataDeviceResource, selectionOffer);
210     const auto formats = m_retainedData.formats();
211     for (const QString &format : formats) {
212         QByteArray ba = format.toLatin1();
213         wl_data_offer_send_offer(selectionOffer, ba.constData());
214     }
215     wl_data_device_send_selection(clientDataDeviceResource, selectionOffer);
216 
217     return true;
218 }
219 
offerRetainedSelection(wl_resource * clientDataDeviceResource)220 void DataDeviceManager::offerRetainedSelection(wl_resource *clientDataDeviceResource)
221 {
222     if (m_retainedData.formats().isEmpty())
223         return;
224 
225     m_compositorOwnsSelection = true;
226     offerFromCompositorToClient(clientDataDeviceResource);
227 }
228 
data_device_manager_create_data_source(Resource * resource,uint32_t id)229 void DataDeviceManager::data_device_manager_create_data_source(Resource *resource, uint32_t id)
230 {
231     new DataSource(resource->client(), id, m_compositor->currentTimeMsecs());
232 }
233 
data_device_manager_get_data_device(Resource * resource,uint32_t id,struct::wl_resource * seat)234 void DataDeviceManager::data_device_manager_get_data_device(Resource *resource, uint32_t id, struct ::wl_resource *seat)
235 {
236     QWaylandSeat *input_device = QWaylandSeat::fromSeatResource(seat);
237     QWaylandSeatPrivate::get(input_device)->clientRequestedDataDevice(this, resource->client(), id);
238 }
239 
comp_accept(wl_client *,wl_resource *,uint32_t,const char *)240 void DataDeviceManager::comp_accept(wl_client *, wl_resource *, uint32_t, const char *)
241 {
242 }
243 
comp_receive(wl_client * client,wl_resource * resource,const char * mime_type,int32_t fd)244 void DataDeviceManager::comp_receive(wl_client *client, wl_resource *resource, const char *mime_type, int32_t fd)
245 {
246     Q_UNUSED(client);
247     DataDeviceManager *self = static_cast<DataDeviceManager *>(wl_resource_get_user_data(resource));
248     //qDebug("client %p wants data for type %s from compositor", client, mime_type);
249     QByteArray content = QWaylandMimeHelper::getByteArray(&self->m_retainedData, QString::fromLatin1(mime_type));
250     if (!content.isEmpty()) {
251         QFile f;
252         if (f.open(fd, QIODevice::WriteOnly))
253             f.write(content);
254     }
255     close(fd);
256 }
257 
comp_destroy(wl_client *,wl_resource *)258 void DataDeviceManager::comp_destroy(wl_client *, wl_resource *)
259 {
260 }
261 
262 QT_WARNING_DISABLE_GCC("-Wmissing-field-initializers")
263 QT_WARNING_DISABLE_CLANG("-Wmissing-field-initializers")
264 
265 const struct wl_data_offer_interface DataDeviceManager::compositor_offer_interface = {
266     DataDeviceManager::comp_accept,
267     DataDeviceManager::comp_receive,
268     DataDeviceManager::comp_destroy
269 };
270 
271 } //namespace
272 
273 QT_END_NAMESPACE
274