Mir

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

Proposed by Robert Carr
Status: Superseded
Proposed branch: lp:~robertcarr/mir/focus-tell-dont-ask
Merge into: lp:~mir-team/mir/trunk
Diff against target: 478 lines (+160/-105)
14 files modified
include/server/mir/frontend/communicator_report.h (+0/-1)
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/frontend/protobuf_socket_communicator.cpp (+1/-1)
src/server/frontend/socket_messenger.cpp (+13/-3)
src/server/frontend/socket_messenger.h (+6/-1)
src/server/shell/application_session.cpp (+38/-3)
src/server/shell/default_focus_mechanism.cpp (+10/-18)
tests/unit-tests/frontend/CMakeLists.txt (+1/-0)
tests/unit-tests/frontend/test_socket_messenger.cpp (+37/-0)
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
Alan Griffiths Pending
Daniel van Vugt Pending
PS Jenkins bot continuous-integration Pending
Alexandros Frantzis Pending
Review via email: mp+186655@code.launchpad.net

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

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

Description of the change

== 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
1085. By Robert Carr

Typo

1086. By Robert Carr

Merge development-branch

1087. By Robert Carr

Merge socket-messenger-reporitng

1088. By Robert Carr

Fix error in relinquish focus

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/frontend/communicator_report.h'
2--- include/server/mir/frontend/communicator_report.h 2013-06-19 16:14:40 +0000
3+++ include/server/mir/frontend/communicator_report.h 2013-09-23 14:32:39 +0000
4@@ -42,7 +42,6 @@
5 class NullCommunicatorReport : public CommunicatorReport
6 {
7 public:
8-
9 void error(std::exception const& error);
10 };
11 }
12
13=== modified file 'include/server/mir/shell/application_session.h'
14--- include/server/mir/shell/application_session.h 2013-08-28 03:41:48 +0000
15+++ include/server/mir/shell/application_session.h 2013-09-23 14:32:39 +0000
16@@ -67,6 +67,9 @@
17
18 void set_lifecycle_state(MirLifecycleState state);
19
20+ void receive_focus(std::shared_ptr<InputTargeter> const& targeter, std::shared_ptr<SurfaceController> const& controller);
21+ void relinquish_focus();
22+
23 protected:
24 ApplicationSession(ApplicationSession const&) = delete;
25 ApplicationSession& operator=(ApplicationSession const&) = delete;
26@@ -79,6 +82,7 @@
27 std::shared_ptr<frontend::EventSink> const event_sink;
28
29 frontend::SurfaceId next_id();
30+ std::shared_ptr<Surface> default_surface_locked(std::unique_lock<std::mutex> const& lock) const;
31
32 std::atomic<int> next_surface_id;
33
34@@ -86,6 +90,8 @@
35 Surfaces::const_iterator checked_find(frontend::SurfaceId id) const;
36 std::mutex mutable surfaces_mutex;
37 Surfaces surfaces;
38+
39+ std::weak_ptr<Surface> last_focused_surface;
40 };
41
42 }
43
44=== modified file 'include/server/mir/shell/default_focus_mechanism.h'
45--- include/server/mir/shell/default_focus_mechanism.h 2013-08-28 03:41:48 +0000
46+++ include/server/mir/shell/default_focus_mechanism.h 2013-09-23 14:32:39 +0000
47@@ -50,8 +50,8 @@
48 std::shared_ptr<InputTargeter> const input_targeter;
49 std::shared_ptr<SurfaceController> const surface_controller;
50
51- std::mutex surface_focus_lock;
52- std::weak_ptr<Surface> currently_focused_surface;
53+ std::mutex focus_lock;
54+ std::weak_ptr<Session> currently_focused_session;
55 };
56
57 }
58
59=== modified file 'include/server/mir/shell/session.h'
60--- include/server/mir/shell/session.h 2013-08-28 03:41:48 +0000
61+++ include/server/mir/shell/session.h 2013-09-23 14:32:39 +0000
62@@ -28,6 +28,8 @@
63 namespace shell
64 {
65 class Surface;
66+class InputTargeter;
67+class SurfaceController;
68
69 class Session : public frontend::Session
70 {
71@@ -37,6 +39,9 @@
72
73 virtual void take_snapshot(SnapshotCallback const& snapshot_taken) = 0;
74 virtual std::shared_ptr<Surface> default_surface() const = 0;
75+
76+ virtual void receive_focus(std::shared_ptr<InputTargeter> const& targeter, std::shared_ptr<SurfaceController> const& controller) = 0;
77+ virtual void relinquish_focus() = 0;
78 };
79
80 }
81
82=== modified file 'include/test/mir_test_doubles/mock_shell_session.h'
83--- include/test/mir_test_doubles/mock_shell_session.h 2013-08-28 03:41:48 +0000
84+++ include/test/mir_test_doubles/mock_shell_session.h 2013-09-23 14:32:39 +0000
85@@ -49,6 +49,9 @@
86
87 MOCK_METHOD1(send_display_config, void(graphics::DisplayConfiguration const&));
88 MOCK_METHOD3(configure_surface, int(frontend::SurfaceId, MirSurfaceAttrib, int));
89+
90+ MOCK_METHOD2(receive_focus, void(std::shared_ptr<shell::InputTargeter> const&, std::shared_ptr<shell::SurfaceController> const&));
91+ MOCK_METHOD0(relinquish_focus, void());
92 };
93
94 }
95
96=== modified file 'include/test/mir_test_doubles/stub_shell_session.h'
97--- include/test/mir_test_doubles/stub_shell_session.h 2013-08-13 22:20:37 +0000
98+++ include/test/mir_test_doubles/stub_shell_session.h 2013-09-23 14:32:39 +0000
99@@ -71,6 +71,14 @@
100 {
101 return std::shared_ptr<shell::Surface>();
102 }
103+
104+ void receive_focus(std::shared_ptr<shell::InputTargeter> const&,
105+ std::shared_ptr<shell::SurfaceController> const&)
106+ {
107+ }
108+ void relinquish_focus()
109+ {
110+ }
111 };
112
113 }
114
115=== modified file 'src/server/frontend/protobuf_socket_communicator.cpp'
116--- src/server/frontend/protobuf_socket_communicator.cpp 2013-08-28 03:41:48 +0000
117+++ src/server/frontend/protobuf_socket_communicator.cpp 2013-09-23 14:32:39 +0000
118@@ -135,7 +135,7 @@
119 {
120 if (!ec)
121 {
122- auto messenger = std::make_shared<detail::SocketMessenger>(socket);
123+ auto messenger = std::make_shared<detail::SocketMessenger>(socket, report);
124 auto client_pid = messenger->client_pid();
125 if (session_authorizer->connection_is_allowed(client_pid))
126 {
127
128=== modified file 'src/server/frontend/socket_messenger.cpp'
129--- src/server/frontend/socket_messenger.cpp 2013-08-28 03:41:48 +0000
130+++ src/server/frontend/socket_messenger.cpp 2013-09-23 14:32:39 +0000
131@@ -18,13 +18,16 @@
132
133 #include "socket_messenger.h"
134 #include "mir/frontend/client_constants.h"
135+#include "mir/frontend/communicator_report.h"
136
137 namespace mfd = mir::frontend::detail;
138 namespace bs = boost::system;
139 namespace ba = boost::asio;
140
141-mfd::SocketMessenger::SocketMessenger(std::shared_ptr<ba::local::stream_protocol::socket> const& socket)
142- : socket(socket)
143+mfd::SocketMessenger::SocketMessenger(std::shared_ptr<ba::local::stream_protocol::socket> const& socket,
144+ std::shared_ptr<CommunicatorReport> const& report)
145+ : socket(socket),
146+ report(report)
147 {
148 whole_message.reserve(serialization_buffer_size);
149 }
150@@ -59,7 +62,14 @@
151 // function has completed (if it would be executed asynchronously.
152 // NOTE: we rely on this synchronous behavior as per the comment in
153 // mf::SessionMediator::create_surface
154- ba::write(*socket, ba::buffer(whole_message));
155+ try
156+ {
157+ ba::write(*socket, ba::buffer(whole_message));
158+ }
159+ catch (std::exception &ex)
160+ {
161+ report->error(ex);
162+ }
163 }
164
165 void mfd::SocketMessenger::send_fds(std::vector<int32_t> const& fds)
166
167=== modified file 'src/server/frontend/socket_messenger.h'
168--- src/server/frontend/socket_messenger.h 2013-08-28 03:41:48 +0000
169+++ src/server/frontend/socket_messenger.h 2013-09-23 14:32:39 +0000
170@@ -26,13 +26,16 @@
171 {
172 namespace frontend
173 {
174+class CommunicatorReport;
175+
176 namespace detail
177 {
178 class SocketMessenger : public MessageSender,
179 public MessageReceiver
180 {
181 public:
182- SocketMessenger(std::shared_ptr<boost::asio::local::stream_protocol::socket> const& socket);
183+ SocketMessenger(std::shared_ptr<boost::asio::local::stream_protocol::socket> const& socket,
184+ std::shared_ptr<frontend::CommunicatorReport> const& report);
185
186 void send(std::string const& body);
187 void send_fds(std::vector<int32_t> const& fds);
188@@ -42,6 +45,8 @@
189
190 private:
191 std::shared_ptr<boost::asio::local::stream_protocol::socket> socket;
192+ std::shared_ptr<CommunicatorReport> const& report;
193+
194 std::vector<char> whole_message;
195 };
196 }
197
198=== modified file 'src/server/shell/application_session.cpp'
199--- src/server/shell/application_session.cpp 2013-08-28 03:41:48 +0000
200+++ src/server/shell/application_session.cpp 2013-09-23 14:32:39 +0000
201@@ -21,6 +21,7 @@
202 #include "mir/shell/surface_factory.h"
203 #include "mir/shell/snapshot_strategy.h"
204 #include "mir/shell/session_listener.h"
205+#include "mir/shell/input_targeter.h"
206 #include "mir/frontend/event_sink.h"
207
208 #include <boost/throw_exception.hpp>
209@@ -99,16 +100,21 @@
210 snapshot_strategy->take_snapshot_of(default_surface(), snapshot_taken);
211 }
212
213-std::shared_ptr<msh::Surface> msh::ApplicationSession::default_surface() const
214+std::shared_ptr<msh::Surface> msh::ApplicationSession::default_surface_locked(std::unique_lock<std::mutex> const& /* lock */) const
215 {
216- std::unique_lock<std::mutex> lock(surfaces_mutex);
217-
218 if (surfaces.size())
219 return surfaces.begin()->second;
220 else
221 return std::shared_ptr<msh::Surface>();
222 }
223
224+std::shared_ptr<msh::Surface> msh::ApplicationSession::default_surface() const
225+{
226+ std::unique_lock<std::mutex> lock(surfaces_mutex);
227+
228+ return default_surface_locked(lock);
229+}
230+
231 void msh::ApplicationSession::destroy_surface(mf::SurfaceId id)
232 {
233 std::unique_lock<std::mutex> lock(surfaces_mutex);
234@@ -171,3 +177,32 @@
235 {
236 event_sink->handle_lifecycle_event(state);
237 }
238+
239+void msh::ApplicationSession::receive_focus(std::shared_ptr<msh::InputTargeter> const& targeter,
240+ std::shared_ptr<SurfaceController> const& controller)
241+{
242+ std::unique_lock<std::mutex> lock(surfaces_mutex);
243+ if (surfaces.size() == 0)
244+ {
245+ targeter->focus_cleared();
246+ return;
247+ }
248+ // Use default_surface_locked idiom instead
249+ auto focus_surface = surfaces.begin()->second;
250+
251+ focus_surface->raise(controller);
252+ focus_surface->take_input_focus(targeter);
253+ focus_surface->configure(mir_surface_attrib_focus, mir_surface_focused);
254+
255+ last_focused_surface = focus_surface;
256+}
257+
258+void msh::ApplicationSession::relinquish_focus()
259+{
260+ std::unique_lock<std::mutex> lock(surfaces_mutex);
261+ auto surf = default_surface_locked(lock);
262+ if (surf)
263+ {
264+ surf->configure(mir_surface_attrib_focus, mir_surface_unfocused);
265+ }
266+}
267
268=== modified file 'src/server/shell/default_focus_mechanism.cpp'
269--- src/server/shell/default_focus_mechanism.cpp 2013-08-28 03:41:48 +0000
270+++ src/server/shell/default_focus_mechanism.cpp 2013-09-23 14:32:39 +0000
271@@ -35,28 +35,20 @@
272
273 void msh::DefaultFocusMechanism::set_focus_to(std::shared_ptr<Session> const& focus_session)
274 {
275+ std::lock_guard<std::mutex> lg(focus_lock);
276+
277+ auto last_session = currently_focused_session.lock();
278+ if (last_session)
279+ {
280+ last_session->relinquish_focus();
281+ }
282+
283 // TODO: This path should be encapsulated in a seperate clear_focus message
284 if (!focus_session)
285 {
286 input_targeter->focus_cleared();
287 return;
288 }
289-
290- auto surface = focus_session->default_surface();
291- if (surface)
292- {
293- std::lock_guard<std::mutex> lg(surface_focus_lock);
294- auto current_focus = currently_focused_surface.lock();
295- if (current_focus)
296- current_focus->configure(mir_surface_attrib_focus, mir_surface_unfocused);
297- surface->configure(mir_surface_attrib_focus, mir_surface_focused);
298- currently_focused_surface = surface;
299-
300- surface->raise(surface_controller);
301- surface->take_input_focus(input_targeter);
302- }
303- else
304- {
305- input_targeter->focus_cleared();
306- }
307+ focus_session->receive_focus(input_targeter, surface_controller);
308+ currently_focused_session = focus_session;
309 }
310
311=== modified file 'tests/unit-tests/frontend/CMakeLists.txt'
312--- tests/unit-tests/frontend/CMakeLists.txt 2013-08-28 03:41:48 +0000
313+++ tests/unit-tests/frontend/CMakeLists.txt 2013-09-23 14:32:39 +0000
314@@ -8,6 +8,7 @@
315 ${CMAKE_CURRENT_SOURCE_DIR}/test_resource_cache.cpp
316 ${CMAKE_CURRENT_SOURCE_DIR}/test_session_mediator.cpp
317 ${CMAKE_CURRENT_SOURCE_DIR}/test_socket_session.cpp
318+ ${CMAKE_CURRENT_SOURCE_DIR}/test_socket_messenger.cpp
319 ${CMAKE_CURRENT_SOURCE_DIR}/test_event_sender.cpp
320 ${CMAKE_CURRENT_SOURCE_DIR}/test_global_event_sender.cpp
321 )
322
323=== added file 'tests/unit-tests/frontend/test_socket_messenger.cpp'
324--- tests/unit-tests/frontend/test_socket_messenger.cpp 1970-01-01 00:00:00 +0000
325+++ tests/unit-tests/frontend/test_socket_messenger.cpp 2013-09-23 14:32:39 +0000
326@@ -0,0 +1,37 @@
327+/*
328+ * Copyright © 2013 Canonical Ltd.
329+ *
330+ * This program is free software: you can redistribute it and/or modify
331+ * it under the terms of the GNU General Public License version 3 as
332+ * published by the Free Software Foundation.
333+ *
334+ * This program is distributed in the hope that it will be useful,
335+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
336+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
337+ * GNU General Public License for more details.
338+ *
339+ * You should have received a copy of the GNU General Public License
340+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
341+ *
342+ * Authored by: Daniel van Vugt <daniel.van.vugt@canonical.com>
343+ */
344+
345+#include "mir/frontend/communicator_report.h"
346+#include "src/server/frontend/socket_messenger.h"
347+
348+#include <gmock/gmock.h>
349+#include <gtest/gtest.h>
350+
351+namespace mf = mir::frontend;
352+
353+using namespace mf::detail;
354+using namespace boost::asio;
355+
356+TEST(SocketMessengerTest, write_failures_never_throw)
357+{
358+ io_service svc;
359+ auto sock = std::make_shared<local::stream_protocol::socket>(svc);
360+ SocketMessenger mess(sock, std::make_shared<mf::NullCommunicatorReport>());
361+
362+ EXPECT_NO_THROW( mess.send("foo") );
363+}
364
365=== modified file 'tests/unit-tests/shell/test_default_focus_mechanism.cpp'
366--- tests/unit-tests/shell/test_default_focus_mechanism.cpp 2013-08-28 03:41:48 +0000
367+++ tests/unit-tests/shell/test_default_focus_mechanism.cpp 2013-09-23 14:32:39 +0000
368@@ -47,80 +47,34 @@
369 namespace mt = mir::test;
370 namespace mtd = mir::test::doubles;
371
372-TEST(DefaultFocusMechanism, raises_default_surface)
373-{
374- using namespace ::testing;
375-
376- NiceMock<mtd::MockShellSession> app1;
377- mtd::MockSurface mock_surface(&app1, std::make_shared<mtd::StubSurfaceBuilder>());
378- {
379- InSequence seq;
380- EXPECT_CALL(app1, default_surface()).Times(1)
381- .WillOnce(Return(mt::fake_shared(mock_surface)));
382- }
383-
384- auto controller = std::make_shared<mtd::StubSurfaceController>();
385- EXPECT_CALL(mock_surface, raise(Eq(controller))).Times(1);
386- mtd::StubInputTargeter targeter;
387- msh::DefaultFocusMechanism focus_mechanism(mt::fake_shared(targeter), controller);
388-
389- focus_mechanism.set_focus_to(mt::fake_shared(app1));
390-}
391-
392-TEST(DefaultFocusMechanism, mechanism_notifies_default_surface_of_focus_changes)
393-{
394- using namespace ::testing;
395-
396- NiceMock<mtd::MockShellSession> app1, app2;
397- mtd::MockSurface mock_surface1(&app1, std::make_shared<mtd::StubSurfaceBuilder>());
398- mtd::MockSurface mock_surface2(&app2, std::make_shared<mtd::StubSurfaceBuilder>());
399-
400- ON_CALL(app1, default_surface()).WillByDefault(Return(mt::fake_shared(mock_surface1)));
401- ON_CALL(app2, default_surface()).WillByDefault(Return(mt::fake_shared(mock_surface2)));
402-
403-
404- {
405- InSequence seq;
406- EXPECT_CALL(mock_surface1, configure(mir_surface_attrib_focus, mir_surface_focused)).Times(1);
407- EXPECT_CALL(mock_surface1, configure(mir_surface_attrib_focus, mir_surface_unfocused)).Times(1);
408- EXPECT_CALL(mock_surface2, configure(mir_surface_attrib_focus, mir_surface_focused)).Times(1);
409- }
410-
411- msh::DefaultFocusMechanism focus_mechanism(std::make_shared<mtd::StubInputTargeter>(),
412- std::make_shared<mtd::StubSurfaceController>());
413-
414- focus_mechanism.set_focus_to(mt::fake_shared(app1));
415- focus_mechanism.set_focus_to(mt::fake_shared(app2));
416-}
417-
418-TEST(DefaultFocusMechanism, sets_input_focus)
419-{
420- using namespace ::testing;
421-
422- NiceMock<mtd::MockShellSession> app1;
423- mtd::MockSurface mock_surface(&app1, std::make_shared<mtd::StubSurfaceBuilder>());
424- {
425- InSequence seq;
426- EXPECT_CALL(app1, default_surface()).Times(1)
427- .WillOnce(Return(mt::fake_shared(mock_surface)));
428- EXPECT_CALL(app1, default_surface()).Times(1)
429- .WillOnce(Return(std::shared_ptr<msh::Surface>()));
430- }
431-
432- mtd::MockInputTargeter targeter;
433-
434- msh::DefaultFocusMechanism focus_mechanism(mt::fake_shared(targeter), std::make_shared<mtd::StubSurfaceController>());
435-
436- {
437- InSequence seq;
438- EXPECT_CALL(mock_surface, take_input_focus(_)).Times(1);
439- // When we have no default surface.
440- EXPECT_CALL(targeter, focus_cleared()).Times(1);
441- // When we have no session.
442- EXPECT_CALL(targeter, focus_cleared()).Times(1);
443- }
444-
445- focus_mechanism.set_focus_to(mt::fake_shared(app1));
446- focus_mechanism.set_focus_to(mt::fake_shared(app1));
447- focus_mechanism.set_focus_to(std::shared_ptr<msh::Session>());
448+TEST(DefaultFocusMechanism, hands_focus_to_session)
449+{
450+ using namespace ::testing;
451+
452+ mtd::MockShellSession app1;
453+ EXPECT_CALL(app1, receive_focus(_, _)).Times(1);
454+
455+ msh::DefaultFocusMechanism focus_mechanism(std::make_shared<mtd::StubInputTargeter>(), std::make_shared<mtd::StubSurfaceController>());
456+
457+ focus_mechanism.set_focus_to(mt::fake_shared(app1));
458+}
459+
460+TEST(DefaultFocusMechanism, relinquishes_focus_from_last_session)
461+{
462+ using namespace ::testing;
463+
464+ auto app1 = std::make_shared<mtd::MockShellSession>();
465+ auto app2 = std::make_shared<mtd::MockShellSession>();
466+
467+ {
468+ InSequence seq;
469+ EXPECT_CALL(*app1, receive_focus(_, _)).Times(1);
470+ EXPECT_CALL(*app1, relinquish_focus()).Times(1);
471+ EXPECT_CALL(*app2, receive_focus(_, _)).Times(1);
472+ }
473+
474+ msh::DefaultFocusMechanism focus_mechanism(std::make_shared<mtd::StubInputTargeter>(), std::make_shared<mtd::StubSurfaceController>());
475+
476+ focus_mechanism.set_focus_to(app1);
477+ focus_mechanism.set_focus_to(app2);
478 }

Subscribers

People subscribed via source and target branches