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 "qxcbmime.h"
5
6#include <QtGui/QImageWriter>
7#include <QtCore/QBuffer>
8#include <qdebug.h>
9
10QT_BEGIN_NAMESPACE
11
12using namespace Qt::StringLiterals;
13
14QXcbMime::QXcbMime()
15 : QInternalMimeData()
16{ }
17
18QXcbMime::~QXcbMime()
19{}
20
21
22
23QString QXcbMime::mimeAtomToString(QXcbConnection *connection, xcb_atom_t a)
24{
25 if (a == XCB_NONE)
26 return QString();
27
28 // special cases for string type
29 if (a == XCB_ATOM_STRING
30 || a == connection->atom(qatom: QXcbAtom::AtomUTF8_STRING)
31 || a == connection->atom(qatom: QXcbAtom::AtomTEXT))
32 return "text/plain"_L1;
33
34 // special case for images
35 if (a == XCB_ATOM_PIXMAP)
36 return "image/ppm"_L1;
37
38 QByteArray atomName = connection->atomName(atom: a);
39
40 // special cases for uris
41 if (atomName == "text/x-moz-url")
42 atomName = "text/uri-list";
43
44 return QString::fromLatin1(ba: atomName.constData());
45}
46
47bool QXcbMime::mimeDataForAtom(QXcbConnection *connection, xcb_atom_t a, QMimeData *mimeData, QByteArray *data,
48 xcb_atom_t *atomFormat, int *dataFormat)
49{
50 if (!data)
51 return false;
52
53 bool ret = false;
54 *atomFormat = a;
55 *dataFormat = 8;
56
57 if ((a == connection->atom(qatom: QXcbAtom::AtomUTF8_STRING)
58 || a == XCB_ATOM_STRING
59 || a == connection->atom(qatom: QXcbAtom::AtomTEXT))
60 && QInternalMimeData::hasFormatHelper(mimeType: "text/plain"_L1, data: mimeData)) {
61 if (a == connection->atom(qatom: QXcbAtom::AtomUTF8_STRING)) {
62 *data = QInternalMimeData::renderDataHelper(mimeType: "text/plain"_L1, data: mimeData);
63 ret = true;
64 } else if (a == XCB_ATOM_STRING ||
65 a == connection->atom(qatom: QXcbAtom::AtomTEXT)) {
66 // ICCCM says STRING is latin1
67 *data = QString::fromUtf8(ba: QInternalMimeData::renderDataHelper(
68 mimeType: "text/plain"_L1, data: mimeData)).toLatin1();
69 ret = true;
70 }
71 return ret;
72 }
73
74 QString atomName = mimeAtomToString(connection, a);
75 if (QInternalMimeData::hasFormatHelper(mimeType: atomName, data: mimeData)) {
76 *data = QInternalMimeData::renderDataHelper(mimeType: atomName, data: mimeData);
77 // mimeAtomToString() converts "text/x-moz-url" to "text/uri-list",
78 // so QXcbConnection::atomName() has to be used.
79 if (atomName == "text/uri-list"_L1
80 && connection->atomName(atom: a) == "text/x-moz-url") {
81 const QString mozUri = QLatin1StringView(data->split(sep: '\n').constFirst()) + u'\n';
82 data->assign(v: {reinterpret_cast<const char *>(mozUri.data()), mozUri.size() * 2});
83 } else if (atomName == "application/x-color"_L1)
84 *dataFormat = 16;
85 ret = true;
86 } else if ((a == XCB_ATOM_PIXMAP || a == XCB_ATOM_BITMAP) && mimeData->hasImage()) {
87 ret = true;
88 } else if (atomName == "text/plain"_L1 && mimeData->hasFormat(mimetype: "text/uri-list"_L1)) {
89 // Return URLs also as plain text.
90 *data = QInternalMimeData::renderDataHelper(mimeType: atomName, data: mimeData);
91 ret = true;
92 }
93 return ret;
94}
95
96QList<xcb_atom_t> QXcbMime::mimeAtomsForFormat(QXcbConnection *connection, const QString &format)
97{
98 QList<xcb_atom_t> atoms;
99 atoms.reserve(asize: 7);
100 atoms.append(t: connection->internAtom(name: format.toLatin1()));
101
102 // special cases for strings
103 if (format == "text/plain"_L1) {
104 atoms.append(t: connection->atom(qatom: QXcbAtom::AtomUTF8_STRING));
105 atoms.append(t: XCB_ATOM_STRING);
106 atoms.append(t: connection->atom(qatom: QXcbAtom::AtomTEXT));
107 }
108
109 // special cases for uris
110 if (format == "text/uri-list"_L1) {
111 atoms.append(t: connection->internAtom(name: "text/x-moz-url"));
112 atoms.append(t: connection->internAtom(name: "text/plain"));
113 }
114
115 //special cases for images
116 if (format == "image/ppm"_L1)
117 atoms.append(t: XCB_ATOM_PIXMAP);
118 if (format == "image/pbm"_L1)
119 atoms.append(t: XCB_ATOM_BITMAP);
120
121 return atoms;
122}
123
124QVariant QXcbMime::mimeConvertToFormat(QXcbConnection *connection, xcb_atom_t a, const QByteArray &d, const QString &format,
125 QMetaType requestedType, bool hasUtf8)
126{
127 QByteArray data = d;
128 QString atomName = mimeAtomToString(connection, a);
129// qDebug() << "mimeConvertDataToFormat" << format << atomName << data;
130
131 if (hasUtf8 && atomName == format + ";charset=utf-8"_L1) {
132 if (requestedType.id() == QMetaType::QString)
133 return QString::fromUtf8(ba: data);
134 return data;
135 }
136
137 // special cases for string types
138 if (format == "text/plain"_L1) {
139 if (data.endsWith(c: '\0'))
140 data.chop(n: 1);
141 if (a == connection->atom(qatom: QXcbAtom::AtomUTF8_STRING)) {
142 return QString::fromUtf8(ba: data);
143 }
144 if (a == XCB_ATOM_STRING ||
145 a == connection->atom(qatom: QXcbAtom::AtomTEXT))
146 return QString::fromLatin1(ba: data);
147 }
148 // If data contains UTF16 text, convert it to a string.
149 // Firefox uses UTF16 without BOM for text/x-moz-url, "text/html",
150 // Google Chrome uses UTF16 without BOM for "text/x-moz-url",
151 // UTF16 with BOM for "text/html".
152 if ((format == "text/html"_L1 || format == "text/uri-list"_L1)
153 && data.size() > 1) {
154 const quint8 byte0 = data.at(i: 0);
155 const quint8 byte1 = data.at(i: 1);
156 if ((byte0 == 0xff && byte1 == 0xfe) || (byte0 == 0xfe && byte1 == 0xff)
157 || (byte0 != 0 && byte1 == 0) || (byte0 == 0 && byte1 != 0)) {
158 const QString str = QString::fromUtf16(
159 reinterpret_cast<const char16_t *>(data.constData()), size: data.size() / 2);
160 if (!str.isNull()) {
161 if (format == "text/uri-list"_L1) {
162 const auto urls = QStringView{str}.split(sep: u'\n');
163 QList<QVariant> list;
164 list.reserve(asize: urls.size());
165 for (const QStringView &s : urls) {
166 const QUrl url(s.trimmed().toString());
167 if (url.isValid())
168 list.append(t: url);
169 }
170 // We expect "text/x-moz-url" as <url><space><title>.
171 // The atomName variable is not used because mimeAtomToString()
172 // converts "text/x-moz-url" to "text/uri-list".
173 if (!list.isEmpty() && connection->atomName(atom: a) == "text/x-moz-url")
174 return list.constFirst();
175 return list;
176 } else {
177 return str;
178 }
179 }
180 }
181 // 8 byte encoding, remove a possible 0 at the end
182 if (data.endsWith(c: '\0'))
183 data.chop(n: 1);
184 }
185
186 if (atomName == format)
187 return data;
188
189#if 0 // ###
190 // special case for images
191 if (format == "image/ppm"_L1) {
192 if (a == XCB_ATOM_PIXMAP && data.size() == sizeof(Pixmap)) {
193 Pixmap xpm = *((Pixmap*)data.data());
194 if (!xpm)
195 return QByteArray();
196 Window root;
197 int x;
198 int y;
199 uint width;
200 uint height;
201 uint border_width;
202 uint depth;
203
204 XGetGeometry(display, xpm, &root, &x, &y, &width, &height, &border_width, &depth);
205 XImage *ximg = XGetImage(display,xpm,x,y,width,height,AllPlanes,depth==1 ? XYPixmap : ZPixmap);
206 QImage qimg = QXlibStatic::qimageFromXImage(ximg);
207 XDestroyImage(ximg);
208
209 QImageWriter imageWriter;
210 imageWriter.setFormat("PPMRAW");
211 QBuffer buf;
212 buf.open(QIODevice::WriteOnly);
213 imageWriter.setDevice(&buf);
214 imageWriter.write(qimg);
215 return buf.buffer();
216 }
217 }
218#endif
219 return QVariant();
220}
221
222xcb_atom_t QXcbMime::mimeAtomForFormat(QXcbConnection *connection, const QString &format, QMetaType requestedType,
223 const QList<xcb_atom_t> &atoms, bool *hasUtf8)
224{
225 *hasUtf8 = false;
226
227 // find matches for string types
228 if (format == "text/plain"_L1) {
229 if (atoms.contains(t: connection->atom(qatom: QXcbAtom::AtomUTF8_STRING)))
230 return connection->atom(qatom: QXcbAtom::AtomUTF8_STRING);
231 if (atoms.contains(t: XCB_ATOM_STRING))
232 return XCB_ATOM_STRING;
233 if (atoms.contains(t: connection->atom(qatom: QXcbAtom::AtomTEXT)))
234 return connection->atom(qatom: QXcbAtom::AtomTEXT);
235 }
236
237 // find matches for uri types
238 if (format == "text/uri-list"_L1) {
239 xcb_atom_t a = connection->internAtom(name: format.toLatin1());
240 if (a && atoms.contains(t: a))
241 return a;
242 a = connection->internAtom(name: "text/x-moz-url");
243 if (a && atoms.contains(t: a))
244 return a;
245 }
246
247 // find match for image
248 if (format == "image/ppm"_L1) {
249 if (atoms.contains(t: XCB_ATOM_PIXMAP))
250 return XCB_ATOM_PIXMAP;
251 }
252
253 // for string/text requests try to use a format with a well-defined charset
254 // first to avoid encoding problems
255 if (requestedType.id() == QMetaType::QString
256 && format.startsWith(s: "text/"_L1)
257 && !format.contains(s: "charset="_L1)) {
258
259 QString formatWithCharset = format;
260 formatWithCharset.append(s: ";charset=utf-8"_L1);
261
262 xcb_atom_t a = connection->internAtom(name: std::move(formatWithCharset).toLatin1());
263 if (a && atoms.contains(t: a)) {
264 *hasUtf8 = true;
265 return a;
266 }
267 }
268
269 xcb_atom_t a = connection->internAtom(name: format.toLatin1());
270 if (a && atoms.contains(t: a))
271 return a;
272
273 return 0;
274}
275
276QT_END_NAMESPACE
277
278#include "moc_qxcbmime.cpp"
279

source code of qtbase/src/plugins/platforms/xcb/qxcbmime.cpp