Mir

Merge lp:~mir-team/mir/introduce-scene-observer into lp:mir

Proposed by Robert Carr
Status: Superseded
Proposed branch: lp:~mir-team/mir/introduce-scene-observer
Merge into: lp:mir
Diff against target: 1103 lines (+594/-77) (has conflicts)
17 files modified
include/server/mir/compositor/scene.h (+7/-9)
include/server/mir/scene/legacy_scene_change_notification.h (+58/-0)
include/server/mir/scene/observer.h (+50/-0)
include/test/mir_test_doubles/mock_scene.h (+3/-3)
include/test/mir_test_doubles/mock_surface.h (+1/-0)
src/server/compositor/multi_threaded_compositor.cpp (+20/-3)
src/server/compositor/multi_threaded_compositor.h (+8/-0)
src/server/scene/CMakeLists.txt (+2/-1)
src/server/scene/legacy_scene_change_notification.cpp (+71/-0)
src/server/scene/surface_stack.cpp (+76/-16)
src/server/scene/surface_stack.h (+22/-6)
tests/integration-tests/test_surface_first_frame_sync.cpp (+15/-11)
tests/unit-tests/compositor/test_default_display_buffer_compositor.cpp (+9/-2)
tests/unit-tests/compositor/test_multi_threaded_compositor.cpp (+45/-24)
tests/unit-tests/scene/CMakeLists.txt (+1/-0)
tests/unit-tests/scene/test_legacy_scene_change_notification.cpp (+96/-0)
tests/unit-tests/scene/test_surface_stack.cpp (+110/-2)
Text conflict in src/server/scene/surface_stack.cpp
To merge this branch: bzr merge lp:~mir-team/mir/introduce-scene-observer
Reviewer Review Type Date Requested Status
Alan Griffiths Needs Fixing
Alberto Aguirre (community) Needs Fixing
PS Jenkins bot (community) continuous-integration Needs Fixing
Kevin DuBois (community) Approve
Alexandros Frantzis (community) Approve
Review via email: mp+215969@code.launchpad.net

This proposal supersedes a proposal from 2014-04-15.

This proposal has been superseded by a proposal from 2014-04-23.

Commit message

Introduce a mir::scene::Observer interface as the top level entrypoint to scene observation.

Description of the change

An alternative to:

https://code.launchpad.net/~mir-team/mir/multiple-change-callbacks

Which better mirrors the other interfaces and supports the future needs of the compositor.

To post a comment you must log in.
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 overall.

Nits:

29 + virtual void add_observer(std::shared_ptr<scene::Observer> const& observer) = 0;
30 + virtual void remove_observer(std::shared_ptr<scene::Observer> const& observer) = 0;

Adding or removing an observer from within a scene::Observer can cause a deadlock. It would be good to document this.

442 + std::unique_lock<decltype(mutex)> lg(mutex);

We should prefer std::lock_guard<> which is slighly more efficient.

review: Approve
Revision history for this message
Kevin DuBois (kdub) wrote :

+1! I hope that in the future the MultiThreadedCompositor's threads subscribe themselves as observers and are a bit more intelligent about when they elect to recomposite as the Scene changes.

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

~~~
784 + scene->throw_on_add_observer(true);
~~~

The test needs to be modified so starts throws to check for cleanup. The above won't do it anymore.

~~~
427 + std::unique_lock<decltype(mutex)> lg(mutex);
428 +
429 + for (auto observer : observers)
430 + observer->surface_added(surface);

and the rest...
~~~

During a callback if an observer modifies the scene either directly or indirectly (which could be subtle) you can end up in deadlock. Maybe copy the observer list and release the lock before calling back?

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

I'm happy with this approach, but...

491 + void remove_observer(std::shared_ptr<Observer> const& observer);

I think that the observed object should own the observer and that the client code should only ever need a weak_ptr (and then only if it intended to remove it at some point). Accordingly this signature should be:

   void remove_observer(std::weak_ptr<Observer> const& observer);

~~~~

124 +// A simple implementation of surface observer which forwards all changes to a provided callback.
125 +// Also installs surface observers on each added surface which in turn forward each change to
126 +// said callback.
127 +class SimpleObserver : public Observer

I think a better name would be LegacySceneChangeNotification (c.f. LegacySurfaceChangeNotification)

A (pre-existing and non-blocking) issue I've just noticed is that we don't remove SurfaceObservers when surfaces are removed from the scene - so that *if* surfaces were repeatedly added and removed then we'd have an increasing number of them.

~~~~

74 + virtual void surface_added(std::shared_ptr<Surface> const& surface) = 0;
75 + virtual void surface_removed(std::shared_ptr<Surface> const& surface) = 0;

I'm not (yet) convinced that observers should be given shared ownership like this. I note that a pointer/reference to the surface is needed in SimpleObserver, but the ownership transfer implied by shared_ptr worries me.

~~~~

We'll likely want to supply more information about reordering than the current interface provides for.

review: Needs Fixing

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1=== modified file 'include/server/mir/compositor/scene.h'
2--- include/server/mir/compositor/scene.h 2014-04-15 05:31:19 +0000
3+++ include/server/mir/compositor/scene.h 2014-04-23 22:49:30 +0000
4@@ -27,6 +27,11 @@
5
6 namespace mir
7 {
8+namespace scene
9+{
10+class Observer;
11+}
12+
13 namespace compositor
14 {
15
16@@ -41,15 +46,8 @@
17 */
18 virtual graphics::RenderableList generate_renderable_list() const = 0;
19
20- /**
21- * Sets a callback to be called whenever the state of the
22- * Scene changes.
23- *
24- * The supplied callback should not directly or indirectly (e.g.,
25- * by changing a property of a surface) change the state of
26- * the Scene, otherwise a deadlock may occur.
27- */
28- virtual void set_change_callback(std::function<void()> const& f) = 0;
29+ virtual void add_observer(std::shared_ptr<scene::Observer> const& observer) = 0;
30+ virtual void remove_observer(std::weak_ptr<scene::Observer> const& observer) = 0;
31
32 protected:
33 Scene() = default;
34
35=== added file 'include/server/mir/scene/legacy_scene_change_notification.h'
36--- include/server/mir/scene/legacy_scene_change_notification.h 1970-01-01 00:00:00 +0000
37+++ include/server/mir/scene/legacy_scene_change_notification.h 2014-04-23 22:49:30 +0000
38@@ -0,0 +1,58 @@
39+/*
40+ * Copyright © 2014 Canonical Ltd.
41+ *
42+ * This program is free software: you can redistribute it and/or modify it
43+ * under the terms of the GNU General Public License version 3,
44+ * as published by the Free Software Foundation.
45+ *
46+ * This program is distributed in the hope that it will be useful,
47+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
48+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
49+ * GNU General Public License for more details.
50+ *
51+ * You should have received a copy of the GNU General Public License
52+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
53+ *
54+ * Authored by: Robert Carr <robert.carr@canonical.com>
55+ */
56+
57+#ifndef MIR_SCENE_SIMPLE_OBSERVER_H_
58+#define MIR_SCENE_SIMPLE_OBSERVER_H_
59+
60+#include "mir/scene/observer.h"
61+
62+#include <functional>
63+#include <map>
64+#include <mutex>
65+
66+namespace mir
67+{
68+namespace scene
69+{
70+class SurfaceObserver;
71+
72+// A simple implementation of surface observer which forwards all changes to a provided callback.
73+// Also installs surface observers on each added surface which in turn forward each change to
74+// said callback.
75+class LegacySceneChangeNotification : public Observer
76+{
77+public:
78+ LegacySceneChangeNotification(std::function<void()> const& notify_change);
79+ ~LegacySceneChangeNotification();
80+
81+ void surface_added(std::shared_ptr<Surface> const& surface) override;
82+ void surface_removed(std::shared_ptr<Surface> const& surface) override;
83+ void surfaces_reordered() override;
84+
85+private:
86+ std::function<void()> notify_change;
87+
88+ std::mutex surface_observers_guard;
89+ std::map<std::weak_ptr<Surface>, std::shared_ptr<SurfaceObserver>,
90+ std::owner_less<std::weak_ptr<Surface>>> surface_observers;
91+};
92+
93+}
94+} // namespace mir
95+
96+#endif // MIR_SCENE_SIMPLE_OBSERVER_H_
97
98=== added file 'include/server/mir/scene/observer.h'
99--- include/server/mir/scene/observer.h 1970-01-01 00:00:00 +0000
100+++ include/server/mir/scene/observer.h 2014-04-23 22:49:30 +0000
101@@ -0,0 +1,50 @@
102+/*
103+ * Copyright © 2014 Canonical Ltd.
104+ *
105+ * This program is free software: you can redistribute it and/or modify it
106+ * under the terms of the GNU General Public License version 3,
107+ * as published by the Free Software Foundation.
108+ *
109+ * This program is distributed in the hope that it will be useful,
110+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
111+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
112+ * GNU General Public License for more details.
113+ *
114+ * You should have received a copy of the GNU General Public License
115+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
116+ *
117+ * Authored by: Robert Carr <robert.carr@canonical.com>
118+ */
119+
120+#ifndef MIR_SCENE_OBSERVER_H_
121+#define MIR_SCENE_OBSERVER_H_
122+
123+#include <memory>
124+
125+namespace mir
126+{
127+namespace scene
128+{
129+class Surface;
130+
131+/// An observer for top level notifications of scene changes. In order
132+/// to receive more granular change notifications a user may install
133+/// mir::scene::SurfaceObserver in surface_added.
134+class Observer
135+{
136+public:
137+ virtual void surface_added(std::shared_ptr<Surface> const& surface) = 0;
138+ virtual void surface_removed(std::shared_ptr<Surface> const& surface) = 0;
139+ virtual void surfaces_reordered() = 0;
140+
141+protected:
142+ Observer() = default;
143+ virtual ~Observer() = default;
144+ Observer(Observer const&) = delete;
145+ Observer& operator=(Observer const&) = delete;
146+};
147+
148+}
149+} // namespace mir
150+
151+#endif // MIR_SCENE_OBSERVER_H_
152
153=== modified file 'include/test/mir_test_doubles/mock_scene.h'
154--- include/test/mir_test_doubles/mock_scene.h 2014-04-15 05:31:19 +0000
155+++ include/test/mir_test_doubles/mock_scene.h 2014-04-23 22:49:30 +0000
156@@ -38,9 +38,9 @@
157 .WillByDefault(testing::Return(graphics::RenderableList{}));
158 }
159 MOCK_CONST_METHOD0(generate_renderable_list, graphics::RenderableList());
160- MOCK_METHOD1(set_change_callback, void(std::function<void()> const&));
161- MOCK_METHOD0(lock, void());
162- MOCK_METHOD0(unlock, void());
163+
164+ MOCK_METHOD1(add_observer, void(std::shared_ptr<scene::Observer> const&));
165+ MOCK_METHOD1(remove_observer, void(std::weak_ptr<scene::Observer> const&));
166 };
167
168 } // namespace doubles
169
170=== modified file 'include/test/mir_test_doubles/mock_surface.h'
171--- include/test/mir_test_doubles/mock_surface.h 2014-04-15 05:31:19 +0000
172+++ include/test/mir_test_doubles/mock_surface.h 2014-04-23 22:49:30 +0000
173@@ -63,6 +63,7 @@
174 MOCK_METHOD2(configure, int(MirSurfaceAttrib, int));
175 MOCK_METHOD1(take_input_focus, void(std::shared_ptr<shell::InputTargeter> const&));
176 MOCK_METHOD1(add_observer, void(std::shared_ptr<scene::SurfaceObserver> const&));
177+ MOCK_METHOD1(remove_observer, void(std::shared_ptr<scene::SurfaceObserver> const&));
178 };
179
180 }
181
182=== modified file 'src/server/compositor/multi_threaded_compositor.cpp'
183--- src/server/compositor/multi_threaded_compositor.cpp 2014-04-17 01:34:35 +0000
184+++ src/server/compositor/multi_threaded_compositor.cpp 2014-04-23 22:49:30 +0000
185@@ -23,6 +23,9 @@
186 #include "mir/compositor/display_buffer_compositor_factory.h"
187 #include "mir/compositor/scene.h"
188 #include "mir/compositor/compositor_report.h"
189+#include "mir/scene/legacy_scene_change_notification.h"
190+#include "mir/scene/surface_observer.h"
191+#include "mir/scene/surface.h"
192
193 #include <thread>
194 #include <condition_variable>
195@@ -31,6 +34,7 @@
196
197 namespace mc = mir::compositor;
198 namespace mg = mir::graphics;
199+namespace ms = mir::scene;
200
201 namespace
202 {
203@@ -259,6 +263,7 @@
204 void mc::MultiThreadedCompositor::schedule_compositing()
205 {
206 std::unique_lock<std::mutex> lk(state_guard);
207+
208 report->scheduled();
209 for (auto& f : thread_functors)
210 f->schedule_compositing();
211@@ -282,8 +287,7 @@
212 }};
213
214 lk.unlock();
215- /* Recomposite whenever the scene changes */
216- scene->set_change_callback([this]() { schedule_compositing(); });
217+ add_observer();
218 lk.lock();
219
220 create_compositing_threads();
221@@ -314,7 +318,7 @@
222 }};
223
224 lk.unlock();
225- scene->set_change_callback([]{});
226+ scene->remove_observer(observer);
227 lk.lock();
228
229 destroy_compositing_threads(lk);
230@@ -370,3 +374,16 @@
231
232 state = CompositorState::stopped;
233 }
234+
235+void mc::MultiThreadedCompositor::add_observer()
236+{
237+ auto strong_observer = std::make_shared<ms::LegacySceneChangeNotification>([this]()
238+ {
239+ /* Recomposite whenever the scene changes */
240+ schedule_compositing();
241+ });
242+ observer = strong_observer;
243+
244+ scene->add_observer(strong_observer);
245+}
246+
247
248=== modified file 'src/server/compositor/multi_threaded_compositor.h'
249--- src/server/compositor/multi_threaded_compositor.h 2014-04-14 18:34:06 +0000
250+++ src/server/compositor/multi_threaded_compositor.h 2014-04-23 22:49:30 +0000
251@@ -32,6 +32,11 @@
252 {
253 class Display;
254 }
255+namespace scene
256+{
257+class Observer;
258+}
259+
260 namespace compositor
261 {
262
263@@ -78,6 +83,9 @@
264 bool compose_on_start;
265
266 void schedule_compositing();
267+
268+ void add_observer();
269+ std::weak_ptr<mir::scene::Observer> observer;
270 };
271
272 }
273
274=== modified file 'src/server/scene/CMakeLists.txt'
275--- src/server/scene/CMakeLists.txt 2014-04-17 13:55:46 +0000
276+++ src/server/scene/CMakeLists.txt 2014-04-23 22:49:30 +0000
277@@ -8,7 +8,6 @@
278 default_session_container.cpp
279 gl_pixel_buffer.cpp
280 global_event_sender.cpp
281- legacy_surface_change_notification.cpp
282 mediating_display_changer.cpp
283 session_manager.cpp
284 surface_allocator.cpp
285@@ -18,4 +17,6 @@
286 surface_event_source.cpp
287 surface_observer.cpp
288 threaded_snapshot_strategy.cpp
289+ legacy_scene_change_notification.cpp
290+ legacy_surface_change_notification.cpp
291 )
292
293=== added file 'src/server/scene/legacy_scene_change_notification.cpp'
294--- src/server/scene/legacy_scene_change_notification.cpp 1970-01-01 00:00:00 +0000
295+++ src/server/scene/legacy_scene_change_notification.cpp 2014-04-23 22:49:30 +0000
296@@ -0,0 +1,71 @@
297+/*
298+ * Copyright © 2014 Canonical Ltd.
299+ *
300+ * This program is free software: you can redistribute it and/or modify it
301+ * under the terms of the GNU General Public License version 3,
302+ * as published by the Free Software Foundation.
303+ *
304+ * This program is distributed in the hope that it will be useful,
305+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
306+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
307+ * GNU General Public License for more details.
308+ *
309+ * You should have received a copy of the GNU General Public License
310+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
311+ *
312+ * Authored by: Robert Carr <robert.carr@canonical.com>
313+ */
314+
315+#include "legacy_surface_change_notification.h"
316+
317+#include "mir/scene/legacy_scene_change_notification.h"
318+#include "mir/scene/surface.h"
319+
320+#include <boost/throw_exception.hpp>
321+
322+namespace ms = mir::scene;
323+
324+ms::LegacySceneChangeNotification::LegacySceneChangeNotification(std::function<void()> const& notify_change)
325+ : notify_change(notify_change)
326+{
327+}
328+
329+ms::LegacySceneChangeNotification::~LegacySceneChangeNotification()
330+{
331+ std::unique_lock<decltype(surface_observers_guard)> lg(surface_observers_guard);
332+ for (auto &kv : surface_observers)
333+ {
334+ auto surface = kv.first.lock();
335+ if (surface)
336+ surface->remove_observer(kv.second);
337+ }
338+}
339+
340+void ms::LegacySceneChangeNotification::surface_added(std::shared_ptr<ms::Surface> const& surface)
341+{
342+ auto observer = std::make_shared<ms::LegacySurfaceChangeNotification>(notify_change);
343+ surface->add_observer(observer);
344+
345+ {
346+ std::unique_lock<decltype(surface_observers_guard)> lg(surface_observers_guard);
347+ surface_observers[surface] = observer;
348+ }
349+ notify_change();
350+}
351+
352+void ms::LegacySceneChangeNotification::surface_removed(std::shared_ptr<ms::Surface> const& surface)
353+{
354+ {
355+ std::unique_lock<decltype(surface_observers_guard)> lg(surface_observers_guard);
356+ auto it = surface_observers.find(surface);
357+ if (it != surface_observers.end())
358+ surface_observers.erase(it);
359+ }
360+ notify_change();
361+}
362+
363+void ms::LegacySceneChangeNotification::surfaces_reordered()
364+{
365+ notify_change();
366+}
367+
368
369=== modified file 'src/server/scene/surface_stack.cpp'
370--- src/server/scene/surface_stack.cpp 2014-04-22 21:37:27 +0000
371+++ src/server/scene/surface_stack.cpp 2014-04-23 22:49:30 +0000
372@@ -23,7 +23,6 @@
373 #include "surface_stack.h"
374 #include "mir/compositor/buffer_stream.h"
375 #include "mir/scene/input_registrar.h"
376-#include "legacy_surface_change_notification.h"
377 #include "mir/input/input_channel_factory.h"
378 #include "mir/scene/scene_report.h"
379
380@@ -52,9 +51,7 @@
381 std::shared_ptr<InputRegistrar> const& input_registrar,
382 std::shared_ptr<SceneReport> const& report) :
383 input_registrar{input_registrar},
384- report{report},
385- change_cb{[this]() { emit_change_notification(); }},
386- notify_change{[]{}}
387+ report{report}
388 {
389 }
390
391@@ -125,13 +122,6 @@
392 return list;
393 }
394
395-void ms::SurfaceStack::set_change_callback(std::function<void()> const& f)
396-{
397- std::lock_guard<decltype(notify_change_mutex)> lg(notify_change_mutex);
398- assert(f);
399- notify_change = f;
400-}
401-
402 void ms::SurfaceStack::add_surface(
403 std::shared_ptr<Surface> const& surface,
404 DepthId depth,
405@@ -142,10 +132,9 @@
406 layers_by_depth[depth].push_back(surface);
407 }
408 input_registrar->input_channel_opened(surface->input_channel(), surface, input_mode);
409+ observers.surface_added(surface);
410+
411 report->surface_added(surface.get(), surface.get()->name());
412- auto const observer = std::make_shared<LegacySurfaceChangeNotification>(change_cb);
413- surface->add_observer(observer);
414- emit_change_notification();
415 }
416
417
418@@ -174,12 +163,14 @@
419 if (found_surface)
420 {
421 input_registrar->input_channel_closed(keep_alive->input_channel());
422+ observers.surface_removed(keep_alive);
423+
424 report->surface_removed(keep_alive.get(), keep_alive.get()->name());
425- emit_change_notification();
426 }
427 // TODO: error logging when surface not found
428 }
429
430+<<<<<<< TREE
431 void ms::SurfaceStack::emit_change_notification()
432 {
433 std::lock_guard<decltype(notify_change_mutex)> lg(notify_change_mutex);
434@@ -187,6 +178,9 @@
435 }
436
437 void ms::SurfaceStack::for_each(std::function<void(std::shared_ptr<mi::Surface> const&)> const& callback)
438+=======
439+void ms::SurfaceStack::for_each(std::function<void(std::shared_ptr<mi::InputChannel> const&)> const& callback)
440+>>>>>>> MERGE-SOURCE
441 {
442 std::lock_guard<decltype(guard)> lg(guard);
443 for (auto &layer : layers_by_depth)
444@@ -213,7 +207,7 @@
445 surfaces.push_back(surface);
446
447 ul.unlock();
448- emit_change_notification();
449+ observers.surfaces_reordered();
450
451 return;
452 }
453@@ -222,3 +216,69 @@
454
455 BOOST_THROW_EXCEPTION(std::runtime_error("Invalid surface"));
456 }
457+
458+void ms::SurfaceStack::add_observer(std::shared_ptr<ms::Observer> const& observer)
459+{
460+ observers.add_observer(observer);
461+
462+ // Notify observer of existing surfaces
463+ {
464+ std::unique_lock<decltype(guard)> ul(guard);
465+ for (auto &layer : layers_by_depth)
466+ {
467+ for (auto &surface : layer.second)
468+ observer->surface_added(surface);
469+ }
470+ }
471+}
472+
473+void ms::SurfaceStack::remove_observer(std::weak_ptr<ms::Observer> const& observer)
474+{
475+ observers.remove_observer(observer);
476+}
477+
478+void ms::Observers::surface_added(std::shared_ptr<ms::Surface> const& surface)
479+{
480+ std::unique_lock<decltype(mutex)> lg(mutex);
481+
482+ for (auto observer : observers)
483+ observer->surface_added(surface);
484+}
485+
486+void ms::Observers::surface_removed(std::shared_ptr<ms::Surface> const& surface)
487+{
488+ std::unique_lock<decltype(mutex)> lg(mutex);
489+
490+ for (auto observer : observers)
491+ observer->surface_removed(surface);
492+}
493+
494+void ms::Observers::surfaces_reordered()
495+{
496+ std::unique_lock<decltype(mutex)> lg(mutex);
497+
498+ for (auto observer : observers)
499+ observer->surfaces_reordered();
500+}
501+
502+void ms::Observers::add_observer(std::shared_ptr<ms::Observer> const& observer)
503+{
504+ std::unique_lock<decltype(mutex)> lg(mutex);
505+
506+ observers.push_back(observer);
507+}
508+
509+void ms::Observers::remove_observer(std::weak_ptr<ms::Observer> const& observer)
510+{
511+ std::unique_lock<decltype(mutex)> lg(mutex);
512+
513+ auto o = observer.lock();
514+ if (!o)
515+ BOOST_THROW_EXCEPTION(std::logic_error("Invalid observer (destroyed)"));
516+
517+ auto it = std::find(observers.begin(), observers.end(), o);
518+ if (it == observers.end())
519+ BOOST_THROW_EXCEPTION(std::runtime_error("Invalid observer (not previously added)"));
520+
521+ observers.erase(it);
522+}
523
524=== modified file 'src/server/scene/surface_stack.h'
525--- src/server/scene/surface_stack.h 2014-04-15 16:41:27 +0000
526+++ src/server/scene/surface_stack.h 2014-04-23 22:49:30 +0000
527@@ -23,6 +23,7 @@
528
529 #include "mir/compositor/scene.h"
530 #include "mir/scene/depth_id.h"
531+#include "mir/scene/observer.h"
532 #include "mir/input/input_targets.h"
533
534 #include <memory>
535@@ -57,6 +58,22 @@
536 class BasicSurface;
537 class SceneReport;
538
539+class Observers : public Observer
540+{
541+public:
542+ // ms::Observer
543+ void surface_added(std::shared_ptr<Surface> const& surface) override;
544+ void surface_removed(std::shared_ptr<Surface> const& surface) override;
545+ void surfaces_reordered();
546+
547+ void add_observer(std::shared_ptr<Observer> const& observer);
548+ void remove_observer(std::weak_ptr<Observer> const& observer);
549+
550+private:
551+ std::mutex mutex;
552+ std::vector<std::shared_ptr<Observer>> observers;
553+};
554+
555 class SurfaceStack : public compositor::Scene, public input::InputTargets, public SurfaceStackModel
556 {
557 public:
558@@ -67,7 +84,6 @@
559
560 // From Scene
561 graphics::RenderableList generate_renderable_list() const;
562- virtual void set_change_callback(std::function<void()> const& f);
563
564 // From InputTargets
565 void for_each(std::function<void(std::shared_ptr<input::Surface> const&)> const& callback);
566@@ -80,23 +96,23 @@
567 std::shared_ptr<Surface> const& surface,
568 DepthId depth,
569 input::InputReceptionMode input_mode) override;
570+
571+ void add_observer(std::shared_ptr<Observer> const& observer) override;
572+ void remove_observer(std::weak_ptr<Observer> const& observer) override;
573
574 private:
575 SurfaceStack(const SurfaceStack&) = delete;
576 SurfaceStack& operator=(const SurfaceStack&) = delete;
577
578- void emit_change_notification();
579-
580 std::mutex mutable guard;
581+
582 std::shared_ptr<InputRegistrar> const input_registrar;
583 std::shared_ptr<SceneReport> const report;
584- std::function<void()> const change_cb;
585
586 typedef std::vector<std::shared_ptr<Surface>> Layer;
587 std::map<DepthId, Layer> layers_by_depth;
588
589- std::mutex notify_change_mutex;
590- std::function<void()> notify_change;
591+ Observers observers;
592 };
593
594 }
595
596=== modified file 'tests/integration-tests/test_surface_first_frame_sync.cpp'
597--- tests/integration-tests/test_surface_first_frame_sync.cpp 2014-04-16 03:38:42 +0000
598+++ tests/integration-tests/test_surface_first_frame_sync.cpp 2014-04-23 22:49:30 +0000
599@@ -24,6 +24,7 @@
600 #include "mir/compositor/display_buffer_compositor.h"
601 #include "mir/compositor/display_buffer_compositor_factory.h"
602 #include "mir/compositor/scene.h"
603+#include "mir/scene/legacy_scene_change_notification.h"
604
605 #include "mir_test_framework/display_server_test_fixture.h"
606 #include "mir_test_doubles/null_display.h"
607@@ -54,10 +55,10 @@
608 class SynchronousCompositor : public mc::Compositor
609 {
610 public:
611- SynchronousCompositor(std::shared_ptr<mg::Display> const& display,
612+ SynchronousCompositor(std::shared_ptr<mg::Display> const& display_,
613 std::shared_ptr<mc::Scene> const& scene,
614 std::shared_ptr<mc::DisplayBufferCompositorFactory> const& db_compositor_factory)
615- : display{display},
616+ : display{display_},
617 scene{scene}
618 {
619 display->for_each_display_buffer(
620@@ -65,28 +66,31 @@
621 {
622 display_buffer_compositor_map[&display_buffer] = db_compositor_factory->create_compositor_for(display_buffer);
623 });
624-}
625+
626+ observer = std::make_shared<ms::LegacySceneChangeNotification>([this]() {
627+ display->for_each_display_buffer([this](mg::DisplayBuffer& display_buffer)
628+ {
629+ display_buffer_compositor_map[&display_buffer]->composite();
630+ });
631+ });
632+ }
633
634 void start()
635 {
636- scene->set_change_callback([this]()
637- {
638- display->for_each_display_buffer([this](mg::DisplayBuffer& display_buffer)
639- {
640- display_buffer_compositor_map[&display_buffer]->composite();
641- });
642- });
643+ scene->add_observer(observer);
644 }
645
646 void stop()
647 {
648- scene->set_change_callback([]{});
649+ scene->remove_observer(observer);
650 }
651
652 private:
653 std::shared_ptr<mg::Display> const display;
654 std::shared_ptr<mc::Scene> const scene;
655 std::unordered_map<mg::DisplayBuffer*,std::unique_ptr<mc::DisplayBufferCompositor>> display_buffer_compositor_map;
656+
657+ std::shared_ptr<ms::Observer> observer;
658 };
659
660 class StubRenderer : public mtd::StubRenderer
661
662=== modified file 'tests/unit-tests/compositor/test_default_display_buffer_compositor.cpp'
663--- tests/unit-tests/compositor/test_default_display_buffer_compositor.cpp 2014-04-16 03:38:42 +0000
664+++ tests/unit-tests/compositor/test_default_display_buffer_compositor.cpp 2014-04-23 22:49:30 +0000
665@@ -39,9 +39,11 @@
666 namespace mg = mir::graphics;
667 namespace mc = mir::compositor;
668 namespace geom = mir::geometry;
669+namespace ms = mir::scene;
670+namespace mr = mir::report;
671+
672 namespace mt = mir::test;
673 namespace mtd = mir::test::doubles;
674-namespace mr = mir::report;
675
676 namespace
677 {
678@@ -58,7 +60,12 @@
679 return renderlist;
680 }
681
682- void set_change_callback(std::function<void()> const&) {}
683+ void add_observer(std::shared_ptr<ms::Observer> const& /* observer */) override
684+ {
685+ }
686+ void remove_observer(std::weak_ptr<ms::Observer> const& /* observer */) override
687+ {
688+ }
689
690 void change(mg::RenderableList const& new_renderlist)
691 {
692
693=== modified file 'tests/unit-tests/compositor/test_multi_threaded_compositor.cpp'
694--- tests/unit-tests/compositor/test_multi_threaded_compositor.cpp 2014-04-16 14:49:54 +0000
695+++ tests/unit-tests/compositor/test_multi_threaded_compositor.cpp 2014-04-23 22:49:30 +0000
696@@ -17,10 +17,13 @@
697 */
698
699 #include "src/server/compositor/multi_threaded_compositor.h"
700+#include "src/server/report/null_report_factory.h"
701+
702 #include "mir/compositor/display_buffer_compositor.h"
703 #include "mir/compositor/scene.h"
704 #include "mir/compositor/display_buffer_compositor_factory.h"
705-#include "src/server/report/null_report_factory.h"
706+#include "mir/scene/observer.h"
707+
708 #include "mir_test_doubles/null_display.h"
709 #include "mir_test_doubles/null_display_buffer.h"
710 #include "mir_test_doubles/mock_display_buffer.h"
711@@ -30,6 +33,8 @@
712 #include "mir_test_doubles/null_display_buffer_compositor_factory.h"
713 #include "mir_test/spin_wait.h"
714
715+#include <boost/throw_exception.hpp>
716+
717 #include <unordered_map>
718 #include <unordered_set>
719 #include <thread>
720@@ -42,9 +47,10 @@
721
722 namespace mc = mir::compositor;
723 namespace mg = mir::graphics;
724+namespace ms = mir::scene;
725+namespace mr = mir::report;
726 namespace geom = mir::geometry;
727 namespace mtd = mir::test::doubles;
728-namespace mr = mir::report;
729
730 namespace
731 {
732@@ -89,9 +95,8 @@
733 {
734 public:
735 StubScene(mg::RenderableList const& list)
736- : callback{[]{}},
737- throw_on_set_callback_{false},
738- renderable_list{list}
739+ : renderable_list{list},
740+ throw_on_add_observer_{false}
741 {
742 }
743 StubScene() : StubScene(mg::RenderableList{}) {}
744@@ -101,38 +106,51 @@
745 return renderable_list;
746 }
747
748- void set_change_callback(std::function<void()> const& f)
749- {
750- if (throw_on_set_callback_)
751- throw std::runtime_error("");
752- std::lock_guard<std::mutex> lock{callback_mutex};
753- assert(f);
754- callback = f;
755+ void add_observer(std::shared_ptr<ms::Observer> const& observer_)
756+ {
757+ std::lock_guard<std::mutex> lock{observer_mutex};
758+
759+ if (throw_on_add_observer_)
760+ BOOST_THROW_EXCEPTION(std::runtime_error(""));
761+
762+ assert(!observer);
763+ observer = observer_;
764+ }
765+
766+ void remove_observer(std::weak_ptr<ms::Observer> const& /* observer */)
767+ {
768+ std::lock_guard<std::mutex> lock{observer_mutex};
769+ observer.reset();
770 }
771
772 void emit_change_event()
773 {
774 {
775- std::lock_guard<std::mutex> lock{callback_mutex};
776- callback();
777+ std::lock_guard<std::mutex> lock{observer_mutex};
778+
779+ // Any old event will do.
780+ if (observer)
781+ observer->surfaces_reordered();
782 }
783 /* Reduce run-time under valgrind */
784 std::this_thread::yield();
785 }
786
787- void throw_on_set_callback(bool flag)
788+ void throw_on_add_observer(bool flag)
789 {
790- throw_on_set_callback_ = flag;
791+ throw_on_add_observer_ = flag;
792 }
793
794 void lock() {}
795 void unlock() {}
796
797 private:
798- std::function<void()> callback;
799- std::mutex callback_mutex;
800- bool throw_on_set_callback_;
801+ std::mutex observer_mutex;
802 mg::RenderableList renderable_list;
803+
804+ std::shared_ptr<ms::Observer> observer;
805+
806+ bool throw_on_add_observer_;
807 };
808
809 class RecordingDisplayBufferCompositor : public mc::DisplayBufferCompositor
810@@ -564,19 +582,22 @@
811
812 TEST(MultiThreadedCompositor, double_start_or_stop_ignored)
813 {
814- using namespace testing;
815+ using namespace ::testing;
816
817 unsigned int const nbuffers{3};
818 auto display = std::make_shared<StubDisplayWithMockBuffers>(nbuffers);
819 auto mock_scene = std::make_shared<mtd::MockScene>();
820 auto db_compositor_factory = std::make_shared<mtd::NullDisplayBufferCompositorFactory>();
821 auto mock_report = std::make_shared<testing::NiceMock<mtd::MockCompositorReport>>();
822+
823 EXPECT_CALL(*mock_report, started())
824 .Times(1);
825 EXPECT_CALL(*mock_report, stopped())
826 .Times(1);
827- EXPECT_CALL(*mock_scene, set_change_callback(_))
828- .Times(2);
829+ EXPECT_CALL(*mock_scene, add_observer(_))
830+ .Times(1);
831+ EXPECT_CALL(*mock_scene, remove_observer(_))
832+ .Times(1);
833 EXPECT_CALL(*mock_scene, generate_renderable_list())
834 .Times(AtLeast(0))
835 .WillRepeatedly(Return(mg::RenderableList{}));
836@@ -632,11 +653,11 @@
837 auto db_compositor_factory = std::make_shared<RecordingDisplayBufferCompositorFactory>();
838 mc::MultiThreadedCompositor compositor{display, scene, db_compositor_factory, null_report, true};
839
840- scene->throw_on_set_callback(true);
841+ scene->throw_on_add_observer(true);
842
843 EXPECT_THROW(compositor.start(), std::runtime_error);
844
845- scene->throw_on_set_callback(false);
846+ scene->throw_on_add_observer(false);
847
848 /* No point in running the rest of the test if it throws again */
849 ASSERT_NO_THROW(compositor.start());
850
851=== modified file 'tests/unit-tests/scene/CMakeLists.txt'
852--- tests/unit-tests/scene/CMakeLists.txt 2014-03-06 06:05:17 +0000
853+++ tests/unit-tests/scene/CMakeLists.txt 2014-04-23 22:49:30 +0000
854@@ -14,6 +14,7 @@
855 ${CMAKE_CURRENT_SOURCE_DIR}/test_basic_surface.cpp
856 ${CMAKE_CURRENT_SOURCE_DIR}/test_surface_stack.cpp
857 ${CMAKE_CURRENT_SOURCE_DIR}/test_surface_controller.cpp
858+ ${CMAKE_CURRENT_SOURCE_DIR}/test_legacy_scene_change_notification.cpp
859 )
860
861 set(UNIT_TEST_SOURCES ${UNIT_TEST_SOURCES} PARENT_SCOPE)
862
863=== added file 'tests/unit-tests/scene/test_legacy_scene_change_notification.cpp'
864--- tests/unit-tests/scene/test_legacy_scene_change_notification.cpp 1970-01-01 00:00:00 +0000
865+++ tests/unit-tests/scene/test_legacy_scene_change_notification.cpp 2014-04-23 22:49:30 +0000
866@@ -0,0 +1,96 @@
867+/*
868+ * Copyright © 2014 Canonical Ltd.
869+ *
870+ * This program is free software: you can redistribute it and/or modify
871+ * it under the terms of the GNU General Public License version 3 as
872+ * published by the Free Software Foundation.
873+ *
874+ * This program is distributed in the hope that it will be useful,
875+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
876+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
877+ * GNU General Public License for more details.
878+ *
879+ * You should have received a copy of the GNU General Public License
880+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
881+ *
882+ * Authored by: Robert Carr <robert.carr@canonical.com>
883+ */
884+
885+#include "mir/scene/legacy_scene_change_notification.h"
886+#include "mir/scene/surface_observer.h"
887+
888+#include "mir_test/fake_shared.h"
889+#include "mir_test_doubles/mock_surface.h"
890+
891+#include <gtest/gtest.h>
892+#include <gmock/gmock.h>
893+
894+namespace ms = mir::scene;
895+namespace mt = mir::test;
896+namespace mtd = mt::doubles;
897+
898+namespace
899+{
900+struct MockCallback
901+{
902+ MOCK_METHOD0(invoke, void());
903+};
904+}
905+
906+TEST(LegacySceneChangeNotificationTest, fowards_all_observations_to_callback)
907+{
908+ MockCallback callback;
909+ ms::LegacySceneChangeNotification observer([&](){ callback.invoke(); });
910+
911+ testing::NiceMock<mtd::MockSurface> surface;
912+
913+ EXPECT_CALL(callback, invoke()).Times(3);
914+ observer.surface_added(mt::fake_shared(surface));
915+ observer.surface_removed(mt::fake_shared(surface));
916+ observer.surfaces_reordered();
917+}
918+
919+TEST(LegacySceneChangeNotificationTest, registers_observer_with_surfaces)
920+{
921+ using namespace ::testing;
922+
923+ mtd::MockSurface surface;
924+ ms::LegacySceneChangeNotification observer([](){});
925+
926+ EXPECT_CALL(surface, add_observer(_)).Times(1);
927+ observer.surface_added(mt::fake_shared(surface));
928+}
929+
930+TEST(LegacySceneChangeNotification, observes_surface_changes)
931+{
932+ using namespace ::testing;
933+
934+ mtd::MockSurface surface;
935+ MockCallback callback;
936+ ms::LegacySceneChangeNotification observer([&](){ callback.invoke(); });
937+
938+ std::shared_ptr<ms::SurfaceObserver> surface_observer;
939+ EXPECT_CALL(surface, add_observer(_)).Times(1)
940+ .WillOnce(SaveArg<0>(&surface_observer));
941+
942+ // Once for surface added and once for surface change
943+ EXPECT_CALL(callback, invoke()).Times(2);
944+ observer.surface_added(mt::fake_shared(surface));
945+
946+ surface_observer->frame_posted();
947+}
948+
949+TEST(LegacySceneChangeNotification, destroying_observer_unregisters_surface_observers)
950+{
951+ using namespace ::testing;
952+
953+ auto surface = std::make_shared<mtd::MockSurface>();
954+
955+ EXPECT_CALL(*surface, add_observer(_)).Times(1);
956+ EXPECT_CALL(*surface, remove_observer(_)).Times(1);
957+
958+ {
959+ ms::LegacySceneChangeNotification observer([](){});
960+ observer.surface_added(surface);
961+ }
962+}
963
964=== modified file 'tests/unit-tests/scene/test_surface_stack.cpp'
965--- tests/unit-tests/scene/test_surface_stack.cpp 2014-04-15 05:31:19 +0000
966+++ tests/unit-tests/scene/test_surface_stack.cpp 2014-04-23 22:49:30 +0000
967@@ -19,6 +19,7 @@
968 #include "src/server/scene/surface_stack.h"
969 #include "mir/graphics/buffer_properties.h"
970 #include "mir/geometry/rectangle.h"
971+#include "mir/scene/observer.h"
972 #include "mir/scene/surface_creation_parameters.h"
973 #include "src/server/report/null_report_factory.h"
974 #include "src/server/scene/basic_surface.h"
975@@ -80,12 +81,18 @@
976 int const c_fd;
977 };
978
979-class MockCallback
980+struct MockCallback
981 {
982-public:
983 MOCK_METHOD0(call, void());
984 };
985
986+struct MockSceneObserver : public ms::Observer
987+{
988+ MOCK_METHOD1(surface_added, void(std::shared_ptr<ms::Surface> const&));
989+ MOCK_METHOD1(surface_removed, void(std::shared_ptr<ms::Surface> const&));
990+ MOCK_METHOD0(surfaces_reordered, void());
991+};
992+
993 struct SurfaceStack : public ::testing::Test
994 {
995 void SetUp()
996@@ -333,6 +340,107 @@
997 stack.remove_surface(surface);
998 }
999
1000+TEST_F(SurfaceStack, scene_observer_notified_of_add_and_remove)
1001+{
1002+ using namespace ::testing;
1003+
1004+ ms::SurfaceStack stack(
1005+ mt::fake_shared(input_registrar), report);
1006+ MockSceneObserver observer;
1007+
1008+ InSequence seq;
1009+ EXPECT_CALL(observer, surface_added(Eq(stub_surface1))).Times(1);
1010+ EXPECT_CALL(observer, surface_removed(Eq(stub_surface1)))
1011+ .Times(1);
1012+
1013+ stack.add_observer(mt::fake_shared(observer));
1014+
1015+ stack.add_surface(stub_surface1, default_params.depth, default_params.input_mode);
1016+ stack.remove_surface(stub_surface1);
1017+}
1018+
1019+TEST_F(SurfaceStack, multiple_observers)
1020+{
1021+ using namespace ::testing;
1022+
1023+ ms::SurfaceStack stack(
1024+ mt::fake_shared(input_registrar), report);
1025+ MockSceneObserver observer1, observer2;
1026+
1027+ InSequence seq;
1028+ EXPECT_CALL(observer1, surface_added(Eq(stub_surface1))).Times(1);
1029+ EXPECT_CALL(observer2, surface_added(Eq(stub_surface1))).Times(1);
1030+
1031+ stack.add_observer(mt::fake_shared(observer1));
1032+ stack.add_observer(mt::fake_shared(observer2));
1033+
1034+ stack.add_surface(stub_surface1, default_params.depth, default_params.input_mode);
1035+}
1036+
1037+TEST_F(SurfaceStack, remove_scene_observer)
1038+{
1039+ using namespace ::testing;
1040+
1041+ ms::SurfaceStack stack(
1042+ mt::fake_shared(input_registrar), report);
1043+ MockSceneObserver observer;
1044+
1045+ InSequence seq;
1046+ EXPECT_CALL(observer, surface_added(Eq(stub_surface1))).Times(1);
1047+ // We remove the scene observer before removing the surface, and thus
1048+ // expect to NOT see the surface_removed call
1049+ EXPECT_CALL(observer, surface_removed(Eq(stub_surface1)))
1050+ .Times(0);
1051+
1052+ stack.add_observer(mt::fake_shared(observer));
1053+
1054+ stack.add_surface(stub_surface1, default_params.depth, default_params.input_mode);
1055+ stack.remove_observer(mt::fake_shared(observer));
1056+
1057+ stack.remove_surface(stub_surface1);
1058+}
1059+
1060+// Many clients of the scene observer wish to install surface observers to monitor surface
1061+// notifications. We offer them a surface_added event for existing surfaces to give them
1062+// a chance to do this.
1063+TEST_F(SurfaceStack, scene_observer_informed_of_existing_surfaces)
1064+{
1065+ using namespace ::testing;
1066+
1067+ using namespace ::testing;
1068+
1069+ ms::SurfaceStack stack(
1070+ mt::fake_shared(input_registrar), report);
1071+ MockSceneObserver observer;
1072+
1073+ InSequence seq;
1074+ EXPECT_CALL(observer, surface_added(Eq(stub_surface1))).Times(1);
1075+
1076+ stack.add_surface(stub_surface1, default_params.depth, default_params.input_mode);
1077+
1078+ stack.add_observer(mt::fake_shared(observer));
1079+}
1080+
1081+TEST_F(SurfaceStack, surfaces_reordered)
1082+{
1083+ using namespace ::testing;
1084+
1085+ ms::SurfaceStack stack(
1086+ mt::fake_shared(input_registrar), report);
1087+ MockSceneObserver observer;
1088+
1089+ EXPECT_CALL(observer, surface_added(_)).Times(AnyNumber());
1090+ EXPECT_CALL(observer, surface_removed(_)).Times(AnyNumber());
1091+
1092+ EXPECT_CALL(observer, surfaces_reordered()).Times(1);
1093+
1094+ stack.add_observer(mt::fake_shared(observer));
1095+
1096+ stack.add_surface(stub_surface1, default_params.depth, default_params.input_mode);
1097+ stack.add_surface(stub_surface2, default_params.depth, default_params.input_mode);
1098+ stack.raise(stub_surface1);
1099+}
1100+
1101 TEST_F(SurfaceStack, renderlist_is_snapshot_of_positioning_info)
1102 {
1103 size_t num_surfaces{3};

Subscribers

People subscribed via source and target branches