Merge lp:~andreas-pokorny/mir/nested-hardware-cursor into lp:mir
- nested-hardware-cursor
- Merge into development-branch
Status: | Work in progress |
---|---|
Proposed branch: | lp:~andreas-pokorny/mir/nested-hardware-cursor |
Merge into: | lp:mir |
Diff against target: |
1199 lines (+407/-117) 28 files modified
debian/control (+2/-2) debian/libmirserver42.install (+1/-1) examples/render_surfaces.cpp (+2/-0) include/server/mir/server_status_listener.h (+2/-0) src/include/platform/mir/options/configuration.h (+1/-0) src/include/server/mir/default_server_status_listener.h (+8/-0) src/platform/options/default_configuration.cpp (+3/-0) src/platform/symbols.map (+1/-0) src/platforms/mesa/server/kms/bypass.cpp (+3/-1) src/renderers/gl/renderer.cpp (+1/-0) src/server/CMakeLists.txt (+1/-1) src/server/graphics/default_configuration.cpp (+6/-2) src/server/graphics/nested/cursor.cpp (+2/-1) src/server/graphics/nested/display_buffer.cpp (+1/-0) src/server/graphics/nested/host_connection.h (+2/-0) src/server/graphics/nested/input_platform.cpp (+3/-0) src/server/graphics/nested/mir_client_host_connection.cpp (+195/-43) src/server/graphics/nested/mir_client_host_connection.h (+11/-1) src/server/input/default_configuration.cpp (+2/-1) src/server/input/default_input_device_hub.cpp (+16/-1) src/server/input/default_input_device_hub.h (+5/-1) src/server/symbols.map (+2/-8) tests/acceptance-tests/test_nested_mir.cpp (+103/-50) tests/include/mir/test/doubles/mock_server_status_listener.h (+2/-0) tests/include/mir/test/doubles/stub_host_connection.h (+5/-2) tests/integration-tests/input/test_single_seat_setup.cpp (+3/-1) tests/mir_test_framework/testing_server_options.cpp (+2/-0) tests/unit-tests/input/test_default_input_device_hub.cpp (+22/-1) |
To merge this branch: | bzr merge lp:~andreas-pokorny/mir/nested-hardware-cursor |
Related bugs: |
Reviewer | Review Type | Date Requested | Status |
---|---|---|---|
Mir CI Bot | continuous-integration | Needs Fixing | |
Andreas Pokorny (community) | Disapprove | ||
Review via email: mp+305448@code.launchpad.net |
Commit message
Add an option to specify the behavior of the nested cursor
The nested cursor previously only forwarded cursor image changes to the host compositor. That way the host compositor or shell is still in control of the position of the cursor. With this change now, the nested Cursor has a new mode of operation: Disabling the display of the host compositor and providing its own cursor as a separate stacked buffer stream.
Since the nested cursor is not blended into the nested display buffers, movement of the nested cursor does not cause a partial or full screen redraw inside the nested shell.
Description of the change
No request for review yet..
This is for testing the cursor mode in which the session shell controls the position but the system compositor draws the cursor.
tests still missing ..
Mir CI Bot (mir-ci-bot) wrote : | # |
- 3655. By Andreas Pokorny
-
merge ci fixes
- 3656. By Andreas Pokorny
-
Trying with 10 seconds now
Unmerged revisions
- 3656. By Andreas Pokorny
-
Trying with 10 seconds now
- 3655. By Andreas Pokorny
-
merge ci fixes
- 3654. By Andreas Pokorny
-
merge lp:mir
- 3653. By Andreas Pokorny
-
Merge nested display changes from lp:mir
- 3652. By Andreas Pokorny
-
merge ci fix
- 3651. By Andreas Pokorny
-
merge startup race fixes
- 3650. By Andreas Pokorny
-
merge shutdown race fixes
- 3649. By Andreas Pokorny
-
merge lp:mir
- 3648. By Andreas Pokorny
-
Update cursor size needed to calculate cursor image bounds
- 3647. By Andreas Pokorny
-
merged bypass fix and more dbugging
Preview Diff
1 | === modified file 'debian/control' |
2 | --- debian/control 2016-09-01 14:08:30 +0000 |
3 | +++ debian/control 2016-09-13 09:13:43 +0000 |
4 | @@ -69,7 +69,7 @@ |
5 | |
6 | #TODO: Packaging infrastructure for better dependency generation, |
7 | # ala pkg-xorg's xviddriver:Provides and ABI detection. |
8 | -Package: libmirserver41 |
9 | +Package: libmirserver42 |
10 | Section: libs |
11 | Architecture: linux-any |
12 | Multi-Arch: same |
13 | @@ -135,7 +135,7 @@ |
14 | Architecture: linux-any |
15 | Multi-Arch: same |
16 | Pre-Depends: ${misc:Pre-Depends} |
17 | -Depends: libmirserver41 (= ${binary:Version}), |
18 | +Depends: libmirserver42 (= ${binary:Version}), |
19 | libmirplatform-dev (= ${binary:Version}), |
20 | libmircommon-dev (= ${binary:Version}), |
21 | libglm-dev, |
22 | |
23 | === renamed file 'debian/libmirserver41.install' => 'debian/libmirserver42.install' |
24 | --- debian/libmirserver41.install 2016-06-30 11:36:23 +0000 |
25 | +++ debian/libmirserver42.install 2016-09-13 09:13:43 +0000 |
26 | @@ -1,1 +1,1 @@ |
27 | -usr/lib/*/libmirserver.so.41 |
28 | +usr/lib/*/libmirserver.so.42 |
29 | |
30 | === modified file 'examples/render_surfaces.cpp' |
31 | --- examples/render_surfaces.cpp 2016-09-02 10:44:49 +0000 |
32 | +++ examples/render_surfaces.cpp 2016-09-13 09:13:43 +0000 |
33 | @@ -229,6 +229,8 @@ |
34 | virtual void paused() override {} |
35 | virtual void resumed() override {} |
36 | virtual void started() override {callback(); callback = []{}; } |
37 | + virtual void ready_for_user_input() override {} |
38 | + virtual void stop_receiving_input() override {} |
39 | |
40 | std::function<void()> callback; |
41 | }; |
42 | |
43 | === modified file 'include/server/mir/server_status_listener.h' |
44 | --- include/server/mir/server_status_listener.h 2013-10-15 09:22:41 +0000 |
45 | +++ include/server/mir/server_status_listener.h 2016-09-13 09:13:43 +0000 |
46 | @@ -29,6 +29,8 @@ |
47 | virtual void resumed() = 0; |
48 | virtual void started() = 0; |
49 | |
50 | + virtual void ready_for_user_input() = 0; |
51 | + virtual void stop_receiving_input() = 0; |
52 | protected: |
53 | ServerStatusListener() = default; |
54 | virtual ~ServerStatusListener() = default; |
55 | |
56 | === modified file 'src/include/platform/mir/options/configuration.h' |
57 | --- src/include/platform/mir/options/configuration.h 2016-07-29 17:33:01 +0000 |
58 | +++ src/include/platform/mir/options/configuration.h 2016-09-13 09:13:43 +0000 |
59 | @@ -51,6 +51,7 @@ |
60 | extern char const* const nbuffers_opt; |
61 | extern char const* const composite_delay_opt; |
62 | extern char const* const enable_key_repeat_opt; |
63 | +extern char const* const control_host_cursor_opt; |
64 | |
65 | extern char const* const name_opt; |
66 | |
67 | |
68 | === modified file 'src/include/server/mir/default_server_status_listener.h' |
69 | --- src/include/server/mir/default_server_status_listener.h 2015-02-22 07:46:25 +0000 |
70 | +++ src/include/server/mir/default_server_status_listener.h 2016-09-13 09:13:43 +0000 |
71 | @@ -37,6 +37,14 @@ |
72 | virtual void started() |
73 | { |
74 | } |
75 | + |
76 | + virtual void ready_for_user_input() |
77 | + { |
78 | + } |
79 | + |
80 | + virtual void stop_receiving_input() |
81 | + { |
82 | + } |
83 | }; |
84 | } |
85 | |
86 | |
87 | === modified file 'src/platform/options/default_configuration.cpp' |
88 | --- src/platform/options/default_configuration.cpp 2016-07-29 17:33:01 +0000 |
89 | +++ src/platform/options/default_configuration.cpp 2016-09-13 09:13:43 +0000 |
90 | @@ -52,6 +52,7 @@ |
91 | char const* const mo::nbuffers_opt = "nbuffers"; |
92 | char const* const mo::composite_delay_opt = "composite-delay"; |
93 | char const* const mo::enable_key_repeat_opt = "enable-key-repeat"; |
94 | +char const* const mo::control_host_cursor_opt = "control-host-cursor"; |
95 | |
96 | char const* const mo::off_opt_value = "off"; |
97 | char const* const mo::log_opt_value = "log"; |
98 | @@ -150,6 +151,8 @@ |
99 | "Library to use for platform input support (default: input-stub.so)") |
100 | (platform_path, po::value<std::string>()->default_value(MIR_SERVER_PLATFORM_PATH), |
101 | "Directory to look for platform libraries (default: " MIR_SERVER_PLATFORM_PATH ")") |
102 | + (control_host_cursor_opt, |
103 | + "Control host cursor position from nested server") |
104 | (enable_input_opt, po::value<bool>()->default_value(enable_input_default), |
105 | "Enable input.") |
106 | (compositor_report_opt, po::value<std::string>()->default_value(off_opt_value), |
107 | |
108 | === modified file 'src/platform/symbols.map' |
109 | --- src/platform/symbols.map 2016-09-02 16:51:10 +0000 |
110 | +++ src/platform/symbols.map 2016-09-13 09:13:43 +0000 |
111 | @@ -96,6 +96,7 @@ |
112 | # Why are server-only options here in libmirplatform?... |
113 | mir::options::composite_delay_opt*; |
114 | mir::options::compositor_report_opt*; |
115 | + mir::options::control_host_cursor_opt*; |
116 | mir::options::Configuration::?Configuration*; |
117 | mir::options::Configuration::Configuration*; |
118 | mir::options::Configuration::operator*; |
119 | |
120 | === modified file 'src/platforms/mesa/server/kms/bypass.cpp' |
121 | --- src/platforms/mesa/server/kms/bypass.cpp 2016-08-24 02:09:08 +0000 |
122 | +++ src/platforms/mesa/server/kms/bypass.cpp 2016-09-13 09:13:43 +0000 |
123 | @@ -19,6 +19,7 @@ |
124 | #include "mir/graphics/renderable.h" |
125 | #include "mir/graphics/display_buffer.h" |
126 | #include "bypass.h" |
127 | +#include <iostream> |
128 | |
129 | using namespace mir; |
130 | namespace mgm = mir::graphics::mesa; |
131 | @@ -35,8 +36,9 @@ |
132 | if (!bypass_is_feasible) |
133 | return false; |
134 | |
135 | + mir::geometry::Rectangle const empty{}; |
136 | //offscreen surfaces don't affect if bypass is possible |
137 | - if (!view_area.overlaps(renderable->screen_position())) |
138 | + if (view_area.intersection_with(renderable->screen_position()) == empty) |
139 | return false; |
140 | |
141 | auto const is_opaque = !((renderable->alpha() != 1.0f) || renderable->shaped()); |
142 | |
143 | === modified file 'src/renderers/gl/renderer.cpp' |
144 | --- src/renderers/gl/renderer.cpp 2016-06-02 05:33:50 +0000 |
145 | +++ src/renderers/gl/renderer.cpp 2016-09-13 09:13:43 +0000 |
146 | @@ -36,6 +36,7 @@ |
147 | |
148 | #include <boost/throw_exception.hpp> |
149 | #include <stdexcept> |
150 | +#include <iostream> |
151 | #include <cmath> |
152 | |
153 | namespace mg = mir::graphics; |
154 | |
155 | === modified file 'src/server/CMakeLists.txt' |
156 | --- src/server/CMakeLists.txt 2016-08-08 03:22:02 +0000 |
157 | +++ src/server/CMakeLists.txt 2016-09-13 09:13:43 +0000 |
158 | @@ -124,7 +124,7 @@ |
159 | ${CMAKE_SOURCE_DIR}/include/server/mir DESTINATION "include/mirserver" |
160 | ) |
161 | |
162 | -set(MIRSERVER_ABI 41) # Be sure to increment MIR_VERSION_MINOR at the same time |
163 | +set(MIRSERVER_ABI 42) # Be sure to increment MIR_VERSION_MINOR at the same time |
164 | set(symbol_map ${CMAKE_CURRENT_SOURCE_DIR}/symbols.map) |
165 | |
166 | set_target_properties( |
167 | |
168 | === modified file 'src/server/graphics/default_configuration.cpp' |
169 | --- src/server/graphics/default_configuration.cpp 2016-07-26 03:56:14 +0000 |
170 | +++ src/server/graphics/default_configuration.cpp 2016-09-13 09:13:43 +0000 |
171 | @@ -228,11 +228,15 @@ |
172 | auto const my_name = options->is_set(options::name_opt) ? |
173 | options->get<std::string>(options::name_opt) : |
174 | "nested-mir@:" + server_socket; |
175 | + auto const cursor_mode = options->is_set(options::control_host_cursor_opt) ? |
176 | + mgn::MirClientHostConnection::CursorMode::ReplaceHostCursor : |
177 | + mgn::MirClientHostConnection::CursorMode::ForwardCursorImage; |
178 | |
179 | - return std::make_shared<graphics::nested::MirClientHostConnection>( |
180 | + return std::make_shared<mgn::MirClientHostConnection>( |
181 | host_socket, |
182 | my_name, |
183 | - the_host_lifecycle_event_listener() |
184 | + the_host_lifecycle_event_listener(), |
185 | + cursor_mode |
186 | ); |
187 | }); |
188 | } |
189 | |
190 | === modified file 'src/server/graphics/nested/cursor.cpp' |
191 | --- src/server/graphics/nested/cursor.cpp 2016-01-29 08:18:22 +0000 |
192 | +++ src/server/graphics/nested/cursor.cpp 2016-09-13 09:13:43 +0000 |
193 | @@ -35,8 +35,9 @@ |
194 | { |
195 | } |
196 | |
197 | -void mgn::Cursor::move_to(geom::Point) |
198 | +void mgn::Cursor::move_to(geom::Point pos) |
199 | { |
200 | + connection->set_cursor_position(pos); |
201 | } |
202 | |
203 | void mgn::Cursor::show(mg::CursorImage const& cursor_image) |
204 | |
205 | === modified file 'src/server/graphics/nested/display_buffer.cpp' |
206 | --- src/server/graphics/nested/display_buffer.cpp 2016-08-25 13:34:29 +0000 |
207 | +++ src/server/graphics/nested/display_buffer.cpp 2016-09-13 09:13:43 +0000 |
208 | @@ -52,6 +52,7 @@ |
209 | mg::BufferProperties properties(output.extents().size, output.current_format, mg::BufferUsage::hardware); |
210 | return connection.create_surface( |
211 | host_stream, mir::geometry::Displacement{0, 0}, properties, |
212 | + output.extents(), |
213 | surface_title.str().c_str(), static_cast<uint32_t>(output.id.as_value())); |
214 | } |
215 | } |
216 | |
217 | === modified file 'src/server/graphics/nested/host_connection.h' |
218 | --- src/server/graphics/nested/host_connection.h 2016-09-01 16:26:31 +0000 |
219 | +++ src/server/graphics/nested/host_connection.h 2016-09-13 09:13:43 +0000 |
220 | @@ -60,9 +60,11 @@ |
221 | std::shared_ptr<HostStream> const& stream, |
222 | geometry::Displacement stream_displacement, |
223 | graphics::BufferProperties properties, |
224 | + geometry::Rectangle const& extents, |
225 | char const* name, uint32_t output_id) = 0; |
226 | |
227 | virtual void set_cursor_image(CursorImage const& image) = 0; |
228 | + virtual void set_cursor_position(geometry::Point const& pos) = 0; |
229 | virtual void hide_cursor() = 0; |
230 | virtual auto graphics_platform_library() -> std::string = 0; |
231 | |
232 | |
233 | === modified file 'src/server/graphics/nested/input_platform.cpp' |
234 | --- src/server/graphics/nested/input_platform.cpp 2016-07-07 09:59:19 +0000 |
235 | +++ src/server/graphics/nested/input_platform.cpp 2016-09-13 09:13:43 +0000 |
236 | @@ -217,6 +217,7 @@ |
237 | |
238 | if (event_type == mir_event_type_input) |
239 | { |
240 | + std::lock_guard<std::mutex> lock(devices_guard); |
241 | auto const* input_ev = mir_event_get_input_event(&event); |
242 | auto const id = mir_input_event_get_device_id(input_ev); |
243 | auto it = devices.find(id); |
244 | @@ -234,6 +235,7 @@ |
245 | } |
246 | else if (event_type == mir_event_type_input_device_state) |
247 | { |
248 | + std::lock_guard<std::mutex> lock(devices_guard); |
249 | if (!devices.empty()) |
250 | { |
251 | auto const* device_state = mir_event_get_input_device_state_event(&event); |
252 | @@ -270,6 +272,7 @@ |
253 | std::function<void(MirEvent const&, mir::geometry::Rectangle const&)> empty_event_callback; |
254 | connection->set_input_event_callback(empty_event_callback); |
255 | |
256 | + std::lock_guard<std::mutex> lock(devices_guard); |
257 | for(auto const& device : devices) |
258 | input_device_registry->remove_device(device.second); |
259 | |
260 | |
261 | === modified file 'src/server/graphics/nested/mir_client_host_connection.cpp' |
262 | --- src/server/graphics/nested/mir_client_host_connection.cpp 2016-09-01 16:26:31 +0000 |
263 | +++ src/server/graphics/nested/mir_client_host_connection.cpp 2016-09-13 09:13:43 +0000 |
264 | @@ -24,6 +24,7 @@ |
265 | #include "mir/raii.h" |
266 | #include "mir/graphics/platform_operation_message.h" |
267 | #include "mir/graphics/cursor_image.h" |
268 | +#include "mir/geometry/point.h" |
269 | #include "mir/input/device.h" |
270 | #include "mir/input/device_capability.h" |
271 | #include "mir/input/pointer_configuration.h" |
272 | @@ -44,6 +45,7 @@ |
273 | namespace mgn = mir::graphics::nested; |
274 | namespace mi = mir::input; |
275 | namespace mf = mir::frontend; |
276 | +namespace geom = mir::geometry; |
277 | |
278 | namespace |
279 | { |
280 | @@ -90,15 +92,13 @@ |
281 | } |
282 | } |
283 | |
284 | -class MirClientHostSurface : public mgn::HostSurface |
285 | +class BasicHostSurface : public mgn::HostSurface |
286 | { |
287 | public: |
288 | - MirClientHostSurface( |
289 | - MirConnection* mir_connection, |
290 | - MirSurfaceSpec* spec) |
291 | - : mir_connection(mir_connection), |
292 | - mir_surface{ |
293 | - mir_surface_create_sync(spec)} |
294 | + BasicHostSurface(MirConnection* mir_connection, |
295 | + MirSurfaceSpec* spec, |
296 | + std::shared_ptr<mgn::HostStream> const& stream) |
297 | + : mir_connection(mir_connection), mir_surface(mir_surface_create_sync(spec)), main_stream(stream) |
298 | { |
299 | if (!mir_surface_is_valid(mir_surface)) |
300 | { |
301 | @@ -107,16 +107,15 @@ |
302 | } |
303 | } |
304 | |
305 | - ~MirClientHostSurface() |
306 | + ~BasicHostSurface() |
307 | { |
308 | - if (cursor) mir_buffer_stream_release_sync(cursor); |
309 | + destroy_cursor(); |
310 | mir_surface_release_sync(mir_surface); |
311 | } |
312 | |
313 | EGLNativeWindowType egl_native_window() override |
314 | { |
315 | - return reinterpret_cast<EGLNativeWindowType>( |
316 | - mir_buffer_stream_get_egl_native_window(mir_surface_get_buffer_stream(mir_surface))); |
317 | + return main_stream->egl_native_window(); |
318 | } |
319 | |
320 | void set_event_handler(mir_surface_event_callback cb, void* context) override |
321 | @@ -124,7 +123,12 @@ |
322 | mir_surface_set_event_handler(mir_surface, cb, context); |
323 | } |
324 | |
325 | - void set_cursor_image(mg::CursorImage const& image) |
326 | + virtual void set_cursor_image(mg::CursorImage const& image) = 0; |
327 | + virtual void set_cursor_position(geom::Point const&) = 0; |
328 | + virtual void hide_cursor() = 0; |
329 | + |
330 | +protected: |
331 | + bool update_cursor(mg::CursorImage const& image) |
332 | { |
333 | auto const image_width = image.size().width.as_int(); |
334 | auto const image_height = image.size().height.as_int(); |
335 | @@ -132,7 +136,7 @@ |
336 | if ((image_width <= 0) || (image_height <= 0)) |
337 | { |
338 | hide_cursor(); |
339 | - return; |
340 | + return true; |
341 | } |
342 | |
343 | MirGraphicsRegion g; |
344 | @@ -166,9 +170,43 @@ |
345 | |
346 | mir_buffer_stream_swap_buffers_sync(cursor); |
347 | |
348 | - if (new_cursor || cursor_hotspot != image.hotspot()) |
349 | - { |
350 | - cursor_hotspot = image.hotspot(); |
351 | + return new_cursor; |
352 | + } |
353 | + |
354 | + void destroy_cursor() |
355 | + { |
356 | + if (cursor) |
357 | + { |
358 | + mir_buffer_stream_release_sync(cursor); |
359 | + cursor = nullptr; |
360 | + } |
361 | + } |
362 | + |
363 | + MirConnection* const mir_connection; |
364 | + MirSurface* const mir_surface; |
365 | + std::shared_ptr<mgn::HostStream> const main_stream; |
366 | + MirBufferStream* cursor{nullptr}; |
367 | + |
368 | +}; |
369 | + |
370 | +class CursorConfigurationHostSurface : public BasicHostSurface |
371 | +{ |
372 | +public: |
373 | + CursorConfigurationHostSurface( |
374 | + MirConnection* connection, |
375 | + MirSurfaceSpec* spec, |
376 | + std::shared_ptr<mgn::HostStream> const& stream) |
377 | + : BasicHostSurface(connection, spec, stream) |
378 | + { |
379 | + } |
380 | + |
381 | + void set_cursor_image(mg::CursorImage const& image) override |
382 | + { |
383 | + bool new_cursor = update_cursor(image); |
384 | + |
385 | + if (new_cursor) |
386 | + { |
387 | + auto cursor_hotspot = image.hotspot(); |
388 | |
389 | auto conf = mir_cursor_configuration_from_buffer_stream( |
390 | cursor, cursor_hotspot.dx.as_int(), cursor_hotspot.dy.as_int()); |
391 | @@ -178,20 +216,103 @@ |
392 | } |
393 | } |
394 | |
395 | - void hide_cursor() |
396 | - { |
397 | - if (cursor) { mir_buffer_stream_release_sync(cursor); cursor = nullptr; } |
398 | - |
399 | - auto conf = mir_cursor_configuration_from_name(mir_disabled_cursor_name); |
400 | - mir_surface_configure_cursor(mir_surface, conf); |
401 | - mir_cursor_configuration_destroy(conf); |
402 | - } |
403 | - |
404 | + void set_cursor_position(geom::Point const&) override |
405 | + { |
406 | + } |
407 | + |
408 | + void hide_cursor() override |
409 | + { |
410 | + destroy_cursor(); |
411 | + auto conf = mir_cursor_configuration_from_name(mir_disabled_cursor_name); |
412 | + mir_surface_configure_cursor(mir_surface, conf); |
413 | + mir_cursor_configuration_destroy(conf); |
414 | + } |
415 | +}; |
416 | + |
417 | +class BufferStreamCursorHostSurface : public BasicHostSurface |
418 | +{ |
419 | +public: |
420 | + BufferStreamCursorHostSurface( |
421 | + MirConnection* mir_connection, |
422 | + MirSurfaceSpec* spec, |
423 | + std::shared_ptr<mgn::HostStream> const stream, |
424 | + geom::Rectangle const& extents) |
425 | + : BasicHostSurface(mir_connection, spec, stream), |
426 | + extents{extents} |
427 | + { |
428 | + // hide cursor in surface: |
429 | + auto conf = mir_cursor_configuration_from_name(mir_disabled_cursor_name); |
430 | + mir_surface_configure_cursor(mir_surface, conf); |
431 | + mir_cursor_configuration_destroy(conf); |
432 | + } |
433 | + |
434 | + void set_cursor_image(mg::CursorImage const& image) |
435 | + { |
436 | + bool force_update = update_cursor(image); |
437 | + cursor_size = image.size(); |
438 | + update_cursor_position(force_update, cursor_position, image.hotspot()); |
439 | + } |
440 | + |
441 | + void set_cursor_position(geom::Point const& pos) override |
442 | + { |
443 | + if (pos != cursor_position && cursor) |
444 | + { |
445 | + update_cursor_position(false, pos, cursor_hotspot); |
446 | + } |
447 | + } |
448 | + |
449 | + void update_cursor_position(bool force_update, geom::Point const& pos, geom::Displacement const& new_hotspot) |
450 | + { |
451 | + auto old_cursor_bounds = geom::Rectangle{cursor_position - cursor_hotspot, cursor_size}; |
452 | + auto new_cursor_bounds = geom::Rectangle{pos - new_hotspot, cursor_size}; |
453 | + auto stream_position = new_cursor_bounds.top_left - extents.top_left; |
454 | + auto was_visible = extents.overlaps(old_cursor_bounds); |
455 | + auto is_visible = extents.overlaps(new_cursor_bounds); |
456 | + auto changed = force_update || pos != cursor_position || cursor_hotspot != new_hotspot; |
457 | + |
458 | + if (changed) |
459 | + { |
460 | + cursor_hotspot = new_hotspot; |
461 | + cursor_position = pos; |
462 | + } |
463 | + |
464 | + if (was_visible && !is_visible) |
465 | + { |
466 | + hide_cursor(); |
467 | + } |
468 | + else if (changed && is_visible) |
469 | + { |
470 | + std::unique_ptr<MirSurfaceSpec, void(*)(MirSurfaceSpec *)> spec{ |
471 | + mir_connection_create_spec_for_changes(mir_connection), |
472 | + mir_surface_spec_release}; |
473 | + |
474 | + MirBufferStreamInfo infos[2] = |
475 | + { |
476 | + {main_stream->handle(), 0, 0}, |
477 | + {cursor, stream_position.dx.as_int(), stream_position.dy.as_int()} |
478 | + }; |
479 | + mir_surface_spec_set_streams(spec.get(), infos, 2); |
480 | + |
481 | + mir_surface_apply_spec(mir_surface, spec.get()); |
482 | + } |
483 | + } |
484 | + |
485 | + void hide_cursor() override |
486 | + { |
487 | + std::unique_ptr<MirSurfaceSpec, void(*)(MirSurfaceSpec *)> spec{ |
488 | + mir_connection_create_spec_for_changes(mir_connection), |
489 | + mir_surface_spec_release}; |
490 | + |
491 | + MirBufferStreamInfo stream = {main_stream->handle(), 0, 0}; |
492 | + mir_surface_spec_set_streams(spec.get(), &stream, 1); |
493 | + |
494 | + mir_surface_apply_spec(mir_surface, spec.get()); |
495 | + } |
496 | private: |
497 | - MirConnection* const mir_connection; |
498 | - MirSurface* const mir_surface; |
499 | - MirBufferStream* cursor{nullptr}; |
500 | mir::geometry::Displacement cursor_hotspot; |
501 | + geom::Rectangle extents; |
502 | + geom::Point cursor_position; |
503 | + geom::Size cursor_size; |
504 | }; |
505 | |
506 | class MirClientHostStream : public mgn::HostStream |
507 | @@ -261,11 +382,13 @@ |
508 | mgn::MirClientHostConnection::MirClientHostConnection( |
509 | std::string const& host_socket, |
510 | std::string const& name, |
511 | - std::shared_ptr<msh::HostLifecycleEventListener> const& host_lifecycle_event_listener) |
512 | + std::shared_ptr<msh::HostLifecycleEventListener> const& host_lifecycle_event_listener, |
513 | + CursorMode cursor_mode) |
514 | : mir_connection{mir_connect_sync(host_socket.c_str(), name.c_str())}, |
515 | conf_change_callback{[]{}}, |
516 | host_lifecycle_event_listener{host_lifecycle_event_listener}, |
517 | - event_callback{[](MirEvent const&, mir::geometry::Rectangle const&){}} |
518 | + event_callback{[](MirEvent const&, mir::geometry::Rectangle const&){}}, |
519 | + cursor_mode{cursor_mode} |
520 | { |
521 | if (!mir_connection_is_valid(mir_connection)) |
522 | { |
523 | @@ -340,8 +463,10 @@ |
524 | std::shared_ptr<HostStream> const& stream, |
525 | mir::geometry::Displacement displacement, |
526 | mg::BufferProperties properties, |
527 | + geometry::Rectangle const& extents, |
528 | char const* name, uint32_t output_id) |
529 | { |
530 | + // TODO pass down Displacement |
531 | std::lock_guard<std::mutex> lg(surfaces_mutex); |
532 | auto spec = mir::raii::deleter_for( |
533 | mir_connection_create_spec_for_normal_surface( |
534 | @@ -352,7 +477,7 @@ |
535 | mir_surface_spec_release); |
536 | |
537 | MirBufferUsage usage = (properties.usage == mg::BufferUsage::hardware) ? |
538 | - mir_buffer_usage_hardware : mir_buffer_usage_software; |
539 | + mir_buffer_usage_hardware : mir_buffer_usage_software; |
540 | |
541 | mir_surface_spec_set_name(spec.get(), name); |
542 | mir_surface_spec_set_buffer_usage(spec.get(), usage); |
543 | @@ -360,16 +485,31 @@ |
544 | MirBufferStreamInfo info { stream->handle(), displacement.dx.as_int(), displacement.dy.as_int() }; |
545 | mir_surface_spec_set_streams(spec.get(), &info, 1); |
546 | |
547 | - auto surf = std::shared_ptr<MirClientHostSurface>( |
548 | - new MirClientHostSurface(mir_connection, spec.get()), |
549 | - [this](MirClientHostSurface *surf) |
550 | - { |
551 | - std::lock_guard<std::mutex> lg(surfaces_mutex); |
552 | - auto it = std::find(surfaces.begin(), surfaces.end(), surf); |
553 | - surfaces.erase(it); |
554 | - delete surf; |
555 | - }); |
556 | - |
557 | + std::shared_ptr<BasicHostSurface> surf; |
558 | + if (cursor_mode == CursorMode::ReplaceHostCursor) |
559 | + { |
560 | + surf = std::shared_ptr<BasicHostSurface>( |
561 | + new BufferStreamCursorHostSurface(mir_connection, spec.get(), stream, extents), |
562 | + [this](BufferStreamCursorHostSurface *surf) |
563 | + { |
564 | + std::lock_guard<std::mutex> lg(surfaces_mutex); |
565 | + auto it = std::find(surfaces.begin(), surfaces.end(), surf); |
566 | + surfaces.erase(it); |
567 | + delete surf; |
568 | + }); |
569 | + } |
570 | + else |
571 | + { |
572 | + surf = std::shared_ptr<BasicHostSurface>( |
573 | + new CursorConfigurationHostSurface(mir_connection, spec.get(), stream), |
574 | + [this](CursorConfigurationHostSurface *surf) |
575 | + { |
576 | + std::lock_guard<std::mutex> lg(surfaces_mutex); |
577 | + auto it = std::find(surfaces.begin(), surfaces.end(), surf); |
578 | + surfaces.erase(it); |
579 | + delete surf; |
580 | + }); |
581 | + } |
582 | if (stored_cursor_image.size().width.as_int() * stored_cursor_image.size().height.as_int()) |
583 | surf->set_cursor_image(stored_cursor_image); |
584 | |
585 | @@ -409,20 +549,32 @@ |
586 | void mgn::MirClientHostConnection::set_cursor_image(mg::CursorImage const& image) |
587 | { |
588 | std::lock_guard<std::mutex> lg(surfaces_mutex); |
589 | + |
590 | stored_cursor_image = image; |
591 | + |
592 | for (auto s : surfaces) |
593 | { |
594 | - auto surface = static_cast<MirClientHostSurface*>(s); |
595 | + auto surface = static_cast<BasicHostSurface*>(s); |
596 | surface->set_cursor_image(stored_cursor_image); |
597 | } |
598 | } |
599 | |
600 | +void mgn::MirClientHostConnection::set_cursor_position(geom::Point const& pos) |
601 | +{ |
602 | + std::lock_guard<std::mutex> lg(surfaces_mutex); |
603 | + for (auto s : surfaces) |
604 | + { |
605 | + auto surface = static_cast<BasicHostSurface*>(s); |
606 | + surface->set_cursor_position(pos); |
607 | + } |
608 | +} |
609 | + |
610 | void mgn::MirClientHostConnection::hide_cursor() |
611 | { |
612 | std::lock_guard<std::mutex> lg(surfaces_mutex); |
613 | for (auto s : surfaces) |
614 | { |
615 | - auto surface = static_cast<MirClientHostSurface*>(s); |
616 | + auto surface = static_cast<BasicHostSurface*>(s); |
617 | surface->hide_cursor(); |
618 | } |
619 | } |
620 | |
621 | === modified file 'src/server/graphics/nested/mir_client_host_connection.h' |
622 | --- src/server/graphics/nested/mir_client_host_connection.h 2016-09-01 16:26:31 +0000 |
623 | +++ src/server/graphics/nested/mir_client_host_connection.h 2016-09-13 09:13:43 +0000 |
624 | @@ -54,9 +54,16 @@ |
625 | class MirClientHostConnection : public HostConnection |
626 | { |
627 | public: |
628 | + enum class CursorMode |
629 | + { |
630 | + ForwardCursorImage, |
631 | + ReplaceHostCursor |
632 | + }; |
633 | + |
634 | MirClientHostConnection(std::string const& host_socket, |
635 | std::string const& name, |
636 | - std::shared_ptr<msh::HostLifecycleEventListener> const& host_lifecycle_event_listener); |
637 | + std::shared_ptr<msh::HostLifecycleEventListener> const& host_lifecycle_event_listener, |
638 | + CursorMode mode); |
639 | ~MirClientHostConnection(); |
640 | |
641 | std::vector<int> platform_fd_items() override; |
642 | @@ -67,11 +74,13 @@ |
643 | std::shared_ptr<HostStream> const& stream, |
644 | geometry::Displacement stream_displacement, |
645 | graphics::BufferProperties properties, |
646 | + geometry::Rectangle const& extents, |
647 | char const* name, uint32_t output_id) override; |
648 | void set_display_config_change_callback(std::function<void()> const& cb) override; |
649 | void apply_display_config(MirDisplayConfiguration&) override; |
650 | |
651 | void set_cursor_image(CursorImage const& image) override; |
652 | + void set_cursor_position(geometry::Point const& pos) override; |
653 | void hide_cursor() override; |
654 | auto graphics_platform_library() -> std::string override; |
655 | |
656 | @@ -114,6 +123,7 @@ |
657 | std::vector<uint8_t> buffer; |
658 | }; |
659 | NestedCursorImage stored_cursor_image; |
660 | + CursorMode cursor_mode; |
661 | }; |
662 | |
663 | } |
664 | |
665 | === modified file 'src/server/input/default_configuration.cpp' |
666 | --- src/server/input/default_configuration.cpp 2016-07-28 23:00:22 +0000 |
667 | +++ src/server/input/default_configuration.cpp 2016-09-13 09:13:43 +0000 |
668 | @@ -361,7 +361,8 @@ |
669 | the_input_reading_multiplexer(), |
670 | the_main_loop(), |
671 | the_cookie_authority(), |
672 | - the_key_mapper()); |
673 | + the_key_mapper(), |
674 | + the_server_status_listener()); |
675 | |
676 | if (key_repeater && !the_options()->is_set(options::host_socket_opt)) |
677 | key_repeater->set_input_device_hub(hub); |
678 | |
679 | === modified file 'src/server/input/default_input_device_hub.cpp' |
680 | --- src/server/input/default_input_device_hub.cpp 2016-07-07 09:59:19 +0000 |
681 | +++ src/server/input/default_input_device_hub.cpp 2016-09-13 09:13:43 +0000 |
682 | @@ -22,6 +22,7 @@ |
683 | #include "mir/input/input_device.h" |
684 | #include "mir/input/input_device_observer.h" |
685 | #include "mir/geometry/point.h" |
686 | +#include "mir/server_status_listener.h" |
687 | #include "mir/dispatch/multiplexing_dispatchable.h" |
688 | #include "mir/dispatch/action_queue.h" |
689 | #include "mir/frontend/event_sink.h" |
690 | @@ -44,7 +45,8 @@ |
691 | std::shared_ptr<dispatch::MultiplexingDispatchable> const& input_multiplexer, |
692 | std::shared_ptr<mir::ServerActionQueue> const& observer_queue, |
693 | std::shared_ptr<mir::cookie::Authority> const& cookie_authority, |
694 | - std::shared_ptr<mi::KeyMapper> const& key_mapper) |
695 | + std::shared_ptr<mi::KeyMapper> const& key_mapper, |
696 | + std::shared_ptr<mir::ServerStatusListener> const& server_status_listener) |
697 | : seat{seat}, |
698 | sink{sink}, |
699 | input_dispatchable{input_multiplexer}, |
700 | @@ -52,6 +54,7 @@ |
701 | device_queue(std::make_shared<dispatch::ActionQueue>()), |
702 | cookie_authority(cookie_authority), |
703 | key_mapper(key_mapper), |
704 | + server_status_listener(server_status_listener), |
705 | device_id_generator{0} |
706 | { |
707 | input_dispatchable->add_watch(device_queue); |
708 | @@ -264,6 +267,12 @@ |
709 | observer->device_added(handles.back()); |
710 | observer->changes_complete(); |
711 | } |
712 | + |
713 | + if (!ready && handles.size()) |
714 | + { |
715 | + server_status_listener->ready_for_user_input(); |
716 | + ready = true; |
717 | + } |
718 | } |
719 | |
720 | void mi::DefaultInputDeviceHub::remove_device_handle(MirInputDeviceId id) |
721 | @@ -289,4 +298,10 @@ |
722 | |
723 | handles.erase(handle_it, end(handles)); |
724 | sink->handle_input_device_change(handles); |
725 | + |
726 | + if (ready && 0 == handles.size()) |
727 | + { |
728 | + ready = false; |
729 | + server_status_listener->stop_receiving_input(); |
730 | + } |
731 | } |
732 | |
733 | === modified file 'src/server/input/default_input_device_hub.h' |
734 | --- src/server/input/default_input_device_hub.h 2016-07-07 09:59:19 +0000 |
735 | +++ src/server/input/default_input_device_hub.h 2016-09-13 09:13:43 +0000 |
736 | @@ -37,6 +37,7 @@ |
737 | namespace mir |
738 | { |
739 | class ServerActionQueue; |
740 | +class ServerStatusListener; |
741 | namespace frontend |
742 | { |
743 | class EventSink; |
744 | @@ -67,7 +68,8 @@ |
745 | std::shared_ptr<dispatch::MultiplexingDispatchable> const& input_multiplexer, |
746 | std::shared_ptr<ServerActionQueue> const& observer_queue, |
747 | std::shared_ptr<cookie::Authority> const& cookie_authority, |
748 | - std::shared_ptr<KeyMapper> const& key_mapper); |
749 | + std::shared_ptr<KeyMapper> const& key_mapper, |
750 | + std::shared_ptr<ServerStatusListener> const& server_status_listener); |
751 | |
752 | // InputDeviceRegistry - calls from mi::Platform |
753 | void add_device(std::shared_ptr<InputDevice> const& device) override; |
754 | @@ -91,6 +93,7 @@ |
755 | std::shared_ptr<dispatch::ActionQueue> const device_queue; |
756 | std::shared_ptr<cookie::Authority> const cookie_authority; |
757 | std::shared_ptr<KeyMapper> const key_mapper; |
758 | + std::shared_ptr<ServerStatusListener> const server_status_listener; |
759 | |
760 | struct RegisteredDevice : public InputSink |
761 | { |
762 | @@ -124,6 +127,7 @@ |
763 | std::vector<std::shared_ptr<InputDeviceObserver>> observers; |
764 | |
765 | MirInputDeviceId device_id_generator; |
766 | + bool ready{false}; |
767 | }; |
768 | |
769 | } |
770 | |
771 | === modified file 'src/server/symbols.map' |
772 | --- src/server/symbols.map 2016-08-26 19:01:27 +0000 |
773 | +++ src/server/symbols.map 2016-09-13 09:13:43 +0000 |
774 | @@ -1,4 +1,4 @@ |
775 | -MIR_SERVER_0.24 { |
776 | +MIR_SERVER_0.25 { |
777 | global: |
778 | extern "C++" { |
779 | # Symbols not yet picked up by script |
780 | @@ -176,6 +176,7 @@ |
781 | mir::Server::override_the_logger*; |
782 | mir::Server::override_the_prompt_session_listener*; |
783 | mir::Server::override_the_prompt_session_manager*; |
784 | + mir::Server::override_the_seat_report*; |
785 | mir::Server::override_the_server_status_listener*; |
786 | mir::Server::override_the_session_authorizer*; |
787 | mir::Server::override_the_session_listener*; |
788 | @@ -835,10 +836,3 @@ |
789 | }; |
790 | local: *; |
791 | }; |
792 | - |
793 | -MIR_SERVER_0.25 { |
794 | - global: |
795 | - extern "C++" { |
796 | - mir::Server::override_the_seat_report*; |
797 | - }; |
798 | -} MIR_SERVER_0.24; |
799 | |
800 | === modified file 'tests/acceptance-tests/test_nested_mir.cpp' |
801 | --- tests/acceptance-tests/test_nested_mir.cpp 2016-08-26 11:25:15 +0000 |
802 | +++ tests/acceptance-tests/test_nested_mir.cpp 2016-09-13 09:13:43 +0000 |
803 | @@ -24,10 +24,12 @@ |
804 | #include "mir/graphics/display_configuration.h" |
805 | #include "mir/graphics/display_configuration_policy.h" |
806 | #include "mir/graphics/display_configuration_report.h" |
807 | +#include "mir/input/input_device_info.h" |
808 | #include "mir/shell/shell.h" |
809 | #include "mir/input/cursor_listener.h" |
810 | #include "mir/cached_ptr.h" |
811 | #include "mir/main_loop.h" |
812 | +#include "mir/server_status_listener.h" |
813 | #include "mir/scene/session_coordinator.h" |
814 | #include "mir/scene/session.h" |
815 | #include "mir/scene/surface.h" |
816 | @@ -37,8 +39,10 @@ |
817 | |
818 | #include "mir_test_framework/headless_in_process_server.h" |
819 | #include "mir_test_framework/using_stub_client_platform.h" |
820 | +#include "mir_test_framework/stub_server_platform_factory.h" |
821 | #include "mir_test_framework/headless_nested_server_runner.h" |
822 | #include "mir_test_framework/any_surface.h" |
823 | +#include "mir_test_framework/fake_input_device.h" |
824 | #include "mir/test/signal.h" |
825 | #include "mir/test/spin_wait.h" |
826 | #include "mir/test/display_config_matchers.h" |
827 | @@ -50,12 +54,14 @@ |
828 | #include "mir/test/doubles/nested_mock_egl.h" |
829 | #include "mir/test/fake_shared.h" |
830 | |
831 | +#include <future> |
832 | #include <gtest/gtest.h> |
833 | #include <gmock/gmock.h> |
834 | |
835 | namespace geom = mir::geometry; |
836 | namespace mf = mir::frontend; |
837 | namespace mg = mir::graphics; |
838 | +namespace mi = mir::input; |
839 | namespace msh = mir::shell; |
840 | namespace mt = mir::test; |
841 | namespace mtd = mir::test::doubles; |
842 | @@ -67,6 +73,23 @@ |
843 | |
844 | namespace |
845 | { |
846 | +struct InputReadyServerStatusListener : mir::ServerStatusListener |
847 | +{ |
848 | + std::shared_ptr<std::promise<void>> const input_ready; |
849 | + InputReadyServerStatusListener(std::shared_ptr<std::promise<void>> const& promise) |
850 | + : input_ready(promise) |
851 | + {} |
852 | + |
853 | + void paused() override {} |
854 | + void resumed() override {} |
855 | + void started() override {} |
856 | + void ready_for_user_input() override |
857 | + { |
858 | + input_ready->set_value(); |
859 | + } |
860 | + void stop_receiving_input() override {} |
861 | +}; |
862 | + |
863 | struct MockSessionMediatorReport : mf::SessionMediatorReport |
864 | { |
865 | MockSessionMediatorReport() |
866 | @@ -127,6 +150,7 @@ |
867 | }; |
868 | |
869 | std::chrono::seconds const timeout{5}; |
870 | +std::chrono::seconds const long_timeout{10}; |
871 | |
872 | // We can't rely on the test environment to have X cursors, so we provide some of our own |
873 | auto const cursor_names = { |
874 | @@ -403,10 +427,21 @@ |
875 | { return std::make_shared<NiceMock<MockDisplayConfigurationPolicy>>(); }); |
876 | } |
877 | |
878 | + void wait_until_ready() |
879 | + { |
880 | + auto future_status = ready_for_input->get_future().wait_for(10s); |
881 | + |
882 | + if (future_status != std::future_status::ready) |
883 | + BOOST_THROW_EXCEPTION(std::runtime_error("Nested Server never started")); |
884 | + } |
885 | + |
886 | protected: |
887 | NestedMirRunner(std::string const& connection_string, bool) |
888 | : mtf::HeadlessNestedServerRunner(connection_string) |
889 | { |
890 | + server.override_the_server_status_listener([this] |
891 | + { return std::make_shared<InputReadyServerStatusListener>(ready_for_input); }); |
892 | + |
893 | server.override_the_host_lifecycle_event_listener([this] |
894 | { return the_mock_host_lifecycle_event_listener(); }); |
895 | |
896 | @@ -422,14 +457,19 @@ |
897 | |
898 | private: |
899 | mir::CachedPtr<MockHostLifecycleEventListener> mock_host_lifecycle_event_listener; |
900 | - |
901 | mir::CachedPtr<MockDisplayConfigurationPolicy> mock_display_configuration_policy_; |
902 | + std::shared_ptr<std::promise<void>> ready_for_input = std::make_shared<std::promise<void>>(); |
903 | + mir::UniqueModulePtr<mtf::FakeInputDevice> fake_device{mtf::add_fake_input_device(mi::InputDeviceInfo{ |
904 | + "test-devce", "test-device", |
905 | + mi::DeviceCapability::pointer | mi::DeviceCapability::keyboard | mi::DeviceCapability::alpha_numeric})}; |
906 | }; |
907 | |
908 | struct NestedServer : mtf::HeadlessInProcessServer |
909 | { |
910 | mtd::NestedMockEGL mock_egl; |
911 | mtf::UsingStubClientPlatform using_stub_client_platform; |
912 | + mt::Signal condition; |
913 | + mt::Signal test_processed_result; |
914 | |
915 | std::shared_ptr<MockSessionMediatorReport> mock_session_mediator_report; |
916 | NiceMock<MockDisplay> display{display_geometry}; |
917 | @@ -906,38 +946,43 @@ |
918 | |
919 | server.the_cursor_listener()->cursor_moved_to(489, 9); |
920 | |
921 | - // TODO workaround for lp:1523621 |
922 | - // (I don't see a way to detect that the host has placed focus on "Mir nested display for output #1") |
923 | - std::this_thread::sleep_for(10ms); |
924 | - |
925 | - { |
926 | - mt::Signal condition; |
927 | - EXPECT_CALL(*mock_cursor, show(_)).Times(1) |
928 | - .WillRepeatedly(InvokeWithoutArgs([&] { condition.raise(); })); |
929 | - |
930 | - auto conf = mir_cursor_configuration_from_buffer_stream(client.buffer_stream, 0, 0); |
931 | - mir_wait_for(mir_surface_configure_cursor(client.surface, conf)); |
932 | - mir_cursor_configuration_destroy(conf); |
933 | - |
934 | - condition.wait_for(timeout); |
935 | - Mock::VerifyAndClearExpectations(mock_cursor.get()); |
936 | - } |
937 | + nested_mir.wait_until_ready(); |
938 | + |
939 | + // FIXME: In this test setup the software cursor will trigger scene_changed() on show(...). |
940 | + // Thus a new frame will be composed. Then a "FramePostObserver" in basic_surface.cpp will |
941 | + // react to the frame_posted callback by setting the cursor buffer again via show(..) |
942 | + // The number of show calls depends solely on scheduling decisions |
943 | + EXPECT_CALL(*mock_cursor, show(_)).Times(AtLeast(frames)) |
944 | + .WillRepeatedly(InvokeWithoutArgs( |
945 | + [&] |
946 | + { |
947 | + condition.raise(); |
948 | + test_processed_result.wait_for(timeout); |
949 | + test_processed_result.reset(); |
950 | + })); |
951 | + |
952 | + auto conf = mir_cursor_configuration_from_buffer_stream(client.buffer_stream, 0, 0); |
953 | + mir_wait_for(mir_surface_configure_cursor(client.surface, conf)); |
954 | + mir_cursor_configuration_destroy(conf); |
955 | + |
956 | + EXPECT_TRUE(condition.wait_for(timeout)); |
957 | + condition.reset(); |
958 | + test_processed_result.raise(); |
959 | |
960 | for (int i = 0; i != frames; ++i) |
961 | { |
962 | - mt::Signal condition; |
963 | - EXPECT_CALL(*mock_cursor, show(_)).Times(1) |
964 | - .WillRepeatedly(InvokeWithoutArgs([&] { condition.raise(); })); |
965 | - |
966 | mir_buffer_stream_swap_buffers_sync(client.buffer_stream); |
967 | |
968 | - condition.wait_for(timeout); |
969 | - Mock::VerifyAndClearExpectations(mock_cursor.get()); |
970 | + EXPECT_TRUE(condition.wait_for(timeout)); |
971 | + condition.reset(); |
972 | + test_processed_result.raise(); |
973 | } |
974 | + Mock::VerifyAndClearExpectations(mock_cursor.get()); |
975 | } |
976 | |
977 | TEST_F(NestedServer, named_cursor_image_changes_are_forwarded_to_host) |
978 | { |
979 | + mt::Signal condition; |
980 | NestedMirRunner nested_mir{new_connection()}; |
981 | |
982 | ClientWithAPaintedSurface client(nested_mir); |
983 | @@ -945,25 +990,32 @@ |
984 | |
985 | server.the_cursor_listener()->cursor_moved_to(489, 9); |
986 | |
987 | - // TODO workaround for lp:1523621 |
988 | - // (I don't see a way to detect that the host has placed focus on "Mir nested display for output #1") |
989 | - std::this_thread::sleep_for(1s); |
990 | - Mock::VerifyAndClearExpectations(mock_cursor.get()); |
991 | + nested_mir.wait_until_ready(); |
992 | + |
993 | + // FIXME: In this test setup the software cursor will trigger scene_changed() on show(...). |
994 | + // Thus a new frame will be composed. Then a "FramePostObserver" in basic_surface.cpp will |
995 | + // react to the frame_posted callback by setting the cursor buffer again via show(..) |
996 | + // The number of show calls depends solely on scheduling decisions |
997 | + EXPECT_CALL(*mock_cursor, show(_)).Times(AtLeast(cursor_names.size())) |
998 | + .WillRepeatedly(InvokeWithoutArgs( |
999 | + [&] |
1000 | + { |
1001 | + condition.raise(); |
1002 | + test_processed_result.wait_for(long_timeout); |
1003 | + test_processed_result.reset(); |
1004 | + })); |
1005 | |
1006 | for (auto const name : cursor_names) |
1007 | { |
1008 | - mt::Signal condition; |
1009 | - |
1010 | - EXPECT_CALL(*mock_cursor, show(_)).Times(1) |
1011 | - .WillOnce(InvokeWithoutArgs([&] { condition.raise(); })); |
1012 | - |
1013 | auto const cursor = mir_cursor_configuration_from_name(name); |
1014 | mir_wait_for(mir_surface_configure_cursor(client.surface, cursor)); |
1015 | mir_cursor_configuration_destroy(cursor); |
1016 | |
1017 | - condition.wait_for(timeout); |
1018 | - Mock::VerifyAndClearExpectations(mock_cursor.get()); |
1019 | + EXPECT_TRUE(condition.wait_for(long_timeout)); |
1020 | + condition.reset(); |
1021 | + test_processed_result.raise(); |
1022 | } |
1023 | + Mock::VerifyAndClearExpectations(mock_cursor.get()); |
1024 | } |
1025 | |
1026 | TEST_F(NestedServer, can_hide_the_host_cursor) |
1027 | @@ -976,22 +1028,23 @@ |
1028 | |
1029 | server.the_cursor_listener()->cursor_moved_to(489, 9); |
1030 | |
1031 | - // TODO workaround for lp:1523621 |
1032 | - // (I don't see a way to detect that the host has placed focus on "Mir nested display for output #1") |
1033 | - std::this_thread::sleep_for(10ms); |
1034 | - |
1035 | - { |
1036 | - mt::Signal condition; |
1037 | - EXPECT_CALL(*mock_cursor, show(_)).Times(1) |
1038 | - .WillRepeatedly(InvokeWithoutArgs([&] { condition.raise(); })); |
1039 | - |
1040 | - auto conf = mir_cursor_configuration_from_buffer_stream(client.buffer_stream, 0, 0); |
1041 | - mir_wait_for(mir_surface_configure_cursor(client.surface, conf)); |
1042 | - mir_cursor_configuration_destroy(conf); |
1043 | - |
1044 | - condition.wait_for(timeout); |
1045 | - Mock::VerifyAndClearExpectations(mock_cursor.get()); |
1046 | - } |
1047 | + nested_mir.wait_until_ready(); |
1048 | + Mock::VerifyAndClearExpectations(mock_cursor.get()); |
1049 | + |
1050 | + // FIXME: In this test setup the software cursor will trigger scene_changed() on show(...). |
1051 | + // Thus a new frame will be composed. Then a "FramePostObserver" in basic_surface.cpp will |
1052 | + // react to the frame_posted callback by setting the cursor buffer again via show(..) |
1053 | + // The number of show calls depends solely on scheduling decisions |
1054 | + EXPECT_CALL(*mock_cursor, show(_)).Times(AtLeast(1)) |
1055 | + .WillOnce(mt::WakeUp(&condition)); |
1056 | + |
1057 | + auto conf = mir_cursor_configuration_from_buffer_stream(client.buffer_stream, 0, 0); |
1058 | + mir_wait_for(mir_surface_configure_cursor(client.surface, conf)); |
1059 | + mir_cursor_configuration_destroy(conf); |
1060 | + |
1061 | + std::this_thread::sleep_for(500ms); |
1062 | + condition.wait_for(timeout); |
1063 | + Mock::VerifyAndClearExpectations(mock_cursor.get()); |
1064 | |
1065 | nested_mir.cursor_wrapper->set_hidden(true); |
1066 | |
1067 | |
1068 | === modified file 'tests/include/mir/test/doubles/mock_server_status_listener.h' |
1069 | --- tests/include/mir/test/doubles/mock_server_status_listener.h 2013-10-15 09:22:41 +0000 |
1070 | +++ tests/include/mir/test/doubles/mock_server_status_listener.h 2016-09-13 09:13:43 +0000 |
1071 | @@ -36,6 +36,8 @@ |
1072 | MOCK_METHOD0(paused, void()); |
1073 | MOCK_METHOD0(resumed, void()); |
1074 | MOCK_METHOD0(started, void()); |
1075 | + MOCK_METHOD0(ready_for_user_input, void()); |
1076 | + MOCK_METHOD0(stop_receiving_input, void()); |
1077 | }; |
1078 | |
1079 | } |
1080 | |
1081 | === modified file 'tests/include/mir/test/doubles/stub_host_connection.h' |
1082 | --- tests/include/mir/test/doubles/stub_host_connection.h 2016-09-01 16:26:31 +0000 |
1083 | +++ tests/include/mir/test/doubles/stub_host_connection.h 2016-09-13 09:13:43 +0000 |
1084 | @@ -66,7 +66,7 @@ |
1085 | std::shared_ptr<graphics::nested::HostSurface> |
1086 | create_surface( |
1087 | std::shared_ptr<graphics::nested::HostStream> const&, geometry::Displacement, |
1088 | - graphics::BufferProperties, char const*, uint32_t) override |
1089 | + graphics::BufferProperties, geometry::Rectangle const& , char const*, uint32_t) override |
1090 | { |
1091 | return surface; |
1092 | } |
1093 | @@ -83,6 +83,9 @@ |
1094 | void hide_cursor() override |
1095 | { |
1096 | } |
1097 | + void set_cursor_position(geometry::Point const&) override |
1098 | + { |
1099 | + } |
1100 | |
1101 | auto graphics_platform_library() -> std::string override { return {}; } |
1102 | |
1103 | @@ -118,7 +121,7 @@ |
1104 | void set_event_handler(mir_surface_event_callback, void*) override {} |
1105 | }; |
1106 | std::shared_ptr<graphics::nested::HostSurface> const surface; |
1107 | - |
1108 | + |
1109 | std::shared_ptr<MirBuffer> create_buffer(graphics::BufferProperties const&) |
1110 | { |
1111 | return nullptr; |
1112 | |
1113 | === modified file 'tests/integration-tests/input/test_single_seat_setup.cpp' |
1114 | --- tests/integration-tests/input/test_single_seat_setup.cpp 2016-07-28 23:00:22 +0000 |
1115 | +++ tests/integration-tests/input/test_single_seat_setup.cpp 2016-09-13 09:13:43 +0000 |
1116 | @@ -27,6 +27,7 @@ |
1117 | #include "mir/test/doubles/mock_cursor_listener.h" |
1118 | #include "mir/test/doubles/mock_event_sink.h" |
1119 | #include "mir/test/doubles/mock_seat_report.h" |
1120 | +#include "mir/test/doubles/mock_server_status_listener.h" |
1121 | #include "mir/test/doubles/triggered_main_loop.h" |
1122 | #include "mir/test/event_matchers.h" |
1123 | #include "mir/test/doubles/advanceable_clock.h" |
1124 | @@ -82,6 +83,7 @@ |
1125 | NiceMock<mtd::MockCursorListener> mock_cursor_listener; |
1126 | NiceMock<mtd::MockTouchVisualizer> mock_visualizer; |
1127 | NiceMock<mtd::MockSeatReport> mock_seat_report; |
1128 | + NiceMock<mtd::MockServerStatusListener> mock_status_listener; |
1129 | mi::receiver::XKBMapper key_mapper; |
1130 | mir::dispatch::MultiplexingDispatchable multiplexer; |
1131 | mtd::AdvanceableClock clock; |
1132 | @@ -90,7 +92,7 @@ |
1133 | mt::fake_shared(mock_region), mt::fake_shared(key_mapper), mt::fake_shared(clock), mt::fake_shared(mock_seat_report)}; |
1134 | mi::DefaultInputDeviceHub hub{ |
1135 | mt::fake_shared(mock_sink), mt::fake_shared(seat), mt::fake_shared(multiplexer), mt::fake_shared(observer_loop), |
1136 | - cookie_authority, mt::fake_shared(key_mapper)}; |
1137 | + cookie_authority, mt::fake_shared(key_mapper), mt::fake_shared(mock_status_listener)}; |
1138 | NiceMock<mtd::MockInputDeviceObserver> mock_observer; |
1139 | |
1140 | mi::DeviceCapabilities const keyboard_caps = mi::DeviceCapability::keyboard | mi::DeviceCapability::alpha_numeric; |
1141 | |
1142 | === modified file 'tests/mir_test_framework/testing_server_options.cpp' |
1143 | --- tests/mir_test_framework/testing_server_options.cpp 2016-05-03 06:55:25 +0000 |
1144 | +++ tests/mir_test_framework/testing_server_options.cpp 2016-09-13 09:13:43 +0000 |
1145 | @@ -119,6 +119,8 @@ |
1146 | server_started_sync.try_signal_ready_for(); |
1147 | on_start(); |
1148 | } |
1149 | + void ready_for_user_input() {} |
1150 | + void stop_receiving_input() {} |
1151 | |
1152 | mt::CrossProcessSync server_started_sync; |
1153 | std::function<void(void)> const on_start; |
1154 | |
1155 | === modified file 'tests/unit-tests/input/test_default_input_device_hub.cpp' |
1156 | --- tests/unit-tests/input/test_default_input_device_hub.cpp 2016-06-02 08:20:22 +0000 |
1157 | +++ tests/unit-tests/input/test_default_input_device_hub.cpp 2016-09-13 09:13:43 +0000 |
1158 | @@ -25,6 +25,7 @@ |
1159 | #include "mir/test/doubles/mock_input_seat.h" |
1160 | #include "mir/test/doubles/mock_event_sink.h" |
1161 | #include "mir/test/doubles/mock_key_mapper.h" |
1162 | +#include "mir/test/doubles/mock_server_status_listener.h" |
1163 | #include "mir/test/doubles/stub_cursor_listener.h" |
1164 | #include "mir/test/doubles/stub_touch_visualizer.h" |
1165 | #include "mir/test/doubles/triggered_main_loop.h" |
1166 | @@ -81,8 +82,9 @@ |
1167 | NiceMock<mtd::MockInputSeat> mock_seat; |
1168 | NiceMock<mtd::MockEventSink> mock_sink; |
1169 | NiceMock<mtd::MockKeyMapper> mock_key_mapper; |
1170 | + NiceMock<mtd::MockServerStatusListener> mock_server_status_listener; |
1171 | mi::DefaultInputDeviceHub hub{mt::fake_shared(mock_sink), mt::fake_shared(mock_seat), mt::fake_shared(multiplexer), |
1172 | - mt::fake_shared(observer_loop), cookie_authority, mt::fake_shared(mock_key_mapper)}; |
1173 | + mt::fake_shared(observer_loop), cookie_authority, mt::fake_shared(mock_key_mapper), mt::fake_shared(mock_server_status_listener)}; |
1174 | NiceMock<mtd::MockInputDeviceObserver> mock_observer; |
1175 | NiceMock<mtd::MockInputDevice> device{"device","dev-1", mi::DeviceCapability::unknown}; |
1176 | NiceMock<mtd::MockInputDevice> another_device{"another_device","dev-2", mi::DeviceCapability::keyboard}; |
1177 | @@ -210,3 +212,22 @@ |
1178 | observer_loop.trigger_server_actions(); |
1179 | } |
1180 | |
1181 | +TEST_F(InputDeviceHubTest, emit_ready_to_receive_input_after_first_device_added) |
1182 | +{ |
1183 | + EXPECT_CALL(mock_server_status_listener, ready_for_user_input()).Times(1); |
1184 | + hub.add_device(mt::fake_shared(device)); |
1185 | + hub.add_device(mt::fake_shared(another_device)); |
1186 | + |
1187 | + observer_loop.trigger_server_actions(); |
1188 | +} |
1189 | + |
1190 | +TEST_F(InputDeviceHubTest, emit_stop_receiving_input_after_last_device_added) |
1191 | +{ |
1192 | + EXPECT_CALL(mock_server_status_listener, stop_receiving_input()).Times(1); |
1193 | + hub.add_device(mt::fake_shared(device)); |
1194 | + hub.add_device(mt::fake_shared(another_device)); |
1195 | + |
1196 | + hub.remove_device(mt::fake_shared(device)); |
1197 | + hub.remove_device(mt::fake_shared(another_device)); |
1198 | + observer_loop.trigger_server_actions(); |
1199 | +} |
FAILED: Continuous integration, rev:3654 /mir-jenkins. ubuntu. com/job/ mir-ci/ 1690/ /mir-jenkins. ubuntu. com/job/ build-mir/ 2115/console /mir-jenkins. ubuntu. com/job/ build-0- fetch/2177 /mir-jenkins. ubuntu. com/job/ build-1- sourcepkg/ release= vivid+overlay/ 2168 /mir-jenkins. ubuntu. com/job/ build-1- sourcepkg/ release= xenial+ overlay/ 2168 /mir-jenkins. ubuntu. com/job/ build-1- sourcepkg/ release= yakkety/ 2168 /mir-jenkins. ubuntu. com/job/ build-2- binpkg- mir/arch= amd64,compiler= clang,platform= mesa,release= yakkety/ 2143/console /mir-jenkins. ubuntu. com/job/ build-2- binpkg- mir/arch= amd64,compiler= gcc,platform= mesa,release= xenial+ overlay/ 2143 /mir-jenkins. ubuntu. com/job/ build-2- binpkg- mir/arch= amd64,compiler= gcc,platform= mesa,release= xenial+ overlay/ 2143/artifact/ output/ *zip*/output. zip /mir-jenkins. ubuntu. com/job/ build-2- binpkg- mir/arch= amd64,compiler= gcc,platform= mesa,release= yakkety/ 2143 /mir-jenkins. ubuntu. com/job/ build-2- binpkg- mir/arch= amd64,compiler= gcc,platform= mesa,release= yakkety/ 2143/artifact/ output/ *zip*/output. zip /mir-jenkins. ubuntu. com/job/ build-2- binpkg- mir/arch= i386,compiler= gcc,platform= android, release= vivid+overlay/ 2143 /mir-jenkins. ubuntu. com/job/ build-2- binpkg- mir/arch= i386,compiler= gcc,platform= android, release= vivid+overlay/ 2143/artifact/ output/ *zip*/output. zip /mir-jenkins. ubuntu. com/job/ build-2- binpkg- mir/arch= i386,compiler= gcc,platform= mesa,release= xenial+ overlay/ 2143 /mir-jenkins. ubuntu. com/job/ build-2- binpkg- mir/arch= i386,compiler= gcc,platform= mesa,release= xenial+ overlay/ 2143/artifact/ output/ *zip*/output. zip
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:/
Click here to trigger a rebuild: /mir-jenkins. ubuntu. com/job/ mir-ci/ 1690/rebuild
https:/