Mir

Merge lp:~alan-griffiths/mir/integrate-generic-shell into lp:mir

Proposed by Alan Griffiths
Status: Merged
Approved by: Alan Griffiths
Approved revision: no longer in the source branch.
Merged at revision: 2375
Proposed branch: lp:~alan-griffiths/mir/integrate-generic-shell
Merge into: lp:mir
Prerequisite: lp:~alan-griffiths/mir/integrate-focus-controller
Diff against target: 832 lines (+523/-56)
10 files modified
examples/CMakeLists.txt (+0/-1)
examples/server_example_basic_window_manager.h (+4/-5)
examples/server_example_window_management.cpp (+5/-5)
include/server/mir/shell/generic_shell.h (+10/-13)
include/server/mir/shell/window_manager.h (+7/-9)
src/server/shell/CMakeLists.txt (+1/-0)
src/server/shell/generic_shell.cpp (+19/-23)
src/server/symbols.map (+10/-0)
tests/unit-tests/scene/CMakeLists.txt (+1/-0)
tests/unit-tests/scene/test_generic_shell.cpp (+466/-0)
To merge this branch: bzr merge lp:~alan-griffiths/mir/integrate-generic-shell
Reviewer Review Type Date Requested Status
Alexandros Frantzis (community) Approve
PS Jenkins bot (community) continuous-integration Approve
Robert Carr (community) Approve
Review via email: mp+251581@code.launchpad.net

Commit message

examples, shell: integrate the GenericShell support for writing window management policies.

Description of the change

examples, shell: integrate the GenericShell support for writing window management policies.

To post a comment you must log in.
Revision history for this message
PS Jenkins bot (ps-jenkins) wrote :
review: Approve (continuous-integration)
Revision history for this message
PS Jenkins bot (ps-jenkins) wrote :
review: Approve (continuous-integration)
Revision history for this message
Robert Carr (robertcarr) wrote :

LGTM

review: Approve
Revision history for this message
Alexandros Frantzis (afrantzis) wrote :

Looking at GenericShell, it seem to have very little logic in it, mostly forwarding calls to the WindowManager interface. It makes me wonder if the WindowManager/GenericShell is really needed and ifusers would be happy enough with AbstractShell.

98 #define MIR_EXAMPLE_GENERIC_SHELL_H_

Needs update.

Needs discussion.

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

> Looking at GenericShell, it seem to have very little logic in it, mostly
> forwarding calls to the WindowManager interface. It makes me wonder if the
> WindowManager/GenericShell is really needed and ifusers would be happy enough
> with AbstractShell.

I contemplated rolling AbstractShell and GenericShell into one and using WindowManager as the customization point - the only real impediment to doing that is DefaultShell. (U8 makes trivial use of AbstractShell to avoid DefaultShell's behavior but I don't see that being hard to port.)

Given that DefaultShell has window management behavior that could only be described as perverse I'd like like to get rid of it as soon it is possible to change USC (and our examples) to something based on WindowManager.

> 98 #define MIR_EXAMPLE_GENERIC_SHELL_H_

Fixed.

Revision history for this message
PS Jenkins bot (ps-jenkins) wrote :
review: Approve (continuous-integration)
Revision history for this message
Alexandros Frantzis (afrantzis) wrote :

I am not convinced that GenericShell/WindowManager is the right abstraction to expose, since the WindowManager interface is too strongly coupled conceptually to Shell, and, as mentioned above, GenericShell doesn't contain much logic. The GenericShell brings some convenience, but I am not sure the extra layer of abstraction it adds is worth it vs shell implementers just subclassing AbstractShell.

Alan mentioned in IRC an alternative approach in which WindowManager is exposed and *Shell classes are internal. I like this idea and I think it's worth pursuing it further. My only concern is that the WindowManager interface won't be enough for clients that have other shell needs (e.g. need to handle prompt sessions specially), but my experience in that area is limited.

All that being said, this MP seems like a reasonable starting point for further investigation and experimentation.

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

I would have been happy to take on board a different approach, but given the silence around here lets land and see where we can refactor to before the next release.

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1=== modified file 'examples/CMakeLists.txt'
2--- examples/CMakeLists.txt 2015-03-05 04:24:05 +0000
3+++ examples/CMakeLists.txt 2015-03-05 13:54:59 +0000
4@@ -17,7 +17,6 @@
5
6 add_library(exampleserverconfig STATIC
7 server_example_canonical_window_manager.cpp
8- server_example_generic_shell.cpp
9 server_example_display_configuration_policy.cpp
10 server_example_input_event_filter.cpp
11 server_example_log_options.cpp
12
13=== modified file 'examples/server_example_basic_window_manager.h'
14--- examples/server_example_basic_window_manager.h 2015-03-05 05:47:28 +0000
15+++ examples/server_example_basic_window_manager.h 2015-03-05 13:54:59 +0000
16@@ -19,11 +19,10 @@
17 #ifndef MIR_EXAMPLE_BASIC_WINDOW_MANAGER_H_
18 #define MIR_EXAMPLE_BASIC_WINDOW_MANAGER_H_
19
20-#include "server_example_generic_shell.h"
21-
22 #include "mir/geometry/rectangles.h"
23 #include "mir/scene/session.h"
24 #include "mir/scene/surface_creation_parameters.h"
25+#include "mir/shell/generic_shell.h"
26
27 #include <map>
28 #include <mutex>
29@@ -105,7 +104,7 @@
30 ///
31 /// \tparam SurfaceInfo must be constructable from (std::shared_ptr<ms::Session>, std::shared_ptr<ms::Surface>)
32 template<typename WindowManagementPolicy, typename SessionInfo, typename SurfaceInfo>
33-class BasicWindowManager : public WindowManager,
34+class BasicWindowManager : public shell::WindowManager,
35 private BasicWindowManagerTools<SessionInfo, SurfaceInfo>
36 {
37 public:
38@@ -148,8 +147,8 @@
39 }
40
41 void remove_surface(
42- std::weak_ptr<scene::Surface> const& surface,
43- std::shared_ptr<scene::Session> const& session) override
44+ std::shared_ptr<scene::Session> const& session,
45+ std::weak_ptr<scene::Surface> const& surface) override
46 {
47 std::lock_guard<decltype(mutex)> lock(mutex);
48 policy.handle_delete_surface(session, surface);
49
50=== modified file 'examples/server_example_window_management.cpp'
51--- examples/server_example_window_management.cpp 2015-03-05 04:24:05 +0000
52+++ examples/server_example_window_management.cpp 2015-03-05 13:54:59 +0000
53@@ -127,25 +127,25 @@
54
55 auto const selection = options->get<std::string>(wm_option);
56
57- std::function<std::shared_ptr<WindowManager>(msh::FocusController* focus_controller)> wm_builder;
58+ std::function<std::shared_ptr<msh::WindowManager>(msh::FocusController* focus_controller)> wm_builder;
59
60 if (selection == wm_tiling)
61 {
62- wm_builder = [&server](msh::FocusController* focus_controller) -> std::shared_ptr<WindowManager>
63+ wm_builder = [&server](msh::FocusController* focus_controller) -> std::shared_ptr<msh::WindowManager>
64 {
65 return std::make_shared<TilingWindowManager>(focus_controller);
66 };
67 }
68 else if (selection == wm_fullscreen)
69 {
70- wm_builder = [&server](msh::FocusController* focus_controller) -> std::shared_ptr<WindowManager>
71+ wm_builder = [&server](msh::FocusController* focus_controller) -> std::shared_ptr<msh::WindowManager>
72 {
73 return std::make_shared<FullscreenWindowManager>(focus_controller, server.the_shell_display_layout());
74 };
75 }
76 else if (selection == wm_canonical)
77 {
78- wm_builder = [&server](msh::FocusController* focus_controller) -> std::shared_ptr<WindowManager>
79+ wm_builder = [&server](msh::FocusController* focus_controller) -> std::shared_ptr<msh::WindowManager>
80 {
81 return std::make_shared<CanonicalWindowManager>(focus_controller);
82 };
83@@ -154,7 +154,7 @@
84 throw mir::AbnormalExit("Unknown window manager: " + selection);
85
86
87- auto tmp = std::make_shared<GenericShell>(
88+ auto tmp = std::make_shared<msh::GenericShell>(
89 server.the_input_targeter(),
90 server.the_surface_coordinator(),
91 server.the_session_coordinator(),
92
93=== renamed file 'examples/server_example_generic_shell.h' => 'include/server/mir/shell/generic_shell.h'
94--- examples/server_example_generic_shell.h 2015-03-05 05:47:28 +0000
95+++ include/server/mir/shell/generic_shell.h 2015-03-05 13:54:59 +0000
96@@ -16,28 +16,25 @@
97 * Authored By: Alan Griffiths <alan@octopull.co.uk>
98 */
99
100-#ifndef MIR_EXAMPLE_GENERIC_SHELL_H_
101-#define MIR_EXAMPLE_GENERIC_SHELL_H_
102-
103-#include "server_example_window_manager.h"
104+#ifndef MIR_SHELL_GENERIC_SHELL_H_
105+#define MIR_SHELL_GENERIC_SHELL_H_
106
107 #include "mir/shell/abstract_shell.h"
108-
109-///\example server_example_generic_shell.h
110-/// A generic shell that supports a window manager
111+#include "mir/shell/window_manager.h"
112
113 namespace mir
114 {
115 namespace geometry { class Point; }
116
117-namespace examples
118+namespace shell
119 {
120-class GenericShell : public virtual shell::Shell, public virtual shell::FocusController,
121- private shell::AbstractShell
122+/// A generic shell that supports a window manager
123+class GenericShell : public virtual Shell, public virtual FocusController,
124+ private AbstractShell
125 {
126 public:
127 GenericShell(
128- std::shared_ptr<shell::InputTargeter> const& input_targeter,
129+ std::shared_ptr<InputTargeter> const& input_targeter,
130 std::shared_ptr<scene::SurfaceCoordinator> const& surface_coordinator,
131 std::shared_ptr<scene::SessionCoordinator> const& session_coordinator,
132 std::shared_ptr<scene::PromptSessionManager> const& prompt_session_manager,
133@@ -62,13 +59,13 @@
134 MirSurfaceAttrib attrib,
135 int value) override;
136
137-private:
138 void add_display(geometry::Rectangle const& area) override;
139 void remove_display(geometry::Rectangle const& area) override;
140
141+private:
142 std::shared_ptr<WindowManager> const window_manager;
143 };
144 }
145 }
146
147-#endif /* MIR_EXAMPLE_GENERIC_SHELL_H_ */
148+#endif /* MIR_SHELL_GENERIC_SHELL_H_ */
149
150=== renamed file 'examples/server_example_window_manager.h' => 'include/server/mir/shell/window_manager.h'
151--- examples/server_example_window_manager.h 2015-02-27 15:23:05 +0000
152+++ include/server/mir/shell/window_manager.h 2015-03-05 13:54:59 +0000
153@@ -16,8 +16,8 @@
154 * Authored By: Alan Griffiths <alan@octopull.co.uk>
155 */
156
157-#ifndef MIR_EXAMPLE_WINDOW_MANAGER_H_
158-#define MIR_EXAMPLE_WINDOW_MANAGER_H_
159+#ifndef MIR_SHELL_WINDOW_MANAGER_H_
160+#define MIR_SHELL_WINDOW_MANAGER_H_
161
162 #include "mir/frontend/surface_id.h"
163 #include "mir_toolkit/common.h"
164@@ -25,15 +25,13 @@
165
166 #include <memory>
167
168-///\example server_example_window_manager.h
169-/// A window manager interface
170-
171 namespace mir
172 {
173 namespace geometry { class Rectangle; }
174 namespace scene { class Session; class Surface; class SurfaceCreationParameters; }
175-namespace examples
176+namespace shell
177 {
178+/// interface to provide window management logic
179 class WindowManager
180 {
181 public:
182@@ -47,8 +45,8 @@
183 std::function<frontend::SurfaceId(std::shared_ptr<scene::Session> const& session, scene::SurfaceCreationParameters const& params)> const& build) = 0;
184
185 virtual void remove_surface(
186- std::weak_ptr<scene::Surface> const& surface,
187- std::shared_ptr<scene::Session> const& session) = 0;
188+ std::shared_ptr<scene::Session> const& session,
189+ std::weak_ptr<scene::Surface> const& surface) = 0;
190
191 virtual void add_display(geometry::Rectangle const& area) = 0;
192
193@@ -70,4 +68,4 @@
194 }
195 }
196
197-#endif /* MIR_EXAMPLE_WINDOW_MANAGER_H_ */
198+#endif /* MIR_SHELL_WINDOW_MANAGER_H_ */
199
200=== modified file 'src/server/shell/CMakeLists.txt'
201--- src/server/shell/CMakeLists.txt 2015-02-18 05:27:28 +0000
202+++ src/server/shell/CMakeLists.txt 2015-03-05 13:54:59 +0000
203@@ -4,6 +4,7 @@
204 abstract_shell.cpp
205 default_placement_strategy.cpp
206 frontend_shell.cpp
207+ generic_shell.cpp
208 graphics_display_layout.cpp
209 default_configuration.cpp
210 default_shell.cpp
211
212=== renamed file 'examples/server_example_generic_shell.cpp' => 'src/server/shell/generic_shell.cpp'
213--- examples/server_example_generic_shell.cpp 2015-03-05 05:47:28 +0000
214+++ src/server/shell/generic_shell.cpp 2015-03-05 13:54:59 +0000
215@@ -16,66 +16,62 @@
216 * Authored By: Alan Griffiths <alan@octopull.co.uk>
217 */
218
219-#include "server_example_generic_shell.h"
220+#include "mir/shell/generic_shell.h"
221
222 #include "mir/geometry/point.h"
223 #include "mir/scene/session.h"
224 #include "mir/scene/surface_coordinator.h"
225 #include "mir/scene/surface_creation_parameters.h"
226
227-namespace me = mir::examples;
228 namespace mf = mir::frontend;
229 namespace ms = mir::scene;
230 namespace msh = mir::shell;
231
232-///\example server_example_generic_shell.cpp
233-/// A shell that accepts a WindowManager
234-
235-me::GenericShell::GenericShell(
236- std::shared_ptr<msh::InputTargeter> const& input_targeter,
237+msh::GenericShell::GenericShell(
238+ std::shared_ptr<InputTargeter> const& input_targeter,
239 std::shared_ptr<ms::SurfaceCoordinator> const& surface_coordinator,
240 std::shared_ptr<ms::SessionCoordinator> const& session_coordinator,
241 std::shared_ptr<ms::PromptSessionManager> const& prompt_session_manager,
242- std::function<std::shared_ptr<WindowManager>(FocusController* focus_controller)> const& wm_builder) :
243+ std::function<std::shared_ptr<shell::WindowManager>(FocusController* focus_controller)> const& wm_builder) :
244 AbstractShell(input_targeter, surface_coordinator, session_coordinator, prompt_session_manager),
245 window_manager(wm_builder(this))
246 {
247 }
248
249-std::shared_ptr<ms::Session> me::GenericShell::open_session(
250+std::shared_ptr<ms::Session> msh::GenericShell::open_session(
251 pid_t client_pid,
252 std::string const& name,
253 std::shared_ptr<mf::EventSink> const& sink)
254 {
255- auto const result = msh::AbstractShell::open_session(client_pid, name, sink);
256+ auto const result = AbstractShell::open_session(client_pid, name, sink);
257 window_manager->add_session(result);
258 return result;
259 }
260
261-void me::GenericShell::close_session(std::shared_ptr<ms::Session> const& session)
262+void msh::GenericShell::close_session(std::shared_ptr<ms::Session> const& session)
263 {
264 window_manager->remove_session(session);
265- msh::AbstractShell::close_session(session);
266+ AbstractShell::close_session(session);
267 }
268
269-auto me::GenericShell::create_surface(std::shared_ptr<ms::Session> const& session, ms::SurfaceCreationParameters const& params)
270+auto msh::GenericShell::create_surface(std::shared_ptr<ms::Session> const& session, ms::SurfaceCreationParameters const& params)
271 -> mf::SurfaceId
272 {
273 auto const build = [this](std::shared_ptr<ms::Session> const& session, ms::SurfaceCreationParameters const& placed_params)
274 {
275- return msh::AbstractShell::create_surface(session, placed_params);
276+ return AbstractShell::create_surface(session, placed_params);
277 };
278
279 return window_manager->add_surface(session, params, build);
280 }
281
282-void me::GenericShell::destroy_surface(std::shared_ptr<ms::Session> const& session, mf::SurfaceId surface)
283+void msh::GenericShell::destroy_surface(std::shared_ptr<ms::Session> const& session, mf::SurfaceId surface)
284 {
285- window_manager->remove_surface(session->surface(surface), session);
286- msh::AbstractShell::destroy_surface(session, surface);
287+ window_manager->remove_surface(session, session->surface(surface));
288+ AbstractShell::destroy_surface(session, surface);
289 }
290
291-bool me::GenericShell::handle(MirEvent const& event)
292+bool msh::GenericShell::handle(MirEvent const& event)
293 {
294 if (mir_event_get_type(&event) != mir_event_type_input)
295 return false;
296@@ -97,7 +93,7 @@
297 return false;
298 }
299
300-int me::GenericShell::set_surface_attribute(
301+int msh::GenericShell::set_surface_attribute(
302 std::shared_ptr<ms::Session> const& session,
303 std::shared_ptr<ms::Surface> const& surface,
304 MirSurfaceAttrib attrib,
305@@ -108,19 +104,19 @@
306 case mir_surface_attrib_state:
307 {
308 auto const state = window_manager->handle_set_state(surface, MirSurfaceState(value));
309- return msh::AbstractShell::set_surface_attribute(session, surface, attrib, state);
310+ return AbstractShell::set_surface_attribute(session, surface, attrib, state);
311 }
312 default:
313- return msh::AbstractShell::set_surface_attribute(session, surface, attrib, value);
314+ return AbstractShell::set_surface_attribute(session, surface, attrib, value);
315 }
316 }
317
318-void me::GenericShell::add_display(geometry::Rectangle const& area)
319+void msh::GenericShell::add_display(geometry::Rectangle const& area)
320 {
321 window_manager->add_display(area);
322 }
323
324-void me::GenericShell::remove_display(geometry::Rectangle const& area)
325+void msh::GenericShell::remove_display(geometry::Rectangle const& area)
326 {
327 window_manager->remove_display(area);
328 }
329
330=== modified file 'src/server/symbols.map'
331--- src/server/symbols.map 2015-03-05 05:47:28 +0000
332+++ src/server/symbols.map 2015-03-05 13:54:59 +0000
333@@ -392,6 +392,16 @@
334 mir::shell::FocusController::focused_session*;
335 mir::shell::FocusController::operator*;
336 mir::shell::FocusController::set_focus_to*;
337+ mir::shell::FocusController::surface_at*;
338+ mir::shell::GenericShell::add_display*;
339+ mir::shell::GenericShell::close_session*;
340+ mir::shell::GenericShell::create_surface*;
341+ mir::shell::GenericShell::destroy_surface*;
342+ mir::shell::GenericShell::GenericShell*;
343+ mir::shell::GenericShell::handle*;
344+ mir::shell::GenericShell::open_session*;
345+ mir::shell::GenericShell::remove_display*;
346+ mir::shell::GenericShell::set_surface_attribute*;
347 mir::shell::HostLifecycleEventListener::?HostLifecycleEventListener*;
348 mir::shell::HostLifecycleEventListener::HostLifecycleEventListener*;
349 mir::shell::HostLifecycleEventListener::lifecycle_event_occurred*;
350
351=== modified file 'tests/unit-tests/scene/CMakeLists.txt'
352--- tests/unit-tests/scene/CMakeLists.txt 2015-03-05 05:47:28 +0000
353+++ tests/unit-tests/scene/CMakeLists.txt 2015-03-05 13:54:59 +0000
354@@ -16,6 +16,7 @@
355 ${CMAKE_CURRENT_SOURCE_DIR}/test_surface_impl.cpp
356 ${CMAKE_CURRENT_SOURCE_DIR}/test_basic_surface.cpp
357 ${CMAKE_CURRENT_SOURCE_DIR}/test_default_shell.cpp
358+ ${CMAKE_CURRENT_SOURCE_DIR}/test_generic_shell.cpp
359 ${CMAKE_CURRENT_SOURCE_DIR}/test_surface_stack.cpp
360 ${CMAKE_CURRENT_SOURCE_DIR}/test_surface_controller.cpp
361 ${CMAKE_CURRENT_SOURCE_DIR}/test_legacy_scene_change_notification.cpp
362
363=== added file 'tests/unit-tests/scene/test_generic_shell.cpp'
364--- tests/unit-tests/scene/test_generic_shell.cpp 1970-01-01 00:00:00 +0000
365+++ tests/unit-tests/scene/test_generic_shell.cpp 2015-03-05 13:54:59 +0000
366@@ -0,0 +1,466 @@
367+/*
368+ * Copyright © 2015 Canonical Ltd.
369+ *
370+ * This program is free software: you can redistribute it and/or modify
371+ * it under the terms of the GNU General Public License version 3 as
372+ * published by the Free Software Foundation.
373+ *
374+ * This program is distributed in the hope that it will be useful,
375+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
376+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
377+ * GNU General Public License for more details.
378+ *
379+ * You should have received a copy of the GNU General Public License
380+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
381+ *
382+ * Authored by: ALan Griffiths <alan.griffiths@canonical.com>
383+ */
384+
385+#include "mir/shell/generic_shell.h"
386+#include "mir/shell/window_manager.h"
387+
388+#include "mir/events/event_builders.h"
389+#include "mir/scene/session.h"
390+#include "mir/scene/null_session_listener.h"
391+#include "mir/scene/surface_creation_parameters.h"
392+
393+#include "src/server/scene/default_session_container.h"
394+#include "src/server/scene/session_event_sink.h"
395+#include "src/server/scene/session_manager.h"
396+
397+#include "mir_test_doubles/mock_surface_coordinator.h"
398+#include "mir_test_doubles/mock_session_listener.h"
399+#include "mir_test_doubles/mock_surface.h"
400+#include "mir_test_doubles/null_snapshot_strategy.h"
401+#include "mir_test_doubles/null_prompt_session_manager.h"
402+#include "mir_test_doubles/stub_input_targeter.h"
403+
404+#include "mir_test/fake_shared.h"
405+
406+#include <gmock/gmock.h>
407+#include <gtest/gtest.h>
408+
409+namespace mf = mir::frontend;
410+namespace mi = mir::input;
411+namespace ms = mir::scene;
412+namespace msh = mir::shell;
413+namespace geom = mir::geometry;
414+
415+namespace mt = mir::test;
416+namespace mtd = mir::test::doubles;
417+using namespace ::testing;
418+
419+namespace
420+{
421+MATCHER_P(WeakPtrTo, p, "")
422+{
423+ return !arg.owner_before(p) && !p.owner_before(arg);
424+}
425+
426+struct MockSessionContainer : public ms::SessionContainer
427+{
428+ MOCK_METHOD1(insert_session, void(std::shared_ptr<ms::Session> const&));
429+ MOCK_METHOD1(remove_session, void(std::shared_ptr<ms::Session> const&));
430+ MOCK_CONST_METHOD1(successor_of, std::shared_ptr<ms::Session>(std::shared_ptr<ms::Session> const&));
431+ MOCK_CONST_METHOD1(for_each, void(std::function<void(std::shared_ptr<ms::Session> const&)>));
432+ MOCK_METHOD0(lock, void());
433+ MOCK_METHOD0(unlock, void());
434+ ~MockSessionContainer() noexcept {}
435+};
436+
437+struct MockSessionEventSink : public ms::SessionEventSink
438+{
439+ MOCK_METHOD1(handle_focus_change, void(std::shared_ptr<ms::Session> const& session));
440+ MOCK_METHOD0(handle_no_focus, void());
441+ MOCK_METHOD1(handle_session_stopping, void(std::shared_ptr<ms::Session> const& session));
442+};
443+
444+struct MockSessionManager : ms::SessionManager
445+{
446+ using ms::SessionManager::SessionManager;
447+
448+ MOCK_METHOD1(set_focus_to, void (std::shared_ptr<ms::Session> const& focus));
449+
450+ void unmocked_set_focus_to(std::shared_ptr<ms::Session> const& focus)
451+ { ms::SessionManager::set_focus_to(focus); }
452+};
453+
454+struct MockWindowManager : msh::WindowManager
455+{
456+ MockWindowManager()
457+ {
458+ ON_CALL(*this, add_surface(_,_,_)).WillByDefault(Invoke(
459+ [](std::shared_ptr<ms::Session> const& session,
460+ ms::SurfaceCreationParameters const& params,
461+ std::function<mf::SurfaceId(std::shared_ptr<ms::Session> const& session, ms::SurfaceCreationParameters const& params)> const& build)
462+ { return build(session, params); }));
463+ }
464+
465+ MOCK_METHOD1(add_session, void (std::shared_ptr<ms::Session> const&));
466+ MOCK_METHOD1(remove_session, void (std::shared_ptr<ms::Session> const&));
467+
468+ MOCK_METHOD3(add_surface, mf::SurfaceId(
469+ std::shared_ptr<ms::Session> const& session,
470+ ms::SurfaceCreationParameters const& params,
471+ std::function<mf::SurfaceId(std::shared_ptr<ms::Session> const& session, ms::SurfaceCreationParameters const& params)> const& build));
472+
473+ MOCK_METHOD2(remove_surface, void(std::shared_ptr<ms::Session> const&, std::weak_ptr<ms::Surface> const&));
474+
475+ MOCK_METHOD1(add_display, void(geom::Rectangle const&));
476+ MOCK_METHOD1(remove_display, void(geom::Rectangle const&));
477+
478+ MOCK_METHOD1(handle_key_event, bool(MirKeyInputEvent const*));
479+ MOCK_METHOD1(handle_touch_event, bool(MirTouchInputEvent const*));
480+ MOCK_METHOD1(handle_pointer_event, bool(MirPointerInputEvent const*));
481+
482+ MOCK_METHOD2(handle_set_state, int(std::shared_ptr<ms::Surface> const&, MirSurfaceState value));
483+};
484+
485+using NiceMockWindowManager = NiceMock<MockWindowManager>;
486+
487+struct GenericShell : Test
488+{
489+ NiceMock<mtd::MockSurface> mock_surface;
490+ NiceMock<mtd::MockSurfaceCoordinator> surface_coordinator;
491+ NiceMock<MockSessionContainer> session_container;
492+ NiceMock<MockSessionEventSink> session_event_sink;
493+ NiceMock<mtd::MockSessionListener> session_listener;
494+
495+ NiceMock<MockSessionManager> session_manager{
496+ mt::fake_shared(surface_coordinator),
497+ mt::fake_shared(session_container),
498+ std::make_shared<mtd::NullSnapshotStrategy>(),
499+ mt::fake_shared(session_event_sink),
500+ mt::fake_shared(session_listener)};
501+
502+ mtd::StubInputTargeter input_targeter;
503+ std::shared_ptr<MockWindowManager> wm;
504+
505+ msh::GenericShell shell{
506+ mt::fake_shared(input_targeter),
507+ mt::fake_shared(surface_coordinator),
508+ mt::fake_shared(session_manager),
509+ std::make_shared<mtd::NullPromptSessionManager>(),
510+ [this](msh::FocusController*) { return wm = std::make_shared<NiceMockWindowManager>(); }};
511+
512+ void SetUp() override
513+ {
514+ ON_CALL(session_container, successor_of(_)).WillByDefault(Return((std::shared_ptr<ms::Session>())));
515+ ON_CALL(session_manager, set_focus_to(_)).
516+ WillByDefault(Invoke(&session_manager, &MockSessionManager::unmocked_set_focus_to));
517+ ON_CALL(surface_coordinator, add_surface(_,_))
518+ .WillByDefault(Return(mt::fake_shared(mock_surface)));
519+ }
520+};
521+}
522+
523+TEST_F(GenericShell, open_session_adds_session_to_window_manager)
524+{
525+ std::shared_ptr<ms::Session> new_session;
526+
527+ InSequence s;
528+ EXPECT_CALL(session_container, insert_session(_)).Times(1);
529+ EXPECT_CALL(*wm, add_session(_)).WillOnce(SaveArg<0>(&new_session));
530+
531+ auto session = shell.open_session(__LINE__, "Visual Basic Studio", std::shared_ptr<mf::EventSink>());
532+ EXPECT_EQ(session, new_session);
533+}
534+
535+TEST_F(GenericShell, close_session_removes_session_from_window_manager)
536+{
537+ std::shared_ptr<ms::Session> old_session;
538+
539+ InSequence s;
540+ EXPECT_CALL(session_container, insert_session(_));
541+ EXPECT_CALL(*wm, remove_session(_)).WillOnce(SaveArg<0>(&old_session));
542+
543+ auto session = shell.open_session(__LINE__, "XPlane", std::shared_ptr<mf::EventSink>());
544+ shell.close_session(session);
545+ EXPECT_EQ(session, old_session);
546+}
547+
548+TEST_F(GenericShell, close_session_notifies_session_event_sink)
549+{
550+ auto session = shell.open_session(__LINE__, "XPlane", std::shared_ptr<mf::EventSink>());
551+ auto session1 = shell.open_session(__LINE__, "Bla", std::shared_ptr<mf::EventSink>());
552+
553+ InSequence s;
554+ EXPECT_CALL(session_event_sink, handle_session_stopping(session1));
555+ EXPECT_CALL(session_event_sink, handle_session_stopping(session));
556+
557+ shell.close_session(session1);
558+ shell.close_session(session);
559+}
560+
561+TEST_F(GenericShell, create_surface_provides_create_parameters_to_window_manager)
562+{
563+ std::shared_ptr<ms::Session> session =
564+ shell.open_session(__LINE__, "XPlane", std::shared_ptr<mf::EventSink>());
565+
566+ auto params = ms::a_surface();
567+
568+ EXPECT_CALL(*wm, add_surface(session, Ref(params), _));
569+
570+ shell.create_surface(session, params);
571+}
572+
573+TEST_F(GenericShell, create_surface_allows_window_manager_to_set_create_parameters)
574+{
575+ std::shared_ptr<ms::Session> const session =
576+ shell.open_session(__LINE__, "XPlane", std::shared_ptr<mf::EventSink>());
577+
578+ auto params = ms::a_surface();
579+ auto placed_params = params;
580+ placed_params.size.width = geom::Width{100};
581+
582+ EXPECT_CALL(surface_coordinator, add_surface(placed_params, _));
583+
584+ EXPECT_CALL(*wm, add_surface(session, Ref(params), _)).WillOnce(Invoke(
585+ [&](std::shared_ptr<ms::Session> const& session,
586+ ms::SurfaceCreationParameters const&,
587+ std::function<mf::SurfaceId(std::shared_ptr<ms::Session> const& session, ms::SurfaceCreationParameters const&)> const& build)
588+ { return build(session, placed_params); }));
589+
590+ shell.create_surface(session, params);
591+}
592+
593+TEST_F(GenericShell, destroy_surface_removes_surface_from_window_manager)
594+{
595+ auto const params = ms::a_surface();
596+ std::shared_ptr<ms::Session> const session =
597+ shell.open_session(__LINE__, "XPlane", std::shared_ptr<mf::EventSink>());
598+
599+ auto const surface_id = shell.create_surface(session, params);
600+ auto const surface = session->surface(surface_id);
601+
602+ EXPECT_CALL(*wm, remove_surface(session, WeakPtrTo(surface)));
603+
604+ shell.destroy_surface(session, surface_id);
605+}
606+
607+TEST_F(GenericShell, add_display_adds_display_to_window_manager)
608+{
609+ geom::Rectangle const arbitrary_area{{0,0}, {__LINE__,__LINE__}};
610+
611+ EXPECT_CALL(*wm, add_display(arbitrary_area));
612+
613+ shell.add_display(arbitrary_area);
614+}
615+
616+TEST_F(GenericShell, remove_display_adds_display_to_window_manager)
617+{
618+ geom::Rectangle const arbitrary_area{{0,0}, {__LINE__,__LINE__}};
619+
620+ EXPECT_CALL(*wm, remove_display(arbitrary_area));
621+
622+ shell.remove_display(arbitrary_area);
623+}
624+
625+TEST_F(GenericShell, key_input_events_are_handled_by_window_manager)
626+{
627+ int64_t const timestamp{0};
628+ MirKeyInputEventAction const action{mir_key_input_event_action_down};
629+ xkb_keysym_t const key_code{0};
630+ int const scan_code{0};
631+ MirInputEventModifiers const modifiers{mir_input_event_modifier_none};
632+
633+ auto const event = mir::events::make_event(
634+ mir_input_event_type_key,
635+ timestamp,
636+ action,
637+ key_code,
638+ scan_code,
639+ modifiers);
640+
641+ EXPECT_CALL(*wm, handle_key_event(_))
642+ .WillOnce(Return(false))
643+ .WillOnce(Return(true));
644+
645+ EXPECT_FALSE(shell.handle(*event));
646+ EXPECT_TRUE(shell.handle(*event));
647+}
648+
649+TEST_F(GenericShell, touch_input_events_are_handled_by_window_manager)
650+{
651+ int64_t const timestamp{0};
652+ MirInputEventModifiers const modifiers{mir_input_event_modifier_none};
653+
654+ auto const event = mir::events::make_event(
655+ mir_input_event_type_touch,
656+ timestamp,
657+ modifiers);
658+
659+ EXPECT_CALL(*wm, handle_touch_event(_))
660+ .WillOnce(Return(false))
661+ .WillOnce(Return(true));
662+
663+ EXPECT_FALSE(shell.handle(*event));
664+ EXPECT_TRUE(shell.handle(*event));
665+}
666+
667+TEST_F(GenericShell, pointer_input_events_are_handled_by_window_manager)
668+{
669+ int64_t const timestamp{0};
670+ MirInputEventModifiers const modifiers{mir_input_event_modifier_none};
671+ MirPointerInputEventAction const action{mir_pointer_input_event_action_button_down};
672+ std::vector<MirPointerInputEventButton> const buttons_pressed{mir_pointer_input_button_primary};
673+ float const x_axis_value{0.0};
674+ float const y_axis_value{0.0};
675+ float const hscroll_value{0.0};
676+ float const vscroll_value{0.0};
677+
678+ auto const event = mir::events::make_event(
679+ mir_input_event_type_pointer,
680+ timestamp,
681+ modifiers,
682+ action,
683+ buttons_pressed,
684+ x_axis_value,
685+ y_axis_value,
686+ hscroll_value,
687+ vscroll_value);
688+
689+ EXPECT_CALL(*wm, handle_pointer_event(_))
690+ .WillOnce(Return(false))
691+ .WillOnce(Return(true));
692+
693+ EXPECT_FALSE(shell.handle(*event));
694+ EXPECT_TRUE(shell.handle(*event));
695+}
696+
697+TEST_F(GenericShell, setting_surface_state_is_handled_by_window_manager)
698+{
699+ std::shared_ptr<ms::Session> const session =
700+ shell.open_session(__LINE__, "XPlane", std::shared_ptr<mf::EventSink>());
701+
702+ auto const surface_id = shell.create_surface(session, ms::a_surface());
703+ auto const surface = session->surface(surface_id);
704+
705+ MirSurfaceState const state{mir_surface_state_fullscreen};
706+
707+ EXPECT_CALL(*wm, handle_set_state(surface, state))
708+ .WillOnce(Return(mir_surface_state_maximized));
709+
710+ EXPECT_CALL(mock_surface, configure(mir_surface_attrib_state, mir_surface_state_maximized));
711+
712+ shell.set_surface_attribute(session, surface, mir_surface_attrib_state, mir_surface_state_fullscreen);
713+}
714+
715+TEST_F(GenericShell, as_focus_controller_set_focus_to_notifies_session_event_sink)
716+{
717+ auto session = shell.open_session(__LINE__, "XPlane", std::shared_ptr<mf::EventSink>());
718+ auto session1 = shell.open_session(__LINE__, "Bla", std::shared_ptr<mf::EventSink>());
719+
720+ InSequence s;
721+ EXPECT_CALL(session_event_sink, handle_focus_change(session1));
722+ EXPECT_CALL(session_event_sink, handle_focus_change(session));
723+ EXPECT_CALL(session_event_sink, handle_no_focus());
724+
725+ msh::FocusController& focus_controller = shell;
726+ focus_controller.set_focus_to(session1, {});
727+ focus_controller.set_focus_to(session, {});
728+ focus_controller.set_focus_to({}, {});
729+}
730+
731+TEST_F(GenericShell, as_focus_controller_focus_next_notifies_session_event_sink)
732+{
733+ msh::FocusController& focus_controller = shell;
734+ auto session = shell.open_session(__LINE__, "XPlane", std::shared_ptr<mf::EventSink>());
735+ auto session1 = shell.open_session(__LINE__, "Bla", std::shared_ptr<mf::EventSink>());
736+ focus_controller.set_focus_to(session, {});
737+
738+ EXPECT_CALL(session_container, successor_of(session)).
739+ WillOnce(Return(session1));
740+
741+ EXPECT_CALL(session_event_sink, handle_focus_change(session1));
742+
743+ focus_controller.focus_next();
744+}
745+
746+TEST_F(GenericShell, as_focus_controller_focused_session_follows_focus)
747+{
748+ auto session = shell.open_session(__LINE__, "XPlane", std::shared_ptr<mf::EventSink>());
749+ auto session1 = shell.open_session(__LINE__, "Bla", std::shared_ptr<mf::EventSink>());
750+ EXPECT_CALL(session_container, successor_of(session1)).
751+ WillOnce(Return(session));
752+
753+ msh::FocusController& focus_controller = shell;
754+
755+ focus_controller.set_focus_to(session, {});
756+ EXPECT_THAT(focus_controller.focused_session(), Eq(session));
757+ focus_controller.set_focus_to(session1, {});
758+ EXPECT_THAT(focus_controller.focused_session(), Eq(session1));
759+ focus_controller.focus_next();
760+ EXPECT_THAT(focus_controller.focused_session(), Eq(session));
761+}
762+
763+TEST_F(GenericShell, as_focus_controller_focused_surface_follows_focus)
764+{
765+ auto const session0 = shell.open_session(__LINE__, "XPlane", std::shared_ptr<mf::EventSink>());
766+ auto const session1 = shell.open_session(__LINE__, "Bla", std::shared_ptr<mf::EventSink>());
767+ NiceMock<mtd::MockSurface> dummy_surface;
768+
769+ EXPECT_CALL(surface_coordinator, add_surface(_,_)).Times(AnyNumber())
770+ .WillOnce(Return(mt::fake_shared(dummy_surface)))
771+ .WillOnce(Return(mt::fake_shared(mock_surface)));
772+ EXPECT_CALL(session_container, successor_of(session1)).
773+ WillOnce(Return(session0));
774+
775+
776+ auto const surface0_id = shell.create_surface(session0, ms::a_surface());
777+ auto const surface0 = session0->surface(surface0_id);
778+ auto const surface1_id = shell.create_surface(session1, ms::a_surface());
779+ auto const surface1 = session1->surface(surface1_id);
780+
781+ msh::FocusController& focus_controller = shell;
782+
783+ focus_controller.set_focus_to(session0, surface0);
784+ EXPECT_THAT(focus_controller.focused_surface(), Eq(surface0));
785+ focus_controller.set_focus_to(session1, surface1);
786+ EXPECT_THAT(focus_controller.focused_surface(), Eq(surface1));
787+ focus_controller.focus_next();
788+ EXPECT_THAT(focus_controller.focused_surface(), Eq(surface0));
789+
790+ shell.destroy_surface(session0, surface0_id);
791+ shell.destroy_surface(session1, surface1_id);
792+ shell.close_session(session1);
793+ shell.close_session(session0);
794+}
795+
796+TEST_F(GenericShell, as_focus_controller_delegates_surface_at_to_surface_coordinator)
797+{
798+ auto const surface = mt::fake_shared(mock_surface);
799+ geom::Point const cursor{__LINE__, __LINE__};
800+
801+ EXPECT_CALL(surface_coordinator, surface_at(cursor)).
802+ WillOnce(Return(surface));
803+
804+ msh::FocusController& focus_controller = shell;
805+
806+ EXPECT_THAT(focus_controller.surface_at(cursor), Eq(surface));
807+}
808+
809+namespace mir
810+{
811+namespace scene
812+{
813+// The next test is easier to write if we can compare std::weak_ptr<ms::Surface>
814+inline bool operator==(
815+ std::weak_ptr<ms::Surface> const& lhs,
816+ std::weak_ptr<ms::Surface> const& rhs)
817+{
818+ return !lhs.owner_before(rhs) && !rhs.owner_before(lhs);
819+}
820+}
821+}
822+
823+TEST_F(GenericShell, as_focus_controller_delegates_raise_to_surface_coordinator)
824+{
825+ msh::SurfaceSet const surfaces{mt::fake_shared(mock_surface)};
826+
827+ EXPECT_CALL(surface_coordinator, raise(surfaces));
828+
829+ msh::FocusController& focus_controller = shell;
830+
831+ focus_controller.raise(surfaces);
832+}

Subscribers

People subscribed via source and target branches