1 /*
2     KWin - the KDE window manager
3     This file is part of the KDE project.
4 
5     SPDX-FileCopyrightText: 2016 Martin Gräßlin <mgraesslin@kde.org>
6 
7     SPDX-License-Identifier: GPL-2.0-or-later
8 */
9 #include "debug_console.h"
10 #include "composite.h"
11 #include "input_event.h"
12 #include "internal_client.h"
13 #include "keyboard_input.h"
14 #include "libinput/connection.h"
15 #include "libinput/device.h"
16 #include "main.h"
17 #include "scene.h"
18 #include "subsurfacemonitor.h"
19 #include "unmanaged.h"
20 #include "wayland_server.h"
21 #include "waylandclient.h"
22 #include "workspace.h"
23 #include "x11client.h"
24 #include <kwinglplatform.h>
25 #include <kwinglutils.h>
26 
27 #include "ui_debug_console.h"
28 
29 // KWayland
30 #include <KWaylandServer/abstract_data_source.h>
31 #include <KWaylandServer/clientconnection.h>
32 #include <KWaylandServer/datacontrolsource_v1_interface.h>
33 #include <KWaylandServer/datasource_interface.h>
34 #include <KWaylandServer/display.h>
35 #include <KWaylandServer/primaryselectionsource_v1_interface.h>
36 #include <KWaylandServer/seat_interface.h>
37 #include <KWaylandServer/shmclientbuffer.h>
38 #include <KWaylandServer/subcompositor_interface.h>
39 #include <KWaylandServer/surface_interface.h>
40 // frameworks
41 #include <KLocalizedString>
42 #include <NETWM>
43 // Qt
44 #include <QFutureWatcher>
45 #include <QMetaProperty>
46 #include <QMetaType>
47 #include <QMouseEvent>
48 #include <QScopeGuard>
49 #include <QtConcurrentRun>
50 
51 #include <wayland-server-core.h>
52 
53 // xkb
54 #include <xkbcommon/xkbcommon.h>
55 
56 #include <fcntl.h>
57 #include <functional>
58 #include <sys/poll.h>
59 #include <unistd.h>
60 
61 namespace KWin
62 {
63 
64 
tableHeaderRow(const QString & title)65 static QString tableHeaderRow(const QString &title)
66 {
67     return QStringLiteral("<tr><th colspan=\"2\">%1</th></tr>").arg(title);
68 }
69 
70 template<typename T>
71 static
tableRow(const QString & title,const T & argument)72 QString tableRow(const QString &title, const T &argument)
73 {
74     return QStringLiteral("<tr><td>%1</td><td>%2</td></tr>").arg(title).arg(argument);
75 }
76 
timestampRow(quint32 timestamp)77 static QString timestampRow(quint32 timestamp)
78 {
79     return tableRow(i18n("Timestamp"), timestamp);
80 }
81 
timestampRowUsec(quint64 timestamp)82 static QString timestampRowUsec(quint64 timestamp)
83 {
84     return tableRow(i18n("Timestamp (µsec)"), timestamp);
85 }
86 
buttonToString(Qt::MouseButton button)87 static QString buttonToString(Qt::MouseButton button)
88 {
89     switch (button) {
90         case Qt::LeftButton:
91             return i18nc("A mouse button", "Left");
92         case Qt::RightButton:
93             return i18nc("A mouse button", "Right");
94         case Qt::MiddleButton:
95             return i18nc("A mouse button", "Middle");
96         case Qt::BackButton:
97             return i18nc("A mouse button", "Back");
98         case Qt::ForwardButton:
99             return i18nc("A mouse button", "Forward");
100         case Qt::TaskButton:
101             return i18nc("A mouse button", "Task");
102         case Qt::ExtraButton4:
103             return i18nc("A mouse button", "Extra Button 4");
104         case Qt::ExtraButton5:
105             return i18nc("A mouse button", "Extra Button 5");
106         case Qt::ExtraButton6:
107             return i18nc("A mouse button", "Extra Button 6");
108         case Qt::ExtraButton7:
109             return i18nc("A mouse button", "Extra Button 7");
110         case Qt::ExtraButton8:
111             return i18nc("A mouse button", "Extra Button 8");
112         case Qt::ExtraButton9:
113             return i18nc("A mouse button", "Extra Button 9");
114         case Qt::ExtraButton10:
115             return i18nc("A mouse button", "Extra Button 10");
116         case Qt::ExtraButton11:
117             return i18nc("A mouse button", "Extra Button 11");
118         case Qt::ExtraButton12:
119             return i18nc("A mouse button", "Extra Button 12");
120         case Qt::ExtraButton13:
121             return i18nc("A mouse button", "Extra Button 13");
122         case Qt::ExtraButton14:
123             return i18nc("A mouse button", "Extra Button 14");
124         case Qt::ExtraButton15:
125             return i18nc("A mouse button", "Extra Button 15");
126         case Qt::ExtraButton16:
127             return i18nc("A mouse button", "Extra Button 16");
128         case Qt::ExtraButton17:
129             return i18nc("A mouse button", "Extra Button 17");
130         case Qt::ExtraButton18:
131             return i18nc("A mouse button", "Extra Button 18");
132         case Qt::ExtraButton19:
133             return i18nc("A mouse button", "Extra Button 19");
134         case Qt::ExtraButton20:
135             return i18nc("A mouse button", "Extra Button 20");
136         case Qt::ExtraButton21:
137             return i18nc("A mouse button", "Extra Button 21");
138         case Qt::ExtraButton22:
139             return i18nc("A mouse button", "Extra Button 22");
140         case Qt::ExtraButton23:
141             return i18nc("A mouse button", "Extra Button 23");
142         case Qt::ExtraButton24:
143             return i18nc("A mouse button", "Extra Button 24");
144     default:
145         return QString();
146     }
147 }
148 
deviceRow(LibInput::Device * device)149 static QString deviceRow(LibInput::Device *device)
150 {
151     if (!device) {
152         return tableRow(i18n("Input Device"), i18nc("The input device of the event is not known", "Unknown"));
153     }
154     return tableRow(i18n("Input Device"), QStringLiteral("%1 (%2)").arg(device->name(), device->sysName()));
155 }
156 
buttonsToString(Qt::MouseButtons buttons)157 static QString buttonsToString(Qt::MouseButtons buttons)
158 {
159     QString ret;
160     for (uint i = 1; i < Qt::ExtraButton24; i = i << 1) {
161         if (buttons & i) {
162             ret.append(buttonToString(Qt::MouseButton(uint(buttons) & i)));
163             ret.append(QStringLiteral(" "));
164         }
165     };
166     return ret.trimmed();
167 }
168 
169 static const QString s_hr = QStringLiteral("<hr/>");
170 static const QString s_tableStart = QStringLiteral("<table>");
171 static const QString s_tableEnd = QStringLiteral("</table>");
172 
DebugConsoleFilter(QTextEdit * textEdit)173 DebugConsoleFilter::DebugConsoleFilter(QTextEdit *textEdit)
174     : InputEventSpy()
175     , m_textEdit(textEdit)
176 {
177 }
178 
179 DebugConsoleFilter::~DebugConsoleFilter() = default;
180 
pointerEvent(MouseEvent * event)181 void DebugConsoleFilter::pointerEvent(MouseEvent *event)
182 {
183     QString text = s_hr;
184     const QString timestamp = timestampRow(event->timestamp());
185 
186     text.append(s_tableStart);
187     switch (event->type()) {
188     case QEvent::MouseMove: {
189         text.append(tableHeaderRow(i18nc("A mouse pointer motion event", "Pointer Motion")));
190         text.append(deviceRow(event->device()));
191         text.append(timestamp);
192         if (event->timestampMicroseconds() != 0) {
193             text.append(timestampRowUsec(event->timestampMicroseconds()));
194         }
195         if (event->delta() != QSizeF()) {
196             text.append(tableRow(i18nc("The relative mouse movement", "Delta"),
197                                  QStringLiteral("%1/%2").arg(event->delta().width()).arg(event->delta().height())));
198         }
199         if (event->deltaUnaccelerated() != QSizeF()) {
200             text.append(tableRow(i18nc("The relative mouse movement", "Delta (not accelerated)"),
201                                  QStringLiteral("%1/%2").arg(event->deltaUnaccelerated().width()).arg(event->deltaUnaccelerated().height())));
202         }
203         text.append(tableRow(i18nc("The global mouse pointer position", "Global Position"), QStringLiteral("%1/%2").arg(event->pos().x()).arg(event->pos().y())));
204         break;
205     }
206     case QEvent::MouseButtonPress:
207         text.append(tableHeaderRow(i18nc("A mouse pointer button press event", "Pointer Button Press")));
208         text.append(deviceRow(event->device()));
209         text.append(timestamp);
210         text.append(tableRow(i18nc("A button in a mouse press/release event", "Button"), buttonToString(event->button())));
211         text.append(tableRow(i18nc("A button in a mouse press/release event",  "Native Button code"), event->nativeButton()));
212         text.append(tableRow(i18nc("All currently pressed buttons in a mouse press/release event", "Pressed Buttons"), buttonsToString(event->buttons())));
213         break;
214     case QEvent::MouseButtonRelease:
215         text.append(tableHeaderRow(i18nc("A mouse pointer button release event", "Pointer Button Release")));
216         text.append(deviceRow(event->device()));
217         text.append(timestamp);
218         text.append(tableRow(i18nc("A button in a mouse press/release event", "Button"), buttonToString(event->button())));
219         text.append(tableRow(i18nc("A button in a mouse press/release event", "Native Button code"), event->nativeButton()));
220         text.append(tableRow(i18nc("All currently pressed buttons in a mouse press/release event", "Pressed Buttons"), buttonsToString(event->buttons())));
221         break;
222     default:
223         break;
224     }
225     text.append(s_tableEnd);
226 
227     m_textEdit->insertHtml(text);
228     m_textEdit->ensureCursorVisible();
229 }
230 
wheelEvent(WheelEvent * event)231 void DebugConsoleFilter::wheelEvent(WheelEvent *event)
232 {
233     QString text = s_hr;
234     text.append(s_tableStart);
235     text.append(tableHeaderRow(i18nc("A mouse pointer axis (wheel) event", "Pointer Axis")));
236     text.append(deviceRow(event->device()));
237     text.append(timestampRow(event->timestamp()));
238     const Qt::Orientation orientation = event->angleDelta().x() == 0 ? Qt::Vertical : Qt::Horizontal;
239     text.append(tableRow(i18nc("The orientation of a pointer axis event", "Orientation"),
240                          orientation == Qt::Horizontal ? i18nc("An orientation of a pointer axis event", "Horizontal")
241                                                        : i18nc("An orientation of a pointer axis event", "Vertical")));
242     text.append(tableRow(i18nc("The angle delta of a pointer axis event", "Delta"),
243                          orientation == Qt::Horizontal ? event->angleDelta().x() : event->angleDelta().y()));
244     text.append(s_tableEnd);
245 
246     m_textEdit->insertHtml(text);
247     m_textEdit->ensureCursorVisible();
248 }
249 
keyEvent(KeyEvent * event)250 void DebugConsoleFilter::keyEvent(KeyEvent *event)
251 {
252     QString text = s_hr;
253     text.append(s_tableStart);
254 
255     switch (event->type()) {
256     case QEvent::KeyPress:
257         text.append(tableHeaderRow(i18nc("A key press event", "Key Press")));
258         break;
259     case QEvent::KeyRelease:
260         text.append(tableHeaderRow(i18nc("A key release event", "Key Release")));
261         break;
262     default:
263         break;
264     }
265     text.append(deviceRow(event->device()));
266     auto modifiersToString = [event] {
267         QString ret;
268         if (event->modifiers().testFlag(Qt::ShiftModifier)) {
269             ret.append(i18nc("A keyboard modifier", "Shift"));
270             ret.append(QStringLiteral(" "));
271         }
272         if (event->modifiers().testFlag(Qt::ControlModifier)) {
273             ret.append(i18nc("A keyboard modifier", "Control"));
274             ret.append(QStringLiteral(" "));
275         }
276         if (event->modifiers().testFlag(Qt::AltModifier)) {
277             ret.append(i18nc("A keyboard modifier", "Alt"));
278             ret.append(QStringLiteral(" "));
279         }
280         if (event->modifiers().testFlag(Qt::MetaModifier)) {
281             ret.append(i18nc("A keyboard modifier", "Meta"));
282             ret.append(QStringLiteral(" "));
283         }
284         if (event->modifiers().testFlag(Qt::KeypadModifier)) {
285             ret.append(i18nc("A keyboard modifier", "Keypad"));
286             ret.append(QStringLiteral(" "));
287         }
288         if (event->modifiers().testFlag(Qt::GroupSwitchModifier)) {
289             ret.append(i18nc("A keyboard modifier", "Group-switch"));
290             ret.append(QStringLiteral(" "));
291         }
292         return ret;
293     };
294     text.append(timestampRow(event->timestamp()));
295     text.append(tableRow(i18nc("Whether the event is an automatic key repeat", "Repeat"), event->isAutoRepeat()));
296 
297     const auto keyMetaObject = Qt::qt_getEnumMetaObject(Qt::Key());
298     const auto enumerator = keyMetaObject->enumerator(keyMetaObject->indexOfEnumerator("Key"));
299     text.append(tableRow(i18nc("The code as read from the input device", "Scan code"), event->nativeScanCode()));
300     text.append(tableRow(i18nc("Key according to Qt", "Qt::Key code"),
301                          enumerator.valueToKey(event->key())));
302     text.append(tableRow(i18nc("The translated code to an Xkb symbol", "Xkb symbol"), event->nativeVirtualKey()));
303     text.append(tableRow(i18nc("The translated code interpreted as text", "Utf8"), event->text()));
304     text.append(tableRow(i18nc("The currently active modifiers", "Modifiers"), modifiersToString()));
305 
306     text.append(s_tableEnd);
307 
308     m_textEdit->insertHtml(text);
309     m_textEdit->ensureCursorVisible();
310 }
311 
touchDown(qint32 id,const QPointF & pos,quint32 time)312 void DebugConsoleFilter::touchDown(qint32 id, const QPointF &pos, quint32 time)
313 {
314     QString text = s_hr;
315     text.append(s_tableStart);
316     text.append(tableHeaderRow(i18nc("A touch down event", "Touch down")));
317     text.append(timestampRow(time));
318     text.append(tableRow(i18nc("The id of the touch point in the touch event", "Point identifier"), id));
319     text.append(tableRow(i18nc("The global position of the touch point", "Global position"),
320                          QStringLiteral("%1/%2").arg(pos.x()).arg(pos.y())));
321     text.append(s_tableEnd);
322 
323     m_textEdit->insertHtml(text);
324     m_textEdit->ensureCursorVisible();
325 }
326 
touchMotion(qint32 id,const QPointF & pos,quint32 time)327 void DebugConsoleFilter::touchMotion(qint32 id, const QPointF &pos, quint32 time)
328 {
329     QString text = s_hr;
330     text.append(s_tableStart);
331     text.append(tableHeaderRow(i18nc("A touch motion event", "Touch Motion")));
332     text.append(timestampRow(time));
333     text.append(tableRow(i18nc("The id of the touch point in the touch event", "Point identifier"), id));
334     text.append(tableRow(i18nc("The global position of the touch point", "Global position"),
335                          QStringLiteral("%1/%2").arg(pos.x()).arg(pos.y())));
336     text.append(s_tableEnd);
337 
338     m_textEdit->insertHtml(text);
339     m_textEdit->ensureCursorVisible();
340 }
341 
touchUp(qint32 id,quint32 time)342 void DebugConsoleFilter::touchUp(qint32 id, quint32 time)
343 {
344     QString text = s_hr;
345     text.append(s_tableStart);
346     text.append(tableHeaderRow(i18nc("A touch up event", "Touch Up")));
347     text.append(timestampRow(time));
348     text.append(tableRow(i18nc("The id of the touch point in the touch event", "Point identifier"), id));
349     text.append(s_tableEnd);
350 
351     m_textEdit->insertHtml(text);
352     m_textEdit->ensureCursorVisible();
353 }
354 
pinchGestureBegin(int fingerCount,quint32 time)355 void DebugConsoleFilter::pinchGestureBegin(int fingerCount, quint32 time)
356 {
357     QString text = s_hr;
358     text.append(s_tableStart);
359     text.append(tableHeaderRow(i18nc("A pinch gesture is started", "Pinch start")));
360     text.append(timestampRow(time));
361     text.append(tableRow(i18nc("Number of fingers in this pinch gesture", "Finger count"), fingerCount));
362     text.append(s_tableEnd);
363 
364     m_textEdit->insertHtml(text);
365     m_textEdit->ensureCursorVisible();
366 }
367 
pinchGestureUpdate(qreal scale,qreal angleDelta,const QSizeF & delta,quint32 time)368 void DebugConsoleFilter::pinchGestureUpdate(qreal scale, qreal angleDelta, const QSizeF &delta, quint32 time)
369 {
370     QString text = s_hr;
371     text.append(s_tableStart);
372     text.append(tableHeaderRow(i18nc("A pinch gesture is updated", "Pinch update")));
373     text.append(timestampRow(time));
374     text.append(tableRow(i18nc("Current scale in pinch gesture", "Scale"), scale));
375     text.append(tableRow(i18nc("Current angle in pinch gesture", "Angle delta"), angleDelta));
376     text.append(tableRow(i18nc("Current delta in pinch gesture", "Delta x"), delta.width()));
377     text.append(tableRow(i18nc("Current delta in pinch gesture", "Delta y"), delta.height()));
378     text.append(s_tableEnd);
379 
380     m_textEdit->insertHtml(text);
381     m_textEdit->ensureCursorVisible();
382 }
383 
pinchGestureEnd(quint32 time)384 void DebugConsoleFilter::pinchGestureEnd(quint32 time)
385 {
386     QString text = s_hr;
387     text.append(s_tableStart);
388     text.append(tableHeaderRow(i18nc("A pinch gesture ended", "Pinch end")));
389     text.append(timestampRow(time));
390     text.append(s_tableEnd);
391 
392     m_textEdit->insertHtml(text);
393     m_textEdit->ensureCursorVisible();
394 }
395 
pinchGestureCancelled(quint32 time)396 void DebugConsoleFilter::pinchGestureCancelled(quint32 time)
397 {
398     QString text = s_hr;
399     text.append(s_tableStart);
400     text.append(tableHeaderRow(i18nc("A pinch gesture got cancelled", "Pinch cancelled")));
401     text.append(timestampRow(time));
402     text.append(s_tableEnd);
403 
404     m_textEdit->insertHtml(text);
405     m_textEdit->ensureCursorVisible();
406 }
407 
swipeGestureBegin(int fingerCount,quint32 time)408 void DebugConsoleFilter::swipeGestureBegin(int fingerCount, quint32 time)
409 {
410     QString text = s_hr;
411     text.append(s_tableStart);
412     text.append(tableHeaderRow(i18nc("A swipe gesture is started", "Swipe start")));
413     text.append(timestampRow(time));
414     text.append(tableRow(i18nc("Number of fingers in this swipe gesture", "Finger count"), fingerCount));
415     text.append(s_tableEnd);
416 
417     m_textEdit->insertHtml(text);
418     m_textEdit->ensureCursorVisible();
419 }
420 
swipeGestureUpdate(const QSizeF & delta,quint32 time)421 void DebugConsoleFilter::swipeGestureUpdate(const QSizeF &delta, quint32 time)
422 {
423     QString text = s_hr;
424     text.append(s_tableStart);
425     text.append(tableHeaderRow(i18nc("A swipe gesture is updated", "Swipe update")));
426     text.append(timestampRow(time));
427     text.append(tableRow(i18nc("Current delta in swipe gesture", "Delta x"), delta.width()));
428     text.append(tableRow(i18nc("Current delta in swipe gesture", "Delta y"), delta.height()));
429     text.append(s_tableEnd);
430 
431     m_textEdit->insertHtml(text);
432     m_textEdit->ensureCursorVisible();
433 }
434 
swipeGestureEnd(quint32 time)435 void DebugConsoleFilter::swipeGestureEnd(quint32 time)
436 {
437     QString text = s_hr;
438     text.append(s_tableStart);
439     text.append(tableHeaderRow(i18nc("A swipe gesture ended", "Swipe end")));
440     text.append(timestampRow(time));
441     text.append(s_tableEnd);
442 
443     m_textEdit->insertHtml(text);
444     m_textEdit->ensureCursorVisible();
445 }
446 
swipeGestureCancelled(quint32 time)447 void DebugConsoleFilter::swipeGestureCancelled(quint32 time)
448 {
449     QString text = s_hr;
450     text.append(s_tableStart);
451     text.append(tableHeaderRow(i18nc("A swipe gesture got cancelled", "Swipe cancelled")));
452     text.append(timestampRow(time));
453     text.append(s_tableEnd);
454 
455     m_textEdit->insertHtml(text);
456     m_textEdit->ensureCursorVisible();
457 }
458 
switchEvent(SwitchEvent * event)459 void DebugConsoleFilter::switchEvent(SwitchEvent *event)
460 {
461     QString text = s_hr;
462     text.append(s_tableStart);
463     text.append(tableHeaderRow(i18nc("A hardware switch (e.g. notebook lid) got toggled", "Switch toggled")));
464     text.append(timestampRow(event->timestamp()));
465     if (event->timestampMicroseconds() != 0) {
466         text.append(timestampRowUsec(event->timestampMicroseconds()));
467     }
468     text.append(deviceRow(event->device()));
469     QString switchName;
470     if (event->device()->isLidSwitch()) {
471         switchName = i18nc("Name of a hardware switch", "Notebook lid");
472     } else if (event->device()->isTabletModeSwitch()) {
473         switchName = i18nc("Name of a hardware switch", "Tablet mode");
474     }
475     text.append(tableRow(i18nc("A hardware switch", "Switch"), switchName));
476     QString switchState;
477     switch (event->state()) {
478     case SwitchEvent::State::Off:
479         switchState = i18nc("The hardware switch got turned off", "Off");
480         break;
481     case SwitchEvent::State::On:
482         switchState = i18nc("The hardware switch got turned on", "On");
483         break;
484     default:
485         Q_UNREACHABLE();
486     }
487     text.append(tableRow(i18nc("State of a hardware switch (on/off)", "State"), switchState));
488     text.append(s_tableEnd);
489 
490     m_textEdit->insertHtml(text);
491     m_textEdit->ensureCursorVisible();
492 }
493 
tabletToolEvent(TabletEvent * event)494 void DebugConsoleFilter::tabletToolEvent(TabletEvent *event)
495 {
496     QString typeString;
497     {
498         QDebug d(&typeString);
499         d << event->type();
500     }
501 
502     QString text = s_hr + s_tableStart + tableHeaderRow(i18n("Tablet Tool"))
503                  + tableRow(i18n("EventType"), typeString)
504                  + tableRow(i18n("Position"),
505                             QStringLiteral("%1,%2").arg(event->pos().x()).arg(event->pos().y()))
506                  + tableRow(i18n("Tilt"),
507                             QStringLiteral("%1,%2").arg(event->xTilt()).arg(event->yTilt()))
508                  + tableRow(i18n("Rotation"), QString::number(event->rotation()))
509                  + tableRow(i18n("Pressure"), QString::number(event->pressure()))
510                  + tableRow(i18n("Buttons"), QString::number(event->buttons()))
511                  + tableRow(i18n("Modifiers"), QString::number(event->modifiers()))
512                  + s_tableEnd;
513 
514     m_textEdit->insertHtml(text);
515     m_textEdit->ensureCursorVisible();
516 }
517 
tabletToolButtonEvent(uint button,bool pressed,const TabletToolId & tabletToolId)518 void DebugConsoleFilter::tabletToolButtonEvent(uint button, bool pressed, const TabletToolId &tabletToolId)
519 {
520     QString text = s_hr + s_tableStart + tableHeaderRow(i18n("Tablet Tool Button"))
521                  + tableRow(i18n("Button"), button)
522                  + tableRow(i18n("Pressed"), pressed)
523                  + tableRow(i18n("Tablet"), qHash(tabletToolId.m_deviceGroupData))
524                  + s_tableEnd;
525 
526     m_textEdit->insertHtml(text);
527     m_textEdit->ensureCursorVisible();
528 }
529 
tabletPadButtonEvent(uint button,bool pressed,const TabletPadId & tabletPadId)530 void DebugConsoleFilter::tabletPadButtonEvent(uint button, bool pressed, const TabletPadId &tabletPadId)
531 {
532     QString text = s_hr + s_tableStart
533                  + tableHeaderRow(i18n("Tablet Pad Button"))
534                  + tableRow(i18n("Button"), button)
535                  + tableRow(i18n("Pressed"), pressed)
536                  + tableRow(i18n("Tablet"), qHash(tabletPadId.data))
537                  + s_tableEnd;
538 
539     m_textEdit->insertHtml(text);
540     m_textEdit->ensureCursorVisible();
541 }
542 
tabletPadStripEvent(int number,int position,bool isFinger,const TabletPadId & tabletPadId)543 void DebugConsoleFilter::tabletPadStripEvent(int number, int position, bool isFinger, const TabletPadId &tabletPadId)
544 {
545     QString text = s_hr + s_tableStart + tableHeaderRow(i18n("Tablet Pad Strip"))
546                  + tableRow(i18n("Number"), number)
547                  + tableRow(i18n("Position"), position)
548                  + tableRow(i18n("isFinger"), isFinger)
549                  + tableRow(i18n("Tablet"), qHash(tabletPadId.data))
550                  + s_tableEnd;
551 
552     m_textEdit->insertHtml(text);
553     m_textEdit->ensureCursorVisible();
554 }
555 
tabletPadRingEvent(int number,int position,bool isFinger,const TabletPadId & tabletPadId)556 void DebugConsoleFilter::tabletPadRingEvent(int number, int position, bool isFinger, const TabletPadId &tabletPadId)
557 {
558     QString text = s_hr + s_tableStart + tableHeaderRow(i18n("Tablet Pad Ring"))
559                  + tableRow(i18n("Number"), number)
560                  + tableRow(i18n("Position"), position)
561                  + tableRow(i18n("isFinger"), isFinger)
562                  + tableRow(i18n("Tablet"), qHash(tabletPadId.data))
563                  + s_tableEnd;
564 
565     m_textEdit->insertHtml(text);
566     m_textEdit->ensureCursorVisible();
567 }
568 
sourceString(const KWaylandServer::AbstractDataSource * const source)569 static QString sourceString(const KWaylandServer::AbstractDataSource *const source)
570 {
571     if (!source) {
572         return QString();
573     }
574 
575     if (!source->client()) {
576         return QStringLiteral("XWayland source");
577     }
578 
579     const QString executable = waylandServer()->display()->getConnection(source->client())->executablePath();
580 
581     if (auto dataSource = qobject_cast<const KWaylandServer::DataSourceInterface *const>(source)) {
582         return QStringLiteral("wl_data_source@%1 of %2").arg(wl_resource_get_id(dataSource->resource())).arg(executable);
583     } else if (qobject_cast<const KWaylandServer::PrimarySelectionSourceV1Interface *const>(source)) {
584         return QStringLiteral("zwp_primary_selection_source_v1 of %2").arg(executable);
585     } else if (qobject_cast<const KWaylandServer::DataControlSourceV1Interface *const>(source)) {
586         return QStringLiteral("data control by %1").arg(executable);
587     }
588     return QStringLiteral("unknown source of").arg(executable);
589 }
590 
DebugConsole()591 DebugConsole::DebugConsole()
592     : QWidget()
593     , m_ui(new Ui::DebugConsole)
594 {
595     setAttribute(Qt::WA_ShowWithoutActivating);
596     m_ui->setupUi(this);
597     m_ui->windowsView->setItemDelegate(new DebugConsoleDelegate(this));
598     m_ui->windowsView->setModel(new DebugConsoleModel(this));
599     m_ui->surfacesView->setModel(new SurfaceTreeModel(this));
600     m_ui->clipboardContent->setModel(new DataSourceModel(this));
601     m_ui->primaryContent->setModel(new DataSourceModel(this));
602     if (kwinApp()->usesLibinput()) {
603         m_ui->inputDevicesView->setModel(new InputDeviceModel(this));
604         m_ui->inputDevicesView->setItemDelegate(new DebugConsoleDelegate(this));
605     }
606     m_ui->quitButton->setIcon(QIcon::fromTheme(QStringLiteral("application-exit")));
607     m_ui->tabWidget->setTabIcon(0, QIcon::fromTheme(QStringLiteral("view-list-tree")));
608     m_ui->tabWidget->setTabIcon(1, QIcon::fromTheme(QStringLiteral("view-list-tree")));
609 
610     if (kwinApp()->operationMode() == Application::OperationMode::OperationModeX11) {
611         m_ui->tabWidget->setTabEnabled(1, false);
612         m_ui->tabWidget->setTabEnabled(2, false);
613         m_ui->tabWidget->setTabEnabled(6, false);
614     }
615     if (!kwinApp()->usesLibinput()) {
616         m_ui->tabWidget->setTabEnabled(3, false);
617     }
618 
619     connect(m_ui->quitButton, &QAbstractButton::clicked, this, &DebugConsole::deleteLater);
620     connect(m_ui->tabWidget, &QTabWidget::currentChanged, this,
621         [this] (int index) {
622             // delay creation of input event filter until the tab is selected
623             if (index == 2 && m_inputFilter.isNull()) {
624                 m_inputFilter.reset(new DebugConsoleFilter(m_ui->inputTextEdit));
625                 input()->installInputEventSpy(m_inputFilter.data());
626             }
627             if (index == 5) {
628                 updateKeyboardTab();
629                 connect(input(), &InputRedirection::keyStateChanged, this, &DebugConsole::updateKeyboardTab);
630             }
631             if (index == 6) {
632                 static_cast<DataSourceModel *>(m_ui->clipboardContent->model())->setSource(waylandServer()->seat()->selection());
633                 m_ui->clipboardSource->setText(sourceString(waylandServer()->seat()->selection()));
634                 connect(waylandServer()->seat(), &KWaylandServer::SeatInterface::selectionChanged, this, [this](KWaylandServer::AbstractDataSource *source) {
635                     static_cast<DataSourceModel *>(m_ui->clipboardContent->model())->setSource(source);
636                     m_ui->clipboardSource->setText(sourceString(source));
637                 });
638                 static_cast<DataSourceModel *>(m_ui->primaryContent->model())->setSource(waylandServer()->seat()->primarySelection());
639                 m_ui->primarySource->setText(sourceString(waylandServer()->seat()->primarySelection()));
640                 connect(waylandServer()->seat(), &KWaylandServer::SeatInterface::primarySelectionChanged, this, [this](KWaylandServer::AbstractDataSource *source) {
641                     static_cast<DataSourceModel *>(m_ui->primaryContent->model())->setSource(source);
642                     m_ui->primarySource->setText(sourceString(source));
643                 });
644             }
645         }
646     );
647 
648     // for X11
649     setWindowFlags(Qt::X11BypassWindowManagerHint);
650 
651     initGLTab();
652 }
653 
654 DebugConsole::~DebugConsole() = default;
655 
initGLTab()656 void DebugConsole::initGLTab()
657 {
658     if (!effects || !effects->isOpenGLCompositing()) {
659         m_ui->noOpenGLLabel->setVisible(true);
660         m_ui->glInfoScrollArea->setVisible(false);
661         return;
662     }
663     GLPlatform *gl = GLPlatform::instance();
664     m_ui->noOpenGLLabel->setVisible(false);
665     m_ui->glInfoScrollArea->setVisible(true);
666     m_ui->glVendorStringLabel->setText(QString::fromLocal8Bit(gl->glVendorString()));
667     m_ui->glRendererStringLabel->setText(QString::fromLocal8Bit(gl->glRendererString()));
668     m_ui->glVersionStringLabel->setText(QString::fromLocal8Bit(gl->glVersionString()));
669     m_ui->glslVersionStringLabel->setText(QString::fromLocal8Bit(gl->glShadingLanguageVersionString()));
670     m_ui->glDriverLabel->setText(GLPlatform::driverToString(gl->driver()));
671     m_ui->glGPULabel->setText(GLPlatform::chipClassToString(gl->chipClass()));
672     m_ui->glVersionLabel->setText(GLPlatform::versionToString(gl->glVersion()));
673     m_ui->glslLabel->setText(GLPlatform::versionToString(gl->glslVersion()));
674 
675     auto extensionsString = [] (const auto &extensions) {
676         QString text = QStringLiteral("<ul>");
677         for (auto extension : extensions) {
678             text.append(QStringLiteral("<li>%1</li>").arg(QString::fromLocal8Bit(extension)));
679         }
680         text.append(QStringLiteral("</ul>"));
681         return text;
682     };
683 
684     m_ui->platformExtensionsLabel->setText(extensionsString(Compositor::self()->scene()->openGLPlatformInterfaceExtensions()));
685     m_ui->openGLExtensionsLabel->setText(extensionsString(openGLExtensions()));
686 }
687 
688 template <typename T>
keymapComponentToString(xkb_keymap * map,const T & count,std::function<const char * (xkb_keymap *,T)> f)689 QString keymapComponentToString(xkb_keymap *map, const T &count, std::function<const char*(xkb_keymap*,T)> f)
690 {
691     QString text = QStringLiteral("<ul>");
692     for (T i = 0; i < count; i++) {
693         text.append(QStringLiteral("<li>%1</li>").arg(QString::fromLocal8Bit(f(map, i))));
694     }
695     text.append(QStringLiteral("</ul>"));
696     return text;
697 }
698 
699 template <typename T>
stateActiveComponents(xkb_state * state,const T & count,std::function<int (xkb_state *,T)> f,std::function<const char * (xkb_keymap *,T)> name)700 QString stateActiveComponents(xkb_state *state, const T &count, std::function<int(xkb_state*,T)> f, std::function<const char*(xkb_keymap*,T)> name)
701 {
702     QString text = QStringLiteral("<ul>");
703     xkb_keymap *map = xkb_state_get_keymap(state);
704     for (T i = 0; i < count; i++) {
705         if (f(state, i) == 1) {
706             text.append(QStringLiteral("<li>%1</li>").arg(QString::fromLocal8Bit(name(map, i))));
707         }
708     }
709     text.append(QStringLiteral("</ul>"));
710     return text;
711 }
712 
updateKeyboardTab()713 void DebugConsole::updateKeyboardTab()
714 {
715     auto xkb = input()->keyboard()->xkb();
716     xkb_keymap *map = xkb->keymap();
717     xkb_state *state = xkb->state();
718     m_ui->layoutsLabel->setText(keymapComponentToString<xkb_layout_index_t>(map, xkb_keymap_num_layouts(map), &xkb_keymap_layout_get_name));
719     m_ui->currentLayoutLabel->setText(xkb_keymap_layout_get_name(map, xkb->currentLayout()));
720     m_ui->modifiersLabel->setText(keymapComponentToString<xkb_mod_index_t>(map, xkb_keymap_num_mods(map), &xkb_keymap_mod_get_name));
721     m_ui->ledsLabel->setText(keymapComponentToString<xkb_led_index_t>(map, xkb_keymap_num_leds(map), &xkb_keymap_led_get_name));
722     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));
723 
724     using namespace std::placeholders;
725     auto modActive = std::bind(xkb_state_mod_index_is_active, _1, _2, XKB_STATE_MODS_EFFECTIVE);
726     m_ui->activeModifiersLabel->setText(stateActiveComponents<xkb_mod_index_t>(state, xkb_keymap_num_mods(map), modActive, &xkb_keymap_mod_get_name));
727 }
728 
showEvent(QShowEvent * event)729 void DebugConsole::showEvent(QShowEvent *event)
730 {
731     QWidget::showEvent(event);
732 
733     // delay the connection to the show event as in ctor the windowHandle returns null
734     connect(windowHandle(), &QWindow::visibleChanged, this,
735         [this] (bool visible) {
736             if (visible) {
737                 // ignore
738                 return;
739             }
740             deleteLater();
741         }
742     );
743 }
744 
DebugConsoleDelegate(QObject * parent)745 DebugConsoleDelegate::DebugConsoleDelegate(QObject *parent)
746     : QStyledItemDelegate(parent)
747 {
748 }
749 
750 DebugConsoleDelegate::~DebugConsoleDelegate() = default;
751 
displayText(const QVariant & value,const QLocale & locale) const752 QString DebugConsoleDelegate::displayText(const QVariant &value, const QLocale &locale) const
753 {
754     switch (value.userType()) {
755     case QMetaType::QPoint: {
756         const QPoint p = value.toPoint();
757         return QStringLiteral("%1,%2").arg(p.x()).arg(p.y());
758     }
759     case QMetaType::QPointF: {
760         const QPointF p = value.toPointF();
761         return QStringLiteral("%1,%2").arg(p.x()).arg(p.y());
762     }
763     case QMetaType::QSize: {
764         const QSize s = value.toSize();
765         return QStringLiteral("%1x%2").arg(s.width()).arg(s.height());
766     }
767     case QMetaType::QSizeF: {
768         const QSizeF s = value.toSizeF();
769         return QStringLiteral("%1x%2").arg(s.width()).arg(s.height());
770     }
771     case QMetaType::QRect: {
772         const QRect r = value.toRect();
773         return QStringLiteral("%1,%2 %3x%4").arg(r.x()).arg(r.y()).arg(r.width()).arg(r.height());
774     }
775     default:
776         if (value.userType() == qMetaTypeId<KWaylandServer::SurfaceInterface*>()) {
777             if (auto s = value.value<KWaylandServer::SurfaceInterface*>()) {
778                 return QStringLiteral("KWaylandServer::SurfaceInterface(0x%1)").arg(qulonglong(s), 0, 16);
779             } else {
780                 return QStringLiteral("nullptr");
781             }
782         }
783         if (value.userType() == qMetaTypeId<Qt::MouseButtons>()) {
784             const auto buttons = value.value<Qt::MouseButtons>();
785             if (buttons == Qt::NoButton) {
786                 return i18n("No Mouse Buttons");
787             }
788             QStringList list;
789             if (buttons.testFlag(Qt::LeftButton)) {
790                 list << i18nc("Mouse Button", "left");
791             }
792             if (buttons.testFlag(Qt::RightButton)) {
793                 list << i18nc("Mouse Button", "right");
794             }
795             if (buttons.testFlag(Qt::MiddleButton)) {
796                 list << i18nc("Mouse Button", "middle");
797             }
798             if (buttons.testFlag(Qt::BackButton)) {
799                 list << i18nc("Mouse Button", "back");
800             }
801             if (buttons.testFlag(Qt::ForwardButton)) {
802                 list << i18nc("Mouse Button", "forward");
803             }
804             if (buttons.testFlag(Qt::ExtraButton1)) {
805                 list << i18nc("Mouse Button", "extra 1");
806             }
807             if (buttons.testFlag(Qt::ExtraButton2)) {
808                 list << i18nc("Mouse Button", "extra 2");
809             }
810             if (buttons.testFlag(Qt::ExtraButton3)) {
811                 list << i18nc("Mouse Button", "extra 3");
812             }
813             if (buttons.testFlag(Qt::ExtraButton4)) {
814                 list << i18nc("Mouse Button", "extra 4");
815             }
816             if (buttons.testFlag(Qt::ExtraButton5)) {
817                 list << i18nc("Mouse Button", "extra 5");
818             }
819             if (buttons.testFlag(Qt::ExtraButton6)) {
820                 list << i18nc("Mouse Button", "extra 6");
821             }
822             if (buttons.testFlag(Qt::ExtraButton7)) {
823                 list << i18nc("Mouse Button", "extra 7");
824             }
825             if (buttons.testFlag(Qt::ExtraButton8)) {
826                 list << i18nc("Mouse Button", "extra 8");
827             }
828             if (buttons.testFlag(Qt::ExtraButton9)) {
829                 list << i18nc("Mouse Button", "extra 9");
830             }
831             if (buttons.testFlag(Qt::ExtraButton10)) {
832                 list << i18nc("Mouse Button", "extra 10");
833             }
834             if (buttons.testFlag(Qt::ExtraButton11)) {
835                 list << i18nc("Mouse Button", "extra 11");
836             }
837             if (buttons.testFlag(Qt::ExtraButton12)) {
838                 list << i18nc("Mouse Button", "extra 12");
839             }
840             if (buttons.testFlag(Qt::ExtraButton13)) {
841                 list << i18nc("Mouse Button", "extra 13");
842             }
843             if (buttons.testFlag(Qt::ExtraButton14)) {
844                 list << i18nc("Mouse Button", "extra 14");
845             }
846             if (buttons.testFlag(Qt::ExtraButton15)) {
847                 list << i18nc("Mouse Button", "extra 15");
848             }
849             if (buttons.testFlag(Qt::ExtraButton16)) {
850                 list << i18nc("Mouse Button", "extra 16");
851             }
852             if (buttons.testFlag(Qt::ExtraButton17)) {
853                 list << i18nc("Mouse Button", "extra 17");
854             }
855             if (buttons.testFlag(Qt::ExtraButton18)) {
856                 list << i18nc("Mouse Button", "extra 18");
857             }
858             if (buttons.testFlag(Qt::ExtraButton19)) {
859                 list << i18nc("Mouse Button", "extra 19");
860             }
861             if (buttons.testFlag(Qt::ExtraButton20)) {
862                 list << i18nc("Mouse Button", "extra 20");
863             }
864             if (buttons.testFlag(Qt::ExtraButton21)) {
865                 list << i18nc("Mouse Button", "extra 21");
866             }
867             if (buttons.testFlag(Qt::ExtraButton22)) {
868                 list << i18nc("Mouse Button", "extra 22");
869             }
870             if (buttons.testFlag(Qt::ExtraButton23)) {
871                 list << i18nc("Mouse Button", "extra 23");
872             }
873             if (buttons.testFlag(Qt::ExtraButton24)) {
874                 list << i18nc("Mouse Button", "extra 24");
875             }
876             if (buttons.testFlag(Qt::TaskButton)) {
877                 list << i18nc("Mouse Button", "task");
878             }
879             return list.join(QStringLiteral(", "));
880         }
881         break;
882     }
883     return QStyledItemDelegate::displayText(value, locale);
884 }
885 
886 static const int s_x11ClientId = 1;
887 static const int s_x11UnmanagedId = 2;
888 static const int s_waylandClientId = 3;
889 static const int s_workspaceInternalId = 4;
890 static const quint32 s_propertyBitMask = 0xFFFF0000;
891 static const quint32 s_clientBitMask   = 0x0000FFFF;
892 static const quint32 s_idDistance = 10000;
893 
894 template <class T>
add(int parentRow,QVector<T * > & clients,T * client)895 void DebugConsoleModel::add(int parentRow, QVector<T*> &clients, T *client)
896 {
897     beginInsertRows(index(parentRow, 0, QModelIndex()), clients.count(), clients.count());
898     clients.append(client);
899     endInsertRows();
900 }
901 
902 template <class T>
remove(int parentRow,QVector<T * > & clients,T * client)903 void DebugConsoleModel::remove(int parentRow, QVector<T*> &clients, T *client)
904 {
905     const int remove = clients.indexOf(client);
906     if (remove == -1) {
907         return;
908     }
909     beginRemoveRows(index(parentRow, 0, QModelIndex()), remove, remove);
910     clients.removeAt(remove);
911     endRemoveRows();
912 }
913 
DebugConsoleModel(QObject * parent)914 DebugConsoleModel::DebugConsoleModel(QObject *parent)
915     : QAbstractItemModel(parent)
916 {
917     const auto clients = workspace()->allClientList();
918     for (auto c : clients) {
919         handleClientAdded(c);
920     }
921     connect(workspace(), &Workspace::clientAdded, this, &DebugConsoleModel::handleClientAdded);
922     connect(workspace(), &Workspace::clientRemoved, this, &DebugConsoleModel::handleClientRemoved);
923 
924     const auto unmangeds = workspace()->unmanagedList();
925     for (auto u : unmangeds) {
926         m_unmanageds.append(u);
927     }
928     connect(workspace(), &Workspace::unmanagedAdded, this,
929         [this] (Unmanaged *u) {
930             add(s_x11UnmanagedId -1, m_unmanageds, u);
931         }
932     );
933     connect(workspace(), &Workspace::unmanagedRemoved, this,
934         [this] (Unmanaged *u) {
935             remove(s_x11UnmanagedId -1, m_unmanageds, u);
936         }
937     );
938     for (InternalClient *client : workspace()->internalClients()) {
939         m_internalClients.append(client);
940     }
941     connect(workspace(), &Workspace::internalClientAdded, this,
942         [this](InternalClient *client) {
943             add(s_workspaceInternalId -1, m_internalClients, client);
944         }
945     );
946     connect(workspace(), &Workspace::internalClientRemoved, this,
947         [this](InternalClient *client) {
948             remove(s_workspaceInternalId -1, m_internalClients, client);
949         }
950     );
951 }
952 
handleClientAdded(AbstractClient * client)953 void DebugConsoleModel::handleClientAdded(AbstractClient *client)
954 {
955     X11Client *x11Client = qobject_cast<X11Client *>(client);
956     if (x11Client) {
957         add(s_x11ClientId - 1, m_x11Clients, x11Client);
958         return;
959     }
960 
961     WaylandClient *waylandClient = qobject_cast<WaylandClient *>(client);
962     if (waylandClient) {
963         add(s_waylandClientId - 1, m_waylandClients, waylandClient);
964         return;
965     }
966 }
967 
handleClientRemoved(AbstractClient * client)968 void DebugConsoleModel::handleClientRemoved(AbstractClient *client)
969 {
970     X11Client *x11Client = qobject_cast<X11Client *>(client);
971     if (x11Client) {
972         remove(s_x11ClientId - 1, m_x11Clients, x11Client);
973         return;
974     }
975 
976     WaylandClient *waylandClient = qobject_cast<WaylandClient *>(client);
977     if (waylandClient) {
978         remove(s_waylandClientId - 1, m_waylandClients, waylandClient);
979         return;
980     }
981 }
982 
983 DebugConsoleModel::~DebugConsoleModel() = default;
984 
columnCount(const QModelIndex & parent) const985 int DebugConsoleModel::columnCount(const QModelIndex &parent) const
986 {
987     Q_UNUSED(parent)
988     return 2;
989 }
990 
topLevelRowCount() const991 int DebugConsoleModel::topLevelRowCount() const
992 {
993     return kwinApp()->shouldUseWaylandForCompositing() ? 4 : 2;
994 }
995 
996 template <class T>
propertyCount(const QModelIndex & parent,T * (DebugConsoleModel::* filter)(const QModelIndex &)const) const997 int DebugConsoleModel::propertyCount(const QModelIndex &parent, T *(DebugConsoleModel::*filter)(const QModelIndex&) const) const
998 {
999     if (T *t = (this->*filter)(parent)) {
1000         return t->metaObject()->propertyCount();
1001     }
1002     return 0;
1003 }
1004 
rowCount(const QModelIndex & parent) const1005 int DebugConsoleModel::rowCount(const QModelIndex &parent) const
1006 {
1007     if (!parent.isValid()) {
1008         return topLevelRowCount();
1009     }
1010 
1011     switch (parent.internalId()) {
1012     case s_x11ClientId:
1013         return m_x11Clients.count();
1014     case s_x11UnmanagedId:
1015         return m_unmanageds.count();
1016     case s_waylandClientId:
1017         return m_waylandClients.count();
1018     case s_workspaceInternalId:
1019         return m_internalClients.count();
1020     default:
1021         break;
1022     }
1023 
1024     if (parent.internalId() & s_propertyBitMask) {
1025         // properties do not have children
1026         return 0;
1027     }
1028 
1029     if (parent.internalId() < s_idDistance * (s_x11ClientId + 1)) {
1030         return propertyCount(parent, &DebugConsoleModel::x11Client);
1031     } else if (parent.internalId() < s_idDistance * (s_x11UnmanagedId + 1)) {
1032         return propertyCount(parent, &DebugConsoleModel::unmanaged);
1033     } else if (parent.internalId() < s_idDistance * (s_waylandClientId + 1)) {
1034         return propertyCount(parent, &DebugConsoleModel::waylandClient);
1035     } else if (parent.internalId() < s_idDistance * (s_workspaceInternalId + 1)) {
1036         return propertyCount(parent, &DebugConsoleModel::internalClient);
1037     }
1038 
1039     return 0;
1040 }
1041 
1042 template <class T>
indexForClient(int row,int column,const QVector<T * > & clients,int id) const1043 QModelIndex DebugConsoleModel::indexForClient(int row, int column, const QVector<T*> &clients, int id) const
1044 {
1045     if (column != 0) {
1046         return QModelIndex();
1047     }
1048     if (row >= clients.count()) {
1049         return QModelIndex();
1050     }
1051     return createIndex(row, column, s_idDistance * id + row);
1052 }
1053 
1054 template <class T>
indexForProperty(int row,int column,const QModelIndex & parent,T * (DebugConsoleModel::* filter)(const QModelIndex &)const) const1055 QModelIndex DebugConsoleModel::indexForProperty(int row, int column, const QModelIndex &parent, T *(DebugConsoleModel::*filter)(const QModelIndex&) const) const
1056 {
1057     if (T *t = (this->*filter)(parent)) {
1058         if (row >= t->metaObject()->propertyCount()) {
1059             return QModelIndex();
1060         }
1061         return createIndex(row, column, quint32(row + 1) << 16 | parent.internalId());
1062     }
1063     return QModelIndex();
1064 }
1065 
index(int row,int column,const QModelIndex & parent) const1066 QModelIndex DebugConsoleModel::index(int row, int column, const QModelIndex &parent) const
1067 {
1068     if (!parent.isValid()) {
1069         // index for a top level item
1070         if (column != 0 || row >= topLevelRowCount()) {
1071             return QModelIndex();
1072         }
1073         return createIndex(row, column, row + 1);
1074     }
1075     if (column >= 2) {
1076         // max of 2 columns
1077         return QModelIndex();
1078     }
1079     // index for a client (second level)
1080     switch (parent.internalId()) {
1081     case s_x11ClientId:
1082         return indexForClient(row, column, m_x11Clients, s_x11ClientId);
1083     case s_x11UnmanagedId:
1084         return indexForClient(row, column, m_unmanageds, s_x11UnmanagedId);
1085     case s_waylandClientId:
1086         return indexForClient(row, column, m_waylandClients, s_waylandClientId);
1087     case s_workspaceInternalId:
1088         return indexForClient(row, column, m_internalClients, s_workspaceInternalId);
1089     default:
1090         break;
1091     }
1092 
1093     // index for a property (third level)
1094     if (parent.internalId() < s_idDistance * (s_x11ClientId + 1)) {
1095         return indexForProperty(row, column, parent, &DebugConsoleModel::x11Client);
1096     } else if (parent.internalId() < s_idDistance * (s_x11UnmanagedId + 1)) {
1097         return indexForProperty(row, column, parent, &DebugConsoleModel::unmanaged);
1098     } else if (parent.internalId() < s_idDistance * (s_waylandClientId + 1)) {
1099         return indexForProperty(row, column, parent, &DebugConsoleModel::waylandClient);
1100     } else if (parent.internalId() < s_idDistance * (s_workspaceInternalId + 1)) {
1101         return indexForProperty(row, column, parent, &DebugConsoleModel::internalClient);
1102     }
1103 
1104     return QModelIndex();
1105 }
1106 
parent(const QModelIndex & child) const1107 QModelIndex DebugConsoleModel::parent(const QModelIndex &child) const
1108 {
1109     if (child.internalId() <= s_workspaceInternalId) {
1110         return QModelIndex();
1111     }
1112     if (child.internalId() & s_propertyBitMask) {
1113         // a property
1114         const quint32 parentId = child.internalId() & s_clientBitMask;
1115         if (parentId < s_idDistance * (s_x11ClientId + 1)) {
1116             return createIndex(parentId - (s_idDistance * s_x11ClientId), 0, parentId);
1117         } else if (parentId < s_idDistance * (s_x11UnmanagedId + 1)) {
1118             return createIndex(parentId - (s_idDistance * s_x11UnmanagedId), 0, parentId);
1119         } else if (parentId < s_idDistance * (s_waylandClientId + 1)) {
1120             return createIndex(parentId - (s_idDistance * s_waylandClientId), 0, parentId);
1121         } else if (parentId < s_idDistance * (s_workspaceInternalId + 1)) {
1122             return createIndex(parentId - (s_idDistance * s_workspaceInternalId), 0, parentId);
1123         }
1124         return QModelIndex();
1125     }
1126     if (child.internalId() < s_idDistance * (s_x11ClientId + 1)) {
1127         return createIndex(s_x11ClientId -1, 0, s_x11ClientId);
1128     } else if (child.internalId() < s_idDistance * (s_x11UnmanagedId + 1)) {
1129         return createIndex(s_x11UnmanagedId -1, 0, s_x11UnmanagedId);
1130     } else if (child.internalId() < s_idDistance * (s_waylandClientId + 1)) {
1131         return createIndex(s_waylandClientId -1, 0, s_waylandClientId);
1132     } else if (child.internalId() < s_idDistance * (s_workspaceInternalId + 1)) {
1133         return createIndex(s_workspaceInternalId -1, 0, s_workspaceInternalId);
1134     }
1135     return QModelIndex();
1136 }
1137 
propertyData(QObject * object,const QModelIndex & index,int role) const1138 QVariant DebugConsoleModel::propertyData(QObject *object, const QModelIndex &index, int role) const
1139 {
1140     Q_UNUSED(role)
1141     const auto property = object->metaObject()->property(index.row());
1142     if (index.column() == 0) {
1143         return property.name();
1144     } else {
1145         const QVariant value = property.read(object);
1146         if (qstrcmp(property.name(), "windowType") == 0) {
1147             switch (value.toInt()) {
1148             case NET::Normal:
1149                 return QStringLiteral("NET::Normal");
1150             case NET::Desktop:
1151                 return QStringLiteral("NET::Desktop");
1152             case NET::Dock:
1153                 return QStringLiteral("NET::Dock");
1154             case NET::Toolbar:
1155                 return QStringLiteral("NET::Toolbar");
1156             case NET::Menu:
1157                 return QStringLiteral("NET::Menu");
1158             case NET::Dialog:
1159                 return QStringLiteral("NET::Dialog");
1160             case NET::Override:
1161                 return QStringLiteral("NET::Override");
1162             case NET::TopMenu:
1163                 return QStringLiteral("NET::TopMenu");
1164             case NET::Utility:
1165                 return QStringLiteral("NET::Utility");
1166             case NET::Splash:
1167                 return QStringLiteral("NET::Splash");
1168             case NET::DropdownMenu:
1169                 return QStringLiteral("NET::DropdownMenu");
1170             case NET::PopupMenu:
1171                 return QStringLiteral("NET::PopupMenu");
1172             case NET::Tooltip:
1173                 return QStringLiteral("NET::Tooltip");
1174             case NET::Notification:
1175                 return QStringLiteral("NET::Notification");
1176             case NET::ComboBox:
1177                 return QStringLiteral("NET::ComboBox");
1178             case NET::DNDIcon:
1179                 return QStringLiteral("NET::DNDIcon");
1180             case NET::OnScreenDisplay:
1181                 return QStringLiteral("NET::OnScreenDisplay");
1182             case NET::CriticalNotification:
1183                 return QStringLiteral("NET::CriticalNotification");
1184             case NET::Unknown:
1185             default:
1186                 return QStringLiteral("NET::Unknown");
1187             }
1188         } else if (qstrcmp(property.name(), "layer") == 0) {
1189             return QMetaEnum::fromType<Layer>().valueToKey(value.value<Layer>());
1190         }
1191         return value;
1192     }
1193     return QVariant();
1194 }
1195 
1196 template <class T>
clientData(const QModelIndex & index,int role,const QVector<T * > clients,const std::function<QString (T *)> & toString) const1197 QVariant DebugConsoleModel::clientData(const QModelIndex &index, int role, const QVector<T*> clients, const std::function<QString(T*)> &toString) const
1198 {
1199     if (index.row() >= clients.count()) {
1200         return QVariant();
1201     }
1202     auto c = clients.at(index.row());
1203     if (role == Qt::DisplayRole) {
1204         return toString(c);
1205     } else if (role == Qt::DecorationRole) {
1206         return c->icon();
1207     }
1208     return QVariant();
1209 }
1210 
data(const QModelIndex & index,int role) const1211 QVariant DebugConsoleModel::data(const QModelIndex &index, int role) const
1212 {
1213     if (!index.isValid()) {
1214         return QVariant();
1215     }
1216     if (!index.parent().isValid()) {
1217         // one of the top levels
1218         if (index.column() != 0 || role != Qt::DisplayRole) {
1219             return QVariant();
1220         }
1221         switch (index.internalId()) {
1222         case s_x11ClientId:
1223             return i18n("X11 Client Windows");
1224         case s_x11UnmanagedId:
1225             return i18n("X11 Unmanaged Windows");
1226         case s_waylandClientId:
1227             return i18n("Wayland Windows");
1228         case s_workspaceInternalId:
1229             return i18n("Internal Windows");
1230         default:
1231             return QVariant();
1232         }
1233     }
1234     if (index.internalId() & s_propertyBitMask) {
1235         if (index.column() >= 2 || role != Qt::DisplayRole) {
1236             return QVariant();
1237         }
1238         if (AbstractClient *c = waylandClient(index)) {
1239             return propertyData(c, index, role);
1240         } else if (InternalClient *c = internalClient(index)) {
1241             return propertyData(c, index, role);
1242         } else if (X11Client *c = x11Client(index)) {
1243             return propertyData(c, index, role);
1244         } else if (Unmanaged *u = unmanaged(index)) {
1245             return propertyData(u, index, role);
1246         }
1247     } else {
1248         if (index.column() != 0) {
1249             return QVariant();
1250         }
1251 
1252         auto generic = [] (AbstractClient *c) -> QString {
1253             return c->caption() + QLatin1Char(' ') + QString::fromUtf8(c->metaObject()->className());
1254         };
1255         switch (index.parent().internalId()) {
1256         case s_x11ClientId:
1257             return clientData<X11Client>(index, role, m_x11Clients, [](X11Client *c) -> QString {
1258                 return QStringLiteral("0x%1: %2").arg(c->window(), 0, 16).arg(c->caption());
1259             });
1260         case s_x11UnmanagedId: {
1261             if (index.row() >= m_unmanageds.count()) {
1262                 return QVariant();
1263             }
1264             auto u = m_unmanageds.at(index.row());
1265             if (role == Qt::DisplayRole) {
1266                 return QStringLiteral("0x%1").arg(u->window(), 0, 16);
1267             }
1268             break;
1269         }
1270         case s_waylandClientId:
1271             return clientData<WaylandClient>(index, role, m_waylandClients, generic);
1272         case s_workspaceInternalId:
1273             return clientData<InternalClient>(index, role, m_internalClients, generic);
1274         default:
1275             break;
1276         }
1277     }
1278 
1279     return QVariant();
1280 }
1281 
1282 template<class T>
clientForIndex(const QModelIndex & index,const QVector<T * > & clients,int id)1283 static T *clientForIndex(const QModelIndex &index, const QVector<T*> &clients, int id)
1284 {
1285     const qint32 row = (index.internalId() & s_clientBitMask) - (s_idDistance * id);
1286     if (row < 0 || row >= clients.count()) {
1287         return nullptr;
1288     }
1289     return clients.at(row);
1290 }
1291 
waylandClient(const QModelIndex & index) const1292 WaylandClient *DebugConsoleModel::waylandClient(const QModelIndex &index) const
1293 {
1294     return clientForIndex(index, m_waylandClients, s_waylandClientId);
1295 }
1296 
internalClient(const QModelIndex & index) const1297 InternalClient *DebugConsoleModel::internalClient(const QModelIndex &index) const
1298 {
1299     return clientForIndex(index, m_internalClients, s_workspaceInternalId);
1300 }
1301 
x11Client(const QModelIndex & index) const1302 X11Client *DebugConsoleModel::x11Client(const QModelIndex &index) const
1303 {
1304     return clientForIndex(index, m_x11Clients, s_x11ClientId);
1305 }
1306 
unmanaged(const QModelIndex & index) const1307 Unmanaged *DebugConsoleModel::unmanaged(const QModelIndex &index) const
1308 {
1309     return clientForIndex(index, m_unmanageds, s_x11UnmanagedId);
1310 }
1311 
1312 /////////////////////////////////////// SurfaceTreeModel
SurfaceTreeModel(QObject * parent)1313 SurfaceTreeModel::SurfaceTreeModel(QObject *parent)
1314     : QAbstractItemModel(parent)
1315 {
1316     // TODO: it would be nice to not have to reset the model on each change
1317     auto reset = [this] {
1318         beginResetModel();
1319         endResetModel();
1320     };
1321     using namespace KWaylandServer;
1322 
1323     auto watchSubsurfaces = [this, reset](AbstractClient *c) {
1324         if (!c->surface()) {
1325             return;
1326         }
1327         auto monitor = new SubSurfaceMonitor(c->surface(), this);
1328         connect(monitor, &SubSurfaceMonitor::subSurfaceAdded, this, reset);
1329         connect(monitor, &SubSurfaceMonitor::subSurfaceRemoved, this, reset);
1330         connect (c, &QObject::destroyed, monitor, &QObject::deleteLater);
1331     };
1332 
1333     for (auto c : workspace()->allClientList()) {
1334         watchSubsurfaces(c);
1335     }
1336     connect(workspace(), &Workspace::clientAdded, this,
1337         [reset, watchSubsurfaces] (AbstractClient *c) {
1338             watchSubsurfaces(c);
1339             reset();
1340         }
1341     );
1342     connect(workspace(), &Workspace::clientRemoved, this, reset);
1343     connect(workspace(), &Workspace::unmanagedAdded, this, reset);
1344     connect(workspace(), &Workspace::unmanagedRemoved, this, reset);
1345 }
1346 
1347 SurfaceTreeModel::~SurfaceTreeModel() = default;
1348 
columnCount(const QModelIndex & parent) const1349 int SurfaceTreeModel::columnCount(const QModelIndex &parent) const
1350 {
1351     Q_UNUSED(parent)
1352     return 1;
1353 }
1354 
rowCount(const QModelIndex & parent) const1355 int SurfaceTreeModel::rowCount(const QModelIndex &parent) const
1356 {
1357     if (parent.isValid()) {
1358         using namespace KWaylandServer;
1359         if (SurfaceInterface *surface = static_cast<SurfaceInterface*>(parent.internalPointer())) {
1360             return surface->below().count() + surface->above().count();
1361         }
1362         return 0;
1363     }
1364     // toplevel are all windows
1365     return workspace()->allClientList().count() +
1366            workspace()->unmanagedList().count();
1367 }
1368 
index(int row,int column,const QModelIndex & parent) const1369 QModelIndex SurfaceTreeModel::index(int row, int column, const QModelIndex &parent) const
1370 {
1371     if (column != 0) {
1372         // invalid column
1373         return QModelIndex();
1374     }
1375 
1376     if (parent.isValid()) {
1377         using namespace KWaylandServer;
1378         if (SurfaceInterface *surface = static_cast<SurfaceInterface*>(parent.internalPointer())) {
1379             int reference = 0;
1380             const auto &below = surface->below();
1381             if (row < reference + below.count()) {
1382                 return createIndex(row, column, below.at(row - reference)->surface());
1383             }
1384             reference += below.count();
1385 
1386             const auto &above = surface->above();
1387             if (row < reference + above.count()) {
1388                 return createIndex(row, column, above.at(row - reference)->surface());
1389             }
1390         }
1391         return QModelIndex();
1392     }
1393     // a window
1394     const auto &allClients = workspace()->allClientList();
1395     if (row < allClients.count()) {
1396         // references a client
1397         return createIndex(row, column, allClients.at(row)->surface());
1398     }
1399     int reference = allClients.count();
1400     const auto &unmanaged = workspace()->unmanagedList();
1401     if (row < reference + unmanaged.count()) {
1402         return createIndex(row, column, unmanaged.at(row-reference)->surface());
1403     }
1404     reference += unmanaged.count();
1405     // not found
1406     return QModelIndex();
1407 }
1408 
parent(const QModelIndex & child) const1409 QModelIndex SurfaceTreeModel::parent(const QModelIndex &child) const
1410 {
1411     using namespace KWaylandServer;
1412     if (SurfaceInterface *surface = static_cast<SurfaceInterface*>(child.internalPointer())) {
1413         const auto &subsurface = surface->subSurface();
1414         if (!subsurface) {
1415             // doesn't reference a subsurface, this is a top-level window
1416             return QModelIndex();
1417         }
1418         SurfaceInterface *parent = subsurface->parentSurface();
1419         if (!parent) {
1420             // something is wrong
1421             return QModelIndex();
1422         }
1423         // is the parent a subsurface itself?
1424         if (parent->subSurface()) {
1425             auto grandParent = parent->subSurface()->parentSurface();
1426             if (!grandParent) {
1427                 // something is wrong
1428                 return QModelIndex();
1429             }
1430             int row = 0;
1431             const auto &below = grandParent->below();
1432             for (int i = 0; i < below.count(); i++) {
1433                 if (below.at(i) == parent->subSurface()) {
1434                     return createIndex(row + i, 0, parent);
1435                 }
1436             }
1437             row += below.count();
1438             const auto &above = grandParent->above();
1439             for (int i = 0; i < above.count(); i++) {
1440                 if (above.at(i) == parent->subSurface()) {
1441                     return createIndex(row + i, 0, parent);
1442                 }
1443             }
1444             return QModelIndex();
1445         }
1446         // not a subsurface, thus it's a true window
1447         int row = 0;
1448         const auto &allClients = workspace()->allClientList();
1449         for (; row < allClients.count(); row++) {
1450             if (allClients.at(row)->surface() == parent) {
1451                 return createIndex(row, 0, parent);
1452             }
1453         }
1454         row = allClients.count();
1455         const auto &unmanaged = workspace()->unmanagedList();
1456         for (int i = 0; i < unmanaged.count(); i++) {
1457             if (unmanaged.at(i)->surface() == parent) {
1458                 return createIndex(row + i, 0, parent);
1459             }
1460         }
1461         row += unmanaged.count();
1462     }
1463     return QModelIndex();
1464 }
1465 
data(const QModelIndex & index,int role) const1466 QVariant SurfaceTreeModel::data(const QModelIndex &index, int role) const
1467 {
1468     if (!index.isValid()) {
1469         return QVariant();
1470     }
1471     using namespace KWaylandServer;
1472     if (SurfaceInterface *surface = static_cast<SurfaceInterface*>(index.internalPointer())) {
1473         if (role == Qt::DisplayRole || role == Qt::ToolTipRole) {
1474             return QStringLiteral("%1 (%2) - %3").arg(surface->client()->executablePath())
1475                                                 .arg(surface->client()->processId())
1476                                                 .arg(surface->id());
1477         } else if (role == Qt::DecorationRole) {
1478             if (auto buffer = qobject_cast<KWaylandServer::ShmClientBuffer *>(surface->buffer())) {
1479                 return buffer->data().scaled(QSize(64, 64), Qt::KeepAspectRatio);
1480             }
1481         }
1482     }
1483     return QVariant();
1484 }
1485 
InputDeviceModel(QObject * parent)1486 InputDeviceModel::InputDeviceModel(QObject *parent)
1487     : QAbstractItemModel(parent)
1488     , m_devices(LibInput::Connection::self()->devices())
1489 {
1490     for (auto it = m_devices.constBegin(); it != m_devices.constEnd(); ++it) {
1491         setupDeviceConnections(*it);
1492     }
1493     auto c = LibInput::Connection::self();
1494     connect(c, &LibInput::Connection::deviceAdded, this,
1495         [this] (LibInput::Device *d) {
1496             beginInsertRows(QModelIndex(), m_devices.count(), m_devices.count());
1497             m_devices << d;
1498             setupDeviceConnections(d);
1499             endInsertRows();
1500         }
1501     );
1502     connect(c, &LibInput::Connection::deviceRemoved, this,
1503         [this] (LibInput::Device *d) {
1504             const int index = m_devices.indexOf(d);
1505             if (index == -1) {
1506                 return;
1507             }
1508             beginRemoveRows(QModelIndex(), index, index);
1509             m_devices.removeAt(index);
1510             endRemoveRows();
1511         }
1512     );
1513 }
1514 
1515 InputDeviceModel::~InputDeviceModel() = default;
1516 
1517 
columnCount(const QModelIndex & parent) const1518 int InputDeviceModel::columnCount(const QModelIndex &parent) const
1519 {
1520     Q_UNUSED(parent)
1521     return 2;
1522 }
1523 
data(const QModelIndex & index,int role) const1524 QVariant InputDeviceModel::data(const QModelIndex &index, int role) const
1525 {
1526     if (!index.isValid()) {
1527         return QVariant();
1528     }
1529     if (!index.parent().isValid() && index.column() == 0) {
1530         const auto devices = LibInput::Connection::self()->devices();
1531         if (index.row() >= devices.count()) {
1532             return QVariant();
1533         }
1534         if (role == Qt::DisplayRole) {
1535             return devices.at(index.row())->name();
1536         }
1537     }
1538     if (index.parent().isValid()) {
1539         if (role == Qt::DisplayRole) {
1540             const auto device = LibInput::Connection::self()->devices().at(index.parent().row());
1541             const auto property = device->metaObject()->property(index.row());
1542             if (index.column() == 0) {
1543                 return property.name();
1544             } else if (index.column() == 1) {
1545                 return device->property(property.name());
1546             }
1547         }
1548     }
1549     return QVariant();
1550 }
1551 
index(int row,int column,const QModelIndex & parent) const1552 QModelIndex InputDeviceModel::index(int row, int column, const QModelIndex &parent) const
1553 {
1554     if (column >= 2) {
1555         return QModelIndex();
1556     }
1557     if (parent.isValid()) {
1558         if (parent.internalId() & s_propertyBitMask) {
1559             return QModelIndex();
1560         }
1561         if (row >= LibInput::Connection::self()->devices().at(parent.row())->metaObject()->propertyCount()) {
1562             return QModelIndex();
1563         }
1564         return createIndex(row, column, quint32(row + 1) << 16 | parent.internalId());
1565     }
1566     if (row >= LibInput::Connection::self()->devices().count()) {
1567         return QModelIndex();
1568     }
1569     return createIndex(row, column, row + 1);
1570 }
1571 
rowCount(const QModelIndex & parent) const1572 int InputDeviceModel::rowCount(const QModelIndex &parent) const
1573 {
1574     if (!parent.isValid()) {
1575         return LibInput::Connection::self()->devices().count();
1576     }
1577     if (parent.internalId() & s_propertyBitMask) {
1578         return 0;
1579     }
1580 
1581     return LibInput::Connection::self()->devices().at(parent.row())->metaObject()->propertyCount();
1582 }
1583 
parent(const QModelIndex & child) const1584 QModelIndex InputDeviceModel::parent(const QModelIndex &child) const
1585 {
1586     if (child.internalId() & s_propertyBitMask) {
1587         const quintptr parentId = child.internalId() & s_clientBitMask;
1588         return createIndex(parentId - 1, 0, parentId);
1589     }
1590     return QModelIndex();
1591 }
1592 
setupDeviceConnections(LibInput::Device * device)1593 void InputDeviceModel::setupDeviceConnections(LibInput::Device *device)
1594 {
1595     connect(device, &LibInput::Device::enabledChanged, this,
1596         [this, device] {
1597             const QModelIndex parent = index(m_devices.indexOf(device), 0, QModelIndex());
1598             const QModelIndex child = index(device->metaObject()->indexOfProperty("enabled"), 1, parent);
1599             Q_EMIT dataChanged(child, child, QVector<int>{Qt::DisplayRole});
1600         }
1601     );
1602     connect(device, &LibInput::Device::leftHandedChanged, this,
1603         [this, device] {
1604             const QModelIndex parent = index(m_devices.indexOf(device), 0, QModelIndex());
1605             const QModelIndex child = index(device->metaObject()->indexOfProperty("leftHanded"), 1, parent);
1606             Q_EMIT dataChanged(child, child, QVector<int>{Qt::DisplayRole});
1607         }
1608     );
1609     connect(device, &LibInput::Device::pointerAccelerationChanged, this,
1610         [this, device] {
1611             const QModelIndex parent = index(m_devices.indexOf(device), 0, QModelIndex());
1612             const QModelIndex child = index(device->metaObject()->indexOfProperty("pointerAcceleration"), 1, parent);
1613             Q_EMIT dataChanged(child, child, QVector<int>{Qt::DisplayRole});
1614         }
1615     );
1616 }
1617 
index(int row,int column,const QModelIndex & parent) const1618 QModelIndex DataSourceModel::index(int row, int column, const QModelIndex &parent) const
1619 {
1620     if (!m_source || parent.isValid() || column >= 2 || row >= m_source->mimeTypes().size()) {
1621         return QModelIndex();
1622     }
1623     return createIndex(row, column, nullptr);
1624 }
1625 
parent(const QModelIndex & child) const1626 QModelIndex DataSourceModel::parent(const QModelIndex &child) const
1627 {
1628     return QModelIndex();
1629 }
1630 
rowCount(const QModelIndex & parent) const1631 int DataSourceModel::rowCount(const QModelIndex &parent) const
1632 {
1633     if (!parent.isValid()) {
1634         return m_source ? m_source->mimeTypes().count() : 0;
1635     }
1636     return 0;
1637 }
1638 
headerData(int section,Qt::Orientation orientation,int role) const1639 QVariant DataSourceModel::headerData(int section, Qt::Orientation orientation, int role) const
1640 {
1641     if (role != Qt::DisplayRole || orientation != Qt::Horizontal || section >= 2) {
1642         return QVariant();
1643     }
1644     return section == 0 ? QStringLiteral("Mime type") : QStringLiteral("Content");
1645 }
1646 
data(const QModelIndex & index,int role) const1647 QVariant DataSourceModel::data(const QModelIndex &index, int role) const
1648 {
1649     if (!checkIndex(index, CheckIndexOption::ParentIsInvalid | CheckIndexOption::IndexIsValid)) {
1650         return QVariant();
1651     }
1652     const QString mimeType = m_source->mimeTypes().at(index.row());
1653     ;
1654     if (index.column() == 0 && role == Qt::DisplayRole) {
1655         return mimeType;
1656     } else if (index.column() == 1 && index.row() < m_data.count()) {
1657         const QByteArray &data = m_data.at(index.row());
1658         if (mimeType.contains(QLatin1String("image"))) {
1659             if (role == Qt::DecorationRole) {
1660                 return QImage::fromData(data);
1661             }
1662         } else if (role == Qt::DisplayRole) {
1663             return data;
1664         }
1665     }
1666     return QVariant();
1667 }
1668 
readData(int fd)1669 static QByteArray readData(int fd)
1670 {
1671     pollfd pfd;
1672     pfd.fd = fd;
1673     pfd.events = POLLIN;
1674     auto closeFd = qScopeGuard([fd] {
1675         close(fd);
1676     });
1677     QByteArray data;
1678     while (true) {
1679         const int ready = poll(&pfd, 1, 1000);
1680         if (ready < 0) {
1681             if (errno != EINTR) {
1682                 return QByteArrayLiteral("poll() failed: ") + strerror(errno);
1683             }
1684         } else if (ready == 0) {
1685             return QByteArrayLiteral("timeout reading from pipe");
1686         } else {
1687             char buf[4096];
1688             int n = read(fd, buf, sizeof buf);
1689 
1690             if (n < 0) {
1691                 return QByteArrayLiteral("read failed: ") + strerror(errno);
1692             } else if (n == 0) {
1693                 return data;
1694             } else if (n > 0) {
1695                 data.append(buf, n);
1696             }
1697         }
1698     }
1699 }
1700 
setSource(KWaylandServer::AbstractDataSource * source)1701 void DataSourceModel::setSource(KWaylandServer::AbstractDataSource *source)
1702 {
1703     beginResetModel();
1704     m_source = source;
1705     m_data.clear();
1706     if (source) {
1707         m_data.resize(m_source->mimeTypes().size());
1708         for (auto type = m_source->mimeTypes().cbegin(); type != m_source->mimeTypes().cend(); ++type) {
1709             int pipeFds[2];
1710             if (pipe2(pipeFds, O_CLOEXEC) != 0) {
1711                 continue;
1712             }
1713             source->requestData(*type, pipeFds[1]);
1714             QFuture<QByteArray> data = QtConcurrent::run(readData, pipeFds[0]);
1715             auto watcher = new QFutureWatcher<QByteArray>(this);
1716             watcher->setFuture(data);
1717             const int index = type - m_source->mimeTypes().cbegin();
1718             connect(watcher, &QFutureWatcher<QByteArray>::finished, this, [this, watcher, index, source = QPointer(source)] {
1719                 watcher->deleteLater();
1720                 if (source && source == m_source) {
1721                     m_data[index] = watcher->result();
1722                     Q_EMIT dataChanged(this->index(index, 1), this->index(index, 1), {Qt::DecorationRole | Qt::DisplayRole});
1723                 }
1724             });
1725         }
1726     }
1727     endResetModel();
1728 }
1729 }
1730