1// Copyright (C) 2016 The Qt Company Ltd.
2// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
3
4#include "qwaylanddataoffer_p.h"
5#include "qwaylanddatadevicemanager_p.h"
6#include "qwaylanddisplay_p.h"
7
8#include <QtCore/private/qcore_unix_p.h>
9#include <QtGui/private/qguiapplication_p.h>
10#include <qpa/qplatformclipboard.h>
11
12#include <QtCore/QDebug>
13
14QT_BEGIN_NAMESPACE
15
16namespace QtWaylandClient {
17
18static QString utf8Text()
19{
20 return QStringLiteral("text/plain;charset=utf-8");
21}
22
23QWaylandDataOffer::QWaylandDataOffer(QWaylandDisplay *display, struct ::wl_data_offer *offer)
24 : QtWayland::wl_data_offer(offer)
25 , m_display(display)
26 , m_mimeData(new QWaylandMimeData(this))
27{
28}
29
30QWaylandDataOffer::~QWaylandDataOffer()
31{
32 destroy();
33}
34
35
36QString QWaylandDataOffer::firstFormat() const
37{
38 if (m_mimeData->formats().isEmpty())
39 return QString();
40
41 return m_mimeData->formats().first();
42}
43
44QMimeData *QWaylandDataOffer::mimeData()
45{
46 return m_mimeData.data();
47}
48
49Qt::DropActions QWaylandDataOffer::supportedActions() const
50{
51 if (version() < 3) {
52 return Qt::MoveAction | Qt::CopyAction;
53 }
54
55 return m_supportedActions;
56}
57
58void QWaylandDataOffer::startReceiving(const QString &mimeType, int fd)
59{
60 receive(mimeType, fd);
61 wl_display_flush(m_display->wl_display());
62}
63
64void QWaylandDataOffer::data_offer_offer(const QString &mime_type)
65{
66 m_mimeData->appendFormat(mimeType: mime_type);
67}
68
69void QWaylandDataOffer::data_offer_action(uint32_t dnd_action)
70{
71 Q_UNUSED(dnd_action);
72 // This is the compositor telling the drag target what action it should perform
73 // It does not map nicely into Qt final drop semantics, other than pretending there is only one supported action?
74}
75
76void QWaylandDataOffer::data_offer_source_actions(uint32_t source_actions)
77{
78 m_supportedActions = Qt::DropActions();
79 if (source_actions & WL_DATA_DEVICE_MANAGER_DND_ACTION_MOVE)
80 m_supportedActions |= Qt::MoveAction;
81 if (source_actions & WL_DATA_DEVICE_MANAGER_DND_ACTION_COPY)
82 m_supportedActions |= Qt::CopyAction;
83}
84
85QWaylandMimeData::QWaylandMimeData(QWaylandAbstractDataOffer *dataOffer)
86 : m_dataOffer(dataOffer)
87{
88}
89
90QWaylandMimeData::~QWaylandMimeData()
91{
92}
93
94void QWaylandMimeData::appendFormat(const QString &mimeType)
95{
96 m_types << mimeType;
97 m_data.remove(key: mimeType); // Clear previous contents
98}
99
100bool QWaylandMimeData::hasFormat_sys(const QString &mimeType) const
101{
102 if (m_types.contains(str: mimeType))
103 return true;
104
105 if (mimeType == QStringLiteral("text/plain") && m_types.contains(str: utf8Text()))
106 return true;
107
108 return false;
109}
110
111QStringList QWaylandMimeData::formats_sys() const
112{
113 return m_types;
114}
115
116QVariant QWaylandMimeData::retrieveData_sys(const QString &mimeType, QMetaType type) const
117{
118 Q_UNUSED(type);
119
120 auto it = m_data.constFind(key: mimeType);
121 if (it != m_data.constEnd())
122 return *it;
123
124 QString mime = mimeType;
125
126 if (!m_types.contains(str: mimeType)) {
127 if (mimeType == QStringLiteral("text/plain") && m_types.contains(str: utf8Text()))
128 mime = utf8Text();
129 else
130 return QVariant();
131 }
132
133 int pipefd[2];
134 if (qt_safe_pipe(pipefd) == -1) {
135 qWarning(msg: "QWaylandMimeData: pipe2() failed");
136 return QVariant();
137 }
138
139 m_dataOffer->startReceiving(mimeType: mime, fd: pipefd[1]);
140
141 close(fd: pipefd[1]);
142
143 QByteArray content;
144 if (readData(fd: pipefd[0], data&: content) != 0) {
145 qWarning(msg: "QWaylandDataOffer: error reading data for mimeType %s", qPrintable(mimeType));
146 content = QByteArray();
147 }
148
149 close(fd: pipefd[0]);
150 m_data.insert(key: mimeType, value: content);
151 return content;
152}
153
154int QWaylandMimeData::readData(int fd, QByteArray &data) const
155{
156 struct pollfd readset;
157 readset.fd = fd;
158 readset.events = POLLIN;
159 struct timespec timeout;
160 timeout.tv_sec = 1;
161 timeout.tv_nsec = 0;
162
163
164 Q_FOREVER {
165 int ready = qt_safe_poll(fds: &readset, nfds: 1, timeout_ts: &timeout);
166 if (ready < 0) {
167 qWarning() << "QWaylandDataOffer: qt_safe_poll() failed";
168 return -1;
169 } else if (ready == 0) {
170 qWarning(msg: "QWaylandDataOffer: timeout reading from pipe");
171 return -1;
172 } else {
173 char buf[4096];
174 int n = QT_READ(fd, data: buf, maxlen: sizeof buf);
175
176 if (n < 0) {
177 qWarning(msg: "QWaylandDataOffer: read() failed");
178 return -1;
179 } else if (n == 0) {
180 return 0;
181 } else if (n > 0) {
182 data.append(s: buf, len: n);
183 }
184 }
185 }
186}
187
188}
189
190QT_END_NAMESPACE
191

source code of qtwayland/src/client/qwaylanddataoffer.cpp