diff --git a/src/debug_console.cpp b/src/debug_console.cpp index 76d5f2b076..61a4d9573e 100644 --- a/src/debug_console.cpp +++ b/src/debug_console.cpp @@ -8,41 +8,55 @@ */ #include "debug_console.h" #include "composite.h" -#include "x11client.h" #include "input_event.h" #include "internal_client.h" -#include "main.h" -#include "scene.h" -#include "unmanaged.h" -#include "waylandclient.h" -#include "workspace.h" #include "keyboard_input.h" -#include "input_event.h" -#include "subsurfacemonitor.h" #include "libinput/connection.h" #include "libinput/device.h" +#include "main.h" +#include "scene.h" +#include "subsurfacemonitor.h" +#include "unmanaged.h" +#include "wayland_server.h" +#include "waylandclient.h" +#include "workspace.h" +#include "x11client.h" #include #include #include "ui_debug_console.h" // KWayland -#include +#include #include +#include +#include +#include +#include +#include +#include #include #include // frameworks #include #include // Qt -#include +#include #include #include +#include +#include +#include + +#include // xkb #include +#include #include +#include +#include namespace KWin { @@ -552,6 +566,28 @@ void DebugConsoleFilter::tabletPadRingEvent(int number, int position, bool isFin m_textEdit->ensureCursorVisible(); } +static QString sourceString(const KWaylandServer::AbstractDataSource *const source) +{ + if (!source) { + return QString(); + } + + if (!source->client()) { + return QStringLiteral("XWayland source"); + } + + const QString executable = waylandServer()->display()->getConnection(source->client())->executablePath(); + + if (auto dataSource = qobject_cast(source)) { + return QStringLiteral("wl_data_source@%1 of %2").arg(wl_resource_get_id(dataSource->resource())).arg(executable); + } else if (qobject_cast(source)) { + return QStringLiteral("zwp_primary_selection_source_v1 of %2").arg(executable); + } else if (qobject_cast(source)) { + return QStringLiteral("data control by %1").arg(executable); + } + return QStringLiteral("unknown source of").arg(executable); +} + DebugConsole::DebugConsole() : QWidget() , m_ui(new Ui::DebugConsole) @@ -561,6 +597,8 @@ DebugConsole::DebugConsole() m_ui->windowsView->setItemDelegate(new DebugConsoleDelegate(this)); m_ui->windowsView->setModel(new DebugConsoleModel(this)); m_ui->surfacesView->setModel(new SurfaceTreeModel(this)); + m_ui->clipboardContent->setModel(new DataSourceModel(this)); + m_ui->primaryContent->setModel(new DataSourceModel(this)); if (kwinApp()->usesLibinput()) { m_ui->inputDevicesView->setModel(new InputDeviceModel(this)); m_ui->inputDevicesView->setItemDelegate(new DebugConsoleDelegate(this)); @@ -572,6 +610,7 @@ DebugConsole::DebugConsole() if (kwinApp()->operationMode() == Application::OperationMode::OperationModeX11) { m_ui->tabWidget->setTabEnabled(1, false); m_ui->tabWidget->setTabEnabled(2, false); + m_ui->tabWidget->setTabEnabled(6, false); } if (!kwinApp()->usesLibinput()) { m_ui->tabWidget->setTabEnabled(3, false); @@ -589,6 +628,20 @@ DebugConsole::DebugConsole() updateKeyboardTab(); connect(input(), &InputRedirection::keyStateChanged, this, &DebugConsole::updateKeyboardTab); } + if (index == 6) { + static_cast(m_ui->clipboardContent->model())->setSource(waylandServer()->seat()->selection()); + m_ui->clipboardSource->setText(sourceString(waylandServer()->seat()->selection())); + connect(waylandServer()->seat(), &KWaylandServer::SeatInterface::selectionChanged, this, [this](KWaylandServer::AbstractDataSource *source) { + static_cast(m_ui->clipboardContent->model())->setSource(source); + m_ui->clipboardSource->setText(sourceString(source)); + }); + static_cast(m_ui->primaryContent->model())->setSource(waylandServer()->seat()->primarySelection()); + m_ui->primarySource->setText(sourceString(waylandServer()->seat()->primarySelection())); + connect(waylandServer()->seat(), &KWaylandServer::SeatInterface::primarySelectionChanged, this, [this](KWaylandServer::AbstractDataSource *source) { + static_cast(m_ui->primaryContent->model())->setSource(source); + m_ui->primarySource->setText(sourceString(source)); + }); + } } ); @@ -1562,4 +1615,119 @@ void InputDeviceModel::setupDeviceConnections(LibInput::Device *device) ); } +QModelIndex DataSourceModel::index(int row, int column, const QModelIndex &parent) const +{ + if (!m_source || parent.isValid() || column >= 2 || row >= m_source->mimeTypes().size()) { + return QModelIndex(); + } + return createIndex(row, column, nullptr); +} + +QModelIndex DataSourceModel::parent(const QModelIndex &child) const +{ + return QModelIndex(); +} + +int DataSourceModel::rowCount(const QModelIndex &parent) const +{ + if (!parent.isValid()) { + return m_source ? m_source->mimeTypes().count() : 0; + } + return 0; +} + +QVariant DataSourceModel::headerData(int section, Qt::Orientation orientation, int role) const +{ + if (role != Qt::DisplayRole || orientation != Qt::Horizontal || section >= 2) { + return QVariant(); + } + return section == 0 ? QStringLiteral("Mime type") : QStringLiteral("Content"); +} + +QVariant DataSourceModel::data(const QModelIndex &index, int role) const +{ + if (!checkIndex(index, CheckIndexOption::ParentIsInvalid | CheckIndexOption::IndexIsValid)) { + return QVariant(); + } + const QString mimeType = m_source->mimeTypes().at(index.row()); + ; + if (index.column() == 0 && role == Qt::DisplayRole) { + return mimeType; + } else if (index.column() == 1) { + const QByteArray &data = m_data.at(index.row()); + if (mimeType.contains(QLatin1String("image"))) { + if (role == Qt::DecorationRole) { + return QImage::fromData(data); + } + } else if (role == Qt::DisplayRole) { + return data; + } + } + return QVariant(); +} + +static QByteArray readData(int fd) +{ + pollfd pfd; + pfd.fd = fd; + pfd.events = POLLIN; + auto closeFd = qScopeGuard([fd] { + close(fd); + }); + QByteArray data; + while (true) { + const int ready = poll(&pfd, 1, 1000); + if (ready < 0) { + if (errno != EINTR) { + return QByteArrayLiteral("poll() failed: ") + strerror(errno); + } + } else if (ready == 0) { + return QByteArrayLiteral("timeout reading from pipe"); + } else { + char buf[4096]; + int n = read(fd, buf, sizeof buf); + + if (n < 0) { + return QByteArrayLiteral("read failed: ") + strerror(errno); + } else if (n == 0) { + return data; + } else if (n > 0) { + data.append(buf, n); + } + } + } +} + +void DataSourceModel::setSource(KWaylandServer::AbstractDataSource *source) +{ + beginResetModel(); + m_source = source; + m_data.clear(); + if (source) { + const auto client = source->client(); + m_data.resize(m_source->mimeTypes().size()); + for (auto type = m_source->mimeTypes().cbegin(); type != m_source->mimeTypes().cend(); ++type) { + int pipeFds[2]; + if (pipe2(pipeFds, O_CLOEXEC) != 0) { + continue; + } + source->requestData(*type, pipeFds[1]); + if (client && client != waylandServer()->internalConnection()->client()) { + close(pipeFds[1]); + } + QFuture data = QtConcurrent::run(readData, pipeFds[0]); + auto watcher = new QFutureWatcher(this); + watcher->setFuture(data); + const int index = type - m_source->mimeTypes().cbegin(); + connect(watcher, &QFutureWatcher::finished, this, [this, watcher, index, source = QPointer(source)] { + watcher->deleteLater(); + if (source && source == m_source) { + m_data[index] = watcher->result(); + Q_EMIT dataChanged(this->index(index, 1), this->index(index, 1), {Qt::DecorationRole | Qt::DisplayRole}); + } + }); + } + } + endResetModel(); +} } diff --git a/src/debug_console.h b/src/debug_console.h index fb5533fd79..810731a704 100644 --- a/src/debug_console.h +++ b/src/debug_console.h @@ -21,6 +21,11 @@ class QTextEdit; +namespace KWaylandServer +{ +class AbstractDataSource; +} + namespace Ui { class DebugConsole; @@ -181,6 +186,27 @@ private: QVector m_devices; }; +class DataSourceModel : public QAbstractItemModel +{ +public: + using QAbstractItemModel::QAbstractItemModel; + + QModelIndex index(int row, int column, const QModelIndex &parent = QModelIndex()) const override; + QModelIndex parent(const QModelIndex &child) const override; + int rowCount(const QModelIndex &parent) const override; + int columnCount(const QModelIndex &parent) const override + { + return parent.isValid() ? 0 : 2; + } + QVariant data(const QModelIndex &index, int role) const override; + QVariant headerData(int section, Qt::Orientation orientation, int role = Qt::DisplayRole) const override; + + void setSource(KWaylandServer::AbstractDataSource *source); + +private: + KWaylandServer::AbstractDataSource *m_source = nullptr; + QVector m_data; +}; } #endif diff --git a/src/debug_console.ui b/src/debug_console.ui index cb6cc0ae16..a56de52a5e 100644 --- a/src/debug_console.ui +++ b/src/debug_console.ui @@ -420,10 +420,98 @@ + + + Clipboard + + + + + + + + + 0 + 0 + + + + Clipboard + + + + + + + + 0 + 0 + + + + + + + + + + + + + true + + + false + + + + + + + + + + 0 + 0 + + + + Primary Selection + + + + + + + + + + + + + + + + true + + + false + + + + + + + + KTitleWidget + QWidget +
ktitlewidget.h
+
+
quitButton