Merge lp:unity-system-compositor into lp:unity-system-compositor/ubuntu

Proposed by Andreas Pokorny
Status: Merged
Approved by: Andreas Pokorny
Approved revision: 226
Merged at revision: 207
Proposed branch: lp:unity-system-compositor
Merge into: lp:unity-system-compositor/ubuntu
Diff against target: 2561 lines (+1216/-599)
31 files modified
CMakeLists.txt (+1/-2)
cmake/FindPIL.cmake (+8/-0)
debian/changelog (+6/-0)
debian/control (+1/-2)
debian/unity-system-compositor.install (+0/-1)
spinner/CMakeLists.txt (+45/-11)
spinner/eglapp.cpp (+117/-231)
spinner/eglapp.h (+4/-17)
spinner/eglspinner.cpp (+278/-220)
spinner/miregl.cpp (+247/-0)
spinner/miregl.h (+67/-0)
src/external_spinner.cpp (+17/-0)
src/mir_screen.cpp (+2/-5)
src/mir_screen.h (+1/-1)
src/screen.h (+1/-1)
src/screen_event_handler.cpp (+22/-1)
src/screen_event_handler.h (+1/-0)
src/server.cpp (+50/-7)
src/session_monitor.h (+63/-0)
src/session_switcher.h (+6/-23)
src/window_manager.cpp (+13/-43)
src/window_manager.h (+4/-3)
tests/integration-tests/CMakeLists.txt (+1/-0)
tests/integration-tests/spin_wait.cpp (+39/-0)
tests/integration-tests/spin_wait.h (+39/-0)
tests/integration-tests/test_dbus_event_loop.cpp (+2/-1)
tests/integration-tests/test_external_spinner.cpp (+44/-11)
tests/integration-tests/test_unity_screen_service.cpp (+1/-1)
tests/unit-tests/test_mir_screen.cpp (+8/-5)
tests/unit-tests/test_screen_event_handler.cpp (+42/-13)
tools/png2header.py (+86/-0)
To merge this branch: bzr merge lp:unity-system-compositor
Reviewer Review Type Date Requested Status
PS Jenkins bot (community) continuous-integration Needs Fixing
Unity System Compositor Development Team Pending
Review via email: mp+262866@code.launchpad.net

Commit message

Release in step with Mir 0.14.0

Description of the change

Release in step with Mir 0.14.0

To post a comment you must log in.
Revision history for this message
PS Jenkins bot (ps-jenkins) wrote :
review: Needs Fixing (continuous-integration)
lp:unity-system-compositor updated
221. By CI Train Bot Account

merge lp:unity-system-compositor/ubuntu.

Approved by Andreas Pokorny, PS Jenkins bot.

Revision history for this message
PS Jenkins bot (ps-jenkins) wrote :
review: Needs Fixing (continuous-integration)
lp:unity-system-compositor updated
222. By Andreas Pokorny

add wily changelog entry from last release.

Approved by PS Jenkins bot.

Revision history for this message
PS Jenkins bot (ps-jenkins) wrote :
review: Needs Fixing (continuous-integration)
lp:unity-system-compositor updated
223. By Mirco Müller

Updated visuals of spinner boot-anim to latest spec from Design.

Approved by PS Jenkins bot, Alexandros Frantzis.

Revision history for this message
PS Jenkins bot (ps-jenkins) wrote :
review: Needs Fixing (continuous-integration)
lp:unity-system-compositor updated
224. By Alexandros Frantzis

Don't restart the inactivity timers when receiving invalid dbus calls or uninteresting dbus events

Previously, invalid dbus calls (e.g., trying to remove a non-existent keepDisplayOn request), or uninteresting dbus events (e.g., being informed about disconnections of dbus clients that hadn't issued any keepDisplayOn requests), would restart the inactivity timers, effectively increasing the delay until display dimming and poweroff. Fixes: https://bugs.launchpad.net/bugs/1461476.

Approved by Andreas Pokorny, Alberto Aguirre, PS Jenkins bot.

Revision history for this message
PS Jenkins bot (ps-jenkins) wrote :
review: Needs Fixing (continuous-integration)
lp:unity-system-compositor updated
225. By Alexandros Frantzis

Ensure inactivity timers are enabled when turning the screen on using the power button. Fixes: https://bugs.launchpad.net/bugs/1473979.

Approved by Alberto Aguirre, PS Jenkins bot.

Revision history for this message
PS Jenkins bot (ps-jenkins) wrote :
review: Needs Fixing (continuous-integration)
lp:unity-system-compositor updated
226. By Andreas Pokorny

update changelog

227. By kevin gunn

update changelog to something proper

228. By kevin gunn

removed bad paste from changelog

229. By kevin gunn

one more changelog update to trick it

230. By kevin gunn

changelog update to trick it

231. By kevin gunn

changelog update to trick it2

232. By kevin gunn

clean up change log mess, this should work

233. By Alexandros Frantzis

Wait a bit for spinner process to appear in AnExternalSpinner tests. Fixes: https://bugs.launchpad.net/bugs/1473418.

Approved by Kevin DuBois, PS Jenkins bot.

234. By Alexandros Frantzis

Don't leave zombie spinner processes. Fixes: https://bugs.launchpad.net/bugs/1473061.

Approved by PS Jenkins bot, Kevin DuBois.

235. By Daniel van Vugt

Drop build dep on g++-4.9 (LP: #1452347). Fixes: https://bugs.launchpad.net/bugs/1452347.

Approved by Alexandros Frantzis, Kevin DuBois, PS Jenkins bot.

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1=== modified file 'CMakeLists.txt'
2--- CMakeLists.txt 2015-04-07 13:36:48 +0000
3+++ CMakeLists.txt 2015-07-20 10:34:37 +0000
4@@ -15,7 +15,6 @@
5 # Authored by: Robert Ancell <robert.ancell@canonical.com>
6
7 project(UnitySystemCompositor)
8-set(PACKAGE "unity-system-compositor")
9 set(USC_VERSION_MAJOR 0)
10 set(USC_VERSION_MINOR 0)
11 set(USC_VERSION_PATCH 4)
12@@ -40,7 +39,6 @@
13
14 find_package(PkgConfig)
15 pkg_check_modules(ANDROIDPROPS libandroid-properties)
16-pkg_check_modules(CAIRO REQUIRED cairo)
17 pkg_check_modules(GLIB REQUIRED glib-2.0)
18 pkg_check_modules(MIRCLIENT REQUIRED mirclient)
19 pkg_check_modules(MIRSERVER REQUIRED mirserver)
20@@ -48,6 +46,7 @@
21
22 find_package(Boost 1.48.0 COMPONENTS system REQUIRED)
23 find_package(GLESv2 REQUIRED)
24+find_package(PIL REQUIRED)
25
26 add_subdirectory(spinner/)
27 add_subdirectory(src/)
28
29=== added file 'cmake/FindPIL.cmake'
30--- cmake/FindPIL.cmake 1970-01-01 00:00:00 +0000
31+++ cmake/FindPIL.cmake 2015-07-20 10:34:37 +0000
32@@ -0,0 +1,8 @@
33+execute_process(
34+ COMMAND python -c "from PIL import Image"
35+ RESULT_VARIABLE HAVE_PIL
36+)
37+
38+if (NOT ${HAVE_PIL} EQUAL 0)
39+ message(FATAL_ERROR "Python Imaging Library (PIL) not found")
40+endif()
41
42=== modified file 'debian/changelog'
43--- debian/changelog 2015-07-13 14:17:04 +0000
44+++ debian/changelog 2015-07-20 10:34:37 +0000
45@@ -1,3 +1,9 @@
46+unity-system-compositor (0.0.5+15.10.20150506.2-0ubuntu1) UNRELEASED; urgency=medium
47+
48+ * Release in step with Mir 0.14
49+
50+ -- Kevin Gunn <kevin.gunn@canonical.com> Thu, 16 Jul 2015 15:14:22 -0500
51+
52 unity-system-compositor (0.0.5+15.04.20150713-0ubuntu1) vivid; urgency=medium
53
54 [ Alexandros Frantzis ]
55
56=== modified file 'debian/control'
57--- debian/control 2015-05-06 16:09:19 +0000
58+++ debian/control 2015-07-20 10:34:37 +0000
59@@ -6,12 +6,10 @@
60 cmake-data,
61 dbus,
62 debhelper (>= 9),
63- g++-4.9,
64 google-mock,
65 libandroid-properties-dev [i386 amd64 armhf],
66 libboost-dev,
67 libboost-system-dev,
68- libcairo2-dev,
69 libdbus-1-dev,
70 libglib2.0-dev,
71 libgles2-mesa-dev,
72@@ -21,6 +19,7 @@
73 pkg-config,
74 python:any (>= 2.7),
75 python-setuptools,
76+ python-pil,
77 Standards-Version: 3.9.4
78 Homepage: https://launchpad.net/unity-system-compositor
79 # if you don't have have commit access to this branch but would like to upload
80
81=== modified file 'debian/unity-system-compositor.install'
82--- debian/unity-system-compositor.install 2014-06-16 16:53:55 +0000
83+++ debian/unity-system-compositor.install 2015-07-20 10:34:37 +0000
84@@ -4,4 +4,3 @@
85 usr/bin/unity-system-compositor-spinner
86 usr/sbin/unity-system-compositor
87 usr/share/dbus-1/interfaces/com.canonical.Unity.Screen.xml
88-usr/share/unity-system-compositor
89
90=== modified file 'spinner/CMakeLists.txt'
91--- spinner/CMakeLists.txt 2014-05-06 20:06:27 +0000
92+++ spinner/CMakeLists.txt 2015-07-20 10:34:37 +0000
93@@ -14,17 +14,44 @@
94 # You should have received a copy of the GNU General Public License
95 # along with this program. If not, see <http://www.gnu.org/licenses/>.
96
97+function(png2header png header varname)
98+ add_custom_command(
99+ OUTPUT ${header}
100+ COMMAND python ${CMAKE_SOURCE_DIR}/tools/png2header.py ${png} ${varname} > ${header}
101+ DEPENDS ${png} ${CMAKE_SOURCE_DIR}/tools/png2header.py
102+ )
103+endfunction()
104+
105+png2header(
106+ ${CMAKE_CURRENT_SOURCE_DIR}/wallpaper.png
107+ ${CMAKE_CURRENT_BINARY_DIR}/wallpaper.h
108+ wallpaper
109+)
110+
111+png2header(
112+ ${CMAKE_CURRENT_SOURCE_DIR}/logo.png
113+ ${CMAKE_CURRENT_BINARY_DIR}/logo.h
114+ logo
115+)
116+
117+png2header(
118+ ${CMAKE_CURRENT_SOURCE_DIR}/white-dot.png
119+ ${CMAKE_CURRENT_BINARY_DIR}/white_dot.h
120+ white_dot
121+)
122+
123+png2header(
124+ ${CMAKE_CURRENT_SOURCE_DIR}/orange-dot.png
125+ ${CMAKE_CURRENT_BINARY_DIR}/orange_dot.h
126+ orange_dot
127+)
128+
129 include_directories(
130- ${CAIRO_INCLUDE_DIRS}
131 ${GLIB_INCLUDE_DIRS}
132 ${ANDROIDPROPS_INCLUDE_DIRS}
133 ${GLESv2_INCLUDE_DIRS}
134 ${MIRCLIENT_INCLUDE_DIRS}
135-)
136-add_definitions(
137- -DPACKAGE="${PACKAGE}"
138- -DLOCALEDIR="${CMAKE_INSTALL_FULL_LOCALEDIR}"
139- -DPKGDATADIR="${CMAKE_INSTALL_FULL_DATAROOTDIR}/${PACKAGE}"
140+ ${CMAKE_CURRENT_BINARY_DIR}
141 )
142
143 if(ANDROIDPROPS_FOUND)
144@@ -33,16 +60,23 @@
145
146 link_directories(${MIRCLIENT_LIBRARY_DIRS})
147
148-add_executable(unity-system-compositor-spinner eglapp.c eglapp.h eglspinner.c)
149+add_executable(unity-system-compositor-spinner
150+ eglapp.cpp
151+ eglapp.h
152+ eglspinner.cpp
153+ miregl.h
154+ miregl.cpp
155+ ${CMAKE_CURRENT_BINARY_DIR}/wallpaper.h
156+ ${CMAKE_CURRENT_BINARY_DIR}/logo.h
157+ ${CMAKE_CURRENT_BINARY_DIR}/white_dot.h
158+ ${CMAKE_CURRENT_BINARY_DIR}/orange_dot.h
159+)
160+
161 target_link_libraries(unity-system-compositor-spinner
162 EGL
163- ${CAIRO_LDFLAGS}
164 ${GLIB_LDFLAGS}
165 ${ANDROIDPROPS_LDFLAGS}
166 ${GLESv2_LIBRARIES}
167 ${MIRCLIENT_LDFLAGS}
168 )
169 install(TARGETS unity-system-compositor-spinner RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR})
170-
171-install(FILES spinner-logo.png DESTINATION ${CMAKE_INSTALL_DATAROOTDIR}/${PACKAGE})
172-install(FILES spinner-glow.png DESTINATION ${CMAKE_INSTALL_DATAROOTDIR}/${PACKAGE})
173
174=== renamed file 'spinner/eglapp.c' => 'spinner/eglapp.cpp'
175--- spinner/eglapp.c 2015-04-24 12:48:10 +0000
176+++ spinner/eglapp.cpp 2015-07-20 10:34:37 +0000
177@@ -1,5 +1,5 @@
178 /*
179- * Copyright © 2013 Canonical Ltd.
180+ * Copyright © 2013, 2015 Canonical Ltd.
181 *
182 * This program is free software: you can redistribute it and/or modify
183 * it under the terms of the GNU General Public License version 3 as
184@@ -12,145 +12,77 @@
185 *
186 * You should have received a copy of the GNU General Public License
187 * along with this program. If not, see <http://www.gnu.org/licenses/>.
188- *
189- * Author: Daniel van Vugt <daniel.van.vugt@canonical.com>
190 */
191
192 #include "eglapp.h"
193-#include "mir_toolkit/mir_client_library.h"
194-#include <stdio.h>
195-#include <stdlib.h>
196-#include <signal.h>
197-#include <time.h>
198-#include <EGL/egl.h>
199-#include <GLES2/gl2.h>
200+
201+#include "miregl.h"
202+
203+
204
205 float mir_eglapp_background_opacity = 1.0f;
206
207 static const char appname[] = "eglspinner";
208
209-static MirConnection *connection;
210-static MirSurface *surface;
211-static EGLDisplay egldisplay;
212-static EGLSurface eglsurface;
213-static volatile sig_atomic_t running = 0;
214-
215-#define CHECK(_cond, _err) \
216- if (!(_cond)) \
217- { \
218- printf("%s\n", (_err)); \
219- return 0; \
220- }
221-
222-void mir_eglapp_shutdown(void)
223-{
224- eglMakeCurrent(egldisplay, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT);
225- eglTerminate(egldisplay);
226- mir_surface_release_sync(surface);
227- surface = NULL;
228- mir_connection_release(connection);
229- connection = NULL;
230-}
231-
232-static void shutdown(int signum)
233-{
234- if (running)
235- {
236- running = 0;
237- printf("Signal %d received. Good night.\n", signum);
238- }
239-}
240-
241-mir_eglapp_bool mir_eglapp_running(void)
242-{
243- return running;
244-}
245-
246-void mir_eglapp_swap_buffers(void)
247-{
248- EGLint width, height;
249-
250- if (!running)
251- return;
252-
253- eglSwapBuffers(egldisplay, eglsurface);
254-
255- /*
256- * Querying the surface (actually the current buffer) dimensions here is
257- * the only truly safe way to be sure that the dimensions we think we
258- * have are those of the buffer being rendered to. But this should be
259- * improved in future; https://bugs.launchpad.net/mir/+bug/1194384
260- */
261- if (eglQuerySurface(egldisplay, eglsurface, EGL_WIDTH, &width) &&
262- eglQuerySurface(egldisplay, eglsurface, EGL_HEIGHT, &height))
263- {
264- glViewport(0, 0, width, height);
265- }
266-}
267-
268-static void mir_eglapp_handle_event(MirSurface* surface, MirEvent const* ev, void* context)
269-{
270- (void) surface;
271- (void) context;
272- if (mir_event_get_type(ev) == mir_event_type_resize)
273- {
274- MirResizeEvent const* resize = mir_event_get_resize_event(ev);
275- /*
276- * FIXME: https://bugs.launchpad.net/mir/+bug/1194384
277- * It is unsafe to set the width and height here because we're in a
278- * different thread to that doing the rendering. So we either need
279- * support for event queuing (directing them to another thread) or
280- * full single-threaded callbacks. (LP: #1194384).
281- */
282- printf("Resized to %dx%d\n",
283- mir_resize_event_get_width(resize),
284- mir_resize_event_get_height(resize));
285- }
286-}
287-
288-static const MirDisplayOutput *find_active_output(
289- const MirDisplayConfiguration *conf)
290-{
291- const MirDisplayOutput *output = NULL;
292- int d;
293-
294- for (d = 0; d < (int)conf->num_outputs; d++)
295- {
296- const MirDisplayOutput *out = conf->outputs + d;
297-
298- if (out->used &&
299- out->connected &&
300- out->num_modes &&
301- out->current_mode < out->num_modes)
302+
303+namespace
304+{
305+template<typename ActiveOutputHandler>
306+void for_each_active_output(
307+ MirConnection* const connection, ActiveOutputHandler const& handler)
308+{
309+ /* eglapps are interested in the screen size, so
310+ use mir_connection_create_display_config */
311+ MirDisplayConfiguration* display_config =
312+ mir_connection_create_display_config(connection);
313+
314+ for (MirDisplayOutput* output = display_config->outputs;
315+ output != display_config->outputs + display_config->num_outputs;
316+ ++output)
317+ {
318+ if (output->used &&
319+ output->connected &&
320+ output->num_modes &&
321+ output->current_mode < output->num_modes)
322 {
323- output = out;
324- break;
325+ handler(output);
326 }
327 }
328
329- return output;
330-}
331-
332-mir_eglapp_bool mir_eglapp_init(int argc, char *argv[],
333- unsigned int *width, unsigned int *height)
334-{
335- EGLint ctxattribs[] =
336- {
337- EGL_CONTEXT_CLIENT_VERSION, 2,
338- EGL_NONE
339- };
340+ mir_display_config_destroy(display_config);
341+}
342+
343+MirPixelFormat select_pixel_format(MirConnection* connection)
344+{
345+ unsigned int format[mir_pixel_formats];
346+ unsigned int nformats;
347+
348+ mir_connection_get_available_surface_formats(
349+ connection,
350+ (MirPixelFormat*) format,
351+ mir_pixel_formats,
352+ &nformats);
353+
354+ auto const pixel_format = (MirPixelFormat) format[0];
355+
356+ printf("Server supports %d of %d surface pixel formats. Using format: %d\n",
357+ nformats, mir_pixel_formats, pixel_format);
358+
359+ return pixel_format;
360+}
361+}
362+
363+std::vector<std::shared_ptr<MirEglSurface>> mir_eglapp_init(int argc, char *argv[])
364+{
365 MirSurfaceParameters surfaceparm =
366- {
367- "eglappsurface",
368- 256, 256,
369- mir_pixel_format_xbgr_8888,
370- mir_buffer_usage_hardware,
371- mir_display_output_id_invalid
372- };
373- EGLConfig eglconfig;
374- EGLint neglconfigs;
375- EGLContext eglctx;
376- EGLBoolean ok;
377+ {
378+ "eglappsurface",
379+ 0, 0,
380+ mir_pixel_format_xbgr_8888,
381+ mir_buffer_usage_hardware,
382+ mir_display_output_id_invalid
383+ };
384+
385 EGLint swapinterval = 1;
386 char *mir_socket = NULL;
387
388@@ -159,7 +91,7 @@
389 int i;
390 for (i = 1; i < argc; i++)
391 {
392- mir_eglapp_bool help = 0;
393+ bool help = 0;
394 const char *arg = argv[i];
395
396 if (arg[0] == '-')
397@@ -209,10 +141,6 @@
398 }
399 }
400 break;
401- case 'f':
402- *width = 0;
403- *height = 0;
404- break;
405 case 's':
406 {
407 unsigned int w, h;
408@@ -224,8 +152,8 @@
409 }
410 if (sscanf(arg, "%ux%u", &w, &h) == 2)
411 {
412- *width = w;
413- *height = h;
414+ surfaceparm.width = w;
415+ surfaceparm.height = h;
416 }
417 else
418 {
419@@ -259,111 +187,69 @@
420 printf("Usage: %s [<options>]\n"
421 " -b Background opacity (0.0 - 1.0)\n"
422 " -h Show this help text\n"
423- " -f Force full screen\n"
424 " -o ID Force placement on output monitor ID\n"
425 " -n Don't sync to vblank\n"
426 " -m socket Mir server socket\n"
427 " -s WIDTHxHEIGHT Force surface size\n"
428 " -q Quiet mode (no messages output)\n"
429 , argv[0]);
430- return 0;
431+ return {};
432 }
433 }
434 }
435
436- connection = mir_connect_sync(mir_socket, appname);
437- CHECK(mir_connection_is_valid(connection), "Can't get connection");
438-
439- /* eglapps are interested in the screen size, so
440- use mir_connection_create_display_config */
441- MirDisplayConfiguration* display_config =
442- mir_connection_create_display_config(connection);
443-
444- const MirDisplayOutput *output = find_active_output(display_config);
445-
446- if (output == NULL)
447- {
448- printf("No active outputs found.\n");
449- return 0;
450- }
451-
452- const MirDisplayMode *mode = &output->modes[output->current_mode];
453-
454- unsigned int format[mir_pixel_formats];
455- unsigned int nformats;
456-
457- mir_connection_get_available_surface_formats(connection,
458- (MirPixelFormat*) format, mir_pixel_formats, &nformats);
459-
460- surfaceparm.pixel_format = (MirPixelFormat) format[0];
461-
462- printf("Current active output is %dx%d %+d%+d\n",
463- mode->horizontal_resolution, mode->vertical_resolution,
464- output->position_x, output->position_y);
465-
466- surfaceparm.width = *width > 0 ? *width : mode->horizontal_resolution;
467- surfaceparm.height = *height > 0 ? *height : mode->vertical_resolution;
468-
469- mir_display_config_destroy(display_config);
470-
471- printf("Server supports %d of %d surface pixel formats. Using format: %d\n",
472- nformats, mir_pixel_formats, surfaceparm.pixel_format);
473- unsigned int bpp = 8 * MIR_BYTES_PER_PIXEL(surfaceparm.pixel_format);
474- EGLint attribs[] =
475- {
476- EGL_SURFACE_TYPE, EGL_WINDOW_BIT,
477- EGL_RENDERABLE_TYPE, EGL_OPENGL_ES2_BIT,
478- EGL_COLOR_BUFFER_TYPE, EGL_RGB_BUFFER,
479- EGL_BUFFER_SIZE, (EGLint) bpp,
480- EGL_NONE
481- };
482-
483- surface = mir_connection_create_surface_sync(connection, &surfaceparm);
484- CHECK(mir_surface_is_valid(surface), "Can't create a surface");
485-
486- mir_surface_set_event_handler(surface, mir_eglapp_handle_event, NULL);
487-
488- egldisplay = eglGetDisplay(
489- (EGLNativeDisplayType) mir_connection_get_egl_native_display(connection));
490- CHECK(egldisplay != EGL_NO_DISPLAY, "Can't eglGetDisplay");
491-
492- ok = eglInitialize(egldisplay, NULL, NULL);
493- CHECK(ok, "Can't eglInitialize");
494-
495- ok = eglChooseConfig(egldisplay, attribs, &eglconfig, 1, &neglconfigs);
496- CHECK(ok, "Could not eglChooseConfig");
497- CHECK(neglconfigs > 0, "No EGL config available");
498-
499- eglsurface = eglCreateWindowSurface(egldisplay, eglconfig,
500- (EGLNativeWindowType)mir_buffer_stream_get_egl_native_window(mir_surface_get_buffer_stream(surface)), NULL);
501- CHECK(eglsurface != EGL_NO_SURFACE, "eglCreateWindowSurface failed");
502-
503- eglctx = eglCreateContext(egldisplay, eglconfig, EGL_NO_CONTEXT,
504- ctxattribs);
505- CHECK(eglctx != EGL_NO_CONTEXT, "eglCreateContext failed");
506-
507- ok = eglMakeCurrent(egldisplay, eglsurface, eglsurface, eglctx);
508- CHECK(ok, "Can't eglMakeCurrent");
509-
510- signal(SIGINT, shutdown);
511- signal(SIGTERM, shutdown);
512-
513- *width = surfaceparm.width;
514- *height = surfaceparm.height;
515-
516- eglSwapInterval(egldisplay, swapinterval);
517-
518- running = 1;
519-
520- return 1;
521-}
522-
523-struct MirConnection* mir_eglapp_native_connection()
524-{
525- return connection;
526-}
527-
528-struct MirSurface* mir_eglapp_native_surface()
529-{
530- return surface;
531+ MirConnection* const connection{mir_connect_sync(mir_socket, appname)};
532+ if (!mir_connection_is_valid(connection))
533+ throw std::runtime_error("Can't get connection");
534+
535+ auto const pixel_format = select_pixel_format(connection);
536+ surfaceparm.pixel_format = pixel_format;
537+
538+ auto const mir_egl_app = make_mir_eglapp(connection, pixel_format);
539+
540+ std::vector<std::shared_ptr<MirEglSurface>> result;
541+
542+ // If a size has been specified just do that
543+ if (surfaceparm.width && surfaceparm.height)
544+ {
545+ result.push_back(std::make_shared<MirEglSurface>(mir_egl_app, surfaceparm, swapinterval));
546+ return result;
547+ }
548+
549+ // If an output has been specified just do that
550+ if (surfaceparm.output_id != mir_display_output_id_invalid)
551+ {
552+ for_each_active_output(connection, [&](MirDisplayOutput const* output)
553+ {
554+ if (output->output_id == surfaceparm.output_id)
555+ {
556+ auto const& mode = output->modes[output->current_mode];
557+ surfaceparm.width = mode.horizontal_resolution;
558+ surfaceparm.height = mode.vertical_resolution;
559+ }
560+ });
561+ result.push_back(std::make_shared<MirEglSurface>(mir_egl_app, surfaceparm, swapinterval));
562+ return result;
563+ }
564+
565+ // but normally, we're fullscreen on every active output
566+ for_each_active_output(connection, [&](MirDisplayOutput const* output)
567+ {
568+ auto const& mode = output->modes[output->current_mode];
569+
570+ printf("Active output [%u] at (%d, %d) is %dx%d\n",
571+ output->output_id,
572+ output->position_x, output->position_y,
573+ mode.horizontal_resolution, mode.vertical_resolution);
574+
575+ surfaceparm.width = mode.horizontal_resolution;
576+ surfaceparm.height = mode.vertical_resolution;
577+ surfaceparm.output_id = output->output_id;
578+ result.push_back(std::make_shared<MirEglSurface>(mir_egl_app, surfaceparm, swapinterval));
579+ });
580+
581+ if (result.empty())
582+ throw std::runtime_error("No active outputs found.");
583+
584+ return result;
585 }
586
587=== modified file 'spinner/eglapp.h'
588--- spinner/eglapp.h 2014-03-08 22:01:45 +0000
589+++ spinner/eglapp.h 2015-07-20 10:34:37 +0000
590@@ -19,26 +19,13 @@
591 #ifndef __EGLAPP_H__
592 #define __EGLAPP_H__
593
594-#ifdef __cplusplus
595-extern "C" {
596-#endif
597+#include <memory>
598+#include <vector>
599
600-typedef int mir_eglapp_bool;
601-struct MirConnection;
602-struct MirSurface;
603+class MirEglSurface;
604
605 extern float mir_eglapp_background_opacity;
606
607-mir_eglapp_bool mir_eglapp_init(int argc, char *argv[],
608- unsigned int *width, unsigned int *height);
609-void mir_eglapp_swap_buffers(void);
610-mir_eglapp_bool mir_eglapp_running(void);
611-void mir_eglapp_shutdown(void);
612-
613-struct MirConnection* mir_eglapp_native_connection();
614-struct MirSurface* mir_eglapp_native_surface();
615-#ifdef __cplusplus
616-}
617-#endif
618+std::vector<std::shared_ptr<MirEglSurface>> mir_eglapp_init(int argc, char *argv[]);
619
620 #endif
621
622=== renamed file 'spinner/eglspinner.c' => 'spinner/eglspinner.cpp'
623--- spinner/eglspinner.c 2015-05-01 14:16:27 +0000
624+++ spinner/eglspinner.cpp 2015-07-20 10:34:37 +0000
625@@ -1,5 +1,5 @@
626 /*
627- * Copyright © 2013 Canonical Ltd.
628+ * Copyright © 2013-2015 Canonical Ltd.
629 *
630 * This program is free software: you can redistribute it and/or modify
631 * it under the terms of the GNU General Public License version 3 as
632@@ -15,22 +15,26 @@
633 *
634 * Authors: Daniel van Vugt <daniel.van.vugt@canonical.com>
635 * Mirco Müller <mirco.mueller@canonical.com>
636+ * Alan Griffiths <alan@octopull.co.uk>
637+ * Kevin DuBois <kevin.dubois@canonical.com>
638 */
639
640 #include "eglapp.h"
641+#include "miregl.h"
642 #include <assert.h>
643-#include <cairo.h>
644 #include <glib.h>
645-#include <stdio.h>
646 #include <string.h>
647-#include <strings.h>
648-#include <stdlib.h>
649 #include <GLES2/gl2.h>
650-#include <math.h>
651 #include <sys/stat.h>
652 #if HAVE_PROPS
653 #include <hybris/properties/properties.h>
654 #endif
655+#include <signal.h>
656+
657+#include "wallpaper.h"
658+#include "logo.h"
659+#include "white_dot.h"
660+#include "orange_dot.h"
661
662 // this is needed for get_gu() to obtain the grid-unit value
663 #define MAX_LENGTH 256
664@@ -41,9 +45,17 @@
665 #define FILE_BASE "/etc/ubuntu-touch-session.d/"
666 #define FILE_EXTENSION ".conf"
667
668+enum TextureIds {
669+ WALLPAPER = 0,
670+ LOGO,
671+ WHITE_DOT,
672+ ORANGE_DOT,
673+ MAX_TEXTURES
674+};
675+
676 int get_gu ()
677 {
678- int gu = 10; // use 10 as a default value
679+ int gu = 13; // use 13 as a default value
680 FILE* handle = NULL;
681 int i = 0;
682 int j = 0;
683@@ -63,7 +75,7 @@
684 else
685 {
686 #ifdef HAVE_PROPS
687- char* defaultValue = "";
688+ char const* defaultValue = "";
689 char value[PROP_VALUE_MAX];
690 property_get (PROP_KEY, value, defaultValue);
691 strcat (filename, value);
692@@ -125,56 +137,34 @@
693 }
694
695 // Colours from http://design.ubuntu.com/brand/colour-palette
696-#define MID_AUBERGINE 0.368627451f, 0.152941176f, 0.31372549f
697-#define ORANGE 0.866666667f, 0.282352941f, 0.141414141f
698-#define WARM_GREY 0.682352941f, 0.654901961f, 0.623529412f
699-#define COOL_GREY 0.2f, 0.2f, 0.2f
700-#define LIGHT_AUBERGINE 0.466666667f, 0.297297297f, 0.435294118f
701-#define DARK_AUBERGINE 0.17254902f, 0.0f, 0.117647059f
702+//#define MID_AUBERGINE 0.368627451f, 0.152941176f, 0.31372549f
703+//#define ORANGE 0.866666667f, 0.282352941f, 0.141414141f
704+//#define WARM_GREY 0.682352941f, 0.654901961f, 0.623529412f
705+//#define COOL_GREY 0.2f, 0.2f, 0.2f
706+//#define LIGHT_AUBERGINE 0.466666667f, 0.297297297f, 0.435294118f
707+//#define DARK_AUBERGINE 0.17254902f, 0.0f, 0.117647059f
708 #define BLACK 0.0f, 0.0f, 0.0f
709-#define WHITE 1.0f, 1.0f, 1.0f
710-
711-cairo_surface_t* pngToSurface (const char* filename)
712-{
713- // sanity check
714- if (!filename)
715- return NULL;
716-
717- // create surface from PNG
718- cairo_surface_t* surface = NULL;
719- surface = cairo_image_surface_create_from_png (filename);
720- if (cairo_surface_status (surface) != CAIRO_STATUS_SUCCESS)
721- return NULL;
722-
723- return surface;
724-}
725-
726-void uploadTexture (GLuint id, const char* filename)
727-{
728- if (!id || !filename)
729- return;
730-
731- cairo_surface_t* surface = pngToSurface (filename);
732-
733+//#define WHITE 1.0f, 1.0f, 1.0f
734+
735+template <typename Image>
736+void uploadTexture (GLuint id, Image& image)
737+{
738 glBindTexture(GL_TEXTURE_2D, id);
739
740- if (cairo_surface_status (surface) == CAIRO_STATUS_SUCCESS)
741- {
742- glTexImage2D(GL_TEXTURE_2D,
743- 0,
744- cairo_image_surface_get_format (surface) == CAIRO_FORMAT_ARGB32 ? GL_RGBA : GL_RGB,
745- cairo_image_surface_get_width (surface),
746- cairo_image_surface_get_height (surface),
747- 0,
748- cairo_image_surface_get_format (surface) == CAIRO_FORMAT_ARGB32 ? GL_RGBA : GL_RGB,
749- GL_UNSIGNED_BYTE,
750- cairo_image_surface_get_data (surface));
751- cairo_surface_destroy (surface);
752- }
753+ glTexImage2D(GL_TEXTURE_2D,
754+ 0,
755+ GL_RGBA,
756+ image.width,
757+ image.height,
758+ 0,
759+ GL_RGBA,
760+ GL_UNSIGNED_BYTE,
761+ image.pixel_data);
762 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
763- glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
764+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_LINEAR);
765 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
766 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
767+ glGenerateMipmap(GL_TEXTURE_2D);
768 glBindTexture(GL_TEXTURE_2D, 0);
769 }
770
771@@ -215,145 +205,150 @@
772 typedef struct _AnimationValues
773 {
774 double lastTimeStamp;
775- double angle;
776- double fadeBackground;
777- double fadeLogo;
778- double fadeGlow;
779+ GLfloat fadeBackground;
780+ int dot_mask;
781 } AnimationValues;
782
783+void ortho(GLfloat* mat,
784+ GLfloat left,
785+ GLfloat right,
786+ GLfloat bottom,
787+ GLfloat top,
788+ GLfloat near,
789+ GLfloat far)
790+{
791+ if (right == left ||
792+ top == bottom ||
793+ far == near ||
794+ mat == NULL)
795+ {
796+ return;
797+ }
798+
799+ mat[0] = 2.0f / (right - left);
800+ mat[1] = 0.0f;
801+ mat[2] = 0.0f;
802+ mat[3] = 0.0f;
803+
804+ mat[4] = 0.0f;
805+ mat[5] = 2.0f / (top - bottom);
806+ mat[6] = 0.0f;
807+ mat[7] = 0.0f;
808+
809+ mat[8] = 0.0f;
810+ mat[9] = 0.0f;
811+ mat[10] = -2.0f / (far - near);
812+ mat[11] = 0.0f;
813+
814+ mat[12] = -(right + left) / (right - left);
815+ mat[13] = -(top + bottom) / (top - bottom);
816+ mat[14] = -(far + near) / (far - near);
817+ mat[15] = 1.0f;
818+}
819+
820+GLfloat gu2px(GLfloat gu) {
821+ static GLfloat pixelsPerGU = get_gu();
822+
823+ return gu * pixelsPerGU;
824+}
825+
826 void
827 updateAnimation (GTimer* timer, AnimationValues* anim)
828 {
829 if (!timer || !anim)
830 return;
831
832- //1.) 0.0 - 0.6: logo fades in fully
833- //2.) 0.0 - 6.0: logo does one full spin 360°
834- //3.) 6.0 - 6.833: glow fades in fully, black-background fades out to 50%
835- //4.) 6.833 - 7.666: glow fades out fully, black-background fades out to 0%
836- //5.) 7.666 - 8.266: logo fades out fully
837- //8.266..: now spinner can be closed as all its elements are faded out
838+ static const int sequence[] = {0, 1, 3, 7, 15, 31};
839+ static int counter = 0;
840+ static double second = 0.0f;
841
842 double elapsed = g_timer_elapsed (timer, NULL);
843- double dt = elapsed - anim->lastTimeStamp;
844+
845+ if (second >= 1.0f) {
846+ second = 0.0f;
847+ counter++;
848+ } else {
849+ second += elapsed - anim->lastTimeStamp;
850+ }
851+ anim->dot_mask = sequence[counter%6];
852+
853 anim->lastTimeStamp = elapsed;
854-
855- // step 1.)
856- if (elapsed < 0.6f)
857- anim->fadeLogo += 1.6f * dt;
858-
859- // step 2.)
860- anim->angle -= (0.017453292519943f * 360.0f / 6.0f) * dt;
861-
862- // step 3.) glow
863- if (elapsed > 6.0f && elapsed < 6.833f)
864- anim->fadeGlow += 1.2f * dt;
865-
866- // Ignore the following three until we can synchronize with greeter
867-
868- // step 3.) background
869- //if (elapsed > 6.0f && elapsed < 6.833f)
870- // anim->fadeBackground -= 0.6f * dt;
871-
872- // step 4.) background
873- //if (elapsed > 7.0f)
874- // anim->fadeBackground -= 0.6f * dt;
875-
876- // step 5.)
877- //if (elapsed > 6.833f)
878- // anim->fadeLogo -= 1.6f * dt;
879+}
880+
881+namespace
882+{
883+const char vShaderSrcPlain[] =
884+ "attribute vec4 aPosition; \n"
885+ "attribute vec2 aTexCoords; \n"
886+ "uniform vec2 uOffset; \n"
887+ "varying vec2 vTexCoords; \n"
888+ "uniform mat4 uProjMat; \n"
889+ "void main() \n"
890+ "{ \n"
891+ " vTexCoords = aTexCoords + vec2 (0.5, 0.5); \n"
892+ " gl_Position = uProjMat * vec4(aPosition.xy + uOffset.xy, 0.0, 1.0);\n"
893+ "} \n";
894+
895+const char fShaderSrcPlain[] =
896+ "precision mediump float; \n"
897+ "varying vec2 vTexCoords; \n"
898+ "uniform sampler2D uSampler; \n"
899+ "void main() \n"
900+ "{ \n"
901+ " gl_FragColor = texture2D(uSampler, vTexCoords);\n"
902+ "} \n";
903+
904+static volatile sig_atomic_t running = 0;
905+
906+static void shutdown(int signum)
907+{
908+ if (running)
909+ {
910+ running = 0;
911+ printf("Signal %d received. Good night.\n", signum);
912+ }
913+}
914+
915+bool mir_eglapp_running()
916+{
917+ return running;
918+}
919 }
920
921 int main(int argc, char *argv[])
922+try
923 {
924- const char vShaderSrcSpinner[] =
925- "attribute vec4 vPosition; \n"
926- "attribute vec2 aTexCoords; \n"
927- "uniform float theta; \n"
928- "varying vec2 vTexCoords; \n"
929- "void main() \n"
930- "{ \n"
931- " float c = cos(theta); \n"
932- " float s = sin(theta); \n"
933- " mat2 m; \n"
934- " m[0] = vec2(c, s); \n"
935- " m[1] = vec2(-s, c); \n"
936- " vTexCoords = m * aTexCoords + vec2 (0.5, 0.5); \n"
937- " gl_Position = vec4(vPosition.xy, -1.0, 1.0); \n"
938- "} \n";
939-
940- const char fShaderSrcGlow[] =
941- "precision mediump float; \n"
942- "varying vec2 vTexCoords; \n"
943- "uniform sampler2D uSampler; \n"
944- "uniform float uFadeGlow; \n"
945- "void main() \n"
946- "{ \n"
947- " // swizzle because texture was created with cairo\n"
948- " vec4 col = texture2D(uSampler, vTexCoords).bgra; \n"
949- " float r = col.r * uFadeGlow; \n"
950- " float g = col.g * uFadeGlow; \n"
951- " float b = col.b * uFadeGlow; \n"
952- " float a = col.a * uFadeGlow; \n"
953- " gl_FragColor = vec4(r, g, b, a); \n"
954- "} \n";
955-
956- const char fShaderSrcLogo[] =
957- "precision mediump float; \n"
958- "varying vec2 vTexCoords; \n"
959- "uniform sampler2D uSampler; \n"
960- "uniform float uFadeLogo; \n"
961- "void main() \n"
962- "{ \n"
963- " // swizzle because texture was created with cairo\n"
964- " vec4 col = texture2D(uSampler, vTexCoords).bgra; \n"
965- " float r = col.r * uFadeLogo; \n"
966- " float g = col.g * uFadeLogo; \n"
967- " float b = col.b * uFadeLogo; \n"
968- " float a = col.a * uFadeLogo; \n"
969- " gl_FragColor = vec4(r, g, b, a); \n"
970- "} \n";
971-
972- GLuint prog[2];
973- GLuint texture[2];
974- GLint vpos[2];
975- GLint theta;
976- GLint fadeGlow;
977- GLint fadeLogo;
978- GLint aTexCoords[2];
979- GLint sampler[2];
980- unsigned int width = 0;
981- unsigned int height = 0;
982-
983- if (!mir_eglapp_init(argc, argv, &width, &height))
984- return 1;
985-
986- double pixelSize = (double) get_gu () * 11.18;
987- double halfRealWidth = ((2.0 / (double) width) * pixelSize) / 2.0;
988- double halfRealHeight = ((2.0 / (double) height) * pixelSize) / 2.0;
989-
990- const GLfloat vertices[] =
991- {
992- halfRealWidth, halfRealHeight,
993- halfRealWidth, -halfRealHeight,
994- -halfRealWidth, halfRealHeight,
995- -halfRealWidth, -halfRealHeight,
996- };
997-
998- const GLfloat texCoordsSpinner[] =
999- {
1000- -0.5f, 0.5f,
1001+ GLuint prog[3];
1002+ GLuint texture[MAX_TEXTURES];
1003+ GLint vpos[MAX_TEXTURES];
1004+ GLint aTexCoords[MAX_TEXTURES];
1005+ GLint sampler[MAX_TEXTURES];
1006+ GLint offset[MAX_TEXTURES];
1007+ GLint projMat[MAX_TEXTURES];
1008+
1009+ auto const surfaces = mir_eglapp_init(argc, argv);
1010+
1011+ if (!surfaces.size())
1012+ {
1013+ printf("No surfaces created\n");
1014+ return EXIT_SUCCESS;
1015+ }
1016+
1017+ running = 1;
1018+ signal(SIGINT, shutdown);
1019+ signal(SIGTERM, shutdown);
1020+
1021+ const GLfloat texCoords[] =
1022+ {
1023+ 0.5f, -0.5f,
1024+ 0.5f, 0.5f,
1025 -0.5f, -0.5f,
1026- 0.5f, 0.5f,
1027- 0.5f, -0.5f,
1028+ -0.5f, 0.5f,
1029 };
1030
1031- prog[0] = createShaderProgram (vShaderSrcSpinner, fShaderSrcGlow);
1032- prog[1] = createShaderProgram (vShaderSrcSpinner, fShaderSrcLogo);
1033-
1034- // setup viewport and projection
1035- glClearColor(BLACK, mir_eglapp_background_opacity);
1036- glViewport(0, 0, width, height);
1037+ prog[WALLPAPER] = createShaderProgram(vShaderSrcPlain, fShaderSrcPlain);
1038+ prog[LOGO] = createShaderProgram(vShaderSrcPlain, fShaderSrcPlain);
1039+ prog[WHITE_DOT] = createShaderProgram(vShaderSrcPlain, fShaderSrcPlain);
1040
1041 // setup proper GL-blending
1042 glEnable(GL_BLEND);
1043@@ -361,66 +356,129 @@
1044 glBlendEquation(GL_FUNC_ADD);
1045
1046 // get locations of shader-attributes/uniforms
1047- vpos[0] = glGetAttribLocation(prog[0], "vPosition");
1048- aTexCoords[0] = glGetAttribLocation(prog[0], "aTexCoords");
1049- theta = glGetUniformLocation(prog[0], "theta");
1050- sampler[0] = glGetUniformLocation(prog[0], "uSampler");
1051- fadeGlow = glGetUniformLocation(prog[0], "uFadeGlow");
1052- vpos[1] = glGetAttribLocation(prog[1], "vPosition");
1053- aTexCoords[1] = glGetAttribLocation(prog[1], "aTexCoords");
1054- sampler[1] = glGetUniformLocation(prog[1], "uSampler");
1055- fadeLogo = glGetUniformLocation(prog[1], "uFadeLogo");
1056+ vpos[WALLPAPER] = glGetAttribLocation(prog[WALLPAPER], "aPosition");
1057+ aTexCoords[WALLPAPER] = glGetAttribLocation(prog[WALLPAPER], "aTexCoords");
1058+ sampler[WALLPAPER] = glGetUniformLocation(prog[WALLPAPER], "uSampler");
1059+ offset[WALLPAPER] = glGetUniformLocation(prog[WALLPAPER], "uOffset");
1060+ projMat[WALLPAPER] = glGetUniformLocation(prog[WALLPAPER], "uProjMat");
1061+
1062+ vpos[LOGO] = glGetAttribLocation(prog[LOGO], "aPosition");
1063+ aTexCoords[LOGO] = glGetAttribLocation(prog[LOGO], "aTexCoords");
1064+ sampler[LOGO] = glGetUniformLocation(prog[LOGO], "uSampler");
1065+ offset[LOGO] = glGetUniformLocation(prog[LOGO], "uOffset");
1066+ projMat[LOGO] = glGetUniformLocation(prog[LOGO], "uProjMat");
1067+
1068+ vpos[WHITE_DOT] = glGetAttribLocation(prog[WHITE_DOT], "aPosition");
1069+ aTexCoords[WHITE_DOT] = glGetAttribLocation(prog[WHITE_DOT], "aTexCoords");
1070+ sampler[WHITE_DOT] = glGetUniformLocation(prog[WHITE_DOT], "uSampler");
1071+ offset[WHITE_DOT] = glGetUniformLocation(prog[WHITE_DOT], "uOffset");
1072+ projMat[WHITE_DOT] = glGetUniformLocation(prog[WHITE_DOT], "uProjMat");
1073
1074 // create and upload spinner-artwork
1075- glGenTextures(2, texture);
1076- uploadTexture(texture[0], PKGDATADIR "/spinner-glow.png");
1077- uploadTexture(texture[1], PKGDATADIR "/spinner-logo.png");
1078+ // note that the embedded image data has pre-multiplied alpha
1079+ glGenTextures(MAX_TEXTURES, texture);
1080+ uploadTexture(texture[WALLPAPER], wallpaper);
1081+ uploadTexture(texture[LOGO], logo);
1082+ uploadTexture(texture[WHITE_DOT], white_dot);
1083+ uploadTexture(texture[ORANGE_DOT], orange_dot);
1084
1085 // bunch of shader-attributes to enable
1086- glVertexAttribPointer(vpos[0], 2, GL_FLOAT, GL_FALSE, 0, vertices);
1087- glVertexAttribPointer(vpos[1], 2, GL_FLOAT, GL_FALSE, 0, vertices);
1088- glVertexAttribPointer(aTexCoords[0], 2, GL_FLOAT, GL_FALSE, 0, texCoordsSpinner);
1089- glVertexAttribPointer(aTexCoords[1], 2, GL_FLOAT, GL_FALSE, 0, texCoordsSpinner);
1090- glEnableVertexAttribArray(vpos[0]);
1091- glEnableVertexAttribArray(vpos[1]);
1092- glEnableVertexAttribArray(aTexCoords[0]);
1093- glEnableVertexAttribArray(aTexCoords[1]);
1094+ glVertexAttribPointer(aTexCoords[WALLPAPER], 2, GL_FLOAT, GL_FALSE, 0, texCoords);
1095+ glEnableVertexAttribArray(vpos[WALLPAPER]);
1096+ glEnableVertexAttribArray(aTexCoords[WALLPAPER]);
1097+ glVertexAttribPointer(aTexCoords[LOGO], 2, GL_FLOAT, GL_FALSE, 0, texCoords);
1098+ glEnableVertexAttribArray(vpos[LOGO]);
1099+ glEnableVertexAttribArray(aTexCoords[LOGO]);
1100+ glVertexAttribPointer(aTexCoords[WHITE_DOT], 2, GL_FLOAT, GL_FALSE, 0, texCoords);
1101+ glEnableVertexAttribArray(vpos[WHITE_DOT]);
1102+ glEnableVertexAttribArray(aTexCoords[WHITE_DOT]);
1103 glActiveTexture(GL_TEXTURE0);
1104
1105- AnimationValues anim = {0.0, 0.0, 1.0, 0.0, 0.0};
1106- GTimer* timer = g_timer_new ();
1107+ AnimationValues anim = {0.0, 0.0, 0};
1108+ GTimer* timer = g_timer_new();
1109
1110 while (mir_eglapp_running())
1111 {
1112- glClearColor(BLACK, anim.fadeBackground);
1113- glClear(GL_COLOR_BUFFER_BIT);
1114-
1115- // draw glow
1116- glUseProgram(prog[0]);
1117- glBindTexture(GL_TEXTURE_2D, texture[0]);
1118- glUniform1i(sampler[0], 0);
1119- glUniform1f(theta, anim.angle);
1120- glUniform1f(fadeGlow, anim.fadeGlow);
1121- glDrawArrays(GL_TRIANGLE_STRIP, 0, 4);
1122-
1123- // draw logo
1124- glUseProgram(prog[1]);
1125- glBindTexture(GL_TEXTURE_2D, texture[1]);
1126- glUniform1i(sampler[1], 0);
1127- glUniform1f(theta, anim.angle);
1128- glUniform1f(fadeLogo, anim.fadeLogo);
1129- glDrawArrays(GL_TRIANGLE_STRIP, 0, 4);
1130+ for (auto const& surface : surfaces)
1131+ surface->paint([&](unsigned int width, unsigned int height)
1132+ {
1133+ GLfloat logoWidth = gu2px (14.5f);
1134+ GLfloat logoHeight = gu2px (3.0f);
1135+ GLfloat logoXOffset = gu2px (1.0f);
1136+ GLfloat dotSize = gu2px (0.5f);
1137+ GLfloat dotXGap = gu2px (2.5f);
1138+ GLfloat dotYGap = gu2px (2.0f);
1139+
1140+ const GLfloat fullscreen[] = {
1141+ (GLfloat) width, 0.0f,
1142+ (GLfloat) width, (GLfloat) height,
1143+ 0.0f, 0.0f,
1144+ 0.0f, (GLfloat) height
1145+ };
1146+
1147+ const GLfloat logo[] = {
1148+ logoWidth, 0.0f,
1149+ logoWidth, logoHeight,
1150+ 0.0f, 0.0f,
1151+ 0.0f, logoHeight
1152+ };
1153+
1154+ const GLfloat dot[] = {
1155+ dotSize, 0.0f,
1156+ dotSize, dotSize,
1157+ 0.0f, 0.0f,
1158+ 0.0f, dotSize
1159+ };
1160+
1161+ GLfloat projMatrix[16];
1162+ ortho(&projMatrix[0], 0.0f, (GLfloat) width, (GLfloat) height, 0.0f, -1.0f, 1.0f);
1163+
1164+ glViewport(0, 0, width, height);
1165+
1166+ glClearColor(BLACK, anim.fadeBackground);
1167+ glClear(GL_COLOR_BUFFER_BIT);
1168+
1169+ // draw wallpaper backdrop
1170+ glVertexAttribPointer(vpos[WALLPAPER], 2, GL_FLOAT, GL_FALSE, 0, fullscreen);
1171+ glUseProgram(prog[WALLPAPER]);
1172+ glBindTexture(GL_TEXTURE_2D, texture[WALLPAPER]);
1173+ glUniform1i(sampler[WALLPAPER], 0);
1174+ glUniform2f(offset[WALLPAPER], 0.0f, 0.0f);
1175+ glUniformMatrix4fv(projMat[WALLPAPER], 1, GL_FALSE, projMatrix);
1176+ glDrawArrays(GL_TRIANGLE_STRIP, 0, 4);
1177+
1178+ // draw logo
1179+ glVertexAttribPointer(vpos[LOGO], 2, GL_FLOAT, GL_FALSE, 0, logo);
1180+ glUseProgram(prog[LOGO]);
1181+ glBindTexture(GL_TEXTURE_2D, texture[LOGO]);
1182+ glUniform1i(sampler[LOGO], 0);
1183+ glUniform2f(offset[LOGO], width/2.0f - logoWidth / 2.0f + logoXOffset, height / 2.0f - logoHeight * 0.75f);
1184+ glUniformMatrix4fv(projMat[LOGO], 1, GL_FALSE, projMatrix);
1185+ glDrawArrays(GL_TRIANGLE_STRIP, 0, 4);
1186+
1187+ // draw white/orange dots
1188+ glVertexAttribPointer(vpos[WHITE_DOT], 2, GL_FLOAT, GL_FALSE, 0, dot);
1189+ glUseProgram(prog[WHITE_DOT]);
1190+ glUniform1i(sampler[WHITE_DOT], 0);
1191+ glUniformMatrix4fv(projMat[WHITE_DOT], 1, GL_FALSE, projMatrix);
1192+ for (int i = -2; i < 3; i++) {
1193+ glBindTexture(GL_TEXTURE_2D, texture[anim.dot_mask >> (i + 2) ? ORANGE_DOT : WHITE_DOT]);
1194+ glUniform2f(offset[WHITE_DOT], width/2.0f + i * dotXGap, height / 2.0f + logoHeight / 2.0f + dotYGap - logoHeight * 0.25f);
1195+ glDrawArrays(GL_TRIANGLE_STRIP, 0, 4);
1196+ }
1197+ });
1198
1199 // update animation variable
1200- updateAnimation (timer, &anim);
1201-
1202- mir_eglapp_swap_buffers();
1203+ updateAnimation(timer, &anim);
1204 }
1205
1206- mir_eglapp_shutdown();
1207-
1208- glDeleteTextures(2, texture);
1209+ glDeleteTextures(MAX_TEXTURES, texture);
1210 g_timer_destroy (timer);
1211
1212- return 0;
1213+ return EXIT_SUCCESS;
1214+}
1215+catch (std::exception const& x)
1216+{
1217+ printf("%s\n", x.what());
1218+ return EXIT_FAILURE;
1219 }
1220
1221=== added file 'spinner/logo.png'
1222Binary files spinner/logo.png 1970-01-01 00:00:00 +0000 and spinner/logo.png 2015-07-20 10:34:37 +0000 differ
1223=== added file 'spinner/miregl.cpp'
1224--- spinner/miregl.cpp 1970-01-01 00:00:00 +0000
1225+++ spinner/miregl.cpp 2015-07-20 10:34:37 +0000
1226@@ -0,0 +1,247 @@
1227+/*
1228+ * Copyright © 2015 Canonical Ltd.
1229+ *
1230+ * This program is free software: you can redistribute it and/or modify
1231+ * it under the terms of the GNU General Public License version 3 as
1232+ * published by the Free Software Foundation.
1233+ *
1234+ * This program is distributed in the hope that it will be useful,
1235+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
1236+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
1237+ * GNU General Public License for more details.
1238+ *
1239+ * You should have received a copy of the GNU General Public License
1240+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
1241+ */
1242+
1243+#include "miregl.h"
1244+
1245+#include <cstring>
1246+
1247+#include <GLES2/gl2.h>
1248+
1249+class MirEglApp
1250+{
1251+public:
1252+ MirEglApp(MirConnection* const connection, MirPixelFormat pixel_format);
1253+
1254+ EGLSurface create_surface(MirSurface* surface);
1255+
1256+ void release_current();
1257+
1258+ void make_current(EGLSurface eglsurface) const;
1259+
1260+ void swap_buffers(EGLSurface eglsurface) const;
1261+
1262+ void destroy_surface(EGLSurface eglsurface) const;
1263+
1264+ void get_surface_size(EGLSurface eglsurface, int* width, int* height) const;
1265+
1266+ void set_swap_interval(EGLSurface eglsurface, int interval) const;
1267+
1268+ bool supports_surfaceless_context();
1269+
1270+ ~MirEglApp();
1271+
1272+ MirConnection* const connection;
1273+private:
1274+ EGLDisplay egldisplay;
1275+ EGLContext eglctx;
1276+ EGLConfig eglconfig;
1277+ EGLint neglconfigs;
1278+ EGLSurface dummy_surface;
1279+};
1280+
1281+std::shared_ptr<MirEglApp> make_mir_eglapp(
1282+ MirConnection* const connection, MirPixelFormat const& pixel_format)
1283+{
1284+ return std::make_shared<MirEglApp>(connection, pixel_format);
1285+}
1286+
1287+namespace
1288+{
1289+MirSurface* create_surface(MirConnection* const connection, MirSurfaceParameters const& surfaceparm)
1290+{
1291+ auto const spec = mir_connection_create_spec_for_normal_surface(
1292+ connection,
1293+ surfaceparm.width,
1294+ surfaceparm.height,
1295+ surfaceparm.pixel_format);
1296+
1297+ mir_surface_spec_set_name(spec, surfaceparm.name);
1298+ mir_surface_spec_set_buffer_usage(spec, surfaceparm.buffer_usage);
1299+ mir_surface_spec_set_fullscreen_on_output(spec, surfaceparm.output_id);
1300+
1301+ auto const surface = mir_surface_create_sync(spec);
1302+ mir_surface_spec_release(spec);
1303+
1304+ if (!mir_surface_is_valid(surface))
1305+ throw std::runtime_error(std::string("Can't create a surface ") + mir_surface_get_error_message(surface));
1306+
1307+ if (surfaceparm.output_id != mir_display_output_id_invalid)
1308+ mir_surface_set_state(surface, mir_surface_state_fullscreen);
1309+
1310+ return surface;
1311+}
1312+}
1313+
1314+MirEglSurface::MirEglSurface(std::shared_ptr<MirEglApp> const& mir_egl_app, MirSurfaceParameters const& surfaceparm, int swapinterval) :
1315+ mir_egl_app{mir_egl_app},
1316+ surface{create_surface(mir_egl_app->connection, surfaceparm)},
1317+ eglsurface{mir_egl_app->create_surface(surface)},
1318+ width_{0},
1319+ height_{0}
1320+{
1321+ mir_egl_app->set_swap_interval(eglsurface, swapinterval);
1322+}
1323+
1324+MirEglSurface::~MirEglSurface()
1325+{
1326+ mir_egl_app->destroy_surface(eglsurface);
1327+ mir_surface_release_sync(surface);
1328+}
1329+
1330+void MirEglSurface::egl_make_current()
1331+{
1332+ mir_egl_app->get_surface_size(eglsurface, &width_, &height_);
1333+ mir_egl_app->make_current(eglsurface);
1334+}
1335+
1336+void MirEglSurface::swap_buffers()
1337+{
1338+ mir_egl_app->swap_buffers(eglsurface);
1339+}
1340+
1341+unsigned int MirEglSurface::width() const
1342+{
1343+ return width_;
1344+}
1345+
1346+unsigned int MirEglSurface::height() const
1347+{
1348+ return height_;
1349+}
1350+
1351+MirEglApp::MirEglApp(MirConnection* const connection, MirPixelFormat pixel_format) :
1352+ connection{connection},
1353+ dummy_surface{EGL_NO_SURFACE}
1354+{
1355+ unsigned int bpp = 8*MIR_BYTES_PER_PIXEL(pixel_format);
1356+
1357+ EGLint attribs[] =
1358+ {
1359+ EGL_SURFACE_TYPE, EGL_WINDOW_BIT,
1360+ EGL_RENDERABLE_TYPE, EGL_OPENGL_ES2_BIT,
1361+ EGL_COLOR_BUFFER_TYPE, EGL_RGB_BUFFER,
1362+ EGL_BUFFER_SIZE, (EGLint) bpp,
1363+ EGL_NONE
1364+ };
1365+
1366+ egldisplay = eglGetDisplay((EGLNativeDisplayType) mir_connection_get_egl_native_display(connection));
1367+ if (egldisplay == EGL_NO_DISPLAY)
1368+ throw std::runtime_error("Can't eglGetDisplay");
1369+
1370+ EGLint major;
1371+ EGLint minor;
1372+ if (!eglInitialize(egldisplay, &major, &minor))
1373+ throw std::runtime_error("Can't eglInitialize");
1374+
1375+ if (major != 1 || minor != 4)
1376+ throw std::runtime_error("EGL version is not 1.4");
1377+
1378+ if (!eglChooseConfig(egldisplay, attribs, &eglconfig, 1, &neglconfigs))
1379+ throw std::runtime_error("Could not eglChooseConfig");
1380+
1381+ if (neglconfigs == 0)
1382+ throw std::runtime_error("No EGL config available");
1383+
1384+ EGLint ctxattribs[] =
1385+ {
1386+ EGL_CONTEXT_CLIENT_VERSION, 2,
1387+ EGL_NONE
1388+ };
1389+
1390+ eglctx = eglCreateContext(egldisplay, eglconfig, EGL_NO_CONTEXT, ctxattribs);
1391+ if (eglctx == EGL_NO_CONTEXT)
1392+ throw std::runtime_error("eglCreateContext failed");
1393+
1394+ if (!supports_surfaceless_context())
1395+ {
1396+ static EGLint const dummy_pbuffer_attribs[] =
1397+ {
1398+ EGL_WIDTH, 1,
1399+ EGL_HEIGHT, 1,
1400+ EGL_NONE
1401+ };
1402+
1403+ dummy_surface = eglCreatePbufferSurface(egldisplay, eglconfig, dummy_pbuffer_attribs);
1404+ if (dummy_surface == EGL_NO_SURFACE)
1405+ throw std::runtime_error("eglCreatePbufferSurface failed");
1406+ }
1407+
1408+ make_current(dummy_surface);
1409+}
1410+
1411+EGLSurface MirEglApp::create_surface(MirSurface* surface)
1412+{
1413+ auto const eglsurface = eglCreateWindowSurface(
1414+ egldisplay,
1415+ eglconfig,
1416+ (EGLNativeWindowType) mir_buffer_stream_get_egl_native_window(mir_surface_get_buffer_stream(surface)), NULL);
1417+
1418+ if (eglsurface == EGL_NO_SURFACE)
1419+ throw std::runtime_error("eglCreateWindowSurface failed");
1420+
1421+ return eglsurface;
1422+}
1423+
1424+void MirEglApp::make_current(EGLSurface eglsurface) const
1425+{
1426+ if (!eglMakeCurrent(egldisplay, eglsurface, eglsurface, eglctx))
1427+ throw std::runtime_error("Can't eglMakeCurrent");
1428+}
1429+
1430+void MirEglApp::swap_buffers(EGLSurface eglsurface) const
1431+{
1432+ eglSwapBuffers(egldisplay, eglsurface);
1433+}
1434+
1435+void MirEglApp::destroy_surface(EGLSurface eglsurface) const
1436+{
1437+ eglDestroySurface(egldisplay, eglsurface);
1438+}
1439+
1440+void MirEglApp::get_surface_size(EGLSurface eglsurface, int* width, int* height) const
1441+{
1442+ eglQuerySurface(egldisplay, eglsurface, EGL_WIDTH, width);
1443+ eglQuerySurface(egldisplay, eglsurface, EGL_HEIGHT, height);
1444+}
1445+
1446+void MirEglApp::set_swap_interval(EGLSurface eglsurface, int interval) const
1447+{
1448+ auto const previous_surface = eglGetCurrentSurface(EGL_DRAW);
1449+
1450+ make_current(eglsurface);
1451+ eglSwapInterval(egldisplay, interval);
1452+
1453+ if (previous_surface != EGL_NO_SURFACE)
1454+ make_current(previous_surface);
1455+}
1456+
1457+bool MirEglApp::supports_surfaceless_context()
1458+{
1459+ auto const extensions = eglQueryString(egldisplay, EGL_EXTENSIONS);
1460+ if (!extensions)
1461+ return false;
1462+ return std::strstr(extensions, "EGL_KHR_surfaceless_context") != nullptr;
1463+}
1464+
1465+MirEglApp::~MirEglApp()
1466+{
1467+ eglMakeCurrent(egldisplay, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT);
1468+ if (dummy_surface != EGL_NO_SURFACE)
1469+ destroy_surface(dummy_surface);
1470+ eglDestroyContext(egldisplay, eglctx);
1471+ eglTerminate(egldisplay);
1472+ mir_connection_release(connection);
1473+}
1474
1475=== added file 'spinner/miregl.h'
1476--- spinner/miregl.h 1970-01-01 00:00:00 +0000
1477+++ spinner/miregl.h 2015-07-20 10:34:37 +0000
1478@@ -0,0 +1,67 @@
1479+/*
1480+ * Copyright © 2015 Canonical Ltd.
1481+ *
1482+ * This program is free software: you can redistribute it and/or modify
1483+ * it under the terms of the GNU General Public License version 3 as
1484+ * published by the Free Software Foundation.
1485+ *
1486+ * This program is distributed in the hope that it will be useful,
1487+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
1488+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
1489+ * GNU General Public License for more details.
1490+ *
1491+ * You should have received a copy of the GNU General Public License
1492+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
1493+ */
1494+
1495+#ifndef UNITYSYSTEMCOMPOSITOR_MIREGL_H
1496+#define UNITYSYSTEMCOMPOSITOR_MIREGL_H
1497+
1498+#include <mir_toolkit/common.h>
1499+#include <mir_toolkit/client_types.h>
1500+#include "mir_toolkit/mir_client_library.h"
1501+
1502+#include <EGL/egl.h>
1503+
1504+#include <memory>
1505+
1506+class MirEglApp;
1507+class MirEglSurface;
1508+
1509+std::shared_ptr<MirEglApp> make_mir_eglapp(
1510+ MirConnection* const connection,
1511+ MirPixelFormat const& pixel_format);
1512+
1513+class MirEglSurface
1514+{
1515+public:
1516+ MirEglSurface(
1517+ std::shared_ptr<MirEglApp> const& mir_egl_app,
1518+ MirSurfaceParameters const& surfaceparm,
1519+ int swapinterval);
1520+
1521+ ~MirEglSurface();
1522+
1523+ template<typename Painter>
1524+ void paint(Painter const& functor)
1525+ {
1526+ egl_make_current();
1527+ functor(width(), height());
1528+ swap_buffers();
1529+ }
1530+
1531+private:
1532+ void egl_make_current();
1533+
1534+ void swap_buffers();
1535+ unsigned int width() const;
1536+ unsigned int height() const;
1537+
1538+ std::shared_ptr<MirEglApp> const mir_egl_app;
1539+ MirSurface* const surface;
1540+ EGLSurface const eglsurface;
1541+ int width_;
1542+ int height_;
1543+};
1544+
1545+#endif //UNITYSYSTEMCOMPOSITOR_MIREGL_H
1546
1547=== added file 'spinner/orange-dot.png'
1548Binary files spinner/orange-dot.png 1970-01-01 00:00:00 +0000 and spinner/orange-dot.png 2015-07-20 10:34:37 +0000 differ
1549=== removed file 'spinner/spinner-glow.png'
1550Binary files spinner/spinner-glow.png 2014-04-02 18:27:53 +0000 and spinner/spinner-glow.png 1970-01-01 00:00:00 +0000 differ
1551=== removed file 'spinner/spinner-logo.png'
1552Binary files spinner/spinner-logo.png 2014-04-02 18:27:53 +0000 and spinner/spinner-logo.png 1970-01-01 00:00:00 +0000 differ
1553=== added file 'spinner/wallpaper.png'
1554Binary files spinner/wallpaper.png 1970-01-01 00:00:00 +0000 and spinner/wallpaper.png 2015-07-20 10:34:37 +0000 differ
1555=== added file 'spinner/white-dot.png'
1556Binary files spinner/white-dot.png 1970-01-01 00:00:00 +0000 and spinner/white-dot.png 2015-07-20 10:34:37 +0000 differ
1557=== modified file 'src/external_spinner.cpp'
1558--- src/external_spinner.cpp 2015-04-01 17:06:16 +0000
1559+++ src/external_spinner.cpp 2015-07-20 10:34:37 +0000
1560@@ -20,6 +20,18 @@
1561
1562 #include <unistd.h>
1563 #include <signal.h>
1564+#include <sys/wait.h>
1565+
1566+namespace
1567+{
1568+
1569+void wait_for_child(int)
1570+{
1571+ while (waitpid(-1, nullptr, WNOHANG) > 0)
1572+ continue;
1573+}
1574+
1575+}
1576
1577 usc::ExternalSpinner::ExternalSpinner(
1578 std::string const& executable,
1579@@ -28,6 +40,11 @@
1580 mir_socket{mir_socket},
1581 spinner_pid{0}
1582 {
1583+ struct sigaction sa;
1584+ sigfillset(&sa.sa_mask);
1585+ sa.sa_handler = wait_for_child;
1586+ sa.sa_flags = 0;
1587+ sigaction(SIGCHLD, &sa, nullptr);
1588 }
1589
1590 usc::ExternalSpinner::~ExternalSpinner()
1591
1592=== modified file 'src/mir_screen.cpp'
1593--- src/mir_screen.cpp 2015-03-16 10:24:45 +0000
1594+++ src/mir_screen.cpp 2015-07-20 10:34:37 +0000
1595@@ -79,13 +79,10 @@
1596 enable_inactivity_timers_l(enable);
1597 }
1598
1599-void usc::MirScreen::toggle_screen_power_mode(PowerStateChangeReason reason)
1600+MirPowerMode usc::MirScreen::get_screen_power_mode()
1601 {
1602 std::lock_guard<std::mutex> lock{guard};
1603- MirPowerMode new_mode = (current_power_mode == MirPowerMode::mir_power_mode_on) ?
1604- MirPowerMode::mir_power_mode_off : MirPowerMode::mir_power_mode_on;
1605-
1606- set_screen_power_mode_l(new_mode, reason);
1607+ return current_power_mode;
1608 }
1609
1610 void usc::MirScreen::set_screen_power_mode(MirPowerMode mode, PowerStateChangeReason reason)
1611
1612=== modified file 'src/mir_screen.h'
1613--- src/mir_screen.h 2015-03-16 10:24:45 +0000
1614+++ src/mir_screen.h 2015-07-20 10:34:37 +0000
1615@@ -51,9 +51,9 @@
1616 ~MirScreen();
1617
1618 void enable_inactivity_timers(bool enable) override;
1619- void toggle_screen_power_mode(PowerStateChangeReason reason) override;
1620 void keep_display_on_temporarily() override;
1621
1622+ MirPowerMode get_screen_power_mode() override;
1623 void set_screen_power_mode(MirPowerMode mode, PowerStateChangeReason reason) override;
1624 void keep_display_on(bool on) override;
1625 void set_brightness(int brightness) override;
1626
1627=== modified file 'src/screen.h'
1628--- src/screen.h 2015-02-18 14:40:29 +0000
1629+++ src/screen.h 2015-07-20 10:34:37 +0000
1630@@ -33,9 +33,9 @@
1631 virtual ~Screen() = default;
1632
1633 virtual void enable_inactivity_timers(bool enable) = 0;
1634- virtual void toggle_screen_power_mode(PowerStateChangeReason reason) = 0;
1635 virtual void keep_display_on_temporarily() = 0;
1636
1637+ virtual MirPowerMode get_screen_power_mode() = 0;
1638 virtual void set_screen_power_mode(MirPowerMode mode, PowerStateChangeReason reason) = 0;
1639 virtual void keep_display_on(bool on) = 0;
1640 virtual void set_brightness(int brightness) = 0;
1641
1642=== modified file 'src/screen_event_handler.cpp'
1643--- src/screen_event_handler.cpp 2015-04-07 13:36:48 +0000
1644+++ src/screen_event_handler.cpp 2015-07-20 10:34:37 +0000
1645@@ -35,6 +35,7 @@
1646 shutdown_timeout{shutdown_timeout},
1647 shutdown{shutdown},
1648 long_press_detected{false},
1649+ mode_at_press_start{MirPowerMode::mir_power_mode_off},
1650 shutdown_alarm{alarm_factory->create_alarm([this]{ shutdown_alarm_notification(); })},
1651 long_press_alarm{alarm_factory->create_alarm([this]{ long_press_notification(); })}
1652 {
1653@@ -77,6 +78,14 @@
1654 void usc::ScreenEventHandler::power_key_down()
1655 {
1656 std::lock_guard<std::mutex> lock{guard};
1657+
1658+ mode_at_press_start = screen->get_screen_power_mode();
1659+ if (mode_at_press_start != MirPowerMode::mir_power_mode_on)
1660+ {
1661+ screen->set_screen_power_mode(
1662+ MirPowerMode::mir_power_mode_on, PowerStateChangeReason::power_key);
1663+ }
1664+
1665 screen->enable_inactivity_timers(false);
1666 long_press_detected = false;
1667 long_press_alarm->reschedule_in(power_key_ignore_timeout);
1668@@ -88,9 +97,18 @@
1669 std::lock_guard<std::mutex> lock{guard};
1670 shutdown_alarm->cancel();
1671 long_press_alarm->cancel();
1672+
1673 if (!long_press_detected)
1674 {
1675- screen->toggle_screen_power_mode(PowerStateChangeReason::power_key);
1676+ if (mode_at_press_start == MirPowerMode::mir_power_mode_on)
1677+ {
1678+ screen->set_screen_power_mode(
1679+ MirPowerMode::mir_power_mode_off, PowerStateChangeReason::power_key);
1680+ }
1681+ else
1682+ {
1683+ screen->enable_inactivity_timers(true);
1684+ }
1685 }
1686 }
1687
1688@@ -103,6 +121,9 @@
1689
1690 void usc::ScreenEventHandler::long_press_notification()
1691 {
1692+ // We know the screen is already on after power_key_down(), but we turn the
1693+ // screen on here to ensure that it is also at full brightness for the
1694+ // presumed system power dialog that will appear.
1695 screen->set_screen_power_mode(
1696 MirPowerMode::mir_power_mode_on, PowerStateChangeReason::power_key);
1697 long_press_detected = true;
1698
1699=== modified file 'src/screen_event_handler.h'
1700--- src/screen_event_handler.h 2015-04-07 13:36:48 +0000
1701+++ src/screen_event_handler.h 2015-07-20 10:34:37 +0000
1702@@ -65,6 +65,7 @@
1703 std::function<void()> const shutdown;
1704
1705 std::atomic<bool> long_press_detected;
1706+ std::atomic<MirPowerMode> mode_at_press_start;
1707 std::unique_ptr<mir::time::Alarm> shutdown_alarm;
1708 std::unique_ptr<mir::time::Alarm> long_press_alarm;
1709 };
1710
1711=== modified file 'src/server.cpp'
1712--- src/server.cpp 2015-04-07 13:36:48 +0000
1713+++ src/server.cpp 2015-07-20 10:34:37 +0000
1714@@ -30,6 +30,7 @@
1715 #include <mir/server_status_listener.h>
1716 #include <mir/shell/focus_controller.h>
1717 #include <mir/scene/session.h>
1718+#include <mir/abnormal_exit.h>
1719 #include <mir/main_loop.h>
1720
1721 #include <iostream>
1722@@ -92,12 +93,20 @@
1723
1724 std::shared_ptr<msh::FocusController> const focus_controller;
1725 };
1726+const char* const dm_from_fd = "from-dm-fd";
1727+const char* const dm_to_fd = "to-dm-fd";
1728+const char* const dm_stub = "debug-without-dm";
1729+const char* const dm_stub_active = "debug-active-session-name";
1730 }
1731
1732 usc::Server::Server(int argc, char** argv)
1733 {
1734- add_configuration_option("from-dm-fd", "File descriptor of read end of pipe from display manager [int]", mir::OptionType::integer);
1735- add_configuration_option("to-dm-fd", "File descriptor of write end of pipe to display manager [int]", mir::OptionType::integer);
1736+ add_configuration_option(dm_from_fd, "File descriptor of read end of pipe from display manager [int]",
1737+ mir::OptionType::integer);
1738+ add_configuration_option(dm_to_fd, "File descriptor of write end of pipe to display manager [int]",
1739+ mir::OptionType::integer);
1740+ add_configuration_option(dm_stub, "Run without a display manager (only useful when debugging)", mir::OptionType::null);
1741+ add_configuration_option(dm_stub_active, "Expected connection when run without a display manager (only useful when debugging)", "nested-mir@:/run/user/1000/mir_socket");
1742 add_configuration_option("blacklist", "Video blacklist regex to use", mir::OptionType::string);
1743 add_configuration_option("version", "Show version of Unity System Compositor", mir::OptionType::null);
1744 add_configuration_option("spinner", "Path to spinner executable", mir::OptionType::string);
1745@@ -171,15 +180,49 @@
1746 return the_session_switcher();
1747 }
1748
1749+namespace
1750+{
1751+struct NullDMMessageHandler : usc::DMConnection
1752+{
1753+ explicit NullDMMessageHandler(
1754+ std::shared_ptr<usc::DMMessageHandler> const& dm_message_handler,
1755+ std::string const& client_name) :
1756+ dm_message_handler{dm_message_handler},
1757+ client_name{client_name}
1758+ {}
1759+
1760+ ~NullDMMessageHandler() = default;
1761+
1762+ void start() override
1763+ {
1764+ dm_message_handler->set_active_session(client_name);
1765+ };
1766+
1767+ std::shared_ptr<usc::DMMessageHandler> const dm_message_handler;
1768+ std::string const client_name;
1769+};
1770+}
1771+
1772 std::shared_ptr<usc::DMConnection> usc::Server::the_dm_connection()
1773 {
1774 return dm_connection(
1775- [this]
1776+ [this]() -> std::shared_ptr<usc::DMConnection>
1777 {
1778- return std::make_shared<AsioDMConnection>(
1779- the_options()->get("from-dm-fd", -1),
1780- the_options()->get("to-dm-fd", -1),
1781- the_dm_message_handler());
1782+ if (the_options()->is_set(dm_from_fd) && the_options()->is_set(dm_to_fd))
1783+ {
1784+ return std::make_shared<AsioDMConnection>(
1785+ the_options()->get(dm_from_fd, -1),
1786+ the_options()->get(dm_to_fd, -1),
1787+ the_dm_message_handler());
1788+ }
1789+ else if (the_options()->is_set(dm_stub))
1790+ {
1791+ return std::make_shared<NullDMMessageHandler>(
1792+ the_dm_message_handler(),
1793+ the_options()->get<std::string>(dm_stub_active));
1794+ }
1795+
1796+ BOOST_THROW_EXCEPTION(mir::AbnormalExit("to and from FDs are required for display manager"));
1797 });
1798 }
1799
1800
1801=== added file 'src/session_monitor.h'
1802--- src/session_monitor.h 1970-01-01 00:00:00 +0000
1803+++ src/session_monitor.h 2015-07-20 10:34:37 +0000
1804@@ -0,0 +1,63 @@
1805+/*
1806+ * Copyright © 2015 Canonical Ltd.
1807+ *
1808+ * This program is free software: you can redistribute it and/or modify
1809+ * it under the terms of the GNU General Public License version 3 as
1810+ * published by the Free Software Foundation.
1811+ *
1812+ * This program is distributed in the hope that it will be useful,
1813+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
1814+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
1815+ * GNU General Public License for more details.
1816+ *
1817+ * You should have received a copy of the GNU General Public License
1818+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
1819+ *
1820+ * Authored by: Alan Griffiths <alan.griffiths@canonical.com>
1821+ */
1822+
1823+#ifndef USC_SESSION_MONITOR_H
1824+#define USC_SESSION_MONITOR_H
1825+
1826+#include <sys/types.h>
1827+
1828+#include <memory>
1829+#include <string>
1830+
1831+namespace mir { namespace frontend { class Session; }};
1832+
1833+namespace usc
1834+{
1835+class Session
1836+{
1837+public:
1838+ virtual ~Session() = default;
1839+
1840+ virtual std::string name() = 0;
1841+ virtual void show() = 0;
1842+ virtual void hide() = 0;
1843+ virtual void raise_and_focus() = 0;
1844+ virtual bool corresponds_to(mir::frontend::Session const*) = 0;
1845+
1846+protected:
1847+ Session() = default;
1848+ Session(Session const&) = delete;
1849+ Session& operator=(Session const&) = delete;
1850+};
1851+
1852+class SessionMonitor
1853+{
1854+public:
1855+ virtual void add(std::shared_ptr<Session> const& session, pid_t pid) = 0;
1856+ virtual void remove(std::shared_ptr<mir::frontend::Session> const& session) = 0;
1857+ virtual void mark_ready(mir::frontend::Session const* session) = 0;
1858+
1859+protected:
1860+ SessionMonitor() = default;
1861+ SessionMonitor(SessionMonitor const&) = delete;
1862+ SessionMonitor& operator=(SessionMonitor const&) = delete;
1863+ virtual ~SessionMonitor() = default;
1864+};
1865+}
1866+
1867+#endif //USC_SESSION_MONITOR_H
1868
1869=== modified file 'src/session_switcher.h'
1870--- src/session_switcher.h 2014-10-22 19:31:57 +0000
1871+++ src/session_switcher.h 2015-07-20 10:34:37 +0000
1872@@ -20,41 +20,24 @@
1873 #define USC_SESSION_SWITCHER_H_
1874
1875 #include "dm_connection.h"
1876+#include "session_monitor.h"
1877
1878 #include <map>
1879-#include <memory>
1880 #include <mutex>
1881
1882-namespace mir { namespace frontend { class Session; }};
1883 namespace usc
1884 {
1885 class Spinner;
1886
1887-class Session
1888-{
1889-public:
1890- virtual ~Session() = default;
1891-
1892- virtual std::string name() = 0;
1893- virtual void show() = 0;
1894- virtual void hide() = 0;
1895- virtual void raise_and_focus() = 0;
1896- virtual bool corresponds_to(mir::frontend::Session const*) = 0;
1897-
1898-protected:
1899- Session() = default;
1900- Session(Session const&) = delete;
1901- Session& operator=(Session const&) = delete;
1902-};
1903-
1904-class SessionSwitcher : public DMMessageHandler
1905+class SessionSwitcher : public DMMessageHandler, public SessionMonitor
1906 {
1907 public:
1908 explicit SessionSwitcher(std::shared_ptr<Spinner> const& spinner);
1909
1910- void add(std::shared_ptr<Session> const& session, pid_t pid);
1911- void remove(std::shared_ptr<mir::frontend::Session> const& session);
1912- void mark_ready(mir::frontend::Session const* session);
1913+ /* From SessionMonitor */
1914+ void add(std::shared_ptr<Session> const& session, pid_t pid) override;
1915+ void remove(std::shared_ptr<mir::frontend::Session> const& session) override;
1916+ void mark_ready(mir::frontend::Session const* session) override;
1917
1918 /* From DMMessageHandler */
1919 void set_active_session(std::string const& name) override;
1920
1921=== modified file 'src/window_manager.cpp'
1922--- src/window_manager.cpp 2015-04-09 11:27:06 +0000
1923+++ src/window_manager.cpp 2015-07-20 10:34:37 +0000
1924@@ -18,10 +18,10 @@
1925
1926 #include "window_manager.h"
1927
1928-#include "session_switcher.h"
1929+#include "session_monitor.h"
1930
1931 #include "mir/geometry/rectangle.h"
1932-#include "mir/scene/null_surface_observer.h"
1933+#include "mir/shell/surface_ready_observer.h"
1934 #include "mir/scene/session.h"
1935 #include "mir/scene/session_coordinator.h"
1936 #include "mir/scene/surface.h"
1937@@ -82,52 +82,17 @@
1938 std::shared_ptr<ms::Session> const scene_session;
1939 msh::FocusController& focus_controller;
1940 };
1941-
1942-
1943-struct SessionReadyObserver : ms::NullSurfaceObserver,
1944- std::enable_shared_from_this<SessionReadyObserver>
1945-{
1946- SessionReadyObserver(
1947- std::shared_ptr<usc::SessionSwitcher> const& switcher,
1948- std::shared_ptr<ms::Surface> const& surface,
1949- ms::Session const* session)
1950- : switcher{switcher},
1951- surface{surface},
1952- session{session}
1953- {
1954- }
1955-
1956- void frame_posted(int) override
1957- {
1958- ++num_frames_posted;
1959- if (num_frames_posted == num_frames_for_session_ready)
1960- {
1961- switcher->mark_ready(session);
1962- surface->remove_observer(shared_from_this());
1963- }
1964- }
1965-
1966- std::shared_ptr<usc::SessionSwitcher> const switcher;
1967- std::shared_ptr<ms::Surface> const surface;
1968- ms::Session const* const session;
1969- // We need to wait for the second frame before marking the session
1970- // as ready. The first frame posted from sessions is a blank frame.
1971- // TODO: Solve this issue at its root and remove this workaround
1972- int const num_frames_for_session_ready{2};
1973- int num_frames_posted{0};
1974-};
1975-
1976 }
1977
1978 usc::WindowManager::WindowManager(
1979 mir::shell::FocusController* focus_controller,
1980 std::shared_ptr<mir::shell::DisplayLayout> const& display_layout,
1981 std::shared_ptr<ms::SessionCoordinator> const& session_coordinator,
1982- std::shared_ptr<SessionSwitcher> const& session_switcher) :
1983+ std::shared_ptr<SessionMonitor> const& session_monitor) :
1984 focus_controller{focus_controller},
1985 display_layout{display_layout},
1986 session_coordinator{session_coordinator},
1987- session_switcher{session_switcher}
1988+ session_monitor{session_monitor}
1989 {
1990 }
1991
1992@@ -139,7 +104,7 @@
1993
1994 auto const usc_session = std::make_shared<UscSession>(session, *focus_controller);
1995
1996- session_switcher->add(usc_session, session->process_id());
1997+ session_monitor->add(usc_session, session->process_id());
1998 }
1999
2000 void usc::WindowManager::remove_session(std::shared_ptr<ms::Session> const& session)
2001@@ -152,7 +117,7 @@
2002 else
2003 focus_controller->set_focus_to(next_session, {});
2004
2005- session_switcher->remove(session);
2006+ session_monitor->remove(session);
2007 }
2008
2009 auto usc::WindowManager::add_surface(
2010@@ -178,8 +143,13 @@
2011 auto const result = build(session, placed_parameters);
2012 auto const surface = session->surface(result);
2013
2014- auto const session_ready_observer = std::make_shared<SessionReadyObserver>(
2015- session_switcher, surface, session.get());
2016+ auto const session_ready_observer = std::make_shared<msh::SurfaceReadyObserver>(
2017+ [this](std::shared_ptr<ms::Session> const& session, std::shared_ptr<ms::Surface> const& surface)
2018+ {
2019+ session_monitor->mark_ready(session.get());
2020+ },
2021+ session,
2022+ surface);
2023
2024 surface->add_observer(session_ready_observer);
2025
2026
2027=== modified file 'src/window_manager.h'
2028--- src/window_manager.h 2015-04-09 11:27:06 +0000
2029+++ src/window_manager.h 2015-07-20 10:34:37 +0000
2030@@ -20,6 +20,7 @@
2031 #define USC_WINDOW_MANAGER_H_
2032
2033 #include <mir/shell/window_manager.h>
2034+#include "session_monitor.h"
2035
2036 namespace mir
2037 {
2038@@ -29,7 +30,7 @@
2039
2040 namespace usc
2041 {
2042-class SessionSwitcher;
2043+class SessionMonitor;
2044
2045 class WindowManager : public mir::shell::WindowManager
2046 {
2047@@ -38,7 +39,7 @@
2048 mir::shell::FocusController* focus_controller,
2049 std::shared_ptr<mir::shell::DisplayLayout> const& display_layout,
2050 std::shared_ptr<mir::scene::SessionCoordinator> const& session_coordinator,
2051- std::shared_ptr<SessionSwitcher> const& session_switcher);
2052+ std::shared_ptr<SessionMonitor> const& session_switcher);
2053
2054 void add_session(std::shared_ptr<mir::scene::Session> const& session) override;
2055
2056@@ -78,7 +79,7 @@
2057 mir::shell::FocusController* const focus_controller;
2058 std::shared_ptr<mir::shell::DisplayLayout> const display_layout;
2059 std::shared_ptr<mir::scene::SessionCoordinator> const session_coordinator;
2060- std::shared_ptr<SessionSwitcher> const session_switcher;
2061+ std::shared_ptr<SessionMonitor> const session_monitor;
2062 };
2063 }
2064
2065
2066=== modified file 'tests/integration-tests/CMakeLists.txt'
2067--- tests/integration-tests/CMakeLists.txt 2015-04-07 13:36:48 +0000
2068+++ tests/integration-tests/CMakeLists.txt 2015-07-20 10:34:37 +0000
2069@@ -32,6 +32,7 @@
2070 run_command.cpp
2071 dbus_bus.cpp
2072 dbus_client.cpp
2073+ spin_wait.cpp
2074 test_dbus_event_loop.cpp
2075 test_unity_screen_service.cpp
2076 test_external_spinner.cpp
2077
2078=== added file 'tests/integration-tests/spin_wait.cpp'
2079--- tests/integration-tests/spin_wait.cpp 1970-01-01 00:00:00 +0000
2080+++ tests/integration-tests/spin_wait.cpp 2015-07-20 10:34:37 +0000
2081@@ -0,0 +1,39 @@
2082+/*
2083+ * Copyright © 2015 Canonical Ltd.
2084+ *
2085+ * This program is free software: you can redistribute it and/or modify it
2086+ * under the terms of the GNU General Public License version 3,
2087+ * as published by the Free Software Foundation.
2088+ *
2089+ * This program is distributed in the hope that it will be useful,
2090+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
2091+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
2092+ * GNU General Public License for more details.
2093+ *
2094+ * You should have received a copy of the GNU General Public License
2095+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
2096+ *
2097+ * Authored by: Alexandros Frantzis <alexandros.frantzis@canonical.com>
2098+ */
2099+
2100+#include "spin_wait.h"
2101+
2102+#include <thread>
2103+
2104+bool usc::test::spin_wait_for_condition_or_timeout(
2105+ std::function<bool()> const& condition,
2106+ std::chrono::milliseconds timeout,
2107+ std::chrono::milliseconds spin_period)
2108+{
2109+ auto const end = std::chrono::steady_clock::now() + timeout;
2110+ bool condition_fulfilled = false;
2111+
2112+ while (std::chrono::steady_clock::now() < end &&
2113+ !(condition_fulfilled = condition()))
2114+ {
2115+ std::this_thread::sleep_for(spin_period);
2116+ }
2117+
2118+ return condition_fulfilled;
2119+}
2120+
2121
2122=== added file 'tests/integration-tests/spin_wait.h'
2123--- tests/integration-tests/spin_wait.h 1970-01-01 00:00:00 +0000
2124+++ tests/integration-tests/spin_wait.h 2015-07-20 10:34:37 +0000
2125@@ -0,0 +1,39 @@
2126+/*
2127+ * Copyright © 2015 Canonical Ltd.
2128+ *
2129+ * This program is free software: you can redistribute it and/or modify it
2130+ * under the terms of the GNU General Public License version 3,
2131+ * as published by the Free Software Foundation.
2132+ *
2133+ * This program is distributed in the hope that it will be useful,
2134+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
2135+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
2136+ * GNU General Public License for more details.
2137+ *
2138+ * You should have received a copy of the GNU General Public License
2139+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
2140+ *
2141+ * Authored by: Alexandros Frantzis <alexandros.frantzis@canonical.com>
2142+ */
2143+
2144+#ifndef USC_SPIN_WAIT_H_
2145+#define USC_SPIN_WAIT_H_
2146+
2147+#include <functional>
2148+#include <chrono>
2149+
2150+namespace usc
2151+{
2152+namespace test
2153+{
2154+
2155+
2156+bool spin_wait_for_condition_or_timeout(
2157+ std::function<bool()> const& condition,
2158+ std::chrono::milliseconds timeout,
2159+ std::chrono::milliseconds spin_period = std::chrono::milliseconds{10});
2160+
2161+}
2162+}
2163+
2164+#endif
2165
2166=== modified file 'tests/integration-tests/test_dbus_event_loop.cpp'
2167--- tests/integration-tests/test_dbus_event_loop.cpp 2015-03-18 12:48:33 +0000
2168+++ tests/integration-tests/test_dbus_event_loop.cpp 2015-07-20 10:34:37 +0000
2169@@ -201,6 +201,8 @@
2170
2171 static int const timeout_ms = 100;
2172
2173+ auto const start = std::chrono::steady_clock::now();
2174+
2175 dbus_event_loop.enqueue(
2176 [this,&pending_promise]
2177 {
2178@@ -218,7 +220,6 @@
2179 });
2180
2181 // No one is going to reply to the signal, so the notification should time out
2182- auto const start = std::chrono::steady_clock::now();
2183 auto pending = pending_future.get();
2184 auto const end = std::chrono::steady_clock::now();
2185 auto const delay = end - start;
2186
2187=== modified file 'tests/integration-tests/test_external_spinner.cpp'
2188--- tests/integration-tests/test_external_spinner.cpp 2015-04-01 17:11:57 +0000
2189+++ tests/integration-tests/test_external_spinner.cpp 2015-07-20 10:34:37 +0000
2190@@ -18,6 +18,7 @@
2191
2192 #include "src/external_spinner.h"
2193 #include "run_command.h"
2194+#include "spin_wait.h"
2195
2196 #include <fstream>
2197 #include <chrono>
2198@@ -70,16 +71,35 @@
2199 return pids;
2200 }
2201
2202+bool is_zombie(pid_t pid)
2203+{
2204+ std::ifstream stat("/proc/" + std::to_string(pid) + "/stat");
2205+
2206+ std::stringstream ss;
2207+ ss << stat.rdbuf();
2208+
2209+ return ss.str().find(" Z ") != std::string::npos;
2210+}
2211+
2212 struct AnExternalSpinner : testing::Test
2213 {
2214 std::vector<pid_t> spinner_pids()
2215 {
2216- return pidof(spinner_cmd);
2217+ std::vector<pid_t> pids;
2218+
2219+ usc::test::spin_wait_for_condition_or_timeout(
2220+ [&pids, this] { pids = pidof(spinner_cmd); return !pids.empty(); },
2221+ timeout);
2222+
2223+ if (pids.empty())
2224+ BOOST_THROW_EXCEPTION(std::runtime_error("spinner_pids timed out"));
2225+
2226+ return pids;
2227 }
2228
2229 std::vector<std::string> environment_of_spinner()
2230 {
2231- auto const pids = pidof(spinner_cmd);
2232+ auto const pids = spinner_pids();
2233 if (pids.size() > 1)
2234 BOOST_THROW_EXCEPTION(std::runtime_error("Detected multiple spinner processes"));
2235 std::vector<std::string> env;
2236@@ -96,19 +116,14 @@
2237
2238 void wait_for_spinner_to_terminate()
2239 {
2240- auto const timeout = std::chrono::milliseconds{3000};
2241- auto const expire = std::chrono::steady_clock::now() + timeout;
2242-
2243- while (spinner_pids().size() > 0)
2244- {
2245- if (std::chrono::steady_clock::now() > expire)
2246- BOOST_THROW_EXCEPTION(std::runtime_error("wait_for_no_spinner timed out"));
2247- std::this_thread::sleep_for(std::chrono::milliseconds{10});
2248- }
2249+ usc::test::spin_wait_for_condition_or_timeout(
2250+ [this] { return pidof(spinner_cmd).empty(); },
2251+ timeout);
2252 }
2253
2254 std::string const spinner_cmd{executable_path() + "/usc_test_helper_wait_for_signal"};
2255 std::string const mir_socket{"usc_mir_socket"};
2256+ std::chrono::milliseconds const timeout{3000};
2257 usc::ExternalSpinner spinner{spinner_cmd, mir_socket};
2258 };
2259
2260@@ -165,3 +180,21 @@
2261
2262 EXPECT_THAT(environment_of_spinner(), Contains("MIR_SOCKET=" + mir_socket));
2263 }
2264+
2265+TEST_F(AnExternalSpinner, does_not_leave_zombie_process)
2266+{
2267+ using namespace testing;
2268+
2269+ spinner.ensure_running();
2270+ auto const spinner_pid = spinner_pids()[0];
2271+ spinner.kill();
2272+
2273+ wait_for_spinner_to_terminate();
2274+
2275+ // Wait a bit for zombie to be reaped by parent
2276+ bool const spinner_is_not_zombie = usc::test::spin_wait_for_condition_or_timeout(
2277+ [spinner_pid] { return !is_zombie(spinner_pid); },
2278+ timeout);
2279+
2280+ EXPECT_TRUE(spinner_is_not_zombie);
2281+}
2282
2283=== modified file 'tests/integration-tests/test_unity_screen_service.cpp'
2284--- tests/integration-tests/test_unity_screen_service.cpp 2015-07-08 11:38:59 +0000
2285+++ tests/integration-tests/test_unity_screen_service.cpp 2015-07-20 10:34:37 +0000
2286@@ -40,9 +40,9 @@
2287 struct MockScreen : usc::Screen
2288 {
2289 MOCK_METHOD1(enable_inactivity_timers, void(bool enable));
2290- MOCK_METHOD1(toggle_screen_power_mode, void(PowerStateChangeReason reason));
2291 MOCK_METHOD0(keep_display_on_temporarily, void());
2292
2293+ MOCK_METHOD0(get_screen_power_mode, MirPowerMode());
2294 MOCK_METHOD2(set_screen_power_mode, void(MirPowerMode mode, PowerStateChangeReason reason));
2295 MOCK_METHOD1(keep_display_on, void(bool on));
2296 MOCK_METHOD1(set_brightness, void(int brightness));
2297
2298=== modified file 'tests/unit-tests/test_mir_screen.cpp'
2299--- tests/unit-tests/test_mir_screen.cpp 2015-03-16 10:24:45 +0000
2300+++ tests/unit-tests/test_mir_screen.cpp 2015-07-20 10:34:37 +0000
2301@@ -298,18 +298,20 @@
2302 timer->advance_by(power_off_timeout);
2303 }
2304
2305-TEST_F(AMirScreen, toggle_screen_power_mode_from_on_to_off)
2306+TEST_F(AMirScreen, set_screen_power_mode_from_on_to_off)
2307 {
2308 expect_screen_is_turned_off();
2309- mir_screen.toggle_screen_power_mode(PowerStateChangeReason::power_key);
2310+ mir_screen.set_screen_power_mode(MirPowerMode::mir_power_mode_off,
2311+ PowerStateChangeReason::power_key);
2312 }
2313
2314-TEST_F(AMirScreen, toggle_screen_power_mode_from_off_to_on)
2315+TEST_F(AMirScreen, set_screen_power_mode_from_off_to_on)
2316 {
2317 turn_screen_off();
2318
2319 expect_screen_is_turned_on();
2320- mir_screen.toggle_screen_power_mode(PowerStateChangeReason::power_key);
2321+ mir_screen.set_screen_power_mode(MirPowerMode::mir_power_mode_on,
2322+ PowerStateChangeReason::power_key);
2323 }
2324
2325 TEST_F(AMirScreen, sets_hardware_brightness)
2326@@ -359,7 +361,8 @@
2327 mir_screen.register_power_state_change_handler(handler);
2328
2329 auto const toggle_reason = PowerStateChangeReason::power_key;
2330- mir_screen.toggle_screen_power_mode(PowerStateChangeReason::power_key);
2331+ mir_screen.set_screen_power_mode(MirPowerMode::mir_power_mode_off,
2332+ PowerStateChangeReason::power_key);
2333
2334 EXPECT_THAT(handler_reason, Eq(toggle_reason));
2335 EXPECT_THAT(handler_mode, Eq(MirPowerMode::mir_power_mode_off));
2336
2337=== modified file 'tests/unit-tests/test_screen_event_handler.cpp'
2338--- tests/unit-tests/test_screen_event_handler.cpp 2015-04-07 13:36:48 +0000
2339+++ tests/unit-tests/test_screen_event_handler.cpp 2015-07-20 10:34:37 +0000
2340@@ -36,9 +36,9 @@
2341 struct MockScreen : usc::Screen
2342 {
2343 MOCK_METHOD1(enable_inactivity_timers, void(bool enable));
2344- MOCK_METHOD1(toggle_screen_power_mode, void(PowerStateChangeReason reason));
2345 MOCK_METHOD0(keep_display_on_temporarily, void());
2346
2347+ MirPowerMode get_screen_power_mode() override {return mock_mode;}
2348 MOCK_METHOD2(set_screen_power_mode, void(MirPowerMode mode, PowerStateChangeReason reason));
2349 MOCK_METHOD1(keep_display_on, void(bool on));
2350 MOCK_METHOD1(set_brightness, void(int brightness));
2351@@ -48,6 +48,8 @@
2352 MOCK_METHOD1(set_touch_visualization_enabled, void(bool enabled));
2353 MOCK_METHOD1(register_power_state_change_handler, void(
2354 usc::PowerStateChangeHandler const& handler));
2355+
2356+ MirPowerMode mock_mode = MirPowerMode::mir_power_mode_off;
2357 };
2358
2359 struct AScreenEventHandler : testing::Test
2360@@ -74,21 +76,29 @@
2361
2362 static const int32_t POWER_KEY_CODE = 26;
2363 mir::EventUPtr power_key_down_event = mir::events::make_event(
2364- MirInputDeviceId{1}, 0, mir_keyboard_action_down,
2365+ MirInputDeviceId{1}, std::chrono::nanoseconds(0),
2366+ mir_keyboard_action_down,
2367 POWER_KEY_CODE, 0, mir_input_event_modifier_none);
2368+
2369 mir::EventUPtr power_key_up_event = mir::events::make_event(
2370- MirInputDeviceId{1}, 0, mir_keyboard_action_up,
2371+ MirInputDeviceId{1}, std::chrono::nanoseconds(0),
2372+ mir_keyboard_action_up,
2373 POWER_KEY_CODE, 0, mir_input_event_modifier_none);
2374+
2375 mir::EventUPtr touch_event = mir::events::make_event(
2376- MirInputDeviceId{1}, 0, mir_input_event_modifier_none);
2377+ MirInputDeviceId{1}, std::chrono::nanoseconds(0),
2378+ mir_input_event_modifier_none);
2379+
2380 mir::EventUPtr pointer_event = mir::events::make_event(
2381- MirInputDeviceId{1}, 0, mir_input_event_modifier_none,
2382+ MirInputDeviceId{1}, std::chrono::nanoseconds(0),
2383+ mir_input_event_modifier_none,
2384 mir_pointer_action_motion,
2385 {}, 0.0f, 0.0f, 0.0f, 0.0f);
2386
2387 AdvanceableTimer timer;
2388 std::chrono::milliseconds const power_key_ignore_timeout{5000};
2389 std::chrono::milliseconds const shutdown_timeout{10000};
2390+ std::chrono::milliseconds const normal_press_duration{100};
2391 testing::NiceMock<MockScreen> mock_screen;
2392 std::atomic<bool> shutdown_called{false};
2393 usc::ScreenEventHandler screen_event_handler{
2394@@ -101,7 +111,7 @@
2395
2396 }
2397
2398-TEST_F(AScreenEventHandler, turns_screen_on_on_long_press)
2399+TEST_F(AScreenEventHandler, turns_screen_on_immediately_on_press)
2400 {
2401 auto const long_press_duration = power_key_ignore_timeout;
2402
2403@@ -110,7 +120,6 @@
2404 PowerStateChangeReason::power_key));
2405
2406 press_power_key();
2407- timer.advance_by(long_press_duration);
2408 }
2409
2410 TEST_F(AScreenEventHandler, shuts_down_system_when_power_key_pressed_for_long_enough)
2411@@ -158,28 +167,32 @@
2412 move_pointer();
2413 }
2414
2415-TEST_F(AScreenEventHandler, toggles_screen_mode_on_normal_press_release)
2416+TEST_F(AScreenEventHandler, sets_screen_mode_off_normal_press_release)
2417 {
2418- std::chrono::milliseconds const normal_press_duration{100};
2419-
2420 EXPECT_CALL(mock_screen,
2421- toggle_screen_power_mode(PowerStateChangeReason::power_key));
2422+ set_screen_power_mode(MirPowerMode::mir_power_mode_off,
2423+ PowerStateChangeReason::power_key));
2424
2425+ mock_screen.mock_mode = MirPowerMode::mir_power_mode_on;
2426 press_power_key();
2427 timer.advance_by(normal_press_duration);
2428 release_power_key();
2429 }
2430
2431-TEST_F(AScreenEventHandler, does_not_toggle_screen_mode_on_long_press_release)
2432+TEST_F(AScreenEventHandler, does_not_set_screen_mode_off_long_press_release)
2433 {
2434 using namespace testing;
2435
2436 auto const long_press_duration = power_key_ignore_timeout;
2437
2438 EXPECT_CALL(mock_screen,
2439- toggle_screen_power_mode(_))
2440+ set_screen_power_mode(MirPowerMode::mir_power_mode_on,
2441+ PowerStateChangeReason::power_key));
2442+ EXPECT_CALL(mock_screen,
2443+ set_screen_power_mode(MirPowerMode::mir_power_mode_off, _))
2444 .Times(0);
2445
2446+ mock_screen.mock_mode = MirPowerMode::mir_power_mode_on;
2447 press_power_key();
2448 timer.advance_by(long_press_duration);
2449 release_power_key();
2450@@ -194,3 +207,19 @@
2451 EXPECT_FALSE(screen_event_handler.handle(*touch_event));
2452 EXPECT_FALSE(screen_event_handler.handle(*pointer_event));
2453 }
2454+
2455+TEST_F(AScreenEventHandler, disables_inactivity_timers_on_power_key_down)
2456+{
2457+ EXPECT_CALL(mock_screen, enable_inactivity_timers(false));
2458+
2459+ press_power_key();
2460+}
2461+
2462+TEST_F(AScreenEventHandler, enables_inactivity_timers_on_power_key_up_when_turning_screen_on)
2463+{
2464+ press_power_key();
2465+ timer.advance_by(normal_press_duration);
2466+
2467+ EXPECT_CALL(mock_screen, enable_inactivity_timers(true));
2468+ release_power_key();
2469+}
2470
2471=== added directory 'tools'
2472=== added file 'tools/png2header.py'
2473--- tools/png2header.py 1970-01-01 00:00:00 +0000
2474+++ tools/png2header.py 2015-07-20 10:34:37 +0000
2475@@ -0,0 +1,86 @@
2476+#!/usr/bin/env python
2477+# coding: utf-8
2478+
2479+# Copyright © 2015 Canonical Ltd.
2480+#
2481+# This program is free software: you can redistribute it and/or modify
2482+# it under the terms of the GNU General Public License version 3 as
2483+# published by the Free Software Foundation.
2484+#
2485+# This program is distributed in the hope that it will be useful,
2486+# but WITHOUT ANY WARRANTY; without even the implied warranty of
2487+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
2488+# GNU General Public License for more details.
2489+#
2490+# You should have received a copy of the GNU General Public License
2491+# along with this program. If not, see <http://www.gnu.org/licenses/>.
2492+#
2493+# Authored by: Alexandros Frantzis <alexandros.frantzis@canonical.com>
2494+
2495+import sys
2496+from PIL import Image
2497+
2498+def premultiply(image):
2499+ pixels = image.load()
2500+ for i in range(image.size[0]):
2501+ for j in range(image.size[1]):
2502+ orig = pixels[i,j]
2503+ m = orig[3] / 255.0
2504+ pixels[i,j] = (int(orig[0] * m) , int(orig[1] * m), int(orig[2] * m), orig[3])
2505+
2506+def tocstring(data):
2507+ result = ''
2508+ line_chars = 0
2509+ line_limit = 80
2510+
2511+ for c in data:
2512+ if line_chars == 0:
2513+ result += ' "'
2514+
2515+ s = '\\%o' % ord(c)
2516+ result += s
2517+ line_chars += len(s)
2518+
2519+ if line_chars >= line_limit:
2520+ result += '"\n'
2521+ line_chars = 0
2522+
2523+ if line_chars != 0:
2524+ result += '"'
2525+
2526+ return result
2527+
2528+def bytes_per_pixel(image):
2529+ if image.mode == 'RGBA':
2530+ return 4
2531+ elif image.mode == 'RGB':
2532+ return 3
2533+ else:
2534+ raise "Unsupported image mode %s" % image.mode
2535+
2536+def export(image, variable_name):
2537+ image_info = (image.size[0], image.size[1], bytes_per_pixel(image))
2538+ print "static const struct {"
2539+ print " unsigned int width;"
2540+ print " unsigned int height;"
2541+ print " unsigned int bytes_per_pixel; /* 3:RGB, 4:RGBA */"
2542+ print " unsigned char pixel_data[%d * %d * %d + 1];" % image_info
2543+ print "} %s = {" % variable_name
2544+ print " %d, %d, %d," % image_info
2545+ print tocstring(image.tostring())
2546+ print "};"
2547+
2548+def show_usage():
2549+ print >>sys.stderr, "Usage: ./png2header.py PNGFILE VARNAME > HEADER_FILE"
2550+ print >>sys.stderr, "Convert a PNG image to an embeddable C/C++ header file"
2551+
2552+if len(sys.argv) < 3:
2553+ show_usage()
2554+ sys.exit(1)
2555+
2556+image_filename = sys.argv[1]
2557+variable_name = sys.argv[2]
2558+
2559+image = Image.open(image_filename)
2560+premultiply(image)
2561+export(image, variable_name)

Subscribers

People subscribed via source and target branches