Mir

Merge lp:~mir-team/mir/unify-pointer-button into lp:mir

Proposed by Robert Carr
Status: Superseded
Proposed branch: lp:~mir-team/mir/unify-pointer-button
Merge into: lp:mir
Prerequisite: lp:~mir-team/mir/remaining-nsec-removal
Diff against target: 2677 lines (+854/-963)
40 files modified
3rd_party/android-input/android/frameworks/base/services/input/InputDispatcher.cpp (+11/-11)
include/client/mir/events/event_builders.h (+1/-2)
include/client/mir_toolkit/events/input/pointer_event.h (+15/-5)
src/client/events/event_builders.cpp (+19/-30)
src/client/input/android/android_input_lexicon.cpp (+1/-1)
src/client/input/android/event_conversion_helpers.cpp (+34/-0)
src/client/input/input_event.cpp (+11/-5)
src/client/symbols.map (+3/-0)
src/include/common/mir/events/event_private.h (+1/-9)
src/include/common/mir/input/android/event_conversion_helpers.h (+3/-1)
src/server/input/android/android_input_dispatcher.cpp (+1/-1)
src/server/input/android/input_sender.cpp (+1/-1)
src/server/input/android/input_translator.cpp (+1/-1)
src/server/input/default_configuration.cpp (+2/-0)
src/server/input/default_input_device_hub.cpp (+16/-1)
src/server/input/default_input_device_hub.h (+4/-0)
tests/acceptance-tests/test_client_input.cpp (+510/-69)
tests/acceptance-tests/test_surface_modifications.cpp (+2/-4)
tests/acceptance-tests/test_surface_placement.cpp (+1/-1)
tests/acceptance-tests/throwback/CMakeLists.txt (+0/-1)
tests/acceptance-tests/throwback/test_client_input.cpp (+0/-704)
tests/acceptance-tests/throwback/test_custom_input_targeter.cpp (+0/-1)
tests/include/mir_test/event_matchers.h (+28/-27)
tests/include/mir_test_framework/input_testing_server_configuration.h (+0/-22)
tests/include/mir_test_framework/placement_applying_shell.h (+59/-0)
tests/integration-tests/input/CMakeLists.txt (+1/-4)
tests/integration-tests/input/android/CMakeLists.txt (+0/-9)
tests/integration-tests/input/test_cursor_listener.cpp (+17/-10)
tests/mir_test_framework/CMakeLists.txt (+1/-0)
tests/mir_test_framework/fake_input_device_impl.cpp (+3/-3)
tests/mir_test_framework/fake_input_device_impl.h (+1/-3)
tests/mir_test_framework/input_testing_server_options.cpp (+0/-17)
tests/mir_test_framework/placement_applying_shell.cpp (+59/-0)
tests/unit-tests/input/android/test_android_input_dispatcher.cpp (+5/-4)
tests/unit-tests/input/android/test_android_input_lexicon.cpp (+2/-5)
tests/unit-tests/input/android/test_input_translator.cpp (+2/-3)
tests/unit-tests/input/test_default_input_device_hub.cpp (+28/-1)
tests/unit-tests/input/test_event_builders.cpp (+2/-4)
tests/unit-tests/input/test_input_event.cpp (+8/-2)
tests/unit-tests/scene/test_abstract_shell.cpp (+1/-1)
To merge this branch: bzr merge lp:~mir-team/mir/unify-pointer-button
Reviewer Review Type Date Requested Status
Alan Griffiths Needs Fixing
Alexandros Frantzis (community) Approve
Daniel van Vugt Needs Fixing
PS Jenkins bot (community) continuous-integration Approve
Andreas Pokorny (community) Approve
Kevin DuBois (community) Needs Information
Review via email: mp+258322@code.launchpad.net

This proposal has been superseded by a proposal from 2015-05-14.

Commit message

Unify the pointer button enums. The new style event buttons are not using a mask so as to allow for extensibility so there is some churn in the change of the internal representation.

Description of the change

Unify the pointer button enums. The new style event buttons are not using a mask so as to allow for extensibility so there is some churn in the change of the internal representation.

This branch is part of the event cleaning pipeline:

Pluck-low-hanging-event-fruit: Remove unused event members.
Unify-event-modifiers: Consolidate modifier enums (https://code.launchpad.net/~mir-team/mir/unify-event-modifiers/+merge/258218)
Unify-keyboard-actions: Consolidate keyboard action enums and key repeat representation (https://code.launchpad.net/~mir-team/mir/unify-keyboard-actions/+merge/258288)
Remaining-nsec-removal: Remove usage of nsecs_t in event_private.h https://code.launchpad.net/~mir-team/mir/remaining-nsec-removal/+merge/258292
unify-pointer-button: Consolidate pointer button enums
https://code.launchpad.net/~mir-team/mir/unify-pointer-button

To post a comment you must log in.
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 :

> The new style event buttons are not using a mask so as to allow for extensibility so there
> is some churn in the change of the internal representation.

How is having a mask reduce extensibility? Do you mean running out of bits?

190+ bool button_state[mir_pointer_button_forward + 1];

This will break if we add a new member to the enum (although we will probably catch the issue at compile time). In any case, it's best to introduce a mir_pointer_button_count (or similar) value, and actually make it a policy to have such a member in all suitable enums.

Needs info/fixing

review: Needs Fixing
Revision history for this message
Robert Carr (robertcarr) wrote :

Added mir_pointer_button_Count...good call...tbh I dont like this solution that much but I'm concerned about the extensibility with flags (e.g. running out of bits). In particular what if devices have buttons only available through some sort of mir_input_device_list_buttons_ API (e.g. they don't correspond to forward/back...)? ON the other hand I guess this device wouldn't be a pointer...maybe flags are fine...I'd like to get another opinion! RAOF?

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: Approve (continuous-integration)
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 :

Seems self-consistent.

But I'm a little concerned that other APIs treat buttons as bitmasks (e.g. "143+ android_state |= AMOTION_EVENT_BUTTON_PRIMARY;"). Apart from not matching expectations are we really sure we know better?

review: Needs Information
Revision history for this message
Robert Carr (robertcarr) wrote :

I've thought through my concerns that related to nto storing buttons as a flag. Primarly I was worried about exotic devices and allocating button indices for them and potentially running out of flag values. I suppose if anything though such devices would not be pointers and only available throuhg the raw input API so I'm not concerned. Updated branch to use flag...required more client ABI break :)

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

nonblocking comment:
The bitmask seems okay for now, plenty of unused bits left.

needs-info
77+ mev.button_state[mir_pointer_button_tertiary] = true;

Since this appears private now, could we have something more like:
mev.button_state[mir_pointer_button_tertiary] = pressed;
It took a bit of reading of the struct to figure out what was going on in the present form.

review: Needs Information
Revision history for this message
PS Jenkins bot (ps-jenkins) wrote :
review: Needs Fixing (continuous-integration)
Revision history for this message
Andreas Pokorny (andreas-pokorny) wrote :

ok looks good apart from the merge conflict

Revision history for this message
Andreas Pokorny (andreas-pokorny) :
review: Approve
Revision history for this message
PS Jenkins bot (ps-jenkins) wrote :
review: Approve (continuous-integration)
Revision history for this message
Daniel van Vugt (vanvugt) wrote :

(0) MirPointerButton: Definitely a client ABI break now, so we must remember to bump to libmirclient9 ASAP (Alan is working on it but blocked by CI annoyances).

(1) I guess this isn't well tested yet because there's a logic error (should be "&="):
365 - buttons.erase(remove(begin(buttons), end(buttons), button));
366 + buttons |= ~button;

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

Besides Daniel's "Needs Fixing", looks good.

Nit:

239-

Why remove the line?

review: Approve
Revision history for this message
Alan Griffiths (alan-griffiths) wrote :

Agree with Daniel about test coverage and Alexandros about whitespace.

review: Needs Fixing

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1=== modified file '3rd_party/android-input/android/frameworks/base/services/input/InputDispatcher.cpp'
2--- 3rd_party/android-input/android/frameworks/base/services/input/InputDispatcher.cpp 2015-05-01 08:54:27 +0000
3+++ 3rd_party/android-input/android/frameworks/base/services/input/InputDispatcher.cpp 2015-05-14 18:12:40 +0000
4@@ -20,30 +20,30 @@
5 //#define LOG_NDEBUG 0
6
7 // Log detailed debug messages about each inbound event notification to the dispatcher.
8-#define DEBUG_INBOUND_EVENT_DETAILS 0
9+#define DEBUG_INBOUND_EVENT_DETAILS 1
10
11 // Log detailed debug messages about each outbound event processed by the dispatcher.
12-#define DEBUG_OUTBOUND_EVENT_DETAILS 0
13+#define DEBUG_OUTBOUND_EVENT_DETAILS 1
14
15 // Log debug messages about the dispatch cycle.
16-#define DEBUG_DISPATCH_CYCLE 0
17+#define DEBUG_DISPATCH_CYCLE 1
18
19 // Log debug messages about registrations.
20-#define DEBUG_REGISTRATION 0
21+#define DEBUG_REGISTRATION 1
22
23-// Log debug messages about input event injection.
24-#define DEBUG_INJECTION 0
25+ // Log debug messages about input event injection.
26+#define DEBUG_INJECTION 1
27
28 // Log debug messages about input focus tracking.
29-#define DEBUG_FOCUS 0
30+#define DEBUG_FOCUS 1
31
32 // Log debug messages about the app switch latency optimization.
33-#define DEBUG_APP_SWITCH 0
34+#define DEBUG_APP_SWITCH 1
35
36 // Log debug messages about hover events.
37-#define DEBUG_HOVER 0
38+#define DEBUG_HOVER 1
39
40-#define ENABLE_APP_SWITCH_OPTIMIZATION 0
41+#define ENABLE_APP_SWITCH_OPTIMIZATION 1
42
43 #include "InputDispatcher.h"
44
45@@ -3464,7 +3464,7 @@
46 originalKeyCode, keyEntry->action, keyEntry->repeatCount,
47 keyEntry->policyFlags);
48 #endif
49- return false;
50+ return false;
51 }
52
53 // Dispatch the unhandled key to the policy.
54
55=== modified file 'include/client/mir/events/event_builders.h'
56--- include/client/mir/events/event_builders.h 2015-05-05 18:01:08 +0000
57+++ include/client/mir/events/event_builders.h 2015-05-14 18:12:40 +0000
58@@ -25,7 +25,6 @@
59 #include "mir/frontend/surface_id.h"
60
61 #include <memory>
62-#include <vector>
63 #include <functional>
64 #include <chrono>
65
66@@ -63,7 +62,7 @@
67 // Pointer event
68 EventUPtr make_event(MirInputDeviceId device_id, std::chrono::nanoseconds timestamp,
69 MirInputEventModifiers modifiers, MirPointerAction action,
70- std::vector<MirPointerButton> const& buttons_pressed,
71+ MirPointerButtons buttons_pressed,
72 float x_axis_value, float y_axis_value,
73 float hscroll_value, float vscroll_value);
74 }
75
76=== modified file 'include/client/mir_toolkit/events/input/pointer_event.h'
77--- include/client/mir_toolkit/events/input/pointer_event.h 2015-03-31 02:35:42 +0000
78+++ include/client/mir_toolkit/events/input/pointer_event.h 2015-05-14 18:12:40 +0000
79@@ -68,12 +68,13 @@
80 * Identifiers for pointer buttons
81 */
82 typedef enum {
83- mir_pointer_button_primary = 1,
84- mir_pointer_button_secondary = 2,
85- mir_pointer_button_tertiary = 3,
86- mir_pointer_button_back = 4,
87- mir_pointer_button_forward = 5
88+ mir_pointer_button_primary = 1 << 0,
89+ mir_pointer_button_secondary = 1 << 1,
90+ mir_pointer_button_tertiary = 1 << 2,
91+ mir_pointer_button_back = 1 << 3,
92+ mir_pointer_button_forward = 1 << 4
93 } MirPointerButton;
94+typedef unsigned int MirPointerButtons;
95
96 /**
97 * Retrieve the modifier keys pressed when the pointer action occured.
98@@ -103,6 +104,15 @@
99 MirPointerButton button);
100
101 /**
102+ * Retreive the pointer button state as a masked set of values.
103+ *
104+ * \param [in] event The pointer event
105+ *
106+ * \return The button state
107+ */
108+MirPointerButtons mir_pointer_event_buttons(MirPointerEvent const* event);
109+
110+/**
111 * Retrieve the axis value reported by a given pointer event.
112 *
113 * \param [in] event The pointer event
114
115=== modified file 'src/client/events/event_builders.cpp'
116--- src/client/events/event_builders.cpp 2015-05-08 20:54:52 +0000
117+++ src/client/events/event_builders.cpp 2015-05-14 18:12:40 +0000
118@@ -231,29 +231,40 @@
119
120 namespace
121 {
122-MirMotionAction old_action_from_pointer_action(MirPointerAction action, MirMotionButton button_state)
123+MirMotionAction old_action_from_pointer_action(MirPointerAction action, int buttons_pressed)
124 {
125 switch (action)
126 {
127 case mir_pointer_action_button_up:
128- return mir_motion_action_up;
129+ return buttons_pressed == 0 ? mir_motion_action_up : mir_motion_action_pointer_up;
130 case mir_pointer_action_button_down:
131- return mir_motion_action_down;
132+ return buttons_pressed == 1 ? mir_motion_action_down : mir_motion_action_pointer_down;
133 case mir_pointer_action_enter:
134 return mir_motion_action_hover_enter;
135 case mir_pointer_action_leave:
136 return mir_motion_action_hover_exit;
137 case mir_pointer_action_motion:
138- return button_state ? mir_motion_action_move : mir_motion_action_hover_move;
139+ return buttons_pressed ? mir_motion_action_move : mir_motion_action_hover_move;
140 default:
141 BOOST_THROW_EXCEPTION(std::logic_error("Invalid pointer action"));
142 }
143 }
144+int count_buttons(MirPointerButtons buttons)
145+{
146+ int ret = 0;
147+ if (buttons & mir_pointer_button_primary) ret++;
148+ if (buttons & mir_pointer_button_secondary) ret++;
149+ if (buttons & mir_pointer_button_tertiary) ret++;
150+ if (buttons & mir_pointer_button_forward) ret++;
151+ if (buttons & mir_pointer_button_back) ret++;
152+
153+ return ret;
154+}
155 }
156
157 mir::EventUPtr mev::make_event(MirInputDeviceId device_id, std::chrono::nanoseconds timestamp,
158 MirInputEventModifiers modifiers, MirPointerAction action,
159- std::vector<MirPointerButton> const& buttons_pressed,
160+ MirPointerButtons buttons_pressed,
161 float x_axis_value, float y_axis_value,
162 float hscroll_value, float vscroll_value)
163 {
164@@ -266,31 +277,9 @@
165 mev.event_time = timestamp;
166 mev.modifiers = modifiers;
167 mev.source_id = AINPUT_SOURCE_MOUSE;
168-
169- int button_state = 0;
170- for (auto button : buttons_pressed)
171- {
172- switch (button)
173- {
174- case mir_pointer_button_primary:
175- button_state |= mir_motion_button_primary;
176- break;
177- case mir_pointer_button_secondary:
178- button_state |= mir_motion_button_secondary;
179- break;
180- case mir_pointer_button_tertiary:
181- button_state |= mir_motion_button_tertiary;
182- break;
183- case mir_pointer_button_back:
184- button_state |= mir_motion_button_back;
185- break;
186- case mir_pointer_button_forward:
187- button_state |= mir_motion_button_forward;
188- break;
189- }
190- }
191- mev.button_state = static_cast<MirMotionButton>(button_state);
192- mev.action = old_action_from_pointer_action(action, mev.button_state);
193+ mev.buttons = buttons_pressed;
194+
195+ mev.action = old_action_from_pointer_action(action, count_buttons(buttons_pressed));
196
197 mev.pointer_count = 1;
198 auto& pc = mev.pointer_coordinates[0];
199
200=== modified file 'src/client/input/android/android_input_lexicon.cpp'
201--- src/client/input/android/android_input_lexicon.cpp 2015-05-05 18:01:08 +0000
202+++ src/client/input/android/android_input_lexicon.cpp 2015-05-14 18:12:40 +0000
203@@ -50,7 +50,7 @@
204 mir_event.motion.source_id = android_event->getSource();
205 mir_event.motion.action = mev->getAction();
206 mir_event.motion.modifiers = mia::mir_modifiers_from_android(mev->getMetaState());
207- mir_event.motion.button_state = static_cast<MirMotionButton>(mev->getButtonState());
208+ mir_event.motion.buttons = mia::mir_pointer_buttons_from_android(mev->getButtonState());
209 mir_event.motion.event_time = mev->getEventTime();
210 mir_event.motion.pointer_count = mev->getPointerCount();
211 for(unsigned int i = 0; i < mev->getPointerCount(); i++)
212
213=== modified file 'src/client/input/android/event_conversion_helpers.cpp'
214--- src/client/input/android/event_conversion_helpers.cpp 2015-05-08 20:44:56 +0000
215+++ src/client/input/android/event_conversion_helpers.cpp 2015-05-14 18:12:40 +0000
216@@ -100,3 +100,37 @@
217 return AKEY_EVENT_ACTION_UP;
218 }
219 }
220+
221+MirPointerButtons mia::mir_pointer_buttons_from_android(int32_t android_state)
222+{
223+ MirPointerButtons buttons = 0;
224+
225+ if (android_state & AMOTION_EVENT_BUTTON_PRIMARY)
226+ buttons |= mir_pointer_button_primary;
227+ if (android_state & AMOTION_EVENT_BUTTON_SECONDARY)
228+ buttons |= mir_pointer_button_secondary;
229+ if (android_state & AMOTION_EVENT_BUTTON_TERTIARY)
230+ buttons |= mir_pointer_button_tertiary;
231+ if (android_state & AMOTION_EVENT_BUTTON_BACK)
232+ buttons |= mir_pointer_button_back;
233+ if (android_state & AMOTION_EVENT_BUTTON_FORWARD)
234+ buttons |= mir_pointer_button_forward;
235+
236+ return buttons;
237+}
238+
239+int32_t mia::android_pointer_buttons_from_mir(MirPointerButtons buttons)
240+{
241+ int32_t android_state = 0;
242+ if (buttons & mir_pointer_button_primary)
243+ android_state |= AMOTION_EVENT_BUTTON_PRIMARY;
244+ if (buttons & mir_pointer_button_secondary)
245+ android_state |= AMOTION_EVENT_BUTTON_SECONDARY;
246+ if (buttons & mir_pointer_button_tertiary)
247+ android_state |= AMOTION_EVENT_BUTTON_TERTIARY;
248+ if (buttons & mir_pointer_button_back)
249+ android_state |= AMOTION_EVENT_BUTTON_BACK;
250+ if (buttons & mir_pointer_button_forward)
251+ android_state |= AMOTION_EVENT_BUTTON_FORWARD;
252+ return android_state;
253+}
254
255=== modified file 'src/client/input/input_event.cpp'
256--- src/client/input/input_event.cpp 2015-05-05 18:01:08 +0000
257+++ src/client/input/input_event.cpp 2015-05-14 18:12:40 +0000
258@@ -424,20 +424,26 @@
259 switch (button)
260 {
261 case mir_pointer_button_primary:
262- return old_mev.button_state & mir_motion_button_primary;
263+ return old_mev.buttons & mir_pointer_button_primary;
264 case mir_pointer_button_secondary:
265- return old_mev.button_state & mir_motion_button_secondary;
266+ return old_mev.buttons & mir_pointer_button_secondary;
267 case mir_pointer_button_tertiary:
268- return old_mev.button_state & mir_motion_button_tertiary;
269+ return old_mev.buttons & mir_pointer_button_tertiary;
270 case mir_pointer_button_back:
271- return old_mev.button_state & mir_motion_button_back;
272+ return old_mev.buttons & mir_pointer_button_back;
273 case mir_pointer_button_forward:
274- return old_mev.button_state & mir_motion_button_forward;
275+ return old_mev.buttons & mir_pointer_button_forward;
276 default:
277 return false;
278 }
279 }
280
281+MirPointerButtons mir_pointer_event_buttons(MirPointerEvent const* pev)
282+{
283+ auto const& old_mev = old_mev_from_new(pev);
284+ return old_mev.buttons;
285+}
286+
287 float mir_pointer_event_axis_value(MirPointerEvent const* pev, MirPointerAxis axis)
288 {
289 auto const& old_mev = old_mev_from_new(pev);
290
291=== modified file 'src/client/symbols.map'
292--- src/client/symbols.map 2015-05-07 19:41:08 +0000
293+++ src/client/symbols.map 2015-05-14 18:12:40 +0000
294@@ -196,6 +196,7 @@
295 mir_buffer_stream_release;
296 mir_buffer_stream_release_sync;
297 mir_buffer_stream_is_valid;
298+ mir_pointer_event_buttons; # Move to 8.5
299 } MIR_CLIENT_8.3;
300
301 MIR_CLIENT_DETAIL_8 {
302@@ -208,6 +209,8 @@
303 mir::input::android::mir_modifiers_from_android*;
304 mir::input::android::mir_keyboard_action_from_android*;
305 mir::input::android::android_keyboard_action_from_mir*;
306+ mir::input::android::mir_pointer_buttons_from_android*;
307+ mir::input::android::android_pointer_buttons_from_mir*;
308 mir::client::DefaultConnectionConfiguration::DefaultConnectionConfiguration*;
309 mir::client::DefaultConnectionConfiguration::the_surface_map*;
310 mir::client::DefaultConnectionConfiguration::the_rpc_channel*;
311
312=== modified file 'src/include/common/mir/events/event_private.h'
313--- src/include/common/mir/events/event_private.h 2015-05-07 19:43:49 +0000
314+++ src/include/common/mir/events/event_private.h 2015-05-14 18:12:40 +0000
315@@ -60,14 +60,6 @@
316 } MirMotionAction;
317
318 typedef enum {
319- mir_motion_button_primary = 1 << 0,
320- mir_motion_button_secondary = 1 << 1,
321- mir_motion_button_tertiary = 1 << 2,
322- mir_motion_button_back = 1 << 3,
323- mir_motion_button_forward = 1 << 4
324-} MirMotionButton;
325-
326-typedef enum {
327 mir_motion_tool_type_unknown = 0,
328 mir_motion_tool_type_finger = 1,
329 mir_motion_tool_type_stylus = 2,
330@@ -125,7 +117,7 @@
331 int action;
332 MirInputEventModifiers modifiers;
333
334- MirMotionButton button_state;
335+ MirPointerButtons buttons;
336 std::chrono::nanoseconds event_time;
337
338 size_t pointer_count;
339
340=== modified file 'src/include/common/mir/input/android/event_conversion_helpers.h'
341--- src/include/common/mir/input/android/event_conversion_helpers.h 2015-05-08 20:44:56 +0000
342+++ src/include/common/mir/input/android/event_conversion_helpers.h 2015-05-14 18:12:40 +0000
343@@ -31,13 +31,15 @@
344 int32_t android_modifiers_from_mir(MirInputEventModifiers modifiers);
345
346 MirKeyboardAction mir_keyboard_action_from_android(int32_t android_action, int32_t repeat_count);
347-
348 // Mir differentiates between mir_keyboard_action_down
349 // and mir_keyboard_action_repeat whereas android encodes
350 // keyrepeats as AKEY_EVENT_ACTION_DOWN and a repeatCount of > 0
351 // Thus when converting from MirKeyboardAction to an android
352 // action we must also fetch a repeat count for the android event.
353 int32_t android_keyboard_action_from_mir(int32_t& repeat_count_out, MirKeyboardAction action);
354+
355+MirPointerButtons mir_pointer_buttons_from_android(int32_t android_state);
356+int32_t android_pointer_buttons_from_mir(MirPointerButtons buttons);
357 }
358 }
359 }
360
361=== modified file 'src/server/input/android/android_input_dispatcher.cpp'
362--- src/server/input/android/android_input_dispatcher.cpp 2015-05-08 20:54:52 +0000
363+++ src/server/input/android/android_input_dispatcher.cpp 2015-05-14 18:12:40 +0000
364@@ -102,7 +102,7 @@
365 event.motion.action,
366 0, /* flags */
367 mia::android_modifiers_from_mir(event.motion.modifiers),
368- event.motion.button_state,
369+ mia::android_pointer_buttons_from_mir(event.motion.buttons),
370 0, /* edge_flags */
371 event.motion.pointer_count,
372 pointer_properties.data(),
373
374=== modified file 'src/server/input/android/input_sender.cpp'
375--- src/server/input/android/input_sender.cpp 2015-05-08 20:54:52 +0000
376+++ src/server/input/android/input_sender.cpp 2015-05-14 18:12:40 +0000
377@@ -288,7 +288,7 @@
378 0, /* flags */
379 0, /* edge flags */
380 mia::android_modifiers_from_mir(event.modifiers),
381- static_cast<int32_t>(event.button_state),
382+ mia::android_pointer_buttons_from_mir(event.buttons),
383 0.0f, // event.x_offset,
384 0.0f, // event.y_offset,
385 0, 0, /* unused x/y precision */
386
387=== modified file 'src/server/input/android/input_translator.cpp'
388--- src/server/input/android/input_translator.cpp 2015-05-07 19:43:49 +0000
389+++ src/server/input/android/input_translator.cpp 2015-05-14 18:12:40 +0000
390@@ -147,7 +147,7 @@
391 mir_event.motion.source_id = args->source;
392 mir_event.motion.action = args->action;
393 mir_event.motion.modifiers = mia::mir_modifiers_from_android(args->metaState);
394- mir_event.motion.button_state = static_cast<MirMotionButton>(args->buttonState);
395+ mir_event.motion.buttons = mia::mir_pointer_buttons_from_android(args->buttonState);
396 mir_event.motion.event_time = args->eventTime;
397 mir_event.motion.pointer_count = args->pointerCount;
398 for(unsigned int i = 0; i < args->pointerCount; i++)
399
400=== modified file 'src/server/input/default_configuration.cpp'
401--- src/server/input/default_configuration.cpp 2015-05-04 10:45:05 +0000
402+++ src/server/input/default_configuration.cpp 2015-05-14 18:12:40 +0000
403@@ -426,6 +426,7 @@
404 the_input_reading_multiplexer(),
405 the_main_loop(),
406 the_touch_visualizer(),
407+ the_cursor_listener(),
408 the_input_region());
409 });
410 }
411@@ -439,6 +440,7 @@
412 the_input_reading_multiplexer(),
413 the_main_loop(),
414 the_touch_visualizer(),
415+ the_cursor_listener(),
416 the_input_region());
417 });
418 }
419
420=== modified file 'src/server/input/default_input_device_hub.cpp'
421--- src/server/input/default_input_device_hub.cpp 2015-04-14 11:08:21 +0000
422+++ src/server/input/default_input_device_hub.cpp 2015-05-14 18:12:40 +0000
423@@ -23,6 +23,7 @@
424 #include "mir/input/input_dispatcher.h"
425 #include "mir/input/input_device.h"
426 #include "mir/input/input_device_observer.h"
427+#include "mir/input/cursor_listener.h"
428 #include "mir/input/input_region.h"
429 #include "mir/events/event_private.h"
430 #include "mir/dispatch/multiplexing_dispatchable.h"
431@@ -42,9 +43,10 @@
432 std::shared_ptr<dispatch::MultiplexingDispatchable> const& input_multiplexer,
433 std::shared_ptr<mir::ServerActionQueue> const& observer_queue,
434 std::shared_ptr<TouchVisualizer> const& touch_visualizer,
435+ std::shared_ptr<CursorListener> const& cursor_listener,
436 std::shared_ptr<InputRegion> const& input_region)
437 : input_dispatcher(input_dispatcher), input_dispatchable{input_multiplexer}, observer_queue(observer_queue),
438- touch_visualizer(touch_visualizer), input_region(input_region)
439+ touch_visualizer(touch_visualizer), cursor_listener(cursor_listener), input_region(input_region)
440 {
441 }
442
443@@ -163,9 +165,22 @@
444 BOOST_THROW_EXCEPTION(std::invalid_argument("Invalid input event receivd from device"));
445
446 update_spots(mir_event_get_input_event(&event));
447+ notify_cursor_listener(mir_event_get_input_event(&event));
448 dispatcher->dispatch(event);
449 }
450
451+void mi::DefaultInputDeviceHub::RegisteredDevice::notify_cursor_listener(MirInputEvent const* event)
452+{
453+ if (mir_input_event_get_type(event) != mir_input_event_type_pointer)
454+ return;
455+
456+ auto pointer_ev = mir_input_event_get_pointer_event(event);
457+ hub->cursor_listener->cursor_moved_to(
458+ mir_pointer_event_axis_value(pointer_ev, mir_pointer_axis_x),
459+ mir_pointer_event_axis_value(pointer_ev, mir_pointer_axis_y)
460+ );
461+}
462+
463 void mi::DefaultInputDeviceHub::RegisteredDevice::update_spots(MirInputEvent const* event)
464 {
465 if (mir_input_event_get_type(event) != mir_input_event_type_touch)
466
467=== modified file 'src/server/input/default_input_device_hub.h'
468--- src/server/input/default_input_device_hub.h 2015-04-14 11:08:21 +0000
469+++ src/server/input/default_input_device_hub.h 2015-05-14 18:12:40 +0000
470@@ -46,6 +46,7 @@
471 class InputDeviceObserver;
472 class TouchVisualizer;
473 class InputRegion;
474+class CursorListener;
475
476 class DefaultInputDeviceHub : public InputDeviceRegistry, public InputDeviceHub
477 {
478@@ -54,6 +55,7 @@
479 std::shared_ptr<dispatch::MultiplexingDispatchable> const& input_multiplexer,
480 std::shared_ptr<ServerActionQueue> const& observer_queue,
481 std::shared_ptr<TouchVisualizer> const& touch_visualizer,
482+ std::shared_ptr<CursorListener> const& cursor_listener,
483 std::shared_ptr<InputRegion> const& input_region);
484
485 // InputDeviceRegistry - calls from mi::Platform
486@@ -72,6 +74,7 @@
487 std::shared_ptr<dispatch::MultiplexingDispatchable> const input_dispatchable;
488 std::shared_ptr<ServerActionQueue> const observer_queue;
489 std::shared_ptr<TouchVisualizer> const touch_visualizer;
490+ std::shared_ptr<CursorListener> const cursor_listener;
491 std::shared_ptr<InputRegion> const input_region;
492
493 struct RegisteredDevice : public InputSink
494@@ -89,6 +92,7 @@
495 std::vector<TouchVisualizer::Spot> const& spots() const;
496 private:
497 void update_spots(MirInputEvent const* event);
498+ void notify_cursor_listener(MirInputEvent const* event);
499 static int32_t create_new_device_id();
500 int32_t device_id;
501 std::shared_ptr<InputDevice> const device;
502
503=== modified file 'tests/acceptance-tests/test_client_input.cpp'
504--- tests/acceptance-tests/test_client_input.cpp 2015-05-04 17:33:07 +0000
505+++ tests/acceptance-tests/test_client_input.cpp 2015-05-14 18:12:40 +0000
506@@ -16,20 +16,22 @@
507 * Authored by: Andreas Pokorny <andreas.pokorny@canonical.com>
508 */
509
510-#define MIR_INCLUDE_DEPRECATED_EVENT_HEADER
511-
512 #include "mir/input/input_device_info.h"
513 #include "mir/input/event_filter.h"
514 #include "mir/input/composite_event_filter.h"
515
516-#include "mir_test_framework/connected_client_with_a_surface.h"
517+#include "mir_test_framework/headless_in_process_server.h"
518 #include "mir_test_framework/fake_input_device.h"
519+#include "mir_test_framework/placement_applying_shell.h"
520 #include "mir_test_framework/stub_server_platform_factory.h"
521 #include "mir_test/wait_condition.h"
522 #include "mir_test/spin_wait.h"
523 #include "mir_test/event_matchers.h"
524 #include "mir_test/event_factory.h"
525
526+#include "mir_toolkit/mir_client_library.h"
527+#include "mir/events/event_builders.h"
528+
529 #include <gtest/gtest.h>
530 #include <gmock/gmock.h>
531
532@@ -41,17 +43,14 @@
533
534 namespace mi = mir::input;
535 namespace mt = mir::test;
536+namespace ms = mir::scene;
537 namespace mis = mir::input::synthesis;
538 namespace mtf = mir_test_framework;
539+namespace geom = mir::geometry;
540
541 namespace
542 {
543
544-struct MockInputHandler
545-{
546- MOCK_METHOD1(handle_input, void(MirEvent const*));
547-};
548-
549 struct MockEventFilter : public mi::EventFilter
550 {
551 // Work around GMock wanting to know how to construct MirEvent
552@@ -63,71 +62,127 @@
553 }
554 };
555
556-struct TestClientInputNew : mtf::ConnectedClientWithASurface
557+const int surface_width = 100;
558+const int surface_height = 100;
559+
560+struct Client
561 {
562- void SetUp() override
563+ MirSurface* surface{nullptr};
564+
565+ MOCK_METHOD1(handle_input, void(MirEvent const*));
566+ MOCK_METHOD1(handle_keymap, void(MirEvent const*));
567+
568+ Client(std::string const& con, std::string const& name)
569 {
570- ConnectedClientWithASurface::SetUp();
571-
572- mir_surface_set_event_handler(surface, handle_input, this);
573+ connection = mir_connect_sync(con.c_str(), name.c_str());
574+
575+ if (!mir_connection_is_valid(connection))
576+ {
577+ BOOST_THROW_EXCEPTION(
578+ std::runtime_error{std::string{"Failed to connect to test server: "} +
579+ mir_connection_get_error_message(connection)});
580+ }
581+ auto spec = mir_connection_create_spec_for_normal_surface(connection, surface_width,
582+ surface_height, mir_pixel_format_abgr_8888);
583+ mir_surface_spec_set_name(spec, name.c_str());
584+ surface = mir_surface_create_sync(spec);
585+ mir_surface_spec_release(spec);
586+ if (!mir_surface_is_valid(surface))
587+ BOOST_THROW_EXCEPTION(std::runtime_error{std::string{"Failed creating a surface: "}+
588+ mir_surface_get_error_message(surface)});
589+
590+ mir_surface_set_event_handler(surface, handle_event, this);
591 mir_buffer_stream_swap_buffers_sync(
592 mir_surface_get_buffer_stream(surface));
593
594- wait_for_surface_to_become_focused_and_exposed();
595- ready_to_accept_events.wake_up_everyone();
596+ ready_to_accept_events.wait_for_at_most_seconds(4);
597+ if (!ready_to_accept_events.woken())
598+ BOOST_THROW_EXCEPTION(std::runtime_error("Timeout waiting for surface to become focused and exposed"));
599 }
600
601- static void handle_input(MirSurface*, MirEvent const* ev, void* context)
602+ static void handle_event(MirSurface*, MirEvent const* ev, void* context)
603 {
604- auto const client = static_cast<TestClientInputNew*>(context);
605+ auto const client = static_cast<Client*>(context);
606 auto type = mir_event_get_type(ev);
607+ if (type == mir_event_type_surface)
608+ {
609+ auto surface_event = mir_event_get_surface_event(ev);
610+
611+ if (mir_surface_attrib_focus == mir_surface_event_get_attribute(surface_event) &&
612+ 1 == mir_surface_event_get_attribute_value(surface_event))
613+ client->ready_to_accept_events.wake_up_everyone();
614+ }
615 if (type == mir_event_type_input)
616- client->handler.handle_input(ev);
617- }
618-
619- void wait_for_surface_to_become_focused_and_exposed()
620- {
621- bool success = mt::spin_wait_for_condition_or_timeout(
622- [&]
623+ client->handle_input(ev);
624+ if (type == mir_event_type_keymap)
625+ client->handle_keymap(ev);
626+ }
627+ ~Client()
628+ {
629+ mir_surface_release_sync(surface);
630+ mir_connection_release(connection);
631+ }
632+ MirConnection * connection;
633+ mir::test::WaitCondition ready_to_accept_events;
634+ mir::test::WaitCondition all_events_received;
635+};
636+
637+struct TestClientInput : mtf::HeadlessInProcessServer
638+{
639+ void SetUp() override
640+ {
641+ initial_display_layout({screen_geometry});
642+
643+ server.wrap_shell(
644+ [this](std::shared_ptr<mir::shell::Shell> const& wrapped)
645 {
646- return mir_surface_get_visibility(surface) == mir_surface_visibility_exposed &&
647- mir_surface_get_focus(surface) == mir_surface_focused;
648- },
649- std::chrono::seconds{5});
650-
651- if (!success)
652- throw std::runtime_error("Timeout waiting for surface to become focused and exposed");
653+ return std::make_shared<mtf::PlacementApplyingShell>(
654+ wrapped,
655+ input_regions, positions, depths);
656+ });
657+
658+ HeadlessInProcessServer::SetUp();
659+
660+ depths[first] = ms::DepthId{0};
661+ positions[first] = geom::Rectangle{{0,0}, {surface_width, surface_height}};
662 }
663
664 std::unique_ptr<mtf::FakeInputDevice> fake_keyboard{
665 mtf::add_fake_input_device(mi::InputDeviceInfo{ 0, "keyboard", "keyboard-uid" , mi::DeviceCapability::keyboard})
666 };
667- ::testing::NiceMock<MockInputHandler> handler;
668- mir::test::WaitCondition all_events_received;
669- mir::test::WaitCondition ready_to_accept_events;
670+ std::unique_ptr<mtf::FakeInputDevice> fake_mouse{
671+ mtf::add_fake_input_device(mi::InputDeviceInfo{ 0, "mouse", "mouse-uid" , mi::DeviceCapability::pointer})
672+ };
673+ std::unique_ptr<mtf::FakeInputDevice> fake_touch_screen{mtf::add_fake_input_device(mi::InputDeviceInfo{
674+ 0, "touch screen", "touch-screen-uid", mi::DeviceCapability::touchscreen | mi::DeviceCapability::multitouch})};
675
676+ std::string first{"first"};
677+ std::string second{"second"};
678+ mtf::ClientInputRegions input_regions;
679+ mtf::ClientPositions positions;
680+ mtf::ClientDepths depths;
681+ geom::Rectangle screen_geometry{{0,0}, {1000,800}};
682 std::shared_ptr<MockEventFilter> mock_event_filter = std::make_shared<MockEventFilter>();
683 };
684
685 }
686
687+using namespace ::testing;
688
689-TEST_F(TestClientInputNew, clients_receive_keys)
690+TEST_F(TestClientInput, clients_receive_keys)
691 {
692- using namespace testing;
693+ Client first_client(new_connection(), first);
694
695 InSequence seq;
696- EXPECT_CALL(handler, handle_input(AllOf(mt::KeyDownEvent(), mt::KeyOfSymbol(XKB_KEY_Shift_R))));
697- EXPECT_CALL(handler, handle_input(AllOf(mt::KeyDownEvent(), mt::KeyOfSymbol(XKB_KEY_M))));
698- EXPECT_CALL(handler, handle_input(AllOf(mt::KeyUpEvent(), mt::KeyOfSymbol(XKB_KEY_M))));
699- EXPECT_CALL(handler, handle_input(AllOf(mt::KeyUpEvent(), mt::KeyOfSymbol(XKB_KEY_Shift_R))));
700- EXPECT_CALL(handler, handle_input(AllOf(mt::KeyDownEvent(), mt::KeyOfSymbol(XKB_KEY_i))));
701- EXPECT_CALL(handler, handle_input(AllOf(mt::KeyUpEvent(), mt::KeyOfSymbol(XKB_KEY_i))));
702- EXPECT_CALL(handler, handle_input(AllOf(mt::KeyDownEvent(), mt::KeyOfSymbol(XKB_KEY_r))));
703- EXPECT_CALL(handler, handle_input(AllOf(mt::KeyUpEvent(), mt::KeyOfSymbol(XKB_KEY_r)))).WillOnce(
704- mt::WakeUp(&all_events_received));
705-
706- ready_to_accept_events.wait_for_at_most_seconds(5);
707+ EXPECT_CALL(first_client, handle_input(AllOf(mt::KeyDownEvent(), mt::KeyOfSymbol(XKB_KEY_Shift_R))));
708+ EXPECT_CALL(first_client, handle_input(AllOf(mt::KeyDownEvent(), mt::KeyOfSymbol(XKB_KEY_M))));
709+ EXPECT_CALL(first_client, handle_input(AllOf(mt::KeyUpEvent(), mt::KeyOfSymbol(XKB_KEY_M))));
710+ EXPECT_CALL(first_client, handle_input(AllOf(mt::KeyUpEvent(), mt::KeyOfSymbol(XKB_KEY_Shift_R))));
711+ EXPECT_CALL(first_client, handle_input(AllOf(mt::KeyDownEvent(), mt::KeyOfSymbol(XKB_KEY_i))));
712+ EXPECT_CALL(first_client, handle_input(AllOf(mt::KeyUpEvent(), mt::KeyOfSymbol(XKB_KEY_i))));
713+ EXPECT_CALL(first_client, handle_input(AllOf(mt::KeyDownEvent(), mt::KeyOfSymbol(XKB_KEY_r))));
714+ EXPECT_CALL(first_client, handle_input(AllOf(mt::KeyUpEvent(), mt::KeyOfSymbol(XKB_KEY_r)))).WillOnce(
715+ mt::WakeUp(&first_client.all_events_received));
716
717 fake_keyboard->emit_event(mis::a_key_down_event().of_scancode(KEY_RIGHTSHIFT));
718 fake_keyboard->emit_event(mis::a_key_down_event().of_scancode(KEY_M));
719@@ -138,38 +193,424 @@
720 fake_keyboard->emit_event(mis::a_key_down_event().of_scancode(KEY_R));
721 fake_keyboard->emit_event(mis::a_key_up_event().of_scancode(KEY_R));
722
723- all_events_received.wait_for_at_most_seconds(10);
724-}
725-
726-namespace
727-{
728-struct TestClientInputNewEventFilter : public TestClientInputNew
729-{
730- void SetUp() override
731- {
732- TestClientInputNew::SetUp();
733- server.the_composite_event_filter()->append(mock_event_filter);
734- }
735+ first_client.all_events_received.wait_for_at_most_seconds(10);
736+}
737+
738+TEST_F(TestClientInput, clients_receive_us_english_mapped_keys)
739+{
740+ Client first_client(new_connection(), first);
741+
742+ InSequence seq;
743+ EXPECT_CALL(first_client, handle_input(AllOf(mt::KeyDownEvent(), mt::KeyOfSymbol(XKB_KEY_Shift_L))));
744+ EXPECT_CALL(first_client, handle_input(AllOf(mt::KeyDownEvent(), mt::KeyOfSymbol(XKB_KEY_dollar))))
745+ .WillOnce(mt::WakeUp(&first_client.all_events_received));
746+
747+ fake_keyboard->emit_event(mis::a_key_down_event().of_scancode(KEY_LEFTSHIFT));
748+ fake_keyboard->emit_event(mis::a_key_down_event().of_scancode(KEY_4));
749+ first_client.all_events_received.wait_for_at_most_seconds(10);
750+}
751+
752+TEST_F(TestClientInput, clients_receive_pointer_inside_window_and_crossing_events)
753+{
754+ positions[first] = geom::Rectangle{{0,0}, {surface_width, surface_height}};
755+ Client first_client(new_connection(), first);
756+
757+ // We should see the cursor enter
758+ InSequence seq;
759+ EXPECT_CALL(first_client, handle_input(mt::PointerEnterEvent()));
760+ EXPECT_CALL(first_client, handle_input(mt::PointerEventWithPosition(surface_width - 1, surface_height - 1)));
761+ EXPECT_CALL(first_client, handle_input(mt::PointerLeaveEvent()))
762+ .WillOnce(mt::WakeUp(&first_client.all_events_received));
763+ // But we should not receive an event for the second movement outside of our surface!
764+
765+ fake_mouse->emit_event(mis::a_pointer_event().with_movement(surface_width - 1, surface_height - 1));
766+ fake_mouse->emit_event(mis::a_pointer_event().with_movement(2, 2));
767+
768+ first_client.all_events_received.wait_for_at_most_seconds(120);
769+}
770+
771+TEST_F(TestClientInput, clients_receive_button_events_inside_window)
772+{
773+ Client first_client(new_connection(), first);
774+ // The cursor starts at (0, 0).
775+ EXPECT_CALL(first_client, handle_input(mt::ButtonDownEvent(0, 0)))
776+ .WillOnce(mt::WakeUp(&first_client.all_events_received));
777+
778+ fake_mouse->emit_event(mis::a_button_down_event().of_button(BTN_LEFT).with_action(mis::EventAction::Down));
779+
780+ first_client.all_events_received.wait_for_at_most_seconds(10);
781+}
782+
783+TEST_F(TestClientInput, clients_receive_many_button_events_inside_window)
784+{
785+ Client first_client(new_connection(), first);
786+ // The cursor starts at (0, 0).
787+
788+ InSequence seq;
789+ auto expect_buttons = [&](MirPointerButtons b) {
790+ EXPECT_CALL(first_client, handle_input(mt::ButtonsDown(0, 0, b)));
791+ };
792+
793+ MirPointerButtons buttons = mir_pointer_button_primary;
794+ expect_buttons(buttons);
795+ expect_buttons(buttons |= mir_pointer_button_secondary);
796+ expect_buttons(buttons |= mir_pointer_button_tertiary);
797+ expect_buttons(buttons |= mir_pointer_button_forward);
798+ expect_buttons(buttons |= mir_pointer_button_back);
799+ expect_buttons(buttons &= ~mir_pointer_button_back);
800+ expect_buttons(buttons &= ~mir_pointer_button_forward);
801+ expect_buttons(buttons &= ~mir_pointer_button_tertiary);
802+ expect_buttons(buttons &= ~mir_pointer_button_secondary);
803+ EXPECT_CALL(first_client, handle_input(mt::ButtonsDown(0, 0, 0))).WillOnce(
804+ mt::WakeUp(&first_client.all_events_received));
805+
806+ auto press_button = [&](int button) {
807+ fake_mouse->emit_event(mis::a_button_down_event().of_button(button).with_action(mis::EventAction::Down));
808+ };
809+ auto release_button = [&](int button) {
810+ fake_mouse->emit_event(mis::a_button_up_event().of_button(button).with_action(mis::EventAction::Up));
811+ };
812+ press_button(BTN_LEFT);
813+ press_button(BTN_RIGHT);
814+ press_button(BTN_MIDDLE);
815+ press_button(BTN_FORWARD);
816+ press_button(BTN_BACK);
817+ release_button(BTN_BACK);
818+ release_button(BTN_FORWARD);
819+ release_button(BTN_MIDDLE);
820+ release_button(BTN_RIGHT);
821+ release_button(BTN_LEFT);
822+
823+ first_client.all_events_received.wait_for_at_most_seconds(10);
824+}
825+
826+TEST_F(TestClientInput, multiple_clients_receive_pointer_inside_windows)
827+{
828+ int const screen_width = screen_geometry.size.width.as_int();
829+ int const screen_height = screen_geometry.size.height.as_int();
830+ int const client_height = screen_height / 2;
831+ int const client_width = screen_width / 2;
832+
833+ positions[first] = {{0, 0}, {client_width, client_height}};
834+ positions[second] = {{client_width, client_height}, {client_width, client_height}};
835+
836+ Client first_client(new_connection(), first);
837+ Client second_client(new_connection(), second);
838+
839+ {
840+ InSequence seq;
841+ EXPECT_CALL(first_client, handle_input(mt::PointerEnterEvent()));
842+ EXPECT_CALL(first_client, handle_input(mt::PointerEventWithPosition(client_width - 1, client_height - 1)));
843+ EXPECT_CALL(first_client, handle_input(mt::PointerLeaveEvent()))
844+ .WillOnce(mt::WakeUp(&first_client.all_events_received));
845+ }
846+
847+ {
848+ InSequence seq;
849+ EXPECT_CALL(second_client, handle_input(mt::PointerEnterEvent()));
850+ EXPECT_CALL(second_client, handle_input(mt::PointerEventWithPosition(client_width - 1, client_height - 1)))
851+ .WillOnce(mt::WakeUp(&second_client.all_events_received));
852+ }
853+
854+ // In the bounds of the first surface
855+ fake_mouse->emit_event(mis::a_pointer_event().with_movement(client_width - 1, client_height - 1));
856+ // In the bounds of the second surface
857+ fake_mouse->emit_event(mis::a_pointer_event().with_movement(client_width, client_height));
858+
859+ first_client.all_events_received.wait_for_at_most_seconds(2);
860+ second_client.all_events_received.wait_for_at_most_seconds(2);
861+}
862+
863+TEST_F(TestClientInput, clients_do_not_receive_pointer_outside_input_region)
864+{
865+ int const client_height = surface_height;
866+ int const client_width = surface_width;
867+
868+ input_regions[first] = {{{0, 0}, {client_width - 80, client_height}},
869+ {{client_width - 20, 0}, {client_width - 80, client_height}}};
870+
871+ Client first_client(new_connection(), first);
872+
873+ EXPECT_CALL(first_client, handle_input(mt::PointerEnterEvent())).Times(AnyNumber());
874+ EXPECT_CALL(first_client, handle_input(mt::PointerLeaveEvent())).Times(AnyNumber());
875+ EXPECT_CALL(first_client, handle_input(mt::PointerMovementEvent())).Times(AnyNumber());
876+
877+ {
878+ // We should see two of the three button pairs.
879+ InSequence seq;
880+ EXPECT_CALL(first_client, handle_input(mt::ButtonDownEvent(1, 1)));
881+ EXPECT_CALL(first_client, handle_input(mt::ButtonUpEvent(1, 1)));
882+ EXPECT_CALL(first_client, handle_input(mt::ButtonDownEvent(99, 99)));
883+ EXPECT_CALL(first_client, handle_input(mt::ButtonUpEvent(99, 99)))
884+ .WillOnce(mt::WakeUp(&first_client.all_events_received));
885+ }
886+
887+ // First we will move the cursor in to the input region on the left side of
888+ // the window. We should see a click here.
889+ fake_mouse->emit_event(mis::a_pointer_event().with_movement(1, 1));
890+ fake_mouse->emit_event(mis::a_button_down_event().of_button(BTN_LEFT).with_action(mis::EventAction::Down));
891+ fake_mouse->emit_event(mis::a_button_up_event().of_button(BTN_LEFT));
892+
893+ // Now in to the dead zone in the center of the window. We should not see
894+ // a click here.
895+ fake_mouse->emit_event(mis::a_pointer_event().with_movement(49, 49));
896+ fake_mouse->emit_event(mis::a_button_down_event().of_button(BTN_LEFT).with_action(mis::EventAction::Down));
897+ fake_mouse->emit_event(mis::a_button_up_event().of_button(BTN_LEFT));
898+
899+ // Now in to the right edge of the window, in the right input region.
900+ // Again we should see a click.
901+ fake_mouse->emit_event(mis::a_pointer_event().with_movement(49, 49));
902+ fake_mouse->emit_event(mis::a_button_down_event().of_button(BTN_LEFT).with_action(mis::EventAction::Down));
903+ fake_mouse->emit_event(mis::a_button_up_event().of_button(BTN_LEFT));
904+
905+ first_client.all_events_received.wait_for_at_most_seconds(5);
906+}
907+
908+TEST_F(TestClientInput, scene_obscure_motion_events_by_stacking)
909+{
910+ auto smaller_geometry = screen_geometry;
911+ smaller_geometry.size.width =
912+ geom::Width{screen_geometry.size.width.as_uint32_t() / 2};
913+
914+ positions[first] = screen_geometry;
915+ positions[second] = smaller_geometry;
916+ depths[second] = ms::DepthId{1};
917+
918+ Client first_client(new_connection(), first);
919+ Client second_client(new_connection(), second);
920+
921+ EXPECT_CALL(first_client, handle_input(mt::PointerEnterEvent())).Times(AnyNumber());
922+ EXPECT_CALL(first_client, handle_input(mt::PointerLeaveEvent())).Times(AnyNumber());
923+ EXPECT_CALL(first_client, handle_input(mt::PointerMovementEvent())).Times(AnyNumber());
924+ {
925+ // We should only see one button event sequence.
926+ InSequence seq;
927+ EXPECT_CALL(first_client, handle_input(mt::ButtonDownEvent(501, 1)));
928+ EXPECT_CALL(first_client, handle_input(mt::ButtonUpEvent(501, 1)))
929+ .WillOnce(mt::WakeUp(&first_client.all_events_received));
930+ }
931+
932+ EXPECT_CALL(second_client, handle_input(mt::PointerEnterEvent())).Times(AnyNumber());
933+ EXPECT_CALL(second_client, handle_input(mt::PointerLeaveEvent())).Times(AnyNumber());
934+ EXPECT_CALL(second_client, handle_input(mt::PointerMovementEvent())).Times(AnyNumber());
935+ {
936+ // Likewise we should only see one button sequence.
937+ InSequence seq;
938+ EXPECT_CALL(second_client, handle_input(mt::ButtonDownEvent(1, 1)));
939+ EXPECT_CALL(second_client, handle_input(mt::ButtonUpEvent(1, 1)))
940+ .WillOnce(mt::WakeUp(&second_client.all_events_received));
941+ }
942+
943+ // First we will move the cursor in to the region where client 2 obscures client 1
944+ fake_mouse->emit_event(mis::a_pointer_event().with_movement(1, 1));
945+ fake_mouse->emit_event(
946+ mis::a_button_down_event().of_button(BTN_LEFT).with_action(mis::EventAction::Down));
947+ fake_mouse->emit_event(mis::a_button_up_event().of_button(BTN_LEFT));
948+ // Now we move to the unobscured region of client 1
949+ fake_mouse->emit_event(mis::a_pointer_event().with_movement(500, 0));
950+ fake_mouse->emit_event(mis::a_button_down_event().of_button(BTN_LEFT).with_action(mis::EventAction::Down));
951+ fake_mouse->emit_event(mis::a_button_up_event().of_button(BTN_LEFT));
952+
953+ first_client.all_events_received.wait_for_at_most_seconds(5);
954+ second_client.all_events_received.wait_for_at_most_seconds(5);
955+}
956+
957+TEST_F(TestClientInput, hidden_clients_do_not_receive_pointer_events)
958+{
959+ depths[second] = ms::DepthId{1};
960+ positions[second] = {{0,0}, {surface_width, surface_height}};
961+
962+ Client first_client(new_connection(), first);
963+ Client second_client(new_connection(), second);
964+
965+ EXPECT_CALL(second_client, handle_input(mt::PointerEnterEvent())).Times(AnyNumber());
966+ EXPECT_CALL(second_client, handle_input(mt::PointerLeaveEvent())).Times(AnyNumber());
967+ EXPECT_CALL(second_client, handle_input(mt::PointerEventWithPosition(1, 1)))
968+ .WillOnce(mt::WakeUp(&second_client.all_events_received));
969+
970+ EXPECT_CALL(first_client, handle_input(mt::PointerEnterEvent())).Times(AnyNumber());
971+ EXPECT_CALL(first_client, handle_input(mt::PointerLeaveEvent())).Times(AnyNumber());
972+ EXPECT_CALL(first_client, handle_input(mt::PointerEventWithPosition(2, 2)))
973+ .WillOnce(mt::WakeUp(&first_client.all_events_received));
974+
975+ // We send one event and then hide the surface on top before sending the next.
976+ // So we expect each of the two surfaces to receive one event
977+ fake_mouse->emit_event(mis::a_pointer_event().with_movement(1,1));
978+
979+ second_client.all_events_received.wait_for_at_most_seconds(2);
980+
981+ server.the_shell()->focused_session()->hide();
982+
983+ fake_mouse->emit_event(mis::a_pointer_event().with_movement(1,1));
984+ first_client.all_events_received.wait_for_at_most_seconds(2);
985+}
986+
987+TEST_F(TestClientInput, clients_receive_pointer_within_coordinate_system_of_window)
988+{
989+ int const screen_width = screen_geometry.size.width.as_int();
990+ int const screen_height = screen_geometry.size.height.as_int();
991+ int const client_height = screen_height / 2;
992+ int const client_width = screen_width / 2;
993+
994+ positions[first] = {{screen_width / 2, screen_height / 2}, {client_width, client_height}};
995+
996+ Client first_client(new_connection(), first);
997+
998+ InSequence seq;
999+ EXPECT_CALL(first_client, handle_input(mt::PointerEnterEvent()));
1000+ EXPECT_CALL(first_client, handle_input(mt::PointerEventWithPosition(80, 170)))
1001+ .Times(AnyNumber())
1002+ .WillOnce(mt::WakeUp(&first_client.all_events_received));
1003+
1004+ server.the_shell()->focused_surface()->move_to(geom::Point{screen_width / 2 - 40, screen_height / 2 - 80});
1005+
1006+ fake_mouse->emit_event(mis::a_pointer_event().with_movement(screen_width / 2 + 40, screen_height / 2 + 90));
1007+
1008+ first_client.all_events_received.wait_for_at_most_seconds(2);
1009+}
1010+
1011+// TODO: Consider tests for more input devices with custom mapping (i.e. joysticks...)
1012+TEST_F(TestClientInput, usb_direct_input_devices_work)
1013+{
1014+ float const minimum_touch = mtf::FakeInputDevice::minimum_touch_axis_value;
1015+ float const maximum_touch = mtf::FakeInputDevice::maximum_touch_axis_value;
1016+ auto const display_width = screen_geometry.size.width.as_float();
1017+ auto const display_height = screen_geometry.size.height.as_float();
1018+
1019+ // We place a click 10% in to the touchscreens space in both axis,
1020+ // and a second at 0,0. Thus we expect to see a click at
1021+ // .1*screen_width/height and a second at zero zero.
1022+ float const abs_touch_x_1 = minimum_touch + (maximum_touch - minimum_touch) * 0.1f;
1023+ float const abs_touch_y_1 = minimum_touch + (maximum_touch - minimum_touch) * 0.1f;
1024+ float const abs_touch_x_2 = 0;
1025+ float const abs_touch_y_2 = 0;
1026+
1027+ float const expected_scale_x = display_width / (maximum_touch - minimum_touch + 1.0f);
1028+ float const expected_scale_y = display_height / (maximum_touch - minimum_touch + 1.0f);
1029+
1030+ float const expected_motion_x_1 = expected_scale_x * abs_touch_x_1;
1031+ float const expected_motion_y_1 = expected_scale_y * abs_touch_y_1;
1032+ float const expected_motion_x_2 = expected_scale_x * abs_touch_x_2;
1033+ float const expected_motion_y_2 = expected_scale_y * abs_touch_y_2;
1034+
1035+ positions[first] = screen_geometry;
1036+
1037+ Client first_client(new_connection(), first);
1038+
1039+ InSequence seq;
1040+ EXPECT_CALL(first_client, handle_input(
1041+ mt::TouchEvent(expected_motion_x_1, expected_motion_y_1)));
1042+ EXPECT_CALL(first_client, handle_input(
1043+ mt::TouchEventInDirection(expected_motion_x_1,
1044+ expected_motion_y_1,
1045+ expected_motion_x_2,
1046+ expected_motion_y_2)))
1047+ .Times(AnyNumber())
1048+ .WillOnce(mt::WakeUp(&first_client.all_events_received));
1049+
1050+ fake_touch_screen->emit_event(mis::a_touch_event()
1051+ .at_position({abs_touch_x_1, abs_touch_y_1}));
1052+ // Sleep here to trigger more failures (simulate slow machine)
1053+ // TODO why would that cause failures?b
1054+ std::this_thread::sleep_for(std::chrono::milliseconds(10));
1055+ fake_touch_screen->emit_event(mis::a_touch_event()
1056+ .with_action(mis::TouchParameters::Action::Move)
1057+ .at_position({abs_touch_x_2, abs_touch_y_2}));
1058+
1059+ first_client.all_events_received.wait_for_at_most_seconds(2);
1060+}
1061+
1062+TEST_F(TestClientInput, send_mir_input_events_through_surface)
1063+{
1064+ Client first_client(new_connection(), first);
1065+
1066+ EXPECT_CALL(first_client, handle_input(mt::KeyDownEvent()))
1067+ .WillOnce(mt::WakeUp(&first_client.all_events_received));
1068+
1069+ auto key_event = mir::events::make_event(MirInputDeviceId{0}, std::chrono::nanoseconds(0), mir_keyboard_action_down, 0, KEY_M,
1070+ mir_input_event_modifier_none);
1071+
1072+ server.the_shell()->focused_surface()->consume(*key_event);
1073+
1074+ first_client.all_events_received.wait_for_at_most_seconds(2);
1075+}
1076+
1077+TEST_F(TestClientInput, clients_receive_keymap_change_events)
1078+{
1079+ Client first_client(new_connection(), first);
1080+
1081+ xkb_rule_names names;
1082+ names.rules = "evdev";
1083+ names.model = "pc105";
1084+ names.layout = "dvorak";
1085+ names.variant = "";
1086+ names.options = "";
1087+
1088+ EXPECT_CALL(first_client, handle_keymap(mt::KeymapEventWithRules(names)))
1089+ .Times(1)
1090+ .WillOnce(mt::WakeUp(&first_client.all_events_received));
1091+
1092+ server.the_shell()->focused_surface()->set_keymap(names);
1093+ first_client.all_events_received.wait_for_at_most_seconds(2);
1094+}
1095+
1096+TEST_F(TestClientInput, keymap_changes_change_keycode_received)
1097+{
1098+ Client first_client(new_connection(), first);
1099+
1100+ xkb_rule_names names;
1101+ names.rules = "evdev";
1102+ names.model = "pc105";
1103+ names.layout = "us";
1104+ names.variant = "dvorak";
1105+ names.options = "";
1106+
1107+ mt::WaitCondition first_event_received,
1108+ client_sees_keymap_change;
1109+
1110+ InSequence seq;
1111+ EXPECT_CALL(first_client, handle_input(AllOf(mt::KeyDownEvent(), mt::KeyOfSymbol(XKB_KEY_n))));
1112+ EXPECT_CALL(first_client, handle_input(mt::KeyUpEvent()))
1113+ .WillOnce(mt::WakeUp(&first_event_received));
1114+ EXPECT_CALL(first_client, handle_keymap(mt::KeymapEventWithRules(names)))
1115+ .WillOnce(mt::WakeUp(&client_sees_keymap_change));
1116+
1117+ EXPECT_CALL(first_client, handle_input(AllOf(mt::KeyDownEvent(), mt::KeyOfSymbol(XKB_KEY_b))));
1118+ EXPECT_CALL(first_client, handle_input(mt::KeyUpEvent()))
1119+ .WillOnce(mt::WakeUp(&first_client.all_events_received));
1120+
1121+ fake_keyboard->emit_event(mis::a_key_down_event().of_scancode(KEY_N));
1122+ fake_keyboard->emit_event(mis::a_key_up_event().of_scancode(KEY_N));
1123+
1124+ first_event_received.wait_for_at_most_seconds(60);
1125+
1126+ server.the_shell()->focused_surface()->set_keymap(names);
1127+
1128+ client_sees_keymap_change.wait_for_at_most_seconds(60);
1129+
1130+ fake_keyboard->emit_event(mis::a_key_down_event().of_scancode(KEY_N));
1131+ fake_keyboard->emit_event(mis::a_key_up_event().of_scancode(KEY_N));
1132+
1133+ first_client.all_events_received.wait_for_at_most_seconds(5);
1134+}
1135+
1136+TEST_F(TestClientInput, event_filter_may_consume_events)
1137+{
1138 std::shared_ptr<MockEventFilter> mock_event_filter = std::make_shared<MockEventFilter>();
1139-};
1140-}
1141+ server.the_composite_event_filter()->append(mock_event_filter);
1142
1143-TEST_F(TestClientInputNewEventFilter, event_filter_may_consume_events)
1144-{
1145- using namespace ::testing;
1146+ Client first_client(new_connection(), first);
1147
1148 InSequence seq;
1149 EXPECT_CALL(*mock_event_filter, handle(_)).WillOnce(Return(true));
1150 EXPECT_CALL(*mock_event_filter, handle(_)).WillOnce(
1151- DoAll(mt::WakeUp(&all_events_received), Return(true)));
1152+ DoAll(mt::WakeUp(&first_client.all_events_received), Return(true)));
1153
1154 // Since we handle the events in the filter the client should not receive them.
1155- EXPECT_CALL(handler, handle_input(_)).Times(0);
1156-
1157- ready_to_accept_events.wait_for_at_most_seconds(5);
1158+ EXPECT_CALL(first_client, handle_input(_)).Times(0);
1159
1160 fake_keyboard->emit_event(mis::a_key_down_event().of_scancode(KEY_M));
1161 fake_keyboard->emit_event(mis::a_key_up_event().of_scancode(KEY_M));
1162
1163- all_events_received.wait_for_at_most_seconds(10);
1164+ first_client.all_events_received.wait_for_at_most_seconds(10);
1165 }
1166
1167=== modified file 'tests/acceptance-tests/test_surface_modifications.cpp'
1168--- tests/acceptance-tests/test_surface_modifications.cpp 2015-05-05 18:01:08 +0000
1169+++ tests/acceptance-tests/test_surface_modifications.cpp 2015-05-14 18:12:40 +0000
1170@@ -93,7 +93,6 @@
1171 void generate_alt_click_at(Point const& click_position)
1172 {
1173 auto const modifiers = mir_input_event_modifier_alt;
1174- std::vector<MirPointerButton> depressed_buttons{mir_pointer_button_tertiary};
1175
1176 auto const x_axis_value = click_position.x.as_float();
1177 auto const y_axis_value = click_position.y.as_float();
1178@@ -102,7 +101,7 @@
1179 auto const action = mir_pointer_action_button_down;
1180
1181 auto const click_event = mev::make_event(device_id, timestamp, modifiers,
1182- action, depressed_buttons, x_axis_value, y_axis_value, hscroll_value, vscroll_value);
1183+ action, mir_pointer_button_tertiary, x_axis_value, y_axis_value, hscroll_value, vscroll_value);
1184
1185 server.the_shell()->handle(*click_event);
1186 }
1187@@ -110,7 +109,6 @@
1188 void generate_alt_move_to(Point const& drag_position)
1189 {
1190 auto const modifiers = mir_input_event_modifier_alt;
1191- std::vector<MirPointerButton> depressed_buttons{mir_pointer_button_tertiary};
1192
1193 auto const x_axis_value = drag_position.x.as_float();
1194 auto const y_axis_value = drag_position.y.as_float();
1195@@ -119,7 +117,7 @@
1196 auto const action = mir_pointer_action_motion;
1197
1198 auto const drag_event = mev::make_event(device_id, timestamp, modifiers,
1199- action, depressed_buttons, x_axis_value, y_axis_value, hscroll_value, vscroll_value);
1200+ action, mir_pointer_button_tertiary, x_axis_value, y_axis_value, hscroll_value, vscroll_value);
1201
1202 server.the_shell()->handle(*drag_event);
1203 }
1204
1205=== modified file 'tests/acceptance-tests/test_surface_placement.cpp'
1206--- tests/acceptance-tests/test_surface_placement.cpp 2015-05-05 18:01:08 +0000
1207+++ tests/acceptance-tests/test_surface_placement.cpp 2015-05-14 18:12:40 +0000
1208@@ -121,7 +121,7 @@
1209 MirInputDeviceId const device_id{7};
1210
1211 auto const modifiers = mir_input_event_modifier_none;
1212- std::vector<MirPointerButton> depressed_buttons{mir_pointer_button_primary};
1213+ auto const depressed_buttons = mir_pointer_button_primary;
1214
1215 auto const x_axis_value = click_position.x.as_float();
1216 auto const y_axis_value = click_position.y.as_float();
1217
1218=== modified file 'tests/acceptance-tests/throwback/CMakeLists.txt'
1219--- tests/acceptance-tests/throwback/CMakeLists.txt 2015-04-13 14:53:35 +0000
1220+++ tests/acceptance-tests/throwback/CMakeLists.txt 2015-05-14 18:12:40 +0000
1221@@ -9,7 +9,6 @@
1222
1223 clients.cpp
1224 test_client_cursor_api.cpp
1225- test_client_input.cpp
1226 test_custom_input_targeter.cpp
1227 test_focus_selection.cpp
1228 test_touchspot_visualization.cpp
1229
1230=== removed file 'tests/acceptance-tests/throwback/test_client_input.cpp'
1231--- tests/acceptance-tests/throwback/test_client_input.cpp 2015-05-05 17:11:45 +0000
1232+++ tests/acceptance-tests/throwback/test_client_input.cpp 1970-01-01 00:00:00 +0000
1233@@ -1,704 +0,0 @@
1234-/*
1235- * Copyright © 2013-2015 Canonical Ltd.
1236- *
1237- * This program is free software: you can redistribute it and/or modify
1238- * it under the terms of the GNU General Public License version 3 as
1239- * published by the Free Software Foundation.
1240- *
1241- * This program is distributed in the hope that it will be useful,
1242- * but WITHOUT ANY WARRANTY; without even the implied warranty of
1243- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
1244- * GNU General Public License for more details.
1245- *
1246- * You should have received a copy of the GNU General Public License
1247- * along with this program. If not, see <http://www.gnu.org/licenses/>.
1248- *
1249- * Authored by: Robert Carr <robert.carr@canonical.com>
1250- * Andreas Pokorny <andreas.pokorny@canonical.com>
1251- * Alexandros Frantzis <alexandros.frantzis@canonical.com>
1252- */
1253-
1254-#include "mir/events/event_private.h"
1255-#include "mir/shell/shell_wrapper.h"
1256-#include "mir/scene/surface_creation_parameters.h"
1257-#include "mir/scene/surface.h"
1258-#include "mir/scene/session.h"
1259-#include "src/server/scene/session_container.h"
1260-
1261-#include "mir_test_framework/in_process_server.h"
1262-#include "mir_test_framework/using_stub_client_platform.h"
1263-#include "mir_test_framework/fake_event_hub_server_configuration.h"
1264-#include "mir_test_framework/declarative_placement_strategy.h"
1265-#include "mir_test/wait_condition.h"
1266-#include "mir_test/fake_event_hub.h"
1267-#include "mir_test/event_matchers.h"
1268-#include "mir_test/spin_wait.h"
1269-
1270-#include "mir_toolkit/mir_client_library.h"
1271-
1272-#include <linux/input.h>
1273-
1274-#include <gtest/gtest.h>
1275-#include <gmock/gmock.h>
1276-#include <cstring>
1277-
1278-namespace mf = mir::frontend;
1279-namespace mt = mir::test;
1280-namespace mtf = mir_test_framework;
1281-namespace mis = mir::input::synthesis;
1282-namespace mia = mir::input::android;
1283-namespace msh = mir::shell;
1284-namespace ms = mir::scene;
1285-namespace geom = mir::geometry;
1286-
1287-namespace
1288-{
1289-
1290-struct MockInputHandler
1291-{
1292- MOCK_METHOD1(handle_input, void(MirEvent const*));
1293-};
1294-
1295-struct InputClient
1296-{
1297- InputClient(std::string const& connect_string, std::string const& client_name)
1298- : connect_string{connect_string}, client_name{client_name}
1299- {
1300- }
1301-
1302-
1303- ~InputClient()
1304- {
1305- if (client_thread.joinable())
1306- client_thread.join();
1307- }
1308-
1309- void start()
1310- {
1311- client_thread = std::thread{[this] { run(); }};
1312- ready_to_accept_events.wait_for_at_most_seconds(5);
1313- }
1314-
1315- void run()
1316- {
1317- auto const connection =
1318- mir_connect_sync(connect_string.c_str(), client_name.c_str());
1319-
1320- auto spec = mir_connection_create_spec_for_normal_surface(connection, surface_width,
1321- surface_height, mir_pixel_format_bgr_888);
1322- mir_surface_spec_set_name(spec, client_name.c_str());
1323- surface = mir_surface_create_sync(spec);
1324- mir_surface_spec_release(spec);
1325-
1326- mir_surface_set_event_handler(surface, handle_input, this);
1327- mir_buffer_stream_swap_buffers_sync(
1328- mir_surface_get_buffer_stream(surface));
1329-
1330- wait_for_surface_to_become_focused_and_exposed();
1331-
1332- ready_to_accept_events.wake_up_everyone();
1333- all_events_received.wait_for_at_most_seconds(10);
1334-
1335- mir_surface_release_sync(surface);
1336- mir_connection_release(connection);
1337- }
1338-
1339- static void handle_input(MirSurface*, MirEvent const* ev, void* context)
1340- {
1341- auto const client = static_cast<InputClient*>(context);
1342-
1343- if (mir_event_get_type(ev) == mir_event_type_surface)
1344- return;
1345-
1346- client->handler.handle_input(ev);
1347- }
1348-
1349- void wait_for_surface_to_become_focused_and_exposed()
1350- {
1351- bool success = mt::spin_wait_for_condition_or_timeout(
1352- [&]
1353- {
1354- return mir_surface_get_visibility(surface) == mir_surface_visibility_exposed &&
1355- mir_surface_get_focus(surface) == mir_surface_focused;
1356- },
1357- std::chrono::seconds{5});
1358-
1359- if (!success)
1360- throw std::runtime_error("Timeout waiting for surface to become focused and exposed");
1361- }
1362-
1363- static int const surface_width = 100;
1364- static int const surface_height = 100;
1365-
1366- std::string const connect_string;
1367- std::string const client_name;
1368-
1369- std::thread client_thread;
1370- MockInputHandler handler;
1371- mir::test::WaitCondition all_events_received;
1372- mir::test::WaitCondition ready_to_accept_events;
1373-
1374- MirSurface* surface;
1375-};
1376-
1377-using ClientInputRegions = std::map<std::string, std::vector<geom::Rectangle>>;
1378-
1379-struct RegionApplyingShell : msh::ShellWrapper
1380-{
1381- RegionApplyingShell(
1382- std::shared_ptr<msh::Shell> wrapped_coordinator,
1383- ClientInputRegions const& client_input_regions)
1384- : msh::ShellWrapper(wrapped_coordinator),
1385- client_input_regions(client_input_regions)
1386- {
1387- }
1388-
1389- mf::SurfaceId create_surface(
1390- std::shared_ptr<ms::Session> const& session,
1391- ms::SurfaceCreationParameters const& params) override
1392- {
1393- auto const id = wrapped->create_surface(session, params);
1394-
1395- auto const surface = session->surface(id);
1396-
1397- if (client_input_regions.find(params.name) != client_input_regions.end())
1398- surface->set_input_region(client_input_regions.at(params.name));
1399-
1400- return id;
1401- }
1402-
1403- ClientInputRegions const& client_input_regions;
1404-};
1405-
1406-struct TestServerConfiguration : mtf::FakeEventHubServerConfiguration
1407-{
1408- TestServerConfiguration(geom::Rectangle const& screen_geometry)
1409- : mtf::FakeEventHubServerConfiguration(
1410- std::vector<geom::Rectangle>{screen_geometry})
1411- {
1412- }
1413-
1414- std::shared_ptr<mir::scene::PlacementStrategy> the_placement_strategy() override
1415- {
1416- return std::make_shared<mtf::DeclarativePlacementStrategy>(
1417- FakeEventHubServerConfiguration::the_placement_strategy(),
1418- client_geometries, client_depths);
1419- }
1420-
1421- std::shared_ptr<msh::Shell>
1422- wrap_shell(std::shared_ptr<msh::Shell> const& wrapped) override
1423- {
1424- return std::make_shared<RegionApplyingShell>(
1425- wrapped,
1426- client_input_regions);
1427- }
1428-
1429- mtf::SurfaceGeometries client_geometries;
1430- mtf::SurfaceDepths client_depths;
1431- ClientInputRegions client_input_regions;
1432-};
1433-
1434-struct TestClientInput : mtf::InProcessServer
1435-{
1436- mir::DefaultServerConfiguration& server_config() override
1437- {
1438- return server_configuration_;
1439- }
1440-
1441- TestServerConfiguration& test_server_config()
1442- {
1443- return server_configuration_;
1444- }
1445-
1446- std::shared_ptr<mir::input::android::FakeEventHub> fake_event_hub()
1447- {
1448- return server_configuration_.fake_event_hub;
1449- }
1450-
1451- std::string const test_client_name_1{"client1"};
1452- std::string const test_client_name_2{"client2"};
1453- geom::Rectangle const screen_geometry{{0, 0}, {1000, 800}};
1454- TestServerConfiguration server_configuration_{screen_geometry};
1455- mtf::UsingStubClientPlatform using_stub_client_platform;
1456-};
1457-
1458-}
1459-
1460-TEST_F(TestClientInput, clients_receive_key_input)
1461-{
1462- using namespace testing;
1463-
1464- InputClient client{new_connection(), test_client_name_1};
1465-
1466- InSequence seq;
1467- EXPECT_CALL(client.handler, handle_input(mt::KeyDownEvent())).Times(1);
1468- EXPECT_CALL(client.handler, handle_input(mt::KeyRepeatEvent())).Times(1);
1469- EXPECT_CALL(client.handler, handle_input(mt::KeyRepeatEvent()))
1470- .WillOnce(mt::WakeUp(&client.all_events_received));
1471-
1472- int const num_events_produced = 3;
1473-
1474- client.start();
1475-
1476- for (int i = 0; i < num_events_produced; i++)
1477- fake_event_hub()->synthesize_event(
1478- mis::a_key_down_event().of_scancode(KEY_ENTER));
1479-}
1480-
1481-TEST_F(TestClientInput, clients_receive_us_english_mapped_keys)
1482-{
1483- using namespace testing;
1484-
1485- InputClient client{new_connection(), test_client_name_1};
1486-
1487- InSequence seq;
1488-
1489- EXPECT_CALL(client.handler,
1490- handle_input(
1491- AllOf(mt::KeyDownEvent(), mt::KeyOfSymbol(XKB_KEY_Shift_L))));
1492- EXPECT_CALL(client.handler,
1493- handle_input(
1494- AllOf(mt::KeyDownEvent(), mt::KeyOfSymbol(XKB_KEY_dollar))))
1495- .WillOnce(mt::WakeUp(&client.all_events_received));
1496-
1497- client.start();
1498-
1499- fake_event_hub()->synthesize_event(
1500- mis::a_key_down_event().of_scancode(KEY_LEFTSHIFT));
1501- fake_event_hub()->synthesize_event(
1502- mis::a_key_down_event().of_scancode(KEY_4));
1503-}
1504-
1505-TEST_F(TestClientInput, clients_receive_pointer_inside_window_and_crossing_events)
1506-{
1507- using namespace testing;
1508-
1509- InputClient client{new_connection(), test_client_name_1};
1510-
1511- InSequence seq;
1512-
1513- // We should see the cursor enter
1514- EXPECT_CALL(client.handler, handle_input(mt::PointerEnterEvent()));
1515- EXPECT_CALL(client.handler,
1516- handle_input(
1517- mt::PointerEventWithPosition(
1518- InputClient::surface_width - 1,
1519- InputClient::surface_height - 1)));
1520- EXPECT_CALL(client.handler, handle_input(mt::PointerLeaveEvent()))
1521- .WillOnce(mt::WakeUp(&client.all_events_received));
1522- // But we should not receive an event for the second movement outside of our surface!
1523-
1524- client.start();
1525-
1526- fake_event_hub()->synthesize_event(
1527- mis::a_pointer_event().with_movement(
1528- InputClient::surface_width - 1,
1529- InputClient::surface_height - 1));
1530- fake_event_hub()->synthesize_event(mis::a_pointer_event().with_movement(2,2));
1531-}
1532-
1533-TEST_F(TestClientInput, clients_receive_button_events_inside_window)
1534-{
1535- using namespace testing;
1536-
1537- InputClient client{new_connection(), test_client_name_1};
1538-
1539- // The cursor starts at (0, 0).
1540- EXPECT_CALL(client.handler, handle_input(mt::ButtonDownEvent(0, 0)))
1541- .WillOnce(mt::WakeUp(&client.all_events_received));
1542-
1543- client.start();
1544-
1545- fake_event_hub()->synthesize_event(
1546- mis::a_button_down_event()
1547- .of_button(BTN_LEFT)
1548- .with_action(mis::EventAction::Down));
1549-}
1550-
1551-TEST_F(TestClientInput, multiple_clients_receive_pointer_inside_windows)
1552-{
1553- using namespace testing;
1554-
1555- int const screen_width = screen_geometry.size.width.as_int();
1556- int const screen_height = screen_geometry.size.height.as_int();
1557- int const client_height = screen_height / 2;
1558- int const client_width = screen_width / 2;
1559-
1560- test_server_config().client_geometries[test_client_name_1] =
1561- {{0, 0}, {client_width, client_height}};
1562- test_server_config().client_geometries[test_client_name_2] =
1563- {{screen_width / 2, screen_height / 2}, {client_width, client_height}};
1564-
1565- InputClient client1{new_connection(), test_client_name_1};
1566- InputClient client2{new_connection(), test_client_name_2};
1567-
1568- {
1569- InSequence seq;
1570- EXPECT_CALL(client1.handler, handle_input(mt::PointerEnterEvent()));
1571- EXPECT_CALL(client1.handler,
1572- handle_input(
1573- mt::PointerEventWithPosition(client_width - 1, client_height - 1)));
1574- EXPECT_CALL(client1.handler, handle_input(mt::PointerLeaveEvent()))
1575- .WillOnce(mt::WakeUp(&client1.all_events_received));
1576- }
1577-
1578- {
1579- InSequence seq;
1580- EXPECT_CALL(client2.handler, handle_input(mt::PointerEnterEvent()));
1581- EXPECT_CALL(client2.handler,
1582- handle_input(
1583- mt::PointerEventWithPosition(client_width - 1, client_height - 1)))
1584- .WillOnce(mt::WakeUp(&client2.all_events_received));
1585- }
1586-
1587- client1.start();
1588- client2.start();
1589-
1590- // In the bounds of the first surface
1591- fake_event_hub()->synthesize_event(
1592- mis::a_pointer_event().with_movement(screen_width / 2 - 1, screen_height / 2 - 1));
1593- // In the bounds of the second surface
1594- fake_event_hub()->synthesize_event(
1595- mis::a_pointer_event().with_movement(screen_width / 2, screen_height / 2));
1596-}
1597-
1598-TEST_F(TestClientInput, clients_do_not_receive_pointer_outside_input_region)
1599-{
1600- using namespace testing;
1601-
1602- int const client_height = InputClient::surface_height;
1603- int const client_width = InputClient::surface_width;
1604-
1605- test_server_config().client_input_regions[test_client_name_1] = {
1606- {{0, 0}, {client_width - 80, client_height}},
1607- {{client_width - 20, 0}, {client_width - 80, client_height}}};
1608-
1609- InputClient client{new_connection(), test_client_name_1};
1610-
1611- EXPECT_CALL(client.handler, handle_input(mt::PointerEnterEvent())).Times(AnyNumber());
1612- EXPECT_CALL(client.handler, handle_input(mt::PointerLeaveEvent())).Times(AnyNumber());
1613- EXPECT_CALL(client.handler, handle_input(mt::PointerMovementEvent())).Times(AnyNumber());
1614-
1615- {
1616- // We should see two of the three button pairs.
1617- InSequence seq;
1618- EXPECT_CALL(client.handler, handle_input(mt::ButtonDownEvent(1, 1)));
1619- EXPECT_CALL(client.handler, handle_input(mt::ButtonUpEvent(1, 1)));
1620- EXPECT_CALL(client.handler, handle_input(mt::ButtonDownEvent(99, 99)));
1621- EXPECT_CALL(client.handler, handle_input(mt::ButtonUpEvent(99, 99)))
1622- .WillOnce(mt::WakeUp(&client.all_events_received));
1623- }
1624-
1625- client.start();
1626-
1627- // First we will move the cursor in to the input region on the left side of
1628- // the window. We should see a click here.
1629- fake_event_hub()->synthesize_event(mis::a_pointer_event().with_movement(1, 1));
1630- fake_event_hub()->synthesize_event(
1631- mis::a_button_down_event()
1632- .of_button(BTN_LEFT)
1633- .with_action(mis::EventAction::Down));
1634- fake_event_hub()->synthesize_event(mis::a_button_up_event().of_button(BTN_LEFT));
1635- // Now in to the dead zone in the center of the window. We should not see
1636- // a click here.
1637- fake_event_hub()->synthesize_event(mis::a_pointer_event().with_movement(49, 49));
1638- fake_event_hub()->synthesize_event(
1639- mis::a_button_down_event()
1640- .of_button(BTN_LEFT)
1641- .with_action(mis::EventAction::Down));
1642- fake_event_hub()->synthesize_event(mis::a_button_up_event().of_button(BTN_LEFT));
1643- // Now in to the right edge of the window, in the right input region.
1644- // Again we should see a click.
1645- fake_event_hub()->synthesize_event(mis::a_pointer_event().with_movement(49, 49));
1646- fake_event_hub()->synthesize_event(
1647- mis::a_button_down_event()
1648- .of_button(BTN_LEFT)
1649- .with_action(mis::EventAction::Down));
1650- fake_event_hub()->synthesize_event(mis::a_button_up_event().of_button(BTN_LEFT));
1651-}
1652-
1653-TEST_F(TestClientInput, scene_obscure_motion_events_by_stacking)
1654-{
1655- using namespace testing;
1656-
1657- auto smaller_geometry = screen_geometry;
1658- smaller_geometry.size.width =
1659- geom::Width{screen_geometry.size.width.as_uint32_t() / 2};
1660-
1661- test_server_config().client_geometries[test_client_name_1] = screen_geometry;
1662- test_server_config().client_geometries[test_client_name_2] = smaller_geometry;
1663- test_server_config().client_depths[test_client_name_1] = ms::DepthId{0};
1664- test_server_config().client_depths[test_client_name_2] = ms::DepthId{1};
1665-
1666- InputClient client1{new_connection(), test_client_name_1};
1667- InputClient client2{new_connection(), test_client_name_2};
1668-
1669- EXPECT_CALL(client1.handler, handle_input(mt::PointerEnterEvent())).Times(AnyNumber());
1670- EXPECT_CALL(client1.handler, handle_input(mt::PointerLeaveEvent())).Times(AnyNumber());
1671- EXPECT_CALL(client1.handler, handle_input(mt::PointerMovementEvent())).Times(AnyNumber());
1672- {
1673- // We should only see one button event sequence.
1674- InSequence seq;
1675- EXPECT_CALL(client1.handler, handle_input(mt::ButtonDownEvent(501, 1)));
1676- EXPECT_CALL(client1.handler, handle_input(mt::ButtonUpEvent(501, 1)))
1677- .WillOnce(mt::WakeUp(&client1.all_events_received));
1678- }
1679-
1680- EXPECT_CALL(client2.handler, handle_input(mt::PointerEnterEvent())).Times(AnyNumber());
1681- EXPECT_CALL(client2.handler, handle_input(mt::PointerLeaveEvent())).Times(AnyNumber());
1682- EXPECT_CALL(client2.handler, handle_input(mt::PointerMovementEvent())).Times(AnyNumber());
1683- {
1684- // Likewise we should only see one button sequence.
1685- InSequence seq;
1686- EXPECT_CALL(client2.handler, handle_input(mt::ButtonDownEvent(1, 1)));
1687- EXPECT_CALL(client2.handler, handle_input(mt::ButtonUpEvent(1, 1)))
1688- .WillOnce(mt::WakeUp(&client2.all_events_received));
1689- }
1690-
1691- client1.start();
1692- client2.start();
1693-
1694- // First we will move the cursor in to the region where client 2 obscures client 1
1695- fake_event_hub()->synthesize_event(mis::a_pointer_event().with_movement(1, 1));
1696- fake_event_hub()->synthesize_event(
1697- mis::a_button_down_event().of_button(BTN_LEFT).with_action(mis::EventAction::Down));
1698- fake_event_hub()->synthesize_event(mis::a_button_up_event().of_button(BTN_LEFT));
1699- // Now we move to the unobscured region of client 1
1700- fake_event_hub()->synthesize_event(mis::a_pointer_event().with_movement(500, 0));
1701- fake_event_hub()->synthesize_event(
1702- mis::a_button_down_event().of_button(BTN_LEFT).with_action(mis::EventAction::Down));
1703- fake_event_hub()->synthesize_event(mis::a_button_up_event().of_button(BTN_LEFT));
1704-}
1705-
1706-TEST_F(TestClientInput, hidden_clients_do_not_receive_pointer_events)
1707-{
1708- using namespace testing;
1709-
1710- mt::WaitCondition second_client_done;
1711-
1712- test_server_config().client_depths[test_client_name_1] = ms::DepthId{0};
1713- test_server_config().client_depths[test_client_name_2] = ms::DepthId{1};
1714-
1715- InputClient client1{new_connection(), test_client_name_1};
1716- InputClient client2{new_connection(), test_client_name_2};
1717-
1718- EXPECT_CALL(client1.handler, handle_input(mt::PointerEnterEvent())).Times(AnyNumber());
1719- EXPECT_CALL(client1.handler, handle_input(mt::PointerLeaveEvent())).Times(AnyNumber());
1720- EXPECT_CALL(client1.handler, handle_input(mt::PointerEventWithPosition(2, 2)))
1721- .WillOnce(mt::WakeUp(&client1.all_events_received));
1722-
1723- EXPECT_CALL(client2.handler, handle_input(mt::PointerEnterEvent())).Times(AnyNumber());
1724- EXPECT_CALL(client2.handler, handle_input(mt::PointerLeaveEvent())).Times(AnyNumber());
1725- EXPECT_CALL(client2.handler, handle_input(mt::PointerEventWithPosition(1, 1)))
1726- .WillOnce(DoAll(mt::WakeUp(&second_client_done),
1727- mt::WakeUp(&client2.all_events_received)));
1728-
1729- client1.start();
1730- client2.start();
1731-
1732- // We send one event and then hide the surface on top before sending the next.
1733- // So we expect each of the two surfaces to receive one even
1734- fake_event_hub()->synthesize_event(mis::a_pointer_event().with_movement(1,1));
1735- // We use a fence to ensure we do not hide the client
1736- // before event dispatch occurs
1737- second_client_done.wait_for_at_most_seconds(60);
1738-
1739- server_config().the_session_container()->for_each(
1740- [&](std::shared_ptr<ms::Session> const& session) -> void
1741- {
1742- if (session->name() == test_client_name_2)
1743- session->hide();
1744- });
1745- // As the surface will not be unocludded immediately when the other surface is
1746- // hidden (due to the compositor feedback approach used)
1747- // See bug: https://bugs.launchpad.net/mir/+bug/1408168
1748- client1.wait_for_surface_to_become_focused_and_exposed();
1749-
1750- fake_event_hub()->synthesize_event(mis::a_pointer_event().with_movement(1,1));
1751-}
1752-
1753-TEST_F(TestClientInput, clients_receive_pointer_within_coordinate_system_of_window)
1754-{
1755- using namespace testing;
1756-
1757- int const screen_width = screen_geometry.size.width.as_int();
1758- int const screen_height = screen_geometry.size.height.as_int();
1759- int const client_height = screen_height / 2;
1760- int const client_width = screen_width / 2;
1761-
1762- test_server_config().client_geometries[test_client_name_1] =
1763- {{screen_width / 2, screen_height / 2}, {client_width, client_height}};
1764-
1765- InputClient client1{new_connection(), test_client_name_1};
1766-
1767- InSequence seq;
1768- EXPECT_CALL(client1.handler, handle_input(mt::PointerEnterEvent()));
1769- EXPECT_CALL(client1.handler, handle_input(mt::PointerEventWithPosition(80, 170)))
1770- .Times(AnyNumber())
1771- .WillOnce(mt::WakeUp(&client1.all_events_received));
1772-
1773- client1.start();
1774-
1775- server_config().the_session_container()->for_each(
1776- [&](std::shared_ptr<ms::Session> const& session) -> void
1777- {
1778- session->default_surface()->move_to(
1779- geom::Point{screen_width / 2 - 40, screen_height / 2 - 80});
1780- });
1781-
1782- fake_event_hub()->synthesize_event(
1783- mis::a_pointer_event().with_movement(screen_width / 2 + 40, screen_height / 2 + 90));
1784-}
1785-
1786-// TODO: Consider tests for more input devices with custom mapping (i.e. joysticks...)
1787-TEST_F(TestClientInput, usb_direct_input_devices_work)
1788-{
1789- using namespace ::testing;
1790-
1791- auto const minimum_touch = mia::FakeEventHub::TouchScreenMinAxisValue;
1792- auto const maximum_touch = mia::FakeEventHub::TouchScreenMaxAxisValue;
1793- auto const display_width = screen_geometry.size.width.as_uint32_t();
1794- auto const display_height = screen_geometry.size.height.as_uint32_t();
1795-
1796- // We place a click 10% in to the touchscreens space in both axis,
1797- // and a second at 0,0. Thus we expect to see a click at
1798- // .1*screen_width/height and a second at zero zero.
1799- static int const abs_touch_x_1 = minimum_touch + (maximum_touch - minimum_touch) * 0.1;
1800- static int const abs_touch_y_1 = minimum_touch + (maximum_touch - minimum_touch) * 0.1;
1801- static int const abs_touch_x_2 = 0;
1802- static int const abs_touch_y_2 = 0;
1803-
1804- static float const expected_scale_x =
1805- float(display_width) / (maximum_touch - minimum_touch + 1);
1806- static float const expected_scale_y =
1807- float(display_height) / (maximum_touch - minimum_touch + 1);
1808-
1809- static float const expected_motion_x_1 = expected_scale_x * abs_touch_x_1;
1810- static float const expected_motion_y_1 = expected_scale_y * abs_touch_y_1;
1811- static float const expected_motion_x_2 = expected_scale_x * abs_touch_x_2;
1812- static float const expected_motion_y_2 = expected_scale_y * abs_touch_y_2;
1813-
1814- test_server_config().client_geometries[test_client_name_1] = screen_geometry;
1815-
1816- InputClient client1{new_connection(), test_client_name_1};
1817-
1818- InSequence seq;
1819- EXPECT_CALL(client1.handler, handle_input(
1820- mt::TouchEvent(expected_motion_x_1, expected_motion_y_1)));
1821- EXPECT_CALL(client1.handler, handle_input(
1822- mt::TouchEventInDirection(expected_motion_x_1,
1823- expected_motion_y_1,
1824- expected_motion_x_2,
1825- expected_motion_y_2)))
1826- .WillOnce(mt::WakeUp(&client1.all_events_received));
1827-
1828- client1.start();
1829-
1830- fake_event_hub()->synthesize_event(
1831- mis::a_touch_event().at_position({abs_touch_x_1, abs_touch_y_1}));
1832- // Sleep here to trigger more failures (simulate slow machine)
1833- std::this_thread::sleep_for(std::chrono::milliseconds(10));
1834- fake_event_hub()->synthesize_event(
1835- mis::a_touch_event().at_position({abs_touch_x_2, abs_touch_y_2}));
1836-}
1837-
1838-TEST_F(TestClientInput, send_mir_input_events_through_surface)
1839-{
1840- InputClient client1{new_connection(), test_client_name_1};
1841-
1842- EXPECT_CALL(client1.handler, handle_input(mt::KeyDownEvent()))
1843- .WillOnce(mt::WakeUp(&client1.all_events_received));
1844-
1845- client1.start();
1846-
1847- server_config().the_session_container()->for_each(
1848- [] (std::shared_ptr<ms::Session> const& session) -> void
1849- {
1850- MirEvent key_event;
1851- std::memset(&key_event, 0, sizeof key_event);
1852- key_event.type = mir_event_type_key;
1853- key_event.key.action = mir_keyboard_action_down;
1854-
1855- session->default_surface()->consume(key_event);
1856- });
1857-}
1858-
1859-TEST_F(TestClientInput, clients_receive_keymap_change_events)
1860-{
1861- using namespace testing;
1862-
1863- mt::WaitCondition first_event_received;
1864- InputClient client{new_connection(), test_client_name_1};
1865-
1866- xkb_rule_names names;
1867- names.rules = "evdev";
1868- names.model = "pc105";
1869- names.layout = "dvorak";
1870- names.variant = "";
1871- names.options = "";
1872-
1873- InSequence seq;
1874- EXPECT_CALL(client.handler, handle_input(
1875- mt::KeymapEventWithRules(names)))
1876- .Times(1).WillOnce(mt::WakeUp(&client.all_events_received));
1877-
1878- client.start();
1879-
1880- server_config().the_session_container()->for_each(
1881- [names] (std::shared_ptr<ms::Session> const& session) -> void
1882- {
1883- session->default_surface()->set_keymap(names);
1884- });
1885-}
1886-
1887-
1888-TEST_F(TestClientInput, keymap_changes_change_keycode_received)
1889-{
1890- using namespace testing;
1891-
1892- xkb_rule_names names;
1893- names.rules = "evdev";
1894- names.model = "pc105";
1895- names.layout = "us";
1896- names.variant = "dvorak";
1897- names.options = "";
1898-
1899- mt::WaitCondition first_event_received,
1900- client_sees_keymap_change;
1901- InputClient client{new_connection(), test_client_name_1};
1902-
1903- InSequence seq;
1904- EXPECT_CALL(client.handler, handle_input(AllOf(
1905- mt::KeyDownEvent(), mt::KeyOfSymbol(XKB_KEY_n)))).Times(1);
1906- EXPECT_CALL(client.handler, handle_input(mt::KeyUpEvent()))
1907- .Times(1).WillOnce(mt::WakeUp(&first_event_received));
1908- EXPECT_CALL(client.handler, handle_input(
1909- mt::KeymapEventWithRules(names)))
1910- .Times(1).WillOnce(mt::WakeUp(&client_sees_keymap_change));
1911- EXPECT_CALL(client.handler, handle_input(AllOf(
1912- mt::KeyDownEvent(), mt::KeyOfSymbol(XKB_KEY_b)))).Times(1);
1913- EXPECT_CALL(client.handler, handle_input(mt::KeyUpEvent()))
1914- .Times(1).WillOnce(mt::WakeUp(&client.all_events_received));
1915-
1916- client.start();
1917-
1918- fake_event_hub()->synthesize_event(
1919- mis::a_key_down_event().of_scancode(KEY_N));
1920- fake_event_hub()->synthesize_event(
1921- mis::a_key_up_event().of_scancode(KEY_N));
1922-
1923- first_event_received.wait_for_at_most_seconds(60);
1924-
1925- server_config().the_session_container()->for_each(
1926- [&names] (std::shared_ptr<ms::Session> const& session) -> void
1927- {
1928- session->default_surface()->set_keymap(names);
1929- });
1930-
1931- client_sees_keymap_change.wait_for_at_most_seconds(60);
1932-
1933- fake_event_hub()->synthesize_event(
1934- mis::a_key_down_event().of_scancode(KEY_N));
1935- fake_event_hub()->synthesize_event(
1936- mis::a_key_up_event().of_scancode(KEY_N));
1937-}
1938
1939=== modified file 'tests/acceptance-tests/throwback/test_custom_input_targeter.cpp'
1940--- tests/acceptance-tests/throwback/test_custom_input_targeter.cpp 2015-05-07 07:07:20 +0000
1941+++ tests/acceptance-tests/throwback/test_custom_input_targeter.cpp 2015-05-14 18:12:40 +0000
1942@@ -40,7 +40,6 @@
1943 namespace ms = mir::scene;
1944 namespace mi = mir::input;
1945 namespace mt = mir::test;
1946-namespace mtd = mt::doubles;
1947 namespace mt = mir::test;
1948 namespace mc = mir::compositor;
1949 namespace mtf = mir_test_framework;
1950
1951=== modified file 'tests/include/mir_test/event_matchers.h'
1952--- tests/include/mir_test/event_matchers.h 2015-05-07 07:07:20 +0000
1953+++ tests/include/mir_test/event_matchers.h 2015-05-14 18:12:40 +0000
1954@@ -135,10 +135,9 @@
1955 return false;
1956
1957 if(mir_keyboard_event_modifiers(kev) != modifiers)
1958- {
1959- printf("modifiers: %d vs expected %d \n", mir_keyboard_event_modifiers(kev), modifiers);
1960+ {
1961 return false;
1962- }
1963+ }
1964
1965 return true;
1966 }
1967@@ -229,36 +228,38 @@
1968 return false;
1969 }
1970
1971+inline bool button_event_matches(MirPointerEvent const* pev, float x, float y, MirPointerAction action, MirPointerButtons button_state,
1972+ bool check_action = true)
1973+{
1974+ if (pev == nullptr)
1975+ return false;
1976+ if (check_action && mir_pointer_event_action(pev) != action)
1977+ return false;
1978+ if (mir_pointer_event_buttons(pev) != button_state)
1979+ return false;
1980+ if (mir_pointer_event_axis_value(pev, mir_pointer_axis_x) != x)
1981+ return false;
1982+ if (mir_pointer_event_axis_value(pev, mir_pointer_axis_y) != y)
1983+ return false;
1984+ return true;
1985+}
1986+
1987 MATCHER_P2(ButtonDownEvent, x, y, "")
1988 {
1989 auto pev = maybe_pointer_event(to_address(arg));
1990- if (pev == nullptr)
1991- return false;
1992- if (mir_pointer_event_action(pev) != mir_pointer_action_button_down)
1993- return false;
1994- if (mir_pointer_event_button_state(pev, mir_pointer_button_primary) == false)
1995- return false;
1996- if (mir_pointer_event_axis_value(pev, mir_pointer_axis_x) != x)
1997- return false;
1998- if (mir_pointer_event_axis_value(pev, mir_pointer_axis_y) != y)
1999- return false;
2000- return true;
2001+ return button_event_matches(pev, x, y, mir_pointer_action_button_down, mir_pointer_button_primary);
2002 }
2003
2004 MATCHER_P2(ButtonUpEvent, x, y, "")
2005 {
2006 auto pev = maybe_pointer_event(to_address(arg));
2007- if (pev == nullptr)
2008- return false;
2009- if (mir_pointer_event_action(pev) != mir_pointer_action_button_up)
2010- return false;
2011- if (mir_pointer_event_button_state(pev, mir_pointer_button_primary) == true)
2012- return false;
2013- if (mir_pointer_event_axis_value(pev, mir_pointer_axis_x) != x)
2014- return false;
2015- if (mir_pointer_event_axis_value(pev, mir_pointer_axis_y) != y)
2016- return false;
2017- return true;
2018+ return button_event_matches(pev, x, y, mir_pointer_action_button_up, 0);
2019+}
2020+
2021+MATCHER_P3(ButtonsDown, x, y, buttons, "")
2022+{
2023+ auto pev = maybe_pointer_event(to_address(arg));
2024+ return button_event_matches(pev, x, y, mir_pointer_action_button_down, buttons, false);
2025 }
2026
2027 MATCHER_P2(TouchEvent, x, y, "")
2028@@ -269,9 +270,9 @@
2029
2030 if (mir_touch_event_action(tev, 0) != mir_touch_action_down)
2031 return false;
2032- if (mir_touch_event_axis_value(tev, 0, mir_touch_axis_x) != x)
2033+ if (std::abs(mir_touch_event_axis_value(tev, 0, mir_touch_axis_x) - x) > 0.5f)
2034 return false;
2035- if (mir_touch_event_axis_value(tev, 0, mir_touch_axis_y) != y)
2036+ if (std::abs(mir_touch_event_axis_value(tev, 0, mir_touch_axis_y) - y) > 0.5f)
2037 return false;
2038
2039 return true;
2040
2041=== modified file 'tests/include/mir_test_framework/input_testing_server_configuration.h'
2042--- tests/include/mir_test_framework/input_testing_server_configuration.h 2015-03-31 09:10:52 +0000
2043+++ tests/include/mir_test_framework/input_testing_server_configuration.h 2015-05-14 18:12:40 +0000
2044@@ -31,24 +31,6 @@
2045 #include <atomic>
2046 #include <thread>
2047
2048-namespace mir
2049-{
2050-namespace input
2051-{
2052-namespace android
2053-{
2054-class FakeEventHub;
2055-}
2056-}
2057-namespace test
2058-{
2059-namespace doubles
2060-{
2061-class FakeEventHubInputConfiguration;
2062-}
2063-}
2064-}
2065-
2066 namespace mir_test_framework
2067 {
2068
2069@@ -61,20 +43,16 @@
2070 void on_start() override;
2071 void on_exit() override;
2072
2073- std::shared_ptr<droidinput::EventHubInterface> the_event_hub() override;
2074 std::shared_ptr<mir::input::InputManager> the_input_manager() override;
2075 std::shared_ptr<mir::input::InputDispatcher> the_input_dispatcher() override;
2076 std::shared_ptr<mir::shell::InputTargeter> the_input_targeter() override;
2077 std::shared_ptr<mir::input::InputSender> the_input_sender() override;
2078
2079-
2080 protected:
2081 virtual void inject_input() = 0;
2082
2083 void wait_until_client_appears(std::string const& surface_name);
2084
2085-private:
2086- std::shared_ptr<mir::input::android::FakeEventHub> fake_event_hub;
2087 std::thread input_injection_thread;
2088 };
2089
2090
2091=== added file 'tests/include/mir_test_framework/placement_applying_shell.h'
2092--- tests/include/mir_test_framework/placement_applying_shell.h 1970-01-01 00:00:00 +0000
2093+++ tests/include/mir_test_framework/placement_applying_shell.h 2015-05-14 18:12:40 +0000
2094@@ -0,0 +1,59 @@
2095+/*
2096+ * Copyright © 2015 Canonical Ltd.
2097+ *
2098+ * This program is free software: you can redistribute it and/or modify it
2099+ * under the terms of the GNU General Public License version 3,
2100+ * as published by the Free Software Foundation.
2101+ *
2102+ * This program is distributed in the hope that it will be useful,
2103+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
2104+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
2105+ * GNU General Public License for more details.
2106+ *
2107+ * You should have received a copy of the GNU General Public License
2108+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
2109+ *
2110+ * Authored by: Andreas Pokorny <andreas.pokorny@canonical.com>
2111+ */
2112+
2113+#ifndef MIR_TEST_FRAMEWORK_PLACEMENT_APPLYING_SHELL_H_
2114+#define MIR_TEST_FRAMEWORK_PLACEMENT_APPLYING_SHELL_H_
2115+
2116+#include "mir/shell/shell_wrapper.h"
2117+#include "mir/geometry/rectangle.h"
2118+#include "mir/scene/depth_id.h"
2119+
2120+#include "mir/scene/session.h"
2121+#include "mir/scene/surface.h"
2122+#include "mir/scene/surface_creation_parameters.h"
2123+
2124+#include <vector>
2125+#include <string>
2126+#include <map>
2127+
2128+namespace mir_test_framework
2129+{
2130+using ClientInputRegions = std::map<std::string, std::vector<mir::geometry::Rectangle>>;
2131+using ClientPositions = std::map<std::string, mir::geometry::Rectangle>;
2132+using ClientDepths = std::map<std::string, mir::scene::DepthId>;
2133+
2134+struct PlacementApplyingShell : mir::shell::ShellWrapper
2135+{
2136+ PlacementApplyingShell(
2137+ std::shared_ptr<mir::shell::Shell> wrapped_coordinator,
2138+ ClientInputRegions const& client_input_regions,
2139+ ClientPositions const& client_positions,
2140+ ClientDepths const& client_depths);
2141+
2142+ mir::frontend::SurfaceId create_surface(
2143+ std::shared_ptr<mir::scene::Session> const& session,
2144+ mir::scene::SurfaceCreationParameters const& params) override;
2145+private:
2146+ ClientInputRegions const& client_input_regions;
2147+ ClientPositions const& client_positions;
2148+ ClientDepths const& client_depths;
2149+};
2150+
2151+}
2152+
2153+#endif
2154
2155=== modified file 'tests/integration-tests/input/CMakeLists.txt'
2156--- tests/integration-tests/input/CMakeLists.txt 2015-04-16 22:11:07 +0000
2157+++ tests/integration-tests/input/CMakeLists.txt 2015-05-14 18:12:40 +0000
2158@@ -1,12 +1,9 @@
2159 list(
2160 APPEND INTEGRATION_TESTS_SRCS
2161 ${CMAKE_CURRENT_SOURCE_DIR}/test_configuring_input_manager.cpp
2162+ ${CMAKE_CURRENT_SOURCE_DIR}/test_cursor_listener.cpp
2163 )
2164
2165-if(NOT MIR_TEST_PLATFORM STREQUAL "android")
2166- add_subdirectory(android)
2167-endif()
2168-
2169 set(
2170 INTEGRATION_TESTS_SRCS
2171 ${INTEGRATION_TESTS_SRCS}
2172
2173=== removed directory 'tests/integration-tests/input/android'
2174=== removed file 'tests/integration-tests/input/android/CMakeLists.txt'
2175--- tests/integration-tests/input/android/CMakeLists.txt 2015-05-06 12:57:11 +0000
2176+++ tests/integration-tests/input/android/CMakeLists.txt 1970-01-01 00:00:00 +0000
2177@@ -1,9 +0,0 @@
2178-list(
2179- APPEND INTEGRATION_TESTS_SRCS
2180- ${CMAKE_CURRENT_SOURCE_DIR}/test_android_cursor_listener.cpp
2181-)
2182-
2183-set(
2184- INTEGRATION_TESTS_SRCS
2185- ${INTEGRATION_TESTS_SRCS}
2186- PARENT_SCOPE)
2187
2188=== renamed file 'tests/integration-tests/input/android/test_android_cursor_listener.cpp' => 'tests/integration-tests/input/test_cursor_listener.cpp'
2189--- tests/integration-tests/input/android/test_android_cursor_listener.cpp 2015-04-20 20:07:52 +0000
2190+++ tests/integration-tests/input/test_cursor_listener.cpp 2015-05-14 18:12:40 +0000
2191@@ -20,8 +20,10 @@
2192 #include "mir/events/event_private.h"
2193
2194 #include "mir_test/fake_shared.h"
2195-#include "mir_test/fake_event_hub.h"
2196-#include "mir_test_framework/fake_event_hub_server_configuration.h"
2197+#include "mir_test_framework/fake_input_device.h"
2198+#include "mir_test_framework/stubbed_server_configuration.h"
2199+#include "mir_test_framework/stub_server_platform_factory.h"
2200+#include "mir_test_framework/temporary_environment_value.h"
2201 #include "mir_test_doubles/stub_input_enumerator.h"
2202 #include "mir_test_doubles/stub_touch_visualizer.h"
2203 #include "mir_test/wait_condition.h"
2204@@ -30,17 +32,18 @@
2205 #include "mir/input/cursor_listener.h"
2206 #include "mir/input/input_dispatcher.h"
2207 #include "mir/input/input_manager.h"
2208+#include "mir/input/input_device_info.h"
2209+#include "mir_test_framework/executable_path.h"
2210
2211 #include <gmock/gmock.h>
2212 #include <gtest/gtest.h>
2213
2214 #include <thread>
2215+#include <cstdlib>
2216
2217 namespace mi = mir::input;
2218-namespace mia = mir::input::android;
2219 namespace mis = mir::input::synthesis;
2220 namespace mt = mir::test;
2221-namespace mtd = mir::test::doubles;
2222 namespace mtf = mir_test_framework;
2223
2224 namespace
2225@@ -54,8 +57,11 @@
2226 ~MockCursorListener() noexcept {}
2227 };
2228
2229-struct AndroidCursorListenerIntegrationTest : testing::Test, mtf::FakeEventHubServerConfiguration
2230+struct CursorListenerIntegrationTest : testing::Test, mtf::StubbedServerConfiguration
2231 {
2232+ mtf::TemporaryEnvironmentValue input_lib{"MIR_SERVER_PLATFORM_INPUT_LIB", mtf::server_platform("input-stub.so").c_str()};
2233+ mtf::TemporaryEnvironmentValue real_input{"MIR_SERVER_TESTS_USE_REAL_INPUT", "1"};
2234+
2235 bool is_key_repeat_enabled() const override
2236 {
2237 return false;
2238@@ -83,11 +89,15 @@
2239 MockCursorListener cursor_listener;
2240 std::shared_ptr<mi::InputManager> input_manager;
2241 std::shared_ptr<mi::InputDispatcher> input_dispatcher;
2242+
2243+ std::unique_ptr<mtf::FakeInputDevice> fake_mouse{
2244+ mtf::add_fake_input_device(mi::InputDeviceInfo{ 0, "mouse", "mouse-uid" , mi::DeviceCapability::pointer})
2245+ };
2246 };
2247
2248 }
2249
2250-TEST_F(AndroidCursorListenerIntegrationTest, cursor_listener_receives_motion)
2251+TEST_F(CursorListenerIntegrationTest, cursor_listener_receives_motion)
2252 {
2253 using namespace ::testing;
2254
2255@@ -98,10 +108,7 @@
2256
2257 EXPECT_CALL(cursor_listener, cursor_moved_to(x, y)).Times(1).WillOnce(mt::WakeUp(wait_condition));
2258
2259- fake_event_hub->synthesize_builtin_cursor_added();
2260- fake_event_hub->synthesize_device_scan_complete();
2261-
2262- fake_event_hub->synthesize_event(mis::a_pointer_event().with_movement(x, y));
2263+ fake_mouse->emit_event(mis::a_pointer_event().with_movement(x, y));
2264
2265 wait_condition->wait_for_at_most_seconds(10);
2266 }
2267
2268=== modified file 'tests/mir_test_framework/CMakeLists.txt'
2269--- tests/mir_test_framework/CMakeLists.txt 2015-04-30 11:36:36 +0000
2270+++ tests/mir_test_framework/CMakeLists.txt 2015-05-14 18:12:40 +0000
2271@@ -46,6 +46,7 @@
2272 fake_event_hub_server_configuration.cpp
2273 any_surface.cpp
2274 headless_nested_server_runner.cpp
2275+ placement_applying_shell.cpp
2276 )
2277
2278 list(APPEND TEST_FRAMEWORK_SRCS
2279
2280=== modified file 'tests/mir_test_framework/fake_input_device_impl.cpp'
2281--- tests/mir_test_framework/fake_input_device_impl.cpp 2015-05-07 20:41:25 +0000
2282+++ tests/mir_test_framework/fake_input_device_impl.cpp 2015-05-14 18:12:40 +0000
2283@@ -144,7 +144,7 @@
2284
2285 mtf::FakeInputDeviceImpl::InputDevice::InputDevice(mi::InputDeviceInfo const& info,
2286 std::shared_ptr<mir::dispatch::Dispatchable> const& dispatchable)
2287- : info(info), queue{dispatchable}
2288+ : info(info), queue{dispatchable}, buttons{0}
2289 {
2290 }
2291
2292@@ -197,12 +197,12 @@
2293 {
2294 if (action == synthesis::EventAction::Down)
2295 {
2296- buttons.push_back(button);
2297+ buttons |= button;
2298 return mir_pointer_action_button_down;
2299 }
2300 else
2301 {
2302- buttons.erase(remove(begin(buttons), end(buttons), button));
2303+ buttons &= ~button;
2304 return mir_pointer_action_button_up;
2305 }
2306 }
2307
2308=== modified file 'tests/mir_test_framework/fake_input_device_impl.h'
2309--- tests/mir_test_framework/fake_input_device_impl.h 2015-05-04 07:28:06 +0000
2310+++ tests/mir_test_framework/fake_input_device_impl.h 2015-05-14 18:12:40 +0000
2311@@ -25,8 +25,6 @@
2312 #include "mir/input/input_device_info.h"
2313 #include "mir/geometry/point.h"
2314
2315-#include <vector>
2316-
2317 namespace mir
2318 {
2319 namespace dispatch
2320@@ -76,7 +74,7 @@
2321 std::shared_ptr<mir::dispatch::Dispatchable> const queue;
2322 uint32_t modifiers{0};
2323 mir::geometry::Point pos, scroll;
2324- std::vector<MirPointerButton> buttons;
2325+ MirPointerButtons buttons;
2326 };
2327 std::shared_ptr<mir::dispatch::ActionQueue> queue;
2328 std::shared_ptr<InputDevice> device;
2329
2330=== modified file 'tests/mir_test_framework/input_testing_server_options.cpp'
2331--- tests/mir_test_framework/input_testing_server_options.cpp 2015-03-31 02:35:42 +0000
2332+++ tests/mir_test_framework/input_testing_server_options.cpp 2015-05-14 18:12:40 +0000
2333@@ -24,7 +24,6 @@
2334 #include "mir/frontend/session.h"
2335 #include "mir/input/composite_event_filter.h"
2336
2337-#include "mir_test/fake_event_hub.h"
2338 #include "mir_test/wait_condition.h"
2339
2340 #include <boost/throw_exception.hpp>
2341@@ -41,7 +40,6 @@
2342 namespace ms = mir::shell;
2343 namespace mia = mi::android;
2344 namespace geom = mir::geometry;
2345-namespace mtd = mir::test::doubles;
2346
2347 mtf::InputTestingServerConfiguration::InputTestingServerConfiguration()
2348 {
2349@@ -96,18 +94,3 @@
2350 {
2351 return DefaultServerConfiguration::the_input_sender();
2352 }
2353-
2354-std::shared_ptr<droidinput::EventHubInterface> mtf::InputTestingServerConfiguration::the_event_hub()
2355-{
2356- if (!fake_event_hub)
2357- {
2358- fake_event_hub = std::make_shared<mia::FakeEventHub>();
2359-
2360- fake_event_hub->synthesize_builtin_keyboard_added();
2361- fake_event_hub->synthesize_builtin_cursor_added();
2362- fake_event_hub->synthesize_usb_touchscreen_added();
2363- fake_event_hub->synthesize_device_scan_complete();
2364- }
2365-
2366- return fake_event_hub;
2367-}
2368
2369=== added file 'tests/mir_test_framework/placement_applying_shell.cpp'
2370--- tests/mir_test_framework/placement_applying_shell.cpp 1970-01-01 00:00:00 +0000
2371+++ tests/mir_test_framework/placement_applying_shell.cpp 2015-05-14 18:12:40 +0000
2372@@ -0,0 +1,59 @@
2373+/*
2374+ * Copyright © 2015 Canonical Ltd.
2375+ *
2376+ * This program is free software: you can redistribute it and/or modify it
2377+ * under the terms of the GNU General Public License version 3,
2378+ * as published by the Free Software Foundation.
2379+ *
2380+ * This program is distributed in the hope that it will be useful,
2381+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
2382+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
2383+ * GNU General Public License for more details.
2384+ *
2385+ * You should have received a copy of the GNU General Public License
2386+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
2387+ *
2388+ * Authored by: Andreas Pokorny <andreas.pokorny@canonical.com>
2389+ */
2390+
2391+#include "mir_test_framework/placement_applying_shell.h"
2392+
2393+namespace mtf = mir_test_framework;
2394+
2395+mtf::PlacementApplyingShell::PlacementApplyingShell(std::shared_ptr<mir::shell::Shell> wrapped_coordinator,
2396+ ClientInputRegions const& client_input_regions,
2397+ ClientPositions const& client_positions,
2398+ ClientDepths const& client_depths) :
2399+ mir::shell::ShellWrapper(wrapped_coordinator),
2400+ client_input_regions(client_input_regions),
2401+ client_positions(client_positions),
2402+ client_depths(client_depths)
2403+{
2404+}
2405+
2406+mir::frontend::SurfaceId mtf::PlacementApplyingShell::create_surface(
2407+ std::shared_ptr<mir::scene::Session> const& session,
2408+ mir::scene::SurfaceCreationParameters const& params)
2409+{
2410+ auto creation_parameters = params;
2411+
2412+ auto depth = client_depths.find(params.name);
2413+ if (depth != client_depths.end())
2414+ creation_parameters.depth = depth->second;
2415+
2416+ auto const id = wrapped->create_surface(session, creation_parameters);
2417+ auto const surface = session->surface(id);
2418+
2419+ auto position= client_positions.find(params.name);
2420+ if (position != client_positions.end())
2421+ {
2422+ surface->move_to(position->second.top_left);
2423+ surface->resize(position->second.size);
2424+ }
2425+
2426+ auto regions = client_input_regions.find(params.name);
2427+ if (regions != client_input_regions.end())
2428+ surface->set_input_region(regions->second);
2429+
2430+ return id;
2431+}
2432
2433=== modified file 'tests/unit-tests/input/android/test_android_input_dispatcher.cpp'
2434--- tests/unit-tests/input/android/test_android_input_dispatcher.cpp 2015-05-05 18:01:08 +0000
2435+++ tests/unit-tests/input/android/test_android_input_dispatcher.cpp 2015-05-14 18:12:40 +0000
2436@@ -173,15 +173,16 @@
2437 std::memset(expected_coords, 0, sizeof(expected_coords));
2438 std::memset(expected_properties, 0, sizeof(expected_properties));
2439 MirEvent event;
2440+ std::memset(&event, 0, sizeof(event));
2441+
2442 event.type = mir_event_type_motion;
2443 event.motion.pointer_count = 1;
2444 event.motion.event_time = std::chrono::nanoseconds(2);
2445 event.motion.device_id = 3;
2446 event.motion.source_id = 4;
2447 event.motion.action = mir_motion_action_scroll;
2448- event.motion.modifiers = mir_input_event_modifier_shift,
2449- event.motion.button_state =
2450- static_cast<MirMotionButton>(mir_motion_button_forward | mir_motion_button_secondary);
2451+ event.motion.modifiers = mir_input_event_modifier_shift;
2452+ event.motion.buttons = mir_pointer_button_forward | mir_pointer_button_secondary;
2453
2454 auto & pointer = event.motion.pointer_coordinates[0];
2455 pointer.id = 1;
2456@@ -215,7 +216,7 @@
2457 event.motion.action,
2458 0, /* flags */
2459 AMETA_SHIFT_ON,
2460- event.motion.button_state,
2461+ AMOTION_EVENT_BUTTON_FORWARD | AMOTION_EVENT_BUTTON_SECONDARY,
2462 0, /* edge_flags */
2463 event.motion.pointer_count,
2464 expected_properties,
2465
2466=== modified file 'tests/unit-tests/input/android/test_android_input_lexicon.cpp'
2467--- tests/unit-tests/input/android/test_android_input_lexicon.cpp 2015-05-05 18:01:08 +0000
2468+++ tests/unit-tests/input/android/test_android_input_lexicon.cpp 2015-05-14 18:12:40 +0000
2469@@ -78,7 +78,7 @@
2470 const int32_t flags = 4;
2471 const int32_t edge_flags = 5;
2472 const int32_t meta_state = 6;
2473- const int32_t button_state = 7;
2474+ const int32_t button_state = 0;
2475 const float x_offset = 8;
2476 const float y_offset = 9;
2477 const float x_precision = 10;
2478@@ -134,7 +134,6 @@
2479
2480 auto mir_motion_ev = &mir_ev.motion;
2481
2482- EXPECT_EQ(mir_motion_ev->button_state, button_state);
2483 EXPECT_EQ(mir_motion_ev->event_time, event_time);
2484
2485 EXPECT_EQ(mir_motion_ev->pointer_count, pointer_count);
2486@@ -170,7 +169,6 @@
2487 const int32_t flags = 4;
2488 const int32_t edge_flags = 5;
2489 const int32_t meta_state = 6;
2490- const int32_t button_state = 7;
2491 const float x_offset = 8;
2492 const float y_offset = 9;
2493 const float x_precision = 10;
2494@@ -223,7 +221,7 @@
2495 }
2496
2497 android_motion_ev->initialize(device_id, source_id, action, flags,
2498- edge_flags, meta_state, button_state,
2499+ edge_flags, meta_state, 0,
2500 x_offset, y_offset, x_precision, y_precision,
2501 down_time, event_time, pointer_count,
2502 pointer_properties, pointer_coords);
2503@@ -241,7 +239,6 @@
2504
2505 auto mir_motion_ev = &mir_ev.motion;
2506
2507- EXPECT_EQ(mir_motion_ev->button_state, button_state);
2508 EXPECT_EQ(mir_motion_ev->event_time, event_time);
2509 EXPECT_EQ(mir_motion_ev->pointer_count, pointer_count);
2510
2511
2512=== modified file 'tests/unit-tests/input/android/test_input_translator.cpp'
2513--- tests/unit-tests/input/android/test_input_translator.cpp 2015-05-07 19:43:49 +0000
2514+++ tests/unit-tests/input/android/test_input_translator.cpp 2015-05-14 18:12:40 +0000
2515@@ -266,8 +266,7 @@
2516 expected.motion.source_id = 4;
2517 expected.motion.action = mir_motion_action_scroll;
2518 expected.motion.modifiers = 6;
2519- expected.motion.button_state =
2520- static_cast<MirMotionButton>(mir_motion_button_forward | mir_motion_button_secondary);
2521+ expected.motion.buttons = mir_pointer_button_forward | mir_pointer_button_secondary;
2522
2523 auto & pointer = expected.motion.pointer_coordinates[0];
2524 pointer.id = 1;
2525@@ -303,7 +302,7 @@
2526 expected.motion.action,
2527 0, /* flags */
2528 expected.motion.modifiers,
2529- expected.motion.button_state,
2530+ AMOTION_EVENT_BUTTON_FORWARD | AMOTION_EVENT_BUTTON_SECONDARY,
2531 0, /* edge flags */
2532 expected.motion.pointer_count,
2533 properties,
2534
2535=== modified file 'tests/unit-tests/input/test_default_input_device_hub.cpp'
2536--- tests/unit-tests/input/test_default_input_device_hub.cpp 2015-05-07 20:41:25 +0000
2537+++ tests/unit-tests/input/test_default_input_device_hub.cpp 2015-05-14 18:12:40 +0000
2538@@ -31,6 +31,7 @@
2539 #include "mir/dispatch/multiplexing_dispatchable.h"
2540 #include "mir/dispatch/action_queue.h"
2541 #include "mir/events/event_builders.h"
2542+#include "mir/input/cursor_listener.h"
2543
2544 #include <gmock/gmock.h>
2545 #include <gtest/gtest.h>
2546@@ -73,6 +74,13 @@
2547 MOCK_METHOD0(changes_complete, void());
2548 };
2549
2550+struct MockCursorListener : public mi::CursorListener
2551+{
2552+ MOCK_METHOD2(cursor_moved_to, void(float, float));
2553+
2554+ ~MockCursorListener() noexcept {}
2555+};
2556+
2557 struct MockInputDevice : public mi::InputDevice
2558 {
2559 mir::dispatch::ActionQueue queue;
2560@@ -90,11 +98,12 @@
2561 mtd::TriggeredMainLoop observer_loop;
2562 Nice<mtd::MockInputDispatcher> mock_dispatcher;
2563 Nice<mtd::MockInputRegion> mock_region;
2564+ Nice<MockCursorListener> mock_cursor_listener;
2565 Nice<MockTouchVisualizer> mock_visualizer;
2566 mir::dispatch::MultiplexingDispatchable multiplexer;
2567 mi::DefaultInputDeviceHub hub{mt::fake_shared(mock_dispatcher), mt::fake_shared(multiplexer),
2568 mt::fake_shared(observer_loop), mt::fake_shared(mock_visualizer),
2569- mt::fake_shared(mock_region)};
2570+ mt::fake_shared(mock_cursor_listener), mt::fake_shared(mock_region)};
2571 Nice<MockInputDeviceObserver> mock_observer;
2572 Nice<MockInputDevice> device;
2573 Nice<MockInputDevice> another_device;
2574@@ -352,3 +361,21 @@
2575 EXPECT_THAT(pos1, Eq(confined_pos));
2576 EXPECT_THAT(pos2, Eq(confined_pos));
2577 }
2578+
2579+TEST_F(InputDeviceHubTest, forwards_pointer_updates_to_cursor_listener)
2580+{
2581+ using namespace ::testing;
2582+
2583+ auto x = 12.2f, y = 14.3f;
2584+ auto event = mir::events::make_event(0, std::chrono::nanoseconds(0),
2585+ mir_input_event_modifier_none, mir_pointer_action_motion, 0,
2586+ x, y, 0.0f, 0.0f);
2587+
2588+ EXPECT_CALL(mock_cursor_listener, cursor_moved_to(x, y)).Times(1);
2589+
2590+ mi::InputSink* sink;
2591+ capture_input_sink(device, sink);
2592+ hub.add_device(mt::fake_shared(device));
2593+
2594+ sink->handle_input(*event);
2595+}
2596
2597=== modified file 'tests/unit-tests/input/test_event_builders.cpp'
2598--- tests/unit-tests/input/test_event_builders.cpp 2015-05-08 20:54:52 +0000
2599+++ tests/unit-tests/input/test_event_builders.cpp 2015-05-14 18:12:40 +0000
2600@@ -99,8 +99,7 @@
2601 TEST_F(InputEventBuilder, makes_valid_pointer_event)
2602 {
2603 MirPointerAction action = mir_pointer_action_enter;
2604- std::vector<MirPointerButton> depressed_buttons =
2605- {mir_pointer_button_back, mir_pointer_button_tertiary};
2606+ auto depressed_buttons = mir_pointer_button_back | mir_pointer_button_tertiary;
2607 float x_axis_value = 3.9, y_axis_value = 7.4, hscroll_value = .9, vscroll_value = .3;
2608 auto ev = mev::make_event(device_id, timestamp, modifiers,
2609 action, depressed_buttons, x_axis_value, y_axis_value, hscroll_value, vscroll_value);
2610@@ -162,11 +161,10 @@
2611
2612 TEST_F(InputEventBuilder, map_to_hover_if_no_button_pressed)
2613 {
2614- std::vector<MirPointerButton> no_pressed_buttons;
2615 float x_axis_value = 3.9, y_axis_value = 7.4, hscroll_value = .9, vscroll_value = .3;
2616 MirPointerAction action = mir_pointer_action_motion;
2617 auto ev = mev::make_event(device_id, timestamp, modifiers,
2618- action, no_pressed_buttons, x_axis_value, y_axis_value, hscroll_value, vscroll_value);
2619+ action, 0, x_axis_value, y_axis_value, hscroll_value, vscroll_value);
2620 auto e = ev.get();
2621
2622 auto ie = mir_event_get_input_event(e);
2623
2624=== modified file 'tests/unit-tests/input/test_input_event.cpp'
2625--- tests/unit-tests/input/test_input_event.cpp 2015-05-05 18:01:08 +0000
2626+++ tests/unit-tests/input/test_input_event.cpp 2015-05-14 18:12:40 +0000
2627@@ -61,15 +61,21 @@
2628 MirEvent a_key_ev()
2629 {
2630 MirEvent key_ev;
2631+ memset(&key_ev, 0, sizeof(key_ev));
2632+
2633 key_ev.type = mir_event_type_key;
2634+
2635 return key_ev;
2636 }
2637
2638 MirEvent a_motion_ev(int device_class = AINPUT_SOURCE_UNKNOWN)
2639 {
2640 MirEvent motion_ev;
2641+ memset(&motion_ev, 0, sizeof(motion_ev));
2642+
2643 motion_ev.type = mir_event_type_motion;
2644 motion_ev.motion.source_id = device_class;
2645+
2646 return motion_ev;
2647 }
2648
2649@@ -350,13 +356,13 @@
2650 {
2651 auto old_ev = a_motion_ev(AINPUT_SOURCE_MOUSE);
2652
2653- old_ev.motion.button_state = mir_motion_button_primary;
2654+ old_ev.motion.buttons = mir_pointer_button_primary;
2655 auto pev = mir_input_event_get_pointer_event(mir_event_get_input_event(&old_ev));
2656
2657 EXPECT_TRUE(mir_pointer_event_button_state(pev, mir_pointer_button_primary));
2658 EXPECT_FALSE(mir_pointer_event_button_state(pev, mir_pointer_button_secondary));
2659
2660- old_ev.motion.button_state = static_cast<MirMotionButton>(old_ev.motion.button_state | (mir_motion_button_secondary));
2661+ old_ev.motion.buttons |= mir_pointer_button_secondary;
2662
2663 EXPECT_TRUE(mir_pointer_event_button_state(pev, mir_pointer_button_primary));
2664 EXPECT_TRUE(mir_pointer_event_button_state(pev, mir_pointer_button_secondary));
2665
2666=== modified file 'tests/unit-tests/scene/test_abstract_shell.cpp'
2667--- tests/unit-tests/scene/test_abstract_shell.cpp 2015-05-11 18:19:27 +0000
2668+++ tests/unit-tests/scene/test_abstract_shell.cpp 2015-05-14 18:12:40 +0000
2669@@ -283,7 +283,7 @@
2670 {
2671 MirInputEventModifiers const modifiers{mir_input_event_modifier_none};
2672 MirPointerAction const action{mir_pointer_action_button_down};
2673- std::vector<MirPointerButton> const buttons_pressed{mir_pointer_button_primary};
2674+ auto const buttons_pressed = mir_pointer_button_primary;
2675 float const x_axis_value{0.0};
2676 float const y_axis_value{0.0};
2677 float const hscroll_value{0.0};

Subscribers

People subscribed via source and target branches