Merge lp:~andreas-pokorny/mir/map-touchscreen-to-output into lp:mir
- map-touchscreen-to-output
- Merge into development-branch
Status: | Merged |
---|---|
Approved by: | Daniel van Vugt |
Approved revision: | no longer in the source branch. |
Merged at revision: | 4067 |
Proposed branch: | lp:~andreas-pokorny/mir/map-touchscreen-to-output |
Merge into: | lp:mir |
Prerequisite: | lp:~andreas-pokorny/mir/add-touchscreen-settings |
Diff against target: |
1859 lines (+946/-126) 29 files modified
include/client/mir_toolkit/client_types.h (+1/-0) include/client/mir_toolkit/mir_input_device.h (+68/-0) include/core/mir_toolkit/mir_input_device_types.h (+2/-2) include/platform/mir/input/input_sink.h (+34/-1) include/platform/mir/input/touchscreen_settings.h (+5/-0) include/test/mir_test_framework/fake_input_device.h (+9/-0) src/client/mir_input_device_api.cpp (+35/-0) src/client/symbols.map (+6/-0) src/include/server/mir/input/seat.h (+5/-1) src/platforms/evdev/libinput_device.cpp (+49/-4) src/platforms/evdev/libinput_device.h (+3/-0) src/server/graphics/nested/input_platform.cpp (+18/-2) src/server/input/basic_seat.cpp (+132/-12) src/server/input/basic_seat.h (+15/-7) src/server/input/default_configuration.cpp (+1/-1) src/server/input/default_input_device_hub.cpp (+9/-1) src/server/input/default_input_device_hub.h (+2/-1) src/server/input/seat_input_device_tracker.cpp (+8/-4) src/server/input/seat_input_device_tracker.h (+11/-2) src/server/scene/surface_stack.cpp (+1/-5) tests/acceptance-tests/test_client_input.cpp (+207/-33) tests/include/mir/test/doubles/mock_input_seat.h (+3/-1) tests/include/mir/test/doubles/mock_input_sink.h (+1/-0) tests/integration-tests/input/test_single_seat_setup.cpp (+47/-14) tests/mir_test_framework/fake_input_device_impl.cpp (+76/-14) tests/mir_test_framework/fake_input_device_impl.h (+14/-0) tests/unit-tests/input/evdev/test_libinput_device.cpp (+181/-15) tests/unit-tests/input/test_seat_input_device_tracker.cpp (+1/-4) tests/unit-tests/scene/test_surface_stack.cpp (+2/-2) |
To merge this branch: | bzr merge lp:~andreas-pokorny/mir/map-touchscreen-to-output |
Related bugs: |
Reviewer | Review Type | Date Requested | Status |
---|---|---|---|
Daniel van Vugt | Abstain | ||
Mir CI Bot | continuous-integration | Approve | |
Alan Griffiths | Approve | ||
Review via email:
|
Commit message
Map Touchscreen to Output: Allow clients to configure a relation between touchscreen and display outputs
When configured the touch contact coordinates will be mapped into the screen space occupied by the output. Previously the first output was used as default and orientation information was ignored. This default behavior is still in place - when not configured the builtin output on android devices will be used. Additionally the connection between input devices and outputs is used to disable input events from deactivated outputs.
Description of the change
This change allows clients to configure a mapping of touchscreen onto actual outputs. Such that the coordinates originating from the configured device will map into the screen space occupied by the output.
This change also bypasses the existing code around InputRegion which removed the output information to early in the stack..
The information about output to input device relation is forwarded to the host server, and handled in stub and evdev platform. Thus the feature needed an ABI/API break in input platform - which is the second inside the yet unreleased 0.27 series - so there is no ABI bump in this MP.

Mir CI Bot (mir-ci-bot) wrote : | # |

Mir CI Bot (mir-ci-bot) wrote : | # |
FAILED: Continuous integration, rev:4010
https:/
Executed test runs:
FAILURE: https:/
SUCCESS: https:/
SUCCESS: https:/
SUCCESS: https:/
SUCCESS: https:/
FAILURE: https:/
FAILURE: https:/
FAILURE: https:/
FAILURE: https:/
FAILURE: https:/
FAILURE: https:/
Click here to trigger a rebuild:
https:/

Mir CI Bot (mir-ci-bot) wrote : | # |
FAILED: Continuous integration, rev:4011
https:/
Executed test runs:
FAILURE: https:/
FAILURE: https:/
FAILURE: https:/
FAILURE: https:/
FAILURE: https:/
FAILURE: https:/
FAILURE: https:/
FAILURE: https:/
FAILURE: https:/
FAILURE: https:/
FAILURE: https:/
Click here to trigger a rebuild:
https:/

Mir CI Bot (mir-ci-bot) wrote : | # |
FAILED: Continuous integration, rev:4012
https:/
Executed test runs:
FAILURE: https:/
SUCCESS: https:/
SUCCESS: https:/
SUCCESS: https:/
SUCCESS: https:/
FAILURE: https:/
FAILURE: https:/
FAILURE: https:/
FAILURE: https:/
FAILURE: https:/
FAILURE: https:/
Click here to trigger a rebuild:
https:/

Mir CI Bot (mir-ci-bot) wrote : | # |
FAILED: Continuous integration, rev:4013
https:/
Executed test runs:
FAILURE: https:/
SUCCESS: https:/
SUCCESS: https:/
SUCCESS: https:/
SUCCESS: https:/
FAILURE: https:/
FAILURE: https:/
FAILURE: https:/
FAILURE: https:/
FAILURE: https:/
FAILURE: https:/
Click here to trigger a rebuild:
https:/

Mir CI Bot (mir-ci-bot) wrote : | # |
FAILED: Continuous integration, rev:4014
https:/
Executed test runs:
FAILURE: https:/
SUCCESS: https:/
SUCCESS: https:/
SUCCESS: https:/
SUCCESS: https:/
FAILURE: https:/
FAILURE: https:/
FAILURE: https:/
SUCCESS: https:/
deb: https:/
SUCCESS: https:/
deb: https:/
FAILURE: https:/
Click here to trigger a rebuild:
https:/

Mir CI Bot (mir-ci-bot) wrote : | # |
PASSED: Continuous integration, rev:4015
https:/
Executed test runs:
SUCCESS: https:/
SUCCESS: https:/
SUCCESS: https:/
SUCCESS: https:/
SUCCESS: https:/
SUCCESS: https:/
deb: https:/
SUCCESS: https:/
deb: https:/
SUCCESS: https:/
deb: https:/
SUCCESS: https:/
deb: https:/
SUCCESS: https:/
deb: https:/
SUCCESS: https:/
deb: https:/
Click here to trigger a rebuild:
https:/

Mir CI Bot (mir-ci-bot) wrote : | # |
FAILED: Continuous integration, rev:4016
https:/
Executed test runs:
FAILURE: https:/
SUCCESS: https:/
SUCCESS: https:/
SUCCESS: https:/
SUCCESS: https:/
FAILURE: https:/
SUCCESS: https:/
deb: https:/
FAILURE: https:/
SUCCESS: https:/
deb: https:/
SUCCESS: https:/
deb: https:/
SUCCESS: https:/
deb: https:/
Click here to trigger a rebuild:
https:/

Mir CI Bot (mir-ci-bot) wrote : | # |
PASSED: Continuous integration, rev:4017
https:/
Executed test runs:
SUCCESS: https:/
SUCCESS: https:/
SUCCESS: https:/
SUCCESS: https:/
SUCCESS: https:/
SUCCESS: https:/
deb: https:/
SUCCESS: https:/
deb: https:/
SUCCESS: https:/
deb: https:/
SUCCESS: https:/
deb: https:/
SUCCESS: https:/
deb: https:/
SUCCESS: https:/
deb: https:/
Click here to trigger a rebuild:
https:/

Mir CI Bot (mir-ci-bot) wrote : | # |
PASSED: Continuous integration, rev:4018
https:/
Executed test runs:
SUCCESS: https:/
SUCCESS: https:/
SUCCESS: https:/
SUCCESS: https:/
SUCCESS: https:/
SUCCESS: https:/
deb: https:/
SUCCESS: https:/
deb: https:/
SUCCESS: https:/
deb: https:/
SUCCESS: https:/
deb: https:/
SUCCESS: https:/
deb: https:/
SUCCESS: https:/
deb: https:/
Click here to trigger a rebuild:
https:/

Alan Griffiths (alan-griffiths) wrote : | # |
+void mir_touchscreen
We shouldn't need to type "enum MirTouchscreenM
enum MirTouchscreenM
{
...
};
=>
typedef enum MirTouchscreenM
{
...
} MirTouchscreenM

Alan Griffiths (alan-griffiths) wrote : | # |
1. Is it worthwhile having both mutable and immutable MirTouchscreenC
2. Are we confident this API won't ever change? (If it might change then it ought to be accessed via the extensions mechanism.)

Andreas Pokorny (andreas-pokorny) wrote : | # |
> 1. Is it worthwhile having both mutable and immutable MirTouchscreenC
We have a non const / const API for all configurations. Touchscreen is not different or special in that reguard.
> 2. Are we confident this API won't ever change? (If it might change then it
> ought to be accessed via the extensions mechanism.)

Andreas Pokorny (andreas-pokorny) wrote : | # |
> 2. Are we confident this API won't ever change? (If it might change then it
> ought to be accessed via the extensions mechanism.)
I cannot respond for the group but this proposal contains the minimum necessary information to get touchscreen coordinates into the system.. I do not expect we would need less in the future - maybe more.. but even that is rather unlikely.

Mir CI Bot (mir-ci-bot) wrote : | # |
FAILED: Continuous integration, rev:4019
https:/
Executed test runs:
FAILURE: https:/
FAILURE: https:/
FAILURE: https:/
FAILURE: https:/
FAILURE: https:/
FAILURE: https:/
FAILURE: https:/
FAILURE: https:/
FAILURE: https:/
FAILURE: https:/
FAILURE: https:/
Click here to trigger a rebuild:
https:/

Mir CI Bot (mir-ci-bot) wrote : | # |
PASSED: Continuous integration, rev:4020
https:/
Executed test runs:
SUCCESS: https:/
SUCCESS: https:/
SUCCESS: https:/
SUCCESS: https:/
SUCCESS: https:/
SUCCESS: https:/
deb: https:/
SUCCESS: https:/
deb: https:/
SUCCESS: https:/
deb: https:/
SUCCESS: https:/
deb: https:/
SUCCESS: https:/
deb: https:/
SUCCESS: https:/
deb: https:/
Click here to trigger a rebuild:
https:/

Alan Griffiths (alan-griffiths) wrote : | # |
Nits:
+enum MirTouchscreenM
...
+void mir_touchscreen
Don't need "enum" now.

Daniel van Vugt (vanvugt) wrote : | # |
I'm slightly concerned about this (but only if it hasn't been tested):
527 + if (output.
528 + {
529 + data.output_size = output.
530 + if (data.active)
531 + output_
532 + }
Can you confirm that the touch coordinates are correct (like in target or fingerpaint) when the current mode is lower resolution than the screen's native mode? In future the opposite might also be true (screen is mirroring a higher res output and is scaled down)...

Daniel van Vugt (vanvugt) wrote : | # |
Don't we want preferred_
preferred_

Daniel van Vugt (vanvugt) wrote : | # |
The current_mode_index is already taken into consideration in the implementation of extents(). So that's your (target) logical rectangle. Your (source) physical touchscreen rectangle should be from preferred_

Andreas Pokorny (andreas-pokorny) wrote : | # |
> Don't we want preferred_
>
> preferred_
> that will more likely match with touchscreen coordinates.
But the intent is not to get the original screen size but the position size and orientation of the output inside the scene. Actually the native resolution does not matter all..
If the touchscreen coordinate values are not in the range defined by the evdev axis for x and y. The system vendor should either fix the kernel driver or the users has to set a calibration matrix via udev. libinput uses the evdev axis ranges and the calibration matrix to return proper device coordinates.

Daniel van Vugt (vanvugt) wrote : | # |
If preferred_
Soon "output.

Andreas Pokorny (andreas-pokorny) wrote : | # |
> If preferred_
> the near future we'll have a logical output size that's different to the
> current mode dimensions (bug 1639226). So the current mode dimensions must be
> ignored. Instead please use "extents().size" to get the correct size of the
> output in the scene.
So you are requiring a change based on an future change that may or may not affect the validity of the modes vector?
But you do understand that I do need an untransformed coordinate range for the x and y axis of the touchscreen? I can use the preferred_
extents().size would be incorrect now and later since it already applies rotation.
I do think about changing the Output to expose a transformation matrix instead of the output orientation and output position. But then still libinput needs an output size.

Mir CI Bot (mir-ci-bot) wrote : | # |
FAILED: Continuous integration, rev:4022
https:/
Executed test runs:
FAILURE: https:/
SUCCESS: https:/
SUCCESS: https:/
SUCCESS: https:/
SUCCESS: https:/
FAILURE: https:/
FAILURE: https:/
FAILURE: https:/
FAILURE: https:/
FAILURE: https:/
FAILURE: https:/
Click here to trigger a rebuild:
https:/

Mir CI Bot (mir-ci-bot) wrote : | # |
FAILED: Continuous integration, rev:4023
https:/
Executed test runs:
FAILURE: https:/
SUCCESS: https:/
SUCCESS: https:/
SUCCESS: https:/
SUCCESS: https:/
FAILURE: https:/
FAILURE: https:/
FAILURE: https:/
FAILURE: https:/
FAILURE: https:/
FAILURE: https:/
Click here to trigger a rebuild:
https:/

Mir CI Bot (mir-ci-bot) wrote : | # |
FAILED: Continuous integration, rev:4024
https:/
Executed test runs:
FAILURE: https:/
SUCCESS: https:/
SUCCESS: https:/
SUCCESS: https:/
SUCCESS: https:/
FAILURE: https:/
FAILURE: https:/
FAILURE: https:/
FAILURE: https:/
FAILURE: https:/
FAILURE: https:/
Click here to trigger a rebuild:
https:/

Mir CI Bot (mir-ci-bot) wrote : | # |
FAILED: Continuous integration, rev:4025
https:/
Executed test runs:
FAILURE: https:/
SUCCESS: https:/
SUCCESS: https:/
SUCCESS: https:/
SUCCESS: https:/
FAILURE: https:/
FAILURE: https:/
FAILURE: https:/
FAILURE: https:/
FAILURE: https:/
FAILURE: https:/
Click here to trigger a rebuild:
https:/

Daniel van Vugt (vanvugt) wrote : | # |
> So you are requiring a change based on an future change that may or may not
> affect the validity of the modes vector?
Yes, that "future change" I am told is my #1 priority to get working for Mir 1.0 (bug 1639226).
> But you do understand that I do need an untransformed coordinate range for the
> x and y axis of the touchscreen? I can use the preferred_
> for that,
Yes, that's why I suggested preferred_
> I do think about changing the Output to expose a transformation matrix instead
> of the output orientation and output position. But then still libinput needs
> an output size.
Yes, I agree and have been thinking that Output may soon need to expose a transformation matrix. DisplayBuffer already does (since r4001), but I think it may need to be guided by a transformation matrix of the output in future.

Mir CI Bot (mir-ci-bot) wrote : | # |
FAILED: Continuous integration, rev:4026
https:/
Executed test runs:
FAILURE: https:/
SUCCESS: https:/
SUCCESS: https:/
SUCCESS: https:/
SUCCESS: https:/
FAILURE: https:/
FAILURE: https:/
FAILURE: https:/
FAILURE: https:/
deb: https:/
SUCCESS: https:/
deb: https:/
FAILURE: https:/
Click here to trigger a rebuild:
https:/

Mir CI Bot (mir-ci-bot) wrote : | # |
FAILED: Continuous integration, rev:4027
https:/
Executed test runs:
FAILURE: https:/
SUCCESS: https:/
SUCCESS: https:/
SUCCESS: https:/
SUCCESS: https:/
FAILURE: https:/
FAILURE: https:/
FAILURE: https:/
FAILURE: https:/
deb: https:/
SUCCESS: https:/
deb: https:/
FAILURE: https:/
Click here to trigger a rebuild:
https:/

Mir CI Bot (mir-ci-bot) wrote : | # |
FAILED: Continuous integration, rev:4028
https:/
Executed test runs:
FAILURE: https:/
SUCCESS: https:/
SUCCESS: https:/
SUCCESS: https:/
SUCCESS: https:/
FAILURE: https:/
SUCCESS: https:/
deb: https:/
SUCCESS: https:/
deb: https:/
SUCCESS: https:/
deb: https:/
SUCCESS: https:/
deb: https:/
SUCCESS: https:/
deb: https:/
Click here to trigger a rebuild:
https:/

Mir CI Bot (mir-ci-bot) wrote : | # |
PASSED: Continuous integration, rev:4029
https:/
Executed test runs:
SUCCESS: https:/
SUCCESS: https:/
SUCCESS: https:/
SUCCESS: https:/
SUCCESS: https:/
SUCCESS: https:/
deb: https:/
SUCCESS: https:/
deb: https:/
SUCCESS: https:/
deb: https:/
SUCCESS: https:/
deb: https:/
SUCCESS: https:/
deb: https:/
SUCCESS: https:/
deb: https:/
Click here to trigger a rebuild:
https:/

Daniel van Vugt (vanvugt) wrote : | # |
Oh, sorry... Are you using current_mode_index because the raw touch coordinates from the kernel/libinput change based on the current display mode? I was assuming raw touch coordinates from the kernel would always just match the native resolution (preferred_

Andreas Pokorny (andreas-pokorny) wrote : | # |
> Oh, sorry... Are you using current_mode_index because the raw touch
> coordinates from the kernel/libinput change based on the current display mode?
> I was assuming raw touch coordinates from the kernel would always just match
> the native resolution (preferred_
No neither is true. The kernel gives touch coordinates in the ranges the device offers. The two ranges sometimes contain information to calculate physical coordinates (as in mm or inch), but on android device that factor is either zero or one. We cannot assume that the ranges given will match the display resolution. (side note: one can affect the values by setting udev properties, but those are meant to make the coordinate axis align with the display axis, those calibration settings cannot account for runtime configuration of the display) Because of that libinput requires a range parameter when reading pixel coordinates from touch screens. Since the touchscreen itself is not rotated when the display is rotated via kms or software rotation I use the number of pixels in unrotated x and y direction (extent() gives me the axis ranges after rotation). By already using the (currently) final scalings of the x and y coordinate axis the code that does rotation and translation to the scene setup of the output is easier to follow.. i.e does not involve further scaling steps..
So with the last change I tried to detangle the display configuration properties from the touchscreen to output mapping. No more orientation enum and position parameter. So with a transformation matrix in place you will be able to modify that as needed.. I.e. make all scalings inside the matrix and make the output sizes such that the coordinates get mapped into unit-square.. or supply an already scaled output size..

Daniel van Vugt (vanvugt) wrote : | # |
OK. If there are any real problems with this approach, we won't notice any time soon. It's unlikely many people will be using touchscreens in the non-native mode or as clones other outputs.
I mean we may hit bugs in this code but we'll really need to release it first to see what those bugs are...
Preview Diff
1 | === modified file 'include/client/mir_toolkit/client_types.h' |
2 | --- include/client/mir_toolkit/client_types.h 2017-02-22 14:36:45 +0000 |
3 | +++ include/client/mir_toolkit/client_types.h 2017-03-01 16:08:29 +0000 |
4 | @@ -394,6 +394,7 @@ |
5 | typedef struct MirKeyboardConfig MirKeyboardConfig; |
6 | typedef struct MirPointerConfig MirPointerConfig; |
7 | typedef struct MirTouchpadConfig MirTouchpadConfig; |
8 | +typedef struct MirTouchscreenConfig MirTouchscreenConfig; |
9 | |
10 | /** |
11 | * MirScreencastParameters is the structure of required information that |
12 | |
13 | === modified file 'include/client/mir_toolkit/mir_input_device.h' |
14 | --- include/client/mir_toolkit/mir_input_device.h 2017-02-14 16:08:27 +0000 |
15 | +++ include/client/mir_toolkit/mir_input_device.h 2017-03-01 16:08:29 +0000 |
16 | @@ -572,6 +572,74 @@ |
17 | void mir_touchpad_config_set_disable_while_typing( |
18 | MirTouchpadConfig* conf, bool active); |
19 | |
20 | +/** |
21 | + * Retrieve a structure containing the touchscreen related config options |
22 | + * of the input device. |
23 | + * |
24 | + * If the input device does not contain a touchscreen, there are no |
25 | + * config options, and the function will return a null pointer. |
26 | + * |
27 | + * \param [in] device The input device |
28 | + * |
29 | + * \return The touchscreen config |
30 | + */ |
31 | +MirTouchscreenConfig const* mir_input_device_get_touchscreen_config( |
32 | + MirInputDevice const* device); |
33 | + |
34 | +/** |
35 | + * Get the output ID of the display the coordinates of this device will be mapped to. |
36 | + * |
37 | + * This setting is ignored when the mapping mode is set to |
38 | + * mir_touchscreen_mapping_mode_to_display_wall. |
39 | + * |
40 | + * \param [in] device The touchscreen configuration |
41 | + * |
42 | + * \return The output id |
43 | + */ |
44 | +uint32_t mir_touchscreen_config_get_output_id(MirTouchscreenConfig const* device); |
45 | + |
46 | +/** |
47 | + * Get the mapping mode used for the touchscreen |
48 | + * |
49 | + * \param [in] device The touchscreen configuration |
50 | + * |
51 | + * \return The mapping mode |
52 | + */ |
53 | +enum MirTouchscreenMappingMode mir_touchscreen_config_get_mapping_mode(MirTouchscreenConfig const* device); |
54 | + |
55 | +/** |
56 | + * Retrieve a mutable structure containing the touchscreen related options |
57 | + * of the input device to change the device configuration. |
58 | + * |
59 | + * If the input device does not contain a touchscreen, there are no |
60 | + * config options, and the function will return a null pointer. |
61 | + * |
62 | + * \param [in] device The input device |
63 | + * |
64 | + * \return The touchscreen config |
65 | + */ |
66 | +MirTouchscreenConfig* mir_input_device_get_mutable_touchscreen_config( |
67 | + MirInputDevice* device); |
68 | + |
69 | +/** |
70 | + * Set the output ID of the display the coordinates of this device will be mapped to. |
71 | + * |
72 | + * This setting is ignored when the mapping mode is set to |
73 | + * mir_touchscreen_mapping_mode_to_display_wall. |
74 | + * |
75 | + * \param [in] config The touchscreen configuration |
76 | + * \param [in] output The output id |
77 | + */ |
78 | +void mir_touchscreen_config_set_output_id(MirTouchscreenConfig* config, uint32_t output); |
79 | + |
80 | +/** |
81 | + * Set the mapping mode used for the touchscreen. |
82 | + * |
83 | + * \param [in] config The touchscreen configuration |
84 | + * \param [in] mode The mapping mode |
85 | + */ |
86 | +void mir_touchscreen_config_set_mapping_mode(MirTouchscreenConfig* config, enum MirTouchscreenMappingMode mode); |
87 | + |
88 | #ifdef __cplusplus |
89 | } |
90 | #endif |
91 | |
92 | === modified file 'include/core/mir_toolkit/mir_input_device_types.h' |
93 | --- include/core/mir_toolkit/mir_input_device_types.h 2017-01-18 02:29:37 +0000 |
94 | +++ include/core/mir_toolkit/mir_input_device_types.h 2017-03-01 16:08:29 +0000 |
95 | @@ -113,7 +113,7 @@ |
96 | * i.e handheld devices with builtin touchscreens or external graphic tablets or |
97 | * external monitors with touchscreen capabilities. |
98 | */ |
99 | -enum MirTouchscreenMappingMode |
100 | +typedef enum MirTouchscreenMappingMode |
101 | { |
102 | /** |
103 | * Map the device coordinates onto specific output. |
104 | @@ -123,7 +123,7 @@ |
105 | * Map the device coordinates onto the whole wall of outputs. |
106 | */ |
107 | mir_touchscreen_mapping_mode_to_display_wall |
108 | -}; |
109 | +} MirTouchscreenMappingMode; |
110 | |
111 | |
112 | #ifdef __cplusplus |
113 | |
114 | === modified file 'include/platform/mir/input/input_sink.h' |
115 | --- include/platform/mir/input/input_sink.h 2016-07-07 09:59:19 +0000 |
116 | +++ include/platform/mir/input/input_sink.h 2017-03-01 16:08:29 +0000 |
117 | @@ -22,14 +22,40 @@ |
118 | |
119 | #include "mir_toolkit/event.h" |
120 | #include "mir/geometry/rectangle.h" |
121 | -#include "mir/geometry/displacement.h" |
122 | +#include "mir/geometry/point.h" |
123 | |
124 | #include <vector> |
125 | +#include <array> |
126 | |
127 | namespace mir |
128 | { |
129 | namespace input |
130 | { |
131 | + |
132 | +struct OutputInfo |
133 | +{ |
134 | + using Matrix = std::array<float,6>; // 2x3 row major matrix |
135 | + OutputInfo() {} |
136 | + OutputInfo(bool active, geometry::Size size, Matrix const& transformation) |
137 | + : active{active}, output_size{size}, output_to_scene(transformation) |
138 | + {} |
139 | + |
140 | + bool active{false}; |
141 | + geometry::Size output_size; |
142 | + Matrix output_to_scene{{1,0,0, |
143 | + 0,1,0}}; |
144 | + |
145 | + inline void transform_to_scene(float& x, float& y) const |
146 | + { |
147 | + auto original_x = x; |
148 | + auto original_y = y; |
149 | + auto const& mat = output_to_scene; |
150 | + |
151 | + x = mat[0]*original_x + mat[1]*original_y + mat[2]*1; |
152 | + y = mat[3]*original_x + mat[4]*original_y + mat[5]*1; |
153 | + } |
154 | +}; |
155 | + |
156 | class InputSink |
157 | { |
158 | public: |
159 | @@ -41,6 +67,13 @@ |
160 | */ |
161 | virtual mir::geometry::Rectangle bounding_rectangle() const = 0; |
162 | |
163 | + /**! |
164 | + * Obtain the output information for a specific ouput. |
165 | + * |
166 | + * \param[in] output_id the id of the output |
167 | + */ |
168 | + virtual OutputInfo output_info(uint32_t output_id) const = 0; |
169 | + |
170 | /** |
171 | * \name Device State interface of InputSink |
172 | * |
173 | |
174 | === modified file 'include/platform/mir/input/touchscreen_settings.h' |
175 | --- include/platform/mir/input/touchscreen_settings.h 2017-02-03 13:42:56 +0000 |
176 | +++ include/platform/mir/input/touchscreen_settings.h 2017-03-01 16:08:29 +0000 |
177 | @@ -30,6 +30,11 @@ |
178 | |
179 | struct TouchscreenSettings |
180 | { |
181 | + TouchscreenSettings() {} |
182 | + TouchscreenSettings(uint32_t output_id, MirTouchscreenMappingMode mode) |
183 | + : output_id{output_id}, mapping_mode{mode} |
184 | + {} |
185 | + |
186 | /** |
187 | * Configures the output the input device should map its coordinates to. |
188 | * The value of this setting is only relevant when the mapping mode is |
189 | |
190 | === modified file 'include/test/mir_test_framework/fake_input_device.h' |
191 | --- include/test/mir_test_framework/fake_input_device.h 2017-01-18 02:29:37 +0000 |
192 | +++ include/test/mir_test_framework/fake_input_device.h 2017-03-01 16:08:29 +0000 |
193 | @@ -24,6 +24,14 @@ |
194 | #include <chrono> |
195 | #include <functional> |
196 | |
197 | +namespace mir |
198 | +{ |
199 | +namespace input |
200 | +{ |
201 | +class InputDevice; |
202 | +} |
203 | +} |
204 | + |
205 | namespace mir_test_framework |
206 | { |
207 | |
208 | @@ -52,6 +60,7 @@ |
209 | virtual void emit_touch_sequence(std::function<mir::input::synthesis::TouchParameters(int)> const& generate_parameters, |
210 | int count, |
211 | std::chrono::duration<double> delay) = 0; |
212 | + virtual void on_new_configuration_do(std::function<void(mir::input::InputDevice const& device)> callback) = 0; |
213 | |
214 | FakeInputDevice(FakeInputDevice const&) = delete; |
215 | FakeInputDevice& operator=(FakeInputDevice const&) = delete; |
216 | |
217 | === modified file 'src/client/mir_input_device_api.cpp' |
218 | --- src/client/mir_input_device_api.cpp 2017-02-09 10:49:40 +0000 |
219 | +++ src/client/mir_input_device_api.cpp 2017-03-01 16:08:29 +0000 |
220 | @@ -20,6 +20,7 @@ |
221 | #include "mir/input/mir_keyboard_config.h" |
222 | #include "mir/input/mir_pointer_config.h" |
223 | #include "mir/input/mir_touchpad_config.h" |
224 | +#include "mir/input/mir_touchscreen_config.h" |
225 | #include "mir/require.h" |
226 | #include "handle_event_exception.h" |
227 | |
228 | @@ -282,3 +283,37 @@ |
229 | { |
230 | conf->disable_while_typing(active); |
231 | }) |
232 | + |
233 | +MirTouchscreenConfig const* mir_input_device_get_touchscreen_config(MirInputDevice const* device) MIR_HANDLE_EVENT_EXCEPTION( |
234 | +{ |
235 | + if (device->has_touchscreen_config()) |
236 | + return &device->touchscreen_config(); |
237 | + return nullptr; |
238 | +}) |
239 | + |
240 | +uint32_t mir_touchscreen_config_get_output_id(MirTouchscreenConfig const* config) MIR_HANDLE_EVENT_EXCEPTION( |
241 | +{ |
242 | + return config->output_id(); |
243 | +}) |
244 | + |
245 | +MirTouchscreenMappingMode mir_touchscreen_config_get_mapping_mode(MirTouchscreenConfig const* config) MIR_HANDLE_EVENT_EXCEPTION( |
246 | +{ |
247 | + return config->mapping_mode(); |
248 | +}) |
249 | + |
250 | +MirTouchscreenConfig* mir_input_device_get_mutable_touchscreen_config(MirInputDevice* device) MIR_HANDLE_EVENT_EXCEPTION( |
251 | +{ |
252 | + if (device->has_touchscreen_config()) |
253 | + return &device->touchscreen_config(); |
254 | + return nullptr; |
255 | +}) |
256 | + |
257 | +void mir_touchscreen_config_set_output_id(MirTouchscreenConfig* config, uint32_t id) MIR_HANDLE_EVENT_EXCEPTION( |
258 | +{ |
259 | + return config->output_id(id); |
260 | +}) |
261 | + |
262 | +void mir_touchscreen_config_set_mapping_mode(MirTouchscreenConfig* config, MirTouchscreenMappingMode mode) MIR_HANDLE_EVENT_EXCEPTION( |
263 | +{ |
264 | + return config->mapping_mode(mode); |
265 | +}) |
266 | |
267 | === modified file 'src/client/symbols.map' |
268 | --- src/client/symbols.map 2017-02-24 14:11:28 +0000 |
269 | +++ src/client/symbols.map 2017-03-01 16:08:29 +0000 |
270 | @@ -616,6 +616,8 @@ |
271 | mir_get_client_api_version; |
272 | mir_input_device_get_keyboard_config; |
273 | mir_input_device_get_mutable_keyboard_config; |
274 | + mir_input_device_get_mutable_touchscreen_config; |
275 | + mir_input_device_get_touchscreen_config; |
276 | mir_keyboard_config_get_keymap_layout; |
277 | mir_keyboard_config_get_keymap_model; |
278 | mir_keyboard_config_get_keymap_options; |
279 | @@ -627,4 +629,8 @@ |
280 | mir_keyboard_event_key_text; |
281 | mir_output_get_logical_height; |
282 | mir_output_get_logical_width; |
283 | + mir_touchscreen_config_get_mapping_mode; |
284 | + mir_touchscreen_config_get_output_id; |
285 | + mir_touchscreen_config_set_mapping_mode; |
286 | + mir_touchscreen_config_set_output_id; |
287 | } MIR_CLIENT_0.26.1; |
288 | |
289 | === modified file 'src/include/server/mir/input/seat.h' |
290 | --- src/include/server/mir/input/seat.h 2017-01-18 02:29:37 +0000 |
291 | +++ src/include/server/mir/input/seat.h 2017-03-01 16:08:29 +0000 |
292 | @@ -33,6 +33,8 @@ |
293 | namespace input |
294 | { |
295 | class Device; |
296 | +class OutputInfo; |
297 | + |
298 | class Seat |
299 | { |
300 | public: |
301 | @@ -41,7 +43,6 @@ |
302 | virtual void add_device(Device const& device) = 0; |
303 | virtual void remove_device(Device const& device) = 0; |
304 | virtual void dispatch_event(MirEvent& event) = 0; |
305 | - virtual geometry::Rectangle get_rectangle_for(Device const& dev) = 0; |
306 | virtual EventUPtr create_device_state() = 0; |
307 | |
308 | virtual void set_key_state(Device const& dev, std::vector<uint32_t> const& scan_codes) = 0; |
309 | @@ -49,6 +50,9 @@ |
310 | virtual void set_cursor_position(float cursor_x, float cursor_y) = 0; |
311 | virtual void set_confinement_regions(geometry::Rectangles const& regions) = 0; |
312 | virtual void reset_confinement_regions() = 0; |
313 | + |
314 | + virtual geometry::Rectangle bounding_rectangle() const = 0; |
315 | + virtual input::OutputInfo output_info(uint32_t output_id) const = 0; |
316 | private: |
317 | Seat(Seat const&) = delete; |
318 | Seat& operator=(Seat const&) = delete; |
319 | |
320 | === modified file 'src/platforms/evdev/libinput_device.cpp' |
321 | --- src/platforms/evdev/libinput_device.cpp 2017-02-03 01:10:42 +0000 |
322 | +++ src/platforms/evdev/libinput_device.cpp 2017-03-01 16:08:29 +0000 |
323 | @@ -114,7 +114,10 @@ |
324 | // Not yet provided by libinput. |
325 | break; |
326 | case LIBINPUT_EVENT_TOUCH_FRAME: |
327 | - sink->handle_input(*convert_touch_frame(libinput_event_get_touch_event(event))); |
328 | + if (is_output_active()) |
329 | + { |
330 | + sink->handle_input(*convert_touch_frame(libinput_event_get_touch_event(event))); |
331 | + } |
332 | break; |
333 | default: |
334 | break; |
335 | @@ -184,6 +187,7 @@ |
336 | auto const action = mir_pointer_action_motion; |
337 | auto const hscroll_value = 0.0f; |
338 | auto const vscroll_value = 0.0f; |
339 | + // either the bounding box .. or the specific output .. |
340 | auto const screen = sink->bounding_rectangle(); |
341 | uint32_t const width = screen.size.width.as_int(); |
342 | uint32_t const height = screen.size.height.as_int(); |
343 | @@ -290,9 +294,10 @@ |
344 | |
345 | void mie::LibInputDevice::update_contact_data(ContactData & data, MirTouchAction action, libinput_event_touch* touch) |
346 | { |
347 | - auto const screen = sink->bounding_rectangle(); |
348 | - uint32_t const width = screen.size.width.as_int(); |
349 | - uint32_t const height = screen.size.height.as_int(); |
350 | + auto info = get_output_info(); |
351 | + |
352 | + uint32_t width = info.output_size.width.as_int(); |
353 | + uint32_t height = info.output_size.height.as_int(); |
354 | |
355 | data.action = action; |
356 | data.pressure = libinput_event_touch_get_pressure(touch); |
357 | @@ -300,6 +305,8 @@ |
358 | data.y = libinput_event_touch_get_y_transformed(touch, height); |
359 | data.major = libinput_event_touch_get_major_transformed(touch, width, height); |
360 | data.minor = libinput_event_touch_get_minor_transformed(touch, width, height); |
361 | + |
362 | + info.transform_to_scene(data.x, data.y); |
363 | } |
364 | |
365 | void mie::LibInputDevice::handle_touch_motion(libinput_event_touch* touch) |
366 | @@ -351,6 +358,10 @@ |
367 | } |
368 | } |
369 | |
370 | + if (contains(caps, mi::DeviceCapability::touchscreen) && |
371 | + !contains(info.capabilities, mi::DeviceCapability::touchscreen)) |
372 | + touchscreen = mi::TouchscreenSettings{}; |
373 | + |
374 | info = mi::InputDeviceInfo{name, unique_id.str(), caps}; |
375 | } |
376 | |
377 | @@ -364,6 +375,40 @@ |
378 | return devices.front().get(); |
379 | } |
380 | |
381 | +mi::OutputInfo mie::LibInputDevice::get_output_info() const |
382 | +{ |
383 | + if (touchscreen.is_set() && touchscreen.value().mapping_mode == mir_touchscreen_mapping_mode_to_output) |
384 | + { |
385 | + return sink->output_info(touchscreen.value().output_id); |
386 | + } |
387 | + else |
388 | + { |
389 | + auto scene_bbox = sink->bounding_rectangle(); |
390 | + return mi::OutputInfo( |
391 | + true, |
392 | + scene_bbox.size, |
393 | + mi::OutputInfo::Matrix{{1.0f, 0.0f, float(scene_bbox.top_left.x.as_int()), |
394 | + 0.0f, 1.0f, float(scene_bbox.top_left.y.as_int())}}); |
395 | + } |
396 | +} |
397 | + |
398 | +bool mie::LibInputDevice::is_output_active() const |
399 | +{ |
400 | + if (!sink) |
401 | + return false; |
402 | + |
403 | + if (touchscreen.is_set()) |
404 | + { |
405 | + auto const& touchscreen_config = touchscreen.value(); |
406 | + if (touchscreen_config.mapping_mode == mir_touchscreen_mapping_mode_to_output) |
407 | + { |
408 | + auto output = sink->output_info(touchscreen_config.output_id); |
409 | + return output.active; |
410 | + } |
411 | + } |
412 | + return true; |
413 | +} |
414 | + |
415 | mir::optional_value<mi::PointerSettings> mie::LibInputDevice::get_pointer_settings() const |
416 | { |
417 | if (!contains(info.capabilities, mi::DeviceCapability::pointer)) |
418 | |
419 | === modified file 'src/platforms/evdev/libinput_device.h' |
420 | --- src/platforms/evdev/libinput_device.h 2017-02-03 01:10:42 +0000 |
421 | +++ src/platforms/evdev/libinput_device.h 2017-03-01 16:08:29 +0000 |
422 | @@ -41,6 +41,7 @@ |
423 | { |
424 | namespace input |
425 | { |
426 | +class OutputInfo; |
427 | class InputReport; |
428 | namespace evdev |
429 | { |
430 | @@ -77,6 +78,8 @@ |
431 | void handle_touch_up(libinput_event_touch* touch); |
432 | void handle_touch_motion(libinput_event_touch* touch); |
433 | void update_device_info(); |
434 | + bool is_output_active() const; |
435 | + OutputInfo get_output_info() const; |
436 | |
437 | std::shared_ptr<InputReport> report; |
438 | std::shared_ptr<::libinput> lib; |
439 | |
440 | === modified file 'src/server/graphics/nested/input_platform.cpp' |
441 | --- src/server/graphics/nested/input_platform.cpp 2017-02-20 13:19:28 +0000 |
442 | +++ src/server/graphics/nested/input_platform.cpp 2017-03-01 16:08:29 +0000 |
443 | @@ -104,7 +104,15 @@ |
444 | touchpad_settings = settings; |
445 | } |
446 | |
447 | - // TODO Forwars config when mirclient API is there |
448 | + auto touchscreen_config = mir_input_device_get_touchscreen_config(dev); |
449 | + if (touchscreen_config && contains(device_info.capabilities, mi::DeviceCapability::touchscreen)) |
450 | + { |
451 | + mi::TouchscreenSettings settings; |
452 | + settings.output_id = mir_touchscreen_config_get_output_id(touchscreen_config); |
453 | + settings.mapping_mode = mir_touchscreen_config_get_mapping_mode(touchscreen_config); |
454 | + |
455 | + touchscreen_settings = settings; |
456 | + } |
457 | } |
458 | |
459 | void start(mi::InputSink* destination, mi::EventBuilder* builder) override |
460 | @@ -251,7 +259,15 @@ |
461 | if (touchscreen_settings.is_set() && touchscreen_settings.value() == new_settings) |
462 | return; |
463 | |
464 | - // TODO update the MirInputConfig.. |
465 | + auto ts_conf = mir_input_device_get_mutable_touchscreen_config(device); |
466 | + |
467 | + if (ts_conf) |
468 | + { |
469 | + mir_touchscreen_config_set_output_id(ts_conf, new_settings.output_id); |
470 | + mir_touchscreen_config_set_mapping_mode(ts_conf, new_settings.mapping_mode); |
471 | + |
472 | + emit_device_change(); |
473 | + } |
474 | } |
475 | |
476 | MirInputDeviceId device_id; |
477 | |
478 | === modified file 'src/server/input/basic_seat.cpp' |
479 | --- src/server/input/basic_seat.cpp 2017-01-18 02:29:37 +0000 |
480 | +++ src/server/input/basic_seat.cpp 2017-03-01 16:08:29 +0000 |
481 | @@ -19,29 +19,147 @@ |
482 | |
483 | #include "basic_seat.h" |
484 | #include "mir/input/device.h" |
485 | -#include "mir/input/input_region.h" |
486 | +#include "mir/input/input_sink.h" |
487 | +#include "mir/graphics/display_configuration_observer.h" |
488 | +#include "mir/graphics/display_configuration.h" |
489 | +#include "mir/graphics/transformation.h" |
490 | +#include "mir/geometry/rectangle.h" |
491 | +#include "mir/geometry/point.h" |
492 | +#include "mir/geometry/size.h" |
493 | +#include "mir_toolkit/common.h" |
494 | |
495 | #include <algorithm> |
496 | +#include <array> |
497 | |
498 | namespace mi = mir::input; |
499 | namespace mf = mir::frontend; |
500 | +namespace mg = mir::graphics; |
501 | +namespace geom = mir::geometry; |
502 | + |
503 | +struct mi::BasicSeat::OutputTracker : mg::DisplayConfigurationObserver |
504 | +{ |
505 | + OutputTracker(SeatInputDeviceTracker& tracker) |
506 | + : input_state_tracker{tracker} |
507 | + { |
508 | + } |
509 | + |
510 | + void update_outputs(mg::DisplayConfiguration const& conf) |
511 | + { |
512 | + std::lock_guard<std::mutex> lock(output_mutex); |
513 | + outputs.clear(); |
514 | + geom::Rectangles output_rectangles; |
515 | + conf.for_each_output( |
516 | + [this, &output_rectangles](mg::DisplayConfigurationOutput const& output) |
517 | + { |
518 | + if (!output.used || !output.connected) |
519 | + return; |
520 | + if (!output.valid() || (output.current_mode_index >= output.modes.size())) |
521 | + return; |
522 | + |
523 | + // TODO make the decision whether display that is used but powered off should emit |
524 | + // touch screen events in a policy |
525 | + bool active = output.power_mode == mir_power_mode_on; |
526 | + |
527 | + auto output_size = output.modes[output.current_mode_index].size; |
528 | + auto width = output_size.width.as_int(); |
529 | + auto height = output_size.height.as_int(); |
530 | + OutputInfo::Matrix output_matrix{{ |
531 | + 1.0f, 0.0f, float(output.top_left.x.as_int()), |
532 | + 0.0f, 1.0f, float(output.top_left.y.as_int())}}; |
533 | + |
534 | + switch(output.orientation) |
535 | + { |
536 | + case mir_orientation_left: |
537 | + output_matrix[3] = -1; |
538 | + output_matrix[5] += width; |
539 | + break; |
540 | + case mir_orientation_right: |
541 | + output_matrix[1] = -1; |
542 | + output_matrix[2] += height; |
543 | + break; |
544 | + case mir_orientation_inverted: |
545 | + output_matrix[0] = -1; |
546 | + output_matrix[2] += width; |
547 | + output_matrix[4] = -1; |
548 | + output_matrix[5] += height; |
549 | + default: |
550 | + break; |
551 | + } |
552 | + if (active) |
553 | + output_rectangles.add(output.extents()); |
554 | + outputs.insert(std::make_pair(output.id.as_value(), OutputInfo{active, output_size, output_matrix})); |
555 | + }); |
556 | + input_state_tracker.update_outputs(output_rectangles); |
557 | + bounding_rectangle = output_rectangles.bounding_rectangle(); |
558 | + } |
559 | + |
560 | + void initial_configuration(std::shared_ptr<mg::DisplayConfiguration const> const& config) override |
561 | + { |
562 | + update_outputs(*config.get()); |
563 | + } |
564 | + |
565 | + void configuration_applied(std::shared_ptr<mg::DisplayConfiguration const> const& config) override |
566 | + { |
567 | + update_outputs(*config.get()); |
568 | + } |
569 | + |
570 | + void base_configuration_updated(std::shared_ptr<mg::DisplayConfiguration const> const&) override |
571 | + {} |
572 | + |
573 | + void session_configuration_applied(std::shared_ptr<mf::Session> const&, |
574 | + std::shared_ptr<mg::DisplayConfiguration> const&) override |
575 | + {} |
576 | + |
577 | + void session_configuration_removed(std::shared_ptr<mf::Session> const&) override |
578 | + {} |
579 | + |
580 | + void configuration_failed( |
581 | + std::shared_ptr<mg::DisplayConfiguration const> const&, |
582 | + std::exception const&) override |
583 | + {} |
584 | + |
585 | + void catastrophic_configuration_error( |
586 | + std::shared_ptr<mg::DisplayConfiguration const> const&, |
587 | + std::exception const&) override |
588 | + {} |
589 | + |
590 | + geom::Rectangle get_bounding_rectangle() const |
591 | + { |
592 | + std::lock_guard<std::mutex> lock(output_mutex); |
593 | + return bounding_rectangle; |
594 | + } |
595 | + |
596 | + mi::OutputInfo get_output_info(uint32_t output) const |
597 | + { |
598 | + std::lock_guard<std::mutex> lock(output_mutex); |
599 | + auto pos = outputs.find(output); |
600 | + if (pos != end(outputs)) |
601 | + return pos->second; |
602 | + return OutputInfo{}; |
603 | + } |
604 | +private: |
605 | + mutable std::mutex output_mutex; |
606 | + mi::SeatInputDeviceTracker& input_state_tracker; |
607 | + std::unordered_map<uint32_t, mi::OutputInfo> outputs; |
608 | + geom::Rectangle bounding_rectangle; |
609 | +}; |
610 | |
611 | mi::BasicSeat::BasicSeat(std::shared_ptr<mi::InputDispatcher> const& dispatcher, |
612 | std::shared_ptr<mi::TouchVisualizer> const& touch_visualizer, |
613 | std::shared_ptr<mi::CursorListener> const& cursor_listener, |
614 | - std::shared_ptr<mi::InputRegion> const& input_region, |
615 | + std::shared_ptr<Registrar> const& registrar, |
616 | std::shared_ptr<mi::KeyMapper> const& key_mapper, |
617 | std::shared_ptr<time::Clock> const& clock, |
618 | std::shared_ptr<mi::SeatObserver> const& observer) : |
619 | input_state_tracker{dispatcher, |
620 | touch_visualizer, |
621 | cursor_listener, |
622 | - input_region, |
623 | key_mapper, |
624 | clock, |
625 | observer}, |
626 | - input_region{input_region} |
627 | + output_tracker{std::make_shared<OutputTracker>(input_state_tracker)} |
628 | { |
629 | + registrar->register_interest(output_tracker); |
630 | } |
631 | |
632 | void mi::BasicSeat::add_device(input::Device const& device) |
633 | @@ -59,13 +177,14 @@ |
634 | input_state_tracker.dispatch(event); |
635 | } |
636 | |
637 | -mir::geometry::Rectangle mi::BasicSeat::get_rectangle_for(input::Device const&) |
638 | -{ |
639 | - // TODO: With knowledge of the outputs attached to this seat and the output the given input |
640 | - // device is associated this method should only return the rectangle of that output. For now |
641 | - // we rely on the existing workaround in DisplayInputRegion::bounding_rectangle() which |
642 | - // assumes that only the first output may have a touch screen associated to it. |
643 | - return input_region->bounding_rectangle(); |
644 | +geom::Rectangle mi::BasicSeat::bounding_rectangle() const |
645 | +{ |
646 | + return output_tracker->get_bounding_rectangle(); |
647 | +} |
648 | + |
649 | +mi::OutputInfo mi::BasicSeat::output_info(uint32_t output_id) const |
650 | +{ |
651 | + return output_tracker->get_output_info(output_id); |
652 | } |
653 | |
654 | mir::EventUPtr mi::BasicSeat::create_device_state() |
655 | @@ -88,7 +207,7 @@ |
656 | input_state_tracker.set_cursor_position(cursor_x, cursor_y); |
657 | } |
658 | |
659 | -void mi::BasicSeat::set_confinement_regions(geometry::Rectangles const& regions) |
660 | +void mi::BasicSeat::set_confinement_regions(geom::Rectangles const& regions) |
661 | { |
662 | input_state_tracker.set_confinement_regions(regions); |
663 | } |
664 | @@ -97,3 +216,4 @@ |
665 | { |
666 | input_state_tracker.reset_confinement_regions(); |
667 | } |
668 | + |
669 | |
670 | === modified file 'src/server/input/basic_seat.h' |
671 | --- src/server/input/basic_seat.h 2017-01-18 02:29:37 +0000 |
672 | +++ src/server/input/basic_seat.h 2017-03-01 16:08:29 +0000 |
673 | @@ -20,9 +20,10 @@ |
674 | #ifndef MIR_BASIC_SEAT_H_ |
675 | #define MIR_BASIC_SEAT_H_ |
676 | |
677 | +#include "seat_input_device_tracker.h" |
678 | #include "mir/input/seat.h" |
679 | #include "mir/frontend/event_sink.h" |
680 | -#include "seat_input_device_tracker.h" |
681 | +#include "mir/observer_registrar.h" |
682 | |
683 | #include <mutex> |
684 | |
685 | @@ -32,6 +33,10 @@ |
686 | { |
687 | class Clock; |
688 | } |
689 | +namespace graphics |
690 | +{ |
691 | +class DisplayConfigurationObserver; |
692 | +} |
693 | namespace input |
694 | { |
695 | class TouchVisualizer; |
696 | @@ -44,10 +49,11 @@ |
697 | class BasicSeat : public Seat |
698 | { |
699 | public: |
700 | + using Registrar = ObserverRegistrar<graphics::DisplayConfigurationObserver>; |
701 | BasicSeat(std::shared_ptr<InputDispatcher> const& dispatcher, |
702 | std::shared_ptr<TouchVisualizer> const& touch_visualizer, |
703 | std::shared_ptr<CursorListener> const& cursor_listener, |
704 | - std::shared_ptr<InputRegion> const& input_region, |
705 | + std::shared_ptr<Registrar> const& registrar, |
706 | std::shared_ptr<KeyMapper> const& key_mapper, |
707 | std::shared_ptr<time::Clock> const& clock, |
708 | std::shared_ptr<SeatObserver> const& observer); |
709 | @@ -55,17 +61,19 @@ |
710 | void add_device(Device const& device) override; |
711 | void remove_device(Device const& device) override; |
712 | void dispatch_event(MirEvent& event) override; |
713 | - geometry::Rectangle get_rectangle_for(Device const& dev) override; |
714 | - virtual EventUPtr create_device_state() override; |
715 | - virtual void set_confinement_regions(geometry::Rectangles const& regions) override; |
716 | - virtual void reset_confinement_regions() override; |
717 | + geometry::Rectangle bounding_rectangle() const override; |
718 | + input::OutputInfo output_info(uint32_t output_id) const override; |
719 | + EventUPtr create_device_state() override; |
720 | + void set_confinement_regions(geometry::Rectangles const& regions) override; |
721 | + void reset_confinement_regions() override; |
722 | |
723 | void set_key_state(Device const& dev, std::vector<uint32_t> const& scan_codes) override; |
724 | void set_pointer_state(Device const& dev, MirPointerButtons buttons) override; |
725 | void set_cursor_position(float cursor_x, float cursor_y) override; |
726 | private: |
727 | SeatInputDeviceTracker input_state_tracker; |
728 | - std::shared_ptr<InputRegion> const input_region; |
729 | + struct OutputTracker; |
730 | + std::shared_ptr<OutputTracker> const output_tracker; |
731 | }; |
732 | } |
733 | } |
734 | |
735 | === modified file 'src/server/input/default_configuration.cpp' |
736 | --- src/server/input/default_configuration.cpp 2017-02-16 05:44:15 +0000 |
737 | +++ src/server/input/default_configuration.cpp 2017-03-01 16:08:29 +0000 |
738 | @@ -297,7 +297,7 @@ |
739 | the_input_dispatcher(), |
740 | the_touch_visualizer(), |
741 | the_cursor_listener(), |
742 | - the_input_region(), |
743 | + the_display_configuration_observer_registrar(), |
744 | the_key_mapper(), |
745 | the_clock(), |
746 | the_seat_observer()); |
747 | |
748 | === modified file 'src/server/input/default_input_device_hub.cpp' |
749 | --- src/server/input/default_input_device_hub.cpp 2017-02-27 18:23:08 +0000 |
750 | +++ src/server/input/default_input_device_hub.cpp 2017-03-01 16:08:29 +0000 |
751 | @@ -201,7 +201,15 @@ |
752 | if (!seat) |
753 | BOOST_THROW_EXCEPTION(std::runtime_error("Device not started and has no seat assigned")); |
754 | |
755 | - return seat->get_rectangle_for(*handle); |
756 | + return seat->bounding_rectangle(); |
757 | +} |
758 | + |
759 | +mi::OutputInfo mi::DefaultInputDeviceHub::RegisteredDevice::output_info(uint32_t output_id) const |
760 | +{ |
761 | + if (!seat) |
762 | + BOOST_THROW_EXCEPTION(std::runtime_error("Device not started and has no seat assigned")); |
763 | + |
764 | + return seat->output_info(output_id); |
765 | } |
766 | |
767 | void mi::DefaultInputDeviceHub::RegisteredDevice::key_state(std::vector<uint32_t> const& scan_codes) |
768 | |
769 | === modified file 'src/server/input/default_input_device_hub.h' |
770 | --- src/server/input/default_input_device_hub.h 2017-02-27 18:23:08 +0000 |
771 | +++ src/server/input/default_input_device_hub.h 2017-03-01 16:08:29 +0000 |
772 | @@ -104,7 +104,8 @@ |
773 | std::shared_ptr<cookie::Authority> const& cookie_authority, |
774 | std::shared_ptr<DefaultDevice> const& handle); |
775 | void handle_input(MirEvent& event) override; |
776 | - mir::geometry::Rectangle bounding_rectangle() const override; |
777 | + geometry::Rectangle bounding_rectangle() const override; |
778 | + input::OutputInfo output_info(uint32_t output_id) const override; |
779 | bool device_matches(std::shared_ptr<InputDevice> const& dev) const; |
780 | void start(std::shared_ptr<Seat> const& seat); |
781 | void stop(); |
782 | |
783 | === modified file 'src/server/input/seat_input_device_tracker.cpp' |
784 | --- src/server/input/seat_input_device_tracker.cpp 2017-02-17 07:03:56 +0000 |
785 | +++ src/server/input/seat_input_device_tracker.cpp 2017-03-01 16:08:29 +0000 |
786 | @@ -20,7 +20,6 @@ |
787 | #include "seat_input_device_tracker.h" |
788 | #include "mir/input/device.h" |
789 | #include "mir/input/cursor_listener.h" |
790 | -#include "mir/input/input_region.h" |
791 | #include "mir/input/input_dispatcher.h" |
792 | #include "mir/input/key_mapper.h" |
793 | #include "mir/input/seat_observer.h" |
794 | @@ -45,12 +44,11 @@ |
795 | mi::SeatInputDeviceTracker::SeatInputDeviceTracker(std::shared_ptr<InputDispatcher> const& dispatcher, |
796 | std::shared_ptr<TouchVisualizer> const& touch_visualizer, |
797 | std::shared_ptr<CursorListener> const& cursor_listener, |
798 | - std::shared_ptr<InputRegion> const& input_region, |
799 | std::shared_ptr<KeyMapper> const& key_mapper, |
800 | std::shared_ptr<time::Clock> const& clock, |
801 | std::shared_ptr<SeatObserver> const& observer) |
802 | : dispatcher{dispatcher}, touch_visualizer{touch_visualizer}, cursor_listener{cursor_listener}, |
803 | - input_region{input_region}, key_mapper{key_mapper}, clock{clock}, observer{observer}, buttons{0} |
804 | + key_mapper{key_mapper}, clock{clock}, observer{observer}, buttons{0} |
805 | { |
806 | } |
807 | |
808 | @@ -231,10 +229,16 @@ |
809 | observer->seat_reset_confinement_regions(); |
810 | } |
811 | |
812 | +void mi::SeatInputDeviceTracker::update_outputs(geom::Rectangles const& output_regions) |
813 | +{ |
814 | + std::lock_guard<std::mutex> lg(output_mutex); |
815 | + input_region = output_regions; |
816 | +} |
817 | + |
818 | void mi::SeatInputDeviceTracker::confine_function(mir::geometry::Point& p) const |
819 | { |
820 | std::lock_guard<std::mutex> lg(region_mutex); |
821 | - input_region->confine(p); |
822 | + input_region.confine(p); |
823 | confined_region.confine(p); |
824 | } |
825 | |
826 | |
827 | === modified file 'src/server/input/seat_input_device_tracker.h' |
828 | --- src/server/input/seat_input_device_tracker.h 2017-02-17 07:03:56 +0000 |
829 | +++ src/server/input/seat_input_device_tracker.h 2017-03-01 16:08:29 +0000 |
830 | @@ -23,7 +23,10 @@ |
831 | #include "mir/input/touch_visualizer.h" |
832 | #include "mir/geometry/point.h" |
833 | #include "mir/geometry/rectangles.h" |
834 | +#include "mir/geometry/size.h" |
835 | +#include "mir/optional_value.h" |
836 | #include "mir_toolkit/event.h" |
837 | + |
838 | #include <unordered_map> |
839 | #include <memory> |
840 | #include <mutex> |
841 | @@ -56,7 +59,6 @@ |
842 | SeatInputDeviceTracker(std::shared_ptr<InputDispatcher> const& dispatcher, |
843 | std::shared_ptr<TouchVisualizer> const& touch_visualizer, |
844 | std::shared_ptr<CursorListener> const& cursor_listener, |
845 | - std::shared_ptr<InputRegion> const& input_region, |
846 | std::shared_ptr<KeyMapper> const& key_mapper, |
847 | std::shared_ptr<time::Clock> const& clock, |
848 | std::shared_ptr<SeatObserver> const& observer); |
849 | @@ -74,6 +76,8 @@ |
850 | void set_cursor_position(float cursor_x, float cursor_y); |
851 | void set_confinement_regions(geometry::Rectangles const& region); |
852 | void reset_confinement_regions(); |
853 | + |
854 | + void update_outputs(geometry::Rectangles const& outputs); |
855 | private: |
856 | void update_seat_properties(MirInputEvent const* event); |
857 | void update_cursor(MirPointerEvent const* event); |
858 | @@ -86,7 +90,6 @@ |
859 | std::shared_ptr<InputDispatcher> const dispatcher; |
860 | std::shared_ptr<TouchVisualizer> const touch_visualizer; |
861 | std::shared_ptr<CursorListener> const cursor_listener; |
862 | - std::shared_ptr<InputRegion> const input_region; |
863 | std::shared_ptr<KeyMapper> const key_mapper; |
864 | std::shared_ptr<time::Clock> const clock; |
865 | std::shared_ptr<SeatObserver> const observer; |
866 | @@ -102,6 +105,9 @@ |
867 | MirPointerButtons buttons{0}; |
868 | std::vector<TouchVisualizer::Spot> spots; |
869 | std::vector<uint32_t> scan_codes; |
870 | + |
871 | + MirTouchscreenMappingMode mapping_mode{mir_touchscreen_mapping_mode_to_output}; |
872 | + mir::optional_value<uint32_t> output_id; |
873 | }; |
874 | |
875 | // Libinput's acceleration curve means the cursor moves by non-integer |
876 | @@ -115,6 +121,9 @@ |
877 | |
878 | std::mutex mutable device_state_mutex; |
879 | std::mutex mutable region_mutex; |
880 | + |
881 | + std::mutex mutable output_mutex; |
882 | + geometry::Rectangles input_region; |
883 | }; |
884 | |
885 | } |
886 | |
887 | === modified file 'src/server/scene/surface_stack.cpp' |
888 | --- src/server/scene/surface_stack.cpp 2017-01-20 12:32:45 +0000 |
889 | +++ src/server/scene/surface_stack.cpp 2017-03-01 16:08:29 +0000 |
890 | @@ -309,11 +309,7 @@ |
891 | RecursiveReadLock lg(guard); |
892 | for (auto &surface : surfaces) |
893 | { |
894 | - if (surface->query(mir_window_attrib_visibility) == |
895 | - MirWindowVisibility::mir_window_visibility_exposed) |
896 | - { |
897 | - callback(surface); |
898 | - } |
899 | + callback(surface); |
900 | } |
901 | } |
902 | |
903 | |
904 | === modified file 'tests/acceptance-tests/test_client_input.cpp' |
905 | --- tests/acceptance-tests/test_client_input.cpp 2017-02-22 14:36:45 +0000 |
906 | +++ tests/acceptance-tests/test_client_input.cpp 2017-03-01 16:08:29 +0000 |
907 | @@ -24,6 +24,8 @@ |
908 | #include "mir/scene/surface.h" |
909 | #include "mir/input/mir_touchpad_config.h" |
910 | #include "mir/input/mir_input_config.h" |
911 | +#include "mir/input/input_device.h" |
912 | +#include "mir/input/touchscreen_settings.h" |
913 | |
914 | #include "mir_test_framework/headless_in_process_server.h" |
915 | #include "mir_test_framework/fake_input_device.h" |
916 | @@ -95,6 +97,22 @@ |
917 | } |
918 | }; |
919 | |
920 | + |
921 | +template<typename SetCallback, typename Apply> |
922 | +void apply_and_wait_for_completion(MirConnection* connection, SetCallback set_callback, Apply apply) |
923 | +{ |
924 | + mt::Signal change_complete; |
925 | + set_callback(connection, |
926 | + [](MirConnection*, void* context) |
927 | + { |
928 | + static_cast<mt::Signal*>(context)->raise(); |
929 | + }, |
930 | + &change_complete |
931 | + ); |
932 | + apply(connection); |
933 | + ASSERT_TRUE(change_complete.wait_for(10s)); |
934 | +} |
935 | + |
936 | const int surface_width = 100; |
937 | const int surface_height = 100; |
938 | |
939 | @@ -356,6 +374,35 @@ |
940 | } |
941 | }; |
942 | |
943 | +struct TestClientInputWithTwoScreens : TestClientInput |
944 | +{ |
945 | + geom::Rectangle second_screen{{1000,0}, {200,400}}; |
946 | + |
947 | + int const width{second_screen.size.width.as_int()}; |
948 | + int const height{second_screen.size.height.as_int()}; |
949 | + float const touch_range = mtf::FakeInputDevice::maximum_touch_axis_value - mtf::FakeInputDevice::minimum_touch_axis_value + 1; |
950 | + float const scale_to_device_width = touch_range / width; |
951 | + float const scale_to_device_height = touch_range / height; |
952 | + |
953 | + void SetUp() override |
954 | + { |
955 | + initial_display_layout({screen_geometry, second_screen}); |
956 | + |
957 | + server.wrap_shell( |
958 | + [this](std::shared_ptr<mir::shell::Shell> const& wrapped) |
959 | + { |
960 | + shell = std::make_shared<mtf::PlacementApplyingShell>(wrapped, input_regions, positions); |
961 | + return shell; |
962 | + }); |
963 | + |
964 | + HeadlessInProcessServer::SetUp(); |
965 | + |
966 | + // make surface span over both outputs to test coordinates mapped into second ouput: |
967 | + positions[first] = |
968 | + geom::Rectangle{{0, 0}, {second_screen.bottom_right().x.as_int(), second_screen.bottom_right().y.as_int()}}; |
969 | + } |
970 | +}; |
971 | + |
972 | } |
973 | |
974 | TEST_F(TestClientInput, clients_receive_keys) |
975 | @@ -1246,19 +1293,14 @@ |
976 | mir_pointer_config_set_acceleration(pointer_config, mir_pointer_acceleration_adaptive); |
977 | mir_pointer_config_set_acceleration_bias(pointer_config, increased_acceleration); |
978 | |
979 | - mt::Signal changes_complete; |
980 | - mir_connection_set_input_config_change_callback( |
981 | + apply_and_wait_for_completion( |
982 | a_client.connection, |
983 | - [](MirConnection*, void* context) |
984 | + mir_connection_set_input_config_change_callback, |
985 | + [config](MirConnection* connection) |
986 | { |
987 | - static_cast<mt::Signal*>(context)->raise(); |
988 | - }, |
989 | - &changes_complete |
990 | - ); |
991 | - mir_connection_apply_session_input_config(a_client.connection, config); |
992 | - mir_input_config_release(config); |
993 | - |
994 | - EXPECT_TRUE(changes_complete.wait_for(10s)); |
995 | + mir_connection_apply_session_input_config(connection, config); |
996 | + mir_input_config_release(config); |
997 | + }); |
998 | |
999 | config = mir_connection_create_input_config(a_client.connection); |
1000 | mouse = get_mutable_device_with_capabilities(config, mir_input_device_capability_pointer); |
1001 | @@ -1353,19 +1395,14 @@ |
1002 | |
1003 | mir_pointer_config_set_acceleration(pointer_config, mir_pointer_acceleration_adaptive); |
1004 | |
1005 | - mt::Signal changes_complete; |
1006 | - mir_connection_set_input_config_change_callback( |
1007 | + apply_and_wait_for_completion( |
1008 | unfocused_client.connection, |
1009 | - [](MirConnection*, void* context) |
1010 | + mir_connection_set_input_config_change_callback, |
1011 | + [config](MirConnection* connection) |
1012 | { |
1013 | - static_cast<mt::Signal*>(context)->raise(); |
1014 | - }, |
1015 | - &changes_complete |
1016 | - ); |
1017 | - mir_connection_set_base_input_config(unfocused_client.connection, config); |
1018 | - mir_input_config_release(config); |
1019 | - |
1020 | - EXPECT_TRUE(changes_complete.wait_for(10s)); |
1021 | + mir_connection_set_base_input_config(connection, config); |
1022 | + mir_input_config_release(config); |
1023 | + }); |
1024 | |
1025 | config = mir_connection_create_input_config(unfocused_client.connection); |
1026 | mouse = get_mutable_device_with_capabilities(config, mir_input_device_capability_pointer); |
1027 | @@ -1414,19 +1451,14 @@ |
1028 | |
1029 | mir_pointer_config_set_acceleration(pointer_config, mir_pointer_acceleration_adaptive); |
1030 | |
1031 | - mt::Signal changes_complete; |
1032 | - mir_connection_set_input_config_change_callback( |
1033 | + apply_and_wait_for_completion( |
1034 | focused_client.connection, |
1035 | - [](MirConnection*, void* context) |
1036 | + mir_connection_set_input_config_change_callback, |
1037 | + [config](MirConnection* connection) |
1038 | { |
1039 | - static_cast<mt::Signal*>(context)->raise(); |
1040 | - }, |
1041 | - &changes_complete |
1042 | - ); |
1043 | - mir_connection_set_base_input_config(focused_client.connection, config); |
1044 | - mir_input_config_release(config); |
1045 | - |
1046 | - changes_complete.wait_for(10s); |
1047 | + mir_connection_set_base_input_config(connection, config); |
1048 | + mir_input_config_release(config); |
1049 | + }); |
1050 | |
1051 | config = mir_connection_create_input_config(focused_client.connection); |
1052 | mouse = get_mutable_device_with_capabilities(config, mir_input_device_capability_pointer); |
1053 | @@ -1511,3 +1543,145 @@ |
1054 | |
1055 | EXPECT_TRUE(wait_for_error.wait_for(10s)); |
1056 | } |
1057 | + |
1058 | +TEST_F(TestClientInput, touchscreen_config_is_mutable) |
1059 | +{ |
1060 | + wait_for_input_devices(); |
1061 | + |
1062 | + Client a_client(new_connection(), first); |
1063 | + auto config = mir_connection_create_input_config(a_client.connection); |
1064 | + auto touchscreen = get_mutable_device_with_capabilities(config, |
1065 | + mir_input_device_capability_touchscreen| |
1066 | + mir_input_device_capability_multitouch); |
1067 | + ASSERT_THAT(touchscreen, Ne(nullptr)); |
1068 | + |
1069 | + auto touchscreen_config = mir_input_device_get_mutable_touchscreen_config(touchscreen); |
1070 | + |
1071 | + mir_touchscreen_config_set_output_id(touchscreen_config, 4); |
1072 | + mir_touchscreen_config_set_mapping_mode(touchscreen_config, mir_touchscreen_mapping_mode_to_display_wall); |
1073 | + |
1074 | + EXPECT_THAT(mir_touchscreen_config_get_mapping_mode(touchscreen_config), Eq(mir_touchscreen_mapping_mode_to_display_wall)); |
1075 | + EXPECT_THAT(mir_touchscreen_config_get_output_id(touchscreen_config), Eq(4)); |
1076 | + |
1077 | + mir_input_config_release(config); |
1078 | +} |
1079 | + |
1080 | +TEST_F(TestClientInputWithTwoScreens, touchscreen_can_be_mapped_to_second_output) |
1081 | +{ |
1082 | + wait_for_input_devices(); |
1083 | + uint32_t const second_output = 2; |
1084 | + int const touch_x = 10; |
1085 | + int const touch_y = 10; |
1086 | + |
1087 | + int const expected_x = touch_x + second_screen.top_left.x.as_int(); |
1088 | + int const expected_y = touch_y + second_screen.top_left.y.as_int(); |
1089 | + |
1090 | + mt::Signal touchscreen_ready; |
1091 | + fake_touch_screen->on_new_configuration_do( |
1092 | + [&touchscreen_ready, second_output](mi::InputDevice const& dev) |
1093 | + { |
1094 | + auto ts = dev.get_touchscreen_settings(); |
1095 | + if (ts.is_set() && ts.value().output_id == second_output) |
1096 | + touchscreen_ready.raise(); |
1097 | + }); |
1098 | + |
1099 | + Client client(new_connection(), first); |
1100 | + auto config = mir_connection_create_input_config(client.connection); |
1101 | + auto touchscreen = get_mutable_device_with_capabilities(config, |
1102 | + mir_input_device_capability_touchscreen| |
1103 | + mir_input_device_capability_multitouch); |
1104 | + auto touchscreen_config = mir_input_device_get_mutable_touchscreen_config(touchscreen); |
1105 | + mir_touchscreen_config_set_output_id(touchscreen_config, second_output); |
1106 | + mir_touchscreen_config_set_mapping_mode(touchscreen_config, mir_touchscreen_mapping_mode_to_output); |
1107 | + |
1108 | + apply_and_wait_for_completion( |
1109 | + client.connection, |
1110 | + mir_connection_set_input_config_change_callback, |
1111 | + [config](MirConnection* connection) |
1112 | + { |
1113 | + mir_connection_set_base_input_config(connection, config); |
1114 | + mir_input_config_release(config); |
1115 | + }); |
1116 | + |
1117 | + EXPECT_TRUE(touchscreen_ready.wait_for(10s)); |
1118 | + EXPECT_CALL(client, handle_input(mt::TouchEvent(expected_x, expected_y))) |
1119 | + .WillOnce(mt::WakeUp(&client.all_events_received)); |
1120 | + fake_touch_screen->emit_event(mis::a_touch_event() |
1121 | + .at_position({touch_x*scale_to_device_width, touch_y*scale_to_device_height})); |
1122 | + |
1123 | + EXPECT_TRUE(client.all_events_received.wait_for(10s)); |
1124 | +} |
1125 | + |
1126 | +TEST_F(TestClientInputWithTwoScreens, touchscreen_mapped_to_deactivated_output_is_filtered_out) |
1127 | +{ |
1128 | + wait_for_input_devices(); |
1129 | + uint32_t const second_output = 2; |
1130 | + int const touch_x = 10; |
1131 | + int const touch_y = 10; |
1132 | + |
1133 | + int const expected_x = second_screen.top_left.x.as_int() + touch_x; |
1134 | + int const expected_y = second_screen.top_left.y.as_int() + touch_y; |
1135 | + |
1136 | + Client client(new_connection(), first); |
1137 | + auto display_config = mir_connection_create_display_configuration(client.connection); |
1138 | + auto second_output_ptr = mir_display_config_get_mutable_output(display_config, 1); |
1139 | + mir_output_set_power_mode(second_output_ptr, mir_power_mode_off); |
1140 | + |
1141 | + apply_and_wait_for_completion( |
1142 | + client.connection, |
1143 | + mir_connection_set_display_config_change_callback, |
1144 | + [display_config](MirConnection* con) |
1145 | + { |
1146 | + mir_connection_preview_base_display_configuration(con, display_config, 10); |
1147 | + }); |
1148 | + |
1149 | + apply_and_wait_for_completion( |
1150 | + client.connection, |
1151 | + mir_connection_set_display_config_change_callback, |
1152 | + [display_config](MirConnection* con) |
1153 | + { |
1154 | + mir_connection_confirm_base_display_configuration(con, display_config); |
1155 | + mir_display_config_release(display_config); |
1156 | + }); |
1157 | + |
1158 | + display_config = mir_connection_create_display_configuration(client.connection); |
1159 | + ASSERT_THAT(mir_output_get_power_mode(mir_display_config_get_output(display_config, 1)), Eq(mir_power_mode_off)); |
1160 | + mir_display_config_release(display_config); |
1161 | + |
1162 | + mt::Signal touchscreen_ready; |
1163 | + fake_touch_screen->on_new_configuration_do( |
1164 | + [&touchscreen_ready, second_output](mi::InputDevice const& dev) |
1165 | + { |
1166 | + auto ts = dev.get_touchscreen_settings(); |
1167 | + if (ts.is_set() |
1168 | + && ts.value().output_id == second_output |
1169 | + && ts.value().mapping_mode == mir_touchscreen_mapping_mode_to_output) |
1170 | + touchscreen_ready.raise(); |
1171 | + }); |
1172 | + |
1173 | + auto config = mir_connection_create_input_config(client.connection); |
1174 | + auto touchscreen = get_mutable_device_with_capabilities(config, |
1175 | + mir_input_device_capability_touchscreen| |
1176 | + mir_input_device_capability_multitouch); |
1177 | + auto touchscreen_config = mir_input_device_get_mutable_touchscreen_config(touchscreen); |
1178 | + |
1179 | + mir_touchscreen_config_set_output_id(touchscreen_config, second_output); |
1180 | + mir_touchscreen_config_set_mapping_mode(touchscreen_config, mir_touchscreen_mapping_mode_to_output); |
1181 | + |
1182 | + apply_and_wait_for_completion( |
1183 | + client.connection, |
1184 | + mir_connection_set_input_config_change_callback, |
1185 | + [config](MirConnection* connection) |
1186 | + { |
1187 | + mir_connection_set_base_input_config(connection, config); |
1188 | + mir_input_config_release(config); |
1189 | + }); |
1190 | + |
1191 | + EXPECT_TRUE(touchscreen_ready.wait_for(10s)); |
1192 | + ON_CALL(client, handle_input(mt::TouchEvent(expected_x, expected_y))) |
1193 | + .WillByDefault(mt::WakeUp(&client.all_events_received)); |
1194 | + fake_touch_screen->emit_event(mis::a_touch_event() |
1195 | + .at_position({touch_x*scale_to_device_width, touch_y*scale_to_device_height})); |
1196 | + |
1197 | + EXPECT_FALSE(client.all_events_received.wait_for(5s)); |
1198 | +} |
1199 | |
1200 | === modified file 'tests/include/mir/test/doubles/mock_input_seat.h' |
1201 | --- tests/include/mir/test/doubles/mock_input_seat.h 2017-01-18 02:29:37 +0000 |
1202 | +++ tests/include/mir/test/doubles/mock_input_seat.h 2017-03-01 16:08:29 +0000 |
1203 | @@ -21,6 +21,7 @@ |
1204 | |
1205 | #include "mir/input/seat.h" |
1206 | #include "mir/input/device.h" |
1207 | +#include "mir/input/input_sink.h" |
1208 | #include "mir/test/gmock_fixes.h" |
1209 | |
1210 | #include <gmock/gmock.h> |
1211 | @@ -36,13 +37,14 @@ |
1212 | MOCK_METHOD1(add_device, void(input::Device const& device)); |
1213 | MOCK_METHOD1(remove_device, void(input::Device const& device)); |
1214 | MOCK_METHOD1(dispatch_event, void(MirEvent& event)); |
1215 | - MOCK_METHOD1(get_rectangle_for, geometry::Rectangle(input::Device const& dev)); |
1216 | MOCK_METHOD0(create_device_state, mir::EventUPtr()); |
1217 | MOCK_METHOD2(set_key_state, void(input::Device const&, std::vector<uint32_t> const&)); |
1218 | MOCK_METHOD2(set_pointer_state, void (input::Device const&, MirPointerButtons)); |
1219 | MOCK_METHOD2(set_cursor_position, void (float, float)); |
1220 | MOCK_METHOD1(set_confinement_regions, void(geometry::Rectangles const&)); |
1221 | MOCK_METHOD0(reset_confinement_regions, void()); |
1222 | + MOCK_CONST_METHOD0(bounding_rectangle, geometry::Rectangle()); |
1223 | + MOCK_CONST_METHOD1(output_info, input::OutputInfo(uint32_t)); |
1224 | }; |
1225 | } |
1226 | } |
1227 | |
1228 | === modified file 'tests/include/mir/test/doubles/mock_input_sink.h' |
1229 | --- tests/include/mir/test/doubles/mock_input_sink.h 2016-07-07 09:59:19 +0000 |
1230 | +++ tests/include/mir/test/doubles/mock_input_sink.h 2017-03-01 16:08:29 +0000 |
1231 | @@ -35,6 +35,7 @@ |
1232 | MOCK_METHOD1(handle_input, void(MirEvent&)); |
1233 | MOCK_METHOD1(confine_pointer, void(mir::geometry::Point&)); |
1234 | MOCK_CONST_METHOD0(bounding_rectangle, mir::geometry::Rectangle()); |
1235 | + MOCK_CONST_METHOD1(output_info, mir::input::OutputInfo(uint32_t)); |
1236 | MOCK_METHOD1(key_state, void(std::vector<uint32_t> const&)); |
1237 | MOCK_METHOD1(pointer_state, void(MirPointerButtons)); |
1238 | }; |
1239 | |
1240 | === modified file 'tests/integration-tests/input/test_single_seat_setup.cpp' |
1241 | --- tests/integration-tests/input/test_single_seat_setup.cpp 2017-02-15 07:38:33 +0000 |
1242 | +++ tests/integration-tests/input/test_single_seat_setup.cpp 2017-03-01 16:08:29 +0000 |
1243 | @@ -24,7 +24,6 @@ |
1244 | #include "mir/test/doubles/mock_input_device.h" |
1245 | #include "mir/test/doubles/mock_input_device_observer.h" |
1246 | #include "mir/test/doubles/mock_input_dispatcher.h" |
1247 | -#include "mir/test/doubles/mock_input_region.h" |
1248 | #include "mir/test/doubles/mock_touch_visualizer.h" |
1249 | #include "mir/test/doubles/mock_cursor_listener.h" |
1250 | #include "mir/test/doubles/mock_input_manager.h" |
1251 | @@ -36,10 +35,12 @@ |
1252 | #include "mir/test/event_matchers.h" |
1253 | #include "mir/test/doubles/advanceable_clock.h" |
1254 | #include "mir/test/fake_shared.h" |
1255 | +#include "mir/test/doubles/stub_display_configuration.h" |
1256 | |
1257 | #include "mir/dispatch/multiplexing_dispatchable.h" |
1258 | #include "mir/cookie/authority.h" |
1259 | #include "mir/graphics/buffer.h" |
1260 | +#include "mir/graphics/display_configuration_observer.h" |
1261 | |
1262 | #include "mir/input/device.h" |
1263 | #include "mir/input/xkb_mapper.h" |
1264 | @@ -73,17 +74,47 @@ |
1265 | } |
1266 | } |
1267 | |
1268 | +namespace |
1269 | +{ |
1270 | + |
1271 | MATCHER_P(DeviceMatches, device_info, "") |
1272 | { |
1273 | return arg.name() == device_info.name && |
1274 | arg.unique_id() == device_info.unique_id; |
1275 | } |
1276 | |
1277 | +struct FakeDisplayConfigurationObserverRegistrar : mir::ObserverRegistrar<mir::graphics::DisplayConfigurationObserver> |
1278 | +{ |
1279 | + using Observer = mir::graphics::DisplayConfigurationObserver; |
1280 | + mtd::StubDisplayConfig output{{geom::Rectangle{{0, 0}, {100, 100}}}}; |
1281 | + void register_interest(std::weak_ptr<Observer> const& obs) override |
1282 | + { |
1283 | + observer = obs; |
1284 | + auto o = observer.lock(); |
1285 | + o->initial_configuration(mt::fake_shared(output)); |
1286 | + } |
1287 | + void register_interest( |
1288 | + std::weak_ptr<Observer> const& obs, |
1289 | + mir::Executor&) override |
1290 | + { |
1291 | + observer = obs; |
1292 | + } |
1293 | + void unregister_interest(Observer const&) override |
1294 | + { |
1295 | + observer.reset(); |
1296 | + } |
1297 | + void update_output(geom::Size const& output_size) |
1298 | + { |
1299 | + output.outputs[0].modes[0].size = output_size; |
1300 | + auto o = observer.lock(); |
1301 | + o->configuration_applied(mt::fake_shared(output)); |
1302 | + } |
1303 | + std::weak_ptr<Observer> observer; |
1304 | +}; |
1305 | struct SingleSeatInputDeviceHubSetup : ::testing::Test |
1306 | { |
1307 | mtd::TriggeredMainLoop observer_loop; |
1308 | NiceMock<mtd::MockInputDispatcher> mock_dispatcher; |
1309 | - NiceMock<mtd::MockInputRegion> mock_region; |
1310 | std::shared_ptr<mir::cookie::Authority> cookie_authority = mir::cookie::Authority::create(); |
1311 | NiceMock<mtd::MockCursorListener> mock_cursor_listener; |
1312 | NiceMock<mtd::MockTouchVisualizer> mock_visualizer; |
1313 | @@ -95,12 +126,14 @@ |
1314 | mtd::MockInputManager mock_input_manager; |
1315 | mtd::StubSessionContainer stub_session_container; |
1316 | ms::BroadcastingSessionEventSink session_event_sink; |
1317 | - mi::BasicSeat seat{ |
1318 | - mt::fake_shared(mock_dispatcher),mt::fake_shared(mock_visualizer), mt::fake_shared(mock_cursor_listener), |
1319 | - mt::fake_shared(mock_region), mt::fake_shared(key_mapper), mt::fake_shared(clock), mt::fake_shared(mock_seat_observer)}; |
1320 | - mi::DefaultInputDeviceHub hub{ |
1321 | - mt::fake_shared(seat), mt::fake_shared(multiplexer), mt::fake_shared(observer_loop), |
1322 | - cookie_authority, mt::fake_shared(key_mapper), mt::fake_shared(mock_status_listener)}; |
1323 | + FakeDisplayConfigurationObserverRegistrar display_config; |
1324 | + mi::BasicSeat seat{mt::fake_shared(mock_dispatcher), mt::fake_shared(mock_visualizer), |
1325 | + mt::fake_shared(mock_cursor_listener), mt::fake_shared(display_config), |
1326 | + mt::fake_shared(key_mapper), mt::fake_shared(clock), |
1327 | + mt::fake_shared(mock_seat_observer)}; |
1328 | + mi::DefaultInputDeviceHub hub{mt::fake_shared(seat), mt::fake_shared(multiplexer), |
1329 | + mt::fake_shared(observer_loop), cookie_authority, |
1330 | + mt::fake_shared(key_mapper), mt::fake_shared(mock_status_listener)}; |
1331 | NiceMock<mtd::MockInputDeviceObserver> mock_observer; |
1332 | mi::ConfigChanger changer{ |
1333 | mt::fake_shared(mock_input_manager), |
1334 | @@ -130,6 +163,8 @@ |
1335 | } |
1336 | }; |
1337 | |
1338 | +} |
1339 | + |
1340 | TEST_F(SingleSeatInputDeviceHubSetup, input_sink_posts_events_to_input_dispatcher) |
1341 | { |
1342 | mi::InputSink* sink; |
1343 | @@ -198,13 +233,12 @@ |
1344 | sink->handle_input(*touch_event_4); |
1345 | } |
1346 | |
1347 | - |
1348 | TEST_F(SingleSeatInputDeviceHubSetup, tracks_pointer_position) |
1349 | { |
1350 | geom::Point first{10,10}, second{20,20}, third{10,30}; |
1351 | - EXPECT_CALL(mock_region, confine(first)); |
1352 | - EXPECT_CALL(mock_region, confine(second)); |
1353 | - EXPECT_CALL(mock_region, confine(third)); |
1354 | + EXPECT_CALL(mock_cursor_listener, cursor_moved_to(first.x.as_int(), first.y.as_int())); |
1355 | + EXPECT_CALL(mock_cursor_listener, cursor_moved_to(second.x.as_int(), second.y.as_int())); |
1356 | + EXPECT_CALL(mock_cursor_listener, cursor_moved_to(third.x.as_int(), third.y.as_int())); |
1357 | |
1358 | mi::InputSink* sink; |
1359 | mi::EventBuilder* builder; |
1360 | @@ -223,9 +257,8 @@ |
1361 | TEST_F(SingleSeatInputDeviceHubSetup, confines_pointer_movement) |
1362 | { |
1363 | geom::Point confined_pos{10, 18}; |
1364 | + display_config.update_output(geom::Size{confined_pos.x.as_int() + 1, confined_pos.y.as_int() + 1}); |
1365 | |
1366 | - ON_CALL(mock_region,confine(_)) |
1367 | - .WillByDefault(SetArgReferee<0>(confined_pos)); |
1368 | EXPECT_CALL(mock_cursor_listener, cursor_moved_to(confined_pos.x.as_int(), confined_pos.y.as_int())).Times(2); |
1369 | |
1370 | mi::InputSink* sink; |
1371 | |
1372 | === modified file 'tests/mir_test_framework/fake_input_device_impl.cpp' |
1373 | --- tests/mir_test_framework/fake_input_device_impl.cpp 2017-02-03 01:10:42 +0000 |
1374 | +++ tests/mir_test_framework/fake_input_device_impl.cpp 2017-03-01 16:08:29 +0000 |
1375 | @@ -110,14 +110,27 @@ |
1376 | }); |
1377 | } |
1378 | |
1379 | +void mtf::FakeInputDeviceImpl::on_new_configuration_do(std::function<void(mir::input::InputDevice const& device)> callback) |
1380 | +{ |
1381 | + device->set_apply_settings_callback(callback); |
1382 | +} |
1383 | +void mtf::FakeInputDeviceImpl::InputDevice::set_apply_settings_callback(std::function<void(mir::input::InputDevice const&)> const& callback) |
1384 | +{ |
1385 | + std::lock_guard<std::mutex> lock(config_callback_mutex); |
1386 | + this->callback = callback; |
1387 | +} |
1388 | |
1389 | mtf::FakeInputDeviceImpl::InputDevice::InputDevice(mi::InputDeviceInfo const& info, |
1390 | std::shared_ptr<mir::dispatch::Dispatchable> const& dispatchable) |
1391 | - : info(info), queue{dispatchable}, buttons{0} |
1392 | + : info(info), queue{dispatchable}, buttons{0}, callback([](mir::input::InputDevice const&){}) |
1393 | { |
1394 | // the default setup results in a direct mapping of input velocity to output velocity. |
1395 | settings.acceleration = mir_pointer_acceleration_none; |
1396 | settings.cursor_acceleration_bias = 0.0; |
1397 | + |
1398 | + // add default setup for touchscreen.. |
1399 | + if (contains(info.capabilities, mi::DeviceCapability::touchscreen)) |
1400 | + touchscreen = mi::TouchscreenSettings{}; |
1401 | } |
1402 | |
1403 | void mtf::FakeInputDeviceImpl::InputDevice::synthesize_events(synthesis::KeyParameters const& key_params) |
1404 | @@ -211,13 +224,15 @@ |
1405 | float abs_x = touch.abs_x; |
1406 | float abs_y = touch.abs_y; |
1407 | map_touch_coordinates(abs_x, abs_y); |
1408 | - // those values would need scaling too as soon as they can be controlled by the caller |
1409 | - |
1410 | - auto touch_event = builder->touch_event( |
1411 | - event_time, |
1412 | - {{MirTouchId{1}, touch_action, mir_touch_tooltype_finger, abs_x, abs_y, 1.0f, 8.0f, 5.0f, 0.0f}}); |
1413 | - |
1414 | - sink->handle_input(*touch_event); |
1415 | + |
1416 | + if (is_output_active()) |
1417 | + { |
1418 | + auto touch_event = builder->touch_event( |
1419 | + event_time, |
1420 | + {{MirTouchId{1}, touch_action, mir_touch_tooltype_finger, abs_x, abs_y, 1.0f, 8.0f, 5.0f, 0.0f}}); |
1421 | + |
1422 | + sink->handle_input(*touch_event); |
1423 | + } |
1424 | } |
1425 | |
1426 | mir::optional_value<mi::PointerSettings> mtf::FakeInputDeviceImpl::InputDevice::get_pointer_settings() const |
1427 | @@ -235,6 +250,7 @@ |
1428 | if (!contains(info.capabilities, mi::DeviceCapability::pointer)) |
1429 | return; |
1430 | this->settings = settings; |
1431 | + trigger_callback(); |
1432 | } |
1433 | |
1434 | mir::optional_value<mi::TouchpadSettings> mtf::FakeInputDeviceImpl::InputDevice::get_touchpad_settings() const |
1435 | @@ -250,6 +266,17 @@ |
1436 | { |
1437 | // Not applicable for configuration since FakeInputDevice just |
1438 | // forwards already interpreted events. |
1439 | + trigger_callback(); |
1440 | +} |
1441 | + |
1442 | +void mtf::FakeInputDeviceImpl::InputDevice::trigger_callback() const |
1443 | +{ |
1444 | + decltype(callback) stored_callback; |
1445 | + { |
1446 | + std::lock_guard<std::mutex> lock(config_callback_mutex); |
1447 | + stored_callback = callback; |
1448 | + } |
1449 | + stored_callback(*this); |
1450 | } |
1451 | |
1452 | mir::optional_value<mi::TouchscreenSettings> mtf::FakeInputDeviceImpl::InputDevice::get_touchscreen_settings() const |
1453 | @@ -267,17 +294,22 @@ |
1454 | if (!contains(info.capabilities, mi::DeviceCapability::touchscreen)) |
1455 | return; |
1456 | this->touchscreen = new_settings; |
1457 | + |
1458 | + trigger_callback(); |
1459 | } |
1460 | |
1461 | void mtf::FakeInputDeviceImpl::InputDevice::map_touch_coordinates(float& x, float& y) |
1462 | { |
1463 | - // TODO take orientation of input sink into account? |
1464 | - auto area = sink->bounding_rectangle(); |
1465 | + auto info = get_output_info(); |
1466 | auto touch_range = FakeInputDevice::maximum_touch_axis_value - FakeInputDevice::minimum_touch_axis_value + 1; |
1467 | - auto x_scale = area.size.width.as_int() / float(touch_range); |
1468 | - auto y_scale = area.size.height.as_int() / float(touch_range); |
1469 | - x = (x - float(FakeInputDevice::minimum_touch_axis_value))*x_scale + area.top_left.x.as_int(); |
1470 | - y = (y - float(FakeInputDevice::minimum_touch_axis_value))*y_scale + area.top_left.y.as_int(); |
1471 | + auto const width = info.output_size.width.as_int(); |
1472 | + auto const height = info.output_size.height.as_int(); |
1473 | + auto x_scale = width / float(touch_range); |
1474 | + auto y_scale = height / float(touch_range); |
1475 | + x = (x - float(FakeInputDevice::minimum_touch_axis_value))*x_scale; |
1476 | + y = (y - float(FakeInputDevice::minimum_touch_axis_value))*y_scale; |
1477 | + |
1478 | + info.transform_to_scene(x, y); |
1479 | } |
1480 | |
1481 | void mtf::FakeInputDeviceImpl::InputDevice::start(mi::InputSink* destination, mi::EventBuilder* event_builder) |
1482 | @@ -293,3 +325,33 @@ |
1483 | builder = nullptr; |
1484 | mtf::StubInputPlatform::unregister_dispatchable(queue); |
1485 | } |
1486 | + |
1487 | +mi::OutputInfo mtf::FakeInputDeviceImpl::InputDevice::get_output_info() const |
1488 | +{ |
1489 | + if (touchscreen.mapping_mode == mir_touchscreen_mapping_mode_to_output) |
1490 | + { |
1491 | + return sink->output_info(touchscreen.output_id); |
1492 | + } |
1493 | + else |
1494 | + { |
1495 | + auto scene_bbox = sink->bounding_rectangle(); |
1496 | + return mi::OutputInfo( |
1497 | + true, |
1498 | + scene_bbox.size, |
1499 | + mi::OutputInfo::Matrix{{1.0f, 0.0f, float(scene_bbox.top_left.x.as_int()), |
1500 | + 0.0f, 1.0f, float(scene_bbox.top_left.y.as_int())}}); |
1501 | + } |
1502 | +} |
1503 | + |
1504 | +bool mtf::FakeInputDeviceImpl::InputDevice::is_output_active() const |
1505 | +{ |
1506 | + if (!sink) |
1507 | + return false; |
1508 | + |
1509 | + if (touchscreen.mapping_mode == mir_touchscreen_mapping_mode_to_output) |
1510 | + { |
1511 | + auto output = sink->output_info(touchscreen.output_id); |
1512 | + return output.active; |
1513 | + } |
1514 | + return true; |
1515 | +} |
1516 | |
1517 | === modified file 'tests/mir_test_framework/fake_input_device_impl.h' |
1518 | --- tests/mir_test_framework/fake_input_device_impl.h 2017-02-03 01:10:42 +0000 |
1519 | +++ tests/mir_test_framework/fake_input_device_impl.h 2017-03-01 16:08:29 +0000 |
1520 | @@ -27,8 +27,15 @@ |
1521 | #include "mir/input/input_device_info.h" |
1522 | #include "mir/geometry/point.h" |
1523 | |
1524 | +#include <mutex> |
1525 | +#include <functional> |
1526 | + |
1527 | namespace mir |
1528 | { |
1529 | +namespace input |
1530 | +{ |
1531 | +class OutputInfo; |
1532 | +} |
1533 | namespace dispatch |
1534 | { |
1535 | class ActionQueue; |
1536 | @@ -50,6 +57,7 @@ |
1537 | void emit_touch_sequence(std::function<mir::input::synthesis::TouchParameters(int)> const& event_generator, |
1538 | int count, |
1539 | std::chrono::duration<double> delay) override; |
1540 | + virtual void on_new_configuration_do(std::function<void(mir::input::InputDevice const& device)> callback) override; |
1541 | |
1542 | private: |
1543 | class InputDevice : public mir::input::InputDevice |
1544 | @@ -76,11 +84,15 @@ |
1545 | void apply_settings(mir::input::TouchpadSettings const& settings) override; |
1546 | mir::optional_value<mir::input::TouchscreenSettings> get_touchscreen_settings() const override; |
1547 | void apply_settings(mir::input::TouchscreenSettings const& settings) override; |
1548 | + void set_apply_settings_callback(std::function<void(mir::input::InputDevice const&)> const& callback); |
1549 | |
1550 | private: |
1551 | MirPointerAction update_buttons(mir::input::synthesis::EventAction action, MirPointerButton button); |
1552 | void update_position(int rel_x, int rel_y); |
1553 | void map_touch_coordinates(float& x, float& y); |
1554 | + mir::input::OutputInfo get_output_info() const; |
1555 | + bool is_output_active() const; |
1556 | + void trigger_callback() const; |
1557 | |
1558 | mir::input::InputSink* sink{nullptr}; |
1559 | mir::input::EventBuilder* builder{nullptr}; |
1560 | @@ -90,6 +102,8 @@ |
1561 | MirPointerButtons buttons; |
1562 | mir::input::PointerSettings settings; |
1563 | mir::input::TouchscreenSettings touchscreen; |
1564 | + mutable std::mutex config_callback_mutex; |
1565 | + std::function<void(mir::input::InputDevice const&)> callback; |
1566 | }; |
1567 | std::shared_ptr<mir::dispatch::ActionQueue> queue; |
1568 | std::shared_ptr<InputDevice> device; |
1569 | |
1570 | === modified file 'tests/unit-tests/input/evdev/test_libinput_device.cpp' |
1571 | --- tests/unit-tests/input/evdev/test_libinput_device.cpp 2017-01-18 02:29:37 +0000 |
1572 | +++ tests/unit-tests/input/evdev/test_libinput_device.cpp 2017-03-01 16:08:29 +0000 |
1573 | @@ -31,6 +31,7 @@ |
1574 | #include "mir/test/event_matchers.h" |
1575 | #include "mir/test/doubles/mock_libinput.h" |
1576 | #include "mir/test/doubles/mock_input_seat.h" |
1577 | +#include "mir/test/doubles/mock_input_sink.h" |
1578 | #include "mir/test/gmock_fixes.h" |
1579 | #include "mir/test/fake_shared.h" |
1580 | #include "mir/udev/wrapper.h" |
1581 | @@ -63,20 +64,7 @@ |
1582 | }; |
1583 | |
1584 | using namespace ::testing; |
1585 | - |
1586 | -struct MockInputSink : mi::InputSink |
1587 | -{ |
1588 | - MockInputSink() |
1589 | - { |
1590 | - ON_CALL(*this, bounding_rectangle()) |
1591 | - .WillByDefault(Return(geom::Rectangle({0,0}, {100,100}))); |
1592 | - } |
1593 | - MOCK_METHOD1(handle_input,void(MirEvent &)); |
1594 | - MOCK_METHOD1(confine_pointer, void(geom::Point&)); |
1595 | - MOCK_CONST_METHOD0(bounding_rectangle, geom::Rectangle()); |
1596 | - MOCK_METHOD1(key_state, void(std::vector<uint32_t> const&)); |
1597 | - MOCK_METHOD1(pointer_state, void(MirPointerButtons)); |
1598 | -}; |
1599 | +using Matrix = mi::OutputInfo::Matrix; |
1600 | |
1601 | struct MockEventBuilder : mi::EventBuilder |
1602 | { |
1603 | @@ -128,7 +116,7 @@ |
1604 | struct LibInputDevice : public ::testing::Test |
1605 | { |
1606 | mtf::LibInputEnvironment env; |
1607 | - ::testing::NiceMock<MockInputSink> mock_sink; |
1608 | + ::testing::NiceMock<mtd::MockInputSink> mock_sink; |
1609 | ::testing::NiceMock<MockEventBuilder> mock_builder; |
1610 | std::shared_ptr<libinput> lib; |
1611 | |
1612 | @@ -145,6 +133,8 @@ |
1613 | |
1614 | LibInputDevice() |
1615 | { |
1616 | + ON_CALL(mock_sink, bounding_rectangle()) |
1617 | + .WillByDefault(Return(geom::Rectangle({0,0}, {100,100}))); |
1618 | lib = mie::make_libinput(fake_udev); |
1619 | } |
1620 | |
1621 | @@ -268,6 +258,16 @@ |
1622 | { |
1623 | libinput_device*const fake_device = setup_touchscreen(); |
1624 | mie::LibInputDevice touch_screen{mir::report::null_input_report(), mie::make_libinput_device(lib, fake_device)}; |
1625 | + |
1626 | + const float x = 10; |
1627 | + const float y = 5; |
1628 | + const uint32_t output_id = 2; |
1629 | + const float output_x_pos = 20; |
1630 | + const float output_y_pos = 50; |
1631 | + const float screen_x_pos = 70; |
1632 | + const float screen_y_pos = 30; |
1633 | + const int width = 100; |
1634 | + const int height = 200; |
1635 | }; |
1636 | |
1637 | struct LibInputDeviceOnTouchpad : public LibInputDevice |
1638 | @@ -777,3 +777,169 @@ |
1639 | lib.reset(); |
1640 | device_ptr.reset(); |
1641 | } |
1642 | + |
1643 | +TEST_F(LibInputDeviceOnTouchScreen, device_maps_to_selected_output) |
1644 | +{ |
1645 | + const float x = 30; |
1646 | + const float y = 20; |
1647 | + const uint32_t output_id = 2; |
1648 | + const float output_x_pos = 20; |
1649 | + const float output_y_pos = 50; |
1650 | + const int width = 100; |
1651 | + const int height = 200; |
1652 | + |
1653 | + EXPECT_CALL(mock_sink, bounding_rectangle()) |
1654 | + .Times(0); |
1655 | + EXPECT_CALL(mock_sink, output_info(output_id)) |
1656 | + .WillRepeatedly(Return( |
1657 | + mi::OutputInfo{ |
1658 | + true, |
1659 | + geom::Size{width, height}, |
1660 | + Matrix{{1.0f, 0.0f, output_x_pos, |
1661 | + 0.0f, 1.0f, output_y_pos}}})); |
1662 | + EXPECT_CALL(mock_sink, |
1663 | + handle_input( |
1664 | + mt::TouchContact( |
1665 | + 0, |
1666 | + mir_touch_action_down, |
1667 | + x + output_x_pos, |
1668 | + y + output_y_pos))) |
1669 | + .Times(1); |
1670 | + |
1671 | + touch_screen.apply_settings(mi::TouchscreenSettings{output_id, mir_touchscreen_mapping_mode_to_output}); |
1672 | + touch_screen.start(&mock_sink, &mock_builder); |
1673 | + |
1674 | + env.mock_libinput.setup_touch_event(fake_device, LIBINPUT_EVENT_TOUCH_DOWN, event_time_1, 0, x, y, 0, 0, 0, 0); |
1675 | + env.mock_libinput.setup_touch_frame(fake_device, event_time_1); |
1676 | + |
1677 | + process_events(touch_screen); |
1678 | +} |
1679 | + |
1680 | +TEST_F(LibInputDeviceOnTouchScreen, device_maps_to_left_rotated_output) |
1681 | +{ |
1682 | + const float x = 10; |
1683 | + const float y = 5; |
1684 | + const uint32_t output_id = 2; |
1685 | + const float output_x_pos = 20; |
1686 | + const float output_y_pos = 50; |
1687 | + const int width = 100; |
1688 | + const int height = 200; |
1689 | + |
1690 | + EXPECT_CALL(mock_sink, bounding_rectangle()) |
1691 | + .Times(0); |
1692 | + EXPECT_CALL(mock_sink, output_info(output_id)) |
1693 | + .WillRepeatedly(Return( |
1694 | + mi::OutputInfo{ |
1695 | + true, |
1696 | + geom::Size{width, height}, |
1697 | + Matrix{{0.0f, 1.0f, output_x_pos, |
1698 | + -1.0f, 0.0f, width + output_y_pos}}})); |
1699 | + EXPECT_CALL(mock_sink, |
1700 | + handle_input( |
1701 | + mt::TouchContact( |
1702 | + 0, |
1703 | + mir_touch_action_down, |
1704 | + output_x_pos + y, |
1705 | + output_y_pos + width - x))) |
1706 | + .Times(1); |
1707 | + |
1708 | + touch_screen.apply_settings(mi::TouchscreenSettings{output_id, mir_touchscreen_mapping_mode_to_output}); |
1709 | + touch_screen.start(&mock_sink, &mock_builder); |
1710 | + |
1711 | + env.mock_libinput.setup_touch_event(fake_device, LIBINPUT_EVENT_TOUCH_DOWN, event_time_1, 0, x, y, 0, 0, 0, 0); |
1712 | + env.mock_libinput.setup_touch_frame(fake_device, event_time_1); |
1713 | + |
1714 | + process_events(touch_screen); |
1715 | +} |
1716 | + |
1717 | +TEST_F(LibInputDeviceOnTouchScreen, device_maps_to_right_rotated_output) |
1718 | +{ |
1719 | + EXPECT_CALL(mock_sink, bounding_rectangle()) |
1720 | + .Times(0); |
1721 | + EXPECT_CALL(mock_sink, output_info(output_id)) |
1722 | + .WillRepeatedly(Return( |
1723 | + mi::OutputInfo{ |
1724 | + true, |
1725 | + geom::Size{width, height}, |
1726 | + Matrix{{0.0f, -1.0f, height + output_x_pos, |
1727 | + 1.0f, 0.0f, output_y_pos}}})); |
1728 | + EXPECT_CALL(mock_sink, |
1729 | + handle_input( |
1730 | + mt::TouchContact( |
1731 | + 0, |
1732 | + mir_touch_action_down, |
1733 | + output_x_pos + height - y, |
1734 | + output_y_pos + x))) |
1735 | + .Times(1); |
1736 | + |
1737 | + touch_screen.apply_settings(mi::TouchscreenSettings{output_id, mir_touchscreen_mapping_mode_to_output}); |
1738 | + touch_screen.start(&mock_sink, &mock_builder); |
1739 | + |
1740 | + env.mock_libinput.setup_touch_event(fake_device, LIBINPUT_EVENT_TOUCH_DOWN, event_time_1, 0, x, y, 0, 0, 0, 0); |
1741 | + env.mock_libinput.setup_touch_frame(fake_device, event_time_1); |
1742 | + |
1743 | + process_events(touch_screen); |
1744 | +} |
1745 | + |
1746 | +TEST_F(LibInputDeviceOnTouchScreen, device_maps_to_inverted_output) |
1747 | +{ |
1748 | + EXPECT_CALL(mock_sink, bounding_rectangle()) |
1749 | + .Times(0); |
1750 | + EXPECT_CALL(mock_sink, output_info(output_id)) |
1751 | + .WillRepeatedly(Return( |
1752 | + mi::OutputInfo{ |
1753 | + true, |
1754 | + geom::Size{width, height}, |
1755 | + Matrix{{-1.0f, 0.0f, width + output_x_pos, |
1756 | + 0.0f, -1.0f, height + output_y_pos}}})); |
1757 | + EXPECT_CALL(mock_sink, |
1758 | + handle_input( |
1759 | + mt::TouchContact( |
1760 | + 0, |
1761 | + mir_touch_action_down, |
1762 | + output_x_pos + width - x, |
1763 | + output_y_pos + height - y))) |
1764 | + .Times(1); |
1765 | + |
1766 | + touch_screen.apply_settings(mi::TouchscreenSettings{output_id, mir_touchscreen_mapping_mode_to_output}); |
1767 | + touch_screen.start(&mock_sink, &mock_builder); |
1768 | + |
1769 | + env.mock_libinput.setup_touch_event(fake_device, LIBINPUT_EVENT_TOUCH_DOWN, event_time_1, 0, x, y, 0, 0, 0, 0); |
1770 | + env.mock_libinput.setup_touch_frame(fake_device, event_time_1); |
1771 | + |
1772 | + process_events(touch_screen); |
1773 | +} |
1774 | + |
1775 | +TEST_F(LibInputDeviceOnTouchScreen, display_wall_device_maps_to_bounding_rectangle) |
1776 | +{ |
1777 | + EXPECT_CALL(mock_sink, output_info(output_id)) |
1778 | + .Times(0); |
1779 | + EXPECT_CALL(mock_sink, bounding_rectangle()) |
1780 | + .WillOnce(Return(geom::Rectangle{geom::Point{screen_x_pos, screen_y_pos}, geom::Size{100, 100}})); |
1781 | + EXPECT_CALL(mock_sink, handle_input(mt::TouchContact(0, mir_touch_action_down, x + screen_x_pos, y + screen_y_pos))) |
1782 | + .Times(1); |
1783 | + |
1784 | + touch_screen.apply_settings(mi::TouchscreenSettings{output_id, mir_touchscreen_mapping_mode_to_display_wall}); |
1785 | + touch_screen.start(&mock_sink, &mock_builder); |
1786 | + |
1787 | + env.mock_libinput.setup_touch_event(fake_device, LIBINPUT_EVENT_TOUCH_DOWN, event_time_1, 0, x, y, 0, 0, 0, 0); |
1788 | + env.mock_libinput.setup_touch_frame(fake_device, event_time_1); |
1789 | + |
1790 | + process_events(touch_screen); |
1791 | +} |
1792 | + |
1793 | +TEST_F(LibInputDeviceOnTouchScreen, drops_touchscreen_event_on_deactivated_output) |
1794 | +{ |
1795 | + EXPECT_CALL(mock_sink, handle_input(_)).Times(0); |
1796 | + |
1797 | + ON_CALL(mock_sink, output_info(output_id)) |
1798 | + .WillByDefault(Return(mi::OutputInfo{false, geom::Size{width, height}, Matrix{{1,0,0,0,1,0}}})); |
1799 | + |
1800 | + touch_screen.apply_settings(mi::TouchscreenSettings{output_id, mir_touchscreen_mapping_mode_to_output}); |
1801 | + |
1802 | + touch_screen.start(&mock_sink, &mock_builder); |
1803 | + env.mock_libinput.setup_touch_event(fake_device, LIBINPUT_EVENT_TOUCH_DOWN, event_time_1, 0, x, y, 0, 0, 0, 0); |
1804 | + env.mock_libinput.setup_touch_frame(fake_device, event_time_1); |
1805 | + |
1806 | + process_events(touch_screen); |
1807 | +} |
1808 | |
1809 | === modified file 'tests/unit-tests/input/test_seat_input_device_tracker.cpp' |
1810 | --- tests/unit-tests/input/test_seat_input_device_tracker.cpp 2017-01-18 02:29:37 +0000 |
1811 | +++ tests/unit-tests/input/test_seat_input_device_tracker.cpp 2017-03-01 16:08:29 +0000 |
1812 | @@ -22,7 +22,6 @@ |
1813 | #include "mir/input/xkb_mapper.h" |
1814 | #include "mir/test/doubles/mock_input_device.h" |
1815 | #include "mir/test/doubles/mock_input_dispatcher.h" |
1816 | -#include "mir/test/doubles/mock_input_region.h" |
1817 | #include "mir/test/doubles/mock_cursor_listener.h" |
1818 | #include "mir/test/doubles/mock_touch_visualizer.h" |
1819 | #include "mir/test/doubles/mock_input_seat.h" |
1820 | @@ -53,7 +52,6 @@ |
1821 | { |
1822 | mi::EventBuilder* builder; |
1823 | Nice<mtd::MockInputDispatcher> mock_dispatcher; |
1824 | - Nice<mtd::MockInputRegion> mock_region; |
1825 | Nice<mtd::MockCursorListener> mock_cursor_listener; |
1826 | Nice<mtd::MockTouchVisualizer> mock_visualizer; |
1827 | Nice<mtd::MockInputSeat> mock_seat; |
1828 | @@ -70,8 +68,7 @@ |
1829 | mi::receiver::XKBMapper mapper; |
1830 | mi::SeatInputDeviceTracker tracker{ |
1831 | mt::fake_shared(mock_dispatcher), mt::fake_shared(mock_visualizer), mt::fake_shared(mock_cursor_listener), |
1832 | - mt::fake_shared(mock_region), mt::fake_shared(mapper), mt::fake_shared(clock), |
1833 | - mt::fake_shared(mock_seat_report)}; |
1834 | + mt::fake_shared(mapper), mt::fake_shared(clock), mt::fake_shared(mock_seat_report)}; |
1835 | |
1836 | std::chrono::nanoseconds arbitrary_timestamp; |
1837 | }; |
1838 | |
1839 | === modified file 'tests/unit-tests/scene/test_surface_stack.cpp' |
1840 | --- tests/unit-tests/scene/test_surface_stack.cpp 2017-02-15 14:45:41 +0000 |
1841 | +++ tests/unit-tests/scene/test_surface_stack.cpp 2017-03-01 16:08:29 +0000 |
1842 | @@ -989,7 +989,7 @@ |
1843 | stack.emit_scene_changed(); |
1844 | } |
1845 | |
1846 | -TEST_F(SurfaceStack, only_enumerates_exposed_input_surfaces) |
1847 | +TEST_F(SurfaceStack, for_each_enumerates_all_input_surfaces) |
1848 | { |
1849 | using namespace ::testing; |
1850 | |
1851 | @@ -1007,7 +1007,7 @@ |
1852 | }; |
1853 | |
1854 | stack.for_each(count_exposed_surfaces); |
1855 | - EXPECT_THAT(num_exposed_surfaces, Eq(1)); |
1856 | + EXPECT_THAT(num_exposed_surfaces, Eq(3)); |
1857 | } |
1858 | |
1859 | using namespace ::testing; |
FAILED: Continuous integration, rev:4009 /mir-jenkins. ubuntu. com/job/ mir-ci/ 2954/ /mir-jenkins. ubuntu. com/job/ build-mir/ 3915/console /mir-jenkins. ubuntu. com/job/ build-0- fetch/4001/ console /mir-jenkins. ubuntu. com/job/ build-1- sourcepkg/ release= vivid+overlay/ 3991/console /mir-jenkins. ubuntu. com/job/ build-1- sourcepkg/ release= xenial+ overlay/ 3991/console /mir-jenkins. ubuntu. com/job/ build-1- sourcepkg/ release= zesty/3991/ console /mir-jenkins. ubuntu. com/job/ build-2- binpkg- mir/arch= amd64,compiler= clang,platform= mesa,release= zesty/3942/ console /mir-jenkins. ubuntu. com/job/ build-2- binpkg- mir/arch= amd64,compiler= gcc,platform= mesa,release= xenial+ overlay/ 3942/console /mir-jenkins. ubuntu. com/job/ build-2- binpkg- mir/arch= amd64,compiler= gcc,platform= mesa,release= zesty/3942/ console /mir-jenkins. ubuntu. com/job/ build-2- binpkg- mir/arch= cross-armhf, compiler= gcc,platform= android, release= vivid+overlay/ 3942/console /mir-jenkins. ubuntu. com/job/ build-2- binpkg- mir/arch= i386,compiler= gcc,platform= android, release= vivid+overlay/ 3942/console /mir-jenkins. ubuntu. com/job/ build-2- binpkg- mir/arch= i386,compiler= gcc,platform= mesa,release= xenial+ overlay/ 3942/console
https:/
Executed test runs:
FAILURE: https:/
FAILURE: https:/
FAILURE: https:/
FAILURE: https:/
FAILURE: https:/
FAILURE: https:/
FAILURE: https:/
FAILURE: https:/
FAILURE: https:/
FAILURE: https:/
FAILURE: https:/
Click here to trigger a rebuild: /mir-jenkins. ubuntu. com/job/ mir-ci/ 2954/rebuild
https:/