Merge lp:~mir-team/mir/cursor-spike-phase-4-implement-api into lp:mir
- cursor-spike-phase-4-implement-api
- Merge into development-branch
Status: | Merged |
---|---|
Approved by: | Robert Carr |
Approved revision: | no longer in the source branch. |
Merged at revision: | 1717 |
Proposed branch: | lp:~mir-team/mir/cursor-spike-phase-4-implement-api |
Merge into: | lp:mir |
Prerequisite: | lp:~mir-team/mir/cursor-spike-phase-3-wire-surface-data |
Diff against target: |
2029 lines (+1172/-415) 27 files modified
include/client/mir_toolkit/mir_cursor_configuration.h (+2/-11) include/server/mir/input/input_targets.h (+8/-0) include/server/mir/input/surface.h (+11/-0) include/server/mir/scene/surface.h (+1/-1) include/shared/mir_toolkit/common.h (+11/-0) include/test/mir_test_doubles/mock_input_surface.h (+1/-14) include/test/mir_test_doubles/stub_input_surface.h (+2/-0) include/test/mir_test_doubles/stub_input_targets.h (+6/-0) include/test/mir_test_doubles/stub_scene_surface.h (+1/-1) include/test/mir_test_framework/stubbed_server_configuration.h (+2/-0) src/client/cursor_configuration.h (+2/-0) src/client/mir_cursor_api.cpp (+8/-5) src/client/mir_surface.cpp (+1/-1) src/server/default_server_configuration.cpp (+0/-24) src/server/graphics/default_configuration.cpp (+3/-1) src/server/input/CMakeLists.txt (+1/-0) src/server/input/cursor_controller.cpp (+244/-0) src/server/input/cursor_controller.h (+77/-0) src/server/input/default_configuration.cpp (+11/-0) src/server/scene/basic_surface.cpp (+5/-2) src/server/scene/basic_surface.h (+1/-1) tests/acceptance-tests/test_client_cursor_api.cpp (+357/-350) tests/mir_test_framework/input_testing_server_options.cpp (+1/-3) tests/mir_test_framework/stubbed_server_configuration.cpp (+15/-1) tests/unit-tests/input/CMakeLists.txt (+1/-0) tests/unit-tests/input/android/test_android_input_target_enumerator.cpp (+9/-0) tests/unit-tests/input/test_cursor_controller.cpp (+391/-0) |
To merge this branch: | bzr merge lp:~mir-team/mir/cursor-spike-phase-4-implement-api |
Related bugs: |
Reviewer | Review Type | Date Requested | Status |
---|---|---|---|
PS Jenkins bot (community) | continuous-integration | Approve | |
Andreas Pokorny (community) | Approve | ||
Alan Griffiths | Approve | ||
Kevin DuBois (community) | Approve | ||
Alberto Aguirre | Pending | ||
Review via email: mp+220108@code.launchpad.net |
Commit message
Enable client cursor API.
Description of the change
Enable the cursor API.
Remaining phases:
5 - XCursor loader
6 - Demo client
Diff will shrink once
https:/
lands
PS Jenkins bot (ps-jenkins) wrote : | # |
Kevin DuBois (kdub) wrote : | # |
looks good overall, just nits really:
361, 423-425
could be private
416/417 indentation
CursorController seems more like a CursorState. It listens for events and changes the state accordingly.
we could get rid of returning nullptr in topmost_
Andreas Pokorny (andreas-pokorny) wrote : | # |
A few nits, and I think some left-overs that you do not need -> Needs Fixing.
There are some Information Needed questions inside the inline comments.
PS Jenkins bot (ps-jenkins) wrote : | # |
FAILED: Continuous integration, rev:1538
http://
Executed test runs:
SUCCESS: http://
FAILURE: http://
FAILURE: http://
FAILURE: http://
SUCCESS: http://
deb: http://
SUCCESS: http://
deb: http://
FAILURE: http://
SUCCESS: http://
Click here to trigger a rebuild:
http://
Alan Griffiths (alan-griffiths) wrote : | # |
Start 21: mir_acceptance_
Build timed out (after 120 minutes). Marking the build as failed.
I concur - this test hangs.
Robert Carr (robertcarr) wrote : | # |
Things now not present:
anpoks nits
deadlock (removed lock scope {} in a merge error)
PS Jenkins bot (ps-jenkins) wrote : | # |
FAILED: Continuous integration, rev:1539
http://
Executed test runs:
FAILURE: http://
FAILURE: http://
FAILURE: http://
FAILURE: http://
FAILURE: http://
FAILURE: http://
Click here to trigger a rebuild:
http://
Robert Carr (robertcarr) wrote : | # |
Fixed conflicts
PS Jenkins bot (ps-jenkins) wrote : | # |
FAILED: Continuous integration, rev:1540
http://
Executed test runs:
SUCCESS: http://
SUCCESS: http://
FAILURE: http://
SUCCESS: http://
deb: http://
SUCCESS: http://
deb: http://
SUCCESS: http://
deb: http://
FAILURE: http://
SUCCESS: http://
Click here to trigger a rebuild:
http://
PS Jenkins bot (ps-jenkins) wrote : | # |
PASSED: Continuous integration, rev:1541
http://
Executed test runs:
SUCCESS: http://
SUCCESS: http://
SUCCESS: http://
SUCCESS: http://
deb: http://
SUCCESS: http://
deb: http://
SUCCESS: http://
deb: http://
SUCCESS: http://
SUCCESS: http://
Click here to trigger a rebuild:
http://
Alan Griffiths (alan-griffiths) wrote : | # |
299 +struct SurfaceObserver : ms::SurfaceObserver
"SurfaceObserver" is a bad name - it doesn't indicate why the class exists. "UpdateCursorOn
~~~~
301 + SurfaceObserver
I don't see the advantage of "std::function<
~~~~
436 + auto strong_observer = std::make_
437 + {
438 + std::lock_
439 + update_
440 + });
I'd prefer to see a lambda that didn't manipulate internal state; Vis
auto strong_observer = std::make_
But, given the above comments, we probably don't need a lambda at all. Just:
auto strong_observer = std::make_
~~~~
20 + virtual void add_observer(
21 + virtual void remove_
It is unintuitive (to say the least) that scene::Observer observes input::
Robert Carr (robertcarr) wrote : | # |
Alan: Addressed your suggestions.
PS Jenkins bot (ps-jenkins) wrote : | # |
FAILED: Continuous integration, rev:1544
http://
Executed test runs:
SUCCESS: http://
FAILURE: http://
SUCCESS: http://
FAILURE: http://
SUCCESS: http://
deb: http://
SUCCESS: http://
deb: http://
SUCCESS: http://
SUCCESS: http://
Click here to trigger a rebuild:
http://
PS Jenkins bot (ps-jenkins) wrote : | # |
FAILED: Continuous integration, rev:1545
http://
Executed test runs:
SUCCESS: http://
FAILURE: http://
SUCCESS: http://
FAILURE: http://
SUCCESS: http://
deb: http://
SUCCESS: http://
deb: http://
SUCCESS: http://
SUCCESS: http://
Click here to trigger a rebuild:
http://
PS Jenkins bot (ps-jenkins) wrote : | # |
FAILED: Continuous integration, rev:1546
http://
Executed test runs:
SUCCESS: http://
FAILURE: http://
SUCCESS: http://
SUCCESS: http://
deb: http://
SUCCESS: http://
deb: http://
SUCCESS: http://
deb: http://
SUCCESS: http://
SUCCESS: http://
Click here to trigger a rebuild:
http://
PS Jenkins bot (ps-jenkins) wrote : | # |
FAILED: Continuous integration, rev:1547
http://
Executed test runs:
FAILURE: http://
SUCCESS: http://
SUCCESS: http://
SUCCESS: http://
deb: http://
SUCCESS: http://
deb: http://
SUCCESS: http://
deb: http://
SUCCESS: http://
SUCCESS: http://
Click here to trigger a rebuild:
http://
Alan Griffiths (alan-griffiths) wrote : | # |
Failure looks spurious. Rebuilding
Alan Griffiths (alan-griffiths) wrote : | # |
Broken chroot tarball. Rebuilding.
PS Jenkins bot (ps-jenkins) wrote : | # |
FAILED: Continuous integration, rev:1547
http://
Executed test runs:
FAILURE: http://
SUCCESS: http://
FAILURE: http://
SUCCESS: http://
deb: http://
SUCCESS: http://
deb: http://
FAILURE: http://
Click here to trigger a rebuild:
http://
PS Jenkins bot (ps-jenkins) wrote : | # |
FAILED: Continuous integration, rev:1547
http://
Executed test runs:
SUCCESS: http://
SUCCESS: http://
SUCCESS: http://
SUCCESS: http://
deb: http://
SUCCESS: http://
deb: http://
SUCCESS: http://
deb: http://
Click here to trigger a rebuild:
http://
PS Jenkins bot (ps-jenkins) wrote : | # |
PASSED: Continuous integration, rev:1548
http://
Executed test runs:
SUCCESS: http://
SUCCESS: http://
SUCCESS: http://
SUCCESS: http://
deb: http://
SUCCESS: http://
deb: http://
SUCCESS: http://
deb: http://
SUCCESS: http://
SUCCESS: http://
Click here to trigger a rebuild:
http://
Kevin DuBois (kdub) wrote : | # |
could be private:
405 + mi::CursorContr
406 +
407 + std::mutex surface_
408 + std::map<
should guard against exceptions:
441 +mi::CursorCont
442 +{
443 + input_targets-
444 +}
needed?
484 + assert(
Alan Griffiths (alan-griffiths) wrote : | # |
152 char const *const mir_default_
237 + return the_cursor_
1575 +std::string const default_cursor_name = "default";
I assume these string constants all need to be the same. Can't we use a single constant?
~~~~
164 + std::unique_
165 + c->name = name ? std::string(name) : std::string();
166
167 - return c;
168 + return c.release();
Wouldn't a constructor that took a char* be a cleaner way to initialize the c?
Robert Carr (robertcarr) wrote : | # |
kdub:
>> could be private:
Now private!
>> should guard against exceptions:
Good catch!
>> needed?
>> 484 + assert(
Nah, left over from an earlier version which had two phase initialization.
Alan:
>> I assume these string constants all need to be the same. Can't we use a single constant?
Consolidated!
~~~~
>> Wouldn't a constructor that took a char* be a cleaner way to initialize the c?
Handling it this way now.
Alan Griffiths (alan-griffiths) wrote : | # |
A lot of nits:
222 +MirCursorConfi
223 +{
224 + s_name ? name = std::string(s_name) : name = std::string();
225 +}
MirCursorConfig
name{name ? std::string(s_name) : std::string()}
{
}
~~~~
235 + std::unique_
236
237 - return c;
238 + return c.release();
return new MirCursorConfig
~~~~
263 +
Why?
~~~~
434 + auto observer = std::make_
auto const?
~~~~
494 + std::shared_
std::shared_
~~~~
523 + try
524 + {
525 + input_targets-
526 + }
527 + catch (...)
528 + {
529 + }
I don't expect input_targets-
If removing observer does throw then surely things are so broken that we should stop execution. Not eat the exception!
~~~~
730 #include "mir_test_
Unused. (I suspect a whole bunch more includes are unused - e.g. #include "mir/compositor
~~~~
1017 +struct DeferredInProce
1018 +{
1019 + void SetUp() override { /* TODO this is a nasty frig around the unfortunate coupling in InProcessServer */ }
1020 + void start_server() { mtf::InProcessS
1021 +};
You might like to update to match test_client_
struct DeferredInProce
{
void TearDown() override { ServerRunner:
using ServerRunner:
using ServerRunner:
};
~~~~
2001 + StubInputSurface surface1{
2002 + geom::Size{
2003 + image};
2004 + StubInputSurface surface2{
2005 + geom::Size{
2006 + image};
Alignment is wrong. (That's why I don't like aligning to the open brace.)
Also, I'm pretty sure most of those explicit constructions are unnecessary.
PS Jenkins bot (ps-jenkins) wrote : | # |
PASSED: Continuous integration, rev:1551
http://
Executed test runs:
SUCCESS: http://
SUCCESS: http://
SUCCESS: http://
SUCCESS: http://
deb: http://
SUCCESS: http://
deb: http://
SUCCESS: http://
deb: http://
SUCCESS: http://
SUCCESS: http://
Click here to trigger a rebuild:
http://
Robert Carr (robertcarr) wrote : | # |
Nits addressed. updated to look like test client input.
Kevin DuBois (kdub) wrote : | # |
okay, once the nitfixes suggested by Alan are pushed
Robert Carr (robertcarr) wrote : | # |
Pushed! somehow my SSH password saver is just totally broken.
PS Jenkins bot (ps-jenkins) wrote : | # |
FAILED: Continuous integration, rev:1552
http://
Executed test runs:
FAILURE: http://
FAILURE: http://
FAILURE: http://
FAILURE: http://
FAILURE: http://
FAILURE: http://
Click here to trigger a rebuild:
http://
Alan Griffiths (alan-griffiths) wrote : | # |
> FAILED: Continuous integration, rev:1552
> http://
> Executed test runs:
> FAILURE: http://
> utopic-
> FAILURE: http://
> amd64-build/
> FAILURE: http://
> touch/590/console
> FAILURE: http://
> utopic-
> FAILURE: http://
> utopic-
> FAILURE: http://
> utopic-
>
> Click here to trigger a rebuild:
> http://
> ci/1919/rebuild
bzr: ERROR: Conflicts from merge
PS Jenkins bot (ps-jenkins) wrote : | # |
PASSED: Continuous integration, rev:1554
http://
Executed test runs:
SUCCESS: http://
SUCCESS: http://
SUCCESS: http://
SUCCESS: http://
deb: http://
SUCCESS: http://
deb: http://
SUCCESS: http://
deb: http://
SUCCESS: http://
SUCCESS: http://
Click here to trigger a rebuild:
http://
Alan Griffiths (alan-griffiths) wrote : | # |
203 - * along with this program. If not, see <http://
204 +o * along with this program. If not, see <http://
:)
Alan Griffiths (alan-griffiths) wrote : | # |
1912 + StubInputSurface surface{
1913 + geom::Size{
1914 + nullptr};
There's a lot of verbose stuff repeated in the test bodies that could be shifted to the fixture. (Making the tests easier to read.)
E.g.
static geom::Rectangle const rect_1_1_1_1{{1, 1}, {1, 1}};
StubInputSurface surface_
Robert Carr (robertcarr) wrote : | # |
Thanks alan. Fixed stray o. Made the tests a bit easier to read...factored out some strings in addition to the rects.
PS Jenkins bot (ps-jenkins) wrote : | # |
FAILED: Continuous integration, rev:1556
http://
Executed test runs:
FAILURE: http://
FAILURE: http://
FAILURE: http://
FAILURE: http://
FAILURE: http://
FAILURE: http://
Click here to trigger a rebuild:
http://
PS Jenkins bot (ps-jenkins) wrote : | # |
PASSED: Continuous integration, rev:1557
http://
Executed test runs:
SUCCESS: http://
SUCCESS: http://
SUCCESS: http://
SUCCESS: http://
deb: http://
SUCCESS: http://
deb: http://
SUCCESS: http://
deb: http://
SUCCESS: http://
SUCCESS: http://
Click here to trigger a rebuild:
http://
Andreas Pokorny (andreas-pokorny) wrote : | # |
Ok - I still do not like that extra book keeping of surfaces and observers to surfaces are necessary necessary. But that is something to address later.
PS Jenkins bot (ps-jenkins) : | # |
Preview Diff
1 | === modified file 'include/client/mir_toolkit/mir_cursor_configuration.h' |
2 | --- include/client/mir_toolkit/mir_cursor_configuration.h 2014-05-14 16:50:03 +0000 |
3 | +++ include/client/mir_toolkit/mir_cursor_configuration.h 2014-06-23 02:58:32 +0000 |
4 | @@ -18,6 +18,8 @@ |
5 | #ifndef MIR_TOOLKIT_MIR_CURSOR_H_ |
6 | #define MIR_TOOLKIT_MIR_CURSOR_H_ |
7 | |
8 | +#include <mir_toolkit/common.h> |
9 | + |
10 | /** |
11 | * Opaque structure containing cursor parameterization. Create with mir_cursor* family. |
12 | * Used with mir_surface_configure_cursor. |
13 | @@ -33,17 +35,6 @@ |
14 | #endif |
15 | |
16 | /** |
17 | - * A special cursor name for use with mir_cursor_configuration_from_name |
18 | - * representing the system default cursor. |
19 | - */ |
20 | -extern char const *const mir_default_cursor_name; |
21 | -/** |
22 | - * A special cursor name for use with mir_cursor_configuration_from_name |
23 | - * representing a disabled cursor image. |
24 | - */ |
25 | -extern char const *const mir_disabled_cursor_name; |
26 | - |
27 | -/** |
28 | * Release resources assosciated with cursor parameters |
29 | * \param [in] parameters The operand |
30 | */ |
31 | |
32 | === modified file 'include/server/mir/input/input_targets.h' |
33 | --- include/server/mir/input/input_targets.h 2014-04-24 08:42:12 +0000 |
34 | +++ include/server/mir/input/input_targets.h 2014-06-23 02:58:32 +0000 |
35 | @@ -26,6 +26,11 @@ |
36 | |
37 | namespace mir |
38 | { |
39 | +namespace scene |
40 | +{ |
41 | +class Observer; |
42 | +} |
43 | + |
44 | namespace input |
45 | { |
46 | class Surface; |
47 | @@ -37,6 +42,9 @@ |
48 | |
49 | virtual void for_each(std::function<void(std::shared_ptr<input::Surface> const&)> const& callback) = 0; |
50 | |
51 | + virtual void add_observer(std::shared_ptr<scene::Observer> const& observer) = 0; |
52 | + virtual void remove_observer(std::weak_ptr<scene::Observer> const& observer) = 0; |
53 | + |
54 | protected: |
55 | InputTargets() = default; |
56 | InputTargets(InputTargets const&) = delete; |
57 | |
58 | === modified file 'include/server/mir/input/surface.h' |
59 | --- include/server/mir/input/surface.h 2014-06-03 11:04:15 +0000 |
60 | +++ include/server/mir/input/surface.h 2014-06-23 02:58:32 +0000 |
61 | @@ -28,6 +28,16 @@ |
62 | |
63 | namespace mir |
64 | { |
65 | +namespace graphics |
66 | +{ |
67 | +class CursorImage; |
68 | +} |
69 | + |
70 | +namespace scene |
71 | +{ |
72 | +class SurfaceObserver; |
73 | +} |
74 | + |
75 | namespace input |
76 | { |
77 | class InputChannel; |
78 | @@ -39,6 +49,7 @@ |
79 | virtual geometry::Rectangle input_bounds() const = 0; |
80 | virtual bool input_area_contains(geometry::Point const& point) const = 0; |
81 | virtual std::shared_ptr<input::InputChannel> input_channel() const = 0; |
82 | + virtual std::shared_ptr<graphics::CursorImage> cursor_image() const = 0; |
83 | virtual InputReceptionMode reception_mode() const = 0; |
84 | |
85 | protected: |
86 | |
87 | === modified file 'include/server/mir/scene/surface.h' |
88 | --- include/server/mir/scene/surface.h 2014-06-19 11:35:40 +0000 |
89 | +++ include/server/mir/scene/surface.h 2014-06-23 02:58:32 +0000 |
90 | @@ -74,7 +74,7 @@ |
91 | virtual void force_requests_to_complete() = 0; |
92 | |
93 | virtual void set_cursor_image(std::shared_ptr<graphics::CursorImage> const& image) = 0; |
94 | - virtual std::shared_ptr<graphics::CursorImage> cursor_image() = 0; |
95 | + virtual std::shared_ptr<graphics::CursorImage> cursor_image() const = 0; |
96 | |
97 | virtual void add_observer(std::shared_ptr<SurfaceObserver> const& observer) = 0; |
98 | virtual void remove_observer(std::weak_ptr<SurfaceObserver> const& observer) = 0; |
99 | |
100 | === modified file 'include/shared/mir_toolkit/common.h' |
101 | --- include/shared/mir_toolkit/common.h 2014-06-20 12:05:55 +0000 |
102 | +++ include/shared/mir_toolkit/common.h 2014-06-23 02:58:32 +0000 |
103 | @@ -133,6 +133,17 @@ |
104 | mir_orientation_right = 270 |
105 | } MirOrientation; |
106 | |
107 | +/** |
108 | + * A special cursor name for use with mir_cursor_configuration_from_name |
109 | + * representing the system default cursor. |
110 | + */ |
111 | +extern char const *const mir_default_cursor_name; |
112 | +/** |
113 | + * A special cursor name for use with mir_cursor_configuration_from_name |
114 | + * representing a disabled cursor image. |
115 | + */ |
116 | +extern char const *const mir_disabled_cursor_name; |
117 | + |
118 | /**@}*/ |
119 | |
120 | #endif |
121 | |
122 | === modified file 'include/test/mir_test_doubles/mock_input_surface.h' |
123 | --- include/test/mir_test_doubles/mock_input_surface.h 2014-06-03 11:04:15 +0000 |
124 | +++ include/test/mir_test_doubles/mock_input_surface.h 2014-06-23 02:58:32 +0000 |
125 | @@ -32,25 +32,12 @@ |
126 | class MockInputSurface : public input::Surface |
127 | { |
128 | public: |
129 | - MockInputSurface() |
130 | - { |
131 | - using namespace ::testing; |
132 | - ON_CALL(*this, input_bounds()) |
133 | - .WillByDefault(Return(geometry::Rectangle())); |
134 | - static std::string n; |
135 | - ON_CALL(*this, name()) |
136 | - .WillByDefault(Return(n)); |
137 | - static std::shared_ptr<input::InputChannel> c{nullptr}; |
138 | - ON_CALL(*this, input_channel()) |
139 | - .WillByDefault(Return(c)); |
140 | - ON_CALL(*this, reception_mode()) |
141 | - .WillByDefault(Return(input::InputReceptionMode::normal)); |
142 | - } |
143 | ~MockInputSurface() noexcept {} |
144 | MOCK_CONST_METHOD0(name, std::string()); |
145 | MOCK_CONST_METHOD0(input_bounds, geometry::Rectangle()); |
146 | MOCK_CONST_METHOD1(input_area_contains, bool(geometry::Point const&)); |
147 | MOCK_CONST_METHOD0(input_channel, std::shared_ptr<input::InputChannel>()); |
148 | + MOCK_CONST_METHOD0(cursor_image, std::shared_ptr<graphics::CursorImage>()); |
149 | MOCK_CONST_METHOD0(reception_mode, input::InputReceptionMode()); |
150 | }; |
151 | |
152 | |
153 | === modified file 'include/test/mir_test_doubles/stub_input_surface.h' |
154 | --- include/test/mir_test_doubles/stub_input_surface.h 2014-05-14 16:50:03 +0000 |
155 | +++ include/test/mir_test_doubles/stub_input_surface.h 2014-06-23 02:58:32 +0000 |
156 | @@ -50,6 +50,8 @@ |
157 | std::string name() const { return {}; } |
158 | mir::geometry::Rectangle input_bounds() const override { return {{},{}}; } |
159 | bool input_area_contains(mir::geometry::Point const&) const { return false; } |
160 | + |
161 | + std::shared_ptr<graphics::CursorImage> cursor_image() const { return nullptr; } |
162 | |
163 | std::shared_ptr<mir::input::InputChannel> const channel; |
164 | }; |
165 | |
166 | === modified file 'include/test/mir_test_doubles/stub_input_targets.h' |
167 | --- include/test/mir_test_doubles/stub_input_targets.h 2014-04-01 22:48:55 +0000 |
168 | +++ include/test/mir_test_doubles/stub_input_targets.h 2014-06-23 02:58:32 +0000 |
169 | @@ -33,6 +33,12 @@ |
170 | void for_each(std::function<void(std::shared_ptr<input::Surface> const&)> const& ) override |
171 | { |
172 | } |
173 | + void add_observer(std::shared_ptr<scene::Observer> const& /* observer */) |
174 | + { |
175 | + } |
176 | + void remove_observer(std::weak_ptr<scene::Observer> const& /* observer */) |
177 | + { |
178 | + } |
179 | }; |
180 | |
181 | } |
182 | |
183 | === modified file 'include/test/mir_test_doubles/stub_scene_surface.h' |
184 | --- include/test/mir_test_doubles/stub_scene_surface.h 2014-06-19 11:35:40 +0000 |
185 | +++ include/test/mir_test_doubles/stub_scene_surface.h 2014-06-23 02:58:32 +0000 |
186 | @@ -86,7 +86,7 @@ |
187 | void set_reception_mode(input::InputReceptionMode mode) override { input_mode = mode; } |
188 | |
189 | void set_cursor_image(std::shared_ptr<graphics::CursorImage> const& /* image */) {} |
190 | - std::shared_ptr<graphics::CursorImage> cursor_image() { return {}; } |
191 | + std::shared_ptr<graphics::CursorImage> cursor_image() const { return {}; } |
192 | |
193 | MirPixelFormat pixel_format() const override { return mir_pixel_format_xrgb_8888; } |
194 | |
195 | |
196 | === modified file 'include/test/mir_test_framework/stubbed_server_configuration.h' |
197 | --- include/test/mir_test_framework/stubbed_server_configuration.h 2014-06-17 11:21:31 +0000 |
198 | +++ include/test/mir_test_framework/stubbed_server_configuration.h 2014-06-23 02:58:32 +0000 |
199 | @@ -47,6 +47,8 @@ |
200 | std::shared_ptr<input::InputDispatcher> the_input_dispatcher() override; |
201 | std::shared_ptr<shell::InputTargeter> the_input_targeter() override; |
202 | |
203 | + std::shared_ptr<graphics::Cursor> the_cursor() override; |
204 | + |
205 | private: |
206 | std::shared_ptr<graphics::Platform> graphics_platform; |
207 | std::vector<geometry::Rectangle> const display_rects; |
208 | |
209 | === modified file 'src/client/cursor_configuration.h' |
210 | --- src/client/cursor_configuration.h 2014-05-14 16:50:03 +0000 |
211 | +++ src/client/cursor_configuration.h 2014-06-23 02:58:32 +0000 |
212 | @@ -25,6 +25,8 @@ |
213 | // Will grow to include cursors specified by raw RGBA data, hotspots, etc... |
214 | struct MirCursorConfiguration |
215 | { |
216 | + MirCursorConfiguration(char const* name); |
217 | + |
218 | std::string name; |
219 | }; |
220 | |
221 | |
222 | === modified file 'src/client/mir_cursor_api.cpp' |
223 | --- src/client/mir_cursor_api.cpp 2014-05-14 16:50:03 +0000 |
224 | +++ src/client/mir_cursor_api.cpp 2014-06-23 02:58:32 +0000 |
225 | @@ -19,9 +19,15 @@ |
226 | #include "mir_toolkit/mir_cursor_configuration.h" |
227 | #include "cursor_configuration.h" |
228 | |
229 | +#include <memory> |
230 | + |
231 | char const *const mir_default_cursor_name = "default"; |
232 | -char const *const mir_disabled_cursor_name = nullptr; |
233 | +char const *const mir_disabled_cursor_name = "disabled"; |
234 | |
235 | +MirCursorConfiguration::MirCursorConfiguration(char const* name) : |
236 | + name{name ? name : std::string()} |
237 | +{ |
238 | +} |
239 | |
240 | void mir_cursor_configuration_destroy(MirCursorConfiguration *cursor) |
241 | { |
242 | @@ -32,10 +38,7 @@ |
243 | { |
244 | try |
245 | { |
246 | - auto c = new MirCursorConfiguration; |
247 | - c->name = std::string(name); |
248 | - |
249 | - return c; |
250 | + return new MirCursorConfiguration(name); |
251 | } |
252 | catch (...) |
253 | { |
254 | |
255 | === modified file 'src/client/mir_surface.cpp' |
256 | --- src/client/mir_surface.cpp 2014-06-19 16:15:42 +0000 |
257 | +++ src/client/mir_surface.cpp 2014-06-23 02:58:32 +0000 |
258 | @@ -326,7 +326,7 @@ |
259 | { |
260 | std::unique_lock<decltype(mutex)> lock(mutex); |
261 | setting.mutable_surfaceid()->CopyFrom(surface.id()); |
262 | - if (cursor->name != "") |
263 | + if (cursor && cursor->name != mir_disabled_cursor_name) |
264 | setting.set_name(cursor->name.c_str()); |
265 | } |
266 | |
267 | |
268 | === modified file 'src/server/default_server_configuration.cpp' |
269 | --- src/server/default_server_configuration.cpp 2014-06-11 16:11:32 +0000 |
270 | +++ src/server/default_server_configuration.cpp 2014-06-23 02:58:32 +0000 |
271 | @@ -101,30 +101,6 @@ |
272 | }); |
273 | } |
274 | |
275 | -std::shared_ptr<mi::CursorListener> |
276 | -mir::DefaultServerConfiguration::the_cursor_listener() |
277 | -{ |
278 | - struct DefaultCursorListener : mi::CursorListener |
279 | - { |
280 | - DefaultCursorListener(std::shared_ptr<mg::Cursor> const& cursor) : |
281 | - cursor(cursor) |
282 | - { |
283 | - } |
284 | - |
285 | - void cursor_moved_to(float abs_x, float abs_y) |
286 | - { |
287 | - cursor->move_to(geom::Point{abs_x, abs_y}); |
288 | - } |
289 | - |
290 | - std::shared_ptr<mg::Cursor> const cursor; |
291 | - }; |
292 | - return cursor_listener( |
293 | - [this]() -> std::shared_ptr<mi::CursorListener> |
294 | - { |
295 | - return std::make_shared<DefaultCursorListener>(the_cursor()); |
296 | - }); |
297 | -} |
298 | - |
299 | std::shared_ptr<ms::SurfaceConfigurator> mir::DefaultServerConfiguration::the_surface_configurator() |
300 | { |
301 | struct DefaultSurfaceConfigurator : public ms::SurfaceConfigurator |
302 | |
303 | === modified file 'src/server/graphics/default_configuration.cpp' |
304 | --- src/server/graphics/default_configuration.cpp 2014-06-10 14:40:23 +0000 |
305 | +++ src/server/graphics/default_configuration.cpp 2014-06-23 02:58:32 +0000 |
306 | @@ -34,6 +34,8 @@ |
307 | #include "mir/abnormal_exit.h" |
308 | #include "mir/emergency_cleanup.h" |
309 | |
310 | +#include "mir_toolkit/common.h" |
311 | + |
312 | #include <boost/throw_exception.hpp> |
313 | |
314 | #include "builtin_cursor_images.h" |
315 | @@ -138,7 +140,7 @@ |
316 | return default_cursor_image( |
317 | [this]() |
318 | { |
319 | - return the_cursor_images()->image("arrow", default_cursor_size); |
320 | + return the_cursor_images()->image(mir_default_cursor_name, default_cursor_size); |
321 | }); |
322 | } |
323 | |
324 | |
325 | === modified file 'src/server/input/CMakeLists.txt' |
326 | --- src/server/input/CMakeLists.txt 2014-06-02 17:07:02 +0000 |
327 | +++ src/server/input/CMakeLists.txt 2014-06-23 02:58:32 +0000 |
328 | @@ -10,6 +10,7 @@ |
329 | nested_input_configuration.cpp |
330 | display_input_region.cpp |
331 | vt_filter.cpp |
332 | + cursor_controller.cpp |
333 | default_configuration.cpp |
334 | ) |
335 | |
336 | |
337 | === added file 'src/server/input/cursor_controller.cpp' |
338 | --- src/server/input/cursor_controller.cpp 1970-01-01 00:00:00 +0000 |
339 | +++ src/server/input/cursor_controller.cpp 2014-06-23 02:58:32 +0000 |
340 | @@ -0,0 +1,244 @@ |
341 | +/* |
342 | + * Copyright © 2014 Canonical Ltd. |
343 | + * |
344 | + * This program is free software: you can redistribute it and/or modify it |
345 | + * under the terms of the GNU General Public License version 3, |
346 | + * as published by the Free Software Foundation. |
347 | + * |
348 | + * This program is distributed in the hope that it will be useful, |
349 | + * but WITHOUT ANY WARRANTY; without even the implied warranty of |
350 | + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
351 | + * GNU General Public License for more details. |
352 | + * |
353 | + * You should have received a copy of the GNU General Public License |
354 | + * along with this program. If not, see <http://www.gnu.org/licenses/>. |
355 | + * |
356 | + * Authored by: Robert Carr <robert.carr@canonical.com> |
357 | + */ |
358 | + |
359 | +#include "cursor_controller.h" |
360 | + |
361 | +#include "mir/input/input_targets.h" |
362 | +#include "mir/input/surface.h" |
363 | +#include "mir/graphics/cursor.h" |
364 | +#include "mir/scene/observer.h" |
365 | +#include "mir/scene/surface_observer.h" |
366 | +#include "mir/scene/surface.h" |
367 | + |
368 | +#include <functional> |
369 | +#include <mutex> |
370 | +#include <map> |
371 | + |
372 | +#include <assert.h> |
373 | + |
374 | +namespace mi = mir::input; |
375 | +namespace mg = mir::graphics; |
376 | +namespace ms = mir::scene; |
377 | +namespace geom = mir::geometry; |
378 | + |
379 | +namespace |
380 | +{ |
381 | + |
382 | +struct UpdateCursorOnSurfaceChanges : ms::SurfaceObserver |
383 | +{ |
384 | + UpdateCursorOnSurfaceChanges(mi::CursorController* cursor_controller) |
385 | + : cursor_controller(cursor_controller) |
386 | + { |
387 | + } |
388 | + |
389 | + void attrib_changed(MirSurfaceAttrib, int) override |
390 | + { |
391 | + // Attribute changing alone wont trigger a cursor update |
392 | + } |
393 | + void resized_to(geom::Size const&) override |
394 | + { |
395 | + cursor_controller->update_cursor_image(); |
396 | + } |
397 | + void moved_to(geom::Point const&) override |
398 | + { |
399 | + cursor_controller->update_cursor_image(); |
400 | + } |
401 | + void hidden_set_to(bool) override |
402 | + { |
403 | + cursor_controller->update_cursor_image(); |
404 | + } |
405 | + void frame_posted(int) override |
406 | + { |
407 | + // Frame posting wont trigger a cursor update |
408 | + } |
409 | + void alpha_set_to(float) override |
410 | + { |
411 | + cursor_controller->update_cursor_image(); |
412 | + } |
413 | + void transformation_set_to(glm::mat4 const&) override |
414 | + { |
415 | + cursor_controller->update_cursor_image(); |
416 | + } |
417 | + void reception_mode_set_to(mi::InputReceptionMode) override |
418 | + { |
419 | + cursor_controller->update_cursor_image(); |
420 | + } |
421 | + void cursor_image_set_to(mg::CursorImage const&) override |
422 | + { |
423 | + cursor_controller->update_cursor_image(); |
424 | + } |
425 | + void orientation_set_to(MirOrientation /* orientation */) override |
426 | + { |
427 | + // No need to update cursor for orientation property change alone. |
428 | + } |
429 | + |
430 | + mi::CursorController* const cursor_controller; |
431 | +}; |
432 | + |
433 | +struct UpdateCursorOnSceneChanges : ms::Observer |
434 | +{ |
435 | + UpdateCursorOnSceneChanges(mi::CursorController* cursor_controller) |
436 | + : cursor_controller(cursor_controller) |
437 | + { |
438 | + } |
439 | + |
440 | + void add_surface_observer(ms::Surface* surface) |
441 | + { |
442 | + auto const observer = std::make_shared<UpdateCursorOnSurfaceChanges>(cursor_controller); |
443 | + surface->add_observer(observer); |
444 | + |
445 | + { |
446 | + std::unique_lock<decltype(surface_observers_guard)> lg(surface_observers_guard); |
447 | + surface_observers[surface] = observer; |
448 | + } |
449 | + } |
450 | + |
451 | + void surface_added(ms::Surface *surface) |
452 | + { |
453 | + add_surface_observer(surface); |
454 | + cursor_controller->update_cursor_image(); |
455 | + } |
456 | + void surface_removed(ms::Surface *surface) |
457 | + { |
458 | + { |
459 | + std::unique_lock<decltype(surface_observers_guard)> lg(surface_observers_guard); |
460 | + auto it = surface_observers.find(surface); |
461 | + if (it != surface_observers.end()) |
462 | + { |
463 | + surface->remove_observer(it->second); |
464 | + surface_observers.erase(it); |
465 | + } |
466 | + } |
467 | + cursor_controller->update_cursor_image(); |
468 | + } |
469 | + void surfaces_reordered() |
470 | + { |
471 | + cursor_controller->update_cursor_image(); |
472 | + } |
473 | + |
474 | + void surface_exists(ms::Surface *surface) |
475 | + { |
476 | + add_surface_observer(surface); |
477 | + cursor_controller->update_cursor_image(); |
478 | + } |
479 | + |
480 | + void end_observation() |
481 | + { |
482 | + std::unique_lock<decltype(surface_observers_guard)> lg(surface_observers_guard); |
483 | + for (auto &kv : surface_observers) |
484 | + { |
485 | + auto surface = kv.first; |
486 | + if (surface) |
487 | + surface->remove_observer(kv.second); |
488 | + } |
489 | + surface_observers.clear(); |
490 | + } |
491 | + |
492 | +private: |
493 | + mi::CursorController* const cursor_controller; |
494 | + |
495 | + std::mutex surface_observers_guard; |
496 | + std::map<ms::Surface*, std::weak_ptr<ms::SurfaceObserver>> surface_observers; |
497 | +}; |
498 | + |
499 | +std::shared_ptr<mi::Surface> topmost_surface_containing_point( |
500 | + std::shared_ptr<mi::InputTargets> const& targets, geom::Point const& point) |
501 | +{ |
502 | + std::shared_ptr<mi::Surface> top_surface_at_point; |
503 | + targets->for_each([&top_surface_at_point, &point] |
504 | + (std::shared_ptr<mi::Surface> const& surface) |
505 | + { |
506 | + if (surface->input_area_contains(point)) |
507 | + top_surface_at_point = surface; |
508 | + }); |
509 | + return top_surface_at_point; |
510 | +} |
511 | + |
512 | +} |
513 | + |
514 | +mi::CursorController::CursorController(std::shared_ptr<mi::InputTargets> const& input_targets, |
515 | + std::shared_ptr<mg::Cursor> const& cursor, |
516 | + std::shared_ptr<mg::CursorImage> const& default_cursor_image) : |
517 | + input_targets(input_targets), |
518 | + cursor(cursor), |
519 | + default_cursor_image(default_cursor_image), |
520 | + current_cursor(default_cursor_image) |
521 | +{ |
522 | + // TODO: Add observer could return weak_ptr to eliminate this |
523 | + // pattern |
524 | + auto strong_observer = std::make_shared<UpdateCursorOnSceneChanges>(this); |
525 | + input_targets->add_observer(strong_observer); |
526 | + observer = strong_observer; |
527 | +} |
528 | + |
529 | +mi::CursorController::~CursorController() |
530 | +{ |
531 | + try |
532 | + { |
533 | + input_targets->remove_observer(observer); |
534 | + } |
535 | + catch (...) |
536 | + { |
537 | + std::terminate(); |
538 | + } |
539 | +} |
540 | + |
541 | +void mi::CursorController::set_cursor_image_locked(std::lock_guard<std::mutex> const&, |
542 | + std::shared_ptr<mg::CursorImage> const& image) |
543 | +{ |
544 | + if (current_cursor == image) |
545 | + { |
546 | + return; |
547 | + } |
548 | + |
549 | + current_cursor = image; |
550 | + if (image) |
551 | + cursor->show(*image); |
552 | + else |
553 | + cursor->hide(); |
554 | +} |
555 | + |
556 | +void mi::CursorController::update_cursor_image_locked(std::lock_guard<std::mutex> const& lg) |
557 | +{ |
558 | + auto surface = topmost_surface_containing_point(input_targets, cursor_location); |
559 | + if (surface) |
560 | + { |
561 | + set_cursor_image_locked(lg, surface->cursor_image()); |
562 | + } |
563 | + else |
564 | + { |
565 | + set_cursor_image_locked(lg, default_cursor_image); |
566 | + } |
567 | +} |
568 | + |
569 | +void mi::CursorController::update_cursor_image() |
570 | +{ |
571 | + std::lock_guard<std::mutex> lg(cursor_state_guard); |
572 | + update_cursor_image_locked(lg); |
573 | +} |
574 | + |
575 | +void mi::CursorController::cursor_moved_to(float abs_x, float abs_y) |
576 | +{ |
577 | + std::lock_guard<std::mutex> lg(cursor_state_guard); |
578 | + |
579 | + cursor_location = geom::Point{geom::X{abs_x}, geom::Y{abs_y}}; |
580 | + |
581 | + update_cursor_image_locked(lg); |
582 | + |
583 | + cursor->move_to(cursor_location); |
584 | +} |
585 | |
586 | === added file 'src/server/input/cursor_controller.h' |
587 | --- src/server/input/cursor_controller.h 1970-01-01 00:00:00 +0000 |
588 | +++ src/server/input/cursor_controller.h 2014-06-23 02:58:32 +0000 |
589 | @@ -0,0 +1,77 @@ |
590 | +/* |
591 | + * Copyright © 2014 Canonical Ltd. |
592 | + * |
593 | + * This program is free software: you can redistribute it and/or modify it |
594 | + * under the terms of the GNU General Public License version 3, |
595 | + * as published by the Free Software Foundation. |
596 | + * |
597 | + * This program is distributed in the hope that it will be useful, |
598 | + * but WITHOUT ANY WARRANTY; without even the implied warranty of |
599 | + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
600 | + * GNU General Public License for more details. |
601 | + * |
602 | + * You should have received a copy of the GNU General Public License |
603 | + * along with this program. If not, see <http://www.gnu.org/licenses/>. |
604 | + * |
605 | + * Authored by: Robert Carr <robert.carr@canonical.com> |
606 | + */ |
607 | + |
608 | +#ifndef MIR_INPUT_CURSOR_CONTROLLER_H_ |
609 | +#define MIR_INPUT_CURSOR_CONTROLLER_H_ |
610 | + |
611 | +#include "mir/input/cursor_listener.h" |
612 | +#include "mir/geometry/point.h" |
613 | + |
614 | +#include <memory> |
615 | +#include <mutex> |
616 | + |
617 | +namespace mir |
618 | +{ |
619 | +namespace graphics |
620 | +{ |
621 | +class Cursor; |
622 | +class CursorImage; |
623 | +} |
624 | +namespace scene |
625 | +{ |
626 | +class Observer; |
627 | +} |
628 | + |
629 | +namespace input |
630 | +{ |
631 | +class InputTargets; |
632 | + |
633 | +class CursorController : public CursorListener |
634 | +{ |
635 | +public: |
636 | + CursorController(std::shared_ptr<InputTargets> const& input_targets, |
637 | + std::shared_ptr<graphics::Cursor> const& cursor, |
638 | + std::shared_ptr<graphics::CursorImage> const& default_cursor_image); |
639 | + virtual ~CursorController(); |
640 | + |
641 | + void cursor_moved_to(float abs_x, float abs_y); |
642 | + |
643 | + // Trigger an update of the cursor image without cursor motion, e.g. |
644 | + // in response to scene changes. |
645 | + void update_cursor_image(); |
646 | + |
647 | +private: |
648 | + std::shared_ptr<InputTargets> const input_targets; |
649 | + std::shared_ptr<graphics::Cursor> const cursor; |
650 | + std::shared_ptr<graphics::CursorImage> const default_cursor_image; |
651 | + |
652 | + std::mutex cursor_state_guard; |
653 | + geometry::Point cursor_location; |
654 | + std::shared_ptr<graphics::CursorImage> current_cursor; |
655 | + |
656 | + std::weak_ptr<scene::Observer> observer; |
657 | + |
658 | + |
659 | + void update_cursor_image_locked(std::lock_guard<std::mutex> const&); |
660 | + void set_cursor_image_locked(std::lock_guard<std::mutex> const&, std::shared_ptr<graphics::CursorImage> const& image); |
661 | +}; |
662 | + |
663 | +} |
664 | +} |
665 | + |
666 | +#endif // MIR_INPUT_CURSOR_CONTROLLER_H_ |
667 | |
668 | === modified file 'src/server/input/default_configuration.cpp' |
669 | --- src/server/input/default_configuration.cpp 2014-06-06 10:03:54 +0000 |
670 | +++ src/server/input/default_configuration.cpp 2014-06-23 02:58:32 +0000 |
671 | @@ -28,6 +28,7 @@ |
672 | #include "event_filter_chain.h" |
673 | #include "nested_input_configuration.h" |
674 | #include "null_input_configuration.h" |
675 | +#include "cursor_controller.h" |
676 | #include "null_input_dispatcher.h" |
677 | #include "null_input_targeter.h" |
678 | |
679 | @@ -193,4 +194,14 @@ |
680 | return the_input_configuration()->the_input_channel_factory(); |
681 | } |
682 | |
683 | +std::shared_ptr<mi::CursorListener> |
684 | +mir::DefaultServerConfiguration::the_cursor_listener() |
685 | +{ |
686 | + return cursor_listener( |
687 | + [this]() -> std::shared_ptr<mi::CursorListener> |
688 | + { |
689 | + return std::make_shared<mi::CursorController>(the_input_targets(), |
690 | + the_cursor(), the_default_cursor_image()); |
691 | + }); |
692 | |
693 | +} |
694 | |
695 | === modified file 'src/server/scene/basic_surface.cpp' |
696 | --- src/server/scene/basic_surface.cpp 2014-06-20 12:05:55 +0000 |
697 | +++ src/server/scene/basic_surface.cpp 2014-06-23 02:58:32 +0000 |
698 | @@ -504,13 +504,16 @@ |
699 | |
700 | void ms::BasicSurface::set_cursor_image(std::shared_ptr<mg::CursorImage> const& image) |
701 | { |
702 | + { |
703 | std::unique_lock<std::mutex> lock(guard); |
704 | cursor_image_ = image; |
705 | + } |
706 | |
707 | - observers.cursor_image_set_to(*image); |
708 | + observers.cursor_image_set_to(*image); |
709 | } |
710 | |
711 | -std::shared_ptr<mg::CursorImage> ms::BasicSurface::cursor_image() |
712 | + |
713 | +std::shared_ptr<mg::CursorImage> ms::BasicSurface::cursor_image() const |
714 | { |
715 | std::unique_lock<std::mutex> lock(guard); |
716 | return cursor_image_; |
717 | |
718 | === modified file 'src/server/scene/basic_surface.h' |
719 | --- src/server/scene/basic_surface.h 2014-06-20 12:05:55 +0000 |
720 | +++ src/server/scene/basic_surface.h 2014-06-23 02:58:32 +0000 |
721 | @@ -140,7 +140,7 @@ |
722 | void show() override; |
723 | |
724 | void set_cursor_image(std::shared_ptr<graphics::CursorImage> const& image); |
725 | - std::shared_ptr<graphics::CursorImage> cursor_image(); |
726 | + std::shared_ptr<graphics::CursorImage> cursor_image() const; |
727 | |
728 | void add_observer(std::shared_ptr<SurfaceObserver> const& observer) override; |
729 | void remove_observer(std::weak_ptr<SurfaceObserver> const& observer) override; |
730 | |
731 | === modified file 'tests/acceptance-tests/test_client_cursor_api.cpp' |
732 | --- tests/acceptance-tests/test_client_cursor_api.cpp 2014-06-19 00:02:28 +0000 |
733 | +++ tests/acceptance-tests/test_client_cursor_api.cpp 2014-06-23 02:58:32 +0000 |
734 | @@ -23,7 +23,6 @@ |
735 | #include "mir/scene/surface_factory.h" |
736 | #include "mir/scene/null_observer.h" |
737 | #include "mir/scene/null_surface_observer.h" |
738 | -#include "mir/compositor/scene.h" |
739 | |
740 | #include "mir_toolkit/mir_client_library.h" |
741 | |
742 | @@ -31,6 +30,7 @@ |
743 | #include "mir_test/fake_shared.h" |
744 | #include "mir_test/event_factory.h" |
745 | #include "mir_test/wait_condition.h" |
746 | +#include "mir_test_framework/server_runner.h" |
747 | #include "mir_test_framework/display_server_test_fixture.h" |
748 | #include "mir_test_framework/input_testing_server_configuration.h" |
749 | #include "mir_test_framework/input_testing_client_configuration.h" |
750 | @@ -59,7 +59,11 @@ |
751 | MOCK_METHOD1(show, void(mg::CursorImage const&)); |
752 | MOCK_METHOD0(hide, void()); |
753 | |
754 | - MOCK_METHOD1(move_to, void(geom::Point)); |
755 | + // We are not interested in mocking the motion in these tests as we |
756 | + // generate it ourself. |
757 | + void move_to(geom::Point) |
758 | + { |
759 | + } |
760 | }; |
761 | |
762 | struct NamedCursorImage : public mg::CursorImage |
763 | @@ -101,133 +105,87 @@ |
764 | return cursor_is_named(arg, name); |
765 | } |
766 | |
767 | -struct CursorSettingClient : mtf::TestingClientConfiguration |
768 | +struct ClientConfig : mtf::TestingClientConfiguration |
769 | { |
770 | - static std::string const mir_test_socket; |
771 | + std::string connect_string; |
772 | |
773 | std::string const client_name; |
774 | |
775 | - mtf::CrossProcessSync set_cursor_complete; |
776 | - mtf::CrossProcessSync client_may_exit; |
777 | + mt::Barrier& set_cursor_complete; |
778 | + mt::Barrier& client_may_exit; |
779 | |
780 | - std::function<void(MirSurface*)> const set_cursor; |
781 | + std::function<void(MirSurface*)> set_cursor; |
782 | |
783 | - CursorSettingClient(std::string const& client_name, |
784 | - mtf::CrossProcessSync const& cursor_ready_fence, |
785 | - mtf::CrossProcessSync const& client_may_exit_fence, |
786 | - std::function<void(MirSurface*)> const& set_cursor) |
787 | + ClientConfig(std::string const& client_name, |
788 | + mt::Barrier& cursor_ready_fence, |
789 | + mt::Barrier& client_may_exit_fence) |
790 | : client_name(client_name), |
791 | set_cursor_complete(cursor_ready_fence), |
792 | - client_may_exit(client_may_exit_fence), |
793 | - set_cursor(set_cursor) |
794 | - { |
795 | - } |
796 | + client_may_exit(client_may_exit_fence) |
797 | + { |
798 | + } |
799 | + |
800 | + virtual void thread_exec() |
801 | + { |
802 | + auto connection = mir_connect_sync(connect_string.c_str(), |
803 | + client_name.c_str()); |
804 | + |
805 | + ASSERT_TRUE(connection != NULL); |
806 | + MirSurfaceParameters const request_params = |
807 | + { |
808 | + client_name.c_str(), |
809 | + // For this fixture, we force geometry on server side |
810 | + 0, 0, |
811 | + mir_pixel_format_abgr_8888, |
812 | + mir_buffer_usage_hardware, |
813 | + mir_display_output_id_invalid |
814 | + }; |
815 | + auto surface = mir_connection_create_surface_sync(connection, &request_params); |
816 | + |
817 | + set_cursor(surface); |
818 | + set_cursor_complete.ready(); |
819 | + |
820 | + client_may_exit.ready(); |
821 | + |
822 | + mir_surface_release_sync(surface); |
823 | + mir_connection_release(connection); |
824 | + } |
825 | + void tear_down() { if (thread.joinable()) thread.join(); } |
826 | |
827 | void exec() override |
828 | { |
829 | - auto connection = mir_connect_sync(mir_test_socket.c_str(), |
830 | - client_name.c_str()); |
831 | - |
832 | - ASSERT_TRUE(connection != NULL); |
833 | - MirSurfaceParameters const request_params = |
834 | - { |
835 | - client_name.c_str(), |
836 | - // For this fixture, we force geometry on server side |
837 | - 0, 0, |
838 | - mir_pixel_format_abgr_8888, |
839 | - mir_buffer_usage_hardware, |
840 | - mir_display_output_id_invalid |
841 | - }; |
842 | - auto surface = mir_connection_create_surface_sync(connection, &request_params); |
843 | - |
844 | - set_cursor(surface); |
845 | - set_cursor_complete.signal_ready(); |
846 | - |
847 | - client_may_exit.wait_for_signal_ready_for(); |
848 | - |
849 | - mir_surface_release_sync(surface); |
850 | - mir_connection_release(connection); |
851 | - } |
852 | -}; |
853 | - |
854 | -std::string const CursorSettingClient::mir_test_socket = mtf::test_socket_file(); |
855 | - |
856 | -struct MockSurfaceObserver : public ms::NullSurfaceObserver |
857 | -{ |
858 | - MOCK_METHOD1(cursor_image_set_to, void(mg::CursorImage const&)); |
859 | -}; |
860 | - |
861 | -struct SurfaceObserverInstaller : public ms::NullObserver |
862 | -{ |
863 | - SurfaceObserverInstaller(std::shared_ptr<ms::SurfaceObserver> const& observer) |
864 | - : observer(observer) |
865 | - { |
866 | - } |
867 | - |
868 | - void surface_added(ms::Surface *surf) override |
869 | - { |
870 | - surf->add_observer(observer); |
871 | - } |
872 | - |
873 | - std::shared_ptr<ms::SurfaceObserver> const observer; |
874 | -}; |
875 | - |
876 | -struct SurfaceObservingServerConfiguration : mtf::TestingServerConfiguration |
877 | -{ |
878 | - SurfaceObservingServerConfiguration(std::function<void(MockSurfaceObserver&)> const& set_expectations) |
879 | - : set_expectations(set_expectations), |
880 | - observer(std::make_shared<MockSurfaceObserver>()) |
881 | - { |
882 | - } |
883 | - |
884 | - void on_start() override |
885 | - { |
886 | - auto scene = the_scene(); |
887 | - scene->add_observer(std::make_shared<SurfaceObserverInstaller>(observer)); |
888 | - |
889 | - set_expectations(*observer); |
890 | - } |
891 | - |
892 | - std::function<void(MockSurfaceObserver&)> const set_expectations; |
893 | - std::shared_ptr<MockSurfaceObserver> const observer; |
894 | -}; |
895 | - |
896 | -typedef unsigned ClientCount; |
897 | -struct CursorTestServerConfiguration : mtf::InputTestingServerConfiguration |
898 | -{ |
899 | - std::shared_ptr<ms::PlacementStrategy> placement_strategy; |
900 | - mtf::CrossProcessSync client_ready_fence; |
901 | - mtf::CrossProcessSync client_may_exit_fence; |
902 | - int const number_of_clients; |
903 | - |
904 | - std::function<void(MockCursor&, mt::WaitCondition&)> const expect_cursor_states; |
905 | - std::function<void(CursorTestServerConfiguration*)> const synthesize_cursor_motion; |
906 | - |
907 | + thread = std::thread([this]{ thread_exec(); }); |
908 | + } |
909 | +private: |
910 | + std::thread thread; |
911 | +}; |
912 | + |
913 | +struct ServerConfiguration : mtf::InputTestingServerConfiguration |
914 | +{ |
915 | + mt::Barrier& cursor_configured_fence; |
916 | + mt::Barrier& client_may_exit_fence; |
917 | + |
918 | + std::function<void(MockCursor&, mt::WaitCondition&)> expect_cursor_states; |
919 | + std::function<void(ServerConfiguration*)> synthesize_cursor_motion; |
920 | + |
921 | + mtf::SurfaceGeometries client_geometries; |
922 | + mtf::SurfaceDepths client_depths; |
923 | + |
924 | MockCursor cursor; |
925 | - |
926 | - CursorTestServerConfiguration(mtf::SurfaceGeometries surface_geometries_by_name, |
927 | - mtf::SurfaceDepths surface_depths_by_name, |
928 | - mtf::CrossProcessSync client_ready_fence, |
929 | - mtf::CrossProcessSync client_may_exit_fence, |
930 | - ClientCount const number_of_clients, |
931 | - std::function<void(MockCursor&, mt::WaitCondition&)> const& expect_cursor_states, |
932 | - std::function<void(CursorTestServerConfiguration*)> const& synthesize_cursor_motion) |
933 | - : placement_strategy( |
934 | - std::make_shared<mtf::DeclarativePlacementStrategy>(InputTestingServerConfiguration::the_placement_strategy(), |
935 | - surface_geometries_by_name, surface_depths_by_name)), |
936 | - client_ready_fence(client_ready_fence), |
937 | - client_may_exit_fence(client_may_exit_fence), |
938 | - number_of_clients(number_of_clients), |
939 | - expect_cursor_states(expect_cursor_states), |
940 | - synthesize_cursor_motion(synthesize_cursor_motion) |
941 | + |
942 | + ServerConfiguration(mt::Barrier& cursor_configured_fence, mt::Barrier& client_may_exit_fence) |
943 | + : cursor_configured_fence(cursor_configured_fence), |
944 | + client_may_exit_fence(client_may_exit_fence) |
945 | { |
946 | } |
947 | - |
948 | + |
949 | std::shared_ptr<ms::PlacementStrategy> the_placement_strategy() override |
950 | { |
951 | - return placement_strategy; |
952 | + return std::make_shared<mtf::DeclarativePlacementStrategy>( |
953 | + InputTestingServerConfiguration::the_placement_strategy(), |
954 | + client_geometries, client_depths); |
955 | } |
956 | - |
957 | + |
958 | std::shared_ptr<mg::Cursor> the_cursor() override |
959 | { |
960 | return mt::fake_shared(cursor); |
961 | @@ -241,283 +199,332 @@ |
962 | void inject_input() |
963 | { |
964 | using namespace ::testing; |
965 | - |
966 | - for (int i = 1; i < number_of_clients + 1; i++) |
967 | - EXPECT_EQ(i, client_ready_fence.wait_for_signal_ready_for()); |
968 | - |
969 | + cursor_configured_fence.ready(); |
970 | + |
971 | mt::WaitCondition expectations_satisfied; |
972 | - |
973 | + |
974 | // Clear any states applied during server initialization. |
975 | Mock::VerifyAndClearExpectations(&cursor); |
976 | expect_cursor_states(cursor, expectations_satisfied); |
977 | + |
978 | + // We are only interested in the cursor image changes, not |
979 | + // the synthetic motion. |
980 | |
981 | synthesize_cursor_motion(this); |
982 | expectations_satisfied.wait_for_at_most_seconds(60); |
983 | - |
984 | - EXPECT_CALL(cursor, show(_)).Times(AnyNumber()); // Client shutdown |
985 | - for (int i = 0; i < number_of_clients; i++) |
986 | - client_may_exit_fence.signal_ready(); |
987 | - } |
988 | -}; |
989 | - |
990 | -} |
991 | - |
992 | -// TODO: A lot of common code setup in these tests could be moved to |
993 | -// a fixture. |
994 | -using TestClientCursorAPI = BespokeDisplayServerTestFixture; |
995 | - |
996 | -TEST_F(TestClientCursorAPI, client_cursor_request_is_made_surface_data) |
997 | -{ |
998 | - using namespace ::testing; |
999 | - |
1000 | - static std::string const test_client_name = "1"; |
1001 | - static std::string const client_1_cursor = "1"; |
1002 | - |
1003 | - static mtf::CrossProcessSync client_ready_fence, client_may_exit_fence; |
1004 | - |
1005 | - SurfaceObservingServerConfiguration config([&](MockSurfaceObserver &observer) |
1006 | - { |
1007 | - EXPECT_CALL(observer, cursor_image_set_to(_)).WillOnce(Invoke( |
1008 | - [&](mg::CursorImage const&) |
1009 | - { |
1010 | - client_may_exit_fence.signal_ready(); |
1011 | - })); |
1012 | - client_ready_fence.signal_ready(); |
1013 | - }); |
1014 | - launch_server_process(config); |
1015 | - |
1016 | - CursorSettingClient client1_conf(test_client_name, client_ready_fence, client_may_exit_fence, |
1017 | - [](MirSurface *surface) |
1018 | - { |
1019 | - auto conf = mir_cursor_configuration_from_name(client_1_cursor.c_str()); |
1020 | - mir_wait_for(mir_surface_configure_cursor(surface, conf)); |
1021 | - mir_cursor_configuration_destroy(conf); |
1022 | - }); |
1023 | - launch_client_process(client1_conf); |
1024 | + |
1025 | + Mock::VerifyAndClearExpectations(&cursor); |
1026 | + |
1027 | + // Client shutdown |
1028 | + EXPECT_CALL(cursor, show(_)).Times(AnyNumber()); |
1029 | + EXPECT_CALL(cursor, hide()).Times(AnyNumber()); |
1030 | + client_may_exit_fence.ready(); |
1031 | + } |
1032 | + |
1033 | +}; |
1034 | + |
1035 | +struct DeferredInProcessServer : testing::Test, private mtf::ServerRunner |
1036 | +{ |
1037 | + void TearDown() override { ServerRunner::stop_server(); } |
1038 | + |
1039 | + using ServerRunner::start_server; |
1040 | + using ServerRunner::new_connection; |
1041 | +}; |
1042 | + |
1043 | +struct TestClientCursorAPI : DeferredInProcessServer |
1044 | +{ |
1045 | + std::string const client_name_1 = "1"; |
1046 | + std::string const client_name_2 = "2"; |
1047 | + std::string const client_cursor_1 = "cursor-1"; |
1048 | + std::string const client_cursor_2 = "cursor-2"; |
1049 | + |
1050 | + // Reset to higher values for more clients. |
1051 | + mt::Barrier cursor_configured_fence{2}; |
1052 | + mt::Barrier client_may_exit_fence{2}; |
1053 | + |
1054 | + ServerConfiguration server_configuration{cursor_configured_fence, client_may_exit_fence}; |
1055 | + mir::DefaultServerConfiguration& server_config() override { return server_configuration; } |
1056 | + |
1057 | + ClientConfig client_config_1{client_name_1, cursor_configured_fence, client_may_exit_fence}; |
1058 | + ClientConfig client_config_2{client_name_2, cursor_configured_fence, client_may_exit_fence}; |
1059 | + |
1060 | + // Default number allows one client. |
1061 | + void set_client_count(unsigned count) |
1062 | + { |
1063 | + cursor_configured_fence.reset(count + 1); |
1064 | + client_may_exit_fence.reset(count + 1); |
1065 | + } |
1066 | + |
1067 | + void start_server() |
1068 | + { |
1069 | + DeferredInProcessServer::start_server(); |
1070 | + server_configuration.exec(); |
1071 | + } |
1072 | + |
1073 | + void start_client(ClientConfig& config) |
1074 | + { |
1075 | + config.connect_string = new_connection(); |
1076 | + config.exec(); |
1077 | + } |
1078 | + |
1079 | + void TearDown() |
1080 | + { |
1081 | + client_config_1.tear_down(); |
1082 | + client_config_2.tear_down(); |
1083 | + server_configuration.on_exit(); |
1084 | + DeferredInProcessServer::TearDown(); |
1085 | + } |
1086 | +}; |
1087 | + |
1088 | } |
1089 | |
1090 | // In this set we create a 1x1 client surface at the point (1,0). The client requests to disable the cursor |
1091 | // over this surface. Since the cursor starts at (0,0) we when we move the cursor by (1,0) thus causing it |
1092 | // to enter the bounds of the first surface, we should observe it being disabled. |
1093 | -// TODO: Enable |
1094 | -TEST_F(TestClientCursorAPI, DISABLED_client_may_disable_cursor_over_surface) |
1095 | +TEST_F(TestClientCursorAPI, client_may_disable_cursor_over_surface) |
1096 | { |
1097 | using namespace ::testing; |
1098 | |
1099 | - std::string const test_client_name = "1"; |
1100 | - mtf::SurfaceGeometries client_geometries; |
1101 | - client_geometries[test_client_name] = geom::Rectangle{geom::Point{geom::X{1}, geom::Y{0}}, |
1102 | - geom::Size{geom::Width{1}, geom::Height{1}}}; |
1103 | + server_configuration.client_geometries[client_name_1] = geom::Rectangle{geom::Point{geom::X{1}, geom::Y{0}}, |
1104 | + geom::Size{geom::Width{1}, geom::Height{1}}}; |
1105 | |
1106 | - mtf::CrossProcessSync client_ready_fence, client_may_exit_fence; |
1107 | |
1108 | - CursorTestServerConfiguration server_conf( |
1109 | - client_geometries, mtf::SurfaceDepths(), |
1110 | - client_ready_fence, client_may_exit_fence, |
1111 | - ClientCount{1}, |
1112 | - [](MockCursor& cursor, mt::WaitCondition& expectations_satisfied) |
1113 | + server_configuration.expect_cursor_states = [](MockCursor& cursor, mt::WaitCondition& expectations_satisfied) |
1114 | { |
1115 | EXPECT_CALL(cursor, hide()).Times(1) |
1116 | .WillOnce(mt::WakeUp(&expectations_satisfied)); |
1117 | - }, |
1118 | - [](CursorTestServerConfiguration *server) |
1119 | + }; |
1120 | + server_configuration.synthesize_cursor_motion = [](ServerConfiguration *server) |
1121 | { |
1122 | server->fake_event_hub->synthesize_event(mis::a_motion_event().with_movement(1, 0)); |
1123 | - }); |
1124 | - launch_server_process(server_conf); |
1125 | + }; |
1126 | + start_server(); |
1127 | |
1128 | - CursorSettingClient client_conf(test_client_name, client_ready_fence, client_may_exit_fence, |
1129 | - [](MirSurface *surface) |
1130 | + client_config_1.set_cursor = [](MirSurface *surface) |
1131 | { |
1132 | - // Disable cursor |
1133 | - mir_wait_for(mir_surface_configure_cursor(surface, |
1134 | - mir_cursor_configuration_from_name(mir_disabled_cursor_name))); |
1135 | - }); |
1136 | - launch_client_process(client_conf); |
1137 | + auto conf = mir_cursor_configuration_from_name(mir_disabled_cursor_name); |
1138 | + mir_wait_for(mir_surface_configure_cursor(surface, conf)); |
1139 | + mir_cursor_configuration_destroy(conf); |
1140 | + }; |
1141 | + start_client(client_config_1); |
1142 | } |
1143 | |
1144 | -// TODO: Enable |
1145 | -TEST_F(TestClientCursorAPI, DISABLED_cursor_restored_when_leaving_surface) |
1146 | +TEST_F(TestClientCursorAPI, cursor_restored_when_leaving_surface) |
1147 | { |
1148 | using namespace ::testing; |
1149 | |
1150 | - std::string const test_client_name = "1"; |
1151 | - mtf::SurfaceGeometries client_geometries; |
1152 | - client_geometries[test_client_name] = geom::Rectangle{geom::Point{geom::X{1}, geom::Y{0}}, |
1153 | - geom::Size{geom::Width{1}, geom::Height{1}}}; |
1154 | + server_configuration.client_geometries[client_name_1] = geom::Rectangle{geom::Point{geom::X{1}, geom::Y{0}}, |
1155 | + geom::Size{geom::Width{1}, geom::Height{1}}}; |
1156 | |
1157 | mtf::CrossProcessSync client_ready_fence, client_may_exit_fence; |
1158 | |
1159 | - CursorTestServerConfiguration server_conf( |
1160 | - client_geometries, mtf::SurfaceDepths(), |
1161 | - client_ready_fence, client_may_exit_fence, |
1162 | - ClientCount{1}, |
1163 | - [](MockCursor& cursor, mt::WaitCondition& expectations_satisfied) |
1164 | + server_configuration.expect_cursor_states = [](MockCursor& cursor, mt::WaitCondition& expectations_satisfied) |
1165 | { |
1166 | InSequence seq; |
1167 | EXPECT_CALL(cursor, hide()).Times(1); |
1168 | EXPECT_CALL(cursor, show(DefaultCursorImage())).Times(1) |
1169 | .WillOnce(mt::WakeUp(&expectations_satisfied)); |
1170 | - }, |
1171 | - [](CursorTestServerConfiguration *server) |
1172 | + }; |
1173 | + server_configuration.synthesize_cursor_motion = [](ServerConfiguration *server) |
1174 | { |
1175 | server->fake_event_hub->synthesize_event(mis::a_motion_event().with_movement(1, 0)); |
1176 | server->fake_event_hub->synthesize_event(mis::a_motion_event().with_movement(2,0)); |
1177 | - }); |
1178 | - launch_server_process(server_conf); |
1179 | - |
1180 | - CursorSettingClient client_conf(test_client_name, client_ready_fence, client_may_exit_fence, |
1181 | - [](MirSurface *surface) |
1182 | - { |
1183 | - // Disable cursor |
1184 | - mir_wait_for(mir_surface_configure_cursor(surface, |
1185 | - mir_cursor_configuration_from_name(mir_disabled_cursor_name))); |
1186 | - }); |
1187 | - launch_client_process(client_conf); |
1188 | -} |
1189 | - |
1190 | -// TODO: Enable |
1191 | -TEST_F(TestClientCursorAPI, DISABLED_cursor_changed_when_crossing_surface_boundaries) |
1192 | -{ |
1193 | - using namespace ::testing; |
1194 | - |
1195 | - static std::string const test_client_name_1 = "1"; |
1196 | - static std::string const test_client_name_2 = "2"; |
1197 | - static std::string const client_1_cursor = test_client_name_1; |
1198 | - static std::string const client_2_cursor = test_client_name_2; |
1199 | - |
1200 | - mtf::SurfaceGeometries client_geometries; |
1201 | - client_geometries[test_client_name_1] = geom::Rectangle{geom::Point{geom::X{1}, geom::Y{0}}, |
1202 | - geom::Size{geom::Width{1}, geom::Height{1}}}; |
1203 | - client_geometries[test_client_name_2] = geom::Rectangle{geom::Point{geom::X{2}, geom::Y{0}}, |
1204 | - geom::Size{geom::Width{1}, geom::Height{1}}}; |
1205 | - |
1206 | - mtf::CrossProcessSync client_ready_fence, client_may_exit_fence; |
1207 | - |
1208 | - CursorTestServerConfiguration server_conf( |
1209 | - client_geometries, mtf::SurfaceDepths(), |
1210 | - client_ready_fence, client_may_exit_fence, |
1211 | - ClientCount{2}, |
1212 | - [](MockCursor& cursor, mt::WaitCondition& expectations_satisfied) |
1213 | - { |
1214 | - InSequence seq; |
1215 | - EXPECT_CALL(cursor, show(CursorNamed(client_1_cursor))).Times(1); |
1216 | - EXPECT_CALL(cursor, show(CursorNamed(client_2_cursor))).Times(1) |
1217 | - .WillOnce(mt::WakeUp(&expectations_satisfied)); |
1218 | - }, |
1219 | - [](CursorTestServerConfiguration *server) |
1220 | - { |
1221 | - server->fake_event_hub->synthesize_event(mis::a_motion_event().with_movement(1, 0)); |
1222 | - server->fake_event_hub->synthesize_event(mis::a_motion_event().with_movement(1, 0)); |
1223 | - }); |
1224 | - launch_server_process(server_conf); |
1225 | - |
1226 | - CursorSettingClient client1_conf(test_client_name_1, client_ready_fence, client_may_exit_fence, |
1227 | - [](MirSurface *surface) |
1228 | - { |
1229 | - mir_wait_for(mir_surface_configure_cursor(surface, mir_cursor_configuration_from_name(client_1_cursor.c_str()))); |
1230 | - }); |
1231 | - launch_client_process(client1_conf); |
1232 | - CursorSettingClient client2_conf(test_client_name_2, client_ready_fence, client_may_exit_fence, |
1233 | - [](MirSurface *surface) |
1234 | - { |
1235 | - // Disable cursor |
1236 | - mir_wait_for(mir_surface_configure_cursor(surface, mir_cursor_configuration_from_name(client_2_cursor.c_str()))); |
1237 | - }); |
1238 | - launch_client_process(client2_conf); |
1239 | -} |
1240 | - |
1241 | -// TODO: Enable |
1242 | -TEST_F(TestClientCursorAPI, DISABLED_cursor_request_taken_from_top_surface) |
1243 | -{ |
1244 | - using namespace ::testing; |
1245 | - |
1246 | - static std::string const test_client_name_1 = "1"; |
1247 | - static std::string const test_client_name_2 = "2"; |
1248 | - static std::string const client_1_cursor = test_client_name_1; |
1249 | - static std::string const client_2_cursor = test_client_name_2; |
1250 | - |
1251 | - mtf::SurfaceGeometries client_geometries; |
1252 | - client_geometries[test_client_name_1] = geom::Rectangle{geom::Point{geom::X{1}, geom::Y{0}}, |
1253 | - geom::Size{geom::Width{1}, geom::Height{1}}}; |
1254 | - client_geometries[test_client_name_1] = geom::Rectangle{geom::Point{geom::X{1}, geom::Y{0}}, |
1255 | - geom::Size{geom::Width{1}, geom::Height{1}}}; |
1256 | - mtf::SurfaceDepths client_depths; |
1257 | - client_depths[test_client_name_1] = ms::DepthId{0}; |
1258 | - client_depths[test_client_name_2] = ms::DepthId{1}; |
1259 | - |
1260 | - mtf::CrossProcessSync client_ready_fence, client_may_exit_fence; |
1261 | - |
1262 | - CursorTestServerConfiguration server_conf( |
1263 | - client_geometries, client_depths, |
1264 | - client_ready_fence, client_may_exit_fence, |
1265 | - ClientCount{2}, |
1266 | - [](MockCursor& cursor, mt::WaitCondition& expectations_satisfied) |
1267 | - { |
1268 | - InSequence seq; |
1269 | - EXPECT_CALL(cursor, show(CursorNamed(client_2_cursor))).Times(1) |
1270 | - .WillOnce(mt::WakeUp(&expectations_satisfied)); |
1271 | - }, |
1272 | - [](CursorTestServerConfiguration *server) |
1273 | - { |
1274 | - server->fake_event_hub->synthesize_event(mis::a_motion_event().with_movement(1, 0)); |
1275 | - }); |
1276 | - launch_server_process(server_conf); |
1277 | - |
1278 | - CursorSettingClient client1_conf(test_client_name_1, client_ready_fence, client_may_exit_fence, |
1279 | - [](MirSurface *surface) |
1280 | - { |
1281 | - mir_wait_for(mir_surface_configure_cursor(surface, mir_cursor_configuration_from_name(client_1_cursor.c_str()))); |
1282 | - }); |
1283 | - launch_client_process(client1_conf); |
1284 | - CursorSettingClient client2_conf(test_client_name_2, client_ready_fence, client_may_exit_fence, |
1285 | - [](MirSurface *surface) |
1286 | - { |
1287 | - mir_wait_for(mir_surface_configure_cursor(surface, mir_cursor_configuration_from_name(client_1_cursor.c_str()))); |
1288 | - }); |
1289 | - |
1290 | - launch_client_process(client2_conf); |
1291 | -} |
1292 | - |
1293 | -// TODO: Enable |
1294 | -TEST_F(TestClientCursorAPI, DISABLED_cursor_request_applied_without_cursor_motion) |
1295 | -{ |
1296 | - using namespace ::testing; |
1297 | - static std::string const test_client_name_1 = "1"; |
1298 | - static std::string const client_1_cursor = test_client_name_1; |
1299 | - |
1300 | - mtf::SurfaceGeometries client_geometries; |
1301 | - client_geometries[test_client_name_1] = geom::Rectangle{geom::Point{geom::X{0}, geom::Y{0}}, |
1302 | - geom::Size{geom::Width{1}, geom::Height{1}}}; |
1303 | - |
1304 | - mtf::CrossProcessSync client_ready_fence, client_may_exit_fence; |
1305 | - static mtf::CrossProcessSync client_may_change_cursor; |
1306 | - |
1307 | - CursorTestServerConfiguration server_conf( |
1308 | - client_geometries, mtf::SurfaceDepths(), |
1309 | - client_ready_fence, client_may_exit_fence, |
1310 | - ClientCount{1}, |
1311 | - [](MockCursor& cursor, mt::WaitCondition& expectations_satisfied) |
1312 | - { |
1313 | - InSequence seq; |
1314 | - EXPECT_CALL(cursor, show(CursorNamed(client_1_cursor))).Times(1); |
1315 | + }; |
1316 | + start_server(); |
1317 | + |
1318 | + |
1319 | + client_config_1.set_cursor = [](MirSurface *surface) |
1320 | + { |
1321 | + // Disable cursor |
1322 | + auto conf = mir_cursor_configuration_from_name(mir_disabled_cursor_name); |
1323 | + mir_wait_for(mir_surface_configure_cursor(surface, conf)); |
1324 | + mir_cursor_configuration_destroy(conf); |
1325 | + }; |
1326 | + start_client(client_config_1); |
1327 | +} |
1328 | + |
1329 | +TEST_F(TestClientCursorAPI, cursor_changed_when_crossing_surface_boundaries) |
1330 | +{ |
1331 | + using namespace ::testing; |
1332 | + |
1333 | + server_configuration.client_geometries[client_name_1] = geom::Rectangle{geom::Point{geom::X{1}, geom::Y{0}}, |
1334 | + geom::Size{geom::Width{1}, geom::Height{1}}}; |
1335 | + server_configuration.client_geometries[client_name_2] = geom::Rectangle{geom::Point{geom::X{2}, geom::Y{0}}, |
1336 | + geom::Size{geom::Width{1}, geom::Height{1}}}; |
1337 | + set_client_count(2); |
1338 | + |
1339 | + server_configuration.expect_cursor_states = |
1340 | + [this](MockCursor& cursor, mt::WaitCondition& expectations_satisfied) |
1341 | + { |
1342 | + InSequence seq; |
1343 | + EXPECT_CALL(cursor, show(CursorNamed(client_cursor_1))).Times(1); |
1344 | + EXPECT_CALL(cursor, show(CursorNamed(client_cursor_2))).Times(1) |
1345 | + .WillOnce(mt::WakeUp(&expectations_satisfied)); |
1346 | + }; |
1347 | + server_configuration.synthesize_cursor_motion = |
1348 | + [](ServerConfiguration *server) |
1349 | + { |
1350 | + server->fake_event_hub->synthesize_event(mis::a_motion_event().with_movement(1, 0)); |
1351 | + server->fake_event_hub->synthesize_event(mis::a_motion_event().with_movement(1, 0)); |
1352 | + }; |
1353 | + start_server(); |
1354 | + |
1355 | + client_config_1.set_cursor = |
1356 | + [this](MirSurface *surface) |
1357 | + { |
1358 | + auto conf = mir_cursor_configuration_from_name(client_cursor_1.c_str()); |
1359 | + mir_wait_for(mir_surface_configure_cursor(surface, conf)); |
1360 | + mir_cursor_configuration_destroy(conf); |
1361 | + }; |
1362 | + start_client(client_config_1); |
1363 | + |
1364 | + client_config_2.set_cursor = |
1365 | + [this](MirSurface *surface) |
1366 | + { |
1367 | + auto conf = mir_cursor_configuration_from_name(client_cursor_2.c_str()); |
1368 | + mir_wait_for(mir_surface_configure_cursor(surface, conf)); |
1369 | + mir_cursor_configuration_destroy(conf); |
1370 | + }; |
1371 | + start_client(client_config_2); |
1372 | +} |
1373 | + |
1374 | +TEST_F(TestClientCursorAPI, cursor_request_taken_from_top_surface) |
1375 | +{ |
1376 | + using namespace ::testing; |
1377 | + |
1378 | + server_configuration.client_geometries[client_name_1] = geom::Rectangle{geom::Point{geom::X{1}, geom::Y{0}}, |
1379 | + geom::Size{geom::Width{1}, geom::Height{1}}}; |
1380 | + server_configuration.client_geometries[client_name_2] = geom::Rectangle{geom::Point{geom::X{1}, geom::Y{0}}, |
1381 | + geom::Size{geom::Width{1}, geom::Height{1}}}; |
1382 | + server_configuration.client_depths[client_name_1] = ms::DepthId{0}; |
1383 | + server_configuration.client_depths[client_name_2] = ms::DepthId{1}; |
1384 | + |
1385 | + set_client_count(2); |
1386 | + |
1387 | + server_configuration.expect_cursor_states = |
1388 | + [this](MockCursor& cursor, mt::WaitCondition& expectations_satisfied) |
1389 | + { |
1390 | + InSequence seq; |
1391 | + EXPECT_CALL(cursor, show(CursorNamed(client_cursor_2))).Times(1) |
1392 | + .WillOnce(mt::WakeUp(&expectations_satisfied)); |
1393 | + }; |
1394 | + server_configuration.synthesize_cursor_motion = |
1395 | + [](ServerConfiguration *server) |
1396 | + { |
1397 | + server->fake_event_hub->synthesize_event(mis::a_motion_event().with_movement(1, 0)); |
1398 | + }; |
1399 | + start_server(); |
1400 | + |
1401 | + |
1402 | + client_config_1.set_cursor = |
1403 | + [this](MirSurface *surface) |
1404 | + { |
1405 | + auto conf = mir_cursor_configuration_from_name(client_cursor_1.c_str()); |
1406 | + mir_wait_for(mir_surface_configure_cursor(surface, conf)); |
1407 | + mir_cursor_configuration_destroy(conf); |
1408 | + }; |
1409 | + client_config_2.set_cursor = |
1410 | + [this](MirSurface *surface) |
1411 | + { |
1412 | + auto conf = mir_cursor_configuration_from_name(client_cursor_2.c_str()); |
1413 | + mir_wait_for(mir_surface_configure_cursor(surface, conf)); |
1414 | + mir_cursor_configuration_destroy(conf); |
1415 | + }; |
1416 | + start_client(client_config_1); |
1417 | + start_client(client_config_2); |
1418 | +} |
1419 | + |
1420 | +namespace |
1421 | +{ |
1422 | + |
1423 | +// In the following test the cursor changes are not responsive |
1424 | +// to cursor motion so we need a different synchronization model. |
1425 | +struct WaitsToChangeCursorClient : ClientConfig |
1426 | +{ |
1427 | + WaitsToChangeCursorClient(std::string const& client_name, |
1428 | + mt::Barrier& cursor_ready_fence, |
1429 | + mt::Barrier& client_may_exit_fence) |
1430 | + : ClientConfig(client_name, cursor_ready_fence, client_may_exit_fence) |
1431 | + { |
1432 | + } |
1433 | + |
1434 | + void thread_exec() override |
1435 | + { |
1436 | + auto connection = mir_connect_sync(connect_string.c_str(), |
1437 | + client_name.c_str()); |
1438 | + |
1439 | + ASSERT_TRUE(connection != NULL); |
1440 | + MirSurfaceParameters const request_params = |
1441 | + { |
1442 | + client_name.c_str(), |
1443 | + // For this fixture, we force geometry on server side |
1444 | + 0, 0, |
1445 | + mir_pixel_format_abgr_8888, |
1446 | + mir_buffer_usage_hardware, |
1447 | + mir_display_output_id_invalid |
1448 | + }; |
1449 | + auto surface = mir_connection_create_surface_sync(connection, &request_params); |
1450 | + |
1451 | + set_cursor_complete.ready(); |
1452 | + set_cursor(surface); |
1453 | + |
1454 | + client_may_exit.ready(); |
1455 | + |
1456 | + mir_surface_release_sync(surface); |
1457 | + mir_connection_release(connection); |
1458 | + } |
1459 | +}; |
1460 | + |
1461 | +struct TestClientCursorAPINoMotion : TestClientCursorAPI |
1462 | +{ |
1463 | + mt::Barrier client_may_change_cursor{2}; |
1464 | + WaitsToChangeCursorClient waiting_client{client_name_1, cursor_configured_fence, client_may_exit_fence}; |
1465 | + |
1466 | + void TearDown() override |
1467 | + { |
1468 | + waiting_client.tear_down(); |
1469 | + TestClientCursorAPI::TearDown(); |
1470 | + } |
1471 | +}; |
1472 | + |
1473 | +} |
1474 | + |
1475 | +TEST_F(TestClientCursorAPINoMotion, cursor_request_applied_without_cursor_motion) |
1476 | +{ |
1477 | + using namespace ::testing; |
1478 | + |
1479 | + server_configuration.client_geometries[client_name_1] = |
1480 | + geom::Rectangle{geom::Point{geom::X{0}, geom::Y{0}}, |
1481 | + geom::Size{geom::Width{1}, geom::Height{1}}}; |
1482 | + |
1483 | + server_configuration.expect_cursor_states = |
1484 | + [this](MockCursor& cursor, mt::WaitCondition& expectations_satisfied) |
1485 | + { |
1486 | + InSequence seq; |
1487 | + EXPECT_CALL(cursor, show(CursorNamed(client_cursor_1))).Times(1); |
1488 | EXPECT_CALL(cursor, hide()).Times(1) |
1489 | - .WillOnce(mt::WakeUp(&expectations_satisfied)); |
1490 | - }, |
1491 | - [](CursorTestServerConfiguration * /* server */) |
1492 | - { |
1493 | - client_may_change_cursor.signal_ready(); |
1494 | - }); |
1495 | - launch_server_process(server_conf); |
1496 | - |
1497 | - CursorSettingClient client1_conf(test_client_name_1, client_ready_fence, client_may_exit_fence, |
1498 | - [&client_ready_fence](MirSurface *surface) |
1499 | - { |
1500 | - client_ready_fence.signal_ready(); |
1501 | - client_may_change_cursor.wait_for_signal_ready_for(); |
1502 | - mir_wait_for(mir_surface_configure_cursor(surface, mir_cursor_configuration_from_name(client_1_cursor.c_str()))); |
1503 | - mir_wait_for(mir_surface_configure_cursor(surface, |
1504 | - mir_cursor_configuration_from_name(mir_disabled_cursor_name))); |
1505 | - }); |
1506 | - launch_client_process(client1_conf); |
1507 | + .WillOnce(mt::WakeUp(&expectations_satisfied)); |
1508 | + }; |
1509 | + server_configuration.synthesize_cursor_motion = |
1510 | + [this](ServerConfiguration * /* server */) |
1511 | + { |
1512 | + client_may_change_cursor.ready(); |
1513 | + }; |
1514 | + start_server(); |
1515 | + |
1516 | + waiting_client.set_cursor = |
1517 | + [this](MirSurface *surface) |
1518 | + { |
1519 | + client_may_change_cursor.ready(); |
1520 | + auto conf1 = mir_cursor_configuration_from_name(client_cursor_1.c_str()); |
1521 | + auto conf2 = mir_cursor_configuration_from_name(mir_disabled_cursor_name); |
1522 | + |
1523 | + mir_wait_for(mir_surface_configure_cursor(surface, conf1)); |
1524 | + mir_wait_for(mir_surface_configure_cursor(surface, conf2)); |
1525 | + |
1526 | + mir_cursor_configuration_destroy(conf1); |
1527 | + mir_cursor_configuration_destroy(conf2); |
1528 | + }; |
1529 | + start_client(waiting_client); |
1530 | } |
1531 | + |
1532 | |
1533 | === modified file 'tests/mir_test_framework/input_testing_server_options.cpp' |
1534 | --- tests/mir_test_framework/input_testing_server_options.cpp 2014-06-03 11:04:15 +0000 |
1535 | +++ tests/mir_test_framework/input_testing_server_options.cpp 2014-06-23 02:58:32 +0000 |
1536 | @@ -71,12 +71,10 @@ |
1537 | { |
1538 | if (!input_configuration) |
1539 | { |
1540 | - std::shared_ptr<mi::CursorListener> null_cursor_listener{nullptr}; |
1541 | - |
1542 | input_configuration = std::make_shared<mtd::FakeEventHubInputConfiguration>( |
1543 | the_input_dispatcher(), |
1544 | the_input_region(), |
1545 | - null_cursor_listener, |
1546 | + the_cursor_listener(), |
1547 | the_input_report()); |
1548 | fake_event_hub = input_configuration->the_fake_event_hub(); |
1549 | |
1550 | |
1551 | === modified file 'tests/mir_test_framework/stubbed_server_configuration.cpp' |
1552 | --- tests/mir_test_framework/stubbed_server_configuration.cpp 2014-06-19 09:06:54 +0000 |
1553 | +++ tests/mir_test_framework/stubbed_server_configuration.cpp 2014-06-23 02:58:32 +0000 |
1554 | @@ -21,6 +21,7 @@ |
1555 | |
1556 | #include "mir/options/default_configuration.h" |
1557 | #include "mir/graphics/buffer_ipc_packer.h" |
1558 | +#include "mir/graphics/cursor.h" |
1559 | #include "mir/input/input_channel.h" |
1560 | #include "mir/input/input_manager.h" |
1561 | |
1562 | @@ -118,6 +119,13 @@ |
1563 | } |
1564 | }; |
1565 | |
1566 | +class StubCursor : public mg::Cursor |
1567 | +{ |
1568 | + void show(mg::CursorImage const&) override {} |
1569 | + void hide() override {} |
1570 | + void move_to(geom::Point) override {} |
1571 | +}; |
1572 | + |
1573 | class StubGraphicPlatform : public mtd::NullPlatform |
1574 | { |
1575 | public: |
1576 | @@ -162,7 +170,7 @@ |
1577 | { |
1578 | return std::make_shared<mtd::StubDisplay>(display_rects); |
1579 | } |
1580 | - |
1581 | + |
1582 | std::vector<geom::Rectangle> const display_rects; |
1583 | }; |
1584 | |
1585 | @@ -253,3 +261,9 @@ |
1586 | else |
1587 | return std::make_shared<mi::NullInputDispatcher>(); |
1588 | } |
1589 | + |
1590 | +std::shared_ptr<mg::Cursor> mtf::StubbedServerConfiguration::the_cursor() |
1591 | +{ |
1592 | + return std::make_shared<StubCursor>(); |
1593 | +} |
1594 | + |
1595 | |
1596 | === modified file 'tests/unit-tests/input/CMakeLists.txt' |
1597 | --- tests/unit-tests/input/CMakeLists.txt 2013-08-28 03:41:48 +0000 |
1598 | +++ tests/unit-tests/input/CMakeLists.txt 2014-06-23 02:58:32 +0000 |
1599 | @@ -3,6 +3,7 @@ |
1600 | list(APPEND UNIT_TEST_SOURCES |
1601 | ${CMAKE_CURRENT_SOURCE_DIR}/test_event_filter_chain.cpp |
1602 | ${CMAKE_CURRENT_SOURCE_DIR}/test_display_input_region.cpp |
1603 | + ${CMAKE_CURRENT_SOURCE_DIR}/test_cursor_controller.cpp |
1604 | ) |
1605 | |
1606 | set( |
1607 | |
1608 | === modified file 'tests/unit-tests/input/android/test_android_input_target_enumerator.cpp' |
1609 | --- tests/unit-tests/input/android/test_android_input_target_enumerator.cpp 2014-06-03 11:04:15 +0000 |
1610 | +++ tests/unit-tests/input/android/test_android_input_target_enumerator.cpp 2014-06-23 02:58:32 +0000 |
1611 | @@ -39,6 +39,7 @@ |
1612 | |
1613 | namespace mi = mir::input; |
1614 | namespace mia = mir::input::android; |
1615 | +namespace ms = mir::scene; |
1616 | namespace geom = mir::geometry; |
1617 | |
1618 | namespace mt = mir::test; |
1619 | @@ -60,6 +61,14 @@ |
1620 | callback(target); |
1621 | } |
1622 | |
1623 | + void add_observer(std::shared_ptr<ms::Observer> const& /* observer */) |
1624 | + { |
1625 | + } |
1626 | + |
1627 | + void remove_observer(std::weak_ptr<ms::Observer> const& /* observer */) |
1628 | + { |
1629 | + } |
1630 | + |
1631 | std::vector<std::shared_ptr<mi::Surface>> targets; |
1632 | }; |
1633 | |
1634 | |
1635 | === added file 'tests/unit-tests/input/test_cursor_controller.cpp' |
1636 | --- tests/unit-tests/input/test_cursor_controller.cpp 1970-01-01 00:00:00 +0000 |
1637 | +++ tests/unit-tests/input/test_cursor_controller.cpp 2014-06-23 02:58:32 +0000 |
1638 | @@ -0,0 +1,391 @@ |
1639 | +/* |
1640 | + * Copyright © 2014 Canonical Ltd. |
1641 | + * |
1642 | + * This program is free software: you can redistribute it and/or modify |
1643 | + * it under the terms of the GNU General Public License version 3 as |
1644 | + * published by the Free Software Foundation. |
1645 | + * |
1646 | + * This program is distributed in the hope that it will be useful, |
1647 | + * but WITHOUT ANY WARRANTY; without even the implied warranty of |
1648 | + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
1649 | + * GNU General Public License for more details. |
1650 | + * |
1651 | + * You should have received a copy of the GNU General Public License |
1652 | + * along with this program. If not, see <http://www.gnu.org/licenses/>. |
1653 | + * |
1654 | + * Authored by: Robert Carr <robert.carr@canonical.com> |
1655 | + */ |
1656 | + |
1657 | +#include "src/server/input/cursor_controller.h" |
1658 | + |
1659 | +#include "mir/input/surface.h" |
1660 | +#include "mir/input/input_targets.h" |
1661 | +#include "mir/scene/observer.h" |
1662 | +#include "mir/scene/surface_observer.h" |
1663 | +#include "mir/graphics/cursor_image.h" |
1664 | +#include "mir/graphics/cursor.h" |
1665 | + |
1666 | +#include "mir_toolkit/common.h" |
1667 | + |
1668 | +#include "mir_test/fake_shared.h" |
1669 | +#include "mir_test_doubles/stub_scene_surface.h" |
1670 | + |
1671 | +#include <gtest/gtest.h> |
1672 | +#include <gmock/gmock.h> |
1673 | + |
1674 | +#include <initializer_list> |
1675 | +#include <mutex> |
1676 | +#include <algorithm> |
1677 | + |
1678 | +#include <assert.h> |
1679 | + |
1680 | +namespace mi = mir::input; |
1681 | +namespace mg = mir::graphics; |
1682 | +namespace ms = mir::scene; |
1683 | +namespace geom = mir::geometry; |
1684 | +namespace mt = mir::test; |
1685 | +namespace mtd = mt::doubles; |
1686 | + |
1687 | +namespace |
1688 | +{ |
1689 | + |
1690 | +struct NamedCursorImage : public mg::CursorImage |
1691 | +{ |
1692 | + NamedCursorImage(std::string const& name) |
1693 | + : cursor_name(name) |
1694 | + { |
1695 | + } |
1696 | + |
1697 | + void const* as_argb_8888() const { return nullptr; } |
1698 | + geom::Size size() const { return geom::Size{}; } |
1699 | + |
1700 | + std::string const cursor_name; |
1701 | +}; |
1702 | + |
1703 | +bool cursor_is_named(mg::CursorImage const& i, std::string const& name) |
1704 | +{ |
1705 | + auto image = dynamic_cast<NamedCursorImage const*>(&i); |
1706 | + assert(image); |
1707 | + |
1708 | + return image->cursor_name == name; |
1709 | +} |
1710 | + |
1711 | +MATCHER(DefaultCursorImage, "") |
1712 | +{ |
1713 | + return cursor_is_named(arg, mir_default_cursor_name); |
1714 | +} |
1715 | + |
1716 | +MATCHER_P(CursorNamed, name, "") |
1717 | +{ |
1718 | + return cursor_is_named(arg, name); |
1719 | +} |
1720 | + |
1721 | +struct MockCursor : public mg::Cursor |
1722 | +{ |
1723 | + MOCK_METHOD1(show, void(mg::CursorImage const&)); |
1724 | + MOCK_METHOD0(hide, void()); |
1725 | + |
1726 | + MOCK_METHOD1(move_to, void(geom::Point)); |
1727 | +}; |
1728 | + |
1729 | +// TODO: This should only inherit from mi::Surface but to use the Scene observer we need an |
1730 | +// ms::Surface base class. |
1731 | +struct StubInputSurface : public mtd::StubSceneSurface |
1732 | +{ |
1733 | + StubInputSurface(geom::Rectangle const& input_bounds, std::shared_ptr<mg::CursorImage> const& cursor_image) |
1734 | + : mtd::StubSceneSurface(0), |
1735 | + bounds(input_bounds), |
1736 | + cursor_image_(cursor_image) |
1737 | + { |
1738 | + } |
1739 | + |
1740 | + std::string name() const override |
1741 | + { |
1742 | + return std::string(); |
1743 | + } |
1744 | + |
1745 | + geom::Rectangle input_bounds() const override |
1746 | + { |
1747 | + // We could return bounds here but lets make sure the cursor controller |
1748 | + // is only using input_area_contains. |
1749 | + return geom::Rectangle(); |
1750 | + } |
1751 | + |
1752 | + bool input_area_contains(geom::Point const& point) const override |
1753 | + { |
1754 | + return bounds.contains(point); |
1755 | + } |
1756 | + |
1757 | + std::shared_ptr<mi::InputChannel> input_channel() const override |
1758 | + { |
1759 | + return nullptr; |
1760 | + } |
1761 | + |
1762 | + mi::InputReceptionMode reception_mode() const override |
1763 | + { |
1764 | + return mi::InputReceptionMode::normal; |
1765 | + } |
1766 | + |
1767 | + std::shared_ptr<mg::CursorImage> cursor_image() const override |
1768 | + { |
1769 | + return cursor_image_; |
1770 | + } |
1771 | + |
1772 | + void set_cursor_image(std::shared_ptr<mg::CursorImage> const& image) override |
1773 | + { |
1774 | + cursor_image_ = image; |
1775 | + |
1776 | + { |
1777 | + std::unique_lock<decltype(observer_guard)> lk(observer_guard); |
1778 | + for (auto o : observers) |
1779 | + o->cursor_image_set_to(*image); |
1780 | + } |
1781 | + } |
1782 | + |
1783 | + void add_observer(std::shared_ptr<ms::SurfaceObserver> const& observer) override |
1784 | + { |
1785 | + std::unique_lock<decltype(observer_guard)> lk(observer_guard); |
1786 | + |
1787 | + observers.push_back(observer); |
1788 | + } |
1789 | + |
1790 | + void remove_observer(std::weak_ptr<ms::SurfaceObserver> const& observer) override |
1791 | + { |
1792 | + auto o = observer.lock(); |
1793 | + assert(o); |
1794 | + |
1795 | + auto it = std::find(observers.begin(), observers.end(), o); |
1796 | + observers.erase(it); |
1797 | + } |
1798 | + |
1799 | + geom::Rectangle const bounds; |
1800 | + std::shared_ptr<mg::CursorImage> cursor_image_; |
1801 | + |
1802 | + std::mutex observer_guard; |
1803 | + std::vector<std::shared_ptr<ms::SurfaceObserver>> observers; |
1804 | +}; |
1805 | + |
1806 | +struct StubInputTargets : public mi::InputTargets |
1807 | +{ |
1808 | + StubInputTargets(std::initializer_list<std::shared_ptr<ms::Surface>> const& targets) |
1809 | + : targets(targets.begin(), targets.end()) |
1810 | + { |
1811 | + } |
1812 | + |
1813 | + void for_each(std::function<void(std::shared_ptr<mi::Surface> const&)> const& callback) override |
1814 | + { |
1815 | + for (auto const& target : targets) |
1816 | + callback(target); |
1817 | + } |
1818 | + |
1819 | + void add_observer(std::shared_ptr<ms::Observer> const& observer) override |
1820 | + { |
1821 | + std::unique_lock<decltype(observer_guard)> lk(observer_guard); |
1822 | + |
1823 | + observers.push_back(observer); |
1824 | + |
1825 | + for (auto target : targets) |
1826 | + { |
1827 | + for (auto observer : observers) |
1828 | + { |
1829 | + observer->surface_exists(target.get()); |
1830 | + } |
1831 | + } |
1832 | + } |
1833 | + |
1834 | + void remove_observer(std::weak_ptr<ms::Observer> const& observer) override |
1835 | + { |
1836 | + std::unique_lock<decltype(observer_guard)> lk(observer_guard); |
1837 | + |
1838 | + auto o = observer.lock(); |
1839 | + assert(o); |
1840 | + |
1841 | + auto it = std::find(observers.begin(), observers.end(), o); |
1842 | + observers.erase(it); |
1843 | + } |
1844 | + |
1845 | + void add_surface(std::shared_ptr<StubInputSurface> const& surface) |
1846 | + { |
1847 | + targets.push_back(surface); |
1848 | + for (auto observer : observers) |
1849 | + { |
1850 | + observer->surface_added(surface.get()); |
1851 | + } |
1852 | + } |
1853 | + |
1854 | + // TODO: Should be mi::Surface. See comment on StubInputSurface. |
1855 | + std::vector<std::shared_ptr<ms::Surface>> targets; |
1856 | + |
1857 | + std::mutex observer_guard; |
1858 | + |
1859 | + std::vector<std::shared_ptr<ms::Observer>> observers; |
1860 | +}; |
1861 | + |
1862 | +struct TestCursorController : public testing::Test |
1863 | +{ |
1864 | + TestCursorController() |
1865 | + : default_cursor_image(std::make_shared<NamedCursorImage>(mir_default_cursor_name)) |
1866 | + { |
1867 | + } |
1868 | + geom::Rectangle const rect_0_0_1_1{{0, 0}, {1, 1}}; |
1869 | + geom::Rectangle const rect_1_1_1_1{{1, 1}, {1, 1}}; |
1870 | + std::string const cursor_name_1 = "test-cursor-1"; |
1871 | + std::string const cursor_name_2 = "test-cursor-2"; |
1872 | + |
1873 | + MockCursor cursor; |
1874 | + std::shared_ptr<mg::CursorImage> const default_cursor_image; |
1875 | +}; |
1876 | + |
1877 | +} |
1878 | + |
1879 | +TEST_F(TestCursorController, moves_cursor) |
1880 | +{ |
1881 | + using namespace ::testing; |
1882 | + |
1883 | + StubInputTargets targets({}); |
1884 | + |
1885 | + mi::CursorController controller(mt::fake_shared(targets), |
1886 | + mt::fake_shared(cursor), default_cursor_image); |
1887 | + |
1888 | + InSequence seq; |
1889 | + EXPECT_CALL(cursor, move_to(geom::Point{geom::X{1.0f}, geom::Y{1.0f}})); |
1890 | + EXPECT_CALL(cursor, move_to(geom::Point{geom::X{0.0f}, geom::Y{0.0f}})); |
1891 | + |
1892 | + controller.cursor_moved_to(1.0f, 1.0f); |
1893 | + controller.cursor_moved_to(0.0f, 0.0f); |
1894 | +} |
1895 | + |
1896 | +TEST_F(TestCursorController, updates_cursor_image_when_entering_surface) |
1897 | +{ |
1898 | + using namespace ::testing; |
1899 | + |
1900 | + StubInputSurface surface{rect_1_1_1_1, |
1901 | + std::make_shared<NamedCursorImage>(cursor_name_1)}; |
1902 | + StubInputTargets targets({mt::fake_shared(surface)}); |
1903 | + |
1904 | + mi::CursorController controller(mt::fake_shared(targets), |
1905 | + mt::fake_shared(cursor), default_cursor_image); |
1906 | + |
1907 | + EXPECT_CALL(cursor, move_to(_)).Times(AnyNumber()); |
1908 | + EXPECT_CALL(cursor, show(CursorNamed(cursor_name_1))).Times(1); |
1909 | + |
1910 | + controller.cursor_moved_to(1.0f, 1.0f); |
1911 | +} |
1912 | + |
1913 | +TEST_F(TestCursorController, surface_with_no_cursor_image_hides_cursor) |
1914 | +{ |
1915 | + using namespace ::testing; |
1916 | + |
1917 | + StubInputSurface surface{rect_1_1_1_1, |
1918 | + nullptr}; |
1919 | + StubInputTargets targets({mt::fake_shared(surface)}); |
1920 | + |
1921 | + mi::CursorController controller(mt::fake_shared(targets), |
1922 | + mt::fake_shared(cursor), default_cursor_image); |
1923 | + |
1924 | + EXPECT_CALL(cursor, move_to(_)).Times(AnyNumber()); |
1925 | + EXPECT_CALL(cursor, hide()).Times(1); |
1926 | + |
1927 | + controller.cursor_moved_to(1.0f, 1.0f); |
1928 | +} |
1929 | + |
1930 | +TEST_F(TestCursorController, takes_cursor_image_from_topmost_surface) |
1931 | +{ |
1932 | + using namespace ::testing; |
1933 | + |
1934 | + StubInputSurface surface_1{rect_1_1_1_1, std::make_shared<NamedCursorImage>(cursor_name_1)}; |
1935 | + StubInputSurface surface_2{rect_1_1_1_1, std::make_shared<NamedCursorImage>(cursor_name_2)}; |
1936 | + StubInputTargets targets({mt::fake_shared(surface_1), mt::fake_shared(surface_2)}); |
1937 | + |
1938 | + mi::CursorController controller(mt::fake_shared(targets), |
1939 | + mt::fake_shared(cursor), default_cursor_image); |
1940 | + |
1941 | + EXPECT_CALL(cursor, move_to(_)).Times(AnyNumber()); |
1942 | + EXPECT_CALL(cursor, show(CursorNamed(cursor_name_2))).Times(1); |
1943 | + |
1944 | + controller.cursor_moved_to(1.0f, 1.0f); |
1945 | +} |
1946 | + |
1947 | +TEST_F(TestCursorController, restores_cursor_when_leaving_surface) |
1948 | +{ |
1949 | + using namespace ::testing; |
1950 | + |
1951 | + StubInputSurface surface{rect_1_1_1_1, |
1952 | + std::make_shared<NamedCursorImage>(cursor_name_1)}; |
1953 | + StubInputTargets targets({mt::fake_shared(surface)}); |
1954 | + |
1955 | + mi::CursorController controller(mt::fake_shared(targets), |
1956 | + mt::fake_shared(cursor), default_cursor_image); |
1957 | + |
1958 | + EXPECT_CALL(cursor, move_to(_)).Times(AnyNumber()); |
1959 | + |
1960 | + { |
1961 | + InSequence seq; |
1962 | + EXPECT_CALL(cursor, show(CursorNamed(cursor_name_1))).Times(1); |
1963 | + EXPECT_CALL(cursor, show(DefaultCursorImage())).Times(1); |
1964 | + } |
1965 | + |
1966 | + controller.cursor_moved_to(1.0f, 1.0f); |
1967 | + controller.cursor_moved_to(2.0f, 2.0f); |
1968 | +} |
1969 | + |
1970 | +TEST_F(TestCursorController, change_in_cursor_request_triggers_image_update_without_cursor_motion) |
1971 | +{ |
1972 | + using namespace ::testing; |
1973 | + |
1974 | + StubInputSurface surface{rect_1_1_1_1, |
1975 | + std::make_shared<NamedCursorImage>(cursor_name_1)}; |
1976 | + StubInputTargets targets({mt::fake_shared(surface)}); |
1977 | + |
1978 | + mi::CursorController controller(mt::fake_shared(targets), |
1979 | + mt::fake_shared(cursor), default_cursor_image); |
1980 | + |
1981 | + EXPECT_CALL(cursor, move_to(_)).Times(AnyNumber()); |
1982 | + { |
1983 | + InSequence seq; |
1984 | + EXPECT_CALL(cursor, show(CursorNamed(cursor_name_1))).Times(1); |
1985 | + EXPECT_CALL(cursor, show(CursorNamed(cursor_name_2))).Times(1); |
1986 | + } |
1987 | + |
1988 | + controller.cursor_moved_to(1.0f, 1.0f); |
1989 | + surface.set_cursor_image(std::make_shared<NamedCursorImage>(cursor_name_2)); |
1990 | +} |
1991 | + |
1992 | +TEST_F(TestCursorController, change_in_scene_triggers_image_update) |
1993 | +{ |
1994 | + using namespace ::testing; |
1995 | + |
1996 | + // Here we also demonstrate that the cursor begins at 0,0. |
1997 | + StubInputSurface surface{rect_0_0_1_1, |
1998 | + std::make_shared<NamedCursorImage>(cursor_name_1)}; |
1999 | + StubInputTargets targets({}); |
2000 | + |
2001 | + mi::CursorController controller(mt::fake_shared(targets), |
2002 | + mt::fake_shared(cursor), default_cursor_image); |
2003 | + |
2004 | + EXPECT_CALL(cursor, move_to(_)).Times(AnyNumber()); |
2005 | + EXPECT_CALL(cursor, show(CursorNamed(cursor_name_1))).Times(1); |
2006 | + |
2007 | + targets.add_surface(mt::fake_shared(surface)); |
2008 | +} |
2009 | + |
2010 | +TEST_F(TestCursorController, cursor_image_not_reset_needlessly) |
2011 | +{ |
2012 | + using namespace ::testing; |
2013 | + |
2014 | + auto image = std::make_shared<NamedCursorImage>(cursor_name_1); |
2015 | + |
2016 | + // Here we also demonstrate that the cursor begins at 0,0. |
2017 | + StubInputSurface surface1{rect_0_0_1_1, image}; |
2018 | + StubInputSurface surface2{rect_0_0_1_1, image}; |
2019 | + StubInputTargets targets({}); |
2020 | + |
2021 | + mi::CursorController controller(mt::fake_shared(targets), |
2022 | + mt::fake_shared(cursor), default_cursor_image); |
2023 | + |
2024 | + EXPECT_CALL(cursor, move_to(_)).Times(AnyNumber()); |
2025 | + EXPECT_CALL(cursor, show(CursorNamed(cursor_name_1))).Times(1); |
2026 | + |
2027 | + targets.add_surface(mt::fake_shared(surface1)); |
2028 | + targets.add_surface(mt::fake_shared(surface2)); |
2029 | +} |
PASSED: Continuous integration, rev:1535 jenkins. qa.ubuntu. com/job/ mir-team- mir-development -branch- ci/1671/ jenkins. qa.ubuntu. com/job/ mir-android- utopic- i386-build/ 245 jenkins. qa.ubuntu. com/job/ mir-clang- utopic- amd64-build/ 246 jenkins. qa.ubuntu. com/job/ mir-mediumtests -utopic- touch/245 jenkins. qa.ubuntu. com/job/ mir-team- mir-development -branch- utopic- amd64-ci/ 190 jenkins. qa.ubuntu. com/job/ mir-team- mir-development -branch- utopic- amd64-ci/ 190/artifact/ work/output/ *zip*/output. zip jenkins. qa.ubuntu. com/job/ mir-team- mir-development -branch- utopic- armhf-ci/ 189 jenkins. qa.ubuntu. com/job/ mir-team- mir-development -branch- utopic- armhf-ci/ 189/artifact/ work/output/ *zip*/output. zip jenkins. qa.ubuntu. com/job/ generic- mediumtests- builder- utopic- armhf/639 jenkins. qa.ubuntu. com/job/ generic- mediumtests- builder- utopic- armhf/639/ artifact/ work/output/ *zip*/output. zip jenkins. qa.ubuntu. com/job/ mir-mediumtests -runner- mako/1496 s-jenkins. ubuntu- ci:8080/ job/touch- flash-device/ 7349
http://
Executed test runs:
SUCCESS: http://
SUCCESS: http://
SUCCESS: http://
SUCCESS: http://
deb: http://
SUCCESS: http://
deb: http://
SUCCESS: http://
deb: http://
SUCCESS: http://
SUCCESS: http://
Click here to trigger a rebuild: s-jenkins. ubuntu- ci:8080/ job/mir- team-mir- development- branch- ci/1671/ rebuild
http://