Mir

Merge lp:~robertcarr/mir/input-injecter-api into lp:mir

Proposed by Robert Carr
Status: Superseded
Proposed branch: lp:~robertcarr/mir/input-injecter-api
Merge into: lp:mir
Diff against target: 1070 lines (+735/-3)
22 files modified
3rd_party/android-input/android/frameworks/base/services/input/InputDispatcher.cpp (+55/-0)
3rd_party/android-input/android/frameworks/base/services/input/InputDispatcher.h (+13/-2)
include/server/mir/default_server_configuration.h (+3/-0)
include/server/mir/input/android/dispatcher_input_configuration.h (+2/-0)
include/server/mir/input/input_configuration.h (+2/-0)
include/server/mir/input/null_input_configuration.h (+1/-0)
include/server/mir/shell/input_injecter.h (+55/-0)
include/server/mir/shell/surface.h (+4/-0)
include/shared/mir/input/android/android_input_lexicon.h (+1/-0)
include/test/mir_test_doubles/mock_input_dispatcher.h (+2/-0)
src/server/default_server_configuration.cpp (+10/-0)
src/server/input/android/CMakeLists.txt (+1/-0)
src/server/input/android/android_input_injecter.cpp (+58/-0)
src/server/input/android/android_input_injecter.h (+69/-0)
src/server/input/android/dispatcher_input_configuration.cpp (+10/-0)
src/server/input/null_input_configuration.cpp (+13/-0)
src/server/shell/surface.cpp (+13/-0)
src/shared/input/android/android_input_lexicon.cpp (+58/-0)
tests/acceptance-tests/test_client_input.cpp (+121/-0)
tests/unit-tests/input/android/CMakeLists.txt (+1/-0)
tests/unit-tests/input/android/test_android_input_injecter.cpp (+93/-0)
tests/unit-tests/input/android/test_android_input_lexicon.cpp (+150/-1)
To merge this branch: bzr merge lp:~robertcarr/mir/input-injecter-api
Reviewer Review Type Date Requested Status
Mir development team Pending
Review via email: mp+188743@code.launchpad.net

This proposal has been superseded by a proposal from 2013-10-02.

Commit message

Implement input injection API to satisfy the shell hud use case.

Description of the change

Implement input injection API to satisfy the shell hud use case.

-- Still work in progress -- needs a little cleanup and an additional unit test for android input lexicon. Just getting some early air. --

To post a comment you must log in.
1104. By Robert Carr

Complete android input lexicon reversal tests

1105. By Robert Carr

Merge development branch

1106. By Robert Carr

Clarify exception case

1107. By Robert Carr

Cleanup

1108. By Robert Carr

Cleanup

1109. By Robert Carr

Fix unitialized event structures in test

1110. By Robert Carr

Correct uninitialized portion of android event to silence valgrind errors

1111. By Robert Carr

Correct ordering

Unmerged revisions

1111. By Robert Carr

Correct ordering

1110. By Robert Carr

Correct uninitialized portion of android event to silence valgrind errors

1109. By Robert Carr

Fix unitialized event structures in test

1108. By Robert Carr

Cleanup

1107. By Robert Carr

Cleanup

1106. By Robert Carr

Clarify exception case

1105. By Robert Carr

Merge development branch

1104. By Robert Carr

Complete android input lexicon reversal tests

1103. By Robert Carr

Implement injected dispatch of motion events. ACceptance test passes

1102. By Robert Carr

Support reverse motion event mapping

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 2013-08-28 03:41:48 +0000
3+++ 3rd_party/android-input/android/frameworks/base/services/input/InputDispatcher.cpp 2013-10-01 22:23:19 +0000
4@@ -4266,6 +4266,61 @@
5 return haveSlipperyForegroundWindow;
6 }
7
8+void InputDispatcher::publishEventToConnectionLocked(sp<Connection> const& connection, InputEvent const* event)
9+{
10+ switch (event->getType())
11+ {
12+ case AINPUT_EVENT_TYPE_KEY:
13+ {
14+ auto seq = DispatchEntry::nextSeq();
15+ auto kev = static_cast<KeyEvent const*>(event);
16+ auto status = connection->inputPublisher.publishKeyEvent(seq,
17+ kev->getDeviceId(), kev->getSource(),
18+ kev->getAction(), kev->getFlags(),
19+ kev->getKeyCode(), kev->getScanCode(),
20+ kev->getMetaState(), kev->getRepeatCount(), kev->getDownTime(),
21+ kev->getEventTime());
22+
23+ input_report->published_key_event(connection->inputChannel->getFd(),
24+ seq,
25+ kev->getEventTime());
26+ break;
27+ }
28+ case AINPUT_EVENT_TYPE_MOTION:
29+ {
30+ auto seq = DispatchEntry::nextSeq();
31+ auto mev = static_cast<MotionEvent const*>(event);
32+ auto status = connection->inputPublisher.publishMotionEvent(seq,
33+ mev->getDeviceId(), mev->getSource(),
34+ mev->getAction(), mev->getFlags(),
35+ mev->getEdgeFlags(), mev->getMetaState(), mev->getButtonState(),
36+ mev->getXOffset(), mev->getYOffset(),
37+ mev->getXPrecision(), mev->getYPrecision(),
38+ mev->getDownTime(), mev->getEventTime(),
39+ mev->getPointerCount(), mev->getPointerProperties(), mev->getSamplePointerCoords());
40+ input_report->published_motion_event(connection->inputChannel->getFd(),
41+ seq,
42+ mev->getEventTime());
43+ }
44+ default:
45+ break;
46+ }
47+}
48+
49+
50+void InputDispatcher::injectEventToWindow(sp<InputWindowHandle> const& windowHandle, InputEvent const* event)
51+{
52+ AutoMutex _l(mLock);
53+
54+ auto index = getConnectionIndexLocked(windowHandle->getInfo()->inputChannel);
55+
56+ if (index >= 0)
57+ {
58+ auto connection = mConnectionsByFd.valueAt(index);
59+ publishEventToConnectionLocked(connection, event);
60+ }
61+}
62+
63
64 // --- InputDispatcherThread ---
65
66
67=== modified file '3rd_party/android-input/android/frameworks/base/services/input/InputDispatcher.h'
68--- 3rd_party/android-input/android/frameworks/base/services/input/InputDispatcher.h 2013-05-30 19:24:29 +0000
69+++ 3rd_party/android-input/android/frameworks/base/services/input/InputDispatcher.h 2013-10-01 22:23:19 +0000
70@@ -323,6 +323,13 @@
71 * This method may be called on any thread.
72 */
73 virtual void setKeyboardFocus(const sp<InputWindowHandle>& windowHandle) = 0;
74+
75+ /*
76+ * Asynchronously publish an event directly to a given input channel.
77+ *
78+ * This method may be called on any thread.
79+ */
80+ virtual void injectEventToWindow(sp<InputWindowHandle> const& windowHandle, InputEvent const* event) = 0;
81 /*
82 * Notify that a window handle is about to vanish
83
84@@ -423,6 +430,8 @@
85 virtual status_t registerInputChannel(const sp<InputChannel>& inputChannel,
86 const sp<InputWindowHandle>& inputWindowHandle, bool monitor);
87 virtual status_t unregisterInputChannel(const sp<InputChannel>& inputChannel);
88+
89+ void injectEventToWindow(sp<InputWindowHandle> const& windowHandle, InputEvent const* event);
90
91 private:
92 std::shared_ptr<mir::input::InputReport> const input_report;
93@@ -586,10 +595,10 @@
94 return targetFlags & InputTarget::FLAG_SPLIT;
95 }
96
97+ static uint32_t nextSeq();
98+
99 private:
100 static android_atomic_int32_t sNextSeqAtomic;
101-
102- static uint32_t nextSeq();
103 };
104
105 // A command entry captures state and behavior for an action to be performed in the
106@@ -1117,6 +1126,8 @@
107 int32_t injectionResult, nsecs_t timeSpentWaitingForApplication);
108
109 void setKeyboardFocusLocked(const sp<InputWindowHandle>& windowHandle);
110+
111+ void publishEventToConnectionLocked(sp<Connection> const& connection, InputEvent const* event);
112 };
113
114 /* Enqueues and dispatches input events, endlessly. */
115
116=== modified file 'include/server/mir/default_server_configuration.h'
117--- include/server/mir/default_server_configuration.h 2013-09-27 16:53:41 +0000
118+++ include/server/mir/default_server_configuration.h 2013-10-01 22:23:19 +0000
119@@ -58,6 +58,7 @@
120 class SurfaceBuilder;
121 class SurfaceController;
122 class InputTargeter;
123+class InputInjecter;
124 class SessionContainer;
125 class FocusSetter;
126 class FocusSequence;
127@@ -225,6 +226,7 @@
128 virtual std::shared_ptr<input::CompositeEventFilter> the_composite_event_filter();
129 virtual std::shared_ptr<surfaces::InputRegistrar> the_input_registrar();
130 virtual std::shared_ptr<shell::InputTargeter> the_input_targeter();
131+ virtual std::shared_ptr<shell::InputInjecter> the_input_injecter();
132 virtual std::shared_ptr<input::CursorListener> the_cursor_listener();
133 virtual std::shared_ptr<input::InputRegion> the_input_region();
134 /** @} */
135@@ -263,6 +265,7 @@
136 CachedPtr<input::InputRegion> input_region;
137 CachedPtr<surfaces::InputRegistrar> input_registrar;
138 CachedPtr<shell::InputTargeter> input_targeter;
139+ CachedPtr<shell::InputInjecter> input_injecter;
140 CachedPtr<input::CursorListener> cursor_listener;
141 CachedPtr<graphics::Platform> graphics_platform;
142 CachedPtr<graphics::BufferInitializer> buffer_initializer;
143
144=== modified file 'include/server/mir/input/android/dispatcher_input_configuration.h'
145--- include/server/mir/input/android/dispatcher_input_configuration.h 2013-09-10 16:42:04 +0000
146+++ include/server/mir/input/android/dispatcher_input_configuration.h 2013-10-01 22:23:19 +0000
147@@ -93,6 +93,7 @@
148 std::shared_ptr<surfaces::InputRegistrar> the_input_registrar();
149 std::shared_ptr<shell::InputTargeter> the_input_targeter();
150 std::shared_ptr<input::InputManager> the_input_manager();
151+ std::shared_ptr<shell::InputInjecter> the_input_injecter();
152
153 void set_input_targets(std::shared_ptr<input::InputTargets> const& targets);
154
155@@ -119,6 +120,7 @@
156 CachedPtr<InputRegistrar> input_registrar;
157
158 CachedPtr<shell::InputTargeter> input_targeter;
159+ CachedPtr<shell::InputInjecter> input_injecter;
160
161 CachedAndroidPtr<droidinput::InputDispatcherPolicyInterface> dispatcher_policy;
162 };
163
164=== modified file 'include/server/mir/input/input_configuration.h'
165--- include/server/mir/input/input_configuration.h 2013-05-24 19:27:37 +0000
166+++ include/server/mir/input/input_configuration.h 2013-10-01 22:23:19 +0000
167@@ -30,6 +30,7 @@
168 namespace shell
169 {
170 class InputTargeter;
171+class InputInjecter;
172 }
173 namespace input
174 {
175@@ -44,6 +45,7 @@
176 virtual std::shared_ptr<surfaces::InputRegistrar> the_input_registrar() = 0;
177 virtual std::shared_ptr<shell::InputTargeter> the_input_targeter() = 0;
178 virtual std::shared_ptr<input::InputManager> the_input_manager() = 0;
179+ virtual std::shared_ptr<shell::InputInjecter> the_input_injecter() = 0;
180
181 virtual void set_input_targets(std::shared_ptr<input::InputTargets> const& targets) = 0;
182
183
184=== modified file 'include/server/mir/input/null_input_configuration.h'
185--- include/server/mir/input/null_input_configuration.h 2013-05-28 13:31:27 +0000
186+++ include/server/mir/input/null_input_configuration.h 2013-10-01 22:23:19 +0000
187@@ -34,6 +34,7 @@
188
189 std::shared_ptr<surfaces::InputRegistrar> the_input_registrar();
190 std::shared_ptr<shell::InputTargeter> the_input_targeter();
191+ std::shared_ptr<shell::InputInjecter> the_input_injecter();
192 std::shared_ptr<InputManager> the_input_manager();
193
194 void set_input_targets(std::shared_ptr<InputTargets> const& /* targets */);
195
196=== added file 'include/server/mir/shell/input_injecter.h'
197--- include/server/mir/shell/input_injecter.h 1970-01-01 00:00:00 +0000
198+++ include/server/mir/shell/input_injecter.h 2013-10-01 22:23:19 +0000
199@@ -0,0 +1,55 @@
200+/*
201+ * Copyright © 2013 Canonical Ltd.
202+ *
203+ * This program is free software: you can redistribute it and/or modify it
204+ * under the terms of the GNU General Public License version 3,
205+ * as published by the Free Software Foundation.
206+ *
207+ * This program is distributed in the hope that it will be useful,
208+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
209+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
210+ * GNU General Public License for more details.
211+ *
212+ * You should have received a copy of the GNU General Public License
213+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
214+ *
215+ * Authored by: Robert Carr <robert.carr@canonical.com>
216+ */
217+
218+#ifndef MIR_SHELL_INPUT_INJECTER_H_
219+#define MIR_SHELL_INPUT_INJECTER_H_
220+
221+#include "mir_toolkit/event.h"
222+
223+#include <memory>
224+
225+namespace mir
226+{
227+
228+namespace input
229+{
230+class InputChannel;
231+}
232+
233+namespace shell
234+{
235+
236+/// An interface used to control the selection of keyboard input focus.
237+class InputInjecter
238+{
239+public:
240+ virtual ~InputInjecter() = default;
241+
242+ virtual void inject_input(std::shared_ptr<input::InputChannel const> const& target,
243+ MirEvent const& ev) = 0;
244+
245+protected:
246+ InputInjecter() = default;
247+ InputInjecter(InputInjecter const&) = delete;
248+ InputInjecter& operator=(InputInjecter const&) = delete;
249+};
250+
251+}
252+} // namespace mir
253+
254+#endif // MIR_SHELL_INPUT_INJECTER_H_
255
256=== modified file 'include/server/mir/shell/surface.h'
257--- include/server/mir/shell/surface.h 2013-08-28 03:41:48 +0000
258+++ include/server/mir/shell/surface.h 2013-10-01 22:23:19 +0000
259@@ -26,6 +26,7 @@
260 #include "mir/surfaces/surface.h"
261
262 #include "mir_toolkit/common.h"
263+#include "mir_toolkit/event.h"
264
265 #include <string>
266
267@@ -42,6 +43,7 @@
268 class SurfaceBuilder;
269 class SurfaceConfigurator;
270 class SurfaceController;
271+class InputInjecter;
272 struct SurfaceCreationParameters;
273
274 class Surface : public frontend::ClientTrackingSurface, public shell::SurfaceBufferAccess
275@@ -90,6 +92,8 @@
276 virtual void allow_framedropping(bool);
277
278 virtual void raise(std::shared_ptr<SurfaceController> const& controller);
279+
280+ virtual void inject_input(std::shared_ptr<InputInjecter> const& injecter, MirEvent const& ev);
281 private:
282 bool set_type(MirSurfaceType t); // Use configure() to make public changes
283 bool set_state(MirSurfaceState s);
284
285=== modified file 'include/shared/mir/input/android/android_input_lexicon.h'
286--- include/shared/mir/input/android/android_input_lexicon.h 2013-04-24 05:22:20 +0000
287+++ include/shared/mir/input/android/android_input_lexicon.h 2013-10-01 22:23:19 +0000
288@@ -42,6 +42,7 @@
289 {
290 public:
291 static void translate(const droidinput::InputEvent *android_event, MirEvent &mir_event);
292+ static void translate(MirEvent const& mir_event, droidinput::InputEvent **android_event);
293 };
294
295 }
296
297=== modified file 'include/test/mir_test_doubles/mock_input_dispatcher.h'
298--- include/test/mir_test_doubles/mock_input_dispatcher.h 2013-05-24 18:23:48 +0000
299+++ include/test/mir_test_doubles/mock_input_dispatcher.h 2013-10-01 22:23:19 +0000
300@@ -48,6 +48,8 @@
301
302 MOCK_METHOD1(setKeyboardFocus, void(droidinput::sp<droidinput::InputWindowHandle> const&));
303 MOCK_METHOD1(notifyWindowRemoved, void(droidinput::sp<droidinput::InputWindowHandle> const&));
304+
305+ MOCK_METHOD2(injectEventToWindow, void(droidinput::sp<droidinput::InputWindowHandle> const&, droidinput::InputEvent const*));
306
307 // droidinput::InputListener interface
308 MOCK_METHOD1(notifyConfigurationChanged, void(droidinput::NotifyConfigurationChangedArgs const*));
309
310=== modified file 'src/server/default_server_configuration.cpp'
311--- src/server/default_server_configuration.cpp 2013-09-30 08:41:27 +0000
312+++ src/server/default_server_configuration.cpp 2013-10-01 22:23:19 +0000
313@@ -45,6 +45,7 @@
314 #include "mir/shell/graphics_display_layout.h"
315 #include "mir/shell/surface_configurator.h"
316 #include "mir/shell/broadcasting_session_event_sink.h"
317+#include "mir/shell/input_injecter.h"
318 #include "mir/graphics/cursor.h"
319 #include "mir/graphics/nested/host_connection.h"
320 #include "mir/shell/null_session_listener.h"
321@@ -929,6 +930,15 @@
322 });
323 }
324
325+std::shared_ptr<msh::InputInjecter> mir::DefaultServerConfiguration::the_input_injecter()
326+{
327+ return input_injecter(
328+ [&]() -> std::shared_ptr<msh::InputInjecter>
329+ {
330+ return the_input_configuration()->the_input_injecter();
331+ });
332+}
333+
334 std::shared_ptr<ms::InputRegistrar> mir::DefaultServerConfiguration::the_input_registrar()
335 {
336 return input_registrar(
337
338=== modified file 'src/server/input/android/CMakeLists.txt'
339--- src/server/input/android/CMakeLists.txt 2013-09-06 10:06:15 +0000
340+++ src/server/input/android/CMakeLists.txt 2013-10-01 22:23:19 +0000
341@@ -11,6 +11,7 @@
342 ${CMAKE_CURRENT_SOURCE_DIR}/android_input_window_handle.cpp
343 ${CMAKE_CURRENT_SOURCE_DIR}/android_input_registrar.cpp
344 ${CMAKE_CURRENT_SOURCE_DIR}/android_input_targeter.cpp
345+ ${CMAKE_CURRENT_SOURCE_DIR}/android_input_injecter.cpp
346 ${CMAKE_CURRENT_SOURCE_DIR}/android_input_target_enumerator.cpp
347 ${CMAKE_CURRENT_SOURCE_DIR}/dispatcher_input_configuration.cpp
348 ${CMAKE_CURRENT_SOURCE_DIR}/input_dispatcher_manager.cpp
349
350=== added file 'src/server/input/android/android_input_injecter.cpp'
351--- src/server/input/android/android_input_injecter.cpp 1970-01-01 00:00:00 +0000
352+++ src/server/input/android/android_input_injecter.cpp 2013-10-01 22:23:19 +0000
353@@ -0,0 +1,58 @@
354+/*
355+ * Copyright © 2013 Canonical Ltd.
356+ *
357+ * This program is free software: you can redistribute it and/or modify it
358+ * under the terms of the GNU General Public License version 3,
359+ * as published by the Free Software Foundation.
360+ *
361+ * This program is distributed in the hope that it will be useful,
362+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
363+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
364+ * GNU General Public License for more details.
365+ *
366+ * You should have received a copy of the GNU General Public License
367+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
368+ *
369+ * Authored by: Robert Carr <robert.carr@canonical.com>
370+ */
371+
372+#include "android_input_injecter.h"
373+#include "android_input_registrar.h"
374+
375+#include "android_input_window_handle.h"
376+#include "android_input_application_handle.h"
377+
378+#include "mir/input/android/android_input_lexicon.h"
379+
380+#include <InputDispatcher.h>
381+
382+#include <boost/throw_exception.hpp>
383+
384+#include <stdexcept>
385+#include <mutex>
386+
387+namespace mi = mir::input;
388+namespace mia = mi::android;
389+namespace ms = mir::surfaces;
390+
391+mia::InputInjecter::InputInjecter(droidinput::sp<droidinput::InputDispatcherInterface> const& input_dispatcher,
392+ std::shared_ptr<mia::WindowHandleRepository> const& repository) :
393+ input_dispatcher(input_dispatcher),
394+ repository(repository)
395+{
396+}
397+
398+mia::InputInjecter::~InputInjecter() noexcept(true) {}
399+
400+void mia::InputInjecter::inject_input(std::shared_ptr<mi::InputChannel const> const& focus_channel,
401+ MirEvent const& ev)
402+{
403+ auto window_handle = repository->handle_for_channel(focus_channel);
404+
405+ if (window_handle == NULL)
406+ BOOST_THROW_EXCEPTION(std::logic_error("Attempt to inject input to an unregistered input channel"));
407+
408+ droidinput::InputEvent *android_event;
409+ mia::Lexicon::translate(ev, &android_event);
410+ input_dispatcher->injectEventToWindow(window_handle, android_event);
411+}
412
413=== added file 'src/server/input/android/android_input_injecter.h'
414--- src/server/input/android/android_input_injecter.h 1970-01-01 00:00:00 +0000
415+++ src/server/input/android/android_input_injecter.h 2013-10-01 22:23:19 +0000
416@@ -0,0 +1,69 @@
417+/*
418+ * Copyright © 2013 Canonical Ltd.
419+ *
420+ * This program is free software: you can redistribute it and/or modify it
421+ * under the terms of the GNU General Public License version 3,
422+ * as published by the Free Software Foundation.
423+ *
424+ * This program is distributed in the hope that it will be useful,
425+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
426+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
427+ * GNU General Public License for more details.
428+ *
429+ * You should have received a copy of the GNU General Public License
430+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
431+ *
432+ * Authored by: Robert Carr <robert.carr@canonical.com>
433+ */
434+
435+#ifndef MIR_INPUT_ANDROID_INJECTER_H_
436+#define MIR_INPUT_ANDROID_INJECTER_H_
437+
438+#include "mir/shell/input_injecter.h"
439+
440+#include <utils/StrongPointer.h>
441+
442+#include <mutex>
443+
444+namespace android
445+{
446+class InputDispatcherInterface;
447+class InputWindowHandle;
448+}
449+
450+namespace droidinput = android;
451+
452+namespace mir
453+{
454+namespace input
455+{
456+namespace android
457+{
458+class InputConfiguration;
459+class WindowHandleRepository;
460+
461+class InputInjecter : public shell::InputInjecter
462+{
463+public:
464+ explicit InputInjecter(droidinput::sp<droidinput::InputDispatcherInterface> const& input_dispatcher,
465+ std::shared_ptr<WindowHandleRepository> const& repository);
466+ virtual ~InputInjecter() noexcept(true);
467+
468+ void inject_input(std::shared_ptr<input::InputChannel const> const& target,
469+ MirEvent const& ev);
470+
471+protected:
472+ InputInjecter(const InputInjecter&) = delete;
473+ InputInjecter& operator=(const InputInjecter&) = delete;
474+
475+private:
476+ droidinput::sp<droidinput::InputDispatcherInterface> input_dispatcher;
477+
478+ std::shared_ptr<WindowHandleRepository> const repository;
479+};
480+
481+}
482+}
483+} // namespace mir
484+
485+#endif // MIR_INPUT_ANDROID_INJECTER_H_
486
487=== modified file 'src/server/input/android/dispatcher_input_configuration.cpp'
488--- src/server/input/android/dispatcher_input_configuration.cpp 2013-09-10 16:42:04 +0000
489+++ src/server/input/android/dispatcher_input_configuration.cpp 2013-10-01 22:23:19 +0000
490@@ -22,6 +22,7 @@
491 #include "android_input_thread.h"
492 #include "android_input_registrar.h"
493 #include "android_input_targeter.h"
494+#include "android_input_injecter.h"
495 #include "android_input_target_enumerator.h"
496 #include "android_input_manager.h"
497 #include "mir/input/event_filter.h"
498@@ -135,6 +136,15 @@
499 });
500 }
501
502+std::shared_ptr<msh::InputInjecter> mia::DispatcherInputConfiguration::the_input_injecter()
503+{
504+ return input_injecter(
505+ [this]()
506+ {
507+ return std::make_shared<mia::InputInjecter>(the_dispatcher(), the_window_handle_repository());
508+ });
509+}
510+
511 bool mia::DispatcherInputConfiguration::is_key_repeat_enabled()
512 {
513 return true;
514
515=== modified file 'src/server/input/null_input_configuration.cpp'
516--- src/server/input/null_input_configuration.cpp 2013-08-28 03:41:48 +0000
517+++ src/server/input/null_input_configuration.cpp 2013-10-01 22:23:19 +0000
518@@ -20,6 +20,7 @@
519
520 #include "mir/surfaces/input_registrar.h"
521 #include "mir/shell/input_targeter.h"
522+#include "mir/shell/input_injecter.h"
523 #include "mir/input/input_manager.h"
524
525 namespace mi = mir::input;
526@@ -73,6 +74,13 @@
527 }
528 };
529
530+struct NullInputInjecter : public msh::InputInjecter
531+{
532+ void inject_input(std::shared_ptr<mi::InputChannel const> const&, MirEvent const&)
533+ {
534+ }
535+};
536+
537 }
538
539 std::shared_ptr<ms::InputRegistrar> mi::NullInputConfiguration::the_input_registrar()
540@@ -85,6 +93,11 @@
541 return std::make_shared<NullInputTargeter>();
542 }
543
544+std::shared_ptr<msh::InputInjecter> mi::NullInputConfiguration::the_input_injecter()
545+{
546+ return std::make_shared<NullInputInjecter>();
547+}
548+
549 std::shared_ptr<mi::InputManager> mi::NullInputConfiguration::the_input_manager()
550 {
551 return std::make_shared<NullInputManager>();
552
553=== modified file 'src/server/shell/surface.cpp'
554--- src/server/shell/surface.cpp 2013-08-28 03:41:48 +0000
555+++ src/server/shell/surface.cpp 2013-10-01 22:23:19 +0000
556@@ -21,6 +21,7 @@
557 #include "mir/shell/surface_configurator.h"
558 #include "mir/shell/surface_controller.h"
559 #include "mir/shell/input_targeter.h"
560+#include "mir/shell/input_injecter.h"
561 #include "mir/input/input_channel.h"
562 #include "mir/frontend/event_sink.h"
563
564@@ -349,3 +350,15 @@
565 BOOST_THROW_EXCEPTION(std::runtime_error("Invalid surface"));
566 }
567 }
568+
569+void msh::Surface::inject_input(std::shared_ptr<msh::InputInjecter> const& injecter, MirEvent const& ev)
570+{
571+ if (auto const& s = surface.lock())
572+ {
573+ injecter->inject_input(s->input_channel(), ev);
574+ }
575+ else
576+ {
577+ BOOST_THROW_EXCEPTION(std::runtime_error("Invalid surface"));
578+ }
579+}
580
581=== modified file 'src/shared/input/android/android_input_lexicon.cpp'
582--- src/shared/input/android/android_input_lexicon.cpp 2013-08-28 03:41:48 +0000
583+++ src/shared/input/android/android_input_lexicon.cpp 2013-10-01 22:23:19 +0000
584@@ -81,3 +81,61 @@
585
586 }
587
588+void mia::Lexicon::translate(MirEvent const& mir_event, droidinput::InputEvent **android_event)
589+{
590+ switch (mir_event.type)
591+ {
592+ case mir_event_type_key:
593+ {
594+ auto key_event = new droidinput::KeyEvent();
595+ key_event->initialize(mir_event.key.device_id, mir_event.key.source_id,
596+ mir_event.key.action, mir_event.key.flags, mir_event.key.key_code,
597+ mir_event.key.scan_code, mir_event.key.modifiers,
598+ mir_event.key.repeat_count, mir_event.key.down_time, mir_event.key.event_time);
599+ *android_event = key_event;
600+ break;
601+ }
602+ case mir_event_type_motion:
603+ {
604+ auto motion_event = new droidinput::MotionEvent();
605+ auto const& mmev = mir_event.motion;
606+ auto pc = mmev.pointer_count;
607+
608+ droidinput::PointerProperties *pointer_properties = new droidinput::PointerProperties[pc];
609+ droidinput::PointerCoords *pointer_coords = new droidinput::PointerCoords[pc];
610+
611+ for (unsigned int i = 0; i < pc; i++)
612+ {
613+ auto const& mir_coords = mmev.pointer_coordinates[i];
614+ pointer_properties[i].id = mir_coords.id;
615+
616+ auto &coords = pointer_coords[i];
617+ coords.setAxisValue(AMOTION_EVENT_AXIS_X, mir_coords.x);
618+ coords.setAxisValue(AMOTION_EVENT_AXIS_Y, mir_coords.y);
619+ coords.setAxisValue(AMOTION_EVENT_AXIS_TOUCH_MAJOR,
620+ mir_coords.touch_major);
621+ coords.setAxisValue(AMOTION_EVENT_AXIS_TOUCH_MINOR,
622+ mir_coords.touch_minor);
623+ coords.setAxisValue(AMOTION_EVENT_AXIS_SIZE, mir_coords.size);
624+ coords.setAxisValue(AMOTION_EVENT_AXIS_PRESSURE,
625+ mir_coords.pressure);
626+ coords.setAxisValue(AMOTION_EVENT_AXIS_ORIENTATION,
627+ mir_coords.orientation);
628+ }
629+
630+ motion_event->initialize(mmev.device_id, mmev.source_id, mmev.action, mmev.flags,
631+ mmev.edge_flags, mmev.modifiers, mmev.button_state,
632+ mmev.x_offset, mmev.y_offset, mmev.x_precision, mmev.y_precision,
633+ mmev.down_time, mmev.event_time, mmev.pointer_count,
634+ pointer_properties, pointer_coords);
635+ *android_event = motion_event;
636+
637+ delete pointer_properties;
638+ delete pointer_coords;
639+ }
640+ case mir_event_type_surface:
641+ default:
642+ // TODO: LOL
643+ break;
644+ }
645+}
646
647=== modified file 'tests/acceptance-tests/test_client_input.cpp'
648--- tests/acceptance-tests/test_client_input.cpp 2013-09-25 18:39:57 +0000
649+++ tests/acceptance-tests/test_client_input.cpp 2013-10-01 22:23:19 +0000
650@@ -23,6 +23,8 @@
651 #include "mir/shell/surface.h"
652 #include "mir/shell/session_container.h"
653 #include "mir/shell/session.h"
654+#include "mir/shell/focus_sequence.h"
655+#include "mir/shell/input_injecter.h"
656 #include "mir/surfaces/surface_controller.h"
657 #include "mir/surfaces/surface_stack_model.h"
658
659@@ -943,3 +945,122 @@
660 launch_client_process(client_1);
661 launch_client_process(client_2);
662 }
663+
664+
665+TEST_F(TestClientInput, clients_receive_shell_injected_key_input_per_norm)
666+{
667+ using namespace ::testing;
668+
669+ static std::string const test_client_name = "1";
670+
671+ mtf::CrossProcessSync fence;
672+
673+ struct ServerConfiguration : mtf::InputTestingServerConfiguration
674+ {
675+ mtf::CrossProcessSync input_cb_setup_fence;
676+
677+ ServerConfiguration(const mtf::CrossProcessSync& input_cb_setup_fence)
678+ : input_cb_setup_fence(input_cb_setup_fence)
679+ {
680+ }
681+
682+ std::shared_ptr<msh::Surface> the_most_default_surface()
683+ {
684+ return the_shell_focus_sequence()->default_focus()->default_surface();
685+ }
686+
687+ void inject_event_to_default_surface(MirEvent &ev)
688+ {
689+ the_most_default_surface()->inject_input(the_input_injecter(), ev);
690+ }
691+
692+ void inject_input()
693+ {
694+ wait_until_client_appears(test_client_name);
695+ input_cb_setup_fence.wait_for_signal_ready_for();
696+
697+ MirEvent ev;
698+ ev.type = mir_event_type_key;
699+ ev.key.action = mir_key_action_down;
700+
701+ inject_event_to_default_surface(ev);
702+ }
703+ } server_config(fence);
704+ launch_server_process(server_config);
705+
706+ struct KeyReceivingClient : InputClient
707+ {
708+ KeyReceivingClient(const mtf::CrossProcessSync& fence) : InputClient(fence, test_client_name) {}
709+ void expect_input(mt::WaitCondition& events_received) override
710+ {
711+ using namespace ::testing;
712+ InSequence seq;
713+
714+ EXPECT_CALL(*handler, handle_input(KeyDownEvent())).Times(1)
715+ .WillOnce(mt::WakeUp(&events_received));
716+ }
717+ } client_config(fence);
718+ launch_client_process(client_config);
719+}
720+
721+TEST_F(TestClientInput, clients_receive_shell_injected_pointer_input_per_norm)
722+{
723+ using namespace ::testing;
724+
725+ static std::string const test_client_name = "1";
726+
727+ mtf::CrossProcessSync fence;
728+
729+ struct ServerConfiguration : mtf::InputTestingServerConfiguration
730+ {
731+ mtf::CrossProcessSync input_cb_setup_fence;
732+
733+ ServerConfiguration(const mtf::CrossProcessSync& input_cb_setup_fence)
734+ : input_cb_setup_fence(input_cb_setup_fence)
735+ {
736+ }
737+
738+ std::shared_ptr<msh::Surface> the_most_default_surface()
739+ {
740+ return the_shell_focus_sequence()->default_focus()->default_surface();
741+ }
742+
743+ void inject_event_to_default_surface(MirEvent &ev)
744+ {
745+ the_most_default_surface()->inject_input(the_input_injecter(), ev);
746+ }
747+
748+ void inject_input()
749+ {
750+ wait_until_client_appears(test_client_name);
751+ input_cb_setup_fence.wait_for_signal_ready_for();
752+
753+ MirEvent ev;
754+ ev.type = mir_event_type_motion;
755+ auto &mev = ev.motion;
756+ mev.action = mir_motion_action_down;
757+ mev.button_state = mir_motion_button_primary;
758+
759+ mev.pointer_count = 1;
760+ mev.pointer_coordinates[0].x = 1;
761+ mev.pointer_coordinates[0].y = 1;
762+
763+ inject_event_to_default_surface(ev);
764+ }
765+ } server_config(fence);
766+ launch_server_process(server_config);
767+
768+ struct MotionReceivingClient : InputClient
769+ {
770+ MotionReceivingClient(const mtf::CrossProcessSync& fence) : InputClient(fence, test_client_name) {}
771+ void expect_input(mt::WaitCondition& events_received) override
772+ {
773+ using namespace ::testing;
774+ InSequence seq;
775+
776+ EXPECT_CALL(*handler, handle_input(ButtonDownEvent(1, 1))).Times(1)
777+ .WillOnce(mt::WakeUp(&events_received));
778+ }
779+ } client_config(fence);
780+ launch_client_process(client_config);
781+}
782
783=== modified file 'tests/unit-tests/input/android/CMakeLists.txt'
784--- tests/unit-tests/input/android/CMakeLists.txt 2013-08-28 03:41:48 +0000
785+++ tests/unit-tests/input/android/CMakeLists.txt 2013-10-01 22:23:19 +0000
786@@ -8,6 +8,7 @@
787 ${CMAKE_CURRENT_SOURCE_DIR}/test_android_input_application_handle.cpp
788 ${CMAKE_CURRENT_SOURCE_DIR}/test_android_input_window_handle.cpp
789 ${CMAKE_CURRENT_SOURCE_DIR}/test_android_input_targeter.cpp
790+ ${CMAKE_CURRENT_SOURCE_DIR}/test_android_input_injecter.cpp
791 ${CMAKE_CURRENT_SOURCE_DIR}/test_android_input_target_enumerator.cpp
792 ${CMAKE_CURRENT_SOURCE_DIR}/test_android_input_registrar.cpp
793 )
794
795=== added file 'tests/unit-tests/input/android/test_android_input_injecter.cpp'
796--- tests/unit-tests/input/android/test_android_input_injecter.cpp 1970-01-01 00:00:00 +0000
797+++ tests/unit-tests/input/android/test_android_input_injecter.cpp 2013-10-01 22:23:19 +0000
798@@ -0,0 +1,93 @@
799+/*
800+ * Copyright © 2013 Canonical Ltd.
801+ *
802+ * This program is free software: you can redistribute it and/or modify it
803+ * under the terms of the GNU General Public License version 3,
804+ * as published by the Free Software Foundation.
805+ *
806+ * This program is distributed in the hope that it will be useful,
807+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
808+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
809+ * GNU General Public License for more details.
810+ *
811+ * You should have received a copy of the GNU General Public License
812+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
813+ *
814+ * Authored by: Robert Carr <robert.carr@canonical.com>
815+ */
816+
817+#include "src/server/input/android/android_input_injecter.h"
818+#include "src/server/input/android/android_input_registrar.h"
819+#include "src/server/input/android/android_window_handle_repository.h"
820+
821+#include "mir/input/input_channel.h"
822+
823+#include "mir_test_doubles/mock_input_dispatcher.h"
824+#include "mir_test_doubles/stub_input_channel.h"
825+
826+#include "mir_test/fake_shared.h"
827+#include "mir_test_doubles/stub_input_channel.h"
828+#include "mir_test_doubles/stub_input_handles.h"
829+#include "mir_test_doubles/mock_window_handle_repository.h"
830+
831+#include <InputWindow.h>
832+#include <InputApplication.h>
833+
834+#include <gtest/gtest.h>
835+#include <gmock/gmock.h>
836+
837+#include <sys/types.h>
838+#include <sys/socket.h>
839+
840+#include <stdexcept>
841+
842+namespace mi = mir::input;
843+namespace mia = mi::android;
844+namespace mt = mir::test;
845+namespace mtd = mt::doubles;
846+
847+TEST(AndroidInputInjecterSetup, inject_input_throw_behavior)
848+{
849+ using namespace ::testing;
850+
851+ droidinput::sp<mtd::MockInputDispatcher> dispatcher = new mtd::MockInputDispatcher;
852+ mtd::MockWindowHandleRepository repository;
853+ mia::InputInjecter injecter(dispatcher, mt::fake_shared(repository));
854+
855+ std::shared_ptr<mi::InputChannel const> stub_channel = std::make_shared<mtd::StubInputChannel>();
856+
857+ EXPECT_CALL(repository, handle_for_channel(stub_channel))
858+ .Times(1)
859+ .WillOnce(Return(droidinput::sp<droidinput::InputWindowHandle>()));
860+
861+ EXPECT_THROW({
862+ // We can't inject input to unregistered channels
863+ MirEvent ev;
864+ memset(&ev, 0, sizeof(MirEvent));
865+
866+ injecter.inject_input(stub_channel, ev);
867+ }, std::logic_error);
868+}
869+
870+TEST(AndroidInputInjecterSetup, inject_input_behavior)
871+{
872+ using namespace ::testing;
873+
874+ std::shared_ptr<mi::InputChannel const> stub_channel = std::make_shared<mtd::StubInputChannel>();
875+ mtd::MockWindowHandleRepository repository;
876+
877+ droidinput::sp<mtd::MockInputDispatcher> dispatcher = new mtd::MockInputDispatcher;
878+ droidinput::sp<droidinput::InputWindowHandle> stub_window_handle = new mtd::StubWindowHandle;
879+
880+ EXPECT_CALL(*dispatcher, injectEventToWindow(stub_window_handle, _))
881+ .Times(1);
882+ EXPECT_CALL(repository, handle_for_channel(stub_channel))
883+ .Times(1)
884+ .WillOnce(Return(stub_window_handle));
885+ mia::InputInjecter injecter(dispatcher, mt::fake_shared(repository));
886+
887+ MirEvent ev;
888+
889+ memset(&ev, 0, sizeof(MirEvent));
890+ injecter.inject_input(stub_channel, ev);
891+}
892
893=== modified file 'tests/unit-tests/input/android/test_android_input_lexicon.cpp'
894--- tests/unit-tests/input/android/test_android_input_lexicon.cpp 2013-08-28 03:41:48 +0000
895+++ tests/unit-tests/input/android/test_android_input_lexicon.cpp 2013-10-01 22:23:19 +0000
896@@ -27,6 +27,7 @@
897 namespace mi = mir::input;
898 namespace mia = mir::input::android;
899
900+// Translate key events from android::KeyEvent to MirEvent
901 TEST(AndroidInputLexicon, translates_key_events)
902 {
903 using namespace ::testing;
904@@ -71,6 +72,54 @@
905 delete android_key_ev;
906 }
907
908+// Translate MirEvent to android::KeyEvent
909+TEST(AndroidInputLexicon, translates_key_events_in_reverse)
910+{
911+ using namespace ::testing;
912+
913+ const int32_t device_id = 1;
914+ const int32_t source_id = 2;
915+ const int32_t action = 3;
916+ const int32_t flags = 4;
917+ const int32_t key_code = 5;
918+ const int32_t scan_code = 6;
919+ const int32_t meta_state = 7;
920+ const int32_t repeat_count = 8;
921+ const nsecs_t down_time = 9;
922+ const nsecs_t event_time = 10;
923+
924+ MirEvent mir_ev;
925+ mir_ev.type = mir_event_type_key;
926+ mir_ev.key.device_id = device_id;
927+ mir_ev.key.source_id = source_id;
928+ mir_ev.key.action = static_cast<MirKeyAction>(action);
929+ mir_ev.key.flags = static_cast<MirKeyFlag>(flags);
930+ mir_ev.key.key_code = key_code;
931+ mir_ev.key.scan_code = scan_code;
932+ mir_ev.key.modifiers = meta_state;
933+ mir_ev.key.repeat_count = repeat_count;
934+ mir_ev.key.down_time = down_time;
935+ mir_ev.key.event_time = event_time;
936+
937+ droidinput::InputEvent *aev;
938+ mia::Lexicon::translate(mir_ev, &aev);
939+ auto android_key_ev = dynamic_cast<droidinput::KeyEvent*>(aev);
940+
941+ EXPECT_EQ(device_id, android_key_ev->getDeviceId());
942+ EXPECT_EQ(source_id, android_key_ev->getSource());
943+ EXPECT_EQ(action, android_key_ev->getAction());
944+ EXPECT_EQ(flags, android_key_ev->getFlags());
945+ EXPECT_EQ(meta_state, android_key_ev->getMetaState());
946+ EXPECT_EQ(AINPUT_EVENT_TYPE_KEY, android_key_ev->getType());
947+ EXPECT_EQ(key_code, android_key_ev->getKeyCode());
948+ EXPECT_EQ(scan_code, android_key_ev->getScanCode());
949+ EXPECT_EQ(repeat_count, android_key_ev->getRepeatCount());
950+ EXPECT_EQ(down_time, android_key_ev->getDownTime());
951+ EXPECT_EQ(event_time, android_key_ev->getEventTime());
952+
953+ delete android_key_ev;
954+}
955+
956 TEST(AndroidInputLexicon, translates_single_pointer_motion_events)
957 {
958 using namespace ::testing;
959@@ -160,10 +209,110 @@
960 EXPECT_EQ(mir_pointer_coords->pressure, pressure);
961 EXPECT_EQ(mir_pointer_coords->orientation, orientation);
962
963-
964 delete android_motion_ev;
965 }
966
967+TEST(AndroidInputLexicon, translates_single_pointer_motion_events_in_reverse)
968+{
969+ using namespace ::testing;
970+
971+ // Common event properties
972+ const int32_t device_id = 1;
973+ const int32_t source_id = 2;
974+ const int32_t action = 3;
975+ const int32_t flags = 4;
976+ const int32_t edge_flags = 5;
977+ const int32_t meta_state = 6;
978+ const int32_t button_state = 7;
979+ const float x_offset = 8;
980+ const float y_offset = 9;
981+ const float x_precision = 10;
982+ const float y_precision = 11;
983+ const nsecs_t down_time = 12;
984+ const nsecs_t event_time = 13;
985+ const size_t pointer_count = 1;
986+
987+ // Set up the MirEvent for translation
988+ // General event properties
989+ MirEvent mir_ev;
990+ auto mmev = &mir_ev.motion;
991+ mir_ev.type = mir_event_type_motion;
992+ // General motion event properties
993+ mmev->device_id = device_id;
994+ mmev->source_id = source_id;
995+ mmev->action = action;
996+ mmev->flags = static_cast<MirMotionFlag>(flags);
997+ mmev->edge_flags = edge_flags;
998+ mmev->modifiers = meta_state;
999+ mmev->button_state = static_cast<MirMotionButton>(button_state);
1000+ mmev->down_time = down_time;
1001+ mmev->event_time = event_time;
1002+ mmev->x_offset = x_offset;
1003+ mmev->y_offset = y_offset;
1004+ mmev->x_precision = x_precision;
1005+ mmev->y_precision = y_precision;
1006+ mmev->pointer_count = pointer_count;
1007+
1008+ // Pointer specific properties
1009+ const int pointer_id = 1;
1010+ const float x_axis = 100.0;
1011+ const float y_axis = 200.0;
1012+ const float touch_minor = 300.0;
1013+ const float touch_major = 400.0;
1014+ const float size = 500.0;
1015+ const float pressure = 600.0;
1016+ const float orientation = 700.0;
1017+
1018+ auto coords = &mmev->pointer_coordinates[0];
1019+ coords->id = pointer_id;
1020+ coords->x = x_axis;
1021+ coords->y = y_axis;
1022+ coords->raw_x = x_axis;
1023+ coords->raw_y = y_axis;
1024+ coords->touch_minor = touch_minor;
1025+ coords->touch_major = touch_major;
1026+ coords->size = size;
1027+ coords->pressure = pressure;
1028+ coords->orientation = orientation;
1029+
1030+ droidinput::InputEvent *android_ev;
1031+ mia::Lexicon::translate(mir_ev, &android_ev);
1032+
1033+ // Verify the produced android event
1034+ // General event properties
1035+ EXPECT_EQ(AINPUT_EVENT_TYPE_MOTION, android_ev->getType());
1036+ // Motion event properties
1037+ auto android_mev = static_cast<droidinput::MotionEvent*>(android_ev);
1038+ EXPECT_EQ(device_id, android_mev->getDeviceId());
1039+ EXPECT_EQ(source_id, android_mev->getSource());
1040+ EXPECT_EQ(action, android_mev->getAction());
1041+ EXPECT_EQ(flags, android_mev->getFlags());
1042+ EXPECT_EQ(edge_flags, android_mev->getEdgeFlags());
1043+ EXPECT_EQ(meta_state, android_mev->getMetaState());
1044+ EXPECT_EQ(button_state, android_mev->getButtonState());
1045+ EXPECT_EQ(down_time, android_mev->getDownTime());
1046+ EXPECT_EQ(event_time, android_mev->getEventTime());
1047+ EXPECT_EQ(x_offset, android_mev->getXOffset());
1048+ EXPECT_EQ(y_offset, android_mev->getYOffset());
1049+ EXPECT_EQ(x_precision, android_mev->getXPrecision());
1050+ EXPECT_EQ(y_precision, android_mev->getYPrecision());
1051+ EXPECT_EQ(pointer_count, android_mev->getPointerCount());
1052+
1053+ // Pointer properties and coordinates
1054+ auto a_pp = android_mev->getPointerProperties(0);
1055+ auto a_pc = android_mev->getRawPointerCoords(0);
1056+ EXPECT_EQ(pointer_id, a_pp->id);
1057+ EXPECT_EQ(x_axis, a_pc->getAxisValue(AMOTION_EVENT_AXIS_X));
1058+ EXPECT_EQ(y_axis, a_pc->getAxisValue(AMOTION_EVENT_AXIS_Y));
1059+ EXPECT_EQ(touch_major, a_pc->getAxisValue(AMOTION_EVENT_AXIS_TOUCH_MAJOR));
1060+ EXPECT_EQ(touch_minor, a_pc->getAxisValue(AMOTION_EVENT_AXIS_TOUCH_MINOR));
1061+ EXPECT_EQ(size, a_pc->getAxisValue(AMOTION_EVENT_AXIS_SIZE));
1062+ EXPECT_EQ(pressure, a_pc->getAxisValue(AMOTION_EVENT_AXIS_PRESSURE));
1063+ EXPECT_EQ(orientation, a_pc->getAxisValue(AMOTION_EVENT_AXIS_ORIENTATION));
1064+
1065+ delete android_ev;
1066+}
1067+
1068 TEST(AndroidInputLexicon, translates_multi_pointer_motion_events)
1069 {
1070 using namespace ::testing;

Subscribers

People subscribed via source and target branches