Mir

Merge lp:~andreas-pokorny/mir/add-key-repeat-utility into lp:mir

Proposed by Andreas Pokorny
Status: Work in progress
Proposed branch: lp:~andreas-pokorny/mir/add-key-repeat-utility
Merge into: lp:mir
Prerequisite: lp:~andreas-pokorny/mir/add-dispatchable-alarm-factory
Diff against target: 407 lines (+340/-11)
6 files modified
include/test/mir/test/doubles/mock_alarm_factory.h (+18/-11)
src/platforms/evdev/CMakeLists.txt (+1/-0)
src/platforms/evdev/key_repeater.cpp (+97/-0)
src/platforms/evdev/key_repeater.h (+64/-0)
tests/unit-tests/input/CMakeLists.txt (+1/-0)
tests/unit-tests/input/test_key_repeater.cpp (+159/-0)
To merge this branch: bzr merge lp:~andreas-pokorny/mir/add-key-repeat-utility
Reviewer Review Type Date Requested Status
Mir CI Bot continuous-integration Needs Fixing
Kevin DuBois (community) Approve
Review via email: mp+289961@code.launchpad.net

Commit message

Add Key repeat utility

Similar to the key press tracking in KeyRepeatDispatcher, this class will support the evdev platform and fake input devices in implementing repeat handling.

Description of the change

A split out of an upcoming branch that moves repeat handling into the input platforms and makes it configureable. In this MP an implementation of the necessary key code tracking is added.

To post a comment you must log in.
Revision history for this message
Kevin DuBois (kdub) wrote :

+ // destructor cancels the alarm
+ ~MockAlarm()
+ {
+ cancel();
+ }
Seems specific to a test

+ if (existing_timer == repeat_alarms_by_scancode.end())
+ existing_timer = repeat_alarms_by_scancode.emplace(
+ std::make_pair(
+ code,
+ alarm_factory->create_alarm(
+ [this, code]()
+ {
+ this->handle_timeout(code);
+ })
+ )
+ ).first;

braces around multi-line if statement

+ mtd::MockAlarm * alarm = new mtd::MockAlarm;
needs delete/unique_ptr?

review: Needs Fixing
Revision history for this message
Kevin DuBois (kdub) wrote :

> + mtd::MockAlarm * alarm = new mtd::MockAlarm;
> needs delete/unique_ptr?
Ah, reading through the mock factory, it seems this gets wrapped with a unique_ptr on invocation.... Still not threadsafe, and will leak if the test fails to call the function.

mir/test/gmock_fixes.h and .WillOnce(Invoke([]{ return std::make_unique<T>(); })) is the best way I've found to work around gmock's difficulty with move-only types.

Revision history for this message
Mir CI Bot (mir-ci-bot) wrote :

PASSED: Continuous integration, rev:3417
https://mir-jenkins.ubuntu.com/job/mir-ci/642/
Executed test runs:
    SUCCESS: https://mir-jenkins.ubuntu.com/job/build-mir/578
    SUCCESS: https://mir-jenkins.ubuntu.com/job/build-0-fetch/614
    SUCCESS: https://mir-jenkins.ubuntu.com/job/build-1-sourcepkg/release=vivid+overlay/606
    SUCCESS: https://mir-jenkins.ubuntu.com/job/build-1-sourcepkg/release=xenial/606
    SUCCESS: https://mir-jenkins.ubuntu.com/job/build-2-binpkg-mir/arch=amd64,compiler=clang,platform=mesa,release=vivid+overlay/588
        deb: https://mir-jenkins.ubuntu.com/job/build-2-binpkg-mir/arch=amd64,compiler=clang,platform=mesa,release=vivid+overlay/588/artifact/output/*zip*/output.zip
    SUCCESS: https://mir-jenkins.ubuntu.com/job/build-2-binpkg-mir/arch=amd64,compiler=gcc,platform=mesa,release=xenial/588
        deb: https://mir-jenkins.ubuntu.com/job/build-2-binpkg-mir/arch=amd64,compiler=gcc,platform=mesa,release=xenial/588/artifact/output/*zip*/output.zip
    SUCCESS: https://mir-jenkins.ubuntu.com/job/build-2-binpkg-mir/arch=cross-armhf,compiler=gcc,platform=android,release=vivid+overlay/588
        deb: https://mir-jenkins.ubuntu.com/job/build-2-binpkg-mir/arch=cross-armhf,compiler=gcc,platform=android,release=vivid+overlay/588/artifact/output/*zip*/output.zip
    SUCCESS: https://mir-jenkins.ubuntu.com/job/build-2-binpkg-mir/arch=i386,compiler=gcc,platform=android,release=vivid+overlay/588
        deb: https://mir-jenkins.ubuntu.com/job/build-2-binpkg-mir/arch=i386,compiler=gcc,platform=android,release=vivid+overlay/588/artifact/output/*zip*/output.zip
    SUCCESS: https://mir-jenkins.ubuntu.com/job/build-2-binpkg-mir/arch=i386,compiler=gcc,platform=mesa,release=xenial/588
        deb: https://mir-jenkins.ubuntu.com/job/build-2-binpkg-mir/arch=i386,compiler=gcc,platform=mesa,release=xenial/588/artifact/output/*zip*/output.zip

Click here to trigger a rebuild:
https://mir-jenkins.ubuntu.com/job/mir-ci/642/rebuild

review: Approve (continuous-integration)
Revision history for this message
Andreas Pokorny (andreas-pokorny) wrote :

> > + mtd::MockAlarm * alarm = new mtd::MockAlarm;
> > needs delete/unique_ptr?
> Ah, reading through the mock factory, it seems this gets wrapped with a
> unique_ptr on invocation.... Still not threadsafe, and will leak if the test
> fails to call the function.

In some of the tests I want to define call expectations.. but when the unique_ptr is created inside the method I dont get access to the object. But I guess in those cases I could still create the Alarm object in advance with make_unique.

> mir/test/gmock_fixes.h and .WillOnce(Invoke([]{ return std::make_unique<T>();
> })) is the best way I've found to work around gmock's difficulty with move-
> only types.

Revision history for this message
Andreas Pokorny (andreas-pokorny) wrote :

> + // destructor cancels the alarm
> + ~MockAlarm()
> + {
> + cancel();
> + }
> Seems specific to a test

But thats the behavior of Alarm objects..

> + if (existing_timer == repeat_alarms_by_scancode.end())
> + existing_timer = repeat_alarms_by_scancode.emplace(
> + std::make_pair(
> + code,
> + alarm_factory->create_alarm(
> + [this, code]()
> + {
> + this->handle_timeout(code);
> + })
> + )
> + ).first;
>
> braces around multi-line if statement
>
> + mtd::MockAlarm * alarm = new mtd::MockAlarm;
> needs delete/unique_ptr?

ack

Revision history for this message
Andreas Pokorny (andreas-pokorny) wrote :

> > > + mtd::MockAlarm * alarm = new mtd::MockAlarm;
> > > needs delete/unique_ptr?
> > Ah, reading through the mock factory, it seems this gets wrapped with a
> > unique_ptr on invocation.... Still not threadsafe, and will leak if the test
> > fails to call the function.
>
> In some of the tests I want to define call expectations.. but when the
> unique_ptr is created inside the method I dont get access to the object. But I
> guess in those cases I could still create the Alarm object in advance with
> make_unique.
>
> > mir/test/gmock_fixes.h and .WillOnce(Invoke([]{ return
> std::make_unique<T>();
> > })) is the best way I've found to work around gmock's difficulty with move-
> > only types.

It does not work here. I had to reorder the expectations to still have a valid Alarm ptr in the test case. But to get the unique_ptr to the caller .. I cannot use Return() with a move only type, and an Invoke action with a mutable lambda.

3418. By Andreas Pokorny

fixed indenting

Revision history for this message
Mir CI Bot (mir-ci-bot) wrote :

FAILED: Continuous integration, rev:3418
https://mir-jenkins.ubuntu.com/job/mir-ci/651/
Executed test runs:
    FAILURE: https://mir-jenkins.ubuntu.com/job/build-mir/592/console
    SUCCESS: https://mir-jenkins.ubuntu.com/job/build-0-fetch/628
    SUCCESS: https://mir-jenkins.ubuntu.com/job/build-1-sourcepkg/release=vivid+overlay/620
    SUCCESS: https://mir-jenkins.ubuntu.com/job/build-1-sourcepkg/release=xenial/620
    SUCCESS: https://mir-jenkins.ubuntu.com/job/build-2-binpkg-mir/arch=amd64,compiler=clang,platform=mesa,release=vivid+overlay/602
        deb: https://mir-jenkins.ubuntu.com/job/build-2-binpkg-mir/arch=amd64,compiler=clang,platform=mesa,release=vivid+overlay/602/artifact/output/*zip*/output.zip
    SUCCESS: https://mir-jenkins.ubuntu.com/job/build-2-binpkg-mir/arch=amd64,compiler=gcc,platform=mesa,release=xenial/602
        deb: https://mir-jenkins.ubuntu.com/job/build-2-binpkg-mir/arch=amd64,compiler=gcc,platform=mesa,release=xenial/602/artifact/output/*zip*/output.zip
    ABORTED: https://mir-jenkins.ubuntu.com/job/build-2-binpkg-mir/arch=cross-armhf,compiler=gcc,platform=android,release=vivid+overlay/602/console
        deb: https://mir-jenkins.ubuntu.com/job/build-2-binpkg-mir/arch=cross-armhf,compiler=gcc,platform=android,release=vivid+overlay/602/artifact/output/*zip*/output.zip
    SUCCESS: https://mir-jenkins.ubuntu.com/job/build-2-binpkg-mir/arch=i386,compiler=gcc,platform=android,release=vivid+overlay/602
        deb: https://mir-jenkins.ubuntu.com/job/build-2-binpkg-mir/arch=i386,compiler=gcc,platform=android,release=vivid+overlay/602/artifact/output/*zip*/output.zip
    SUCCESS: https://mir-jenkins.ubuntu.com/job/build-2-binpkg-mir/arch=i386,compiler=gcc,platform=mesa,release=xenial/602
        deb: https://mir-jenkins.ubuntu.com/job/build-2-binpkg-mir/arch=i386,compiler=gcc,platform=mesa,release=xenial/602/artifact/output/*zip*/output.zip

Click here to trigger a rebuild:
https://mir-jenkins.ubuntu.com/job/mir-ci/651/rebuild

review: Needs Fixing (continuous-integration)
Revision history for this message
Mir CI Bot (mir-ci-bot) wrote :

PASSED: Continuous integration, rev:3418
https://mir-jenkins.ubuntu.com/job/mir-ci/667/
Executed test runs:
    SUCCESS: https://mir-jenkins.ubuntu.com/job/build-mir/614
    SUCCESS: https://mir-jenkins.ubuntu.com/job/build-0-fetch/651
    SUCCESS: https://mir-jenkins.ubuntu.com/job/build-1-sourcepkg/release=vivid+overlay/643
    SUCCESS: https://mir-jenkins.ubuntu.com/job/build-1-sourcepkg/release=xenial/643
    SUCCESS: https://mir-jenkins.ubuntu.com/job/build-2-binpkg-mir/arch=amd64,compiler=clang,platform=mesa,release=vivid+overlay/624
        deb: https://mir-jenkins.ubuntu.com/job/build-2-binpkg-mir/arch=amd64,compiler=clang,platform=mesa,release=vivid+overlay/624/artifact/output/*zip*/output.zip
    SUCCESS: https://mir-jenkins.ubuntu.com/job/build-2-binpkg-mir/arch=amd64,compiler=gcc,platform=mesa,release=xenial/624
        deb: https://mir-jenkins.ubuntu.com/job/build-2-binpkg-mir/arch=amd64,compiler=gcc,platform=mesa,release=xenial/624/artifact/output/*zip*/output.zip
    SUCCESS: https://mir-jenkins.ubuntu.com/job/build-2-binpkg-mir/arch=cross-armhf,compiler=gcc,platform=android,release=vivid+overlay/624
        deb: https://mir-jenkins.ubuntu.com/job/build-2-binpkg-mir/arch=cross-armhf,compiler=gcc,platform=android,release=vivid+overlay/624/artifact/output/*zip*/output.zip
    SUCCESS: https://mir-jenkins.ubuntu.com/job/build-2-binpkg-mir/arch=i386,compiler=gcc,platform=android,release=vivid+overlay/624
        deb: https://mir-jenkins.ubuntu.com/job/build-2-binpkg-mir/arch=i386,compiler=gcc,platform=android,release=vivid+overlay/624/artifact/output/*zip*/output.zip
    SUCCESS: https://mir-jenkins.ubuntu.com/job/build-2-binpkg-mir/arch=i386,compiler=gcc,platform=mesa,release=xenial/624
        deb: https://mir-jenkins.ubuntu.com/job/build-2-binpkg-mir/arch=i386,compiler=gcc,platform=mesa,release=xenial/624/artifact/output/*zip*/output.zip

Click here to trigger a rebuild:
https://mir-jenkins.ubuntu.com/job/mir-ci/667/rebuild

review: Approve (continuous-integration)
Revision history for this message
Kevin DuBois (kdub) wrote :

> It does not work here. I had to reorder the expectations to still have a valid
> Alarm ptr in the test case. But to get the unique_ptr to the caller .. I
> cannot use Return() with a move only type, and an Invoke action with a mutable
> lambda.

can merge this branch in, makes things a bit more raii in the test: lp:~kdub/mir/key-repeat-gmock-fixes

Revision history for this message
Andreas Pokorny (andreas-pokorny) wrote :

wow.. for some reason I always tried binding the alarm object by value to lambda.
Works fine. Thanks!

3419. By Andreas Pokorny

merge prereq

3420. By Andreas Pokorny

Merge improvement from kdub..

Revision history for this message
Kevin DuBois (kdub) wrote :

lgtm

review: Approve
Revision history for this message
Mir CI Bot (mir-ci-bot) wrote :
review: Needs Fixing (continuous-integration)
3421. By Andreas Pokorny

merge prereq to resolve conflict

Revision history for this message
Mir CI Bot (mir-ci-bot) wrote :

FAILED: Continuous integration, rev:3421
https://mir-jenkins.ubuntu.com/job/mir-ci/716/
Executed test runs:
    FAILURE: https://mir-jenkins.ubuntu.com/job/build-mir/680/console
    SUCCESS: https://mir-jenkins.ubuntu.com/job/build-0-fetch/717
    SUCCESS: https://mir-jenkins.ubuntu.com/job/build-1-sourcepkg/release=vivid+overlay/708
    SUCCESS: https://mir-jenkins.ubuntu.com/job/build-1-sourcepkg/release=xenial/708
    FAILURE: https://mir-jenkins.ubuntu.com/job/build-2-binpkg-mir/arch=amd64,compiler=clang,platform=mesa,release=vivid+overlay/689/console
    SUCCESS: https://mir-jenkins.ubuntu.com/job/build-2-binpkg-mir/arch=amd64,compiler=gcc,platform=mesa,release=xenial/689
        deb: https://mir-jenkins.ubuntu.com/job/build-2-binpkg-mir/arch=amd64,compiler=gcc,platform=mesa,release=xenial/689/artifact/output/*zip*/output.zip
    SUCCESS: https://mir-jenkins.ubuntu.com/job/build-2-binpkg-mir/arch=cross-armhf,compiler=gcc,platform=android,release=vivid+overlay/689
        deb: https://mir-jenkins.ubuntu.com/job/build-2-binpkg-mir/arch=cross-armhf,compiler=gcc,platform=android,release=vivid+overlay/689/artifact/output/*zip*/output.zip
    SUCCESS: https://mir-jenkins.ubuntu.com/job/build-2-binpkg-mir/arch=i386,compiler=gcc,platform=android,release=vivid+overlay/689
        deb: https://mir-jenkins.ubuntu.com/job/build-2-binpkg-mir/arch=i386,compiler=gcc,platform=android,release=vivid+overlay/689/artifact/output/*zip*/output.zip
    SUCCESS: https://mir-jenkins.ubuntu.com/job/build-2-binpkg-mir/arch=i386,compiler=gcc,platform=mesa,release=xenial/689
        deb: https://mir-jenkins.ubuntu.com/job/build-2-binpkg-mir/arch=i386,compiler=gcc,platform=mesa,release=xenial/689/artifact/output/*zip*/output.zip

Click here to trigger a rebuild:
https://mir-jenkins.ubuntu.com/job/mir-ci/716/rebuild

review: Needs Fixing (continuous-integration)

Unmerged revisions

3421. By Andreas Pokorny

merge prereq to resolve conflict

3420. By Andreas Pokorny

Merge improvement from kdub..

3419. By Andreas Pokorny

merge prereq

3418. By Andreas Pokorny

fixed indenting

3417. By Andreas Pokorny

merge prereq

3416. By Andreas Pokorny

Add accessors

3415. By Andreas Pokorny

Add Key repeat utility

Similar to the key press tracking in KeyRepeatDispatcher, this class will support the evdev platform and fake input devices in implementing repeat handling.

3414. By Andreas Pokorny

Add mir::dispatch::AlarmFactory

Implementation of mir::time::AlarmFactory that works with mir::dispatch::Dispatchable and supports all the state transitions required by the test suite accumulated in test_glib_main_loop.cpp.

With this change several GPL interfaces defined inside the mirserver API have been moved to mircommon and LGPL.

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1=== modified file 'include/test/mir/test/doubles/mock_alarm_factory.h'
2--- include/test/mir/test/doubles/mock_alarm_factory.h 2016-03-31 18:14:16 +0000
3+++ include/test/mir/test/doubles/mock_alarm_factory.h 2016-03-31 18:14:16 +0000
4@@ -20,6 +20,8 @@
5 #define MIR_TEST_DOUBLES_MOCK_ALARM_FACTORY_H
6
7 #include "mir/time/alarm_factory.h"
8+#include "mir/time/alarm.h"
9+#include "mir/test/gmock_fixes.h"
10
11 namespace mir
12 {
13@@ -28,19 +30,24 @@
14 namespace doubles
15 {
16
17+struct MockAlarm : public mir::time::Alarm
18+{
19+ MOCK_METHOD0(cancel, bool());
20+ MOCK_CONST_METHOD0(state, mir::time::Alarm::State());
21+ MOCK_METHOD1(reschedule_in, bool(std::chrono::milliseconds));
22+ MOCK_METHOD1(reschedule_for, bool(mir::time::Timestamp));
23+
24+ // destructor cancels the alarm
25+ ~MockAlarm()
26+ {
27+ cancel();
28+ }
29+};
30+
31 struct MockAlarmFactory : public mir::time::AlarmFactory
32 {
33- MOCK_METHOD1(create_alarm_adapter, mir::time::Alarm*(std::function<void()> const&));
34- MOCK_METHOD1(create_lockable_alarm_adapter, mir::time::Alarm*(std::shared_ptr<mir::LockableCallback> const&));
35- std::unique_ptr<mir::time::Alarm> create_alarm(std::function<void()> const& cb)
36- {
37- return std::unique_ptr<mir::time::Alarm>(create_alarm_adapter(cb));
38- }
39-
40- std::unique_ptr<mir::time::Alarm> create_alarm(std::shared_ptr<mir::LockableCallback> const& lcb)
41- {
42- return std::unique_ptr<mir::time::Alarm>(create_lockable_alarm_adapter(lcb));
43- }
44+ MOCK_METHOD1(create_alarm, std::unique_ptr<time::Alarm>(std::function<void()> const&));
45+ MOCK_METHOD1(create_alarm, std::unique_ptr<time::Alarm>(std::shared_ptr<mir::LockableCallback> const&));
46 };
47
48 }
49
50=== modified file 'src/platforms/evdev/CMakeLists.txt'
51--- src/platforms/evdev/CMakeLists.txt 2016-03-23 06:39:56 +0000
52+++ src/platforms/evdev/CMakeLists.txt 2016-03-31 18:14:16 +0000
53@@ -18,6 +18,7 @@
54 add_library(mirevdevutilsobjects OBJECT
55 evdev_device_detection.cpp
56 button_utils.cpp
57+ key_repeater.cpp
58 )
59
60 add_library(mirplatforminputevdevobjects OBJECT
61
62=== added file 'src/platforms/evdev/key_repeater.cpp'
63--- src/platforms/evdev/key_repeater.cpp 1970-01-01 00:00:00 +0000
64+++ src/platforms/evdev/key_repeater.cpp 2016-03-31 18:14:16 +0000
65@@ -0,0 +1,97 @@
66+/*
67+ * Copyright © 2016 Canonical Ltd.
68+ *
69+ * This program is free software: you can redistribute it and/or modify it
70+ * under the terms of the GNU General Public License version 3,
71+ * as published by the Free Software Foundation.
72+ *
73+ * This program is distributed in the hope that it will be useful,
74+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
75+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
76+ * GNU General Public License for more details.
77+ *
78+ * You should have received a copy of the GNU General Public License
79+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
80+ *
81+ * Authored by: Andreas Pokorny <andreas.pokorny@canonical.com>
82+ */
83+
84+#include "key_repeater.h"
85+
86+#include "mir/time/alarm_factory.h"
87+#include "mir/time/alarm.h"
88+
89+namespace mie = mir::input::evdev;
90+namespace mt = mir::time;
91+
92+mie::KeyRepeater::KeyRepeater(std::shared_ptr<mir::time::AlarmFactory> const& alarm_factory, std::function<void(int32_t keycode)> const& send_repeat_key)
93+ : send_repeat{send_repeat_key}, alarm_factory{alarm_factory}
94+{
95+}
96+
97+void mie::KeyRepeater::enable(std::chrono::milliseconds repeat_delay, std::chrono::milliseconds repeat_interval)
98+{
99+ enabled_ = true;
100+ this->repeat_delay = repeat_delay;
101+ this->repeat_interval = repeat_interval;
102+}
103+
104+void mie::KeyRepeater::disable()
105+{
106+ enabled_ = false;
107+ repeat_alarms_by_scancode.clear();
108+}
109+
110+void mie::KeyRepeater::press(int32_t code)
111+{
112+ if (enabled_)
113+ {
114+ auto existing_timer = repeat_alarms_by_scancode.find(code);
115+ if (existing_timer == repeat_alarms_by_scancode.end())
116+ {
117+ existing_timer = repeat_alarms_by_scancode.emplace(
118+ std::make_pair(
119+ code,
120+ alarm_factory->create_alarm(
121+ [this, code]()
122+ {
123+ this->handle_timeout(code);
124+ })
125+ )
126+ ).first;
127+ }
128+ existing_timer->second->reschedule_in(repeat_delay);
129+ }
130+}
131+
132+void mie::KeyRepeater::release(int32_t code)
133+{
134+ repeat_alarms_by_scancode.erase(code);
135+}
136+
137+void mie::KeyRepeater::handle_timeout(int32_t code)
138+{
139+ if (enabled_)
140+ {
141+ auto existing_timer = repeat_alarms_by_scancode.find(code);
142+ if (existing_timer != repeat_alarms_by_scancode.end())
143+ existing_timer->second->reschedule_in(repeat_interval);
144+
145+ send_repeat(code);
146+ }
147+}
148+
149+bool mie::KeyRepeater::enabled() const
150+{
151+ return enabled_;
152+}
153+
154+std::chrono::milliseconds mie::KeyRepeater::delay() const
155+{
156+ return repeat_delay;
157+}
158+
159+std::chrono::milliseconds mie::KeyRepeater::interval() const
160+{
161+ return repeat_interval;
162+}
163
164=== added file 'src/platforms/evdev/key_repeater.h'
165--- src/platforms/evdev/key_repeater.h 1970-01-01 00:00:00 +0000
166+++ src/platforms/evdev/key_repeater.h 2016-03-31 18:14:16 +0000
167@@ -0,0 +1,64 @@
168+/*
169+ * Copyright © 2016 Canonical Ltd.
170+ *
171+ * This program is free software: you can redistribute it and/or modify it
172+ * under the terms of the GNU General Public License version 3,
173+ * as published by the Free Software Foundation.
174+ *
175+ * This program is distributed in the hope that it will be useful,
176+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
177+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
178+ * GNU General Public License for more details.
179+ *
180+ * You should have received a copy of the GNU General Public License
181+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
182+ *
183+ * Authored by: Andreas Pokorny <andreas.pokorny@canonical.com>
184+ */
185+
186+#ifndef MIR_INPUT_EVDEV_KEY_REPEATER_H_
187+#define MIR_INPUT_EVDEV_KEY_REPEATER_H_
188+
189+#include <memory>
190+#include <chrono>
191+#include <unordered_map>
192+#include <functional>
193+
194+namespace mir
195+{
196+namespace time
197+{
198+class Alarm;
199+class AlarmFactory;
200+}
201+namespace input
202+{
203+namespace evdev
204+{
205+
206+struct KeyRepeater
207+{
208+ KeyRepeater(std::shared_ptr<mir::time::AlarmFactory> const& alarm_factory, std::function<void(int32_t keycode)> const& send_repeat_key);
209+ void enable(std::chrono::milliseconds repeat_delay, std::chrono::milliseconds repeat_interval);
210+ void disable();
211+ bool enabled() const;
212+ std::chrono::milliseconds delay() const;
213+ std::chrono::milliseconds interval() const;
214+ void press(int32_t code);
215+ void release(int32_t code);
216+
217+private:
218+ void handle_timeout(int32_t code);
219+ bool enabled_{true};
220+
221+ std::chrono::milliseconds repeat_delay{500};
222+ std::chrono::milliseconds repeat_interval{50};
223+
224+ std::function<void(int32_t)> send_repeat;
225+ std::unordered_map<int32_t, std::unique_ptr<mir::time::Alarm>> repeat_alarms_by_scancode;
226+ std::shared_ptr<mir::time::AlarmFactory> const alarm_factory;
227+};
228+}
229+}
230+}
231+#endif
232
233=== modified file 'tests/unit-tests/input/CMakeLists.txt'
234--- tests/unit-tests/input/CMakeLists.txt 2016-03-23 06:39:56 +0000
235+++ tests/unit-tests/input/CMakeLists.txt 2016-03-31 18:14:16 +0000
236@@ -15,6 +15,7 @@
237 ${CMAKE_CURRENT_SOURCE_DIR}/test_surface_input_dispatcher.cpp
238 ${CMAKE_CURRENT_SOURCE_DIR}/test_seat_input_device_tracker.cpp
239 ${CMAKE_CURRENT_SOURCE_DIR}/test_key_repeat_dispatcher.cpp
240+ ${CMAKE_CURRENT_SOURCE_DIR}/test_key_repeater.cpp
241 ${CMAKE_CURRENT_SOURCE_DIR}/test_validator.cpp
242 )
243
244
245=== added file 'tests/unit-tests/input/test_key_repeater.cpp'
246--- tests/unit-tests/input/test_key_repeater.cpp 1970-01-01 00:00:00 +0000
247+++ tests/unit-tests/input/test_key_repeater.cpp 2016-03-31 18:14:16 +0000
248@@ -0,0 +1,159 @@
249+/*
250+ * Copyright © 2016 Canonical Ltd.
251+ *
252+ * This program is free software: you can redistribute it and/or modify
253+ * it under the terms of the GNU General Public License version 3 as
254+ * published by the Free Software Foundation.
255+ *
256+ * This program is distributed in the hope that it will be useful,
257+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
258+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
259+ * GNU General Public License for more details.
260+ *
261+ * You should have received a copy of the GNU General Public License
262+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
263+ *
264+ * Authored by: Andreas Pokorny <andreas.pokorny@canonical.com>
265+ */
266+
267+#include "src/platforms/evdev/key_repeater.h"
268+#include "mir/test/doubles/mock_alarm_factory.h"
269+#include "mir/test/fake_shared.h"
270+
271+#include <gtest/gtest.h>
272+#include <gmock/gmock.h>
273+
274+namespace mt = mir::test;
275+namespace mtd = mt::doubles;
276+namespace mie = mir::input::evdev;
277+
278+using namespace ::testing;
279+using namespace std::chrono_literals;
280+
281+namespace
282+{
283+struct KeyRepeater : Test
284+{
285+ MOCK_METHOD1(repeat_called, void(int32_t));
286+
287+ NiceMock<mtd::MockAlarmFactory> mock_factory;
288+ mie::KeyRepeater repeater{
289+ mt::fake_shared(mock_factory),
290+ [this](int32_t code)
291+ {
292+ repeat_called(code);
293+ }};
294+
295+ const int32_t a_key = 32;
296+ const int32_t another_key = 33;
297+};
298+}
299+
300+TEST_F(KeyRepeater, no_repeat_alarm_when_disabled)
301+{
302+ repeater.disable();
303+ EXPECT_CALL(mock_factory, create_alarm(An<std::function<void()> const&>())).Times(0);
304+
305+ repeater.press(a_key);
306+ repeater.release(a_key);
307+}
308+
309+TEST_F(KeyRepeater, repeat_enabled_by_default)
310+{
311+ auto alarm = std::make_unique<mtd::MockAlarm>();
312+ EXPECT_CALL(*alarm, reschedule_in(_)).Times(1);
313+ EXPECT_CALL(mock_factory, create_alarm(An<std::function<void()> const&>()))
314+ .Times(1)
315+ .WillOnce(Invoke([&](std::function<void()> const&){ return std::move(alarm); }));
316+
317+ repeater.press(a_key);
318+}
319+
320+TEST_F(KeyRepeater, alarm_canceled_on_disable)
321+{
322+ auto alarm = std::make_unique<mtd::MockAlarm>();
323+ EXPECT_CALL(*alarm, cancel()).Times(1);
324+ EXPECT_CALL(mock_factory, create_alarm(An<std::function<void()> const&>()))
325+ .Times(1)
326+ .WillOnce(Invoke([&](std::function<void()> const&){ return std::move(alarm); }));
327+
328+ repeater.press(a_key);
329+ repeater.disable();
330+}
331+
332+TEST_F(KeyRepeater, alarm_canceled_on_release)
333+{
334+ auto alarm = std::make_unique<mtd::MockAlarm>();
335+ EXPECT_CALL(*alarm, reschedule_in(_)).Times(1);
336+ EXPECT_CALL(*alarm, cancel()).Times(1);
337+ EXPECT_CALL(mock_factory, create_alarm(An<std::function<void()> const&>()))
338+ .Times(1)
339+ .WillOnce(Invoke([&](std::function<void()> const&){ return std::move(alarm); }));
340+
341+ repeater.press(a_key);
342+ repeater.release(a_key);
343+}
344+
345+TEST_F(KeyRepeater, tracks_key_code)
346+{
347+ auto alarm = std::make_unique<mtd::MockAlarm>();
348+ auto second_alarm = std::make_unique<mtd::MockAlarm>();
349+
350+ Sequence seq1;
351+ Sequence seq2;
352+ EXPECT_CALL(mock_factory, create_alarm(An<std::function<void()> const&>()))
353+ .InSequence(seq1)
354+ .WillOnce(Invoke([&](std::function<void()> const&){ return std::move(alarm); }));
355+ EXPECT_CALL(*alarm, reschedule_in(_))
356+ .InSequence(seq2);
357+
358+ EXPECT_CALL(mock_factory, create_alarm(An<std::function<void()> const&>()))
359+ .InSequence(seq1)
360+ .WillOnce(Invoke([&](std::function<void()> const&){ return std::move(second_alarm); }));
361+ EXPECT_CALL(*second_alarm, reschedule_in(_))
362+ .InSequence(seq2);
363+ EXPECT_CALL(*second_alarm, cancel())
364+ .InSequence(seq2);
365+
366+ EXPECT_CALL(*alarm, cancel())
367+ .InSequence(seq2);
368+
369+ repeater.press(a_key);
370+ repeater.press(another_key);
371+ repeater.release(another_key);
372+ repeater.release(a_key);
373+}
374+
375+TEST_F(KeyRepeater, alarm_rescheduled_on_timeout)
376+{
377+ auto alarm = std::make_unique<mtd::MockAlarm>();
378+ std::function<void()> timer_callback;
379+ EXPECT_CALL(*this, repeat_called(a_key)).Times(1);
380+ EXPECT_CALL(*alarm, reschedule_in(50ms)).Times(1);
381+ EXPECT_CALL(*alarm, reschedule_in(10ms)).Times(1);
382+
383+ EXPECT_CALL(mock_factory, create_alarm(An<std::function<void()> const&>()))
384+ .Times(1)
385+ .WillOnce(Invoke([&](std::function<void()> const& callback){
386+ timer_callback = callback;
387+ return std::move(alarm); }));
388+
389+ repeater.enable(50ms, 10ms);
390+ repeater.press(a_key);
391+ timer_callback();
392+}
393+
394+TEST_F(KeyRepeater, repeat_called_on_timeout)
395+{
396+ auto alarm = std::make_unique<mtd::MockAlarm>();
397+ std::function<void()> timer_callback;
398+ EXPECT_CALL(*this, repeat_called(a_key)).Times(1);
399+ EXPECT_CALL(mock_factory, create_alarm(An<std::function<void()> const&>()))
400+ .Times(1)
401+ .WillOnce(Invoke([&](std::function<void()> const& callback){
402+ timer_callback = callback;
403+ return std::move(alarm); }));
404+
405+ repeater.press(a_key);
406+ timer_callback();
407+}

Subscribers

People subscribed via source and target branches