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