1 /****************************************************************************
3 ** Copyright (C) 2012 Nokia Corporation and/or its subsidiary(-ies).
4 ** All rights reserved.
5 ** Contact: Nokia Corporation (qt-info@nokia.com)
7 ** This file is part of the Qt Compositor.
9 ** $QT_BEGIN_LICENSE:BSD$
10 ** You may use this file under the terms of the BSD license as follows:
12 ** "Redistribution and use in source and binary forms, with or without
13 ** modification, are permitted provided that the following conditions are
15 ** * Redistributions of source code must retain the above copyright
16 ** notice, this list of conditions and the following disclaimer.
17 ** * Redistributions in binary form must reproduce the above copyright
18 ** notice, this list of conditions and the following disclaimer in
19 ** the documentation and/or other materials provided with the
21 ** * Neither the name of Nokia Corporation and its Subsidiary(-ies) nor
22 ** the names of its contributors may be used to endorse or promote
23 ** products derived from this software without specific prior written
26 ** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
27 ** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
28 ** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
29 ** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
30 ** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
31 ** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
32 ** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
33 ** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
34 ** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
35 ** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
36 ** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE."
39 ****************************************************************************/
41 #include "wldatadevicemanager.h"
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"
51 #include <QtCore/QDebug>
52 #include <QtCore/QSocketNotifier>
54 #include <QtCore/private/qcore_unix_p.h>
55 #include <QtCore/QFile>
59 DataDeviceManager::DataDeviceManager(Compositor *compositor)
60 : m_compositor(compositor)
61 , m_current_selection_source(0)
62 , m_retainedReadNotifier(0)
63 , m_compositorOwnsSelection(false)
65 wl_display_add_global(compositor->wl_display(), &wl_data_device_manager_interface, this, DataDeviceManager::bind_func_drag);
68 void DataDeviceManager::setCurrentSelectionSource(DataSource *source)
70 if (m_current_selection_source
71 && m_current_selection_source->time() > source->time()) {
72 qDebug() << "Trying to set older selection";
76 m_compositorOwnsSelection = false;
78 finishReadFromClient();
80 m_current_selection_source = source;
81 source->setManager(this);
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;
96 void DataDeviceManager::sourceDestroyed(DataSource *source)
98 if (m_current_selection_source == source)
99 finishReadFromClient();
102 void DataDeviceManager::retain()
104 QList<QByteArray> offers = m_current_selection_source->offerList();
105 finishReadFromClient();
106 if (m_retainedReadIndex >= offers.count()) {
107 m_compositor->feedRetainedSelectionData(&m_retainedData);
110 QByteArray mimeType = offers.at(m_retainedReadIndex);
111 m_retainedReadBuf.clear();
113 if (pipe(fd) == -1) {
114 qWarning("Clipboard: Failed to create pipe");
117 fcntl(fd[0], F_SETFL, fcntl(fd[0], F_GETFL, 0) | O_NONBLOCK);
118 m_current_selection_source->postSendEvent(mimeType, fd[1]);
120 m_retainedReadNotifier = new QSocketNotifier(fd[0], QSocketNotifier::Read, this);
121 connect(m_retainedReadNotifier, SIGNAL(activated(int)), SLOT(readFromClient(int)));
124 void DataDeviceManager::finishReadFromClient(bool exhausted)
126 if (m_retainedReadNotifier) {
128 int fd = m_retainedReadNotifier->socket();
129 delete m_retainedReadNotifier;
132 // Do not close the handle or destroy the read notifier here
133 // or else clients may SIGPIPE.
134 m_obsoleteRetainedReadNotifiers.append(m_retainedReadNotifier);
136 m_retainedReadNotifier = 0;
140 void DataDeviceManager::readFromClient(int fd)
142 static char buf[4096];
143 int obsCount = m_obsoleteRetainedReadNotifiers.count();
144 for (int i = 0; i < obsCount; ++i) {
145 QSocketNotifier *sn = m_obsoleteRetainedReadNotifiers.at(i);
146 if (sn->socket() == fd) {
147 // Read and drop the data, stopping to read and closing the handle
148 // is not yet safe because that could kill the client with SIGPIPE
149 // when it still tries to write.
152 n = QT_READ(fd, buf, sizeof buf);
154 if (n != -1 || (errno != EAGAIN && errno != EWOULDBLOCK)) {
155 m_obsoleteRetainedReadNotifiers.removeAt(i);
162 int n = QT_READ(fd, buf, sizeof buf);
164 if (n != -1 || (errno != EAGAIN && errno != EWOULDBLOCK)) {
165 finishReadFromClient(true);
166 QList<QByteArray> offers = m_current_selection_source->offerList();
167 QString mimeType = QString::fromLatin1(offers.at(m_retainedReadIndex));
168 m_retainedData.setData(mimeType, m_retainedReadBuf);
169 ++m_retainedReadIndex;
173 m_retainedReadBuf.append(buf, n);
177 DataSource *DataDeviceManager::currentSelectionSource()
179 return m_current_selection_source;
182 struct wl_display *DataDeviceManager::display() const
184 return m_compositor->wl_display();
187 void DataDeviceManager::bind_func_drag(struct wl_client *client, void *data, uint32_t version, uint32_t id)
189 wl_client_add_object(client,&wl_data_device_manager_interface,&drag_interface,id,data);
192 void DataDeviceManager::bind_func_data(struct wl_client *client, void *data, uint32_t version, uint32_t id)
196 void DataDeviceManager::get_data_device(struct wl_client *client,
197 struct wl_resource *data_device_manager_resource,
199 struct wl_resource *input_device_resource)
201 DataDeviceManager *data_device_manager = static_cast<DataDeviceManager *>(data_device_manager_resource->data);
202 InputDevice *input_device = reinterpret_cast<InputDevice *>(input_device_resource->data);
203 input_device->clientRequestedDataDevice(data_device_manager,client,id);
206 void DataDeviceManager::create_data_source(struct wl_client *client,
207 struct wl_resource *data_device_manager_resource,
210 Q_UNUSED(data_device_manager_resource);
211 new DataSource(client,id, Compositor::currentTimeMsecs());
214 struct wl_data_device_manager_interface DataDeviceManager::drag_interface = {
215 DataDeviceManager::create_data_source,
216 DataDeviceManager::get_data_device
219 void DataDeviceManager::overrideSelection(const QMimeData &mimeData)
221 QStringList formats = mimeData.formats();
222 if (formats.isEmpty())
225 m_retainedData.clear();
226 foreach (const QString &format, formats)
227 m_retainedData.setData(format, mimeData.data(format));
229 m_compositor->feedRetainedSelectionData(&m_retainedData);
231 m_compositorOwnsSelection = true;
233 InputDevice *dev = m_compositor->defaultInputDevice();
234 Surface *focusSurface = dev->keyboardFocus();
236 offerFromCompositorToClient(
237 dev->dataDevice(focusSurface->base()->resource.client)->dataDeviceResource());
240 bool DataDeviceManager::offerFromCompositorToClient(wl_resource *clientDataDeviceResource)
242 if (!m_compositorOwnsSelection)
245 wl_client *client = clientDataDeviceResource->client;
246 qDebug("compositor offers %d types to %p", m_retainedData.formats().count(), client);
248 struct wl_resource *selectionOffer =
249 wl_client_new_object(client, &wl_data_offer_interface, &compositor_offer_interface, this);
250 wl_resource_post_event(clientDataDeviceResource, WL_DATA_DEVICE_DATA_OFFER, selectionOffer);
251 foreach (const QString &format, m_retainedData.formats()) {
252 QByteArray ba = format.toLatin1();
253 wl_resource_post_event(selectionOffer, WL_DATA_OFFER_OFFER, ba.constData());
255 wl_resource_post_event(clientDataDeviceResource, WL_DATA_DEVICE_SELECTION, selectionOffer);
260 void DataDeviceManager::comp_accept(wl_client *, wl_resource *, uint32_t, const char *)
264 void DataDeviceManager::comp_receive(wl_client *client, wl_resource *resource, const char *mime_type, int32_t fd)
266 DataDeviceManager *self = static_cast<DataDeviceManager *>(resource->data);
267 qDebug("client %p wants data for type %s from compositor", client, mime_type);
268 QByteArray content = QWaylandMimeHelper::getByteArray(&self->m_retainedData, QString::fromLatin1(mime_type));
269 if (!content.isEmpty()) {
271 if (f.open(fd, QIODevice::WriteOnly))
277 void DataDeviceManager::comp_destroy(wl_client *, wl_resource *)
281 const struct wl_data_offer_interface DataDeviceManager::compositor_offer_interface = {
282 DataDeviceManager::comp_accept,
283 DataDeviceManager::comp_receive,
284 DataDeviceManager::comp_destroy