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 | |
14 | QT_BEGIN_NAMESPACE |
15 | |
16 | namespace QtWaylandClient { |
17 | |
18 | static QString utf8Text() |
19 | { |
20 | return QStringLiteral("text/plain;charset=utf-8" ); |
21 | } |
22 | |
23 | QWaylandDataOffer::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 | |
30 | QWaylandDataOffer::~QWaylandDataOffer() |
31 | { |
32 | destroy(); |
33 | } |
34 | |
35 | |
36 | QString QWaylandDataOffer::firstFormat() const |
37 | { |
38 | if (m_mimeData->formats().isEmpty()) |
39 | return QString(); |
40 | |
41 | return m_mimeData->formats().first(); |
42 | } |
43 | |
44 | QMimeData *QWaylandDataOffer::mimeData() |
45 | { |
46 | return m_mimeData.data(); |
47 | } |
48 | |
49 | Qt::DropActions QWaylandDataOffer::supportedActions() const |
50 | { |
51 | if (version() < 3) { |
52 | return Qt::MoveAction | Qt::CopyAction; |
53 | } |
54 | |
55 | return m_supportedActions; |
56 | } |
57 | |
58 | void QWaylandDataOffer::startReceiving(const QString &mimeType, int fd) |
59 | { |
60 | receive(mimeType, fd); |
61 | wl_display_flush(m_display->wl_display()); |
62 | } |
63 | |
64 | void QWaylandDataOffer::data_offer_offer(const QString &mime_type) |
65 | { |
66 | m_mimeData->appendFormat(mimeType: mime_type); |
67 | } |
68 | |
69 | void 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 | |
76 | void 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 | |
85 | QWaylandMimeData::QWaylandMimeData(QWaylandAbstractDataOffer *dataOffer) |
86 | : m_dataOffer(dataOffer) |
87 | { |
88 | } |
89 | |
90 | QWaylandMimeData::~QWaylandMimeData() |
91 | { |
92 | } |
93 | |
94 | void QWaylandMimeData::appendFormat(const QString &mimeType) |
95 | { |
96 | m_types << mimeType; |
97 | m_data.remove(key: mimeType); // Clear previous contents |
98 | } |
99 | |
100 | bool 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 | |
111 | QStringList QWaylandMimeData::formats_sys() const |
112 | { |
113 | return m_types; |
114 | } |
115 | |
116 | QVariant 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 | |
154 | int 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 | |
190 | QT_END_NAMESPACE |
191 | |