on xenial help sign in
Pay attention to input event timestamps
c61e603Michael Terry
plugins/Utils/windowkeysfilter.cpp+15−1
@@ -21,7 +21,8 @@
21#include <QQuickWindow>
21#include <QQuickWindow>
22
22
23WindowKeysFilter::WindowKeysFilter(QQuickItem *parent)
23WindowKeysFilter::WindowKeysFilter(QQuickItem *parent)
24− : QQuickItem(parent)
24+ : QQuickItem(parent),
25+ m_currentEventTimestamp(0)
25{
26{
26 connect(this, &QQuickItem::windowChanged,
27 connect(this, &QQuickItem::windowChanged,
27 this, &WindowKeysFilter::setupFilterOnWindow);
28 this, &WindowKeysFilter::setupFilterOnWindow);
@@ -36,7 +37,15 @@ bool WindowKeysFilter::eventFilter(QObject *watched, QEvent *event)
36 if (event->type() == QEvent::KeyPress || event->type() == QEvent::KeyRelease) {
37 if (event->type() == QEvent::KeyPress || event->type() == QEvent::KeyRelease) {
37 // Let QML see this event and decide if it does not want it
38 // Let QML see this event and decide if it does not want it
38 event->accept();
39 event->accept();
40+
41+ m_currentEventTimestamp = static_cast<QInputEvent*>(event)->timestamp();
42+ Q_EMIT currentEventTimestampChanged();
43+
39 QCoreApplication::sendEvent(this, event);
44 QCoreApplication::sendEvent(this, event);
45+
46+ m_currentEventTimestamp = 0;
47+ Q_EMIT currentEventTimestampChanged();
48+
40 return event->isAccepted();
49 return event->isAccepted();
41 } else {
50 } else {
42 // Not interested
51 // Not interested
@@ -56,3 +65,8 @@ void WindowKeysFilter::setupFilterOnWindow(QQuickWindow *window)
56 m_filteredWindow = window;
65 m_filteredWindow = window;
57 }
66 }
58}
67}
68+
69+ulong WindowKeysFilter::currentEventTimestamp() const
70+{
71+ return m_currentEventTimestamp;
72+}
plugins/Utils/windowkeysfilter.h+7
@@ -36,16 +36,23 @@
36class WindowKeysFilter : public QQuickItem
36class WindowKeysFilter : public QQuickItem
37{
37{
38 Q_OBJECT
38 Q_OBJECT
39+ Q_PROPERTY(ulong currentEventTimestamp READ currentEventTimestamp NOTIFY currentEventTimestampChanged)
39public:
40public:
40 WindowKeysFilter(QQuickItem *parent = 0);
41 WindowKeysFilter(QQuickItem *parent = 0);
41
42
42 bool eventFilter(QObject *watched, QEvent *event) override;
43 bool eventFilter(QObject *watched, QEvent *event) override;
43
44
45+ ulong currentEventTimestamp() const;
46+
47+Q_SIGNALS:
48+ void currentEventTimestampChanged();
49+
44private Q_SLOTS:
50private Q_SLOTS:
45 void setupFilterOnWindow(QQuickWindow *window);
51 void setupFilterOnWindow(QQuickWindow *window);
46
52
47private:
53private:
48 QPointer<QQuickWindow> m_filteredWindow;
54 QPointer<QQuickWindow> m_filteredWindow;
55+ ulong m_currentEventTimestamp;
49};
56};
50
57
51#endif // UNITY_WINDOWKEYSFILTER_H
58#endif // UNITY_WINDOWKEYSFILTER_H
qml/Components/PhysicalKeysMapper.qml+10−14
@@ -16,6 +16,7 @@
16
16
17import QtQuick 2.0
17import QtQuick 2.0
18import Powerd 0.1
18import Powerd 0.1
19+import Utils 0.1 as Utils
19
20
20/*!
21/*!
21 \brief A mapper for the physical keys on the device
22 \brief A mapper for the physical keys on the device
@@ -42,7 +43,7 @@ Item {
42
43
43 readonly property bool altTabPressed: d.altTabPressed
44 readonly property bool altTabPressed: d.altTabPressed
44
45
45− property int powerKeyLongPressCount: 31 // about 2s
46+ property int powerKeyLongPressTime: 2000
46
47
47 // For testing. If running windowed (e.g. tryShell), Alt+Tab is taken by the
48 // For testing. If running windowed (e.g. tryShell), Alt+Tab is taken by the
48 // running desktop, set this to true to use Ctrl+Tab instead.
49 // running desktop, set this to true to use Ctrl+Tab instead.
@@ -58,25 +59,19 @@ Item {
58 property bool altPressed: false
59 property bool altPressed: false
59 property bool altTabPressed: false
60 property bool altTabPressed: false
60
61
61− property int powerButtonCount
62+ property var powerButtonPressStart: 0
62 }
63 }
63
64
64− function onKeyPressed(event) {
65+ function onKeyPressed(event, currentEventTimestamp) {
65 if (event.key == Qt.Key_PowerDown || event.key == Qt.Key_PowerOff) {
66 if (event.key == Qt.Key_PowerDown || event.key == Qt.Key_PowerOff) {
66 if (event.isAutoRepeat) {
67 if (event.isAutoRepeat) {
67− d.powerButtonCount++;
68+ if (d.powerButtonPressStart > 0
68−
69+ && currentEventTimestamp - d.powerButtonPressStart >= powerKeyLongPressTime) {
69− // We count repeats rather than watch the time because if the
70+ d.powerButtonPressStart = 0;
70− // system is loaded, we may misinterpret a long wall-clock
71− // delay as a long press, when in fact we just didn't get
72− // around to noticing the key-release event in time. But if we
73− // count events instead, we will be in sync with the event
74− // queue.
75− if (d.powerButtonCount === powerKeyLongPressCount) {
76 root.powerKeyLongPressed();
71 root.powerKeyLongPressed();
77 }
72 }
78 } else {
73 } else {
79− d.powerButtonCount = 0;
74+ d.powerButtonPressStart = currentEventTimestamp;
80 }
75 }
81 } else if ((event.key == Qt.Key_MediaTogglePlayPause || event.key == Qt.Key_MediaPlay) && !event.isAutoRepeat) {
76 } else if ((event.key == Qt.Key_MediaTogglePlayPause || event.key == Qt.Key_MediaPlay) && !event.isAutoRepeat) {
82 event.accepted = callManager.handleMediaKey(false);
77 event.accepted = callManager.handleMediaKey(false);
@@ -112,8 +107,9 @@ Item {
112 }
107 }
113 }
108 }
114
109
115− function onKeyReleased(event) {
110+ function onKeyReleased(event, currentEventTimestamp) {
116 if (event.key == Qt.Key_PowerDown || event.key == Qt.Key_PowerOff) {
111 if (event.key == Qt.Key_PowerDown || event.key == Qt.Key_PowerOff) {
112+ d.powerButtonPressStart = 0;
117 event.accepted = true;
113 event.accepted = true;
118 } else if (event.key == Qt.Key_VolumeDown) {
114 } else if (event.key == Qt.Key_VolumeDown) {
119 if (!d.ignoreVolumeEvents) root.volumeDownTriggered();
115 if (!d.ignoreVolumeEvents) root.volumeDownTriggered();
qml/Shell.qml+2−2
@@ -200,8 +200,8 @@ Item {
200 }
200 }
201
201
202 WindowKeysFilter {
202 WindowKeysFilter {
203− Keys.onPressed: physicalKeysMapper.onKeyPressed(event);
203+ Keys.onPressed: physicalKeysMapper.onKeyPressed(event, currentEventTimestamp);
204− Keys.onReleased: physicalKeysMapper.onKeyReleased(event);
204+ Keys.onReleased: physicalKeysMapper.onKeyReleased(event, currentEventTimestamp);
205 }
205 }
206
206
207 HomeKeyWatcher {
207 HomeKeyWatcher {
tests/qmltests/Components/tst_PhysicalKeysMapper.qml+7−10
@@ -65,27 +65,24 @@ TestCase {
65
65
66 function test_powerKeyLongPressed(data) {
66 function test_powerKeyLongPressed(data) {
67 Powerd.setStatus(data.status, Powerd.Unknown);
67 Powerd.setStatus(data.status, Powerd.Unknown);
68− physicalKeysMapper.powerKeyLongPressCount = 5;
68+ physicalKeysMapper.powerKeyLongPressTime = 500;
69
69
70− physicalKeysMapper.onKeyPressed({ key: Qt.Key_PowerDown, isAutoRepeat: false });
70+ physicalKeysMapper.onKeyPressed({ key: Qt.Key_PowerDown, isAutoRepeat: false }, 100);
71
71
72 // After the first key press the screen is always on
72 // After the first key press the screen is always on
73 // and the rest of keypresses are auto repeat
73 // and the rest of keypresses are auto repeat
74 Powerd.setStatus(Powerd.On, Powerd.Unknown);
74 Powerd.setStatus(Powerd.On, Powerd.Unknown);
75
75
76− for (var i = 0; i < 4; ++i) {
76+ physicalKeysMapper.onKeyPressed({ key: Qt.Key_PowerDown, isAutoRepeat: true}, 300);
77+ physicalKeysMapper.onKeyPressed({ key: Qt.Key_PowerDown, isAutoRepeat: true}, 400);
77− physicalKeysMapper.onKeyPressed({ key: Qt.Key_PowerDown, isAutoRepeat: true});
78+ physicalKeysMapper.onKeyPressed({ key: Qt.Key_PowerDown, isAutoRepeat: true}, 500);
78− }
79+ physicalKeysMapper.onKeyPressed({ key: Qt.Key_PowerDown, isAutoRepeat: true}, 599);
79
80
80 // powerKeyLongPressed should not have been emitted yet.
81 // powerKeyLongPressed should not have been emitted yet.
81 compare(powerSpy.count, 0);
82 compare(powerSpy.count, 0);
82
83
83− // Fifth repeat event will trigger emission
84− physicalKeysMapper.onKeyPressed({ key: Qt.Key_PowerDown, isAutoRepeat: true});
84+ physicalKeysMapper.onKeyPressed({ key: Qt.Key_PowerDown, isAutoRepeat: true}, 600);
85− compare(powerSpy.count, 1);
86
85
87− // Make sure we don't double-emit
88− physicalKeysMapper.onKeyPressed({ key: Qt.Key_PowerDown, isAutoRepeat: true});
89 compare(powerSpy.count, 1);
86 compare(powerSpy.count, 1);
90 }
87 }
91
88