Mir

Merge lp:~vanvugt/mir/resize-events into lp:mir

Proposed by Daniel van Vugt
Status: Merged
Approved by: Alan Griffiths
Approved revision: no longer in the source branch.
Merged at revision: 1259
Proposed branch: lp:~vanvugt/mir/resize-events
Merge into: lp:mir
Diff against target: 275 lines (+131/-8)
7 files modified
examples/eglapp.c (+19/-4)
examples/fingerpaint.c (+12/-0)
include/shared/mir_toolkit/event.h (+12/-1)
src/server/frontend/event_sender.cpp (+1/-1)
src/server/scene/surface_impl.cpp (+9/-0)
tests/unit-tests/frontend/test_event_sender.cpp (+36/-0)
tests/unit-tests/scene/test_surface_impl.cpp (+42/-2)
To merge this branch: bzr merge lp:~vanvugt/mir/resize-events
Reviewer Review Type Date Requested Status
PS Jenkins bot (community) continuous-integration Approve
Alan Griffiths Approve
Alexandros Frantzis (community) Approve
Review via email: mp+196679@code.launchpad.net

Commit message

Add mir_event_type_resize / MirResizeEvent, allowing clients to react
immediately to surface resizing. (LP: #1227744)

Description of the change

You will see FIXME comments in the example code. I spent several days prototyping solutions to bug 1194384 to avoid the FIXMEs, before realizing it's a much bigger can of worms and beyond this scope of this work. Most importantly, the design of resize events is not affected by the presence of a fix for bug 1194384. So any sufficiently powerful toolkit (one with its own event queue and/or locking primitives) will be able to utilize MirResizeEvent immediately, without needing a fix for bug 1194384.

I admit, this was not my first choice of design. I started with "size" being a MirSurfaceAttrib with value:
    int value = (width << 16) | height;
However as nice as that was reusing the existing logic and maintaining "size" as an "attribute", it was a bit kludgy and I feared reviewers would disapprove of the shifting. Not to mention the 4-gigapixel limitation ;)

I also prototyped a solution where size was a multi-value MirSurfaceAttrib. However modifying all the code to support multi-value MirSurfaceAttrib's resulted in a mess that I did not like at all.

And then there were several other possible designs I disliked so much I did not even prototype. So this is a compromise, which I think more people will approve of.

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
Alan Griffiths (alan-griffiths) wrote :

157 +TEST(TestEventSender, sends_all_but_input_events)

I don't think this test does what you think. E.g. you can comment out the following lines and it still passes:

168 + EXPECT_CALL(*msg_sender, send(_))
169 + .Times(0);

It would be better written as a series of individual (or parameterized) tests

review: Needs Fixing
Revision history for this message
Daniel van Vugt (vanvugt) wrote :

Alan,

I don't think that's a valid argument. You can comment out asserts/expectations from any tests and they will of course pass.

The point is to verify send is never called at all for those event types. And that it is called for other event types. I just re-tested changing "Times(0)" to "Times(1)" and the test then fails. So the test is correct.

Revision history for this message
PS Jenkins bot (ps-jenkins) wrote :
review: Approve (continuous-integration)
Revision history for this message
Alan Griffiths (alan-griffiths) wrote :

> Alan,
>
> I don't think that's a valid argument. You can comment out
> asserts/expectations from any tests and they will of course pass.
>
> The point is to verify send is never called at all for those event types. And
> that it is called for other event types. I just re-tested changing "Times(0)"
> to "Times(1)" and the test then fails. So the test is correct.

Sorry, I wasn't clear.

The canonical way to write a test is /1/ setup, /2/ expectations, /3/ execution and /4/ validation.

You've mixed expectations and execution - but that doesn't change the way the fact that validation only occurs at the end. Essentially you've written:

TEST(TestEventSender, sends_all_but_input_events)
{
    using namespace testing;

    // Setup
    auto msg_sender = std::make_shared<MockMsgSender>();
    mfd::EventSender event_sender(msg_sender);
    MirEvent event;
    memset(&event, 0, sizeof event);

    // Expectations
    InSequence seq;
    EXPECT_CALL(*msg_sender, send(_))
        .Times(2);

    // Execution
    event.type = mir_event_type_surface;
    event_sender.handle_event(event);
    event.type = mir_event_type_resize;
    event_sender.handle_event(event);
    event.type = mir_event_type_key;
    event_sender.handle_event(event);
    event.type = mir_event_type_motion;
    event_sender.handle_event(event);
}

Which doesn't give any information on which events generate which calls - just that there are two of them.

Revision history for this message
Alexandros Frantzis (afrantzis) wrote :

Looks good.

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

ack

review: Approve
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
=== modified file 'examples/eglapp.c'
--- examples/eglapp.c 2013-11-21 03:16:21 +0000
+++ examples/eglapp.c 2013-11-27 10:05:11 +0000
@@ -93,8 +93,12 @@
93 lastcount = count;93 lastcount = count;
94 }94 }
9595
96 /* This is one way to handle window resizing. But in future it would be96 /*
97 better to have resize events coming from the server */97 * Querying the surface (actually the current buffer) dimensions here is
98 * the only truly safe way to be sure that the dimensions we think we
99 * have are those of the buffer being rendered to. But this should be
100 * improved in future; https://bugs.launchpad.net/mir/+bug/1194384
101 */
98 if (eglQuerySurface(egldisplay, eglsurface, EGL_WIDTH, &width) &&102 if (eglQuerySurface(egldisplay, eglsurface, EGL_WIDTH, &width) &&
99 eglQuerySurface(egldisplay, eglsurface, EGL_HEIGHT, &height))103 eglQuerySurface(egldisplay, eglsurface, EGL_HEIGHT, &height))
100 {104 {
@@ -102,7 +106,7 @@
102 }106 }
103}107}
104108
105static void mir_eglapp_handle_input(MirSurface* surface, MirEvent const* ev, void* context)109static void mir_eglapp_handle_event(MirSurface* surface, MirEvent const* ev, void* context)
106{110{
107 (void) surface;111 (void) surface;
108 (void) context;112 (void) context;
@@ -112,6 +116,17 @@
112 {116 {
113 running = 0;117 running = 0;
114 }118 }
119 else if (ev->type == mir_event_type_resize)
120 {
121 /*
122 * FIXME: https://bugs.launchpad.net/mir/+bug/1194384
123 * It is unsafe to set the width and height here because we're in a
124 * different thread to that doing the rendering. So we either need
125 * support for event queuing (directing them to another thread) or
126 * full single-threaded callbacks. (LP: #1194384).
127 */
128 printf("Resized to %dx%d\n", ev->resize.width, ev->resize.height);
129 }
115}130}
116131
117static unsigned int get_bpp(MirPixelFormat pf)132static unsigned int get_bpp(MirPixelFormat pf)
@@ -172,7 +187,7 @@
172 };187 };
173 MirEventDelegate delegate = 188 MirEventDelegate delegate =
174 {189 {
175 mir_eglapp_handle_input,190 mir_eglapp_handle_event,
176 NULL191 NULL
177 };192 };
178 EGLConfig eglconfig;193 EGLConfig eglconfig;
179194
=== modified file 'examples/fingerpaint.c'
--- examples/fingerpaint.c 2013-09-19 13:24:22 +0000
+++ examples/fingerpaint.c 2013-11-27 10:05:11 +0000
@@ -247,6 +247,18 @@
247 redraw(surface, canvas);247 redraw(surface, canvas);
248 }248 }
249 }249 }
250 else if (event->type == mir_event_type_resize)
251 {
252 /* FIXME: https://bugs.launchpad.net/mir/+bug/1194384
253 * mir_event_type_resize will arrive in a different thread to that of
254 * mir_event_type_motion, so we cannot safely redraw from this thread.
255 * Either the callbacks will need to become thread-safe, or we'd have
256 * to employ some non-trivial event queuing and inter-thread signals,
257 * which I think is beyond the scope of this example code.
258 *
259 * redraw(surface, canvas);
260 */
261 }
250}262}
251263
252static const MirDisplayOutput *find_active_output(264static const MirDisplayOutput *find_active_output(
253265
=== modified file 'include/shared/mir_toolkit/event.h'
--- include/shared/mir_toolkit/event.h 2013-11-22 13:53:36 +0000
+++ include/shared/mir_toolkit/event.h 2013-11-27 10:05:11 +0000
@@ -39,7 +39,8 @@
39{39{
40 mir_event_type_key,40 mir_event_type_key,
41 mir_event_type_motion,41 mir_event_type_motion,
42 mir_event_type_surface42 mir_event_type_surface,
43 mir_event_type_resize
43} MirEventType;44} MirEventType;
4445
45typedef enum {46typedef enum {
@@ -194,12 +195,22 @@
194 int value;195 int value;
195} MirSurfaceEvent;196} MirSurfaceEvent;
196197
198typedef struct
199{
200 MirEventType type;
201
202 int surface_id;
203 int width;
204 int height;
205} MirResizeEvent;
206
197typedef union207typedef union
198{208{
199 MirEventType type;209 MirEventType type;
200 MirKeyEvent key;210 MirKeyEvent key;
201 MirMotionEvent motion;211 MirMotionEvent motion;
202 MirSurfaceEvent surface;212 MirSurfaceEvent surface;
213 MirResizeEvent resize;
203} MirEvent;214} MirEvent;
204215
205#ifdef __cplusplus216#ifdef __cplusplus
206217
=== modified file 'src/server/frontend/event_sender.cpp'
--- src/server/frontend/event_sender.cpp 2013-10-15 08:53:10 +0000
+++ src/server/frontend/event_sender.cpp 2013-11-27 10:05:11 +0000
@@ -37,7 +37,7 @@
37void mfd::EventSender::handle_event(MirEvent const& e)37void mfd::EventSender::handle_event(MirEvent const& e)
38{38{
39 // Limit the types of events we wish to send over protobuf, for now.39 // Limit the types of events we wish to send over protobuf, for now.
40 if (e.type == mir_event_type_surface)40 if (e.type != mir_event_type_key && e.type != mir_event_type_motion)
41 {41 {
42 // In future we might send multiple events, or insert them into messages42 // In future we might send multiple events, or insert them into messages
43 // containing other responses, but for now we send them individually.43 // containing other responses, but for now we send them individually.
4444
=== modified file 'src/server/scene/surface_impl.cpp'
--- src/server/scene/surface_impl.cpp 2013-11-25 11:21:54 +0000
+++ src/server/scene/surface_impl.cpp 2013-11-27 10:05:11 +0000
@@ -247,6 +247,15 @@
247void ms::SurfaceImpl::resize(geom::Size const& size)247void ms::SurfaceImpl::resize(geom::Size const& size)
248{248{
249 surface->resize(size);249 surface->resize(size);
250
251 MirEvent e;
252 memset(&e, 0, sizeof e);
253 e.type = mir_event_type_resize;
254 e.resize.surface_id = id.as_value();
255 e.resize.width = size.width.as_int();
256 e.resize.height = size.height.as_int();
257
258 event_sink->handle_event(e);
250}259}
251260
252void ms::SurfaceImpl::set_rotation(float degrees, glm::vec3 const& axis)261void ms::SurfaceImpl::set_rotation(float degrees, glm::vec3 const& axis)
253262
=== modified file 'tests/unit-tests/frontend/test_event_sender.cpp'
--- tests/unit-tests/frontend/test_event_sender.cpp 2013-11-22 13:53:36 +0000
+++ tests/unit-tests/frontend/test_event_sender.cpp 2013-11-27 10:05:11 +0000
@@ -67,3 +67,39 @@
6767
68 sender.handle_display_config_change(config);68 sender.handle_display_config_change(config);
69}69}
70
71TEST(TestEventSender, sends_noninput_events)
72{
73 using namespace testing;
74
75 auto msg_sender = std::make_shared<MockMsgSender>();
76 mfd::EventSender event_sender(msg_sender);
77
78 MirEvent event;
79 memset(&event, 0, sizeof event);
80
81 EXPECT_CALL(*msg_sender, send(_))
82 .Times(2);
83 event.type = mir_event_type_surface;
84 event_sender.handle_event(event);
85 event.type = mir_event_type_resize;
86 event_sender.handle_event(event);
87}
88
89TEST(TestEventSender, never_sends_input_events)
90{
91 using namespace testing;
92
93 auto msg_sender = std::make_shared<MockMsgSender>();
94 mfd::EventSender event_sender(msg_sender);
95
96 MirEvent event;
97 memset(&event, 0, sizeof event);
98
99 EXPECT_CALL(*msg_sender, send(_))
100 .Times(0);
101 event.type = mir_event_type_key;
102 event_sender.handle_event(event);
103 event.type = mir_event_type_motion;
104 event_sender.handle_event(event);
105}
70106
=== modified file 'tests/unit-tests/scene/test_surface_impl.cpp'
--- tests/unit-tests/scene/test_surface_impl.cpp 2013-11-25 14:35:33 +0000
+++ tests/unit-tests/scene/test_surface_impl.cpp 2013-11-27 10:05:11 +0000
@@ -40,6 +40,7 @@
40#include "mir_test/fake_shared.h"40#include "mir_test/fake_shared.h"
41#include "mir_test/event_matchers.h"41#include "mir_test/event_matchers.h"
4242
43#include <cstring>
43#include <stdexcept>44#include <stdexcept>
44#include "gmock_set_arg.h"45#include "gmock_set_arg.h"
45#include <gmock/gmock.h>46#include <gmock/gmock.h>
@@ -70,6 +71,7 @@
70{71{
71public:72public:
72 StubSurfaceBuilder() :73 StubSurfaceBuilder() :
74 mock_surface_state(std::make_shared<mtd::MockSurfaceState>()),
73 stub_buffer_stream_(std::make_shared<mtd::StubBufferStream>()),75 stub_buffer_stream_(std::make_shared<mtd::StubBufferStream>()),
74 dummy_surface()76 dummy_surface()
75 {77 {
@@ -77,9 +79,8 @@
7779
78 std::weak_ptr<ms::BasicSurface> create_surface(msh::Session*, msh::SurfaceCreationParameters const& )80 std::weak_ptr<ms::BasicSurface> create_surface(msh::Session*, msh::SurfaceCreationParameters const& )
79 {81 {
80 auto state = std::make_shared<mtd::MockSurfaceState>();
81 dummy_surface = std::make_shared<ms::Surface>(82 dummy_surface = std::make_shared<ms::Surface>(
82 state,83 mock_surface_state,
83 stub_buffer_stream_,84 stub_buffer_stream_,
84 std::shared_ptr<mi::InputChannel>(),85 std::shared_ptr<mi::InputChannel>(),
85 report);86 report);
@@ -101,6 +102,8 @@
101 return stub_buffer_stream_;102 return stub_buffer_stream_;
102 }103 }
103104
105 std::shared_ptr<mtd::MockSurfaceState> mock_surface_state;
106
104private:107private:
105 std::shared_ptr<mtd::StubBufferStream> const stub_buffer_stream_;108 std::shared_ptr<mtd::StubBufferStream> const stub_buffer_stream_;
106 std::shared_ptr<ms::BasicSurface> dummy_surface;109 std::shared_ptr<ms::BasicSurface> dummy_surface;
@@ -296,6 +299,43 @@
296 EXPECT_EQ(mir_surface_state_fullscreen, surf.state());299 EXPECT_EQ(mir_surface_state_fullscreen, surf.state());
297}300}
298301
302bool operator==(MirEvent const& a, MirEvent const& b)
303{
304 // We will always fill unused bytes with zero, so memcmp is accurate...
305 return !memcmp(&a, &b, sizeof(MirEvent));
306}
307
308TEST_F(SurfaceImpl, emits_resize_events)
309{
310 using namespace testing;
311
312 auto sink = std::make_shared<MockEventSink>();
313 ms::SurfaceImpl surf(
314 nullptr,
315 mt::fake_shared(surface_builder),
316 std::make_shared<mtd::NullSurfaceConfigurator>(),
317 msh::a_surface(),
318 stub_id,
319 sink);
320
321 geom::Size const new_size{123, 456};
322
323 MirEvent e;
324 memset(&e, 0, sizeof e);
325 e.type = mir_event_type_resize;
326 e.resize.surface_id = stub_id.as_value();
327 e.resize.width = new_size.width.as_int();
328 e.resize.height = new_size.height.as_int();
329
330 EXPECT_CALL(*sink, handle_event(e))
331 .Times(1);
332
333 EXPECT_CALL(*surface_builder.mock_surface_state, resize(Ref(new_size)))
334 .Times(1);
335
336 surf.resize(new_size);
337}
338
299TEST_F(SurfaceImpl, sends_focus_notifications_when_focus_gained_and_lost)339TEST_F(SurfaceImpl, sends_focus_notifications_when_focus_gained_and_lost)
300{340{
301 using namespace testing;341 using namespace testing;

Subscribers

People subscribed via source and target branches