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
1=== modified file 'examples/eglapp.c'
2--- examples/eglapp.c 2013-11-21 03:16:21 +0000
3+++ examples/eglapp.c 2013-11-27 10:05:11 +0000
4@@ -93,8 +93,12 @@
5 lastcount = count;
6 }
7
8- /* This is one way to handle window resizing. But in future it would be
9- better to have resize events coming from the server */
10+ /*
11+ * Querying the surface (actually the current buffer) dimensions here is
12+ * the only truly safe way to be sure that the dimensions we think we
13+ * have are those of the buffer being rendered to. But this should be
14+ * improved in future; https://bugs.launchpad.net/mir/+bug/1194384
15+ */
16 if (eglQuerySurface(egldisplay, eglsurface, EGL_WIDTH, &width) &&
17 eglQuerySurface(egldisplay, eglsurface, EGL_HEIGHT, &height))
18 {
19@@ -102,7 +106,7 @@
20 }
21 }
22
23-static void mir_eglapp_handle_input(MirSurface* surface, MirEvent const* ev, void* context)
24+static void mir_eglapp_handle_event(MirSurface* surface, MirEvent const* ev, void* context)
25 {
26 (void) surface;
27 (void) context;
28@@ -112,6 +116,17 @@
29 {
30 running = 0;
31 }
32+ else if (ev->type == mir_event_type_resize)
33+ {
34+ /*
35+ * FIXME: https://bugs.launchpad.net/mir/+bug/1194384
36+ * It is unsafe to set the width and height here because we're in a
37+ * different thread to that doing the rendering. So we either need
38+ * support for event queuing (directing them to another thread) or
39+ * full single-threaded callbacks. (LP: #1194384).
40+ */
41+ printf("Resized to %dx%d\n", ev->resize.width, ev->resize.height);
42+ }
43 }
44
45 static unsigned int get_bpp(MirPixelFormat pf)
46@@ -172,7 +187,7 @@
47 };
48 MirEventDelegate delegate =
49 {
50- mir_eglapp_handle_input,
51+ mir_eglapp_handle_event,
52 NULL
53 };
54 EGLConfig eglconfig;
55
56=== modified file 'examples/fingerpaint.c'
57--- examples/fingerpaint.c 2013-09-19 13:24:22 +0000
58+++ examples/fingerpaint.c 2013-11-27 10:05:11 +0000
59@@ -247,6 +247,18 @@
60 redraw(surface, canvas);
61 }
62 }
63+ else if (event->type == mir_event_type_resize)
64+ {
65+ /* FIXME: https://bugs.launchpad.net/mir/+bug/1194384
66+ * mir_event_type_resize will arrive in a different thread to that of
67+ * mir_event_type_motion, so we cannot safely redraw from this thread.
68+ * Either the callbacks will need to become thread-safe, or we'd have
69+ * to employ some non-trivial event queuing and inter-thread signals,
70+ * which I think is beyond the scope of this example code.
71+ *
72+ * redraw(surface, canvas);
73+ */
74+ }
75 }
76
77 static const MirDisplayOutput *find_active_output(
78
79=== modified file 'include/shared/mir_toolkit/event.h'
80--- include/shared/mir_toolkit/event.h 2013-11-22 13:53:36 +0000
81+++ include/shared/mir_toolkit/event.h 2013-11-27 10:05:11 +0000
82@@ -39,7 +39,8 @@
83 {
84 mir_event_type_key,
85 mir_event_type_motion,
86- mir_event_type_surface
87+ mir_event_type_surface,
88+ mir_event_type_resize
89 } MirEventType;
90
91 typedef enum {
92@@ -194,12 +195,22 @@
93 int value;
94 } MirSurfaceEvent;
95
96+typedef struct
97+{
98+ MirEventType type;
99+
100+ int surface_id;
101+ int width;
102+ int height;
103+} MirResizeEvent;
104+
105 typedef union
106 {
107 MirEventType type;
108 MirKeyEvent key;
109 MirMotionEvent motion;
110 MirSurfaceEvent surface;
111+ MirResizeEvent resize;
112 } MirEvent;
113
114 #ifdef __cplusplus
115
116=== modified file 'src/server/frontend/event_sender.cpp'
117--- src/server/frontend/event_sender.cpp 2013-10-15 08:53:10 +0000
118+++ src/server/frontend/event_sender.cpp 2013-11-27 10:05:11 +0000
119@@ -37,7 +37,7 @@
120 void mfd::EventSender::handle_event(MirEvent const& e)
121 {
122 // Limit the types of events we wish to send over protobuf, for now.
123- if (e.type == mir_event_type_surface)
124+ if (e.type != mir_event_type_key && e.type != mir_event_type_motion)
125 {
126 // In future we might send multiple events, or insert them into messages
127 // containing other responses, but for now we send them individually.
128
129=== modified file 'src/server/scene/surface_impl.cpp'
130--- src/server/scene/surface_impl.cpp 2013-11-25 11:21:54 +0000
131+++ src/server/scene/surface_impl.cpp 2013-11-27 10:05:11 +0000
132@@ -247,6 +247,15 @@
133 void ms::SurfaceImpl::resize(geom::Size const& size)
134 {
135 surface->resize(size);
136+
137+ MirEvent e;
138+ memset(&e, 0, sizeof e);
139+ e.type = mir_event_type_resize;
140+ e.resize.surface_id = id.as_value();
141+ e.resize.width = size.width.as_int();
142+ e.resize.height = size.height.as_int();
143+
144+ event_sink->handle_event(e);
145 }
146
147 void ms::SurfaceImpl::set_rotation(float degrees, glm::vec3 const& axis)
148
149=== modified file 'tests/unit-tests/frontend/test_event_sender.cpp'
150--- tests/unit-tests/frontend/test_event_sender.cpp 2013-11-22 13:53:36 +0000
151+++ tests/unit-tests/frontend/test_event_sender.cpp 2013-11-27 10:05:11 +0000
152@@ -67,3 +67,39 @@
153
154 sender.handle_display_config_change(config);
155 }
156+
157+TEST(TestEventSender, sends_noninput_events)
158+{
159+ using namespace testing;
160+
161+ auto msg_sender = std::make_shared<MockMsgSender>();
162+ mfd::EventSender event_sender(msg_sender);
163+
164+ MirEvent event;
165+ memset(&event, 0, sizeof event);
166+
167+ EXPECT_CALL(*msg_sender, send(_))
168+ .Times(2);
169+ event.type = mir_event_type_surface;
170+ event_sender.handle_event(event);
171+ event.type = mir_event_type_resize;
172+ event_sender.handle_event(event);
173+}
174+
175+TEST(TestEventSender, never_sends_input_events)
176+{
177+ using namespace testing;
178+
179+ auto msg_sender = std::make_shared<MockMsgSender>();
180+ mfd::EventSender event_sender(msg_sender);
181+
182+ MirEvent event;
183+ memset(&event, 0, sizeof event);
184+
185+ EXPECT_CALL(*msg_sender, send(_))
186+ .Times(0);
187+ event.type = mir_event_type_key;
188+ event_sender.handle_event(event);
189+ event.type = mir_event_type_motion;
190+ event_sender.handle_event(event);
191+}
192
193=== modified file 'tests/unit-tests/scene/test_surface_impl.cpp'
194--- tests/unit-tests/scene/test_surface_impl.cpp 2013-11-25 14:35:33 +0000
195+++ tests/unit-tests/scene/test_surface_impl.cpp 2013-11-27 10:05:11 +0000
196@@ -40,6 +40,7 @@
197 #include "mir_test/fake_shared.h"
198 #include "mir_test/event_matchers.h"
199
200+#include <cstring>
201 #include <stdexcept>
202 #include "gmock_set_arg.h"
203 #include <gmock/gmock.h>
204@@ -70,6 +71,7 @@
205 {
206 public:
207 StubSurfaceBuilder() :
208+ mock_surface_state(std::make_shared<mtd::MockSurfaceState>()),
209 stub_buffer_stream_(std::make_shared<mtd::StubBufferStream>()),
210 dummy_surface()
211 {
212@@ -77,9 +79,8 @@
213
214 std::weak_ptr<ms::BasicSurface> create_surface(msh::Session*, msh::SurfaceCreationParameters const& )
215 {
216- auto state = std::make_shared<mtd::MockSurfaceState>();
217 dummy_surface = std::make_shared<ms::Surface>(
218- state,
219+ mock_surface_state,
220 stub_buffer_stream_,
221 std::shared_ptr<mi::InputChannel>(),
222 report);
223@@ -101,6 +102,8 @@
224 return stub_buffer_stream_;
225 }
226
227+ std::shared_ptr<mtd::MockSurfaceState> mock_surface_state;
228+
229 private:
230 std::shared_ptr<mtd::StubBufferStream> const stub_buffer_stream_;
231 std::shared_ptr<ms::BasicSurface> dummy_surface;
232@@ -296,6 +299,43 @@
233 EXPECT_EQ(mir_surface_state_fullscreen, surf.state());
234 }
235
236+bool operator==(MirEvent const& a, MirEvent const& b)
237+{
238+ // We will always fill unused bytes with zero, so memcmp is accurate...
239+ return !memcmp(&a, &b, sizeof(MirEvent));
240+}
241+
242+TEST_F(SurfaceImpl, emits_resize_events)
243+{
244+ using namespace testing;
245+
246+ auto sink = std::make_shared<MockEventSink>();
247+ ms::SurfaceImpl surf(
248+ nullptr,
249+ mt::fake_shared(surface_builder),
250+ std::make_shared<mtd::NullSurfaceConfigurator>(),
251+ msh::a_surface(),
252+ stub_id,
253+ sink);
254+
255+ geom::Size const new_size{123, 456};
256+
257+ MirEvent e;
258+ memset(&e, 0, sizeof e);
259+ e.type = mir_event_type_resize;
260+ e.resize.surface_id = stub_id.as_value();
261+ e.resize.width = new_size.width.as_int();
262+ e.resize.height = new_size.height.as_int();
263+
264+ EXPECT_CALL(*sink, handle_event(e))
265+ .Times(1);
266+
267+ EXPECT_CALL(*surface_builder.mock_surface_state, resize(Ref(new_size)))
268+ .Times(1);
269+
270+ surf.resize(new_size);
271+}
272+
273 TEST_F(SurfaceImpl, sends_focus_notifications_when_focus_gained_and_lost)
274 {
275 using namespace testing;

Subscribers

People subscribed via source and target branches