Input events mode in debug console

Summary: Inspired by xev a new tool which prints out all input events.

Reviewers: #plasma

Subscribers: plasma-devel

Projects: #plasma

Differential Revision: https://phabricator.kde.org/D1264
This commit is contained in:
Martin Gräßlin 2016-03-30 16:23:58 +02:00
parent c1b709b250
commit 3edf5a7b0a
3 changed files with 328 additions and 0 deletions

View file

@ -36,12 +36,274 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
#include <KLocalizedString>
#include <NETWM>
// Qt
#include <QMouseEvent>
#include <QMetaProperty>
#include <QMetaType>
namespace KWin
{
static QString tableHeaderRow(const QString &title)
{
return QStringLiteral("<tr><th colspan=\"2\">%1</th></tr>").arg(title);
}
template<typename T>
static
QString tableRow(const QString &title, const T &argument)
{
return QStringLiteral("<tr><td>%1</td><td>%2</td></tr>").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("<hr/>");
static const QString s_tableStart = QStringLiteral("<table>");
static const QString s_tableEnd = QStringLiteral("</table>");
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 @@ DebugConsole::DebugConsole()
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,8 +327,11 @@ DebugConsole::DebugConsole()
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,
@ -74,10 +340,30 @@ DebugConsole::DebugConsole()
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);

View file

@ -21,11 +21,14 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
#define KWIN_DEBUG_CONSOLE_H
#include <kwin_export.h>
#include "input.h"
#include <QAbstractItemModel>
#include <QStyledItemDelegate>
#include <QVector>
class QTextEdit;
namespace Ui
{
class DebugConsole;
@ -37,6 +40,7 @@ namespace KWin
class Client;
class ShellClient;
class Unmanaged;
class DebugConsoleFilter;
class KWIN_EXPORT DebugConsoleModel : public QAbstractItemModel
{
@ -98,6 +102,7 @@ public:
private:
QScopedPointer<Ui::DebugConsole> m_ui;
QScopedPointer<DebugConsoleFilter> m_inputFilter;
};
class SurfaceTreeModel : public QAbstractItemModel
@ -114,6 +119,23 @@ public:
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

View file

@ -39,6 +39,16 @@
</property>
</widget>
</item>
<item>
<widget class="QPushButton" name="inputEventsButton">
<property name="text">
<string>Input Events</string>
</property>
<property name="checkable">
<bool>true</bool>
</property>
</widget>
</item>
<item>
<spacer name="horizontalSpacer">
<property name="orientation">
@ -68,6 +78,16 @@
</attribute>
</widget>
</item>
<item>
<widget class="QTextEdit" name="inputTextEdit">
<property name="enabled">
<bool>false</bool>
</property>
<property name="readOnly">
<bool>true</bool>
</property>
</widget>
</item>
</layout>
</widget>
<tabstops>