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
=== modified file 'include/server/mir/shell/application_session.h'
--- include/server/mir/shell/application_session.h 2013-08-28 03:41:48 +0000
+++ include/server/mir/shell/application_session.h 2013-09-26 22:16:32 +0000
@@ -67,6 +67,9 @@
6767
68 void set_lifecycle_state(MirLifecycleState state);68 void set_lifecycle_state(MirLifecycleState state);
6969
70 void receive_focus(std::shared_ptr<InputTargeter> const& targeter, std::shared_ptr<SurfaceController> const& controller);
71 void relinquish_focus();
72
70protected:73protected:
71 ApplicationSession(ApplicationSession const&) = delete;74 ApplicationSession(ApplicationSession const&) = delete;
72 ApplicationSession& operator=(ApplicationSession const&) = delete;75 ApplicationSession& operator=(ApplicationSession const&) = delete;
@@ -79,6 +82,7 @@
79 std::shared_ptr<frontend::EventSink> const event_sink;82 std::shared_ptr<frontend::EventSink> const event_sink;
8083
81 frontend::SurfaceId next_id();84 frontend::SurfaceId next_id();
85 std::shared_ptr<Surface> default_surface_locked(std::unique_lock<std::mutex> const& lock) const;
8286
83 std::atomic<int> next_surface_id;87 std::atomic<int> next_surface_id;
8488
@@ -86,6 +90,8 @@
86 Surfaces::const_iterator checked_find(frontend::SurfaceId id) const;90 Surfaces::const_iterator checked_find(frontend::SurfaceId id) const;
87 std::mutex mutable surfaces_mutex;91 std::mutex mutable surfaces_mutex;
88 Surfaces surfaces;92 Surfaces surfaces;
93
94 std::weak_ptr<Surface> last_focused_surface;
89};95};
9096
91}97}
9298
=== modified file 'include/server/mir/shell/default_focus_mechanism.h'
--- include/server/mir/shell/default_focus_mechanism.h 2013-08-28 03:41:48 +0000
+++ include/server/mir/shell/default_focus_mechanism.h 2013-09-26 22:16:32 +0000
@@ -50,8 +50,8 @@
50 std::shared_ptr<InputTargeter> const input_targeter;50 std::shared_ptr<InputTargeter> const input_targeter;
51 std::shared_ptr<SurfaceController> const surface_controller;51 std::shared_ptr<SurfaceController> const surface_controller;
5252
53 std::mutex surface_focus_lock;53 std::mutex focus_lock;
54 std::weak_ptr<Surface> currently_focused_surface;54 std::weak_ptr<Session> currently_focused_session;
55};55};
5656
57}57}
5858
=== modified file 'include/server/mir/shell/session.h'
--- include/server/mir/shell/session.h 2013-08-28 03:41:48 +0000
+++ include/server/mir/shell/session.h 2013-09-26 22:16:32 +0000
@@ -28,6 +28,8 @@
28namespace shell28namespace shell
29{29{
30class Surface;30class Surface;
31class InputTargeter;
32class SurfaceController;
3133
32class Session : public frontend::Session34class Session : public frontend::Session
33{35{
@@ -37,6 +39,9 @@
3739
38 virtual void take_snapshot(SnapshotCallback const& snapshot_taken) = 0;40 virtual void take_snapshot(SnapshotCallback const& snapshot_taken) = 0;
39 virtual std::shared_ptr<Surface> default_surface() const = 0;41 virtual std::shared_ptr<Surface> default_surface() const = 0;
42
43 virtual void receive_focus(std::shared_ptr<InputTargeter> const& targeter, std::shared_ptr<SurfaceController> const& controller) = 0;
44 virtual void relinquish_focus() = 0;
40};45};
4146
42}47}
4348
=== modified file 'include/test/mir_test_doubles/mock_shell_session.h'
--- include/test/mir_test_doubles/mock_shell_session.h 2013-08-28 03:41:48 +0000
+++ include/test/mir_test_doubles/mock_shell_session.h 2013-09-26 22:16:32 +0000
@@ -49,6 +49,9 @@
49 49
50 MOCK_METHOD1(send_display_config, void(graphics::DisplayConfiguration const&));50 MOCK_METHOD1(send_display_config, void(graphics::DisplayConfiguration const&));
51 MOCK_METHOD3(configure_surface, int(frontend::SurfaceId, MirSurfaceAttrib, int));51 MOCK_METHOD3(configure_surface, int(frontend::SurfaceId, MirSurfaceAttrib, int));
52
53 MOCK_METHOD2(receive_focus, void(std::shared_ptr<shell::InputTargeter> const&, std::shared_ptr<shell::SurfaceController> const&));
54 MOCK_METHOD0(relinquish_focus, void());
52};55};
5356
54}57}
5558
=== modified file 'include/test/mir_test_doubles/stub_shell_session.h'
--- include/test/mir_test_doubles/stub_shell_session.h 2013-08-13 22:20:37 +0000
+++ include/test/mir_test_doubles/stub_shell_session.h 2013-09-26 22:16:32 +0000
@@ -71,6 +71,14 @@
71 {71 {
72 return std::shared_ptr<shell::Surface>();72 return std::shared_ptr<shell::Surface>();
73 }73 }
74
75 void receive_focus(std::shared_ptr<shell::InputTargeter> const&,
76 std::shared_ptr<shell::SurfaceController> const&)
77 {
78 }
79 void relinquish_focus()
80 {
81 }
74};82};
7583
76}84}
7785
=== modified file 'src/server/shell/application_session.cpp'
--- src/server/shell/application_session.cpp 2013-08-28 03:41:48 +0000
+++ src/server/shell/application_session.cpp 2013-09-26 22:16:32 +0000
@@ -21,6 +21,7 @@
21#include "mir/shell/surface_factory.h"21#include "mir/shell/surface_factory.h"
22#include "mir/shell/snapshot_strategy.h"22#include "mir/shell/snapshot_strategy.h"
23#include "mir/shell/session_listener.h"23#include "mir/shell/session_listener.h"
24#include "mir/shell/input_targeter.h"
24#include "mir/frontend/event_sink.h"25#include "mir/frontend/event_sink.h"
2526
26#include <boost/throw_exception.hpp>27#include <boost/throw_exception.hpp>
@@ -99,16 +100,21 @@
99 snapshot_strategy->take_snapshot_of(default_surface(), snapshot_taken);100 snapshot_strategy->take_snapshot_of(default_surface(), snapshot_taken);
100}101}
101102
102std::shared_ptr<msh::Surface> msh::ApplicationSession::default_surface() const103std::shared_ptr<msh::Surface> msh::ApplicationSession::default_surface_locked(std::unique_lock<std::mutex> const& /* lock */) const
103{104{
104 std::unique_lock<std::mutex> lock(surfaces_mutex);
105
106 if (surfaces.size())105 if (surfaces.size())
107 return surfaces.begin()->second;106 return surfaces.begin()->second;
108 else107 else
109 return std::shared_ptr<msh::Surface>();108 return std::shared_ptr<msh::Surface>();
110}109}
111110
111std::shared_ptr<msh::Surface> msh::ApplicationSession::default_surface() const
112{
113 std::unique_lock<std::mutex> lock(surfaces_mutex);
114
115 return default_surface_locked(lock);
116}
117
112void msh::ApplicationSession::destroy_surface(mf::SurfaceId id)118void msh::ApplicationSession::destroy_surface(mf::SurfaceId id)
113{119{
114 std::unique_lock<std::mutex> lock(surfaces_mutex);120 std::unique_lock<std::mutex> lock(surfaces_mutex);
@@ -171,3 +177,33 @@
171{177{
172 event_sink->handle_lifecycle_event(state);178 event_sink->handle_lifecycle_event(state);
173}179}
180
181void msh::ApplicationSession::receive_focus(std::shared_ptr<msh::InputTargeter> const& targeter,
182 std::shared_ptr<SurfaceController> const& controller)
183{
184 std::unique_lock<std::mutex> lock(surfaces_mutex);
185 if (surfaces.size() == 0)
186 {
187 targeter->focus_cleared();
188 return;
189 }
190 // Use default_surface_locked idiom instead
191 auto focus_surface = surfaces.begin()->second;
192
193 focus_surface->raise(controller);
194 focus_surface->take_input_focus(targeter);
195 focus_surface->configure(mir_surface_attrib_focus, mir_surface_focused);
196
197 last_focused_surface = focus_surface;
198}
199
200void msh::ApplicationSession::relinquish_focus()
201{
202 std::unique_lock<std::mutex> lock(surfaces_mutex);
203 auto surf = last_focused_surface.lock();
204 if (surf)
205 {
206 surf->configure(mir_surface_attrib_focus, mir_surface_unfocused);
207 }
208 last_focused_surface.reset();
209}
174210
=== modified file 'src/server/shell/default_focus_mechanism.cpp'
--- src/server/shell/default_focus_mechanism.cpp 2013-08-28 03:41:48 +0000
+++ src/server/shell/default_focus_mechanism.cpp 2013-09-26 22:16:32 +0000
@@ -35,28 +35,20 @@
3535
36void msh::DefaultFocusMechanism::set_focus_to(std::shared_ptr<Session> const& focus_session)36void msh::DefaultFocusMechanism::set_focus_to(std::shared_ptr<Session> const& focus_session)
37{37{
38 std::lock_guard<std::mutex> lg(focus_lock);
39
40 auto last_session = currently_focused_session.lock();
41 if (last_session)
42 {
43 last_session->relinquish_focus();
44 }
45
38 // TODO: This path should be encapsulated in a seperate clear_focus message46 // TODO: This path should be encapsulated in a seperate clear_focus message
39 if (!focus_session)47 if (!focus_session)
40 {48 {
41 input_targeter->focus_cleared();49 input_targeter->focus_cleared();
42 return;50 return;
43 }51 }
44 52 focus_session->receive_focus(input_targeter, surface_controller);
45 auto surface = focus_session->default_surface();53 currently_focused_session = focus_session;
46 if (surface)
47 {
48 std::lock_guard<std::mutex> lg(surface_focus_lock);
49 auto current_focus = currently_focused_surface.lock();
50 if (current_focus)
51 current_focus->configure(mir_surface_attrib_focus, mir_surface_unfocused);
52 surface->configure(mir_surface_attrib_focus, mir_surface_focused);
53 currently_focused_surface = surface;
54
55 surface->raise(surface_controller);
56 surface->take_input_focus(input_targeter);
57 }
58 else
59 {
60 input_targeter->focus_cleared();
61 }
62}54}
6355
=== modified file 'tests/acceptance-tests/test_client_focus_notification.cpp'
--- tests/acceptance-tests/test_client_focus_notification.cpp 2013-09-17 14:31:42 +0000
+++ tests/acceptance-tests/test_client_focus_notification.cpp 2013-09-26 22:16:32 +0000
@@ -159,7 +159,9 @@
159 void expect_events(mt::WaitCondition* all_events_received) override159 void expect_events(mt::WaitCondition* all_events_received) override
160 {160 {
161 EXPECT_CALL(*observer, see(Pointee(mt::SurfaceEvent(mir_surface_attrib_focus, mir_surface_focused)))).Times(1)161 EXPECT_CALL(*observer, see(Pointee(mt::SurfaceEvent(mir_surface_attrib_focus, mir_surface_focused)))).Times(1)
162 .WillOnce(mt::WakeUp(all_events_received));162 .WillOnce(mt::WakeUp(all_events_received)); // Now we close the surface generating an unfocused event
163 // libmirclient guarantees we will see this before mir_surface_release finishes, so we don't need further
164 // synchronization
163 EXPECT_CALL(*observer, see(Pointee(mt::SurfaceEvent(mir_surface_attrib_focus, mir_surface_unfocused)))).Times(1);165 EXPECT_CALL(*observer, see(Pointee(mt::SurfaceEvent(mir_surface_attrib_focus, mir_surface_unfocused)))).Times(1);
164 }166 }
165 } client_config;167 } client_config;
166168
=== modified file 'tests/unit-tests/shell/test_default_focus_mechanism.cpp'
--- tests/unit-tests/shell/test_default_focus_mechanism.cpp 2013-08-28 03:41:48 +0000
+++ tests/unit-tests/shell/test_default_focus_mechanism.cpp 2013-09-26 22:16:32 +0000
@@ -47,80 +47,34 @@
47namespace mt = mir::test;47namespace mt = mir::test;
48namespace mtd = mir::test::doubles;48namespace mtd = mir::test::doubles;
4949
50TEST(DefaultFocusMechanism, raises_default_surface)50TEST(DefaultFocusMechanism, hands_focus_to_session)
51{51{
52 using namespace ::testing;52 using namespace ::testing;
53 53
54 NiceMock<mtd::MockShellSession> app1;54 mtd::MockShellSession app1;
55 mtd::MockSurface mock_surface(&app1, std::make_shared<mtd::StubSurfaceBuilder>());55 EXPECT_CALL(app1, receive_focus(_, _)).Times(1);
56 {56
57 InSequence seq;57 msh::DefaultFocusMechanism focus_mechanism(std::make_shared<mtd::StubInputTargeter>(), std::make_shared<mtd::StubSurfaceController>());
58 EXPECT_CALL(app1, default_surface()).Times(1)58
59 .WillOnce(Return(mt::fake_shared(mock_surface)));59 focus_mechanism.set_focus_to(mt::fake_shared(app1));
60 }60}
6161
62 auto controller = std::make_shared<mtd::StubSurfaceController>();62TEST(DefaultFocusMechanism, relinquishes_focus_from_last_session)
63 EXPECT_CALL(mock_surface, raise(Eq(controller))).Times(1);63{
64 mtd::StubInputTargeter targeter;64 using namespace ::testing;
65 msh::DefaultFocusMechanism focus_mechanism(mt::fake_shared(targeter), controller);65
66 66 auto app1 = std::make_shared<mtd::MockShellSession>();
67 focus_mechanism.set_focus_to(mt::fake_shared(app1));67 auto app2 = std::make_shared<mtd::MockShellSession>();
68}68
6969 {
70TEST(DefaultFocusMechanism, mechanism_notifies_default_surface_of_focus_changes)70 InSequence seq;
71{71 EXPECT_CALL(*app1, receive_focus(_, _)).Times(1);
72 using namespace ::testing;72 EXPECT_CALL(*app1, relinquish_focus()).Times(1);
7373 EXPECT_CALL(*app2, receive_focus(_, _)).Times(1);
74 NiceMock<mtd::MockShellSession> app1, app2;74 }
75 mtd::MockSurface mock_surface1(&app1, std::make_shared<mtd::StubSurfaceBuilder>());75
76 mtd::MockSurface mock_surface2(&app2, std::make_shared<mtd::StubSurfaceBuilder>());76 msh::DefaultFocusMechanism focus_mechanism(std::make_shared<mtd::StubInputTargeter>(), std::make_shared<mtd::StubSurfaceController>());
77 77
78 ON_CALL(app1, default_surface()).WillByDefault(Return(mt::fake_shared(mock_surface1)));78 focus_mechanism.set_focus_to(app1);
79 ON_CALL(app2, default_surface()).WillByDefault(Return(mt::fake_shared(mock_surface2)));79 focus_mechanism.set_focus_to(app2);
80
81
82 {
83 InSequence seq;
84 EXPECT_CALL(mock_surface1, configure(mir_surface_attrib_focus, mir_surface_focused)).Times(1);
85 EXPECT_CALL(mock_surface1, configure(mir_surface_attrib_focus, mir_surface_unfocused)).Times(1);
86 EXPECT_CALL(mock_surface2, configure(mir_surface_attrib_focus, mir_surface_focused)).Times(1);
87 }
88
89 msh::DefaultFocusMechanism focus_mechanism(std::make_shared<mtd::StubInputTargeter>(),
90 std::make_shared<mtd::StubSurfaceController>());
91
92 focus_mechanism.set_focus_to(mt::fake_shared(app1));
93 focus_mechanism.set_focus_to(mt::fake_shared(app2));
94}
95
96TEST(DefaultFocusMechanism, sets_input_focus)
97{
98 using namespace ::testing;
99
100 NiceMock<mtd::MockShellSession> app1;
101 mtd::MockSurface mock_surface(&app1, std::make_shared<mtd::StubSurfaceBuilder>());
102 {
103 InSequence seq;
104 EXPECT_CALL(app1, default_surface()).Times(1)
105 .WillOnce(Return(mt::fake_shared(mock_surface)));
106 EXPECT_CALL(app1, default_surface()).Times(1)
107 .WillOnce(Return(std::shared_ptr<msh::Surface>()));
108 }
109
110 mtd::MockInputTargeter targeter;
111
112 msh::DefaultFocusMechanism focus_mechanism(mt::fake_shared(targeter), std::make_shared<mtd::StubSurfaceController>());
113
114 {
115 InSequence seq;
116 EXPECT_CALL(mock_surface, take_input_focus(_)).Times(1);
117 // When we have no default surface.
118 EXPECT_CALL(targeter, focus_cleared()).Times(1);
119 // When we have no session.
120 EXPECT_CALL(targeter, focus_cleared()).Times(1);
121 }
122
123 focus_mechanism.set_focus_to(mt::fake_shared(app1));
124 focus_mechanism.set_focus_to(mt::fake_shared(app1));
125 focus_mechanism.set_focus_to(std::shared_ptr<msh::Session>());
126}80}

Subscribers

People subscribed via source and target branches