Merge lp:~alan-griffiths/mir/integrate-generic-shell into lp:mir
- integrate-generic-shell
- Merge into development-branch
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 |
Related bugs: |
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.
PS Jenkins bot (ps-jenkins) wrote : | # |
PS Jenkins bot (ps-jenkins) wrote : | # |
PASSED: Continuous integration, rev:2363
http://
Executed test runs:
SUCCESS: http://
SUCCESS: http://
SUCCESS: http://
SUCCESS: http://
deb: http://
SUCCESS: http://
deb: http://
SUCCESS: http://
SUCCESS: http://
Click here to trigger a rebuild:
http://
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/
98 #define MIR_EXAMPLE_
Needs update.
Needs discussion.
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/
> 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_
Fixed.
PS Jenkins bot (ps-jenkins) wrote : | # |
PASSED: Continuous integration, rev:2365
http://
Executed test runs:
SUCCESS: http://
SUCCESS: http://
SUCCESS: http://
SUCCESS: http://
deb: http://
SUCCESS: http://
deb: http://
SUCCESS: http://
SUCCESS: http://
Click here to trigger a rebuild:
http://
Alexandros Frantzis (afrantzis) wrote : | # |
I am not convinced that GenericShell/
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.
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
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 | +} |
PASSED: Continuous integration, rev:2362 jenkins. qa.ubuntu. com/job/ mir-ci/ 3113/ jenkins. qa.ubuntu. com/job/ mir-android- vivid-i386- build/1485 jenkins. qa.ubuntu. com/job/ mir-clang- vivid-amd64- build/1484 jenkins. qa.ubuntu. com/job/ mir-mediumtests -vivid- touch/1439 jenkins. qa.ubuntu. com/job/ mir-vivid- amd64-ci/ 1110 jenkins. qa.ubuntu. com/job/ mir-vivid- amd64-ci/ 1110/artifact/ work/output/ *zip*/output. zip jenkins. qa.ubuntu. com/job/ mir-mediumtests -builder- vivid-armhf/ 1439 jenkins. qa.ubuntu. com/job/ mir-mediumtests -builder- vivid-armhf/ 1439/artifact/ work/output/ *zip*/output. zip jenkins. qa.ubuntu. com/job/ mir-mediumtests -runner- mako/4459 s-jenkins. ubuntu- ci:8080/ job/touch- flash-device/ 18504
http://
Executed test runs:
SUCCESS: http://
SUCCESS: http://
SUCCESS: http://
SUCCESS: http://
deb: http://
SUCCESS: http://
deb: http://
SUCCESS: http://
SUCCESS: http://
Click here to trigger a rebuild: s-jenkins. ubuntu- ci:8080/ job/mir- ci/3113/ rebuild
http://