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