Mir

Merge lp:~andreas-pokorny/mir/libinput-platform into lp:mir

Proposed by Andreas Pokorny
Status: Merged
Approved by: Andreas Pokorny
Approved revision: no longer in the source branch.
Merged at revision: 2956
Proposed branch: lp:~andreas-pokorny/mir/libinput-platform
Merge into: lp:mir
Prerequisite: lp:~andreas-pokorny/mir/remove-dispatchable-from-input-device
Diff against target: 2503 lines (+2279/-9)
25 files modified
CMakeLists.txt (+1/-0)
debian/control (+1/-0)
include/test/mir/test/event_matchers.h (+45/-0)
src/CMakeLists.txt (+1/-0)
src/platforms/CMakeLists.txt (+2/-0)
src/platforms/evdev/CMakeLists.txt (+29/-0)
src/platforms/evdev/input_modifier_utils.cpp (+8/-5)
src/platforms/evdev/input_modifier_utils.h (+1/-1)
src/platforms/evdev/libinput_device.cpp (+347/-0)
src/platforms/evdev/libinput_device.h (+90/-0)
src/platforms/evdev/libinput_device_ptr.cpp (+30/-0)
src/platforms/evdev/libinput_device_ptr.h (+40/-0)
src/platforms/evdev/libinput_ptr.cpp (+47/-0)
src/platforms/evdev/libinput_ptr.h (+42/-0)
src/platforms/evdev/platform.cpp (+238/-0)
src/platforms/evdev/platform.h (+94/-0)
src/platforms/evdev/platform_factory.cpp (+68/-0)
tests/include/mir/test/doubles/mock_libinput.h (+110/-0)
tests/mir_test_doubles/CMakeLists.txt (+1/-0)
tests/mir_test_doubles/mock_libinput.cpp (+316/-0)
tests/mir_test_framework/CMakeLists.txt (+1/-1)
tests/mir_test_framework/fake_input_device_impl.cpp (+2/-2)
tests/unit-tests/input/evdev/CMakeLists.txt (+3/-0)
tests/unit-tests/input/evdev/test_evdev_input_platform.cpp (+264/-0)
tests/unit-tests/input/evdev/test_libinput_device.cpp (+498/-0)
To merge this branch: bzr merge lp:~andreas-pokorny/mir/libinput-platform
Reviewer Review Type Date Requested Status
PS Jenkins bot (community) continuous-integration Approve
Alexandros Frantzis (community) Approve
Kevin DuBois (community) Approve
Alan Griffiths Approve
Cemil Azizoglu (community) Needs Fixing
Review via email: mp+270313@code.launchpad.net

This proposal supersedes a proposal from 2015-08-28.

Commit message

Add input-evdev.so based on libinput

This adds the evdev platform that shall replace InputReader, EventHub and the InputMappers. The platform is not yet selected by default. Code to probe and select the right platform is still missing for the input side.

Description of the change

This adds the libinput platform as input-evdev.so

It does not yet use the extended libinput_touch_event_get_{major,minor,orientation,pressure} API which was not yet accepted upstream. Additionally it does not yet exist yet as a driver package.

You can test that in the build dir with --platform-input-lib=../lib/server-modules/input-evdev.so

To post a comment you must log in.
Revision history for this message
Andreas Pokorny (andreas-pokorny) wrote : Posted in a previous version of this proposal

ok i just saw two more things I can split out..

Revision history for this message
PS Jenkins bot (ps-jenkins) wrote : Posted in a previous version of this proposal
review: Needs Fixing (continuous-integration)
Revision history for this message
PS Jenkins bot (ps-jenkins) wrote : Posted in a previous version of this proposal
review: Needs Fixing (continuous-integration)
Revision history for this message
Andreas Pokorny (andreas-pokorny) wrote : Posted in a previous version of this proposal

This MP needs a newer libinput in vivid+overlay. Silo for that is on the way.

Revision history for this message
PS Jenkins bot (ps-jenkins) wrote : Posted in a previous version of this proposal
review: Needs Fixing (continuous-integration)
Revision history for this message
PS Jenkins bot (ps-jenkins) wrote : Posted in a previous version of this proposal
review: Needs Fixing (continuous-integration)
Revision history for this message
PS Jenkins bot (ps-jenkins) wrote : Posted in a previous version of this proposal
review: Needs Fixing (continuous-integration)
Revision history for this message
PS Jenkins bot (ps-jenkins) wrote : Posted in a previous version of this proposal
review: Needs Fixing (continuous-integration)
Revision history for this message
PS Jenkins bot (ps-jenkins) wrote : Posted in a previous version of this proposal
review: Needs Fixing (continuous-integration)
Revision history for this message
PS Jenkins bot (ps-jenkins) wrote : Posted in a previous version of this proposal
review: Needs Fixing (continuous-integration)
Revision history for this message
PS Jenkins bot (ps-jenkins) wrote : Posted in a previous version of this proposal
review: Needs Fixing (continuous-integration)
Revision history for this message
Alan Griffiths (alan-griffiths) wrote : Posted in a previous version of this proposal

Text conflict in include/common/mir/log.h
1 conflicts encountered

review: Needs Fixing
Revision history for this message
PS Jenkins bot (ps-jenkins) wrote :
review: Needs Fixing (continuous-integration)
Revision history for this message
PS Jenkins bot (ps-jenkins) wrote : Posted in a previous version of this proposal
review: Needs Fixing (continuous-integration)
Revision history for this message
PS Jenkins bot (ps-jenkins) wrote :
review: Needs Fixing (continuous-integration)
Revision history for this message
PS Jenkins bot (ps-jenkins) wrote :
review: Needs Fixing (continuous-integration)
Revision history for this message
PS Jenkins bot (ps-jenkins) wrote :
review: Needs Fixing (continuous-integration)
Revision history for this message
PS Jenkins bot (ps-jenkins) wrote :
review: Needs Fixing (continuous-integration)
Revision history for this message
Cemil Azizoglu (cemil-azizoglu) wrote :

(Haven't completed the review yet.)

1211 +extern "C" mir::UniqueModulePtr<mi::Platform> create_input_platform(
1222 +extern "C" void add_input_platform_options(
1228 +extern "C" mi::PlatformPriority probe_input_platform(
1238 +extern "C" mir::ModuleProperties const* describe_input_module()

Please remove these "extern" keywords in the definition of functions as they cause problems with
abi-compliance-checker.
---------------------------------------------------------------------------------------------

review: Needs Fixing
Revision history for this message
PS Jenkins bot (ps-jenkins) wrote :
review: Needs Fixing (continuous-integration)
Revision history for this message
PS Jenkins bot (ps-jenkins) wrote :
review: Needs Fixing (continuous-integration)
Revision history for this message
PS Jenkins bot (ps-jenkins) wrote :
review: Needs Fixing (continuous-integration)
Revision history for this message
Francis Ginther (fginther) wrote :

Andreas, the most recent CI run includes the addition of the overlay PPA for all builds. Please let me know if you have any questions.

Revision history for this message
PS Jenkins bot (ps-jenkins) wrote :
review: Approve (continuous-integration)
Revision history for this message
Alan Griffiths (alan-griffiths) wrote :

A mild "needs fixing"

+ EXPECT_CALL(mock_sink, handle_input(mt::ButtonDownEventWithButton(0,0,1)));
+ EXPECT_CALL(mock_sink, handle_input(mt::ButtonDownEvent(0,0)));
+ EXPECT_CALL(mock_sink, handle_input(mt::ButtonUpEvent(0,0)));

These magic numbers are hard to interpret. Couldn't we have, for example:

EXPECT_CALL(mock_sink, handle_input(mt::ButtonDownEventWithButton(Point{0,0}, mir_pointer_button_primary)));

~~~~

-MirInputEventModifier mie::to_modifier(int32_t scan_code)
+MirInputEventModifiers mie::to_modifier(int32_t scan_code)

Now the function name disagrees with the return type.

~~~~

+ auto action = ...

Here and elsewhere could be "auto const"

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

> A mild "needs fixing"
>
> + EXPECT_CALL(mock_sink,
> handle_input(mt::ButtonDownEventWithButton(0,0,1)));
> + EXPECT_CALL(mock_sink, handle_input(mt::ButtonDownEvent(0,0)));
> + EXPECT_CALL(mock_sink, handle_input(mt::ButtonUpEvent(0,0)));
>
> These magic numbers are hard to interpret. Couldn't we have, for example:
>
> EXPECT_CALL(mock_sink, handle_input(mt::ButtonDownEventWithButton(Point{0,0},
> mir_pointer_button_primary)));
>

Only did that for the new matcher. The other matchers are used frequently. I think we can do the typing of event matchers in separate mps.

Revision history for this message
PS Jenkins bot (ps-jenkins) wrote :
review: Approve (continuous-integration)
Revision history for this message
Alan Griffiths (alan-griffiths) wrote :

LGTM

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

LGTM... I have a slight preference for aligning indentation at 4 spaces for multiline instead of open-parenthesis alignment, but thats no reason to block.

review: Approve
Revision history for this message
Alexandros Frantzis (afrantzis) wrote :

+ MATCHER_P3(ButtonUpEventWithButton, x, y, button, "")
+ ...
+ if (mir_pointer_event_action(pev) != mir_pointer_action_button_down)
+ return false;

Shouldn't this be != mir_pointer_action_button_up ?

+void mie::Platform::device_removed(mu::Device const& dev)
+{
+ log_info("device added %s", dev.devnode());

Wrong log message.

+ mir::log_info("Input device %s opened", dev.devnode());
+ report->opened_input_device(dev.devnode(), "evdev-input");

If the logging report is selected we will get the message twice. The mix of logging and reporting here doesn't seem correct.

+ for(auto ev = next_event(); ev; ev = next_event())

Simpler as 'while (auto ev = next_event()) ...'

review: Needs Fixing
Revision history for this message
PS Jenkins bot (ps-jenkins) wrote :
review: Needs Fixing (continuous-integration)
Revision history for this message
PS Jenkins bot (ps-jenkins) wrote :
review: Approve (continuous-integration)
Revision history for this message
Alexandros Frantzis (afrantzis) wrote :

Looks good.

review: Approve
Revision history for this message
PS Jenkins bot (ps-jenkins) :
review: Approve (continuous-integration)

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1=== modified file 'CMakeLists.txt'
2--- CMakeLists.txt 2015-09-08 21:22:41 +0000
3+++ CMakeLists.txt 2015-09-18 10:27:31 +0000
4@@ -175,6 +175,7 @@
5 find_package(LTTngUST REQUIRED)
6 pkg_check_modules(UDEV REQUIRED libudev)
7 pkg_check_modules(GLIB REQUIRED glib-2.0)
8+pkg_check_modules(LIBINPUT REQUIRED libinput)
9 pkg_check_modules(NETTLE REQUIRED nettle)
10 pkg_check_modules(UUID REQUIRED uuid)
11
12
13=== modified file 'debian/control'
14--- debian/control 2015-09-18 10:23:57 +0000
15+++ debian/control 2015-09-18 10:27:31 +0000
16@@ -40,6 +40,7 @@
17 libfreetype6-dev,
18 abi-compliance-checker,
19 libevdev-dev,
20+ libinput-dev (>= 0.21),
21 uuid-dev,
22 python3:any,
23 dh-python,
24
25=== modified file 'include/test/mir/test/event_matchers.h'
26--- include/test/mir/test/event_matchers.h 2015-07-27 07:24:50 +0000
27+++ include/test/mir/test/event_matchers.h 2015-09-18 10:27:31 +0000
28@@ -250,6 +250,22 @@
29 return button_event_matches(pev, x, y, mir_pointer_action_button_down, 0, true, false);
30 }
31
32+MATCHER_P2(ButtonDownEventWithButton, pos, button, "")
33+{
34+ auto pev = maybe_pointer_event(to_address(arg));
35+ if (pev == nullptr)
36+ return false;
37+ if (mir_pointer_event_action(pev) != mir_pointer_action_button_down)
38+ return false;
39+ if (mir_pointer_event_button_state(pev, static_cast<MirPointerButton>(button)) == false)
40+ return false;
41+ if (mir_pointer_event_axis_value(pev, mir_pointer_axis_x) != pos.x.as_float())
42+ return false;
43+ if (mir_pointer_event_axis_value(pev, mir_pointer_axis_y) != pos.y.as_float())
44+ return false;
45+ return true;
46+}
47+
48 MATCHER_P2(ButtonUpEvent, x, y, "")
49 {
50 auto pev = maybe_pointer_event(to_address(arg));
51@@ -262,6 +278,35 @@
52 return button_event_matches(pev, x, y, mir_pointer_action_button_down, buttons, false);
53 }
54
55+MATCHER_P2(ButtonUpEventWithButton, pos, button, "")
56+{
57+ auto pev = maybe_pointer_event(to_address(arg));
58+ if (pev == nullptr)
59+ return false;
60+ if (mir_pointer_event_action(pev) != mir_pointer_action_button_up)
61+ return false;
62+ if (mir_pointer_event_button_state(pev, button) == true)
63+ return false;
64+ if (mir_pointer_event_axis_value(pev, mir_pointer_axis_x) != pos.x.as_float())
65+ return false;
66+ if (mir_pointer_event_axis_value(pev, mir_pointer_axis_y) != pos.y.as_float())
67+ return false;
68+ return true;
69+}
70+
71+MATCHER_P2(PointerAxisChange, scroll_axis, value, "")
72+{
73+ auto parg = to_address(arg);
74+ auto pev = maybe_pointer_event(parg);
75+ if (pev == nullptr)
76+ return false;
77+ if (mir_pointer_event_action(pev) != mir_pointer_action_motion)
78+ return false;
79+ if (mir_pointer_event_axis_value(pev, scroll_axis) != value)
80+ return false;
81+ return true;
82+}
83+
84 MATCHER_P2(TouchEvent, x, y, "")
85 {
86 auto tev = maybe_touch_event(to_address(arg));
87
88=== modified file 'src/CMakeLists.txt'
89--- src/CMakeLists.txt 2015-09-17 08:57:46 +0000
90+++ src/CMakeLists.txt 2015-09-18 10:27:31 +0000
91@@ -49,4 +49,5 @@
92 # We need the ABI versions in the tests
93 set(MIR_SERVER_GRAPHICS_PLATFORM_ABI ${MIR_SERVER_GRAPHICS_PLATFORM_ABI} PARENT_SCOPE)
94 set(MIR_CLIENT_PLATFORM_ABI ${MIR_CLIENT_PLATFORM_ABI} PARENT_SCOPE)
95+set(MIR_INPUT_PLATFORM_VERSION_SCRIPT ${MIR_INPUT_PLATFORM_VERSION_SCRIPT} PARENT_SCOPE)
96 set(MIR_CLIENT_PLATFORM_VERSION ${MIR_CLIENT_PLATFORM_VERSION} PARENT_SCOPE)
97
98=== modified file 'src/platforms/CMakeLists.txt'
99--- src/platforms/CMakeLists.txt 2015-09-15 17:13:45 +0000
100+++ src/platforms/CMakeLists.txt 2015-09-18 10:27:31 +0000
101@@ -11,6 +11,8 @@
102 set(MIR_SERVER_GRAPHICS_PLATFORM_ABI ${MIR_SERVER_GRAPHICS_PLATFORM_ABI} PARENT_SCOPE)
103 set(MIR_SERVER_GRAPHICS_PLATFORM_VERSION "MIR_GRAPHICS_PLATFORM_${MIR_SERVER_GRAPHICS_PLATFORM_ABI}")
104 set(MIR_SERVER_GRAPHICS_PLATFORM_VERSION ${MIR_SERVER_GRAPHICS_PLATFORM_VERSION} PARENT_SCOPE)
105+set(MIR_INPUT_PLATFORM_VERSION_SCRIPT ${CMAKE_CURRENT_SOURCE_DIR}/input_platform_symbols.map)
106+set(MIR_INPUT_PLATFORM_VERSION_SCRIPT ${MIR_INPUT_PLATFORM_VERSION_SCRIPT} PARENT_SCOPE)
107
108 set(MIR_SERVER_PLATFORM_PATH
109 ${CMAKE_INSTALL_PREFIX}/${CMAKE_INSTALL_LIBDIR}/mir/server-platform
110
111=== modified file 'src/platforms/evdev/CMakeLists.txt'
112--- src/platforms/evdev/CMakeLists.txt 2015-09-08 03:25:06 +0000
113+++ src/platforms/evdev/CMakeLists.txt 2015-09-18 10:27:31 +0000
114@@ -1,5 +1,7 @@
115 include_directories(
116+ ${LIBINPUT_CFLAGS}
117 ${PROJECT_SOURCE_DIR}/include/platform
118+ ${PROJECT_SOURCE_DIR}/src/include/platform # udev wrapper
119 ${PROJECT_SOURCE_DIR}/include/common
120 ${PROJECT_SOURCE_DIR}/include/client
121 )
122@@ -9,3 +11,30 @@
123 input_modifier_utils.cpp
124 )
125
126+add_library(mirplatforminputevdevobjects OBJECT
127+ libinput_device.cpp
128+ libinput_device_ptr.cpp
129+ libinput_ptr.cpp
130+ platform.cpp
131+ )
132+
133+add_library(mirplatforminputevdev MODULE
134+ platform_factory.cpp
135+ $<TARGET_OBJECTS:mirplatforminputevdevobjects>
136+ $<TARGET_OBJECTS:mirevdevutilsobjects>
137+)
138+
139+set_target_properties(
140+ mirplatforminputevdev PROPERTIES
141+ OUTPUT_NAME input-evdev
142+ LIBRARY_OUTPUT_DIRECTORY ${CMAKE_LIBRARY_OUTPUT_DIRECTORY}/server-modules
143+ PREFIX ""
144+ LINK_FLAGS "-Wl,--exclude-libs=ALL -Wl,--version-script,${MIR_INPUT_PLATFORM_VERSION_SCRIPT}"
145+)
146+
147+target_link_libraries(mirplatforminputevdev
148+ mirplatform
149+ mirclient
150+ ${Boost_PROGRAM_OPTIONS_LIBRARY}
151+ ${LIBINPUT_LDFLAGS} ${LIBINPUT_LIBRARIES}
152+)
153
154=== modified file 'src/platforms/evdev/input_modifier_utils.cpp'
155--- src/platforms/evdev/input_modifier_utils.cpp 2015-08-05 13:04:00 +0000
156+++ src/platforms/evdev/input_modifier_utils.cpp 2015-09-18 10:27:31 +0000
157@@ -40,7 +40,7 @@
158 BOOST_THROW_EXCEPTION(std::runtime_error("Invalid mouse button"));
159 }
160
161-MirInputEventModifier mie::to_modifier(int32_t scan_code)
162+MirInputEventModifiers mie::to_modifiers(int32_t scan_code)
163 {
164 switch(scan_code)
165 {
166@@ -67,12 +67,15 @@
167 case KEY_RIGHTSHIFT:
168 return mir_input_event_modifier_shift_right;
169 default:
170+ return MirInputEventModifiers{0};
171+ }
172+}
173+
174+MirInputEventModifiers mie::expand_modifiers(MirInputEventModifiers modifiers)
175+{
176+ if (modifiers == 0)
177 return mir_input_event_modifier_none;
178- }
179-}
180
181-MirInputEventModifiers mie::expand_modifiers(MirInputEventModifiers modifiers)
182-{
183 if ((modifiers & mir_input_event_modifier_alt_left) || (modifiers & mir_input_event_modifier_alt_right))
184 modifiers |= mir_input_event_modifier_alt;
185
186
187=== modified file 'src/platforms/evdev/input_modifier_utils.h'
188--- src/platforms/evdev/input_modifier_utils.h 2015-08-05 13:04:00 +0000
189+++ src/platforms/evdev/input_modifier_utils.h 2015-09-18 10:27:31 +0000
190@@ -28,7 +28,7 @@
191 namespace evdev
192 {
193 MirPointerButton to_pointer_button(int button);
194-MirInputEventModifier to_modifier(int32_t scan_code);
195+MirInputEventModifiers to_modifiers(int32_t scan_code);
196 MirInputEventModifiers expand_modifiers(MirInputEventModifiers modifiers);
197 }
198 }
199
200=== added file 'src/platforms/evdev/libinput_device.cpp'
201--- src/platforms/evdev/libinput_device.cpp 1970-01-01 00:00:00 +0000
202+++ src/platforms/evdev/libinput_device.cpp 2015-09-18 10:27:31 +0000
203@@ -0,0 +1,347 @@
204+/*
205+ * Copyright © 2015 Canonical Ltd.
206+ *
207+ * This program is free software: you can redistribute it and/or modify it
208+ * under the terms of the GNU Lesser General Public License version 3,
209+ * as published by the Free Software Foundation.
210+ *
211+ * This program is distributed in the hope that it will be useful,
212+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
213+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
214+ * GNU Lesser General Public License for more details.
215+ *
216+ * You should have received a copy of the GNU Lesser General Public License
217+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
218+ *
219+ * Authored by: Andreas Pokorny <andreas.pokorny@canonical.com>
220+ */
221+
222+#include "libinput_device.h"
223+#include "libinput_ptr.h"
224+#include "libinput_device_ptr.h"
225+#include "input_modifier_utils.h"
226+#include "evdev_device_detection.h"
227+
228+#include "mir/input/input_sink.h"
229+#include "mir/input/input_report.h"
230+#include "mir/input/device_capability.h"
231+#include "mir/input/input_device_info.h"
232+#include "mir/events/event_builders.h"
233+#include "mir/geometry/displacement.h"
234+#include "mir/dispatch/dispatchable.h"
235+#include "mir/fd.h"
236+
237+#include <libinput.h>
238+#include <linux/input.h> // only used to get constants for input reports
239+
240+#include <cstring>
241+#include <chrono>
242+#include <sstream>
243+#include <algorithm>
244+
245+namespace md = mir::dispatch;
246+namespace mi = mir::input;
247+namespace mie = mi::evdev;
248+using namespace std::literals::chrono_literals;
249+
250+namespace
251+{
252+
253+void null_deleter(MirEvent *) {}
254+
255+}
256+
257+mie::LibInputDevice::LibInputDevice(std::shared_ptr<mi::InputReport> const& report, char const* path,
258+ LibInputDevicePtr dev)
259+ : report{report}, accumulated_touch_event{nullptr, null_deleter}, pointer_pos{0, 0}, modifier_state{0},
260+ button_state{0}
261+{
262+ add_device_of_group(path, std::move(dev));
263+}
264+
265+void mie::LibInputDevice::add_device_of_group(char const* path, LibInputDevicePtr dev)
266+{
267+ paths.emplace_back(path);
268+ devices.emplace_back(std::move(dev));
269+}
270+
271+bool mie::LibInputDevice::is_in_group(char const* path)
272+{
273+ return end(paths) != find(begin(paths), end(paths), std::string{path});
274+}
275+
276+mie::LibInputDevice::~LibInputDevice() = default;
277+
278+void mie::LibInputDevice::start(InputSink* sink, EventBuilder* builder)
279+{
280+ this->sink = sink;
281+ this->builder = builder;
282+}
283+
284+void mie::LibInputDevice::stop()
285+{
286+ sink = nullptr;
287+ builder = nullptr;
288+}
289+
290+void mie::LibInputDevice::process_event(libinput_event* event)
291+{
292+ if (!sink)
293+ return;
294+
295+ switch(libinput_event_get_type(event))
296+ {
297+ case LIBINPUT_EVENT_KEYBOARD_KEY:
298+ sink->handle_input(*convert_event(libinput_event_get_keyboard_event(event)));
299+ break;
300+ case LIBINPUT_EVENT_POINTER_MOTION:
301+ sink->handle_input(*convert_motion_event(libinput_event_get_pointer_event(event)));
302+ break;
303+ case LIBINPUT_EVENT_POINTER_MOTION_ABSOLUTE:
304+ sink->handle_input(*convert_absolute_motion_event(libinput_event_get_pointer_event(event)));
305+ break;
306+ case LIBINPUT_EVENT_POINTER_BUTTON:
307+ sink->handle_input(*convert_button_event(libinput_event_get_pointer_event(event)));
308+ break;
309+ case LIBINPUT_EVENT_POINTER_AXIS:
310+ sink->handle_input(*convert_axis_event(libinput_event_get_pointer_event(event)));
311+ break;
312+ // touch events are processed as a batch of changes over all touch pointts
313+ case LIBINPUT_EVENT_TOUCH_DOWN:
314+ add_touch_down_event(libinput_event_get_touch_event(event));
315+ break;
316+ case LIBINPUT_EVENT_TOUCH_UP:
317+ add_touch_up_event(libinput_event_get_touch_event(event));
318+ break;
319+ case LIBINPUT_EVENT_TOUCH_MOTION:
320+ add_touch_motion_event(libinput_event_get_touch_event(event));
321+ break;
322+ case LIBINPUT_EVENT_TOUCH_CANCEL:
323+ // Not yet provided by libinput.
324+ break;
325+ case LIBINPUT_EVENT_TOUCH_FRAME:
326+ sink->handle_input(get_accumulated_touch_event(0ns));
327+ accumulated_touch_event.reset();
328+ break;
329+ default:
330+ break;
331+ }
332+}
333+
334+mir::EventUPtr mie::LibInputDevice::convert_event(libinput_event_keyboard* keyboard)
335+{
336+ std::chrono::nanoseconds const time = std::chrono::microseconds(libinput_event_keyboard_get_time_usec(keyboard));
337+ auto const action = libinput_event_keyboard_get_key_state(keyboard) == LIBINPUT_KEY_STATE_PRESSED ?
338+ mir_keyboard_action_down :
339+ mir_keyboard_action_up;
340+ auto const code = libinput_event_keyboard_get_key(keyboard);
341+ report->received_event_from_kernel(time.count(), EV_KEY, code, action);
342+
343+ auto event = builder->key_event(time,
344+ action,
345+ xkb_keysym_t{0},
346+ code,
347+ mie::expand_modifiers(modifier_state));
348+
349+ if (action == mir_keyboard_action_down)
350+ modifier_state |= mie::to_modifiers(code);
351+ else
352+ modifier_state &= ~mie::to_modifiers(code);
353+
354+ return event;
355+}
356+
357+mir::EventUPtr mie::LibInputDevice::convert_button_event(libinput_event_pointer* pointer)
358+{
359+ std::chrono::nanoseconds const time = std::chrono::microseconds(libinput_event_pointer_get_time_usec(pointer));
360+ auto const button = libinput_event_pointer_get_button(pointer);
361+ auto const action = (libinput_event_pointer_get_button_state(pointer) == LIBINPUT_BUTTON_STATE_PRESSED)?
362+ mir_pointer_action_button_down : mir_pointer_action_button_up;
363+
364+ auto const pointer_button = mie::to_pointer_button(button);
365+ auto const relative_x_value = 0.0f;
366+ auto const relative_y_value = 0.0f;
367+ auto const hscroll_value = 0.0f;
368+ auto const vscroll_value = 0.0f;
369+
370+ report->received_event_from_kernel(time.count(), EV_KEY, pointer_button, action);
371+
372+ if (action == mir_pointer_action_button_down)
373+ button_state = MirPointerButton(button_state | uint32_t(pointer_button));
374+ else
375+ button_state = MirPointerButton(button_state & ~uint32_t(pointer_button));
376+
377+ auto event = builder->pointer_event(time, mie::expand_modifiers(modifier_state), action, button_state,
378+ pointer_pos.x.as_float(), pointer_pos.y.as_float(), hscroll_value,
379+ vscroll_value, relative_x_value, relative_y_value);
380+
381+ return event;
382+}
383+
384+mir::EventUPtr mie::LibInputDevice::convert_motion_event(libinput_event_pointer* pointer)
385+{
386+ std::chrono::nanoseconds const time = std::chrono::microseconds(libinput_event_pointer_get_time_usec(pointer));
387+ auto const action = mir_pointer_action_motion;
388+ auto const hscroll_value = 0.0f;
389+ auto const vscroll_value = 0.0f;
390+
391+ report->received_event_from_kernel(time.count(), EV_REL, 0, 0);
392+
393+ mir::geometry::Displacement const movement{
394+ libinput_event_pointer_get_dx(pointer),
395+ libinput_event_pointer_get_dy(pointer)};
396+ pointer_pos = pointer_pos + movement;
397+
398+ sink->confine_pointer(pointer_pos);
399+
400+ auto event = builder->pointer_event(time, mie::expand_modifiers(modifier_state), action, button_state,
401+ pointer_pos.x.as_float(), pointer_pos.y.as_float(), hscroll_value,
402+ vscroll_value, movement.dx.as_float(), movement.dy.as_float());
403+
404+ return event;
405+}
406+
407+mir::EventUPtr mie::LibInputDevice::convert_absolute_motion_event(libinput_event_pointer* pointer)
408+{
409+ // a pointing device that emits absolute coordinates
410+ std::chrono::nanoseconds const time = std::chrono::microseconds(libinput_event_pointer_get_time_usec(pointer));
411+ auto const action = mir_pointer_action_motion;
412+ auto const hscroll_value = 0.0f;
413+ auto const vscroll_value = 0.0f;
414+
415+ report->received_event_from_kernel(time.count(), EV_ABS, 0, 0);
416+ auto const old_pointer_pos = pointer_pos;
417+ pointer_pos = mir::geometry::Point{
418+ libinput_event_pointer_get_absolute_x(pointer),
419+ libinput_event_pointer_get_absolute_y(pointer)};
420+ auto const movement = pointer_pos - old_pointer_pos;
421+
422+ sink->confine_pointer(pointer_pos);
423+
424+ auto event = builder->pointer_event(time, mie::expand_modifiers(modifier_state), action, button_state,
425+ pointer_pos.x.as_float(), pointer_pos.y.as_float(), hscroll_value,
426+ vscroll_value, movement.dx.as_float(), movement.dy.as_float());
427+ return event;
428+}
429+
430+mir::EventUPtr mie::LibInputDevice::convert_axis_event(libinput_event_pointer* pointer)
431+{
432+ std::chrono::nanoseconds const time = std::chrono::microseconds(libinput_event_pointer_get_time_usec(pointer));
433+ auto const action = mir_pointer_action_motion;
434+ auto const relative_x_value = 0.0f;
435+ auto const relative_y_value = 0.0f;
436+ auto const hscroll_value = libinput_event_pointer_has_axis(pointer, LIBINPUT_POINTER_AXIS_SCROLL_HORIZONTAL)
437+ ? libinput_event_pointer_get_axis_value(pointer, LIBINPUT_POINTER_AXIS_SCROLL_HORIZONTAL)
438+ : 0.0f;
439+ auto const vscroll_value = libinput_event_pointer_has_axis(pointer, LIBINPUT_POINTER_AXIS_SCROLL_VERTICAL)
440+ ? libinput_event_pointer_get_axis_value(pointer, LIBINPUT_POINTER_AXIS_SCROLL_VERTICAL)
441+ : 0.0f;
442+
443+ report->received_event_from_kernel(time.count(), EV_REL, 0, 0);
444+ auto event = builder->pointer_event(time, mie::expand_modifiers(modifier_state), action, button_state,
445+ pointer_pos.x.as_float(), pointer_pos.y.as_float(), hscroll_value,
446+ vscroll_value, relative_x_value, relative_y_value);
447+ return event;
448+}
449+
450+MirEvent& mie::LibInputDevice::get_accumulated_touch_event(std::chrono::nanoseconds timestamp)
451+{
452+ if (!accumulated_touch_event)
453+ {
454+ report->received_event_from_kernel(timestamp.count(), EV_SYN, 0, 0);
455+ accumulated_touch_event = builder->touch_event(timestamp, mie::expand_modifiers(modifier_state));
456+ }
457+
458+ return *accumulated_touch_event;
459+}
460+
461+void mie::LibInputDevice::add_touch_down_event(libinput_event_touch* touch)
462+{
463+ std::chrono::nanoseconds const time = std::chrono::microseconds(libinput_event_touch_get_time_usec(touch));
464+ auto& event = get_accumulated_touch_event(time);
465+
466+ MirTouchId const id = libinput_event_touch_get_slot(touch);
467+ auto const action = mir_touch_action_down;
468+ // TODO make libinput indicate tool type
469+ auto const tool = mir_touch_tooltype_finger;
470+ float const pressure = libinput_event_touch_get_pressure(touch);
471+ auto const screen = sink->bounding_rectangle();
472+ uint32_t const width = screen.size.width.as_int();
473+ uint32_t const height = screen.size.height.as_int();
474+ float const x = libinput_event_touch_get_x_transformed(touch, width);
475+ float const y = libinput_event_touch_get_y_transformed(touch, height);
476+ float const major = libinput_event_touch_get_major_transformed(touch, width, height);
477+ float const minor = libinput_event_touch_get_minor_transformed(touch, width, height);
478+ // TODO why do we send size to clients?
479+ float const size = std::max(major, minor);
480+
481+ // TODO extend for touch screens that provide orientation
482+ builder->add_touch(event, id, action, tool, x, y, pressure, major, minor, size);
483+}
484+
485+void mie::LibInputDevice::add_touch_up_event(libinput_event_touch* touch)
486+{
487+ std::chrono::nanoseconds const time = std::chrono::microseconds(libinput_event_touch_get_time_usec(touch));
488+ auto& event = get_accumulated_touch_event(time);
489+ MirTouchId const id = libinput_event_touch_get_slot(touch);
490+ auto const action = mir_touch_action_up;
491+ auto const tool = mir_touch_tooltype_finger; // TODO make libinput indicate tool type
492+
493+ float const pressure = 0.0f;
494+ float const major = 0.0f;
495+ float const minor = 0.0f;
496+ float const size = 0.0f;
497+ // TODO extend for touch screens that provide orientation and major/minor
498+ builder->add_touch(event, id, action, tool, libinput_event_touch_get_x(touch), libinput_event_touch_get_y(touch),
499+ pressure, major, minor, size);
500+}
501+
502+void mie::LibInputDevice::add_touch_motion_event(libinput_event_touch* touch)
503+{
504+ std::chrono::nanoseconds const time = std::chrono::microseconds(libinput_event_touch_get_time_usec(touch));
505+ auto& event = get_accumulated_touch_event(time);
506+
507+ MirTouchId const id = libinput_event_touch_get_slot(touch);
508+ auto const action = mir_touch_action_change;
509+ auto const tool = mir_touch_tooltype_finger; // TODO make libinput indicate tool type
510+ float const pressure = libinput_event_touch_get_pressure(touch);
511+ auto const screen = sink->bounding_rectangle();
512+ uint32_t const width = screen.size.width.as_int();
513+ uint32_t const height = screen.size.height.as_int();
514+ float const x = libinput_event_touch_get_x_transformed(touch, width);
515+ float const y = libinput_event_touch_get_y_transformed(touch, height);
516+ float const major = libinput_event_touch_get_major_transformed(touch, width, height);
517+ float const minor = libinput_event_touch_get_minor_transformed(touch, width, height);
518+ // TODO why do we send size to clients?
519+ float const size = std::max(major, minor);
520+
521+ // TODO extend for touch screens that provide orientation
522+ builder->add_touch(event, id, action, tool, x, y, pressure, major, minor, size);
523+}
524+
525+mi::InputDeviceInfo mie::LibInputDevice::get_device_info()
526+{
527+ auto dev = device();
528+ std::string name = libinput_device_get_name(dev);
529+ std::stringstream unique_id(name);
530+ unique_id << '-' << libinput_device_get_sysname(dev) << '-' <<
531+ libinput_device_get_id_vendor(dev) << '-' <<
532+ libinput_device_get_id_product(dev);
533+ mi::DeviceCapabilities caps;
534+
535+ for (auto const& path : paths)
536+ caps |= mie::detect_device_capabilities(path.c_str());
537+
538+ return mi::InputDeviceInfo{0, name, unique_id.str(), caps};
539+}
540+
541+libinput_device_group* mie::LibInputDevice::group()
542+{
543+ return libinput_device_get_device_group(device());
544+}
545+
546+libinput_device* mie::LibInputDevice::device()
547+{
548+ return devices.front().get();
549+}
550+
551
552=== added file 'src/platforms/evdev/libinput_device.h'
553--- src/platforms/evdev/libinput_device.h 1970-01-01 00:00:00 +0000
554+++ src/platforms/evdev/libinput_device.h 2015-09-18 10:27:31 +0000
555@@ -0,0 +1,90 @@
556+/*
557+ * Copyright © 2015 Canonical Ltd.
558+ *
559+ * This program is free software: you can redistribute it and/or modify it
560+ * under the terms of the GNU Lesser General Public License version 3,
561+ * as published by the Free Software Foundation.
562+ *
563+ * This program is distributed in the hope that it will be useful,
564+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
565+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
566+ * GNU Lesser General Public License for more details.
567+ *
568+ * You should have received a copy of the GNU Lesser General Public License
569+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
570+ *
571+ * Authored by: Andreas Pokorny <andreas.pokorny@canonical.com>
572+ */
573+
574+#ifndef MIR_INPUT_EVDEV_LIBINPUT_DEVICE_H_
575+#define MIR_INPUT_EVDEV_LIBINPUT_DEVICE_H_
576+
577+#include "libinput_ptr.h"
578+#include "libinput_device_ptr.h"
579+
580+#include "mir/input/event_builder.h"
581+#include "mir/input/input_device.h"
582+#include "mir/geometry/point.h"
583+
584+#include <vector>
585+
586+struct libinput_event;
587+struct libinput_event_keyboard;
588+struct libinput_event_touch;
589+struct libinput_event_pointer;
590+struct libinput_device_group;
591+
592+namespace mir
593+{
594+namespace input
595+{
596+class InputReport;
597+namespace evdev
598+{
599+struct PointerState;
600+struct KeyboardState;
601+
602+class LibInputDevice : public input::InputDevice
603+{
604+public:
605+ LibInputDevice(std::shared_ptr<InputReport> const& report, char const* path, LibInputDevicePtr dev);
606+ ~LibInputDevice();
607+ void start(InputSink* sink, EventBuilder* builder) override;
608+ void stop() override;
609+ virtual InputDeviceInfo get_device_info() override;
610+
611+ void process_event(libinput_event* event);
612+ ::libinput_device* device();
613+ ::libinput_device_group* group();
614+ void add_device_of_group(char const* path, LibInputDevicePtr ptr);
615+ bool is_in_group(char const* path);
616+private:
617+ EventUPtr convert_event(libinput_event_keyboard* keyboard);
618+ EventUPtr convert_button_event(libinput_event_pointer* pointer);
619+ EventUPtr convert_motion_event(libinput_event_pointer* pointer);
620+ EventUPtr convert_absolute_motion_event(libinput_event_pointer* pointer);
621+ EventUPtr convert_axis_event(libinput_event_pointer* pointer);
622+ void add_touch_down_event(libinput_event_touch* touch);
623+ void add_touch_up_event(libinput_event_touch* touch);
624+ void add_touch_motion_event(libinput_event_touch* touch);
625+ MirEvent& get_accumulated_touch_event(std::chrono::nanoseconds timestamp);
626+
627+ std::shared_ptr<InputReport> report;
628+ std::shared_ptr<::libinput> lib;
629+ std::vector<std::string> paths;
630+ std::vector<LibInputDevicePtr> devices;
631+ std::shared_ptr<dispatch::Dispatchable> dispatchable_fd;
632+
633+ InputSink* sink{nullptr};
634+ EventBuilder* builder{nullptr};
635+ EventUPtr accumulated_touch_event;
636+
637+ mir::geometry::Point pointer_pos;
638+ MirInputEventModifiers modifier_state;
639+ MirPointerButtons button_state;
640+};
641+}
642+}
643+}
644+
645+#endif
646
647=== added file 'src/platforms/evdev/libinput_device_ptr.cpp'
648--- src/platforms/evdev/libinput_device_ptr.cpp 1970-01-01 00:00:00 +0000
649+++ src/platforms/evdev/libinput_device_ptr.cpp 2015-09-18 10:27:31 +0000
650@@ -0,0 +1,30 @@
651+/*
652+ * Copyright © 2015 Canonical Ltd.
653+ *
654+ * This program is free software: you can redistribute it and/or modify it
655+ * under the terms of the GNU Lesser General Public License version 3,
656+ * as published by the Free Software Foundation.
657+ *
658+ * This program is distributed in the hope that it will be useful,
659+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
660+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
661+ * GNU Lesser General Public License for more details.
662+ *
663+ * You should have received a copy of the GNU Lesser General Public License
664+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
665+ *
666+ * Authored by: Andreas Pokorny <andreas.pokorny@canonical.com>
667+ */
668+
669+#include "libinput.h"
670+#include "libinput_device_ptr.h"
671+
672+namespace mie = mir::input::evdev;
673+
674+mie::LibInputDevicePtr mie::make_libinput_device(libinput* lib, char const* path)
675+{
676+ auto ret = mie::LibInputDevicePtr(::libinput_path_add_device(lib, path), libinput_device_unref);
677+ if (ret)
678+ libinput_device_ref(ret.get());
679+ return ret;
680+}
681
682=== added file 'src/platforms/evdev/libinput_device_ptr.h'
683--- src/platforms/evdev/libinput_device_ptr.h 1970-01-01 00:00:00 +0000
684+++ src/platforms/evdev/libinput_device_ptr.h 2015-09-18 10:27:31 +0000
685@@ -0,0 +1,40 @@
686+/*
687+ * Copyright © 2015 Canonical Ltd.
688+ *
689+ * This program is free software: you can redistribute it and/or modify it
690+ * under the terms of the GNU Lesser General Public License version 3,
691+ * as published by the Free Software Foundation.
692+ *
693+ * This program is distributed in the hope that it will be useful,
694+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
695+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
696+ * GNU Lesser General Public License for more details.
697+ *
698+ * You should have received a copy of the GNU Lesser General Public License
699+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
700+ *
701+ * Authored by: Andreas Pokorny <andreas.pokorny@canonical.com>
702+ */
703+
704+#ifndef MIR_INPUT_EVDEV_LIBINPUT_DEVICE_PTR_H_
705+#define MIR_INPUT_EVDEV_LIBINPUT_DEVICE_PTR_H_
706+
707+#include <memory>
708+
709+struct libinput_device;
710+struct libinput;
711+
712+namespace mir
713+{
714+namespace input
715+{
716+namespace evdev
717+{
718+using LibInputDevicePtr = std::unique_ptr<libinput_device, libinput_device*(*)(libinput_device*)>;
719+
720+LibInputDevicePtr make_libinput_device(libinput* lib, char const* path);
721+}
722+}
723+}
724+
725+#endif
726
727=== added file 'src/platforms/evdev/libinput_ptr.cpp'
728--- src/platforms/evdev/libinput_ptr.cpp 1970-01-01 00:00:00 +0000
729+++ src/platforms/evdev/libinput_ptr.cpp 2015-09-18 10:27:31 +0000
730@@ -0,0 +1,47 @@
731+/*
732+ * Copyright © 2015 Canonical Ltd.
733+ *
734+ * This program is free software: you can redistribute it and/or modify it
735+ * under the terms of the GNU Lesser General Public License version 3,
736+ * as published by the Free Software Foundation.
737+ *
738+ * This program is distributed in the hope that it will be useful,
739+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
740+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
741+ * GNU Lesser General Public License for more details.
742+ *
743+ * You should have received a copy of the GNU Lesser General Public License
744+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
745+ *
746+ * Authored by: Andreas Pokorny <andreas.pokorny@canonical.com>
747+ */
748+
749+#include "libinput.h"
750+#include "libinput_ptr.h"
751+
752+#include <sys/types.h>
753+#include <sys/stat.h>
754+#include <fcntl.h>
755+#include <unistd.h>
756+
757+namespace mie = mir::input::evdev;
758+
759+namespace
760+{
761+int fd_open(const char* path, int flags, void* /*userdata*/)
762+{
763+ return ::open(path, flags);
764+}
765+
766+void fd_close(int fd, void* /*userdata*/)
767+{
768+ ::close(fd);
769+}
770+
771+const libinput_interface fd_ops = {fd_open, fd_close};
772+}
773+
774+mie::LibInputPtr mie::make_libinput()
775+{
776+ return mie::LibInputPtr{libinput_path_create_context(&fd_ops, nullptr),libinput_unref};
777+}
778
779=== added file 'src/platforms/evdev/libinput_ptr.h'
780--- src/platforms/evdev/libinput_ptr.h 1970-01-01 00:00:00 +0000
781+++ src/platforms/evdev/libinput_ptr.h 2015-09-18 10:27:31 +0000
782@@ -0,0 +1,42 @@
783+/*
784+ * Copyright © 2015 Canonical Ltd.
785+ *
786+ * This program is free software: you can redistribute it and/or modify it
787+ * under the terms of the GNU Lesser General Public License version 3,
788+ * as published by the Free Software Foundation.
789+ *
790+ * This program is distributed in the hope that it will be useful,
791+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
792+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
793+ * GNU Lesser General Public License for more details.
794+ *
795+ * You should have received a copy of the GNU Lesser General Public License
796+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
797+ *
798+ * Authored by: Andreas Pokorny <andreas.pokorny@canonical.com>
799+ */
800+
801+#ifndef MIR_INPUT_EVDEV_LIBINPUT_PTR_H_
802+#define MIR_INPUT_EVDEV_LIBINPUT_PTR_H_
803+
804+#include "mir_toolkit/event.h"
805+
806+#include <memory>
807+#include <list>
808+
809+struct libinput;
810+
811+namespace mir
812+{
813+namespace input
814+{
815+namespace evdev
816+{
817+using LibInputPtr = std::unique_ptr<libinput, libinput*(*)(libinput*)>;
818+
819+LibInputPtr make_libinput();
820+}
821+}
822+}
823+
824+#endif
825
826=== added file 'src/platforms/evdev/platform.cpp'
827--- src/platforms/evdev/platform.cpp 1970-01-01 00:00:00 +0000
828+++ src/platforms/evdev/platform.cpp 2015-09-18 10:27:31 +0000
829@@ -0,0 +1,238 @@
830+/*
831+ * Copyright © 2015 Canonical Ltd.
832+ *
833+ * This program is free software: you can redistribute it and/or modify it
834+ * under the terms of the GNU Lesser General Public License version 3,
835+ * as published by the Free Software Foundation.
836+ *
837+ * This program is distributed in the hope that it will be useful,
838+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
839+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
840+ * GNU Lesser General Public License for more details.
841+ *
842+ * You should have received a copy of the GNU Lesser General Public License
843+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
844+ *
845+ * Authored by: Andreas Pokorny <andreas.pokorny@canonical.com>
846+ */
847+
848+#include "platform.h"
849+#include "libinput_device.h"
850+#include "libinput_ptr.h"
851+#include "mir/udev/wrapper.h"
852+#include "mir/dispatch/dispatchable.h"
853+#include "mir/dispatch/readable_fd.h"
854+#include "mir/dispatch/multiplexing_dispatchable.h"
855+#include "mir/module_properties.h"
856+
857+#include "mir/input/input_device_registry.h"
858+#include "mir/input/input_device.h"
859+#include "mir/input/input_report.h"
860+#include "mir/fd.h"
861+
862+#define MIR_LOG_COMPONENT "evdev-input"
863+#include "mir/log.h"
864+
865+#include <boost/exception/errinfo_errno.hpp>
866+#include <boost/throw_exception.hpp>
867+
868+#include <libinput.h>
869+
870+namespace mi = mir::input;
871+namespace md = mir::dispatch;
872+namespace mu = mir::udev;
873+namespace mie = mi::evdev;
874+
875+mie::Platform::Platform(std::shared_ptr<InputDeviceRegistry> const& registry,
876+ std::shared_ptr<InputReport> const& report,
877+ std::unique_ptr<udev::Context>&& udev_context,
878+ std::unique_ptr<udev::Monitor>&& monitor)
879+ : report(report), udev_context(std::move(udev_context)), monitor(std::move(monitor)),
880+ input_device_registry(registry),
881+ platform_dispatchable{std::make_shared<md::MultiplexingDispatchable>()},
882+ monitor_dispatchable{
883+ std::make_shared<md::ReadableFd>(
884+ Fd{IntOwnedFd{this->monitor->fd()}},
885+ [this](){process_changes();}
886+ )},
887+ lib{make_libinput()},
888+ libinput_dispatchable{
889+ std::make_shared<md::ReadableFd>(
890+ Fd{IntOwnedFd{libinput_get_fd(lib.get())}},
891+ [this](){process_input_events();}
892+ )}
893+{
894+ this->monitor->filter_by_subsystem("input");
895+ this->monitor->enable();
896+
897+}
898+
899+std::shared_ptr<mir::dispatch::Dispatchable> mie::Platform::dispatchable()
900+{
901+ return platform_dispatchable;
902+}
903+
904+void mie::Platform::start()
905+{
906+ platform_dispatchable->add_watch(monitor_dispatchable);
907+ platform_dispatchable->add_watch(libinput_dispatchable);
908+ scan_for_devices();
909+}
910+
911+void mie::Platform::process_input_events()
912+{
913+ int status = libinput_dispatch(lib.get());
914+ if (status != 0)
915+ BOOST_THROW_EXCEPTION(boost::enable_error_info(std::runtime_error("libinput_dispatch_failed"))
916+ << boost::errinfo_errno(-status));
917+
918+ using EventType = std::unique_ptr<libinput_event,void(*)(libinput_event*)>;
919+ auto next_event = [lilib=lib.get()]
920+ {
921+ return EventType(libinput_get_event(lilib), libinput_event_destroy);
922+ };
923+
924+ while(auto ev = next_event())
925+ {
926+ auto dev = find_device(
927+ libinput_device_get_device_group(
928+ libinput_event_get_device(ev.get())
929+ ));
930+ if (dev != end(devices))
931+ (*dev)->process_event(ev.get());
932+ }
933+}
934+
935+void mie::Platform::process_changes()
936+{
937+ monitor->process_events(
938+ [this](mu::Monitor::EventType event, mu::Device const& dev)
939+ {
940+ if (!dev.devnode())
941+ return;
942+ if (event == mu::Monitor::ADDED)
943+ {
944+ log_info("udev:device added %s", dev.devnode());
945+ device_added(dev);
946+ }
947+ if (event == mu::Monitor::REMOVED)
948+ {
949+ log_info("udev:device removed %s", dev.devnode());
950+ device_removed(dev);
951+ }
952+ if (event == mu::Monitor::CHANGED)
953+ {
954+ log_info("udev:device changed %s", dev.devnode());
955+ device_changed(dev);
956+ }
957+ });
958+}
959+
960+void mie::Platform::scan_for_devices()
961+{
962+ mu::Enumerator input_enumerator{udev_context};
963+ input_enumerator.match_subsystem("input");
964+ input_enumerator.scan_devices();
965+
966+ for (auto& device : input_enumerator)
967+ {
968+ if (device.devnode() != nullptr)
969+ {
970+ log_info("udev:device added %s", device.devnode());
971+ device_added(device);
972+ }
973+ }
974+}
975+
976+void mie::Platform::device_added(mu::Device const& dev)
977+{
978+ if (end(devices) != find_device(dev.devnode()))
979+ return;
980+
981+ auto device_ptr = make_libinput_device(lib.get(), dev.devnode());
982+
983+ // libinput might refuse to open certain devices nodes like /dev/input/mice
984+ // or ignore devices with odd evdev bits/capabilities set
985+ if (!device_ptr)
986+ {
987+ report->failed_to_open_input_device(dev.devnode(), "evdev-input");
988+ log_info("libinput refused to open device %s", dev.devnode());
989+ return;
990+ }
991+
992+ auto device_it = find_device(libinput_device_get_device_group(device_ptr.get()));
993+ if (end(devices) != device_it)
994+ {
995+ (*device_it)->add_device_of_group(dev.devnode(), move(device_ptr));
996+ log_debug("Device %s is part of an already opened device group", dev.devnode());
997+ return;
998+ }
999+
1000+ try
1001+ {
1002+ devices.emplace_back(std::make_shared<mie::LibInputDevice>(report, dev.devnode(), move(device_ptr)));
1003+
1004+ input_device_registry->add_device(devices.back());
1005+
1006+ report->opened_input_device(dev.devnode(), "evdev-input");
1007+ } catch(...)
1008+ {
1009+ mir::log_error("Failure opening device %s", dev.devnode());
1010+ report->failed_to_open_input_device(dev.devnode(), "evdev-input");
1011+ }
1012+}
1013+
1014+void mie::Platform::device_removed(mu::Device const& dev)
1015+{
1016+ auto known_device_pos = find_device(dev.devnode());
1017+
1018+ if (known_device_pos == end(devices))
1019+ return;
1020+
1021+ input_device_registry->remove_device(*known_device_pos);
1022+ devices.erase(known_device_pos);
1023+}
1024+
1025+
1026+auto mie::Platform::find_device(char const* devnode) -> decltype(devices)::iterator
1027+{
1028+ return std::find_if(
1029+ begin(devices),
1030+ end(devices),
1031+ [devnode](auto const& item)
1032+ {
1033+ return item->is_in_group(devnode);
1034+ }
1035+ );
1036+}
1037+
1038+auto mie::Platform::find_device(libinput_device_group const* devgroup) -> decltype(devices)::iterator
1039+{
1040+ if (devgroup == nullptr)
1041+ return end(devices);
1042+ return std::find_if(
1043+ begin(devices),
1044+ end(devices),
1045+ [devgroup](auto const& item)
1046+ {
1047+ return devgroup == item->group();
1048+ }
1049+ );
1050+}
1051+
1052+void mie::Platform::device_changed(mu::Device const& dev)
1053+{
1054+ device_removed(dev);
1055+ device_added(dev);
1056+}
1057+
1058+void mie::Platform::stop()
1059+{
1060+ platform_dispatchable->remove_watch(monitor_dispatchable);
1061+ platform_dispatchable->remove_watch(libinput_dispatchable);
1062+ while (!devices.empty())
1063+ {
1064+ input_device_registry->remove_device(devices.back());
1065+ devices.pop_back();
1066+ }
1067+}
1068
1069=== added file 'src/platforms/evdev/platform.h'
1070--- src/platforms/evdev/platform.h 1970-01-01 00:00:00 +0000
1071+++ src/platforms/evdev/platform.h 2015-09-18 10:27:31 +0000
1072@@ -0,0 +1,94 @@
1073+/*
1074+ * Copyright © 2015 Canonical Ltd.
1075+ *
1076+ * This program is free software: you can redistribute it and/or modify it
1077+ * under the terms of the GNU Lesser General Public License version 3,
1078+ * as published by the Free Software Foundation.
1079+ *
1080+ * This program is distributed in the hope that it will be useful,
1081+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
1082+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
1083+ * GNU Lesser General Public License for more details.
1084+ *
1085+ * You should have received a copy of the GNU Lesser General Public License
1086+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
1087+ *
1088+ * Authored by: Andreas Pokorny <andreas.pokorny@canonical.com>
1089+ */
1090+
1091+#ifndef MIR_INPUT_EVDEV_PLATFORM_H_
1092+#define MIR_INPUT_EVDEV_PLATFORM_H_
1093+
1094+#include "libinput_ptr.h"
1095+#include "libinput_device_ptr.h"
1096+
1097+#include "mir/input/platform.h"
1098+
1099+#include <vector>
1100+
1101+struct libinput_device_group;
1102+
1103+namespace mir
1104+{
1105+namespace udev
1106+{
1107+class Device;
1108+class Monitor;
1109+class Context;
1110+}
1111+namespace dispatch
1112+{
1113+class MultiplexingDispatchable;
1114+class ReadableFd;
1115+}
1116+namespace input
1117+{
1118+class InputDeviceRegistry;
1119+namespace evdev
1120+{
1121+
1122+struct MonitorDispatchable;
1123+class LibInputDevice;
1124+
1125+class Platform : public input::Platform
1126+{
1127+public:
1128+ Platform(std::shared_ptr<InputDeviceRegistry> const& registry,
1129+ std::shared_ptr<InputReport> const& report,
1130+ std::unique_ptr<udev::Context>&& udev_context,
1131+ std::unique_ptr<udev::Monitor>&& monitor);
1132+ std::shared_ptr<mir::dispatch::Dispatchable> dispatchable() override;
1133+ void start() override;
1134+ void stop() override;
1135+
1136+private:
1137+ void scan_for_devices();
1138+ void process_changes();
1139+ void device_added(udev::Device const& dev);
1140+ void device_removed(udev::Device const& dev);
1141+ void device_changed(udev::Device const& dev);
1142+ void process_input_events();
1143+
1144+ std::shared_ptr<LibInputDevice> create_device(udev::Device const& dev) const;
1145+
1146+ std::shared_ptr<InputReport> const report;
1147+ std::shared_ptr<udev::Context> const udev_context;
1148+ std::unique_ptr<udev::Monitor> const monitor;
1149+ std::shared_ptr<InputDeviceRegistry> const input_device_registry;
1150+ std::shared_ptr<dispatch::MultiplexingDispatchable> const platform_dispatchable;
1151+ std::shared_ptr<dispatch::ReadableFd> const monitor_dispatchable;
1152+ std::shared_ptr<::libinput> const lib;
1153+ std::shared_ptr<dispatch::ReadableFd> const libinput_dispatchable;
1154+
1155+ std::vector<std::shared_ptr<LibInputDevice>> devices;
1156+ auto find_device(char const* devnode) -> decltype(devices)::iterator;
1157+ auto find_device(libinput_device *) -> decltype(devices)::iterator;
1158+ auto find_device(libinput_device_group const* group) -> decltype(devices)::iterator;
1159+
1160+ friend class MonitorDispatchable;
1161+};
1162+}
1163+}
1164+}
1165+
1166+#endif
1167
1168=== added file 'src/platforms/evdev/platform_factory.cpp'
1169--- src/platforms/evdev/platform_factory.cpp 1970-01-01 00:00:00 +0000
1170+++ src/platforms/evdev/platform_factory.cpp 2015-09-18 10:27:31 +0000
1171@@ -0,0 +1,68 @@
1172+/*
1173+ * Copyright © 2015 Canonical Ltd.
1174+ *
1175+ * This program is free software: you can redistribute it and/or modify it
1176+ * under the terms of the GNU Lesser General Public License version 3,
1177+ * as published by the Free Software Foundation.
1178+ *
1179+ * This program is distributed in the hope that it will be useful,
1180+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
1181+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
1182+ * GNU Lesser General Public License for more details.
1183+ *
1184+ * You should have received a copy of the GNU Lesser General Public License
1185+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
1186+ *
1187+ * Authored by: Andreas Pokorny <andreas.pokorny@canonical.com>
1188+ */
1189+
1190+#include "platform.h"
1191+#include "mir/udev/wrapper.h"
1192+
1193+namespace mo = mir::options;
1194+namespace mi = mir::input;
1195+namespace mu = mir::udev;
1196+namespace mie = mi::evdev;
1197+
1198+namespace
1199+{
1200+char const* const host_socket_opt = "host-socket";
1201+mir::ModuleProperties const description = {
1202+ "evdev-input",
1203+ MIR_VERSION_MAJOR,
1204+ MIR_VERSION_MINOR,
1205+ MIR_VERSION_MICRO
1206+};
1207+}
1208+
1209+mir::UniqueModulePtr<mi::Platform> create_input_platform(
1210+ std::shared_ptr<mo::Option> const& /*options*/,
1211+ std::shared_ptr<mir::EmergencyCleanupRegistry> const& /*emergency_cleanup_registry*/,
1212+ std::shared_ptr<mi::InputDeviceRegistry> const& input_device_registry,
1213+ std::shared_ptr<mi::InputReport> const& report)
1214+{
1215+ auto ctx = std::make_unique<mu::Context>();
1216+ auto monitor = std::make_unique<mu::Monitor>(*ctx.get());
1217+ return mir::make_module_ptr<mie::Platform>(input_device_registry, report, std::move(ctx), std::move(monitor));
1218+}
1219+
1220+void add_input_platform_options(
1221+ boost::program_options::options_description& /*config*/)
1222+{
1223+ // no options to add yet
1224+}
1225+
1226+mi::PlatformPriority probe_input_platform(
1227+ mo::Option const& options)
1228+{
1229+ if (options.is_set(host_socket_opt))
1230+ {
1231+ return mi::PlatformPriority::unsupported;
1232+ }
1233+ return mi::PlatformPriority::supported;
1234+}
1235+
1236+mir::ModuleProperties const* describe_input_module()
1237+{
1238+ return &description;
1239+}
1240
1241=== renamed file 'tests/mir_test_framework/symbols-stub-input.map' => 'src/platforms/input_platform_symbols.map'
1242=== added file 'tests/include/mir/test/doubles/mock_libinput.h'
1243--- tests/include/mir/test/doubles/mock_libinput.h 1970-01-01 00:00:00 +0000
1244+++ tests/include/mir/test/doubles/mock_libinput.h 2015-09-18 10:27:31 +0000
1245@@ -0,0 +1,110 @@
1246+/*
1247+ * Copyright © 2015 Canonical Ltd.
1248+ *
1249+ * This program is free software: you can redistribute it and/or modify
1250+ * it under the terms of the GNU General Public License version 3 as
1251+ * published by the Free Software Foundation.
1252+ *
1253+ * This program is distributed in the hope that it will be useful,
1254+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
1255+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
1256+ * GNU General Public License for more details.
1257+ *
1258+ * You should have received a copy of the GNU General Public License
1259+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
1260+ *
1261+ * Authored by: Andreas Pokorny <andreas.pokorny@canonical.com>
1262+ */
1263+
1264+#ifndef MIR_TEST_DOUBLES_MOCK_LIBINPUT_H_
1265+#define MIR_TEST_DOUBLES_MOCK_LIBINPUT_H_
1266+
1267+#include "mir/dispatch/action_queue.h"
1268+
1269+#include <gmock/gmock.h>
1270+
1271+#include <libinput.h>
1272+
1273+namespace mir
1274+{
1275+namespace test
1276+{
1277+namespace doubles
1278+{
1279+
1280+class MockLibInput
1281+{
1282+public:
1283+ MockLibInput();
1284+ ~MockLibInput() noexcept;
1285+ void wake();
1286+
1287+ MOCK_METHOD1(libinput_ref, libinput*(libinput*));
1288+ MOCK_METHOD1(libinput_unref, libinput*(libinput*));
1289+ MOCK_METHOD1(libinput_dispatch, int(libinput*));
1290+ MOCK_METHOD1(libinput_get_fd, int(libinput*));
1291+ MOCK_METHOD1(libinput_get_event, libinput_event*(libinput*));
1292+ MOCK_METHOD1(libinput_event_get_type, libinput_event_type(libinput_event*));
1293+ MOCK_METHOD1(libinput_event_destroy, void(libinput_event*));
1294+ MOCK_METHOD1(libinput_event_get_device, libinput_device*(libinput_event*));
1295+ MOCK_METHOD1(libinput_event_get_pointer_event, libinput_event_pointer*(libinput_event*));
1296+ MOCK_METHOD1(libinput_event_get_keyboard_event, libinput_event_keyboard*(libinput_event*));
1297+ MOCK_METHOD1(libinput_event_get_touch_event, libinput_event_touch*(libinput_event*));
1298+
1299+ MOCK_METHOD1(libinput_event_keyboard_get_time, uint32_t(libinput_event_keyboard*));
1300+ MOCK_METHOD1(libinput_event_keyboard_get_time_usec, uint64_t(libinput_event_keyboard*));
1301+ MOCK_METHOD1(libinput_event_keyboard_get_key, uint32_t(libinput_event_keyboard*));
1302+ MOCK_METHOD1(libinput_event_keyboard_get_key_state, libinput_key_state(libinput_event_keyboard*));
1303+ MOCK_METHOD1(libinput_event_keyboard_get_seat_key_count, uint32_t(libinput_event_keyboard*));
1304+
1305+ MOCK_METHOD1(libinput_event_pointer_get_time, uint32_t(libinput_event_pointer*));
1306+ MOCK_METHOD1(libinput_event_pointer_get_time_usec, uint64_t(libinput_event_pointer*));
1307+ MOCK_METHOD1(libinput_event_pointer_get_dx, double(libinput_event_pointer*));
1308+ MOCK_METHOD1(libinput_event_pointer_get_dy, double(libinput_event_pointer*));
1309+ MOCK_METHOD1(libinput_event_pointer_get_absolute_x, double(libinput_event_pointer*));
1310+ MOCK_METHOD1(libinput_event_pointer_get_absolute_y, double(libinput_event_pointer*));
1311+ MOCK_METHOD2(libinput_event_pointer_get_absolute_x_transformed, double(libinput_event_pointer*, uint32_t));
1312+ MOCK_METHOD2(libinput_event_pointer_get_absolute_y_transformed, double(libinput_event_pointer*, uint32_t));
1313+ MOCK_METHOD1(libinput_event_pointer_get_button, uint32_t(libinput_event_pointer*));
1314+ MOCK_METHOD1(libinput_event_pointer_get_button_state, libinput_button_state(libinput_event_pointer*));
1315+ MOCK_METHOD1(libinput_event_pointer_get_seat_button_count, uint32_t(libinput_event_pointer*));
1316+ MOCK_METHOD1(libinput_event_pointer_get_axis, libinput_pointer_axis(libinput_event_pointer*));
1317+ MOCK_METHOD2(libinput_event_pointer_get_axis_value, double(libinput_event_pointer*, libinput_pointer_axis));
1318+ MOCK_METHOD2(libinput_event_pointer_has_axis,int(libinput_event_pointer *,libinput_pointer_axis));
1319+
1320+ MOCK_METHOD1(libinput_event_touch_get_time, uint32_t(libinput_event_touch*));
1321+ MOCK_METHOD1(libinput_event_touch_get_time_usec, uint64_t(libinput_event_touch*));
1322+ MOCK_METHOD1(libinput_event_touch_get_slot, int32_t(libinput_event_touch*));
1323+ MOCK_METHOD1(libinput_event_touch_get_seat_slot, int32_t(libinput_event_touch*));
1324+ MOCK_METHOD1(libinput_event_touch_get_x, double(libinput_event_touch*));
1325+ MOCK_METHOD1(libinput_event_touch_get_y, double(libinput_event_touch*));
1326+ MOCK_METHOD2(libinput_event_touch_get_x_transformed, double(libinput_event_touch*, uint32_t));
1327+ MOCK_METHOD2(libinput_event_touch_get_y_transformed, double(libinput_event_touch*, uint32_t));
1328+ MOCK_METHOD1(libinput_event_touch_get_pressure, double(libinput_event_touch*));
1329+ MOCK_METHOD1(libinput_event_touch_get_minor, double(libinput_event_touch*));
1330+ MOCK_METHOD3(libinput_event_touch_get_minor_transformed, double(libinput_event_touch*, uint32_t, uint32_t));
1331+ MOCK_METHOD1(libinput_event_touch_get_major, double(libinput_event_touch*));
1332+ MOCK_METHOD3(libinput_event_touch_get_major_transformed, double(libinput_event_touch*, uint32_t, uint32_t));
1333+ MOCK_METHOD1(libinput_event_touch_get_orientation, double(libinput_event_touch*));
1334+
1335+ MOCK_METHOD2(libinput_path_create_context, libinput*(const libinput_interface *, void*));
1336+ MOCK_METHOD2(libinput_path_add_device, libinput_device*(const libinput*, const char*));
1337+ MOCK_METHOD1(libinput_path_remove_device, void(libinput_device*));
1338+
1339+ MOCK_METHOD1(libinput_device_unref, libinput_device*(libinput_device*));
1340+ MOCK_METHOD1(libinput_device_ref, libinput_device*(libinput_device*));
1341+ MOCK_METHOD1(libinput_device_get_name, char const*(libinput_device*));
1342+ MOCK_METHOD1(libinput_device_get_id_product, unsigned int(libinput_device*));
1343+ MOCK_METHOD1(libinput_device_get_id_vendor, unsigned int(libinput_device*));
1344+ MOCK_METHOD1(libinput_device_get_sysname, char const*(libinput_device*));
1345+ MOCK_METHOD1(libinput_device_get_device_group, libinput_device_group*(libinput_device*));
1346+
1347+private:
1348+ dispatch::ActionQueue libinput_simulation_queue;
1349+};
1350+
1351+}
1352+}
1353+}
1354+
1355+#endif
1356
1357=== modified file 'tests/mir_test_doubles/CMakeLists.txt'
1358--- tests/mir_test_doubles/CMakeLists.txt 2015-08-26 22:28:45 +0000
1359+++ tests/mir_test_doubles/CMakeLists.txt 2015-09-18 10:27:31 +0000
1360@@ -25,6 +25,7 @@
1361 set(
1362 MIR_TEST_DOUBLES_PLATFORM_SRCS
1363
1364+ ${CMAKE_CURRENT_SOURCE_DIR}/mock_libinput.cpp
1365 ${CMAKE_CURRENT_SOURCE_DIR}/mock_egl.cpp
1366 ${CMAKE_CURRENT_SOURCE_DIR}/mock_gl.cpp
1367 )
1368
1369=== added file 'tests/mir_test_doubles/mock_libinput.cpp'
1370--- tests/mir_test_doubles/mock_libinput.cpp 1970-01-01 00:00:00 +0000
1371+++ tests/mir_test_doubles/mock_libinput.cpp 2015-09-18 10:27:31 +0000
1372@@ -0,0 +1,316 @@
1373+/*
1374+ * Copyright © 2015 Canonical Ltd.
1375+ *
1376+ * This program is free software: you can redistribute it and/or modify
1377+ * it under the terms of the GNU General Public License version 3 as
1378+ * published by the Free Software Foundation.
1379+ *
1380+ * This program is distributed in the hope that it will be useful,
1381+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
1382+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
1383+ * GNU General Public License for more details.
1384+ *
1385+ * You should have received a copy of the GNU General Public License
1386+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
1387+ *
1388+ * Authored by: Andreas Pokorny <andreas.pokorny@canonical.com>
1389+ */
1390+
1391+#include "mir/test/doubles/mock_libinput.h"
1392+
1393+namespace mtd = mir::test::doubles;
1394+
1395+namespace
1396+{
1397+mtd::MockLibInput* global_libinput = nullptr;
1398+}
1399+
1400+mtd::MockLibInput::MockLibInput()
1401+{
1402+ using namespace testing;
1403+ assert(global_libinput == NULL && "Only one mock object per process is allowed");
1404+ global_libinput = this;
1405+
1406+ ON_CALL(*this, libinput_device_ref(_)).WillByDefault(ReturnArg<0>());
1407+ ON_CALL(*this, libinput_get_fd(_)).WillByDefault(Return(int(libinput_simulation_queue.watch_fd())));
1408+}
1409+
1410+void mtd::MockLibInput::wake()
1411+{
1412+ libinput_simulation_queue.enqueue([]{});
1413+}
1414+
1415+mtd::MockLibInput::~MockLibInput() noexcept
1416+{
1417+ global_libinput = nullptr;
1418+}
1419+
1420+void libinput_event_destroy(libinput_event* event)
1421+{
1422+ global_libinput->libinput_event_destroy(event);
1423+}
1424+
1425+libinput_event_type libinput_event_get_type(libinput_event* event)
1426+{
1427+ return global_libinput->libinput_event_get_type(event);
1428+}
1429+
1430+libinput_device* libinput_event_get_device(libinput_event* event)
1431+{
1432+ return global_libinput->libinput_event_get_device(event);
1433+}
1434+
1435+libinput_event_pointer* libinput_event_get_pointer_event(libinput_event* event)
1436+{
1437+ return global_libinput->libinput_event_get_pointer_event(event);
1438+}
1439+
1440+libinput_event_keyboard* libinput_event_get_keyboard_event(libinput_event* event)
1441+{
1442+ return global_libinput->libinput_event_get_keyboard_event(event);
1443+}
1444+
1445+libinput_event_touch* libinput_event_get_touch_event(libinput_event* event)
1446+{
1447+ return global_libinput->libinput_event_get_touch_event(event);
1448+}
1449+
1450+uint32_t libinput_event_keyboard_get_time(libinput_event_keyboard* event)
1451+{
1452+ return global_libinput->libinput_event_keyboard_get_time(event);
1453+}
1454+
1455+uint64_t libinput_event_keyboard_get_time_usec(libinput_event_keyboard* event)
1456+{
1457+ return global_libinput->libinput_event_keyboard_get_time_usec(event);
1458+}
1459+
1460+uint32_t libinput_event_keyboard_get_key(libinput_event_keyboard* event)
1461+{
1462+ return global_libinput->libinput_event_keyboard_get_key(event);
1463+}
1464+
1465+libinput_key_state libinput_event_keyboard_get_key_state(libinput_event_keyboard* event)
1466+{
1467+ return global_libinput->libinput_event_keyboard_get_key_state(event);
1468+}
1469+
1470+uint32_t libinput_event_keyboard_get_seat_key_count(libinput_event_keyboard* event)
1471+{
1472+ return global_libinput->libinput_event_keyboard_get_seat_key_count(event);
1473+}
1474+
1475+uint32_t libinput_event_pointer_get_time(libinput_event_pointer* event)
1476+{
1477+ return global_libinput->libinput_event_pointer_get_time(event);
1478+}
1479+
1480+uint64_t libinput_event_pointer_get_time_usec(libinput_event_pointer* event)
1481+{
1482+ return global_libinput->libinput_event_pointer_get_time_usec(event);
1483+}
1484+
1485+double libinput_event_pointer_get_dx(libinput_event_pointer* event)
1486+{
1487+ return global_libinput->libinput_event_pointer_get_dx(event);
1488+}
1489+
1490+double libinput_event_pointer_get_dy(libinput_event_pointer* event)
1491+{
1492+ return global_libinput->libinput_event_pointer_get_dy(event);
1493+}
1494+
1495+double libinput_event_pointer_get_absolute_x(libinput_event_pointer* event)
1496+{
1497+ return global_libinput->libinput_event_pointer_get_absolute_x(event);
1498+}
1499+
1500+double libinput_event_pointer_get_absolute_y(libinput_event_pointer* event)
1501+{
1502+ return global_libinput->libinput_event_pointer_get_absolute_y(event);
1503+}
1504+
1505+double libinput_event_pointer_get_absolute_x_transformed(libinput_event_pointer* event, uint32_t width)
1506+{
1507+ return global_libinput->libinput_event_pointer_get_absolute_x_transformed(event, width);
1508+}
1509+
1510+double libinput_event_pointer_get_absolute_y_transformed(libinput_event_pointer* event, uint32_t height)
1511+{
1512+ return global_libinput->libinput_event_pointer_get_absolute_y_transformed(event, height);
1513+}
1514+
1515+uint32_t libinput_event_pointer_get_button(libinput_event_pointer* event)
1516+{
1517+ return global_libinput->libinput_event_pointer_get_button(event);
1518+}
1519+
1520+libinput_button_state libinput_event_pointer_get_button_state(libinput_event_pointer* event)
1521+{
1522+ return global_libinput->libinput_event_pointer_get_button_state(event);
1523+}
1524+
1525+uint32_t libinput_event_pointer_get_seat_button_count(libinput_event_pointer* event)
1526+{
1527+ return global_libinput->libinput_event_pointer_get_seat_button_count(event);
1528+}
1529+
1530+libinput_pointer_axis libinput_event_pointer_get_axis(libinput_event_pointer* event)
1531+{
1532+ return global_libinput->libinput_event_pointer_get_axis(event);
1533+}
1534+
1535+double libinput_event_pointer_get_axis_value(libinput_event_pointer* event, libinput_pointer_axis axis)
1536+{
1537+ return global_libinput->libinput_event_pointer_get_axis_value(event, axis);
1538+}
1539+
1540+int libinput_event_pointer_has_axis(libinput_event_pointer* event, libinput_pointer_axis axis)
1541+{
1542+ return global_libinput->libinput_event_pointer_has_axis(event, axis);
1543+}
1544+
1545+uint32_t libinput_event_touch_get_time(libinput_event_touch* event)
1546+{
1547+ return global_libinput->libinput_event_touch_get_time(event);
1548+}
1549+
1550+uint64_t libinput_event_touch_get_time_usec(libinput_event_touch* event)
1551+{
1552+ return global_libinput->libinput_event_touch_get_time_usec(event);
1553+}
1554+
1555+int32_t libinput_event_touch_get_slot(libinput_event_touch* event)
1556+{
1557+ return global_libinput->libinput_event_touch_get_slot(event);
1558+}
1559+
1560+int32_t libinput_event_touch_get_seat_slot(libinput_event_touch* event)
1561+{
1562+ return global_libinput->libinput_event_touch_get_seat_slot(event);
1563+}
1564+
1565+double libinput_event_touch_get_x(libinput_event_touch* event)
1566+{
1567+ return global_libinput->libinput_event_touch_get_x(event);
1568+}
1569+
1570+double libinput_event_touch_get_y(libinput_event_touch* event)
1571+{
1572+ return global_libinput->libinput_event_touch_get_y(event);
1573+}
1574+
1575+double libinput_event_touch_get_x_transformed(libinput_event_touch* event, uint32_t width)
1576+{
1577+ return global_libinput->libinput_event_touch_get_x_transformed(event, width);
1578+}
1579+
1580+double libinput_event_touch_get_y_transformed(libinput_event_touch* event, uint32_t height)
1581+{
1582+ return global_libinput->libinput_event_touch_get_y_transformed(event, height);
1583+}
1584+
1585+double libinput_event_touch_get_major(libinput_event_touch* event)
1586+{
1587+ return global_libinput->libinput_event_touch_get_major(event);
1588+}
1589+
1590+double libinput_event_touch_get_minor(libinput_event_touch* event)
1591+{
1592+ return global_libinput->libinput_event_touch_get_minor(event);
1593+}
1594+
1595+double libinput_event_touch_get_major_transformed(libinput_event_touch* event, uint32_t width, uint32_t height)
1596+{
1597+ return global_libinput->libinput_event_touch_get_major_transformed(event, width, height);
1598+}
1599+
1600+double libinput_event_touch_get_minor_transformed(libinput_event_touch* event, uint32_t width, uint32_t height)
1601+{
1602+ return global_libinput->libinput_event_touch_get_minor_transformed(event, width, height);
1603+}
1604+
1605+double libinput_event_touch_get_pressure(libinput_event_touch* event)
1606+{
1607+ return global_libinput->libinput_event_touch_get_pressure(event);
1608+}
1609+
1610+double libinput_event_touch_get_orientation(libinput_event_touch* event)
1611+{
1612+ return global_libinput->libinput_event_touch_get_orientation(event);
1613+}
1614+
1615+libinput* libinput_path_create_context(const libinput_interface* interface, void* user_data)
1616+{
1617+ return global_libinput->libinput_path_create_context(interface, user_data);
1618+}
1619+
1620+libinput_device* libinput_path_add_device(libinput* libinput, const char* path)
1621+{
1622+ return global_libinput->libinput_path_add_device(libinput, path);
1623+}
1624+
1625+void libinput_path_remove_device(libinput_device* device)
1626+{
1627+ return global_libinput->libinput_path_remove_device(device);
1628+}
1629+
1630+int libinput_get_fd(libinput* libinput)
1631+{
1632+ return global_libinput->libinput_get_fd(libinput);
1633+}
1634+
1635+int libinput_dispatch(libinput* libinput)
1636+{
1637+ return global_libinput->libinput_dispatch(libinput);
1638+}
1639+
1640+libinput_event* libinput_get_event(libinput* libinput)
1641+{
1642+ return global_libinput->libinput_get_event(libinput);
1643+}
1644+
1645+libinput* libinput_ref(libinput* libinput)
1646+{
1647+ return global_libinput->libinput_ref(libinput);
1648+}
1649+
1650+libinput* libinput_unref(libinput* libinput)
1651+{
1652+ return global_libinput->libinput_unref(libinput);
1653+}
1654+
1655+libinput_device* libinput_device_ref(libinput_device* device)
1656+{
1657+ return global_libinput->libinput_device_ref(device);
1658+}
1659+
1660+libinput_device* libinput_device_unref(libinput_device* device)
1661+{
1662+ return global_libinput->libinput_device_unref(device);
1663+}
1664+
1665+char const* libinput_device_get_name(libinput_device* device)
1666+{
1667+ return global_libinput->libinput_device_get_name(device);
1668+}
1669+
1670+unsigned int libinput_device_get_id_vendor(libinput_device* device)
1671+{
1672+ return global_libinput->libinput_device_get_id_vendor(device);
1673+}
1674+
1675+unsigned int libinput_device_get_id_product(libinput_device* device)
1676+{
1677+ return global_libinput->libinput_device_get_id_product(device);
1678+}
1679+
1680+char const* libinput_device_get_sysname(libinput_device* device)
1681+{
1682+ return global_libinput->libinput_device_get_sysname(device);
1683+}
1684+
1685+libinput_device_group* libinput_device_get_device_group(libinput_device* device)
1686+{
1687+ return global_libinput->libinput_device_get_device_group(device);
1688+}
1689
1690=== modified file 'tests/mir_test_framework/CMakeLists.txt'
1691--- tests/mir_test_framework/CMakeLists.txt 2015-09-16 21:04:57 +0000
1692+++ tests/mir_test_framework/CMakeLists.txt 2015-09-18 10:27:31 +0000
1693@@ -115,7 +115,7 @@
1694 LIBRARY_OUTPUT_DIRECTORY ${CMAKE_LIBRARY_OUTPUT_DIRECTORY}/server-modules
1695 OUTPUT_NAME input-stub
1696 PREFIX ""
1697- LINK_FLAGS "-Wl,--version-script,${CMAKE_CURRENT_SOURCE_DIR}/symbols-stub-input.map"
1698+ LINK_FLAGS "-Wl,--version-script,${MIR_INPUT_PLATFORM_VERSION_SCRIPT}"
1699 )
1700
1701 install(TARGETS mirplatforminputstub LIBRARY DESTINATION ${MIR_SERVER_PLATFORM_PATH})
1702
1703=== modified file 'tests/mir_test_framework/fake_input_device_impl.cpp'
1704--- tests/mir_test_framework/fake_input_device_impl.cpp 2015-09-01 07:41:29 +0000
1705+++ tests/mir_test_framework/fake_input_device_impl.cpp 2015-09-18 10:27:31 +0000
1706@@ -104,9 +104,9 @@
1707 auto key_event = builder->key_event(event_time, input_action, key_code, key_params.scancode, event_modifiers);
1708
1709 if (key_params.action == synthesis::EventAction::Down)
1710- modifiers |= mie::to_modifier(key_params.scancode);
1711+ modifiers |= mie::to_modifiers(key_params.scancode);
1712 else
1713- modifiers &= ~mie::to_modifier(key_params.scancode);
1714+ modifiers &= ~mie::to_modifiers(key_params.scancode);
1715
1716 if (!sink)
1717 BOOST_THROW_EXCEPTION(std::runtime_error("Device is not started."));
1718
1719=== modified file 'tests/unit-tests/input/evdev/CMakeLists.txt'
1720--- tests/unit-tests/input/evdev/CMakeLists.txt 2015-09-02 13:10:33 +0000
1721+++ tests/unit-tests/input/evdev/CMakeLists.txt 2015-09-18 10:27:31 +0000
1722@@ -1,6 +1,9 @@
1723 list(APPEND UNIT_TEST_SOURCES
1724 ${CMAKE_CURRENT_SOURCE_DIR}/test_evdev_device_detection.cpp
1725+ ${CMAKE_CURRENT_SOURCE_DIR}/test_evdev_input_platform.cpp
1726+ ${CMAKE_CURRENT_SOURCE_DIR}/test_libinput_device.cpp
1727 $<TARGET_OBJECTS:mirevdevutilsobjects>
1728+ $<TARGET_OBJECTS:mirplatforminputevdevobjects>
1729 )
1730
1731 set(
1732
1733=== added file 'tests/unit-tests/input/evdev/test_evdev_input_platform.cpp'
1734--- tests/unit-tests/input/evdev/test_evdev_input_platform.cpp 1970-01-01 00:00:00 +0000
1735+++ tests/unit-tests/input/evdev/test_evdev_input_platform.cpp 2015-09-18 10:27:31 +0000
1736@@ -0,0 +1,264 @@
1737+/*
1738+ * Copyright © 2015 Canonical Ltd.
1739+ *
1740+ * This program is free software: you can redistribute it and/or modify it
1741+ * under the terms of the GNU General Public License version 3,
1742+ * as published by the Free Software Foundation.
1743+ *
1744+ * This program is distributed in the hope that it will be useful,
1745+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
1746+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
1747+ * GNU General Public License for more details.
1748+ *
1749+ * You should have received a copy of the GNU General Public License
1750+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
1751+ *
1752+ * Authored by: Andreas Pokorny <andreas.pokorny@canonical.com>
1753+ */
1754+
1755+#include "src/platforms/evdev/platform.h"
1756+#include "src/server/report/null_report_factory.h"
1757+
1758+#include "mir/input/input_device_registry.h"
1759+#include "mir/dispatch/dispatchable.h"
1760+
1761+#include "mir/udev/wrapper.h"
1762+#include "mir_test_framework/udev_environment.h"
1763+#include "mir/test/fake_shared.h"
1764+#include "mir/test/doubles/mock_libinput.h"
1765+
1766+#include <gtest/gtest.h>
1767+#include <gmock/gmock.h>
1768+
1769+#include <umockdev.h>
1770+#include <memory>
1771+#include <vector>
1772+#include <initializer_list>
1773+
1774+namespace mi = mir::input;
1775+namespace mie = mi::evdev;
1776+namespace mr = mir::report;
1777+namespace mu = mir::udev;
1778+namespace mt = mir::test;
1779+namespace mtd = mt::doubles;
1780+
1781+using ::testing::_;
1782+namespace
1783+{
1784+
1785+struct MockInputDeviceRegistry : public mi::InputDeviceRegistry
1786+{
1787+ MOCK_METHOD1(add_device, void(std::shared_ptr<mi::InputDevice> const&));
1788+ MOCK_METHOD1(remove_device, void(std::shared_ptr<mi::InputDevice> const&));
1789+};
1790+
1791+struct EvdevInputPlatform : public ::testing::TestWithParam<std::string>
1792+{
1793+ mir_test_framework::UdevEnvironment env;
1794+ testing::NiceMock<mtd::MockLibInput> mock_libinput;
1795+ testing::NiceMock<MockInputDeviceRegistry> mock_registry;
1796+ libinput *li_context{reinterpret_cast<libinput*>(0xFEBA)};
1797+
1798+ std::vector<std::pair<std::string, libinput_device*>> devices;
1799+ std::vector<std::pair<libinput_device_group*, std::vector<libinput_device*>>> groups;
1800+
1801+ EvdevInputPlatform()
1802+ {
1803+ using namespace testing;
1804+ ON_CALL(mock_libinput, libinput_path_create_context(_,_))
1805+ .WillByDefault(Return(li_context));
1806+ ON_CALL(mock_libinput, libinput_path_add_device(li_context,_))
1807+ .WillByDefault(Invoke(
1808+ [this](libinput const*, char const* path) -> libinput_device*
1809+ {
1810+ return open_device_from_path(path);
1811+ }
1812+ ));
1813+ ON_CALL(mock_libinput, libinput_device_get_device_group(_))
1814+ .WillByDefault(Invoke(
1815+ [this](libinput_device *dev)
1816+ {
1817+ return get_device_group(dev);
1818+ }));
1819+ }
1820+
1821+ auto get_device_group(libinput_device *dev) -> libinput_device_group*
1822+ {
1823+ auto it = find_if(begin(groups),
1824+ end(groups),
1825+ [dev] (auto const& item)
1826+ {
1827+ return end(item.second) != find(begin(item.second), end(item.second), dev);
1828+ });
1829+ if (end(groups) == it)
1830+ {
1831+ groups.emplace_back(get_next_fake_ptr<libinput_device_group*>(groups), std::vector<libinput_device*>{dev});
1832+ return groups.back().first;
1833+ }
1834+
1835+ return it->first;
1836+ }
1837+
1838+
1839+ template<typename PtrT>
1840+ PtrT to_fake_ptr(unsigned int number)
1841+ {
1842+ return reinterpret_cast<PtrT>(number);
1843+ }
1844+
1845+ template<typename PtrT, typename Container>
1846+ PtrT get_next_fake_ptr(Container const& container)
1847+ {
1848+ return to_fake_ptr<PtrT>(container.size()+1);
1849+ }
1850+
1851+ void setup_groupped_devices(std::initializer_list<libinput_device*> devices)
1852+ {
1853+ groups.emplace_back(get_next_fake_ptr<libinput_device_group*>(groups), devices);
1854+ }
1855+
1856+ auto open_device_from_path(char const* path) -> libinput_device*
1857+ {
1858+ auto it = find_if(begin(devices),
1859+ end(devices),
1860+ [path] (auto const& item)
1861+ {
1862+ return item.first == path;
1863+ });
1864+ if (end(devices) == it)
1865+ {
1866+ devices.emplace_back(path, get_next_fake_ptr<libinput_device*>(devices));
1867+ return devices.back().second;
1868+ }
1869+
1870+ return it->second;
1871+ }
1872+
1873+ auto create_input_platform()
1874+ {
1875+ auto ctx = std::make_unique<mu::Context>();
1876+ auto monitor = std::make_unique<mu::Monitor>(*ctx.get());
1877+ return std::make_unique<mie::Platform>(mt::fake_shared(mock_registry), mr::null_input_report(), std::move(ctx),
1878+ std::move(monitor));
1879+ }
1880+
1881+ void remove_devices()
1882+ {
1883+ mir::udev::Enumerator devices{std::make_shared<mir::udev::Context>()};
1884+ devices.scan_devices();
1885+
1886+ for (auto& device : devices)
1887+ {
1888+ /*
1889+ * Remove just the device providing dev/input/event*
1890+ * If we remove more, it's possible that we'll remove the parent of the
1891+ * /dev/input device, and umockdev will not generate a remove event
1892+ * in that case.
1893+ */
1894+ if (device.devnode() && (std::string(device.devnode()).find("input/event") != std::string::npos))
1895+ {
1896+ env.remove_device((std::string("/sys") + device.devpath()).c_str());
1897+ }
1898+ }
1899+ }
1900+};
1901+
1902+}
1903+
1904+inline void run_dispatchable( mir::input::Platform& platform)
1905+{
1906+ platform.dispatchable()->dispatch(mir::dispatch::FdEvent::readable);
1907+}
1908+
1909+TEST_P(EvdevInputPlatform, scans_on_start)
1910+{
1911+ env.add_standard_device(GetParam());
1912+
1913+ using namespace ::testing;
1914+ auto platform = create_input_platform();
1915+
1916+ EXPECT_CALL(mock_registry, add_device(_));
1917+
1918+ platform->start();
1919+}
1920+
1921+TEST_P(EvdevInputPlatform, detects_on_hotplug)
1922+{
1923+ using namespace ::testing;
1924+ auto platform = create_input_platform();
1925+
1926+ platform->start();
1927+
1928+ {
1929+ EXPECT_CALL(mock_registry, add_device(_));
1930+ env.add_standard_device(GetParam());
1931+
1932+ run_dispatchable(*platform);
1933+ }
1934+}
1935+
1936+TEST_P(EvdevInputPlatform, detects_hot_removal)
1937+{
1938+ using namespace ::testing;
1939+ auto platform = create_input_platform();
1940+
1941+ platform->start();
1942+
1943+ {
1944+ EXPECT_CALL(mock_registry, add_device(_));
1945+ EXPECT_CALL(mock_registry, remove_device(_));
1946+
1947+ env.add_standard_device(GetParam());
1948+ run_dispatchable(*platform);
1949+ remove_devices();
1950+ run_dispatchable(*platform);
1951+ }
1952+}
1953+
1954+TEST_P(EvdevInputPlatform, removes_devices_on_stop)
1955+{
1956+ using namespace ::testing;
1957+ auto platform = create_input_platform();
1958+
1959+ env.add_standard_device(GetParam());
1960+ platform->start();
1961+
1962+ EXPECT_CALL(mock_registry, remove_device(_));
1963+ platform->stop();
1964+}
1965+
1966+INSTANTIATE_TEST_CASE_P(DeviceHandling,
1967+ EvdevInputPlatform,
1968+ ::testing::Values(std::string("synaptics-touchpad"),
1969+ std::string("usb-keyboard"),
1970+ std::string("usb-mouse"),
1971+ std::string("laptop-keyboard"),
1972+ std::string("bluetooth-magic-trackpad")));
1973+
1974+
1975+
1976+TEST_F(EvdevInputPlatform, register_ungrouped_devices)
1977+{
1978+ auto platform = create_input_platform();
1979+ platform->start();
1980+
1981+ EXPECT_CALL(mock_registry, add_device(_)).Times(2);
1982+
1983+ env.add_standard_device("synaptics-touchpad");
1984+ env.add_standard_device("usb-keyboard");
1985+ run_dispatchable(*platform);
1986+}
1987+
1988+TEST_F(EvdevInputPlatform, ignore_devices_from_same_group)
1989+{
1990+ auto platform = create_input_platform();
1991+ platform->start();
1992+
1993+ EXPECT_CALL(mock_registry, add_device(_)).Times(1);
1994+
1995+ setup_groupped_devices({to_fake_ptr<libinput_device*>(1), to_fake_ptr<libinput_device*>(2)});
1996+
1997+ env.add_standard_device("synaptics-touchpad");
1998+ env.add_standard_device("usb-keyboard");
1999+ run_dispatchable(*platform);
2000+}
2001
2002=== added file 'tests/unit-tests/input/evdev/test_libinput_device.cpp'
2003--- tests/unit-tests/input/evdev/test_libinput_device.cpp 1970-01-01 00:00:00 +0000
2004+++ tests/unit-tests/input/evdev/test_libinput_device.cpp 2015-09-18 10:27:31 +0000
2005@@ -0,0 +1,498 @@
2006+/*
2007+ * Copyright © 2015 Canonical Ltd.
2008+ *
2009+ * This program is free software: you can redistribute it and/or modify it
2010+ * under the terms of the GNU General Public License version 3,
2011+ * as published by the Free Software Foundation.
2012+ *
2013+ * This program is distributed in the hope that it will be useful,
2014+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
2015+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
2016+ * GNU General Public License for more details.
2017+ *
2018+ * You should have received a copy of the GNU General Public License
2019+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
2020+ *
2021+ * Authored by: Andreas Pokorny <andreas.pokorny@canonical.com>
2022+ */
2023+
2024+#include "src/platforms/evdev/libinput_device.h"
2025+#include "src/server/report/null_report_factory.h"
2026+#include "src/server/input/default_event_builder.h"
2027+
2028+#include "mir/input/input_device_registry.h"
2029+#include "mir/input/input_sink.h"
2030+#include "mir/geometry/point.h"
2031+#include "mir/geometry/rectangle.h"
2032+#include "mir/test/event_matchers.h"
2033+#include "mir/test/doubles/mock_libinput.h"
2034+#include "mir/test/gmock_fixes.h"
2035+
2036+#include <gmock/gmock.h>
2037+#include <gtest/gtest.h>
2038+#include <linux/input.h>
2039+
2040+#include <chrono>
2041+
2042+namespace mi = mir::input;
2043+namespace mie = mi::evdev;
2044+namespace mt = mir::test;
2045+namespace mtd = mt::doubles;
2046+namespace geom = mir::geometry;
2047+
2048+namespace
2049+{
2050+
2051+class StubInputDeviceRegistry : public mi::InputDeviceRegistry
2052+{
2053+public:
2054+ void add_device(std::shared_ptr<mi::InputDevice> const&) override {}
2055+ void remove_device(std::shared_ptr<mi::InputDevice> const&) override {}
2056+};
2057+
2058+using namespace ::testing;
2059+
2060+struct MockInputSink : mi::InputSink
2061+{
2062+ MockInputSink()
2063+ {
2064+ ON_CALL(*this, bounding_rectangle())
2065+ .WillByDefault(Return(geom::Rectangle({0,0}, {100,100})));
2066+ }
2067+ MOCK_METHOD1(handle_input,void(MirEvent &));
2068+ MOCK_METHOD1(confine_pointer, void(geom::Point&));
2069+ MOCK_CONST_METHOD0(bounding_rectangle, geom::Rectangle());
2070+};
2071+
2072+struct MockEventBuilder : mi::EventBuilder
2073+{
2074+ mi::DefaultEventBuilder builder{MirInputDeviceId{3}};
2075+ MockEventBuilder()
2076+ {
2077+ ON_CALL(*this, key_event(_,_,_,_,_))
2078+ .WillByDefault(Invoke([this](Timestamp time, MirKeyboardAction action, xkb_keysym_t key, int scan_code,
2079+ MirInputEventModifiers modifier)
2080+ {
2081+ return builder.key_event(time, action, key, scan_code, modifier);
2082+ }));
2083+ ON_CALL(*this, touch_event(_,_))
2084+ .WillByDefault(Invoke([this](Timestamp time, MirInputEventModifiers modifier)
2085+ {
2086+ return builder.touch_event(time, modifier);
2087+ }));
2088+ ON_CALL(*this, add_touch(_,_,_,_,_,_,_,_,_,_))
2089+ .WillByDefault(Invoke([this](MirEvent& event, MirTouchId id, MirTouchAction action,
2090+ MirTouchTooltype tooltype, float x, float y, float major, float minor,
2091+ float pressure, float size)
2092+ {
2093+ return builder.add_touch(event, id, action, tooltype, x, y, major, minor,
2094+ pressure, size);
2095+ }));
2096+ ON_CALL(*this, pointer_event(_,_,_,_,_,_,_,_,_,_))
2097+ .WillByDefault(Invoke([this](Timestamp time, MirInputEventModifiers modifier, MirPointerAction action,
2098+ MirPointerButtons buttons, float x, float y, float hscroll, float vscroll,
2099+ float relative_x, float relative_y)
2100+ {
2101+ return builder.pointer_event(time, modifier, action, buttons, x, y, hscroll,
2102+ vscroll, relative_x, relative_y);
2103+ }));
2104+ ON_CALL(*this, configuration_event(_,_))
2105+ .WillByDefault(Invoke([this](Timestamp time, MirInputConfigurationAction action)
2106+ {
2107+ return builder.configuration_event(time, action);
2108+ }));
2109+ }
2110+ using EventBuilder::Timestamp;
2111+ MOCK_METHOD5(key_event, mir::EventUPtr(Timestamp, MirKeyboardAction, xkb_keysym_t, int, MirInputEventModifiers));
2112+
2113+ MOCK_METHOD2(touch_event, mir::EventUPtr(Timestamp, MirInputEventModifiers));
2114+ MOCK_METHOD10(add_touch, void(MirEvent&, MirTouchId, MirTouchAction, MirTouchTooltype, float, float, float, float,
2115+ float, float));
2116+
2117+ MOCK_METHOD10(pointer_event, mir::EventUPtr(Timestamp, MirInputEventModifiers, MirPointerAction, MirPointerButtons,
2118+ float, float, float, float, float, float));
2119+ MOCK_METHOD2(configuration_event, mir::EventUPtr(Timestamp, MirInputConfigurationAction));
2120+};
2121+
2122+struct LibInputDevice : public ::testing::Test
2123+{
2124+ ::testing::NiceMock<mir::test::doubles::MockLibInput> mock_libinput;
2125+ ::testing::NiceMock<MockInputSink> mock_sink;
2126+ ::testing::NiceMock<MockEventBuilder> mock_builder;
2127+
2128+ libinput* fake_input = reinterpret_cast<libinput*>(0xF4C3);
2129+ libinput_device* fake_device = reinterpret_cast<libinput_device*>(0xF4C4);
2130+ libinput_event* fake_event_1 = reinterpret_cast<libinput_event*>(0xF4C5);
2131+ libinput_event* fake_event_2 = reinterpret_cast<libinput_event*>(0xF4C6);
2132+ libinput_event* fake_event_3 = reinterpret_cast<libinput_event*>(0xF4C7);
2133+ libinput_event* fake_event_4 = reinterpret_cast<libinput_event*>(0xF4C8);
2134+ libinput_device* second_fake_device = reinterpret_cast<libinput_device*>(0xF4C9);
2135+
2136+ const uint64_t event_time_1 = 1000;
2137+ const mi::EventBuilder::Timestamp time_stamp_1{std::chrono::microseconds{event_time_1}};
2138+ const uint64_t event_time_2 = 2000;
2139+ const mi::EventBuilder::Timestamp time_stamp_2{std::chrono::microseconds{event_time_2}};
2140+ const uint64_t event_time_3 = 3000;
2141+ const mi::EventBuilder::Timestamp time_stamp_3{std::chrono::microseconds{event_time_3}};
2142+ const uint64_t event_time_4 = 4000;
2143+ const mi::EventBuilder::Timestamp time_stamp_4{std::chrono::microseconds{event_time_4}};
2144+
2145+ char const* path = "/path/to/dev";
2146+
2147+ LibInputDevice()
2148+ {
2149+ ON_CALL(mock_libinput, libinput_path_create_context(_,_))
2150+ .WillByDefault(Return(fake_input));
2151+ ON_CALL(mock_libinput, libinput_path_add_device(fake_input,_))
2152+ .WillByDefault(Return(fake_device));
2153+ ON_CALL(mock_libinput, libinput_device_ref(fake_device))
2154+ .WillByDefault(Return(fake_device));
2155+ ON_CALL(mock_libinput, libinput_device_unref(fake_device))
2156+ .WillByDefault(Return(nullptr));
2157+ }
2158+
2159+ void setup_key_event(libinput_event* event, uint64_t event_time, uint32_t key, libinput_key_state state)
2160+ {
2161+ auto key_event = reinterpret_cast<libinput_event_keyboard*>(event);
2162+
2163+ EXPECT_CALL(mock_libinput, libinput_event_get_type(event))
2164+ .WillRepeatedly(Return(LIBINPUT_EVENT_KEYBOARD_KEY));
2165+ EXPECT_CALL(mock_libinput, libinput_event_get_keyboard_event(event))
2166+ .WillRepeatedly(Return(key_event));
2167+ EXPECT_CALL(mock_libinput, libinput_event_keyboard_get_time_usec(key_event))
2168+ .WillRepeatedly(Return(event_time));
2169+ EXPECT_CALL(mock_libinput, libinput_event_keyboard_get_key(key_event))
2170+ .WillRepeatedly(Return(key));
2171+ EXPECT_CALL(mock_libinput, libinput_event_keyboard_get_key_state(key_event))
2172+ .WillRepeatedly(Return(state));
2173+ }
2174+
2175+ void setup_pointer_event(libinput_event* event, uint64_t event_time, float relatve_x, float relatve_y)
2176+ {
2177+ auto pointer_event = reinterpret_cast<libinput_event_pointer*>(event);
2178+
2179+ EXPECT_CALL(mock_libinput, libinput_event_get_type(event))
2180+ .WillRepeatedly(Return(LIBINPUT_EVENT_POINTER_MOTION));
2181+ EXPECT_CALL(mock_libinput, libinput_event_get_pointer_event(event))
2182+ .WillRepeatedly(Return(pointer_event));
2183+ EXPECT_CALL(mock_libinput, libinput_event_pointer_get_time_usec(pointer_event))
2184+ .WillRepeatedly(Return(event_time));
2185+ EXPECT_CALL(mock_libinput, libinput_event_pointer_get_dx(pointer_event))
2186+ .WillRepeatedly(Return(relatve_x));
2187+ EXPECT_CALL(mock_libinput, libinput_event_pointer_get_dy(pointer_event))
2188+ .WillRepeatedly(Return(relatve_y));
2189+ }
2190+
2191+ void setup_button_event(libinput_event* event, uint64_t event_time, int button, libinput_button_state state)
2192+ {
2193+ auto pointer_event = reinterpret_cast<libinput_event_pointer*>(event);
2194+
2195+ EXPECT_CALL(mock_libinput, libinput_event_get_type(event))
2196+ .WillRepeatedly(Return(LIBINPUT_EVENT_POINTER_BUTTON));
2197+ EXPECT_CALL(mock_libinput, libinput_event_get_pointer_event(event))
2198+ .WillRepeatedly(Return(pointer_event));
2199+ EXPECT_CALL(mock_libinput, libinput_event_pointer_get_time_usec(pointer_event))
2200+ .WillRepeatedly(Return(event_time));
2201+ EXPECT_CALL(mock_libinput, libinput_event_pointer_get_button(pointer_event))
2202+ .WillRepeatedly(Return(button));
2203+ EXPECT_CALL(mock_libinput, libinput_event_pointer_get_button_state(pointer_event))
2204+ .WillRepeatedly(Return(state));
2205+ }
2206+
2207+ void setup_axis_event(libinput_event* event, uint64_t event_time, double horizontal, double vertical)
2208+ {
2209+ auto pointer_event = reinterpret_cast<libinput_event_pointer*>(event);
2210+
2211+ EXPECT_CALL(mock_libinput, libinput_event_get_type(event))
2212+ .WillRepeatedly(Return(LIBINPUT_EVENT_POINTER_AXIS));
2213+ EXPECT_CALL(mock_libinput, libinput_event_get_pointer_event(event))
2214+ .WillRepeatedly(Return(pointer_event));
2215+ EXPECT_CALL(mock_libinput, libinput_event_pointer_get_time_usec(pointer_event))
2216+ .WillRepeatedly(Return(event_time));
2217+ EXPECT_CALL(mock_libinput, libinput_event_pointer_has_axis(pointer_event, LIBINPUT_POINTER_AXIS_SCROLL_HORIZONTAL))
2218+ .WillRepeatedly(Return(horizontal!=0.0));
2219+ EXPECT_CALL(mock_libinput, libinput_event_pointer_has_axis(pointer_event, LIBINPUT_POINTER_AXIS_SCROLL_VERTICAL))
2220+ .WillRepeatedly(Return(vertical!=0.0));
2221+ EXPECT_CALL(mock_libinput, libinput_event_pointer_get_axis_value(pointer_event, LIBINPUT_POINTER_AXIS_SCROLL_VERTICAL))
2222+ .WillRepeatedly(Return(vertical));
2223+ EXPECT_CALL(mock_libinput, libinput_event_pointer_get_axis_value(pointer_event, LIBINPUT_POINTER_AXIS_SCROLL_HORIZONTAL))
2224+ .WillRepeatedly(Return(horizontal));
2225+ }
2226+
2227+ void setup_touch_event(libinput_event* event, libinput_event_type type, uint64_t event_time, int slot, float x,
2228+ float y, float major, float minor, float pressure)
2229+ {
2230+ auto touch_event = reinterpret_cast<libinput_event_touch*>(event);
2231+
2232+ EXPECT_CALL(mock_libinput, libinput_event_get_type(event))
2233+ .WillRepeatedly(Return(type));
2234+ EXPECT_CALL(mock_libinput, libinput_event_get_touch_event(event))
2235+ .WillRepeatedly(Return(touch_event));
2236+ EXPECT_CALL(mock_libinput, libinput_event_touch_get_slot(touch_event))
2237+ .WillRepeatedly(Return(slot));
2238+ EXPECT_CALL(mock_libinput, libinput_event_touch_get_x_transformed(touch_event, _))
2239+ .WillRepeatedly(Return(x));
2240+ EXPECT_CALL(mock_libinput, libinput_event_touch_get_y_transformed(touch_event, _))
2241+ .WillRepeatedly(Return(y));
2242+ EXPECT_CALL(mock_libinput, libinput_event_touch_get_time_usec(touch_event))
2243+ .WillRepeatedly(Return(event_time));
2244+ EXPECT_CALL(mock_libinput, libinput_event_touch_get_major_transformed(touch_event, _, _))
2245+ .WillRepeatedly(Return(major));
2246+ EXPECT_CALL(mock_libinput, libinput_event_touch_get_minor_transformed(touch_event, _, _))
2247+ .WillRepeatedly(Return(minor));
2248+ EXPECT_CALL(mock_libinput, libinput_event_touch_get_pressure(touch_event))
2249+ .WillRepeatedly(Return(pressure));
2250+
2251+ }
2252+
2253+ void setup_touch_frame(libinput_event* event)
2254+ {
2255+ EXPECT_CALL(mock_libinput, libinput_event_get_type(event))
2256+ .WillRepeatedly(Return(LIBINPUT_EVENT_TOUCH_FRAME));
2257+ }
2258+};
2259+
2260+}
2261+
2262+TEST_F(LibInputDevice, start_creates_and_unrefs_libinput_device_from_path)
2263+{
2264+ EXPECT_CALL(mock_libinput, libinput_path_add_device(fake_input,StrEq(path)))
2265+ .Times(1);
2266+ // according to manual libinput_path_add_device creates a temporary device with a ref count 0.
2267+ // hence it needs a manual ref call
2268+ EXPECT_CALL(mock_libinput, libinput_device_ref(fake_device))
2269+ .Times(1);
2270+ std::shared_ptr<libinput> lib = mie::make_libinput();
2271+ mie::LibInputDevice dev(mir::report::null_input_report(),
2272+ path,
2273+ mie::make_libinput_device(lib.get(), path));
2274+ dev.start(&mock_sink, &mock_builder);
2275+}
2276+
2277+TEST_F(LibInputDevice, open_device_of_group)
2278+{
2279+ char const* first_dev = "/path/to/dev1";
2280+ char const* second_dev = "/path/to/dev2";
2281+ std::shared_ptr<libinput> lib = mie::make_libinput();
2282+
2283+ InSequence seq;
2284+ EXPECT_CALL(mock_libinput, libinput_path_add_device(fake_input,StrEq(first_dev)));
2285+ // according to manual libinput_path_add_device creates a temporary device with a ref count 0.
2286+ // hence it needs a manual ref call
2287+ EXPECT_CALL(mock_libinput, libinput_device_ref(fake_device));
2288+ EXPECT_CALL(mock_libinput, libinput_path_add_device(fake_input,StrEq(second_dev)))
2289+ .WillOnce(Return(second_fake_device));
2290+ EXPECT_CALL(mock_libinput, libinput_device_ref(second_fake_device));
2291+
2292+ mie::LibInputDevice dev(mir::report::null_input_report(),
2293+ first_dev,
2294+ mie::make_libinput_device(lib.get(), first_dev));
2295+ dev.add_device_of_group(second_dev, mie::make_libinput_device(lib.get(), second_dev));
2296+ dev.start(&mock_sink, &mock_builder);
2297+}
2298+
2299+TEST_F(LibInputDevice, removal_unrefs_libinput_device)
2300+{
2301+ std::shared_ptr<libinput> lib = mie::make_libinput();
2302+
2303+ EXPECT_CALL(mock_libinput, libinput_device_unref(fake_device))
2304+ .Times(1);
2305+
2306+ mie::LibInputDevice dev(mir::report::null_input_report(), path, mie::make_libinput_device(lib.get(), path));
2307+}
2308+
2309+
2310+TEST_F(LibInputDevice, process_event_converts_key_event)
2311+{
2312+ std::shared_ptr<libinput> lib = mie::make_libinput();
2313+ mie::LibInputDevice dev(mir::report::null_input_report(), path, mie::make_libinput_device(lib.get(), path));
2314+
2315+ setup_key_event(fake_event_1, event_time_1, KEY_A, LIBINPUT_KEY_STATE_PRESSED);
2316+ setup_key_event(fake_event_2, event_time_2, KEY_A, LIBINPUT_KEY_STATE_RELEASED);
2317+
2318+ EXPECT_CALL(mock_builder, key_event(time_stamp_1, mir_keyboard_action_down, _, KEY_A, mir_input_event_modifier_none));
2319+ EXPECT_CALL(mock_sink, handle_input(AllOf(mt::KeyOfScanCode(KEY_A),mt::KeyDownEvent())));
2320+ EXPECT_CALL(mock_builder, key_event(time_stamp_2, mir_keyboard_action_up, _, KEY_A, mir_input_event_modifier_none));
2321+ EXPECT_CALL(mock_sink, handle_input(AllOf(mt::KeyOfScanCode(KEY_A),mt::KeyUpEvent())));
2322+
2323+ dev.start(&mock_sink, &mock_builder);
2324+ dev.process_event(fake_event_1);
2325+ dev.process_event(fake_event_2);
2326+}
2327+
2328+TEST_F(LibInputDevice, process_event_accumulates_key_state)
2329+{
2330+ std::shared_ptr<libinput> lib = mie::make_libinput();
2331+ mie::LibInputDevice dev(mir::report::null_input_report(), path, mie::make_libinput_device(lib.get(), path));
2332+
2333+
2334+ setup_key_event(fake_event_1, event_time_1, KEY_C, LIBINPUT_KEY_STATE_PRESSED);
2335+ setup_key_event(fake_event_2, event_time_2, KEY_LEFTALT, LIBINPUT_KEY_STATE_PRESSED);
2336+ setup_key_event(fake_event_3, event_time_3, KEY_C, LIBINPUT_KEY_STATE_RELEASED);
2337+
2338+ InSequence seq;
2339+ EXPECT_CALL(mock_builder, key_event(time_stamp_1, mir_keyboard_action_down, _, KEY_C, mir_input_event_modifier_none));
2340+ EXPECT_CALL(mock_sink, handle_input(AllOf(mt::KeyOfScanCode(KEY_C),mt::KeyDownEvent())));
2341+ EXPECT_CALL(mock_builder, key_event(time_stamp_2, mir_keyboard_action_down, _, KEY_LEFTALT, mir_input_event_modifier_none));
2342+ EXPECT_CALL(mock_sink, handle_input(AllOf(mt::KeyOfScanCode(KEY_LEFTALT),mt::KeyDownEvent())));
2343+ EXPECT_CALL(mock_builder, key_event(time_stamp_3, mir_keyboard_action_up, _, KEY_C, mir_input_event_modifier_alt|mir_input_event_modifier_alt_left));
2344+ EXPECT_CALL(mock_sink, handle_input(AllOf(mt::KeyOfScanCode(KEY_C),
2345+ mt::KeyWithModifiers(
2346+ MirInputEventModifiers{
2347+ mir_input_event_modifier_alt|
2348+ mir_input_event_modifier_alt_left
2349+ }),
2350+ mt::KeyUpEvent())));
2351+
2352+ dev.start(&mock_sink, &mock_builder);
2353+ dev.process_event(fake_event_1);
2354+ dev.process_event(fake_event_2);
2355+ dev.process_event(fake_event_3);
2356+}
2357+
2358+TEST_F(LibInputDevice, process_event_converts_pointer_event)
2359+{
2360+ std::shared_ptr<libinput> lib = mie::make_libinput();
2361+ mie::LibInputDevice dev(mir::report::null_input_report(), path, mie::make_libinput_device(lib.get(), path));
2362+
2363+ float x = 15;
2364+ float y = 17;
2365+ setup_pointer_event(fake_event_1, event_time_1, x, y);
2366+
2367+ EXPECT_CALL(mock_sink, handle_input(mt::PointerEventWithPosition(x,y)));
2368+
2369+ dev.start(&mock_sink, &mock_builder);
2370+ dev.process_event(fake_event_1);
2371+}
2372+
2373+TEST_F(LibInputDevice, process_event_provides_relative_coordinates)
2374+{
2375+ std::shared_ptr<libinput> lib = mie::make_libinput();
2376+ mie::LibInputDevice dev(mir::report::null_input_report(), path, mie::make_libinput_device(lib.get(), path));
2377+
2378+ float x = -5;
2379+ float y = 20;
2380+ setup_pointer_event(fake_event_1, event_time_1, x, y);
2381+
2382+ EXPECT_CALL(mock_sink, handle_input(mt::PointerEventWithDiff(x,y)));
2383+
2384+ dev.start(&mock_sink, &mock_builder);
2385+ dev.process_event(fake_event_1);
2386+}
2387+
2388+TEST_F(LibInputDevice, process_event_accumulates_pointer_movement)
2389+{
2390+ std::shared_ptr<libinput> lib = mie::make_libinput();
2391+ mie::LibInputDevice dev(mir::report::null_input_report(), path, mie::make_libinput_device(lib.get(), path));
2392+
2393+ float x1 = 15, x2 = 23;
2394+ float y1 = 17, y2 = 21;
2395+
2396+ setup_pointer_event(fake_event_1, event_time_1, x1, y1);
2397+ setup_pointer_event(fake_event_2, event_time_2, x2, y2);
2398+
2399+ EXPECT_CALL(mock_sink, handle_input(mt::PointerEventWithPosition(x1,y1)));
2400+ EXPECT_CALL(mock_sink, handle_input(mt::PointerEventWithPosition(x1+x2,y1+y2)));
2401+
2402+ dev.start(&mock_sink, &mock_builder);
2403+ dev.process_event(fake_event_1);
2404+ dev.process_event(fake_event_2);
2405+}
2406+
2407+TEST_F(LibInputDevice, process_event_handles_press_and_release)
2408+{
2409+ std::shared_ptr<libinput> lib = mie::make_libinput();
2410+ mie::LibInputDevice dev(mir::report::null_input_report(), path, mie::make_libinput_device(lib.get(), path));
2411+ float const x = 0;
2412+ float const y = 0;
2413+ geom::Point const pos{x,y};
2414+
2415+ setup_button_event(fake_event_1, event_time_1, BTN_LEFT, LIBINPUT_BUTTON_STATE_PRESSED);
2416+ setup_button_event(fake_event_2, event_time_2, BTN_RIGHT, LIBINPUT_BUTTON_STATE_PRESSED);
2417+ setup_button_event(fake_event_3, event_time_3, BTN_RIGHT, LIBINPUT_BUTTON_STATE_RELEASED);
2418+ setup_button_event(fake_event_4, event_time_4, BTN_LEFT, LIBINPUT_BUTTON_STATE_RELEASED);
2419+
2420+ InSequence seq;
2421+ EXPECT_CALL(mock_sink, handle_input(mt::ButtonDownEventWithButton(pos, mir_pointer_button_primary)));
2422+ EXPECT_CALL(mock_sink, handle_input(mt::ButtonDownEventWithButton(pos, mir_pointer_button_secondary)));
2423+ EXPECT_CALL(mock_sink, handle_input(mt::ButtonUpEventWithButton(pos, mir_pointer_button_secondary)));
2424+ EXPECT_CALL(mock_sink, handle_input(mt::ButtonUpEventWithButton(pos, mir_pointer_button_primary)));
2425+
2426+ dev.start(&mock_sink, &mock_builder);
2427+ dev.process_event(fake_event_1);
2428+ dev.process_event(fake_event_2);
2429+ dev.process_event(fake_event_3);
2430+ dev.process_event(fake_event_4);
2431+}
2432+
2433+TEST_F(LibInputDevice, process_event_handles_scroll)
2434+{
2435+ std::shared_ptr<libinput> lib = mie::make_libinput();
2436+ mie::LibInputDevice dev(mir::report::null_input_report(), path, mie::make_libinput_device(lib.get(), path));
2437+
2438+ setup_axis_event(fake_event_1, event_time_1, 0.0, 20.0);
2439+ setup_axis_event(fake_event_2, event_time_2, 5.0, 0.0);
2440+
2441+ InSequence seq;
2442+ // expect two scroll events..
2443+ EXPECT_CALL(mock_builder, pointer_event(time_stamp_1, mir_input_event_modifier_none, mir_pointer_action_motion, 0, 0.0f, 0.0f, 0.0f, 20.0f, 0.0f, 0.0f));
2444+ EXPECT_CALL(mock_sink, handle_input(mt::PointerAxisChange(mir_pointer_axis_vscroll, 20.0f)));
2445+ EXPECT_CALL(mock_builder, pointer_event(time_stamp_2, mir_input_event_modifier_none, mir_pointer_action_motion, 0, 0.0f, 0.0f, 5.0f, 0.0f, 0.0f, 0.0f));
2446+ EXPECT_CALL(mock_sink, handle_input(mt::PointerAxisChange(mir_pointer_axis_hscroll, 5.0f)));
2447+
2448+ dev.start(&mock_sink, &mock_builder);
2449+ dev.process_event(fake_event_1);
2450+ dev.process_event(fake_event_2);
2451+}
2452+
2453+TEST_F(LibInputDevice, process_event_handles_touch_down_events)
2454+{
2455+ std::shared_ptr<libinput> lib = mie::make_libinput();
2456+ mie::LibInputDevice dev(mir::report::null_input_report(), path, mie::make_libinput_device(lib.get(), path));
2457+
2458+ int slot = 0;
2459+ float major = 6;
2460+ float minor = 5;
2461+ float pressure = 0.6f;
2462+ float x = 100;
2463+ float y = 7;
2464+
2465+ setup_touch_event(fake_event_1, LIBINPUT_EVENT_TOUCH_DOWN, event_time_1, slot, x, y, major, minor, pressure);
2466+ setup_touch_frame(fake_event_2);
2467+
2468+ InSequence seq;
2469+ EXPECT_CALL(mock_builder, touch_event(time_stamp_1, mir_input_event_modifier_none));
2470+ EXPECT_CALL(mock_builder, add_touch(_, MirTouchId{0}, mir_touch_action_down, mir_touch_tooltype_finger, x, y,
2471+ pressure, major, minor, major));
2472+ EXPECT_CALL(mock_sink, handle_input(mt::TouchEvent(x,y)));
2473+
2474+ dev.start(&mock_sink, &mock_builder);
2475+ dev.process_event(fake_event_1);
2476+ dev.process_event(fake_event_2);
2477+}
2478+
2479+TEST_F(LibInputDevice, process_event_handles_touch_move_events)
2480+{
2481+ std::shared_ptr<libinput> lib = mie::make_libinput();
2482+ mie::LibInputDevice dev(mir::report::null_input_report(), path, mie::make_libinput_device(lib.get(), path));
2483+
2484+ int slot = 0;
2485+ float major = 6;
2486+ float minor = 5;
2487+ float pressure = 0.6f;
2488+ float x = 100;
2489+ float y = 7;
2490+
2491+ setup_touch_event(fake_event_1, LIBINPUT_EVENT_TOUCH_MOTION, event_time_1, slot, x, y, major, minor, pressure);
2492+ setup_touch_frame(fake_event_2);
2493+
2494+ InSequence seq;
2495+ EXPECT_CALL(mock_builder, touch_event(time_stamp_1, mir_input_event_modifier_none));
2496+ EXPECT_CALL(mock_builder, add_touch(_, MirTouchId{0}, mir_touch_action_change, mir_touch_tooltype_finger, x, y,
2497+ pressure, major, minor, major));
2498+ EXPECT_CALL(mock_sink, handle_input(mt::TouchMovementEvent()));
2499+
2500+ dev.start(&mock_sink, &mock_builder);
2501+ dev.process_event(fake_event_1);
2502+ dev.process_event(fake_event_2);
2503+}

Subscribers

People subscribed via source and target branches