Mir

Merge lp:~andreas-pokorny/mir/move-key-repeat-into-input-platforms into lp:mir

Proposed by Andreas Pokorny
Status: Work in progress
Proposed branch: lp:~andreas-pokorny/mir/move-key-repeat-into-input-platforms
Merge into: lp:mir
Prerequisite: lp:~andreas-pokorny/mir/add-key-repeat-utility
Diff against target: 1902 lines (+514/-581)
31 files modified
include/platform/mir/input/input_device.h (+4/-0)
include/platform/mir/input/keyboard_settings.h (+42/-0)
src/platforms/evdev/key_repeater.cpp (+2/-0)
src/platforms/evdev/key_repeater.h (+1/-0)
src/platforms/evdev/libinput_device.cpp (+45/-5)
src/platforms/evdev/libinput_device.h (+15/-5)
src/platforms/evdev/platform.cpp (+15/-7)
src/platforms/evdev/platform.h (+7/-0)
src/platforms/mesa/server/x11/input/input_device.cpp (+52/-3)
src/platforms/mesa/server/x11/input/input_device.h (+5/-1)
src/platforms/mesa/server/x11/input/input_platform.cpp (+9/-12)
src/server/input/CMakeLists.txt (+0/-1)
src/server/input/default_configuration.cpp (+7/-26)
src/server/input/default_input_device_hub.cpp (+12/-2)
src/server/input/default_input_device_hub.h (+3/-1)
src/server/input/key_repeat_dispatcher.cpp (+0/-186)
src/server/input/key_repeat_dispatcher.h (+0/-84)
tests/include/mir/test/doubles/mock_input_device.h (+3/-0)
tests/include/mir/test/doubles/mock_x11.h (+8/-0)
tests/include/mir_test_framework/stub_input_platform.h (+15/-0)
tests/mir_test_doubles/mock_input_device.cpp (+8/-0)
tests/mir_test_doubles/mock_x11.cpp (+35/-0)
tests/mir_test_framework/fake_input_device_impl.cpp (+54/-14)
tests/mir_test_framework/fake_input_device_impl.h (+15/-1)
tests/mir_test_framework/stub_input_platform.cpp (+21/-0)
tests/unit-tests/input/CMakeLists.txt (+0/-1)
tests/unit-tests/input/evdev/test_evdev_device_detection.cpp (+8/-1)
tests/unit-tests/input/evdev/test_libinput_device.cpp (+42/-10)
tests/unit-tests/input/test_default_device.cpp (+6/-37)
tests/unit-tests/input/test_key_repeat_dispatcher.cpp (+0/-184)
tests/unit-tests/input/test_x11_platform.cpp (+80/-0)
To merge this branch: bzr merge lp:~andreas-pokorny/mir/move-key-repeat-into-input-platforms
Reviewer Review Type Date Requested Status
Daniel van Vugt Needs Information
Kevin DuBois (community) Approve
Mir CI Bot continuous-integration Needs Fixing
Brandon Schaefer (community) Approve
Review via email: mp+289963@code.launchpad.net

Commit message

Introduces KeyboardSettings to configure key repeat and moves the responsibility out to input platforms

X11 forwards the settings to Xkb, while evdev and stub input implement the repeat handling via the KeyRepeater introduced in a previous change. With that the key repeat is also closer to be being runtime configureable. (wiring to mir::input::Device or the client API still outstanding).

Description of the change

This moves the key repeat tracking into the input platform. With that the dynamic_pointer_cast and the two step initialization required by the KeyRepeatDispatcher is no longer necessary.

Kludge: The mir option MIR_SERVER_KEY_REPEAT is still supported, by letting DefaultInputDeviceHub configure the default setting... That code cries out to be moved somewhere else...

To post a comment you must log in.
Revision history for this message
Mir CI Bot (mir-ci-bot) wrote :
review: Needs Fixing (continuous-integration)
Revision history for this message
Mir CI Bot (mir-ci-bot) wrote :
review: Needs Fixing (continuous-integration)
Revision history for this message
Brandon Schaefer (brandontschaefer) wrote :

Yay for deleting code!

+ std::shared_ptr<mir::time::Clock> const& clock)
+ : report{report},

+ std::unique_ptr<udev::Context>&& udev_context)
+ : report(report),

+mix::XInputDevice::XInputDevice(InputDeviceInfo const& device_info, std::shared_ptr<::Display> const& display)
+ : display{display}, info(device_info)

+ std::shared_ptr<::Display> const& conn)
+ : x11_connection{conn},

The : needs to go on the same line as the ctor (at lease according to the coding style). I see most of those were on the init list line before this branch. Feel free to fix them (just a nit).

+class Alarm;
I dont see that namespace used? (Unless i missed it :). Do we need this forward dec?

Do we just not care about the key_code here?
 sink->handle_input(*builder->key_event(event_time, mir_keyboard_action_repeat, 0, code));

Changes make sense.

review: Approve
3422. By Andreas Pokorny

removed fwd decl

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

> Yay for deleting code!
>
> + std::shared_ptr<mir::time::Clock> const&
> clock)
> + : report{report},
>
> + std::unique_ptr<udev::Context>&& udev_context)
> + : report(report),
>
> +mix::XInputDevice::XInputDevice(InputDeviceInfo const& device_info,
> std::shared_ptr<::Display> const& display)
> + : display{display}, info(device_info)
>
> + std::shared_ptr<::Display> const& conn)
> + : x11_connection{conn},
>
> The : needs to go on the same line as the ctor (at lease according to the
> coding style). I see most of those were on the init list line before this
> branch. Feel free to fix them (just a nit).

We have both variants in the style guide - for some reason our current clang-format setup seems to prefer the version above..

>
> +class Alarm;
> I dont see that namespace used? (Unless i missed it :). Do we need this
> forward dec?

ack removed.

> Do we just not care about the key_code here?
> sink->handle_input(*builder->key_event(event_time,
> mir_keyboard_action_repeat, 0, code));

I guess testing that the actual key code is repeated might be reasonable thing to test..

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

(Besides the conflicts, this looks good to me :)

review: Approve
3423. By Andreas Pokorny

merge prereq

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

nits:
+ KeyboardSettings() {}
not needed

1122: + settings.acceleration = mir_pointer_acceleration_none;
why indent further?

1031: braces around multiline if/else

lgtm otherwise

review: Approve
Revision history for this message
Daniel van Vugt (vanvugt) wrote :

The prerequisite branch is on hold. So should that make this one on hold too?

Revision history for this message
Daniel van Vugt (vanvugt) :
review: Needs Information

Unmerged revisions

3423. By Andreas Pokorny

merge prereq

3422. By Andreas Pokorny

removed fwd decl

3421. By Andreas Pokorny

update libinput test fixture

3420. By Andreas Pokorny

Update X11 Unit tests

3419. By Andreas Pokorny

add missing file - KeyboardSettings

3418. By Andreas Pokorny

merge prereq

3417. By Andreas Pokorny

Integrate Key repeater into stub intput platform

3416. By Andreas Pokorny

Integrate Key Repeater

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
=== modified file 'include/platform/mir/input/input_device.h'
--- include/platform/mir/input/input_device.h 2016-01-29 08:18:22 +0000
+++ include/platform/mir/input/input_device.h 2016-03-31 19:18:58 +0000
@@ -39,6 +39,7 @@
3939
40class PointerSettings;40class PointerSettings;
41class TouchpadSettings;41class TouchpadSettings;
42class KeyboardSettings;
4243
43/**44/**
44 * Represents an input device.45 * Represents an input device.
@@ -65,6 +66,9 @@
6566
66 virtual optional_value<TouchpadSettings> get_touchpad_settings() const = 0;67 virtual optional_value<TouchpadSettings> get_touchpad_settings() const = 0;
67 virtual void apply_settings(TouchpadSettings const&) = 0;68 virtual void apply_settings(TouchpadSettings const&) = 0;
69
70 virtual optional_value<KeyboardSettings> get_keyboard_settings() const = 0;
71 virtual void apply_settings(KeyboardSettings const&) = 0;
68protected:72protected:
69 InputDevice(InputDevice const&) = delete;73 InputDevice(InputDevice const&) = delete;
70 InputDevice& operator=(InputDevice const&) = delete;74 InputDevice& operator=(InputDevice const&) = delete;
7175
=== added file 'include/platform/mir/input/keyboard_settings.h'
--- include/platform/mir/input/keyboard_settings.h 1970-01-01 00:00:00 +0000
+++ include/platform/mir/input/keyboard_settings.h 2016-03-31 19:18:58 +0000
@@ -0,0 +1,42 @@
1/*
2 * Copyright © 2016 Canonical Ltd.
3 *
4 * This program is free software: you can redistribute it and/or modify it
5 * under the terms of the GNU Lesser General Public License version 3,
6 * as published by the Free Software Foundation.
7 *
8 * This program is distributed in the hope that it will be useful,
9 * but WITHOUT ANY WARRANTY; without even the implied warranty of
10 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 * GNU Lesser General Public License for more details.
12 *
13 * You should have received a copy of the GNU Lesser General Public License
14 * along with this program. If not, see <http://www.gnu.org/licenses/>.
15 *
16 * Authored by:
17 * Andreas Pokorny <andreas.pokorny@canonical.com>
18 */
19
20#ifndef MIR_INPUT_KEY_BOARD_SETTINGS_H_
21#define MIR_INPUT_KEY_BOARD_SETTINGS_H_
22
23#include "mir_toolkit/mir_input_device.h"
24#include <chrono>
25
26namespace mir
27{
28namespace input
29{
30
31struct KeyboardSettings
32{
33 KeyboardSettings() {}
34 bool repeat_enabled{true};
35 std::chrono::milliseconds repeat_delay{50};
36 std::chrono::milliseconds repeat_interval{500};
37};
38
39}
40}
41
42#endif
043
=== modified file 'src/platforms/evdev/key_repeater.cpp'
--- src/platforms/evdev/key_repeater.cpp 2016-03-31 19:18:58 +0000
+++ src/platforms/evdev/key_repeater.cpp 2016-03-31 19:18:58 +0000
@@ -29,6 +29,8 @@
29{29{
30}30}
3131
32mie::KeyRepeater::~KeyRepeater() = default;
33
32void mie::KeyRepeater::enable(std::chrono::milliseconds repeat_delay, std::chrono::milliseconds repeat_interval)34void mie::KeyRepeater::enable(std::chrono::milliseconds repeat_delay, std::chrono::milliseconds repeat_interval)
33{35{
34 enabled_ = true;36 enabled_ = true;
3537
=== modified file 'src/platforms/evdev/key_repeater.h'
--- src/platforms/evdev/key_repeater.h 2016-03-31 19:18:58 +0000
+++ src/platforms/evdev/key_repeater.h 2016-03-31 19:18:58 +0000
@@ -39,6 +39,7 @@
39struct KeyRepeater39struct KeyRepeater
40{40{
41 KeyRepeater(std::shared_ptr<mir::time::AlarmFactory> const& alarm_factory, std::function<void(int32_t keycode)> const& send_repeat_key);41 KeyRepeater(std::shared_ptr<mir::time::AlarmFactory> const& alarm_factory, std::function<void(int32_t keycode)> const& send_repeat_key);
42 ~KeyRepeater();
42 void enable(std::chrono::milliseconds repeat_delay, std::chrono::milliseconds repeat_interval);43 void enable(std::chrono::milliseconds repeat_delay, std::chrono::milliseconds repeat_interval);
43 void disable();44 void disable();
44 bool enabled() const;45 bool enabled() const;
4546
=== modified file 'src/platforms/evdev/libinput_device.cpp'
--- src/platforms/evdev/libinput_device.cpp 2016-03-23 06:39:56 +0000
+++ src/platforms/evdev/libinput_device.cpp 2016-03-31 19:18:58 +0000
@@ -27,11 +27,11 @@
27#include "mir/input/device_capability.h"27#include "mir/input/device_capability.h"
28#include "mir/input/pointer_settings.h"28#include "mir/input/pointer_settings.h"
29#include "mir/input/touchpad_settings.h"29#include "mir/input/touchpad_settings.h"
30#include "mir/input/keyboard_settings.h"
30#include "mir/input/input_device_info.h"31#include "mir/input/input_device_info.h"
31#include "mir/events/event_builders.h"32#include "mir/events/event_builders.h"
32#include "mir/geometry/displacement.h"33#include "mir/geometry/displacement.h"
33#include "mir/dispatch/dispatchable.h"34#include "mir/time/clock.h"
34#include "mir/fd.h"
35#define MIR_LOG_COMPONENT "evdev"35#define MIR_LOG_COMPONENT "evdev"
36#include "mir/log.h"36#include "mir/log.h"
37#include "mir/raii.h"37#include "mir/raii.h"
@@ -45,13 +45,19 @@
45#include <sstream>45#include <sstream>
46#include <algorithm>46#include <algorithm>
4747
48namespace md = mir::dispatch;
49namespace mi = mir::input;48namespace mi = mir::input;
50namespace mie = mi::evdev;49namespace mie = mi::evdev;
51using namespace std::literals::chrono_literals;50using namespace std::literals::chrono_literals;
5251
53mie::LibInputDevice::LibInputDevice(std::shared_ptr<mi::InputReport> const& report, LibInputDevicePtr dev)52mie::LibInputDevice::LibInputDevice(std::shared_ptr<mi::InputReport> const& report,
54 : report{report}, pointer_pos{0, 0}, button_state{0}53 LibInputDevicePtr dev,
54 std::shared_ptr<mir::time::AlarmFactory> const& alarm_factory,
55 std::shared_ptr<mir::time::Clock> const& clock)
56 : report{report},
57 repeater{alarm_factory, [this](auto code) { this->handle_repeat(code); }},
58 clock{clock},
59 pointer_pos{0, 0},
60 button_state{0}
55{61{
56 add_device_of_group(std::move(dev));62 add_device_of_group(std::move(dev));
57}63}
@@ -135,6 +141,11 @@
135 auto const code = libinput_event_keyboard_get_key(keyboard);141 auto const code = libinput_event_keyboard_get_key(keyboard);
136 report->received_event_from_kernel(time.count(), EV_KEY, code, action);142 report->received_event_from_kernel(time.count(), EV_KEY, code, action);
137143
144 if (action == mir_keyboard_action_down)
145 repeater.press(code);
146 else
147 repeater.release(code);
148
138 return builder->key_event(time, action, xkb_keysym_t{0}, code);149 return builder->key_event(time, action, xkb_keysym_t{0}, code);
139}150}
140151
@@ -476,3 +487,32 @@
476 LIBINPUT_CONFIG_MIDDLE_EMULATION_ENABLED :487 LIBINPUT_CONFIG_MIDDLE_EMULATION_ENABLED :
477 LIBINPUT_CONFIG_MIDDLE_EMULATION_DISABLED);488 LIBINPUT_CONFIG_MIDDLE_EMULATION_DISABLED);
478}489}
490
491mir::optional_value<mi::KeyboardSettings> mie::LibInputDevice::get_keyboard_settings() const
492{
493 if (!contains(info.capabilities, mi::DeviceCapability::keyboard))
494 return {};
495 mi::KeyboardSettings settings;
496
497 settings.repeat_enabled = repeater.enabled();
498 settings.repeat_delay = repeater.delay();
499 settings.repeat_interval = repeater.interval();
500 return settings;
501}
502
503void mie::LibInputDevice::apply_settings(mi::KeyboardSettings const& settings)
504{
505 if (!contains(info.capabilities, mi::DeviceCapability::keyboard))
506 return;
507
508 if (settings.repeat_enabled)
509 repeater.enable(settings.repeat_delay, settings.repeat_interval);
510 else
511 repeater.disable();
512}
513
514void mie::LibInputDevice::handle_repeat(int32_t code)
515{
516 auto const event_time = clock->now().time_since_epoch();
517 sink->handle_input(*builder->key_event(event_time, mir_keyboard_action_repeat, 0, code));
518}
479519
=== modified file 'src/platforms/evdev/libinput_device.h'
--- src/platforms/evdev/libinput_device.h 2016-03-23 06:39:56 +0000
+++ src/platforms/evdev/libinput_device.h 2016-03-31 19:18:58 +0000
@@ -21,6 +21,7 @@
2121
22#include "libinput_ptr.h"22#include "libinput_ptr.h"
23#include "libinput_device_ptr.h"23#include "libinput_device_ptr.h"
24#include "key_repeater.h"
2425
25#include "mir/input/event_builder.h"26#include "mir/input/event_builder.h"
26#include "mir/input/input_device.h"27#include "mir/input/input_device.h"
@@ -38,18 +39,23 @@
3839
39namespace mir40namespace mir
40{41{
42namespace time
43{
44class Clock;
45class AlarmFactory;
46}
41namespace input47namespace input
42{48{
43class InputReport;49class InputReport;
44namespace evdev50namespace evdev
45{51{
46struct PointerState;
47struct KeyboardState;
4852
49class LibInputDevice : public input::InputDevice53class LibInputDevice : public input::InputDevice
50{54{
51public:55public:
52 LibInputDevice(std::shared_ptr<InputReport> const& report, LibInputDevicePtr dev);56 LibInputDevice(std::shared_ptr<InputReport> const& report, LibInputDevicePtr dev,
57 std::shared_ptr<mir::time::AlarmFactory> const& alarm_factory,
58 std::shared_ptr<mir::time::Clock> const& clock);
53 ~LibInputDevice();59 ~LibInputDevice();
54 void start(InputSink* sink, EventBuilder* builder) override;60 void start(InputSink* sink, EventBuilder* builder) override;
55 void stop() override;61 void stop() override;
@@ -58,6 +64,8 @@
58 void apply_settings(PointerSettings const&) override;64 void apply_settings(PointerSettings const&) override;
59 optional_value<TouchpadSettings> get_touchpad_settings() const override;65 optional_value<TouchpadSettings> get_touchpad_settings() const override;
60 void apply_settings(TouchpadSettings const&) override;66 void apply_settings(TouchpadSettings const&) override;
67 optional_value<KeyboardSettings> get_keyboard_settings() const override;
68 void apply_settings(KeyboardSettings const&) override;
6169
62 void process_event(libinput_event* event);70 void process_event(libinput_event* event);
63 ::libinput_device* device() const;71 ::libinput_device* device() const;
@@ -75,10 +83,11 @@
75 void handle_touch_motion(libinput_event_touch* touch);83 void handle_touch_motion(libinput_event_touch* touch);
76 void update_device_info();84 void update_device_info();
7785
78 std::shared_ptr<InputReport> report;86 std::shared_ptr<InputReport> const report;
87 KeyRepeater repeater;
88 std::shared_ptr<mir::time::Clock> const clock;
79 std::shared_ptr<::libinput> lib;89 std::shared_ptr<::libinput> lib;
80 std::vector<LibInputDevicePtr> devices;90 std::vector<LibInputDevicePtr> devices;
81 std::shared_ptr<dispatch::Dispatchable> dispatchable_fd;
8291
83 InputSink* sink{nullptr};92 InputSink* sink{nullptr};
84 EventBuilder* builder{nullptr};93 EventBuilder* builder{nullptr};
@@ -98,6 +107,7 @@
98 std::map<MirTouchId,ContactData> last_seen_properties;107 std::map<MirTouchId,ContactData> last_seen_properties;
99108
100 void update_contact_data(ContactData &data, MirTouchAction action, libinput_event_touch* touch);109 void update_contact_data(ContactData &data, MirTouchAction action, libinput_event_touch* touch);
110 void handle_repeat(int32_t code);
101};111};
102}112}
103}113}
104114
=== modified file 'src/platforms/evdev/platform.cpp'
--- src/platforms/evdev/platform.cpp 2016-03-23 06:39:56 +0000
+++ src/platforms/evdev/platform.cpp 2016-03-31 19:18:58 +0000
@@ -22,7 +22,10 @@
22#include "mir/udev/wrapper.h"22#include "mir/udev/wrapper.h"
23#include "mir/dispatch/dispatchable.h"23#include "mir/dispatch/dispatchable.h"
24#include "mir/dispatch/readable_fd.h"24#include "mir/dispatch/readable_fd.h"
25#include "mir/dispatch/alarm_factory.h"
25#include "mir/dispatch/multiplexing_dispatchable.h"26#include "mir/dispatch/multiplexing_dispatchable.h"
27#include "mir/time/steady_clock.h"
28#include "mir/time/steady_timer_fd.h"
26#include "mir/module_properties.h"29#include "mir/module_properties.h"
27#include "mir/assert_module_entry_point.h"30#include "mir/assert_module_entry_point.h"
2831
@@ -43,6 +46,7 @@
4346
44namespace mi = mir::input;47namespace mi = mir::input;
45namespace md = mir::dispatch;48namespace md = mir::dispatch;
49namespace mt = mir::time;
46namespace mu = mir::udev;50namespace mu = mir::udev;
47namespace mie = mi::evdev;51namespace mie = mi::evdev;
4852
@@ -68,12 +72,14 @@
68} // namespace72} // namespace
6973
70mie::Platform::Platform(std::shared_ptr<InputDeviceRegistry> const& registry,74mie::Platform::Platform(std::shared_ptr<InputDeviceRegistry> const& registry,
71 std::shared_ptr<InputReport> const& report,75 std::shared_ptr<InputReport> const& report,
72 std::unique_ptr<udev::Context>&& udev_context) :76 std::unique_ptr<udev::Context>&& udev_context)
73 report(report),77 : report(report),
74 udev_context(std::move(udev_context)),78 udev_context(std::move(udev_context)),
75 input_device_registry(registry),79 input_device_registry(registry),
76 platform_dispatchable{std::make_shared<md::MultiplexingDispatchable>()}80 platform_dispatchable{std::make_shared<md::MultiplexingDispatchable>()},
81 clock{std::make_shared<mt::SteadyClock>()},
82 alarm_factory{std::make_shared<md::AlarmFactory>(clock, std::make_shared<mt::SteadyTimerFd>())}
77{83{
78}84}
7985
@@ -91,6 +97,7 @@
91 Fd{IntOwnedFd{libinput_get_fd(lib.get())}}, [this]{process_input_events();}97 Fd{IntOwnedFd{libinput_get_fd(lib.get())}}, [this]{process_input_events();}
92 );98 );
93 platform_dispatchable->add_watch(libinput_dispatchable);99 platform_dispatchable->add_watch(libinput_dispatchable);
100 platform_dispatchable->add_watch(alarm_factory);
94}101}
95102
96void mie::Platform::process_input_events()103void mie::Platform::process_input_events()
@@ -145,7 +152,7 @@
145152
146 try153 try
147 {154 {
148 devices.emplace_back(std::make_shared<mie::LibInputDevice>(report, move(device_ptr)));155 devices.emplace_back(std::make_shared<mie::LibInputDevice>(report, move(device_ptr), alarm_factory, clock));
149156
150 input_device_registry->add_device(devices.back());157 input_device_registry->add_device(devices.back());
151158
@@ -186,6 +193,7 @@
186193
187void mie::Platform::stop()194void mie::Platform::stop()
188{195{
196 platform_dispatchable->remove_watch(alarm_factory);
189 platform_dispatchable->remove_watch(libinput_dispatchable);197 platform_dispatchable->remove_watch(libinput_dispatchable);
190 while (!devices.empty())198 while (!devices.empty())
191 {199 {
192200
=== modified file 'src/platforms/evdev/platform.h'
--- src/platforms/evdev/platform.h 2016-03-23 06:39:56 +0000
+++ src/platforms/evdev/platform.h 2016-03-31 19:18:58 +0000
@@ -37,10 +37,15 @@
37class Monitor;37class Monitor;
38class Context;38class Context;
39}39}
40namespace time
41{
42class Clock;
43}
40namespace dispatch44namespace dispatch
41{45{
42class MultiplexingDispatchable;46class MultiplexingDispatchable;
43class ReadableFd;47class ReadableFd;
48class AlarmFactory;
44}49}
45namespace input50namespace input
46{51{
@@ -73,6 +78,8 @@
73 std::shared_ptr<udev::Context> const udev_context;78 std::shared_ptr<udev::Context> const udev_context;
74 std::shared_ptr<InputDeviceRegistry> const input_device_registry;79 std::shared_ptr<InputDeviceRegistry> const input_device_registry;
75 std::shared_ptr<dispatch::MultiplexingDispatchable> const platform_dispatchable;80 std::shared_ptr<dispatch::MultiplexingDispatchable> const platform_dispatchable;
81 std::shared_ptr<time::Clock> const clock;
82 std::shared_ptr<dispatch::AlarmFactory> const alarm_factory;
76 std::shared_ptr<::libinput> lib;83 std::shared_ptr<::libinput> lib;
77 std::shared_ptr<dispatch::ReadableFd> libinput_dispatchable;84 std::shared_ptr<dispatch::ReadableFd> libinput_dispatchable;
7885
7986
=== modified file 'src/platforms/mesa/server/x11/input/input_device.cpp'
--- src/platforms/mesa/server/x11/input/input_device.cpp 2016-03-23 06:39:56 +0000
+++ src/platforms/mesa/server/x11/input/input_device.cpp 2016-03-31 19:18:58 +0000
@@ -20,13 +20,16 @@
2020
21#include "mir/input/pointer_settings.h"21#include "mir/input/pointer_settings.h"
22#include "mir/input/touchpad_settings.h"22#include "mir/input/touchpad_settings.h"
23#include "mir/input/keyboard_settings.h"
23#include "mir/input/input_device_info.h"24#include "mir/input/input_device_info.h"
24#include "mir/input/device_capability.h"25#include "mir/input/device_capability.h"
25#include "mir/input/event_builder.h"26#include "mir/input/event_builder.h"
26#include "mir/input/input_sink.h"27#include "mir/input/input_sink.h"
2728
29#include "mir/raii.h"
30
28#include <X11/Xlib.h>31#include <X11/Xlib.h>
29#include <iostream>32#include <X11/XKBlib.h>
3033
31namespace mi = mir::input;34namespace mi = mir::input;
32namespace geom = mir::geometry;35namespace geom = mir::geometry;
@@ -69,8 +72,8 @@
6972
70}73}
7174
72mix::XInputDevice::XInputDevice(InputDeviceInfo const& device_info)75mix::XInputDevice::XInputDevice(InputDeviceInfo const& device_info, std::shared_ptr<::Display> const& display)
73 : info(device_info)76 : display{display}, info(device_info)
74{77{
75}78}
7679
@@ -210,3 +213,49 @@
210 )213 )
211 );214 );
212}215}
216
217void mix::XInputDevice::apply_settings(mi::KeyboardSettings const& settings)
218{
219 if (!contains(info.capabilities, mi::DeviceCapability::keyboard))
220 return;
221
222 XKeyboardControl repeat_enabled;
223 repeat_enabled.auto_repeat_mode = settings.repeat_enabled;
224 XChangeKeyboardControl(display.get(), KBAutoRepeatMode, &repeat_enabled);
225
226 int xkbopcode, xkbevent, xkberror;
227 int xkbmajor = XkbMajorVersion, xkbminor = XkbMinorVersion;
228 if (XkbQueryExtension(display.get(), &xkbopcode, &xkbevent, &xkberror, &xkbmajor, &xkbminor))
229 {
230 auto xkb = raii::deleter_for(XkbAllocKeyboard(), [](XkbDescPtr ptr){XkbFreeKeyboard(ptr, 0, True);});
231
232 XkbGetControls(display.get(), XkbRepeatKeysMask, xkb.get());
233 xkb->ctrls->repeat_delay = settings.repeat_delay.count();
234 xkb->ctrls->repeat_interval = settings.repeat_interval.count();
235 XkbSetControls(display.get(), XkbRepeatKeysMask, xkb.get());
236 }
237}
238
239mir::optional_value<mi::KeyboardSettings> mix::XInputDevice::get_keyboard_settings() const
240{
241 if (!contains(info.capabilities, mi::DeviceCapability::keyboard))
242 return {};
243
244 KeyboardSettings settings;
245 XKeyboardState repeat_enabled;
246 XGetKeyboardControl(display.get(), &repeat_enabled);
247 settings.repeat_enabled = repeat_enabled.global_auto_repeat;
248
249 int xkbopcode, xkbevent, xkberror;
250 int xkbmajor = XkbMajorVersion, xkbminor = XkbMinorVersion;
251 if (XkbQueryExtension(display.get(), &xkbopcode, &xkbevent, &xkberror, &xkbmajor, &xkbminor))
252 {
253 auto xkb = raii::deleter_for(XkbAllocKeyboard(), [](XkbDescPtr ptr){ XkbFreeKeyboard(ptr, 0, True);});
254
255 XkbGetControls(display.get(), XkbRepeatKeysMask, xkb.get());
256 settings.repeat_delay = std::chrono::milliseconds{xkb->ctrls->repeat_delay};
257 settings.repeat_interval = std::chrono::milliseconds{xkb->ctrls->repeat_interval};
258 }
259
260 return settings;
261}
213262
=== modified file 'src/platforms/mesa/server/x11/input/input_device.h'
--- src/platforms/mesa/server/x11/input/input_device.h 2016-03-23 06:39:56 +0000
+++ src/platforms/mesa/server/x11/input/input_device.h 2016-03-31 19:18:58 +0000
@@ -26,6 +26,7 @@
26#include "mir/geometry/displacement.h"26#include "mir/geometry/displacement.h"
27#include "mir/optional_value.h"27#include "mir/optional_value.h"
2828
29#include <X11/Xlib.h>
29#include <chrono>30#include <chrono>
3031
31namespace mir32namespace mir
@@ -39,7 +40,7 @@
39class XInputDevice : public input::InputDevice40class XInputDevice : public input::InputDevice
40{41{
41public:42public:
42 XInputDevice(InputDeviceInfo const& info);43 XInputDevice(InputDeviceInfo const& info, std::shared_ptr<::Display> const& display);
4344
44 std::shared_ptr<dispatch::Dispatchable> dispatchable();45 std::shared_ptr<dispatch::Dispatchable> dispatchable();
45 void start(InputSink* destination, EventBuilder* builder) override;46 void start(InputSink* destination, EventBuilder* builder) override;
@@ -50,6 +51,8 @@
50 void apply_settings(PointerSettings const& settings) override;51 void apply_settings(PointerSettings const& settings) override;
51 optional_value<TouchpadSettings> get_touchpad_settings() const override;52 optional_value<TouchpadSettings> get_touchpad_settings() const override;
52 void apply_settings(TouchpadSettings const& settings) override;53 void apply_settings(TouchpadSettings const& settings) override;
54 optional_value<KeyboardSettings> get_keyboard_settings() const override;
55 void apply_settings(KeyboardSettings const& settings) override;
5356
54 bool started() const;57 bool started() const;
55 void key_press(std::chrono::nanoseconds event_time, xkb_keysym_t key_sym, int32_t key_code);58 void key_press(std::chrono::nanoseconds event_time, xkb_keysym_t key_sym, int32_t key_code);
@@ -60,6 +63,7 @@
60 void pointer_motion(std::chrono::nanoseconds event_time, mir::geometry::Point const& pos, mir::geometry::Displacement scroll);63 void pointer_motion(std::chrono::nanoseconds event_time, mir::geometry::Point const& pos, mir::geometry::Displacement scroll);
6164
62private:65private:
66 std::shared_ptr<::Display> const display;
63 MirPointerButtons button_state{0};67 MirPointerButtons button_state{0};
64 InputSink* sink{nullptr};68 InputSink* sink{nullptr};
65 EventBuilder* builder{nullptr};69 EventBuilder* builder{nullptr};
6670
=== modified file 'src/platforms/mesa/server/x11/input/input_platform.cpp'
--- src/platforms/mesa/server/x11/input/input_platform.cpp 2016-03-23 06:39:56 +0000
+++ src/platforms/mesa/server/x11/input/input_platform.cpp 2016-03-31 19:18:58 +0000
@@ -46,18 +46,15 @@
46namespace mix = mi::X;46namespace mix = mi::X;
4747
48mix::XInputPlatform::XInputPlatform(std::shared_ptr<mi::InputDeviceRegistry> const& input_device_registry,48mix::XInputPlatform::XInputPlatform(std::shared_ptr<mi::InputDeviceRegistry> const& input_device_registry,
49 std::shared_ptr<::Display> const& conn) :49 std::shared_ptr<::Display> const& conn)
50 x11_connection{conn},50 : x11_connection{conn},
51 xcon_dispatchable(std::make_shared<md::ReadableFd>(51 xcon_dispatchable(std::make_shared<md::ReadableFd>(mir::Fd{mir::IntOwnedFd{XConnectionNumber(conn.get())}},
52 mir::Fd{mir::IntOwnedFd{XConnectionNumber(conn.get())}}, [this]()52 [this]() { process_input_event(); })),
53 {53 registry(input_device_registry),
54 process_input_event();54 core_keyboard(std::make_shared<mix::XInputDevice>(
55 })),55 mi::InputDeviceInfo{"x11-keyboard-device", "x11-key-dev-1", mi::DeviceCapability::keyboard}, conn)),
56 registry(input_device_registry),56 core_pointer(std::make_shared<mix::XInputDevice>(
57 core_keyboard(std::make_shared<mix::XInputDevice>(57 mi::InputDeviceInfo{"x11-mouse-device", "x11-mouse-dev-1", mi::DeviceCapability::pointer}, conn))
58 mi::InputDeviceInfo{"x11-keyboard-device", "x11-key-dev-1", mi::DeviceCapability::keyboard})),
59 core_pointer(std::make_shared<mix::XInputDevice>(
60 mi::InputDeviceInfo{"x11-mouse-device", "x11-mouse-dev-1", mi::DeviceCapability::pointer}))
61{58{
62}59}
6360
6461
=== modified file 'src/server/input/CMakeLists.txt'
--- src/server/input/CMakeLists.txt 2016-03-23 06:39:56 +0000
+++ src/server/input/CMakeLists.txt 2016-03-31 19:18:58 +0000
@@ -16,7 +16,6 @@
16 event_filter_chain_dispatcher.cpp16 event_filter_chain_dispatcher.cpp
17 input_modifier_utils.cpp17 input_modifier_utils.cpp
18 input_probe.cpp18 input_probe.cpp
19 key_repeat_dispatcher.cpp
20 null_input_channel_factory.cpp19 null_input_channel_factory.cpp
21 null_input_dispatcher.cpp20 null_input_dispatcher.cpp
22 seat_input_device_tracker.cpp21 seat_input_device_tracker.cpp
2322
=== modified file 'src/server/input/default_configuration.cpp'
--- src/server/input/default_configuration.cpp 2016-03-23 06:39:56 +0000
+++ src/server/input/default_configuration.cpp 2016-03-31 19:18:58 +0000
@@ -20,7 +20,6 @@
2020
21#include "android/input_sender.h"21#include "android/input_sender.h"
22#include "android/input_channel_factory.h"22#include "android/input_channel_factory.h"
23#include "key_repeat_dispatcher.h"
24#include "display_input_region.h"23#include "display_input_region.h"
25#include "event_filter_chain_dispatcher.h"24#include "event_filter_chain_dispatcher.h"
26#include "cursor_controller.h"25#include "cursor_controller.h"
@@ -143,15 +142,7 @@
143 return input_dispatcher(142 return input_dispatcher(
144 [this]()143 [this]()
145 {144 {
146 std::chrono::milliseconds const key_repeat_timeout{500};145 return the_event_filter_chain_dispatcher();
147 std::chrono::milliseconds const key_repeat_delay{50};
148
149 auto const options = the_options();
150 auto enable_repeat = options->get<bool>(options::enable_key_repeat_opt);
151
152 return std::make_shared<mi::KeyRepeatDispatcher>(
153 the_event_filter_chain_dispatcher(), the_main_loop(), the_cookie_authority(),
154 enable_repeat, key_repeat_timeout, key_repeat_delay);
155 });146 });
156}147}
157148
@@ -297,18 +288,13 @@
297 return default_input_device_hub(288 return default_input_device_hub(
298 [this]()289 [this]()
299 {290 {
300 auto input_dispatcher = the_input_dispatcher();291 return std::make_shared<mi::DefaultInputDeviceHub>(
301 auto key_repeater = std::dynamic_pointer_cast<mi::KeyRepeatDispatcher>(input_dispatcher);
302 auto hub = std::make_shared<mi::DefaultInputDeviceHub>(
303 the_global_event_sink(),292 the_global_event_sink(),
304 the_seat(),293 the_seat(),
305 the_input_reading_multiplexer(),294 the_input_reading_multiplexer(),
306 the_main_loop(),295 the_main_loop(),
307 the_cookie_authority());296 the_cookie_authority(),
308297 the_options()->get<bool>(options::enable_key_repeat_opt));
309 if (key_repeater)
310 key_repeater->set_input_device_hub(hub);
311 return hub;
312 });298 });
313}299}
314300
@@ -324,18 +310,13 @@
324 return default_input_device_hub(310 return default_input_device_hub(
325 [this]()311 [this]()
326 {312 {
327 auto input_dispatcher = the_input_dispatcher();313 return std::make_shared<mi::DefaultInputDeviceHub>(
328 auto key_repeater = std::dynamic_pointer_cast<mi::KeyRepeatDispatcher>(input_dispatcher);
329 auto hub = std::make_shared<mi::DefaultInputDeviceHub>(
330 the_global_event_sink(),314 the_global_event_sink(),
331 the_seat(),315 the_seat(),
332 the_input_reading_multiplexer(),316 the_input_reading_multiplexer(),
333 the_main_loop(),317 the_main_loop(),
334 the_cookie_authority());318 the_cookie_authority(),
335319 the_options()->get<bool>(options::enable_key_repeat_opt));
336 if (key_repeater)
337 key_repeater->set_input_device_hub(hub);
338 return hub;
339 });320 });
340 }321 }
341}322}
342323
=== modified file 'src/server/input/default_input_device_hub.cpp'
--- src/server/input/default_input_device_hub.cpp 2016-03-23 06:39:56 +0000
+++ src/server/input/default_input_device_hub.cpp 2016-03-31 19:18:58 +0000
@@ -21,6 +21,7 @@
2121
22#include "mir/input/input_device.h"22#include "mir/input/input_device.h"
23#include "mir/input/input_device_observer.h"23#include "mir/input/input_device_observer.h"
24#include "mir/input/keyboard_settings.h"
24#include "mir/geometry/point.h"25#include "mir/geometry/point.h"
25#include "mir/dispatch/multiplexing_dispatchable.h"26#include "mir/dispatch/multiplexing_dispatchable.h"
26#include "mir/dispatch/action_queue.h"27#include "mir/dispatch/action_queue.h"
@@ -43,14 +44,16 @@
43 std::shared_ptr<mi::Seat> const& seat,44 std::shared_ptr<mi::Seat> const& seat,
44 std::shared_ptr<dispatch::MultiplexingDispatchable> const& input_multiplexer,45 std::shared_ptr<dispatch::MultiplexingDispatchable> const& input_multiplexer,
45 std::shared_ptr<mir::ServerActionQueue> const& observer_queue,46 std::shared_ptr<mir::ServerActionQueue> const& observer_queue,
46 std::shared_ptr<mir::cookie::Authority> const& cookie_authority)47 std::shared_ptr<mir::cookie::Authority> const& cookie_authority,
48 bool repeat_enabled)
47 : seat{seat},49 : seat{seat},
48 sink{sink},50 sink{sink},
49 input_dispatchable{input_multiplexer},51 input_dispatchable{input_multiplexer},
50 observer_queue(observer_queue),52 observer_queue(observer_queue),
51 device_queue(std::make_shared<dispatch::ActionQueue>()),53 device_queue(std::make_shared<dispatch::ActionQueue>()),
52 cookie_authority(cookie_authority),54 cookie_authority(cookie_authority),
53 device_id_generator{0}55 device_id_generator{0},
56 repeat_enabled{repeat_enabled}
54{57{
55 input_dispatchable->add_watch(device_queue);58 input_dispatchable->add_watch(device_queue);
56}59}
@@ -60,6 +63,13 @@
60 if (!device)63 if (!device)
61 BOOST_THROW_EXCEPTION(std::invalid_argument("Invalid input device"));64 BOOST_THROW_EXCEPTION(std::invalid_argument("Invalid input device"));
6265
66 if (contains(device->get_device_info().capabilities, mir::input::DeviceCapability::keyboard))
67 {
68 auto key_settings = device->get_keyboard_settings().value();
69 key_settings.repeat_enabled = repeat_enabled;
70 device->apply_settings(key_settings);
71 }
72
63 auto it = find_if(devices.cbegin(),73 auto it = find_if(devices.cbegin(),
64 devices.cend(),74 devices.cend(),
65 [&device](std::unique_ptr<RegisteredDevice> const& item)75 [&device](std::unique_ptr<RegisteredDevice> const& item)
6676
=== modified file 'src/server/input/default_input_device_hub.h'
--- src/server/input/default_input_device_hub.h 2016-03-23 06:39:56 +0000
+++ src/server/input/default_input_device_hub.h 2016-03-31 19:18:58 +0000
@@ -65,7 +65,8 @@
65 std::shared_ptr<Seat> const& seat,65 std::shared_ptr<Seat> const& seat,
66 std::shared_ptr<dispatch::MultiplexingDispatchable> const& input_multiplexer,66 std::shared_ptr<dispatch::MultiplexingDispatchable> const& input_multiplexer,
67 std::shared_ptr<ServerActionQueue> const& observer_queue,67 std::shared_ptr<ServerActionQueue> const& observer_queue,
68 std::shared_ptr<cookie::Authority> const& cookie_authority);68 std::shared_ptr<cookie::Authority> const& cookie_authority,
69 bool repeat_enabled = true);
6970
70 // InputDeviceRegistry - calls from mi::Platform71 // InputDeviceRegistry - calls from mi::Platform
71 void add_device(std::shared_ptr<InputDevice> const& device) override;72 void add_device(std::shared_ptr<InputDevice> const& device) override;
@@ -117,6 +118,7 @@
117 std::vector<std::shared_ptr<InputDeviceObserver>> observers;118 std::vector<std::shared_ptr<InputDeviceObserver>> observers;
118119
119 MirInputDeviceId device_id_generator;120 MirInputDeviceId device_id_generator;
121 bool repeat_enabled;
120};122};
121123
122}124}
123125
=== removed file 'src/server/input/key_repeat_dispatcher.cpp'
--- src/server/input/key_repeat_dispatcher.cpp 2016-03-29 23:37:32 +0000
+++ src/server/input/key_repeat_dispatcher.cpp 1970-01-01 00:00:00 +0000
@@ -1,186 +0,0 @@
1/*
2 * Copyright © 2012 Canonical Ltd.
3 *
4 * This program is free software: you can redistribute it and/or modify it
5 * under the terms of the GNU General Public License version 3,
6 * as published by the Free Software Foundation.
7 *
8 * This program is distributed in the hope that it will be useful,
9 * but WITHOUT ANY WARRANTY; without even the implied warranty of
10 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 * GNU General Public License for more details.
12 *
13 * You should have received a copy of the GNU General Public License
14 * along with this program. If not, see <http://www.gnu.org/licenses/>.
15 *
16 * Authored by: Robert Carr <robert.carr@canonical.com>
17 */
18
19#include "key_repeat_dispatcher.h"
20
21#include "mir/input/device.h"
22#include "mir/input/input_device_hub.h"
23#include "mir/time/alarm_factory.h"
24#include "mir/time/alarm.h"
25#include "mir/events/event_private.h"
26#include "mir/cookie/authority.h"
27
28#include <boost/throw_exception.hpp>
29
30#include <algorithm>
31#include <stdexcept>
32#include <string.h>
33
34namespace mi = mir::input;
35
36namespace
37{
38struct DeviceRemovalFilter : mi::InputDeviceObserver
39{
40 DeviceRemovalFilter(std::function<void(MirInputDeviceId)> const& on_removal)
41 : on_removal(on_removal) {}
42
43 void device_added(std::shared_ptr<mi::Device> const&) override
44 {
45 }
46
47 void device_changed(std::shared_ptr<mi::Device> const&) override
48 {
49 }
50
51 void device_removed(std::shared_ptr<mi::Device> const& device) override
52 {
53 on_removal(device->id());
54 }
55
56 void changes_complete() override
57 {
58 }
59 std::function<void(MirInputDeviceId)> on_removal;
60};
61
62}
63
64mi::KeyRepeatDispatcher::KeyRepeatDispatcher(
65 std::shared_ptr<mi::InputDispatcher> const& next_dispatcher,
66 std::shared_ptr<mir::time::AlarmFactory> const& factory,
67 std::shared_ptr<mir::cookie::Authority> const& cookie_authority,
68 bool repeat_enabled,
69 std::chrono::milliseconds repeat_timeout,
70 std::chrono::milliseconds repeat_delay)
71 : next_dispatcher(next_dispatcher),
72 alarm_factory(factory),
73 cookie_authority(cookie_authority),
74 repeat_enabled(repeat_enabled),
75 repeat_timeout(repeat_timeout),
76 repeat_delay(repeat_delay)
77{
78}
79
80void mi::KeyRepeatDispatcher::set_input_device_hub(std::shared_ptr<InputDeviceHub> const& hub)
81{
82 hub->add_observer(std::make_shared<DeviceRemovalFilter>(
83 [this](MirInputDeviceId id)
84 {
85 std::unique_lock<std::mutex> lock(repeat_state_mutex);
86 repeat_state_by_device.erase(id); // destructor cancels alarms
87 }
88 ));
89}
90
91mi::KeyRepeatDispatcher::KeyboardState& mi::KeyRepeatDispatcher::ensure_state_for_device_locked(std::lock_guard<std::mutex> const&, MirInputDeviceId id)
92{
93 repeat_state_by_device.insert(std::make_pair(id, KeyboardState()));
94 return repeat_state_by_device[id];
95}
96
97bool mi::KeyRepeatDispatcher::dispatch(MirEvent const& event)
98{
99 if (!repeat_enabled) // if we made this mutable we'd need a guard
100 {
101 return next_dispatcher->dispatch(event);
102 }
103
104 if (mir_event_get_type(&event) == mir_event_type_input)
105 {
106 auto iev = mir_event_get_input_event(&event);
107 if (mir_input_event_get_type(iev) != mir_input_event_type_key)
108 return next_dispatcher->dispatch(event);
109 if (!handle_key_input(mir_input_event_get_device_id(iev), mir_input_event_get_keyboard_event(iev)))
110 return next_dispatcher->dispatch(event);
111 else
112 return true;
113 }
114 return next_dispatcher->dispatch(event);
115}
116
117// Returns true if the original event has been handled, that is ::dispatch should not pass it on.
118bool mi::KeyRepeatDispatcher::handle_key_input(MirInputDeviceId id, MirKeyboardEvent const* kev)
119{
120 std::lock_guard<std::mutex> lg(repeat_state_mutex);
121 auto& device_state = ensure_state_for_device_locked(lg, id);
122
123 auto scan_code = mir_keyboard_event_scan_code(kev);
124
125 switch (mir_keyboard_event_action(kev))
126 {
127 case mir_keyboard_action_up:
128 {
129 auto it = device_state.repeat_alarms_by_scancode.find(scan_code);
130 if (it == device_state.repeat_alarms_by_scancode.end())
131 {
132 return false;
133 }
134 device_state.repeat_alarms_by_scancode.erase(it);
135 break;
136 }
137 case mir_keyboard_action_down:
138 {
139 MirKeyboardEvent new_kev = *kev;
140 new_kev.action = mir_keyboard_action_repeat;
141
142 auto it = device_state.repeat_alarms_by_scancode.find(scan_code);
143 if (it != device_state.repeat_alarms_by_scancode.end())
144 {
145 // When we receive a duplicated down we just replace the action
146 next_dispatcher->dispatch(new_kev);
147 return true;
148 }
149 auto& capture_alarm = device_state.repeat_alarms_by_scancode[scan_code];
150 std::shared_ptr<mir::time::Alarm> alarm = alarm_factory->create_alarm([this, &capture_alarm, new_kev]() mutable
151 {
152 std::lock_guard<std::mutex> lg(repeat_state_mutex);
153
154 new_kev.event_time = std::chrono::steady_clock::now().time_since_epoch();
155 auto const cookie = cookie_authority->make_cookie(new_kev.event_time.count());
156 auto const serialized_cookie = cookie->serialize();
157 std::copy_n(std::begin(serialized_cookie), new_kev.cookie.size(), std::begin(new_kev.cookie));
158 next_dispatcher->dispatch(new_kev);
159
160 capture_alarm->reschedule_in(repeat_delay);
161 });
162 alarm->reschedule_in(repeat_timeout);
163 device_state.repeat_alarms_by_scancode[scan_code] = {alarm};
164 }
165 case mir_keyboard_action_repeat:
166 // Should we consume existing repeats?
167 break;
168 default:
169 BOOST_THROW_EXCEPTION(std::logic_error("Unexpected key event action"));
170 }
171 return false;
172}
173
174void mi::KeyRepeatDispatcher::start()
175{
176 next_dispatcher->start();
177}
178
179void mi::KeyRepeatDispatcher::stop()
180{
181 std::lock_guard<std::mutex> lg(repeat_state_mutex);
182
183 repeat_state_by_device.clear();
184
185 next_dispatcher->stop();
186}
1870
=== removed file 'src/server/input/key_repeat_dispatcher.h'
--- src/server/input/key_repeat_dispatcher.h 2016-03-23 06:39:56 +0000
+++ src/server/input/key_repeat_dispatcher.h 1970-01-01 00:00:00 +0000
@@ -1,84 +0,0 @@
1/*
2 * Copyright © 2015-2016 Canonical Ltd.
3 *
4 * This program is free software: you can redistribute it and/or modify it
5 * under the terms of the GNU General Public License version 3,
6 * as published by the Free Software Foundation.
7 *
8 * This program is distributed in the hope that it will be useful,
9 * but WITHOUT ANY WARRANTY; without even the implied warranty of
10 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 * GNU General Public License for more details.
12 *
13 * You should have received a copy of the GNU General Public License
14 * along with this program. If not, see <http://www.gnu.org/licenses/>.
15 *
16 * Authored by: Robert Carr <robert.carr@canonical.com>
17 */
18
19#ifndef MIR_INPUT_KEY_REPEAT_DISPATCHER_H_
20#define MIR_INPUT_KEY_REPEAT_DISPATCHER_H_
21
22#include "mir/input/input_dispatcher.h"
23#include "mir/input/input_device_observer.h"
24
25#include <memory>
26#include <chrono>
27#include <mutex>
28#include <unordered_map>
29
30namespace mir
31{
32namespace cookie
33{
34class Authority;
35}
36namespace time
37{
38class AlarmFactory;
39class Alarm;
40}
41namespace input
42{
43class InputDeviceHub;
44class KeyRepeatDispatcher : public InputDispatcher
45{
46public:
47 KeyRepeatDispatcher(std::shared_ptr<InputDispatcher> const& next_dispatcher,
48 std::shared_ptr<time::AlarmFactory> const& factory,
49 std::shared_ptr<cookie::Authority> const& cookie_authority,
50 bool repeat_enabled,
51 std::chrono::milliseconds repeat_timeout, /* timeout before sending first repeat */
52 std::chrono::milliseconds repeat_delay /* delay between repeated keys */);
53
54 // InputDispatcher
55 bool dispatch(MirEvent const& event) override;
56 void start() override;
57 void stop() override;
58
59 void set_input_device_hub(std::shared_ptr<InputDeviceHub> const& hub);
60
61private:
62 std::mutex repeat_state_mutex;
63
64 std::shared_ptr<InputDispatcher> const next_dispatcher;
65 std::shared_ptr<time::AlarmFactory> const alarm_factory;
66 std::shared_ptr<cookie::Authority> const cookie_authority;
67 bool const repeat_enabled;
68 std::chrono::milliseconds repeat_timeout;
69 std::chrono::milliseconds repeat_delay;
70
71 struct KeyboardState
72 {
73 std::unordered_map<int, std::shared_ptr<mir::time::Alarm>> repeat_alarms_by_scancode;
74 };
75 std::unordered_map<MirInputDeviceId, KeyboardState> repeat_state_by_device;
76 KeyboardState& ensure_state_for_device_locked(std::lock_guard<std::mutex> const&, MirInputDeviceId id);
77
78 bool handle_key_input(MirInputDeviceId id, MirKeyboardEvent const* ev);
79};
80
81}
82}
83
84#endif // MIR_INPUT_KEY_REPEAT_DISPATCHER_H_
850
=== modified file 'tests/include/mir/test/doubles/mock_input_device.h'
--- tests/include/mir/test/doubles/mock_input_device.h 2015-12-18 21:07:54 +0000
+++ tests/include/mir/test/doubles/mock_input_device.h 2016-03-31 19:18:58 +0000
@@ -23,6 +23,7 @@
23#include "mir/input/input_device_info.h" // needed for fake device setup23#include "mir/input/input_device_info.h" // needed for fake device setup
24#include "mir/input/pointer_settings.h"24#include "mir/input/pointer_settings.h"
25#include "mir/input/touchpad_settings.h"25#include "mir/input/touchpad_settings.h"
26#include "mir/input/keyboard_settings.h"
2627
27#include <gmock/gmock.h>28#include <gmock/gmock.h>
2829
@@ -44,6 +45,8 @@
44 MOCK_METHOD1(apply_settings, void(input::PointerSettings const&));45 MOCK_METHOD1(apply_settings, void(input::PointerSettings const&));
45 MOCK_CONST_METHOD0(get_touchpad_settings, mir::optional_value<input::TouchpadSettings>());46 MOCK_CONST_METHOD0(get_touchpad_settings, mir::optional_value<input::TouchpadSettings>());
46 MOCK_METHOD1(apply_settings, void(input::TouchpadSettings const&));47 MOCK_METHOD1(apply_settings, void(input::TouchpadSettings const&));
48 MOCK_CONST_METHOD0(get_keyboard_settings, mir::optional_value<input::KeyboardSettings>());
49 MOCK_METHOD1(apply_settings, void(input::KeyboardSettings const&));
47};50};
48}51}
49}52}
5053
=== modified file 'tests/include/mir/test/doubles/mock_x11.h'
--- tests/include/mir/test/doubles/mock_x11.h 2016-03-23 06:39:56 +0000
+++ tests/include/mir/test/doubles/mock_x11.h 2016-03-31 19:18:58 +0000
@@ -22,6 +22,7 @@
22#include <gmock/gmock.h>22#include <gmock/gmock.h>
2323
24#include <X11/Xlib.h>24#include <X11/Xlib.h>
25#include <X11/XKBlib.h>
25#include <X11/Xutil.h>26#include <X11/Xutil.h>
2627
27namespace mir28namespace mir
@@ -81,6 +82,13 @@
81 MOCK_METHOD1(XSetErrorHandler, XErrorHandler(XErrorHandler));82 MOCK_METHOD1(XSetErrorHandler, XErrorHandler(XErrorHandler));
82 MOCK_METHOD0(XInitThreads, Status());83 MOCK_METHOD0(XInitThreads, Status());
83 MOCK_METHOD3(XSetWMHints, int(Display*, Window, XWMHints*));84 MOCK_METHOD3(XSetWMHints, int(Display*, Window, XWMHints*));
85 MOCK_METHOD3(XChangeKeyboardControl, int(Display*, unsigned long, XKeyboardControl*));
86 MOCK_METHOD2(XGetKeyboardControl, int(Display*, XKeyboardState*));
87 MOCK_METHOD6(XkbQueryExtension, Bool(Display*, int*, int*, int*, int*, int*));
88 MOCK_METHOD3(XkbFreeKeyboard, void(XkbDescPtr, unsigned int, Bool));
89 MOCK_METHOD0(XkbAllocKeyboard, XkbDescPtr());
90 MOCK_METHOD3(XkbGetControls, Status(Display*, unsigned long, XkbDescPtr));
91 MOCK_METHOD3(XkbSetControls, Bool(Display*, unsigned long, XkbDescPtr));
8492
85 FakeX11Resources fake_x11;93 FakeX11Resources fake_x11;
86};94};
8795
=== modified file 'tests/include/mir_test_framework/stub_input_platform.h'
--- tests/include/mir_test_framework/stub_input_platform.h 2016-01-29 08:18:22 +0000
+++ tests/include/mir_test_framework/stub_input_platform.h 2016-03-31 19:18:58 +0000
@@ -26,8 +26,16 @@
2626
27namespace mir27namespace mir
28{28{
29namespace time
30{
31class AlarmFactory;
32class Clock;
33class SteadyTimerFd;
34class SteadyClock;
35}
29namespace dispatch36namespace dispatch
30{37{
38class AlarmFactory;
31class ActionQueue;39class ActionQueue;
32class MultiplexingDispatchable;40class MultiplexingDispatchable;
33}41}
@@ -53,6 +61,8 @@
53 static void remove(std::shared_ptr<mir::input::InputDevice> const& dev);61 static void remove(std::shared_ptr<mir::input::InputDevice> const& dev);
54 static void register_dispatchable(std::shared_ptr<mir::dispatch::Dispatchable> const& queue);62 static void register_dispatchable(std::shared_ptr<mir::dispatch::Dispatchable> const& queue);
55 static void unregister_dispatchable(std::shared_ptr<mir::dispatch::Dispatchable> const& queue);63 static void unregister_dispatchable(std::shared_ptr<mir::dispatch::Dispatchable> const& queue);
64 static std::shared_ptr<mir::time::AlarmFactory> get_alarm_factory();
65 static std::shared_ptr<mir::time::Clock> get_clock();
5666
57private:67private:
58 std::shared_ptr<mir::dispatch::MultiplexingDispatchable> const platform_dispatchable;68 std::shared_ptr<mir::dispatch::MultiplexingDispatchable> const platform_dispatchable;
@@ -61,6 +71,11 @@
61 static std::atomic<StubInputPlatform*> stub_input_platform;71 static std::atomic<StubInputPlatform*> stub_input_platform;
62 static std::vector<std::weak_ptr<mir::input::InputDevice>> device_store;72 static std::vector<std::weak_ptr<mir::input::InputDevice>> device_store;
63 static std::mutex device_store_guard;73 static std::mutex device_store_guard;
74 static std::shared_ptr<mir::time::SteadyClock> steady_clock;
75 static std::shared_ptr<mir::time::SteadyTimerFd> steady_timer_fd;
76 static std::shared_ptr<mir::dispatch::AlarmFactory> alarm_factory;
77
78
64};79};
6580
66}81}
6782
=== modified file 'tests/mir_test_doubles/mock_input_device.cpp'
--- tests/mir_test_doubles/mock_input_device.cpp 2015-12-18 21:07:54 +0000
+++ tests/mir_test_doubles/mock_input_device.cpp 2016-03-31 19:18:58 +0000
@@ -40,4 +40,12 @@
40 else40 else
41 ON_CALL(*this, get_touchpad_settings())41 ON_CALL(*this, get_touchpad_settings())
42 .WillByDefault(Return(mir::optional_value<input::TouchpadSettings>()));42 .WillByDefault(Return(mir::optional_value<input::TouchpadSettings>()));
43
44 if (contains(caps, input::DeviceCapability::keyboard))
45 ON_CALL(*this, get_keyboard_settings())
46 .WillByDefault(Return(input::KeyboardSettings()));
47 else
48 ON_CALL(*this, get_keyboard_settings())
49 .WillByDefault(Return(mir::optional_value<input::KeyboardSettings>()));
50
43}51}
4452
=== modified file 'tests/mir_test_doubles/mock_x11.cpp'
--- tests/mir_test_doubles/mock_x11.cpp 2016-03-23 06:39:56 +0000
+++ tests/mir_test_doubles/mock_x11.cpp 2016-03-31 19:18:58 +0000
@@ -195,3 +195,38 @@
195{195{
196 return global_mock->XPending(display);196 return global_mock->XPending(display);
197}197}
198
199int XChangeKeyboardControl(Display* display, unsigned long value_mask, XKeyboardControl* control)
200{
201 return global_mock->XChangeKeyboardControl(display, value_mask, control);
202}
203
204int XGetKeyboardControl(Display* display, XKeyboardState* control)
205{
206 return global_mock->XGetKeyboardControl(display, control);
207}
208
209Bool XkbQueryExtension(Display* dpy, int* opcode, int* event_base, int* error_base, int* major, int* minor)
210{
211 return global_mock->XkbQueryExtension(dpy, opcode, event_base, error_base, major, minor);
212}
213
214void XkbFreeKeyboard(XkbDescPtr xkb, unsigned int which, Bool free_desc)
215{
216 global_mock->XkbFreeKeyboard(xkb, which, free_desc);
217}
218
219XkbDescPtr XkbAllocKeyboard()
220{
221 return global_mock->XkbAllocKeyboard();
222}
223
224Status XkbGetControls(Display* dpy, unsigned long which, XkbDescPtr desc)
225{
226 return global_mock->XkbGetControls(dpy, which, desc);
227}
228
229Bool XkbSetControls(Display* dpy, unsigned long which, XkbDescPtr desc)
230{
231 return global_mock->XkbSetControls(dpy, which, desc);
232}
198233
=== modified file 'tests/mir_test_framework/fake_input_device_impl.cpp'
--- tests/mir_test_framework/fake_input_device_impl.cpp 2016-03-23 06:39:56 +0000
+++ tests/mir_test_framework/fake_input_device_impl.cpp 2016-03-31 19:18:58 +0000
@@ -24,7 +24,9 @@
24#include "mir/input/input_sink.h"24#include "mir/input/input_sink.h"
25#include "mir/input/pointer_settings.h"25#include "mir/input/pointer_settings.h"
26#include "mir/input/touchpad_settings.h"26#include "mir/input/touchpad_settings.h"
27#include "mir/input/keyboard_settings.h"
27#include "mir/input/event_builder.h"28#include "mir/input/event_builder.h"
29#include "mir/time/clock.h"
28#include "mir/dispatch/action_queue.h"30#include "mir/dispatch/action_queue.h"
29#include "mir/geometry/displacement.h"31#include "mir/geometry/displacement.h"
30#include "src/platforms/evdev/button_utils.h"32#include "src/platforms/evdev/button_utils.h"
@@ -41,7 +43,9 @@
41namespace mtf = mir_test_framework;43namespace mtf = mir_test_framework;
4244
43mtf::FakeInputDeviceImpl::FakeInputDeviceImpl(mi::InputDeviceInfo const& info)45mtf::FakeInputDeviceImpl::FakeInputDeviceImpl(mi::InputDeviceInfo const& info)
44 : queue{std::make_shared<md::ActionQueue>()}, device{std::make_shared<InputDevice>(info, queue)}46 : queue{std::make_shared<md::ActionQueue>()},
47 device{std::make_shared<InputDevice>(
48 info, queue, mtf::StubInputPlatform::get_clock(), mtf::StubInputPlatform::get_alarm_factory())}
45{49{
46 mtf::StubInputPlatform::add(device);50 mtf::StubInputPlatform::add(device);
47}51}
@@ -92,24 +96,31 @@
92}96}
9397
94mtf::FakeInputDeviceImpl::InputDevice::InputDevice(mi::InputDeviceInfo const& info,98mtf::FakeInputDeviceImpl::InputDevice::InputDevice(mi::InputDeviceInfo const& info,
95 std::shared_ptr<mir::dispatch::Dispatchable> const& dispatchable)99 std::shared_ptr<mir::dispatch::Dispatchable> const& dispatchable,
96 : info(info), queue{dispatchable}, buttons{0}100 std::shared_ptr<mir::time::Clock> const& clock,
101 std::shared_ptr<mir::time::AlarmFactory> const& alarm_factory)
102 : info(info), queue{dispatchable}, clock{clock}, buttons{0}, repeater{alarm_factory, [this](auto key_code) {
103 this->handle_repeat(key_code);}}
97{104{
98 // the default setup results in a direct mapping of input velocity to output velocity.105 // the default setup results in a direct mapping of input velocity to output velocity.
99 settings.acceleration = mir_pointer_acceleration_none;106 settings.acceleration = mir_pointer_acceleration_none;
100 settings.cursor_acceleration_bias = 0.0;107 settings.cursor_acceleration_bias = 0.0;
101}108}
102109
103void mtf::FakeInputDeviceImpl::InputDevice::synthesize_events(synthesis::KeyParameters const& key_params)110void mtf::FakeInputDeviceImpl::InputDevice::synthesize_events(synthesis::KeyParameters const& key_params)
104{111{
105 xkb_keysym_t key_code = 0;112 xkb_keysym_t key_code = 0;
106113
107 auto event_time = std::chrono::duration_cast<std::chrono::nanoseconds>(114 auto const event_time = clock->now().time_since_epoch();
108 std::chrono::steady_clock::now().time_since_epoch());
109115
110 auto input_action =116 auto input_action =
111 (key_params.action == synthesis::EventAction::Down) ? mir_keyboard_action_down : mir_keyboard_action_up;117 (key_params.action == synthesis::EventAction::Down) ? mir_keyboard_action_down : mir_keyboard_action_up;
112118
119 if (input_action == mir_keyboard_action_down)
120 repeater.press(key_params.scancode);
121 else
122 repeater.release(key_params.scancode);
123
113 auto key_event = builder->key_event(event_time, input_action, key_code, key_params.scancode);124 auto key_event = builder->key_event(event_time, input_action, key_code, key_params.scancode);
114125
115 if (!sink)126 if (!sink)
@@ -119,8 +130,7 @@
119130
120void mtf::FakeInputDeviceImpl::InputDevice::synthesize_events(synthesis::ButtonParameters const& button)131void mtf::FakeInputDeviceImpl::InputDevice::synthesize_events(synthesis::ButtonParameters const& button)
121{132{
122 auto event_time = std::chrono::duration_cast<std::chrono::nanoseconds>(133 auto const event_time = clock->now().time_since_epoch();
123 std::chrono::steady_clock::now().time_since_epoch());
124 auto action = update_buttons(button.action, mie::to_pointer_button(button.button, settings.handedness));134 auto action = update_buttons(button.action, mie::to_pointer_button(button.button, settings.handedness));
125 auto button_event = builder->pointer_event(event_time,135 auto button_event = builder->pointer_event(event_time,
126 action,136 action,
@@ -154,8 +164,7 @@
154 if (!sink)164 if (!sink)
155 BOOST_THROW_EXCEPTION(std::runtime_error("Device is not started."));165 BOOST_THROW_EXCEPTION(std::runtime_error("Device is not started."));
156166
157 auto event_time = std::chrono::duration_cast<std::chrono::nanoseconds>(167 auto const event_time = clock->now().time_since_epoch();
158 std::chrono::steady_clock::now().time_since_epoch());
159 // constant scaling is used here to simplify checking for the168 // constant scaling is used here to simplify checking for the
160 // expected results. Default settings of the device lead to no169 // expected results. Default settings of the device lead to no
161 // scaling at all.170 // scaling at all.
@@ -179,8 +188,7 @@
179 if (!sink)188 if (!sink)
180 BOOST_THROW_EXCEPTION(std::runtime_error("Device is not started."));189 BOOST_THROW_EXCEPTION(std::runtime_error("Device is not started."));
181190
182 auto event_time = std::chrono::duration_cast<std::chrono::nanoseconds>(191 auto const event_time = clock->now().time_since_epoch();
183 std::chrono::steady_clock::now().time_since_epoch());
184192
185 auto touch_event = builder->touch_event(event_time);193 auto touch_event = builder->touch_event(event_time);
186194
@@ -247,6 +255,29 @@
247 // forwards already interpreted events.255 // forwards already interpreted events.
248}256}
249257
258mir::optional_value<mi::KeyboardSettings> mtf::FakeInputDeviceImpl::InputDevice::get_keyboard_settings() const
259{
260 if (!contains(info.capabilities, mi::DeviceCapability::keyboard))
261 return {};
262 mi::KeyboardSettings settings;
263
264 settings.repeat_enabled = repeater.enabled();
265 settings.repeat_delay = repeater.delay();
266 settings.repeat_interval = repeater.interval();
267 return settings;
268}
269
270void mtf::FakeInputDeviceImpl::InputDevice::apply_settings(mi::KeyboardSettings const& settings)
271{
272 if (!contains(info.capabilities, mi::DeviceCapability::keyboard))
273 return;
274
275 if (settings.repeat_enabled)
276 repeater.enable(settings.repeat_delay, settings.repeat_interval);
277 else
278 repeater.disable();
279}
280
250void mtf::FakeInputDeviceImpl::InputDevice::map_touch_coordinates(float& x, float& y)281void mtf::FakeInputDeviceImpl::InputDevice::map_touch_coordinates(float& x, float& y)
251{282{
252 // TODO take orientation of input sink into account?283 // TODO take orientation of input sink into account?
@@ -258,6 +289,15 @@
258 y = (y - float(FakeInputDevice::minimum_touch_axis_value))*y_scale + area.top_left.y.as_float();289 y = (y - float(FakeInputDevice::minimum_touch_axis_value))*y_scale + area.top_left.y.as_float();
259}290}
260291
292void mtf::FakeInputDeviceImpl::InputDevice::handle_repeat(int32_t code)
293{
294 if (!sink)
295 BOOST_THROW_EXCEPTION(std::runtime_error("Device is not started."));
296 auto const event_time = clock->now().time_since_epoch();
297 auto key_event = builder->key_event(event_time, mir_keyboard_action_repeat, 0, code);
298 sink->handle_input(*key_event);
299}
300
261void mtf::FakeInputDeviceImpl::InputDevice::start(mi::InputSink* destination, mi::EventBuilder* event_builder)301void mtf::FakeInputDeviceImpl::InputDevice::start(mi::InputSink* destination, mi::EventBuilder* event_builder)
262{302{
263 sink = destination;303 sink = destination;
264304
=== modified file 'tests/mir_test_framework/fake_input_device_impl.h'
--- tests/mir_test_framework/fake_input_device_impl.h 2016-03-23 06:39:56 +0000
+++ tests/mir_test_framework/fake_input_device_impl.h 2016-03-31 19:18:58 +0000
@@ -20,6 +20,7 @@
20#define MIR_TEST_FRAMEWORK_FAKE_INPUT_DEVICE_IMPL_H_20#define MIR_TEST_FRAMEWORK_FAKE_INPUT_DEVICE_IMPL_H_
2121
22#include "mir_test_framework/fake_input_device.h"22#include "mir_test_framework/fake_input_device.h"
23#include "src/platforms/evdev/key_repeater.h"
2324
24#include "mir/input/input_device.h"25#include "mir/input/input_device.h"
25#include "mir/input/pointer_settings.h"26#include "mir/input/pointer_settings.h"
@@ -28,6 +29,11 @@
2829
29namespace mir30namespace mir
30{31{
32namespace time
33{
34class Clock;
35class AlarmFactory;
36}
31namespace dispatch37namespace dispatch
32{38{
33class ActionQueue;39class ActionQueue;
@@ -52,7 +58,10 @@
52 {58 {
53 public:59 public:
54 InputDevice(mir::input::InputDeviceInfo const& info,60 InputDevice(mir::input::InputDeviceInfo const& info,
55 std::shared_ptr<mir::dispatch::Dispatchable> const& dispatchable);61 std::shared_ptr<mir::dispatch::Dispatchable> const& dispatchable,
62 std::shared_ptr<mir::time::Clock> const& clock,
63 std::shared_ptr<mir::time::AlarmFactory> const& alarm_factory
64 );
5665
57 void start(mir::input::InputSink* destination, mir::input::EventBuilder* builder) override;66 void start(mir::input::InputSink* destination, mir::input::EventBuilder* builder) override;
58 void stop() override;67 void stop() override;
@@ -70,19 +79,24 @@
70 void apply_settings(mir::input::PointerSettings const& settings) override;79 void apply_settings(mir::input::PointerSettings const& settings) override;
71 mir::optional_value<mir::input::TouchpadSettings> get_touchpad_settings() const override;80 mir::optional_value<mir::input::TouchpadSettings> get_touchpad_settings() const override;
72 void apply_settings(mir::input::TouchpadSettings const& settings) override;81 void apply_settings(mir::input::TouchpadSettings const& settings) override;
82 mir::optional_value<mir::input::KeyboardSettings> get_keyboard_settings() const override;
83 void apply_settings(mir::input::KeyboardSettings const& settings) override;
7384
74 private:85 private:
75 MirPointerAction update_buttons(synthesis::EventAction action, MirPointerButton button);86 MirPointerAction update_buttons(synthesis::EventAction action, MirPointerButton button);
76 void update_position(int rel_x, int rel_y);87 void update_position(int rel_x, int rel_y);
77 void map_touch_coordinates(float& x, float& y);88 void map_touch_coordinates(float& x, float& y);
89 void handle_repeat(int32_t code);
7890
79 mir::input::InputSink* sink{nullptr};91 mir::input::InputSink* sink{nullptr};
80 mir::input::EventBuilder* builder{nullptr};92 mir::input::EventBuilder* builder{nullptr};
81 mir::input::InputDeviceInfo info;93 mir::input::InputDeviceInfo info;
82 std::shared_ptr<mir::dispatch::Dispatchable> const queue;94 std::shared_ptr<mir::dispatch::Dispatchable> const queue;
95 std::shared_ptr<mir::time::Clock> const clock;
83 mir::geometry::Point pos, scroll;96 mir::geometry::Point pos, scroll;
84 MirPointerButtons buttons;97 MirPointerButtons buttons;
85 mir::input::PointerSettings settings;98 mir::input::PointerSettings settings;
99 mir::input::evdev::KeyRepeater repeater;
86 };100 };
87 std::shared_ptr<mir::dispatch::ActionQueue> queue;101 std::shared_ptr<mir::dispatch::ActionQueue> queue;
88 std::shared_ptr<InputDevice> device;102 std::shared_ptr<InputDevice> device;
89103
=== modified file 'tests/mir_test_framework/stub_input_platform.cpp'
--- tests/mir_test_framework/stub_input_platform.cpp 2016-03-23 06:39:56 +0000
+++ tests/mir_test_framework/stub_input_platform.cpp 2016-03-31 19:18:58 +0000
@@ -19,7 +19,10 @@
19#include "mir_test_framework/stub_input_platform.h"19#include "mir_test_framework/stub_input_platform.h"
2020
21#include "mir/input/input_device_registry.h"21#include "mir/input/input_device_registry.h"
22#include "mir/time/steady_timer_fd.h"
23#include "mir/time/steady_clock.h"
22#include "mir/dispatch/action_queue.h"24#include "mir/dispatch/action_queue.h"
25#include "mir/dispatch/alarm_factory.h"
23#include "mir/dispatch/multiplexing_dispatchable.h"26#include "mir/dispatch/multiplexing_dispatchable.h"
24#include "mir/module_deleter.h"27#include "mir/module_deleter.h"
2528
@@ -27,6 +30,8 @@
2730
28namespace mtf = mir_test_framework;31namespace mtf = mir_test_framework;
29namespace mi = mir::input;32namespace mi = mir::input;
33namespace md = mir::dispatch;
34namespace mt = mir::time;
3035
31mtf::StubInputPlatform::StubInputPlatform(36mtf::StubInputPlatform::StubInputPlatform(
32 std::shared_ptr<mi::InputDeviceRegistry> const& input_device_registry)37 std::shared_ptr<mi::InputDeviceRegistry> const& input_device_registry)
@@ -36,10 +41,12 @@
36{41{
37 stub_input_platform = this;42 stub_input_platform = this;
38 platform_dispatchable->add_watch(platform_queue);43 platform_dispatchable->add_watch(platform_queue);
44 platform_dispatchable->add_watch(alarm_factory);
39}45}
4046
41mtf::StubInputPlatform::~StubInputPlatform()47mtf::StubInputPlatform::~StubInputPlatform()
42{48{
49 platform_dispatchable->remove_watch(alarm_factory);
43 std::lock_guard<decltype(device_store_guard)> lk{device_store_guard};50 std::lock_guard<decltype(device_store_guard)> lk{device_store_guard};
44 device_store.clear();51 device_store.clear();
45 stub_input_platform = nullptr;52 stub_input_platform = nullptr;
@@ -128,6 +135,20 @@
128 input_platform->platform_dispatchable->remove_watch(queue);135 input_platform->platform_dispatchable->remove_watch(queue);
129}136}
130137
138std::shared_ptr<mt::AlarmFactory> mtf::StubInputPlatform::get_alarm_factory()
139{
140 return alarm_factory;
141}
142
143std::shared_ptr<mt::Clock> mtf::StubInputPlatform::get_clock()
144{
145 return steady_clock;
146}
147
148std::shared_ptr<mt::SteadyClock> mtf::StubInputPlatform::steady_clock{std::make_shared<mt::SteadyClock>()};
149std::shared_ptr<mt::SteadyTimerFd> mtf::StubInputPlatform::steady_timer_fd{std::make_shared<mt::SteadyTimerFd>()};
150std::shared_ptr<md::AlarmFactory> mtf::StubInputPlatform::alarm_factory{
151 std::make_shared<md::AlarmFactory>(mtf::StubInputPlatform::steady_clock, mtf::StubInputPlatform::steady_timer_fd)};
131std::atomic<mtf::StubInputPlatform*> mtf::StubInputPlatform::stub_input_platform{nullptr};152std::atomic<mtf::StubInputPlatform*> mtf::StubInputPlatform::stub_input_platform{nullptr};
132std::vector<std::weak_ptr<mir::input::InputDevice>> mtf::StubInputPlatform::device_store;153std::vector<std::weak_ptr<mir::input::InputDevice>> mtf::StubInputPlatform::device_store;
133std::mutex mtf::StubInputPlatform::device_store_guard;154std::mutex mtf::StubInputPlatform::device_store_guard;
134155
=== modified file 'tests/unit-tests/input/CMakeLists.txt'
--- tests/unit-tests/input/CMakeLists.txt 2016-03-31 19:18:58 +0000
+++ tests/unit-tests/input/CMakeLists.txt 2016-03-31 19:18:58 +0000
@@ -14,7 +14,6 @@
14 ${CMAKE_CURRENT_SOURCE_DIR}/test_default_input_manager.cpp14 ${CMAKE_CURRENT_SOURCE_DIR}/test_default_input_manager.cpp
15 ${CMAKE_CURRENT_SOURCE_DIR}/test_surface_input_dispatcher.cpp15 ${CMAKE_CURRENT_SOURCE_DIR}/test_surface_input_dispatcher.cpp
16 ${CMAKE_CURRENT_SOURCE_DIR}/test_seat_input_device_tracker.cpp16 ${CMAKE_CURRENT_SOURCE_DIR}/test_seat_input_device_tracker.cpp
17 ${CMAKE_CURRENT_SOURCE_DIR}/test_key_repeat_dispatcher.cpp
18 ${CMAKE_CURRENT_SOURCE_DIR}/test_key_repeater.cpp17 ${CMAKE_CURRENT_SOURCE_DIR}/test_key_repeater.cpp
19 ${CMAKE_CURRENT_SOURCE_DIR}/test_validator.cpp18 ${CMAKE_CURRENT_SOURCE_DIR}/test_validator.cpp
20)19)
2120
=== modified file 'tests/unit-tests/input/evdev/test_evdev_device_detection.cpp'
--- tests/unit-tests/input/evdev/test_evdev_device_detection.cpp 2016-03-23 06:39:56 +0000
+++ tests/unit-tests/input/evdev/test_evdev_device_detection.cpp 2016-03-31 19:18:58 +0000
@@ -22,13 +22,18 @@
22#include "src/platforms/evdev/libinput_device.h"22#include "src/platforms/evdev/libinput_device.h"
23#include "src/server/report/null_report_factory.h"23#include "src/server/report/null_report_factory.h"
2424
25#include "mir/test/doubles/mock_alarm_factory.h"
26#include "mir/test/doubles/advanceable_clock.h"
25#include "mir_test_framework/libinput_environment.h"27#include "mir_test_framework/libinput_environment.h"
28#include "mir/test/fake_shared.h"
2629
27#include <gtest/gtest.h>30#include <gtest/gtest.h>
28#include <gmock/gmock.h>31#include <gmock/gmock.h>
29#include <tuple>32#include <tuple>
3033
31namespace mtf = mir_test_framework;34namespace mtf = mir_test_framework;
35namespace mt = mir::test;
36namespace mtd = mt::doubles;
32namespace mi = mir::input;37namespace mi = mir::input;
33namespace mie = mi::evdev;38namespace mie = mi::evdev;
3439
@@ -44,7 +49,9 @@
44 udev* ctx = reinterpret_cast<udev*>(1);49 udev* ctx = reinterpret_cast<udev*>(1);
45 auto dev = env.setup_device(std::get<0>(param));50 auto dev = env.setup_device(std::get<0>(param));
46 std::shared_ptr<libinput> lib = mie::make_libinput(ctx);51 std::shared_ptr<libinput> lib = mie::make_libinput(ctx);
47 mie::LibInputDevice device(mir::report::null_input_report(), mie::make_libinput_device(lib, dev));52 ::testing::NiceMock<mtd::MockAlarmFactory> mock_alarm_factory;
53 mtd::AdvanceableClock clock;
54 mie::LibInputDevice device(mir::report::null_input_report(), mie::make_libinput_device(lib, dev), mt::fake_shared(mock_alarm_factory), mt::fake_shared(clock));
48 auto info = device.get_device_info();55 auto info = device.get_device_info();
49 EXPECT_THAT(info.capabilities, Eq(std::get<1>(param)));56 EXPECT_THAT(info.capabilities, Eq(std::get<1>(param)));
50}57}
5158
=== modified file 'tests/unit-tests/input/evdev/test_libinput_device.cpp'
--- tests/unit-tests/input/evdev/test_libinput_device.cpp 2016-03-23 06:39:56 +0000
+++ tests/unit-tests/input/evdev/test_libinput_device.cpp 2016-03-31 19:18:58 +0000
@@ -25,12 +25,16 @@
25#include "mir/input/input_sink.h"25#include "mir/input/input_sink.h"
26#include "mir/input/pointer_settings.h"26#include "mir/input/pointer_settings.h"
27#include "mir/input/touchpad_settings.h"27#include "mir/input/touchpad_settings.h"
28#include "mir/input/keyboard_settings.h"
28#include "mir/flags.h"29#include "mir/flags.h"
29#include "mir/geometry/point.h"30#include "mir/geometry/point.h"
30#include "mir/geometry/rectangle.h"31#include "mir/geometry/rectangle.h"
31#include "mir/test/event_matchers.h"32#include "mir/test/event_matchers.h"
32#include "mir/test/doubles/mock_libinput.h"33#include "mir/test/doubles/mock_libinput.h"
34#include "mir/test/doubles/fake_alarm_factory.h"
35#include "mir/test/doubles/advanceable_clock.h"
33#include "mir/test/gmock_fixes.h"36#include "mir/test/gmock_fixes.h"
37#include "mir/test/fake_shared.h"
34#include "mir/udev/wrapper.h"38#include "mir/udev/wrapper.h"
35#include "mir/cookie/authority.h"39#include "mir/cookie/authority.h"
36#include "mir_test_framework/libinput_environment.h"40#include "mir_test_framework/libinput_environment.h"
@@ -60,6 +64,7 @@
60};64};
6165
62using namespace ::testing;66using namespace ::testing;
67using namespace std::chrono_literals;
6368
64struct MockInputSink : mi::InputSink69struct MockInputSink : mi::InputSink
65{70{
@@ -124,6 +129,8 @@
124struct LibInputDevice : public ::testing::Test129struct LibInputDevice : public ::testing::Test
125{130{
126 mtf::LibInputEnvironment env;131 mtf::LibInputEnvironment env;
132 mtd::AdvanceableClock clock;
133 mtd::FakeAlarmFactory alarm_factory;
127 ::testing::NiceMock<MockInputSink> mock_sink;134 ::testing::NiceMock<MockInputSink> mock_sink;
128 ::testing::NiceMock<MockEventBuilder> mock_builder;135 ::testing::NiceMock<MockEventBuilder> mock_builder;
129 std::shared_ptr<libinput> lib;136 std::shared_ptr<libinput> lib;
@@ -243,33 +250,33 @@
243struct LibInputDeviceOnLaptopKeyboard : public LibInputDevice250struct LibInputDeviceOnLaptopKeyboard : public LibInputDevice
244{251{
245 libinput_device*const fake_device = setup_laptop_keyboard();252 libinput_device*const fake_device = setup_laptop_keyboard();
246 mie::LibInputDevice keyboard{mir::report::null_input_report(), mie::make_libinput_device(lib, fake_device)};253 mie::LibInputDevice keyboard{mir::report::null_input_report(), mie::make_libinput_device(lib, fake_device), mt::fake_shared(alarm_factory), mt::fake_shared(clock)};
247};254};
248255
249struct LibInputDeviceOnMouse : public LibInputDevice256struct LibInputDeviceOnMouse : public LibInputDevice
250{257{
251 libinput_device*const fake_device = setup_mouse();258 libinput_device*const fake_device = setup_mouse();
252 mie::LibInputDevice mouse{mir::report::null_input_report(), mie::make_libinput_device(lib, fake_device)};259 mie::LibInputDevice mouse{mir::report::null_input_report(), mie::make_libinput_device(lib, fake_device), mt::fake_shared(alarm_factory), mt::fake_shared(clock)};
253};260};
254261
255struct LibInputDeviceOnLaptopKeyboardAndMouse : public LibInputDevice262struct LibInputDeviceOnLaptopKeyboardAndMouse : public LibInputDevice
256{263{
257 libinput_device*const fake_device = setup_mouse();264 libinput_device*const fake_device = setup_mouse();
258 libinput_device*const fake_device_2 = setup_laptop_keyboard();265 libinput_device*const fake_device_2 = setup_laptop_keyboard();
259 mie::LibInputDevice keyboard{mir::report::null_input_report(), mie::make_libinput_device(lib, fake_device_2)};266 mie::LibInputDevice keyboard{mir::report::null_input_report(), mie::make_libinput_device(lib, fake_device_2), mt::fake_shared(alarm_factory), mt::fake_shared(clock)};
260 mie::LibInputDevice mouse{mir::report::null_input_report(), mie::make_libinput_device(lib, fake_device)};267 mie::LibInputDevice mouse{mir::report::null_input_report(), mie::make_libinput_device(lib, fake_device), mt::fake_shared(alarm_factory), mt::fake_shared(clock)};
261};268};
262269
263struct LibInputDeviceOnTouchScreen : public LibInputDevice270struct LibInputDeviceOnTouchScreen : public LibInputDevice
264{271{
265 libinput_device*const fake_device = setup_touchscreen();272 libinput_device*const fake_device = setup_touchscreen();
266 mie::LibInputDevice touch_screen{mir::report::null_input_report(), mie::make_libinput_device(lib, fake_device)};273 mie::LibInputDevice touch_screen{mir::report::null_input_report(), mie::make_libinput_device(lib, fake_device), mt::fake_shared(alarm_factory), mt::fake_shared(clock)};
267};274};
268275
269struct LibInputDeviceOnTouchpad : public LibInputDevice276struct LibInputDeviceOnTouchpad : public LibInputDevice
270{277{
271 libinput_device*const fake_device = setup_touchpad();278 libinput_device*const fake_device = setup_touchpad();
272 mie::LibInputDevice touchpad{mir::report::null_input_report(), mie::make_libinput_device(lib, fake_device)};279 mie::LibInputDevice touchpad{mir::report::null_input_report(), mie::make_libinput_device(lib, fake_device), mt::fake_shared(alarm_factory), mt::fake_shared(clock)};
273};280};
274}281}
275282
@@ -284,7 +291,7 @@
284 .Times(1);291 .Times(1);
285292
286 mie::LibInputDevice dev(mir::report::null_input_report(),293 mie::LibInputDevice dev(mir::report::null_input_report(),
287 mie::make_libinput_device(lib, fake_device));294 mie::make_libinput_device(lib, fake_device), mt::fake_shared(alarm_factory), mt::fake_shared(clock));
288 dev.start(&mock_sink, &mock_builder);295 dev.start(&mock_sink, &mock_builder);
289}296}
290297
@@ -299,7 +306,7 @@
299 EXPECT_CALL(env.mock_libinput, libinput_device_ref(second_fake_device)).Times(1);306 EXPECT_CALL(env.mock_libinput, libinput_device_ref(second_fake_device)).Times(1);
300307
301 mie::LibInputDevice dev(mir::report::null_input_report(),308 mie::LibInputDevice dev(mir::report::null_input_report(),
302 mie::make_libinput_device(lib, fake_device));309 mie::make_libinput_device(lib, fake_device), mt::fake_shared(alarm_factory), mt::fake_shared(clock));
303 dev.add_device_of_group(mie::make_libinput_device(lib, second_fake_device));310 dev.add_device_of_group(mie::make_libinput_device(lib, second_fake_device));
304 dev.start(&mock_sink, &mock_builder);311 dev.start(&mock_sink, &mock_builder);
305}312}
@@ -310,7 +317,7 @@
310 auto second_fake_device = setup_trackpad();317 auto second_fake_device = setup_trackpad();
311318
312 mie::LibInputDevice dev(mir::report::null_input_report(),319 mie::LibInputDevice dev(mir::report::null_input_report(),
313 mie::make_libinput_device(lib, fake_device));320 mie::make_libinput_device(lib, fake_device), mt::fake_shared(alarm_factory), mt::fake_shared(clock));
314 dev.add_device_of_group(mie::make_libinput_device(lib, second_fake_device));321 dev.add_device_of_group(mie::make_libinput_device(lib, second_fake_device));
315 auto info = dev.get_device_info();322 auto info = dev.get_device_info();
316323
@@ -327,7 +334,7 @@
327 EXPECT_CALL(env.mock_libinput, libinput_device_unref(fake_device))334 EXPECT_CALL(env.mock_libinput, libinput_device_unref(fake_device))
328 .Times(1);335 .Times(1);
329336
330 mie::LibInputDevice dev(mir::report::null_input_report(), mie::make_libinput_device(lib, fake_device));337 mie::LibInputDevice dev(mir::report::null_input_report(), mie::make_libinput_device(lib, fake_device), mt::fake_shared(alarm_factory), mt::fake_shared(clock));
331}338}
332339
333TEST_F(LibInputDeviceOnLaptopKeyboard, process_event_converts_key_event)340TEST_F(LibInputDeviceOnLaptopKeyboard, process_event_converts_key_event)
@@ -343,6 +350,31 @@
343 process_events(keyboard);350 process_events(keyboard);
344}351}
345352
353TEST_F(LibInputDeviceOnLaptopKeyboard, key_press_triggers_repeat)
354{
355 mi::KeyboardSettings keyboard_settings;
356 keyboard_settings.repeat_delay = 600ms;
357 keyboard_settings.repeat_interval = 200ms;
358 std::function<void()> timer_callback;
359
360 EXPECT_CALL(mock_sink, handle_input(AllOf(
361 mt::KeyOfScanCode(KEY_A),
362 mt::KeyDownEvent())))
363 .Times(1);
364 EXPECT_CALL(mock_sink, handle_input(AllOf(
365 mt::KeyOfScanCode(KEY_A),
366 mt::KeyRepeatEvent())))
367 .Times(2);
368
369 keyboard.start(&mock_sink, &mock_builder);
370 keyboard.apply_settings(keyboard_settings);
371 env.mock_libinput.setup_key_event(fake_device, event_time_1, KEY_A, LIBINPUT_KEY_STATE_PRESSED);
372 process_events(keyboard);
373
374 alarm_factory.advance_by(601ms);
375 alarm_factory.advance_by(201ms);
376}
377
346TEST_F(LibInputDeviceOnLaptopKeyboard, process_event_accumulates_key_state)378TEST_F(LibInputDeviceOnLaptopKeyboard, process_event_accumulates_key_state)
347{379{
348 InSequence seq;380 InSequence seq;
349381
=== modified file 'tests/unit-tests/input/test_default_device.cpp'
--- tests/unit-tests/input/test_default_device.cpp 2016-03-23 06:39:56 +0000
+++ tests/unit-tests/input/test_default_device.cpp 2016-03-31 19:18:58 +0000
@@ -21,54 +21,23 @@
21#include "mir/input/touchpad_configuration.h"21#include "mir/input/touchpad_configuration.h"
22#include "mir/input/pointer_configuration.h"22#include "mir/input/pointer_configuration.h"
23#include "mir/dispatch/action_queue.h"23#include "mir/dispatch/action_queue.h"
24#include "mir/test/doubles/mock_input_device.h"
24#include <gtest/gtest.h>25#include <gtest/gtest.h>
25#include <gmock/gmock.h>26#include <gmock/gmock.h>
26#include <stdexcept>27#include <stdexcept>
2728
28namespace mi = mir::input;29namespace mi = mir::input;
30namespace mtd = mir::test::doubles;
29namespace md = mir::dispatch;31namespace md = mir::dispatch;
30using namespace ::testing;32using namespace ::testing;
31namespace33namespace
32{34{
33struct MockInputDevice : public mi::InputDevice
34{
35 MOCK_METHOD2(start, void(mi::InputSink* destination, mi::EventBuilder* builder));
36 MOCK_METHOD0(stop, void());
37 MOCK_METHOD0(get_device_info, mi::InputDeviceInfo());
38 MOCK_CONST_METHOD0(get_pointer_settings, mir::optional_value<mi::PointerSettings>());
39 MOCK_METHOD1(apply_settings, void(mi::PointerSettings const&));
40 MOCK_CONST_METHOD0(get_touchpad_settings, mir::optional_value<mi::TouchpadSettings>());
41 MOCK_METHOD1(apply_settings, void(mi::TouchpadSettings const&));
42};
43
44struct DefaultDevice : Test35struct DefaultDevice : Test
45{36{
46 NiceMock<MockInputDevice> touchpad;37 NiceMock<mtd::MockInputDevice> touchpad{"name", "unique", mi::DeviceCapability::touchpad|mi::DeviceCapability::pointer};
47 NiceMock<MockInputDevice> mouse;38 NiceMock<mtd::MockInputDevice> mouse{"name", "unique", mi::DeviceCapability::pointer};
48 NiceMock<MockInputDevice> keyboard;39 NiceMock<mtd::MockInputDevice> keyboard{"name", "unique", mi::DeviceCapability::keyboard};
49 std::shared_ptr<md::ActionQueue> queue{std::make_shared<md::ActionQueue>()};40 std::shared_ptr<md::ActionQueue> queue{std::make_shared<md::ActionQueue>()};
50
51 DefaultDevice()
52 {
53 using optional_pointer_settings = mir::optional_value<mi::PointerSettings>;
54 using optional_touchpad_settings = mir::optional_value<mi::TouchpadSettings>;
55 ON_CALL(touchpad, get_device_info())
56 .WillByDefault(Return(mi::InputDeviceInfo{"name", "unique", mi::DeviceCapability::touchpad|mi::DeviceCapability::pointer}));
57 ON_CALL(touchpad, get_pointer_settings())
58 .WillByDefault(Return(optional_pointer_settings{mi::PointerSettings{}}));
59 ON_CALL(touchpad, get_touchpad_settings())
60 .WillByDefault(Return(optional_touchpad_settings{mi::TouchpadSettings{}}));
61
62 ON_CALL(mouse, get_device_info())
63 .WillByDefault(Return(mi::InputDeviceInfo{"name", "unique", mi::DeviceCapability::pointer}));
64 ON_CALL(mouse, get_pointer_settings()).WillByDefault(Return(optional_pointer_settings{mi::PointerSettings{}}));
65 ON_CALL(mouse, get_touchpad_settings()).WillByDefault(Return(optional_touchpad_settings{}));
66
67 ON_CALL(keyboard, get_device_info())
68 .WillByDefault(Return(mi::InputDeviceInfo{"name", "unique", mi::DeviceCapability::keyboard}));
69 ON_CALL(keyboard, get_pointer_settings()).WillByDefault(Return(optional_pointer_settings{}));
70 ON_CALL(keyboard, get_touchpad_settings()).WillByDefault(Return(optional_touchpad_settings{}));
71 }
72};41};
7342
74}43}
@@ -91,7 +60,6 @@
91 EXPECT_THROW({dev.apply_pointer_configuration(pointer_conf);}, std::invalid_argument);60 EXPECT_THROW({dev.apply_pointer_configuration(pointer_conf);}, std::invalid_argument);
92}61}
9362
94
95TEST_F(DefaultDevice, accepts_pointer_config_on_mice)63TEST_F(DefaultDevice, accepts_pointer_config_on_mice)
96{64{
97 mi::DefaultDevice dev(MirInputDeviceId{17}, queue, mouse);65 mi::DefaultDevice dev(MirInputDeviceId{17}, queue, mouse);
@@ -118,6 +86,7 @@
118 queue->dispatch(md::FdEvent::readable);86 queue->dispatch(md::FdEvent::readable);
119}87}
12088
89
121TEST_F(DefaultDevice, ensures_cursor_accleration_bias_is_in_range)90TEST_F(DefaultDevice, ensures_cursor_accleration_bias_is_in_range)
122{91{
123 mi::DefaultDevice dev(MirInputDeviceId{17}, queue, touchpad);92 mi::DefaultDevice dev(MirInputDeviceId{17}, queue, touchpad);
12493
=== removed file 'tests/unit-tests/input/test_key_repeat_dispatcher.cpp'
--- tests/unit-tests/input/test_key_repeat_dispatcher.cpp 2016-03-23 06:39:56 +0000
+++ tests/unit-tests/input/test_key_repeat_dispatcher.cpp 1970-01-01 00:00:00 +0000
@@ -1,184 +0,0 @@
1/*
2 * Copyright © 2015 Canonical Ltd.
3 *
4 * This program is free software: you can redistribute it and/or modify
5 * it under the terms of the GNU General Public License version 3 as
6 * published by the Free Software Foundation.
7 *
8 * This program is distributed in the hope that it will be useful,
9 * but WITHOUT ANY WARRANTY; without even the implied warranty of
10 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 * GNU General Public License for more details.
12 *
13 * You should have received a copy of the GNU General Public License
14 * along with this program. If not, see <http://www.gnu.org/licenses/>.
15 *
16 * Authored by: Robert Carr <robert.carr@canonical.com>
17 */
18
19#include "src/server/input/key_repeat_dispatcher.h"
20
21#include "mir/events/event_private.h"
22#include "mir/events/event_builders.h"
23#include "mir/time/alarm.h"
24#include "mir/time/alarm_factory.h"
25#include "mir/cookie/authority.h"
26#include "mir/input/input_device_observer.h"
27#include "mir/input/pointer_configuration.h"
28#include "mir/input/touchpad_configuration.h"
29#include "mir/input/device.h"
30
31#include "mir/test/fake_shared.h"
32#include "mir/test/event_matchers.h"
33#include "mir/test/doubles/mock_input_dispatcher.h"
34#include "mir/test/doubles/mock_input_device_hub.h"
35
36#include <gtest/gtest.h>
37#include <gmock/gmock.h>
38
39namespace mi = mir::input;
40namespace mev = mir::events;
41namespace mt = mir::test;
42namespace mtd = mt::doubles;
43
44using namespace ::testing;
45
46namespace
47{
48struct MockAlarm : public mir::time::Alarm
49{
50 MOCK_METHOD0(cancel, bool());
51 MOCK_CONST_METHOD0(state, mir::time::Alarm::State());
52 MOCK_METHOD1(reschedule_in, bool(std::chrono::milliseconds));
53 MOCK_METHOD1(reschedule_for, bool(mir::time::Timestamp));
54
55 // destructor cancels the alarm
56 ~MockAlarm()
57 {
58 cancel();
59 }
60};
61
62struct MockAlarmFactory : public mir::time::AlarmFactory
63{
64 MOCK_METHOD1(create_alarm_adapter, mir::time::Alarm*(std::function<void()> const&));
65 std::unique_ptr<mir::time::Alarm> create_alarm(std::function<void()> const& cb)
66 {
67 return std::unique_ptr<mir::time::Alarm>(create_alarm_adapter(cb));
68 }
69
70 std::unique_ptr<mir::time::Alarm> create_alarm(std::shared_ptr<mir::LockableCallback> const&)
71 {
72 return nullptr;
73 }
74};
75
76struct StubDevice : public mi::Device
77{
78 MirInputDeviceId device_id;
79 StubDevice(MirInputDeviceId id) : device_id(id) {}
80 MirInputDeviceId id() const { return device_id;}
81 mi::DeviceCapabilities capabilities() const {return mi::DeviceCapability::keyboard;}
82 std::string name() const {return {};}
83 std::string unique_id() const {return {};}
84
85 mir::optional_value<mi::PointerConfiguration> pointer_configuration() const {return {};}
86 void apply_pointer_configuration(mi::PointerConfiguration const&) {;}
87 mir::optional_value<mi::TouchpadConfiguration> touchpad_configuration() const {return {};}
88 void apply_touchpad_configuration(mi::TouchpadConfiguration const&) {}
89};
90
91struct KeyRepeatDispatcher : public testing::Test
92{
93 KeyRepeatDispatcher()
94 : dispatcher(mock_next_dispatcher, mock_alarm_factory, cookie_authority, true, repeat_time, repeat_delay)
95 {
96 ON_CALL(hub,add_observer(_)).WillByDefault(SaveArg<0>(&observer));
97 dispatcher.set_input_device_hub(mt::fake_shared(hub));
98 }
99 void simulate_device_removal()
100 {
101 StubDevice dev(test_device);
102 observer->device_removed(mt::fake_shared(dev));
103 observer->changes_complete();
104 }
105
106 const MirInputDeviceId test_device = 123;
107 std::shared_ptr<mtd::MockInputDispatcher> mock_next_dispatcher = std::make_shared<mtd::MockInputDispatcher>();
108 std::shared_ptr<MockAlarmFactory> mock_alarm_factory = std::make_shared<MockAlarmFactory>();
109 std::shared_ptr<mir::cookie::Authority> cookie_authority = mir::cookie::Authority::create();
110 std::chrono::milliseconds const repeat_time{2};
111 std::chrono::milliseconds const repeat_delay{1};
112 std::shared_ptr<mi::InputDeviceObserver> observer;
113 NiceMock<mtd::MockInputDeviceHub> hub;
114 mi::KeyRepeatDispatcher dispatcher;
115
116 mir::EventUPtr a_key_down_event()
117 {
118 return mev::make_event(test_device, std::chrono::nanoseconds(0), std::vector<uint8_t>{}, mir_keyboard_action_down, 0, 0, mir_input_event_modifier_alt);
119 }
120
121 mir::EventUPtr a_key_up_event()
122 {
123 return mev::make_event(test_device, std::chrono::nanoseconds(0), std::vector<uint8_t>{}, mir_keyboard_action_up, 0, 0, mir_input_event_modifier_alt);
124 }
125};
126}
127
128TEST_F(KeyRepeatDispatcher, forwards_start_stop)
129{
130 InSequence seq;
131 EXPECT_CALL(*mock_next_dispatcher, start()).Times(1);
132 EXPECT_CALL(*mock_next_dispatcher, stop()).Times(1);
133
134 dispatcher.start();
135 dispatcher.stop();
136}
137
138TEST_F(KeyRepeatDispatcher, schedules_alarm_to_repeat_key_down)
139{
140 MockAlarm *mock_alarm = new MockAlarm; // deleted by AlarmFactory
141 std::function<void()> alarm_function;
142
143 InSequence seq;
144 EXPECT_CALL(*mock_alarm_factory, create_alarm_adapter(_)).Times(1).
145 WillOnce(DoAll(SaveArg<0>(&alarm_function), Return(mock_alarm)));
146 // Once for initial down and again when invoked
147 EXPECT_CALL(*mock_alarm, reschedule_in(repeat_time)).Times(1).WillOnce(Return(true));
148 EXPECT_CALL(*mock_next_dispatcher, dispatch(mt::KeyDownEvent())).Times(1);
149 EXPECT_CALL(*mock_next_dispatcher, dispatch(mt::KeyRepeatEvent())).Times(1);
150 EXPECT_CALL(*mock_alarm, reschedule_in(repeat_delay)).Times(1).WillOnce(Return(true));
151 EXPECT_CALL(*mock_next_dispatcher, dispatch(mt::KeyUpEvent())).Times(1);
152
153 // Schedule the repeat
154 dispatcher.dispatch(*a_key_down_event());
155 // Trigger the repeat
156 alarm_function();
157 // Trigger the cancel
158 dispatcher.dispatch(*a_key_up_event());
159}
160
161TEST_F(KeyRepeatDispatcher, stops_repeat_on_device_removal)
162{
163 MockAlarm *mock_alarm = new MockAlarm;
164 std::function<void()> alarm_function;
165 bool alarm_canceled = false;
166
167 InSequence seq;
168 EXPECT_CALL(*mock_alarm_factory, create_alarm_adapter(_)).Times(1).
169 WillOnce(DoAll(SaveArg<0>(&alarm_function), Return(mock_alarm)));
170 // Once for initial down and again when invoked
171 EXPECT_CALL(*mock_alarm, reschedule_in(repeat_time)).Times(1).WillOnce(Return(true));
172 EXPECT_CALL(*mock_next_dispatcher, dispatch(mt::KeyDownEvent())).Times(1);
173 EXPECT_CALL(*mock_next_dispatcher, dispatch(mt::KeyRepeatEvent())).Times(1);
174 EXPECT_CALL(*mock_alarm, reschedule_in(repeat_delay)).Times(1).WillOnce(Return(true));
175 ON_CALL(*mock_alarm, cancel()).WillByDefault(Invoke([&](){alarm_canceled = true; return true;}));
176
177 dispatcher.dispatch(*a_key_down_event());
178
179 alarm_function();
180 Mock::VerifyAndClearExpectations(mock_alarm); // mock_alarm will be deleted after this
181
182 simulate_device_removal();
183 EXPECT_THAT(alarm_canceled, Eq(true));
184}
1850
=== modified file 'tests/unit-tests/input/test_x11_platform.cpp'
--- tests/unit-tests/input/test_x11_platform.cpp 2016-03-23 06:39:56 +0000
+++ tests/unit-tests/input/test_x11_platform.cpp 2016-03-31 19:18:58 +0000
@@ -34,12 +34,15 @@
34#include "mir/cookie/authority.h"34#include "mir/cookie/authority.h"
35#include "mir/test/event_matchers.h"35#include "mir/test/event_matchers.h"
3636
37#include "mir/input/keyboard_settings.h"
38
37namespace md = mir::dispatch;39namespace md = mir::dispatch;
38namespace mi = mir::input;40namespace mi = mir::input;
39namespace mt = mir::test;41namespace mt = mir::test;
40namespace mtd = mt::doubles;42namespace mtd = mt::doubles;
4143
42using namespace ::testing;44using namespace ::testing;
45using namespace std::chrono_literals;
4346
44namespace47namespace
45{48{
@@ -245,3 +248,80 @@
245248
246 process_input_event();249 process_input_event();
247}250}
251
252TEST_F(X11PlatformTest, keyboard_device_forwards_x11_repeat_settings)
253{
254 XkbControlsRec ctrls_desc;
255 XkbDescRec xkb_desc;
256 ctrls_desc.repeat_delay = 600;
257 ctrls_desc.repeat_interval = 100;
258 EXPECT_CALL(mock_x11, XGetKeyboardControl(_,_))
259 .WillOnce(Invoke([](Display*, XKeyboardState* state)
260 {
261 state->global_auto_repeat = 1;
262 return 1;
263 }));
264 EXPECT_CALL(mock_x11, XkbQueryExtension(_,_,_,_,_,_))
265 .WillOnce(Return(1));
266 EXPECT_CALL(mock_x11, XkbAllocKeyboard())
267 .WillOnce(Return(&xkb_desc));
268 EXPECT_CALL(mock_x11, XkbGetControls(_,XkbRepeatKeysMask, &xkb_desc))
269 .WillOnce(Invoke([&](Display*, int, XkbDescPtr)
270 {
271 xkb_desc.ctrls = &ctrls_desc;
272 return 1;
273 }));
274 EXPECT_CALL(mock_x11, XkbFreeKeyboard(&xkb_desc, _, _));
275
276
277 capture_devices(); x11_platform.start();
278
279 for (auto const device : devices)
280 {
281 if (contains(device->get_device_info().capabilities,mi::DeviceCapability::keyboard))
282 {
283 auto settings = device->get_keyboard_settings();
284 EXPECT_THAT(settings.value().repeat_enabled, Eq(true));
285 EXPECT_THAT(settings.value().repeat_delay, Eq(600ms));
286 EXPECT_THAT(settings.value().repeat_interval, Eq(100ms));
287 }
288 }
289}
290
291TEST_F(X11PlatformTest, keyboard_device_forwards_settings_to_x11)
292{
293 XkbControlsRec ctrls_desc;
294 XkbDescRec xkb_desc;
295
296 EXPECT_CALL(mock_x11, XChangeKeyboardControl(_, KBAutoRepeatMode,_));
297 EXPECT_CALL(mock_x11, XkbQueryExtension(_,_,_,_,_,_))
298 .WillOnce(Return(1));
299 EXPECT_CALL(mock_x11, XkbAllocKeyboard())
300 .WillOnce(Return(&xkb_desc));
301 EXPECT_CALL(mock_x11, XkbGetControls(_,XkbRepeatKeysMask, &xkb_desc))
302 .WillOnce(Invoke([&](Display*, int, XkbDescPtr)
303 {
304 xkb_desc.ctrls = &ctrls_desc;
305 return 1;
306 }));
307 EXPECT_CALL(mock_x11, XkbSetControls(_,XkbRepeatKeysMask, &xkb_desc))
308 .WillOnce(Return(1));
309 EXPECT_CALL(mock_x11, XkbFreeKeyboard(&xkb_desc, _, _));
310
311
312 capture_devices(); x11_platform.start();
313
314 for (auto const device : devices)
315 {
316 if (contains(device->get_device_info().capabilities,mi::DeviceCapability::keyboard))
317 {
318 mi::KeyboardSettings settings;
319 settings.repeat_interval = 23ms;
320 settings.repeat_delay = 1000ms;
321 device->apply_settings(settings);
322
323 EXPECT_THAT(ctrls_desc.repeat_delay, Eq(1000));
324 EXPECT_THAT(ctrls_desc.repeat_interval, Eq(23));
325 }
326 }
327}

Subscribers

People subscribed via source and target branches