// Copyright (C) 2016 The Qt Company Ltd. // SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only #include "qwaylanddataoffer_p.h" #include "qwaylanddatadevicemanager_p.h" #include "qwaylanddisplay_p.h" #include #include #include #include QT_BEGIN_NAMESPACE namespace QtWaylandClient { static QString utf8Text() { return QStringLiteral("text/plain;charset=utf-8"); } QWaylandDataOffer::QWaylandDataOffer(QWaylandDisplay *display, struct ::wl_data_offer *offer) : QtWayland::wl_data_offer(offer) , m_display(display) , m_mimeData(new QWaylandMimeData(this)) { } QWaylandDataOffer::~QWaylandDataOffer() { destroy(); } QString QWaylandDataOffer::firstFormat() const { if (m_mimeData->formats().isEmpty()) return QString(); return m_mimeData->formats().first(); } QMimeData *QWaylandDataOffer::mimeData() { return m_mimeData.data(); } Qt::DropActions QWaylandDataOffer::supportedActions() const { if (version() < 3) { return Qt::MoveAction | Qt::CopyAction; } return m_supportedActions; } void QWaylandDataOffer::startReceiving(const QString &mimeType, int fd) { receive(mimeType, fd); wl_display_flush(m_display->wl_display()); } void QWaylandDataOffer::data_offer_offer(const QString &mime_type) { m_mimeData->appendFormat(mime_type); } void QWaylandDataOffer::data_offer_action(uint32_t dnd_action) { Q_UNUSED(dnd_action); // This is the compositor telling the drag target what action it should perform // It does not map nicely into Qt final drop semantics, other than pretending there is only one supported action? } void QWaylandDataOffer::data_offer_source_actions(uint32_t source_actions) { m_supportedActions = Qt::DropActions(); if (source_actions & WL_DATA_DEVICE_MANAGER_DND_ACTION_MOVE) m_supportedActions |= Qt::MoveAction; if (source_actions & WL_DATA_DEVICE_MANAGER_DND_ACTION_COPY) m_supportedActions |= Qt::CopyAction; } QWaylandMimeData::QWaylandMimeData(QWaylandAbstractDataOffer *dataOffer) : m_dataOffer(dataOffer) { } QWaylandMimeData::~QWaylandMimeData() { } void QWaylandMimeData::appendFormat(const QString &mimeType) { m_types << mimeType; m_data.remove(mimeType); // Clear previous contents } bool QWaylandMimeData::hasFormat_sys(const QString &mimeType) const { if (m_types.contains(mimeType)) return true; if (mimeType == QStringLiteral("text/plain") && m_types.contains(utf8Text())) return true; return false; } QStringList QWaylandMimeData::formats_sys() const { return m_types; } QVariant QWaylandMimeData::retrieveData_sys(const QString &mimeType, QMetaType type) const { Q_UNUSED(type); auto it = m_data.constFind(mimeType); if (it != m_data.constEnd()) return *it; QString mime = mimeType; if (!m_types.contains(mimeType)) { if (mimeType == QStringLiteral("text/plain") && m_types.contains(utf8Text())) mime = utf8Text(); else return QVariant(); } int pipefd[2]; if (qt_safe_pipe(pipefd) == -1) { qWarning("QWaylandMimeData: pipe2() failed"); return QVariant(); } m_dataOffer->startReceiving(mime, pipefd[1]); close(pipefd[1]); QByteArray content; if (readData(pipefd[0], content) != 0) { qWarning("QWaylandDataOffer: error reading data for mimeType %s", qPrintable(mimeType)); content = QByteArray(); } close(pipefd[0]); m_data.insert(mimeType, content); return content; } int QWaylandMimeData::readData(int fd, QByteArray &data) const { struct pollfd readset; readset.fd = fd; readset.events = POLLIN; struct timespec timeout; timeout.tv_sec = 1; timeout.tv_nsec = 0; Q_FOREVER { int ready = qt_safe_poll(&readset, 1, &timeout); if (ready < 0) { qWarning() << "QWaylandDataOffer: qt_safe_poll() failed"; return -1; } else if (ready == 0) { qWarning("QWaylandDataOffer: timeout reading from pipe"); return -1; } else { char buf[4096]; int n = QT_READ(fd, buf, sizeof buf); if (n < 0) { qWarning("QWaylandDataOffer: read() failed"); return -1; } else if (n == 0) { return 0; } else if (n > 0) { data.append(buf, n); } } } } } QT_END_NAMESPACE