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
=== modified file 'examples/CMakeLists.txt'
--- examples/CMakeLists.txt 2015-03-05 04:24:05 +0000
+++ examples/CMakeLists.txt 2015-03-05 13:54:59 +0000
@@ -17,7 +17,6 @@
1717
18add_library(exampleserverconfig STATIC18add_library(exampleserverconfig STATIC
19 server_example_canonical_window_manager.cpp19 server_example_canonical_window_manager.cpp
20 server_example_generic_shell.cpp
21 server_example_display_configuration_policy.cpp20 server_example_display_configuration_policy.cpp
22 server_example_input_event_filter.cpp21 server_example_input_event_filter.cpp
23 server_example_log_options.cpp22 server_example_log_options.cpp
2423
=== modified file 'examples/server_example_basic_window_manager.h'
--- examples/server_example_basic_window_manager.h 2015-03-05 05:47:28 +0000
+++ examples/server_example_basic_window_manager.h 2015-03-05 13:54:59 +0000
@@ -19,11 +19,10 @@
19#ifndef MIR_EXAMPLE_BASIC_WINDOW_MANAGER_H_19#ifndef MIR_EXAMPLE_BASIC_WINDOW_MANAGER_H_
20#define MIR_EXAMPLE_BASIC_WINDOW_MANAGER_H_20#define MIR_EXAMPLE_BASIC_WINDOW_MANAGER_H_
2121
22#include "server_example_generic_shell.h"
23
24#include "mir/geometry/rectangles.h"22#include "mir/geometry/rectangles.h"
25#include "mir/scene/session.h"23#include "mir/scene/session.h"
26#include "mir/scene/surface_creation_parameters.h"24#include "mir/scene/surface_creation_parameters.h"
25#include "mir/shell/generic_shell.h"
2726
28#include <map>27#include <map>
29#include <mutex>28#include <mutex>
@@ -105,7 +104,7 @@
105///104///
106/// \tparam SurfaceInfo must be constructable from (std::shared_ptr<ms::Session>, std::shared_ptr<ms::Surface>)105/// \tparam SurfaceInfo must be constructable from (std::shared_ptr<ms::Session>, std::shared_ptr<ms::Surface>)
107template<typename WindowManagementPolicy, typename SessionInfo, typename SurfaceInfo>106template<typename WindowManagementPolicy, typename SessionInfo, typename SurfaceInfo>
108class BasicWindowManager : public WindowManager,107class BasicWindowManager : public shell::WindowManager,
109 private BasicWindowManagerTools<SessionInfo, SurfaceInfo>108 private BasicWindowManagerTools<SessionInfo, SurfaceInfo>
110{109{
111public:110public:
@@ -148,8 +147,8 @@
148 }147 }
149148
150 void remove_surface(149 void remove_surface(
151 std::weak_ptr<scene::Surface> const& surface,150 std::shared_ptr<scene::Session> const& session,
152 std::shared_ptr<scene::Session> const& session) override151 std::weak_ptr<scene::Surface> const& surface) override
153 {152 {
154 std::lock_guard<decltype(mutex)> lock(mutex);153 std::lock_guard<decltype(mutex)> lock(mutex);
155 policy.handle_delete_surface(session, surface);154 policy.handle_delete_surface(session, surface);
156155
=== modified file 'examples/server_example_window_management.cpp'
--- examples/server_example_window_management.cpp 2015-03-05 04:24:05 +0000
+++ examples/server_example_window_management.cpp 2015-03-05 13:54:59 +0000
@@ -127,25 +127,25 @@
127127
128 auto const selection = options->get<std::string>(wm_option);128 auto const selection = options->get<std::string>(wm_option);
129129
130 std::function<std::shared_ptr<WindowManager>(msh::FocusController* focus_controller)> wm_builder;130 std::function<std::shared_ptr<msh::WindowManager>(msh::FocusController* focus_controller)> wm_builder;
131131
132 if (selection == wm_tiling)132 if (selection == wm_tiling)
133 {133 {
134 wm_builder = [&server](msh::FocusController* focus_controller) -> std::shared_ptr<WindowManager>134 wm_builder = [&server](msh::FocusController* focus_controller) -> std::shared_ptr<msh::WindowManager>
135 {135 {
136 return std::make_shared<TilingWindowManager>(focus_controller);136 return std::make_shared<TilingWindowManager>(focus_controller);
137 };137 };
138 }138 }
139 else if (selection == wm_fullscreen)139 else if (selection == wm_fullscreen)
140 {140 {
141 wm_builder = [&server](msh::FocusController* focus_controller) -> std::shared_ptr<WindowManager>141 wm_builder = [&server](msh::FocusController* focus_controller) -> std::shared_ptr<msh::WindowManager>
142 {142 {
143 return std::make_shared<FullscreenWindowManager>(focus_controller, server.the_shell_display_layout());143 return std::make_shared<FullscreenWindowManager>(focus_controller, server.the_shell_display_layout());
144 };144 };
145 }145 }
146 else if (selection == wm_canonical)146 else if (selection == wm_canonical)
147 {147 {
148 wm_builder = [&server](msh::FocusController* focus_controller) -> std::shared_ptr<WindowManager>148 wm_builder = [&server](msh::FocusController* focus_controller) -> std::shared_ptr<msh::WindowManager>
149 {149 {
150 return std::make_shared<CanonicalWindowManager>(focus_controller);150 return std::make_shared<CanonicalWindowManager>(focus_controller);
151 };151 };
@@ -154,7 +154,7 @@
154 throw mir::AbnormalExit("Unknown window manager: " + selection);154 throw mir::AbnormalExit("Unknown window manager: " + selection);
155155
156156
157 auto tmp = std::make_shared<GenericShell>(157 auto tmp = std::make_shared<msh::GenericShell>(
158 server.the_input_targeter(),158 server.the_input_targeter(),
159 server.the_surface_coordinator(),159 server.the_surface_coordinator(),
160 server.the_session_coordinator(),160 server.the_session_coordinator(),
161161
=== renamed file 'examples/server_example_generic_shell.h' => 'include/server/mir/shell/generic_shell.h'
--- examples/server_example_generic_shell.h 2015-03-05 05:47:28 +0000
+++ include/server/mir/shell/generic_shell.h 2015-03-05 13:54:59 +0000
@@ -16,28 +16,25 @@
16 * Authored By: Alan Griffiths <alan@octopull.co.uk>16 * Authored By: Alan Griffiths <alan@octopull.co.uk>
17 */17 */
1818
19#ifndef MIR_EXAMPLE_GENERIC_SHELL_H_19#ifndef MIR_SHELL_GENERIC_SHELL_H_
20#define MIR_EXAMPLE_GENERIC_SHELL_H_20#define MIR_SHELL_GENERIC_SHELL_H_
21
22#include "server_example_window_manager.h"
2321
24#include "mir/shell/abstract_shell.h"22#include "mir/shell/abstract_shell.h"
2523#include "mir/shell/window_manager.h"
26///\example server_example_generic_shell.h
27/// A generic shell that supports a window manager
2824
29namespace mir25namespace mir
30{26{
31namespace geometry { class Point; }27namespace geometry { class Point; }
3228
33namespace examples29namespace shell
34{30{
35class GenericShell : public virtual shell::Shell, public virtual shell::FocusController,31/// A generic shell that supports a window manager
36 private shell::AbstractShell32class GenericShell : public virtual Shell, public virtual FocusController,
33 private AbstractShell
37{34{
38public:35public:
39 GenericShell(36 GenericShell(
40 std::shared_ptr<shell::InputTargeter> const& input_targeter,37 std::shared_ptr<InputTargeter> const& input_targeter,
41 std::shared_ptr<scene::SurfaceCoordinator> const& surface_coordinator,38 std::shared_ptr<scene::SurfaceCoordinator> const& surface_coordinator,
42 std::shared_ptr<scene::SessionCoordinator> const& session_coordinator,39 std::shared_ptr<scene::SessionCoordinator> const& session_coordinator,
43 std::shared_ptr<scene::PromptSessionManager> const& prompt_session_manager,40 std::shared_ptr<scene::PromptSessionManager> const& prompt_session_manager,
@@ -62,13 +59,13 @@
62 MirSurfaceAttrib attrib,59 MirSurfaceAttrib attrib,
63 int value) override;60 int value) override;
6461
65private:
66 void add_display(geometry::Rectangle const& area) override;62 void add_display(geometry::Rectangle const& area) override;
67 void remove_display(geometry::Rectangle const& area) override;63 void remove_display(geometry::Rectangle const& area) override;
6864
65private:
69 std::shared_ptr<WindowManager> const window_manager;66 std::shared_ptr<WindowManager> const window_manager;
70};67};
71}68}
72}69}
7370
74#endif /* MIR_EXAMPLE_GENERIC_SHELL_H_ */71#endif /* MIR_SHELL_GENERIC_SHELL_H_ */
7572
=== renamed file 'examples/server_example_window_manager.h' => 'include/server/mir/shell/window_manager.h'
--- examples/server_example_window_manager.h 2015-02-27 15:23:05 +0000
+++ include/server/mir/shell/window_manager.h 2015-03-05 13:54:59 +0000
@@ -16,8 +16,8 @@
16 * Authored By: Alan Griffiths <alan@octopull.co.uk>16 * Authored By: Alan Griffiths <alan@octopull.co.uk>
17 */17 */
1818
19#ifndef MIR_EXAMPLE_WINDOW_MANAGER_H_19#ifndef MIR_SHELL_WINDOW_MANAGER_H_
20#define MIR_EXAMPLE_WINDOW_MANAGER_H_20#define MIR_SHELL_WINDOW_MANAGER_H_
2121
22#include "mir/frontend/surface_id.h"22#include "mir/frontend/surface_id.h"
23#include "mir_toolkit/common.h"23#include "mir_toolkit/common.h"
@@ -25,15 +25,13 @@
2525
26#include <memory>26#include <memory>
2727
28///\example server_example_window_manager.h
29/// A window manager interface
30
31namespace mir28namespace mir
32{29{
33namespace geometry { class Rectangle; }30namespace geometry { class Rectangle; }
34namespace scene { class Session; class Surface; class SurfaceCreationParameters; }31namespace scene { class Session; class Surface; class SurfaceCreationParameters; }
35namespace examples32namespace shell
36{33{
34/// interface to provide window management logic
37class WindowManager35class WindowManager
38{36{
39public:37public:
@@ -47,8 +45,8 @@
47 std::function<frontend::SurfaceId(std::shared_ptr<scene::Session> const& session, scene::SurfaceCreationParameters const& params)> const& build) = 0;45 std::function<frontend::SurfaceId(std::shared_ptr<scene::Session> const& session, scene::SurfaceCreationParameters const& params)> const& build) = 0;
4846
49 virtual void remove_surface(47 virtual void remove_surface(
50 std::weak_ptr<scene::Surface> const& surface,48 std::shared_ptr<scene::Session> const& session,
51 std::shared_ptr<scene::Session> const& session) = 0;49 std::weak_ptr<scene::Surface> const& surface) = 0;
5250
53 virtual void add_display(geometry::Rectangle const& area) = 0;51 virtual void add_display(geometry::Rectangle const& area) = 0;
5452
@@ -70,4 +68,4 @@
70}68}
71}69}
7270
73#endif /* MIR_EXAMPLE_WINDOW_MANAGER_H_ */71#endif /* MIR_SHELL_WINDOW_MANAGER_H_ */
7472
=== modified file 'src/server/shell/CMakeLists.txt'
--- src/server/shell/CMakeLists.txt 2015-02-18 05:27:28 +0000
+++ src/server/shell/CMakeLists.txt 2015-03-05 13:54:59 +0000
@@ -4,6 +4,7 @@
4 abstract_shell.cpp4 abstract_shell.cpp
5 default_placement_strategy.cpp5 default_placement_strategy.cpp
6 frontend_shell.cpp6 frontend_shell.cpp
7 generic_shell.cpp
7 graphics_display_layout.cpp8 graphics_display_layout.cpp
8 default_configuration.cpp9 default_configuration.cpp
9 default_shell.cpp10 default_shell.cpp
1011
=== renamed file 'examples/server_example_generic_shell.cpp' => 'src/server/shell/generic_shell.cpp'
--- examples/server_example_generic_shell.cpp 2015-03-05 05:47:28 +0000
+++ src/server/shell/generic_shell.cpp 2015-03-05 13:54:59 +0000
@@ -16,66 +16,62 @@
16 * Authored By: Alan Griffiths <alan@octopull.co.uk>16 * Authored By: Alan Griffiths <alan@octopull.co.uk>
17 */17 */
1818
19#include "server_example_generic_shell.h"19#include "mir/shell/generic_shell.h"
2020
21#include "mir/geometry/point.h"21#include "mir/geometry/point.h"
22#include "mir/scene/session.h"22#include "mir/scene/session.h"
23#include "mir/scene/surface_coordinator.h"23#include "mir/scene/surface_coordinator.h"
24#include "mir/scene/surface_creation_parameters.h"24#include "mir/scene/surface_creation_parameters.h"
2525
26namespace me = mir::examples;
27namespace mf = mir::frontend;26namespace mf = mir::frontend;
28namespace ms = mir::scene;27namespace ms = mir::scene;
29namespace msh = mir::shell;28namespace msh = mir::shell;
3029
31///\example server_example_generic_shell.cpp30msh::GenericShell::GenericShell(
32/// A shell that accepts a WindowManager31 std::shared_ptr<InputTargeter> const& input_targeter,
33
34me::GenericShell::GenericShell(
35 std::shared_ptr<msh::InputTargeter> const& input_targeter,
36 std::shared_ptr<ms::SurfaceCoordinator> const& surface_coordinator,32 std::shared_ptr<ms::SurfaceCoordinator> const& surface_coordinator,
37 std::shared_ptr<ms::SessionCoordinator> const& session_coordinator,33 std::shared_ptr<ms::SessionCoordinator> const& session_coordinator,
38 std::shared_ptr<ms::PromptSessionManager> const& prompt_session_manager,34 std::shared_ptr<ms::PromptSessionManager> const& prompt_session_manager,
39 std::function<std::shared_ptr<WindowManager>(FocusController* focus_controller)> const& wm_builder) :35 std::function<std::shared_ptr<shell::WindowManager>(FocusController* focus_controller)> const& wm_builder) :
40 AbstractShell(input_targeter, surface_coordinator, session_coordinator, prompt_session_manager),36 AbstractShell(input_targeter, surface_coordinator, session_coordinator, prompt_session_manager),
41 window_manager(wm_builder(this))37 window_manager(wm_builder(this))
42{38{
43}39}
4440
45std::shared_ptr<ms::Session> me::GenericShell::open_session(41std::shared_ptr<ms::Session> msh::GenericShell::open_session(
46 pid_t client_pid,42 pid_t client_pid,
47 std::string const& name,43 std::string const& name,
48 std::shared_ptr<mf::EventSink> const& sink)44 std::shared_ptr<mf::EventSink> const& sink)
49{45{
50 auto const result = msh::AbstractShell::open_session(client_pid, name, sink);46 auto const result = AbstractShell::open_session(client_pid, name, sink);
51 window_manager->add_session(result);47 window_manager->add_session(result);
52 return result;48 return result;
53}49}
5450
55void me::GenericShell::close_session(std::shared_ptr<ms::Session> const& session)51void msh::GenericShell::close_session(std::shared_ptr<ms::Session> const& session)
56{52{
57 window_manager->remove_session(session);53 window_manager->remove_session(session);
58 msh::AbstractShell::close_session(session);54 AbstractShell::close_session(session);
59}55}
6056
61auto me::GenericShell::create_surface(std::shared_ptr<ms::Session> const& session, ms::SurfaceCreationParameters const& params)57auto msh::GenericShell::create_surface(std::shared_ptr<ms::Session> const& session, ms::SurfaceCreationParameters const& params)
62-> mf::SurfaceId58-> mf::SurfaceId
63{59{
64 auto const build = [this](std::shared_ptr<ms::Session> const& session, ms::SurfaceCreationParameters const& placed_params)60 auto const build = [this](std::shared_ptr<ms::Session> const& session, ms::SurfaceCreationParameters const& placed_params)
65 {61 {
66 return msh::AbstractShell::create_surface(session, placed_params);62 return AbstractShell::create_surface(session, placed_params);
67 };63 };
6864
69 return window_manager->add_surface(session, params, build);65 return window_manager->add_surface(session, params, build);
70}66}
7167
72void me::GenericShell::destroy_surface(std::shared_ptr<ms::Session> const& session, mf::SurfaceId surface)68void msh::GenericShell::destroy_surface(std::shared_ptr<ms::Session> const& session, mf::SurfaceId surface)
73{69{
74 window_manager->remove_surface(session->surface(surface), session);70 window_manager->remove_surface(session, session->surface(surface));
75 msh::AbstractShell::destroy_surface(session, surface);71 AbstractShell::destroy_surface(session, surface);
76}72}
7773
78bool me::GenericShell::handle(MirEvent const& event)74bool msh::GenericShell::handle(MirEvent const& event)
79{75{
80 if (mir_event_get_type(&event) != mir_event_type_input)76 if (mir_event_get_type(&event) != mir_event_type_input)
81 return false;77 return false;
@@ -97,7 +93,7 @@
97 return false;93 return false;
98}94}
9995
100int me::GenericShell::set_surface_attribute(96int msh::GenericShell::set_surface_attribute(
101 std::shared_ptr<ms::Session> const& session,97 std::shared_ptr<ms::Session> const& session,
102 std::shared_ptr<ms::Surface> const& surface,98 std::shared_ptr<ms::Surface> const& surface,
103 MirSurfaceAttrib attrib,99 MirSurfaceAttrib attrib,
@@ -108,19 +104,19 @@
108 case mir_surface_attrib_state:104 case mir_surface_attrib_state:
109 {105 {
110 auto const state = window_manager->handle_set_state(surface, MirSurfaceState(value));106 auto const state = window_manager->handle_set_state(surface, MirSurfaceState(value));
111 return msh::AbstractShell::set_surface_attribute(session, surface, attrib, state);107 return AbstractShell::set_surface_attribute(session, surface, attrib, state);
112 }108 }
113 default:109 default:
114 return msh::AbstractShell::set_surface_attribute(session, surface, attrib, value);110 return AbstractShell::set_surface_attribute(session, surface, attrib, value);
115 }111 }
116}112}
117113
118void me::GenericShell::add_display(geometry::Rectangle const& area)114void msh::GenericShell::add_display(geometry::Rectangle const& area)
119{115{
120 window_manager->add_display(area);116 window_manager->add_display(area);
121}117}
122118
123void me::GenericShell::remove_display(geometry::Rectangle const& area)119void msh::GenericShell::remove_display(geometry::Rectangle const& area)
124{120{
125 window_manager->remove_display(area);121 window_manager->remove_display(area);
126}122}
127123
=== modified file 'src/server/symbols.map'
--- src/server/symbols.map 2015-03-05 05:47:28 +0000
+++ src/server/symbols.map 2015-03-05 13:54:59 +0000
@@ -392,6 +392,16 @@
392 mir::shell::FocusController::focused_session*;392 mir::shell::FocusController::focused_session*;
393 mir::shell::FocusController::operator*;393 mir::shell::FocusController::operator*;
394 mir::shell::FocusController::set_focus_to*;394 mir::shell::FocusController::set_focus_to*;
395 mir::shell::FocusController::surface_at*;
396 mir::shell::GenericShell::add_display*;
397 mir::shell::GenericShell::close_session*;
398 mir::shell::GenericShell::create_surface*;
399 mir::shell::GenericShell::destroy_surface*;
400 mir::shell::GenericShell::GenericShell*;
401 mir::shell::GenericShell::handle*;
402 mir::shell::GenericShell::open_session*;
403 mir::shell::GenericShell::remove_display*;
404 mir::shell::GenericShell::set_surface_attribute*;
395 mir::shell::HostLifecycleEventListener::?HostLifecycleEventListener*;405 mir::shell::HostLifecycleEventListener::?HostLifecycleEventListener*;
396 mir::shell::HostLifecycleEventListener::HostLifecycleEventListener*;406 mir::shell::HostLifecycleEventListener::HostLifecycleEventListener*;
397 mir::shell::HostLifecycleEventListener::lifecycle_event_occurred*;407 mir::shell::HostLifecycleEventListener::lifecycle_event_occurred*;
398408
=== modified file 'tests/unit-tests/scene/CMakeLists.txt'
--- tests/unit-tests/scene/CMakeLists.txt 2015-03-05 05:47:28 +0000
+++ tests/unit-tests/scene/CMakeLists.txt 2015-03-05 13:54:59 +0000
@@ -16,6 +16,7 @@
16 ${CMAKE_CURRENT_SOURCE_DIR}/test_surface_impl.cpp16 ${CMAKE_CURRENT_SOURCE_DIR}/test_surface_impl.cpp
17 ${CMAKE_CURRENT_SOURCE_DIR}/test_basic_surface.cpp17 ${CMAKE_CURRENT_SOURCE_DIR}/test_basic_surface.cpp
18 ${CMAKE_CURRENT_SOURCE_DIR}/test_default_shell.cpp18 ${CMAKE_CURRENT_SOURCE_DIR}/test_default_shell.cpp
19 ${CMAKE_CURRENT_SOURCE_DIR}/test_generic_shell.cpp
19 ${CMAKE_CURRENT_SOURCE_DIR}/test_surface_stack.cpp20 ${CMAKE_CURRENT_SOURCE_DIR}/test_surface_stack.cpp
20 ${CMAKE_CURRENT_SOURCE_DIR}/test_surface_controller.cpp21 ${CMAKE_CURRENT_SOURCE_DIR}/test_surface_controller.cpp
21 ${CMAKE_CURRENT_SOURCE_DIR}/test_legacy_scene_change_notification.cpp22 ${CMAKE_CURRENT_SOURCE_DIR}/test_legacy_scene_change_notification.cpp
2223
=== added file 'tests/unit-tests/scene/test_generic_shell.cpp'
--- tests/unit-tests/scene/test_generic_shell.cpp 1970-01-01 00:00:00 +0000
+++ tests/unit-tests/scene/test_generic_shell.cpp 2015-03-05 13:54:59 +0000
@@ -0,0 +1,466 @@
1/*
2 * Copyright © 2015 Canonical Ltd.
3 *
4 * This program is free software: you can redistribute it and/or modify
5 * it under the terms of the GNU General Public License version 3 as
6 * published by the Free Software Foundation.
7 *
8 * This program is distributed in the hope that it will be useful,
9 * but WITHOUT ANY WARRANTY; without even the implied warranty of
10 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 * GNU General Public License for more details.
12 *
13 * You should have received a copy of the GNU General Public License
14 * along with this program. If not, see <http://www.gnu.org/licenses/>.
15 *
16 * Authored by: ALan Griffiths <alan.griffiths@canonical.com>
17 */
18
19#include "mir/shell/generic_shell.h"
20#include "mir/shell/window_manager.h"
21
22#include "mir/events/event_builders.h"
23#include "mir/scene/session.h"
24#include "mir/scene/null_session_listener.h"
25#include "mir/scene/surface_creation_parameters.h"
26
27#include "src/server/scene/default_session_container.h"
28#include "src/server/scene/session_event_sink.h"
29#include "src/server/scene/session_manager.h"
30
31#include "mir_test_doubles/mock_surface_coordinator.h"
32#include "mir_test_doubles/mock_session_listener.h"
33#include "mir_test_doubles/mock_surface.h"
34#include "mir_test_doubles/null_snapshot_strategy.h"
35#include "mir_test_doubles/null_prompt_session_manager.h"
36#include "mir_test_doubles/stub_input_targeter.h"
37
38#include "mir_test/fake_shared.h"
39
40#include <gmock/gmock.h>
41#include <gtest/gtest.h>
42
43namespace mf = mir::frontend;
44namespace mi = mir::input;
45namespace ms = mir::scene;
46namespace msh = mir::shell;
47namespace geom = mir::geometry;
48
49namespace mt = mir::test;
50namespace mtd = mir::test::doubles;
51using namespace ::testing;
52
53namespace
54{
55MATCHER_P(WeakPtrTo, p, "")
56{
57 return !arg.owner_before(p) && !p.owner_before(arg);
58}
59
60struct MockSessionContainer : public ms::SessionContainer
61{
62 MOCK_METHOD1(insert_session, void(std::shared_ptr<ms::Session> const&));
63 MOCK_METHOD1(remove_session, void(std::shared_ptr<ms::Session> const&));
64 MOCK_CONST_METHOD1(successor_of, std::shared_ptr<ms::Session>(std::shared_ptr<ms::Session> const&));
65 MOCK_CONST_METHOD1(for_each, void(std::function<void(std::shared_ptr<ms::Session> const&)>));
66 MOCK_METHOD0(lock, void());
67 MOCK_METHOD0(unlock, void());
68 ~MockSessionContainer() noexcept {}
69};
70
71struct MockSessionEventSink : public ms::SessionEventSink
72{
73 MOCK_METHOD1(handle_focus_change, void(std::shared_ptr<ms::Session> const& session));
74 MOCK_METHOD0(handle_no_focus, void());
75 MOCK_METHOD1(handle_session_stopping, void(std::shared_ptr<ms::Session> const& session));
76};
77
78struct MockSessionManager : ms::SessionManager
79{
80 using ms::SessionManager::SessionManager;
81
82 MOCK_METHOD1(set_focus_to, void (std::shared_ptr<ms::Session> const& focus));
83
84 void unmocked_set_focus_to(std::shared_ptr<ms::Session> const& focus)
85 { ms::SessionManager::set_focus_to(focus); }
86};
87
88struct MockWindowManager : msh::WindowManager
89{
90 MockWindowManager()
91 {
92 ON_CALL(*this, add_surface(_,_,_)).WillByDefault(Invoke(
93 [](std::shared_ptr<ms::Session> const& session,
94 ms::SurfaceCreationParameters const& params,
95 std::function<mf::SurfaceId(std::shared_ptr<ms::Session> const& session, ms::SurfaceCreationParameters const& params)> const& build)
96 { return build(session, params); }));
97 }
98
99 MOCK_METHOD1(add_session, void (std::shared_ptr<ms::Session> const&));
100 MOCK_METHOD1(remove_session, void (std::shared_ptr<ms::Session> const&));
101
102 MOCK_METHOD3(add_surface, mf::SurfaceId(
103 std::shared_ptr<ms::Session> const& session,
104 ms::SurfaceCreationParameters const& params,
105 std::function<mf::SurfaceId(std::shared_ptr<ms::Session> const& session, ms::SurfaceCreationParameters const& params)> const& build));
106
107 MOCK_METHOD2(remove_surface, void(std::shared_ptr<ms::Session> const&, std::weak_ptr<ms::Surface> const&));
108
109 MOCK_METHOD1(add_display, void(geom::Rectangle const&));
110 MOCK_METHOD1(remove_display, void(geom::Rectangle const&));
111
112 MOCK_METHOD1(handle_key_event, bool(MirKeyInputEvent const*));
113 MOCK_METHOD1(handle_touch_event, bool(MirTouchInputEvent const*));
114 MOCK_METHOD1(handle_pointer_event, bool(MirPointerInputEvent const*));
115
116 MOCK_METHOD2(handle_set_state, int(std::shared_ptr<ms::Surface> const&, MirSurfaceState value));
117};
118
119using NiceMockWindowManager = NiceMock<MockWindowManager>;
120
121struct GenericShell : Test
122{
123 NiceMock<mtd::MockSurface> mock_surface;
124 NiceMock<mtd::MockSurfaceCoordinator> surface_coordinator;
125 NiceMock<MockSessionContainer> session_container;
126 NiceMock<MockSessionEventSink> session_event_sink;
127 NiceMock<mtd::MockSessionListener> session_listener;
128
129 NiceMock<MockSessionManager> session_manager{
130 mt::fake_shared(surface_coordinator),
131 mt::fake_shared(session_container),
132 std::make_shared<mtd::NullSnapshotStrategy>(),
133 mt::fake_shared(session_event_sink),
134 mt::fake_shared(session_listener)};
135
136 mtd::StubInputTargeter input_targeter;
137 std::shared_ptr<MockWindowManager> wm;
138
139 msh::GenericShell shell{
140 mt::fake_shared(input_targeter),
141 mt::fake_shared(surface_coordinator),
142 mt::fake_shared(session_manager),
143 std::make_shared<mtd::NullPromptSessionManager>(),
144 [this](msh::FocusController*) { return wm = std::make_shared<NiceMockWindowManager>(); }};
145
146 void SetUp() override
147 {
148 ON_CALL(session_container, successor_of(_)).WillByDefault(Return((std::shared_ptr<ms::Session>())));
149 ON_CALL(session_manager, set_focus_to(_)).
150 WillByDefault(Invoke(&session_manager, &MockSessionManager::unmocked_set_focus_to));
151 ON_CALL(surface_coordinator, add_surface(_,_))
152 .WillByDefault(Return(mt::fake_shared(mock_surface)));
153 }
154};
155}
156
157TEST_F(GenericShell, open_session_adds_session_to_window_manager)
158{
159 std::shared_ptr<ms::Session> new_session;
160
161 InSequence s;
162 EXPECT_CALL(session_container, insert_session(_)).Times(1);
163 EXPECT_CALL(*wm, add_session(_)).WillOnce(SaveArg<0>(&new_session));
164
165 auto session = shell.open_session(__LINE__, "Visual Basic Studio", std::shared_ptr<mf::EventSink>());
166 EXPECT_EQ(session, new_session);
167}
168
169TEST_F(GenericShell, close_session_removes_session_from_window_manager)
170{
171 std::shared_ptr<ms::Session> old_session;
172
173 InSequence s;
174 EXPECT_CALL(session_container, insert_session(_));
175 EXPECT_CALL(*wm, remove_session(_)).WillOnce(SaveArg<0>(&old_session));
176
177 auto session = shell.open_session(__LINE__, "XPlane", std::shared_ptr<mf::EventSink>());
178 shell.close_session(session);
179 EXPECT_EQ(session, old_session);
180}
181
182TEST_F(GenericShell, close_session_notifies_session_event_sink)
183{
184 auto session = shell.open_session(__LINE__, "XPlane", std::shared_ptr<mf::EventSink>());
185 auto session1 = shell.open_session(__LINE__, "Bla", std::shared_ptr<mf::EventSink>());
186
187 InSequence s;
188 EXPECT_CALL(session_event_sink, handle_session_stopping(session1));
189 EXPECT_CALL(session_event_sink, handle_session_stopping(session));
190
191 shell.close_session(session1);
192 shell.close_session(session);
193}
194
195TEST_F(GenericShell, create_surface_provides_create_parameters_to_window_manager)
196{
197 std::shared_ptr<ms::Session> session =
198 shell.open_session(__LINE__, "XPlane", std::shared_ptr<mf::EventSink>());
199
200 auto params = ms::a_surface();
201
202 EXPECT_CALL(*wm, add_surface(session, Ref(params), _));
203
204 shell.create_surface(session, params);
205}
206
207TEST_F(GenericShell, create_surface_allows_window_manager_to_set_create_parameters)
208{
209 std::shared_ptr<ms::Session> const session =
210 shell.open_session(__LINE__, "XPlane", std::shared_ptr<mf::EventSink>());
211
212 auto params = ms::a_surface();
213 auto placed_params = params;
214 placed_params.size.width = geom::Width{100};
215
216 EXPECT_CALL(surface_coordinator, add_surface(placed_params, _));
217
218 EXPECT_CALL(*wm, add_surface(session, Ref(params), _)).WillOnce(Invoke(
219 [&](std::shared_ptr<ms::Session> const& session,
220 ms::SurfaceCreationParameters const&,
221 std::function<mf::SurfaceId(std::shared_ptr<ms::Session> const& session, ms::SurfaceCreationParameters const&)> const& build)
222 { return build(session, placed_params); }));
223
224 shell.create_surface(session, params);
225}
226
227TEST_F(GenericShell, destroy_surface_removes_surface_from_window_manager)
228{
229 auto const params = ms::a_surface();
230 std::shared_ptr<ms::Session> const session =
231 shell.open_session(__LINE__, "XPlane", std::shared_ptr<mf::EventSink>());
232
233 auto const surface_id = shell.create_surface(session, params);
234 auto const surface = session->surface(surface_id);
235
236 EXPECT_CALL(*wm, remove_surface(session, WeakPtrTo(surface)));
237
238 shell.destroy_surface(session, surface_id);
239}
240
241TEST_F(GenericShell, add_display_adds_display_to_window_manager)
242{
243 geom::Rectangle const arbitrary_area{{0,0}, {__LINE__,__LINE__}};
244
245 EXPECT_CALL(*wm, add_display(arbitrary_area));
246
247 shell.add_display(arbitrary_area);
248}
249
250TEST_F(GenericShell, remove_display_adds_display_to_window_manager)
251{
252 geom::Rectangle const arbitrary_area{{0,0}, {__LINE__,__LINE__}};
253
254 EXPECT_CALL(*wm, remove_display(arbitrary_area));
255
256 shell.remove_display(arbitrary_area);
257}
258
259TEST_F(GenericShell, key_input_events_are_handled_by_window_manager)
260{
261 int64_t const timestamp{0};
262 MirKeyInputEventAction const action{mir_key_input_event_action_down};
263 xkb_keysym_t const key_code{0};
264 int const scan_code{0};
265 MirInputEventModifiers const modifiers{mir_input_event_modifier_none};
266
267 auto const event = mir::events::make_event(
268 mir_input_event_type_key,
269 timestamp,
270 action,
271 key_code,
272 scan_code,
273 modifiers);
274
275 EXPECT_CALL(*wm, handle_key_event(_))
276 .WillOnce(Return(false))
277 .WillOnce(Return(true));
278
279 EXPECT_FALSE(shell.handle(*event));
280 EXPECT_TRUE(shell.handle(*event));
281}
282
283TEST_F(GenericShell, touch_input_events_are_handled_by_window_manager)
284{
285 int64_t const timestamp{0};
286 MirInputEventModifiers const modifiers{mir_input_event_modifier_none};
287
288 auto const event = mir::events::make_event(
289 mir_input_event_type_touch,
290 timestamp,
291 modifiers);
292
293 EXPECT_CALL(*wm, handle_touch_event(_))
294 .WillOnce(Return(false))
295 .WillOnce(Return(true));
296
297 EXPECT_FALSE(shell.handle(*event));
298 EXPECT_TRUE(shell.handle(*event));
299}
300
301TEST_F(GenericShell, pointer_input_events_are_handled_by_window_manager)
302{
303 int64_t const timestamp{0};
304 MirInputEventModifiers const modifiers{mir_input_event_modifier_none};
305 MirPointerInputEventAction const action{mir_pointer_input_event_action_button_down};
306 std::vector<MirPointerInputEventButton> const buttons_pressed{mir_pointer_input_button_primary};
307 float const x_axis_value{0.0};
308 float const y_axis_value{0.0};
309 float const hscroll_value{0.0};
310 float const vscroll_value{0.0};
311
312 auto const event = mir::events::make_event(
313 mir_input_event_type_pointer,
314 timestamp,
315 modifiers,
316 action,
317 buttons_pressed,
318 x_axis_value,
319 y_axis_value,
320 hscroll_value,
321 vscroll_value);
322
323 EXPECT_CALL(*wm, handle_pointer_event(_))
324 .WillOnce(Return(false))
325 .WillOnce(Return(true));
326
327 EXPECT_FALSE(shell.handle(*event));
328 EXPECT_TRUE(shell.handle(*event));
329}
330
331TEST_F(GenericShell, setting_surface_state_is_handled_by_window_manager)
332{
333 std::shared_ptr<ms::Session> const session =
334 shell.open_session(__LINE__, "XPlane", std::shared_ptr<mf::EventSink>());
335
336 auto const surface_id = shell.create_surface(session, ms::a_surface());
337 auto const surface = session->surface(surface_id);
338
339 MirSurfaceState const state{mir_surface_state_fullscreen};
340
341 EXPECT_CALL(*wm, handle_set_state(surface, state))
342 .WillOnce(Return(mir_surface_state_maximized));
343
344 EXPECT_CALL(mock_surface, configure(mir_surface_attrib_state, mir_surface_state_maximized));
345
346 shell.set_surface_attribute(session, surface, mir_surface_attrib_state, mir_surface_state_fullscreen);
347}
348
349TEST_F(GenericShell, as_focus_controller_set_focus_to_notifies_session_event_sink)
350{
351 auto session = shell.open_session(__LINE__, "XPlane", std::shared_ptr<mf::EventSink>());
352 auto session1 = shell.open_session(__LINE__, "Bla", std::shared_ptr<mf::EventSink>());
353
354 InSequence s;
355 EXPECT_CALL(session_event_sink, handle_focus_change(session1));
356 EXPECT_CALL(session_event_sink, handle_focus_change(session));
357 EXPECT_CALL(session_event_sink, handle_no_focus());
358
359 msh::FocusController& focus_controller = shell;
360 focus_controller.set_focus_to(session1, {});
361 focus_controller.set_focus_to(session, {});
362 focus_controller.set_focus_to({}, {});
363}
364
365TEST_F(GenericShell, as_focus_controller_focus_next_notifies_session_event_sink)
366{
367 msh::FocusController& focus_controller = shell;
368 auto session = shell.open_session(__LINE__, "XPlane", std::shared_ptr<mf::EventSink>());
369 auto session1 = shell.open_session(__LINE__, "Bla", std::shared_ptr<mf::EventSink>());
370 focus_controller.set_focus_to(session, {});
371
372 EXPECT_CALL(session_container, successor_of(session)).
373 WillOnce(Return(session1));
374
375 EXPECT_CALL(session_event_sink, handle_focus_change(session1));
376
377 focus_controller.focus_next();
378}
379
380TEST_F(GenericShell, as_focus_controller_focused_session_follows_focus)
381{
382 auto session = shell.open_session(__LINE__, "XPlane", std::shared_ptr<mf::EventSink>());
383 auto session1 = shell.open_session(__LINE__, "Bla", std::shared_ptr<mf::EventSink>());
384 EXPECT_CALL(session_container, successor_of(session1)).
385 WillOnce(Return(session));
386
387 msh::FocusController& focus_controller = shell;
388
389 focus_controller.set_focus_to(session, {});
390 EXPECT_THAT(focus_controller.focused_session(), Eq(session));
391 focus_controller.set_focus_to(session1, {});
392 EXPECT_THAT(focus_controller.focused_session(), Eq(session1));
393 focus_controller.focus_next();
394 EXPECT_THAT(focus_controller.focused_session(), Eq(session));
395}
396
397TEST_F(GenericShell, as_focus_controller_focused_surface_follows_focus)
398{
399 auto const session0 = shell.open_session(__LINE__, "XPlane", std::shared_ptr<mf::EventSink>());
400 auto const session1 = shell.open_session(__LINE__, "Bla", std::shared_ptr<mf::EventSink>());
401 NiceMock<mtd::MockSurface> dummy_surface;
402
403 EXPECT_CALL(surface_coordinator, add_surface(_,_)).Times(AnyNumber())
404 .WillOnce(Return(mt::fake_shared(dummy_surface)))
405 .WillOnce(Return(mt::fake_shared(mock_surface)));
406 EXPECT_CALL(session_container, successor_of(session1)).
407 WillOnce(Return(session0));
408
409
410 auto const surface0_id = shell.create_surface(session0, ms::a_surface());
411 auto const surface0 = session0->surface(surface0_id);
412 auto const surface1_id = shell.create_surface(session1, ms::a_surface());
413 auto const surface1 = session1->surface(surface1_id);
414
415 msh::FocusController& focus_controller = shell;
416
417 focus_controller.set_focus_to(session0, surface0);
418 EXPECT_THAT(focus_controller.focused_surface(), Eq(surface0));
419 focus_controller.set_focus_to(session1, surface1);
420 EXPECT_THAT(focus_controller.focused_surface(), Eq(surface1));
421 focus_controller.focus_next();
422 EXPECT_THAT(focus_controller.focused_surface(), Eq(surface0));
423
424 shell.destroy_surface(session0, surface0_id);
425 shell.destroy_surface(session1, surface1_id);
426 shell.close_session(session1);
427 shell.close_session(session0);
428}
429
430TEST_F(GenericShell, as_focus_controller_delegates_surface_at_to_surface_coordinator)
431{
432 auto const surface = mt::fake_shared(mock_surface);
433 geom::Point const cursor{__LINE__, __LINE__};
434
435 EXPECT_CALL(surface_coordinator, surface_at(cursor)).
436 WillOnce(Return(surface));
437
438 msh::FocusController& focus_controller = shell;
439
440 EXPECT_THAT(focus_controller.surface_at(cursor), Eq(surface));
441}
442
443namespace mir
444{
445namespace scene
446{
447// The next test is easier to write if we can compare std::weak_ptr<ms::Surface>
448inline bool operator==(
449 std::weak_ptr<ms::Surface> const& lhs,
450 std::weak_ptr<ms::Surface> const& rhs)
451{
452 return !lhs.owner_before(rhs) && !rhs.owner_before(lhs);
453}
454}
455}
456
457TEST_F(GenericShell, as_focus_controller_delegates_raise_to_surface_coordinator)
458{
459 msh::SurfaceSet const surfaces{mt::fake_shared(mock_surface)};
460
461 EXPECT_CALL(surface_coordinator, raise(surfaces));
462
463 msh::FocusController& focus_controller = shell;
464
465 focus_controller.raise(surfaces);
466}

Subscribers

People subscribed via source and target branches