Mir

Merge lp:~kdub/mir/initial-visibility-event into lp:mir

Proposed by Kevin DuBois on 2015-10-26
Status: Superseded
Proposed branch: lp:~kdub/mir/initial-visibility-event
Merge into: lp:mir
Diff against target: 477 lines (+202/-132)
6 files modified
src/server/scene/basic_surface.h (+1/-1)
src/server/scene/surface_stack.cpp (+1/-4)
tests/acceptance-tests/test_client_focus_notification.cpp (+106/-114)
tests/acceptance-tests/test_surface_specification.cpp (+44/-10)
tests/unit-tests/scene/test_basic_surface.cpp (+44/-1)
tests/unit-tests/scene/test_surface_stack.cpp (+6/-2)
To merge this branch: bzr merge lp:~kdub/mir/initial-visibility-event
Reviewer Review Type Date Requested Status
PS Jenkins bot continuous-integration Needs Fixing on 2015-11-04
Alexandros Frantzis (community) Needs Fixing on 2015-10-27
Alan Griffiths 2015-10-26 Approve on 2015-10-26
Review via email: mp+275704@code.launchpad.net

This proposal has been superseded by a proposal from 2015-11-30.

Commit Message

Change the default value for surfaces visibily to occluded. Surfaces don't become visible until they've submitted a buffer, and the shell has placed them somewhere visible. This has the practical effect of sending a startup visibility event.

Description of the Change

Change the default value for surfaces visibily to occluded. Surfaces don't become visible until they've submitted a buffer, and the shell has placed them somewhere visible. This has the practical effect of sending a startup visibility event.

nb: interested in this now because SurfaceSpecification test suite relies that a surface is visible. Since NBS will not wait for the submit_buffer() rpc call to return its mp::Void message, we don't have a sync point anymore that guarantees the surface is visible. It seemed appropriate to change the behavior an synchronize around the visibility event in this suite, as a surface isn't visible by default.

To post a comment you must log in.
Alan Griffiths (alan-griffiths) wrote :

More reasonable than current behaviour.

review: Approve
3060. By Kevin DuBois on 2015-10-26

fix problem for clang

3061. By Kevin DuBois on 2015-10-26

merge in mir

Alexandros Frantzis (afrantzis) wrote :

The change in behavior is fine.

[ FAILED ] ClientFocusNotification.a_surface_is_notified_of_receiving_focus

We need to make the tests more resilient to differences in the ordering and quantity of events we expect, since differences in system load and thread scheduling will produce different results.

(The FD leaks seen in the logs are just a consequence of the tests not shutting down cleanly, they are not the core cause of the failures)

review: Needs Fixing
3062. By Kevin DuBois on 2015-10-28

completely rewrite the ClientFocusNotification to be more robust and not to fork off clients

3063. By Kevin DuBois on 2015-11-04

merge in mir

3064. By Kevin DuBois on 2015-11-04

remove spurious change

3065. By Kevin DuBois on 2015-11-04

roll back small change and accommodate gmock not knowing how to print an object

Kevin DuBois (kdub) wrote :

current logic is slightly improper (visible() and visibility attribute are a bit confusing) -> WIP

3066. By Kevin DuBois on 2015-11-25

merge in mir

3067. By Kevin DuBois on 2015-11-30

merge in mir

3068. By Kevin DuBois on 2015-11-30

merge in mir

3069. By Kevin DuBois on 2015-11-30

revert src

3070. By Kevin DuBois on 2015-11-30

change the default value

3071. By Kevin DuBois on 2015-11-30

test to pass

3072. By Kevin DuBois on 2015-11-30

clean up remaining tests,get them to pass

3073. By Kevin DuBois on 2015-11-30

cleanups

3074. By Kevin DuBois on 2015-11-30

further cleanups

3075. By Kevin DuBois on 2015-11-30

merge in mir

3076. By Kevin DuBois on 2015-11-30

roll back change to test

3077. By Kevin DuBois on 2015-11-30

modify test so that occlusion is always modified via the rendering trackers, as designed

3078. By Kevin DuBois on 2015-12-02

adopt Albertos suggestion to match the sequence of events in the surface

3079. By Kevin DuBois on 2015-12-02

merge in mir

Unmerged revisions

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1=== modified file 'src/server/scene/basic_surface.h'
2--- src/server/scene/basic_surface.h 2015-10-20 04:57:13 +0000
3+++ src/server/scene/basic_surface.h 2015-11-30 18:16:24 +0000
4@@ -181,7 +181,7 @@
5 int swapinterval_ = 1;
6 MirSurfaceFocusState focus_ = mir_surface_unfocused;
7 int dpi_ = 0;
8- MirSurfaceVisibility visibility_ = mir_surface_visibility_exposed;
9+ MirSurfaceVisibility visibility_ = mir_surface_visibility_occluded;
10 MirOrientationMode pref_orientation_mode = mir_orientation_mode_any;
11
12 std::unique_ptr<CursorStreamImageAdapter> const cursor_stream_adapter;
13
14=== modified file 'src/server/scene/surface_stack.cpp'
15--- src/server/scene/surface_stack.cpp 2015-11-05 11:51:04 +0000
16+++ src/server/scene/surface_stack.cpp 2015-11-30 18:16:24 +0000
17@@ -160,10 +160,7 @@
18 int result = scene_changed ? 1 : 0;
19 for (auto const& surface : surfaces)
20 {
21- // TODO: Rename mir_surface_attrib_visibility as it's obviously
22- // confusing with visible()
23- if (surface->visible() &&
24- surface->query(mir_surface_attrib_visibility) == mir_surface_visibility_exposed)
25+ if (surface->visible())
26 {
27 auto const tracker = rendering_trackers.find(surface.get());
28 if (tracker != rendering_trackers.end() && tracker->second->is_exposed_in(id))
29
30=== modified file 'tests/acceptance-tests/test_client_focus_notification.cpp'
31--- tests/acceptance-tests/test_client_focus_notification.cpp 2015-10-02 04:27:35 +0000
32+++ tests/acceptance-tests/test_client_focus_notification.cpp 2015-11-30 18:16:24 +0000
33@@ -1,5 +1,5 @@
34 /*
35- * Copyright © 2013-2014 Canonical Ltd.
36+ * Copyright © 2015 Canonical Ltd.
37 *
38 * This program is free software: you can redistribute it and/or modify
39 * it under the terms of the GNU General Public License version 3 as
40@@ -13,7 +13,7 @@
41 * You should have received a copy of the GNU General Public License
42 * along with this program. If not, see <http://www.gnu.org/licenses/>.
43 *
44- * Authored by: Robert Carr <robert.carr@canonical.com>
45+ * Authored by: Kevin DuBois <kevin.dubois@canonical.com>
46 */
47
48 #include "mir_toolkit/mir_client_library.h"
49@@ -21,7 +21,7 @@
50 #include "mir/test/wait_condition.h"
51 #include "mir/test/event_matchers.h"
52
53-#include "mir_test_framework/interprocess_client_server_test.h"
54+#include "mir_test_framework/connected_client_headless_server.h"
55 #include "mir_test_framework/process.h"
56 #include "mir/test/cross_process_sync.h"
57
58@@ -35,134 +35,126 @@
59
60 namespace
61 {
62-struct MockEventObserver
63+struct FocusLogger
64 {
65- MOCK_METHOD1(see, void(MirEvent const*));
66+ void log_focus_event(MirSurface* surface, MirSurfaceFocusState state)
67+ {
68+ std::lock_guard<std::mutex> lk(mutex);
69+ focus_events[surface].push_back(state);
70+ cv.notify_all();
71+ }
72+
73+ void wait_for_num_focus_events(unsigned int num, MirSurface* surface, std::chrono::seconds until)
74+ {
75+ std::unique_lock<std::mutex> lk(mutex);
76+ if (!cv.wait_for(lk, until, [this, num, surface]
77+ {
78+ return ((focus_events.find(surface) != focus_events.end()) &&
79+ (focus_events[surface].size() >= num));
80+ }))
81+ {
82+ throw std::logic_error("timeout waiting for events");
83+ }
84+ }
85+
86+ std::vector<MirSurfaceFocusState> events_for(MirSurface* surface)
87+ {
88+ std::lock_guard<std::mutex> lk(mutex);
89+ if (focus_events.find(surface) == focus_events.end())
90+ throw std::logic_error("no events");
91+ return focus_events[surface];
92+ }
93+
94+ static void handle_event(MirSurface* surface, MirEvent const* ev, void* context)
95+ {
96+ if (mir_event_type_surface == mir_event_get_type(ev))
97+ {
98+ auto surface_ev = mir_event_get_surface_event(ev);
99+ if (mir_surface_attrib_focus == mir_surface_event_get_attribute(surface_ev))
100+ {
101+ auto self = static_cast<FocusLogger*>(context);
102+ self->log_focus_event(surface,
103+ static_cast<MirSurfaceFocusState>(mir_surface_event_get_attribute_value(surface_ev)));
104+ }
105+ }
106+ }
107+private:
108+ std::mutex mutex;
109+ std::condition_variable cv;
110+ std::map<MirSurface*, std::vector<MirSurfaceFocusState>> focus_events;
111 };
112
113-struct ClientFocusNotification : mtf::InterprocessClientServerTest
114+struct FocusSurface
115 {
116- void SetUp() override
117- {
118- mtf::InterprocessClientServerTest::SetUp();
119- run_in_server([]{});
120- }
121-
122- MockEventObserver observer;
123- mt::WaitCondition all_events_received;
124-
125- void connect_and_create_surface()
126- {
127- MirConnection *connection = mir_connect_sync(mir_test_socket, __PRETTY_FUNCTION__);
128- ASSERT_TRUE(mir_connection_is_valid(connection));
129-
130+ FocusSurface(MirConnection* connection, std::shared_ptr<FocusLogger> const& logger) :
131+ connection(connection)
132+ {
133 auto spec = mir_connection_create_spec_for_normal_surface(connection, 100, 100, mir_pixel_format_abgr_8888);
134+ mir_surface_spec_set_event_handler(spec, FocusLogger::handle_event, logger.get());
135
136- mir_wait_for(mir_surface_create(spec, surface_created, this));
137+ surface = mir_surface_create_sync(spec);
138 mir_surface_spec_release(spec);
139+
140 mir_buffer_stream_swap_buffers_sync(mir_surface_get_buffer_stream(surface));
141-
142- all_events_received.wait_for_at_most_seconds(60);
143+ }
144+
145+ ~FocusSurface()
146+ {
147+ if (!released) release();
148+ }
149+
150+ MirSurface* native_handle() const
151+ {
152+ return surface;
153+ }
154+
155+ void release()
156+ {
157 mir_surface_release_sync(surface);
158 mir_connection_release(connection);
159- }
160-
161-private:
162-
163- MirSurface *surface;
164-
165- static void handle_event(MirSurface* /* surface */, MirEvent const* ev, void* context)
166- {
167- auto self = static_cast<ClientFocusNotification*>(context);
168- self->observer.see(ev);
169- }
170-
171- static void surface_created(MirSurface *surface_, void *ctx)
172- {
173- auto self = static_cast<ClientFocusNotification*>(ctx);
174-
175- self->surface = surface_;
176- // We need to set the event delegate from the surface_created
177- // callback so we can block the reading of new events
178- // until we are ready
179- mir_surface_set_event_handler(surface_, handle_event, self);
180- }
181+ released = true;
182+ }
183+
184+ MirConnection* const connection;
185+ MirSurface* surface;
186+ bool released{false};
187+};
188+
189+struct ClientFocusNotification : mtf::ConnectedClientHeadlessServer
190+{
191+ std::shared_ptr<FocusLogger> logger{std::make_shared<FocusLogger>()};
192 };
193 }
194
195 TEST_F(ClientFocusNotification, a_surface_is_notified_of_receiving_focus)
196 {
197- run_in_client([&]
198- {
199- InSequence s;
200- EXPECT_CALL(observer, see(_)); //ignore scaling events
201- EXPECT_CALL(observer, see(mt::SurfaceEvent(mir_surface_attrib_focus, mir_surface_focused))).Times(1)
202- .WillOnce(mt::WakeUp(&all_events_received));
203- // We may not see mir_surface_unfocused before connection closes
204- EXPECT_CALL(observer, see(mt::SurfaceEvent(mir_surface_attrib_focus, mir_surface_unfocused))).Times(AtMost(1));
205-
206- connect_and_create_surface();
207- });
208-}
209-
210-namespace
211-{
212-
213-ACTION_P(SignalFence, fence)
214-{
215- fence->try_signal_ready_for();
216-}
217-
218+ FocusSurface surface(mir_connect_sync(new_connection().c_str(), __PRETTY_FUNCTION__), logger);
219+ surface.release();
220+
221+ logger->wait_for_num_focus_events(2, surface.native_handle(), std::chrono::seconds(5));
222+
223+ auto log = logger->events_for(surface.native_handle());
224+ EXPECT_THAT(log[0], Eq(mir_surface_focused));
225+ EXPECT_THAT(log[1], Eq(mir_surface_unfocused));
226 }
227
228 TEST_F(ClientFocusNotification, two_surfaces_are_notified_of_gaining_and_losing_focus)
229 {
230- // We use this for synchronization to ensure the two clients
231- // are launched in a defined order.
232- mt::CrossProcessSync ready_for_second_client;
233-
234- auto const client_one = new_client_process([&]
235- {
236- InSequence seq;
237- EXPECT_CALL(observer, see(_)); //ignore scaling events
238- // We should receive focus as we are created
239- EXPECT_CALL(observer, see(mt::SurfaceEvent(mir_surface_attrib_focus,
240- mir_surface_focused))).Times(1)
241- .WillOnce(SignalFence(&ready_for_second_client));
242-
243- // And lose it as the second surface is created
244- EXPECT_CALL(observer, see(mt::SurfaceEvent(mir_surface_attrib_focus,
245- mir_surface_unfocused))).Times(1);
246- // And regain it when the second surface is closed
247- EXPECT_CALL(observer, see(mt::SurfaceEvent(mir_surface_attrib_focus,
248- mir_surface_focused))).Times(1).WillOnce(mt::WakeUp(&all_events_received));
249- // And then lose it as we are closed (but we may not see confirmation before connection closes)
250- EXPECT_CALL(observer, see(mt::SurfaceEvent(mir_surface_attrib_focus,
251- mir_surface_unfocused))).Times(AtMost(1));
252-
253- connect_and_create_surface();
254- });
255-
256- auto const client_two = new_client_process([&]
257- {
258- client_one->detach();
259- ready_for_second_client.wait_for_signal_ready_for();
260-
261- EXPECT_CALL(observer, see(_)); //ignore scaling events
262- EXPECT_CALL(observer, see(
263- mt::SurfaceEvent(mir_surface_attrib_focus, mir_surface_focused)))
264- .Times(1).WillOnce(mt::WakeUp(&all_events_received));
265- // We may not see mir_surface_unfocused before connection closes
266- EXPECT_CALL(observer, see(
267- mt::SurfaceEvent(mir_surface_attrib_focus, mir_surface_unfocused)))
268- .Times(AtMost(1));
269-
270- connect_and_create_surface();
271- });
272-
273- if (is_test_process())
274- {
275- EXPECT_THAT(client_one->wait_for_termination().exit_code, Eq(EXIT_SUCCESS));
276- EXPECT_THAT(client_two->wait_for_termination().exit_code, Eq(EXIT_SUCCESS));
277- }
278+ FocusSurface surface1(mir_connect_sync(new_connection().c_str(), __PRETTY_FUNCTION__), logger);
279+ logger->wait_for_num_focus_events(1, surface1.native_handle(), std::chrono::seconds(5));
280+ FocusSurface surface2(mir_connect_sync(new_connection().c_str(), __PRETTY_FUNCTION__), logger);
281+ surface2.release();
282+ surface1.release();
283+ logger->wait_for_num_focus_events(4, surface1.native_handle(), std::chrono::seconds(5));
284+ logger->wait_for_num_focus_events(2, surface2.native_handle(), std::chrono::seconds(5));
285+
286+ auto log = logger->events_for(surface1.native_handle());
287+ EXPECT_THAT(log[0], Eq(mir_surface_focused));
288+ EXPECT_THAT(log[1], Eq(mir_surface_unfocused));
289+ EXPECT_THAT(log[2], Eq(mir_surface_focused));
290+ EXPECT_THAT(log[3], Eq(mir_surface_unfocused));
291+
292+ log = logger->events_for(surface2.native_handle());
293+ EXPECT_THAT(log[0], Eq(mir_surface_focused));
294+ EXPECT_THAT(log[1], Eq(mir_surface_unfocused));
295 }
296
297=== modified file 'tests/acceptance-tests/test_surface_specification.cpp'
298--- tests/acceptance-tests/test_surface_specification.cpp 2015-09-23 21:49:39 +0000
299+++ tests/acceptance-tests/test_surface_specification.cpp 2015-11-30 18:16:24 +0000
300@@ -46,22 +46,60 @@
301 class SurfaceHandle
302 {
303 public:
304- explicit SurfaceHandle(MirSurface* surface) : surface{surface}
305+ explicit SurfaceHandle(MirSurfaceSpec* spec) :
306+ visible{false}
307 {
308+ mir_surface_spec_set_event_handler(spec, SurfaceHandle::event_callback, this);
309+ surface = mir_surface_create_sync(spec);
310 // Swap buffers to ensure surface is visible for event based tests
311 if (mir_surface_is_valid(surface))
312+ {
313 mir_buffer_stream_swap_buffers_sync(mir_surface_get_buffer_stream(surface));
314+
315+ std::unique_lock<std::mutex> lk(mutex);
316+ if (!cv.wait_for(lk, std::chrono::seconds(5), [this] { return visible; }))
317+ throw std::runtime_error("timeout waiting for visibility of surface");
318+ }
319 }
320
321 ~SurfaceHandle() { if (surface) mir_surface_release_sync(surface); }
322
323+ static void event_callback(MirSurface* surf, MirEvent const* ev, void* context)
324+ {
325+ if (mir_event_get_type(ev) == mir_event_type_surface)
326+ {
327+ if (mir_surface_event_get_attribute(mir_event_get_surface_event(ev)) == mir_surface_attrib_visibility)
328+ {
329+ auto ctx = reinterpret_cast<SurfaceHandle*>(context);
330+ ctx->set_visibility(surf, mir_surface_event_get_attribute_value(mir_event_get_surface_event(ev)));
331+ }
332+ }
333+ }
334+
335+ void set_visibility(MirSurface* surf, bool vis)
336+ {
337+ std::lock_guard<std::mutex> lk(mutex);
338+ if (surf != surface) return;
339+ visible = vis;
340+ cv.notify_all();
341+ }
342+
343 operator MirSurface*() const { return surface; }
344 SurfaceHandle(SurfaceHandle&& that) : surface{that.surface} { that.surface = nullptr; }
345 private:
346+ std::mutex mutex;
347+ std::condition_variable cv;
348+
349 SurfaceHandle(SurfaceHandle const&) = delete;
350 MirSurface* surface;
351+ bool visible;
352 };
353
354+//gmocks printer has problems with printing this type
355+::std::ostream& operator<<(::std::ostream& os, const SurfaceHandle& handle) {
356+ return os << static_cast<MirSurface*>(handle);
357+}
358+
359 class MockSurfaceObserver : public ms::NullSurfaceObserver
360 {
361 public:
362@@ -108,16 +146,12 @@
363 }
364
365 template<typename Specifier>
366- SurfaceHandle create_surface(Specifier const& specifier) const
367+ SurfaceHandle create_surface(Specifier const& specifier)
368 {
369- auto const spec = mir_create_surface_spec(connection);
370-
371- specifier(spec);
372-
373- auto const surface = mir_surface_create_sync(spec);
374- mir_surface_spec_release(spec);
375-
376- return SurfaceHandle{surface};
377+ auto del = [] (MirSurfaceSpec* spec) { mir_surface_spec_release(spec); };
378+ std::unique_ptr<MirSurfaceSpec, decltype(del)> spec(mir_create_surface_spec(connection), del);
379+ specifier(spec.get());
380+ return SurfaceHandle{spec.get()};
381 }
382
383 NiceMock<MockSurfaceObserver> surface_observer;
384
385=== modified file 'tests/unit-tests/scene/test_basic_surface.cpp'
386--- tests/unit-tests/scene/test_basic_surface.cpp 2015-10-07 12:06:14 +0000
387+++ tests/unit-tests/scene/test_basic_surface.cpp 2015-11-30 18:16:24 +0000
388@@ -535,8 +535,8 @@
389
390 AttributeTestParameters const surface_visibility_test_parameters{
391 mir_surface_attrib_visibility,
392+ mir_surface_visibility_occluded,
393 mir_surface_visibility_exposed,
394- mir_surface_visibility_occluded,
395 -1
396 };
397
398@@ -995,3 +995,46 @@
399 EXPECT_THAT(renderables[0], IsRenderableOfSize(size0));
400 EXPECT_THAT(renderables[1], IsRenderableOfSize(size1));
401 }
402+
403+namespace
404+{
405+struct VisibilityObserver : ms::NullSurfaceObserver
406+{
407+ void attrib_changed(MirSurfaceAttrib attrib, int value) override
408+ {
409+ if (attrib == mir_surface_attrib_visibility)
410+ {
411+ if (value == mir_surface_visibility_occluded)
412+ hides_++;
413+ else if (value == mir_surface_visibility_exposed)
414+ exposes_++;
415+ }
416+ }
417+ unsigned int exposes()
418+ {
419+ return exposes_;
420+ }
421+ unsigned int hides()
422+ {
423+ return hides_;
424+ }
425+private:
426+ unsigned int exposes_{0};
427+ unsigned int hides_{0};
428+};
429+}
430+
431+TEST_F(BasicSurfaceTest, notifies_when_first_visible)
432+{
433+ using namespace testing;
434+ auto observer = std::make_shared<VisibilityObserver>();
435+ surface.add_observer(observer);
436+
437+ EXPECT_THAT(observer->exposes(), Eq(0));
438+ EXPECT_THAT(observer->hides(), Eq(0));
439+ post_a_frame(surface);
440+ surface.configure(mir_surface_attrib_visibility, mir_surface_visibility_exposed);
441+
442+ EXPECT_THAT(observer->exposes(), Eq(1));
443+ EXPECT_THAT(observer->hides(), Eq(0));
444+}
445
446=== modified file 'tests/unit-tests/scene/test_surface_stack.cpp'
447--- tests/unit-tests/scene/test_surface_stack.cpp 2015-11-05 11:51:04 +0000
448+++ tests/unit-tests/scene/test_surface_stack.cpp 2015-11-30 18:16:24 +0000
449@@ -397,8 +397,9 @@
450 report);
451
452 stack.add_surface(surface, default_params.input_mode);
453- surface->configure(mir_surface_attrib_visibility,
454- mir_surface_visibility_occluded);
455+ auto elements = stack.scene_elements_for(this);
456+ for (auto const& elem : elements)
457+ elem->occluded();
458
459 EXPECT_EQ(0, stack.frames_pending(this));
460 post_a_frame(*surface);
461@@ -433,7 +434,9 @@
462 post_a_frame(*surface);
463 post_a_frame(*surface);
464
465+ EXPECT_EQ(3, stack.frames_pending(comp1));
466 EXPECT_EQ(3, stack.frames_pending(comp2));
467+
468 auto elements = stack.scene_elements_for(comp1);
469 for (auto const& elem : elements)
470 {
471@@ -446,6 +449,7 @@
472 elem->occluded();
473 }
474
475+ EXPECT_EQ(3, stack.frames_pending(comp1));
476 EXPECT_EQ(0, stack.frames_pending(comp2));
477 }
478

Subscribers

People subscribed via source and target branches