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