Mir

Merge lp:~vanvugt/mir/event-queue into lp:mir

Proposed by Daniel van Vugt on 2014-02-27
Status: Work in progress
Proposed branch: lp:~vanvugt/mir/event-queue
Merge into: lp:mir
Diff against target: 564 lines (+265/-58)
10 files modified
examples/eglapp.c (+9/-9)
examples/fingerpaint.c (+23/-23)
examples/progressbar.c (+30/-25)
include/client/mir_toolkit/mir_client_library.h (+8/-0)
include/shared/mir_toolkit/client_types.h (+1/-0)
include/shared/mir_toolkit/event.h (+2/-1)
src/client/CMakeLists.txt (+1/-0)
src/client/mir_client_library.cpp (+51/-0)
src/client/mir_event_queue.cpp (+88/-0)
src/client/mir_event_queue.h (+52/-0)
To merge this branch: bzr merge lp:~vanvugt/mir/event-queue
Reviewer Review Type Date Requested Status
Mir development team 2014-02-27 Pending
Review via email: mp+208563@code.launchpad.net

Description of the change

Prototype client event API as a solution for guaranteeing thread safety (LP: #1194384)

The new API is optional, so existing clients can still work without modification.

Fun tip: Try fingerpaint on a touch device. It's now much more responsive and paints more.

To post a comment you must log in.
lp:~vanvugt/mir/event-queue updated on 2014-02-28
1431. By Daniel van Vugt on 2014-02-28

Merge latest development-branch

1432. By Daniel van Vugt on 2014-02-28

Eliminate confusing "animate" terminology

1433. By Daniel van Vugt on 2014-02-28

Rename the confusing "mir_event_type_null" to "mir_event_type_timeout"

1434. By Daniel van Vugt on 2014-02-28

fingerpaint: Implement redraw on resize, because it's safe now!

Unmerged revisions

1434. By Daniel van Vugt on 2014-02-28

fingerpaint: Implement redraw on resize, because it's safe now!

1433. By Daniel van Vugt on 2014-02-28

Rename the confusing "mir_event_type_null" to "mir_event_type_timeout"

1432. By Daniel van Vugt on 2014-02-28

Eliminate confusing "animate" terminology

1431. By Daniel van Vugt on 2014-02-28

Merge latest development-branch

1430. By Daniel van Vugt on 2014-02-27

Merge branch lp:~vanvugt/mir/event-queue and resolve conflicts.

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 2014-01-13 06:12:33 +0000
3+++ examples/eglapp.c 2014-02-28 07:39:52 +0000
4@@ -35,7 +35,7 @@
5 static MirSurface *surface;
6 static EGLDisplay egldisplay;
7 static EGLSurface eglsurface;
8-static volatile sig_atomic_t running = 0;
9+static MirEventQueue *queue = NULL;
10
11 #define CHECK(_cond, _err) \
12 if (!(_cond)) \
13@@ -46,6 +46,8 @@
14
15 void mir_eglapp_shutdown(void)
16 {
17+ mir_event_queue_release(queue);
18+ queue = NULL;
19 eglMakeCurrent(egldisplay, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT);
20 eglTerminate(egldisplay);
21 mir_surface_release_sync(surface);
22@@ -56,16 +58,13 @@
23
24 static void shutdown(int signum)
25 {
26- if (running)
27- {
28- running = 0;
29+ if (mir_event_queue_quit(queue))
30 printf("Signal %d received. Good night.\n", signum);
31- }
32 }
33
34 mir_eglapp_bool mir_eglapp_running(void)
35 {
36- return running;
37+ return mir_event_queue_wait(queue, NULL);
38 }
39
40 void mir_eglapp_swap_buffers(void)
41@@ -78,7 +77,7 @@
42 int dcount;
43 EGLint width, height;
44
45- if (!running)
46+ if (queue == NULL)
47 return;
48
49 eglSwapBuffers(egldisplay, eglsurface);
50@@ -114,7 +113,7 @@
51 ev->key.key_code == XKB_KEY_q &&
52 ev->key.action == mir_key_action_up)
53 {
54- running = 0;
55+ mir_event_queue_quit(queue);
56 }
57 else if (ev->type == mir_event_type_resize)
58 {
59@@ -393,7 +392,8 @@
60
61 eglSwapInterval(egldisplay, swapinterval);
62
63- running = 1;
64+ queue = mir_create_event_queue();
65+ mir_event_queue_set_timeout(queue, 0);
66
67 return 1;
68 }
69
70=== modified file 'examples/fingerpaint.c'
71--- examples/fingerpaint.c 2014-01-13 06:12:33 +0000
72+++ examples/fingerpaint.c 2014-02-28 07:39:52 +0000
73@@ -21,7 +21,6 @@
74 #include <signal.h>
75 #include <stdint.h>
76 #include <stdlib.h>
77-#include <unistd.h> /* sleep() */
78 #include <string.h>
79
80 #define BYTES_PER_PIXEL(f) ((f) == mir_pixel_format_bgr_888 ? 3 : 4)
81@@ -32,15 +31,12 @@
82 uint8_t r, g, b, a;
83 } Color;
84
85-static volatile sig_atomic_t running = 1;
86+static MirEventQueue *queue = NULL;
87
88 static void shutdown(int signum)
89 {
90- if (running)
91- {
92- running = 0;
93+ if (mir_event_queue_quit(queue))
94 printf("Signal %d received. Good night.\n", signum);
95- }
96 }
97
98 static void blend(uint32_t *dest, uint32_t src, int alpha_shift)
99@@ -201,6 +197,8 @@
100 {0x00, 0x00, 0xff, 0xff},
101 };
102
103+ (void)surface;
104+
105 if (event->type == mir_event_type_motion)
106 {
107 static size_t base_color = 0;
108@@ -243,21 +241,11 @@
109
110 draw_box(canvas, x - radius, y - radius, 2*radius, &tone);
111 }
112-
113- redraw(surface, canvas);
114 }
115 }
116 else if (event->type == mir_event_type_resize)
117 {
118- /* FIXME: https://bugs.launchpad.net/mir/+bug/1194384
119- * mir_event_type_resize will arrive in a different thread to that of
120- * mir_event_type_motion, so we cannot safely redraw from this thread.
121- * Either the callbacks will need to become thread-safe, or we'd have
122- * to employ some non-trivial event queuing and inter-thread signals,
123- * which I think is beyond the scope of this example code.
124- *
125- * redraw(surface, canvas);
126- */
127+ redraw(surface, canvas);
128 }
129 }
130
131@@ -291,7 +279,6 @@
132 MirSurfaceParameters parm;
133 MirSurface *surf;
134 MirGraphicsRegion canvas;
135- MirEventDelegate delegate = {&on_event, &canvas};
136 unsigned int f;
137
138 char *mir_socket = NULL;
139@@ -383,9 +370,10 @@
140 mir_display_config_destroy(display_config);
141
142 surf = mir_connection_create_surface_sync(conn, &parm);
143- if (surf != NULL)
144+ queue = mir_create_event_queue();
145+ if (surf != NULL && queue != NULL)
146 {
147- mir_surface_set_event_handler(surf, &delegate);
148+ mir_surface_set_event_queue(surf, queue);
149
150 canvas.width = parm.width;
151 canvas.height = parm.height;
152@@ -395,19 +383,30 @@
153
154 if (canvas.vaddr != NULL)
155 {
156+ MirEvent event;
157+
158 signal(SIGINT, shutdown);
159 signal(SIGTERM, shutdown);
160
161 clear_region(&canvas, &background);
162 redraw(surf, &canvas);
163
164- while (running)
165+ while (mir_event_queue_wait(queue, &event))
166 {
167- sleep(1); /* Is there a better way yet? */
168+ if (event.type == mir_event_type_timeout)
169+ {
170+ mir_event_queue_set_timeout(queue, -1);
171+ redraw(surf, &canvas);
172+ }
173+ else
174+ {
175+ on_event(surf, &event, &canvas);
176+ mir_event_queue_set_timeout(queue, 5);
177+ }
178 }
179
180 /* Ensure canvas won't be used after it's freed */
181- mir_surface_set_event_handler(surf, NULL);
182+ mir_surface_set_event_queue(surf, NULL);
183 free(canvas.vaddr);
184 }
185 else
186@@ -416,6 +415,7 @@
187 }
188
189 mir_surface_release_sync(surf);
190+ mir_event_queue_release(queue);
191 }
192 else
193 {
194
195=== modified file 'examples/progressbar.c'
196--- examples/progressbar.c 2014-01-13 06:12:33 +0000
197+++ examples/progressbar.c 2014-02-28 07:39:52 +0000
198@@ -21,8 +21,6 @@
199 #include <signal.h>
200 #include <stdint.h>
201 #include <stdlib.h>
202-#define __USE_BSD 1 /* for usleep() */
203-#include <unistd.h> /* sleep() */
204 #include <string.h>
205
206 #define BYTES_PER_PIXEL(f) ((f) == mir_pixel_format_bgr_888 ? 3 : 4)
207@@ -39,15 +37,12 @@
208 static const Color *const foreground = &white;
209 static const Color *const background = &blue;
210
211-static volatile sig_atomic_t running = 1;
212+static MirEventQueue *queue = NULL;
213
214 static void shutdown(int signum)
215 {
216- if (running)
217- {
218- running = 0;
219+ if (mir_event_queue_quit(queue))
220 printf("Signal %d received. Good night.\n", signum);
221- }
222 }
223
224 static void blend(uint32_t *dest, uint32_t src, int alpha_shift)
225@@ -264,31 +259,41 @@
226
227 if (canvas.vaddr != NULL)
228 {
229+ MirEvent event = {mir_event_type_timeout};
230 int t = 0;
231
232+ queue = mir_create_event_queue();
233+ mir_event_queue_set_timeout(queue, sleep_usec / 1000);
234+
235 signal(SIGINT, shutdown);
236 signal(SIGTERM, shutdown);
237-
238- while (running)
239+
240+ do
241 {
242- static const int width = 8;
243- static const int space = 1;
244- const int grid = width + 2 * space;
245- const int row = parm.width / grid;
246- const int square = row * row;
247- const int x = (t % row) * grid + space;
248- const int y = (t / row) * grid + space;
249-
250- if (t % square == 0)
251- clear_region(&canvas, background);
252-
253- t = (t + 1) % square;
254-
255- draw_box(&canvas, x, y, width, foreground);
256+ if (event.type == mir_event_type_timeout)
257+ {
258+ static const int width = 8;
259+ static const int space = 1;
260+ const int grid = width + 2 * space;
261+ const int row = parm.width / grid;
262+ const int square = row * row;
263+ const int x = (t % row) * grid + space;
264+ const int y = (t / row) * grid + space;
265+
266+ if (t % square == 0)
267+ clear_region(&canvas, background);
268+
269+ t = (t + 1) % square;
270+
271+ draw_box(&canvas, x, y, width, foreground);
272+ }
273
274 redraw(surf, &canvas);
275- usleep(sleep_usec);
276- }
277+ } while (mir_event_queue_wait(queue, &event));
278+
279+ signal(SIGINT, SIG_DFL);
280+ signal(SIGTERM, SIG_DFL);
281+ mir_event_queue_release(queue);
282
283 free(canvas.vaddr);
284 }
285
286=== modified file 'include/client/mir_toolkit/mir_client_library.h'
287--- include/client/mir_toolkit/mir_client_library.h 2014-01-13 06:12:33 +0000
288+++ include/client/mir_toolkit/mir_client_library.h 2014-02-28 07:39:52 +0000
289@@ -217,6 +217,8 @@
290 void mir_surface_set_event_handler(MirSurface *surface,
291 MirEventDelegate const *event_handler);
292
293+void mir_surface_set_event_queue(MirSurface *surface, MirEventQueue *queue);
294+
295 /**
296 * Get a window type that can be used for OpenGL ES 2.0 acceleration.
297 * \param [in] surface The surface
298@@ -404,6 +406,12 @@
299 */
300 int mir_surface_get_swapinterval(MirSurface* surface);
301
302+MirEventQueue* mir_create_event_queue();
303+void mir_event_queue_set_timeout(MirEventQueue* q, int milliseconds);
304+int mir_event_queue_wait(MirEventQueue* q, MirEvent* e);
305+int mir_event_queue_quit(MirEventQueue* q);
306+void mir_event_queue_release(MirEventQueue* q);
307+
308 #ifdef __cplusplus
309 }
310 /**@}*/
311
312=== modified file 'include/shared/mir_toolkit/client_types.h'
313--- include/shared/mir_toolkit/client_types.h 2014-01-31 03:46:36 +0000
314+++ include/shared/mir_toolkit/client_types.h 2014-02-28 07:39:52 +0000
315@@ -46,6 +46,7 @@
316 typedef struct MirConnection MirConnection;
317 typedef struct MirSurface MirSurface;
318 typedef struct MirScreencast MirScreencast;
319+typedef struct MirEventQueue MirEventQueue;
320
321 /**
322 * Returned by asynchronous functions. Must not be free'd by
323
324=== modified file 'include/shared/mir_toolkit/event.h'
325--- include/shared/mir_toolkit/event.h 2014-01-13 06:12:33 +0000
326+++ include/shared/mir_toolkit/event.h 2014-02-28 07:39:52 +0000
327@@ -40,7 +40,8 @@
328 mir_event_type_key,
329 mir_event_type_motion,
330 mir_event_type_surface,
331- mir_event_type_resize
332+ mir_event_type_resize,
333+ mir_event_type_timeout
334 } MirEventType;
335
336 typedef enum {
337
338=== modified file 'src/client/CMakeLists.txt'
339--- src/client/CMakeLists.txt 2014-02-27 03:52:58 +0000
340+++ src/client/CMakeLists.txt 2014-02-28 07:39:52 +0000
341@@ -36,6 +36,7 @@
342 mir_connection.cpp
343 mir_wait_handle.cpp
344 mir_surface.cpp
345+ mir_event_queue.cpp
346 logging/rpc_report.cpp
347 logging/input_receiver_report.cpp
348 default_connection_configuration.cpp
349
350=== modified file 'src/client/mir_client_library.cpp'
351--- src/client/mir_client_library.cpp 2014-02-14 09:27:02 +0000
352+++ src/client/mir_client_library.cpp 2014-02-28 07:39:52 +0000
353@@ -29,6 +29,7 @@
354 #include "default_connection_configuration.h"
355 #include "lifecycle_control.h"
356 #include "api_impl_types.h"
357+#include "mir_event_queue.h"
358
359 #include <set>
360 #include <unordered_set>
361@@ -535,3 +536,53 @@
362
363 return connection->set_extra_platform_data(extra_data);
364 }
365+
366+// MirEventQueue
367+
368+namespace
369+{
370+void enqueue_event(MirSurface* surface, MirEvent const* event, void* context)
371+{
372+ (void)surface;
373+ MirEventQueue *q = static_cast<MirEventQueue*>(context);
374+ q->push(event);
375+}
376+}
377+
378+void mir_surface_set_event_queue(MirSurface *surface, MirEventQueue *q)
379+{
380+ if (q)
381+ {
382+ MirEventDelegate delegate{enqueue_event, q};
383+ surface->set_event_handler(&delegate);
384+ }
385+ else
386+ {
387+ surface->set_event_handler(nullptr);
388+ }
389+}
390+
391+MirEventQueue* mir_create_event_queue()
392+{
393+ return new (std::nothrow) MirEventQueue;
394+}
395+
396+void mir_event_queue_set_timeout(MirEventQueue* q, int milliseconds)
397+{
398+ q->set_timeout(milliseconds);
399+}
400+
401+int mir_event_queue_wait(MirEventQueue* q, MirEvent* e)
402+{
403+ return q->wait(e);
404+}
405+
406+int mir_event_queue_quit(MirEventQueue* q)
407+{
408+ return q->quit();
409+}
410+
411+void mir_event_queue_release(MirEventQueue* q)
412+{
413+ delete q;
414+}
415
416=== added file 'src/client/mir_event_queue.cpp'
417--- src/client/mir_event_queue.cpp 1970-01-01 00:00:00 +0000
418+++ src/client/mir_event_queue.cpp 2014-02-28 07:39:52 +0000
419@@ -0,0 +1,88 @@
420+/*
421+ * Copyright © 2013 Canonical Ltd.
422+ *
423+ * This program is free software: you can redistribute it and/or modify it
424+ * under the terms of the GNU Lesser General Public License version 3,
425+ * as published by the Free Software Foundation.
426+ *
427+ * This program is distributed in the hope that it will be useful,
428+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
429+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
430+ * GNU Lesser General Public License for more details.
431+ *
432+ * You should have received a copy of the GNU Lesser General Public License
433+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
434+ *
435+ * Authored by: Daniel van Vugt <daniel.van.vugt@canonical.com>
436+ */
437+
438+#include "mir_event_queue.h"
439+
440+namespace
441+{
442+typedef std::unique_lock<std::mutex> Lock;
443+}
444+
445+MirEventQueue::MirEventQueue()
446+ : running(true), interval_ms(-1)
447+{
448+}
449+
450+void MirEventQueue::set_timeout(int milliseconds)
451+{
452+ Lock lock(guard);
453+ interval_ms = milliseconds;
454+ deadline = std::chrono::system_clock::now() +
455+ std::chrono::milliseconds(interval_ms);
456+ cond.notify_all();
457+}
458+
459+void MirEventQueue::push(MirEvent const* e)
460+{
461+ Lock lock(guard);
462+ if (running)
463+ {
464+ queue.push_back(*e);
465+ cond.notify_one();
466+ } // else events after quit() are ignored.
467+}
468+
469+bool MirEventQueue::quit()
470+{
471+ Lock lock(guard);
472+ bool was_running = running;
473+ running = false;
474+ cond.notify_all();
475+ return was_running;
476+}
477+
478+bool MirEventQueue::wait(MirEvent* e)
479+{
480+ Lock lock(guard);
481+
482+ while (running && queue.empty() && interval_ms)
483+ {
484+ if (interval_ms < 0)
485+ cond.wait(lock);
486+ else if (cond.wait_until(lock, deadline) == std::cv_status::timeout)
487+ break;
488+ }
489+
490+ bool pending = !queue.empty();
491+ if (pending)
492+ {
493+ if (e) *e = queue.front();
494+ queue.pop_front();
495+ }
496+ else
497+ {
498+ if (e) e->type = mir_event_type_timeout;
499+ if (interval_ms > 0)
500+ deadline += std::chrono::milliseconds(interval_ms);
501+ }
502+
503+ // Only break the client's event loop after it has chosen to quit() and
504+ // there are no more events to process.
505+ return running || pending;
506+}
507+
508
509=== added file 'src/client/mir_event_queue.h'
510--- src/client/mir_event_queue.h 1970-01-01 00:00:00 +0000
511+++ src/client/mir_event_queue.h 2014-02-28 07:39:52 +0000
512@@ -0,0 +1,52 @@
513+/*
514+ * Copyright © 2013 Canonical Ltd.
515+ *
516+ * This program is free software: you can redistribute it and/or modify it
517+ * under the terms of the GNU Lesser General Public License version 3,
518+ * as published by the Free Software Foundation.
519+ *
520+ * This program is distributed in the hope that it will be useful,
521+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
522+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
523+ * GNU Lesser General Public License for more details.
524+ *
525+ * You should have received a copy of the GNU Lesser General Public License
526+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
527+ *
528+ * Authored by: Daniel van Vugt <daniel.van.vugt@canonical.com>
529+ */
530+
531+#ifndef MIR_EVENT_QUEUE_H_
532+#define MIR_EVENT_QUEUE_H_
533+
534+#include <chrono>
535+#include <condition_variable>
536+#include <mutex>
537+#include <deque>
538+#include "mir_toolkit/client_types.h"
539+#include "mir_toolkit/event.h"
540+
541+/**
542+ * \addtogroup mir_toolkit
543+ * @{
544+ */
545+struct MirEventQueue
546+{
547+public:
548+ MirEventQueue();
549+ void set_timeout(int milliseconds);
550+ void push(MirEvent const* e);
551+ bool quit();
552+ bool wait(MirEvent* e);
553+
554+private:
555+ bool running;
556+ int interval_ms;
557+ std::chrono::system_clock::time_point deadline;
558+ std::mutex guard;
559+ std::condition_variable cond;
560+ std::deque<MirEvent> queue;
561+};
562+/**@}*/
563+
564+#endif /* MIR_EVENT_QUEUE_H_ */

Subscribers

People subscribed via source and target branches