Merge lp:~andreas-pokorny/mir/fix-1546324 into lp:mir
- fix-1546324
- Merge into development-branch
Status: | Merged | ||||
---|---|---|---|---|---|
Approved by: | Andreas Pokorny | ||||
Approved revision: | no longer in the source branch. | ||||
Merged at revision: | 3348 | ||||
Proposed branch: | lp:~andreas-pokorny/mir/fix-1546324 | ||||
Merge into: | lp:mir | ||||
Prerequisite: | lp:~andreas-pokorny/mir/fix-1544878 | ||||
Diff against target: |
725 lines (+355/-228) 6 files modified
src/platforms/mesa/server/x11/input/input_device.cpp (+137/-1) src/platforms/mesa/server/x11/input/input_device.h (+14/-1) src/platforms/mesa/server/x11/input/input_platform.cpp (+165/-226) tests/include/mir/test/doubles/mock_x11.h (+2/-0) tests/mir_test_doubles/mock_x11.cpp (+15/-0) tests/unit-tests/input/test_x11_platform.cpp (+22/-0) |
||||
To merge this branch: | bzr merge lp:~andreas-pokorny/mir/fix-1546324 | ||||
Related bugs: |
|
Reviewer | Review Type | Date Requested | Status |
---|---|---|---|
Mir CI Bot | continuous-integration | Approve | |
Alexandros Frantzis (community) | Approve | ||
Cemil Azizoglu (community) | Approve | ||
PS Jenkins bot (community) | continuous-integration | Needs Fixing | |
Review via email: mp+286315@code.launchpad.net |
This proposal supersedes a proposal from 2016-02-17.
Commit message
Track cursor movement when no button is pressed
Additionally some of the state tracking code and event sending was moved to mir::X:
Description of the change
This mainly fixes the missing cursor motion when no buttons were pressed. Other things could still be improved - i.e modifier tracking to handle press/release when the window is not touched and things like that.
Andreas Pokorny (andreas-pokorny) wrote : | # |
PS Jenkins bot (ps-jenkins) wrote : | # |
FAILED: Continuous integration, rev:3320
http://
Executed test runs:
SUCCESS: http://
SUCCESS: http://
FAILURE: http://
FAILURE: http://
FAILURE: http://
FAILURE: http://
Click here to trigger a rebuild:
http://
Mir CI Bot (mir-ci-bot) wrote : Posted in a previous version of this proposal | # |
FAILED: Continuous integration, rev:3320
https:/
Executed test runs:
FAILURE: https:/
SUCCESS: https:/
SUCCESS: https:/
SUCCESS: https:/
FAILURE: https:/
SUCCESS: https:/
deb: https:/
FAILURE: https:/
FAILURE: https:/
Click here to trigger a rebuild:
https:/
Mir CI Bot (mir-ci-bot) wrote : | # |
FAILED: Continuous integration, rev:3321
https:/
Executed test runs:
FAILURE: https:/
SUCCESS: https:/
SUCCESS: https:/
SUCCESS: https:/
FAILURE: https:/
SUCCESS: https:/
deb: https:/
FAILURE: https:/
FAILURE: https:/
Click here to trigger a rebuild:
https:/
PS Jenkins bot (ps-jenkins) wrote : | # |
FAILED: Continuous integration, rev:3321
http://
Executed test runs:
SUCCESS: http://
SUCCESS: http://
FAILURE: http://
FAILURE: http://
FAILURE: http://
FAILURE: http://
Click here to trigger a rebuild:
http://
Mir CI Bot (mir-ci-bot) wrote : | # |
PASSED: Continuous integration, rev:3322
https:/
Executed test runs:
SUCCESS: https:/
SUCCESS: https:/
SUCCESS: https:/
SUCCESS: https:/
SUCCESS: https:/
deb: https:/
SUCCESS: https:/
deb: https:/
SUCCESS: https:/
deb: https:/
SUCCESS: https:/
deb: https:/
Click here to trigger a rebuild:
https:/
PS Jenkins bot (ps-jenkins) wrote : | # |
PASSED: Continuous integration, rev:3322
http://
Executed test runs:
SUCCESS: http://
SUCCESS: http://
SUCCESS: http://
SUCCESS: http://
SUCCESS: http://
deb: http://
SUCCESS: http://
deb: http://
SUCCESS: http://
deb: http://
SUCCESS: http://
SUCCESS: http://
SUCCESS: http://
deb: http://
SUCCESS: http://
SUCCESS: http://
Click here to trigger a rebuild:
http://
Cemil Azizoglu (cemil-azizoglu) wrote : | # |
20 +MirPointerButtons to_button_state(int button)
Should be named to_mir_button(int button)
-------
487 + auto const up = 4, down = 5, left = 6, right = 7;
Better to use Button4 for 4, Button5 for 5 as those are standard. For 6 and 7, my understanding is that not every left/right button may be reported as 6 & 7 (though, I'm okay leaving this as you've implemented it). Is this also your understanding? That can also be said for up & down I guess.
-------
114 +void mix::XInputDevi
115 +{
116 + std::cout << "pos " << pos.x.as_float() << std::endl;
117 + auto const movement = pos - pointer_pos;
118 + pointer_pos = pos;
119 + sink->handle_input(
120 + *builder-
121 + event_time,
122 + mir_pointer_
123 + button_state,
124 + scroll.
125 + scroll.
126 + movement.
127 + movement.
128 + )
129 + );
130 +}
button_state is not set to the proper value here. Unfortunately, this function is called at two different times; i) during scrolling, which doesn't require this setting, and ii) mouse motion, which does. So you may have to clone this function (pointer_
Note also that for (ii) you can't simply call to_button_state() function as it has to be extracted from, not 'button' field of the Xevent struct, but from the 'state' field. (You can look at the existing code for that case).
-------
Superluous lines
34 +
60 +
111 +
-------
Hmm, you're right implementing it this way, I think.
77 + button_state |= to_button_
96 + button_state = button_state & ~(to_button_
Though can you change the latter to
for consistency?
Andreas Pokorny (andreas-pokorny) wrote : | # |
>
> 20 +MirPointerButtons to_button_state(int button)
>
> Should be named to_mir_button(int button)
> -------
>
> 487 + auto const up = 4, down = 5, left = 6, right = 7;
>
> Better to use Button4 for 4, Button5 for 5 as those are standard. For 6 and 7,
> my understanding is that not every left/right button may be reported as 6 & 7
> (though, I'm okay leaving this as you've implemented it). Is this also your
> understanding? That can also be said for up & down I guess.
Yeah not entirely sure - but Gtk seems to be sure about that and defines those constants
in its X11 backend. At least my mouse has about 10 buttons - but no horizontal scrolling
wheels and the additional buttons begin with 9 ...
> -------
>
> 114 +void mix::XInputDevi
> event_time, mir::geometry:
> scroll)
> 115 +{
> 116 + std::cout << "pos " << pos.x.as_float() << std::endl;
> 117 + auto const movement = pos - pointer_pos;
> 118 + pointer_pos = pos;
> 119 + sink->handle_input(
> 120 + *builder-
> 121 + event_time,
> 122 + mir_pointer_
> 123 + button_state,
> 124 + scroll.
> 125 + scroll.
> 126 + movement.
> 127 + movement.
> 128 + )
> 129 + );
> 130 +}
>
> button_state is not set to the proper value here. Unfortunately, this function
> is called at two different times; i) during scrolling, which doesn't require
> this setting, and ii) mouse motion, which does. So you may have to clone this
> function (pointer_
>
> Note also that for (ii) you can't simply call to_button_state() function as it
> has to be extracted from, not 'button' field of the Xevent struct, but from
> the 'state' field. (You can look at the existing code for that case).
Ack I should pull it from the most recent event. And remove the iostream mess..
> -------
>
> Superluous lines
> 34 +
> 60 +
> 111 +
>
> -------
>
> Hmm, you're right implementing it this way, I think.
>
> 77 + button_state |= to_button_
> 96 + button_state = button_state & ~(to_button_
>
> Though can you change the latter to
> button_state &= ~(to_button_
> for consistency?
sure..
Mir CI Bot (mir-ci-bot) wrote : | # |
PASSED: Continuous integration, rev:3323
https:/
Executed test runs:
SUCCESS: https:/
SUCCESS: https:/
SUCCESS: https:/
SUCCESS: https:/
SUCCESS: https:/
deb: https:/
SUCCESS: https:/
deb: https:/
SUCCESS: https:/
deb: https:/
SUCCESS: https:/
deb: https:/
Click here to trigger a rebuild:
https:/
PS Jenkins bot (ps-jenkins) wrote : | # |
FAILED: Continuous integration, rev:3323
http://
Executed test runs:
SUCCESS: http://
FAILURE: http://
SUCCESS: http://
SUCCESS: http://
deb: http://
SUCCESS: http://
deb: http://
SUCCESS: http://
deb: http://
SUCCESS: http://
SUCCESS: http://
Click here to trigger a rebuild:
http://
Mir CI Bot (mir-ci-bot) wrote : | # |
FAILED: Continuous integration, rev:3324
https:/
Executed test runs:
FAILURE: https:/
SUCCESS: https:/
SUCCESS: https:/
SUCCESS: https:/
SUCCESS: https:/
deb: https:/
SUCCESS: https:/
deb: https:/
SUCCESS: https:/
deb: https:/
FAILURE: https:/
Click here to trigger a rebuild:
https:/
PS Jenkins bot (ps-jenkins) wrote : | # |
FAILED: Continuous integration, rev:3324
http://
Executed test runs:
SUCCESS: http://
FAILURE: http://
SUCCESS: http://
SUCCESS: http://
deb: http://
SUCCESS: http://
deb: http://
SUCCESS: http://
deb: http://
SUCCESS: http://
SUCCESS: http://
Click here to trigger a rebuild:
http://
Mir CI Bot (mir-ci-bot) wrote : | # |
PASSED: Continuous integration, rev:3325
https:/
Executed test runs:
SUCCESS: https:/
SUCCESS: https:/
SUCCESS: https:/
SUCCESS: https:/
SUCCESS: https:/
deb: https:/
SUCCESS: https:/
deb: https:/
SUCCESS: https:/
deb: https:/
SUCCESS: https:/
deb: https:/
Click here to trigger a rebuild:
https:/
PS Jenkins bot (ps-jenkins) wrote : | # |
PASSED: Continuous integration, rev:3324
http://
Executed test runs:
SUCCESS: http://
SUCCESS: http://
SUCCESS: http://
SUCCESS: http://
SUCCESS: http://
deb: http://
SUCCESS: http://
deb: http://
SUCCESS: http://
deb: http://
SUCCESS: http://
SUCCESS: http://
SUCCESS: http://
deb: http://
SUCCESS: http://
SUCCESS: http://
Click here to trigger a rebuild:
http://
PS Jenkins bot (ps-jenkins) wrote : | # |
FAILED: Continuous integration, rev:3325
http://
Executed test runs:
SUCCESS: http://
SUCCESS: http://
FAILURE: http://
SUCCESS: http://
deb: http://
SUCCESS: http://
deb: http://
SUCCESS: http://
deb: http://
FAILURE: http://
SUCCESS: http://
Click here to trigger a rebuild:
http://
PS Jenkins bot (ps-jenkins) wrote : | # |
PASSED: Continuous integration, rev:3325
http://
Executed test runs:
SUCCESS: http://
SUCCESS: http://
SUCCESS: http://
SUCCESS: http://
SUCCESS: http://
deb: http://
SUCCESS: http://
deb: http://
SUCCESS: http://
deb: http://
SUCCESS: http://
SUCCESS: http://
SUCCESS: http://
deb: http://
SUCCESS: http://
SUCCESS: http://
Click here to trigger a rebuild:
http://
Mir CI Bot (mir-ci-bot) wrote : | # |
PASSED: Continuous integration, rev:3326
https:/
Executed test runs:
SUCCESS: https:/
SUCCESS: https:/
SUCCESS: https:/
SUCCESS: https:/
SUCCESS: https:/
deb: https:/
SUCCESS: https:/
deb: https:/
SUCCESS: https:/
deb: https:/
SUCCESS: https:/
deb: https:/
Click here to trigger a rebuild:
https:/
PS Jenkins bot (ps-jenkins) wrote : | # |
PASSED: Continuous integration, rev:3326
http://
Executed test runs:
SUCCESS: http://
SUCCESS: http://
SUCCESS: http://
SUCCESS: http://
SUCCESS: http://
deb: http://
SUCCESS: http://
deb: http://
SUCCESS: http://
deb: http://
SUCCESS: http://
SUCCESS: http://
SUCCESS: http://
deb: http://
SUCCESS: http://
SUCCESS: http://
Click here to trigger a rebuild:
http://
Cemil Azizoglu (cemil-azizoglu) wrote : | # |
37 +MirPointerButtons to_mir_
38 +{
39 + // the state variable contains modifier and button state.
40 + MirPointerButtons button_state = x_button_key_state << 8;
41 + button_state = (button_state & ~(mir_pointer_
42 + | ((button_state & mir_pointer_
43 + | ((button_state & mir_pointer_
44 + return button_state;
45 +}
This is very hard to follow. It will also be difficult to change once other modifiers need to be handled here. Can we explicitly use 'ButtonNMask' as in the original code?
Mir CI Bot (mir-ci-bot) wrote : | # |
PASSED: Continuous integration, rev:3327
https:/
Executed test runs:
SUCCESS: https:/
SUCCESS: https:/
SUCCESS: https:/
SUCCESS: https:/
SUCCESS: https:/
deb: https:/
SUCCESS: https:/
deb: https:/
SUCCESS: https:/
deb: https:/
SUCCESS: https:/
deb: https:/
Click here to trigger a rebuild:
https:/
PS Jenkins bot (ps-jenkins) wrote : | # |
FAILED: Continuous integration, rev:3327
http://
Executed test runs:
SUCCESS: http://
SUCCESS: http://
None: http://
SUCCESS: http://
deb: http://
SUCCESS: http://
deb: http://
None: http://
Click here to trigger a rebuild:
http://
Cemil Azizoglu (cemil-azizoglu) wrote : | # |
I'm happy
Alexandros Frantzis (afrantzis) wrote : | # |
OK.
Nit/preexisting:
mix::XInputPlat
Mir CI Bot (mir-ci-bot) wrote : | # |
FAILED: Autolanding.
More details in the following jenkins job:
https:/
Executed test runs:
FAILURE: https:/
None: https:/
SUCCESS: https:/
SUCCESS: https:/
SUCCESS: https:/
SUCCESS: https:/
deb: https:/
SUCCESS: https:/
deb: https:/
SUCCESS: https:/
deb: https:/
SUCCESS: https:/
deb: https:/
FAILURE: https:/
Andreas Pokorny (andreas-pokorny) wrote : | # |
Hm NestedServer.
Mir CI Bot (mir-ci-bot) : | # |
Preview Diff
1 | === modified file 'src/platforms/mesa/server/x11/input/input_device.cpp' |
2 | --- src/platforms/mesa/server/x11/input/input_device.cpp 2016-01-29 08:18:22 +0000 |
3 | +++ src/platforms/mesa/server/x11/input/input_device.cpp 2016-02-24 09:00:08 +0000 |
4 | @@ -22,10 +22,53 @@ |
5 | #include "mir/input/touchpad_settings.h" |
6 | #include "mir/input/input_device_info.h" |
7 | #include "mir/input/device_capability.h" |
8 | +#include "mir/input/event_builder.h" |
9 | +#include "mir/input/input_sink.h" |
10 | + |
11 | +#include <X11/Xlib.h> |
12 | +#include <iostream> |
13 | |
14 | namespace mi = mir::input; |
15 | +namespace geom = mir::geometry; |
16 | namespace mix = mi::X; |
17 | |
18 | +namespace |
19 | +{ |
20 | +MirPointerButtons to_mir_button(int button) |
21 | +{ |
22 | + auto const button_side = 8; |
23 | + auto const button_extra = 9; |
24 | + if (button == Button1) |
25 | + return mir_pointer_button_primary; |
26 | + if (button == Button2) // tertiary (middle) button is Button2 in X |
27 | + return mir_pointer_button_tertiary; |
28 | + if (button == Button3) |
29 | + return mir_pointer_button_secondary; |
30 | + if (button == button_side) |
31 | + return mir_pointer_button_side; |
32 | + if (button == button_extra) |
33 | + return mir_pointer_button_extra; |
34 | + return 0; |
35 | +} |
36 | + |
37 | +MirPointerButtons to_mir_button_state(int x_button_key_state) |
38 | +{ |
39 | + MirPointerButtons button_state = 0; |
40 | + if (x_button_key_state & Button1Mask) |
41 | + button_state |= mir_pointer_button_primary; |
42 | + if (x_button_key_state & Button2Mask) |
43 | + button_state |= mir_pointer_button_tertiary; |
44 | + if (x_button_key_state & Button3Mask) |
45 | + button_state |= mir_pointer_button_secondary; |
46 | + if (x_button_key_state & Button4Mask) |
47 | + button_state |= mir_pointer_button_back; |
48 | + if (x_button_key_state & Button5Mask) |
49 | + button_state |= mir_pointer_button_forward; |
50 | + return button_state; |
51 | +} |
52 | + |
53 | +} |
54 | + |
55 | mix::XInputDevice::XInputDevice(InputDeviceInfo const& device_info) |
56 | : info(device_info) |
57 | { |
58 | @@ -45,6 +88,9 @@ |
59 | |
60 | mi::InputDeviceInfo mix::XInputDevice::get_device_info() |
61 | { |
62 | + // TODO Make use if X11-XInput2 to get raw device information |
63 | + // and support other devices than just the unified pointer and |
64 | + // keyboards. |
65 | return info; |
66 | } |
67 | |
68 | @@ -59,7 +105,6 @@ |
69 | |
70 | void mix::XInputDevice::apply_settings(PointerSettings const&) |
71 | { |
72 | - // TODO Make use if X11-XInput2 |
73 | } |
74 | |
75 | mir::optional_value<mi::TouchpadSettings> mix::XInputDevice::get_touchpad_settings() const |
76 | @@ -74,3 +119,94 @@ |
77 | void mix::XInputDevice::apply_settings(TouchpadSettings const&) |
78 | { |
79 | } |
80 | + |
81 | +bool mix::XInputDevice::started() const |
82 | +{ |
83 | + return sink && builder; |
84 | +} |
85 | + |
86 | +void mix::XInputDevice::key_press(std::chrono::nanoseconds event_time, xkb_keysym_t key_sym, int32_t key_code) |
87 | +{ |
88 | + sink->handle_input( |
89 | + *builder->key_event( |
90 | + event_time, |
91 | + mir_keyboard_action_down, |
92 | + key_sym, |
93 | + key_code |
94 | + ) |
95 | + ); |
96 | + |
97 | +} |
98 | + |
99 | +void mix::XInputDevice::key_release(std::chrono::nanoseconds event_time, xkb_keysym_t key_sym, int32_t key_code) |
100 | +{ |
101 | + sink->handle_input( |
102 | + *builder->key_event( |
103 | + event_time, |
104 | + mir_keyboard_action_up, |
105 | + key_sym, |
106 | + key_code |
107 | + ) |
108 | + ); |
109 | +} |
110 | + |
111 | +void mix::XInputDevice::update_button_state(int button) |
112 | +{ |
113 | + button_state = to_mir_button_state(button); |
114 | +} |
115 | + |
116 | +void mix::XInputDevice::pointer_press(std::chrono::nanoseconds event_time, int button, mir::geometry::Point const& pos, mir::geometry::Displacement scroll) |
117 | +{ |
118 | + button_state |= to_mir_button(button); |
119 | + |
120 | + auto const movement = pos - pointer_pos; |
121 | + pointer_pos = pos; |
122 | + sink->handle_input( |
123 | + *builder->pointer_event( |
124 | + event_time, |
125 | + mir_pointer_action_button_down, |
126 | + button_state, |
127 | + scroll.dx.as_float(), |
128 | + scroll.dy.as_float(), |
129 | + movement.dx.as_float(), |
130 | + movement.dy.as_float() |
131 | + ) |
132 | + ); |
133 | +} |
134 | + |
135 | +void mix::XInputDevice::pointer_release(std::chrono::nanoseconds event_time, int button, mir::geometry::Point const& pos, mir::geometry::Displacement scroll) |
136 | +{ |
137 | + button_state &= ~to_mir_button(button); |
138 | + |
139 | + auto const movement = pos - pointer_pos; |
140 | + pointer_pos = pos; |
141 | + sink->handle_input( |
142 | + *builder->pointer_event( |
143 | + event_time, |
144 | + mir_pointer_action_button_up, |
145 | + button_state, |
146 | + scroll.dx.as_float(), |
147 | + scroll.dy.as_float(), |
148 | + movement.dx.as_float(), |
149 | + movement.dy.as_float() |
150 | + ) |
151 | + ); |
152 | + |
153 | +} |
154 | + |
155 | +void mix::XInputDevice::pointer_motion(std::chrono::nanoseconds event_time, mir::geometry::Point const& pos, mir::geometry::Displacement scroll) |
156 | +{ |
157 | + auto const movement = pos - pointer_pos; |
158 | + pointer_pos = pos; |
159 | + sink->handle_input( |
160 | + *builder->pointer_event( |
161 | + event_time, |
162 | + mir_pointer_action_motion, |
163 | + button_state, |
164 | + scroll.dx.as_float(), |
165 | + scroll.dy.as_float(), |
166 | + movement.dx.as_float(), |
167 | + movement.dy.as_float() |
168 | + ) |
169 | + ); |
170 | +} |
171 | |
172 | === modified file 'src/platforms/mesa/server/x11/input/input_device.h' |
173 | --- src/platforms/mesa/server/x11/input/input_device.h 2016-01-29 08:18:22 +0000 |
174 | +++ src/platforms/mesa/server/x11/input/input_device.h 2016-02-24 09:00:08 +0000 |
175 | @@ -21,9 +21,13 @@ |
176 | |
177 | #include "mir/input/input_device.h" |
178 | #include "mir/input/input_device_info.h" |
179 | +#include "mir_toolkit/event.h" |
180 | #include "mir/geometry/point.h" |
181 | +#include "mir/geometry/displacement.h" |
182 | #include "mir/optional_value.h" |
183 | |
184 | +#include <chrono> |
185 | + |
186 | namespace mir |
187 | { |
188 | namespace input |
189 | @@ -47,10 +51,19 @@ |
190 | optional_value<TouchpadSettings> get_touchpad_settings() const override; |
191 | void apply_settings(TouchpadSettings const& settings) override; |
192 | |
193 | + bool started() const; |
194 | + void key_press(std::chrono::nanoseconds event_time, xkb_keysym_t key_sym, int32_t key_code); |
195 | + void key_release(std::chrono::nanoseconds event_time, xkb_keysym_t key_sym, int32_t key_code); |
196 | + void update_button_state(int button); |
197 | + void pointer_press(std::chrono::nanoseconds event_time, int button, mir::geometry::Point const& pos, mir::geometry::Displacement scroll); |
198 | + void pointer_release(std::chrono::nanoseconds event_time, int button, mir::geometry::Point const& pos, mir::geometry::Displacement scroll); |
199 | + void pointer_motion(std::chrono::nanoseconds event_time, mir::geometry::Point const& pos, mir::geometry::Displacement scroll); |
200 | + |
201 | +private: |
202 | + MirPointerButtons button_state{0}; |
203 | InputSink* sink{nullptr}; |
204 | EventBuilder* builder{nullptr}; |
205 | geometry::Point pointer_pos; |
206 | -private: |
207 | InputDeviceInfo info; |
208 | }; |
209 | |
210 | |
211 | === modified file 'src/platforms/mesa/server/x11/input/input_platform.cpp' |
212 | --- src/platforms/mesa/server/x11/input/input_platform.cpp 2016-01-29 08:18:22 +0000 |
213 | +++ src/platforms/mesa/server/x11/input/input_platform.cpp 2016-02-24 09:00:08 +0000 |
214 | @@ -21,8 +21,6 @@ |
215 | |
216 | #include "mir/input/input_device_registry.h" |
217 | #include "mir/input/input_device_info.h" |
218 | -#include "mir/input/event_builder.h" |
219 | -#include "mir/input/input_sink.h" |
220 | #include "mir/dispatch/readable_fd.h" |
221 | |
222 | #define MIR_LOG_COMPONENT "x11-input" |
223 | @@ -43,6 +41,7 @@ |
224 | #define GRAB_KBD |
225 | |
226 | namespace mi = mir::input; |
227 | +namespace geom = mir::geometry; |
228 | namespace md = mir::dispatch; |
229 | namespace mix = mi::X; |
230 | |
231 | @@ -81,233 +80,173 @@ |
232 | |
233 | void mix::XInputPlatform::process_input_event() |
234 | { |
235 | - // This code is based on : |
236 | - // https://tronche.com/gui/x/xlib/events/keyboard-pointer/keyboard-pointer.html |
237 | - XEvent xev; |
238 | - |
239 | - XNextEvent(x11_connection.get(), &xev); |
240 | - |
241 | - if (core_keyboard->sink || core_pointer->sink) |
242 | + do |
243 | { |
244 | - switch (xev.type) |
245 | + // This code is based on : |
246 | + // https://tronche.com/gui/x/xlib/events/keyboard-pointer/keyboard-pointer.html |
247 | + XEvent xev; |
248 | + |
249 | + XNextEvent(x11_connection.get(), &xev); |
250 | + |
251 | + if (core_keyboard->started() && core_pointer->started()) |
252 | { |
253 | + switch (xev.type) |
254 | + { |
255 | #ifdef GRAB_KBD |
256 | - case FocusIn: |
257 | - { |
258 | - auto const& xfiev = (XFocusInEvent&)xev; |
259 | - XGrabKeyboard(xfiev.display, xfiev.window, True, GrabModeAsync, GrabModeAsync, CurrentTime); |
260 | - break; |
261 | - } |
262 | - |
263 | - case FocusOut: |
264 | - { |
265 | - auto const& xfoev = (XFocusOutEvent&)xev; |
266 | - XUngrabKeyboard(xfoev.display, CurrentTime); |
267 | - break; |
268 | - } |
269 | -#endif |
270 | - case KeyPress: |
271 | - case KeyRelease: |
272 | - { |
273 | - auto& xkev = (XKeyEvent&)xev; |
274 | - static const int STRMAX = 32; |
275 | - char str[STRMAX]; |
276 | - KeySym keysym; |
277 | - |
278 | -#ifdef MIR_ON_X11_INPUT_VERBOSE |
279 | - mir::log_info("X11 key event :" |
280 | - " type=%s, serial=%u, send_event=%d, display=%p, window=%p," |
281 | - " root=%p, subwindow=%p, time=%d, x=%d, y=%d, x_root=%d," |
282 | - " y_root=%d, state=0x%0X, keycode=%d, same_screen=%d", |
283 | - xkev.type == KeyPress ? "down" : "up", xkev.serial, |
284 | - xkev.send_event, xkev.display, xkev.window, xkev.root, |
285 | - xkev.subwindow, xkev.time, xkev.x, xkev.y, xkev.x_root, |
286 | - xkev.y_root, xkev.state, xkev.keycode, xkev.same_screen); |
287 | - auto count = |
288 | -#endif |
289 | - XLookupString(&xkev, str, STRMAX, &keysym, NULL); |
290 | - |
291 | - auto event_time = |
292 | - std::chrono::duration_cast<std::chrono::nanoseconds>( |
293 | - std::chrono::milliseconds{xkev.time}); |
294 | - |
295 | -#ifdef MIR_ON_X11_INPUT_VERBOSE |
296 | - for (int i=0; i<count; i++) |
297 | - mir::log_info("buffer[%d]='%c'", i, str[i]); |
298 | - mir::log_info("Mir key event : " |
299 | - "key_code=%d, scan_code=%d, modifiers=0x%0X, event_time=%" PRId64, |
300 | - keysym, xkev.keycode-8, modifiers, event_time); |
301 | -#endif |
302 | - core_keyboard->sink->handle_input( |
303 | - *core_keyboard->builder->key_event( |
304 | - event_time, |
305 | - xkev.type == KeyPress ? |
306 | - mir_keyboard_action_down : |
307 | - mir_keyboard_action_up, |
308 | - keysym, |
309 | - xkev.keycode-8 |
310 | - ) |
311 | - ); |
312 | - break; |
313 | - } |
314 | - |
315 | - case ButtonPress: |
316 | - case ButtonRelease: |
317 | - { |
318 | - auto const& xbev = (XButtonEvent&)xev; |
319 | - |
320 | -#ifdef MIR_ON_X11_INPUT_VERBOSE |
321 | - mir::log_info("X11 button event :" |
322 | - " type=%s, serial=%u, send_event=%d, display=%p, window=%p," |
323 | - " root=%p, subwindow=%p, time=%d, x=%d, y=%d, x_root=%d," |
324 | - " y_root=%d, state=0x%0X, button=%d, same_screen=%d", |
325 | - xbev.type == ButtonPress ? "down" : "up", xbev.serial, |
326 | - xbev.send_event, xbev.display, xbev.window, xbev.root, |
327 | - xbev.subwindow, xbev.time, xbev.x, xbev.y, xbev.x_root, |
328 | - xbev.y_root, xbev.state, xbev.button, xbev.same_screen); |
329 | -#endif |
330 | - if ((xbev.type == ButtonRelease) && |
331 | - ((xbev.button == Button4) || (xbev.button == Button5))) |
332 | - { |
333 | -#ifdef MIR_ON_X11_INPUT_VERBOSE |
334 | - mir::log_info("Swallowed"); |
335 | -#endif |
336 | - break; |
337 | - } |
338 | - auto event_time = |
339 | - std::chrono::duration_cast<std::chrono::nanoseconds>(std::chrono::milliseconds{xbev.time}); |
340 | - |
341 | - MirPointerButtons buttons_pressed = 0; |
342 | - if (xbev.type == ButtonPress) |
343 | - { |
344 | - if (xbev.button == Button1) |
345 | - buttons_pressed |= mir_pointer_button_primary; |
346 | - if (xbev.button == Button2) // tertiary (middle) button is Button2 in X |
347 | - buttons_pressed |= mir_pointer_button_tertiary; |
348 | - if (xbev.button == Button3) |
349 | - buttons_pressed |= mir_pointer_button_secondary; |
350 | - if (xbev.button == Button4) |
351 | - buttons_pressed |= mir_pointer_button_back; |
352 | - if (xbev.button == Button5) |
353 | - buttons_pressed |= mir_pointer_button_forward; |
354 | - } |
355 | - |
356 | -#ifdef MIR_ON_X11_INPUT_VERBOSE |
357 | - mir::log_info("Mir button event : x=%d, y=%d, " |
358 | - "buttons_pressed=0x%0X, event_time=%" PRId64, |
359 | - xbev.x, xbev.y, buttons_pressed, event_time); |
360 | -#endif |
361 | - |
362 | - if ((xbev.button == Button4) || (xbev.button == Button5)) |
363 | - { // scroll event |
364 | - core_pointer->sink->handle_input( |
365 | - *core_pointer->builder->pointer_event( |
366 | - event_time, |
367 | - mir_pointer_action_motion, |
368 | - 0, |
369 | - 0.0f, |
370 | - (xbev.button == Button4) ? 1.0f : -1.0f, |
371 | - 0.0f, |
372 | - 0.0f |
373 | - ) |
374 | - ); |
375 | - } |
376 | - else |
377 | - { |
378 | - auto const old_pointer_pos = core_pointer->pointer_pos; |
379 | - core_pointer->pointer_pos = geometry::Point{xbev.x, xbev.y}; |
380 | - auto const movement = core_pointer->pointer_pos - old_pointer_pos; |
381 | - |
382 | - core_pointer->sink->handle_input( |
383 | - *core_pointer->builder->pointer_event( |
384 | - event_time, |
385 | - xbev.type == ButtonPress ? |
386 | - mir_pointer_action_button_down : |
387 | - mir_pointer_action_button_up, |
388 | - buttons_pressed, |
389 | - 0.0f, |
390 | - 0.0f, |
391 | - movement.dx.as_float(), |
392 | - movement.dy.as_float() |
393 | - ) |
394 | - ); |
395 | - } |
396 | - break; |
397 | - } |
398 | - |
399 | - case MotionNotify: |
400 | - { |
401 | - auto const& xmev = (XMotionEvent&)xev; |
402 | - |
403 | -#ifdef MIR_ON_X11_INPUT_VERBOSE |
404 | - mir::log_info("X11 motion event :" |
405 | - " type=motion, serial=%u, send_event=%d, display=%p, window=%p," |
406 | - " root=%p, subwindow=%p, time=%d, x=%d, y=%d, x_root=%d," |
407 | - " y_root=%d, state=0x%0X, is_hint=%s, same_screen=%d", |
408 | - xmev.serial, xmev.send_event, xmev.display, xmev.window, |
409 | - xmev.root, xmev.subwindow, xmev.time, xmev.x, xmev.y, xmev.x_root, |
410 | - xmev.y_root, xmev.state, xmev.is_hint == NotifyNormal ? "no" : "yes", xmev.same_screen); |
411 | -#endif |
412 | - |
413 | - auto event_time = |
414 | - std::chrono::duration_cast<std::chrono::nanoseconds>( |
415 | - std::chrono::milliseconds{xmev.time}); |
416 | - |
417 | - MirPointerButtons buttons_pressed = 0; |
418 | - if (xmev.state & Button1Mask) |
419 | - buttons_pressed |= mir_pointer_button_primary; |
420 | - if (xmev.state & Button2Mask) // tertiary (middle) button is Button2 in X |
421 | - buttons_pressed |= mir_pointer_button_tertiary; |
422 | - if (xmev.state & Button3Mask) |
423 | - buttons_pressed |= mir_pointer_button_secondary; |
424 | - if (xmev.state & Button4Mask) |
425 | - buttons_pressed |= mir_pointer_button_back; |
426 | - if (xmev.state & Button5Mask) |
427 | - buttons_pressed |= mir_pointer_button_forward; |
428 | - |
429 | -#ifdef MIR_ON_X11_INPUT_VERBOSE |
430 | - mir::log_info("Mir pointer event : " |
431 | - "x=%d, y=%d, buttons_pressed=0x%0X, event_time=%" PRId64, |
432 | - xmev.x, xmev.y, buttons_pressed, event_time); |
433 | -#endif |
434 | - core_pointer->sink->handle_input( |
435 | - *core_pointer->builder->pointer_event( |
436 | - event_time, |
437 | - mir_pointer_action_motion, |
438 | - buttons_pressed, |
439 | - 0.0f, |
440 | - 0.0f, |
441 | - 0.0f, |
442 | - 0.0f |
443 | - ) |
444 | - ); |
445 | - break; |
446 | - } |
447 | - |
448 | - case ConfigureNotify: |
449 | - { |
450 | -#ifdef MIR_ON_X11_INPUT_VERBOSE |
451 | - auto const& xcev = (XConfigureEvent&)xev; |
452 | - mir::log_info("Window size : %dx%d", xcev.width, xcev.height); |
453 | -#endif |
454 | - break; |
455 | - } |
456 | - |
457 | - case MappingNotify: |
458 | -#ifdef MIR_ON_X11_INPUT_VERBOSE |
459 | - mir::log_info("Keyboard mapping changed at server. Refreshing the cache."); |
460 | -#endif |
461 | - XRefreshKeyboardMapping((XMappingEvent*)&xev); |
462 | - break; |
463 | - |
464 | - default: |
465 | -#ifdef MIR_ON_X11_INPUT_VERBOSE |
466 | - mir::log_info("Uninteresting event : %08X", xev.type); |
467 | -#endif |
468 | - break; |
469 | + case FocusIn: |
470 | + { |
471 | + auto const& xfiev = xev.xfocus; |
472 | + XGrabKeyboard(xfiev.display, xfiev.window, True, GrabModeAsync, GrabModeAsync, CurrentTime); |
473 | + break; |
474 | + } |
475 | + |
476 | + case FocusOut: |
477 | + { |
478 | + auto const& xfoev = xev.xfocus; |
479 | + XUngrabKeyboard(xfoev.display, CurrentTime); |
480 | + break; |
481 | + } |
482 | +#endif |
483 | + case KeyPress: |
484 | + case KeyRelease: |
485 | + { |
486 | + auto & xkev = xev.xkey; |
487 | + static const int STRMAX = 32; |
488 | + char str[STRMAX]; |
489 | + KeySym keysym; |
490 | + |
491 | +#ifdef MIR_ON_X11_INPUT_VERBOSE |
492 | + mir::log_info("X11 key event :" |
493 | + " type=%s, serial=%u, send_event=%d, display=%p, window=%p," |
494 | + " root=%p, subwindow=%p, time=%d, x=%d, y=%d, x_root=%d," |
495 | + " y_root=%d, state=0x%0X, keycode=%d, same_screen=%d", |
496 | + xkev.type == KeyPress ? "down" : "up", xkev.serial, |
497 | + xkev.send_event, xkev.display, xkev.window, xkev.root, |
498 | + xkev.subwindow, xkev.time, xkev.x, xkev.y, xkev.x_root, |
499 | + xkev.y_root, xkev.state, xkev.keycode, xkev.same_screen); |
500 | + auto count = |
501 | +#endif |
502 | + XLookupString(&xkev, str, STRMAX, &keysym, NULL); |
503 | + |
504 | + auto const event_time = |
505 | + std::chrono::duration_cast<std::chrono::nanoseconds>( |
506 | + std::chrono::milliseconds{xkev.time}); |
507 | + |
508 | +#ifdef MIR_ON_X11_INPUT_VERBOSE |
509 | + for (int i=0; i<count; i++) |
510 | + mir::log_info("buffer[%d]='%c'", i, str[i]); |
511 | + mir::log_info("Mir key event : " |
512 | + "key_code=%d, scan_code=%d, event_time=%" PRId64, |
513 | + keysym, xkev.keycode-8, event_time); |
514 | +#endif |
515 | + |
516 | + if (xkev.type == KeyPress) |
517 | + core_keyboard->key_press(event_time, keysym, xkev.keycode - 8); |
518 | + else |
519 | + core_keyboard->key_release(event_time, keysym, xkev.keycode - 8); |
520 | + |
521 | + break; |
522 | + } |
523 | + |
524 | + case ButtonPress: |
525 | + case ButtonRelease: |
526 | + { |
527 | + auto const& xbev = xev.xbutton; |
528 | + auto const up = Button4, down = Button5, left = 6, right = 7; |
529 | + |
530 | +#ifdef MIR_ON_X11_INPUT_VERBOSE |
531 | + mir::log_info("X11 button event :" |
532 | + " type=%s, serial=%u, send_event=%d, display=%p, window=%p," |
533 | + " root=%p, subwindow=%p, time=%d, x=%d, y=%d, x_root=%d," |
534 | + " y_root=%d, state=0x%0X, button=%d, same_screen=%d", |
535 | + xbev.type == ButtonPress ? "down" : "up", xbev.serial, |
536 | + xbev.send_event, xbev.display, xbev.window, xbev.root, |
537 | + xbev.subwindow, xbev.time, xbev.x, xbev.y, xbev.x_root, |
538 | + xbev.y_root, xbev.state, xbev.button, xbev.same_screen); |
539 | +#endif |
540 | + if (xbev.type == ButtonRelease && xbev.button >= up && xbev.button <= right) |
541 | + { |
542 | +#ifdef MIR_ON_X11_INPUT_VERBOSE |
543 | + mir::log_info("Swallowed"); |
544 | +#endif |
545 | + break; |
546 | + } |
547 | + auto const event_time = |
548 | + std::chrono::duration_cast<std::chrono::nanoseconds>(std::chrono::milliseconds{xbev.time}); |
549 | + core_pointer->update_button_state(xbev.state); |
550 | + |
551 | + if (xbev.button >= up && xbev.button <= right) |
552 | + { // scroll event |
553 | + core_pointer->pointer_motion( |
554 | + event_time, |
555 | + geom::Point{xbev.x, xbev.y}, |
556 | + geom::Displacement{xbev.button == right ? 1 : xbev.button == left ? -1 : 0, |
557 | + xbev.button == up ? 1 : xbev.button == down ? -1 : 0}); |
558 | + } |
559 | + else |
560 | + { |
561 | + if (xbev.type == ButtonPress) |
562 | + core_pointer->pointer_press( |
563 | + event_time, |
564 | + xbev.button, |
565 | + geom::Point{xbev.x, xbev.y}, |
566 | + geom::Displacement{0, 0}); |
567 | + else |
568 | + core_pointer->pointer_release( |
569 | + event_time, |
570 | + xbev.button, |
571 | + geom::Point{xbev.x, xbev.y}, |
572 | + geom::Displacement{0, 0}); |
573 | + } |
574 | + break; |
575 | + } |
576 | + |
577 | + case MotionNotify: |
578 | + { |
579 | + auto const& xmev = xev.xmotion; |
580 | + |
581 | +#ifdef MIR_ON_X11_INPUT_VERBOSE |
582 | + mir::log_info("X11 motion event :" |
583 | + " type=motion, serial=%u, send_event=%d, display=%p, window=%p," |
584 | + " root=%p, subwindow=%p, time=%d, x=%d, y=%d, x_root=%d," |
585 | + " y_root=%d, state=0x%0X, is_hint=%s, same_screen=%d", |
586 | + xmev.serial, xmev.send_event, xmev.display, xmev.window, |
587 | + xmev.root, xmev.subwindow, xmev.time, xmev.x, xmev.y, xmev.x_root, |
588 | + xmev.y_root, xmev.state, xmev.is_hint == NotifyNormal ? "no" : "yes", xmev.same_screen); |
589 | +#endif |
590 | + |
591 | + core_pointer->update_button_state(xmev.state); |
592 | + core_pointer->pointer_motion( |
593 | + std::chrono::milliseconds{xmev.time}, geom::Point{xmev.x, xmev.y}, geom::Displacement{0, 0}); |
594 | + |
595 | + break; |
596 | + } |
597 | + |
598 | + case ConfigureNotify: |
599 | + { |
600 | +#ifdef MIR_ON_X11_INPUT_VERBOSE |
601 | + auto const& xcev = xev.xconfigure; |
602 | + mir::log_info("Window size : %dx%d", xcev.width, xcev.height); |
603 | +#endif |
604 | + break; |
605 | + } |
606 | + |
607 | + case MappingNotify: |
608 | +#ifdef MIR_ON_X11_INPUT_VERBOSE |
609 | + mir::log_info("Keyboard mapping changed at server. Refreshing the cache."); |
610 | +#endif |
611 | + XRefreshKeyboardMapping(&(xev.xmapping)); |
612 | + break; |
613 | + |
614 | + default: |
615 | +#ifdef MIR_ON_X11_INPUT_VERBOSE |
616 | + mir::log_info("Uninteresting event : %08X", xev.type); |
617 | +#endif |
618 | + break; |
619 | + } |
620 | } |
621 | + else |
622 | + mir::log_error("input event received with no sink to handle it"); |
623 | } |
624 | - else |
625 | - mir::log_error("input event received with no sink to handle it"); |
626 | - |
627 | + while(XPending(x11_connection.get())); |
628 | } |
629 | |
630 | === modified file 'tests/include/mir/test/doubles/mock_x11.h' |
631 | --- tests/include/mir/test/doubles/mock_x11.h 2016-01-29 08:18:22 +0000 |
632 | +++ tests/include/mir/test/doubles/mock_x11.h 2016-02-24 09:00:08 +0000 |
633 | @@ -46,6 +46,7 @@ |
634 | XEvent focus_in_event_return = { 0 }; |
635 | XEvent focus_out_event_return = { 0 }; |
636 | XEvent vscroll_event_return = { 0 }; |
637 | + XEvent motion_event_return = { 0 }; |
638 | }; |
639 | |
640 | class MockX11 |
641 | @@ -56,6 +57,7 @@ |
642 | |
643 | MOCK_METHOD1(XOpenDisplay, Display*(const char*)); |
644 | MOCK_METHOD1(XCloseDisplay, int(Display*)); |
645 | + MOCK_METHOD1(XPending, int(Display*)); |
646 | MOCK_METHOD4(XGetVisualInfo, XVisualInfo*(Display*, long, XVisualInfo*, int*)); |
647 | MOCK_METHOD4(XCreateColormap, Colormap(Display*, Window, Visual*, int)); |
648 | /* Too long to mock, use wrapper instead. |
649 | |
650 | === modified file 'tests/mir_test_doubles/mock_x11.cpp' |
651 | --- tests/mir_test_doubles/mock_x11.cpp 2016-01-29 08:18:22 +0000 |
652 | +++ tests/mir_test_doubles/mock_x11.cpp 2016-02-24 09:00:08 +0000 |
653 | @@ -20,6 +20,8 @@ |
654 | #include "mir/test/doubles/mock_x11.h" |
655 | #include <gtest/gtest.h> |
656 | |
657 | +#include <cstring> |
658 | + |
659 | namespace mtd=mir::test::doubles; |
660 | |
661 | namespace |
662 | @@ -31,6 +33,13 @@ |
663 | : display{reinterpret_cast<Display*>(0x12345678)}, |
664 | window{reinterpret_cast<Window>((long unsigned int)9876543210)} |
665 | { |
666 | + std::memset(&keypress_event_return, 0, sizeof(XEvent)); |
667 | + std::memset(&button_release_event_return, 0, sizeof(XEvent)); |
668 | + std::memset(&expose_event_return, 0, sizeof(XEvent)); |
669 | + std::memset(&focus_in_event_return, 0, sizeof(XEvent)); |
670 | + std::memset(&focus_out_event_return, 0, sizeof(XEvent)); |
671 | + std::memset(&vscroll_event_return, 0, sizeof(XEvent)); |
672 | + std::memset(&motion_event_return, 0, sizeof(XEvent)); |
673 | visual_info.depth = 24; |
674 | keypress_event_return.type = KeyPress; |
675 | button_release_event_return.type = ButtonRelease; |
676 | @@ -40,6 +49,7 @@ |
677 | vscroll_event_return.type = ButtonPress; |
678 | XButtonEvent& xbev = (XButtonEvent&)vscroll_event_return; |
679 | xbev.button = Button4; |
680 | + motion_event_return.type = MotionNotify; |
681 | } |
682 | |
683 | mtd::MockX11::MockX11() |
684 | @@ -171,3 +181,8 @@ |
685 | { |
686 | return global_mock->XSetWMHints(display, window, wmhints); |
687 | } |
688 | + |
689 | +int XPending(Display* display) |
690 | +{ |
691 | + return global_mock->XPending(display); |
692 | +} |
693 | |
694 | === modified file 'tests/unit-tests/input/test_x11_platform.cpp' |
695 | --- tests/unit-tests/input/test_x11_platform.cpp 2016-01-29 08:18:22 +0000 |
696 | +++ tests/unit-tests/input/test_x11_platform.cpp 2016-02-24 09:00:08 +0000 |
697 | @@ -174,6 +174,28 @@ |
698 | process_input_event(); |
699 | } |
700 | |
701 | +TEST_F(X11PlatformTest, motion_event_trigger_pointer_movement) |
702 | +{ |
703 | + prepare_event_processing(); |
704 | + |
705 | + InSequence seq; |
706 | + mock_x11.fake_x11.motion_event_return.xmotion.x = 20; |
707 | + mock_x11.fake_x11.motion_event_return.xmotion.y = 20; |
708 | + EXPECT_CALL(mock_x11, XNextEvent(_,_)) |
709 | + .WillOnce(DoAll(SetArgPointee<1>(mock_x11.fake_x11.motion_event_return), |
710 | + Return(1))); |
711 | + EXPECT_CALL(mock_pointer_sink, handle_input(mt::PointerEventWithDiff(20, 20))); |
712 | + mock_x11.fake_x11.motion_event_return.xmotion.x = 40; |
713 | + mock_x11.fake_x11.motion_event_return.xmotion.y = 25; |
714 | + EXPECT_CALL(mock_x11, XNextEvent(_,_)) |
715 | + .WillOnce(DoAll(SetArgPointee<1>(mock_x11.fake_x11.motion_event_return), |
716 | + Return(1))); |
717 | + EXPECT_CALL(mock_pointer_sink, handle_input(mt::PointerEventWithDiff(20, 5))); |
718 | + |
719 | + process_input_event(); |
720 | + process_input_event(); |
721 | +} |
722 | + |
723 | TEST_F(X11PlatformTest, grabs_keyboard) |
724 | { |
725 | prepare_event_processing(); |
Linux input also defines BTN_BACK and BTN_FORWARD, how are those mapped in X11? I have no mouse with such a button - but I found one with SIDE And EXTRA.