Mir

Merge lp:~andreas-pokorny/mir/nested-hardware-cursor into lp:mir

Proposed by Andreas Pokorny
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
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 ..

To post a comment you must log in.
Revision history for this message
Andreas Pokorny (andreas-pokorny) :
review: Disapprove
Revision history for this message
Mir CI Bot (mir-ci-bot) wrote :

FAILED: Continuous integration, rev:3654
https://mir-jenkins.ubuntu.com/job/mir-ci/1690/
Executed test runs:
    FAILURE: https://mir-jenkins.ubuntu.com/job/build-mir/2115/console
    SUCCESS: https://mir-jenkins.ubuntu.com/job/build-0-fetch/2177
    SUCCESS: https://mir-jenkins.ubuntu.com/job/build-1-sourcepkg/release=vivid+overlay/2168
    SUCCESS: https://mir-jenkins.ubuntu.com/job/build-1-sourcepkg/release=xenial+overlay/2168
    SUCCESS: https://mir-jenkins.ubuntu.com/job/build-1-sourcepkg/release=yakkety/2168
    FAILURE: https://mir-jenkins.ubuntu.com/job/build-2-binpkg-mir/arch=amd64,compiler=clang,platform=mesa,release=yakkety/2143/console
    SUCCESS: https://mir-jenkins.ubuntu.com/job/build-2-binpkg-mir/arch=amd64,compiler=gcc,platform=mesa,release=xenial+overlay/2143
        deb: https://mir-jenkins.ubuntu.com/job/build-2-binpkg-mir/arch=amd64,compiler=gcc,platform=mesa,release=xenial+overlay/2143/artifact/output/*zip*/output.zip
    SUCCESS: https://mir-jenkins.ubuntu.com/job/build-2-binpkg-mir/arch=amd64,compiler=gcc,platform=mesa,release=yakkety/2143
        deb: https://mir-jenkins.ubuntu.com/job/build-2-binpkg-mir/arch=amd64,compiler=gcc,platform=mesa,release=yakkety/2143/artifact/output/*zip*/output.zip
    SUCCESS: https://mir-jenkins.ubuntu.com/job/build-2-binpkg-mir/arch=i386,compiler=gcc,platform=android,release=vivid+overlay/2143
        deb: https://mir-jenkins.ubuntu.com/job/build-2-binpkg-mir/arch=i386,compiler=gcc,platform=android,release=vivid+overlay/2143/artifact/output/*zip*/output.zip
    SUCCESS: https://mir-jenkins.ubuntu.com/job/build-2-binpkg-mir/arch=i386,compiler=gcc,platform=mesa,release=xenial+overlay/2143
        deb: https://mir-jenkins.ubuntu.com/job/build-2-binpkg-mir/arch=i386,compiler=gcc,platform=mesa,release=xenial+overlay/2143/artifact/output/*zip*/output.zip

Click here to trigger a rebuild:
https://mir-jenkins.ubuntu.com/job/mir-ci/1690/rebuild

review: Needs Fixing (continuous-integration)
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

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
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+}

Subscribers

People subscribed via source and target branches