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
1=== modified file 'include/platform/mir/input/input_device.h'
2--- include/platform/mir/input/input_device.h 2016-01-29 08:18:22 +0000
3+++ include/platform/mir/input/input_device.h 2016-03-31 19:18:58 +0000
4@@ -39,6 +39,7 @@
5
6 class PointerSettings;
7 class TouchpadSettings;
8+class KeyboardSettings;
9
10 /**
11 * Represents an input device.
12@@ -65,6 +66,9 @@
13
14 virtual optional_value<TouchpadSettings> get_touchpad_settings() const = 0;
15 virtual void apply_settings(TouchpadSettings const&) = 0;
16+
17+ virtual optional_value<KeyboardSettings> get_keyboard_settings() const = 0;
18+ virtual void apply_settings(KeyboardSettings const&) = 0;
19 protected:
20 InputDevice(InputDevice const&) = delete;
21 InputDevice& operator=(InputDevice const&) = delete;
22
23=== added file 'include/platform/mir/input/keyboard_settings.h'
24--- include/platform/mir/input/keyboard_settings.h 1970-01-01 00:00:00 +0000
25+++ include/platform/mir/input/keyboard_settings.h 2016-03-31 19:18:58 +0000
26@@ -0,0 +1,42 @@
27+/*
28+ * Copyright © 2016 Canonical Ltd.
29+ *
30+ * This program is free software: you can redistribute it and/or modify it
31+ * under the terms of the GNU Lesser General Public License version 3,
32+ * as published by the Free Software Foundation.
33+ *
34+ * This program is distributed in the hope that it will be useful,
35+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
36+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
37+ * GNU Lesser General Public License for more details.
38+ *
39+ * You should have received a copy of the GNU Lesser General Public License
40+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
41+ *
42+ * Authored by:
43+ * Andreas Pokorny <andreas.pokorny@canonical.com>
44+ */
45+
46+#ifndef MIR_INPUT_KEY_BOARD_SETTINGS_H_
47+#define MIR_INPUT_KEY_BOARD_SETTINGS_H_
48+
49+#include "mir_toolkit/mir_input_device.h"
50+#include <chrono>
51+
52+namespace mir
53+{
54+namespace input
55+{
56+
57+struct KeyboardSettings
58+{
59+ KeyboardSettings() {}
60+ bool repeat_enabled{true};
61+ std::chrono::milliseconds repeat_delay{50};
62+ std::chrono::milliseconds repeat_interval{500};
63+};
64+
65+}
66+}
67+
68+#endif
69
70=== modified file 'src/platforms/evdev/key_repeater.cpp'
71--- src/platforms/evdev/key_repeater.cpp 2016-03-31 19:18:58 +0000
72+++ src/platforms/evdev/key_repeater.cpp 2016-03-31 19:18:58 +0000
73@@ -29,6 +29,8 @@
74 {
75 }
76
77+mie::KeyRepeater::~KeyRepeater() = default;
78+
79 void mie::KeyRepeater::enable(std::chrono::milliseconds repeat_delay, std::chrono::milliseconds repeat_interval)
80 {
81 enabled_ = true;
82
83=== modified file 'src/platforms/evdev/key_repeater.h'
84--- src/platforms/evdev/key_repeater.h 2016-03-31 19:18:58 +0000
85+++ src/platforms/evdev/key_repeater.h 2016-03-31 19:18:58 +0000
86@@ -39,6 +39,7 @@
87 struct KeyRepeater
88 {
89 KeyRepeater(std::shared_ptr<mir::time::AlarmFactory> const& alarm_factory, std::function<void(int32_t keycode)> const& send_repeat_key);
90+ ~KeyRepeater();
91 void enable(std::chrono::milliseconds repeat_delay, std::chrono::milliseconds repeat_interval);
92 void disable();
93 bool enabled() const;
94
95=== modified file 'src/platforms/evdev/libinput_device.cpp'
96--- src/platforms/evdev/libinput_device.cpp 2016-03-23 06:39:56 +0000
97+++ src/platforms/evdev/libinput_device.cpp 2016-03-31 19:18:58 +0000
98@@ -27,11 +27,11 @@
99 #include "mir/input/device_capability.h"
100 #include "mir/input/pointer_settings.h"
101 #include "mir/input/touchpad_settings.h"
102+#include "mir/input/keyboard_settings.h"
103 #include "mir/input/input_device_info.h"
104 #include "mir/events/event_builders.h"
105 #include "mir/geometry/displacement.h"
106-#include "mir/dispatch/dispatchable.h"
107-#include "mir/fd.h"
108+#include "mir/time/clock.h"
109 #define MIR_LOG_COMPONENT "evdev"
110 #include "mir/log.h"
111 #include "mir/raii.h"
112@@ -45,13 +45,19 @@
113 #include <sstream>
114 #include <algorithm>
115
116-namespace md = mir::dispatch;
117 namespace mi = mir::input;
118 namespace mie = mi::evdev;
119 using namespace std::literals::chrono_literals;
120
121-mie::LibInputDevice::LibInputDevice(std::shared_ptr<mi::InputReport> const& report, LibInputDevicePtr dev)
122- : report{report}, pointer_pos{0, 0}, button_state{0}
123+mie::LibInputDevice::LibInputDevice(std::shared_ptr<mi::InputReport> const& report,
124+ LibInputDevicePtr dev,
125+ std::shared_ptr<mir::time::AlarmFactory> const& alarm_factory,
126+ std::shared_ptr<mir::time::Clock> const& clock)
127+ : report{report},
128+ repeater{alarm_factory, [this](auto code) { this->handle_repeat(code); }},
129+ clock{clock},
130+ pointer_pos{0, 0},
131+ button_state{0}
132 {
133 add_device_of_group(std::move(dev));
134 }
135@@ -135,6 +141,11 @@
136 auto const code = libinput_event_keyboard_get_key(keyboard);
137 report->received_event_from_kernel(time.count(), EV_KEY, code, action);
138
139+ if (action == mir_keyboard_action_down)
140+ repeater.press(code);
141+ else
142+ repeater.release(code);
143+
144 return builder->key_event(time, action, xkb_keysym_t{0}, code);
145 }
146
147@@ -476,3 +487,32 @@
148 LIBINPUT_CONFIG_MIDDLE_EMULATION_ENABLED :
149 LIBINPUT_CONFIG_MIDDLE_EMULATION_DISABLED);
150 }
151+
152+mir::optional_value<mi::KeyboardSettings> mie::LibInputDevice::get_keyboard_settings() const
153+{
154+ if (!contains(info.capabilities, mi::DeviceCapability::keyboard))
155+ return {};
156+ mi::KeyboardSettings settings;
157+
158+ settings.repeat_enabled = repeater.enabled();
159+ settings.repeat_delay = repeater.delay();
160+ settings.repeat_interval = repeater.interval();
161+ return settings;
162+}
163+
164+void mie::LibInputDevice::apply_settings(mi::KeyboardSettings const& settings)
165+{
166+ if (!contains(info.capabilities, mi::DeviceCapability::keyboard))
167+ return;
168+
169+ if (settings.repeat_enabled)
170+ repeater.enable(settings.repeat_delay, settings.repeat_interval);
171+ else
172+ repeater.disable();
173+}
174+
175+void mie::LibInputDevice::handle_repeat(int32_t code)
176+{
177+ auto const event_time = clock->now().time_since_epoch();
178+ sink->handle_input(*builder->key_event(event_time, mir_keyboard_action_repeat, 0, code));
179+}
180
181=== modified file 'src/platforms/evdev/libinput_device.h'
182--- src/platforms/evdev/libinput_device.h 2016-03-23 06:39:56 +0000
183+++ src/platforms/evdev/libinput_device.h 2016-03-31 19:18:58 +0000
184@@ -21,6 +21,7 @@
185
186 #include "libinput_ptr.h"
187 #include "libinput_device_ptr.h"
188+#include "key_repeater.h"
189
190 #include "mir/input/event_builder.h"
191 #include "mir/input/input_device.h"
192@@ -38,18 +39,23 @@
193
194 namespace mir
195 {
196+namespace time
197+{
198+class Clock;
199+class AlarmFactory;
200+}
201 namespace input
202 {
203 class InputReport;
204 namespace evdev
205 {
206-struct PointerState;
207-struct KeyboardState;
208
209 class LibInputDevice : public input::InputDevice
210 {
211 public:
212- LibInputDevice(std::shared_ptr<InputReport> const& report, LibInputDevicePtr dev);
213+ LibInputDevice(std::shared_ptr<InputReport> const& report, LibInputDevicePtr dev,
214+ std::shared_ptr<mir::time::AlarmFactory> const& alarm_factory,
215+ std::shared_ptr<mir::time::Clock> const& clock);
216 ~LibInputDevice();
217 void start(InputSink* sink, EventBuilder* builder) override;
218 void stop() override;
219@@ -58,6 +64,8 @@
220 void apply_settings(PointerSettings const&) override;
221 optional_value<TouchpadSettings> get_touchpad_settings() const override;
222 void apply_settings(TouchpadSettings const&) override;
223+ optional_value<KeyboardSettings> get_keyboard_settings() const override;
224+ void apply_settings(KeyboardSettings const&) override;
225
226 void process_event(libinput_event* event);
227 ::libinput_device* device() const;
228@@ -75,10 +83,11 @@
229 void handle_touch_motion(libinput_event_touch* touch);
230 void update_device_info();
231
232- std::shared_ptr<InputReport> report;
233+ std::shared_ptr<InputReport> const report;
234+ KeyRepeater repeater;
235+ std::shared_ptr<mir::time::Clock> const clock;
236 std::shared_ptr<::libinput> lib;
237 std::vector<LibInputDevicePtr> devices;
238- std::shared_ptr<dispatch::Dispatchable> dispatchable_fd;
239
240 InputSink* sink{nullptr};
241 EventBuilder* builder{nullptr};
242@@ -98,6 +107,7 @@
243 std::map<MirTouchId,ContactData> last_seen_properties;
244
245 void update_contact_data(ContactData &data, MirTouchAction action, libinput_event_touch* touch);
246+ void handle_repeat(int32_t code);
247 };
248 }
249 }
250
251=== modified file 'src/platforms/evdev/platform.cpp'
252--- src/platforms/evdev/platform.cpp 2016-03-23 06:39:56 +0000
253+++ src/platforms/evdev/platform.cpp 2016-03-31 19:18:58 +0000
254@@ -22,7 +22,10 @@
255 #include "mir/udev/wrapper.h"
256 #include "mir/dispatch/dispatchable.h"
257 #include "mir/dispatch/readable_fd.h"
258+#include "mir/dispatch/alarm_factory.h"
259 #include "mir/dispatch/multiplexing_dispatchable.h"
260+#include "mir/time/steady_clock.h"
261+#include "mir/time/steady_timer_fd.h"
262 #include "mir/module_properties.h"
263 #include "mir/assert_module_entry_point.h"
264
265@@ -43,6 +46,7 @@
266
267 namespace mi = mir::input;
268 namespace md = mir::dispatch;
269+namespace mt = mir::time;
270 namespace mu = mir::udev;
271 namespace mie = mi::evdev;
272
273@@ -68,12 +72,14 @@
274 } // namespace
275
276 mie::Platform::Platform(std::shared_ptr<InputDeviceRegistry> const& registry,
277- std::shared_ptr<InputReport> const& report,
278- std::unique_ptr<udev::Context>&& udev_context) :
279- report(report),
280- udev_context(std::move(udev_context)),
281- input_device_registry(registry),
282- platform_dispatchable{std::make_shared<md::MultiplexingDispatchable>()}
283+ std::shared_ptr<InputReport> const& report,
284+ std::unique_ptr<udev::Context>&& udev_context)
285+ : report(report),
286+ udev_context(std::move(udev_context)),
287+ input_device_registry(registry),
288+ platform_dispatchable{std::make_shared<md::MultiplexingDispatchable>()},
289+ clock{std::make_shared<mt::SteadyClock>()},
290+ alarm_factory{std::make_shared<md::AlarmFactory>(clock, std::make_shared<mt::SteadyTimerFd>())}
291 {
292 }
293
294@@ -91,6 +97,7 @@
295 Fd{IntOwnedFd{libinput_get_fd(lib.get())}}, [this]{process_input_events();}
296 );
297 platform_dispatchable->add_watch(libinput_dispatchable);
298+ platform_dispatchable->add_watch(alarm_factory);
299 }
300
301 void mie::Platform::process_input_events()
302@@ -145,7 +152,7 @@
303
304 try
305 {
306- devices.emplace_back(std::make_shared<mie::LibInputDevice>(report, move(device_ptr)));
307+ devices.emplace_back(std::make_shared<mie::LibInputDevice>(report, move(device_ptr), alarm_factory, clock));
308
309 input_device_registry->add_device(devices.back());
310
311@@ -186,6 +193,7 @@
312
313 void mie::Platform::stop()
314 {
315+ platform_dispatchable->remove_watch(alarm_factory);
316 platform_dispatchable->remove_watch(libinput_dispatchable);
317 while (!devices.empty())
318 {
319
320=== modified file 'src/platforms/evdev/platform.h'
321--- src/platforms/evdev/platform.h 2016-03-23 06:39:56 +0000
322+++ src/platforms/evdev/platform.h 2016-03-31 19:18:58 +0000
323@@ -37,10 +37,15 @@
324 class Monitor;
325 class Context;
326 }
327+namespace time
328+{
329+class Clock;
330+}
331 namespace dispatch
332 {
333 class MultiplexingDispatchable;
334 class ReadableFd;
335+class AlarmFactory;
336 }
337 namespace input
338 {
339@@ -73,6 +78,8 @@
340 std::shared_ptr<udev::Context> const udev_context;
341 std::shared_ptr<InputDeviceRegistry> const input_device_registry;
342 std::shared_ptr<dispatch::MultiplexingDispatchable> const platform_dispatchable;
343+ std::shared_ptr<time::Clock> const clock;
344+ std::shared_ptr<dispatch::AlarmFactory> const alarm_factory;
345 std::shared_ptr<::libinput> lib;
346 std::shared_ptr<dispatch::ReadableFd> libinput_dispatchable;
347
348
349=== modified file 'src/platforms/mesa/server/x11/input/input_device.cpp'
350--- src/platforms/mesa/server/x11/input/input_device.cpp 2016-03-23 06:39:56 +0000
351+++ src/platforms/mesa/server/x11/input/input_device.cpp 2016-03-31 19:18:58 +0000
352@@ -20,13 +20,16 @@
353
354 #include "mir/input/pointer_settings.h"
355 #include "mir/input/touchpad_settings.h"
356+#include "mir/input/keyboard_settings.h"
357 #include "mir/input/input_device_info.h"
358 #include "mir/input/device_capability.h"
359 #include "mir/input/event_builder.h"
360 #include "mir/input/input_sink.h"
361
362+#include "mir/raii.h"
363+
364 #include <X11/Xlib.h>
365-#include <iostream>
366+#include <X11/XKBlib.h>
367
368 namespace mi = mir::input;
369 namespace geom = mir::geometry;
370@@ -69,8 +72,8 @@
371
372 }
373
374-mix::XInputDevice::XInputDevice(InputDeviceInfo const& device_info)
375- : info(device_info)
376+mix::XInputDevice::XInputDevice(InputDeviceInfo const& device_info, std::shared_ptr<::Display> const& display)
377+ : display{display}, info(device_info)
378 {
379 }
380
381@@ -210,3 +213,49 @@
382 )
383 );
384 }
385+
386+void mix::XInputDevice::apply_settings(mi::KeyboardSettings const& settings)
387+{
388+ if (!contains(info.capabilities, mi::DeviceCapability::keyboard))
389+ return;
390+
391+ XKeyboardControl repeat_enabled;
392+ repeat_enabled.auto_repeat_mode = settings.repeat_enabled;
393+ XChangeKeyboardControl(display.get(), KBAutoRepeatMode, &repeat_enabled);
394+
395+ int xkbopcode, xkbevent, xkberror;
396+ int xkbmajor = XkbMajorVersion, xkbminor = XkbMinorVersion;
397+ if (XkbQueryExtension(display.get(), &xkbopcode, &xkbevent, &xkberror, &xkbmajor, &xkbminor))
398+ {
399+ auto xkb = raii::deleter_for(XkbAllocKeyboard(), [](XkbDescPtr ptr){XkbFreeKeyboard(ptr, 0, True);});
400+
401+ XkbGetControls(display.get(), XkbRepeatKeysMask, xkb.get());
402+ xkb->ctrls->repeat_delay = settings.repeat_delay.count();
403+ xkb->ctrls->repeat_interval = settings.repeat_interval.count();
404+ XkbSetControls(display.get(), XkbRepeatKeysMask, xkb.get());
405+ }
406+}
407+
408+mir::optional_value<mi::KeyboardSettings> mix::XInputDevice::get_keyboard_settings() const
409+{
410+ if (!contains(info.capabilities, mi::DeviceCapability::keyboard))
411+ return {};
412+
413+ KeyboardSettings settings;
414+ XKeyboardState repeat_enabled;
415+ XGetKeyboardControl(display.get(), &repeat_enabled);
416+ settings.repeat_enabled = repeat_enabled.global_auto_repeat;
417+
418+ int xkbopcode, xkbevent, xkberror;
419+ int xkbmajor = XkbMajorVersion, xkbminor = XkbMinorVersion;
420+ if (XkbQueryExtension(display.get(), &xkbopcode, &xkbevent, &xkberror, &xkbmajor, &xkbminor))
421+ {
422+ auto xkb = raii::deleter_for(XkbAllocKeyboard(), [](XkbDescPtr ptr){ XkbFreeKeyboard(ptr, 0, True);});
423+
424+ XkbGetControls(display.get(), XkbRepeatKeysMask, xkb.get());
425+ settings.repeat_delay = std::chrono::milliseconds{xkb->ctrls->repeat_delay};
426+ settings.repeat_interval = std::chrono::milliseconds{xkb->ctrls->repeat_interval};
427+ }
428+
429+ return settings;
430+}
431
432=== modified file 'src/platforms/mesa/server/x11/input/input_device.h'
433--- src/platforms/mesa/server/x11/input/input_device.h 2016-03-23 06:39:56 +0000
434+++ src/platforms/mesa/server/x11/input/input_device.h 2016-03-31 19:18:58 +0000
435@@ -26,6 +26,7 @@
436 #include "mir/geometry/displacement.h"
437 #include "mir/optional_value.h"
438
439+#include <X11/Xlib.h>
440 #include <chrono>
441
442 namespace mir
443@@ -39,7 +40,7 @@
444 class XInputDevice : public input::InputDevice
445 {
446 public:
447- XInputDevice(InputDeviceInfo const& info);
448+ XInputDevice(InputDeviceInfo const& info, std::shared_ptr<::Display> const& display);
449
450 std::shared_ptr<dispatch::Dispatchable> dispatchable();
451 void start(InputSink* destination, EventBuilder* builder) override;
452@@ -50,6 +51,8 @@
453 void apply_settings(PointerSettings const& settings) override;
454 optional_value<TouchpadSettings> get_touchpad_settings() const override;
455 void apply_settings(TouchpadSettings const& settings) override;
456+ optional_value<KeyboardSettings> get_keyboard_settings() const override;
457+ void apply_settings(KeyboardSettings const& settings) override;
458
459 bool started() const;
460 void key_press(std::chrono::nanoseconds event_time, xkb_keysym_t key_sym, int32_t key_code);
461@@ -60,6 +63,7 @@
462 void pointer_motion(std::chrono::nanoseconds event_time, mir::geometry::Point const& pos, mir::geometry::Displacement scroll);
463
464 private:
465+ std::shared_ptr<::Display> const display;
466 MirPointerButtons button_state{0};
467 InputSink* sink{nullptr};
468 EventBuilder* builder{nullptr};
469
470=== modified file 'src/platforms/mesa/server/x11/input/input_platform.cpp'
471--- src/platforms/mesa/server/x11/input/input_platform.cpp 2016-03-23 06:39:56 +0000
472+++ src/platforms/mesa/server/x11/input/input_platform.cpp 2016-03-31 19:18:58 +0000
473@@ -46,18 +46,15 @@
474 namespace mix = mi::X;
475
476 mix::XInputPlatform::XInputPlatform(std::shared_ptr<mi::InputDeviceRegistry> const& input_device_registry,
477- std::shared_ptr<::Display> const& conn) :
478- x11_connection{conn},
479- xcon_dispatchable(std::make_shared<md::ReadableFd>(
480- mir::Fd{mir::IntOwnedFd{XConnectionNumber(conn.get())}}, [this]()
481- {
482- process_input_event();
483- })),
484- registry(input_device_registry),
485- core_keyboard(std::make_shared<mix::XInputDevice>(
486- mi::InputDeviceInfo{"x11-keyboard-device", "x11-key-dev-1", mi::DeviceCapability::keyboard})),
487- core_pointer(std::make_shared<mix::XInputDevice>(
488- mi::InputDeviceInfo{"x11-mouse-device", "x11-mouse-dev-1", mi::DeviceCapability::pointer}))
489+ std::shared_ptr<::Display> const& conn)
490+ : x11_connection{conn},
491+ xcon_dispatchable(std::make_shared<md::ReadableFd>(mir::Fd{mir::IntOwnedFd{XConnectionNumber(conn.get())}},
492+ [this]() { process_input_event(); })),
493+ registry(input_device_registry),
494+ core_keyboard(std::make_shared<mix::XInputDevice>(
495+ mi::InputDeviceInfo{"x11-keyboard-device", "x11-key-dev-1", mi::DeviceCapability::keyboard}, conn)),
496+ core_pointer(std::make_shared<mix::XInputDevice>(
497+ mi::InputDeviceInfo{"x11-mouse-device", "x11-mouse-dev-1", mi::DeviceCapability::pointer}, conn))
498 {
499 }
500
501
502=== modified file 'src/server/input/CMakeLists.txt'
503--- src/server/input/CMakeLists.txt 2016-03-23 06:39:56 +0000
504+++ src/server/input/CMakeLists.txt 2016-03-31 19:18:58 +0000
505@@ -16,7 +16,6 @@
506 event_filter_chain_dispatcher.cpp
507 input_modifier_utils.cpp
508 input_probe.cpp
509- key_repeat_dispatcher.cpp
510 null_input_channel_factory.cpp
511 null_input_dispatcher.cpp
512 seat_input_device_tracker.cpp
513
514=== modified file 'src/server/input/default_configuration.cpp'
515--- src/server/input/default_configuration.cpp 2016-03-23 06:39:56 +0000
516+++ src/server/input/default_configuration.cpp 2016-03-31 19:18:58 +0000
517@@ -20,7 +20,6 @@
518
519 #include "android/input_sender.h"
520 #include "android/input_channel_factory.h"
521-#include "key_repeat_dispatcher.h"
522 #include "display_input_region.h"
523 #include "event_filter_chain_dispatcher.h"
524 #include "cursor_controller.h"
525@@ -143,15 +142,7 @@
526 return input_dispatcher(
527 [this]()
528 {
529- std::chrono::milliseconds const key_repeat_timeout{500};
530- std::chrono::milliseconds const key_repeat_delay{50};
531-
532- auto const options = the_options();
533- auto enable_repeat = options->get<bool>(options::enable_key_repeat_opt);
534-
535- return std::make_shared<mi::KeyRepeatDispatcher>(
536- the_event_filter_chain_dispatcher(), the_main_loop(), the_cookie_authority(),
537- enable_repeat, key_repeat_timeout, key_repeat_delay);
538+ return the_event_filter_chain_dispatcher();
539 });
540 }
541
542@@ -297,18 +288,13 @@
543 return default_input_device_hub(
544 [this]()
545 {
546- auto input_dispatcher = the_input_dispatcher();
547- auto key_repeater = std::dynamic_pointer_cast<mi::KeyRepeatDispatcher>(input_dispatcher);
548- auto hub = std::make_shared<mi::DefaultInputDeviceHub>(
549+ return std::make_shared<mi::DefaultInputDeviceHub>(
550 the_global_event_sink(),
551 the_seat(),
552 the_input_reading_multiplexer(),
553 the_main_loop(),
554- the_cookie_authority());
555-
556- if (key_repeater)
557- key_repeater->set_input_device_hub(hub);
558- return hub;
559+ the_cookie_authority(),
560+ the_options()->get<bool>(options::enable_key_repeat_opt));
561 });
562 }
563
564@@ -324,18 +310,13 @@
565 return default_input_device_hub(
566 [this]()
567 {
568- auto input_dispatcher = the_input_dispatcher();
569- auto key_repeater = std::dynamic_pointer_cast<mi::KeyRepeatDispatcher>(input_dispatcher);
570- auto hub = std::make_shared<mi::DefaultInputDeviceHub>(
571+ return std::make_shared<mi::DefaultInputDeviceHub>(
572 the_global_event_sink(),
573 the_seat(),
574 the_input_reading_multiplexer(),
575 the_main_loop(),
576- the_cookie_authority());
577-
578- if (key_repeater)
579- key_repeater->set_input_device_hub(hub);
580- return hub;
581+ the_cookie_authority(),
582+ the_options()->get<bool>(options::enable_key_repeat_opt));
583 });
584 }
585 }
586
587=== modified file 'src/server/input/default_input_device_hub.cpp'
588--- src/server/input/default_input_device_hub.cpp 2016-03-23 06:39:56 +0000
589+++ src/server/input/default_input_device_hub.cpp 2016-03-31 19:18:58 +0000
590@@ -21,6 +21,7 @@
591
592 #include "mir/input/input_device.h"
593 #include "mir/input/input_device_observer.h"
594+#include "mir/input/keyboard_settings.h"
595 #include "mir/geometry/point.h"
596 #include "mir/dispatch/multiplexing_dispatchable.h"
597 #include "mir/dispatch/action_queue.h"
598@@ -43,14 +44,16 @@
599 std::shared_ptr<mi::Seat> const& seat,
600 std::shared_ptr<dispatch::MultiplexingDispatchable> const& input_multiplexer,
601 std::shared_ptr<mir::ServerActionQueue> const& observer_queue,
602- std::shared_ptr<mir::cookie::Authority> const& cookie_authority)
603+ std::shared_ptr<mir::cookie::Authority> const& cookie_authority,
604+ bool repeat_enabled)
605 : seat{seat},
606 sink{sink},
607 input_dispatchable{input_multiplexer},
608 observer_queue(observer_queue),
609 device_queue(std::make_shared<dispatch::ActionQueue>()),
610 cookie_authority(cookie_authority),
611- device_id_generator{0}
612+ device_id_generator{0},
613+ repeat_enabled{repeat_enabled}
614 {
615 input_dispatchable->add_watch(device_queue);
616 }
617@@ -60,6 +63,13 @@
618 if (!device)
619 BOOST_THROW_EXCEPTION(std::invalid_argument("Invalid input device"));
620
621+ if (contains(device->get_device_info().capabilities, mir::input::DeviceCapability::keyboard))
622+ {
623+ auto key_settings = device->get_keyboard_settings().value();
624+ key_settings.repeat_enabled = repeat_enabled;
625+ device->apply_settings(key_settings);
626+ }
627+
628 auto it = find_if(devices.cbegin(),
629 devices.cend(),
630 [&device](std::unique_ptr<RegisteredDevice> const& item)
631
632=== modified file 'src/server/input/default_input_device_hub.h'
633--- src/server/input/default_input_device_hub.h 2016-03-23 06:39:56 +0000
634+++ src/server/input/default_input_device_hub.h 2016-03-31 19:18:58 +0000
635@@ -65,7 +65,8 @@
636 std::shared_ptr<Seat> const& seat,
637 std::shared_ptr<dispatch::MultiplexingDispatchable> const& input_multiplexer,
638 std::shared_ptr<ServerActionQueue> const& observer_queue,
639- std::shared_ptr<cookie::Authority> const& cookie_authority);
640+ std::shared_ptr<cookie::Authority> const& cookie_authority,
641+ bool repeat_enabled = true);
642
643 // InputDeviceRegistry - calls from mi::Platform
644 void add_device(std::shared_ptr<InputDevice> const& device) override;
645@@ -117,6 +118,7 @@
646 std::vector<std::shared_ptr<InputDeviceObserver>> observers;
647
648 MirInputDeviceId device_id_generator;
649+ bool repeat_enabled;
650 };
651
652 }
653
654=== removed file 'src/server/input/key_repeat_dispatcher.cpp'
655--- src/server/input/key_repeat_dispatcher.cpp 2016-03-29 23:37:32 +0000
656+++ src/server/input/key_repeat_dispatcher.cpp 1970-01-01 00:00:00 +0000
657@@ -1,186 +0,0 @@
658-/*
659- * Copyright © 2012 Canonical Ltd.
660- *
661- * This program is free software: you can redistribute it and/or modify it
662- * under the terms of the GNU General Public License version 3,
663- * as published by the Free Software Foundation.
664- *
665- * This program is distributed in the hope that it will be useful,
666- * but WITHOUT ANY WARRANTY; without even the implied warranty of
667- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
668- * GNU General Public License for more details.
669- *
670- * You should have received a copy of the GNU General Public License
671- * along with this program. If not, see <http://www.gnu.org/licenses/>.
672- *
673- * Authored by: Robert Carr <robert.carr@canonical.com>
674- */
675-
676-#include "key_repeat_dispatcher.h"
677-
678-#include "mir/input/device.h"
679-#include "mir/input/input_device_hub.h"
680-#include "mir/time/alarm_factory.h"
681-#include "mir/time/alarm.h"
682-#include "mir/events/event_private.h"
683-#include "mir/cookie/authority.h"
684-
685-#include <boost/throw_exception.hpp>
686-
687-#include <algorithm>
688-#include <stdexcept>
689-#include <string.h>
690-
691-namespace mi = mir::input;
692-
693-namespace
694-{
695-struct DeviceRemovalFilter : mi::InputDeviceObserver
696-{
697- DeviceRemovalFilter(std::function<void(MirInputDeviceId)> const& on_removal)
698- : on_removal(on_removal) {}
699-
700- void device_added(std::shared_ptr<mi::Device> const&) override
701- {
702- }
703-
704- void device_changed(std::shared_ptr<mi::Device> const&) override
705- {
706- }
707-
708- void device_removed(std::shared_ptr<mi::Device> const& device) override
709- {
710- on_removal(device->id());
711- }
712-
713- void changes_complete() override
714- {
715- }
716- std::function<void(MirInputDeviceId)> on_removal;
717-};
718-
719-}
720-
721-mi::KeyRepeatDispatcher::KeyRepeatDispatcher(
722- std::shared_ptr<mi::InputDispatcher> const& next_dispatcher,
723- std::shared_ptr<mir::time::AlarmFactory> const& factory,
724- std::shared_ptr<mir::cookie::Authority> const& cookie_authority,
725- bool repeat_enabled,
726- std::chrono::milliseconds repeat_timeout,
727- std::chrono::milliseconds repeat_delay)
728- : next_dispatcher(next_dispatcher),
729- alarm_factory(factory),
730- cookie_authority(cookie_authority),
731- repeat_enabled(repeat_enabled),
732- repeat_timeout(repeat_timeout),
733- repeat_delay(repeat_delay)
734-{
735-}
736-
737-void mi::KeyRepeatDispatcher::set_input_device_hub(std::shared_ptr<InputDeviceHub> const& hub)
738-{
739- hub->add_observer(std::make_shared<DeviceRemovalFilter>(
740- [this](MirInputDeviceId id)
741- {
742- std::unique_lock<std::mutex> lock(repeat_state_mutex);
743- repeat_state_by_device.erase(id); // destructor cancels alarms
744- }
745- ));
746-}
747-
748-mi::KeyRepeatDispatcher::KeyboardState& mi::KeyRepeatDispatcher::ensure_state_for_device_locked(std::lock_guard<std::mutex> const&, MirInputDeviceId id)
749-{
750- repeat_state_by_device.insert(std::make_pair(id, KeyboardState()));
751- return repeat_state_by_device[id];
752-}
753-
754-bool mi::KeyRepeatDispatcher::dispatch(MirEvent const& event)
755-{
756- if (!repeat_enabled) // if we made this mutable we'd need a guard
757- {
758- return next_dispatcher->dispatch(event);
759- }
760-
761- if (mir_event_get_type(&event) == mir_event_type_input)
762- {
763- auto iev = mir_event_get_input_event(&event);
764- if (mir_input_event_get_type(iev) != mir_input_event_type_key)
765- return next_dispatcher->dispatch(event);
766- if (!handle_key_input(mir_input_event_get_device_id(iev), mir_input_event_get_keyboard_event(iev)))
767- return next_dispatcher->dispatch(event);
768- else
769- return true;
770- }
771- return next_dispatcher->dispatch(event);
772-}
773-
774-// Returns true if the original event has been handled, that is ::dispatch should not pass it on.
775-bool mi::KeyRepeatDispatcher::handle_key_input(MirInputDeviceId id, MirKeyboardEvent const* kev)
776-{
777- std::lock_guard<std::mutex> lg(repeat_state_mutex);
778- auto& device_state = ensure_state_for_device_locked(lg, id);
779-
780- auto scan_code = mir_keyboard_event_scan_code(kev);
781-
782- switch (mir_keyboard_event_action(kev))
783- {
784- case mir_keyboard_action_up:
785- {
786- auto it = device_state.repeat_alarms_by_scancode.find(scan_code);
787- if (it == device_state.repeat_alarms_by_scancode.end())
788- {
789- return false;
790- }
791- device_state.repeat_alarms_by_scancode.erase(it);
792- break;
793- }
794- case mir_keyboard_action_down:
795- {
796- MirKeyboardEvent new_kev = *kev;
797- new_kev.action = mir_keyboard_action_repeat;
798-
799- auto it = device_state.repeat_alarms_by_scancode.find(scan_code);
800- if (it != device_state.repeat_alarms_by_scancode.end())
801- {
802- // When we receive a duplicated down we just replace the action
803- next_dispatcher->dispatch(new_kev);
804- return true;
805- }
806- auto& capture_alarm = device_state.repeat_alarms_by_scancode[scan_code];
807- std::shared_ptr<mir::time::Alarm> alarm = alarm_factory->create_alarm([this, &capture_alarm, new_kev]() mutable
808- {
809- std::lock_guard<std::mutex> lg(repeat_state_mutex);
810-
811- new_kev.event_time = std::chrono::steady_clock::now().time_since_epoch();
812- auto const cookie = cookie_authority->make_cookie(new_kev.event_time.count());
813- auto const serialized_cookie = cookie->serialize();
814- std::copy_n(std::begin(serialized_cookie), new_kev.cookie.size(), std::begin(new_kev.cookie));
815- next_dispatcher->dispatch(new_kev);
816-
817- capture_alarm->reschedule_in(repeat_delay);
818- });
819- alarm->reschedule_in(repeat_timeout);
820- device_state.repeat_alarms_by_scancode[scan_code] = {alarm};
821- }
822- case mir_keyboard_action_repeat:
823- // Should we consume existing repeats?
824- break;
825- default:
826- BOOST_THROW_EXCEPTION(std::logic_error("Unexpected key event action"));
827- }
828- return false;
829-}
830-
831-void mi::KeyRepeatDispatcher::start()
832-{
833- next_dispatcher->start();
834-}
835-
836-void mi::KeyRepeatDispatcher::stop()
837-{
838- std::lock_guard<std::mutex> lg(repeat_state_mutex);
839-
840- repeat_state_by_device.clear();
841-
842- next_dispatcher->stop();
843-}
844
845=== removed file 'src/server/input/key_repeat_dispatcher.h'
846--- src/server/input/key_repeat_dispatcher.h 2016-03-23 06:39:56 +0000
847+++ src/server/input/key_repeat_dispatcher.h 1970-01-01 00:00:00 +0000
848@@ -1,84 +0,0 @@
849-/*
850- * Copyright © 2015-2016 Canonical Ltd.
851- *
852- * This program is free software: you can redistribute it and/or modify it
853- * under the terms of the GNU General Public License version 3,
854- * as published by the Free Software Foundation.
855- *
856- * This program is distributed in the hope that it will be useful,
857- * but WITHOUT ANY WARRANTY; without even the implied warranty of
858- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
859- * GNU General Public License for more details.
860- *
861- * You should have received a copy of the GNU General Public License
862- * along with this program. If not, see <http://www.gnu.org/licenses/>.
863- *
864- * Authored by: Robert Carr <robert.carr@canonical.com>
865- */
866-
867-#ifndef MIR_INPUT_KEY_REPEAT_DISPATCHER_H_
868-#define MIR_INPUT_KEY_REPEAT_DISPATCHER_H_
869-
870-#include "mir/input/input_dispatcher.h"
871-#include "mir/input/input_device_observer.h"
872-
873-#include <memory>
874-#include <chrono>
875-#include <mutex>
876-#include <unordered_map>
877-
878-namespace mir
879-{
880-namespace cookie
881-{
882-class Authority;
883-}
884-namespace time
885-{
886-class AlarmFactory;
887-class Alarm;
888-}
889-namespace input
890-{
891-class InputDeviceHub;
892-class KeyRepeatDispatcher : public InputDispatcher
893-{
894-public:
895- KeyRepeatDispatcher(std::shared_ptr<InputDispatcher> const& next_dispatcher,
896- std::shared_ptr<time::AlarmFactory> const& factory,
897- std::shared_ptr<cookie::Authority> const& cookie_authority,
898- bool repeat_enabled,
899- std::chrono::milliseconds repeat_timeout, /* timeout before sending first repeat */
900- std::chrono::milliseconds repeat_delay /* delay between repeated keys */);
901-
902- // InputDispatcher
903- bool dispatch(MirEvent const& event) override;
904- void start() override;
905- void stop() override;
906-
907- void set_input_device_hub(std::shared_ptr<InputDeviceHub> const& hub);
908-
909-private:
910- std::mutex repeat_state_mutex;
911-
912- std::shared_ptr<InputDispatcher> const next_dispatcher;
913- std::shared_ptr<time::AlarmFactory> const alarm_factory;
914- std::shared_ptr<cookie::Authority> const cookie_authority;
915- bool const repeat_enabled;
916- std::chrono::milliseconds repeat_timeout;
917- std::chrono::milliseconds repeat_delay;
918-
919- struct KeyboardState
920- {
921- std::unordered_map<int, std::shared_ptr<mir::time::Alarm>> repeat_alarms_by_scancode;
922- };
923- std::unordered_map<MirInputDeviceId, KeyboardState> repeat_state_by_device;
924- KeyboardState& ensure_state_for_device_locked(std::lock_guard<std::mutex> const&, MirInputDeviceId id);
925-
926- bool handle_key_input(MirInputDeviceId id, MirKeyboardEvent const* ev);
927-};
928-
929-}
930-}
931-
932-#endif // MIR_INPUT_KEY_REPEAT_DISPATCHER_H_
933
934=== modified file 'tests/include/mir/test/doubles/mock_input_device.h'
935--- tests/include/mir/test/doubles/mock_input_device.h 2015-12-18 21:07:54 +0000
936+++ tests/include/mir/test/doubles/mock_input_device.h 2016-03-31 19:18:58 +0000
937@@ -23,6 +23,7 @@
938 #include "mir/input/input_device_info.h" // needed for fake device setup
939 #include "mir/input/pointer_settings.h"
940 #include "mir/input/touchpad_settings.h"
941+#include "mir/input/keyboard_settings.h"
942
943 #include <gmock/gmock.h>
944
945@@ -44,6 +45,8 @@
946 MOCK_METHOD1(apply_settings, void(input::PointerSettings const&));
947 MOCK_CONST_METHOD0(get_touchpad_settings, mir::optional_value<input::TouchpadSettings>());
948 MOCK_METHOD1(apply_settings, void(input::TouchpadSettings const&));
949+ MOCK_CONST_METHOD0(get_keyboard_settings, mir::optional_value<input::KeyboardSettings>());
950+ MOCK_METHOD1(apply_settings, void(input::KeyboardSettings const&));
951 };
952 }
953 }
954
955=== modified file 'tests/include/mir/test/doubles/mock_x11.h'
956--- tests/include/mir/test/doubles/mock_x11.h 2016-03-23 06:39:56 +0000
957+++ tests/include/mir/test/doubles/mock_x11.h 2016-03-31 19:18:58 +0000
958@@ -22,6 +22,7 @@
959 #include <gmock/gmock.h>
960
961 #include <X11/Xlib.h>
962+#include <X11/XKBlib.h>
963 #include <X11/Xutil.h>
964
965 namespace mir
966@@ -81,6 +82,13 @@
967 MOCK_METHOD1(XSetErrorHandler, XErrorHandler(XErrorHandler));
968 MOCK_METHOD0(XInitThreads, Status());
969 MOCK_METHOD3(XSetWMHints, int(Display*, Window, XWMHints*));
970+ MOCK_METHOD3(XChangeKeyboardControl, int(Display*, unsigned long, XKeyboardControl*));
971+ MOCK_METHOD2(XGetKeyboardControl, int(Display*, XKeyboardState*));
972+ MOCK_METHOD6(XkbQueryExtension, Bool(Display*, int*, int*, int*, int*, int*));
973+ MOCK_METHOD3(XkbFreeKeyboard, void(XkbDescPtr, unsigned int, Bool));
974+ MOCK_METHOD0(XkbAllocKeyboard, XkbDescPtr());
975+ MOCK_METHOD3(XkbGetControls, Status(Display*, unsigned long, XkbDescPtr));
976+ MOCK_METHOD3(XkbSetControls, Bool(Display*, unsigned long, XkbDescPtr));
977
978 FakeX11Resources fake_x11;
979 };
980
981=== modified file 'tests/include/mir_test_framework/stub_input_platform.h'
982--- tests/include/mir_test_framework/stub_input_platform.h 2016-01-29 08:18:22 +0000
983+++ tests/include/mir_test_framework/stub_input_platform.h 2016-03-31 19:18:58 +0000
984@@ -26,8 +26,16 @@
985
986 namespace mir
987 {
988+namespace time
989+{
990+class AlarmFactory;
991+class Clock;
992+class SteadyTimerFd;
993+class SteadyClock;
994+}
995 namespace dispatch
996 {
997+class AlarmFactory;
998 class ActionQueue;
999 class MultiplexingDispatchable;
1000 }
1001@@ -53,6 +61,8 @@
1002 static void remove(std::shared_ptr<mir::input::InputDevice> const& dev);
1003 static void register_dispatchable(std::shared_ptr<mir::dispatch::Dispatchable> const& queue);
1004 static void unregister_dispatchable(std::shared_ptr<mir::dispatch::Dispatchable> const& queue);
1005+ static std::shared_ptr<mir::time::AlarmFactory> get_alarm_factory();
1006+ static std::shared_ptr<mir::time::Clock> get_clock();
1007
1008 private:
1009 std::shared_ptr<mir::dispatch::MultiplexingDispatchable> const platform_dispatchable;
1010@@ -61,6 +71,11 @@
1011 static std::atomic<StubInputPlatform*> stub_input_platform;
1012 static std::vector<std::weak_ptr<mir::input::InputDevice>> device_store;
1013 static std::mutex device_store_guard;
1014+ static std::shared_ptr<mir::time::SteadyClock> steady_clock;
1015+ static std::shared_ptr<mir::time::SteadyTimerFd> steady_timer_fd;
1016+ static std::shared_ptr<mir::dispatch::AlarmFactory> alarm_factory;
1017+
1018+
1019 };
1020
1021 }
1022
1023=== modified file 'tests/mir_test_doubles/mock_input_device.cpp'
1024--- tests/mir_test_doubles/mock_input_device.cpp 2015-12-18 21:07:54 +0000
1025+++ tests/mir_test_doubles/mock_input_device.cpp 2016-03-31 19:18:58 +0000
1026@@ -40,4 +40,12 @@
1027 else
1028 ON_CALL(*this, get_touchpad_settings())
1029 .WillByDefault(Return(mir::optional_value<input::TouchpadSettings>()));
1030+
1031+ if (contains(caps, input::DeviceCapability::keyboard))
1032+ ON_CALL(*this, get_keyboard_settings())
1033+ .WillByDefault(Return(input::KeyboardSettings()));
1034+ else
1035+ ON_CALL(*this, get_keyboard_settings())
1036+ .WillByDefault(Return(mir::optional_value<input::KeyboardSettings>()));
1037+
1038 }
1039
1040=== modified file 'tests/mir_test_doubles/mock_x11.cpp'
1041--- tests/mir_test_doubles/mock_x11.cpp 2016-03-23 06:39:56 +0000
1042+++ tests/mir_test_doubles/mock_x11.cpp 2016-03-31 19:18:58 +0000
1043@@ -195,3 +195,38 @@
1044 {
1045 return global_mock->XPending(display);
1046 }
1047+
1048+int XChangeKeyboardControl(Display* display, unsigned long value_mask, XKeyboardControl* control)
1049+{
1050+ return global_mock->XChangeKeyboardControl(display, value_mask, control);
1051+}
1052+
1053+int XGetKeyboardControl(Display* display, XKeyboardState* control)
1054+{
1055+ return global_mock->XGetKeyboardControl(display, control);
1056+}
1057+
1058+Bool XkbQueryExtension(Display* dpy, int* opcode, int* event_base, int* error_base, int* major, int* minor)
1059+{
1060+ return global_mock->XkbQueryExtension(dpy, opcode, event_base, error_base, major, minor);
1061+}
1062+
1063+void XkbFreeKeyboard(XkbDescPtr xkb, unsigned int which, Bool free_desc)
1064+{
1065+ global_mock->XkbFreeKeyboard(xkb, which, free_desc);
1066+}
1067+
1068+XkbDescPtr XkbAllocKeyboard()
1069+{
1070+ return global_mock->XkbAllocKeyboard();
1071+}
1072+
1073+Status XkbGetControls(Display* dpy, unsigned long which, XkbDescPtr desc)
1074+{
1075+ return global_mock->XkbGetControls(dpy, which, desc);
1076+}
1077+
1078+Bool XkbSetControls(Display* dpy, unsigned long which, XkbDescPtr desc)
1079+{
1080+ return global_mock->XkbSetControls(dpy, which, desc);
1081+}
1082
1083=== modified file 'tests/mir_test_framework/fake_input_device_impl.cpp'
1084--- tests/mir_test_framework/fake_input_device_impl.cpp 2016-03-23 06:39:56 +0000
1085+++ tests/mir_test_framework/fake_input_device_impl.cpp 2016-03-31 19:18:58 +0000
1086@@ -24,7 +24,9 @@
1087 #include "mir/input/input_sink.h"
1088 #include "mir/input/pointer_settings.h"
1089 #include "mir/input/touchpad_settings.h"
1090+#include "mir/input/keyboard_settings.h"
1091 #include "mir/input/event_builder.h"
1092+#include "mir/time/clock.h"
1093 #include "mir/dispatch/action_queue.h"
1094 #include "mir/geometry/displacement.h"
1095 #include "src/platforms/evdev/button_utils.h"
1096@@ -41,7 +43,9 @@
1097 namespace mtf = mir_test_framework;
1098
1099 mtf::FakeInputDeviceImpl::FakeInputDeviceImpl(mi::InputDeviceInfo const& info)
1100- : queue{std::make_shared<md::ActionQueue>()}, device{std::make_shared<InputDevice>(info, queue)}
1101+ : queue{std::make_shared<md::ActionQueue>()},
1102+ device{std::make_shared<InputDevice>(
1103+ info, queue, mtf::StubInputPlatform::get_clock(), mtf::StubInputPlatform::get_alarm_factory())}
1104 {
1105 mtf::StubInputPlatform::add(device);
1106 }
1107@@ -92,24 +96,31 @@
1108 }
1109
1110 mtf::FakeInputDeviceImpl::InputDevice::InputDevice(mi::InputDeviceInfo const& info,
1111- std::shared_ptr<mir::dispatch::Dispatchable> const& dispatchable)
1112- : info(info), queue{dispatchable}, buttons{0}
1113+ std::shared_ptr<mir::dispatch::Dispatchable> const& dispatchable,
1114+ std::shared_ptr<mir::time::Clock> const& clock,
1115+ std::shared_ptr<mir::time::AlarmFactory> const& alarm_factory)
1116+ : info(info), queue{dispatchable}, clock{clock}, buttons{0}, repeater{alarm_factory, [this](auto key_code) {
1117+ this->handle_repeat(key_code);}}
1118 {
1119- // the default setup results in a direct mapping of input velocity to output velocity.
1120- settings.acceleration = mir_pointer_acceleration_none;
1121- settings.cursor_acceleration_bias = 0.0;
1122+ // the default setup results in a direct mapping of input velocity to output velocity.
1123+ settings.acceleration = mir_pointer_acceleration_none;
1124+ settings.cursor_acceleration_bias = 0.0;
1125 }
1126
1127 void mtf::FakeInputDeviceImpl::InputDevice::synthesize_events(synthesis::KeyParameters const& key_params)
1128 {
1129 xkb_keysym_t key_code = 0;
1130
1131- auto event_time = std::chrono::duration_cast<std::chrono::nanoseconds>(
1132- std::chrono::steady_clock::now().time_since_epoch());
1133+ auto const event_time = clock->now().time_since_epoch();
1134
1135 auto input_action =
1136 (key_params.action == synthesis::EventAction::Down) ? mir_keyboard_action_down : mir_keyboard_action_up;
1137
1138+ if (input_action == mir_keyboard_action_down)
1139+ repeater.press(key_params.scancode);
1140+ else
1141+ repeater.release(key_params.scancode);
1142+
1143 auto key_event = builder->key_event(event_time, input_action, key_code, key_params.scancode);
1144
1145 if (!sink)
1146@@ -119,8 +130,7 @@
1147
1148 void mtf::FakeInputDeviceImpl::InputDevice::synthesize_events(synthesis::ButtonParameters const& button)
1149 {
1150- auto event_time = std::chrono::duration_cast<std::chrono::nanoseconds>(
1151- std::chrono::steady_clock::now().time_since_epoch());
1152+ auto const event_time = clock->now().time_since_epoch();
1153 auto action = update_buttons(button.action, mie::to_pointer_button(button.button, settings.handedness));
1154 auto button_event = builder->pointer_event(event_time,
1155 action,
1156@@ -154,8 +164,7 @@
1157 if (!sink)
1158 BOOST_THROW_EXCEPTION(std::runtime_error("Device is not started."));
1159
1160- auto event_time = std::chrono::duration_cast<std::chrono::nanoseconds>(
1161- std::chrono::steady_clock::now().time_since_epoch());
1162+ auto const event_time = clock->now().time_since_epoch();
1163 // constant scaling is used here to simplify checking for the
1164 // expected results. Default settings of the device lead to no
1165 // scaling at all.
1166@@ -179,8 +188,7 @@
1167 if (!sink)
1168 BOOST_THROW_EXCEPTION(std::runtime_error("Device is not started."));
1169
1170- auto event_time = std::chrono::duration_cast<std::chrono::nanoseconds>(
1171- std::chrono::steady_clock::now().time_since_epoch());
1172+ auto const event_time = clock->now().time_since_epoch();
1173
1174 auto touch_event = builder->touch_event(event_time);
1175
1176@@ -247,6 +255,29 @@
1177 // forwards already interpreted events.
1178 }
1179
1180+mir::optional_value<mi::KeyboardSettings> mtf::FakeInputDeviceImpl::InputDevice::get_keyboard_settings() const
1181+{
1182+ if (!contains(info.capabilities, mi::DeviceCapability::keyboard))
1183+ return {};
1184+ mi::KeyboardSettings settings;
1185+
1186+ settings.repeat_enabled = repeater.enabled();
1187+ settings.repeat_delay = repeater.delay();
1188+ settings.repeat_interval = repeater.interval();
1189+ return settings;
1190+}
1191+
1192+void mtf::FakeInputDeviceImpl::InputDevice::apply_settings(mi::KeyboardSettings const& settings)
1193+{
1194+ if (!contains(info.capabilities, mi::DeviceCapability::keyboard))
1195+ return;
1196+
1197+ if (settings.repeat_enabled)
1198+ repeater.enable(settings.repeat_delay, settings.repeat_interval);
1199+ else
1200+ repeater.disable();
1201+}
1202+
1203 void mtf::FakeInputDeviceImpl::InputDevice::map_touch_coordinates(float& x, float& y)
1204 {
1205 // TODO take orientation of input sink into account?
1206@@ -258,6 +289,15 @@
1207 y = (y - float(FakeInputDevice::minimum_touch_axis_value))*y_scale + area.top_left.y.as_float();
1208 }
1209
1210+void mtf::FakeInputDeviceImpl::InputDevice::handle_repeat(int32_t code)
1211+{
1212+ if (!sink)
1213+ BOOST_THROW_EXCEPTION(std::runtime_error("Device is not started."));
1214+ auto const event_time = clock->now().time_since_epoch();
1215+ auto key_event = builder->key_event(event_time, mir_keyboard_action_repeat, 0, code);
1216+ sink->handle_input(*key_event);
1217+}
1218+
1219 void mtf::FakeInputDeviceImpl::InputDevice::start(mi::InputSink* destination, mi::EventBuilder* event_builder)
1220 {
1221 sink = destination;
1222
1223=== modified file 'tests/mir_test_framework/fake_input_device_impl.h'
1224--- tests/mir_test_framework/fake_input_device_impl.h 2016-03-23 06:39:56 +0000
1225+++ tests/mir_test_framework/fake_input_device_impl.h 2016-03-31 19:18:58 +0000
1226@@ -20,6 +20,7 @@
1227 #define MIR_TEST_FRAMEWORK_FAKE_INPUT_DEVICE_IMPL_H_
1228
1229 #include "mir_test_framework/fake_input_device.h"
1230+#include "src/platforms/evdev/key_repeater.h"
1231
1232 #include "mir/input/input_device.h"
1233 #include "mir/input/pointer_settings.h"
1234@@ -28,6 +29,11 @@
1235
1236 namespace mir
1237 {
1238+namespace time
1239+{
1240+class Clock;
1241+class AlarmFactory;
1242+}
1243 namespace dispatch
1244 {
1245 class ActionQueue;
1246@@ -52,7 +58,10 @@
1247 {
1248 public:
1249 InputDevice(mir::input::InputDeviceInfo const& info,
1250- std::shared_ptr<mir::dispatch::Dispatchable> const& dispatchable);
1251+ std::shared_ptr<mir::dispatch::Dispatchable> const& dispatchable,
1252+ std::shared_ptr<mir::time::Clock> const& clock,
1253+ std::shared_ptr<mir::time::AlarmFactory> const& alarm_factory
1254+ );
1255
1256 void start(mir::input::InputSink* destination, mir::input::EventBuilder* builder) override;
1257 void stop() override;
1258@@ -70,19 +79,24 @@
1259 void apply_settings(mir::input::PointerSettings const& settings) override;
1260 mir::optional_value<mir::input::TouchpadSettings> get_touchpad_settings() const override;
1261 void apply_settings(mir::input::TouchpadSettings const& settings) override;
1262+ mir::optional_value<mir::input::KeyboardSettings> get_keyboard_settings() const override;
1263+ void apply_settings(mir::input::KeyboardSettings const& settings) override;
1264
1265 private:
1266 MirPointerAction update_buttons(synthesis::EventAction action, MirPointerButton button);
1267 void update_position(int rel_x, int rel_y);
1268 void map_touch_coordinates(float& x, float& y);
1269+ void handle_repeat(int32_t code);
1270
1271 mir::input::InputSink* sink{nullptr};
1272 mir::input::EventBuilder* builder{nullptr};
1273 mir::input::InputDeviceInfo info;
1274 std::shared_ptr<mir::dispatch::Dispatchable> const queue;
1275+ std::shared_ptr<mir::time::Clock> const clock;
1276 mir::geometry::Point pos, scroll;
1277 MirPointerButtons buttons;
1278 mir::input::PointerSettings settings;
1279+ mir::input::evdev::KeyRepeater repeater;
1280 };
1281 std::shared_ptr<mir::dispatch::ActionQueue> queue;
1282 std::shared_ptr<InputDevice> device;
1283
1284=== modified file 'tests/mir_test_framework/stub_input_platform.cpp'
1285--- tests/mir_test_framework/stub_input_platform.cpp 2016-03-23 06:39:56 +0000
1286+++ tests/mir_test_framework/stub_input_platform.cpp 2016-03-31 19:18:58 +0000
1287@@ -19,7 +19,10 @@
1288 #include "mir_test_framework/stub_input_platform.h"
1289
1290 #include "mir/input/input_device_registry.h"
1291+#include "mir/time/steady_timer_fd.h"
1292+#include "mir/time/steady_clock.h"
1293 #include "mir/dispatch/action_queue.h"
1294+#include "mir/dispatch/alarm_factory.h"
1295 #include "mir/dispatch/multiplexing_dispatchable.h"
1296 #include "mir/module_deleter.h"
1297
1298@@ -27,6 +30,8 @@
1299
1300 namespace mtf = mir_test_framework;
1301 namespace mi = mir::input;
1302+namespace md = mir::dispatch;
1303+namespace mt = mir::time;
1304
1305 mtf::StubInputPlatform::StubInputPlatform(
1306 std::shared_ptr<mi::InputDeviceRegistry> const& input_device_registry)
1307@@ -36,10 +41,12 @@
1308 {
1309 stub_input_platform = this;
1310 platform_dispatchable->add_watch(platform_queue);
1311+ platform_dispatchable->add_watch(alarm_factory);
1312 }
1313
1314 mtf::StubInputPlatform::~StubInputPlatform()
1315 {
1316+ platform_dispatchable->remove_watch(alarm_factory);
1317 std::lock_guard<decltype(device_store_guard)> lk{device_store_guard};
1318 device_store.clear();
1319 stub_input_platform = nullptr;
1320@@ -128,6 +135,20 @@
1321 input_platform->platform_dispatchable->remove_watch(queue);
1322 }
1323
1324+std::shared_ptr<mt::AlarmFactory> mtf::StubInputPlatform::get_alarm_factory()
1325+{
1326+ return alarm_factory;
1327+}
1328+
1329+std::shared_ptr<mt::Clock> mtf::StubInputPlatform::get_clock()
1330+{
1331+ return steady_clock;
1332+}
1333+
1334+std::shared_ptr<mt::SteadyClock> mtf::StubInputPlatform::steady_clock{std::make_shared<mt::SteadyClock>()};
1335+std::shared_ptr<mt::SteadyTimerFd> mtf::StubInputPlatform::steady_timer_fd{std::make_shared<mt::SteadyTimerFd>()};
1336+std::shared_ptr<md::AlarmFactory> mtf::StubInputPlatform::alarm_factory{
1337+ std::make_shared<md::AlarmFactory>(mtf::StubInputPlatform::steady_clock, mtf::StubInputPlatform::steady_timer_fd)};
1338 std::atomic<mtf::StubInputPlatform*> mtf::StubInputPlatform::stub_input_platform{nullptr};
1339 std::vector<std::weak_ptr<mir::input::InputDevice>> mtf::StubInputPlatform::device_store;
1340 std::mutex mtf::StubInputPlatform::device_store_guard;
1341
1342=== modified file 'tests/unit-tests/input/CMakeLists.txt'
1343--- tests/unit-tests/input/CMakeLists.txt 2016-03-31 19:18:58 +0000
1344+++ tests/unit-tests/input/CMakeLists.txt 2016-03-31 19:18:58 +0000
1345@@ -14,7 +14,6 @@
1346 ${CMAKE_CURRENT_SOURCE_DIR}/test_default_input_manager.cpp
1347 ${CMAKE_CURRENT_SOURCE_DIR}/test_surface_input_dispatcher.cpp
1348 ${CMAKE_CURRENT_SOURCE_DIR}/test_seat_input_device_tracker.cpp
1349- ${CMAKE_CURRENT_SOURCE_DIR}/test_key_repeat_dispatcher.cpp
1350 ${CMAKE_CURRENT_SOURCE_DIR}/test_key_repeater.cpp
1351 ${CMAKE_CURRENT_SOURCE_DIR}/test_validator.cpp
1352 )
1353
1354=== modified file 'tests/unit-tests/input/evdev/test_evdev_device_detection.cpp'
1355--- tests/unit-tests/input/evdev/test_evdev_device_detection.cpp 2016-03-23 06:39:56 +0000
1356+++ tests/unit-tests/input/evdev/test_evdev_device_detection.cpp 2016-03-31 19:18:58 +0000
1357@@ -22,13 +22,18 @@
1358 #include "src/platforms/evdev/libinput_device.h"
1359 #include "src/server/report/null_report_factory.h"
1360
1361+#include "mir/test/doubles/mock_alarm_factory.h"
1362+#include "mir/test/doubles/advanceable_clock.h"
1363 #include "mir_test_framework/libinput_environment.h"
1364+#include "mir/test/fake_shared.h"
1365
1366 #include <gtest/gtest.h>
1367 #include <gmock/gmock.h>
1368 #include <tuple>
1369
1370 namespace mtf = mir_test_framework;
1371+namespace mt = mir::test;
1372+namespace mtd = mt::doubles;
1373 namespace mi = mir::input;
1374 namespace mie = mi::evdev;
1375
1376@@ -44,7 +49,9 @@
1377 udev* ctx = reinterpret_cast<udev*>(1);
1378 auto dev = env.setup_device(std::get<0>(param));
1379 std::shared_ptr<libinput> lib = mie::make_libinput(ctx);
1380- mie::LibInputDevice device(mir::report::null_input_report(), mie::make_libinput_device(lib, dev));
1381+ ::testing::NiceMock<mtd::MockAlarmFactory> mock_alarm_factory;
1382+ mtd::AdvanceableClock clock;
1383+ mie::LibInputDevice device(mir::report::null_input_report(), mie::make_libinput_device(lib, dev), mt::fake_shared(mock_alarm_factory), mt::fake_shared(clock));
1384 auto info = device.get_device_info();
1385 EXPECT_THAT(info.capabilities, Eq(std::get<1>(param)));
1386 }
1387
1388=== modified file 'tests/unit-tests/input/evdev/test_libinput_device.cpp'
1389--- tests/unit-tests/input/evdev/test_libinput_device.cpp 2016-03-23 06:39:56 +0000
1390+++ tests/unit-tests/input/evdev/test_libinput_device.cpp 2016-03-31 19:18:58 +0000
1391@@ -25,12 +25,16 @@
1392 #include "mir/input/input_sink.h"
1393 #include "mir/input/pointer_settings.h"
1394 #include "mir/input/touchpad_settings.h"
1395+#include "mir/input/keyboard_settings.h"
1396 #include "mir/flags.h"
1397 #include "mir/geometry/point.h"
1398 #include "mir/geometry/rectangle.h"
1399 #include "mir/test/event_matchers.h"
1400 #include "mir/test/doubles/mock_libinput.h"
1401+#include "mir/test/doubles/fake_alarm_factory.h"
1402+#include "mir/test/doubles/advanceable_clock.h"
1403 #include "mir/test/gmock_fixes.h"
1404+#include "mir/test/fake_shared.h"
1405 #include "mir/udev/wrapper.h"
1406 #include "mir/cookie/authority.h"
1407 #include "mir_test_framework/libinput_environment.h"
1408@@ -60,6 +64,7 @@
1409 };
1410
1411 using namespace ::testing;
1412+using namespace std::chrono_literals;
1413
1414 struct MockInputSink : mi::InputSink
1415 {
1416@@ -124,6 +129,8 @@
1417 struct LibInputDevice : public ::testing::Test
1418 {
1419 mtf::LibInputEnvironment env;
1420+ mtd::AdvanceableClock clock;
1421+ mtd::FakeAlarmFactory alarm_factory;
1422 ::testing::NiceMock<MockInputSink> mock_sink;
1423 ::testing::NiceMock<MockEventBuilder> mock_builder;
1424 std::shared_ptr<libinput> lib;
1425@@ -243,33 +250,33 @@
1426 struct LibInputDeviceOnLaptopKeyboard : public LibInputDevice
1427 {
1428 libinput_device*const fake_device = setup_laptop_keyboard();
1429- mie::LibInputDevice keyboard{mir::report::null_input_report(), mie::make_libinput_device(lib, fake_device)};
1430+ mie::LibInputDevice keyboard{mir::report::null_input_report(), mie::make_libinput_device(lib, fake_device), mt::fake_shared(alarm_factory), mt::fake_shared(clock)};
1431 };
1432
1433 struct LibInputDeviceOnMouse : public LibInputDevice
1434 {
1435 libinput_device*const fake_device = setup_mouse();
1436- mie::LibInputDevice mouse{mir::report::null_input_report(), mie::make_libinput_device(lib, fake_device)};
1437+ mie::LibInputDevice mouse{mir::report::null_input_report(), mie::make_libinput_device(lib, fake_device), mt::fake_shared(alarm_factory), mt::fake_shared(clock)};
1438 };
1439
1440 struct LibInputDeviceOnLaptopKeyboardAndMouse : public LibInputDevice
1441 {
1442 libinput_device*const fake_device = setup_mouse();
1443 libinput_device*const fake_device_2 = setup_laptop_keyboard();
1444- mie::LibInputDevice keyboard{mir::report::null_input_report(), mie::make_libinput_device(lib, fake_device_2)};
1445- mie::LibInputDevice mouse{mir::report::null_input_report(), mie::make_libinput_device(lib, fake_device)};
1446+ 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)};
1447+ mie::LibInputDevice mouse{mir::report::null_input_report(), mie::make_libinput_device(lib, fake_device), mt::fake_shared(alarm_factory), mt::fake_shared(clock)};
1448 };
1449
1450 struct LibInputDeviceOnTouchScreen : public LibInputDevice
1451 {
1452 libinput_device*const fake_device = setup_touchscreen();
1453- mie::LibInputDevice touch_screen{mir::report::null_input_report(), mie::make_libinput_device(lib, fake_device)};
1454+ 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)};
1455 };
1456
1457 struct LibInputDeviceOnTouchpad : public LibInputDevice
1458 {
1459 libinput_device*const fake_device = setup_touchpad();
1460- mie::LibInputDevice touchpad{mir::report::null_input_report(), mie::make_libinput_device(lib, fake_device)};
1461+ mie::LibInputDevice touchpad{mir::report::null_input_report(), mie::make_libinput_device(lib, fake_device), mt::fake_shared(alarm_factory), mt::fake_shared(clock)};
1462 };
1463 }
1464
1465@@ -284,7 +291,7 @@
1466 .Times(1);
1467
1468 mie::LibInputDevice dev(mir::report::null_input_report(),
1469- mie::make_libinput_device(lib, fake_device));
1470+ mie::make_libinput_device(lib, fake_device), mt::fake_shared(alarm_factory), mt::fake_shared(clock));
1471 dev.start(&mock_sink, &mock_builder);
1472 }
1473
1474@@ -299,7 +306,7 @@
1475 EXPECT_CALL(env.mock_libinput, libinput_device_ref(second_fake_device)).Times(1);
1476
1477 mie::LibInputDevice dev(mir::report::null_input_report(),
1478- mie::make_libinput_device(lib, fake_device));
1479+ mie::make_libinput_device(lib, fake_device), mt::fake_shared(alarm_factory), mt::fake_shared(clock));
1480 dev.add_device_of_group(mie::make_libinput_device(lib, second_fake_device));
1481 dev.start(&mock_sink, &mock_builder);
1482 }
1483@@ -310,7 +317,7 @@
1484 auto second_fake_device = setup_trackpad();
1485
1486 mie::LibInputDevice dev(mir::report::null_input_report(),
1487- mie::make_libinput_device(lib, fake_device));
1488+ mie::make_libinput_device(lib, fake_device), mt::fake_shared(alarm_factory), mt::fake_shared(clock));
1489 dev.add_device_of_group(mie::make_libinput_device(lib, second_fake_device));
1490 auto info = dev.get_device_info();
1491
1492@@ -327,7 +334,7 @@
1493 EXPECT_CALL(env.mock_libinput, libinput_device_unref(fake_device))
1494 .Times(1);
1495
1496- mie::LibInputDevice dev(mir::report::null_input_report(), mie::make_libinput_device(lib, fake_device));
1497+ mie::LibInputDevice dev(mir::report::null_input_report(), mie::make_libinput_device(lib, fake_device), mt::fake_shared(alarm_factory), mt::fake_shared(clock));
1498 }
1499
1500 TEST_F(LibInputDeviceOnLaptopKeyboard, process_event_converts_key_event)
1501@@ -343,6 +350,31 @@
1502 process_events(keyboard);
1503 }
1504
1505+TEST_F(LibInputDeviceOnLaptopKeyboard, key_press_triggers_repeat)
1506+{
1507+ mi::KeyboardSettings keyboard_settings;
1508+ keyboard_settings.repeat_delay = 600ms;
1509+ keyboard_settings.repeat_interval = 200ms;
1510+ std::function<void()> timer_callback;
1511+
1512+ EXPECT_CALL(mock_sink, handle_input(AllOf(
1513+ mt::KeyOfScanCode(KEY_A),
1514+ mt::KeyDownEvent())))
1515+ .Times(1);
1516+ EXPECT_CALL(mock_sink, handle_input(AllOf(
1517+ mt::KeyOfScanCode(KEY_A),
1518+ mt::KeyRepeatEvent())))
1519+ .Times(2);
1520+
1521+ keyboard.start(&mock_sink, &mock_builder);
1522+ keyboard.apply_settings(keyboard_settings);
1523+ env.mock_libinput.setup_key_event(fake_device, event_time_1, KEY_A, LIBINPUT_KEY_STATE_PRESSED);
1524+ process_events(keyboard);
1525+
1526+ alarm_factory.advance_by(601ms);
1527+ alarm_factory.advance_by(201ms);
1528+}
1529+
1530 TEST_F(LibInputDeviceOnLaptopKeyboard, process_event_accumulates_key_state)
1531 {
1532 InSequence seq;
1533
1534=== modified file 'tests/unit-tests/input/test_default_device.cpp'
1535--- tests/unit-tests/input/test_default_device.cpp 2016-03-23 06:39:56 +0000
1536+++ tests/unit-tests/input/test_default_device.cpp 2016-03-31 19:18:58 +0000
1537@@ -21,54 +21,23 @@
1538 #include "mir/input/touchpad_configuration.h"
1539 #include "mir/input/pointer_configuration.h"
1540 #include "mir/dispatch/action_queue.h"
1541+#include "mir/test/doubles/mock_input_device.h"
1542 #include <gtest/gtest.h>
1543 #include <gmock/gmock.h>
1544 #include <stdexcept>
1545
1546 namespace mi = mir::input;
1547+namespace mtd = mir::test::doubles;
1548 namespace md = mir::dispatch;
1549 using namespace ::testing;
1550 namespace
1551 {
1552-struct MockInputDevice : public mi::InputDevice
1553-{
1554- MOCK_METHOD2(start, void(mi::InputSink* destination, mi::EventBuilder* builder));
1555- MOCK_METHOD0(stop, void());
1556- MOCK_METHOD0(get_device_info, mi::InputDeviceInfo());
1557- MOCK_CONST_METHOD0(get_pointer_settings, mir::optional_value<mi::PointerSettings>());
1558- MOCK_METHOD1(apply_settings, void(mi::PointerSettings const&));
1559- MOCK_CONST_METHOD0(get_touchpad_settings, mir::optional_value<mi::TouchpadSettings>());
1560- MOCK_METHOD1(apply_settings, void(mi::TouchpadSettings const&));
1561-};
1562-
1563 struct DefaultDevice : Test
1564 {
1565- NiceMock<MockInputDevice> touchpad;
1566- NiceMock<MockInputDevice> mouse;
1567- NiceMock<MockInputDevice> keyboard;
1568+ NiceMock<mtd::MockInputDevice> touchpad{"name", "unique", mi::DeviceCapability::touchpad|mi::DeviceCapability::pointer};
1569+ NiceMock<mtd::MockInputDevice> mouse{"name", "unique", mi::DeviceCapability::pointer};
1570+ NiceMock<mtd::MockInputDevice> keyboard{"name", "unique", mi::DeviceCapability::keyboard};
1571 std::shared_ptr<md::ActionQueue> queue{std::make_shared<md::ActionQueue>()};
1572-
1573- DefaultDevice()
1574- {
1575- using optional_pointer_settings = mir::optional_value<mi::PointerSettings>;
1576- using optional_touchpad_settings = mir::optional_value<mi::TouchpadSettings>;
1577- ON_CALL(touchpad, get_device_info())
1578- .WillByDefault(Return(mi::InputDeviceInfo{"name", "unique", mi::DeviceCapability::touchpad|mi::DeviceCapability::pointer}));
1579- ON_CALL(touchpad, get_pointer_settings())
1580- .WillByDefault(Return(optional_pointer_settings{mi::PointerSettings{}}));
1581- ON_CALL(touchpad, get_touchpad_settings())
1582- .WillByDefault(Return(optional_touchpad_settings{mi::TouchpadSettings{}}));
1583-
1584- ON_CALL(mouse, get_device_info())
1585- .WillByDefault(Return(mi::InputDeviceInfo{"name", "unique", mi::DeviceCapability::pointer}));
1586- ON_CALL(mouse, get_pointer_settings()).WillByDefault(Return(optional_pointer_settings{mi::PointerSettings{}}));
1587- ON_CALL(mouse, get_touchpad_settings()).WillByDefault(Return(optional_touchpad_settings{}));
1588-
1589- ON_CALL(keyboard, get_device_info())
1590- .WillByDefault(Return(mi::InputDeviceInfo{"name", "unique", mi::DeviceCapability::keyboard}));
1591- ON_CALL(keyboard, get_pointer_settings()).WillByDefault(Return(optional_pointer_settings{}));
1592- ON_CALL(keyboard, get_touchpad_settings()).WillByDefault(Return(optional_touchpad_settings{}));
1593- }
1594 };
1595
1596 }
1597@@ -91,7 +60,6 @@
1598 EXPECT_THROW({dev.apply_pointer_configuration(pointer_conf);}, std::invalid_argument);
1599 }
1600
1601-
1602 TEST_F(DefaultDevice, accepts_pointer_config_on_mice)
1603 {
1604 mi::DefaultDevice dev(MirInputDeviceId{17}, queue, mouse);
1605@@ -118,6 +86,7 @@
1606 queue->dispatch(md::FdEvent::readable);
1607 }
1608
1609+
1610 TEST_F(DefaultDevice, ensures_cursor_accleration_bias_is_in_range)
1611 {
1612 mi::DefaultDevice dev(MirInputDeviceId{17}, queue, touchpad);
1613
1614=== removed file 'tests/unit-tests/input/test_key_repeat_dispatcher.cpp'
1615--- tests/unit-tests/input/test_key_repeat_dispatcher.cpp 2016-03-23 06:39:56 +0000
1616+++ tests/unit-tests/input/test_key_repeat_dispatcher.cpp 1970-01-01 00:00:00 +0000
1617@@ -1,184 +0,0 @@
1618-/*
1619- * Copyright © 2015 Canonical Ltd.
1620- *
1621- * This program is free software: you can redistribute it and/or modify
1622- * it under the terms of the GNU General Public License version 3 as
1623- * published by the Free Software Foundation.
1624- *
1625- * This program is distributed in the hope that it will be useful,
1626- * but WITHOUT ANY WARRANTY; without even the implied warranty of
1627- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
1628- * GNU General Public License for more details.
1629- *
1630- * You should have received a copy of the GNU General Public License
1631- * along with this program. If not, see <http://www.gnu.org/licenses/>.
1632- *
1633- * Authored by: Robert Carr <robert.carr@canonical.com>
1634- */
1635-
1636-#include "src/server/input/key_repeat_dispatcher.h"
1637-
1638-#include "mir/events/event_private.h"
1639-#include "mir/events/event_builders.h"
1640-#include "mir/time/alarm.h"
1641-#include "mir/time/alarm_factory.h"
1642-#include "mir/cookie/authority.h"
1643-#include "mir/input/input_device_observer.h"
1644-#include "mir/input/pointer_configuration.h"
1645-#include "mir/input/touchpad_configuration.h"
1646-#include "mir/input/device.h"
1647-
1648-#include "mir/test/fake_shared.h"
1649-#include "mir/test/event_matchers.h"
1650-#include "mir/test/doubles/mock_input_dispatcher.h"
1651-#include "mir/test/doubles/mock_input_device_hub.h"
1652-
1653-#include <gtest/gtest.h>
1654-#include <gmock/gmock.h>
1655-
1656-namespace mi = mir::input;
1657-namespace mev = mir::events;
1658-namespace mt = mir::test;
1659-namespace mtd = mt::doubles;
1660-
1661-using namespace ::testing;
1662-
1663-namespace
1664-{
1665-struct MockAlarm : public mir::time::Alarm
1666-{
1667- MOCK_METHOD0(cancel, bool());
1668- MOCK_CONST_METHOD0(state, mir::time::Alarm::State());
1669- MOCK_METHOD1(reschedule_in, bool(std::chrono::milliseconds));
1670- MOCK_METHOD1(reschedule_for, bool(mir::time::Timestamp));
1671-
1672- // destructor cancels the alarm
1673- ~MockAlarm()
1674- {
1675- cancel();
1676- }
1677-};
1678-
1679-struct MockAlarmFactory : public mir::time::AlarmFactory
1680-{
1681- MOCK_METHOD1(create_alarm_adapter, mir::time::Alarm*(std::function<void()> const&));
1682- std::unique_ptr<mir::time::Alarm> create_alarm(std::function<void()> const& cb)
1683- {
1684- return std::unique_ptr<mir::time::Alarm>(create_alarm_adapter(cb));
1685- }
1686-
1687- std::unique_ptr<mir::time::Alarm> create_alarm(std::shared_ptr<mir::LockableCallback> const&)
1688- {
1689- return nullptr;
1690- }
1691-};
1692-
1693-struct StubDevice : public mi::Device
1694-{
1695- MirInputDeviceId device_id;
1696- StubDevice(MirInputDeviceId id) : device_id(id) {}
1697- MirInputDeviceId id() const { return device_id;}
1698- mi::DeviceCapabilities capabilities() const {return mi::DeviceCapability::keyboard;}
1699- std::string name() const {return {};}
1700- std::string unique_id() const {return {};}
1701-
1702- mir::optional_value<mi::PointerConfiguration> pointer_configuration() const {return {};}
1703- void apply_pointer_configuration(mi::PointerConfiguration const&) {;}
1704- mir::optional_value<mi::TouchpadConfiguration> touchpad_configuration() const {return {};}
1705- void apply_touchpad_configuration(mi::TouchpadConfiguration const&) {}
1706-};
1707-
1708-struct KeyRepeatDispatcher : public testing::Test
1709-{
1710- KeyRepeatDispatcher()
1711- : dispatcher(mock_next_dispatcher, mock_alarm_factory, cookie_authority, true, repeat_time, repeat_delay)
1712- {
1713- ON_CALL(hub,add_observer(_)).WillByDefault(SaveArg<0>(&observer));
1714- dispatcher.set_input_device_hub(mt::fake_shared(hub));
1715- }
1716- void simulate_device_removal()
1717- {
1718- StubDevice dev(test_device);
1719- observer->device_removed(mt::fake_shared(dev));
1720- observer->changes_complete();
1721- }
1722-
1723- const MirInputDeviceId test_device = 123;
1724- std::shared_ptr<mtd::MockInputDispatcher> mock_next_dispatcher = std::make_shared<mtd::MockInputDispatcher>();
1725- std::shared_ptr<MockAlarmFactory> mock_alarm_factory = std::make_shared<MockAlarmFactory>();
1726- std::shared_ptr<mir::cookie::Authority> cookie_authority = mir::cookie::Authority::create();
1727- std::chrono::milliseconds const repeat_time{2};
1728- std::chrono::milliseconds const repeat_delay{1};
1729- std::shared_ptr<mi::InputDeviceObserver> observer;
1730- NiceMock<mtd::MockInputDeviceHub> hub;
1731- mi::KeyRepeatDispatcher dispatcher;
1732-
1733- mir::EventUPtr a_key_down_event()
1734- {
1735- 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);
1736- }
1737-
1738- mir::EventUPtr a_key_up_event()
1739- {
1740- 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);
1741- }
1742-};
1743-}
1744-
1745-TEST_F(KeyRepeatDispatcher, forwards_start_stop)
1746-{
1747- InSequence seq;
1748- EXPECT_CALL(*mock_next_dispatcher, start()).Times(1);
1749- EXPECT_CALL(*mock_next_dispatcher, stop()).Times(1);
1750-
1751- dispatcher.start();
1752- dispatcher.stop();
1753-}
1754-
1755-TEST_F(KeyRepeatDispatcher, schedules_alarm_to_repeat_key_down)
1756-{
1757- MockAlarm *mock_alarm = new MockAlarm; // deleted by AlarmFactory
1758- std::function<void()> alarm_function;
1759-
1760- InSequence seq;
1761- EXPECT_CALL(*mock_alarm_factory, create_alarm_adapter(_)).Times(1).
1762- WillOnce(DoAll(SaveArg<0>(&alarm_function), Return(mock_alarm)));
1763- // Once for initial down and again when invoked
1764- EXPECT_CALL(*mock_alarm, reschedule_in(repeat_time)).Times(1).WillOnce(Return(true));
1765- EXPECT_CALL(*mock_next_dispatcher, dispatch(mt::KeyDownEvent())).Times(1);
1766- EXPECT_CALL(*mock_next_dispatcher, dispatch(mt::KeyRepeatEvent())).Times(1);
1767- EXPECT_CALL(*mock_alarm, reschedule_in(repeat_delay)).Times(1).WillOnce(Return(true));
1768- EXPECT_CALL(*mock_next_dispatcher, dispatch(mt::KeyUpEvent())).Times(1);
1769-
1770- // Schedule the repeat
1771- dispatcher.dispatch(*a_key_down_event());
1772- // Trigger the repeat
1773- alarm_function();
1774- // Trigger the cancel
1775- dispatcher.dispatch(*a_key_up_event());
1776-}
1777-
1778-TEST_F(KeyRepeatDispatcher, stops_repeat_on_device_removal)
1779-{
1780- MockAlarm *mock_alarm = new MockAlarm;
1781- std::function<void()> alarm_function;
1782- bool alarm_canceled = false;
1783-
1784- InSequence seq;
1785- EXPECT_CALL(*mock_alarm_factory, create_alarm_adapter(_)).Times(1).
1786- WillOnce(DoAll(SaveArg<0>(&alarm_function), Return(mock_alarm)));
1787- // Once for initial down and again when invoked
1788- EXPECT_CALL(*mock_alarm, reschedule_in(repeat_time)).Times(1).WillOnce(Return(true));
1789- EXPECT_CALL(*mock_next_dispatcher, dispatch(mt::KeyDownEvent())).Times(1);
1790- EXPECT_CALL(*mock_next_dispatcher, dispatch(mt::KeyRepeatEvent())).Times(1);
1791- EXPECT_CALL(*mock_alarm, reschedule_in(repeat_delay)).Times(1).WillOnce(Return(true));
1792- ON_CALL(*mock_alarm, cancel()).WillByDefault(Invoke([&](){alarm_canceled = true; return true;}));
1793-
1794- dispatcher.dispatch(*a_key_down_event());
1795-
1796- alarm_function();
1797- Mock::VerifyAndClearExpectations(mock_alarm); // mock_alarm will be deleted after this
1798-
1799- simulate_device_removal();
1800- EXPECT_THAT(alarm_canceled, Eq(true));
1801-}
1802
1803=== modified file 'tests/unit-tests/input/test_x11_platform.cpp'
1804--- tests/unit-tests/input/test_x11_platform.cpp 2016-03-23 06:39:56 +0000
1805+++ tests/unit-tests/input/test_x11_platform.cpp 2016-03-31 19:18:58 +0000
1806@@ -34,12 +34,15 @@
1807 #include "mir/cookie/authority.h"
1808 #include "mir/test/event_matchers.h"
1809
1810+#include "mir/input/keyboard_settings.h"
1811+
1812 namespace md = mir::dispatch;
1813 namespace mi = mir::input;
1814 namespace mt = mir::test;
1815 namespace mtd = mt::doubles;
1816
1817 using namespace ::testing;
1818+using namespace std::chrono_literals;
1819
1820 namespace
1821 {
1822@@ -245,3 +248,80 @@
1823
1824 process_input_event();
1825 }
1826+
1827+TEST_F(X11PlatformTest, keyboard_device_forwards_x11_repeat_settings)
1828+{
1829+ XkbControlsRec ctrls_desc;
1830+ XkbDescRec xkb_desc;
1831+ ctrls_desc.repeat_delay = 600;
1832+ ctrls_desc.repeat_interval = 100;
1833+ EXPECT_CALL(mock_x11, XGetKeyboardControl(_,_))
1834+ .WillOnce(Invoke([](Display*, XKeyboardState* state)
1835+ {
1836+ state->global_auto_repeat = 1;
1837+ return 1;
1838+ }));
1839+ EXPECT_CALL(mock_x11, XkbQueryExtension(_,_,_,_,_,_))
1840+ .WillOnce(Return(1));
1841+ EXPECT_CALL(mock_x11, XkbAllocKeyboard())
1842+ .WillOnce(Return(&xkb_desc));
1843+ EXPECT_CALL(mock_x11, XkbGetControls(_,XkbRepeatKeysMask, &xkb_desc))
1844+ .WillOnce(Invoke([&](Display*, int, XkbDescPtr)
1845+ {
1846+ xkb_desc.ctrls = &ctrls_desc;
1847+ return 1;
1848+ }));
1849+ EXPECT_CALL(mock_x11, XkbFreeKeyboard(&xkb_desc, _, _));
1850+
1851+
1852+ capture_devices(); x11_platform.start();
1853+
1854+ for (auto const device : devices)
1855+ {
1856+ if (contains(device->get_device_info().capabilities,mi::DeviceCapability::keyboard))
1857+ {
1858+ auto settings = device->get_keyboard_settings();
1859+ EXPECT_THAT(settings.value().repeat_enabled, Eq(true));
1860+ EXPECT_THAT(settings.value().repeat_delay, Eq(600ms));
1861+ EXPECT_THAT(settings.value().repeat_interval, Eq(100ms));
1862+ }
1863+ }
1864+}
1865+
1866+TEST_F(X11PlatformTest, keyboard_device_forwards_settings_to_x11)
1867+{
1868+ XkbControlsRec ctrls_desc;
1869+ XkbDescRec xkb_desc;
1870+
1871+ EXPECT_CALL(mock_x11, XChangeKeyboardControl(_, KBAutoRepeatMode,_));
1872+ EXPECT_CALL(mock_x11, XkbQueryExtension(_,_,_,_,_,_))
1873+ .WillOnce(Return(1));
1874+ EXPECT_CALL(mock_x11, XkbAllocKeyboard())
1875+ .WillOnce(Return(&xkb_desc));
1876+ EXPECT_CALL(mock_x11, XkbGetControls(_,XkbRepeatKeysMask, &xkb_desc))
1877+ .WillOnce(Invoke([&](Display*, int, XkbDescPtr)
1878+ {
1879+ xkb_desc.ctrls = &ctrls_desc;
1880+ return 1;
1881+ }));
1882+ EXPECT_CALL(mock_x11, XkbSetControls(_,XkbRepeatKeysMask, &xkb_desc))
1883+ .WillOnce(Return(1));
1884+ EXPECT_CALL(mock_x11, XkbFreeKeyboard(&xkb_desc, _, _));
1885+
1886+
1887+ capture_devices(); x11_platform.start();
1888+
1889+ for (auto const device : devices)
1890+ {
1891+ if (contains(device->get_device_info().capabilities,mi::DeviceCapability::keyboard))
1892+ {
1893+ mi::KeyboardSettings settings;
1894+ settings.repeat_interval = 23ms;
1895+ settings.repeat_delay = 1000ms;
1896+ device->apply_settings(settings);
1897+
1898+ EXPECT_THAT(ctrls_desc.repeat_delay, Eq(1000));
1899+ EXPECT_THAT(ctrls_desc.repeat_interval, Eq(23));
1900+ }
1901+ }
1902+}

Subscribers

People subscribed via source and target branches