Mir

Merge lp:~alan-griffiths/mir/msh_SystemCompositorWindowManager into lp:mir

Proposed by Alan Griffiths
Status: Merged
Approved by: Alan Griffiths
Approved revision: no longer in the source branch.
Merged at revision: 2666
Proposed branch: lp:~alan-griffiths/mir/msh_SystemCompositorWindowManager
Merge into: lp:mir
Diff against target: 628 lines (+522/-2)
7 files modified
examples/server_example_window_management.cpp (+10/-1)
include/server/mir/shell/system_compositor_window_manager.h (+99/-0)
src/server/shell/CMakeLists.txt (+1/-0)
src/server/shell/system_compositor_window_manager.cpp (+151/-0)
src/server/symbols.map (+55/-1)
tests/acceptance-tests/CMakeLists.txt (+1/-0)
tests/acceptance-tests/test_system_compositor_window_manager.cpp (+205/-0)
To merge this branch: bzr merge lp:~alan-griffiths/mir/msh_SystemCompositorWindowManager
Reviewer Review Type Date Requested Status
PS Jenkins bot (community) continuous-integration Approve
Alexandros Frantzis (community) Approve
Review via email: mp+261751@code.launchpad.net

Commit message

shell, examples: add a basic SystemCompositorWindowManager

Description of the change

shell, examples: add a basic SystemCompositorWindowManager

Ensures we have explicit support for the system-compositor use case. The following mir_demo_server config now offers a minimal "system compositor":

    $ cat ~/.config/mir/mir_demo_server.config
    file=/tmp/mir_socket
    arw-file=
    vt=1
    display-config=sidebyside
    window-manager=system-compositor

SystemCompositorWindowManager is slightly simplified from USC's WindowManager - mostly by removing the SessionMonitor references and providing hooks to reinstate the behaviour downstream.

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: Needs Fixing (continuous-integration)
Revision history for this message
PS Jenkins bot (ps-jenkins) wrote :
review: Approve (continuous-integration)
Revision history for this message
Alexandros Frantzis (afrantzis) wrote :

Looks good.

I was a bit torn when I started reviewing this (not sure if it's worth the trouble and extra coupling), but I think this may be useful for simple products based on Mir. I still don't think it's worth doing this if it's just to simplify USC.

95 + /// Called the first time each surface owned by the session posts its first buffer

We have always assumed one surface per system-compositor client, so it's not clear what's the correct behavior if such a client creates and posts to multiple surfaces. I guess the proposed approach of always focusing the "default" surface is OK for now, but perhaps in the future on_session_ready method should be extended to also provide the surface that became ready.

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

> We have always assumed one surface per system-compositor client, so it's not
> clear what's the correct behavior if such a client creates and posts to
> multiple surfaces. I guess the proposed approach of always focusing the
> "default" surface is OK for now, but perhaps in the future on_session_ready
> method should be extended to also provide the surface that became ready.

What we actually have (c.f. the nested code and spinner) is one surface for each output for each system-compositor client.

Revision history for this message
PS Jenkins bot (ps-jenkins) wrote :
review: Needs Fixing (continuous-integration)
Revision history for this message
PS Jenkins bot (ps-jenkins) wrote :
review: Approve (continuous-integration)

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1=== modified file 'examples/server_example_window_management.cpp'
2--- examples/server_example_window_management.cpp 2015-05-19 21:34:34 +0000
3+++ examples/server_example_window_management.cpp 2015-06-16 08:37:48 +0000
4@@ -27,6 +27,7 @@
5 #include "mir/input/composite_event_filter.h"
6 #include "mir/options/option.h"
7 #include "mir/shell/display_layout.h"
8+#include "mir/shell/system_compositor_window_manager.h"
9
10 namespace me = mir::examples;
11 namespace mf = mir::frontend;
12@@ -42,11 +43,12 @@
13 namespace
14 {
15 char const* const wm_option = "window-manager";
16-char const* const wm_description = "window management strategy [{tiling|fullscreen|canonical}]";
17+char const* const wm_description = "window management strategy [{tiling|fullscreen|canonical|system-compositor}]";
18
19 char const* const wm_tiling = "tiling";
20 char const* const wm_fullscreen = "fullscreen";
21 char const* const wm_canonical = "canonical";
22+char const* const wm_system_compositor = "system-compositor";
23
24 struct NullSessionInfo
25 {
26@@ -149,6 +151,13 @@
27 {
28 return std::make_shared<CanonicalWindowManager>(focus_controller, server.the_shell_display_layout());
29 }
30+ else if (selection == wm_system_compositor)
31+ {
32+ return std::make_shared<msh::SystemCompositorWindowManager>(
33+ focus_controller,
34+ server.the_shell_display_layout(),
35+ server.the_session_coordinator());
36+ }
37
38 throw mir::AbnormalExit("Unknown window manager: " + selection);
39 });
40
41=== added file 'include/server/mir/shell/system_compositor_window_manager.h'
42--- include/server/mir/shell/system_compositor_window_manager.h 1970-01-01 00:00:00 +0000
43+++ include/server/mir/shell/system_compositor_window_manager.h 2015-06-16 08:37:48 +0000
44@@ -0,0 +1,99 @@
45+/*
46+ * Copyright © 2015 Canonical Ltd.
47+ *
48+ * This program is free software: you can redistribute it and/or modify it
49+ * under the terms of the GNU General Public License version 3,
50+ * as published by the Free Software Foundation.
51+ *
52+ * This program is distributed in the hope that it will be useful,
53+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
54+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
55+ * GNU General Public License for more details.
56+ *
57+ * You should have received a copy of the GNU General Public License
58+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
59+ *
60+ * Authored By: Alan Griffiths <alan@octopull.co.uk>
61+ */
62+
63+#ifndef MIR_SHELL_SYSTEM_COMPOSITOR_WINDOW_MANAGER_H_
64+#define MIR_SHELL_SYSTEM_COMPOSITOR_WINDOW_MANAGER_H_
65+
66+#include "mir/shell/window_manager.h"
67+
68+namespace mir
69+{
70+namespace scene { class PlacementStrategy; class SessionCoordinator; }
71+namespace shell
72+{
73+class FocusController;
74+class DisplayLayout;
75+
76+/** Minimal window management for system compositing.
77+ */
78+class SystemCompositorWindowManager : public WindowManager
79+{
80+public:
81+ SystemCompositorWindowManager(
82+ FocusController* focus_controller,
83+ std::shared_ptr<shell::DisplayLayout> const& display_layout,
84+ std::shared_ptr<scene::SessionCoordinator> const& session_coordinator);
85+
86+/** @name Customization points
87+ * These are the likely events that a system compositor will care about
88+ * @{ */
89+ /// Called when a session first connects (before any surfaces are ready)
90+ virtual void on_session_added(std::shared_ptr<scene::Session> const& session) const;
91+
92+ /// Called when a session disconnects
93+ virtual void on_session_removed(std::shared_ptr<scene::Session> const& session) const;
94+
95+ /// Called the first time each surface owned by the session posts its first buffer
96+ virtual void on_session_ready(std::shared_ptr<scene::Session> const& session) const;
97+/** @} */
98+
99+protected:
100+ FocusController* const focus_controller;
101+ std::shared_ptr<DisplayLayout> const display_layout;
102+ std::shared_ptr<scene::SessionCoordinator> const session_coordinator;
103+
104+private:
105+ void add_session(std::shared_ptr<scene::Session> const& session) override;
106+
107+ void remove_session(std::shared_ptr<scene::Session> const& session) override;
108+
109+ frontend::SurfaceId add_surface(
110+ std::shared_ptr<scene::Session> const& session,
111+ scene::SurfaceCreationParameters const& params,
112+ std::function<frontend::SurfaceId(std::shared_ptr<scene::Session> const& session, scene::SurfaceCreationParameters const& params)> const& build) override;
113+
114+ void modify_surface(
115+ std::shared_ptr<scene::Session> const& session,
116+ std::shared_ptr<scene::Surface> const& surface,
117+ SurfaceSpecification const& modifications) override;
118+
119+ void remove_surface(
120+ std::shared_ptr<scene::Session> const& session,
121+ std::weak_ptr<scene::Surface> const& surface) override;
122+
123+ void add_display(geometry::Rectangle const& area) override;
124+
125+ void remove_display(geometry::Rectangle const& area) override;
126+
127+ bool handle_keyboard_event(MirKeyboardEvent const* event) override;
128+
129+ bool handle_touch_event(MirTouchEvent const* event) override;
130+
131+ bool handle_pointer_event(MirPointerEvent const* event) override;
132+
133+ int set_surface_attribute(
134+ std::shared_ptr<scene::Session> const& session,
135+ std::shared_ptr<scene::Surface> const& surface,
136+ MirSurfaceAttrib attrib,
137+ int value) override;
138+};
139+}
140+}
141+
142+
143+#endif /* MIR_SHELL_SYSTEM_COMPOSITOR_WINDOW_MANAGER_H_ */
144
145=== modified file 'src/server/shell/CMakeLists.txt'
146--- src/server/shell/CMakeLists.txt 2015-05-28 07:48:04 +0000
147+++ src/server/shell/CMakeLists.txt 2015-06-16 08:37:48 +0000
148@@ -10,6 +10,7 @@
149 default_window_manager.cpp
150 shell_wrapper.cpp
151 surface_ready_observer.cpp
152+ system_compositor_window_manager.cpp
153 default_persistent_surface_store.cpp
154 persistent_surface_store.cpp
155 )
156
157=== added file 'src/server/shell/system_compositor_window_manager.cpp'
158--- src/server/shell/system_compositor_window_manager.cpp 1970-01-01 00:00:00 +0000
159+++ src/server/shell/system_compositor_window_manager.cpp 2015-06-16 08:37:48 +0000
160@@ -0,0 +1,151 @@
161+/*
162+ * Copyright © 2015 Canonical Ltd.
163+ *
164+ * This program is free software: you can redistribute it and/or modify it
165+ * under the terms of the GNU General Public License version 3,
166+ * as published by the Free Software Foundation.
167+ *
168+ * This program is distributed in the hope that it will be useful,
169+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
170+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
171+ * GNU General Public License for more details.
172+ *
173+ * You should have received a copy of the GNU General Public License
174+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
175+ *
176+ * Authored By: Alan Griffiths <alan@octopull.co.uk>
177+ */
178+
179+#include "mir/shell/system_compositor_window_manager.h"
180+
181+#include "mir/shell/display_layout.h"
182+#include "mir/shell/focus_controller.h"
183+#include "mir/shell/surface_ready_observer.h"
184+#include "mir/shell/surface_specification.h"
185+
186+#include "mir/scene/session.h"
187+#include "mir/scene/session_coordinator.h"
188+#include "mir/scene/surface.h"
189+#include "mir/scene/surface_creation_parameters.h"
190+
191+namespace mf = mir::frontend;
192+namespace ms = mir::scene;
193+namespace msh = mir::shell;
194+
195+msh::SystemCompositorWindowManager::SystemCompositorWindowManager(
196+ FocusController* focus_controller,
197+ std::shared_ptr<DisplayLayout> const& display_layout,
198+ std::shared_ptr<ms::SessionCoordinator> const& session_coordinator) :
199+ focus_controller{focus_controller},
200+ display_layout{display_layout},
201+ session_coordinator{session_coordinator}
202+{
203+}
204+
205+void msh::SystemCompositorWindowManager::add_session(std::shared_ptr<ms::Session> const& session)
206+{
207+ on_session_added(session);
208+}
209+
210+void msh::SystemCompositorWindowManager::remove_session(std::shared_ptr<ms::Session> const& session)
211+{
212+ on_session_removed(session);
213+}
214+
215+auto msh::SystemCompositorWindowManager::add_surface(
216+ std::shared_ptr<ms::Session> const& session,
217+ ms::SurfaceCreationParameters const& params,
218+ std::function<mf::SurfaceId(std::shared_ptr<ms::Session> const& session, ms::SurfaceCreationParameters const& params)> const& build)
219+-> mf::SurfaceId
220+{
221+ mir::geometry::Rectangle rect{params.top_left, params.size};
222+
223+ display_layout->place_in_output(params.output_id, rect);
224+
225+ auto placed_parameters = params;
226+ placed_parameters.top_left = rect.top_left;
227+ placed_parameters.size = rect.size;
228+
229+ auto const result = build(session, placed_parameters);
230+ auto const surface = session->surface(result);
231+
232+ auto const session_ready_observer = std::make_shared<SurfaceReadyObserver>(
233+ [this](std::shared_ptr<ms::Session> const& session, std::shared_ptr<ms::Surface> const& /*surface*/)
234+ {
235+ on_session_ready(session);
236+ },
237+ session,
238+ surface);
239+
240+ surface->add_observer(session_ready_observer);
241+
242+ return result;
243+}
244+
245+void msh::SystemCompositorWindowManager::modify_surface(
246+ std::shared_ptr<ms::Session> const& /*session*/,
247+ std::shared_ptr<ms::Surface> const& surface,
248+ SurfaceSpecification const& modifications)
249+{
250+ if (modifications.name.is_set())
251+ surface->rename(modifications.name.value());
252+}
253+
254+void msh::SystemCompositorWindowManager::remove_surface(
255+ std::shared_ptr<ms::Session> const& /*session*/,
256+ std::weak_ptr<ms::Surface> const& /*surface*/)
257+{
258+}
259+
260+void msh::SystemCompositorWindowManager::add_display(mir::geometry::Rectangle const& /*area*/)
261+{
262+}
263+
264+void msh::SystemCompositorWindowManager::remove_display(mir::geometry::Rectangle const& /*area*/)
265+{
266+}
267+
268+bool msh::SystemCompositorWindowManager::handle_keyboard_event(MirKeyboardEvent const* /*event*/)
269+{
270+ return false;
271+}
272+
273+bool msh::SystemCompositorWindowManager::handle_touch_event(MirTouchEvent const* /*event*/)
274+{
275+ return false;
276+}
277+
278+bool msh::SystemCompositorWindowManager::handle_pointer_event(MirPointerEvent const* /*event*/)
279+{
280+ return false;
281+}
282+
283+int msh::SystemCompositorWindowManager::set_surface_attribute(
284+ std::shared_ptr<ms::Session> const& /*session*/,
285+ std::shared_ptr<ms::Surface> const& surface,
286+ MirSurfaceAttrib attrib,
287+ int value)
288+{
289+ return surface->configure(attrib, value);
290+}
291+
292+void msh::SystemCompositorWindowManager::on_session_added(std::shared_ptr<mir::scene::Session> const& /*session*/) const
293+{
294+}
295+
296+void msh::SystemCompositorWindowManager::on_session_removed(std::shared_ptr<mir::scene::Session> const& session) const
297+{
298+ if (focus_controller->focused_session() == session)
299+ {
300+ auto const next_session = session_coordinator->successor_of({});
301+ if (next_session)
302+ focus_controller->set_focus_to(next_session, next_session->default_surface());
303+ else
304+ focus_controller->set_focus_to(next_session, {});
305+ }
306+}
307+
308+void msh::SystemCompositorWindowManager::on_session_ready(std::shared_ptr<mir::scene::Session> const& session) const
309+{
310+ focus_controller->set_focus_to(session, session->default_surface());
311+}
312
313=== modified file 'src/server/symbols.map'
314--- src/server/symbols.map 2015-06-10 23:37:28 +0000
315+++ src/server/symbols.map 2015-06-16 08:37:48 +0000
316@@ -5,7 +5,8 @@
317 vtable?for?mir::input::NullInputDispatcher;
318 VTT?for?mir::DefaultServerConfiguration;
319 VTT?for?mir::shell::ShellWrapper;
320-
321+ VTT?for?mir::shell::SystemCompositorWindowManager;
322+
323 # The following symbols come from running a script over the generated docs. Vis:
324 # ../tools/process_doxygen_xml.py doc/xml/*.xml | grep "^mirserver public" | sed "s/mirserver public: / /" | sort
325 mir::compositor::Compositor::Compositor*;
326@@ -252,6 +253,34 @@
327 mir::shell::ShellWrapper::surface_at*;
328 mir::shell::SurfaceReadyObserver::SurfaceReadyObserver*;
329 mir::shell::SurfaceReadyObserver::?SurfaceReadyObserver*;
330+ mir::shell::SystemCompositorWindowManager::SystemCompositorWindowManager*;
331+ mir::shell::SystemCompositorWindowManager::add_display*;
332+ mir::shell::SystemCompositorWindowManager::add_prompt_provider_for*;
333+ mir::shell::SystemCompositorWindowManager::add_session*;
334+ mir::shell::SystemCompositorWindowManager::add_surface*;
335+ mir::shell::SystemCompositorWindowManager::close_session*;
336+ mir::shell::SystemCompositorWindowManager::create_surface*;
337+ mir::shell::SystemCompositorWindowManager::destroy_surface*;
338+ mir::shell::SystemCompositorWindowManager::focused_session*;
339+ mir::shell::SystemCompositorWindowManager::focused_surface*;
340+ mir::shell::SystemCompositorWindowManager::focus_next_session*;
341+ mir::shell::SystemCompositorWindowManager::get_surface_attribute*;
342+ mir::shell::SystemCompositorWindowManager::handle*;
343+ mir::shell::SystemCompositorWindowManager::modify_surface*;
344+ mir::shell::SystemCompositorWindowManager::on_session_added*;
345+ mir::shell::SystemCompositorWindowManager::on_session_removed*;
346+ mir::shell::SystemCompositorWindowManager::on_session_ready*;
347+ mir::shell::SystemCompositorWindowManager::open_session*;
348+ mir::shell::SystemCompositorWindowManager::raise*;
349+ mir::shell::SystemCompositorWindowManager::remove_display*;
350+ mir::shell::SystemCompositorWindowManager::remove_session*;
351+ mir::shell::SystemCompositorWindowManager::remove_surface*;
352+ mir::shell::SystemCompositorWindowManager::set_focus_to*;
353+ mir::shell::SystemCompositorWindowManager::set_surface_attribute*;
354+ mir::shell::SystemCompositorWindowManager::ShellWrapper*;
355+ mir::shell::SystemCompositorWindowManager::start_prompt_session_for*;
356+ mir::shell::SystemCompositorWindowManager::stop_prompt_session*;
357+ mir::shell::SystemCompositorWindowManager::surface_at*;
358 mir::shell::WindowManager::operator*;
359 mir::shell::WindowManager::?WindowManager*;
360 mir::shell::WindowManager::WindowManager*;
361@@ -345,6 +374,29 @@
362 non-virtual?thunk?to?mir::shell::ShellWrapper::stop_prompt_session*;
363 non-virtual?thunk?to?mir::shell::ShellWrapper::surface_at*;
364 non-virtual?thunk?to?mir::shell::SurfaceReadyObserver::?SurfaceReadyObserver*;
365+ non-virtual?thunk?to?mir::shell::SystemCompositorWindowManager::add_display*;
366+ non-virtual?thunk?to?mir::shell::SystemCompositorWindowManager::add_prompt_provider_for*;
367+ non-virtual?thunk?to?mir::shell::SystemCompositorWindowManager::close_session*;
368+ non-virtual?thunk?to?mir::shell::SystemCompositorWindowManager::create_surface*;
369+ non-virtual?thunk?to?mir::shell::SystemCompositorWindowManager::destroy_surface*;
370+ non-virtual?thunk?to?mir::shell::SystemCompositorWindowManager::focused_session*;
371+ non-virtual?thunk?to?mir::shell::SystemCompositorWindowManager::focused_surface*;
372+ non-virtual?thunk?to?mir::shell::SystemCompositorWindowManager::focus_next_session*;
373+ non-virtual?thunk?to?mir::shell::SystemCompositorWindowManager::get_surface_attribute*;
374+ non-virtual?thunk?to?mir::shell::SystemCompositorWindowManager::handle*;
375+ non-virtual?thunk?to?mir::shell::SystemCompositorWindowManager::modify_surface*;
376+ non-virtual?thunk?to?mir::shell::SystemCompositorWindowManager::on_session_added*;
377+ non-virtual?thunk?to?mir::shell::SystemCompositorWindowManager::on_session_removed*;
378+ non-virtual?thunk?to?mir::shell::SystemCompositorWindowManager::on_session_ready*;
379+ non-virtual?thunk?to?mir::shell::SystemCompositorWindowManager::open_session*;
380+ non-virtual?thunk?to?mir::shell::SystemCompositorWindowManager::raise*;
381+ non-virtual?thunk?to?mir::shell::SystemCompositorWindowManager::remove_display*;
382+ non-virtual?thunk?to?mir::shell::SystemCompositorWindowManager::set_focus_to*;
383+ non-virtual?thunk?to?mir::shell::SystemCompositorWindowManager::set_surface_attribute*;
384+ non-virtual?thunk?to?mir::shell::SystemCompositorWindowManager::ShellWrapper*;
385+ non-virtual?thunk?to?mir::shell::SystemCompositorWindowManager::start_prompt_session_for*;
386+ non-virtual?thunk?to?mir::shell::SystemCompositorWindowManager::stop_prompt_session*;
387+ non-virtual?thunk?to?mir::shell::SystemCompositorWindowManager::surface_at*;
388 non-virtual?thunk?to?mir::shell::WindowManager::?WindowManager*;
389 non-virtual?thunk?to?mir::time::Alarm::?Alarm*;
390 non-virtual?thunk?to?mir::time::AlarmFactory::?AlarmFactory*;
391@@ -402,6 +454,7 @@
392 typeinfo?for?mir::shell::Shell;
393 typeinfo?for?mir::shell::ShellWrapper;
394 typeinfo?for?mir::shell::SurfaceReadyObserver;
395+ typeinfo?for?mir::shell::SystemCompositorWindowManager;
396 typeinfo?for?mir::shell::WindowManager;
397 typeinfo?for?mir::time::Alarm;
398 typeinfo?for?mir::time::AlarmFactory;
399@@ -459,6 +512,7 @@
400 vtable?for?mir::shell::Shell;
401 vtable?for?mir::shell::ShellWrapper;
402 vtable?for?mir::shell::SurfaceReadyObserver;
403+ vtable?for?mir::shell::SystemCompositorWindowManager;
404 vtable?for?mir::shell::WindowManager;
405 vtable?for?mir::time::Alarm;
406 vtable?for?mir::time::AlarmFactory;
407
408=== modified file 'tests/acceptance-tests/CMakeLists.txt'
409--- tests/acceptance-tests/CMakeLists.txt 2015-06-11 09:41:30 +0000
410+++ tests/acceptance-tests/CMakeLists.txt 2015-06-16 08:37:48 +0000
411@@ -45,6 +45,7 @@
412 test_surface_modifications.cpp
413 test_surface_placement.cpp
414 test_surface_morphing.cpp
415+ test_system_compositor_window_manager.cpp
416 test_session_mediator_report.cpp
417 )
418
419
420=== added file 'tests/acceptance-tests/test_system_compositor_window_manager.cpp'
421--- tests/acceptance-tests/test_system_compositor_window_manager.cpp 1970-01-01 00:00:00 +0000
422+++ tests/acceptance-tests/test_system_compositor_window_manager.cpp 2015-06-16 08:37:48 +0000
423@@ -0,0 +1,205 @@
424+/*
425+ * Copyright © 2015 Canonical Ltd.
426+ *
427+ * This program is free software: you can redistribute it and/or modify it
428+ * under the terms of the GNU General Public License version 3,
429+ * as published by the Free Software Foundation.
430+ *
431+ * This program is distributed in the hope that it will be useful,
432+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
433+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
434+ * GNU General Public License for more details.
435+ *
436+ * You should have received a copy of the GNU General Public License
437+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
438+ *
439+ * Authored by: Alan Griffiths <alan@octopull.co.uk>
440+ */
441+
442+#include "mir/shell/system_compositor_window_manager.h"
443+#include "mir_toolkit/mir_client_library.h"
444+
445+#include "mir/geometry/rectangle.h"
446+#include "mir_test_framework/headless_test.h"
447+#include "mir_test/signal.h"
448+
449+#include "gmock/gmock.h"
450+
451+namespace msh = mir::shell;
452+namespace mt = mir::test;
453+namespace mtf = mir_test_framework;
454+
455+using namespace testing;
456+using namespace std::chrono_literals;
457+
458+namespace
459+{
460+class SurfaceHandle
461+{
462+public:
463+ explicit SurfaceHandle(MirSurface* surface) : surface{surface} {}
464+ ~SurfaceHandle() { if (surface) mir_surface_release_sync(surface); }
465+
466+ operator MirSurface*() const { return surface; }
467+
468+ void post_buffer()
469+ {
470+ mir_buffer_stream_swap_buffers_sync(mir_surface_get_buffer_stream(surface));
471+ }
472+
473+ SurfaceHandle(SurfaceHandle const&& that) : surface{that.surface} { surface = nullptr; }
474+private:
475+ SurfaceHandle(SurfaceHandle const&) = delete;
476+ MirSurface* surface;
477+};
478+
479+struct MockClient
480+{
481+ explicit MockClient(char const* connect_string) :
482+ connection{mir_connect_sync(connect_string, __PRETTY_FUNCTION__)}
483+ {
484+ }
485+
486+ MockClient(MockClient&& source) :
487+ connection{nullptr}
488+ {
489+ std::swap(connection, source.connection);
490+ }
491+
492+ auto create_surface(int output_id) -> SurfaceHandle
493+ {
494+ auto const spec = mir_connection_create_spec_for_normal_surface(
495+ connection, 800, 600, mir_pixel_format_bgr_888);
496+
497+ mir_surface_spec_set_fullscreen_on_output(spec, output_id);
498+ auto const surface = mir_surface_create_sync(spec);
499+ mir_surface_spec_release(spec);
500+
501+ mir_surface_set_event_handler(surface, on_surface_event, this);
502+
503+ return SurfaceHandle{surface};
504+ };
505+
506+ void disconnect()
507+ {
508+ if (connection)
509+ mir_connection_release(connection);
510+
511+ connection = nullptr;
512+ }
513+
514+ ~MockClient() noexcept
515+ {
516+ disconnect();
517+ }
518+
519+ MOCK_METHOD2(surface_event, void(MirSurface* surface, const MirEvent* event));
520+
521+private:
522+ MirConnection* connection{nullptr};
523+
524+ static void on_surface_event(MirSurface* surface, const MirEvent* event, void* client_ptr)
525+ {
526+ static_cast<MockClient*>(client_ptr)->surface_event(surface, event);
527+ }
528+};
529+
530+struct SystemCompositorWindowManager : mtf::HeadlessTest
531+{
532+ void SetUp() override
533+ {
534+ add_to_environment("MIR_SERVER_NO_FILE", "");
535+
536+ initial_display_layout({{{0, 0}, { 640, 480}}, {{480, 0}, {1920, 1080}}});
537+
538+ server.override_the_window_manager_builder(
539+ [this](msh::FocusController* focus_controller)
540+ {
541+ return std::make_shared<msh::SystemCompositorWindowManager>(
542+ focus_controller,
543+ server.the_shell_display_layout(),
544+ server.the_session_coordinator());
545+ });
546+
547+ start_server();
548+ }
549+
550+ void TearDown() override
551+ {
552+ stop_server();
553+ }
554+
555+ MockClient connect_client()
556+ {
557+ return MockClient(new_connection().c_str());
558+ }
559+};
560+
561+MATCHER_P(MirFocusEvent, expected, "")
562+{
563+ if (mir_event_get_type(arg) != mir_event_type_surface)
564+ return false;
565+
566+ auto surface_event = mir_event_get_surface_event(arg);
567+ auto attrib = mir_surface_event_get_attribute(surface_event);
568+ auto value = mir_surface_event_get_attribute_value(surface_event);
569+
570+ return (attrib == mir_surface_attrib_focus)
571+ && (value == expected);
572+}
573+}
574+
575+TEST_F(SystemCompositorWindowManager, when_output_is_valid_surfaces_creation_succeeds)
576+{
577+ auto client = connect_client();
578+
579+ auto surface1 = client.create_surface(1);
580+ auto surface2 = client.create_surface(2);
581+
582+ EXPECT_TRUE(mir_surface_is_valid(surface1));
583+ EXPECT_TRUE(mir_surface_is_valid(surface2));
584+}
585+
586+TEST_F(SystemCompositorWindowManager, when_output_ID_not_specified_surfaces_creation_fails)
587+{
588+ auto client = connect_client();
589+
590+ auto surface = client.create_surface(0);
591+
592+ EXPECT_FALSE(mir_surface_is_valid(surface));
593+ EXPECT_THAT(mir_surface_get_error_message(surface), HasSubstr("Failed to place surface"));
594+}
595+
596+TEST_F(SystemCompositorWindowManager, if_a_surface_posts_client_gets_focus)
597+{
598+ auto client = connect_client();
599+
600+ // Throw away all uninteresting surface events
601+ EXPECT_CALL(client, surface_event(_, Not(MirFocusEvent(mir_surface_focused)))).Times(AnyNumber());
602+
603+ auto surface = client.create_surface(1);
604+
605+ mt::Signal signal;
606+
607+ EXPECT_CALL(client, surface_event(_, MirFocusEvent(mir_surface_focused))).Times(1)
608+ .WillOnce(InvokeWithoutArgs([&] { signal.raise(); }));
609+
610+ surface.post_buffer();
611+
612+ signal.wait_for(1s);
613+}
614+
615+TEST_F(SystemCompositorWindowManager, if_no_surface_posts_client_never_gets_focus)
616+{
617+ auto client = connect_client();
618+ auto surface = client.create_surface(1);
619+
620+ mt::Signal signal;
621+
622+ ON_CALL(client, surface_event(_, MirFocusEvent(mir_surface_focused)))
623+ .WillByDefault(InvokeWithoutArgs([&] { signal.raise(); }));
624+
625+ EXPECT_CALL(client, surface_event(_, MirFocusEvent(mir_surface_focused))).Times(0);
626+
627+ signal.wait_for(100ms);
628+}

Subscribers

People subscribed via source and target branches