File indexing completed on 2024-04-28 16:48:43

0001 /*
0002     KWin - the KDE window manager
0003     This file is part of the KDE project.
0004 
0005     SPDX-FileCopyrightText: 2016 Martin Gräßlin <mgraesslin@kde.org>
0006 
0007     SPDX-License-Identifier: GPL-2.0-or-later
0008 */
0009 #include "debug_console.h"
0010 #include "composite.h"
0011 #include "core/inputdevice.h"
0012 #include "input_event.h"
0013 #include "internalwindow.h"
0014 #include "keyboard_input.h"
0015 #include "main.h"
0016 #include "platformsupport/scenes/opengl/openglbackend.h"
0017 #include "unmanaged.h"
0018 #include "utils/filedescriptor.h"
0019 #include "utils/subsurfacemonitor.h"
0020 #include "wayland/abstract_data_source.h"
0021 #include "wayland/clientconnection.h"
0022 #include "wayland/datacontrolsource_v1_interface.h"
0023 #include "wayland/datasource_interface.h"
0024 #include "wayland/display.h"
0025 #include "wayland/primaryselectionsource_v1_interface.h"
0026 #include "wayland/seat_interface.h"
0027 #include "wayland/shmclientbuffer.h"
0028 #include "wayland/subcompositor_interface.h"
0029 #include "wayland/surface_interface.h"
0030 #include "wayland_server.h"
0031 #include "waylandwindow.h"
0032 #include "workspace.h"
0033 #include "x11window.h"
0034 #include "xkb.h"
0035 #include <cerrno>
0036 #include <kwinglplatform.h>
0037 #include <kwinglutils.h>
0038 
0039 #include "ui_debug_console.h"
0040 
0041 // frameworks
0042 #include <KLocalizedString>
0043 #include <NETWM>
0044 // Qt
0045 #include <QFutureWatcher>
0046 #include <QMetaProperty>
0047 #include <QMetaType>
0048 #include <QMouseEvent>
0049 #include <QScopeGuard>
0050 #include <QSortFilterProxyModel>
0051 #include <QtConcurrentRun>
0052 
0053 #include <wayland-server-core.h>
0054 
0055 // xkb
0056 #include <xkbcommon/xkbcommon.h>
0057 
0058 #include <fcntl.h>
0059 #include <functional>
0060 #include <sys/poll.h>
0061 #include <unistd.h>
0062 
0063 namespace KWin
0064 {
0065 
0066 static QString tableHeaderRow(const QString &title)
0067 {
0068     return QStringLiteral("<tr><th colspan=\"2\">%1</th></tr>").arg(title);
0069 }
0070 
0071 template<typename T>
0072 static QString tableRow(const QString &title, const T &argument)
0073 {
0074     return QStringLiteral("<tr><td>%1</td><td>%2</td></tr>").arg(title).arg(argument);
0075 }
0076 
0077 static QString timestampRow(uint32_t timestamp)
0078 {
0079     return tableRow(i18n("Timestamp"), timestamp);
0080 }
0081 
0082 static QString timestampRow(std::chrono::microseconds timestamp)
0083 {
0084     return tableRow(i18n("Timestamp"), std::chrono::duration_cast<std::chrono::milliseconds>(timestamp).count());
0085 }
0086 
0087 static QString timestampRowUsec(std::chrono::microseconds timestamp)
0088 {
0089     return tableRow(i18n("Timestamp (µsec)"), timestamp.count());
0090 }
0091 
0092 static QString timestampRowUsec(uint64_t timestamp)
0093 {
0094     return tableRow(i18n("Timestamp (µsec)"), timestamp);
0095 }
0096 
0097 static QString buttonToString(Qt::MouseButton button)
0098 {
0099     switch (button) {
0100     case Qt::LeftButton:
0101         return i18nc("A mouse button", "Left");
0102     case Qt::RightButton:
0103         return i18nc("A mouse button", "Right");
0104     case Qt::MiddleButton:
0105         return i18nc("A mouse button", "Middle");
0106     case Qt::BackButton:
0107         return i18nc("A mouse button", "Back");
0108     case Qt::ForwardButton:
0109         return i18nc("A mouse button", "Forward");
0110     case Qt::TaskButton:
0111         return i18nc("A mouse button", "Task");
0112     case Qt::ExtraButton4:
0113         return i18nc("A mouse button", "Extra Button 4");
0114     case Qt::ExtraButton5:
0115         return i18nc("A mouse button", "Extra Button 5");
0116     case Qt::ExtraButton6:
0117         return i18nc("A mouse button", "Extra Button 6");
0118     case Qt::ExtraButton7:
0119         return i18nc("A mouse button", "Extra Button 7");
0120     case Qt::ExtraButton8:
0121         return i18nc("A mouse button", "Extra Button 8");
0122     case Qt::ExtraButton9:
0123         return i18nc("A mouse button", "Extra Button 9");
0124     case Qt::ExtraButton10:
0125         return i18nc("A mouse button", "Extra Button 10");
0126     case Qt::ExtraButton11:
0127         return i18nc("A mouse button", "Extra Button 11");
0128     case Qt::ExtraButton12:
0129         return i18nc("A mouse button", "Extra Button 12");
0130     case Qt::ExtraButton13:
0131         return i18nc("A mouse button", "Extra Button 13");
0132     case Qt::ExtraButton14:
0133         return i18nc("A mouse button", "Extra Button 14");
0134     case Qt::ExtraButton15:
0135         return i18nc("A mouse button", "Extra Button 15");
0136     case Qt::ExtraButton16:
0137         return i18nc("A mouse button", "Extra Button 16");
0138     case Qt::ExtraButton17:
0139         return i18nc("A mouse button", "Extra Button 17");
0140     case Qt::ExtraButton18:
0141         return i18nc("A mouse button", "Extra Button 18");
0142     case Qt::ExtraButton19:
0143         return i18nc("A mouse button", "Extra Button 19");
0144     case Qt::ExtraButton20:
0145         return i18nc("A mouse button", "Extra Button 20");
0146     case Qt::ExtraButton21:
0147         return i18nc("A mouse button", "Extra Button 21");
0148     case Qt::ExtraButton22:
0149         return i18nc("A mouse button", "Extra Button 22");
0150     case Qt::ExtraButton23:
0151         return i18nc("A mouse button", "Extra Button 23");
0152     case Qt::ExtraButton24:
0153         return i18nc("A mouse button", "Extra Button 24");
0154     default:
0155         return QString();
0156     }
0157 }
0158 
0159 static QString deviceRow(InputDevice *device)
0160 {
0161     if (!device) {
0162         return tableRow(i18n("Input Device"), i18nc("The input device of the event is not known", "Unknown"));
0163     }
0164     return tableRow(i18n("Input Device"), QStringLiteral("%1 (%2)").arg(device->name(), device->sysName()));
0165 }
0166 
0167 static QString buttonsToString(Qt::MouseButtons buttons)
0168 {
0169     QString ret;
0170     for (uint i = 1; i < Qt::ExtraButton24; i = i << 1) {
0171         if (buttons & i) {
0172             ret.append(buttonToString(Qt::MouseButton(uint(buttons) & i)));
0173             ret.append(QStringLiteral(" "));
0174         }
0175     };
0176     return ret.trimmed();
0177 }
0178 
0179 static const QString s_hr = QStringLiteral("<hr/>");
0180 static const QString s_tableStart = QStringLiteral("<table>");
0181 static const QString s_tableEnd = QStringLiteral("</table>");
0182 
0183 DebugConsoleFilter::DebugConsoleFilter(QTextEdit *textEdit)
0184     : InputEventSpy()
0185     , m_textEdit(textEdit)
0186 {
0187 }
0188 
0189 DebugConsoleFilter::~DebugConsoleFilter() = default;
0190 
0191 void DebugConsoleFilter::pointerEvent(MouseEvent *event)
0192 {
0193     QString text = s_hr;
0194     const QString timestamp = timestampRow(event->timestamp());
0195 
0196     text.append(s_tableStart);
0197     switch (event->type()) {
0198     case QEvent::MouseMove: {
0199         text.append(tableHeaderRow(i18nc("A mouse pointer motion event", "Pointer Motion")));
0200         text.append(deviceRow(event->device()));
0201         text.append(timestamp);
0202         text.append(timestampRowUsec(event->timestamp()));
0203         if (!event->delta().isNull()) {
0204             text.append(tableRow(i18nc("The relative mouse movement", "Delta"),
0205                                  QStringLiteral("%1/%2").arg(event->delta().x()).arg(event->delta().y())));
0206         }
0207         if (!event->deltaUnaccelerated().isNull()) {
0208             text.append(tableRow(i18nc("The relative mouse movement", "Delta (not accelerated)"),
0209                                  QStringLiteral("%1/%2").arg(event->deltaUnaccelerated().x()).arg(event->deltaUnaccelerated().y())));
0210         }
0211         text.append(tableRow(i18nc("The global mouse pointer position", "Global Position"), QStringLiteral("%1/%2").arg(event->pos().x()).arg(event->pos().y())));
0212         break;
0213     }
0214     case QEvent::MouseButtonPress:
0215         text.append(tableHeaderRow(i18nc("A mouse pointer button press event", "Pointer Button Press")));
0216         text.append(deviceRow(event->device()));
0217         text.append(timestamp);
0218         text.append(tableRow(i18nc("A button in a mouse press/release event", "Button"), buttonToString(event->button())));
0219         text.append(tableRow(i18nc("A button in a mouse press/release event", "Native Button code"), event->nativeButton()));
0220         text.append(tableRow(i18nc("All currently pressed buttons in a mouse press/release event", "Pressed Buttons"), buttonsToString(event->buttons())));
0221         break;
0222     case QEvent::MouseButtonRelease:
0223         text.append(tableHeaderRow(i18nc("A mouse pointer button release event", "Pointer Button Release")));
0224         text.append(deviceRow(event->device()));
0225         text.append(timestamp);
0226         text.append(tableRow(i18nc("A button in a mouse press/release event", "Button"), buttonToString(event->button())));
0227         text.append(tableRow(i18nc("A button in a mouse press/release event", "Native Button code"), event->nativeButton()));
0228         text.append(tableRow(i18nc("All currently pressed buttons in a mouse press/release event", "Pressed Buttons"), buttonsToString(event->buttons())));
0229         break;
0230     default:
0231         break;
0232     }
0233     text.append(s_tableEnd);
0234 
0235     m_textEdit->insertHtml(text);
0236     m_textEdit->ensureCursorVisible();
0237 }
0238 
0239 void DebugConsoleFilter::wheelEvent(WheelEvent *event)
0240 {
0241     QString text = s_hr;
0242     text.append(s_tableStart);
0243     text.append(tableHeaderRow(i18nc("A mouse pointer axis (wheel) event", "Pointer Axis")));
0244     text.append(deviceRow(event->device()));
0245     text.append(timestampRow(event->timestamp()));
0246     const Qt::Orientation orientation = event->angleDelta().x() == 0 ? Qt::Vertical : Qt::Horizontal;
0247     text.append(tableRow(i18nc("The orientation of a pointer axis event", "Orientation"),
0248                          orientation == Qt::Horizontal ? i18nc("An orientation of a pointer axis event", "Horizontal")
0249                                                        : i18nc("An orientation of a pointer axis event", "Vertical")));
0250     text.append(tableRow(i18nc("The angle delta of a pointer axis event", "Delta"),
0251                          orientation == Qt::Horizontal ? event->angleDelta().x() : event->angleDelta().y()));
0252     text.append(s_tableEnd);
0253 
0254     m_textEdit->insertHtml(text);
0255     m_textEdit->ensureCursorVisible();
0256 }
0257 
0258 void DebugConsoleFilter::keyEvent(KeyEvent *event)
0259 {
0260     QString text = s_hr;
0261     text.append(s_tableStart);
0262 
0263     switch (event->type()) {
0264     case QEvent::KeyPress:
0265         text.append(tableHeaderRow(i18nc("A key press event", "Key Press")));
0266         break;
0267     case QEvent::KeyRelease:
0268         text.append(tableHeaderRow(i18nc("A key release event", "Key Release")));
0269         break;
0270     default:
0271         break;
0272     }
0273     text.append(deviceRow(event->device()));
0274     auto modifiersToString = [event] {
0275         QString ret;
0276         if (event->modifiers().testFlag(Qt::ShiftModifier)) {
0277             ret.append(i18nc("A keyboard modifier", "Shift"));
0278             ret.append(QStringLiteral(" "));
0279         }
0280         if (event->modifiers().testFlag(Qt::ControlModifier)) {
0281             ret.append(i18nc("A keyboard modifier", "Control"));
0282             ret.append(QStringLiteral(" "));
0283         }
0284         if (event->modifiers().testFlag(Qt::AltModifier)) {
0285             ret.append(i18nc("A keyboard modifier", "Alt"));
0286             ret.append(QStringLiteral(" "));
0287         }
0288         if (event->modifiers().testFlag(Qt::MetaModifier)) {
0289             ret.append(i18nc("A keyboard modifier", "Meta"));
0290             ret.append(QStringLiteral(" "));
0291         }
0292         if (event->modifiers().testFlag(Qt::KeypadModifier)) {
0293             ret.append(i18nc("A keyboard modifier", "Keypad"));
0294             ret.append(QStringLiteral(" "));
0295         }
0296         if (event->modifiers().testFlag(Qt::GroupSwitchModifier)) {
0297             ret.append(i18nc("A keyboard modifier", "Group-switch"));
0298             ret.append(QStringLiteral(" "));
0299         }
0300         return ret;
0301     };
0302     text.append(timestampRow(event->timestamp()));
0303     text.append(tableRow(i18nc("Whether the event is an automatic key repeat", "Repeat"), event->isAutoRepeat()));
0304 
0305     const auto keyMetaObject = Qt::qt_getEnumMetaObject(Qt::Key());
0306     const auto enumerator = keyMetaObject->enumerator(keyMetaObject->indexOfEnumerator("Key"));
0307     text.append(tableRow(i18nc("The code as read from the input device", "Scan code"), event->nativeScanCode()));
0308     text.append(tableRow(i18nc("Key according to Qt", "Qt::Key code"),
0309                          enumerator.valueToKey(event->key())));
0310     text.append(tableRow(i18nc("The translated code to an Xkb symbol", "Xkb symbol"), event->nativeVirtualKey()));
0311     text.append(tableRow(i18nc("The translated code interpreted as text", "Utf8"), event->text()));
0312     text.append(tableRow(i18nc("The currently active modifiers", "Modifiers"), modifiersToString()));
0313 
0314     text.append(s_tableEnd);
0315 
0316     m_textEdit->insertHtml(text);
0317     m_textEdit->ensureCursorVisible();
0318 }
0319 
0320 void DebugConsoleFilter::touchDown(qint32 id, const QPointF &pos, std::chrono::microseconds time)
0321 {
0322     QString text = s_hr;
0323     text.append(s_tableStart);
0324     text.append(tableHeaderRow(i18nc("A touch down event", "Touch down")));
0325     text.append(timestampRow(time));
0326     text.append(tableRow(i18nc("The id of the touch point in the touch event", "Point identifier"), id));
0327     text.append(tableRow(i18nc("The global position of the touch point", "Global position"),
0328                          QStringLiteral("%1/%2").arg(pos.x()).arg(pos.y())));
0329     text.append(s_tableEnd);
0330 
0331     m_textEdit->insertHtml(text);
0332     m_textEdit->ensureCursorVisible();
0333 }
0334 
0335 void DebugConsoleFilter::touchMotion(qint32 id, const QPointF &pos, std::chrono::microseconds time)
0336 {
0337     QString text = s_hr;
0338     text.append(s_tableStart);
0339     text.append(tableHeaderRow(i18nc("A touch motion event", "Touch Motion")));
0340     text.append(timestampRow(time));
0341     text.append(tableRow(i18nc("The id of the touch point in the touch event", "Point identifier"), id));
0342     text.append(tableRow(i18nc("The global position of the touch point", "Global position"),
0343                          QStringLiteral("%1/%2").arg(pos.x()).arg(pos.y())));
0344     text.append(s_tableEnd);
0345 
0346     m_textEdit->insertHtml(text);
0347     m_textEdit->ensureCursorVisible();
0348 }
0349 
0350 void DebugConsoleFilter::touchUp(qint32 id, std::chrono::microseconds time)
0351 {
0352     QString text = s_hr;
0353     text.append(s_tableStart);
0354     text.append(tableHeaderRow(i18nc("A touch up event", "Touch Up")));
0355     text.append(timestampRow(time));
0356     text.append(tableRow(i18nc("The id of the touch point in the touch event", "Point identifier"), id));
0357     text.append(s_tableEnd);
0358 
0359     m_textEdit->insertHtml(text);
0360     m_textEdit->ensureCursorVisible();
0361 }
0362 
0363 void DebugConsoleFilter::pinchGestureBegin(int fingerCount, std::chrono::microseconds time)
0364 {
0365     QString text = s_hr;
0366     text.append(s_tableStart);
0367     text.append(tableHeaderRow(i18nc("A pinch gesture is started", "Pinch start")));
0368     text.append(timestampRow(time));
0369     text.append(tableRow(i18nc("Number of fingers in this pinch gesture", "Finger count"), fingerCount));
0370     text.append(s_tableEnd);
0371 
0372     m_textEdit->insertHtml(text);
0373     m_textEdit->ensureCursorVisible();
0374 }
0375 
0376 void DebugConsoleFilter::pinchGestureUpdate(qreal scale, qreal angleDelta, const QPointF &delta, std::chrono::microseconds time)
0377 {
0378     QString text = s_hr;
0379     text.append(s_tableStart);
0380     text.append(tableHeaderRow(i18nc("A pinch gesture is updated", "Pinch update")));
0381     text.append(timestampRow(time));
0382     text.append(tableRow(i18nc("Current scale in pinch gesture", "Scale"), scale));
0383     text.append(tableRow(i18nc("Current angle in pinch gesture", "Angle delta"), angleDelta));
0384     text.append(tableRow(i18nc("Current delta in pinch gesture", "Delta x"), delta.x()));
0385     text.append(tableRow(i18nc("Current delta in pinch gesture", "Delta y"), delta.y()));
0386     text.append(s_tableEnd);
0387 
0388     m_textEdit->insertHtml(text);
0389     m_textEdit->ensureCursorVisible();
0390 }
0391 
0392 void DebugConsoleFilter::pinchGestureEnd(std::chrono::microseconds time)
0393 {
0394     QString text = s_hr;
0395     text.append(s_tableStart);
0396     text.append(tableHeaderRow(i18nc("A pinch gesture ended", "Pinch end")));
0397     text.append(timestampRow(time));
0398     text.append(s_tableEnd);
0399 
0400     m_textEdit->insertHtml(text);
0401     m_textEdit->ensureCursorVisible();
0402 }
0403 
0404 void DebugConsoleFilter::pinchGestureCancelled(std::chrono::microseconds time)
0405 {
0406     QString text = s_hr;
0407     text.append(s_tableStart);
0408     text.append(tableHeaderRow(i18nc("A pinch gesture got cancelled", "Pinch cancelled")));
0409     text.append(timestampRow(time));
0410     text.append(s_tableEnd);
0411 
0412     m_textEdit->insertHtml(text);
0413     m_textEdit->ensureCursorVisible();
0414 }
0415 
0416 void DebugConsoleFilter::swipeGestureBegin(int fingerCount, std::chrono::microseconds time)
0417 {
0418     QString text = s_hr;
0419     text.append(s_tableStart);
0420     text.append(tableHeaderRow(i18nc("A swipe gesture is started", "Swipe start")));
0421     text.append(timestampRow(time));
0422     text.append(tableRow(i18nc("Number of fingers in this swipe gesture", "Finger count"), fingerCount));
0423     text.append(s_tableEnd);
0424 
0425     m_textEdit->insertHtml(text);
0426     m_textEdit->ensureCursorVisible();
0427 }
0428 
0429 void DebugConsoleFilter::swipeGestureUpdate(const QPointF &delta, std::chrono::microseconds time)
0430 {
0431     QString text = s_hr;
0432     text.append(s_tableStart);
0433     text.append(tableHeaderRow(i18nc("A swipe gesture is updated", "Swipe update")));
0434     text.append(timestampRow(time));
0435     text.append(tableRow(i18nc("Current delta in swipe gesture", "Delta x"), delta.x()));
0436     text.append(tableRow(i18nc("Current delta in swipe gesture", "Delta y"), delta.y()));
0437     text.append(s_tableEnd);
0438 
0439     m_textEdit->insertHtml(text);
0440     m_textEdit->ensureCursorVisible();
0441 }
0442 
0443 void DebugConsoleFilter::swipeGestureEnd(std::chrono::microseconds time)
0444 {
0445     QString text = s_hr;
0446     text.append(s_tableStart);
0447     text.append(tableHeaderRow(i18nc("A swipe gesture ended", "Swipe end")));
0448     text.append(timestampRow(time));
0449     text.append(s_tableEnd);
0450 
0451     m_textEdit->insertHtml(text);
0452     m_textEdit->ensureCursorVisible();
0453 }
0454 
0455 void DebugConsoleFilter::swipeGestureCancelled(std::chrono::microseconds time)
0456 {
0457     QString text = s_hr;
0458     text.append(s_tableStart);
0459     text.append(tableHeaderRow(i18nc("A swipe gesture got cancelled", "Swipe cancelled")));
0460     text.append(timestampRow(time));
0461     text.append(s_tableEnd);
0462 
0463     m_textEdit->insertHtml(text);
0464     m_textEdit->ensureCursorVisible();
0465 }
0466 
0467 void DebugConsoleFilter::switchEvent(SwitchEvent *event)
0468 {
0469     QString text = s_hr;
0470     text.append(s_tableStart);
0471     text.append(tableHeaderRow(i18nc("A hardware switch (e.g. notebook lid) got toggled", "Switch toggled")));
0472     text.append(timestampRow(event->timestamp()));
0473     text.append(timestampRowUsec(event->timestamp()));
0474     text.append(deviceRow(event->device()));
0475     QString switchName;
0476     if (event->device()->isLidSwitch()) {
0477         switchName = i18nc("Name of a hardware switch", "Notebook lid");
0478     } else if (event->device()->isTabletModeSwitch()) {
0479         switchName = i18nc("Name of a hardware switch", "Tablet mode");
0480     }
0481     text.append(tableRow(i18nc("A hardware switch", "Switch"), switchName));
0482     QString switchState;
0483     switch (event->state()) {
0484     case SwitchEvent::State::Off:
0485         switchState = i18nc("The hardware switch got turned off", "Off");
0486         break;
0487     case SwitchEvent::State::On:
0488         switchState = i18nc("The hardware switch got turned on", "On");
0489         break;
0490     default:
0491         Q_UNREACHABLE();
0492     }
0493     text.append(tableRow(i18nc("State of a hardware switch (on/off)", "State"), switchState));
0494     text.append(s_tableEnd);
0495 
0496     m_textEdit->insertHtml(text);
0497     m_textEdit->ensureCursorVisible();
0498 }
0499 
0500 void DebugConsoleFilter::tabletToolEvent(TabletEvent *event)
0501 {
0502     QString typeString;
0503     {
0504         QDebug d(&typeString);
0505         d << event->type();
0506     }
0507 
0508     QString text = s_hr + s_tableStart + tableHeaderRow(i18n("Tablet Tool"))
0509         + tableRow(i18n("EventType"), typeString)
0510         + tableRow(i18n("Position"),
0511                    QStringLiteral("%1,%2").arg(event->pos().x()).arg(event->pos().y()))
0512         + tableRow(i18n("Tilt"),
0513                    QStringLiteral("%1,%2").arg(event->xTilt()).arg(event->yTilt()))
0514         + tableRow(i18n("Rotation"), QString::number(event->rotation()))
0515         + tableRow(i18n("Pressure"), QString::number(event->pressure()))
0516         + tableRow(i18n("Buttons"), QString::number(event->buttons()))
0517         + tableRow(i18n("Modifiers"), QString::number(event->modifiers()))
0518         + s_tableEnd;
0519 
0520     m_textEdit->insertHtml(text);
0521     m_textEdit->ensureCursorVisible();
0522 }
0523 
0524 void DebugConsoleFilter::tabletToolButtonEvent(uint button, bool pressed, const TabletToolId &tabletToolId, std::chrono::microseconds time)
0525 {
0526     QString text = s_hr + s_tableStart + tableHeaderRow(i18n("Tablet Tool Button"))
0527         + tableRow(i18n("Button"), button)
0528         + tableRow(i18n("Pressed"), pressed)
0529         + tableRow(i18n("Tablet"), qHash(tabletToolId.m_deviceGroupData))
0530         + timestampRow(time)
0531         + s_tableEnd;
0532 
0533     m_textEdit->insertHtml(text);
0534     m_textEdit->ensureCursorVisible();
0535 }
0536 
0537 void DebugConsoleFilter::tabletPadButtonEvent(uint button, bool pressed, const TabletPadId &tabletPadId, std::chrono::microseconds time)
0538 {
0539     QString text = s_hr + s_tableStart
0540         + tableHeaderRow(i18n("Tablet Pad Button"))
0541         + tableRow(i18n("Button"), button)
0542         + tableRow(i18n("Pressed"), pressed)
0543         + tableRow(i18n("Tablet"), qHash(tabletPadId.data))
0544         + timestampRow(time)
0545         + s_tableEnd;
0546 
0547     m_textEdit->insertHtml(text);
0548     m_textEdit->ensureCursorVisible();
0549 }
0550 
0551 void DebugConsoleFilter::tabletPadStripEvent(int number, int position, bool isFinger, const TabletPadId &tabletPadId, std::chrono::microseconds time)
0552 {
0553     QString text = s_hr + s_tableStart + tableHeaderRow(i18n("Tablet Pad Strip"))
0554         + tableRow(i18n("Number"), number)
0555         + tableRow(i18n("Position"), position)
0556         + tableRow(i18n("isFinger"), isFinger)
0557         + tableRow(i18n("Tablet"), qHash(tabletPadId.data))
0558         + timestampRow(time)
0559         + s_tableEnd;
0560 
0561     m_textEdit->insertHtml(text);
0562     m_textEdit->ensureCursorVisible();
0563 }
0564 
0565 void DebugConsoleFilter::tabletPadRingEvent(int number, int position, bool isFinger, const TabletPadId &tabletPadId, std::chrono::microseconds time)
0566 {
0567     QString text = s_hr + s_tableStart + tableHeaderRow(i18n("Tablet Pad Ring"))
0568         + tableRow(i18n("Number"), number)
0569         + tableRow(i18n("Position"), position)
0570         + tableRow(i18n("isFinger"), isFinger)
0571         + tableRow(i18n("Tablet"), qHash(tabletPadId.data))
0572         + timestampRow(time)
0573         + s_tableEnd;
0574 
0575     m_textEdit->insertHtml(text);
0576     m_textEdit->ensureCursorVisible();
0577 }
0578 
0579 static QString sourceString(const KWaylandServer::AbstractDataSource *const source)
0580 {
0581     if (!source) {
0582         return QString();
0583     }
0584 
0585     if (!source->client()) {
0586         return QStringLiteral("XWayland source");
0587     }
0588 
0589     const QString executable = waylandServer()->display()->getConnection(source->client())->executablePath();
0590 
0591     if (auto dataSource = qobject_cast<const KWaylandServer::DataSourceInterface *const>(source)) {
0592         return QStringLiteral("wl_data_source@%1 of %2").arg(wl_resource_get_id(dataSource->resource())).arg(executable);
0593     } else if (qobject_cast<const KWaylandServer::PrimarySelectionSourceV1Interface *const>(source)) {
0594         return QStringLiteral("zwp_primary_selection_source_v1 of %2").arg(executable);
0595     } else if (qobject_cast<const KWaylandServer::DataControlSourceV1Interface *const>(source)) {
0596         return QStringLiteral("data control by %1").arg(executable);
0597     }
0598     return QStringLiteral("unknown source of").arg(executable);
0599 }
0600 
0601 DebugConsole::DebugConsole()
0602     : QWidget()
0603     , m_ui(new Ui::DebugConsole)
0604 {
0605     setAttribute(Qt::WA_ShowWithoutActivating);
0606     m_ui->setupUi(this);
0607 
0608     auto windowsModel = new DebugConsoleModel(this);
0609     QSortFilterProxyModel *proxyWindowsModel = new QSortFilterProxyModel(this);
0610     proxyWindowsModel->setSourceModel(windowsModel);
0611     m_ui->windowsView->setModel(proxyWindowsModel);
0612     m_ui->windowsView->sortByColumn(0, Qt::AscendingOrder);
0613     m_ui->windowsView->header()->setSortIndicatorShown(true);
0614     m_ui->windowsView->setItemDelegate(new DebugConsoleDelegate(this));
0615 
0616     m_ui->surfacesView->setModel(new SurfaceTreeModel(this));
0617     m_ui->clipboardContent->setModel(new DataSourceModel(this));
0618     m_ui->primaryContent->setModel(new DataSourceModel(this));
0619     m_ui->inputDevicesView->setModel(new InputDeviceModel(this));
0620     m_ui->inputDevicesView->setItemDelegate(new DebugConsoleDelegate(this));
0621     m_ui->quitButton->setIcon(QIcon::fromTheme(QStringLiteral("application-exit")));
0622     m_ui->tabWidget->setTabIcon(0, QIcon::fromTheme(QStringLiteral("view-list-tree")));
0623     m_ui->tabWidget->setTabIcon(1, QIcon::fromTheme(QStringLiteral("view-list-tree")));
0624 
0625     if (kwinApp()->operationMode() == Application::OperationMode::OperationModeX11) {
0626         m_ui->tabWidget->setTabEnabled(1, false);
0627         m_ui->tabWidget->setTabEnabled(2, false);
0628         m_ui->tabWidget->setTabEnabled(6, false);
0629         setWindowFlags(Qt::X11BypassWindowManagerHint);
0630     }
0631 
0632     connect(m_ui->quitButton, &QAbstractButton::clicked, this, &DebugConsole::deleteLater);
0633     connect(m_ui->tabWidget, &QTabWidget::currentChanged, this, [this](int index) {
0634         // delay creation of input event filter until the tab is selected
0635         if (index == 2 && !m_inputFilter) {
0636             m_inputFilter.reset(new DebugConsoleFilter(m_ui->inputTextEdit));
0637             input()->installInputEventSpy(m_inputFilter.get());
0638         }
0639         if (index == 5) {
0640             updateKeyboardTab();
0641             connect(input(), &InputRedirection::keyStateChanged, this, &DebugConsole::updateKeyboardTab);
0642         }
0643         if (index == 6) {
0644             static_cast<DataSourceModel *>(m_ui->clipboardContent->model())->setSource(waylandServer()->seat()->selection());
0645             m_ui->clipboardSource->setText(sourceString(waylandServer()->seat()->selection()));
0646             connect(waylandServer()->seat(), &KWaylandServer::SeatInterface::selectionChanged, this, [this](KWaylandServer::AbstractDataSource *source) {
0647                 static_cast<DataSourceModel *>(m_ui->clipboardContent->model())->setSource(source);
0648                 m_ui->clipboardSource->setText(sourceString(source));
0649             });
0650             static_cast<DataSourceModel *>(m_ui->primaryContent->model())->setSource(waylandServer()->seat()->primarySelection());
0651             m_ui->primarySource->setText(sourceString(waylandServer()->seat()->primarySelection()));
0652             connect(waylandServer()->seat(), &KWaylandServer::SeatInterface::primarySelectionChanged, this, [this](KWaylandServer::AbstractDataSource *source) {
0653                 static_cast<DataSourceModel *>(m_ui->primaryContent->model())->setSource(source);
0654                 m_ui->primarySource->setText(sourceString(source));
0655             });
0656         }
0657     });
0658 
0659     initGLTab();
0660 }
0661 
0662 DebugConsole::~DebugConsole() = default;
0663 
0664 void DebugConsole::initGLTab()
0665 {
0666     if (!effects || !effects->isOpenGLCompositing()) {
0667         m_ui->noOpenGLLabel->setVisible(true);
0668         m_ui->glInfoScrollArea->setVisible(false);
0669         return;
0670     }
0671     GLPlatform *gl = GLPlatform::instance();
0672     m_ui->noOpenGLLabel->setVisible(false);
0673     m_ui->glInfoScrollArea->setVisible(true);
0674     m_ui->glVendorStringLabel->setText(QString::fromLocal8Bit(gl->glVendorString()));
0675     m_ui->glRendererStringLabel->setText(QString::fromLocal8Bit(gl->glRendererString()));
0676     m_ui->glVersionStringLabel->setText(QString::fromLocal8Bit(gl->glVersionString()));
0677     m_ui->glslVersionStringLabel->setText(QString::fromLocal8Bit(gl->glShadingLanguageVersionString()));
0678     m_ui->glDriverLabel->setText(GLPlatform::driverToString(gl->driver()));
0679     m_ui->glGPULabel->setText(GLPlatform::chipClassToString(gl->chipClass()));
0680     m_ui->glVersionLabel->setText(GLPlatform::versionToString(gl->glVersion()));
0681     m_ui->glslLabel->setText(GLPlatform::versionToString(gl->glslVersion()));
0682 
0683     auto extensionsString = [](const auto &extensions) {
0684         QString text = QStringLiteral("<ul>");
0685         for (auto extension : extensions) {
0686             text.append(QStringLiteral("<li>%1</li>").arg(QString::fromLocal8Bit(extension)));
0687         }
0688         text.append(QStringLiteral("</ul>"));
0689         return text;
0690     };
0691 
0692     const OpenGLBackend *backend = static_cast<OpenGLBackend *>(Compositor::self()->backend());
0693     m_ui->platformExtensionsLabel->setText(extensionsString(backend->extensions()));
0694     m_ui->openGLExtensionsLabel->setText(extensionsString(openGLExtensions()));
0695 }
0696 
0697 template<typename T>
0698 QString keymapComponentToString(xkb_keymap *map, const T &count, std::function<const char *(xkb_keymap *, T)> f)
0699 {
0700     QString text = QStringLiteral("<ul>");
0701     for (T i = 0; i < count; i++) {
0702         text.append(QStringLiteral("<li>%1</li>").arg(QString::fromLocal8Bit(f(map, i))));
0703     }
0704     text.append(QStringLiteral("</ul>"));
0705     return text;
0706 }
0707 
0708 template<typename T>
0709 QString stateActiveComponents(xkb_state *state, const T &count, std::function<int(xkb_state *, T)> f, std::function<const char *(xkb_keymap *, T)> name)
0710 {
0711     QString text = QStringLiteral("<ul>");
0712     xkb_keymap *map = xkb_state_get_keymap(state);
0713     for (T i = 0; i < count; i++) {
0714         if (f(state, i) == 1) {
0715             text.append(QStringLiteral("<li>%1</li>").arg(QString::fromLocal8Bit(name(map, i))));
0716         }
0717     }
0718     text.append(QStringLiteral("</ul>"));
0719     return text;
0720 }
0721 
0722 void DebugConsole::updateKeyboardTab()
0723 {
0724     auto xkb = input()->keyboard()->xkb();
0725     xkb_keymap *map = xkb->keymap();
0726     xkb_state *state = xkb->state();
0727     m_ui->layoutsLabel->setText(keymapComponentToString<xkb_layout_index_t>(map, xkb_keymap_num_layouts(map), &xkb_keymap_layout_get_name));
0728     m_ui->currentLayoutLabel->setText(xkb_keymap_layout_get_name(map, xkb->currentLayout()));
0729     m_ui->modifiersLabel->setText(keymapComponentToString<xkb_mod_index_t>(map, xkb_keymap_num_mods(map), &xkb_keymap_mod_get_name));
0730     m_ui->ledsLabel->setText(keymapComponentToString<xkb_led_index_t>(map, xkb_keymap_num_leds(map), &xkb_keymap_led_get_name));
0731     m_ui->activeLedsLabel->setText(stateActiveComponents<xkb_led_index_t>(state, xkb_keymap_num_leds(map), &xkb_state_led_index_is_active, &xkb_keymap_led_get_name));
0732 
0733     using namespace std::placeholders;
0734     auto modActive = std::bind(xkb_state_mod_index_is_active, _1, _2, XKB_STATE_MODS_EFFECTIVE);
0735     m_ui->activeModifiersLabel->setText(stateActiveComponents<xkb_mod_index_t>(state, xkb_keymap_num_mods(map), modActive, &xkb_keymap_mod_get_name));
0736 }
0737 
0738 void DebugConsole::showEvent(QShowEvent *event)
0739 {
0740     QWidget::showEvent(event);
0741 
0742     // delay the connection to the show event as in ctor the windowHandle returns null
0743     connect(windowHandle(), &QWindow::visibleChanged, this, [this](bool visible) {
0744         if (visible) {
0745             // ignore
0746             return;
0747         }
0748         deleteLater();
0749     });
0750 }
0751 
0752 DebugConsoleDelegate::DebugConsoleDelegate(QObject *parent)
0753     : QStyledItemDelegate(parent)
0754 {
0755 }
0756 
0757 DebugConsoleDelegate::~DebugConsoleDelegate() = default;
0758 
0759 QString DebugConsoleDelegate::displayText(const QVariant &value, const QLocale &locale) const
0760 {
0761     switch (value.userType()) {
0762     case QMetaType::QPoint: {
0763         const QPoint p = value.toPoint();
0764         return QStringLiteral("%1,%2").arg(p.x()).arg(p.y());
0765     }
0766     case QMetaType::QPointF: {
0767         const QPointF p = value.toPointF();
0768         return QStringLiteral("%1,%2").arg(p.x()).arg(p.y());
0769     }
0770     case QMetaType::QSize: {
0771         const QSize s = value.toSize();
0772         return QStringLiteral("%1x%2").arg(s.width()).arg(s.height());
0773     }
0774     case QMetaType::QSizeF: {
0775         const QSizeF s = value.toSizeF();
0776         return QStringLiteral("%1x%2").arg(s.width()).arg(s.height());
0777     }
0778     case QMetaType::QRect: {
0779         const QRect r = value.toRect();
0780         return QStringLiteral("%1,%2 %3x%4").arg(r.x()).arg(r.y()).arg(r.width()).arg(r.height());
0781     }
0782     case QMetaType::QRectF: {
0783         const QRectF r = value.toRectF();
0784         return QStringLiteral("%1,%2 %3x%4").arg(r.x()).arg(r.y()).arg(r.width()).arg(r.height());
0785     }
0786     default:
0787         if (value.userType() == qMetaTypeId<KWaylandServer::SurfaceInterface *>()) {
0788             if (auto s = value.value<KWaylandServer::SurfaceInterface *>()) {
0789                 return QStringLiteral("KWaylandServer::SurfaceInterface(0x%1)").arg(qulonglong(s), 0, 16);
0790             } else {
0791                 return QStringLiteral("nullptr");
0792             }
0793         }
0794         if (value.userType() == qMetaTypeId<Qt::MouseButtons>()) {
0795             const auto buttons = value.value<Qt::MouseButtons>();
0796             if (buttons == Qt::NoButton) {
0797                 return i18n("No Mouse Buttons");
0798             }
0799             QStringList list;
0800             if (buttons.testFlag(Qt::LeftButton)) {
0801                 list << i18nc("Mouse Button", "left");
0802             }
0803             if (buttons.testFlag(Qt::RightButton)) {
0804                 list << i18nc("Mouse Button", "right");
0805             }
0806             if (buttons.testFlag(Qt::MiddleButton)) {
0807                 list << i18nc("Mouse Button", "middle");
0808             }
0809             if (buttons.testFlag(Qt::BackButton)) {
0810                 list << i18nc("Mouse Button", "back");
0811             }
0812             if (buttons.testFlag(Qt::ForwardButton)) {
0813                 list << i18nc("Mouse Button", "forward");
0814             }
0815             if (buttons.testFlag(Qt::ExtraButton1)) {
0816                 list << i18nc("Mouse Button", "extra 1");
0817             }
0818             if (buttons.testFlag(Qt::ExtraButton2)) {
0819                 list << i18nc("Mouse Button", "extra 2");
0820             }
0821             if (buttons.testFlag(Qt::ExtraButton3)) {
0822                 list << i18nc("Mouse Button", "extra 3");
0823             }
0824             if (buttons.testFlag(Qt::ExtraButton4)) {
0825                 list << i18nc("Mouse Button", "extra 4");
0826             }
0827             if (buttons.testFlag(Qt::ExtraButton5)) {
0828                 list << i18nc("Mouse Button", "extra 5");
0829             }
0830             if (buttons.testFlag(Qt::ExtraButton6)) {
0831                 list << i18nc("Mouse Button", "extra 6");
0832             }
0833             if (buttons.testFlag(Qt::ExtraButton7)) {
0834                 list << i18nc("Mouse Button", "extra 7");
0835             }
0836             if (buttons.testFlag(Qt::ExtraButton8)) {
0837                 list << i18nc("Mouse Button", "extra 8");
0838             }
0839             if (buttons.testFlag(Qt::ExtraButton9)) {
0840                 list << i18nc("Mouse Button", "extra 9");
0841             }
0842             if (buttons.testFlag(Qt::ExtraButton10)) {
0843                 list << i18nc("Mouse Button", "extra 10");
0844             }
0845             if (buttons.testFlag(Qt::ExtraButton11)) {
0846                 list << i18nc("Mouse Button", "extra 11");
0847             }
0848             if (buttons.testFlag(Qt::ExtraButton12)) {
0849                 list << i18nc("Mouse Button", "extra 12");
0850             }
0851             if (buttons.testFlag(Qt::ExtraButton13)) {
0852                 list << i18nc("Mouse Button", "extra 13");
0853             }
0854             if (buttons.testFlag(Qt::ExtraButton14)) {
0855                 list << i18nc("Mouse Button", "extra 14");
0856             }
0857             if (buttons.testFlag(Qt::ExtraButton15)) {
0858                 list << i18nc("Mouse Button", "extra 15");
0859             }
0860             if (buttons.testFlag(Qt::ExtraButton16)) {
0861                 list << i18nc("Mouse Button", "extra 16");
0862             }
0863             if (buttons.testFlag(Qt::ExtraButton17)) {
0864                 list << i18nc("Mouse Button", "extra 17");
0865             }
0866             if (buttons.testFlag(Qt::ExtraButton18)) {
0867                 list << i18nc("Mouse Button", "extra 18");
0868             }
0869             if (buttons.testFlag(Qt::ExtraButton19)) {
0870                 list << i18nc("Mouse Button", "extra 19");
0871             }
0872             if (buttons.testFlag(Qt::ExtraButton20)) {
0873                 list << i18nc("Mouse Button", "extra 20");
0874             }
0875             if (buttons.testFlag(Qt::ExtraButton21)) {
0876                 list << i18nc("Mouse Button", "extra 21");
0877             }
0878             if (buttons.testFlag(Qt::ExtraButton22)) {
0879                 list << i18nc("Mouse Button", "extra 22");
0880             }
0881             if (buttons.testFlag(Qt::ExtraButton23)) {
0882                 list << i18nc("Mouse Button", "extra 23");
0883             }
0884             if (buttons.testFlag(Qt::ExtraButton24)) {
0885                 list << i18nc("Mouse Button", "extra 24");
0886             }
0887             if (buttons.testFlag(Qt::TaskButton)) {
0888                 list << i18nc("Mouse Button", "task");
0889             }
0890             return list.join(QStringLiteral(", "));
0891         }
0892         break;
0893     }
0894     return QStyledItemDelegate::displayText(value, locale);
0895 }
0896 
0897 static const int s_x11WindowId = 1;
0898 static const int s_x11UnmanagedId = 2;
0899 static const int s_waylandWindowId = 3;
0900 static const int s_workspaceInternalId = 4;
0901 static const quint32 s_propertyBitMask = 0xFFFF0000;
0902 static const quint32 s_windowBitMask = 0x0000FFFF;
0903 static const quint32 s_idDistance = 10000;
0904 
0905 template<class T>
0906 void DebugConsoleModel::add(int parentRow, QVector<T *> &windows, T *window)
0907 {
0908     beginInsertRows(index(parentRow, 0, QModelIndex()), windows.count(), windows.count());
0909     windows.append(window);
0910     endInsertRows();
0911 }
0912 
0913 template<class T>
0914 void DebugConsoleModel::remove(int parentRow, QVector<T *> &windows, T *window)
0915 {
0916     const int remove = windows.indexOf(window);
0917     if (remove == -1) {
0918         return;
0919     }
0920     beginRemoveRows(index(parentRow, 0, QModelIndex()), remove, remove);
0921     windows.removeAt(remove);
0922     endRemoveRows();
0923 }
0924 
0925 DebugConsoleModel::DebugConsoleModel(QObject *parent)
0926     : QAbstractItemModel(parent)
0927 {
0928     const auto windows = workspace()->allClientList();
0929     for (auto window : windows) {
0930         handleWindowAdded(window);
0931     }
0932     connect(workspace(), &Workspace::windowAdded, this, &DebugConsoleModel::handleWindowAdded);
0933     connect(workspace(), &Workspace::windowRemoved, this, &DebugConsoleModel::handleWindowRemoved);
0934 
0935     const auto unmangeds = workspace()->unmanagedList();
0936     for (auto u : unmangeds) {
0937         m_unmanageds.append(u);
0938     }
0939     connect(workspace(), &Workspace::unmanagedAdded, this, [this](Unmanaged *u) {
0940         add(s_x11UnmanagedId - 1, m_unmanageds, u);
0941     });
0942     connect(workspace(), &Workspace::unmanagedRemoved, this, [this](Unmanaged *u) {
0943         remove(s_x11UnmanagedId - 1, m_unmanageds, u);
0944     });
0945     for (InternalWindow *window : workspace()->internalWindows()) {
0946         m_internalWindows.append(window);
0947     }
0948     connect(workspace(), &Workspace::internalWindowAdded, this, [this](InternalWindow *window) {
0949         add(s_workspaceInternalId - 1, m_internalWindows, window);
0950     });
0951     connect(workspace(), &Workspace::internalWindowRemoved, this, [this](InternalWindow *window) {
0952         remove(s_workspaceInternalId - 1, m_internalWindows, window);
0953     });
0954 }
0955 
0956 void DebugConsoleModel::handleWindowAdded(Window *window)
0957 {
0958     if (auto x11 = qobject_cast<X11Window *>(window)) {
0959         add(s_x11WindowId - 1, m_x11Windows, x11);
0960         return;
0961     }
0962 
0963     if (auto wayland = qobject_cast<WaylandWindow *>(window)) {
0964         add(s_waylandWindowId - 1, m_waylandWindows, wayland);
0965         return;
0966     }
0967 }
0968 
0969 void DebugConsoleModel::handleWindowRemoved(Window *window)
0970 {
0971     if (auto x11 = qobject_cast<X11Window *>(window)) {
0972         remove(s_x11WindowId - 1, m_x11Windows, x11);
0973         return;
0974     }
0975 
0976     if (auto wayland = qobject_cast<WaylandWindow *>(window)) {
0977         remove(s_waylandWindowId - 1, m_waylandWindows, wayland);
0978         return;
0979     }
0980 }
0981 
0982 DebugConsoleModel::~DebugConsoleModel() = default;
0983 
0984 int DebugConsoleModel::columnCount(const QModelIndex &parent) const
0985 {
0986     return 2;
0987 }
0988 
0989 int DebugConsoleModel::topLevelRowCount() const
0990 {
0991     return kwinApp()->shouldUseWaylandForCompositing() ? 4 : 2;
0992 }
0993 
0994 template<class T>
0995 int DebugConsoleModel::propertyCount(const QModelIndex &parent, T *(DebugConsoleModel::*filter)(const QModelIndex &) const) const
0996 {
0997     if (T *t = (this->*filter)(parent)) {
0998         return t->metaObject()->propertyCount();
0999     }
1000     return 0;
1001 }
1002 
1003 int DebugConsoleModel::rowCount(const QModelIndex &parent) const
1004 {
1005     if (!parent.isValid()) {
1006         return topLevelRowCount();
1007     }
1008 
1009     switch (parent.internalId()) {
1010     case s_x11WindowId:
1011         return m_x11Windows.count();
1012     case s_x11UnmanagedId:
1013         return m_unmanageds.count();
1014     case s_waylandWindowId:
1015         return m_waylandWindows.count();
1016     case s_workspaceInternalId:
1017         return m_internalWindows.count();
1018     default:
1019         break;
1020     }
1021 
1022     if (parent.internalId() & s_propertyBitMask) {
1023         // properties do not have children
1024         return 0;
1025     }
1026 
1027     if (parent.internalId() < s_idDistance * (s_x11WindowId + 1)) {
1028         return propertyCount(parent, &DebugConsoleModel::x11Window);
1029     } else if (parent.internalId() < s_idDistance * (s_x11UnmanagedId + 1)) {
1030         return propertyCount(parent, &DebugConsoleModel::unmanaged);
1031     } else if (parent.internalId() < s_idDistance * (s_waylandWindowId + 1)) {
1032         return propertyCount(parent, &DebugConsoleModel::waylandWindow);
1033     } else if (parent.internalId() < s_idDistance * (s_workspaceInternalId + 1)) {
1034         return propertyCount(parent, &DebugConsoleModel::internalWindow);
1035     }
1036 
1037     return 0;
1038 }
1039 
1040 template<class T>
1041 QModelIndex DebugConsoleModel::indexForWindow(int row, int column, const QVector<T *> &windows, int id) const
1042 {
1043     if (column != 0) {
1044         return QModelIndex();
1045     }
1046     if (row >= windows.count()) {
1047         return QModelIndex();
1048     }
1049     return createIndex(row, column, s_idDistance * id + row);
1050 }
1051 
1052 template<class T>
1053 QModelIndex DebugConsoleModel::indexForProperty(int row, int column, const QModelIndex &parent, T *(DebugConsoleModel::*filter)(const QModelIndex &) const) const
1054 {
1055     if (T *t = (this->*filter)(parent)) {
1056         if (row >= t->metaObject()->propertyCount()) {
1057             return QModelIndex();
1058         }
1059         return createIndex(row, column, quint32(row + 1) << 16 | parent.internalId());
1060     }
1061     return QModelIndex();
1062 }
1063 
1064 QModelIndex DebugConsoleModel::index(int row, int column, const QModelIndex &parent) const
1065 {
1066     if (!parent.isValid()) {
1067         // index for a top level item
1068         if (column != 0 || row >= topLevelRowCount()) {
1069             return QModelIndex();
1070         }
1071         return createIndex(row, column, row + 1);
1072     }
1073     if (column >= 2) {
1074         // max of 2 columns
1075         return QModelIndex();
1076     }
1077     // index for a window (second level)
1078     switch (parent.internalId()) {
1079     case s_x11WindowId:
1080         return indexForWindow(row, column, m_x11Windows, s_x11WindowId);
1081     case s_x11UnmanagedId:
1082         return indexForWindow(row, column, m_unmanageds, s_x11UnmanagedId);
1083     case s_waylandWindowId:
1084         return indexForWindow(row, column, m_waylandWindows, s_waylandWindowId);
1085     case s_workspaceInternalId:
1086         return indexForWindow(row, column, m_internalWindows, s_workspaceInternalId);
1087     default:
1088         break;
1089     }
1090 
1091     // index for a property (third level)
1092     if (parent.internalId() < s_idDistance * (s_x11WindowId + 1)) {
1093         return indexForProperty(row, column, parent, &DebugConsoleModel::x11Window);
1094     } else if (parent.internalId() < s_idDistance * (s_x11UnmanagedId + 1)) {
1095         return indexForProperty(row, column, parent, &DebugConsoleModel::unmanaged);
1096     } else if (parent.internalId() < s_idDistance * (s_waylandWindowId + 1)) {
1097         return indexForProperty(row, column, parent, &DebugConsoleModel::waylandWindow);
1098     } else if (parent.internalId() < s_idDistance * (s_workspaceInternalId + 1)) {
1099         return indexForProperty(row, column, parent, &DebugConsoleModel::internalWindow);
1100     }
1101 
1102     return QModelIndex();
1103 }
1104 
1105 QModelIndex DebugConsoleModel::parent(const QModelIndex &child) const
1106 {
1107     if (child.internalId() <= s_workspaceInternalId) {
1108         return QModelIndex();
1109     }
1110     if (child.internalId() & s_propertyBitMask) {
1111         // a property
1112         const quint32 parentId = child.internalId() & s_windowBitMask;
1113         if (parentId < s_idDistance * (s_x11WindowId + 1)) {
1114             return createIndex(parentId - (s_idDistance * s_x11WindowId), 0, parentId);
1115         } else if (parentId < s_idDistance * (s_x11UnmanagedId + 1)) {
1116             return createIndex(parentId - (s_idDistance * s_x11UnmanagedId), 0, parentId);
1117         } else if (parentId < s_idDistance * (s_waylandWindowId + 1)) {
1118             return createIndex(parentId - (s_idDistance * s_waylandWindowId), 0, parentId);
1119         } else if (parentId < s_idDistance * (s_workspaceInternalId + 1)) {
1120             return createIndex(parentId - (s_idDistance * s_workspaceInternalId), 0, parentId);
1121         }
1122         return QModelIndex();
1123     }
1124     if (child.internalId() < s_idDistance * (s_x11WindowId + 1)) {
1125         return createIndex(s_x11WindowId - 1, 0, s_x11WindowId);
1126     } else if (child.internalId() < s_idDistance * (s_x11UnmanagedId + 1)) {
1127         return createIndex(s_x11UnmanagedId - 1, 0, s_x11UnmanagedId);
1128     } else if (child.internalId() < s_idDistance * (s_waylandWindowId + 1)) {
1129         return createIndex(s_waylandWindowId - 1, 0, s_waylandWindowId);
1130     } else if (child.internalId() < s_idDistance * (s_workspaceInternalId + 1)) {
1131         return createIndex(s_workspaceInternalId - 1, 0, s_workspaceInternalId);
1132     }
1133     return QModelIndex();
1134 }
1135 
1136 QVariant DebugConsoleModel::propertyData(QObject *object, const QModelIndex &index, int role) const
1137 {
1138     const auto property = object->metaObject()->property(index.row());
1139     if (index.column() == 0) {
1140         return property.name();
1141     } else {
1142         const QVariant value = property.read(object);
1143         if (qstrcmp(property.name(), "windowType") == 0) {
1144             switch (value.toInt()) {
1145             case NET::Normal:
1146                 return QStringLiteral("NET::Normal");
1147             case NET::Desktop:
1148                 return QStringLiteral("NET::Desktop");
1149             case NET::Dock:
1150                 return QStringLiteral("NET::Dock");
1151             case NET::Toolbar:
1152                 return QStringLiteral("NET::Toolbar");
1153             case NET::Menu:
1154                 return QStringLiteral("NET::Menu");
1155             case NET::Dialog:
1156                 return QStringLiteral("NET::Dialog");
1157             case NET::Override:
1158                 return QStringLiteral("NET::Override");
1159             case NET::TopMenu:
1160                 return QStringLiteral("NET::TopMenu");
1161             case NET::Utility:
1162                 return QStringLiteral("NET::Utility");
1163             case NET::Splash:
1164                 return QStringLiteral("NET::Splash");
1165             case NET::DropdownMenu:
1166                 return QStringLiteral("NET::DropdownMenu");
1167             case NET::PopupMenu:
1168                 return QStringLiteral("NET::PopupMenu");
1169             case NET::Tooltip:
1170                 return QStringLiteral("NET::Tooltip");
1171             case NET::Notification:
1172                 return QStringLiteral("NET::Notification");
1173             case NET::ComboBox:
1174                 return QStringLiteral("NET::ComboBox");
1175             case NET::DNDIcon:
1176                 return QStringLiteral("NET::DNDIcon");
1177             case NET::OnScreenDisplay:
1178                 return QStringLiteral("NET::OnScreenDisplay");
1179             case NET::CriticalNotification:
1180                 return QStringLiteral("NET::CriticalNotification");
1181             case NET::AppletPopup:
1182                 return QStringLiteral("NET::AppletPopup");
1183             case NET::Unknown:
1184             default:
1185                 return QStringLiteral("NET::Unknown");
1186             }
1187         } else if (qstrcmp(property.name(), "layer") == 0) {
1188             return QMetaEnum::fromType<Layer>().valueToKey(value.value<Layer>());
1189         }
1190         return value;
1191     }
1192     return QVariant();
1193 }
1194 
1195 template<class T>
1196 QVariant DebugConsoleModel::windowData(const QModelIndex &index, int role, const QVector<T *> windows, const std::function<QString(T *)> &toString) const
1197 {
1198     if (index.row() >= windows.count()) {
1199         return QVariant();
1200     }
1201     auto c = windows.at(index.row());
1202     if (role == Qt::DisplayRole) {
1203         return toString(c);
1204     } else if (role == Qt::DecorationRole) {
1205         return c->icon();
1206     }
1207     return QVariant();
1208 }
1209 
1210 QVariant DebugConsoleModel::data(const QModelIndex &index, int role) const
1211 {
1212     if (!index.isValid()) {
1213         return QVariant();
1214     }
1215     if (!index.parent().isValid()) {
1216         // one of the top levels
1217         if (index.column() != 0 || role != Qt::DisplayRole) {
1218             return QVariant();
1219         }
1220         switch (index.internalId()) {
1221         case s_x11WindowId:
1222             return i18n("X11 Windows");
1223         case s_x11UnmanagedId:
1224             return i18n("X11 Unmanaged Windows");
1225         case s_waylandWindowId:
1226             return i18n("Wayland Windows");
1227         case s_workspaceInternalId:
1228             return i18n("Internal Windows");
1229         default:
1230             return QVariant();
1231         }
1232     }
1233     if (index.internalId() & s_propertyBitMask) {
1234         if (index.column() >= 2 || role != Qt::DisplayRole) {
1235             return QVariant();
1236         }
1237         if (Window *w = waylandWindow(index)) {
1238             return propertyData(w, index, role);
1239         } else if (InternalWindow *w = internalWindow(index)) {
1240             return propertyData(w, index, role);
1241         } else if (X11Window *w = x11Window(index)) {
1242             return propertyData(w, index, role);
1243         } else if (Unmanaged *u = unmanaged(index)) {
1244             return propertyData(u, index, role);
1245         }
1246     } else {
1247         if (index.column() != 0) {
1248             return QVariant();
1249         }
1250 
1251         auto generic = [](Window *c) -> QString {
1252             return c->caption() + QLatin1Char(' ') + QString::fromUtf8(c->metaObject()->className());
1253         };
1254         switch (index.parent().internalId()) {
1255         case s_x11WindowId:
1256             return windowData<X11Window>(index, role, m_x11Windows, [](X11Window *c) -> QString {
1257                 return QStringLiteral("0x%1: %2").arg(c->window(), 0, 16).arg(c->caption());
1258             });
1259         case s_x11UnmanagedId: {
1260             if (index.row() >= m_unmanageds.count()) {
1261                 return QVariant();
1262             }
1263             auto u = m_unmanageds.at(index.row());
1264             if (role == Qt::DisplayRole) {
1265                 return QStringLiteral("0x%1").arg(u->window(), 0, 16);
1266             }
1267             break;
1268         }
1269         case s_waylandWindowId:
1270             return windowData<WaylandWindow>(index, role, m_waylandWindows, generic);
1271         case s_workspaceInternalId:
1272             return windowData<InternalWindow>(index, role, m_internalWindows, generic);
1273         default:
1274             break;
1275         }
1276     }
1277 
1278     return QVariant();
1279 }
1280 
1281 template<class T>
1282 static T *windowForIndex(const QModelIndex &index, const QVector<T *> &windows, int id)
1283 {
1284     const qint32 row = (index.internalId() & s_windowBitMask) - (s_idDistance * id);
1285     if (row < 0 || row >= windows.count()) {
1286         return nullptr;
1287     }
1288     return windows.at(row);
1289 }
1290 
1291 WaylandWindow *DebugConsoleModel::waylandWindow(const QModelIndex &index) const
1292 {
1293     return windowForIndex(index, m_waylandWindows, s_waylandWindowId);
1294 }
1295 
1296 InternalWindow *DebugConsoleModel::internalWindow(const QModelIndex &index) const
1297 {
1298     return windowForIndex(index, m_internalWindows, s_workspaceInternalId);
1299 }
1300 
1301 X11Window *DebugConsoleModel::x11Window(const QModelIndex &index) const
1302 {
1303     return windowForIndex(index, m_x11Windows, s_x11WindowId);
1304 }
1305 
1306 Unmanaged *DebugConsoleModel::unmanaged(const QModelIndex &index) const
1307 {
1308     return windowForIndex(index, m_unmanageds, s_x11UnmanagedId);
1309 }
1310 
1311 /////////////////////////////////////// SurfaceTreeModel
1312 SurfaceTreeModel::SurfaceTreeModel(QObject *parent)
1313     : QAbstractItemModel(parent)
1314 {
1315     // TODO: it would be nice to not have to reset the model on each change
1316     auto reset = [this] {
1317         beginResetModel();
1318         endResetModel();
1319     };
1320     using namespace KWaylandServer;
1321 
1322     auto watchSubsurfaces = [this, reset](Window *c) {
1323         if (!c->surface()) {
1324             return;
1325         }
1326         auto monitor = new SubSurfaceMonitor(c->surface(), this);
1327         connect(monitor, &SubSurfaceMonitor::subSurfaceAdded, this, reset);
1328         connect(monitor, &SubSurfaceMonitor::subSurfaceRemoved, this, reset);
1329         connect(c, &QObject::destroyed, monitor, &QObject::deleteLater);
1330     };
1331 
1332     for (auto c : workspace()->allClientList()) {
1333         watchSubsurfaces(c);
1334     }
1335     connect(workspace(), &Workspace::windowAdded, this, [reset, watchSubsurfaces](Window *c) {
1336         watchSubsurfaces(c);
1337         reset();
1338     });
1339     connect(workspace(), &Workspace::windowRemoved, this, reset);
1340     connect(workspace(), &Workspace::unmanagedAdded, this, reset);
1341     connect(workspace(), &Workspace::unmanagedRemoved, this, reset);
1342 }
1343 
1344 SurfaceTreeModel::~SurfaceTreeModel() = default;
1345 
1346 int SurfaceTreeModel::columnCount(const QModelIndex &parent) const
1347 {
1348     return 1;
1349 }
1350 
1351 int SurfaceTreeModel::rowCount(const QModelIndex &parent) const
1352 {
1353     if (parent.isValid()) {
1354         using namespace KWaylandServer;
1355         if (SurfaceInterface *surface = static_cast<SurfaceInterface *>(parent.internalPointer())) {
1356             return surface->below().count() + surface->above().count();
1357         }
1358         return 0;
1359     }
1360     // toplevel are all windows
1361     return workspace()->allClientList().count() + workspace()->unmanagedList().count();
1362 }
1363 
1364 QModelIndex SurfaceTreeModel::index(int row, int column, const QModelIndex &parent) const
1365 {
1366     if (column != 0) {
1367         // invalid column
1368         return QModelIndex();
1369     }
1370 
1371     if (parent.isValid()) {
1372         using namespace KWaylandServer;
1373         if (SurfaceInterface *surface = static_cast<SurfaceInterface *>(parent.internalPointer())) {
1374             int reference = 0;
1375             const auto &below = surface->below();
1376             if (row < reference + below.count()) {
1377                 return createIndex(row, column, below.at(row - reference)->surface());
1378             }
1379             reference += below.count();
1380 
1381             const auto &above = surface->above();
1382             if (row < reference + above.count()) {
1383                 return createIndex(row, column, above.at(row - reference)->surface());
1384             }
1385         }
1386         return QModelIndex();
1387     }
1388     // a window
1389     const auto &allClients = workspace()->allClientList();
1390     if (row < allClients.count()) {
1391         // references a client
1392         return createIndex(row, column, allClients.at(row)->surface());
1393     }
1394     int reference = allClients.count();
1395     const auto &unmanaged = workspace()->unmanagedList();
1396     if (row < reference + unmanaged.count()) {
1397         return createIndex(row, column, unmanaged.at(row - reference)->surface());
1398     }
1399     reference += unmanaged.count();
1400     // not found
1401     return QModelIndex();
1402 }
1403 
1404 QModelIndex SurfaceTreeModel::parent(const QModelIndex &child) const
1405 {
1406     using namespace KWaylandServer;
1407     if (SurfaceInterface *surface = static_cast<SurfaceInterface *>(child.internalPointer())) {
1408         const auto &subsurface = surface->subSurface();
1409         if (!subsurface) {
1410             // doesn't reference a subsurface, this is a top-level window
1411             return QModelIndex();
1412         }
1413         SurfaceInterface *parent = subsurface->parentSurface();
1414         if (!parent) {
1415             // something is wrong
1416             return QModelIndex();
1417         }
1418         // is the parent a subsurface itself?
1419         if (parent->subSurface()) {
1420             auto grandParent = parent->subSurface()->parentSurface();
1421             if (!grandParent) {
1422                 // something is wrong
1423                 return QModelIndex();
1424             }
1425             int row = 0;
1426             const auto &below = grandParent->below();
1427             for (int i = 0; i < below.count(); i++) {
1428                 if (below.at(i) == parent->subSurface()) {
1429                     return createIndex(row + i, 0, parent);
1430                 }
1431             }
1432             row += below.count();
1433             const auto &above = grandParent->above();
1434             for (int i = 0; i < above.count(); i++) {
1435                 if (above.at(i) == parent->subSurface()) {
1436                     return createIndex(row + i, 0, parent);
1437                 }
1438             }
1439             return QModelIndex();
1440         }
1441         // not a subsurface, thus it's a true window
1442         int row = 0;
1443         const auto &allClients = workspace()->allClientList();
1444         for (; row < allClients.count(); row++) {
1445             if (allClients.at(row)->surface() == parent) {
1446                 return createIndex(row, 0, parent);
1447             }
1448         }
1449         row = allClients.count();
1450         const auto &unmanaged = workspace()->unmanagedList();
1451         for (int i = 0; i < unmanaged.count(); i++) {
1452             if (unmanaged.at(i)->surface() == parent) {
1453                 return createIndex(row + i, 0, parent);
1454             }
1455         }
1456         row += unmanaged.count();
1457     }
1458     return QModelIndex();
1459 }
1460 
1461 QVariant SurfaceTreeModel::data(const QModelIndex &index, int role) const
1462 {
1463     if (!index.isValid()) {
1464         return QVariant();
1465     }
1466     using namespace KWaylandServer;
1467     if (SurfaceInterface *surface = static_cast<SurfaceInterface *>(index.internalPointer())) {
1468         if (role == Qt::DisplayRole || role == Qt::ToolTipRole) {
1469             return QStringLiteral("%1 (%2) - %3").arg(surface->client()->executablePath()).arg(surface->client()->processId()).arg(surface->id());
1470         } else if (role == Qt::DecorationRole) {
1471             if (auto buffer = qobject_cast<KWaylandServer::ShmClientBuffer *>(surface->buffer())) {
1472                 return buffer->data().scaled(QSize(64, 64), Qt::KeepAspectRatio);
1473             }
1474         }
1475     }
1476     return QVariant();
1477 }
1478 
1479 InputDeviceModel::InputDeviceModel(QObject *parent)
1480     : QAbstractItemModel(parent)
1481     , m_devices(input()->devices())
1482 {
1483     for (auto it = m_devices.constBegin(); it != m_devices.constEnd(); ++it) {
1484         setupDeviceConnections(*it);
1485     }
1486 
1487     connect(input(), &InputRedirection::deviceAdded, this, [this](InputDevice *d) {
1488         beginInsertRows(QModelIndex(), m_devices.count(), m_devices.count());
1489         m_devices << d;
1490         setupDeviceConnections(d);
1491         endInsertRows();
1492     });
1493     connect(input(), &InputRedirection::deviceRemoved, this, [this](InputDevice *d) {
1494         const int index = m_devices.indexOf(d);
1495         if (index == -1) {
1496             return;
1497         }
1498         beginRemoveRows(QModelIndex(), index, index);
1499         m_devices.removeAt(index);
1500         endRemoveRows();
1501     });
1502 }
1503 
1504 InputDeviceModel::~InputDeviceModel() = default;
1505 
1506 int InputDeviceModel::columnCount(const QModelIndex &parent) const
1507 {
1508     return 2;
1509 }
1510 
1511 QVariant InputDeviceModel::data(const QModelIndex &index, int role) const
1512 {
1513     if (!index.isValid()) {
1514         return QVariant();
1515     }
1516     if (!index.parent().isValid() && index.column() == 0) {
1517         if (index.row() >= m_devices.count()) {
1518             return QVariant();
1519         }
1520         if (role == Qt::DisplayRole) {
1521             return m_devices.at(index.row())->name();
1522         }
1523     }
1524     if (index.parent().isValid()) {
1525         if (role == Qt::DisplayRole) {
1526             const auto device = m_devices.at(index.parent().row());
1527             const auto property = device->metaObject()->property(index.row());
1528             if (index.column() == 0) {
1529                 return property.name();
1530             } else if (index.column() == 1) {
1531                 return device->property(property.name());
1532             }
1533         }
1534     }
1535     return QVariant();
1536 }
1537 
1538 QModelIndex InputDeviceModel::index(int row, int column, const QModelIndex &parent) const
1539 {
1540     if (column >= 2) {
1541         return QModelIndex();
1542     }
1543     if (parent.isValid()) {
1544         if (parent.internalId() & s_propertyBitMask) {
1545             return QModelIndex();
1546         }
1547         if (row >= m_devices.at(parent.row())->metaObject()->propertyCount()) {
1548             return QModelIndex();
1549         }
1550         return createIndex(row, column, quint32(row + 1) << 16 | parent.internalId());
1551     }
1552     if (row >= m_devices.count()) {
1553         return QModelIndex();
1554     }
1555     return createIndex(row, column, row + 1);
1556 }
1557 
1558 int InputDeviceModel::rowCount(const QModelIndex &parent) const
1559 {
1560     if (!parent.isValid()) {
1561         return m_devices.count();
1562     }
1563     if (parent.internalId() & s_propertyBitMask) {
1564         return 0;
1565     }
1566 
1567     return m_devices.at(parent.row())->metaObject()->propertyCount();
1568 }
1569 
1570 QModelIndex InputDeviceModel::parent(const QModelIndex &child) const
1571 {
1572     if (child.internalId() & s_propertyBitMask) {
1573         const quintptr parentId = child.internalId() & s_windowBitMask;
1574         return createIndex(parentId - 1, 0, parentId);
1575     }
1576     return QModelIndex();
1577 }
1578 
1579 void InputDeviceModel::slotPropertyChanged()
1580 {
1581     const auto device = static_cast<InputDevice *>(sender());
1582 
1583     for (int i = 0; i < device->metaObject()->propertyCount(); ++i) {
1584         const QMetaProperty metaProperty = device->metaObject()->property(i);
1585         if (metaProperty.notifySignalIndex() == senderSignalIndex()) {
1586             const QModelIndex parent = index(m_devices.indexOf(device), 0, QModelIndex());
1587             const QModelIndex child = index(i, 1, parent);
1588             Q_EMIT dataChanged(child, child, QVector<int>{Qt::DisplayRole});
1589         }
1590     }
1591 }
1592 
1593 void InputDeviceModel::setupDeviceConnections(InputDevice *device)
1594 {
1595     QMetaMethod handler = metaObject()->method(metaObject()->indexOfMethod("slotPropertyChanged()"));
1596     for (int i = 0; i < device->metaObject()->propertyCount(); ++i) {
1597         const QMetaProperty metaProperty = device->metaObject()->property(i);
1598         if (metaProperty.hasNotifySignal()) {
1599             connect(device, metaProperty.notifySignal(), this, handler);
1600         }
1601     }
1602 }
1603 
1604 QModelIndex DataSourceModel::index(int row, int column, const QModelIndex &parent) const
1605 {
1606     if (!m_source || parent.isValid() || column >= 2 || row >= m_source->mimeTypes().size()) {
1607         return QModelIndex();
1608     }
1609     return createIndex(row, column, nullptr);
1610 }
1611 
1612 QModelIndex DataSourceModel::parent(const QModelIndex &child) const
1613 {
1614     return QModelIndex();
1615 }
1616 
1617 int DataSourceModel::rowCount(const QModelIndex &parent) const
1618 {
1619     if (!parent.isValid()) {
1620         return m_source ? m_source->mimeTypes().count() : 0;
1621     }
1622     return 0;
1623 }
1624 
1625 QVariant DataSourceModel::headerData(int section, Qt::Orientation orientation, int role) const
1626 {
1627     if (role != Qt::DisplayRole || orientation != Qt::Horizontal || section >= 2) {
1628         return QVariant();
1629     }
1630     return section == 0 ? QStringLiteral("Mime type") : QStringLiteral("Content");
1631 }
1632 
1633 QVariant DataSourceModel::data(const QModelIndex &index, int role) const
1634 {
1635     if (!checkIndex(index, CheckIndexOption::ParentIsInvalid | CheckIndexOption::IndexIsValid)) {
1636         return QVariant();
1637     }
1638     const QString mimeType = m_source->mimeTypes().at(index.row());
1639     ;
1640     if (index.column() == 0 && role == Qt::DisplayRole) {
1641         return mimeType;
1642     } else if (index.column() == 1 && index.row() < m_data.count()) {
1643         const QByteArray &data = m_data.at(index.row());
1644         if (mimeType.contains(QLatin1String("image"))) {
1645             if (role == Qt::DecorationRole) {
1646                 return QImage::fromData(data);
1647             }
1648         } else if (role == Qt::DisplayRole) {
1649             return data;
1650         }
1651     }
1652     return QVariant();
1653 }
1654 
1655 static QByteArray readData(int fd)
1656 {
1657     pollfd pfd;
1658     pfd.fd = fd;
1659     pfd.events = POLLIN;
1660     FileDescriptor closeFd{fd};
1661     QByteArray data;
1662     while (true) {
1663         const int ready = poll(&pfd, 1, 1000);
1664         if (ready < 0) {
1665             if (errno != EINTR) {
1666                 return QByteArrayLiteral("poll() failed: ") + strerror(errno);
1667             }
1668         } else if (ready == 0) {
1669             return QByteArrayLiteral("timeout reading from pipe");
1670         } else {
1671             char buf[4096];
1672             int n = read(fd, buf, sizeof buf);
1673 
1674             if (n < 0) {
1675                 return QByteArrayLiteral("read failed: ") + strerror(errno);
1676             } else if (n == 0) {
1677                 return data;
1678             } else if (n > 0) {
1679                 data.append(buf, n);
1680             }
1681         }
1682     }
1683 }
1684 
1685 void DataSourceModel::setSource(KWaylandServer::AbstractDataSource *source)
1686 {
1687     beginResetModel();
1688     m_source = source;
1689     m_data.clear();
1690     if (source) {
1691         m_data.resize(m_source->mimeTypes().size());
1692         for (auto type = m_source->mimeTypes().cbegin(); type != m_source->mimeTypes().cend(); ++type) {
1693             int pipeFds[2];
1694             if (pipe2(pipeFds, O_CLOEXEC) != 0) {
1695                 continue;
1696             }
1697             source->requestData(*type, pipeFds[1]);
1698             QFuture<QByteArray> data = QtConcurrent::run(readData, pipeFds[0]);
1699             auto watcher = new QFutureWatcher<QByteArray>(this);
1700             watcher->setFuture(data);
1701             const int index = type - m_source->mimeTypes().cbegin();
1702             connect(watcher, &QFutureWatcher<QByteArray>::finished, this, [this, watcher, index, source = QPointer(source)] {
1703                 watcher->deleteLater();
1704                 if (source && source == m_source) {
1705                     m_data[index] = watcher->result();
1706                     Q_EMIT dataChanged(this->index(index, 1), this->index(index, 1), {Qt::DecorationRole | Qt::DisplayRole});
1707                 }
1708             });
1709         }
1710     }
1711     endResetModel();
1712 }
1713 }