Mir

Merge lp:~alan-griffiths/mir/scene-can-identify-surface-under-cursor into lp:mir

Proposed by Alan Griffiths on 2015-02-13
Status: Merged
Approved by: Alan Griffiths on 2015-02-20
Approved revision: 2337
Merged at revision: 2331
Proposed branch: lp:~alan-griffiths/mir/scene-can-identify-surface-under-cursor
Merge into: lp:mir
Diff against target: 340 lines (+112/-32)
11 files modified
include/server/mir/scene/surface_coordinator.h (+4/-0)
src/server/scene/surface_controller.cpp (+6/-0)
src/server/scene/surface_controller.h (+2/-0)
src/server/scene/surface_stack.cpp (+33/-0)
src/server/scene/surface_stack.h (+2/-0)
src/server/scene/surface_stack_model.h (+5/-1)
src/server/symbols.map (+7/-31)
tests/include/mir_test_doubles/mock_surface_coordinator.h (+1/-0)
tests/unit-tests/scene/test_application_session.cpp (+4/-0)
tests/unit-tests/scene/test_surface_controller.cpp (+1/-0)
tests/unit-tests/scene/test_surface_stack.cpp (+47/-0)
To merge this branch: bzr merge lp:~alan-griffiths/mir/scene-can-identify-surface-under-cursor
Reviewer Review Type Date Requested Status
Daniel van Vugt 2015-02-13 Needs Fixing on 2015-02-20
Robert Carr (community) Approve on 2015-02-19
Alberto Aguirre Approve on 2015-02-19
PS Jenkins bot continuous-integration Approve on 2015-02-19
Andreas Pokorny (community) Abstain on 2015-02-19
Alexandros Frantzis (community) Approve on 2015-02-17
Alan Griffiths Abstain on 2015-02-17
Chris Halse Rogers Approve on 2015-02-17
Review via email: mp+249696@code.launchpad.net

Commit Message

scene: Introduce mechanism to find the surface under a cursor

Description of the Change

scene: Introduce mechanism to find the surface under a cursor

There'll be an acceptance test along shortly to expose this functionality for window management, but this bit splits off cleanly.

To post a comment you must log in.
Daniel van Vugt (vanvugt) wrote :

Sounds redundant. We already have such logic to send input events to the right window. Can we just reuse that?

review: Needs Information
Daniel van Vugt (vanvugt) wrote :

Oh you are using that logic already :)

70 + if (surface->input_bounds().contains(cursor))
71 + result = surface;

but that's only a rectangle.

I think the right answer is to test the free-form input region, which we have a setter for but no getter yet:

    virtual void set_input_region(std::vector<geometry::Rectangle> const& region) = 0;

It's not a big deal, but if we wanted to support custom shaped surfaces properly then we would test the region and not just the bounding rectangle.

review: Needs Information
Daniel van Vugt (vanvugt) wrote :

Aha. The right function already exists.

Instead of:
    surface->input_bounds().contains(cursor)
we want something like:
    surface->input_area_contains(cursor)
which will test the region proper and also checks the visible() flag properly.

Furthermore, we don't need to pay attention to this flag:
  67 + if (surface->query(mir_surface_attrib_visibility) ==
  68 + MirSurfaceVisibility::mir_surface_visibility_exposed)
Instead just make sure you're walking the stack from top-to-bottom (reverse?), and break out of the loop when you find the first hit.

review: Needs Fixing
Chris Halse Rogers (raof) wrote :

Looks sensible to me.

review: Approve
Daniel van Vugt (vanvugt) wrote :

Looks good. Just missing server ABI bump to 30 due to the ABI break.

review: Needs Fixing
Alan Griffiths (alan-griffiths) wrote :

Actually, as Daniel indirectly pointed out on another thread, this shouldn't be scanning for the input area under the cursor but for the whole (visible) surface area.

review: Needs Fixing
Alan Griffiths (alan-griffiths) wrote :

> Looks good. Just missing server ABI bump to 30 due to the ABI break.

Done.

review: Abstain
2327. By Alan Griffiths on 2015-02-17

Update debian/*

Alexandros Frantzis (afrantzis) wrote :

>361 + stub_surface1->configure(mir_surface_attrib_visibility, MirSurfaceVisibility::mir_surface_visibility_exposed);
... etc

Not needed.

review: Needs Fixing
2328. By Alan Griffiths on 2015-02-17

Update debian/*

2329. By Alan Griffiths on 2015-02-17

Remove unnecessary setup

Alan Griffiths (alan-griffiths) wrote :

> >361 + stub_surface1->configure(mir_surface_attrib_visibility,
> MirSurfaceVisibility::mir_surface_visibility_exposed);
> ... etc
>
> Not needed.

Fixed, but the diff refuses to show it

2330. By Alan Griffiths on 2015-02-17

Empty checkin to fool lp

Alexandros Frantzis (afrantzis) wrote :

> Fixed, but the diff refuses to show it

Oh, no, we have broken LP! :)

review: Approve
Robert Carr (robertcarr) wrote :

lgtm

review: Approve
Daniel van Vugt (vanvugt) wrote :

This is now incorrect:

133 + if (surface->visible() &&
134 + geom::Rectangle{surface->top_left(), surface->size()}.contains(cursor))
135 + return surface;

A shaped surface occupies less space than its rectangle (e.g. try mir_demo_client_egltriangle -b0). So your new algorithm does not support shaped surfaces that express a reduced input region. Using input_area_contains() would solve this.

Then again, I think we lack any client API to express an input shape at all yet...

review: Needs Fixing
Alan Griffiths (alan-griffiths) wrote :

> This is now incorrect:
>
> 133 + if (surface->visible() &&
> 134 + geom::Rectangle{surface->top_left(),
> surface->size()}.contains(cursor))
> 135 + return surface;
>
> A shaped surface occupies less space than its rectangle (e.g. try
> mir_demo_client_egltriangle -b0). So your new algorithm does not support
> shaped surfaces that express a reduced input region. Using
> input_area_contains() would solve this.

What I want is the surface visible under the cursor position, not the input area. Using is wrong as you described yourself at lp:~alan-griffiths/mir/canonical-window-manager/+merge/249849/comments/619311

"BTW, if you're resizing then the input area the client has designated is probably irrelevant. Instead you want to consider the whole surface rectangle..."

> Then again, I think we lack any client API to express an input shape at all
> yet...

The region I want here includes, for example, decorations that are not part of the input area - maybe we can refine the implementation in future, but the proposed algorithm is more correct than using nput_area_contains().

2331. By Alan Griffiths on 2015-02-18

TODO comment explaining implementation choice

2332. By Alan Griffiths on 2015-02-18

merge lp:mir

Robert Carr (robertcarr) wrote :

I think using input_area_contains is probably correct.

The origin of the input area was to allow unity8 (pre qt-compositor) to punch an input hole in the always on top shell surface. If client API were added I imagine it would also be used for things like a circular clock app e.g. the X shape extension...

Essentially it seems like a bit of a misnomer to say a surface is under the cursor when cursor events will pass through the surface.

review: Needs Information
Alberto Aguirre (albaguirre) wrote :

"A shaped surface occupies less space than its rectangle (e.g. try mir_demo_client_egltriangle -b0). So your new algorithm does not support shaped surfaces that express a reduced input region. Using input_area_contains() would solve this."

After a brief scan of the code, I don't see how input_area_contains tracks client opacity. It would be just as broken if the proposed implementation "used input_area_contains".

@Robert,
A shell surface like that sounds like it should have it's own type or state.

@Alan,
Perhaps we can have:

    std::shared_ptr<Surface> ms::SurfaceStack::surface_find(std::function<bool(ms::Surface*)> predicate)

And then the demo window manager can have the simple implementation proposed here.

review: Needs Information
Robert Carr (robertcarr) wrote :

>> A shell surface like that sounds like it should have it's own type or state.

Sure I was just outlining the history of the code. I think the upcoming use case of say a circular clock app is valid though.

Robert Carr (robertcarr) wrote :

The suggestion not being that input_area_contains will track opacity but rather that the client will have the option of updating its input area via specifying a set of rectangles.

Perhaps this should be it's input/output area. I know at least OSX (Circa 2009 lol) and X11 support setting input shape exclusively.

Daniel van Vugt (vanvugt) wrote :

Alberto says:
"After a brief scan of the code, I don't see how input_area_contains tracks client opacity. It would be just as broken if the proposed implementation "used input_area_contains".

That's right. It's a feature and not a bug. Monitoring pixel opacity could be prohibitively expensive if done regularly. It's just the client's responsibility to designate an input area that matches its visibly opaque area.

As Robert points out, this is based on X11's input area. The goal is noble, but the implementation can be expensive, because in both X11 and Mir the input area is designated by a set of rectangles. But we don't need to be tied to the set-of-rectangles implementation everyone uses right now. The beauty of input_area_contains() is that it's nice and opaque. We can modify the implementation in future.

Long term, I suggest a nice solution would be to resample pixel opacity once per surface resize and use that as a mask for the default input region (which then can be any shape). You can also optimize for the common case of rectangular clients so only surfaces that are actually shaped need to have a mask stored for them.

At least input_area_contains() has a chance of supporting shaped surfaces (like a circular clock). Just testing a single rectangle we've got no chance.

That said, testing a single rectangle might make sense for a purely tiling shell, for ease of use. By definition of the tiling shell there are no "gaps". But a traditional floating window shell should honour surface shapes more accurately.

review: Needs Fixing
Andreas Pokorny (andreas-pokorny) wrote :

oh a classic you cannot to this but you also cannot do the opposite.

Hm most of what was proposed and discussed here might fit.. based on the ambivalent nature of mir::scene::Surface and its processing components.

What I do not like about the MP is that we are running into scene and surface locks while dispatching user input. But I will not block based on that. We can still drive that onto a different implementation path, while keeping the interface.

review: Abstain
2333. By Alan Griffiths on 2015-02-19

Revert debian/* changes

2334. By Alan Griffiths on 2015-02-19

Fix symbol map without updating to MIR_SERVER_30

2335. By Alan Griffiths on 2015-02-19

Back to surface->input_area_contains(cursor) with a note of misgivings

2336. By Alan Griffiths on 2015-02-19

merge lp:mir

Alan Griffiths (alan-griffiths) wrote :

I'm not yet convinced that input_area_contains() can support all the usecases: Some (like the motivating case of the shell determining which window is clicked on) require it includes decorations and I suspect others will exclude decorations.

But input_area_contains() is good enough for now. And decorations are not yet supported.

I've also reverted the ABI bump as that's causing CI problems and can be landed separately.

2337. By Alan Griffiths on 2015-02-19

Forgotten reverts

Alan Griffiths (alan-griffiths) wrote :

> What I do not like about the MP is that we are running into scene and surface
> locks while dispatching user input. But I will not block based on that. We can
> still drive that onto a different implementation path, while keeping the
> interface.

The scene needs some re-architecting to reduce lock contention. Having C++14 sharable mutexes will help I think (as only the shell should be updating the scene).

Alan Griffiths (alan-griffiths) wrote :

> Perhaps we can have:
>
> std::shared_ptr<Surface>
> ms::SurfaceStack::surface_find(std::function<bool(ms::Surface*)> predicate)
>
> And then the demo window manager can have the simple implementation proposed
> here.

I don't think it makes sense for the window manager to decide which surface is under a click. At least not until we understand how decorations are represented in the scene.

Alberto Aguirre (albaguirre) wrote :

OK.

LGTM then. We can improve this as we enhance the scene.

review: Approve
Andreas Pokorny (andreas-pokorny) wrote :

> > What I do not like about the MP is that we are running into scene and
> surface
> > locks while dispatching user input. But I will not block based on that. We
> can
> > still drive that onto a different implementation path, while keeping the
> > interface.
>
> The scene needs some re-architecting to reduce lock contention. Having C++14
> sharable mutexes will help I think (as only the shell should be updating the
> scene).

I think our scenes are rather small we can avoid contention-free acquiring of a lot of locks. So I thought of transforming the grand central scene to an input scene when changes happen. the reduced input scene would be for input dispatch & input related window management only.

Alan Griffiths (alan-griffiths) wrote :

> I think our scenes are rather small we can avoid contention-free acquiring of
> a lot of locks. So I thought of transforming the grand central scene to an
> input scene when changes happen. the reduced input scene would be for input
> dispatch & input related window management only.

We digress, but input can also update an input scene model what it needs on the basis of observers instead of transforming the grand central scene whenever changes happen.

Robert Carr (robertcarr) wrote :

>> I think our scenes are rather small we can avoid contention-free acquiring of a lot of locks. So I >> thought of transforming the grand central scene to an input scene when changes happen. the reduced >> input scene would be for input dispatch & input related window management only.

Android actually uses an approach like this...we used to use..InputDispatcher::setInputTargets(vector<WindowHandle>)

Robert Carr (robertcarr) wrote :

Anyway LGTM

review: Approve
Daniel van Vugt (vanvugt) wrote :

I think we're almost there. Not too concerned about input on decorations as I've recently come to the realization that might be more the shell's job to detect (Unity8 does already). In fact, I already started designing for that and stopped (see TODOs in basic_surface.cpp).

Probable needs fixing: Functions removed from symbols.map without an ABI bump :)

review: Needs Fixing
Alan Griffiths (alan-griffiths) wrote :

> Probable needs fixing: Functions removed from symbols.map without an ABI bump
> :)

I think not:

1. these functions couldn't be used as they don't appear in pubic headers; and,
2. most (if not all) were not actually exported because the symbols were not generated.

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1=== modified file 'include/server/mir/scene/surface_coordinator.h'
2--- include/server/mir/scene/surface_coordinator.h 2015-01-21 07:34:50 +0000
3+++ include/server/mir/scene/surface_coordinator.h 2015-02-19 13:21:41 +0000
4@@ -24,6 +24,7 @@
5
6 namespace mir
7 {
8+namespace geometry { class Point; }
9 namespace scene
10 {
11 class Surface;
12@@ -41,6 +42,9 @@
13 virtual void raise(std::weak_ptr<Surface> const& surface) = 0;
14
15 virtual void remove_surface(std::weak_ptr<Surface> const& surface) = 0;
16+
17+ virtual auto surface_at(geometry::Point) const -> std::shared_ptr<Surface> = 0;
18+
19 protected:
20 SurfaceCoordinator() = default;
21 virtual ~SurfaceCoordinator() = default;
22
23=== modified file 'src/server/scene/surface_controller.cpp'
24--- src/server/scene/surface_controller.cpp 2015-02-13 06:12:34 +0000
25+++ src/server/scene/surface_controller.cpp 2015-02-19 13:21:41 +0000
26@@ -49,3 +49,9 @@
27 {
28 surface_stack->raise(surface);
29 }
30+
31+auto ms::SurfaceController::surface_at(geometry::Point cursor) const
32+-> std::shared_ptr<Surface>
33+{
34+ return surface_stack->surface_at(cursor);
35+}
36
37=== modified file 'src/server/scene/surface_controller.h'
38--- src/server/scene/surface_controller.h 2015-02-13 06:12:34 +0000
39+++ src/server/scene/surface_controller.h 2015-02-19 13:21:41 +0000
40@@ -46,6 +46,8 @@
41
42 void raise(std::weak_ptr<Surface> const& surface) override;
43
44+ auto surface_at(geometry::Point) const -> std::shared_ptr<Surface> override;
45+
46 private:
47 std::shared_ptr<SurfaceFactory> const surface_factory;
48 std::shared_ptr<SurfaceStackModel> const surface_stack;
49
50=== modified file 'src/server/scene/surface_stack.cpp'
51--- src/server/scene/surface_stack.cpp 2015-02-13 06:12:34 +0000
52+++ src/server/scene/surface_stack.cpp 2015-02-19 13:21:41 +0000
53@@ -277,6 +277,39 @@
54 // TODO: error logging when surface not found
55 }
56
57+namespace
58+{
59+template <typename Container>
60+struct InReverse {
61+ Container& container;
62+ auto begin() -> decltype(container.rbegin()) { return container.rbegin(); }
63+ auto end() -> decltype(container.rend()) { return container.rend(); }
64+};
65+
66+template <typename Container>
67+InReverse<Container> in_reverse(Container& container) { return InReverse<Container>{container}; }
68+}
69+
70+auto ms::SurfaceStack::surface_at(geometry::Point cursor) const
71+-> std::shared_ptr<Surface>
72+{
73+ std::lock_guard<decltype(guard)> lg(guard);
74+ for (auto &layer : in_reverse(layers_by_depth))
75+ {
76+ for (auto const& surface : in_reverse(layer.second))
77+ {
78+ // TODO There's a lack of clarity about how the input area will
79+ // TODO be maintained and whether this test will detect clicks on
80+ // TODO decorations (it should) as these may be outside the area
81+ // TODO known to the client. But it works for now.
82+ if (surface->input_area_contains(cursor))
83+ return surface;
84+ }
85+ }
86+
87+ return {};
88+}
89+
90 void ms::SurfaceStack::for_each(std::function<void(std::shared_ptr<mi::Surface> const&)> const& callback)
91 {
92 std::lock_guard<decltype(guard)> lg(guard);
93
94=== modified file 'src/server/scene/surface_stack.h'
95--- src/server/scene/surface_stack.h 2015-01-21 09:51:37 +0000
96+++ src/server/scene/surface_stack.h 2015-02-19 13:21:41 +0000
97@@ -89,6 +89,8 @@
98 DepthId depth,
99 input::InputReceptionMode input_mode) override;
100
101+ auto surface_at(geometry::Point) const -> std::shared_ptr<Surface> override;
102+
103 void add_observer(std::shared_ptr<Observer> const& observer) override;
104 void remove_observer(std::weak_ptr<Observer> const& observer) override;
105
106
107=== modified file 'src/server/scene/surface_stack_model.h'
108--- src/server/scene/surface_stack_model.h 2015-01-21 07:34:50 +0000
109+++ src/server/scene/surface_stack_model.h 2015-02-19 13:21:41 +0000
110@@ -27,6 +27,8 @@
111
112 namespace mir
113 {
114+namespace geometry { class Point; }
115+
116 namespace scene
117 {
118 class Surface;
119@@ -34,7 +36,7 @@
120 class SurfaceStackModel
121 {
122 public:
123- virtual ~SurfaceStackModel() {}
124+ virtual ~SurfaceStackModel() = default;
125
126 virtual void add_surface(
127 std::shared_ptr<Surface> const& surface,
128@@ -45,6 +47,8 @@
129
130 virtual void raise(std::weak_ptr<Surface> const& surface) = 0;
131
132+ virtual auto surface_at(geometry::Point) const -> std::shared_ptr<Surface> = 0;
133+
134 protected:
135 SurfaceStackModel() = default;
136 SurfaceStackModel(const SurfaceStackModel&) = delete;
137
138=== modified file 'src/server/symbols.map'
139--- src/server/symbols.map 2015-02-13 06:12:34 +0000
140+++ src/server/symbols.map 2015-02-19 13:21:41 +0000
141@@ -213,6 +213,7 @@
142 mir::scene::SurfaceBufferAccess::?SurfaceBufferAccess*;
143 mir::scene::SurfaceBufferAccess::SurfaceBufferAccess*;
144 mir::scene::SurfaceBufferAccess::with_most_recent_buffer_do*;
145+ mir::scene::Surface::buffers_ready_for_compositor*;
146 mir::scene::Surface::client_size*;
147 mir::scene::Surface::compositor_snapshot*;
148 mir::scene::SurfaceConfigurator::attribute_set*;
149@@ -377,9 +378,6 @@
150 mir::shell::FocusController::focussed_application*;
151 mir::shell::FocusController::operator*;
152 mir::shell::FocusController::set_focus_to*;
153- mir::shell::FocusSetter::FocusSetter*;
154- mir::shell::FocusSetter::operator*;
155- mir::shell::FocusSetter::set_focus_to*;
156 mir::shell::HostLifecycleEventListener::?HostLifecycleEventListener*;
157 mir::shell::HostLifecycleEventListener::HostLifecycleEventListener*;
158 mir::shell::HostLifecycleEventListener::lifecycle_event_occurred*;
159@@ -389,16 +387,6 @@
160 mir::shell::InputTargeter::?InputTargeter*;
161 mir::shell::InputTargeter::InputTargeter*;
162 mir::shell::InputTargeter::operator*;
163- mir::shell::SessionCoordinatorWrapper::add_prompt_provider_for*;
164- mir::shell::SessionCoordinatorWrapper::close_session*;
165- mir::shell::SessionCoordinatorWrapper::focus_next*;
166- mir::shell::SessionCoordinatorWrapper::focussed_application*;
167- mir::shell::SessionCoordinatorWrapper::handle_surface_created*;
168- mir::shell::SessionCoordinatorWrapper::open_session*;
169- mir::shell::SessionCoordinatorWrapper::SessionCoordinatorWrapper*;
170- mir::shell::SessionCoordinatorWrapper::set_focus_to*;
171- mir::shell::SessionCoordinatorWrapper::start_prompt_session_for*;
172- mir::shell::SessionCoordinatorWrapper::stop_prompt_session*;
173 mir::shell::Shell::add_prompt_provider_for*;
174 mir::shell::Shell::close_session*;
175 mir::shell::Shell::create_surface*;
176@@ -423,10 +411,6 @@
177 mir::shell::ShellWrapper::ShellWrapper*;
178 mir::shell::ShellWrapper::start_prompt_session_for*;
179 mir::shell::ShellWrapper::stop_prompt_session*;
180- mir::shell::SurfaceCoordinatorWrapper::add_surface*;
181- mir::shell::SurfaceCoordinatorWrapper::raise*;
182- mir::shell::SurfaceCoordinatorWrapper::remove_surface*;
183- mir::shell::SurfaceCoordinatorWrapper::SurfaceCoordinatorWrapper*;
184 mir::terminate_with_current_exception*;
185 mir::time::Alarm::?Alarm*;
186 mir::time::Alarm::Alarm*;
187@@ -497,11 +481,6 @@
188 non-virtual?thunk?to?mir::shell::FocusController::?FocusController*;
189 non-virtual?thunk?to?mir::shell::HostLifecycleEventListener::?HostLifecycleEventListener*;
190 non-virtual?thunk?to?mir::shell::InputTargeter::?InputTargeter*;
191- non-virtual?thunk?to?mir::shell::SessionCoordinatorWrapper::close_session*;
192- non-virtual?thunk?to?mir::shell::SessionCoordinatorWrapper::focus_next*;
193- non-virtual?thunk?to?mir::shell::SessionCoordinatorWrapper::focussed_application*;
194- non-virtual?thunk?to?mir::shell::SessionCoordinatorWrapper::open_session*;
195- non-virtual?thunk?to?mir::shell::SessionCoordinatorWrapper::set_focus_to*;
196 non-virtual?thunk?to?mir::shell::ShellWrapper::add_prompt_provider_for*;
197 non-virtual?thunk?to?mir::shell::ShellWrapper::close_session*;
198 non-virtual?thunk?to?mir::shell::ShellWrapper::create_surface*;
199@@ -515,9 +494,6 @@
200 non-virtual?thunk?to?mir::shell::ShellWrapper::set_surface_attribute*;
201 non-virtual?thunk?to?mir::shell::ShellWrapper::start_prompt_session_for*;
202 non-virtual?thunk?to?mir::shell::ShellWrapper::stop_prompt_session*;
203- non-virtual?thunk?to?mir::shell::SurfaceCoordinatorWrapper::add_surface*;
204- non-virtual?thunk?to?mir::shell::SurfaceCoordinatorWrapper::raise*;
205- non-virtual?thunk?to?mir::shell::SurfaceCoordinatorWrapper::remove_surface*;
206 non-virtual?thunk?to?mir::time::Alarm::?Alarm*;
207 non-virtual?thunk?to?mir::time::Timer::?Timer*;
208 typeinfo?for?mir::compositor::Compositor;
209@@ -566,13 +542,10 @@
210 typeinfo?for?mir::shell::AbstractShell;
211 typeinfo?for?mir::shell::DisplayLayout;
212 typeinfo?for?mir::shell::FocusController;
213- typeinfo?for?mir::shell::FocusSetter;
214 typeinfo?for?mir::shell::HostLifecycleEventListener;
215 typeinfo?for?mir::shell::InputTargeter;
216- typeinfo?for?mir::shell::SessionCoordinatorWrapper;
217 typeinfo?for?mir::shell::Shell;
218 typeinfo?for?mir::shell::ShellWrapper;
219- typeinfo?for?mir::shell::SurfaceCoordinatorWrapper;
220 typeinfo?for?mir::time::Alarm;
221 typeinfo?for?mir::time::Timer;
222 vtable?for?mir::compositor::Compositor;
223@@ -621,13 +594,10 @@
224 vtable?for?mir::shell::AbstractShell;
225 vtable?for?mir::shell::DisplayLayout;
226 vtable?for?mir::shell::FocusController;
227- vtable?for?mir::shell::FocusSetter;
228 vtable?for?mir::shell::HostLifecycleEventListener;
229 vtable?for?mir::shell::InputTargeter;
230- vtable?for?mir::shell::SessionCoordinatorWrapper;
231 vtable?for?mir::shell::Shell;
232 vtable?for?mir::shell::ShellWrapper;
233- vtable?for?mir::shell::SurfaceCoordinatorWrapper;
234 vtable?for?mir::time::Alarm;
235 vtable?for?mir::time::Timer;
236
237@@ -765,3 +735,9 @@
238 };
239 local: *;
240 };
241+
242+MIR_SERVER_29.1 {
243+ global:
244+ mir::scene::SurfaceCoordinator::surface_at*;
245+ local: *;
246+} MIR_SERVER_29;
247
248=== modified file 'tests/include/mir_test_doubles/mock_surface_coordinator.h'
249--- tests/include/mir_test_doubles/mock_surface_coordinator.h 2015-02-13 06:12:34 +0000
250+++ tests/include/mir_test_doubles/mock_surface_coordinator.h 2015-02-19 13:21:41 +0000
251@@ -41,6 +41,7 @@
252 scene::Session* session));
253
254 MOCK_METHOD1(remove_surface, void(std::weak_ptr<scene::Surface> const& surface));
255+ MOCK_CONST_METHOD1(surface_at, std::shared_ptr<scene::Surface>(geometry::Point));
256 };
257
258 }
259
260=== modified file 'tests/unit-tests/scene/test_application_session.cpp'
261--- tests/unit-tests/scene/test_application_session.cpp 2015-02-13 06:12:34 +0000
262+++ tests/unit-tests/scene/test_application_session.cpp 2015-02-19 13:21:41 +0000
263@@ -97,6 +97,10 @@
264 void remove_surface(std::weak_ptr<ms::Surface> const&) override
265 {
266 }
267+ auto surface_at(mir::geometry::Point) const -> std::shared_ptr<ms::Surface>
268+ {
269+ return std::shared_ptr<ms::Surface>{};
270+ }
271 };
272
273 struct ApplicationSession : public testing::Test
274
275=== modified file 'tests/unit-tests/scene/test_surface_controller.cpp'
276--- tests/unit-tests/scene/test_surface_controller.cpp 2015-02-13 06:12:34 +0000
277+++ tests/unit-tests/scene/test_surface_controller.cpp 2015-02-19 13:21:41 +0000
278@@ -52,6 +52,7 @@
279 mir::input::InputReceptionMode input_mode));
280 MOCK_METHOD1(remove_surface, void(std::weak_ptr<ms::Surface> const&));
281 MOCK_METHOD1(raise, void(std::weak_ptr<ms::Surface> const&));
282+ MOCK_CONST_METHOD1(surface_at, std::shared_ptr<ms::Surface>(geom::Point));
283 };
284
285 struct SurfaceController : testing::Test
286
287=== modified file 'tests/unit-tests/scene/test_surface_stack.cpp'
288--- tests/unit-tests/scene/test_surface_stack.cpp 2015-02-13 06:12:34 +0000
289+++ tests/unit-tests/scene/test_surface_stack.cpp 2015-02-19 13:21:41 +0000
290@@ -882,3 +882,50 @@
291 stack.for_each(count_exposed_surfaces);
292 EXPECT_THAT(num_exposed_surfaces, Eq(1));
293 }
294+
295+using namespace ::testing;
296+
297+TEST_F(SurfaceStack, returns_top_surface_under_cursor)
298+{
299+ geom::Point const cursor_over_all {100, 100};
300+ geom::Point const cursor_over_12 {200, 100};
301+ geom::Point const cursor_over_1 {600, 600};
302+ geom::Point const cursor_over_none{999, 999};
303+
304+ stack.add_surface(stub_surface1, default_params.depth, default_params.input_mode);
305+ stack.add_surface(stub_surface2, default_params.depth, default_params.input_mode);
306+ stack.add_surface(stub_surface3, default_params.depth, default_params.input_mode);
307+
308+ stub_surface1->resize({900, 900});
309+ stub_surface2->resize({500, 200});
310+ stub_surface3->resize({200, 500});
311+
312+ EXPECT_THAT(stack.surface_at(cursor_over_all), Eq(stub_surface3));
313+ EXPECT_THAT(stack.surface_at(cursor_over_12), Eq(stub_surface2));
314+ EXPECT_THAT(stack.surface_at(cursor_over_1), Eq(stub_surface1));
315+ EXPECT_THAT(stack.surface_at(cursor_over_none).get(), IsNull());
316+}
317+
318+TEST_F(SurfaceStack, returns_top_visible_surface_under_cursor)
319+{
320+ geom::Point const cursor_over_all {100, 100};
321+ geom::Point const cursor_over_12 {200, 100};
322+ geom::Point const cursor_over_1 {600, 600};
323+ geom::Point const cursor_over_none{999, 999};
324+
325+ stack.add_surface(stub_surface1, default_params.depth, default_params.input_mode);
326+ stack.add_surface(stub_surface2, default_params.depth, default_params.input_mode);
327+ stack.add_surface(stub_surface3, default_params.depth, default_params.input_mode);
328+ stack.add_surface(invisible_stub_surface, default_params.depth, default_params.input_mode);
329+
330+ stub_surface1->resize({900, 900});
331+ stub_surface2->resize({500, 200});
332+ stub_surface3->resize({200, 500});
333+ invisible_stub_surface->resize({999, 999});
334+
335+ EXPECT_THAT(stack.surface_at(cursor_over_all), Eq(stub_surface3));
336+ EXPECT_THAT(stack.surface_at(cursor_over_12), Eq(stub_surface2));
337+ EXPECT_THAT(stack.surface_at(cursor_over_1), Eq(stub_surface1));
338+ EXPECT_THAT(stack.surface_at(cursor_over_none).get(), IsNull());
339+}
340+

Subscribers

People subscribed via source and target branches