Mir

Merge lp:~andreas-pokorny/mir/store-device-config into lp:mir

Proposed by Andreas Pokorny
Status: Superseded
Proposed branch: lp:~andreas-pokorny/mir/store-device-config
Merge into: lp:mir
Prerequisite: lp:~andreas-pokorny/mir/wait-for-input-config-before-emitting-input-state
Diff against target: 1605 lines (+753/-181)
18 files modified
src/include/server/mir/default_server_configuration.h (+1/-0)
src/server/graphics/nested/input_platform.cpp (+29/-41)
src/server/graphics/nested/input_platform.h (+0/-2)
src/server/input/default_configuration.cpp (+7/-3)
src/server/input/default_device.cpp (+69/-11)
src/server/input/default_device.h (+12/-0)
src/server/input/default_input_device_hub.cpp (+175/-35)
src/server/input/default_input_device_hub.h (+31/-7)
tests/acceptance-tests/test_client_input.cpp (+3/-2)
tests/acceptance-tests/test_input_device_hub.cpp (+4/-2)
tests/acceptance-tests/test_nested_input.cpp (+8/-7)
tests/include/mir/test/doubles/mock_device.h (+71/-0)
tests/integration-tests/input/test_single_seat_setup.cpp (+26/-26)
tests/unit-tests/input/CMakeLists.txt (+1/-0)
tests/unit-tests/input/test_config_changer.cpp (+3/-30)
tests/unit-tests/input/test_default_device.cpp (+127/-0)
tests/unit-tests/input/test_default_input_device_hub.cpp (+67/-15)
tests/unit-tests/input/test_external_input_device_hub.cpp (+119/-0)
To merge this branch: bzr merge lp:~andreas-pokorny/mir/store-device-config
Reviewer Review Type Date Requested Status
Mir CI Bot continuous-integration Approve
Mir development team Pending
Review via email: mp+319436@code.launchpad.net

This proposal has been superseded by a proposal from 2017-03-14.

Commit message

Store device configurations and device ids

With this change previous configurations of input devices are restored when they reappear.

Description of the change

Store device ids of removed devices to restore them after a vt-switch.

To post a comment you must log in.
Revision history for this message
Mir CI Bot (mir-ci-bot) wrote :

FAILED: Continuous integration, rev:4070
https://mir-jenkins.ubuntu.com/job/mir-ci/3121/
Executed test runs:
    FAILURE: https://mir-jenkins.ubuntu.com/job/build-mir/4189/console
    SUCCESS: https://mir-jenkins.ubuntu.com/job/build-0-fetch/4276
    SUCCESS: https://mir-jenkins.ubuntu.com/job/build-1-sourcepkg/release=vivid+overlay/4266
    SUCCESS: https://mir-jenkins.ubuntu.com/job/build-1-sourcepkg/release=xenial+overlay/4266
    SUCCESS: https://mir-jenkins.ubuntu.com/job/build-1-sourcepkg/release=zesty/4266
    FAILURE: https://mir-jenkins.ubuntu.com/job/build-2-binpkg-mir/arch=amd64,compiler=clang,platform=mesa,release=zesty/4216/console
    SUCCESS: https://mir-jenkins.ubuntu.com/job/build-2-binpkg-mir/arch=amd64,compiler=gcc,platform=mesa,release=xenial+overlay/4216
        deb: https://mir-jenkins.ubuntu.com/job/build-2-binpkg-mir/arch=amd64,compiler=gcc,platform=mesa,release=xenial+overlay/4216/artifact/output/*zip*/output.zip
    SUCCESS: https://mir-jenkins.ubuntu.com/job/build-2-binpkg-mir/arch=amd64,compiler=gcc,platform=mesa,release=zesty/4216
        deb: https://mir-jenkins.ubuntu.com/job/build-2-binpkg-mir/arch=amd64,compiler=gcc,platform=mesa,release=zesty/4216/artifact/output/*zip*/output.zip
    SUCCESS: https://mir-jenkins.ubuntu.com/job/build-2-binpkg-mir/arch=cross-armhf,compiler=gcc,platform=android,release=vivid+overlay/4216
        deb: https://mir-jenkins.ubuntu.com/job/build-2-binpkg-mir/arch=cross-armhf,compiler=gcc,platform=android,release=vivid+overlay/4216/artifact/output/*zip*/output.zip
    SUCCESS: https://mir-jenkins.ubuntu.com/job/build-2-binpkg-mir/arch=i386,compiler=gcc,platform=android,release=vivid+overlay/4216
        deb: https://mir-jenkins.ubuntu.com/job/build-2-binpkg-mir/arch=i386,compiler=gcc,platform=android,release=vivid+overlay/4216/artifact/output/*zip*/output.zip
    SUCCESS: https://mir-jenkins.ubuntu.com/job/build-2-binpkg-mir/arch=i386,compiler=gcc,platform=mesa,release=xenial+overlay/4216
        deb: https://mir-jenkins.ubuntu.com/job/build-2-binpkg-mir/arch=i386,compiler=gcc,platform=mesa,release=xenial+overlay/4216/artifact/output/*zip*/output.zip

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

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

PASSED: Continuous integration, rev:4072
https://mir-jenkins.ubuntu.com/job/mir-ci/3162/
Executed test runs:
    SUCCESS: https://mir-jenkins.ubuntu.com/job/build-mir/4243
    SUCCESS: https://mir-jenkins.ubuntu.com/job/build-0-fetch/4330
    SUCCESS: https://mir-jenkins.ubuntu.com/job/build-1-sourcepkg/release=vivid+overlay/4320
    SUCCESS: https://mir-jenkins.ubuntu.com/job/build-1-sourcepkg/release=xenial+overlay/4320
    SUCCESS: https://mir-jenkins.ubuntu.com/job/build-1-sourcepkg/release=zesty/4320
    SUCCESS: https://mir-jenkins.ubuntu.com/job/build-2-binpkg-mir/arch=amd64,compiler=clang,platform=mesa,release=zesty/4270
        deb: https://mir-jenkins.ubuntu.com/job/build-2-binpkg-mir/arch=amd64,compiler=clang,platform=mesa,release=zesty/4270/artifact/output/*zip*/output.zip
    SUCCESS: https://mir-jenkins.ubuntu.com/job/build-2-binpkg-mir/arch=amd64,compiler=gcc,platform=mesa,release=xenial+overlay/4270
        deb: https://mir-jenkins.ubuntu.com/job/build-2-binpkg-mir/arch=amd64,compiler=gcc,platform=mesa,release=xenial+overlay/4270/artifact/output/*zip*/output.zip
    SUCCESS: https://mir-jenkins.ubuntu.com/job/build-2-binpkg-mir/arch=amd64,compiler=gcc,platform=mesa,release=zesty/4270
        deb: https://mir-jenkins.ubuntu.com/job/build-2-binpkg-mir/arch=amd64,compiler=gcc,platform=mesa,release=zesty/4270/artifact/output/*zip*/output.zip
    SUCCESS: https://mir-jenkins.ubuntu.com/job/build-2-binpkg-mir/arch=cross-armhf,compiler=gcc,platform=android,release=vivid+overlay/4270
        deb: https://mir-jenkins.ubuntu.com/job/build-2-binpkg-mir/arch=cross-armhf,compiler=gcc,platform=android,release=vivid+overlay/4270/artifact/output/*zip*/output.zip
    SUCCESS: https://mir-jenkins.ubuntu.com/job/build-2-binpkg-mir/arch=i386,compiler=gcc,platform=android,release=vivid+overlay/4270
        deb: https://mir-jenkins.ubuntu.com/job/build-2-binpkg-mir/arch=i386,compiler=gcc,platform=android,release=vivid+overlay/4270/artifact/output/*zip*/output.zip
    SUCCESS: https://mir-jenkins.ubuntu.com/job/build-2-binpkg-mir/arch=i386,compiler=gcc,platform=mesa,release=xenial+overlay/4270
        deb: https://mir-jenkins.ubuntu.com/job/build-2-binpkg-mir/arch=i386,compiler=gcc,platform=mesa,release=xenial+overlay/4270/artifact/output/*zip*/output.zip

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

review: Approve (continuous-integration)

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1=== modified file 'src/include/server/mir/default_server_configuration.h'
2--- src/include/server/mir/default_server_configuration.h 2017-03-13 08:12:52 +0000
3+++ src/include/server/mir/default_server_configuration.h 2017-03-14 20:52:46 +0000
4@@ -384,6 +384,7 @@
5 CachedPtr<input::InputManager> input_manager;
6 CachedPtr<input::SurfaceInputDispatcher> surface_input_dispatcher;
7 CachedPtr<input::DefaultInputDeviceHub> default_input_device_hub;
8+ CachedPtr<input::InputDeviceHub> input_device_hub;
9 CachedPtr<dispatch::MultiplexingDispatchable> input_reading_multiplexer;
10 CachedPtr<input::InputDispatcher> input_dispatcher;
11 CachedPtr<shell::InputTargeter> input_targeter;
12
13=== modified file 'src/server/graphics/nested/input_platform.cpp'
14--- src/server/graphics/nested/input_platform.cpp 2017-03-14 20:52:46 +0000
15+++ src/server/graphics/nested/input_platform.cpp 2017-03-14 02:26:28 +0000
16@@ -338,9 +338,34 @@
17 {
18 std::lock_guard<std::mutex> lock(devices_guard);
19 if (!devices.empty())
20- handle_device_state(event);
21- else
22- early_device_states.push_back(mir::events::clone_event(event));
23+ {
24+ auto const* device_state = mir_event_get_input_device_state_event(&event);
25+ for (size_t index = 0, end_index = mir_input_device_state_event_device_count(device_state);
26+ index != end_index; ++index)
27+ {
28+ auto it = devices.find(mir_input_device_state_event_device_id(device_state, index));
29+ if (it != end(devices) && it->second->destination)
30+ {
31+ auto dest = it->second->destination;
32+ auto key_count = mir_input_device_state_event_device_pressed_keys_count(device_state, index);
33+ std::vector<uint32_t> scan_codes;
34+ for (uint32_t i = 0; i < key_count; i++)
35+ {
36+ scan_codes.push_back(mir_input_device_state_event_device_pressed_keys_for_index(device_state, index, i));
37+ }
38+
39+ dest->key_state(scan_codes);
40+ dest->pointer_state(
41+ mir_input_device_state_event_device_pointer_buttons(device_state, index));
42+ }
43+ }
44+
45+ auto& front = begin(devices)->second;
46+ auto device_state_event = front->builder->device_state_event(
47+ mir_input_device_state_event_pointer_axis(device_state, mir_pointer_axis_x),
48+ mir_input_device_state_event_pointer_axis(device_state, mir_pointer_axis_y));
49+ front->destination->handle_input(*device_state_event);
50+ }
51 }
52 });
53 }
54@@ -396,13 +421,7 @@
55 for (auto new_dev : new_devs)
56 {
57 input_device_registry->add_device(new_dev.first);
58- }
59- for (auto const& event : early_device_states)
60- {
61- handle_device_state(*event);
62- }
63- for (auto new_dev : new_devs)
64- {
65+
66 auto early_event_queue = unknown_device_events.find(new_dev.second);
67 if (early_event_queue != end(unknown_device_events))
68 {
69@@ -414,7 +433,6 @@
70 }
71 }
72 unknown_device_events.clear();
73- early_device_states.clear();
74 }
75
76 void mgn::InputPlatform::config_changed()
77@@ -443,33 +461,3 @@
78 }
79 state = started;
80 }
81-
82-void mgn::InputPlatform::handle_device_state(MirEvent const& event)
83-{
84- auto const* device_state = mir_event_get_input_device_state_event(&event);
85- for (size_t index = 0, end_index = mir_input_device_state_event_device_count(device_state);
86- index != end_index; ++index)
87- {
88- auto it = devices.find(mir_input_device_state_event_device_id(device_state, index));
89- if (it != end(devices) && it->second->destination)
90- {
91- auto dest = it->second->destination;
92- auto key_count = mir_input_device_state_event_device_pressed_keys_count(device_state, index);
93- std::vector<uint32_t> scan_codes;
94- for (uint32_t i = 0; i < key_count; i++)
95- {
96- scan_codes.push_back(mir_input_device_state_event_device_pressed_keys_for_index(device_state, index, i));
97- }
98-
99- dest->key_state(scan_codes);
100- dest->pointer_state(
101- mir_input_device_state_event_device_pointer_buttons(device_state, index));
102- }
103- }
104-
105- auto& front = begin(devices)->second;
106- auto device_state_event = front->builder->device_state_event(
107- mir_input_device_state_event_pointer_axis(device_state, mir_pointer_axis_x),
108- mir_input_device_state_event_pointer_axis(device_state, mir_pointer_axis_y));
109- front->destination->handle_input(*device_state_event);
110-}
111
112=== modified file 'src/server/graphics/nested/input_platform.h'
113--- src/server/graphics/nested/input_platform.h 2017-03-14 20:52:46 +0000
114+++ src/server/graphics/nested/input_platform.h 2017-02-15 07:38:33 +0000
115@@ -58,7 +58,6 @@
116 void config_changed();
117 void update_devices();
118 void update_devices_locked();
119- void handle_device_state(MirEvent const& event);
120 struct InputDevice;
121 std::shared_ptr<HostConnection> const connection;
122 std::shared_ptr<input::InputDeviceRegistry> const input_device_registry;
123@@ -71,7 +70,6 @@
124 std::unordered_map<MirInputDeviceId, std::shared_ptr<InputDevice>> devices;
125 std::unordered_map<MirInputDeviceId, std::vector<std::pair<EventUPtr, mir::geometry::Rectangle>>>
126 unknown_device_events;
127- std::vector<EventUPtr> early_device_states;
128 enum State
129 {
130 started, stopped, paused
131
132=== modified file 'src/server/input/default_configuration.cpp'
133--- src/server/input/default_configuration.cpp 2017-03-14 02:26:28 +0000
134+++ src/server/input/default_configuration.cpp 2017-03-14 20:52:46 +0000
135@@ -301,7 +301,12 @@
136
137 std::shared_ptr<mi::InputDeviceHub> mir::DefaultServerConfiguration::the_input_device_hub()
138 {
139- return the_default_input_device_hub();
140+ return input_device_hub(
141+ [this]()
142+ {
143+ return std::make_shared<mi::ExternalInputDeviceHub>(the_default_input_device_hub(),
144+ the_main_loop());
145+ });
146 }
147
148 std::shared_ptr<mi::DefaultInputDeviceHub> mir::DefaultServerConfiguration::the_default_input_device_hub()
149@@ -314,7 +319,6 @@
150 auto hub = std::make_shared<mi::DefaultInputDeviceHub>(
151 the_seat(),
152 the_input_reading_multiplexer(),
153- the_main_loop(),
154 the_cookie_authority(),
155 the_key_mapper(),
156 the_server_status_listener());
157@@ -359,7 +363,7 @@
158 return input_configuration_changer(
159 [this]()
160 {
161- return std::make_shared<mi::ConfigChanger>(the_input_manager(), the_input_device_hub(), the_session_container(), the_session_event_handler_register());
162+ return std::make_shared<mi::ConfigChanger>(the_input_manager(), the_default_input_device_hub(), the_session_container(), the_session_event_handler_register());
163 }
164 );
165 }
166
167=== modified file 'src/server/input/default_device.cpp'
168--- src/server/input/default_device.cpp 2017-02-28 08:53:57 +0000
169+++ src/server/input/default_device.cpp 2017-03-14 20:52:46 +0000
170@@ -60,6 +60,23 @@
171 {
172 }
173
174+mi::DefaultDevice::DefaultDevice(MirInputDevice const& config,
175+ std::shared_ptr<dispatch::ActionQueue> const& actions,
176+ InputDevice& device,
177+ std::shared_ptr<KeyMapper> const& key_mapper,
178+ std::function<void(Device*)> const& callback)
179+ : DefaultDevice(config.id(), actions, device, key_mapper, callback)
180+{
181+ if (config.has_touchpad_config())
182+ set_touchpad_configuration(config.touchpad_config());
183+ if (config.has_keyboard_config())
184+ set_keyboard_configuration(config.keyboard_config());
185+ if (config.has_pointer_config())
186+ set_pointer_configuration(config.pointer_config());
187+ if (config.has_touchscreen_config())
188+ set_touchscreen_configuration(config.touchscreen_config());
189+}
190+
191 mi::DeviceCapabilities mi::DefaultDevice::capabilities() const
192 {
193 return info.capabilities;
194@@ -117,6 +134,12 @@
195 if (conf.cursor_acceleration_bias() < -1.0 || conf.cursor_acceleration_bias() > 1.0)
196 BOOST_THROW_EXCEPTION(std::invalid_argument("Cursor acceleration bias out of range"));
197
198+ set_pointer_configuration(conf);
199+ device_changed_callback(this);
200+}
201+
202+void mi::DefaultDevice::set_pointer_configuration(MirPointerConfig const& conf)
203+{
204 PointerSettings settings;
205 settings.handedness = conf.handedness();
206 settings.acceleration = conf.acceleration();
207@@ -133,7 +156,6 @@
208 {
209 dev->apply_settings(settings);
210 });
211- device_changed_callback(this);
212 }
213
214 void mi::DefaultDevice::apply_touchpad_configuration(MirTouchpadConfig const& conf)
215@@ -148,6 +170,12 @@
216 conf.button_down_scroll_button() == mi::no_scroll_button)
217 BOOST_THROW_EXCEPTION(std::invalid_argument("No scroll button configured"));
218
219+ set_touchpad_configuration(conf);
220+ device_changed_callback(this);
221+}
222+
223+void mi::DefaultDevice::set_touchpad_configuration(MirTouchpadConfig const& conf)
224+{
225 TouchpadSettings settings;
226 settings.click_mode= conf.click_mode();
227 settings.scroll_mode = conf.scroll_mode();
228@@ -166,7 +194,6 @@
229 {
230 dev->apply_settings(settings);
231 });
232- device_changed_callback(this);
233 }
234
235 mir::optional_value<MirKeyboardConfig> mi::DefaultDevice::keyboard_configuration() const
236@@ -180,15 +207,18 @@
237 if (!contains(info.capabilities, mi::DeviceCapability::keyboard))
238 BOOST_THROW_EXCEPTION(std::invalid_argument("Cannot apply a keyboard configuration"));
239
240- {
241- std::lock_guard<std::mutex> lock(config_mutex);
242- if (keyboard.value().device_keymap() != conf.device_keymap())
243- keyboard = conf;
244- else
245- return;
246- }
247+ set_keyboard_configuration(conf);
248+ device_changed_callback(this);
249+}
250+
251+void mi::DefaultDevice::set_keyboard_configuration(MirKeyboardConfig const& conf)
252+{
253+ std::lock_guard<std::mutex> lock(config_mutex);
254+ if (keyboard.value().device_keymap() != conf.device_keymap())
255+ keyboard = conf;
256+ else
257+ return;
258 key_mapper->set_keymap_for_device(device_id, conf.device_keymap());
259- device_changed_callback(this);
260 }
261
262 mir::optional_value<MirTouchscreenConfig> mi::DefaultDevice::touchscreen_configuration() const
263@@ -206,6 +236,12 @@
264 if (!touchscreen.is_set())
265 BOOST_THROW_EXCEPTION(std::invalid_argument("Cannot apply a touchscreen configuration"));
266
267+ set_touchscreen_configuration(config);
268+ device_changed_callback(this);
269+}
270+
271+void mi::DefaultDevice::set_touchscreen_configuration(MirTouchscreenConfig const& config)
272+{
273 TouchscreenSettings settings;
274 settings.output_id = config.output_id();
275 settings.mapping_mode = config.mapping_mode();
276@@ -216,5 +252,27 @@
277 {
278 dev->apply_settings(settings);
279 });
280- device_changed_callback(this);
281+}
282+
283+MirInputDevice mi::DefaultDevice::config() const
284+{
285+ auto stored_dev = MirInputDevice(
286+ id(),
287+ capabilities(),
288+ name(),
289+ unique_id());
290+ auto pointer_conf = pointer_configuration();
291+ if (pointer_conf.is_set())
292+ stored_dev.set_pointer_config(pointer_conf.value());
293+ auto touchpad_conf = touchpad_configuration();
294+ if (touchpad_conf.is_set())
295+ stored_dev.set_touchpad_config(touchpad_conf.value());
296+ auto keyboard_conf = keyboard_configuration();
297+ if (keyboard_conf.is_set())
298+ stored_dev.set_keyboard_config(keyboard_conf.value());
299+ auto touchscreen_conf = touchscreen_configuration();
300+ if (touchscreen_conf.is_set())
301+ stored_dev.set_touchscreen_config(touchscreen_conf.value());
302+
303+ return stored_dev;
304 }
305
306=== modified file 'src/server/input/default_device.h'
307--- src/server/input/default_device.h 2017-02-28 08:53:57 +0000
308+++ src/server/input/default_device.h 2017-03-14 20:52:46 +0000
309@@ -58,6 +58,11 @@
310 InputDevice& device,
311 std::shared_ptr<KeyMapper> const& key_mapper,
312 std::function<void(Device*)> const& change_callback);
313+ DefaultDevice(MirInputDevice const& config,
314+ std::shared_ptr<dispatch::ActionQueue> const& actions,
315+ InputDevice& device,
316+ std::shared_ptr<KeyMapper> const& key_mapper,
317+ std::function<void(Device*)> const& change_callback);
318 MirInputDeviceId id() const override;
319 DeviceCapabilities capabilities() const override;
320 std::string name() const override;
321@@ -71,7 +76,14 @@
322 void apply_keyboard_configuration(MirKeyboardConfig const&) override;
323 optional_value<MirTouchscreenConfig> touchscreen_configuration() const override;
324 void apply_touchscreen_configuration(MirTouchscreenConfig const&) override;
325+
326+ MirInputDevice config() const;
327 private:
328+ void set_pointer_configuration(MirPointerConfig const&);
329+ void set_touchpad_configuration(MirTouchpadConfig const&);
330+ void set_keyboard_configuration(MirKeyboardConfig const&);
331+ void set_touchscreen_configuration(MirTouchscreenConfig const&);
332+
333 void wake_hub_for_device_change();
334 MirInputDeviceId const device_id;
335 InputDevice& device;
336
337=== modified file 'src/server/input/default_input_device_hub.cpp'
338--- src/server/input/default_input_device_hub.cpp 2017-03-14 02:26:28 +0000
339+++ src/server/input/default_input_device_hub.cpp 2017-03-14 20:52:46 +0000
340@@ -41,16 +41,129 @@
341
342 namespace mi = mir::input;
343
344+struct mi::ExternalInputDeviceHub::Internal : InputDeviceObserver
345+{
346+ Internal(std::shared_ptr<mi::InputDeviceHub> const& hub,
347+ std::shared_ptr<ServerActionQueue> const& queue) :
348+ hub{hub}, observer_queue{queue}
349+ {}
350+ void device_added(std::shared_ptr<Device> const& device) override;
351+ void device_changed(std::shared_ptr<Device> const& device) override;
352+ void device_removed(std::shared_ptr<Device> const& device) override;
353+ void changes_complete() override;
354+
355+ std::weak_ptr<InputDeviceHub> hub;
356+ std::shared_ptr<ServerActionQueue> const observer_queue;
357+ ThreadSafeList<std::shared_ptr<InputDeviceObserver>> observers;
358+ std::vector<std::shared_ptr<Device>> devices_added;
359+ std::vector<std::shared_ptr<Device>> devices_changed;
360+ std::vector<std::shared_ptr<Device>> devices_removed;
361+ std::vector<std::shared_ptr<Device>> handles;
362+};
363+
364+mi::ExternalInputDeviceHub::ExternalInputDeviceHub(std::shared_ptr<mi::InputDeviceHub> const& hub, std::shared_ptr<mir::ServerActionQueue> const& queue)
365+ : data{std::make_shared<mi::ExternalInputDeviceHub::Internal>(hub, queue)}
366+{
367+ hub->add_observer(data);
368+}
369+
370+mi::ExternalInputDeviceHub::~ExternalInputDeviceHub()
371+{
372+ data->observer_queue->pause_processing_for(data.get());
373+}
374+
375+void mi::ExternalInputDeviceHub::add_observer(std::shared_ptr<InputDeviceObserver> const& observer)
376+{
377+ auto hub = data->hub.lock();
378+ if (hub)
379+ {
380+ data->observer_queue->enqueue(
381+ data.get(),
382+ [observer, data = this->data]
383+ {
384+ for (auto const& item : data->handles)
385+ observer->device_added(item);
386+ observer->changes_complete();
387+ data->observers.add(observer);
388+ });
389+ }
390+}
391+
392+void mi::ExternalInputDeviceHub::remove_observer(std::weak_ptr<InputDeviceObserver> const& obs)
393+{
394+ auto observer = obs.lock();
395+ data->observers.remove(observer);
396+}
397+
398+void mi::ExternalInputDeviceHub::for_each_input_device(std::function<void(Device const& device)> const& callback)
399+{
400+ auto hub = data->hub.lock();
401+ hub->for_each_input_device(callback);
402+}
403+
404+void mi::ExternalInputDeviceHub::for_each_mutable_input_device(std::function<void(Device& device)> const& callback)
405+{
406+ auto hub = data->hub.lock();
407+ hub->for_each_mutable_input_device(callback);
408+}
409+
410+void mi::ExternalInputDeviceHub::Internal::device_added(std::shared_ptr<Device> const& device)
411+{
412+ devices_added.push_back(device);
413+}
414+
415+void mi::ExternalInputDeviceHub::Internal::device_changed(std::shared_ptr<Device> const& device)
416+{
417+ devices_changed.push_back(device);
418+}
419+
420+void mi::ExternalInputDeviceHub::Internal::device_removed(std::shared_ptr<Device> const& device)
421+{
422+ devices_removed.push_back(device);
423+}
424+
425+void mi::ExternalInputDeviceHub::Internal::changes_complete()
426+{
427+ decltype(devices_added) added, changed, removed;
428+
429+ std::swap(devices_added, added);
430+ std::swap(devices_changed, changed);
431+ std::swap(devices_removed, removed);
432+
433+ if (!(added.empty() && changed.empty() && removed.empty()))
434+ observer_queue->enqueue(
435+ this,
436+ [this, added, changed, removed]
437+ {
438+ observers.for_each([&](std::shared_ptr<InputDeviceObserver> const& observer)
439+ {
440+ for (auto const& dev : added)
441+ observer->device_added(dev);
442+ for (auto const& dev : changed)
443+ observer->device_changed(dev);
444+ for (auto const& dev : removed)
445+ observer->device_removed(dev);
446+ observer->changes_complete();
447+ });
448+
449+ auto end_it = handles.end();
450+ for (auto const& dev : removed)
451+ end_it = remove(begin(handles), end(handles), dev);
452+ if (end_it != handles.end())
453+ handles.erase(end_it, end(handles));
454+ for (auto const& dev : added)
455+ handles.push_back(dev);
456+ });
457+}
458+
459 mi::DefaultInputDeviceHub::DefaultInputDeviceHub(
460 std::shared_ptr<mi::Seat> const& seat,
461 std::shared_ptr<dispatch::MultiplexingDispatchable> const& input_multiplexer,
462- std::shared_ptr<mir::ServerActionQueue> const& observer_queue,
463 std::shared_ptr<mir::cookie::Authority> const& cookie_authority,
464 std::shared_ptr<mi::KeyMapper> const& key_mapper,
465 std::shared_ptr<mir::ServerStatusListener> const& server_status_listener)
466 : seat{seat},
467 input_dispatchable{input_multiplexer},
468- observer_queue(observer_queue),
469 device_queue(std::make_shared<dispatch::ActionQueue>()),
470 cookie_authority(cookie_authority),
471 key_mapper(key_mapper),
472@@ -74,24 +187,16 @@
473
474 if (it == end(devices))
475 {
476- auto id = create_new_device_id();
477- auto handle = std::make_shared<DefaultDevice>(id, device_queue, *device, key_mapper, [this](Device *d){device_changed(d);});
478+ auto handle = restore_or_create_device(*device);
479 // send input device info to observer loop..
480 devices.push_back(std::make_unique<RegisteredDevice>(
481- device, id, input_dispatchable, cookie_authority, handle));
482+ device, handle->id(), input_dispatchable, cookie_authority, handle));
483
484 auto const& dev = devices.back();
485+ add_device_handle(handle);
486
487 seat->add_device(*handle);
488 dev->start(seat);
489-
490- // pass input device handle to observer loop..
491- observer_queue->enqueue(this,
492- [this, handle]()
493- {
494- add_device_handle(handle);
495- });
496-
497 }
498 else
499 {
500@@ -112,19 +217,14 @@
501 {
502 if (item->device_matches(device))
503 {
504+ store_device_config(*item->handle);
505 auto seat = item->seat;
506 if (seat)
507 {
508 seat->remove_device(*item->handle);
509 item->stop();
510 }
511- // send input device info to observer queue..
512- observer_queue->enqueue(
513- this,
514- [this,id = item->id()]()
515- {
516- remove_device_handle(id);
517- });
518+ remove_device_handle(item->id());
519
520 return true;
521 }
522@@ -230,18 +330,15 @@
523
524 void mi::DefaultInputDeviceHub::add_observer(std::shared_ptr<InputDeviceObserver> const& observer)
525 {
526- observer_queue->enqueue(
527- this,
528- [observer,this]
529+ device_queue->enqueue(
530+ [this,observer]()
531 {
532- observers.add(observer);
533+ std::unique_lock<std::mutex> lock(handles_guard);
534 for (auto const& item : handles)
535- {
536 observer->device_added(item);
537- }
538 observer->changes_complete();
539- }
540- );
541+ observers.add(observer);
542+ });
543 }
544
545 void mi::DefaultInputDeviceHub::for_each_input_device(std::function<void(Device const&)> const& callback)
546@@ -270,12 +367,7 @@
547 void mi::DefaultInputDeviceHub::remove_observer(std::weak_ptr<InputDeviceObserver> const& element)
548 {
549 auto observer = element.lock();
550-
551- observer_queue->enqueue(this,
552- [observer, this]
553- {
554- observers.remove(observer);
555- });
556+ observers.remove(observer);
557 }
558
559 void mi::DefaultInputDeviceHub::add_device_handle(std::shared_ptr<DefaultDevice> const& handle)
560@@ -285,7 +377,7 @@
561 handles.push_back(handle);
562 }
563
564- observers.for_each([&handle](std::shared_ptr<InputDeviceObserver> const& observer)
565+ observers.for_each([&handle, this](std::shared_ptr<InputDeviceObserver> const& observer)
566 {
567 observer->device_added(handle);
568 observer->changes_complete();
569@@ -396,3 +488,51 @@
570 }
571 }
572
573+void mi::DefaultInputDeviceHub::store_device_config(mi::DefaultDevice const& dev)
574+{
575+ std::lock_guard<std::mutex> lock(stored_configurations_guard);
576+ stored_devices.push_back(dev.config());
577+}
578+
579+mir::optional_value<MirInputDevice>
580+mi::DefaultInputDeviceHub::get_stored_device_config(std::string const& id)
581+{
582+ mir::optional_value<MirInputDevice> optional_config;
583+ std::lock_guard<std::mutex> lock(stored_configurations_guard);
584+ auto pos = remove_if(
585+ begin(stored_devices),
586+ end(stored_devices),
587+ [&optional_config,id](auto const& handle)
588+ {
589+ if (id == handle.unique_id())
590+ {
591+ optional_config = handle;
592+ return true;
593+ }
594+ return false;
595+ });
596+ stored_devices.erase(pos, end(stored_devices));
597+
598+ return optional_config;
599+}
600+
601+std::shared_ptr<mi::DefaultDevice>
602+mi::DefaultInputDeviceHub::restore_or_create_device(mi::InputDevice& device)
603+{
604+ auto device_config = get_stored_device_config(device.get_device_info().unique_id);
605+
606+ if (device_config.is_set())
607+ return std::make_shared<DefaultDevice>(
608+ device_config.value(),
609+ device_queue,
610+ device,
611+ key_mapper,
612+ [this](Device *d){device_changed(d);});
613+ else
614+ return std::make_shared<DefaultDevice>(
615+ create_new_device_id(),
616+ device_queue,
617+ device,
618+ key_mapper,
619+ [this](Device *d){device_changed(d);});
620+}
621
622=== modified file 'src/server/input/default_input_device_hub.h'
623--- src/server/input/default_input_device_hub.h 2017-03-14 02:26:28 +0000
624+++ src/server/input/default_input_device_hub.h 2017-03-14 20:52:46 +0000
625@@ -25,9 +25,11 @@
626 #include "mir/input/input_sink.h"
627 #include "mir/input/seat.h"
628 #include "mir/input/input_device_hub.h"
629+#include "mir/input/input_device_observer.h"
630 #include "mir/input/input_device_info.h"
631 #include "mir/input/mir_input_config.h"
632 #include "mir/thread_safe_list.h"
633+#include "mir/optional_value.h"
634
635 #include "mir_toolkit/event.h"
636
637@@ -57,13 +59,31 @@
638 class DefaultDevice;
639 class Seat;
640 class KeyMapper;
641-
642-class DefaultInputDeviceHub : public InputDeviceRegistry, public InputDeviceHub
643+class DefaultInputDeviceHub;
644+
645+struct ExternalInputDeviceHub : InputDeviceHub
646+{
647+ ExternalInputDeviceHub(std::shared_ptr<InputDeviceHub> const& actual_hub,
648+ std::shared_ptr<ServerActionQueue> const& observer_queue);
649+ ~ExternalInputDeviceHub();
650+
651+ void add_observer(std::shared_ptr<InputDeviceObserver> const&) override;
652+ void remove_observer(std::weak_ptr<InputDeviceObserver> const&) override;
653+ void for_each_input_device(std::function<void(Device const& device)> const& callback) override;
654+ void for_each_mutable_input_device(std::function<void(Device& device)> const& callback) override;
655+
656+private:
657+ struct Internal;
658+ std::shared_ptr<Internal> data;
659+};
660+
661+class DefaultInputDeviceHub :
662+ public InputDeviceRegistry,
663+ public InputDeviceHub
664 {
665 public:
666 DefaultInputDeviceHub(std::shared_ptr<Seat> const& seat,
667 std::shared_ptr<dispatch::MultiplexingDispatchable> const& input_multiplexer,
668- std::shared_ptr<ServerActionQueue> const& observer_queue,
669 std::shared_ptr<cookie::Authority> const& cookie_authority,
670 std::shared_ptr<KeyMapper> const& key_mapper,
671 std::shared_ptr<ServerStatusListener> const& server_status_listener);
672@@ -77,8 +97,6 @@
673 void remove_observer(std::weak_ptr<InputDeviceObserver> const&) override;
674 void for_each_input_device(std::function<void(Device const& device)> const& callback) override;
675 void for_each_mutable_input_device(std::function<void(Device& device)> const& callback) override;
676-
677-
678 private:
679 void update_spots();
680 void add_device_handle(std::shared_ptr<DefaultDevice> const& handle);
681@@ -86,10 +104,13 @@
682 void device_changed(Device* dev);
683 void emit_changed_devices();
684 MirInputDeviceId create_new_device_id();
685+ void store_device_config(DefaultDevice const& dev);
686+ std::shared_ptr<DefaultDevice> restore_or_create_device(InputDevice& dev);
687+ mir::optional_value<MirInputDevice> get_stored_device_config(std::string const& id);
688+
689 std::shared_ptr<Seat> const seat;
690 std::shared_ptr<dispatch::MultiplexingDispatchable> const input_dispatchable;
691- std::mutex handles_guard;
692- std::shared_ptr<ServerActionQueue> const observer_queue;
693+ std::mutex mutable handles_guard;
694 std::shared_ptr<dispatch::ActionQueue> const device_queue;
695 std::shared_ptr<cookie::Authority> const cookie_authority;
696 std::shared_ptr<KeyMapper> const key_mapper;
697@@ -130,6 +151,9 @@
698 std::mutex changed_devices_guard;
699 std::unique_ptr<std::vector<std::shared_ptr<Device>>> changed_devices;
700
701+ std::mutex stored_configurations_guard;
702+ std::vector<MirInputDevice> stored_devices;
703+
704 MirInputDeviceId device_id_generator;
705 bool ready{false};
706 };
707
708=== modified file 'tests/acceptance-tests/test_client_input.cpp'
709--- tests/acceptance-tests/test_client_input.cpp 2017-03-14 02:26:28 +0000
710+++ tests/acceptance-tests/test_client_input.cpp 2017-03-14 20:52:46 +0000
711@@ -343,12 +343,13 @@
712 devices_available.raise();
713 });
714
715- server.the_input_device_hub()->add_observer(counter);
716+ auto hub = server.the_input_device_hub();
717+ hub->add_observer(counter);
718
719 devices_available.wait_for(5s);
720 ASSERT_THAT(counter->count_devices, Eq(expected_number_of_input_devices));
721
722- server.the_input_device_hub()->remove_observer(counter);
723+ hub->remove_observer(counter);
724 }
725
726 MirInputDevice const* get_device_with_capabilities(MirInputConfig const* config, MirInputDeviceCapabilities caps)
727
728=== modified file 'tests/acceptance-tests/test_input_device_hub.cpp'
729--- tests/acceptance-tests/test_input_device_hub.cpp 2017-01-18 02:29:37 +0000
730+++ tests/acceptance-tests/test_input_device_hub.cpp 2017-03-14 20:52:46 +0000
731@@ -60,7 +60,8 @@
732 EXPECT_CALL(observer, changes_complete())
733 .WillOnce(mt::WakeUp(&observer_registered));
734
735- server.the_input_device_hub()->add_observer(mt::fake_shared(observer));
736+ auto device_hub = server.the_input_device_hub();
737+ device_hub->add_observer(mt::fake_shared(observer));
738 observer_registered.wait_for(std::chrono::seconds{4});
739 }
740
741@@ -75,7 +76,8 @@
742 EXPECT_CALL(observer, changes_complete())
743 .WillOnce(mt::WakeUp(&callbacks_received));
744
745- server.the_input_device_hub()->add_observer(mt::fake_shared(observer));
746+ auto device_hub = server.the_input_device_hub();
747+ device_hub->add_observer(mt::fake_shared(observer));
748 observer_registered.wait_for(std::chrono::seconds{4});
749
750 keep_on_living = mtf::add_fake_input_device(mi::InputDeviceInfo{"keyboard", "keyboard-uid", mir::input::DeviceCapability::keyboard});
751
752=== modified file 'tests/acceptance-tests/test_nested_input.cpp'
753--- tests/acceptance-tests/test_nested_input.cpp 2017-03-14 14:01:42 +0000
754+++ tests/acceptance-tests/test_nested_input.cpp 2017-03-14 20:52:46 +0000
755@@ -112,13 +112,12 @@
756
757 struct NestedInput : public mtf::HeadlessInProcessServer
758 {
759-
760 void SetUp()
761 {
762 initial_display_layout(display_geometry);
763 mtf::HeadlessInProcessServer::SetUp();
764 }
765-
766+
767 mtd::NestedMockEGL mock_egl;
768
769 std::unique_ptr<mtf::FakeInputDevice> fake_keyboard{
770@@ -301,13 +300,12 @@
771 NestedServerWithMockEventFilter nested_mir{new_connection()};
772 auto nested_hub = nested_mir.server.the_input_device_hub();
773
774- EXPECT_CALL(*mock_observer, device_added(_)).Times(1);
775- EXPECT_CALL(*mock_observer, changes_complete())
776- .Times(1)
777+ EXPECT_CALL(*mock_observer, device_added(_))
778 .WillOnce(mt::WakeUp(&input_device_changes_complete));
779
780 nested_hub->add_observer(mock_observer);
781 input_device_changes_complete.wait_for(10s);
782+ nested_hub->remove_observer(mock_observer);
783 }
784
785 TEST_F(NestedInput, device_added_on_host_triggeres_nested_device_observer)
786@@ -315,15 +313,18 @@
787 NestedServerWithMockEventFilter nested_mir{new_connection()};
788 auto nested_hub = nested_mir.server.the_input_device_hub();
789
790- EXPECT_CALL(*mock_observer, changes_complete()).Times(1)
791+ // wait until we see the keyboard from the fixture:
792+ EXPECT_CALL(*mock_observer, device_added(_)).Times(1)
793 .WillOnce(mt::WakeUp(&input_device_changes_complete));
794 nested_hub->add_observer(mock_observer);
795
796 input_device_changes_complete.wait_for(10s);
797 EXPECT_THAT(input_device_changes_complete.raised(), Eq(true));
798 input_device_changes_complete.reset();
799+ ::testing::Mock::VerifyAndClearExpectations(&mock_observer);
800
801- EXPECT_CALL(*mock_observer, changes_complete())
802+ // wait for the fake mouse
803+ EXPECT_CALL(*mock_observer, device_added(_)).Times(1)
804 .WillOnce(mt::WakeUp(&input_device_changes_complete));
805
806 auto mouse = mtf::add_fake_input_device(mi::InputDeviceInfo{"mouse", "mouse-uid" , mi::DeviceCapability::pointer});
807
808=== added file 'tests/include/mir/test/doubles/mock_device.h'
809--- tests/include/mir/test/doubles/mock_device.h 1970-01-01 00:00:00 +0000
810+++ tests/include/mir/test/doubles/mock_device.h 2017-03-14 20:52:46 +0000
811@@ -0,0 +1,71 @@
812+/*
813+ * Copyright © 2017 Canonical Ltd.
814+ *
815+ * This program is free software: you can redistribute it and/or modify
816+ * it under the terms of the GNU General Public License version 3 as
817+ * published by the Free Software Foundation.
818+ *
819+ * This program is distributed in the hope that it will be useful,
820+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
821+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
822+ * GNU General Public License for more details.
823+ *
824+ * You should have received a copy of the GNU General Public License
825+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
826+ *
827+ * Authored by: Andreas Pokorny <andreas.pokorny@canonical.com>
828+ */
829+
830+#ifndef MIR_TEST_DOUBLES_MOCK_DEVICE_H_
831+#define MIR_TEST_DOUBLES_MOCK_DEVICE_H_
832+
833+#include "mir/input/device.h"
834+#include "mir/input/device_capability.h"
835+#include "mir/input/mir_pointer_config.h"
836+#include "mir/input/mir_keyboard_config.h"
837+#include "mir/input/mir_touchpad_config.h"
838+#include "mir/input/mir_touchscreen_config.h"
839+#include "mir/optional_value.h"
840+
841+#include <string>
842+
843+namespace mir
844+{
845+namespace test
846+{
847+namespace doubles
848+{
849+
850+struct MockDevice : input::Device
851+{
852+ MockDevice(MirInputDeviceId id, input::DeviceCapabilities caps, std::string const& name, std::string const& unique_id)
853+ {
854+ ON_CALL(*this, id()).WillByDefault(testing::Return(id));
855+ ON_CALL(*this, name()).WillByDefault(testing::Return(name));
856+ ON_CALL(*this, unique_id()).WillByDefault(testing::Return(unique_id));
857+ ON_CALL(*this, capabilities()).WillByDefault(testing::Return(caps));
858+ ON_CALL(*this, pointer_configuration()).WillByDefault(testing::Return(MirPointerConfig{}));
859+ ON_CALL(*this, keyboard_configuration()).WillByDefault(testing::Return(MirKeyboardConfig{}));
860+ ON_CALL(*this, touchpad_configuration()).WillByDefault(testing::Return(MirTouchpadConfig{}));
861+ ON_CALL(*this, touchscreen_configuration()).WillByDefault(testing::Return(MirTouchscreenConfig{}));
862+ }
863+
864+ MOCK_CONST_METHOD0(id, MirInputDeviceId());
865+ MOCK_CONST_METHOD0(capabilities, input::DeviceCapabilities());
866+ MOCK_CONST_METHOD0(name, std::string());
867+ MOCK_CONST_METHOD0(unique_id, std::string());
868+ MOCK_CONST_METHOD0(pointer_configuration, optional_value<MirPointerConfig>());
869+ MOCK_CONST_METHOD0(touchpad_configuration, optional_value<MirTouchpadConfig>());
870+ MOCK_CONST_METHOD0(keyboard_configuration, optional_value<MirKeyboardConfig>());
871+ MOCK_CONST_METHOD0(touchscreen_configuration, optional_value<MirTouchscreenConfig>());
872+ MOCK_METHOD1(apply_pointer_configuration, void(MirPointerConfig const&));
873+ MOCK_METHOD1(apply_touchpad_configuration, void(MirTouchpadConfig const&));
874+ MOCK_METHOD1(apply_keyboard_configuration, void(MirKeyboardConfig const&));
875+ MOCK_METHOD1(apply_touchscreen_configuration, void(MirTouchscreenConfig const&));
876+};
877+}
878+}
879+}
880+
881+#endif
882+
883
884=== modified file 'tests/integration-tests/input/test_single_seat_setup.cpp'
885--- tests/integration-tests/input/test_single_seat_setup.cpp 2017-03-14 02:26:28 +0000
886+++ tests/integration-tests/input/test_single_seat_setup.cpp 2017-03-14 20:52:46 +0000
887@@ -51,6 +51,7 @@
888 #include "mir/input/input_device_info.h"
889 #include "mir/geometry/rectangles.h"
890 #include "mir/test/input_config_matchers.h"
891+#include "mir/test/fd_utils.h"
892
893 #include <gmock/gmock.h>
894 #include <gtest/gtest.h>
895@@ -131,9 +132,9 @@
896 mt::fake_shared(mock_cursor_listener), mt::fake_shared(display_config),
897 mt::fake_shared(key_mapper), mt::fake_shared(clock),
898 mt::fake_shared(mock_seat_observer)};
899- mi::DefaultInputDeviceHub hub{mt::fake_shared(seat), mt::fake_shared(multiplexer),
900- mt::fake_shared(observer_loop), cookie_authority,
901- mt::fake_shared(key_mapper), mt::fake_shared(mock_status_listener)};
902+ mi::DefaultInputDeviceHub hub{mt::fake_shared(seat), mt::fake_shared(multiplexer),
903+ cookie_authority, mt::fake_shared(key_mapper),
904+ mt::fake_shared(mock_status_listener)};
905 NiceMock<mtd::MockInputDeviceObserver> mock_observer;
906 mi::ConfigChanger changer{
907 mt::fake_shared(mock_input_manager),
908@@ -151,6 +152,21 @@
909
910 std::chrono::nanoseconds arbitrary_timestamp;
911
912+ void SetUp()
913+ {
914+ // execute registration of ConfigChanger
915+ expect_and_execute_multiplexer();
916+ }
917+
918+ void expect_and_execute_multiplexer(int count = 1)
919+ {
920+ for (int i = 0; i != count; ++i)
921+ {
922+ mt::fd_becomes_readable(multiplexer.watch_fd(), 5s);
923+ multiplexer.dispatch(mir::dispatch::FdEvent::readable);
924+ }
925+ }
926+
927 void capture_input_sink(NiceMock<mtd::MockInputDevice>& dev, mi::InputSink*& sink, mi::EventBuilder*& builder)
928 {
929 ON_CALL(dev,start(_,_))
930@@ -173,14 +189,13 @@
931
932 capture_input_sink(device, sink, builder);
933
934- EXPECT_CALL(mock_observer,device_added(_))
935+ EXPECT_CALL(mock_observer, device_added(_))
936 .WillOnce(SaveArg<0>(&handle));
937
938 hub.add_observer(mt::fake_shared(mock_observer));
939+ expect_and_execute_multiplexer(1);
940 hub.add_device(mt::fake_shared(device));
941
942- observer_loop.trigger_server_actions();
943-
944 auto event = builder->key_event(arbitrary_timestamp, mir_keyboard_action_down, 0,
945 KEY_A);
946
947@@ -198,8 +213,6 @@
948
949 hub.add_device(mt::fake_shared(device));
950
951- observer_loop.trigger_server_actions();
952-
953 auto touch_event_1 = builder->touch_event(
954 arbitrary_timestamp,
955 {{0, mir_touch_action_down, mir_touch_tooltype_finger, 21.0f, 34.0f, 50.0f, 15.0f, 5.0f, 4.0f}});
956@@ -245,7 +258,6 @@
957 capture_input_sink(device, sink, builder);
958
959 hub.add_device(mt::fake_shared(device));
960- observer_loop.trigger_server_actions();
961 sink->handle_input(
962 *builder->pointer_event(arbitrary_timestamp, mir_pointer_action_motion, 0, 0.0f, 0.0f, 10.0f, 10.0f));
963 sink->handle_input(
964@@ -265,7 +277,6 @@
965 mi::EventBuilder* builder;
966 capture_input_sink(device, sink, builder);
967 hub.add_device(mt::fake_shared(device));
968- observer_loop.trigger_server_actions();
969
970 sink->handle_input(
971 *builder->pointer_event(arbitrary_timestamp, mir_pointer_action_motion, 0, 0.0f, 0.0f, 10.0f, 20.0f));
972@@ -295,8 +306,8 @@
973 ON_CALL(mock_observer, device_added(_)).WillByDefault(SaveArg<0>(&dev));
974
975 hub.add_observer(mt::fake_shared(mock_observer));
976+ expect_and_execute_multiplexer();
977 hub.add_device(mt::fake_shared(touchpad));
978- observer_loop.trigger_server_actions();
979
980 EXPECT_CALL(touchpad, apply_settings(Matcher<mi::PointerSettings const&>(_)));
981
982@@ -311,8 +322,8 @@
983 ON_CALL(mock_observer, device_added(_)).WillByDefault(SaveArg<0>(&dev));
984
985 hub.add_observer(mt::fake_shared(mock_observer));
986+ expect_and_execute_multiplexer();
987 hub.add_device(mt::fake_shared(touchpad));
988- observer_loop.trigger_server_actions();
989
990 EXPECT_CALL(touchpad, apply_settings(Matcher<mi::TouchpadSettings const&>(_)));
991
992@@ -334,9 +345,9 @@
993 .WillOnce(SaveArg<0>(&key_handle));
994
995 hub.add_observer(mt::fake_shared(mock_observer));
996+ expect_and_execute_multiplexer();
997 hub.add_device(mt::fake_shared(another_device));
998
999- observer_loop.trigger_server_actions();
1000
1001 const MirInputEventModifiers shift_left = mir_input_event_modifier_shift_left | mir_input_event_modifier_shift;
1002 auto shift_down =
1003@@ -370,11 +381,10 @@
1004 .WillOnce(SaveArg<0>(&key_handle));
1005
1006 hub.add_observer(mt::fake_shared(mock_observer));
1007+ expect_and_execute_multiplexer();
1008 hub.add_device(mt::fake_shared(device));
1009 hub.add_device(mt::fake_shared(another_device));
1010
1011- observer_loop.trigger_server_actions();
1012-
1013 const MirInputEventModifiers r_alt_modifier = mir_input_event_modifier_alt_right | mir_input_event_modifier_alt;
1014 auto key =
1015 key_event_builder->key_event(arbitrary_timestamp, mir_keyboard_action_down, 0, KEY_RIGHTALT);
1016@@ -415,6 +425,7 @@
1017 .WillOnce(SaveArg<0>(&key_handle_2));
1018
1019 hub.add_observer(mt::fake_shared(mock_observer));
1020+ expect_and_execute_multiplexer();
1021 hub.add_device(mt::fake_shared(device));
1022 hub.add_device(mt::fake_shared(another_device));
1023 hub.add_device(mt::fake_shared(third_device));
1024@@ -423,8 +434,6 @@
1025 const MirInputEventModifiers l_ctrl_modifier = mir_input_event_modifier_ctrl_left | mir_input_event_modifier_ctrl;
1026 const MirInputEventModifiers combined_modifier = r_alt_modifier | l_ctrl_modifier;
1027
1028- observer_loop.trigger_server_actions();
1029-
1030 auto alt_down = key_event_builder_1->key_event(arbitrary_timestamp, mir_keyboard_action_down, 0, KEY_RIGHTALT);
1031 auto ctrl_down = key_event_builder_2->key_event(arbitrary_timestamp, mir_keyboard_action_down, 0, KEY_LEFTCTRL);
1032 auto ctrl_up = key_event_builder_2->key_event(arbitrary_timestamp, mir_keyboard_action_up, 0, KEY_LEFTCTRL);
1033@@ -462,8 +471,6 @@
1034 hub.add_device(mt::fake_shared(device));
1035 hub.add_device(mt::fake_shared(another_device));
1036
1037- observer_loop.trigger_server_actions();
1038-
1039 auto motion_1 =
1040 mouse_event_builder_1->pointer_event(arbitrary_timestamp, mir_pointer_action_motion, 0, 0, 0, 23, 20);
1041 auto motion_2 =
1042@@ -495,8 +502,6 @@
1043 hub.add_device(mt::fake_shared(device));
1044 hub.add_device(mt::fake_shared(another_device));
1045
1046- observer_loop.trigger_server_actions();
1047-
1048 auto motion_1 =
1049 mouse_event_builder_1->pointer_event(arbitrary_timestamp, mir_pointer_action_button_down, mir_pointer_button_primary, 0, 0, 0, 0);
1050 auto motion_2 =
1051@@ -525,7 +530,6 @@
1052
1053 EXPECT_CALL(session, send_input_config(UnorderedElementsAre(DeviceMatches(device.get_device_info()))));
1054 hub.add_device(mt::fake_shared(device));
1055- observer_loop.trigger_server_actions();
1056 }
1057
1058 TEST_F(SingleSeatInputDeviceHubSetup, input_device_changes_sent_to_session_multiple_devices)
1059@@ -534,25 +538,21 @@
1060 stub_session_container.insert_session(mt::fake_shared(session));
1061
1062 hub.add_device(mt::fake_shared(device));
1063- observer_loop.trigger_server_actions();
1064
1065 EXPECT_CALL(session,
1066 send_input_config(UnorderedElementsAre(DeviceMatches(device.get_device_info()),
1067 DeviceMatches(another_device.get_device_info()))));
1068 hub.add_device(mt::fake_shared(another_device));
1069- observer_loop.trigger_server_actions();
1070 }
1071
1072 TEST_F(SingleSeatInputDeviceHubSetup, input_device_changes_sent_to_sink_removal)
1073 {
1074 hub.add_device(mt::fake_shared(device));
1075 hub.add_device(mt::fake_shared(another_device));
1076- observer_loop.trigger_server_actions();
1077
1078 NiceMock<mtd::MockSceneSession> session;
1079 stub_session_container.insert_session(mt::fake_shared(session));
1080 EXPECT_CALL(session,
1081 send_input_config(UnorderedElementsAre(DeviceMatches(another_device.get_device_info()))));
1082 hub.remove_device(mt::fake_shared(device));
1083- observer_loop.trigger_server_actions();
1084 }
1085
1086=== modified file 'tests/unit-tests/input/CMakeLists.txt'
1087--- tests/unit-tests/input/CMakeLists.txt 2017-03-13 08:12:52 +0000
1088+++ tests/unit-tests/input/CMakeLists.txt 2017-03-14 20:52:46 +0000
1089@@ -8,6 +8,7 @@
1090 ${CMAKE_CURRENT_SOURCE_DIR}/test_input_event.cpp
1091 ${CMAKE_CURRENT_SOURCE_DIR}/test_config_changer.cpp
1092 ${CMAKE_CURRENT_SOURCE_DIR}/test_event_builders.cpp
1093+ ${CMAKE_CURRENT_SOURCE_DIR}/test_external_input_device_hub.cpp
1094 ${CMAKE_CURRENT_SOURCE_DIR}/test_default_device.cpp
1095 ${CMAKE_CURRENT_SOURCE_DIR}/test_default_input_device_hub.cpp
1096 ${CMAKE_CURRENT_SOURCE_DIR}/test_default_input_manager.cpp
1097
1098=== modified file 'tests/unit-tests/input/test_config_changer.cpp'
1099--- tests/unit-tests/input/test_config_changer.cpp 2017-02-28 08:53:57 +0000
1100+++ tests/unit-tests/input/test_config_changer.cpp 2017-03-14 20:52:46 +0000
1101@@ -29,6 +29,7 @@
1102
1103 #include "mir/test/doubles/mock_input_device_hub.h"
1104 #include "mir/test/doubles/mock_input_manager.h"
1105+#include "mir/test/doubles/mock_device.h"
1106 #include "mir/test/doubles/mock_scene_session.h"
1107 #include "mir/test/doubles/stub_session.h"
1108 #include "mir/test/doubles/stub_session_container.h"
1109@@ -47,34 +48,6 @@
1110 namespace
1111 {
1112
1113-struct MockDevice : mir::input::Device
1114-{
1115- MockDevice(MirInputDeviceId id, mi::DeviceCapabilities caps, std::string const& name, std::string const& unique_id)
1116- {
1117- ON_CALL(*this, id()).WillByDefault(Return(id));
1118- ON_CALL(*this, name()).WillByDefault(Return(name));
1119- ON_CALL(*this, unique_id()).WillByDefault(Return(unique_id));
1120- ON_CALL(*this, capabilities()).WillByDefault(Return(caps));
1121- ON_CALL(*this, pointer_configuration()).WillByDefault(Return(MirPointerConfig{}));
1122- ON_CALL(*this, keyboard_configuration()).WillByDefault(Return(MirKeyboardConfig{}));
1123- ON_CALL(*this, touchpad_configuration()).WillByDefault(Return(MirTouchpadConfig{}));
1124- ON_CALL(*this, touchscreen_configuration()).WillByDefault(Return(MirTouchscreenConfig{}));
1125- }
1126-
1127- MOCK_CONST_METHOD0(id, MirInputDeviceId());
1128- MOCK_CONST_METHOD0(capabilities, mir::input::DeviceCapabilities());
1129- MOCK_CONST_METHOD0(name, std::string());
1130- MOCK_CONST_METHOD0(unique_id, std::string());
1131- MOCK_CONST_METHOD0(pointer_configuration, mir::optional_value<MirPointerConfig>());
1132- MOCK_CONST_METHOD0(touchpad_configuration, mir::optional_value<MirTouchpadConfig>());
1133- MOCK_CONST_METHOD0(keyboard_configuration, mir::optional_value<MirKeyboardConfig>());
1134- MOCK_CONST_METHOD0(touchscreen_configuration, mir::optional_value<MirTouchscreenConfig>());
1135- MOCK_METHOD1(apply_pointer_configuration, void(MirPointerConfig const&));
1136- MOCK_METHOD1(apply_touchpad_configuration, void(MirTouchpadConfig const&));
1137- MOCK_METHOD1(apply_keyboard_configuration, void(MirKeyboardConfig const&));
1138- MOCK_METHOD1(apply_touchscreen_configuration, void(MirTouchscreenConfig const&));
1139-};
1140-
1141 struct FakeInputDeviceHub : mir::input::InputDeviceHub
1142 {
1143 std::shared_ptr<mi::InputDeviceObserver> observer;
1144@@ -86,8 +59,8 @@
1145 mi::DeviceCapability::keyboard | mi::DeviceCapability::alpha_numeric |
1146 mi::DeviceCapability::touchscreen;
1147
1148- NiceMock<MockDevice> first_device{first_id, caps, first, first};
1149- NiceMock<MockDevice> second_device{second_id, caps, second, second};
1150+ NiceMock<mtd::MockDevice> first_device{first_id, caps, first, first};
1151+ NiceMock<mtd::MockDevice> second_device{second_id, caps, second, second};
1152 std::vector<std::shared_ptr<mir::input::Device>> active_devices;
1153
1154 void add_observer(std::shared_ptr<mi::InputDeviceObserver> const& obs) override
1155
1156=== modified file 'tests/unit-tests/input/test_default_device.cpp'
1157--- tests/unit-tests/input/test_default_device.cpp 2017-02-28 08:53:57 +0000
1158+++ tests/unit-tests/input/test_default_device.cpp 2017-03-14 20:52:46 +0000
1159@@ -18,6 +18,7 @@
1160
1161 #include "src/server/input/default_device.h"
1162 #include "mir/input/input_device.h"
1163+#include "mir/input/mir_input_config.h"
1164 #include "mir/input/mir_touchpad_config.h"
1165 #include "mir/input/mir_pointer_config.h"
1166 #include "mir/input/mir_touchscreen_config.h"
1167@@ -57,6 +58,7 @@
1168 NiceMock<MockInputDevice> touchscreen;
1169 NiceMock<mtd::MockKeyMapper> key_mapper;
1170 std::shared_ptr<md::ActionQueue> queue{std::make_shared<md::ActionQueue>()};
1171+ std::function<void(mi::Device*)> const change_callback{[](mi::Device*){}};
1172
1173 DefaultDevice()
1174 {
1175@@ -181,3 +183,128 @@
1176
1177 queue->dispatch(md::FdEvent::readable);
1178 }
1179+
1180+TEST_F(DefaultDevice, touchpad_device_can_be_constructed_from_input_config)
1181+{
1182+ MirTouchpadConfig const tpd_conf{mir_touchpad_click_mode_finger_count, mir_touchpad_scroll_mode_edge_scroll, 0, false, true, true, true};
1183+ MirPointerConfig const ptr_conf{mir_pointer_handedness_right, mir_pointer_acceleration_adaptive, 0.5, -1.0, 1.0};
1184+ MirInputDevice conf(MirInputDeviceId{17}, mi::DeviceCapability::touchpad|mi::DeviceCapability::pointer, "touchpad", "toouchpad-event7");
1185+ conf.set_touchpad_config(tpd_conf);
1186+ conf.set_pointer_config(ptr_conf);
1187+ mi::DefaultDevice dev(conf, queue, touchpad, mt::fake_shared(key_mapper), change_callback);
1188+
1189+ EXPECT_EQ(tpd_conf, dev.touchpad_configuration().value());
1190+ EXPECT_EQ(ptr_conf, dev.pointer_configuration().value());
1191+}
1192+
1193+TEST_F(DefaultDevice, pointer_device_can_be_constructed_from_input_config)
1194+{
1195+ MirPointerConfig const ptr_config{mir_pointer_handedness_left, mir_pointer_acceleration_none, 0, 1.0, 1.0};
1196+ MirInputDevice conf(MirInputDeviceId{11}, mi::DeviceCapability::pointer, "pointer", "pointer-event7");
1197+ conf.set_pointer_config(ptr_config);
1198+ mi::DefaultDevice dev(conf, queue, mouse, mt::fake_shared(key_mapper), change_callback);
1199+
1200+ EXPECT_EQ(ptr_config, dev.pointer_configuration().value());
1201+}
1202+
1203+TEST_F(DefaultDevice, keyboard_device_can_be_constructed_from_input_config)
1204+{
1205+ MirKeyboardConfig const kbd_config{mi::Keymap{"pc104", "dvorak", "", ""}};
1206+ MirInputDevice conf(MirInputDeviceId{3}, mi::DeviceCapability::keyboard, "keyboard", "keyboard-event3c");
1207+ conf.set_keyboard_config(kbd_config);
1208+ mi::DefaultDevice dev(conf, queue, keyboard, mt::fake_shared(key_mapper), change_callback);
1209+
1210+ EXPECT_EQ(kbd_config, dev.keyboard_configuration().value());
1211+}
1212+
1213+TEST_F(DefaultDevice, touchscreen_device_can_be_constructed_from_input_config)
1214+{
1215+ MirTouchscreenConfig const ts_config{0, mir_touchscreen_mapping_mode_to_display_wall};
1216+ MirInputDevice conf(MirInputDeviceId{5}, mi::DeviceCapability::touchscreen, "ts", "ts-event7");
1217+ conf.set_touchscreen_config(ts_config);
1218+
1219+ mi::DefaultDevice dev(conf, queue, touchscreen, mt::fake_shared(key_mapper), change_callback);
1220+
1221+ EXPECT_EQ(ts_config, dev.touchscreen_configuration().value());
1222+}
1223+
1224+TEST_F(DefaultDevice, device_config_can_be_querried_from_touchpad)
1225+{
1226+ MirInputDeviceId const device_id{17};
1227+ mi::DefaultDevice dev(device_id, queue, touchpad, mt::fake_shared(key_mapper), change_callback);
1228+
1229+ auto dev_info = touchpad.get_device_info();
1230+ MirInputDevice conf(device_id, dev_info.capabilities, dev_info.name, dev_info.unique_id);
1231+ MirTouchpadConfig const tpd_conf{
1232+ mir_touchpad_click_mode_finger_count,
1233+ mir_touchpad_scroll_mode_edge_scroll,
1234+ 0,
1235+ false,
1236+ true,
1237+ true,
1238+ true};
1239+ MirPointerConfig const ptr_conf{
1240+ mir_pointer_handedness_right,
1241+ mir_pointer_acceleration_adaptive,
1242+ 0.5,
1243+ -1.0,
1244+ 1.0};
1245+
1246+ conf.set_touchpad_config(tpd_conf);
1247+ conf.set_pointer_config(ptr_conf);
1248+
1249+ dev.apply_touchpad_configuration(tpd_conf);
1250+ dev.apply_pointer_configuration(ptr_conf);
1251+
1252+ EXPECT_EQ(conf, dev.config());
1253+}
1254+
1255+TEST_F(DefaultDevice, device_config_can_be_querried_from_pointer_device)
1256+{
1257+ MirInputDeviceId const device_id{11};
1258+ mi::DefaultDevice dev(device_id, queue, mouse, mt::fake_shared(key_mapper), change_callback);
1259+
1260+ auto dev_info = mouse.get_device_info();
1261+ MirInputDevice conf(device_id, dev_info.capabilities, dev_info.name, dev_info.unique_id);
1262+ MirPointerConfig const ptr_config{
1263+ mir_pointer_handedness_left,
1264+ mir_pointer_acceleration_none,
1265+ 0,
1266+ 1.0,
1267+ 1.0};
1268+
1269+ conf.set_pointer_config(ptr_config);
1270+ dev.apply_pointer_configuration(ptr_config);
1271+
1272+ EXPECT_EQ(conf, dev.config());
1273+}
1274+
1275+TEST_F(DefaultDevice, device_config_can_be_querried_from_keyboard_device)
1276+{
1277+ MirInputDeviceId const device_id{3};
1278+ mi::DefaultDevice dev(device_id, queue, keyboard, mt::fake_shared(key_mapper), change_callback);
1279+
1280+ auto dev_info = keyboard.get_device_info();
1281+ MirInputDevice conf(device_id, dev_info.capabilities, dev_info.name, dev_info.unique_id);
1282+ MirKeyboardConfig const kbd_config{mi::Keymap{"pc104", "dvorak", "", ""}};
1283+
1284+ conf.set_keyboard_config(kbd_config);
1285+ dev.apply_keyboard_configuration(kbd_config);
1286+
1287+ EXPECT_EQ(kbd_config, dev.keyboard_configuration().value());
1288+}
1289+
1290+TEST_F(DefaultDevice, device_config_can_be_querried_from_touchscreen)
1291+{
1292+ MirInputDeviceId const device_id{5};
1293+ mi::DefaultDevice dev(device_id, queue, touchscreen, mt::fake_shared(key_mapper), change_callback);
1294+
1295+ auto dev_info = touchscreen.get_device_info();
1296+ MirInputDevice conf(device_id, dev_info.capabilities, dev_info.name, dev_info.unique_id);
1297+ MirTouchscreenConfig const ts_config{0, mir_touchscreen_mapping_mode_to_display_wall};
1298+
1299+ conf.set_touchscreen_config(ts_config);
1300+ dev.apply_touchscreen_configuration(ts_config);
1301+
1302+ EXPECT_EQ(ts_config, dev.touchscreen_configuration().value());
1303+}
1304
1305=== modified file 'tests/unit-tests/input/test_default_input_device_hub.cpp'
1306--- tests/unit-tests/input/test_default_input_device_hub.cpp 2017-03-13 08:12:52 +0000
1307+++ tests/unit-tests/input/test_default_input_device_hub.cpp 2017-03-14 20:52:46 +0000
1308@@ -30,6 +30,7 @@
1309 #include "mir/test/doubles/triggered_main_loop.h"
1310 #include "mir/test/event_matchers.h"
1311 #include "mir/test/fake_shared.h"
1312+#include "mir/test/fd_utils.h"
1313
1314 #include "mir/dispatch/action_queue.h"
1315 #include "mir/geometry/rectangles.h"
1316@@ -77,14 +78,14 @@
1317
1318 struct InputDeviceHubTest : ::testing::Test
1319 {
1320- mtd::TriggeredMainLoop observer_loop;
1321 std::shared_ptr<mir::cookie::Authority> cookie_authority = mir::cookie::Authority::create();
1322 mir::dispatch::MultiplexingDispatchable multiplexer;
1323 NiceMock<mtd::MockInputSeat> mock_seat;
1324 NiceMock<mtd::MockKeyMapper> mock_key_mapper;
1325 NiceMock<mtd::MockServerStatusListener> mock_server_status_listener;
1326 mi::DefaultInputDeviceHub hub{mt::fake_shared(mock_seat), mt::fake_shared(multiplexer),
1327- mt::fake_shared(observer_loop), cookie_authority, mt::fake_shared(mock_key_mapper), mt::fake_shared(mock_server_status_listener)};
1328+ cookie_authority, mt::fake_shared(mock_key_mapper),
1329+ mt::fake_shared(mock_server_status_listener)};
1330 NiceMock<mtd::MockInputDeviceObserver> mock_observer;
1331 NiceMock<mtd::MockInputDevice> device{"device","dev-1", mi::DeviceCapability::unknown};
1332 NiceMock<mtd::MockInputDevice> another_device{"another_device","dev-2", mi::DeviceCapability::keyboard};
1333@@ -104,6 +105,12 @@
1334 }
1335 ));
1336 }
1337+
1338+ void expect_and_execute_multiplexer()
1339+ {
1340+ mt::fd_becomes_readable(multiplexer.watch_fd(), 2s);
1341+ multiplexer.dispatch(mir::dispatch::FdEvent::readable);
1342+ }
1343 };
1344
1345 TEST_F(InputDeviceHubTest, input_device_hub_starts_device)
1346@@ -161,8 +168,7 @@
1347 hub.add_device(mt::fake_shared(another_device));
1348 hub.add_observer(mt::fake_shared(mock_observer));
1349
1350- observer_loop.trigger_server_actions();
1351-
1352+ expect_and_execute_multiplexer();
1353 EXPECT_THAT(handle_1,Ne(handle_2));
1354 EXPECT_THAT(handle_1->unique_id(),Ne(handle_2->unique_id()));
1355 }
1356@@ -208,10 +214,13 @@
1357 EXPECT_CALL(mock_observer, changes_complete());
1358
1359 hub.add_observer(mt::fake_shared(mock_observer));
1360+ expect_and_execute_multiplexer();
1361+
1362 hub.add_device(mt::fake_shared(device));
1363+ expect_and_execute_multiplexer();
1364+
1365 hub.remove_device(mt::fake_shared(device));
1366-
1367- observer_loop.trigger_server_actions();
1368+ expect_and_execute_multiplexer();
1369 }
1370
1371 TEST_F(InputDeviceHubTest, emit_ready_to_receive_input_after_first_device_added)
1372@@ -219,8 +228,6 @@
1373 EXPECT_CALL(mock_server_status_listener, ready_for_user_input()).Times(1);
1374 hub.add_device(mt::fake_shared(device));
1375 hub.add_device(mt::fake_shared(another_device));
1376-
1377- observer_loop.trigger_server_actions();
1378 }
1379
1380 TEST_F(InputDeviceHubTest, emit_stop_receiving_input_after_last_device_added)
1381@@ -231,7 +238,6 @@
1382
1383 hub.remove_device(mt::fake_shared(device));
1384 hub.remove_device(mt::fake_shared(another_device));
1385- observer_loop.trigger_server_actions();
1386 }
1387
1388 TEST_F(InputDeviceHubTest, when_pointer_configuration_is_applied_successfully_observer_is_triggerd)
1389@@ -243,13 +249,13 @@
1390
1391 hub.add_device(mt::fake_shared(mouse));
1392 hub.add_observer(mt::fake_shared(mock_observer));
1393- observer_loop.trigger_server_actions();
1394+ expect_and_execute_multiplexer();
1395
1396 EXPECT_CALL(mock_observer, device_changed(WithName("mouse")));
1397 EXPECT_CALL(mock_observer, changes_complete());
1398
1399 dev_ptr->apply_pointer_configuration(pointer_conf);
1400- observer_loop.trigger_server_actions();
1401+ expect_and_execute_multiplexer();
1402 }
1403
1404 TEST_F(InputDeviceHubTest, when_tpd_configuration_is_applied_successfully_observer_is_triggerd)
1405@@ -261,13 +267,13 @@
1406
1407 hub.add_device(mt::fake_shared(touchpad));
1408 hub.add_observer(mt::fake_shared(mock_observer));
1409- observer_loop.trigger_server_actions();
1410+ expect_and_execute_multiplexer();
1411
1412 EXPECT_CALL(mock_observer, device_changed(WithName("tpd")));
1413 EXPECT_CALL(mock_observer, changes_complete());
1414
1415 dev_ptr->apply_touchpad_configuration(tpd_conf);
1416- observer_loop.trigger_server_actions();
1417+ expect_and_execute_multiplexer();
1418 }
1419
1420 TEST_F(InputDeviceHubTest, when_configuration_attempt_fails_observer_is_not_triggerd)
1421@@ -279,12 +285,58 @@
1422
1423 hub.add_device(mt::fake_shared(mouse));
1424 hub.add_observer(mt::fake_shared(mock_observer));
1425- observer_loop.trigger_server_actions();
1426+ expect_and_execute_multiplexer();
1427
1428 EXPECT_CALL(mock_observer, device_changed(WithName("mouse"))).Times(0);
1429 EXPECT_CALL(mock_observer, changes_complete()).Times(0);
1430
1431 try {dev_ptr->apply_touchpad_configuration(tpd_conf); } catch (...) {}
1432- observer_loop.trigger_server_actions();
1433+ expect_and_execute_multiplexer();
1434 ::testing::Mock::VerifyAndClearExpectations(&mock_observer);
1435 }
1436+
1437+TEST_F(InputDeviceHubTest, restores_device_id_when_device_reappears)
1438+{
1439+ std::shared_ptr<mi::Device> dev_ptr;
1440+
1441+ ON_CALL(mock_observer, device_added(WithName("mouse"))).WillByDefault(SaveArg<0>(&dev_ptr));
1442+
1443+ hub.add_device(mt::fake_shared(mouse));
1444+ hub.add_observer(mt::fake_shared(mock_observer));
1445+ expect_and_execute_multiplexer();
1446+
1447+ auto device_id = dev_ptr->id();
1448+
1449+ hub.remove_device(mt::fake_shared(mouse));
1450+ dev_ptr.reset();
1451+ hub.add_device(mt::fake_shared(mouse));
1452+
1453+ ASSERT_THAT(dev_ptr, Ne(nullptr));
1454+
1455+ EXPECT_THAT(dev_ptr->id(), Eq(device_id));
1456+}
1457+
1458+TEST_F(InputDeviceHubTest, restores_configuration_when_device_reappears)
1459+{
1460+ std::shared_ptr<mi::Device> dev_ptr;
1461+ MirPointerConfig ptr_config;
1462+ ptr_config.handedness(mir_pointer_handedness_left);
1463+ ptr_config.acceleration(mir_pointer_acceleration_adaptive);
1464+ ptr_config.cursor_acceleration_bias(0.6);
1465+
1466+ ON_CALL(mock_observer, device_added(WithName("mouse"))).WillByDefault(SaveArg<0>(&dev_ptr));
1467+
1468+ hub.add_device(mt::fake_shared(mouse));
1469+ hub.add_observer(mt::fake_shared(mock_observer));
1470+ expect_and_execute_multiplexer();
1471+
1472+ dev_ptr->apply_pointer_configuration(ptr_config);
1473+
1474+ hub.remove_device(mt::fake_shared(mouse));
1475+ dev_ptr.reset();
1476+ hub.add_device(mt::fake_shared(mouse));
1477+
1478+ ASSERT_THAT(dev_ptr, Ne(nullptr));
1479+
1480+ EXPECT_THAT(dev_ptr->pointer_configuration().value(), Eq(ptr_config));
1481+}
1482
1483=== added file 'tests/unit-tests/input/test_external_input_device_hub.cpp'
1484--- tests/unit-tests/input/test_external_input_device_hub.cpp 1970-01-01 00:00:00 +0000
1485+++ tests/unit-tests/input/test_external_input_device_hub.cpp 2017-03-14 20:52:46 +0000
1486@@ -0,0 +1,119 @@
1487+/*
1488+ * Copyright © 2017 Canonical Ltd.
1489+ *
1490+ * This program is free software: you can redistribute it and/or modify
1491+ * it under the terms of the GNU General Public License version 3 as
1492+ * published by the Free Software Foundation.
1493+ *
1494+ * This program is distributed in the hope that it will be useful,
1495+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
1496+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
1497+ * GNU General Public License for more details.
1498+ *
1499+ * You should have received a copy of the GNU General Public License
1500+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
1501+ *
1502+ * Authored by: Andreas Pokorny <andreas.pokorny@canonical.com>
1503+ */
1504+
1505+#include "src/server/input/default_input_device_hub.h"
1506+#include "mir/test/doubles/mock_input_device_hub.h"
1507+#include "mir/test/doubles/mock_device.h"
1508+#include "mir/test/doubles/mock_input_device_observer.h"
1509+#include "mir/test/doubles/triggered_main_loop.h"
1510+#include "mir/test/fake_shared.h"
1511+
1512+namespace mt = mir::test;
1513+namespace mtd = mt::doubles;
1514+namespace mi = mir::input;
1515+using namespace testing;
1516+
1517+namespace
1518+{
1519+
1520+struct WrappedHub : mtd::MockInputDeviceHub
1521+{
1522+ std::shared_ptr<mi::InputDeviceObserver> external_hub;
1523+ void add_observer(std::shared_ptr<mi::InputDeviceObserver> const& observer)
1524+ {
1525+ external_hub = observer;
1526+ }
1527+};
1528+
1529+struct ExternalInputDeviceHub : ::testing::Test
1530+{
1531+ mtd::TriggeredMainLoop server_actions;
1532+ std::shared_ptr<WrappedHub> wrapped_hub{std::make_shared<WrappedHub>()};
1533+ mi::ExternalInputDeviceHub hub{wrapped_hub, mt::fake_shared(server_actions)};
1534+ NiceMock<mtd::MockInputDeviceObserver> mock_observer;
1535+
1536+ NiceMock<mtd::MockDevice> device{1, mi::DeviceCapability::unknown, "name", "name-id-1"};
1537+ NiceMock<mtd::MockDevice> another_device{2, mi::DeviceCapability::keyboard, "keyboard", "keyboard-id-2"};
1538+
1539+ void submit_device_to_hub(std::shared_ptr<mi::Device> const& dev)
1540+ {
1541+ wrapped_hub->external_hub->device_added(dev);
1542+ wrapped_hub->external_hub->changes_complete();
1543+ }
1544+
1545+ void remove_device_to_hub(std::shared_ptr<mi::Device> const& dev)
1546+ {
1547+ wrapped_hub->external_hub->device_removed(dev);
1548+ wrapped_hub->external_hub->changes_complete();
1549+ }
1550+};
1551+}
1552+
1553+TEST_F(ExternalInputDeviceHub, is_observer_to_wrapped_hub)
1554+{
1555+ EXPECT_THAT(wrapped_hub->external_hub, Ne(nullptr));
1556+}
1557+
1558+TEST_F(ExternalInputDeviceHub, new_observer_is_called_through_server_action)
1559+{
1560+ EXPECT_CALL(mock_observer, changes_complete()).Times(1);
1561+ hub.add_observer(mt::fake_shared(mock_observer));
1562+ server_actions.trigger_server_actions();
1563+}
1564+
1565+TEST_F(ExternalInputDeviceHub, informs_observer_about_existing_devices)
1566+{
1567+ submit_device_to_hub(mt::fake_shared(device));
1568+ submit_device_to_hub(mt::fake_shared(another_device));
1569+
1570+ EXPECT_CALL(mock_observer, device_added(_)).Times(2);
1571+ EXPECT_CALL(mock_observer, changes_complete()).Times(1);
1572+ hub.add_observer(mt::fake_shared(mock_observer));
1573+ server_actions.trigger_server_actions();
1574+}
1575+
1576+TEST_F(ExternalInputDeviceHub, informs_observer_about_added_devices)
1577+{
1578+ InSequence seq;
1579+
1580+ EXPECT_CALL(mock_observer, changes_complete());
1581+ EXPECT_CALL(mock_observer, device_added(_));
1582+ EXPECT_CALL(mock_observer, changes_complete());
1583+ hub.add_observer(mt::fake_shared(mock_observer));
1584+ server_actions.trigger_server_actions();
1585+
1586+ submit_device_to_hub(mt::fake_shared(device));
1587+
1588+ server_actions.trigger_server_actions();
1589+}
1590+
1591+TEST_F(ExternalInputDeviceHub, triggers_observer_on_removed_devices)
1592+{
1593+ submit_device_to_hub(mt::fake_shared(device));
1594+
1595+ EXPECT_CALL(mock_observer, device_added(_)).Times(1);
1596+ EXPECT_CALL(mock_observer, changes_complete()).Times(1);
1597+ hub.add_observer(mt::fake_shared(mock_observer));
1598+ server_actions.trigger_server_actions();
1599+
1600+ EXPECT_CALL(mock_observer, device_removed(_)).Times(1);
1601+ EXPECT_CALL(mock_observer, changes_complete()).Times(1);
1602+
1603+ remove_device_to_hub(mt::fake_shared(device));
1604+ server_actions.trigger_server_actions();
1605+}

Subscribers

People subscribed via source and target branches