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
=== modified file 'debian/control'
--- debian/control 2016-09-01 14:08:30 +0000
+++ debian/control 2016-09-13 09:13:43 +0000
@@ -69,7 +69,7 @@
6969
70#TODO: Packaging infrastructure for better dependency generation,70#TODO: Packaging infrastructure for better dependency generation,
71# ala pkg-xorg's xviddriver:Provides and ABI detection.71# ala pkg-xorg's xviddriver:Provides and ABI detection.
72Package: libmirserver4172Package: libmirserver42
73Section: libs73Section: libs
74Architecture: linux-any74Architecture: linux-any
75Multi-Arch: same75Multi-Arch: same
@@ -135,7 +135,7 @@
135Architecture: linux-any135Architecture: linux-any
136Multi-Arch: same136Multi-Arch: same
137Pre-Depends: ${misc:Pre-Depends}137Pre-Depends: ${misc:Pre-Depends}
138Depends: libmirserver41 (= ${binary:Version}),138Depends: libmirserver42 (= ${binary:Version}),
139 libmirplatform-dev (= ${binary:Version}),139 libmirplatform-dev (= ${binary:Version}),
140 libmircommon-dev (= ${binary:Version}),140 libmircommon-dev (= ${binary:Version}),
141 libglm-dev,141 libglm-dev,
142142
=== renamed file 'debian/libmirserver41.install' => 'debian/libmirserver42.install'
--- debian/libmirserver41.install 2016-06-30 11:36:23 +0000
+++ debian/libmirserver42.install 2016-09-13 09:13:43 +0000
@@ -1,1 +1,1 @@
1usr/lib/*/libmirserver.so.411usr/lib/*/libmirserver.so.42
22
=== modified file 'examples/render_surfaces.cpp'
--- examples/render_surfaces.cpp 2016-09-02 10:44:49 +0000
+++ examples/render_surfaces.cpp 2016-09-13 09:13:43 +0000
@@ -229,6 +229,8 @@
229 virtual void paused() override {}229 virtual void paused() override {}
230 virtual void resumed() override {}230 virtual void resumed() override {}
231 virtual void started() override {callback(); callback = []{}; }231 virtual void started() override {callback(); callback = []{}; }
232 virtual void ready_for_user_input() override {}
233 virtual void stop_receiving_input() override {}
232234
233 std::function<void()> callback;235 std::function<void()> callback;
234 };236 };
235237
=== modified file 'include/server/mir/server_status_listener.h'
--- include/server/mir/server_status_listener.h 2013-10-15 09:22:41 +0000
+++ include/server/mir/server_status_listener.h 2016-09-13 09:13:43 +0000
@@ -29,6 +29,8 @@
29 virtual void resumed() = 0;29 virtual void resumed() = 0;
30 virtual void started() = 0;30 virtual void started() = 0;
3131
32 virtual void ready_for_user_input() = 0;
33 virtual void stop_receiving_input() = 0;
32protected:34protected:
33 ServerStatusListener() = default;35 ServerStatusListener() = default;
34 virtual ~ServerStatusListener() = default;36 virtual ~ServerStatusListener() = default;
3537
=== modified file 'src/include/platform/mir/options/configuration.h'
--- src/include/platform/mir/options/configuration.h 2016-07-29 17:33:01 +0000
+++ src/include/platform/mir/options/configuration.h 2016-09-13 09:13:43 +0000
@@ -51,6 +51,7 @@
51extern char const* const nbuffers_opt;51extern char const* const nbuffers_opt;
52extern char const* const composite_delay_opt;52extern char const* const composite_delay_opt;
53extern char const* const enable_key_repeat_opt;53extern char const* const enable_key_repeat_opt;
54extern char const* const control_host_cursor_opt;
5455
55extern char const* const name_opt;56extern char const* const name_opt;
5657
5758
=== modified file 'src/include/server/mir/default_server_status_listener.h'
--- src/include/server/mir/default_server_status_listener.h 2015-02-22 07:46:25 +0000
+++ src/include/server/mir/default_server_status_listener.h 2016-09-13 09:13:43 +0000
@@ -37,6 +37,14 @@
37 virtual void started()37 virtual void started()
38 {38 {
39 }39 }
40
41 virtual void ready_for_user_input()
42 {
43 }
44
45 virtual void stop_receiving_input()
46 {
47 }
40};48};
41}49}
4250
4351
=== modified file 'src/platform/options/default_configuration.cpp'
--- src/platform/options/default_configuration.cpp 2016-07-29 17:33:01 +0000
+++ src/platform/options/default_configuration.cpp 2016-09-13 09:13:43 +0000
@@ -52,6 +52,7 @@
52char const* const mo::nbuffers_opt = "nbuffers";52char const* const mo::nbuffers_opt = "nbuffers";
53char const* const mo::composite_delay_opt = "composite-delay";53char const* const mo::composite_delay_opt = "composite-delay";
54char const* const mo::enable_key_repeat_opt = "enable-key-repeat";54char const* const mo::enable_key_repeat_opt = "enable-key-repeat";
55char const* const mo::control_host_cursor_opt = "control-host-cursor";
5556
56char const* const mo::off_opt_value = "off";57char const* const mo::off_opt_value = "off";
57char const* const mo::log_opt_value = "log";58char const* const mo::log_opt_value = "log";
@@ -150,6 +151,8 @@
150 "Library to use for platform input support (default: input-stub.so)")151 "Library to use for platform input support (default: input-stub.so)")
151 (platform_path, po::value<std::string>()->default_value(MIR_SERVER_PLATFORM_PATH),152 (platform_path, po::value<std::string>()->default_value(MIR_SERVER_PLATFORM_PATH),
152 "Directory to look for platform libraries (default: " MIR_SERVER_PLATFORM_PATH ")")153 "Directory to look for platform libraries (default: " MIR_SERVER_PLATFORM_PATH ")")
154 (control_host_cursor_opt,
155 "Control host cursor position from nested server")
153 (enable_input_opt, po::value<bool>()->default_value(enable_input_default),156 (enable_input_opt, po::value<bool>()->default_value(enable_input_default),
154 "Enable input.")157 "Enable input.")
155 (compositor_report_opt, po::value<std::string>()->default_value(off_opt_value),158 (compositor_report_opt, po::value<std::string>()->default_value(off_opt_value),
156159
=== modified file 'src/platform/symbols.map'
--- src/platform/symbols.map 2016-09-02 16:51:10 +0000
+++ src/platform/symbols.map 2016-09-13 09:13:43 +0000
@@ -96,6 +96,7 @@
96# Why are server-only options here in libmirplatform?...96# Why are server-only options here in libmirplatform?...
97 mir::options::composite_delay_opt*; 97 mir::options::composite_delay_opt*;
98 mir::options::compositor_report_opt*;98 mir::options::compositor_report_opt*;
99 mir::options::control_host_cursor_opt*;
99 mir::options::Configuration::?Configuration*;100 mir::options::Configuration::?Configuration*;
100 mir::options::Configuration::Configuration*;101 mir::options::Configuration::Configuration*;
101 mir::options::Configuration::operator*;102 mir::options::Configuration::operator*;
102103
=== modified file 'src/platforms/mesa/server/kms/bypass.cpp'
--- src/platforms/mesa/server/kms/bypass.cpp 2016-08-24 02:09:08 +0000
+++ src/platforms/mesa/server/kms/bypass.cpp 2016-09-13 09:13:43 +0000
@@ -19,6 +19,7 @@
19#include "mir/graphics/renderable.h"19#include "mir/graphics/renderable.h"
20#include "mir/graphics/display_buffer.h"20#include "mir/graphics/display_buffer.h"
21#include "bypass.h"21#include "bypass.h"
22#include <iostream>
2223
23using namespace mir;24using namespace mir;
24namespace mgm = mir::graphics::mesa;25namespace mgm = mir::graphics::mesa;
@@ -35,8 +36,9 @@
35 if (!bypass_is_feasible)36 if (!bypass_is_feasible)
36 return false;37 return false;
3738
39 mir::geometry::Rectangle const empty{};
38 //offscreen surfaces don't affect if bypass is possible40 //offscreen surfaces don't affect if bypass is possible
39 if (!view_area.overlaps(renderable->screen_position()))41 if (view_area.intersection_with(renderable->screen_position()) == empty)
40 return false;42 return false;
4143
42 auto const is_opaque = !((renderable->alpha() != 1.0f) || renderable->shaped());44 auto const is_opaque = !((renderable->alpha() != 1.0f) || renderable->shaped());
4345
=== modified file 'src/renderers/gl/renderer.cpp'
--- src/renderers/gl/renderer.cpp 2016-06-02 05:33:50 +0000
+++ src/renderers/gl/renderer.cpp 2016-09-13 09:13:43 +0000
@@ -36,6 +36,7 @@
3636
37#include <boost/throw_exception.hpp>37#include <boost/throw_exception.hpp>
38#include <stdexcept>38#include <stdexcept>
39#include <iostream>
39#include <cmath>40#include <cmath>
4041
41namespace mg = mir::graphics;42namespace mg = mir::graphics;
4243
=== modified file 'src/server/CMakeLists.txt'
--- src/server/CMakeLists.txt 2016-08-08 03:22:02 +0000
+++ src/server/CMakeLists.txt 2016-09-13 09:13:43 +0000
@@ -124,7 +124,7 @@
124 ${CMAKE_SOURCE_DIR}/include/server/mir DESTINATION "include/mirserver"124 ${CMAKE_SOURCE_DIR}/include/server/mir DESTINATION "include/mirserver"
125)125)
126126
127set(MIRSERVER_ABI 41) # Be sure to increment MIR_VERSION_MINOR at the same time127set(MIRSERVER_ABI 42) # Be sure to increment MIR_VERSION_MINOR at the same time
128set(symbol_map ${CMAKE_CURRENT_SOURCE_DIR}/symbols.map)128set(symbol_map ${CMAKE_CURRENT_SOURCE_DIR}/symbols.map)
129129
130set_target_properties(130set_target_properties(
131131
=== modified file 'src/server/graphics/default_configuration.cpp'
--- src/server/graphics/default_configuration.cpp 2016-07-26 03:56:14 +0000
+++ src/server/graphics/default_configuration.cpp 2016-09-13 09:13:43 +0000
@@ -228,11 +228,15 @@
228 auto const my_name = options->is_set(options::name_opt) ?228 auto const my_name = options->is_set(options::name_opt) ?
229 options->get<std::string>(options::name_opt) :229 options->get<std::string>(options::name_opt) :
230 "nested-mir@:" + server_socket;230 "nested-mir@:" + server_socket;
231 auto const cursor_mode = options->is_set(options::control_host_cursor_opt) ?
232 mgn::MirClientHostConnection::CursorMode::ReplaceHostCursor :
233 mgn::MirClientHostConnection::CursorMode::ForwardCursorImage;
231234
232 return std::make_shared<graphics::nested::MirClientHostConnection>(235 return std::make_shared<mgn::MirClientHostConnection>(
233 host_socket,236 host_socket,
234 my_name,237 my_name,
235 the_host_lifecycle_event_listener()238 the_host_lifecycle_event_listener(),
239 cursor_mode
236 );240 );
237 });241 });
238}242}
239243
=== modified file 'src/server/graphics/nested/cursor.cpp'
--- src/server/graphics/nested/cursor.cpp 2016-01-29 08:18:22 +0000
+++ src/server/graphics/nested/cursor.cpp 2016-09-13 09:13:43 +0000
@@ -35,8 +35,9 @@
35{35{
36}36}
3737
38void mgn::Cursor::move_to(geom::Point)38void mgn::Cursor::move_to(geom::Point pos)
39{39{
40 connection->set_cursor_position(pos);
40}41}
4142
42void mgn::Cursor::show(mg::CursorImage const& cursor_image)43void mgn::Cursor::show(mg::CursorImage const& cursor_image)
4344
=== modified file 'src/server/graphics/nested/display_buffer.cpp'
--- src/server/graphics/nested/display_buffer.cpp 2016-08-25 13:34:29 +0000
+++ src/server/graphics/nested/display_buffer.cpp 2016-09-13 09:13:43 +0000
@@ -52,6 +52,7 @@
52 mg::BufferProperties properties(output.extents().size, output.current_format, mg::BufferUsage::hardware);52 mg::BufferProperties properties(output.extents().size, output.current_format, mg::BufferUsage::hardware);
53 return connection.create_surface(53 return connection.create_surface(
54 host_stream, mir::geometry::Displacement{0, 0}, properties,54 host_stream, mir::geometry::Displacement{0, 0}, properties,
55 output.extents(),
55 surface_title.str().c_str(), static_cast<uint32_t>(output.id.as_value()));56 surface_title.str().c_str(), static_cast<uint32_t>(output.id.as_value()));
56}57}
57}58}
5859
=== modified file 'src/server/graphics/nested/host_connection.h'
--- src/server/graphics/nested/host_connection.h 2016-09-01 16:26:31 +0000
+++ src/server/graphics/nested/host_connection.h 2016-09-13 09:13:43 +0000
@@ -60,9 +60,11 @@
60 std::shared_ptr<HostStream> const& stream,60 std::shared_ptr<HostStream> const& stream,
61 geometry::Displacement stream_displacement,61 geometry::Displacement stream_displacement,
62 graphics::BufferProperties properties,62 graphics::BufferProperties properties,
63 geometry::Rectangle const& extents,
63 char const* name, uint32_t output_id) = 0;64 char const* name, uint32_t output_id) = 0;
6465
65 virtual void set_cursor_image(CursorImage const& image) = 0;66 virtual void set_cursor_image(CursorImage const& image) = 0;
67 virtual void set_cursor_position(geometry::Point const& pos) = 0;
66 virtual void hide_cursor() = 0;68 virtual void hide_cursor() = 0;
67 virtual auto graphics_platform_library() -> std::string = 0;69 virtual auto graphics_platform_library() -> std::string = 0;
6870
6971
=== modified file 'src/server/graphics/nested/input_platform.cpp'
--- src/server/graphics/nested/input_platform.cpp 2016-07-07 09:59:19 +0000
+++ src/server/graphics/nested/input_platform.cpp 2016-09-13 09:13:43 +0000
@@ -217,6 +217,7 @@
217217
218 if (event_type == mir_event_type_input)218 if (event_type == mir_event_type_input)
219 {219 {
220 std::lock_guard<std::mutex> lock(devices_guard);
220 auto const* input_ev = mir_event_get_input_event(&event);221 auto const* input_ev = mir_event_get_input_event(&event);
221 auto const id = mir_input_event_get_device_id(input_ev);222 auto const id = mir_input_event_get_device_id(input_ev);
222 auto it = devices.find(id);223 auto it = devices.find(id);
@@ -234,6 +235,7 @@
234 }235 }
235 else if (event_type == mir_event_type_input_device_state)236 else if (event_type == mir_event_type_input_device_state)
236 {237 {
238 std::lock_guard<std::mutex> lock(devices_guard);
237 if (!devices.empty())239 if (!devices.empty())
238 {240 {
239 auto const* device_state = mir_event_get_input_device_state_event(&event);241 auto const* device_state = mir_event_get_input_device_state_event(&event);
@@ -270,6 +272,7 @@
270 std::function<void(MirEvent const&, mir::geometry::Rectangle const&)> empty_event_callback;272 std::function<void(MirEvent const&, mir::geometry::Rectangle const&)> empty_event_callback;
271 connection->set_input_event_callback(empty_event_callback);273 connection->set_input_event_callback(empty_event_callback);
272274
275 std::lock_guard<std::mutex> lock(devices_guard);
273 for(auto const& device : devices)276 for(auto const& device : devices)
274 input_device_registry->remove_device(device.second);277 input_device_registry->remove_device(device.second);
275278
276279
=== modified file 'src/server/graphics/nested/mir_client_host_connection.cpp'
--- src/server/graphics/nested/mir_client_host_connection.cpp 2016-09-01 16:26:31 +0000
+++ src/server/graphics/nested/mir_client_host_connection.cpp 2016-09-13 09:13:43 +0000
@@ -24,6 +24,7 @@
24#include "mir/raii.h"24#include "mir/raii.h"
25#include "mir/graphics/platform_operation_message.h"25#include "mir/graphics/platform_operation_message.h"
26#include "mir/graphics/cursor_image.h"26#include "mir/graphics/cursor_image.h"
27#include "mir/geometry/point.h"
27#include "mir/input/device.h"28#include "mir/input/device.h"
28#include "mir/input/device_capability.h"29#include "mir/input/device_capability.h"
29#include "mir/input/pointer_configuration.h"30#include "mir/input/pointer_configuration.h"
@@ -44,6 +45,7 @@
44namespace mgn = mir::graphics::nested;45namespace mgn = mir::graphics::nested;
45namespace mi = mir::input;46namespace mi = mir::input;
46namespace mf = mir::frontend;47namespace mf = mir::frontend;
48namespace geom = mir::geometry;
4749
48namespace50namespace
49{51{
@@ -90,15 +92,13 @@
90 }92 }
91}93}
9294
93class MirClientHostSurface : public mgn::HostSurface95class BasicHostSurface : public mgn::HostSurface
94{96{
95public:97public:
96 MirClientHostSurface(98 BasicHostSurface(MirConnection* mir_connection,
97 MirConnection* mir_connection,99 MirSurfaceSpec* spec,
98 MirSurfaceSpec* spec)100 std::shared_ptr<mgn::HostStream> const& stream)
99 : mir_connection(mir_connection),101 : mir_connection(mir_connection), mir_surface(mir_surface_create_sync(spec)), main_stream(stream)
100 mir_surface{
101 mir_surface_create_sync(spec)}
102 {102 {
103 if (!mir_surface_is_valid(mir_surface))103 if (!mir_surface_is_valid(mir_surface))
104 {104 {
@@ -107,16 +107,15 @@
107 }107 }
108 }108 }
109109
110 ~MirClientHostSurface()110 ~BasicHostSurface()
111 {111 {
112 if (cursor) mir_buffer_stream_release_sync(cursor);112 destroy_cursor();
113 mir_surface_release_sync(mir_surface);113 mir_surface_release_sync(mir_surface);
114 }114 }
115115
116 EGLNativeWindowType egl_native_window() override116 EGLNativeWindowType egl_native_window() override
117 {117 {
118 return reinterpret_cast<EGLNativeWindowType>(118 return main_stream->egl_native_window();
119 mir_buffer_stream_get_egl_native_window(mir_surface_get_buffer_stream(mir_surface)));
120 }119 }
121120
122 void set_event_handler(mir_surface_event_callback cb, void* context) override121 void set_event_handler(mir_surface_event_callback cb, void* context) override
@@ -124,7 +123,12 @@
124 mir_surface_set_event_handler(mir_surface, cb, context);123 mir_surface_set_event_handler(mir_surface, cb, context);
125 }124 }
126125
127 void set_cursor_image(mg::CursorImage const& image)126 virtual void set_cursor_image(mg::CursorImage const& image) = 0;
127 virtual void set_cursor_position(geom::Point const&) = 0;
128 virtual void hide_cursor() = 0;
129
130protected:
131 bool update_cursor(mg::CursorImage const& image)
128 {132 {
129 auto const image_width = image.size().width.as_int();133 auto const image_width = image.size().width.as_int();
130 auto const image_height = image.size().height.as_int();134 auto const image_height = image.size().height.as_int();
@@ -132,7 +136,7 @@
132 if ((image_width <= 0) || (image_height <= 0))136 if ((image_width <= 0) || (image_height <= 0))
133 {137 {
134 hide_cursor();138 hide_cursor();
135 return;139 return true;
136 }140 }
137141
138 MirGraphicsRegion g;142 MirGraphicsRegion g;
@@ -166,9 +170,43 @@
166170
167 mir_buffer_stream_swap_buffers_sync(cursor);171 mir_buffer_stream_swap_buffers_sync(cursor);
168172
169 if (new_cursor || cursor_hotspot != image.hotspot())173 return new_cursor;
170 {174 }
171 cursor_hotspot = image.hotspot();175
176 void destroy_cursor()
177 {
178 if (cursor)
179 {
180 mir_buffer_stream_release_sync(cursor);
181 cursor = nullptr;
182 }
183 }
184
185 MirConnection* const mir_connection;
186 MirSurface* const mir_surface;
187 std::shared_ptr<mgn::HostStream> const main_stream;
188 MirBufferStream* cursor{nullptr};
189
190};
191
192class CursorConfigurationHostSurface : public BasicHostSurface
193{
194public:
195 CursorConfigurationHostSurface(
196 MirConnection* connection,
197 MirSurfaceSpec* spec,
198 std::shared_ptr<mgn::HostStream> const& stream)
199 : BasicHostSurface(connection, spec, stream)
200 {
201 }
202
203 void set_cursor_image(mg::CursorImage const& image) override
204 {
205 bool new_cursor = update_cursor(image);
206
207 if (new_cursor)
208 {
209 auto cursor_hotspot = image.hotspot();
172210
173 auto conf = mir_cursor_configuration_from_buffer_stream(211 auto conf = mir_cursor_configuration_from_buffer_stream(
174 cursor, cursor_hotspot.dx.as_int(), cursor_hotspot.dy.as_int());212 cursor, cursor_hotspot.dx.as_int(), cursor_hotspot.dy.as_int());
@@ -178,20 +216,103 @@
178 }216 }
179 }217 }
180218
181 void hide_cursor()219 void set_cursor_position(geom::Point const&) override
182 {220 {
183 if (cursor) { mir_buffer_stream_release_sync(cursor); cursor = nullptr; }221 }
184222
185 auto conf = mir_cursor_configuration_from_name(mir_disabled_cursor_name);223 void hide_cursor() override
186 mir_surface_configure_cursor(mir_surface, conf);224 {
187 mir_cursor_configuration_destroy(conf);225 destroy_cursor();
188 }226 auto conf = mir_cursor_configuration_from_name(mir_disabled_cursor_name);
189227 mir_surface_configure_cursor(mir_surface, conf);
228 mir_cursor_configuration_destroy(conf);
229 }
230};
231
232class BufferStreamCursorHostSurface : public BasicHostSurface
233{
234public:
235 BufferStreamCursorHostSurface(
236 MirConnection* mir_connection,
237 MirSurfaceSpec* spec,
238 std::shared_ptr<mgn::HostStream> const stream,
239 geom::Rectangle const& extents)
240 : BasicHostSurface(mir_connection, spec, stream),
241 extents{extents}
242 {
243 // hide cursor in surface:
244 auto conf = mir_cursor_configuration_from_name(mir_disabled_cursor_name);
245 mir_surface_configure_cursor(mir_surface, conf);
246 mir_cursor_configuration_destroy(conf);
247 }
248
249 void set_cursor_image(mg::CursorImage const& image)
250 {
251 bool force_update = update_cursor(image);
252 cursor_size = image.size();
253 update_cursor_position(force_update, cursor_position, image.hotspot());
254 }
255
256 void set_cursor_position(geom::Point const& pos) override
257 {
258 if (pos != cursor_position && cursor)
259 {
260 update_cursor_position(false, pos, cursor_hotspot);
261 }
262 }
263
264 void update_cursor_position(bool force_update, geom::Point const& pos, geom::Displacement const& new_hotspot)
265 {
266 auto old_cursor_bounds = geom::Rectangle{cursor_position - cursor_hotspot, cursor_size};
267 auto new_cursor_bounds = geom::Rectangle{pos - new_hotspot, cursor_size};
268 auto stream_position = new_cursor_bounds.top_left - extents.top_left;
269 auto was_visible = extents.overlaps(old_cursor_bounds);
270 auto is_visible = extents.overlaps(new_cursor_bounds);
271 auto changed = force_update || pos != cursor_position || cursor_hotspot != new_hotspot;
272
273 if (changed)
274 {
275 cursor_hotspot = new_hotspot;
276 cursor_position = pos;
277 }
278
279 if (was_visible && !is_visible)
280 {
281 hide_cursor();
282 }
283 else if (changed && is_visible)
284 {
285 std::unique_ptr<MirSurfaceSpec, void(*)(MirSurfaceSpec *)> spec{
286 mir_connection_create_spec_for_changes(mir_connection),
287 mir_surface_spec_release};
288
289 MirBufferStreamInfo infos[2] =
290 {
291 {main_stream->handle(), 0, 0},
292 {cursor, stream_position.dx.as_int(), stream_position.dy.as_int()}
293 };
294 mir_surface_spec_set_streams(spec.get(), infos, 2);
295
296 mir_surface_apply_spec(mir_surface, spec.get());
297 }
298 }
299
300 void hide_cursor() override
301 {
302 std::unique_ptr<MirSurfaceSpec, void(*)(MirSurfaceSpec *)> spec{
303 mir_connection_create_spec_for_changes(mir_connection),
304 mir_surface_spec_release};
305
306 MirBufferStreamInfo stream = {main_stream->handle(), 0, 0};
307 mir_surface_spec_set_streams(spec.get(), &stream, 1);
308
309 mir_surface_apply_spec(mir_surface, spec.get());
310 }
190private:311private:
191 MirConnection* const mir_connection;
192 MirSurface* const mir_surface;
193 MirBufferStream* cursor{nullptr};
194 mir::geometry::Displacement cursor_hotspot;312 mir::geometry::Displacement cursor_hotspot;
313 geom::Rectangle extents;
314 geom::Point cursor_position;
315 geom::Size cursor_size;
195};316};
196317
197class MirClientHostStream : public mgn::HostStream318class MirClientHostStream : public mgn::HostStream
@@ -261,11 +382,13 @@
261mgn::MirClientHostConnection::MirClientHostConnection(382mgn::MirClientHostConnection::MirClientHostConnection(
262 std::string const& host_socket,383 std::string const& host_socket,
263 std::string const& name,384 std::string const& name,
264 std::shared_ptr<msh::HostLifecycleEventListener> const& host_lifecycle_event_listener)385 std::shared_ptr<msh::HostLifecycleEventListener> const& host_lifecycle_event_listener,
386 CursorMode cursor_mode)
265 : mir_connection{mir_connect_sync(host_socket.c_str(), name.c_str())},387 : mir_connection{mir_connect_sync(host_socket.c_str(), name.c_str())},
266 conf_change_callback{[]{}},388 conf_change_callback{[]{}},
267 host_lifecycle_event_listener{host_lifecycle_event_listener},389 host_lifecycle_event_listener{host_lifecycle_event_listener},
268 event_callback{[](MirEvent const&, mir::geometry::Rectangle const&){}}390 event_callback{[](MirEvent const&, mir::geometry::Rectangle const&){}},
391 cursor_mode{cursor_mode}
269{392{
270 if (!mir_connection_is_valid(mir_connection))393 if (!mir_connection_is_valid(mir_connection))
271 {394 {
@@ -340,8 +463,10 @@
340 std::shared_ptr<HostStream> const& stream,463 std::shared_ptr<HostStream> const& stream,
341 mir::geometry::Displacement displacement,464 mir::geometry::Displacement displacement,
342 mg::BufferProperties properties,465 mg::BufferProperties properties,
466 geometry::Rectangle const& extents,
343 char const* name, uint32_t output_id)467 char const* name, uint32_t output_id)
344{468{
469 // TODO pass down Displacement
345 std::lock_guard<std::mutex> lg(surfaces_mutex);470 std::lock_guard<std::mutex> lg(surfaces_mutex);
346 auto spec = mir::raii::deleter_for(471 auto spec = mir::raii::deleter_for(
347 mir_connection_create_spec_for_normal_surface(472 mir_connection_create_spec_for_normal_surface(
@@ -352,7 +477,7 @@
352 mir_surface_spec_release);477 mir_surface_spec_release);
353478
354 MirBufferUsage usage = (properties.usage == mg::BufferUsage::hardware) ?479 MirBufferUsage usage = (properties.usage == mg::BufferUsage::hardware) ?
355 mir_buffer_usage_hardware : mir_buffer_usage_software; 480 mir_buffer_usage_hardware : mir_buffer_usage_software;
356481
357 mir_surface_spec_set_name(spec.get(), name);482 mir_surface_spec_set_name(spec.get(), name);
358 mir_surface_spec_set_buffer_usage(spec.get(), usage);483 mir_surface_spec_set_buffer_usage(spec.get(), usage);
@@ -360,16 +485,31 @@
360 MirBufferStreamInfo info { stream->handle(), displacement.dx.as_int(), displacement.dy.as_int() };485 MirBufferStreamInfo info { stream->handle(), displacement.dx.as_int(), displacement.dy.as_int() };
361 mir_surface_spec_set_streams(spec.get(), &info, 1);486 mir_surface_spec_set_streams(spec.get(), &info, 1);
362487
363 auto surf = std::shared_ptr<MirClientHostSurface>(488 std::shared_ptr<BasicHostSurface> surf;
364 new MirClientHostSurface(mir_connection, spec.get()),489 if (cursor_mode == CursorMode::ReplaceHostCursor)
365 [this](MirClientHostSurface *surf)490 {
366 {491 surf = std::shared_ptr<BasicHostSurface>(
367 std::lock_guard<std::mutex> lg(surfaces_mutex);492 new BufferStreamCursorHostSurface(mir_connection, spec.get(), stream, extents),
368 auto it = std::find(surfaces.begin(), surfaces.end(), surf);493 [this](BufferStreamCursorHostSurface *surf)
369 surfaces.erase(it);494 {
370 delete surf;495 std::lock_guard<std::mutex> lg(surfaces_mutex);
371 });496 auto it = std::find(surfaces.begin(), surfaces.end(), surf);
372497 surfaces.erase(it);
498 delete surf;
499 });
500 }
501 else
502 {
503 surf = std::shared_ptr<BasicHostSurface>(
504 new CursorConfigurationHostSurface(mir_connection, spec.get(), stream),
505 [this](CursorConfigurationHostSurface *surf)
506 {
507 std::lock_guard<std::mutex> lg(surfaces_mutex);
508 auto it = std::find(surfaces.begin(), surfaces.end(), surf);
509 surfaces.erase(it);
510 delete surf;
511 });
512 }
373 if (stored_cursor_image.size().width.as_int() * stored_cursor_image.size().height.as_int())513 if (stored_cursor_image.size().width.as_int() * stored_cursor_image.size().height.as_int())
374 surf->set_cursor_image(stored_cursor_image);514 surf->set_cursor_image(stored_cursor_image);
375515
@@ -409,20 +549,32 @@
409void mgn::MirClientHostConnection::set_cursor_image(mg::CursorImage const& image)549void mgn::MirClientHostConnection::set_cursor_image(mg::CursorImage const& image)
410{550{
411 std::lock_guard<std::mutex> lg(surfaces_mutex);551 std::lock_guard<std::mutex> lg(surfaces_mutex);
552
412 stored_cursor_image = image;553 stored_cursor_image = image;
554
413 for (auto s : surfaces)555 for (auto s : surfaces)
414 {556 {
415 auto surface = static_cast<MirClientHostSurface*>(s);557 auto surface = static_cast<BasicHostSurface*>(s);
416 surface->set_cursor_image(stored_cursor_image);558 surface->set_cursor_image(stored_cursor_image);
417 }559 }
418}560}
419561
562void mgn::MirClientHostConnection::set_cursor_position(geom::Point const& pos)
563{
564 std::lock_guard<std::mutex> lg(surfaces_mutex);
565 for (auto s : surfaces)
566 {
567 auto surface = static_cast<BasicHostSurface*>(s);
568 surface->set_cursor_position(pos);
569 }
570}
571
420void mgn::MirClientHostConnection::hide_cursor()572void mgn::MirClientHostConnection::hide_cursor()
421{573{
422 std::lock_guard<std::mutex> lg(surfaces_mutex);574 std::lock_guard<std::mutex> lg(surfaces_mutex);
423 for (auto s : surfaces)575 for (auto s : surfaces)
424 {576 {
425 auto surface = static_cast<MirClientHostSurface*>(s);577 auto surface = static_cast<BasicHostSurface*>(s);
426 surface->hide_cursor();578 surface->hide_cursor();
427 }579 }
428}580}
429581
=== modified file 'src/server/graphics/nested/mir_client_host_connection.h'
--- src/server/graphics/nested/mir_client_host_connection.h 2016-09-01 16:26:31 +0000
+++ src/server/graphics/nested/mir_client_host_connection.h 2016-09-13 09:13:43 +0000
@@ -54,9 +54,16 @@
54class MirClientHostConnection : public HostConnection54class MirClientHostConnection : public HostConnection
55{55{
56public:56public:
57 enum class CursorMode
58 {
59 ForwardCursorImage,
60 ReplaceHostCursor
61 };
62
57 MirClientHostConnection(std::string const& host_socket,63 MirClientHostConnection(std::string const& host_socket,
58 std::string const& name,64 std::string const& name,
59 std::shared_ptr<msh::HostLifecycleEventListener> const& host_lifecycle_event_listener);65 std::shared_ptr<msh::HostLifecycleEventListener> const& host_lifecycle_event_listener,
66 CursorMode mode);
60 ~MirClientHostConnection();67 ~MirClientHostConnection();
6168
62 std::vector<int> platform_fd_items() override;69 std::vector<int> platform_fd_items() override;
@@ -67,11 +74,13 @@
67 std::shared_ptr<HostStream> const& stream,74 std::shared_ptr<HostStream> const& stream,
68 geometry::Displacement stream_displacement,75 geometry::Displacement stream_displacement,
69 graphics::BufferProperties properties,76 graphics::BufferProperties properties,
77 geometry::Rectangle const& extents,
70 char const* name, uint32_t output_id) override;78 char const* name, uint32_t output_id) override;
71 void set_display_config_change_callback(std::function<void()> const& cb) override;79 void set_display_config_change_callback(std::function<void()> const& cb) override;
72 void apply_display_config(MirDisplayConfiguration&) override;80 void apply_display_config(MirDisplayConfiguration&) override;
7381
74 void set_cursor_image(CursorImage const& image) override;82 void set_cursor_image(CursorImage const& image) override;
83 void set_cursor_position(geometry::Point const& pos) override;
75 void hide_cursor() override;84 void hide_cursor() override;
76 auto graphics_platform_library() -> std::string override;85 auto graphics_platform_library() -> std::string override;
7786
@@ -114,6 +123,7 @@
114 std::vector<uint8_t> buffer;123 std::vector<uint8_t> buffer;
115 };124 };
116 NestedCursorImage stored_cursor_image;125 NestedCursorImage stored_cursor_image;
126 CursorMode cursor_mode;
117};127};
118128
119}129}
120130
=== modified file 'src/server/input/default_configuration.cpp'
--- src/server/input/default_configuration.cpp 2016-07-28 23:00:22 +0000
+++ src/server/input/default_configuration.cpp 2016-09-13 09:13:43 +0000
@@ -361,7 +361,8 @@
361 the_input_reading_multiplexer(),361 the_input_reading_multiplexer(),
362 the_main_loop(),362 the_main_loop(),
363 the_cookie_authority(),363 the_cookie_authority(),
364 the_key_mapper());364 the_key_mapper(),
365 the_server_status_listener());
365366
366 if (key_repeater && !the_options()->is_set(options::host_socket_opt))367 if (key_repeater && !the_options()->is_set(options::host_socket_opt))
367 key_repeater->set_input_device_hub(hub);368 key_repeater->set_input_device_hub(hub);
368369
=== modified file 'src/server/input/default_input_device_hub.cpp'
--- src/server/input/default_input_device_hub.cpp 2016-07-07 09:59:19 +0000
+++ src/server/input/default_input_device_hub.cpp 2016-09-13 09:13:43 +0000
@@ -22,6 +22,7 @@
22#include "mir/input/input_device.h"22#include "mir/input/input_device.h"
23#include "mir/input/input_device_observer.h"23#include "mir/input/input_device_observer.h"
24#include "mir/geometry/point.h"24#include "mir/geometry/point.h"
25#include "mir/server_status_listener.h"
25#include "mir/dispatch/multiplexing_dispatchable.h"26#include "mir/dispatch/multiplexing_dispatchable.h"
26#include "mir/dispatch/action_queue.h"27#include "mir/dispatch/action_queue.h"
27#include "mir/frontend/event_sink.h"28#include "mir/frontend/event_sink.h"
@@ -44,7 +45,8 @@
44 std::shared_ptr<dispatch::MultiplexingDispatchable> const& input_multiplexer,45 std::shared_ptr<dispatch::MultiplexingDispatchable> const& input_multiplexer,
45 std::shared_ptr<mir::ServerActionQueue> const& observer_queue,46 std::shared_ptr<mir::ServerActionQueue> const& observer_queue,
46 std::shared_ptr<mir::cookie::Authority> const& cookie_authority,47 std::shared_ptr<mir::cookie::Authority> const& cookie_authority,
47 std::shared_ptr<mi::KeyMapper> const& key_mapper)48 std::shared_ptr<mi::KeyMapper> const& key_mapper,
49 std::shared_ptr<mir::ServerStatusListener> const& server_status_listener)
48 : seat{seat},50 : seat{seat},
49 sink{sink},51 sink{sink},
50 input_dispatchable{input_multiplexer},52 input_dispatchable{input_multiplexer},
@@ -52,6 +54,7 @@
52 device_queue(std::make_shared<dispatch::ActionQueue>()),54 device_queue(std::make_shared<dispatch::ActionQueue>()),
53 cookie_authority(cookie_authority),55 cookie_authority(cookie_authority),
54 key_mapper(key_mapper),56 key_mapper(key_mapper),
57 server_status_listener(server_status_listener),
55 device_id_generator{0}58 device_id_generator{0}
56{59{
57 input_dispatchable->add_watch(device_queue);60 input_dispatchable->add_watch(device_queue);
@@ -264,6 +267,12 @@
264 observer->device_added(handles.back());267 observer->device_added(handles.back());
265 observer->changes_complete();268 observer->changes_complete();
266 }269 }
270
271 if (!ready && handles.size())
272 {
273 server_status_listener->ready_for_user_input();
274 ready = true;
275 }
267}276}
268277
269void mi::DefaultInputDeviceHub::remove_device_handle(MirInputDeviceId id)278void mi::DefaultInputDeviceHub::remove_device_handle(MirInputDeviceId id)
@@ -289,4 +298,10 @@
289298
290 handles.erase(handle_it, end(handles));299 handles.erase(handle_it, end(handles));
291 sink->handle_input_device_change(handles);300 sink->handle_input_device_change(handles);
301
302 if (ready && 0 == handles.size())
303 {
304 ready = false;
305 server_status_listener->stop_receiving_input();
306 }
292}307}
293308
=== modified file 'src/server/input/default_input_device_hub.h'
--- src/server/input/default_input_device_hub.h 2016-07-07 09:59:19 +0000
+++ src/server/input/default_input_device_hub.h 2016-09-13 09:13:43 +0000
@@ -37,6 +37,7 @@
37namespace mir37namespace mir
38{38{
39class ServerActionQueue;39class ServerActionQueue;
40class ServerStatusListener;
40namespace frontend41namespace frontend
41{42{
42class EventSink;43class EventSink;
@@ -67,7 +68,8 @@
67 std::shared_ptr<dispatch::MultiplexingDispatchable> const& input_multiplexer,68 std::shared_ptr<dispatch::MultiplexingDispatchable> const& input_multiplexer,
68 std::shared_ptr<ServerActionQueue> const& observer_queue,69 std::shared_ptr<ServerActionQueue> const& observer_queue,
69 std::shared_ptr<cookie::Authority> const& cookie_authority,70 std::shared_ptr<cookie::Authority> const& cookie_authority,
70 std::shared_ptr<KeyMapper> const& key_mapper);71 std::shared_ptr<KeyMapper> const& key_mapper,
72 std::shared_ptr<ServerStatusListener> const& server_status_listener);
7173
72 // InputDeviceRegistry - calls from mi::Platform74 // InputDeviceRegistry - calls from mi::Platform
73 void add_device(std::shared_ptr<InputDevice> const& device) override;75 void add_device(std::shared_ptr<InputDevice> const& device) override;
@@ -91,6 +93,7 @@
91 std::shared_ptr<dispatch::ActionQueue> const device_queue;93 std::shared_ptr<dispatch::ActionQueue> const device_queue;
92 std::shared_ptr<cookie::Authority> const cookie_authority;94 std::shared_ptr<cookie::Authority> const cookie_authority;
93 std::shared_ptr<KeyMapper> const key_mapper;95 std::shared_ptr<KeyMapper> const key_mapper;
96 std::shared_ptr<ServerStatusListener> const server_status_listener;
9497
95 struct RegisteredDevice : public InputSink98 struct RegisteredDevice : public InputSink
96 {99 {
@@ -124,6 +127,7 @@
124 std::vector<std::shared_ptr<InputDeviceObserver>> observers;127 std::vector<std::shared_ptr<InputDeviceObserver>> observers;
125128
126 MirInputDeviceId device_id_generator;129 MirInputDeviceId device_id_generator;
130 bool ready{false};
127};131};
128132
129}133}
130134
=== modified file 'src/server/symbols.map'
--- src/server/symbols.map 2016-08-26 19:01:27 +0000
+++ src/server/symbols.map 2016-09-13 09:13:43 +0000
@@ -1,4 +1,4 @@
1MIR_SERVER_0.24 {1MIR_SERVER_0.25 {
2 global:2 global:
3 extern "C++" {3 extern "C++" {
4# Symbols not yet picked up by script4# Symbols not yet picked up by script
@@ -176,6 +176,7 @@
176 mir::Server::override_the_logger*;176 mir::Server::override_the_logger*;
177 mir::Server::override_the_prompt_session_listener*;177 mir::Server::override_the_prompt_session_listener*;
178 mir::Server::override_the_prompt_session_manager*;178 mir::Server::override_the_prompt_session_manager*;
179 mir::Server::override_the_seat_report*;
179 mir::Server::override_the_server_status_listener*;180 mir::Server::override_the_server_status_listener*;
180 mir::Server::override_the_session_authorizer*;181 mir::Server::override_the_session_authorizer*;
181 mir::Server::override_the_session_listener*;182 mir::Server::override_the_session_listener*;
@@ -835,10 +836,3 @@
835 };836 };
836 local: *;837 local: *;
837};838};
838
839MIR_SERVER_0.25 {
840 global:
841 extern "C++" {
842 mir::Server::override_the_seat_report*;
843 };
844} MIR_SERVER_0.24;
845839
=== modified file 'tests/acceptance-tests/test_nested_mir.cpp'
--- tests/acceptance-tests/test_nested_mir.cpp 2016-08-26 11:25:15 +0000
+++ tests/acceptance-tests/test_nested_mir.cpp 2016-09-13 09:13:43 +0000
@@ -24,10 +24,12 @@
24#include "mir/graphics/display_configuration.h"24#include "mir/graphics/display_configuration.h"
25#include "mir/graphics/display_configuration_policy.h"25#include "mir/graphics/display_configuration_policy.h"
26#include "mir/graphics/display_configuration_report.h"26#include "mir/graphics/display_configuration_report.h"
27#include "mir/input/input_device_info.h"
27#include "mir/shell/shell.h"28#include "mir/shell/shell.h"
28#include "mir/input/cursor_listener.h"29#include "mir/input/cursor_listener.h"
29#include "mir/cached_ptr.h"30#include "mir/cached_ptr.h"
30#include "mir/main_loop.h"31#include "mir/main_loop.h"
32#include "mir/server_status_listener.h"
31#include "mir/scene/session_coordinator.h"33#include "mir/scene/session_coordinator.h"
32#include "mir/scene/session.h"34#include "mir/scene/session.h"
33#include "mir/scene/surface.h"35#include "mir/scene/surface.h"
@@ -37,8 +39,10 @@
3739
38#include "mir_test_framework/headless_in_process_server.h"40#include "mir_test_framework/headless_in_process_server.h"
39#include "mir_test_framework/using_stub_client_platform.h"41#include "mir_test_framework/using_stub_client_platform.h"
42#include "mir_test_framework/stub_server_platform_factory.h"
40#include "mir_test_framework/headless_nested_server_runner.h"43#include "mir_test_framework/headless_nested_server_runner.h"
41#include "mir_test_framework/any_surface.h"44#include "mir_test_framework/any_surface.h"
45#include "mir_test_framework/fake_input_device.h"
42#include "mir/test/signal.h"46#include "mir/test/signal.h"
43#include "mir/test/spin_wait.h"47#include "mir/test/spin_wait.h"
44#include "mir/test/display_config_matchers.h"48#include "mir/test/display_config_matchers.h"
@@ -50,12 +54,14 @@
50#include "mir/test/doubles/nested_mock_egl.h"54#include "mir/test/doubles/nested_mock_egl.h"
51#include "mir/test/fake_shared.h"55#include "mir/test/fake_shared.h"
5256
57#include <future>
53#include <gtest/gtest.h>58#include <gtest/gtest.h>
54#include <gmock/gmock.h>59#include <gmock/gmock.h>
5560
56namespace geom = mir::geometry;61namespace geom = mir::geometry;
57namespace mf = mir::frontend;62namespace mf = mir::frontend;
58namespace mg = mir::graphics;63namespace mg = mir::graphics;
64namespace mi = mir::input;
59namespace msh = mir::shell;65namespace msh = mir::shell;
60namespace mt = mir::test;66namespace mt = mir::test;
61namespace mtd = mir::test::doubles;67namespace mtd = mir::test::doubles;
@@ -67,6 +73,23 @@
6773
68namespace74namespace
69{75{
76struct InputReadyServerStatusListener : mir::ServerStatusListener
77{
78 std::shared_ptr<std::promise<void>> const input_ready;
79 InputReadyServerStatusListener(std::shared_ptr<std::promise<void>> const& promise)
80 : input_ready(promise)
81 {}
82
83 void paused() override {}
84 void resumed() override {}
85 void started() override {}
86 void ready_for_user_input() override
87 {
88 input_ready->set_value();
89 }
90 void stop_receiving_input() override {}
91};
92
70struct MockSessionMediatorReport : mf::SessionMediatorReport93struct MockSessionMediatorReport : mf::SessionMediatorReport
71{94{
72 MockSessionMediatorReport()95 MockSessionMediatorReport()
@@ -127,6 +150,7 @@
127};150};
128151
129std::chrono::seconds const timeout{5};152std::chrono::seconds const timeout{5};
153std::chrono::seconds const long_timeout{10};
130154
131// We can't rely on the test environment to have X cursors, so we provide some of our own155// We can't rely on the test environment to have X cursors, so we provide some of our own
132auto const cursor_names = {156auto const cursor_names = {
@@ -403,10 +427,21 @@
403 { return std::make_shared<NiceMock<MockDisplayConfigurationPolicy>>(); });427 { return std::make_shared<NiceMock<MockDisplayConfigurationPolicy>>(); });
404 }428 }
405429
430 void wait_until_ready()
431 {
432 auto future_status = ready_for_input->get_future().wait_for(10s);
433
434 if (future_status != std::future_status::ready)
435 BOOST_THROW_EXCEPTION(std::runtime_error("Nested Server never started"));
436 }
437
406protected:438protected:
407 NestedMirRunner(std::string const& connection_string, bool)439 NestedMirRunner(std::string const& connection_string, bool)
408 : mtf::HeadlessNestedServerRunner(connection_string)440 : mtf::HeadlessNestedServerRunner(connection_string)
409 {441 {
442 server.override_the_server_status_listener([this]
443 { return std::make_shared<InputReadyServerStatusListener>(ready_for_input); });
444
410 server.override_the_host_lifecycle_event_listener([this]445 server.override_the_host_lifecycle_event_listener([this]
411 { return the_mock_host_lifecycle_event_listener(); });446 { return the_mock_host_lifecycle_event_listener(); });
412447
@@ -422,14 +457,19 @@
422457
423private:458private:
424 mir::CachedPtr<MockHostLifecycleEventListener> mock_host_lifecycle_event_listener;459 mir::CachedPtr<MockHostLifecycleEventListener> mock_host_lifecycle_event_listener;
425
426 mir::CachedPtr<MockDisplayConfigurationPolicy> mock_display_configuration_policy_;460 mir::CachedPtr<MockDisplayConfigurationPolicy> mock_display_configuration_policy_;
461 std::shared_ptr<std::promise<void>> ready_for_input = std::make_shared<std::promise<void>>();
462 mir::UniqueModulePtr<mtf::FakeInputDevice> fake_device{mtf::add_fake_input_device(mi::InputDeviceInfo{
463 "test-devce", "test-device",
464 mi::DeviceCapability::pointer | mi::DeviceCapability::keyboard | mi::DeviceCapability::alpha_numeric})};
427};465};
428466
429struct NestedServer : mtf::HeadlessInProcessServer467struct NestedServer : mtf::HeadlessInProcessServer
430{468{
431 mtd::NestedMockEGL mock_egl;469 mtd::NestedMockEGL mock_egl;
432 mtf::UsingStubClientPlatform using_stub_client_platform;470 mtf::UsingStubClientPlatform using_stub_client_platform;
471 mt::Signal condition;
472 mt::Signal test_processed_result;
433473
434 std::shared_ptr<MockSessionMediatorReport> mock_session_mediator_report;474 std::shared_ptr<MockSessionMediatorReport> mock_session_mediator_report;
435 NiceMock<MockDisplay> display{display_geometry};475 NiceMock<MockDisplay> display{display_geometry};
@@ -906,38 +946,43 @@
906946
907 server.the_cursor_listener()->cursor_moved_to(489, 9);947 server.the_cursor_listener()->cursor_moved_to(489, 9);
908948
909 // TODO workaround for lp:1523621949 nested_mir.wait_until_ready();
910 // (I don't see a way to detect that the host has placed focus on "Mir nested display for output #1")950
911 std::this_thread::sleep_for(10ms);951 // FIXME: In this test setup the software cursor will trigger scene_changed() on show(...).
912952 // Thus a new frame will be composed. Then a "FramePostObserver" in basic_surface.cpp will
913 {953 // react to the frame_posted callback by setting the cursor buffer again via show(..)
914 mt::Signal condition;954 // The number of show calls depends solely on scheduling decisions
915 EXPECT_CALL(*mock_cursor, show(_)).Times(1)955 EXPECT_CALL(*mock_cursor, show(_)).Times(AtLeast(frames))
916 .WillRepeatedly(InvokeWithoutArgs([&] { condition.raise(); }));956 .WillRepeatedly(InvokeWithoutArgs(
917957 [&]
918 auto conf = mir_cursor_configuration_from_buffer_stream(client.buffer_stream, 0, 0);958 {
919 mir_wait_for(mir_surface_configure_cursor(client.surface, conf));959 condition.raise();
920 mir_cursor_configuration_destroy(conf);960 test_processed_result.wait_for(timeout);
921961 test_processed_result.reset();
922 condition.wait_for(timeout);962 }));
923 Mock::VerifyAndClearExpectations(mock_cursor.get());963
924 }964 auto conf = mir_cursor_configuration_from_buffer_stream(client.buffer_stream, 0, 0);
965 mir_wait_for(mir_surface_configure_cursor(client.surface, conf));
966 mir_cursor_configuration_destroy(conf);
967
968 EXPECT_TRUE(condition.wait_for(timeout));
969 condition.reset();
970 test_processed_result.raise();
925971
926 for (int i = 0; i != frames; ++i)972 for (int i = 0; i != frames; ++i)
927 {973 {
928 mt::Signal condition;
929 EXPECT_CALL(*mock_cursor, show(_)).Times(1)
930 .WillRepeatedly(InvokeWithoutArgs([&] { condition.raise(); }));
931
932 mir_buffer_stream_swap_buffers_sync(client.buffer_stream);974 mir_buffer_stream_swap_buffers_sync(client.buffer_stream);
933975
934 condition.wait_for(timeout);976 EXPECT_TRUE(condition.wait_for(timeout));
935 Mock::VerifyAndClearExpectations(mock_cursor.get());977 condition.reset();
978 test_processed_result.raise();
936 }979 }
980 Mock::VerifyAndClearExpectations(mock_cursor.get());
937}981}
938982
939TEST_F(NestedServer, named_cursor_image_changes_are_forwarded_to_host)983TEST_F(NestedServer, named_cursor_image_changes_are_forwarded_to_host)
940{984{
985 mt::Signal condition;
941 NestedMirRunner nested_mir{new_connection()};986 NestedMirRunner nested_mir{new_connection()};
942987
943 ClientWithAPaintedSurface client(nested_mir);988 ClientWithAPaintedSurface client(nested_mir);
@@ -945,25 +990,32 @@
945990
946 server.the_cursor_listener()->cursor_moved_to(489, 9);991 server.the_cursor_listener()->cursor_moved_to(489, 9);
947992
948 // TODO workaround for lp:1523621993 nested_mir.wait_until_ready();
949 // (I don't see a way to detect that the host has placed focus on "Mir nested display for output #1")994
950 std::this_thread::sleep_for(1s);995 // FIXME: In this test setup the software cursor will trigger scene_changed() on show(...).
951 Mock::VerifyAndClearExpectations(mock_cursor.get());996 // Thus a new frame will be composed. Then a "FramePostObserver" in basic_surface.cpp will
997 // react to the frame_posted callback by setting the cursor buffer again via show(..)
998 // The number of show calls depends solely on scheduling decisions
999 EXPECT_CALL(*mock_cursor, show(_)).Times(AtLeast(cursor_names.size()))
1000 .WillRepeatedly(InvokeWithoutArgs(
1001 [&]
1002 {
1003 condition.raise();
1004 test_processed_result.wait_for(long_timeout);
1005 test_processed_result.reset();
1006 }));
9521007
953 for (auto const name : cursor_names)1008 for (auto const name : cursor_names)
954 {1009 {
955 mt::Signal condition;
956
957 EXPECT_CALL(*mock_cursor, show(_)).Times(1)
958 .WillOnce(InvokeWithoutArgs([&] { condition.raise(); }));
959
960 auto const cursor = mir_cursor_configuration_from_name(name);1010 auto const cursor = mir_cursor_configuration_from_name(name);
961 mir_wait_for(mir_surface_configure_cursor(client.surface, cursor));1011 mir_wait_for(mir_surface_configure_cursor(client.surface, cursor));
962 mir_cursor_configuration_destroy(cursor);1012 mir_cursor_configuration_destroy(cursor);
9631013
964 condition.wait_for(timeout);1014 EXPECT_TRUE(condition.wait_for(long_timeout));
965 Mock::VerifyAndClearExpectations(mock_cursor.get());1015 condition.reset();
1016 test_processed_result.raise();
966 }1017 }
1018 Mock::VerifyAndClearExpectations(mock_cursor.get());
967}1019}
9681020
969TEST_F(NestedServer, can_hide_the_host_cursor)1021TEST_F(NestedServer, can_hide_the_host_cursor)
@@ -976,22 +1028,23 @@
9761028
977 server.the_cursor_listener()->cursor_moved_to(489, 9);1029 server.the_cursor_listener()->cursor_moved_to(489, 9);
9781030
979 // TODO workaround for lp:15236211031 nested_mir.wait_until_ready();
980 // (I don't see a way to detect that the host has placed focus on "Mir nested display for output #1")1032 Mock::VerifyAndClearExpectations(mock_cursor.get());
981 std::this_thread::sleep_for(10ms);1033
9821034 // FIXME: In this test setup the software cursor will trigger scene_changed() on show(...).
983 {1035 // Thus a new frame will be composed. Then a "FramePostObserver" in basic_surface.cpp will
984 mt::Signal condition;1036 // react to the frame_posted callback by setting the cursor buffer again via show(..)
985 EXPECT_CALL(*mock_cursor, show(_)).Times(1)1037 // The number of show calls depends solely on scheduling decisions
986 .WillRepeatedly(InvokeWithoutArgs([&] { condition.raise(); }));1038 EXPECT_CALL(*mock_cursor, show(_)).Times(AtLeast(1))
9871039 .WillOnce(mt::WakeUp(&condition));
988 auto conf = mir_cursor_configuration_from_buffer_stream(client.buffer_stream, 0, 0);1040
989 mir_wait_for(mir_surface_configure_cursor(client.surface, conf));1041 auto conf = mir_cursor_configuration_from_buffer_stream(client.buffer_stream, 0, 0);
990 mir_cursor_configuration_destroy(conf);1042 mir_wait_for(mir_surface_configure_cursor(client.surface, conf));
9911043 mir_cursor_configuration_destroy(conf);
992 condition.wait_for(timeout);1044
993 Mock::VerifyAndClearExpectations(mock_cursor.get());1045 std::this_thread::sleep_for(500ms);
994 }1046 condition.wait_for(timeout);
1047 Mock::VerifyAndClearExpectations(mock_cursor.get());
9951048
996 nested_mir.cursor_wrapper->set_hidden(true);1049 nested_mir.cursor_wrapper->set_hidden(true);
9971050
9981051
=== modified file 'tests/include/mir/test/doubles/mock_server_status_listener.h'
--- tests/include/mir/test/doubles/mock_server_status_listener.h 2013-10-15 09:22:41 +0000
+++ tests/include/mir/test/doubles/mock_server_status_listener.h 2016-09-13 09:13:43 +0000
@@ -36,6 +36,8 @@
36 MOCK_METHOD0(paused, void());36 MOCK_METHOD0(paused, void());
37 MOCK_METHOD0(resumed, void());37 MOCK_METHOD0(resumed, void());
38 MOCK_METHOD0(started, void());38 MOCK_METHOD0(started, void());
39 MOCK_METHOD0(ready_for_user_input, void());
40 MOCK_METHOD0(stop_receiving_input, void());
39};41};
4042
41}43}
4244
=== modified file 'tests/include/mir/test/doubles/stub_host_connection.h'
--- tests/include/mir/test/doubles/stub_host_connection.h 2016-09-01 16:26:31 +0000
+++ tests/include/mir/test/doubles/stub_host_connection.h 2016-09-13 09:13:43 +0000
@@ -66,7 +66,7 @@
66 std::shared_ptr<graphics::nested::HostSurface>66 std::shared_ptr<graphics::nested::HostSurface>
67 create_surface(67 create_surface(
68 std::shared_ptr<graphics::nested::HostStream> const&, geometry::Displacement,68 std::shared_ptr<graphics::nested::HostStream> const&, geometry::Displacement,
69 graphics::BufferProperties, char const*, uint32_t) override69 graphics::BufferProperties, geometry::Rectangle const& , char const*, uint32_t) override
70 {70 {
71 return surface;71 return surface;
72 }72 }
@@ -83,6 +83,9 @@
83 void hide_cursor() override83 void hide_cursor() override
84 {84 {
85 }85 }
86 void set_cursor_position(geometry::Point const&) override
87 {
88 }
8689
87 auto graphics_platform_library() -> std::string override { return {}; }90 auto graphics_platform_library() -> std::string override { return {}; }
8891
@@ -118,7 +121,7 @@
118 void set_event_handler(mir_surface_event_callback, void*) override {}121 void set_event_handler(mir_surface_event_callback, void*) override {}
119 };122 };
120 std::shared_ptr<graphics::nested::HostSurface> const surface;123 std::shared_ptr<graphics::nested::HostSurface> const surface;
121 124
122 std::shared_ptr<MirBuffer> create_buffer(graphics::BufferProperties const&)125 std::shared_ptr<MirBuffer> create_buffer(graphics::BufferProperties const&)
123 {126 {
124 return nullptr;127 return nullptr;
125128
=== modified file 'tests/integration-tests/input/test_single_seat_setup.cpp'
--- tests/integration-tests/input/test_single_seat_setup.cpp 2016-07-28 23:00:22 +0000
+++ tests/integration-tests/input/test_single_seat_setup.cpp 2016-09-13 09:13:43 +0000
@@ -27,6 +27,7 @@
27#include "mir/test/doubles/mock_cursor_listener.h"27#include "mir/test/doubles/mock_cursor_listener.h"
28#include "mir/test/doubles/mock_event_sink.h"28#include "mir/test/doubles/mock_event_sink.h"
29#include "mir/test/doubles/mock_seat_report.h"29#include "mir/test/doubles/mock_seat_report.h"
30#include "mir/test/doubles/mock_server_status_listener.h"
30#include "mir/test/doubles/triggered_main_loop.h"31#include "mir/test/doubles/triggered_main_loop.h"
31#include "mir/test/event_matchers.h"32#include "mir/test/event_matchers.h"
32#include "mir/test/doubles/advanceable_clock.h"33#include "mir/test/doubles/advanceable_clock.h"
@@ -82,6 +83,7 @@
82 NiceMock<mtd::MockCursorListener> mock_cursor_listener;83 NiceMock<mtd::MockCursorListener> mock_cursor_listener;
83 NiceMock<mtd::MockTouchVisualizer> mock_visualizer;84 NiceMock<mtd::MockTouchVisualizer> mock_visualizer;
84 NiceMock<mtd::MockSeatReport> mock_seat_report;85 NiceMock<mtd::MockSeatReport> mock_seat_report;
86 NiceMock<mtd::MockServerStatusListener> mock_status_listener;
85 mi::receiver::XKBMapper key_mapper;87 mi::receiver::XKBMapper key_mapper;
86 mir::dispatch::MultiplexingDispatchable multiplexer;88 mir::dispatch::MultiplexingDispatchable multiplexer;
87 mtd::AdvanceableClock clock;89 mtd::AdvanceableClock clock;
@@ -90,7 +92,7 @@
90 mt::fake_shared(mock_region), mt::fake_shared(key_mapper), mt::fake_shared(clock), mt::fake_shared(mock_seat_report)};92 mt::fake_shared(mock_region), mt::fake_shared(key_mapper), mt::fake_shared(clock), mt::fake_shared(mock_seat_report)};
91 mi::DefaultInputDeviceHub hub{93 mi::DefaultInputDeviceHub hub{
92 mt::fake_shared(mock_sink), mt::fake_shared(seat), mt::fake_shared(multiplexer), mt::fake_shared(observer_loop),94 mt::fake_shared(mock_sink), mt::fake_shared(seat), mt::fake_shared(multiplexer), mt::fake_shared(observer_loop),
93 cookie_authority, mt::fake_shared(key_mapper)};95 cookie_authority, mt::fake_shared(key_mapper), mt::fake_shared(mock_status_listener)};
94 NiceMock<mtd::MockInputDeviceObserver> mock_observer;96 NiceMock<mtd::MockInputDeviceObserver> mock_observer;
9597
96 mi::DeviceCapabilities const keyboard_caps = mi::DeviceCapability::keyboard | mi::DeviceCapability::alpha_numeric;98 mi::DeviceCapabilities const keyboard_caps = mi::DeviceCapability::keyboard | mi::DeviceCapability::alpha_numeric;
9799
=== modified file 'tests/mir_test_framework/testing_server_options.cpp'
--- tests/mir_test_framework/testing_server_options.cpp 2016-05-03 06:55:25 +0000
+++ tests/mir_test_framework/testing_server_options.cpp 2016-09-13 09:13:43 +0000
@@ -119,6 +119,8 @@
119 server_started_sync.try_signal_ready_for();119 server_started_sync.try_signal_ready_for();
120 on_start();120 on_start();
121 }121 }
122 void ready_for_user_input() {}
123 void stop_receiving_input() {}
122124
123 mt::CrossProcessSync server_started_sync;125 mt::CrossProcessSync server_started_sync;
124 std::function<void(void)> const on_start;126 std::function<void(void)> const on_start;
125127
=== modified file 'tests/unit-tests/input/test_default_input_device_hub.cpp'
--- tests/unit-tests/input/test_default_input_device_hub.cpp 2016-06-02 08:20:22 +0000
+++ tests/unit-tests/input/test_default_input_device_hub.cpp 2016-09-13 09:13:43 +0000
@@ -25,6 +25,7 @@
25#include "mir/test/doubles/mock_input_seat.h"25#include "mir/test/doubles/mock_input_seat.h"
26#include "mir/test/doubles/mock_event_sink.h"26#include "mir/test/doubles/mock_event_sink.h"
27#include "mir/test/doubles/mock_key_mapper.h"27#include "mir/test/doubles/mock_key_mapper.h"
28#include "mir/test/doubles/mock_server_status_listener.h"
28#include "mir/test/doubles/stub_cursor_listener.h"29#include "mir/test/doubles/stub_cursor_listener.h"
29#include "mir/test/doubles/stub_touch_visualizer.h"30#include "mir/test/doubles/stub_touch_visualizer.h"
30#include "mir/test/doubles/triggered_main_loop.h"31#include "mir/test/doubles/triggered_main_loop.h"
@@ -81,8 +82,9 @@
81 NiceMock<mtd::MockInputSeat> mock_seat;82 NiceMock<mtd::MockInputSeat> mock_seat;
82 NiceMock<mtd::MockEventSink> mock_sink;83 NiceMock<mtd::MockEventSink> mock_sink;
83 NiceMock<mtd::MockKeyMapper> mock_key_mapper;84 NiceMock<mtd::MockKeyMapper> mock_key_mapper;
85 NiceMock<mtd::MockServerStatusListener> mock_server_status_listener;
84 mi::DefaultInputDeviceHub hub{mt::fake_shared(mock_sink), mt::fake_shared(mock_seat), mt::fake_shared(multiplexer),86 mi::DefaultInputDeviceHub hub{mt::fake_shared(mock_sink), mt::fake_shared(mock_seat), mt::fake_shared(multiplexer),
85 mt::fake_shared(observer_loop), cookie_authority, mt::fake_shared(mock_key_mapper)};87 mt::fake_shared(observer_loop), cookie_authority, mt::fake_shared(mock_key_mapper), mt::fake_shared(mock_server_status_listener)};
86 NiceMock<mtd::MockInputDeviceObserver> mock_observer;88 NiceMock<mtd::MockInputDeviceObserver> mock_observer;
87 NiceMock<mtd::MockInputDevice> device{"device","dev-1", mi::DeviceCapability::unknown};89 NiceMock<mtd::MockInputDevice> device{"device","dev-1", mi::DeviceCapability::unknown};
88 NiceMock<mtd::MockInputDevice> another_device{"another_device","dev-2", mi::DeviceCapability::keyboard};90 NiceMock<mtd::MockInputDevice> another_device{"another_device","dev-2", mi::DeviceCapability::keyboard};
@@ -210,3 +212,22 @@
210 observer_loop.trigger_server_actions();212 observer_loop.trigger_server_actions();
211}213}
212214
215TEST_F(InputDeviceHubTest, emit_ready_to_receive_input_after_first_device_added)
216{
217 EXPECT_CALL(mock_server_status_listener, ready_for_user_input()).Times(1);
218 hub.add_device(mt::fake_shared(device));
219 hub.add_device(mt::fake_shared(another_device));
220
221 observer_loop.trigger_server_actions();
222}
223
224TEST_F(InputDeviceHubTest, emit_stop_receiving_input_after_last_device_added)
225{
226 EXPECT_CALL(mock_server_status_listener, stop_receiving_input()).Times(1);
227 hub.add_device(mt::fake_shared(device));
228 hub.add_device(mt::fake_shared(another_device));
229
230 hub.remove_device(mt::fake_shared(device));
231 hub.remove_device(mt::fake_shared(another_device));
232 observer_loop.trigger_server_actions();
233}

Subscribers

People subscribed via source and target branches