diff --git a/debug_console.h b/debug_console.h --- a/debug_console.h +++ b/debug_console.h @@ -21,11 +21,14 @@ #define KWIN_DEBUG_CONSOLE_H #include +#include "input.h" #include #include #include +class QTextEdit; + namespace Ui { class DebugConsole; @@ -37,6 +40,7 @@ class Client; class ShellClient; class Unmanaged; +class DebugConsoleFilter; class KWIN_EXPORT DebugConsoleModel : public QAbstractItemModel { @@ -98,6 +102,7 @@ private: QScopedPointer m_ui; + QScopedPointer m_inputFilter; }; class SurfaceTreeModel : public QAbstractItemModel @@ -114,6 +119,23 @@ QModelIndex parent(const QModelIndex &child) const override; }; +class DebugConsoleFilter : public InputEventFilter +{ +public: + explicit DebugConsoleFilter(QTextEdit *textEdit); + virtual ~DebugConsoleFilter(); + + bool pointerEvent(QMouseEvent *event, quint32 nativeButton) override; + bool wheelEvent(QWheelEvent *event) override; + bool keyEvent(QKeyEvent *event) override; + bool touchDown(quint32 id, const QPointF &pos, quint32 time) override; + bool touchMotion(quint32 id, const QPointF &pos, quint32 time) override; + bool touchUp(quint32 id, quint32 time) override; + +private: + QTextEdit *m_textEdit; +}; + } #endif diff --git a/debug_console.cpp b/debug_console.cpp --- a/debug_console.cpp +++ b/debug_console.cpp @@ -36,12 +36,274 @@ #include #include // Qt +#include #include #include namespace KWin { + +static QString tableHeaderRow(const QString &title) +{ + return QStringLiteral("%1").arg(title); +} + +template +static +QString tableRow(const QString &title, const T &argument) +{ + return QStringLiteral("%1%2").arg(title).arg(argument); +} + +static QString timestampRow(quint32 timestamp) +{ + return tableRow(i18n("Timestamp"), timestamp); +} + +static QString buttonToString(Qt::MouseButton button) +{ + switch (button) { + case Qt::LeftButton: + return i18nc("A mouse button", "Left"); + case Qt::RightButton: + return i18nc("A mouse button", "Right"); + case Qt::MiddleButton: + return i18nc("A mouse button", "Middle"); + case Qt::BackButton: + return i18nc("A mouse button", "Back"); + case Qt::ForwardButton: + return i18nc("A mouse button", "Forward"); + case Qt::TaskButton: + return i18nc("A mouse button", "Task"); + case Qt::ExtraButton4: + return i18nc("A mouse button", "Extra Button 4"); + case Qt::ExtraButton5: + return i18nc("A mouse button", "Extra Button 5"); + case Qt::ExtraButton6: + return i18nc("A mouse button", "Extra Button 6"); + case Qt::ExtraButton7: + return i18nc("A mouse button", "Extra Button 7"); + case Qt::ExtraButton8: + return i18nc("A mouse button", "Extra Button 8"); + case Qt::ExtraButton9: + return i18nc("A mouse button", "Extra Button 9"); + case Qt::ExtraButton10: + return i18nc("A mouse button", "Extra Button 10"); + case Qt::ExtraButton11: + return i18nc("A mouse button", "Extra Button 11"); + case Qt::ExtraButton12: + return i18nc("A mouse button", "Extra Button 12"); + case Qt::ExtraButton13: + return i18nc("A mouse button", "Extra Button 13"); + case Qt::ExtraButton14: + return i18nc("A mouse button", "Extra Button 14"); + case Qt::ExtraButton15: + return i18nc("A mouse button", "Extra Button 15"); + case Qt::ExtraButton16: + return i18nc("A mouse button", "Extra Button 16"); + case Qt::ExtraButton17: + return i18nc("A mouse button", "Extra Button 17"); + case Qt::ExtraButton18: + return i18nc("A mouse button", "Extra Button 18"); + case Qt::ExtraButton19: + return i18nc("A mouse button", "Extra Button 19"); + case Qt::ExtraButton20: + return i18nc("A mouse button", "Extra Button 20"); + case Qt::ExtraButton21: + return i18nc("A mouse button", "Extra Button 21"); + case Qt::ExtraButton22: + return i18nc("A mouse button", "Extra Button 22"); + case Qt::ExtraButton23: + return i18nc("A mouse button", "Extra Button 23"); + case Qt::ExtraButton24: + return i18nc("A mouse button", "Extra Button 24"); + default: + return QString(); + } +} + +static QString buttonsToString(Qt::MouseButtons buttons) +{ + QString ret; + for (uint i = 1; i < Qt::ExtraButton24; i = i << 1) { + if (buttons & i) { + ret.append(buttonToString(Qt::MouseButton(uint(buttons) & i))); + ret.append(QStringLiteral(" ")); + } + }; + return ret; +} + +static const QString s_hr = QStringLiteral("
"); +static const QString s_tableStart = QStringLiteral(""); +static const QString s_tableEnd = QStringLiteral("
"); + +DebugConsoleFilter::DebugConsoleFilter(QTextEdit *textEdit) + : InputEventFilter() + , m_textEdit(textEdit) +{ +} + +DebugConsoleFilter::~DebugConsoleFilter() = default; + +bool DebugConsoleFilter::pointerEvent(QMouseEvent *event, quint32 nativeButton) +{ + QString text = s_hr; + const QString timestamp = timestampRow(event->timestamp()); + + text.append(s_tableStart); + switch (event->type()) { + case QEvent::MouseMove: + text.append(tableHeaderRow(i18nc("A mouse pointer motion event", "Pointer Motion"))); + text.append(timestamp); + text.append(tableRow(i18nc("The global mouse pointer position", "Global Position"), QStringLiteral("%1/%2").arg(event->pos().x()).arg(event->pos().y()))); + break; + case QEvent::MouseButtonPress: + text.append(tableHeaderRow(i18nc("A mouse pointer button press event", "Pointer Button Press"))); + text.append(timestamp); + text.append(tableRow(i18nc("A button in a mouse press/release event", "Button"), buttonToString(event->button()))); + text.append(tableRow(i18nc("A button in a mouse press/release event", "Native Button code"), nativeButton)); + text.append(tableRow(i18nc("All currently pressed buttons in a mouse press/release event", "Pressed Buttons"), buttonsToString(event->buttons()))); + break; + case QEvent::MouseButtonRelease: + text.append(tableHeaderRow(i18nc("A mouse pointer button release event", "Pointer Button Release"))); + text.append(timestamp); + text.append(tableRow(i18nc("A button in a mouse press/release event", "Button"), buttonToString(event->button()))); + text.append(tableRow(i18nc("A button in a mouse press/release event", "Native Button code"), nativeButton)); + text.append(tableRow(i18nc("All currently pressed buttons in a mouse press/release event", "Pressed Buttons"), buttonsToString(event->buttons()))); + break; + default: + break; + } + text.append(s_tableEnd); + + m_textEdit->insertHtml(text); + m_textEdit->ensureCursorVisible(); + return false; +} + +bool DebugConsoleFilter::wheelEvent(QWheelEvent *event) +{ + QString text = s_hr; + text.append(s_tableStart); + text.append(tableHeaderRow(i18nc("A mouse pointer axis (wheel) event", "Pointer Axis"))); + text.append(timestampRow(event->timestamp())); + const Qt::Orientation orientation = event->angleDelta().x() == 0 ? Qt::Vertical : Qt::Horizontal; + text.append(tableRow(i18nc("The orientation of a pointer axis event", "Orientation"), + orientation == Qt::Horizontal ? i18nc("An orientation of a pointer axis event", "Horizontal") + : i18nc("An orientation of a pointer axis event", "Vertical"))); + text.append(tableRow(QStringLiteral("Delta"), orientation == Qt::Horizontal ? event->angleDelta().x() : event->angleDelta().y())); + text.append(s_tableEnd); + + m_textEdit->insertHtml(text); + m_textEdit->ensureCursorVisible(); + return false; +} + +bool DebugConsoleFilter::keyEvent(QKeyEvent *event) +{ + QString text = s_hr; + text.append(s_tableStart); + + switch (event->type()) { + case QEvent::KeyPress: + text.append(tableHeaderRow(i18nc("A key press event", "Key Press"))); + break; + case QEvent::KeyRelease: + text.append(tableHeaderRow(i18nc("A key release event", "Key Release"))); + break; + default: + break; + } + auto modifiersToString = [event] { + QString ret; + if (event->modifiers().testFlag(Qt::ShiftModifier)) { + ret.append(i18nc("A keyboard modifier", "Shift")); + ret.append(QStringLiteral(" ")); + } + if (event->modifiers().testFlag(Qt::ControlModifier)) { + ret.append(i18nc("A keyboard modifier", "Control")); + ret.append(QStringLiteral(" ")); + } + if (event->modifiers().testFlag(Qt::AltModifier)) { + ret.append(i18nc("A keyboard modifier", "Alt")); + ret.append(QStringLiteral(" ")); + } + if (event->modifiers().testFlag(Qt::MetaModifier)) { + ret.append(i18nc("A keyboard modifier", "Meta")); + ret.append(QStringLiteral(" ")); + } + if (event->modifiers().testFlag(Qt::KeypadModifier)) { + ret.append(i18nc("A keyboard modifier", "Keypad")); + ret.append(QStringLiteral(" ")); + } + if (event->modifiers().testFlag(Qt::GroupSwitchModifier)) { + ret.append(i18nc("A keyboard modifier", "Group-switch")); + ret.append(QStringLiteral(" ")); + } + return ret; + }; + text.append(timestampRow(event->timestamp())); + text.append(tableRow(i18nc("Whether the event is an automatic key repeat", "Repeat"), event->isAutoRepeat())); + text.append(tableRow(i18nc("The code as read from the input device", "Scan code"), event->nativeScanCode())); + text.append(tableRow(i18nc("The translated code to an Xkb symbol", "Xkb symbol"), event->nativeVirtualKey())); + text.append(tableRow(i18nc("The translated code interpreted as text", "Utf8"), event->text())); + text.append(tableRow(i18nc("The currently active modifiers", "Modifiers"), modifiersToString())); + + text.append(s_tableEnd); + + m_textEdit->insertHtml(text); + m_textEdit->ensureCursorVisible(); + return false; +} + +bool DebugConsoleFilter::touchDown(quint32 id, const QPointF &pos, quint32 time) +{ + QString text = s_hr; + text.append(s_tableStart); + text.append(tableHeaderRow(i18nc("A touch down event", "Touch down"))); + text.append(timestampRow(time)); + text.append(tableRow(i18nc("The id of the touch point in the touch event", "Point identifier"), id)); + text.append(tableRow(i18nc("The global position of the touch point", "Global position"), + QStringLiteral("%1/%2").arg(pos.x()).arg(pos.y()))); + text.append(s_tableEnd); + + m_textEdit->insertHtml(text); + m_textEdit->ensureCursorVisible(); + return false; +} + +bool DebugConsoleFilter::touchMotion(quint32 id, const QPointF &pos, quint32 time) +{ + QString text = s_hr; + text.append(s_tableStart); + text.append(tableHeaderRow(i18nc("A touch motion event", "Touch Motion"))); + text.append(timestampRow(time)); + text.append(tableRow(i18nc("The id of the touch point in the touch event", "Point identifier"), id)); + text.append(tableRow(i18nc("The global position of the touch point", "Global position"), + QStringLiteral("%1/%2").arg(pos.x()).arg(pos.y()))); + text.append(s_tableEnd); + + m_textEdit->insertHtml(text); + m_textEdit->ensureCursorVisible(); + return false; +} + +bool DebugConsoleFilter::touchUp(quint32 id, quint32 time) +{ + QString text = s_hr; + text.append(s_tableStart); + text.append(tableHeaderRow(i18nc("A touch up event", "Touch Up"))); + text.append(timestampRow(time)); + text.append(tableRow(i18nc("The id of the touch point in the touch event", "Point identifier"), id)); + text.append(s_tableEnd); + + m_textEdit->insertHtml(text); + m_textEdit->ensureCursorVisible(); + return false; +} + DebugConsole::DebugConsole() : QWidget() , m_ui(new Ui::DebugConsole) @@ -55,6 +317,7 @@ if (kwinApp()->operationMode() == Application::OperationMode::OperationModeX11) { m_ui->surfacesButton->setDisabled(true); + m_ui->inputEventsButton->setDisabled(true); } connect(m_ui->quitButton, &QAbstractButton::clicked, this, &DebugConsole::deleteLater); @@ -64,20 +327,43 @@ return; } m_ui->surfacesButton->setChecked(false); + m_ui->inputEventsButton->setChecked(false); m_ui->treeView->model()->deleteLater(); m_ui->treeView->setModel(new DebugConsoleModel(this)); + m_ui->treeView->setVisible(true); + m_ui->inputTextEdit->setVisible(false); } ); connect(m_ui->surfacesButton, &QAbstractButton::toggled, this, [this] (bool toggled) { if (!toggled) { return; } m_ui->windowsButton->setChecked(false); + m_ui->inputEventsButton->setChecked(false); m_ui->treeView->model()->deleteLater(); m_ui->treeView->setModel(new SurfaceTreeModel(this)); + m_ui->treeView->setVisible(true); + m_ui->inputTextEdit->setVisible(false); } ); + connect(m_ui->inputEventsButton, &QAbstractButton::toggled, this, + [this] (bool toggled) { + if (!toggled) { + return; + } + m_ui->windowsButton->setChecked(false); + m_ui->surfacesButton->setChecked(false); + m_ui->treeView->setVisible(false); + m_ui->inputTextEdit->setVisible(true); + if (m_inputFilter.isNull()) { + m_inputFilter.reset(new DebugConsoleFilter(m_ui->inputTextEdit)); + input()->prepandInputEventFilter(m_inputFilter.data()); + } + } + ); + + m_ui->inputTextEdit->setVisible(false); // for X11 setWindowFlags(Qt::X11BypassWindowManagerHint); diff --git a/debug_console.ui b/debug_console.ui --- a/debug_console.ui +++ b/debug_console.ui @@ -40,6 +40,16 @@ + + + Input Events + + + true + + + + Qt::Horizontal @@ -68,6 +78,16 @@ + + + + false + + + true + + +