File indexing completed on 2024-07-14 14:29:47

0001 /*
0002     SPDX-FileCopyrightText: 2020 David Edmundson <davidedmundson@kde.org>
0003     SPDX-FileCopyrightText: 2021 Méven Car <meven.car@enioka.com>
0004 
0005     SPDX-License-Identifier: LGPL-2.0-or-later
0006 */
0007 
0008 #include "waylandclipboard_p.h"
0009 
0010 #include <QBuffer>
0011 #include <QFile>
0012 #include <QGuiApplication>
0013 #include <QImageReader>
0014 #include <QImageWriter>
0015 #include <QMimeData>
0016 #include <QPointer>
0017 #include <QWindow>
0018 #include <QtWaylandClient/QWaylandClientExtension>
0019 #include <qpa/qplatformnativeinterface.h>
0020 #include <qtwaylandclientversion.h>
0021 
0022 #include <errno.h>
0023 #include <poll.h>
0024 #include <signal.h>
0025 #include <string.h>
0026 #include <unistd.h>
0027 
0028 #include "qwayland-wayland.h"
0029 #include "qwayland-wlr-data-control-unstable-v1.h"
0030 
0031 static inline QString applicationQtXImageLiteral()
0032 {
0033     return QStringLiteral("application/x-qt-image");
0034 }
0035 
0036 // copied from https://code.woboq.org/qt5/qtbase/src/gui/kernel/qinternalmimedata.cpp.html
0037 static QString utf8Text()
0038 {
0039     return QStringLiteral("text/plain;charset=utf-8");
0040 }
0041 
0042 static QStringList imageMimeFormats(const QList<QByteArray> &imageFormats)
0043 {
0044     QStringList formats;
0045     formats.reserve(imageFormats.size());
0046     for (const auto &format : imageFormats)
0047         formats.append(QLatin1String("image/") + QLatin1String(format.toLower()));
0048     // put png at the front because it is best
0049     int pngIndex = formats.indexOf(QLatin1String("image/png"));
0050     if (pngIndex != -1 && pngIndex != 0)
0051         formats.move(pngIndex, 0);
0052     return formats;
0053 }
0054 
0055 static inline QStringList imageReadMimeFormats()
0056 {
0057     return imageMimeFormats(QImageReader::supportedImageFormats());
0058 }
0059 
0060 static inline QStringList imageWriteMimeFormats()
0061 {
0062     return imageMimeFormats(QImageWriter::supportedImageFormats());
0063 }
0064 // end copied
0065 
0066 class DataControlDeviceManager : public QWaylandClientExtensionTemplate<DataControlDeviceManager>, public QtWayland::zwlr_data_control_manager_v1
0067 {
0068     Q_OBJECT
0069 public:
0070     DataControlDeviceManager()
0071         : QWaylandClientExtensionTemplate<DataControlDeviceManager>(2)
0072     {
0073     }
0074 
0075     void instantiate()
0076     {
0077 #if QTWAYLANDCLIENT_VERSION >= QT_VERSION_CHECK(6, 2, 0)
0078         initialize();
0079 #else
0080         // QWaylandClientExtensionTemplate invokes this with a QueuedConnection but we want shortcuts
0081         // to be have access to data_control immediately.
0082         QMetaObject::invokeMethod(this, "addRegistryListener");
0083 #endif
0084     }
0085 
0086     ~DataControlDeviceManager()
0087     {
0088         if (isInitialized()) {
0089             destroy();
0090         }
0091     }
0092 };
0093 
0094 class DataControlOffer : public QMimeData, public QtWayland::zwlr_data_control_offer_v1
0095 {
0096     Q_OBJECT
0097 public:
0098     DataControlOffer(struct ::zwlr_data_control_offer_v1 *id)
0099         : QtWayland::zwlr_data_control_offer_v1(id)
0100     {
0101     }
0102 
0103     ~DataControlOffer()
0104     {
0105         destroy();
0106     }
0107 
0108     QStringList formats() const override
0109     {
0110         return m_receivedFormats;
0111     }
0112 
0113     bool containsImageData() const
0114     {
0115         if (m_receivedFormats.contains(applicationQtXImageLiteral())) {
0116             return true;
0117         }
0118         const auto formats = imageReadMimeFormats();
0119         for (const auto &receivedFormat : m_receivedFormats) {
0120             if (formats.contains(receivedFormat)) {
0121                 return true;
0122             }
0123         }
0124         return false;
0125     }
0126 
0127     bool hasFormat(const QString &mimeType) const override
0128     {
0129         if (mimeType == QStringLiteral("text/plain") && m_receivedFormats.contains(utf8Text())) {
0130             return true;
0131         }
0132         if (m_receivedFormats.contains(mimeType)) {
0133             return true;
0134         }
0135 
0136         // If we have image data
0137         if (containsImageData()) {
0138             // is the requested output mimeType supported ?
0139             const QStringList imageFormats = imageWriteMimeFormats();
0140             for (const QString &imageFormat : imageFormats) {
0141                 if (imageFormat == mimeType) {
0142                     return true;
0143                 }
0144             }
0145             if (mimeType == applicationQtXImageLiteral()) {
0146                 return true;
0147             }
0148         }
0149 
0150         return false;
0151     }
0152 
0153 protected:
0154     void zwlr_data_control_offer_v1_offer(const QString &mime_type) override
0155     {
0156         m_receivedFormats << mime_type;
0157     }
0158 
0159 #if QT_VERSION >= QT_VERSION_CHECK(6, 0, 0)
0160     QVariant retrieveData(const QString &mimeType, QMetaType type) const override;
0161 #else
0162     QVariant retrieveData(const QString &mimeType, QVariant::Type type) const override;
0163 #endif
0164 
0165 private:
0166     /** reads data from a file descriptor with a timeout of 1 second
0167      *  true if data is read successfully
0168      */
0169     static bool readData(int fd, QByteArray &data);
0170     QStringList m_receivedFormats;
0171 };
0172 
0173 #if QT_VERSION >= QT_VERSION_CHECK(6, 0, 0)
0174 QVariant DataControlOffer::retrieveData(const QString &mimeType, QMetaType type) const
0175 #else
0176 QVariant DataControlOffer::retrieveData(const QString &mimeType, QVariant::Type type) const
0177 #endif
0178 {
0179     Q_UNUSED(type);
0180 
0181     QString mime;
0182     if (!m_receivedFormats.contains(mimeType)) {
0183         if (mimeType == QStringLiteral("text/plain") && m_receivedFormats.contains(utf8Text())) {
0184             mime = utf8Text();
0185         } else if (mimeType == applicationQtXImageLiteral()) {
0186             const auto writeFormats = imageWriteMimeFormats();
0187             for (const auto &receivedFormat : m_receivedFormats) {
0188                 if (writeFormats.contains(receivedFormat)) {
0189                     mime = receivedFormat;
0190                     break;
0191                 }
0192             }
0193             if (mime.isEmpty()) {
0194                 // default exchange format
0195                 mime = QStringLiteral("image/png");
0196             }
0197         }
0198 
0199         if (mime.isEmpty()) {
0200             return QVariant();
0201         }
0202     } else {
0203         mime = mimeType;
0204     }
0205 
0206     int pipeFds[2];
0207     if (pipe(pipeFds) != 0) {
0208         return QVariant();
0209     }
0210 
0211     auto t = const_cast<DataControlOffer *>(this);
0212     t->receive(mime, pipeFds[1]);
0213 
0214     close(pipeFds[1]);
0215 
0216     /*
0217      * Ideally we need to introduce a non-blocking QMimeData object
0218      * Or a non-blocking constructor to QMimeData with the mimetypes that are relevant
0219      *
0220      * However this isn't actually any worse than X.
0221      */
0222 
0223     QPlatformNativeInterface *native = qGuiApp->platformNativeInterface();
0224     auto display = static_cast<struct ::wl_display *>(native->nativeResourceForIntegration("wl_display"));
0225     wl_display_flush(display);
0226 
0227     QFile readPipe;
0228     if (readPipe.open(pipeFds[0], QIODevice::ReadOnly)) {
0229         QByteArray data;
0230         if (readData(pipeFds[0], data)) {
0231             close(pipeFds[0]);
0232 
0233             if (mimeType == applicationQtXImageLiteral()) {
0234                 QImage img = QImage::fromData(data, mime.mid(mime.indexOf(QLatin1Char('/')) + 1).toLatin1().toUpper().data());
0235                 if (!img.isNull()) {
0236                     return img;
0237                 }
0238             }
0239             return data;
0240         }
0241         close(pipeFds[0]);
0242     }
0243     return QVariant();
0244 }
0245 
0246 bool DataControlOffer::readData(int fd, QByteArray &data)
0247 {
0248     pollfd pfds[1];
0249     pfds[0].fd = fd;
0250     pfds[0].events = POLLIN;
0251 
0252     while (true) {
0253         const int ready = poll(pfds, 1, 1000);
0254         if (ready < 0) {
0255             if (errno != EINTR) {
0256                 qWarning("DataControlOffer: poll() failed: %s", strerror(errno));
0257                 return false;
0258             }
0259         } else if (ready == 0) {
0260             qWarning("DataControlOffer: timeout reading from pipe");
0261             return false;
0262         } else {
0263             char buf[4096];
0264             int n = read(fd, buf, sizeof buf);
0265 
0266             if (n < 0) {
0267                 qWarning("DataControlOffer: read() failed: %s", strerror(errno));
0268                 return false;
0269             } else if (n == 0) {
0270                 return true;
0271             } else if (n > 0) {
0272                 data.append(buf, n);
0273             }
0274         }
0275     }
0276 }
0277 
0278 class DataControlSource : public QObject, public QtWayland::zwlr_data_control_source_v1
0279 {
0280     Q_OBJECT
0281 public:
0282     DataControlSource(struct ::zwlr_data_control_source_v1 *id, QMimeData *mimeData);
0283     DataControlSource() = default;
0284     ~DataControlSource()
0285     {
0286         destroy();
0287     }
0288 
0289     QMimeData *mimeData()
0290     {
0291         return m_mimeData.get();
0292     }
0293     std::unique_ptr<QMimeData> releaseMimeData()
0294     {
0295         return std::move(m_mimeData);
0296     }
0297 
0298 Q_SIGNALS:
0299     void cancelled();
0300 
0301 protected:
0302     void zwlr_data_control_source_v1_send(const QString &mime_type, int32_t fd) override;
0303     void zwlr_data_control_source_v1_cancelled() override;
0304 
0305 private:
0306     std::unique_ptr<QMimeData> m_mimeData;
0307 };
0308 
0309 DataControlSource::DataControlSource(struct ::zwlr_data_control_source_v1 *id, QMimeData *mimeData)
0310     : QtWayland::zwlr_data_control_source_v1(id)
0311     , m_mimeData(mimeData)
0312 {
0313     const auto formats = mimeData->formats();
0314     for (const QString &format : formats) {
0315         offer(format);
0316     }
0317     if (mimeData->hasText()) {
0318         // ensure GTK applications get this mimetype to avoid them discarding the offer
0319         offer(QStringLiteral("text/plain;charset=utf-8"));
0320     }
0321 
0322     if (mimeData->hasImage()) {
0323         const QStringList imageFormats = imageWriteMimeFormats();
0324         for (const QString &imageFormat : imageFormats) {
0325             if (!formats.contains(imageFormat)) {
0326                 offer(imageFormat);
0327             }
0328         }
0329     }
0330 }
0331 
0332 void DataControlSource::zwlr_data_control_source_v1_send(const QString &mime_type, int32_t fd)
0333 {
0334     QString send_mime_type = mime_type;
0335     if (send_mime_type == QStringLiteral("text/plain;charset=utf-8")) {
0336         // if we get a request on the fallback mime, send the data from the original mime type
0337         send_mime_type = QStringLiteral("text/plain");
0338     }
0339 
0340     QByteArray ba;
0341     if (m_mimeData->hasImage()) {
0342         // adapted from QInternalMimeData::renderDataHelper
0343         if (mime_type == applicationQtXImageLiteral()) {
0344             QImage image = qvariant_cast<QImage>(m_mimeData->imageData());
0345             QBuffer buf(&ba);
0346             buf.open(QBuffer::WriteOnly);
0347             // would there not be PNG ??
0348             image.save(&buf, "PNG");
0349 
0350         } else if (mime_type.startsWith(QLatin1String("image/"))) {
0351             QImage image = qvariant_cast<QImage>(m_mimeData->imageData());
0352             QBuffer buf(&ba);
0353             buf.open(QBuffer::WriteOnly);
0354             image.save(&buf, mime_type.mid(mime_type.indexOf(QLatin1Char('/')) + 1).toLatin1().toUpper().data());
0355         }
0356         // end adapted
0357     } else {
0358         ba = m_mimeData->data(send_mime_type);
0359     }
0360 
0361     // Create a sigpipe handler that does nothing, or clients may be forced to terminate
0362     // if the pipe is closed in the other end.
0363     struct sigaction action, oldAction;
0364     action.sa_handler = SIG_IGN;
0365     sigemptyset(&action.sa_mask);
0366     action.sa_flags = 0;
0367     sigaction(SIGPIPE, &action, &oldAction);
0368     write(fd, ba.constData(), ba.size());
0369     sigaction(SIGPIPE, &oldAction, nullptr);
0370     close(fd);
0371 }
0372 
0373 void DataControlSource::zwlr_data_control_source_v1_cancelled()
0374 {
0375     Q_EMIT cancelled();
0376 }
0377 
0378 class DataControlDevice : public QObject, public QtWayland::zwlr_data_control_device_v1
0379 {
0380     Q_OBJECT
0381 public:
0382     DataControlDevice(struct ::zwlr_data_control_device_v1 *id)
0383         : QtWayland::zwlr_data_control_device_v1(id)
0384     {
0385     }
0386 
0387     ~DataControlDevice()
0388     {
0389         destroy();
0390     }
0391 
0392     void setSelection(std::unique_ptr<DataControlSource> selection);
0393     QMimeData *receivedSelection()
0394     {
0395         return m_receivedSelection.get();
0396     }
0397     QMimeData *selection()
0398     {
0399         return m_selection ? m_selection->mimeData() : nullptr;
0400     }
0401 
0402     void setPrimarySelection(std::unique_ptr<DataControlSource> selection);
0403     QMimeData *receivedPrimarySelection()
0404     {
0405         return m_receivedPrimarySelection.get();
0406     }
0407     QMimeData *primarySelection()
0408     {
0409         return m_primarySelection ? m_primarySelection->mimeData() : nullptr;
0410     }
0411 
0412 Q_SIGNALS:
0413     void receivedSelectionChanged();
0414     void selectionChanged();
0415 
0416     void receivedPrimarySelectionChanged();
0417     void primarySelectionChanged();
0418 
0419 protected:
0420     void zwlr_data_control_device_v1_data_offer(struct ::zwlr_data_control_offer_v1 *id) override
0421     {
0422         // this will become memory managed when we retrieve the selection event
0423         // a compositor calling data_offer without doing that would be a bug
0424         new DataControlOffer(id);
0425     }
0426 
0427     void zwlr_data_control_device_v1_selection(struct ::zwlr_data_control_offer_v1 *id) override
0428     {
0429         if (!id) {
0430             m_receivedSelection.reset();
0431         } else {
0432             auto derivated = QtWayland::zwlr_data_control_offer_v1::fromObject(id);
0433             auto offer = dynamic_cast<DataControlOffer *>(derivated); // dynamic because of the dual inheritance
0434             m_receivedSelection.reset(offer);
0435         }
0436         Q_EMIT receivedSelectionChanged();
0437     }
0438 
0439     void zwlr_data_control_device_v1_primary_selection(struct ::zwlr_data_control_offer_v1 *id) override
0440     {
0441         if (!id) {
0442             m_receivedPrimarySelection.reset();
0443         } else {
0444             auto derivated = QtWayland::zwlr_data_control_offer_v1::fromObject(id);
0445             auto offer = dynamic_cast<DataControlOffer *>(derivated); // dynamic because of the dual inheritance
0446             m_receivedPrimarySelection.reset(offer);
0447         }
0448         Q_EMIT receivedPrimarySelectionChanged();
0449     }
0450 
0451 private:
0452     std::unique_ptr<DataControlSource> m_selection; // selection set locally
0453     std::unique_ptr<DataControlOffer> m_receivedSelection; // latest selection set from externally to here
0454 
0455     std::unique_ptr<DataControlSource> m_primarySelection; // selection set locally
0456     std::unique_ptr<DataControlOffer> m_receivedPrimarySelection; // latest selection set from externally to here
0457     friend WaylandClipboard;
0458 };
0459 
0460 void DataControlDevice::setSelection(std::unique_ptr<DataControlSource> selection)
0461 {
0462     m_selection = std::move(selection);
0463     connect(m_selection.get(), &DataControlSource::cancelled, this, [this]() {
0464         m_selection.reset();
0465     });
0466     set_selection(m_selection->object());
0467     Q_EMIT selectionChanged();
0468 }
0469 
0470 void DataControlDevice::setPrimarySelection(std::unique_ptr<DataControlSource> selection)
0471 {
0472     m_primarySelection = std::move(selection);
0473     connect(m_primarySelection.get(), &DataControlSource::cancelled, this, [this]() {
0474         m_primarySelection.reset();
0475     });
0476 
0477     if (zwlr_data_control_device_v1_get_version(object()) >= ZWLR_DATA_CONTROL_DEVICE_V1_SET_PRIMARY_SELECTION_SINCE_VERSION) {
0478         set_primary_selection(m_primarySelection->object());
0479         Q_EMIT primarySelectionChanged();
0480     }
0481 }
0482 class Keyboard;
0483 // 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
0484 class KeyboardFocusWatcher : public QWaylandClientExtensionTemplate<KeyboardFocusWatcher>, public QtWayland::wl_seat
0485 {
0486     Q_OBJECT
0487 public:
0488     KeyboardFocusWatcher()
0489         : QWaylandClientExtensionTemplate(5)
0490     {
0491 #if QTWAYLANDCLIENT_VERSION >= QT_VERSION_CHECK(6, 2, 0)
0492         initialize();
0493 #else
0494         QMetaObject::invokeMethod(this, "addRegistryListener");
0495 #endif
0496         auto native = qGuiApp->platformNativeInterface();
0497         auto display = static_cast<struct ::wl_display *>(native->nativeResourceForIntegration("wl_display"));
0498         // so we get capabilities
0499         wl_display_roundtrip(display);
0500     }
0501     ~KeyboardFocusWatcher() override
0502     {
0503         if (isActive()) {
0504             release();
0505         }
0506     }
0507     void seat_capabilities(uint32_t capabilities) override
0508     {
0509         const bool hasKeyboard = capabilities & capability_keyboard;
0510         if (hasKeyboard && !m_keyboard) {
0511             m_keyboard = std::make_unique<Keyboard>(get_keyboard(), *this);
0512         } else if (!hasKeyboard && m_keyboard) {
0513             m_keyboard.reset();
0514         }
0515     }
0516     bool hasFocus() const
0517     {
0518         return m_focus;
0519     }
0520 Q_SIGNALS:
0521     void keyboardEntered();
0522 
0523 private:
0524     friend Keyboard;
0525     bool m_focus = false;
0526     std::unique_ptr<Keyboard> m_keyboard;
0527 };
0528 
0529 class Keyboard : public QtWayland::wl_keyboard
0530 {
0531 public:
0532     Keyboard(::wl_keyboard *keyboard, KeyboardFocusWatcher &seat)
0533         : wl_keyboard(keyboard)
0534         , m_seat(seat)
0535     {
0536     }
0537     ~Keyboard()
0538     {
0539         release();
0540     }
0541 
0542 private:
0543     void keyboard_enter([[maybe_unused]] uint32_t serial, [[maybe_unused]] wl_surface *surface, [[maybe_unused]] wl_array *keys) override
0544     {
0545         m_seat.m_focus = true;
0546         Q_EMIT m_seat.keyboardEntered();
0547     }
0548     void keyboard_leave([[maybe_unused]] uint32_t serial, [[maybe_unused]] wl_surface *surface) override
0549     {
0550         m_seat.m_focus = false;
0551     }
0552     KeyboardFocusWatcher &m_seat;
0553 };
0554 
0555 WaylandClipboard::WaylandClipboard(QObject *parent)
0556     : KSystemClipboard(parent)
0557     , m_keyboardFocusWatcher(new KeyboardFocusWatcher)
0558     , m_manager(new DataControlDeviceManager)
0559 {
0560     connect(m_manager.get(), &DataControlDeviceManager::activeChanged, this, [this]() {
0561         if (m_manager->isActive()) {
0562             QPlatformNativeInterface *native = qApp->platformNativeInterface();
0563             if (!native) {
0564                 return;
0565             }
0566             auto seat = static_cast<struct ::wl_seat *>(native->nativeResourceForIntegration("wl_seat"));
0567             if (!seat) {
0568                 return;
0569             }
0570             m_device.reset(new DataControlDevice(m_manager->get_data_device(seat)));
0571 
0572             connect(m_device.get(), &DataControlDevice::receivedSelectionChanged, this, [this]() {
0573                 // When our source is still valid, so the offer is for setting it or we emit changed when it is cancelled
0574                 if (!m_device->selection()) {
0575                     Q_EMIT changed(QClipboard::Clipboard);
0576                 }
0577             });
0578             connect(m_device.get(), &DataControlDevice::selectionChanged, this, [this]() {
0579                 Q_EMIT changed(QClipboard::Clipboard);
0580             });
0581 
0582             connect(m_device.get(), &DataControlDevice::receivedPrimarySelectionChanged, this, [this]() {
0583                 // When our source is still valid, so the offer is for setting it or we emit changed when it is cancelled
0584                 if (!m_device->primarySelection()) {
0585                     Q_EMIT changed(QClipboard::Selection);
0586                 }
0587             });
0588             connect(m_device.get(), &DataControlDevice::primarySelectionChanged, this, [this]() {
0589                 Q_EMIT changed(QClipboard::Selection);
0590             });
0591 
0592         } else {
0593             m_device.reset();
0594         }
0595     });
0596 
0597     m_manager->instantiate();
0598 }
0599 
0600 WaylandClipboard::~WaylandClipboard() = default;
0601 
0602 bool WaylandClipboard::isValid()
0603 {
0604     return m_manager && m_manager->isInitialized();
0605 }
0606 
0607 void WaylandClipboard::setMimeData(QMimeData *mime, QClipboard::Mode mode)
0608 {
0609     if (!m_device) {
0610         return;
0611     }
0612 
0613     // roundtrip to have accurate focus state when losing focus but setting mime data before processing wayland events.
0614     auto native = qGuiApp->platformNativeInterface();
0615     auto display = static_cast<struct ::wl_display *>(native->nativeResourceForIntegration("wl_display"));
0616     wl_display_roundtrip(display);
0617 
0618     // If the application is focused, use the normal mechanism so a future paste will not deadlock itselfs
0619     if (m_keyboardFocusWatcher->hasFocus()) {
0620         QGuiApplication::clipboard()->setMimeData(mime, mode);
0621         return;
0622     }
0623     // If not, set the clipboard once the app receives focus to avoid the deadlock
0624     connect(m_keyboardFocusWatcher.get(), &KeyboardFocusWatcher::keyboardEntered, this, &WaylandClipboard::gainedFocus, Qt::UniqueConnection);
0625     auto source = std::make_unique<DataControlSource>(m_manager->create_data_source(), mime);
0626     if (mode == QClipboard::Clipboard) {
0627         m_device->setSelection(std::move(source));
0628     } else if (mode == QClipboard::Selection) {
0629         m_device->setPrimarySelection(std::move(source));
0630     }
0631 }
0632 
0633 void WaylandClipboard::gainedFocus()
0634 {
0635     disconnect(m_keyboardFocusWatcher.get(), &KeyboardFocusWatcher::keyboardEntered, this, nullptr);
0636     // QClipboard takes ownership of the QMimeData so we need to transfer and unset our selections
0637     if (auto &selection = m_device->m_selection) {
0638         std::unique_ptr<QMimeData> data = selection->releaseMimeData();
0639         WaylandClipboard::clear(QClipboard::Clipboard);
0640         QGuiApplication::clipboard()->setMimeData(data.release(), QClipboard::Clipboard);
0641     }
0642     if (auto &primarySelection = m_device->m_primarySelection) {
0643         std::unique_ptr<QMimeData> data = primarySelection->releaseMimeData();
0644         WaylandClipboard::clear(QClipboard::Selection);
0645         QGuiApplication::clipboard()->setMimeData(data.release(), QClipboard::Selection);
0646     }
0647 }
0648 
0649 void WaylandClipboard::clear(QClipboard::Mode mode)
0650 {
0651     if (!m_device) {
0652         return;
0653     }
0654     if (mode == QClipboard::Clipboard) {
0655         m_device->set_selection(nullptr);
0656         m_device->m_selection.reset();
0657     } else if (mode == QClipboard::Selection) {
0658         if (zwlr_data_control_device_v1_get_version(m_device->object()) >= ZWLR_DATA_CONTROL_DEVICE_V1_SET_PRIMARY_SELECTION_SINCE_VERSION) {
0659             m_device->set_primary_selection(nullptr);
0660             m_device->m_primarySelection.reset();
0661         }
0662     }
0663 }
0664 
0665 const QMimeData *WaylandClipboard::mimeData(QClipboard::Mode mode) const
0666 {
0667     if (!m_device) {
0668         return nullptr;
0669     }
0670 
0671     // return our locally set selection if it's not cancelled to avoid copying data to ourselves
0672     if (mode == QClipboard::Clipboard) {
0673         if (m_device->selection()) {
0674             return m_device->selection();
0675         }
0676         // This application owns the clipboard via the regular data_device, use it so we don't block ourselves
0677         if (QGuiApplication::clipboard()->ownsClipboard()) {
0678             return QGuiApplication::clipboard()->mimeData(mode);
0679         }
0680         return m_device->receivedSelection();
0681     } else if (mode == QClipboard::Selection) {
0682         if (m_device->primarySelection()) {
0683             return m_device->primarySelection();
0684         }
0685         // This application owns the primary selection via the regular primary_selection_device, use it so we don't block ourselves
0686         if (QGuiApplication::clipboard()->ownsSelection()) {
0687             return QGuiApplication::clipboard()->mimeData(mode);
0688         }
0689         return m_device->receivedPrimarySelection();
0690     }
0691     return nullptr;
0692 }
0693 
0694 #include "waylandclipboard.moc"