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