Qt
Internal/Contributor docs for the Qt SDK. <b>Note:</b> These are NOT official API docs; those are found <a href='https://doc.qt.io/'>here</a>.
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
5
6#include <QtWaylandCompositor/QWaylandCompositor>
7
8#include <QtWaylandCompositor/private/qwaylandcompositor_p.h>
9#include <QtWaylandCompositor/private/qwaylandseat_p.h>
10#include "qwldatadevice_p.h"
11#include "qwldatasource_p.h"
12#include "qwldataoffer_p.h"
14
15#include <QtCore/QDebug>
16#include <QtCore/QSocketNotifier>
17#include <fcntl.h>
18#include <QtCore/private/qcore_unix_p.h>
19#include <QtCore/QFile>
20
22
23namespace QtWayland {
24
26 : wl_data_device_manager(compositor->display(), 1)
27 , m_compositor(compositor)
28{
29}
30
32{
33 if (m_current_selection_source && source
34 && m_current_selection_source->time() > source->time()) {
35 qDebug() << "Trying to set older selection";
36 return;
37 }
38
39 m_compositorOwnsSelection = false;
40
41 finishReadFromClient();
42
43 m_current_selection_source = source;
44 if (source)
45 source->setManager(this);
46
47 // When retained selection is enabled, the compositor will query all the data from the client.
48 // This makes it possible to
49 // 1. supply the selection after the offering client is gone
50 // 2. make it possible for the compositor to participate in copy-paste
51 // The downside is decreased performance, therefore this mode has to be enabled
52 // explicitly in the compositors.
53 if (source && m_compositor->retainedSelectionEnabled()) {
54 m_retainedData.clear();
55 m_retainedReadIndex = 0;
56 retain();
57 }
58}
59
61{
62 if (m_current_selection_source == source)
63 finishReadFromClient();
64}
65
66void DataDeviceManager::retain()
67{
68 QList<QString> offers = m_current_selection_source->mimeTypes();
69 finishReadFromClient();
70 if (m_retainedReadIndex >= offers.size()) {
71 QWaylandCompositorPrivate::get(m_compositor)->feedRetainedSelectionData(&m_retainedData);
72 return;
73 }
74 QString mimeType = offers.at(m_retainedReadIndex);
75 m_retainedReadBuf.clear();
76 int fd[2];
77 if (pipe(fd) == -1) {
78 qWarning("Clipboard: Failed to create pipe");
79 return;
80 }
81 fcntl(fd[0], F_SETFL, fcntl(fd[0], F_GETFL, 0) | O_NONBLOCK);
82 m_current_selection_source->send(mimeType, fd[1]);
83 m_retainedReadNotifier = new QSocketNotifier(fd[0], QSocketNotifier::Read, this);
84 connect(m_retainedReadNotifier, &QSocketNotifier::activated, this, &DataDeviceManager::readFromClient);
85}
86
87void DataDeviceManager::finishReadFromClient(bool exhausted)
88{
89 Q_UNUSED(exhausted);
90 if (m_retainedReadNotifier) {
91 if (exhausted) {
92 int fd = m_retainedReadNotifier->socket();
93 delete m_retainedReadNotifier;
94 close(fd);
95 } else {
96 // Do not close the handle or destroy the read notifier here
97 // or else clients may SIGPIPE.
98 m_obsoleteRetainedReadNotifiers.append(m_retainedReadNotifier);
99 }
100 m_retainedReadNotifier = nullptr;
101 }
102}
103
104void DataDeviceManager::readFromClient(int fd)
105{
106 static char buf[4096];
107 int obsCount = m_obsoleteRetainedReadNotifiers.size();
108 for (int i = 0; i < obsCount; ++i) {
109 QSocketNotifier *sn = m_obsoleteRetainedReadNotifiers.at(i);
110 if (sn->socket() == fd) {
111 // Read and drop the data, stopping to read and closing the handle
112 // is not yet safe because that could kill the client with SIGPIPE
113 // when it still tries to write.
114 int n;
115 do {
116 n = QT_READ(fd, buf, sizeof buf);
117 } while (n > 0);
118 if (n != -1 || (errno != EAGAIN && errno != EWOULDBLOCK)) {
119 m_obsoleteRetainedReadNotifiers.removeAt(i);
120 delete sn;
121 close(fd);
122 }
123 return;
124 }
125 }
126 int n = QT_READ(fd, buf, sizeof buf);
127 if (n <= 0) {
128 if (n != -1 || (errno != EAGAIN && errno != EWOULDBLOCK)) {
129 finishReadFromClient(true);
130 QList<QString> offers = m_current_selection_source->mimeTypes();
131 QString mimeType = offers.at(m_retainedReadIndex);
132 m_retainedData.setData(mimeType, m_retainedReadBuf);
133 ++m_retainedReadIndex;
134 retain();
135 }
136 } else {
137 m_retainedReadBuf.append(buf, n);
138 }
139}
140
142{
143 return m_current_selection_source;
144}
145
146struct wl_display *DataDeviceManager::display() const
147{
148 return m_compositor->display();
149}
150
152{
154 if (formats.isEmpty())
155 return;
156
157 m_retainedData.clear();
158 for (const QString &format : formats)
159 m_retainedData.setData(format, mimeData.data(format));
160
161 QWaylandCompositorPrivate::get(m_compositor)->feedRetainedSelectionData(&m_retainedData);
162
163 m_compositorOwnsSelection = true;
164
165 QWaylandSeat *dev = m_compositor->defaultSeat();
166 QWaylandSurface *focusSurface = dev->keyboardFocus();
167 if (focusSurface)
169 QWaylandSeatPrivate::get(dev)->dataDevice()->resourceMap().value(focusSurface->waylandClient())->handle);
170}
171
172bool DataDeviceManager::offerFromCompositorToClient(wl_resource *clientDataDeviceResource)
173{
174 if (!m_compositorOwnsSelection)
175 return false;
176
177 wl_client *client = wl_resource_get_client(clientDataDeviceResource);
178 //qDebug("compositor offers %d types to %p", m_retainedData.formats().count(), client);
179
180 struct wl_resource *selectionOffer =
181 wl_resource_create(client, &wl_data_offer_interface, -1, 0);
182 wl_resource_set_implementation(selectionOffer, &compositor_offer_interface, this, nullptr);
183 wl_data_device_send_data_offer(clientDataDeviceResource, selectionOffer);
184 const auto formats = m_retainedData.formats();
185 for (const QString &format : formats) {
186 QByteArray ba = format.toLatin1();
187 wl_data_offer_send_offer(selectionOffer, ba.constData());
188 }
189 wl_data_device_send_selection(clientDataDeviceResource, selectionOffer);
190
191 return true;
192}
193
194void DataDeviceManager::offerRetainedSelection(wl_resource *clientDataDeviceResource)
195{
196 if (m_retainedData.formats().isEmpty())
197 return;
198
199 m_compositorOwnsSelection = true;
200 offerFromCompositorToClient(clientDataDeviceResource);
201}
202
204{
205 new DataSource(resource->client(), id, m_compositor->currentTimeMsecs());
206}
207
208void DataDeviceManager::data_device_manager_get_data_device(Resource *resource, uint32_t id, struct ::wl_resource *seat)
209{
210 QWaylandSeat *input_device = QWaylandSeat::fromSeatResource(seat);
211 QWaylandSeatPrivate::get(input_device)->clientRequestedDataDevice(this, resource->client(), id);
212}
213
214void DataDeviceManager::comp_accept(wl_client *, wl_resource *, uint32_t, const char *)
215{
216}
217
218void DataDeviceManager::comp_receive(wl_client *client, wl_resource *resource, const char *mime_type, int32_t fd)
219{
220 Q_UNUSED(client);
221 DataDeviceManager *self = static_cast<DataDeviceManager *>(wl_resource_get_user_data(resource));
222 //qDebug("client %p wants data for type %s from compositor", client, mime_type);
223 QByteArray content = QWaylandMimeHelper::getByteArray(&self->m_retainedData, QString::fromLatin1(mime_type));
224 if (!content.isEmpty()) {
225 QFile f;
226 if (f.open(fd, QIODevice::WriteOnly))
227 f.write(content);
228 }
229 close(fd);
230}
231
232void DataDeviceManager::comp_destroy(wl_client *, wl_resource *)
233{
234}
235
236QT_WARNING_DISABLE_GCC("-Wmissing-field-initializers")
237QT_WARNING_DISABLE_CLANG("-Wmissing-field-initializers")
238
239const struct wl_data_offer_interface DataDeviceManager::compositor_offer_interface = {
240 DataDeviceManager::comp_accept,
241 DataDeviceManager::comp_receive,
242 DataDeviceManager::comp_destroy
243};
244
245} //namespace
246
248
249#include "moc_qwldatadevicemanager_p.cpp"
\inmodule QtCore
Definition qbytearray.h:57
const char * constData() const noexcept
Returns a pointer to the const data stored in the byte array.
Definition qbytearray.h:124
bool isEmpty() const noexcept
Returns true if the byte array has size 0; otherwise returns false.
Definition qbytearray.h:107
void clear()
Clears the contents of the byte array and makes it null.
QByteArray & append(char c)
This is an overloaded member function, provided for convenience. It differs from the above function o...
\inmodule QtCore
Definition qfile.h:93
qint64 write(const char *data, qint64 len)
Writes at most maxSize bytes of data from data to the device.
qsizetype size() const noexcept
Definition qlist.h:397
void removeAt(qsizetype i)
Definition qlist.h:590
const_reference at(qsizetype i) const noexcept
Definition qlist.h:446
void append(parameter_type t)
Definition qlist.h:458
\inmodule QtCore
Definition qmimedata.h:16
void setData(const QString &mimetype, const QByteArray &data)
Sets the data associated with the MIME type given by mimeType to the specified data.
QByteArray data(const QString &mimetype) const
Returns the data stored in the object in the format described by the MIME type specified by mimeType.
void clear()
Removes all the MIME type and data entries in the object.
virtual QStringList formats() const
Returns a list of formats supported by the object.
static QMetaObject::Connection connect(const QObject *sender, const char *signal, const QObject *receiver, const char *member, Qt::ConnectionType=Qt::AutoConnection)
\threadsafe
Definition qobject.cpp:2960
\inmodule QtCore
void activated(QSocketDescriptor socket, QSocketNotifier::Type activationEvent, QPrivateSignal)
qintptr socket() const
Returns the socket identifier assigned to this object.
\inmodule QtCore
\macro QT_RESTRICTED_CAST_FROM_ASCII
Definition qstring.h:129
static QString fromLatin1(QByteArrayView ba)
This is an overloaded member function, provided for convenience. It differs from the above function o...
Definition qstring.cpp:5871
static QWaylandCompositorPrivate * get(QWaylandCompositor *compositor)
\qmltype WaylandCompositor \instantiates QWaylandCompositor \inqmlmodule QtWayland....
static QByteArray getByteArray(QMimeData *mimeData, const QString &mimeType)
static QWaylandSeatPrivate * get(QWaylandSeat *device)
\qmltype WaylandSeat \instantiates QWaylandSeat \inqmlmodule QtWayland.Compositor
static QWaylandSeat * fromSeatResource(struct ::wl_resource *resource)
Returns the QWaylandSeat corresponding to the resource.
QWaylandSurface * keyboardFocus() const
Returns the current focused surface for keyboard input.
\qmltype WaylandSurface \instantiates QWaylandSurface \inqmlmodule QtWayland.Compositor
void send(const QString &mimeType, int fd)
uint32_t time() const
QList< QString > mimeTypes() const
struct wl_display * display
Definition linuxdmabuf.h:41
EGLint EGLint * formats
Combined button and popup list for selecting options.
#define QT_WARNING_DISABLE_GCC(text)
#define QT_WARNING_DISABLE_CLANG(text)
#define QT_READ
EGLOutputLayerEXT EGLint EGLAttrib value
[5]
const char * mimeType
#define qDebug
[1]
Definition qlogging.h:164
#define qWarning
Definition qlogging.h:166
static QOpenGLCompositor * compositor
GLfloat GLfloat f
GLenum GLuint GLenum GLsizei const GLchar * buf
GLuint64 GLenum GLint fd
GLfloat n
GLint GLsizei GLsizei GLenum format
GLsizei GLsizei GLchar * source
#define Q_UNUSED(x)
QByteArray ba
[0]
QMimeData * mimeData
bool offerFromCompositorToClient(wl_resource *clientDataDeviceResource)
void data_device_manager_get_data_device(Resource *resource, uint32_t id, struct ::wl_resource *seat) override
DataDeviceManager(QWaylandCompositor *compositor)
struct wl_display * display() const
void overrideSelection(const QMimeData &mimeData)
void sourceDestroyed(DataSource *source)
void offerRetainedSelection(wl_resource *clientDataDeviceResource)
void setCurrentSelectionSource(DataSource *source)
void data_device_manager_create_data_source(Resource *resource, uint32_t id) override