Change copyrights from Nokia to Digia
[profile/ivi/qtwayland.git] / src / compositor / wayland_wrapper / wldatadevicemanager.cpp
1 /****************************************************************************
2 **
3 ** Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies).
4 ** Contact: http://www.qt-project.org/legal
5 **
6 ** This file is part of the Qt Compositor.
7 **
8 ** $QT_BEGIN_LICENSE:BSD$
9 ** You may use this file under the terms of the BSD license as follows:
10 **
11 ** "Redistribution and use in source and binary forms, with or without
12 ** modification, are permitted provided that the following conditions are
13 ** met:
14 **   * Redistributions of source code must retain the above copyright
15 **     notice, this list of conditions and the following disclaimer.
16 **   * Redistributions in binary form must reproduce the above copyright
17 **     notice, this list of conditions and the following disclaimer in
18 **     the documentation and/or other materials provided with the
19 **     distribution.
20 **   * Neither the name of Digia Plc and its Subsidiary(-ies) nor the names
21 **     of its contributors may be used to endorse or promote products derived
22 **     from this software without specific prior written permission.
23 **
24 **
25 ** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
26 ** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
27 ** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
28 ** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
29 ** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
30 ** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
31 ** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
32 ** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
33 ** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
34 ** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
35 ** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE."
36 **
37 ** $QT_END_LICENSE$
38 **
39 ****************************************************************************/
40
41 #include "wldatadevicemanager.h"
42
43 #include "wldatadevice.h"
44 #include "wldatasource.h"
45 #include "wlinputdevice.h"
46 #include "wlcompositor.h"
47 #include "wldataoffer.h"
48 #include "wlsurface.h"
49 #include "qwaylandmimehelper.h"
50
51 #include <QtCore/QDebug>
52 #include <QtCore/QSocketNotifier>
53 #include <fcntl.h>
54 #include <QtCore/private/qcore_unix_p.h>
55 #include <QtCore/QFile>
56
57 namespace Wayland {
58
59 DataDeviceManager::DataDeviceManager(Compositor *compositor)
60     : m_compositor(compositor)
61     , m_current_selection_source(0)
62     , m_retainedReadNotifier(0)
63     , m_compositorOwnsSelection(false)
64 {
65     wl_display_add_global(compositor->wl_display(), &wl_data_device_manager_interface, this, DataDeviceManager::bind_func_drag);
66 }
67
68 void DataDeviceManager::setCurrentSelectionSource(DataSource *source)
69 {
70     if (m_current_selection_source
71             && m_current_selection_source->time() > source->time()) {
72         qDebug() << "Trying to set older selection";
73         return;
74     }
75
76     m_compositorOwnsSelection = false;
77
78     finishReadFromClient();
79
80     m_current_selection_source = source;
81     source->setManager(this);
82
83     // When retained selection is enabled, the compositor will query all the data from the client.
84     // This makes it possible to
85     //    1. supply the selection after the offering client is gone
86     //    2. make it possible for the compositor to participate in copy-paste
87     // The downside is decreased performance, therefore this mode has to be enabled
88     // explicitly in the compositors.
89     if (m_compositor->wantsRetainedSelection()) {
90         m_retainedData.clear();
91         m_retainedReadIndex = 0;
92         retain();
93     }
94 }
95
96 void DataDeviceManager::sourceDestroyed(DataSource *source)
97 {
98     Q_UNUSED(source);
99     if (m_current_selection_source == source)
100         finishReadFromClient();
101 }
102
103 void DataDeviceManager::retain()
104 {
105     QList<QByteArray> offers = m_current_selection_source->offerList();
106     finishReadFromClient();
107     if (m_retainedReadIndex >= offers.count()) {
108         m_compositor->feedRetainedSelectionData(&m_retainedData);
109         return;
110     }
111     QByteArray mimeType = offers.at(m_retainedReadIndex);
112     m_retainedReadBuf.clear();
113     int fd[2];
114     if (pipe(fd) == -1) {
115         qWarning("Clipboard: Failed to create pipe");
116         return;
117     }
118     fcntl(fd[0], F_SETFL, fcntl(fd[0], F_GETFL, 0) | O_NONBLOCK);
119     m_current_selection_source->postSendEvent(mimeType, fd[1]);
120     close(fd[1]);
121     m_retainedReadNotifier = new QSocketNotifier(fd[0], QSocketNotifier::Read, this);
122     connect(m_retainedReadNotifier, SIGNAL(activated(int)), SLOT(readFromClient(int)));
123 }
124
125 void DataDeviceManager::finishReadFromClient(bool exhausted)
126 {
127     Q_UNUSED(exhausted);
128     if (m_retainedReadNotifier) {
129         if (exhausted) {
130             int fd = m_retainedReadNotifier->socket();
131             delete m_retainedReadNotifier;
132             close(fd);
133         } else {
134             // Do not close the handle or destroy the read notifier here
135             // or else clients may SIGPIPE.
136             m_obsoleteRetainedReadNotifiers.append(m_retainedReadNotifier);
137         }
138         m_retainedReadNotifier = 0;
139     }
140 }
141
142 void DataDeviceManager::readFromClient(int fd)
143 {
144     static char buf[4096];
145     int obsCount = m_obsoleteRetainedReadNotifiers.count();
146     for (int i = 0; i < obsCount; ++i) {
147         QSocketNotifier *sn = m_obsoleteRetainedReadNotifiers.at(i);
148         if (sn->socket() == fd) {
149             // Read and drop the data, stopping to read and closing the handle
150             // is not yet safe because that could kill the client with SIGPIPE
151             // when it still tries to write.
152             int n;
153             do {
154                 n = QT_READ(fd, buf, sizeof buf);
155             } while (n > 0);
156             if (n != -1 || (errno != EAGAIN && errno != EWOULDBLOCK)) {
157                 m_obsoleteRetainedReadNotifiers.removeAt(i);
158                 delete sn;
159                 close(fd);
160             }
161             return;
162         }
163     }
164     int n = QT_READ(fd, buf, sizeof buf);
165     if (n <= 0) {
166         if (n != -1 || (errno != EAGAIN && errno != EWOULDBLOCK)) {
167             finishReadFromClient(true);
168             QList<QByteArray> offers = m_current_selection_source->offerList();
169             QString mimeType = QString::fromLatin1(offers.at(m_retainedReadIndex));
170             m_retainedData.setData(mimeType, m_retainedReadBuf);
171             ++m_retainedReadIndex;
172             retain();
173         }
174     } else {
175         m_retainedReadBuf.append(buf, n);
176     }
177 }
178
179 DataSource *DataDeviceManager::currentSelectionSource()
180 {
181     return m_current_selection_source;
182 }
183
184 struct wl_display *DataDeviceManager::display() const
185 {
186     return m_compositor->wl_display();
187 }
188
189 void DataDeviceManager::bind_func_drag(struct wl_client *client, void *data, uint32_t version, uint32_t id)
190 {
191     Q_UNUSED(version);
192     wl_client_add_object(client,&wl_data_device_manager_interface,&drag_interface,id,data);
193 }
194
195 void DataDeviceManager::bind_func_data(struct wl_client *client, void *data, uint32_t version, uint32_t id)
196 {
197     Q_UNUSED(client);
198     Q_UNUSED(data);
199     Q_UNUSED(version);
200     Q_UNUSED(id);
201 }
202
203 void DataDeviceManager::get_data_device(struct wl_client *client,
204                       struct wl_resource *data_device_manager_resource,
205                       uint32_t id,
206                       struct wl_resource *input_device_resource)
207 {
208     DataDeviceManager *data_device_manager = static_cast<DataDeviceManager *>(data_device_manager_resource->data);
209     InputDevice *input_device = resolve<InputDevice>(input_device_resource);
210     input_device->clientRequestedDataDevice(data_device_manager,client,id);
211 }
212
213 void DataDeviceManager::create_data_source(struct wl_client *client,
214                                struct wl_resource *data_device_manager_resource,
215                                uint32_t id)
216 {
217     Q_UNUSED(data_device_manager_resource);
218     new DataSource(client,id, Compositor::currentTimeMsecs());
219 }
220
221 struct wl_data_device_manager_interface DataDeviceManager::drag_interface = {
222     DataDeviceManager::create_data_source,
223     DataDeviceManager::get_data_device
224 };
225
226 void DataDeviceManager::overrideSelection(const QMimeData &mimeData)
227 {
228     QStringList formats = mimeData.formats();
229     if (formats.isEmpty())
230         return;
231
232     m_retainedData.clear();
233     foreach (const QString &format, formats)
234         m_retainedData.setData(format, mimeData.data(format));
235
236     m_compositor->feedRetainedSelectionData(&m_retainedData);
237
238     m_compositorOwnsSelection = true;
239
240     InputDevice *dev = m_compositor->defaultInputDevice();
241     Surface *focusSurface = dev->keyboardFocus();
242     if (focusSurface)
243         offerFromCompositorToClient(
244                     dev->dataDevice(focusSurface->base()->resource.client)->dataDeviceResource());
245 }
246
247 bool DataDeviceManager::offerFromCompositorToClient(wl_resource *clientDataDeviceResource)
248 {
249     if (!m_compositorOwnsSelection)
250         return false;
251
252     wl_client *client = clientDataDeviceResource->client;
253     //qDebug("compositor offers %d types to %p", m_retainedData.formats().count(), client);
254
255     struct wl_resource *selectionOffer =
256              wl_client_new_object(client, &wl_data_offer_interface, &compositor_offer_interface, this);
257     wl_data_device_send_data_offer(clientDataDeviceResource, selectionOffer);
258     foreach (const QString &format, m_retainedData.formats()) {
259         QByteArray ba = format.toLatin1();
260         wl_data_offer_send_offer(selectionOffer, ba.constData());
261     }
262     wl_data_device_send_selection(clientDataDeviceResource, selectionOffer);
263
264     return true;
265 }
266
267 void DataDeviceManager::offerRetainedSelection(wl_resource *clientDataDeviceResource)
268 {
269     if (m_retainedData.formats().isEmpty())
270         return;
271
272     m_compositorOwnsSelection = true;
273     offerFromCompositorToClient(clientDataDeviceResource);
274 }
275
276 void DataDeviceManager::comp_accept(wl_client *, wl_resource *, uint32_t, const char *)
277 {
278 }
279
280 void DataDeviceManager::comp_receive(wl_client *client, wl_resource *resource, const char *mime_type, int32_t fd)
281 {
282     Q_UNUSED(client);
283     DataDeviceManager *self = static_cast<DataDeviceManager *>(resource->data);
284     //qDebug("client %p wants data for type %s from compositor", client, mime_type);
285     QByteArray content = QWaylandMimeHelper::getByteArray(&self->m_retainedData, QString::fromLatin1(mime_type));
286     if (!content.isEmpty()) {
287         QFile f;
288         if (f.open(fd, QIODevice::WriteOnly))
289             f.write(content);
290     }
291     close(fd);
292 }
293
294 void DataDeviceManager::comp_destroy(wl_client *, wl_resource *)
295 {
296 }
297
298 const struct wl_data_offer_interface DataDeviceManager::compositor_offer_interface = {
299     DataDeviceManager::comp_accept,
300     DataDeviceManager::comp_receive,
301     DataDeviceManager::comp_destroy
302 };
303
304 } //namespace