KGuiAddons

waylandclipboard.cpp
1/*
2 SPDX-FileCopyrightText: 2020 David Edmundson <davidedmundson@kde.org>
3 SPDX-FileCopyrightText: 2021 Méven Car <meven.car@enioka.com>
4
5 SPDX-License-Identifier: LGPL-2.0-or-later
6*/
7
8#include "waylandclipboard_p.h"
9
10#include <QBuffer>
11#include <QFile>
12#include <QGuiApplication>
13#include <QImageReader>
14#include <QImageWriter>
15#include <QMimeData>
16#include <QPointer>
17#include <QWaylandClientExtension>
18#include <QWindow>
19#include <QtWaylandClientVersion>
20
21#include <errno.h>
22#include <fcntl.h>
23#include <poll.h>
24#include <signal.h>
25#include <string.h>
26#include <unistd.h>
27
28#include "qwayland-wayland.h"
29#include "qwayland-wlr-data-control-unstable-v1.h"
30
31static inline QString applicationQtXImageLiteral()
32{
33 return QStringLiteral("application/x-qt-image");
34}
35
36// copied from https://code.woboq.org/qt5/qtbase/src/gui/kernel/qinternalmimedata.cpp.html
37static QString utf8Text()
38{
39 return QStringLiteral("text/plain;charset=utf-8");
40}
41
42static QStringList imageMimeFormats(const QList<QByteArray> &imageFormats)
43{
44 QStringList formats;
45 formats.reserve(imageFormats.size());
46 for (const auto &format : imageFormats)
47 formats.append(QLatin1String("image/") + QLatin1String(format.toLower()));
48 // put png at the front because it is best
49 int pngIndex = formats.indexOf(QLatin1String("image/png"));
50 if (pngIndex != -1 && pngIndex != 0)
51 formats.move(pngIndex, 0);
52 return formats;
53}
54
55static inline QStringList imageReadMimeFormats()
56{
57 return imageMimeFormats(QImageReader::supportedImageFormats());
58}
59
60static inline QStringList imageWriteMimeFormats()
61{
62 return imageMimeFormats(QImageWriter::supportedImageFormats());
63}
64// end copied
65
66class DataControlDeviceManager : public QWaylandClientExtensionTemplate<DataControlDeviceManager>, public QtWayland::zwlr_data_control_manager_v1
67{
68 Q_OBJECT
69public:
70 DataControlDeviceManager()
71 : QWaylandClientExtensionTemplate<DataControlDeviceManager>(2)
72 {
73 }
74
75 void instantiate()
76 {
77 initialize();
78 }
79
80 ~DataControlDeviceManager()
81 {
82 if (isInitialized()) {
83 destroy();
84 }
85 }
86};
87
88class DataControlOffer : public QMimeData, public QtWayland::zwlr_data_control_offer_v1
89{
91public:
92 DataControlOffer(struct ::zwlr_data_control_offer_v1 *id)
93 : QtWayland::zwlr_data_control_offer_v1(id)
94 {
95 }
96
97 ~DataControlOffer()
98 {
99 destroy();
100 }
101
102 QStringList formats() const override
103 {
104 return m_receivedFormats;
105 }
106
107 bool containsImageData() const
108 {
109 if (m_receivedFormats.contains(applicationQtXImageLiteral())) {
110 return true;
111 }
112 const auto formats = imageReadMimeFormats();
113 for (const auto &receivedFormat : m_receivedFormats) {
114 if (formats.contains(receivedFormat)) {
115 return true;
116 }
117 }
118 return false;
119 }
120
121 bool hasFormat(const QString &mimeType) const override
122 {
123 if (mimeType == QStringLiteral("text/plain") && m_receivedFormats.contains(utf8Text())) {
124 return true;
125 }
126 if (m_receivedFormats.contains(mimeType)) {
127 return true;
128 }
129
130 // If we have image data
131 if (containsImageData()) {
132 // is the requested output mimeType supported ?
133 const QStringList imageFormats = imageWriteMimeFormats();
134 for (const QString &imageFormat : imageFormats) {
135 if (imageFormat == mimeType) {
136 return true;
137 }
138 }
139 if (mimeType == applicationQtXImageLiteral()) {
140 return true;
141 }
142 }
143
144 return false;
145 }
146
147protected:
148 void zwlr_data_control_offer_v1_offer(const QString &mime_type) override
149 {
150 if (!m_receivedFormats.contains(mime_type)) {
151 m_receivedFormats << mime_type;
152 }
153 }
154
155 QVariant retrieveData(const QString &mimeType, QMetaType type) const override;
156
157private:
158 /** reads data from a file descriptor with a timeout of 1 second
159 * true if data is read successfully
160 */
161 static bool readData(int fd, QByteArray &data);
162 QStringList m_receivedFormats;
163 mutable QHash<QString, QVariant> m_data;
164};
165
166QVariant DataControlOffer::retrieveData(const QString &mimeType, QMetaType type) const
167{
168 Q_UNUSED(type);
169
170 auto it = m_data.constFind(mimeType);
171 if (it != m_data.constEnd())
172 return *it;
173
174 QString mime;
175 if (!m_receivedFormats.contains(mimeType)) {
176 if (mimeType == QStringLiteral("text/plain") && m_receivedFormats.contains(utf8Text())) {
177 mime = utf8Text();
178 } else if (mimeType == applicationQtXImageLiteral()) {
179 const auto writeFormats = imageWriteMimeFormats();
180 for (const auto &receivedFormat : m_receivedFormats) {
181 if (writeFormats.contains(receivedFormat)) {
182 mime = receivedFormat;
183 break;
184 }
185 }
186 if (mime.isEmpty()) {
187 // default exchange format
188 mime = QStringLiteral("image/png");
189 }
190 }
191
192 if (mime.isEmpty()) {
193 return QVariant();
194 }
195 } else {
196 mime = mimeType;
197 }
198
199 int pipeFds[2];
200 if (pipe(pipeFds) != 0) {
201 return QVariant();
202 }
203
204 auto t = const_cast<DataControlOffer *>(this);
205 t->receive(mime, pipeFds[1]);
206
207 close(pipeFds[1]);
208
209 /*
210 * Ideally we need to introduce a non-blocking QMimeData object
211 * Or a non-blocking constructor to QMimeData with the mimetypes that are relevant
212 *
213 * However this isn't actually any worse than X.
214 */
215
216 auto waylandApp = qGuiApp->nativeInterface<QNativeInterface::QWaylandApplication>();
217 auto display = waylandApp->display();
218
219 wl_display_flush(display);
220
221 QFile readPipe;
222 if (readPipe.open(pipeFds[0], QIODevice::ReadOnly)) {
223 QByteArray data;
224 if (readData(pipeFds[0], data)) {
225 close(pipeFds[0]);
226
227 if (mimeType == applicationQtXImageLiteral()) {
228 QImage img = QImage::fromData(data, mime.mid(mime.indexOf(QLatin1Char('/')) + 1).toLatin1().toUpper().data());
229 if (!img.isNull()) {
230 m_data.insert(mimeType, img);
231 return img;
232 }
233 } else if (data.size() > 1 && mimeType == u"text/uri-list") {
234 const auto urls = data.split('\n');
235 QVariantList list;
236 list.reserve(urls.size());
237 for (const QByteArray &s : urls) {
238 if (QUrl url(QUrl::fromEncoded(QByteArrayView(s).trimmed())); url.isValid()) {
239 list.emplace_back(std::move(url));
240 }
241 }
242 m_data.insert(mimeType, list);
243 return list;
244 }
245 m_data.insert(mimeType, data);
246 return data;
247 }
248 close(pipeFds[0]);
249 }
250
251 return QVariant();
252}
253
254bool DataControlOffer::readData(int fd, QByteArray &data)
255{
256 pollfd pfds[1];
257 pfds[0].fd = fd;
258 pfds[0].events = POLLIN;
259
260 while (true) {
261 const int ready = poll(pfds, 1, 1000);
262 if (ready < 0) {
263 if (errno != EINTR) {
264 qWarning("DataControlOffer: poll() failed: %s", strerror(errno));
265 return false;
266 }
267 } else if (ready == 0) {
268 qWarning("DataControlOffer: timeout reading from pipe");
269 return false;
270 } else {
271 char buf[4096];
272 int n = read(fd, buf, sizeof buf);
273
274 if (n < 0) {
275 qWarning("DataControlOffer: read() failed: %s", strerror(errno));
276 return false;
277 } else if (n == 0) {
278 return true;
279 } else if (n > 0) {
280 data.append(buf, n);
281 }
282 }
283 }
284}
285
286class DataControlSource : public QObject, public QtWayland::zwlr_data_control_source_v1
287{
289public:
290 DataControlSource(struct ::zwlr_data_control_source_v1 *id, QMimeData *mimeData);
291 DataControlSource() = default;
292 ~DataControlSource()
293 {
294 destroy();
295 }
296
297 QMimeData *mimeData()
298 {
299 return m_mimeData.get();
300 }
301 std::unique_ptr<QMimeData> releaseMimeData()
302 {
303 return std::move(m_mimeData);
304 }
305
307 void cancelled();
308
309protected:
310 void zwlr_data_control_source_v1_send(const QString &mime_type, int32_t fd) override;
311 void zwlr_data_control_source_v1_cancelled() override;
312
313private:
314 std::unique_ptr<QMimeData> m_mimeData;
315};
316
317DataControlSource::DataControlSource(struct ::zwlr_data_control_source_v1 *id, QMimeData *mimeData)
318 : QtWayland::zwlr_data_control_source_v1(id)
319 , m_mimeData(mimeData)
320{
321 const auto formats = mimeData->formats();
322 for (const QString &format : formats) {
323 offer(format);
324 }
325 if (mimeData->hasText()) {
326 // ensure GTK applications get this mimetype to avoid them discarding the offer
327 offer(QStringLiteral("text/plain;charset=utf-8"));
328 }
329
330 if (mimeData->hasImage()) {
331 const QStringList imageFormats = imageWriteMimeFormats();
332 for (const QString &imageFormat : imageFormats) {
333 if (!formats.contains(imageFormat)) {
334 offer(imageFormat);
335 }
336 }
337 }
338}
339
340void DataControlSource::zwlr_data_control_source_v1_send(const QString &mime_type, int32_t fd)
341{
342 QString send_mime_type = mime_type;
343 if (send_mime_type == QStringLiteral("text/plain;charset=utf-8")) {
344 // if we get a request on the fallback mime, send the data from the original mime type
345 send_mime_type = QStringLiteral("text/plain");
346 }
347
348 QByteArray ba;
349 if (m_mimeData->hasImage()) {
350 // adapted from QInternalMimeData::renderDataHelper
351 if (mime_type == applicationQtXImageLiteral()) {
352 QImage image = qvariant_cast<QImage>(m_mimeData->imageData());
353 QBuffer buf(&ba);
354 buf.open(QBuffer::WriteOnly);
355 // would there not be PNG ??
356 image.save(&buf, "PNG");
357
358 } else if (mime_type.startsWith(QLatin1String("image/"))) {
359 QImage image = qvariant_cast<QImage>(m_mimeData->imageData());
360 QBuffer buf(&ba);
361 buf.open(QBuffer::WriteOnly);
362 image.save(&buf, mime_type.mid(mime_type.indexOf(QLatin1Char('/')) + 1).toLatin1().toUpper().data());
363 }
364 // end adapted
365 } else {
366 ba = m_mimeData->data(send_mime_type);
367 }
368
369 QFile c;
371 return;
372 }
373 // Create a sigpipe handler that does nothing, or clients may be forced to terminate
374 // if the pipe is closed in the other end.
375 struct sigaction action, oldAction;
376 action.sa_handler = SIG_IGN;
377 sigemptyset(&action.sa_mask);
378 action.sa_flags = 0;
379 sigaction(SIGPIPE, &action, &oldAction);
380 const int flags = fcntl(fd, F_GETFL, 0);
381 if (flags & O_NONBLOCK) {
382 fcntl(fd, F_SETFL, flags & ~O_NONBLOCK); // Unset O_NONBLOCK to fix pasting to XWayland windows
383 }
384 const qint64 written = c.write(ba);
385 sigaction(SIGPIPE, &oldAction, nullptr);
386
387 if (written != ba.size()) {
388 qWarning() << "Failed to send all clipobard data; sent" << written << "bytes out of" << ba.size();
389 }
390}
391
392void DataControlSource::zwlr_data_control_source_v1_cancelled()
393{
394 Q_EMIT cancelled();
395}
396
397class DataControlDevice : public QObject, public QtWayland::zwlr_data_control_device_v1
398{
400public:
401 DataControlDevice(struct ::zwlr_data_control_device_v1 *id)
402 : QtWayland::zwlr_data_control_device_v1(id)
403 {
404 }
405
406 ~DataControlDevice()
407 {
408 destroy();
409 }
410
411 void setSelection(std::unique_ptr<DataControlSource> selection);
412 QMimeData *receivedSelection()
413 {
414 return m_receivedSelection.get();
415 }
416 QMimeData *selection()
417 {
418 return m_selection ? m_selection->mimeData() : nullptr;
419 }
420
421 void setPrimarySelection(std::unique_ptr<DataControlSource> selection);
422 QMimeData *receivedPrimarySelection()
423 {
424 return m_receivedPrimarySelection.get();
425 }
426 QMimeData *primarySelection()
427 {
428 return m_primarySelection ? m_primarySelection->mimeData() : nullptr;
429 }
430
432 void receivedSelectionChanged();
433 void selectionChanged();
434
435 void receivedPrimarySelectionChanged();
436 void primarySelectionChanged();
437
438protected:
439 void zwlr_data_control_device_v1_data_offer(struct ::zwlr_data_control_offer_v1 *id) override
440 {
441 // this will become memory managed when we retrieve the selection event
442 // a compositor calling data_offer without doing that would be a bug
443 new DataControlOffer(id);
444 }
445
446 void zwlr_data_control_device_v1_selection(struct ::zwlr_data_control_offer_v1 *id) override
447 {
448 if (!id) {
449 m_receivedSelection.reset();
450 } else {
451 auto derivated = QtWayland::zwlr_data_control_offer_v1::fromObject(id);
452 auto offer = dynamic_cast<DataControlOffer *>(derivated); // dynamic because of the dual inheritance
453 m_receivedSelection.reset(offer);
454 }
455 Q_EMIT receivedSelectionChanged();
456 }
457
458 void zwlr_data_control_device_v1_primary_selection(struct ::zwlr_data_control_offer_v1 *id) override
459 {
460 if (!id) {
461 m_receivedPrimarySelection.reset();
462 } else {
463 auto derivated = QtWayland::zwlr_data_control_offer_v1::fromObject(id);
464 auto offer = dynamic_cast<DataControlOffer *>(derivated); // dynamic because of the dual inheritance
465 m_receivedPrimarySelection.reset(offer);
466 }
467 Q_EMIT receivedPrimarySelectionChanged();
468 }
469
470private:
471 std::unique_ptr<DataControlSource> m_selection; // selection set locally
472 std::unique_ptr<DataControlOffer> m_receivedSelection; // latest selection set from externally to here
473
474 std::unique_ptr<DataControlSource> m_primarySelection; // selection set locally
475 std::unique_ptr<DataControlOffer> m_receivedPrimarySelection; // latest selection set from externally to here
476 friend WaylandClipboard;
477};
478
479void DataControlDevice::setSelection(std::unique_ptr<DataControlSource> selection)
480{
481 m_selection = std::move(selection);
482 connect(m_selection.get(), &DataControlSource::cancelled, this, [this]() {
483 m_selection.reset();
484 });
485 set_selection(m_selection->object());
486 Q_EMIT selectionChanged();
487}
488
489void DataControlDevice::setPrimarySelection(std::unique_ptr<DataControlSource> selection)
490{
491 m_primarySelection = std::move(selection);
492 connect(m_primarySelection.get(), &DataControlSource::cancelled, this, [this]() {
493 m_primarySelection.reset();
494 });
495
496 if (zwlr_data_control_device_v1_get_version(object()) >= ZWLR_DATA_CONTROL_DEVICE_V1_SET_PRIMARY_SELECTION_SINCE_VERSION) {
497 set_primary_selection(m_primarySelection->object());
498 Q_EMIT primarySelectionChanged();
499 }
500}
501class Keyboard;
502// We are binding to Seat/Keyboard manually because we want to react to gaining focus but inside Qt the events are Qt and arrive to late
503class KeyboardFocusWatcher : public QWaylandClientExtensionTemplate<KeyboardFocusWatcher>, public QtWayland::wl_seat
504{
505 Q_OBJECT
506public:
507 KeyboardFocusWatcher()
508 : QWaylandClientExtensionTemplate(5)
509 {
510 initialize();
511 auto waylandApp = qGuiApp->nativeInterface<QNativeInterface::QWaylandApplication>();
512 auto display = waylandApp->display();
513 // so we get capabilities
514 wl_display_roundtrip(display);
515 }
516 ~KeyboardFocusWatcher() override
517 {
518 if (isActive()) {
519 release();
520 }
521 }
522 void seat_capabilities(uint32_t capabilities) override
523 {
524 const bool hasKeyboard = capabilities & capability_keyboard;
525 if (hasKeyboard && !m_keyboard) {
526 m_keyboard = std::make_unique<Keyboard>(get_keyboard(), *this);
527 } else if (!hasKeyboard && m_keyboard) {
528 m_keyboard.reset();
529 }
530 }
531 bool hasFocus() const
532 {
533 return m_focus;
534 }
535Q_SIGNALS:
536 void keyboardEntered();
537
538private:
539 friend Keyboard;
540 bool m_focus = false;
541 std::unique_ptr<Keyboard> m_keyboard;
542};
543
544class Keyboard : public QtWayland::wl_keyboard
545{
546public:
547 Keyboard(::wl_keyboard *keyboard, KeyboardFocusWatcher &seat)
548 : wl_keyboard(keyboard)
549 , m_seat(seat)
550 {
551 }
552 ~Keyboard()
553 {
554 release();
555 }
556
557private:
558 void keyboard_enter([[maybe_unused]] uint32_t serial, [[maybe_unused]] wl_surface *surface, [[maybe_unused]] wl_array *keys) override
559 {
560 m_seat.m_focus = true;
561 Q_EMIT m_seat.keyboardEntered();
562 }
563 void keyboard_leave([[maybe_unused]] uint32_t serial, [[maybe_unused]] wl_surface *surface) override
564 {
565 m_seat.m_focus = false;
566 }
567 KeyboardFocusWatcher &m_seat;
568};
569
570WaylandClipboard::WaylandClipboard(QObject *parent)
571 : KSystemClipboard(parent)
572 , m_keyboardFocusWatcher(new KeyboardFocusWatcher)
573 , m_manager(new DataControlDeviceManager)
574{
575 connect(m_manager.get(), &DataControlDeviceManager::activeChanged, this, [this]() {
576 if (m_manager->isActive()) {
577 auto waylandApp = qGuiApp->nativeInterface<QNativeInterface::QWaylandApplication>();
578 if (!waylandApp) {
579 return;
580 }
581 auto seat = waylandApp->seat();
582
583 if (!seat) {
584 return;
585 }
586 m_device.reset(new DataControlDevice(m_manager->get_data_device(seat)));
587
588 connect(m_device.get(), &DataControlDevice::receivedSelectionChanged, this, [this]() {
589 // When our source is still valid, so the offer is for setting it or we emit changed when it is cancelled
590 if (!m_device->selection()) {
591 Q_EMIT changed(QClipboard::Clipboard);
592 }
593 });
594 connect(m_device.get(), &DataControlDevice::selectionChanged, this, [this]() {
595 Q_EMIT changed(QClipboard::Clipboard);
596 });
597
598 connect(m_device.get(), &DataControlDevice::receivedPrimarySelectionChanged, this, [this]() {
599 // When our source is still valid, so the offer is for setting it or we emit changed when it is cancelled
600 if (!m_device->primarySelection()) {
601 Q_EMIT changed(QClipboard::Selection);
602 }
603 });
604 connect(m_device.get(), &DataControlDevice::primarySelectionChanged, this, [this]() {
605 Q_EMIT changed(QClipboard::Selection);
606 });
607
608 } else {
609 m_device.reset();
610 }
611 });
612
613 m_manager->instantiate();
614}
615
616WaylandClipboard::~WaylandClipboard() = default;
617
618bool WaylandClipboard::isValid()
619{
620 return m_manager && m_manager->isInitialized();
621}
622
623void WaylandClipboard::setMimeData(QMimeData *mime, QClipboard::Mode mode)
624{
625 if (!m_device) {
626 return;
627 }
628
629 // roundtrip to have accurate focus state when losing focus but setting mime data before processing wayland events.
630 auto waylandApp = qGuiApp->nativeInterface<QNativeInterface::QWaylandApplication>();
631 auto display = waylandApp->display();
632 wl_display_roundtrip(display);
633
634 // If the application is focused, use the normal mechanism so a future paste will not deadlock itselfs
635 if (m_keyboardFocusWatcher->hasFocus()) {
637 // if we short-circuit the wlr_data_device, when we receive the data
638 // we cannot identify ourselves as the owner
639 // because of that we act like it's a synchronous action to not confuse klipper.
640 wl_display_roundtrip(display);
641 return;
642 }
643 // If not, set the clipboard once the app receives focus to avoid the deadlock
644 connect(m_keyboardFocusWatcher.get(), &KeyboardFocusWatcher::keyboardEntered, this, &WaylandClipboard::gainedFocus, Qt::UniqueConnection);
645 auto source = std::make_unique<DataControlSource>(m_manager->create_data_source(), mime);
646 if (mode == QClipboard::Clipboard) {
647 m_device->setSelection(std::move(source));
648 } else if (mode == QClipboard::Selection) {
649 m_device->setPrimarySelection(std::move(source));
650 }
651}
652
653void WaylandClipboard::gainedFocus()
654{
655 disconnect(m_keyboardFocusWatcher.get(), &KeyboardFocusWatcher::keyboardEntered, this, nullptr);
656 // QClipboard takes ownership of the QMimeData so we need to transfer and unset our selections
657 if (auto &selection = m_device->m_selection) {
658 std::unique_ptr<QMimeData> data = selection->releaseMimeData();
659 selection.reset();
661 }
662 if (auto &primarySelection = m_device->m_primarySelection) {
663 std::unique_ptr<QMimeData> data = primarySelection->releaseMimeData();
664 primarySelection.reset();
666 }
667}
668
669void WaylandClipboard::clear(QClipboard::Mode mode)
670{
671 if (!m_device) {
672 return;
673 }
674 if (mode == QClipboard::Clipboard) {
675 m_device->set_selection(nullptr);
676 m_device->m_selection.reset();
677 } else if (mode == QClipboard::Selection) {
678 if (zwlr_data_control_device_v1_get_version(m_device->object()) >= ZWLR_DATA_CONTROL_DEVICE_V1_SET_PRIMARY_SELECTION_SINCE_VERSION) {
679 m_device->set_primary_selection(nullptr);
680 m_device->m_primarySelection.reset();
681 }
682 }
683}
684
685const QMimeData *WaylandClipboard::mimeData(QClipboard::Mode mode) const
686{
687 if (!m_device) {
688 return nullptr;
689 }
690
691 // return our locally set selection if it's not cancelled to avoid copying data to ourselves
692 if (mode == QClipboard::Clipboard) {
693 if (m_device->selection()) {
694 return m_device->selection();
695 }
696 // This application owns the clipboard via the regular data_device, use it so we don't block ourselves
697 if (QGuiApplication::clipboard()->ownsClipboard()) {
698 return QGuiApplication::clipboard()->mimeData(mode);
699 }
700 return m_device->receivedSelection();
701 } else if (mode == QClipboard::Selection) {
702 if (m_device->primarySelection()) {
703 return m_device->primarySelection();
704 }
705 // This application owns the primary selection via the regular primary_selection_device, use it so we don't block ourselves
706 if (QGuiApplication::clipboard()->ownsSelection()) {
707 return QGuiApplication::clipboard()->mimeData(mode);
708 }
709 return m_device->receivedPrimarySelection();
710 }
711 return nullptr;
712}
713
714#include "waylandclipboard.moc"
This class mimics QClipboard but unlike QClipboard it will continue to get updates even when our wind...
KCALUTILS_EXPORT QString mimeType()
Capabilities capabilities()
QVariant read(const QByteArray &data, int versionOverride=0)
KIOCORE_EXPORT QStringList list(const QString &fileClass)
KGuiItem close()
void initialize(StandardShortcut id)
char * data()
qsizetype size() const const
const QMimeData * mimeData(Mode mode) const const
void setMimeData(QMimeData *src, Mode mode)
bool open(FILE *fh, OpenMode mode, FileHandleFlags handleFlags)
QClipboard * clipboard()
QImage fromData(QByteArrayView data, const char *format)
bool isNull() const const
bool save(QIODevice *device, const char *format, int quality) const const
QList< QByteArray > supportedImageFormats()
QList< QByteArray > supportedImageFormats()
qint64 write(const QByteArray &data)
void append(QList< T > &&value)
reference emplace_back(Args &&... args)
void move(qsizetype from, qsizetype to)
void reserve(qsizetype size)
qsizetype size() const const
QByteArray data(const QString &mimeType) const const
virtual QStringList formats() const const
bool hasImage() const const
bool hasText() const const
QList< QUrl > urls() const const
virtual wl_display * display() const const=0
Q_EMITQ_EMIT
Q_OBJECTQ_OBJECT
Q_SIGNALSQ_SIGNALS
QMetaObject::Connection connect(const QObject *sender, PointerToMemberFunction signal, Functor functor)
qsizetype indexOf(QChar ch, qsizetype from, Qt::CaseSensitivity cs) const const
bool isEmpty() const const
QString mid(qsizetype position, qsizetype n) const const
bool startsWith(QChar c, Qt::CaseSensitivity cs) const const
qsizetype indexOf(const QRegularExpression &re, qsizetype from) const const
UniqueConnection
QFuture< ArgsType< Signal > > connect(Sender *sender, Signal signal)
QUrl fromEncoded(const QByteArray &input, ParsingMode parsingMode)
This file is part of the KDE documentation.
Documentation copyright © 1996-2025 The KDE developers.
Generated on Fri Apr 11 2025 11:51:51 by doxygen 1.13.2 written by Dimitri van Heesch, © 1997-2006

KDE's Doxygen guidelines are available online.