Mir

Merge lp:~robertcarr/mir/focus-tell-dont-ask into lp:mir

Proposed by Robert Carr
Status: Superseded
Proposed branch: lp:~robertcarr/mir/focus-tell-dont-ask
Merge into: lp:mir
Prerequisite: lp:~robertcarr/mir/socket-messenger-reporting
Diff against target: 345 lines (+106/-100)
9 files modified
include/server/mir/shell/application_session.h (+6/-0)
include/server/mir/shell/default_focus_mechanism.h (+2/-2)
include/server/mir/shell/session.h (+5/-0)
include/test/mir_test_doubles/mock_shell_session.h (+3/-0)
include/test/mir_test_doubles/stub_shell_session.h (+8/-0)
src/server/shell/application_session.cpp (+39/-3)
src/server/shell/default_focus_mechanism.cpp (+10/-18)
tests/acceptance-tests/test_client_focus_notification.cpp (+3/-1)
tests/unit-tests/shell/test_default_focus_mechanism.cpp (+30/-76)
To merge this branch: bzr merge lp:~robertcarr/mir/focus-tell-dont-ask
Reviewer Review Type Date Requested Status
Daniel van Vugt Needs Fixing
Alan Griffiths Abstain
PS Jenkins bot (community) continuous-integration Approve
Alexandros Frantzis Pending
kevin gunn Pending
Review via email: mp+187943@code.launchpad.net

This proposal supersedes a proposal from 2013-09-26.

This proposal has been superseded by a proposal from 2013-09-30.

Commit message

Handle tracking the last focused surface inside the ApplicationSession

Description of the change

Fixed error in ApplicationSession::relinquish_focus :)

Targeting to development-branch

===

Solve some of the races around focus by applying 'tell-dont-ask' in relation to internal Session state and the Focus Mechanism. Now the focus mechanism doesn't have to hold an (unsafe) shared_ptr to the last focused surface. It's ok for the Session to hold this weak_ptr, as it only attempts to lock it when holding surfaces_mutex.

This branch requires: https://code.launchpad.net/~vanvugt/mir/fix-1226139/+merge/186809

As it will cause unfocus messages to be sent in the "connection dropped without disconnect case"(i.e. many of the protobuf tool tests which fail to call disconnect) very likely to lead to a broken pipe error.

== Old Description ==

Fix the races around focus. Essentially these stem from that holding std::shared_ptr<msh::Surface> doesn't guarantee that calling methods on that surface will be safe (the underlying surface could be destroyed via Surface::destroy and then your method call will throw).

So this branch changes ApplicationSession to not destroy the surface explicitly and instead rely on ~msh::Surface, which means that you really can hold a safe pointer. I think this as a reasonable choice, as the shell might want to do similar things, i.e. continue to animate a surface after a session has closed it.

==

New approach relies on ApplicationSession to hold the last focused ptr...WIP

To post a comment you must log in.
Revision history for this message
Robert Carr (robertcarr) wrote : Posted in a previous version of this proposal

I was able to run the stress test with continuous cursor input for the full ten minute run. There is also good reason to believe this fixes: https://bugs.launchpad.net/mir/+bug/1220845

The exception in ~SessionMediator can be triggered if session_manager->close_session throws (due to this focus race), causing SessionMediator::disconnect to fail to reset it's session member-variable pointer. This exception is caught in frontend. However now in ~SessionMediator the pointer has not been reset so it closes the session again throwing an Invalid Session exception. causing the session to be closed again in ~

Revision history for this message
Robert Carr (robertcarr) wrote : Posted in a previous version of this proposal

The unsafely held surface pointer in this case is the previously focused app.

Revision history for this message
PS Jenkins bot (ps-jenkins) wrote : Posted in a previous version of this proposal
review: Approve (continuous-integration)
Revision history for this message
Alexandros Frantzis (afrantzis) wrote : Posted in a previous version of this proposal

There is an inconsistency with this approach: when a session is closed its surface resources are destroyed immediately, but when a surface is explicitly destroyed the resources are not destroyed until there are no more users.

Would it (make sense|be possible) for msh::Surface to own (hold a strong pointer to) ms::Surface and rely on ~msh::Surface() in both the cases above, thus removing the msh::Surface -> ms::Surface weak_ptr dance?

review: Needs Information
Revision history for this message
Alan Griffiths (alan-griffiths) wrote : Posted in a previous version of this proposal

> There is an inconsistency with this approach: when a session is closed its
> surface resources are destroyed immediately, but when a surface is explicitly
> destroyed the resources are not destroyed until there are no more users.
>
> Would it (make sense|be possible) for msh::Surface to own (hold a strong
> pointer to) ms::Surface and rely on ~msh::Surface() in both the cases above,
> thus removing the msh::Surface -> ms::Surface weak_ptr dance?

I've not yet looked at the MP, but I can contribute the origin of the "weak_ptr dance".

Once a surface has been "destroyed" it is no longer in the scenegraph (ok, SurfaceStack) and therefore not composited. If the ms::Surface continued to exist it could reach a state where it has client buffers meaning threads that require them will wait forever.

It may well be that subsequent changes (particularly the introduction of force_requests_to_complete()) permits a better solution than the "weak_ptr dance".

Revision history for this message
Alan Griffiths (alan-griffiths) wrote : Posted in a previous version of this proposal

I don't think this is the right evolution of the code, but I don't think it makes things worse.

The right solution would be more complex and should probably come as part of fixing the data model.

review: Abstain
Revision history for this message
Robert Carr (robertcarr) wrote : Posted in a previous version of this proposal

I think it makes sense to not destroy the surfaces explicitly from the session (as in the last committ). It may be possible to fix the weak_ptr dance (it almost surely is possible). It's not really clear what direction this code should go in right now though (except probably not further down this one), so I am trying to just fix the crashes non invasively

Revision history for this message
PS Jenkins bot (ps-jenkins) wrote : Posted in a previous version of this proposal
review: Approve (continuous-integration)
Revision history for this message
Daniel van Vugt (vanvugt) wrote : Posted in a previous version of this proposal

Bug 1216727 is still happening :(

terminate called after throwing an instance of 'boost::exception_detail::clone_impl<boost::exception_detail::error_info_injector<std::logic_error> >'
  what(): Requesting handle for an unregistered channel

Should we just unlink it from the branch?

review: Needs Fixing
Revision history for this message
Daniel van Vugt (vanvugt) wrote : Posted in a previous version of this proposal

P.S. Before declaring bug 1216727 fixed, please find a machine on which you can reproduce it (using trunk). To be sure the changes are actually having an effect.

Revision history for this message
Robert Carr (robertcarr) wrote : Posted in a previous version of this proposal

Can someone review this again?

Revision history for this message
Daniel van Vugt (vanvugt) : Posted in a previous version of this proposal
review: Abstain
Revision history for this message
PS Jenkins bot (ps-jenkins) wrote : Posted in a previous version of this proposal
review: Needs Fixing (continuous-integration)
Revision history for this message
Daniel van Vugt (vanvugt) wrote : Posted in a previous version of this proposal

Weirdly, merging this branch now causes an almost immediate hang:

Running tests...
Test project /home/dan/bzr/mir/tmp.focus/build
        Start 1: LGPL-required
  1/145 Test #1: LGPL-required ............................................... Passed 0.01 sec
        Start 2: GPL-required
  2/145 Test #2: GPL-required ................................................ Passed 0.01 sec
        Start 3: acceptance-tests.BespokeDisplayServerTestFixture.*

review: Needs Fixing
Revision history for this message
PS Jenkins bot (ps-jenkins) wrote : Posted in a previous version of this proposal
review: Needs Fixing (continuous-integration)
Revision history for this message
Daniel van Vugt (vanvugt) wrote : Posted in a previous version of this proposal

Another bug with this branch is that on Alt+Tab in demo_server_shell, the client rendering freezes. Permanently.

review: Needs Fixing
Revision history for this message
Robert Carr (robertcarr) wrote : Posted in a previous version of this proposal
Revision history for this message
PS Jenkins bot (ps-jenkins) wrote : Posted in a previous version of this proposal

FAILED: Continuous integration, rev:1084
No commit message was specified in the merge proposal. Click on the following link and set the commit message (if you want a jenkins rebuild you need to trigger it yourself):
https://code.launchpad.net/~robertcarr/mir/focus-tell-dont-ask/+merge/187037/+edit-commit-message

http://jenkins.qa.ubuntu.com/job/mir-ci/1558/
Executed test runs:
    SUCCESS: http://jenkins.qa.ubuntu.com/job/mir-android-saucy-i386-build/2090
    SUCCESS: http://jenkins.qa.ubuntu.com/job/mir-clang-saucy-amd64-build/1975
    FAILURE: http://jenkins.qa.ubuntu.com/job/mir-saucy-amd64-ci/796/console
    SUCCESS: http://jenkins.qa.ubuntu.com/job/mir-saucy-armhf-ci/58
        deb: http://jenkins.qa.ubuntu.com/job/mir-saucy-armhf-ci/58/artifact/work/output/*zip*/output.zip

Click here to trigger a rebuild:
http://s-jenkins:8080/job/mir-ci/1558/rebuild

review: Needs Fixing (continuous-integration)
Revision history for this message
kevin gunn (kgunn72) wrote : Posted in a previous version of this proposal

wouldn't this require a server api bump? i think so...

review: Needs Fixing
Revision history for this message
PS Jenkins bot (ps-jenkins) wrote : Posted in a previous version of this proposal
review: Needs Fixing (continuous-integration)
Revision history for this message
Alan Griffiths (alan-griffiths) wrote : Posted in a previous version of this proposal

On both g++ and clang 64bit builds I see:

[ FAILED ] 2 tests, listed below:
[ FAILED ] BespokeDisplayServerTestFixture.a_surface_is_notified_of_receiving_focus
[ FAILED ] BespokeDisplayServerTestFixture.two_surfaces_are_notified_of_gaining_and_losing_focus

(and so does jenkins on amd64)

review: Needs Fixing
Revision history for this message
Robert Ancell (robert-ancell) wrote : Posted in a previous version of this proposal

This merge should be moved to lp:~mir-team/mir/development-branch

Revision history for this message
PS Jenkins bot (ps-jenkins) wrote : Posted in a previous version of this proposal

FAILED: Continuous integration, rev:1085
No commit message was specified in the merge proposal. Click on the following link and set the commit message (if you want a jenkins rebuild you need to trigger it yourself):
https://code.launchpad.net/~robertcarr/mir/focus-tell-dont-ask/+merge/187648/+edit-commit-message

http://jenkins.qa.ubuntu.com/job/mir-team-mir-development-branch-ci/31/
Executed test runs:
    FAILURE: http://jenkins.qa.ubuntu.com/job/mir-android-saucy-i386-build/2142/console
    FAILURE: http://jenkins.qa.ubuntu.com/job/mir-clang-saucy-amd64-build/2027/console
    FAILURE: http://jenkins.qa.ubuntu.com/job/mir-team-mir-development-branch-saucy-amd64-ci/31/console

Click here to trigger a rebuild:
http://s-jenkins:8080/job/mir-team-mir-development-branch-ci/31/rebuild

review: Needs Fixing (continuous-integration)
Revision history for this message
PS Jenkins bot (ps-jenkins) wrote : Posted in a previous version of this proposal
review: Needs Fixing (continuous-integration)
Revision history for this message
PS Jenkins bot (ps-jenkins) wrote :
review: Approve (continuous-integration)
Revision history for this message
Kevin DuBois (kdub) wrote :

I think the idea is a step in the right direction, given the problem :) (makes more sense than the shared_ptr in the focus controller)

receive_focus/relinquish focus seems a funny name for what's going on, given that there's no exchange of resources.
There's some gaps with who really has the-focus though still, like with,
148 + if (surfaces.size() == 0)
149 + {
150 + targeter->focus_cleared();
151 + return;
152 + }
when this happens, the focus controller is unaware that it could reassign the focus... perhaps the session should accept or reject the focus, and let the focus mechanism know. I Maybe we're twisting around the funny way ms::Surface is.

Revision history for this message
Alan Griffiths (alan-griffiths) wrote :

It looked weird before, it looks weird after

review: Abstain
Revision history for this message
Daniel van Vugt (vanvugt) wrote :

The prereq branch needs fixing or even rejecting. And therefore its dependents will need updating too.

review: Needs Fixing

Unmerged revisions

1088. By Robert Carr

Fix error in relinquish focus

1087. By Robert Carr

Merge socket-messenger-reporitng

1086. By Robert Carr

Merge development-branch

1085. By Robert Carr

Typo

1084. By Robert Carr

application_session: Introduce default_surface_locked to dedupe some code

1083. By Robert Carr

Merge fix-1226139

1082. By Robert Carr

More cleanup

1081. By Robert Carr

Merge trunk

1080. By Robert Carr

Cleanup

1079. By Robert Carr

Mutex for surface

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1=== modified file 'include/server/mir/shell/application_session.h'
2--- include/server/mir/shell/application_session.h 2013-08-28 03:41:48 +0000
3+++ include/server/mir/shell/application_session.h 2013-09-26 22:16:32 +0000
4@@ -67,6 +67,9 @@
5
6 void set_lifecycle_state(MirLifecycleState state);
7
8+ void receive_focus(std::shared_ptr<InputTargeter> const& targeter, std::shared_ptr<SurfaceController> const& controller);
9+ void relinquish_focus();
10+
11 protected:
12 ApplicationSession(ApplicationSession const&) = delete;
13 ApplicationSession& operator=(ApplicationSession const&) = delete;
14@@ -79,6 +82,7 @@
15 std::shared_ptr<frontend::EventSink> const event_sink;
16
17 frontend::SurfaceId next_id();
18+ std::shared_ptr<Surface> default_surface_locked(std::unique_lock<std::mutex> const& lock) const;
19
20 std::atomic<int> next_surface_id;
21
22@@ -86,6 +90,8 @@
23 Surfaces::const_iterator checked_find(frontend::SurfaceId id) const;
24 std::mutex mutable surfaces_mutex;
25 Surfaces surfaces;
26+
27+ std::weak_ptr<Surface> last_focused_surface;
28 };
29
30 }
31
32=== modified file 'include/server/mir/shell/default_focus_mechanism.h'
33--- include/server/mir/shell/default_focus_mechanism.h 2013-08-28 03:41:48 +0000
34+++ include/server/mir/shell/default_focus_mechanism.h 2013-09-26 22:16:32 +0000
35@@ -50,8 +50,8 @@
36 std::shared_ptr<InputTargeter> const input_targeter;
37 std::shared_ptr<SurfaceController> const surface_controller;
38
39- std::mutex surface_focus_lock;
40- std::weak_ptr<Surface> currently_focused_surface;
41+ std::mutex focus_lock;
42+ std::weak_ptr<Session> currently_focused_session;
43 };
44
45 }
46
47=== modified file 'include/server/mir/shell/session.h'
48--- include/server/mir/shell/session.h 2013-08-28 03:41:48 +0000
49+++ include/server/mir/shell/session.h 2013-09-26 22:16:32 +0000
50@@ -28,6 +28,8 @@
51 namespace shell
52 {
53 class Surface;
54+class InputTargeter;
55+class SurfaceController;
56
57 class Session : public frontend::Session
58 {
59@@ -37,6 +39,9 @@
60
61 virtual void take_snapshot(SnapshotCallback const& snapshot_taken) = 0;
62 virtual std::shared_ptr<Surface> default_surface() const = 0;
63+
64+ virtual void receive_focus(std::shared_ptr<InputTargeter> const& targeter, std::shared_ptr<SurfaceController> const& controller) = 0;
65+ virtual void relinquish_focus() = 0;
66 };
67
68 }
69
70=== modified file 'include/test/mir_test_doubles/mock_shell_session.h'
71--- include/test/mir_test_doubles/mock_shell_session.h 2013-08-28 03:41:48 +0000
72+++ include/test/mir_test_doubles/mock_shell_session.h 2013-09-26 22:16:32 +0000
73@@ -49,6 +49,9 @@
74
75 MOCK_METHOD1(send_display_config, void(graphics::DisplayConfiguration const&));
76 MOCK_METHOD3(configure_surface, int(frontend::SurfaceId, MirSurfaceAttrib, int));
77+
78+ MOCK_METHOD2(receive_focus, void(std::shared_ptr<shell::InputTargeter> const&, std::shared_ptr<shell::SurfaceController> const&));
79+ MOCK_METHOD0(relinquish_focus, void());
80 };
81
82 }
83
84=== modified file 'include/test/mir_test_doubles/stub_shell_session.h'
85--- include/test/mir_test_doubles/stub_shell_session.h 2013-08-13 22:20:37 +0000
86+++ include/test/mir_test_doubles/stub_shell_session.h 2013-09-26 22:16:32 +0000
87@@ -71,6 +71,14 @@
88 {
89 return std::shared_ptr<shell::Surface>();
90 }
91+
92+ void receive_focus(std::shared_ptr<shell::InputTargeter> const&,
93+ std::shared_ptr<shell::SurfaceController> const&)
94+ {
95+ }
96+ void relinquish_focus()
97+ {
98+ }
99 };
100
101 }
102
103=== modified file 'src/server/shell/application_session.cpp'
104--- src/server/shell/application_session.cpp 2013-08-28 03:41:48 +0000
105+++ src/server/shell/application_session.cpp 2013-09-26 22:16:32 +0000
106@@ -21,6 +21,7 @@
107 #include "mir/shell/surface_factory.h"
108 #include "mir/shell/snapshot_strategy.h"
109 #include "mir/shell/session_listener.h"
110+#include "mir/shell/input_targeter.h"
111 #include "mir/frontend/event_sink.h"
112
113 #include <boost/throw_exception.hpp>
114@@ -99,16 +100,21 @@
115 snapshot_strategy->take_snapshot_of(default_surface(), snapshot_taken);
116 }
117
118-std::shared_ptr<msh::Surface> msh::ApplicationSession::default_surface() const
119+std::shared_ptr<msh::Surface> msh::ApplicationSession::default_surface_locked(std::unique_lock<std::mutex> const& /* lock */) const
120 {
121- std::unique_lock<std::mutex> lock(surfaces_mutex);
122-
123 if (surfaces.size())
124 return surfaces.begin()->second;
125 else
126 return std::shared_ptr<msh::Surface>();
127 }
128
129+std::shared_ptr<msh::Surface> msh::ApplicationSession::default_surface() const
130+{
131+ std::unique_lock<std::mutex> lock(surfaces_mutex);
132+
133+ return default_surface_locked(lock);
134+}
135+
136 void msh::ApplicationSession::destroy_surface(mf::SurfaceId id)
137 {
138 std::unique_lock<std::mutex> lock(surfaces_mutex);
139@@ -171,3 +177,33 @@
140 {
141 event_sink->handle_lifecycle_event(state);
142 }
143+
144+void msh::ApplicationSession::receive_focus(std::shared_ptr<msh::InputTargeter> const& targeter,
145+ std::shared_ptr<SurfaceController> const& controller)
146+{
147+ std::unique_lock<std::mutex> lock(surfaces_mutex);
148+ if (surfaces.size() == 0)
149+ {
150+ targeter->focus_cleared();
151+ return;
152+ }
153+ // Use default_surface_locked idiom instead
154+ auto focus_surface = surfaces.begin()->second;
155+
156+ focus_surface->raise(controller);
157+ focus_surface->take_input_focus(targeter);
158+ focus_surface->configure(mir_surface_attrib_focus, mir_surface_focused);
159+
160+ last_focused_surface = focus_surface;
161+}
162+
163+void msh::ApplicationSession::relinquish_focus()
164+{
165+ std::unique_lock<std::mutex> lock(surfaces_mutex);
166+ auto surf = last_focused_surface.lock();
167+ if (surf)
168+ {
169+ surf->configure(mir_surface_attrib_focus, mir_surface_unfocused);
170+ }
171+ last_focused_surface.reset();
172+}
173
174=== modified file 'src/server/shell/default_focus_mechanism.cpp'
175--- src/server/shell/default_focus_mechanism.cpp 2013-08-28 03:41:48 +0000
176+++ src/server/shell/default_focus_mechanism.cpp 2013-09-26 22:16:32 +0000
177@@ -35,28 +35,20 @@
178
179 void msh::DefaultFocusMechanism::set_focus_to(std::shared_ptr<Session> const& focus_session)
180 {
181+ std::lock_guard<std::mutex> lg(focus_lock);
182+
183+ auto last_session = currently_focused_session.lock();
184+ if (last_session)
185+ {
186+ last_session->relinquish_focus();
187+ }
188+
189 // TODO: This path should be encapsulated in a seperate clear_focus message
190 if (!focus_session)
191 {
192 input_targeter->focus_cleared();
193 return;
194 }
195-
196- auto surface = focus_session->default_surface();
197- if (surface)
198- {
199- std::lock_guard<std::mutex> lg(surface_focus_lock);
200- auto current_focus = currently_focused_surface.lock();
201- if (current_focus)
202- current_focus->configure(mir_surface_attrib_focus, mir_surface_unfocused);
203- surface->configure(mir_surface_attrib_focus, mir_surface_focused);
204- currently_focused_surface = surface;
205-
206- surface->raise(surface_controller);
207- surface->take_input_focus(input_targeter);
208- }
209- else
210- {
211- input_targeter->focus_cleared();
212- }
213+ focus_session->receive_focus(input_targeter, surface_controller);
214+ currently_focused_session = focus_session;
215 }
216
217=== modified file 'tests/acceptance-tests/test_client_focus_notification.cpp'
218--- tests/acceptance-tests/test_client_focus_notification.cpp 2013-09-17 14:31:42 +0000
219+++ tests/acceptance-tests/test_client_focus_notification.cpp 2013-09-26 22:16:32 +0000
220@@ -159,7 +159,9 @@
221 void expect_events(mt::WaitCondition* all_events_received) override
222 {
223 EXPECT_CALL(*observer, see(Pointee(mt::SurfaceEvent(mir_surface_attrib_focus, mir_surface_focused)))).Times(1)
224- .WillOnce(mt::WakeUp(all_events_received));
225+ .WillOnce(mt::WakeUp(all_events_received)); // Now we close the surface generating an unfocused event
226+ // libmirclient guarantees we will see this before mir_surface_release finishes, so we don't need further
227+ // synchronization
228 EXPECT_CALL(*observer, see(Pointee(mt::SurfaceEvent(mir_surface_attrib_focus, mir_surface_unfocused)))).Times(1);
229 }
230 } client_config;
231
232=== modified file 'tests/unit-tests/shell/test_default_focus_mechanism.cpp'
233--- tests/unit-tests/shell/test_default_focus_mechanism.cpp 2013-08-28 03:41:48 +0000
234+++ tests/unit-tests/shell/test_default_focus_mechanism.cpp 2013-09-26 22:16:32 +0000
235@@ -47,80 +47,34 @@
236 namespace mt = mir::test;
237 namespace mtd = mir::test::doubles;
238
239-TEST(DefaultFocusMechanism, raises_default_surface)
240-{
241- using namespace ::testing;
242-
243- NiceMock<mtd::MockShellSession> app1;
244- mtd::MockSurface mock_surface(&app1, std::make_shared<mtd::StubSurfaceBuilder>());
245- {
246- InSequence seq;
247- EXPECT_CALL(app1, default_surface()).Times(1)
248- .WillOnce(Return(mt::fake_shared(mock_surface)));
249- }
250-
251- auto controller = std::make_shared<mtd::StubSurfaceController>();
252- EXPECT_CALL(mock_surface, raise(Eq(controller))).Times(1);
253- mtd::StubInputTargeter targeter;
254- msh::DefaultFocusMechanism focus_mechanism(mt::fake_shared(targeter), controller);
255-
256- focus_mechanism.set_focus_to(mt::fake_shared(app1));
257-}
258-
259-TEST(DefaultFocusMechanism, mechanism_notifies_default_surface_of_focus_changes)
260-{
261- using namespace ::testing;
262-
263- NiceMock<mtd::MockShellSession> app1, app2;
264- mtd::MockSurface mock_surface1(&app1, std::make_shared<mtd::StubSurfaceBuilder>());
265- mtd::MockSurface mock_surface2(&app2, std::make_shared<mtd::StubSurfaceBuilder>());
266-
267- ON_CALL(app1, default_surface()).WillByDefault(Return(mt::fake_shared(mock_surface1)));
268- ON_CALL(app2, default_surface()).WillByDefault(Return(mt::fake_shared(mock_surface2)));
269-
270-
271- {
272- InSequence seq;
273- EXPECT_CALL(mock_surface1, configure(mir_surface_attrib_focus, mir_surface_focused)).Times(1);
274- EXPECT_CALL(mock_surface1, configure(mir_surface_attrib_focus, mir_surface_unfocused)).Times(1);
275- EXPECT_CALL(mock_surface2, configure(mir_surface_attrib_focus, mir_surface_focused)).Times(1);
276- }
277-
278- msh::DefaultFocusMechanism focus_mechanism(std::make_shared<mtd::StubInputTargeter>(),
279- std::make_shared<mtd::StubSurfaceController>());
280-
281- focus_mechanism.set_focus_to(mt::fake_shared(app1));
282- focus_mechanism.set_focus_to(mt::fake_shared(app2));
283-}
284-
285-TEST(DefaultFocusMechanism, sets_input_focus)
286-{
287- using namespace ::testing;
288-
289- NiceMock<mtd::MockShellSession> app1;
290- mtd::MockSurface mock_surface(&app1, std::make_shared<mtd::StubSurfaceBuilder>());
291- {
292- InSequence seq;
293- EXPECT_CALL(app1, default_surface()).Times(1)
294- .WillOnce(Return(mt::fake_shared(mock_surface)));
295- EXPECT_CALL(app1, default_surface()).Times(1)
296- .WillOnce(Return(std::shared_ptr<msh::Surface>()));
297- }
298-
299- mtd::MockInputTargeter targeter;
300-
301- msh::DefaultFocusMechanism focus_mechanism(mt::fake_shared(targeter), std::make_shared<mtd::StubSurfaceController>());
302-
303- {
304- InSequence seq;
305- EXPECT_CALL(mock_surface, take_input_focus(_)).Times(1);
306- // When we have no default surface.
307- EXPECT_CALL(targeter, focus_cleared()).Times(1);
308- // When we have no session.
309- EXPECT_CALL(targeter, focus_cleared()).Times(1);
310- }
311-
312- focus_mechanism.set_focus_to(mt::fake_shared(app1));
313- focus_mechanism.set_focus_to(mt::fake_shared(app1));
314- focus_mechanism.set_focus_to(std::shared_ptr<msh::Session>());
315+TEST(DefaultFocusMechanism, hands_focus_to_session)
316+{
317+ using namespace ::testing;
318+
319+ mtd::MockShellSession app1;
320+ EXPECT_CALL(app1, receive_focus(_, _)).Times(1);
321+
322+ msh::DefaultFocusMechanism focus_mechanism(std::make_shared<mtd::StubInputTargeter>(), std::make_shared<mtd::StubSurfaceController>());
323+
324+ focus_mechanism.set_focus_to(mt::fake_shared(app1));
325+}
326+
327+TEST(DefaultFocusMechanism, relinquishes_focus_from_last_session)
328+{
329+ using namespace ::testing;
330+
331+ auto app1 = std::make_shared<mtd::MockShellSession>();
332+ auto app2 = std::make_shared<mtd::MockShellSession>();
333+
334+ {
335+ InSequence seq;
336+ EXPECT_CALL(*app1, receive_focus(_, _)).Times(1);
337+ EXPECT_CALL(*app1, relinquish_focus()).Times(1);
338+ EXPECT_CALL(*app2, receive_focus(_, _)).Times(1);
339+ }
340+
341+ msh::DefaultFocusMechanism focus_mechanism(std::make_shared<mtd::StubInputTargeter>(), std::make_shared<mtd::StubSurfaceController>());
342+
343+ focus_mechanism.set_focus_to(app1);
344+ focus_mechanism.set_focus_to(app2);
345 }

Subscribers

People subscribed via source and target branches