Merge lp:~unity-team/unity-system-compositor/new-gl-screen into lp:unity-system-compositor

Proposed by Michael Terry
Status: Merged
Approved by: Michael Terry
Approved revision: 150
Merged at revision: 134
Proposed branch: lp:~unity-team/unity-system-compositor/new-gl-screen
Merge into: lp:unity-system-compositor
Diff against target: 1235 lines (+991/-30)
10 files modified
CMakeLists.txt (+10/-12)
debian/control (+5/-1)
debian/unity-system-compositor.install (+2/-0)
spinner/CMakeLists.txt (+48/-0)
spinner/eglapp.c (+372/-0)
spinner/eglapp.h (+44/-0)
spinner/eglspinner.c (+415/-0)
src/CMakeLists.txt (+7/-0)
src/system_compositor.cpp (+84/-17)
src/system_compositor.h (+4/-0)
To merge this branch: bzr merge lp:~unity-team/unity-system-compositor/new-gl-screen
Reviewer Review Type Date Requested Status
Michał Sawicz Abstain
PS Jenkins bot (community) continuous-integration Approve
Oliver Grawert (community) Approve
Review via email: mp+210466@code.launchpad.net

Commit message

This branch adds a new option "--spinner=/path" which allows for an interstitial 'busy wait' program to be specified during boot and between greeter and user sessions.

Description of the change

This branch adds a new option "--spinner=/path" which allows for an interstitial 'busy wait' program to be specified during boot and between greeter and user sessions.

- I've added a default spinner (not used yet, it's still be workshopped with Design, but is functional in current form) in the spinner/ subdirectory
- This meant adding gettext support and a po/ subdirectory (with sample fr.po translation)
- I cleaned up CMake rules a bit to better handle building two different executables.
- And finally added logic to the main USC executable to support the option, launch the spinner, and use it appropriately when a requested session is not available.

Once this lands, in future, we can fix up the spinner to be what design wants. And then add a --spinner option to ubuntu-touch-session to point to the default one. (And eventually, when we want to use the spinner on the desktop too, we can just have USC use the default spinner as a fallback when --spinner isn't specified.)

To post a comment you must log in.
Revision history for this message
PS Jenkins bot (ps-jenkins) wrote :
review: Approve (continuous-integration)
Revision history for this message
Oliver Grawert (ogra) wrote :

Is the design work coordinated with the spinner we have in the recovery img now (which is shown during upgrades) ?

Revision history for this message
Oliver Grawert (ogra) wrote :

hmm, seems the --spinner option is non-functional ... booting while connected via USB and excusting the following on a PC gets me a spinner though ...

adb wait-for-device && adb shell unity-system-compositor-spinner

so it seems there is something missing in usc to actually execute the spinner when the option is set

Revision history for this message
Michael Terry (mterry) wrote :

I talked with Oliver on IRC about this. He was passing --spinner instead of --spinner=/path. Once fixed, the branch worked for him. I corrected the description to make that explicit.

Revision history for this message
Michael Terry (mterry) wrote :

And as far as design goes, no. It's not coordinated with the recovery img animation yet. Design is getting back to me on what this loading screen should look like.

Revision history for this message
Oliver Grawert (ogra) wrote :

Ok with the proper option it works on all devices for me (tested on flo, mako and manta)

review: Approve
Revision history for this message
Michael Terry (mterry) wrote :

Heads up that I changed this branch slightly in response to some design feedback:
- Black background instead of aubergine
- No "Loading..." text

This simplifies the merge a bunch (no gettext support needed now). I figured it was OK to glom this onto an Accepted branch since it doesn't touch the interesting parts of this merge, just the visual design.

Revision history for this message
Michał Sawicz (saviq) wrote :

Hey folks.

Looks like we need to make the libandroid-properties-dev dependency arch-specific (only for i386, amd64, armhf) and fall back to the default when it's unavailable. Otherwise we're limited to those three arches while currently u-s-c builds on arm64 as well.

review: Needs Fixing
Revision history for this message
PS Jenkins bot (ps-jenkins) wrote :
review: Approve (continuous-integration)
Revision history for this message
Michał Sawicz (saviq) wrote :

Confirmed with rsalveti, this (optional dependency on hybris) is the only way forward for now.

150. By Michael Terry

Make libandroid-properties optional and only build-dep on it on supported architectures

Revision history for this message
Michael Terry (mterry) wrote :

Saviq, I've updated this branch to not require libandroid-properties (but to make use of it on the supported architectures).

Revision history for this message
PS Jenkins bot (ps-jenkins) wrote :
review: Approve (continuous-integration)
Revision history for this message
Michał Sawicz (saviq) :
review: Abstain

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 2013-11-20 10:46:10 +0000
3+++ CMakeLists.txt 2014-05-06 20:06:39 +0000
4@@ -14,7 +14,8 @@
5 #
6 # Authored by: Robert Ancell <robert.ancell@canonical.com>
7
8-project(UnitySystemCompisitor)
9+project(UnitySystemCompositor)
10+set(PACKAGE "unity-system-compositor")
11 set(USC_VERSION_MAJOR 0)
12 set(USC_VERSION_MINOR 0)
13 set(USC_VERSION_PATCH 2)
14@@ -34,23 +35,20 @@
15 set(CMAKE_AUTOMOC ON)
16
17 # Use C++11 and warnings
18+set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -std=c11 -Wall")
19 set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++11 -Wall")
20
21-# Check for Mir server library
22 find_package(PkgConfig)
23+pkg_check_modules(ANDROIDPROPS libandroid-properties)
24+pkg_check_modules(CAIRO REQUIRED cairo)
25+pkg_check_modules(GLIB REQUIRED glib-2.0)
26+pkg_check_modules(MIRCLIENT REQUIRED mirclient)
27 pkg_check_modules(MIRSERVER REQUIRED mirserver)
28-include_directories(${MIRSERVER_INCLUDE_DIRS})
29-link_directories(${MIRSERVER_LIBRARY_DIRS})
30
31+find_package(Boost 1.48.0 COMPONENTS chrono date_time filesystem system thread program_options regex REQUIRED)
32+find_package(GLESv2 REQUIRED)
33 find_package(Qt5Core)
34 find_package(Qt5DBus)
35
36-# Check for boost
37-find_package(Boost 1.48.0 COMPONENTS chrono date_time filesystem system thread program_options regex REQUIRED)
38-include_directories(${Boost_INCLUDE_DIRS})
39-
40-# Check for GL
41-find_package(GLESv2 REQUIRED)
42-include_directories (${GLESv2_INCLUDE_DIRS})
43-
44+add_subdirectory(spinner/)
45 add_subdirectory(src/)
46
47=== modified file 'debian/control'
48--- debian/control 2014-04-15 20:47:54 +0000
49+++ debian/control 2014-05-06 20:06:39 +0000
50@@ -5,6 +5,7 @@
51 Build-Depends: cmake,
52 cmake-data,
53 debhelper (>= 9),
54+ libandroid-properties-dev [i386 amd64 armhf],
55 libboost-chrono-dev,
56 libboost-date-time-dev,
57 libboost-filesystem-dev,
58@@ -12,11 +13,14 @@
59 libboost-regex-dev,
60 libboost-system-dev,
61 libboost-thread-dev,
62+ libcairo2-dev,
63+ libglib2.0-dev,
64 libgles2-mesa-dev,
65+ libmirclient-dev (>= 0.1.9),
66 libmirserver-dev (>= 0.1.9),
67 libprotobuf-dev,
68 pkg-config,
69- python (>= 2.7),
70+ python:any (>= 2.7),
71 python-setuptools,
72 qt5-default,
73 qtbase5-dev,
74
75=== modified file 'debian/unity-system-compositor.install'
76--- debian/unity-system-compositor.install 2013-11-19 21:57:55 +0000
77+++ debian/unity-system-compositor.install 2014-05-06 20:06:39 +0000
78@@ -1,5 +1,7 @@
79 debian/source_unity-system-compositor.py usr/share/apport/package-hooks
80 debian/unity-system-compositor.sleep usr/sbin/
81 etc/dbus-1/system.d/com.canonical.Unity.Screen.conf
82+usr/bin/unity-system-compositor-spinner
83 usr/sbin/unity-system-compositor
84 usr/share/dbus-1/interfaces/com.canonical.Unity.Screen.xml
85+usr/share/unity-system-compositor
86
87=== added directory 'spinner'
88=== added file 'spinner/CMakeLists.txt'
89--- spinner/CMakeLists.txt 1970-01-01 00:00:00 +0000
90+++ spinner/CMakeLists.txt 2014-05-06 20:06:39 +0000
91@@ -0,0 +1,48 @@
92+# -*- Mode: CMake; indent-tabs-mode: nil; tab-width: 2 -*-
93+#
94+# Copyright © 2014 Canonical Ltd.
95+#
96+# This program is free software: you can redistribute it and/or modify
97+# it under the terms of the GNU General Public License version 3 as
98+# published by the Free Software Foundation.
99+#
100+# This program is distributed in the hope that it will be useful,
101+# but WITHOUT ANY WARRANTY; without even the implied warranty of
102+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
103+# GNU General Public License for more details.
104+#
105+# You should have received a copy of the GNU General Public License
106+# along with this program. If not, see <http://www.gnu.org/licenses/>.
107+
108+include_directories(
109+ ${CAIRO_INCLUDE_DIRS}
110+ ${GLIB_INCLUDE_DIRS}
111+ ${ANDROIDPROPS_INCLUDE_DIRS}
112+ ${GLESv2_INCLUDE_DIRS}
113+ ${MIRCLIENT_INCLUDE_DIRS}
114+)
115+add_definitions(
116+ -DPACKAGE="${PACKAGE}"
117+ -DLOCALEDIR="${CMAKE_INSTALL_FULL_LOCALEDIR}"
118+ -DPKGDATADIR="${CMAKE_INSTALL_FULL_DATAROOTDIR}/${PACKAGE}"
119+)
120+
121+if(ANDROIDPROPS_FOUND)
122+add_definitions(-DHAVE_PROPS)
123+endif()
124+
125+link_directories(${MIRCLIENT_LIBRARY_DIRS})
126+
127+add_executable(unity-system-compositor-spinner eglapp.c eglapp.h eglspinner.c)
128+target_link_libraries(unity-system-compositor-spinner
129+ EGL
130+ ${CAIRO_LDFLAGS}
131+ ${GLIB_LDFLAGS}
132+ ${ANDROIDPROPS_LDFLAGS}
133+ ${GLESv2_LIBRARIES}
134+ ${MIRCLIENT_LDFLAGS}
135+)
136+install(TARGETS unity-system-compositor-spinner RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR})
137+
138+install(FILES spinner-logo.png DESTINATION ${CMAKE_INSTALL_DATAROOTDIR}/${PACKAGE})
139+install(FILES spinner-glow.png DESTINATION ${CMAKE_INSTALL_DATAROOTDIR}/${PACKAGE})
140
141=== added file 'spinner/eglapp.c'
142--- spinner/eglapp.c 1970-01-01 00:00:00 +0000
143+++ spinner/eglapp.c 2014-05-06 20:06:39 +0000
144@@ -0,0 +1,372 @@
145+/*
146+ * Copyright © 2013 Canonical Ltd.
147+ *
148+ * This program is free software: you can redistribute it and/or modify
149+ * it under the terms of the GNU General Public License version 3 as
150+ * published by the Free Software Foundation.
151+ *
152+ * This program is distributed in the hope that it will be useful,
153+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
154+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
155+ * GNU General Public License for more details.
156+ *
157+ * You should have received a copy of the GNU General Public License
158+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
159+ *
160+ * Author: Daniel van Vugt <daniel.van.vugt@canonical.com>
161+ */
162+
163+#include "eglapp.h"
164+#include "mir_toolkit/mir_client_library.h"
165+#include <stdio.h>
166+#include <stdlib.h>
167+#include <signal.h>
168+#include <time.h>
169+#include <EGL/egl.h>
170+#include <GLES2/gl2.h>
171+
172+float mir_eglapp_background_opacity = 1.0f;
173+
174+static const char appname[] = "eglspinner";
175+
176+static MirConnection *connection;
177+static MirSurface *surface;
178+static EGLDisplay egldisplay;
179+static EGLSurface eglsurface;
180+static volatile sig_atomic_t running = 0;
181+
182+#define CHECK(_cond, _err) \
183+ if (!(_cond)) \
184+ { \
185+ printf("%s\n", (_err)); \
186+ return 0; \
187+ }
188+
189+void mir_eglapp_shutdown(void)
190+{
191+ eglMakeCurrent(egldisplay, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT);
192+ eglTerminate(egldisplay);
193+ mir_surface_release_sync(surface);
194+ surface = NULL;
195+ mir_connection_release(connection);
196+ connection = NULL;
197+}
198+
199+static void shutdown(int signum)
200+{
201+ if (running)
202+ {
203+ running = 0;
204+ printf("Signal %d received. Good night.\n", signum);
205+ }
206+}
207+
208+mir_eglapp_bool mir_eglapp_running(void)
209+{
210+ return running;
211+}
212+
213+void mir_eglapp_swap_buffers(void)
214+{
215+ EGLint width, height;
216+
217+ if (!running)
218+ return;
219+
220+ eglSwapBuffers(egldisplay, eglsurface);
221+
222+ /*
223+ * Querying the surface (actually the current buffer) dimensions here is
224+ * the only truly safe way to be sure that the dimensions we think we
225+ * have are those of the buffer being rendered to. But this should be
226+ * improved in future; https://bugs.launchpad.net/mir/+bug/1194384
227+ */
228+ if (eglQuerySurface(egldisplay, eglsurface, EGL_WIDTH, &width) &&
229+ eglQuerySurface(egldisplay, eglsurface, EGL_HEIGHT, &height))
230+ {
231+ glViewport(0, 0, width, height);
232+ }
233+}
234+
235+static void mir_eglapp_handle_event(MirSurface* surface, MirEvent const* ev, void* context)
236+{
237+ (void) surface;
238+ (void) context;
239+ if (ev->type == mir_event_type_resize)
240+ {
241+ /*
242+ * FIXME: https://bugs.launchpad.net/mir/+bug/1194384
243+ * It is unsafe to set the width and height here because we're in a
244+ * different thread to that doing the rendering. So we either need
245+ * support for event queuing (directing them to another thread) or
246+ * full single-threaded callbacks. (LP: #1194384).
247+ */
248+ printf("Resized to %dx%d\n", ev->resize.width, ev->resize.height);
249+ }
250+}
251+
252+static const MirDisplayOutput *find_active_output(
253+ const MirDisplayConfiguration *conf)
254+{
255+ const MirDisplayOutput *output = NULL;
256+ int d;
257+
258+ for (d = 0; d < (int)conf->num_outputs; d++)
259+ {
260+ const MirDisplayOutput *out = conf->outputs + d;
261+
262+ if (out->used &&
263+ out->connected &&
264+ out->num_modes &&
265+ out->current_mode < out->num_modes)
266+ {
267+ output = out;
268+ break;
269+ }
270+ }
271+
272+ return output;
273+}
274+
275+mir_eglapp_bool mir_eglapp_init(int argc, char *argv[],
276+ unsigned int *width, unsigned int *height)
277+{
278+ EGLint ctxattribs[] =
279+ {
280+ EGL_CONTEXT_CLIENT_VERSION, 2,
281+ EGL_NONE
282+ };
283+ MirSurfaceParameters surfaceparm =
284+ {
285+ "eglappsurface",
286+ 256, 256,
287+ mir_pixel_format_xbgr_8888,
288+ mir_buffer_usage_hardware,
289+ mir_display_output_id_invalid
290+ };
291+ MirEventDelegate delegate =
292+ {
293+ mir_eglapp_handle_event,
294+ NULL
295+ };
296+ EGLConfig eglconfig;
297+ EGLint neglconfigs;
298+ EGLContext eglctx;
299+ EGLBoolean ok;
300+ EGLint swapinterval = 1;
301+ char *mir_socket = NULL;
302+
303+ if (argc > 1)
304+ {
305+ int i;
306+ for (i = 1; i < argc; i++)
307+ {
308+ mir_eglapp_bool help = 0;
309+ const char *arg = argv[i];
310+
311+ if (arg[0] == '-')
312+ {
313+ switch (arg[1])
314+ {
315+ case 'b':
316+ {
317+ float alpha = 1.0f;
318+ arg += 2;
319+ if (!arg[0] && i < argc-1)
320+ {
321+ i++;
322+ arg = argv[i];
323+ }
324+ if (sscanf(arg, "%f", &alpha) == 1)
325+ {
326+ mir_eglapp_background_opacity = alpha;
327+ }
328+ else
329+ {
330+ printf("Invalid opacity value: %s\n", arg);
331+ help = 1;
332+ }
333+ }
334+ break;
335+ case 'n':
336+ swapinterval = 0;
337+ break;
338+ case 'o':
339+ {
340+ unsigned int output_id = 0;
341+ arg += 2;
342+ if (!arg[0] && i < argc-1)
343+ {
344+ i++;
345+ arg = argv[i];
346+ }
347+ if (sscanf(arg, "%u", &output_id) == 1)
348+ {
349+ surfaceparm.output_id = output_id;
350+ }
351+ else
352+ {
353+ printf("Invalid output ID: %s\n", arg);
354+ help = 1;
355+ }
356+ }
357+ break;
358+ case 'f':
359+ *width = 0;
360+ *height = 0;
361+ break;
362+ case 's':
363+ {
364+ unsigned int w, h;
365+ arg += 2;
366+ if (!arg[0] && i < argc-1)
367+ {
368+ i++;
369+ arg = argv[i];
370+ }
371+ if (sscanf(arg, "%ux%u", &w, &h) == 2)
372+ {
373+ *width = w;
374+ *height = h;
375+ }
376+ else
377+ {
378+ printf("Invalid size: %s\n", arg);
379+ help = 1;
380+ }
381+ }
382+ break;
383+ case 'm':
384+ mir_socket = argv[++i];
385+ break;
386+ case 'q':
387+ {
388+ FILE *unused = freopen("/dev/null", "a", stdout);
389+ (void)unused;
390+ break;
391+ }
392+ case 'h':
393+ default:
394+ help = 1;
395+ break;
396+ }
397+ }
398+ else
399+ {
400+ help = 1;
401+ }
402+
403+ if (help)
404+ {
405+ printf("Usage: %s [<options>]\n"
406+ " -b Background opacity (0.0 - 1.0)\n"
407+ " -h Show this help text\n"
408+ " -f Force full screen\n"
409+ " -o ID Force placement on output monitor ID\n"
410+ " -n Don't sync to vblank\n"
411+ " -m socket Mir server socket\n"
412+ " -s WIDTHxHEIGHT Force surface size\n"
413+ " -q Quiet mode (no messages output)\n"
414+ , argv[0]);
415+ return 0;
416+ }
417+ }
418+ }
419+
420+ connection = mir_connect_sync(mir_socket, appname);
421+ CHECK(mir_connection_is_valid(connection), "Can't get connection");
422+
423+ /* eglapps are interested in the screen size, so
424+ use mir_connection_create_display_config */
425+ MirDisplayConfiguration* display_config =
426+ mir_connection_create_display_config(connection);
427+
428+ const MirDisplayOutput *output = find_active_output(display_config);
429+
430+ if (output == NULL)
431+ {
432+ printf("No active outputs found.\n");
433+ return 0;
434+ }
435+
436+ const MirDisplayMode *mode = &output->modes[output->current_mode];
437+
438+ unsigned int format[mir_pixel_formats];
439+ unsigned int nformats;
440+
441+ mir_connection_get_available_surface_formats(connection,
442+ (MirPixelFormat*) format, mir_pixel_formats, &nformats);
443+
444+ surfaceparm.pixel_format = (MirPixelFormat) format[0];
445+
446+ printf("Current active output is %dx%d %+d%+d\n",
447+ mode->horizontal_resolution, mode->vertical_resolution,
448+ output->position_x, output->position_y);
449+
450+ surfaceparm.width = *width > 0 ? *width : mode->horizontal_resolution;
451+ surfaceparm.height = *height > 0 ? *height : mode->vertical_resolution;
452+
453+ mir_display_config_destroy(display_config);
454+
455+ printf("Server supports %d of %d surface pixel formats. Using format: %d\n",
456+ nformats, mir_pixel_formats, surfaceparm.pixel_format);
457+ unsigned int bpp = 8 * MIR_BYTES_PER_PIXEL(surfaceparm.pixel_format);
458+ EGLint attribs[] =
459+ {
460+ EGL_SURFACE_TYPE, EGL_WINDOW_BIT,
461+ EGL_RENDERABLE_TYPE, EGL_OPENGL_ES2_BIT,
462+ EGL_COLOR_BUFFER_TYPE, EGL_RGB_BUFFER,
463+ EGL_BUFFER_SIZE, (EGLint) bpp,
464+ EGL_NONE
465+ };
466+
467+ surface = mir_connection_create_surface_sync(connection, &surfaceparm);
468+ CHECK(mir_surface_is_valid(surface), "Can't create a surface");
469+
470+ mir_surface_set_event_handler(surface, &delegate);
471+
472+ egldisplay = eglGetDisplay(
473+ (EGLNativeDisplayType) mir_connection_get_egl_native_display(connection));
474+ CHECK(egldisplay != EGL_NO_DISPLAY, "Can't eglGetDisplay");
475+
476+ ok = eglInitialize(egldisplay, NULL, NULL);
477+ CHECK(ok, "Can't eglInitialize");
478+
479+ ok = eglChooseConfig(egldisplay, attribs, &eglconfig, 1, &neglconfigs);
480+ CHECK(ok, "Could not eglChooseConfig");
481+ CHECK(neglconfigs > 0, "No EGL config available");
482+
483+ eglsurface = eglCreateWindowSurface(egldisplay, eglconfig,
484+ (EGLNativeWindowType)mir_surface_get_egl_native_window(surface),
485+ NULL);
486+ CHECK(eglsurface != EGL_NO_SURFACE, "eglCreateWindowSurface failed");
487+
488+ eglctx = eglCreateContext(egldisplay, eglconfig, EGL_NO_CONTEXT,
489+ ctxattribs);
490+ CHECK(eglctx != EGL_NO_CONTEXT, "eglCreateContext failed");
491+
492+ ok = eglMakeCurrent(egldisplay, eglsurface, eglsurface, eglctx);
493+ CHECK(ok, "Can't eglMakeCurrent");
494+
495+ signal(SIGINT, shutdown);
496+ signal(SIGTERM, shutdown);
497+
498+ *width = surfaceparm.width;
499+ *height = surfaceparm.height;
500+
501+ eglSwapInterval(egldisplay, swapinterval);
502+
503+ running = 1;
504+
505+ return 1;
506+}
507+
508+struct MirConnection* mir_eglapp_native_connection()
509+{
510+ return connection;
511+}
512+
513+struct MirSurface* mir_eglapp_native_surface()
514+{
515+ return surface;
516+}
517
518=== added file 'spinner/eglapp.h'
519--- spinner/eglapp.h 1970-01-01 00:00:00 +0000
520+++ spinner/eglapp.h 2014-05-06 20:06:39 +0000
521@@ -0,0 +1,44 @@
522+/*
523+ * Copyright © 2013 Canonical Ltd.
524+ *
525+ * This program is free software: you can redistribute it and/or modify
526+ * it under the terms of the GNU General Public License version 3 as
527+ * published by the Free Software Foundation.
528+ *
529+ * This program is distributed in the hope that it will be useful,
530+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
531+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
532+ * GNU General Public License for more details.
533+ *
534+ * You should have received a copy of the GNU General Public License
535+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
536+ *
537+ * Author: Daniel van Vugt <daniel.van.vugt@canonical.com>
538+ */
539+
540+#ifndef __EGLAPP_H__
541+#define __EGLAPP_H__
542+
543+#ifdef __cplusplus
544+extern "C" {
545+#endif
546+
547+typedef int mir_eglapp_bool;
548+struct MirConnection;
549+struct MirSurface;
550+
551+extern float mir_eglapp_background_opacity;
552+
553+mir_eglapp_bool mir_eglapp_init(int argc, char *argv[],
554+ unsigned int *width, unsigned int *height);
555+void mir_eglapp_swap_buffers(void);
556+mir_eglapp_bool mir_eglapp_running(void);
557+void mir_eglapp_shutdown(void);
558+
559+struct MirConnection* mir_eglapp_native_connection();
560+struct MirSurface* mir_eglapp_native_surface();
561+#ifdef __cplusplus
562+}
563+#endif
564+
565+#endif
566
567=== added file 'spinner/eglspinner.c'
568--- spinner/eglspinner.c 1970-01-01 00:00:00 +0000
569+++ spinner/eglspinner.c 2014-05-06 20:06:39 +0000
570@@ -0,0 +1,415 @@
571+/*
572+ * Copyright © 2013 Canonical Ltd.
573+ *
574+ * This program is free software: you can redistribute it and/or modify
575+ * it under the terms of the GNU General Public License version 3 as
576+ * published by the Free Software Foundation.
577+ *
578+ * This program is distributed in the hope that it will be useful,
579+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
580+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
581+ * GNU General Public License for more details.
582+ *
583+ * You should have received a copy of the GNU General Public License
584+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
585+ *
586+ * Authors: Daniel van Vugt <daniel.van.vugt@canonical.com>
587+ * Mirco Müller <mirco.mueller@canonical.com>
588+ */
589+
590+#include "eglapp.h"
591+#include <assert.h>
592+#include <cairo.h>
593+#include <glib.h>
594+#include <stdio.h>
595+#include <string.h>
596+#include <strings.h>
597+#include <stdlib.h>
598+#include <GLES2/gl2.h>
599+#include <math.h>
600+#if HAVE_PROPS
601+#include <hybris/properties/properties.h>
602+#endif
603+
604+// this is needed for get_gu() to obtain the grid-unit value
605+#define MAX_LENGTH 256
606+#define VALUE_KEY "GRID_UNIT_PX"
607+#define VALUE_KEY_LENGTH 12
608+#define PROP_KEY "ro.product.device"
609+#define FILE_BASE "/etc/ubuntu-touch-session.d/"
610+#define FILE_EXTENSION ".conf"
611+
612+int get_gu ()
613+{
614+ int gu = 10; // use 10 as a default value
615+ FILE* handle = NULL;
616+ int i = 0;
617+ int j = 0;
618+ int len = 0;
619+ char line[MAX_LENGTH];
620+ char filename[MAX_LENGTH];
621+
622+ // get name of file to read from
623+ bzero ((void*) filename, MAX_LENGTH);
624+ strcpy (filename, FILE_BASE);
625+#ifdef HAVE_PROPS
626+ char* defaultValue = "";
627+ char value[PROP_VALUE_MAX];
628+ property_get (PROP_KEY, value, defaultValue);
629+ strcat (filename, value);
630+#endif
631+ strcat (filename, FILE_EXTENSION);
632+
633+ // try to open it
634+ handle = fopen ((const char*) filename, "r");
635+ if (!handle)
636+ return gu;
637+
638+ // read one line at a time
639+ while (fgets (line, MAX_LENGTH, handle))
640+ {
641+ // strip line of whitespaces
642+ i = 0;
643+ j = 0;
644+ len = (int) strlen (line);
645+ while (i != len)
646+ {
647+ if (line[i] != ' ' && line[i] != '\t')
648+ line[j++] = line[i];
649+ i++;
650+ }
651+ line[j] = 0;
652+
653+ // parse the line for GU-value
654+ if (!strncmp (line, VALUE_KEY, VALUE_KEY_LENGTH))
655+ sscanf (line, VALUE_KEY"=%d", &gu);
656+ }
657+
658+ // clean up
659+ fclose (handle);
660+
661+ return gu;
662+}
663+
664+static GLuint load_shader(const char *src, GLenum type)
665+{
666+ GLuint shader = glCreateShader(type);
667+ if (shader)
668+ {
669+ GLint compiled;
670+ glShaderSource(shader, 1, &src, NULL);
671+ glCompileShader(shader);
672+ glGetShaderiv(shader, GL_COMPILE_STATUS, &compiled);
673+ if (!compiled)
674+ {
675+ GLchar log[1024];
676+ glGetShaderInfoLog(shader, sizeof log - 1, NULL, log);
677+ log[sizeof log - 1] = '\0';
678+ printf("load_shader compile failed: %s\n", log);
679+ glDeleteShader(shader);
680+ shader = 0;
681+ }
682+ }
683+ return shader;
684+}
685+
686+// Colours from http://design.ubuntu.com/brand/colour-palette
687+#define MID_AUBERGINE 0.368627451f, 0.152941176f, 0.31372549f
688+#define ORANGE 0.866666667f, 0.282352941f, 0.141414141f
689+#define WARM_GREY 0.682352941f, 0.654901961f, 0.623529412f
690+#define COOL_GREY 0.2f, 0.2f, 0.2f
691+#define LIGHT_AUBERGINE 0.466666667f, 0.297297297f, 0.435294118f
692+#define DARK_AUBERGINE 0.17254902f, 0.0f, 0.117647059f
693+#define BLACK 0.0f, 0.0f, 0.0f
694+#define WHITE 1.0f, 1.0f, 1.0f
695+
696+cairo_surface_t* pngToSurface (const char* filename)
697+{
698+ // sanity check
699+ if (!filename)
700+ return NULL;
701+
702+ // create surface from PNG
703+ cairo_surface_t* surface = NULL;
704+ surface = cairo_image_surface_create_from_png (filename);
705+ if (cairo_surface_status (surface) != CAIRO_STATUS_SUCCESS)
706+ return NULL;
707+
708+ return surface;
709+}
710+
711+void uploadTexture (GLuint id, const char* filename)
712+{
713+ if (!id || !filename)
714+ return;
715+
716+ cairo_surface_t* surface = pngToSurface (filename);
717+
718+ glBindTexture(GL_TEXTURE_2D, id);
719+
720+ if (cairo_surface_status (surface) == CAIRO_STATUS_SUCCESS)
721+ {
722+ glTexImage2D(GL_TEXTURE_2D,
723+ 0,
724+ cairo_image_surface_get_format (surface) == CAIRO_FORMAT_ARGB32 ? GL_RGBA : GL_RGB,
725+ cairo_image_surface_get_width (surface),
726+ cairo_image_surface_get_height (surface),
727+ 0,
728+ cairo_image_surface_get_format (surface) == CAIRO_FORMAT_ARGB32 ? GL_RGBA : GL_RGB,
729+ GL_UNSIGNED_BYTE,
730+ cairo_image_surface_get_data (surface));
731+ cairo_surface_destroy (surface);
732+ }
733+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
734+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
735+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
736+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
737+ glBindTexture(GL_TEXTURE_2D, 0);
738+}
739+
740+GLuint createShaderProgram(const char* vertexShaderSrc, const char* fragmentShaderSrc)
741+{
742+ if (!vertexShaderSrc || !fragmentShaderSrc)
743+ return 0;
744+
745+ GLuint vShaderId = 0;
746+ vShaderId = load_shader(vertexShaderSrc, GL_VERTEX_SHADER);
747+ assert(vShaderId);
748+
749+ GLuint fShaderId = 0;
750+ fShaderId = load_shader(fragmentShaderSrc, GL_FRAGMENT_SHADER);
751+ assert(fShaderId);
752+
753+ GLuint progId = 0;
754+ progId = glCreateProgram();
755+ assert(progId);
756+ glAttachShader(progId, vShaderId);
757+ glAttachShader(progId, fShaderId);
758+ glLinkProgram(progId);
759+
760+ GLint linked = 0;
761+ glGetProgramiv(progId, GL_LINK_STATUS, &linked);
762+ if (!linked)
763+ {
764+ GLchar log[1024];
765+ glGetProgramInfoLog(progId, sizeof log - 1, NULL, log);
766+ log[sizeof log - 1] = '\0';
767+ printf("Link failed: %s\n", log);
768+ return 0;
769+ }
770+
771+ return progId;
772+}
773+
774+typedef struct _AnimationValues
775+{
776+ double lastTimeStamp;
777+ double angle;
778+ double fadeBackground;
779+ double fadeLogo;
780+ double fadeGlow;
781+} AnimationValues;
782+
783+void
784+updateAnimation (GTimer* timer, AnimationValues* anim)
785+{
786+ if (!timer || !anim)
787+ return;
788+
789+ //1.) 0.0 - 0.6: logo fades in fully
790+ //2.) 0.0 - 6.0: logo does one full spin 360°
791+ //3.) 6.0 - 6.833: glow fades in fully, black-background fades out to 50%
792+ //4.) 6.833 - 7.666: glow fades out fully, black-background fades out to 0%
793+ //5.) 7.666 - 8.266: logo fades out fully
794+ //8.266..: now spinner can be closed as all its elements are faded out
795+
796+ double elapsed = g_timer_elapsed (timer, NULL);
797+ double dt = elapsed - anim->lastTimeStamp;
798+ anim->lastTimeStamp = elapsed;
799+
800+ // step 1.)
801+ if (elapsed < 0.6f)
802+ anim->fadeLogo += 1.6f * dt;
803+
804+ // step 2.)
805+ anim->angle -= (0.017453292519943f * 360.0f / 6.0f) * dt;
806+
807+ // step 3.) glow
808+ if (elapsed > 6.0f && elapsed < 6.833f)
809+ anim->fadeGlow += 1.2f * dt;
810+
811+ // Ignore the following three until we can synchronize with greeter
812+
813+ // step 3.) background
814+ //if (elapsed > 6.0f && elapsed < 6.833f)
815+ // anim->fadeBackground -= 0.6f * dt;
816+
817+ // step 4.) background
818+ //if (elapsed > 7.0f)
819+ // anim->fadeBackground -= 0.6f * dt;
820+
821+ // step 5.)
822+ //if (elapsed > 6.833f)
823+ // anim->fadeLogo -= 1.6f * dt;
824+}
825+
826+int main(int argc, char *argv[])
827+{
828+ const char vShaderSrcSpinner[] =
829+ "attribute vec4 vPosition; \n"
830+ "attribute vec2 aTexCoords; \n"
831+ "uniform float theta; \n"
832+ "varying vec2 vTexCoords; \n"
833+ "void main() \n"
834+ "{ \n"
835+ " float c = cos(theta); \n"
836+ " float s = sin(theta); \n"
837+ " mat2 m; \n"
838+ " m[0] = vec2(c, s); \n"
839+ " m[1] = vec2(-s, c); \n"
840+ " vTexCoords = m * aTexCoords + vec2 (0.5, 0.5); \n"
841+ " gl_Position = vec4(vPosition.xy, -1.0, 1.0); \n"
842+ "} \n";
843+
844+ const char fShaderSrcGlow[] =
845+ "precision mediump float; \n"
846+ "varying vec2 vTexCoords; \n"
847+ "uniform sampler2D uSampler; \n"
848+ "uniform float uFadeGlow; \n"
849+ "void main() \n"
850+ "{ \n"
851+ " // swizzle because texture was created with cairo\n"
852+ " vec4 col = texture2D(uSampler, vTexCoords).bgra; \n"
853+ " float r = col.r * uFadeGlow; \n"
854+ " float g = col.g * uFadeGlow; \n"
855+ " float b = col.b * uFadeGlow; \n"
856+ " float a = col.a * uFadeGlow; \n"
857+ " gl_FragColor = vec4(r, g, b, a); \n"
858+ "} \n";
859+
860+ const char fShaderSrcLogo[] =
861+ "precision mediump float; \n"
862+ "varying vec2 vTexCoords; \n"
863+ "uniform sampler2D uSampler; \n"
864+ "uniform float uFadeLogo; \n"
865+ "void main() \n"
866+ "{ \n"
867+ " // swizzle because texture was created with cairo\n"
868+ " vec4 col = texture2D(uSampler, vTexCoords).bgra; \n"
869+ " float r = col.r * uFadeLogo; \n"
870+ " float g = col.g * uFadeLogo; \n"
871+ " float b = col.b * uFadeLogo; \n"
872+ " float a = col.a * uFadeLogo; \n"
873+ " gl_FragColor = vec4(r, g, b, a); \n"
874+ "} \n";
875+
876+ GLuint prog[2];
877+ GLuint texture[2];
878+ GLint vpos[2];
879+ GLint theta;
880+ GLint fadeGlow;
881+ GLint fadeLogo;
882+ GLint aTexCoords[2];
883+ GLint sampler[2];
884+ unsigned int width = 0;
885+ unsigned int height = 0;
886+
887+ if (!mir_eglapp_init(argc, argv, &width, &height))
888+ return 1;
889+
890+ double pixelSize = (double) get_gu () * 11.18;
891+ double halfRealWidth = ((2.0 / (double) width) * pixelSize) / 2.0;
892+ double halfRealHeight = ((2.0 / (double) height) * pixelSize) / 2.0;
893+
894+ const GLfloat vertices[] =
895+ {
896+ halfRealWidth, halfRealHeight,
897+ halfRealWidth, -halfRealHeight,
898+ -halfRealWidth, halfRealHeight,
899+ -halfRealWidth, -halfRealHeight,
900+ };
901+
902+ const GLfloat texCoordsSpinner[] =
903+ {
904+ -0.5f, 0.5f,
905+ -0.5f, -0.5f,
906+ 0.5f, 0.5f,
907+ 0.5f, -0.5f,
908+ };
909+
910+ prog[0] = createShaderProgram (vShaderSrcSpinner, fShaderSrcGlow);
911+ prog[1] = createShaderProgram (vShaderSrcSpinner, fShaderSrcLogo);
912+
913+ // setup viewport and projection
914+ glClearColor(BLACK, mir_eglapp_background_opacity);
915+ glViewport(0, 0, width, height);
916+
917+ // setup proper GL-blending
918+ glEnable(GL_BLEND);
919+ glBlendFunc(GL_ONE, GL_ONE_MINUS_SRC_ALPHA);
920+ glBlendEquation(GL_FUNC_ADD);
921+
922+ // get locations of shader-attributes/uniforms
923+ vpos[0] = glGetAttribLocation(prog[0], "vPosition");
924+ aTexCoords[0] = glGetAttribLocation(prog[0], "aTexCoords");
925+ theta = glGetUniformLocation(prog[0], "theta");
926+ sampler[0] = glGetUniformLocation(prog[0], "uSampler");
927+ fadeGlow = glGetUniformLocation(prog[0], "uFadeGlow");
928+ vpos[1] = glGetAttribLocation(prog[1], "vPosition");
929+ aTexCoords[1] = glGetAttribLocation(prog[1], "aTexCoords");
930+ sampler[1] = glGetUniformLocation(prog[1], "uSampler");
931+ fadeLogo = glGetUniformLocation(prog[1], "uFadeLogo");
932+
933+ // create and upload spinner-artwork
934+ glGenTextures(2, texture);
935+ uploadTexture(texture[0], PKGDATADIR "/spinner-glow.png");
936+ uploadTexture(texture[1], PKGDATADIR "/spinner-logo.png");
937+
938+ // bunch of shader-attributes to enable
939+ glVertexAttribPointer(vpos[0], 2, GL_FLOAT, GL_FALSE, 0, vertices);
940+ glVertexAttribPointer(vpos[1], 2, GL_FLOAT, GL_FALSE, 0, vertices);
941+ glVertexAttribPointer(aTexCoords[0], 2, GL_FLOAT, GL_FALSE, 0, texCoordsSpinner);
942+ glVertexAttribPointer(aTexCoords[1], 2, GL_FLOAT, GL_FALSE, 0, texCoordsSpinner);
943+ glEnableVertexAttribArray(vpos[0]);
944+ glEnableVertexAttribArray(vpos[1]);
945+ glEnableVertexAttribArray(aTexCoords[0]);
946+ glEnableVertexAttribArray(aTexCoords[1]);
947+ glActiveTexture(GL_TEXTURE0);
948+
949+ AnimationValues anim = {0.0, 0.0, 1.0, 0.0, 0.0};
950+ GTimer* timer = g_timer_new ();
951+
952+ while (mir_eglapp_running())
953+ {
954+ glClearColor(BLACK, anim.fadeBackground);
955+ glClear(GL_COLOR_BUFFER_BIT);
956+
957+ // draw glow
958+ glUseProgram(prog[0]);
959+ glBindTexture(GL_TEXTURE_2D, texture[0]);
960+ glUniform1i(sampler[0], 0);
961+ glUniform1f(theta, anim.angle);
962+ glUniform1f(fadeGlow, anim.fadeGlow);
963+ glDrawArrays(GL_TRIANGLE_STRIP, 0, 4);
964+
965+ // draw logo
966+ glUseProgram(prog[1]);
967+ glBindTexture(GL_TEXTURE_2D, texture[1]);
968+ glUniform1i(sampler[1], 0);
969+ glUniform1f(theta, anim.angle);
970+ glUniform1f(fadeLogo, anim.fadeLogo);
971+ glDrawArrays(GL_TRIANGLE_STRIP, 0, 4);
972+
973+ // update animation variable
974+ updateAnimation (timer, &anim);
975+
976+ mir_eglapp_swap_buffers();
977+ }
978+
979+ mir_eglapp_shutdown();
980+
981+ glDeleteTextures(2, texture);
982+ g_timer_destroy (timer);
983+
984+ return 0;
985+}
986
987=== added file 'spinner/spinner-glow.png'
988Binary files spinner/spinner-glow.png 1970-01-01 00:00:00 +0000 and spinner/spinner-glow.png 2014-05-06 20:06:39 +0000 differ
989=== added file 'spinner/spinner-logo.png'
990Binary files spinner/spinner-logo.png 1970-01-01 00:00:00 +0000 and spinner/spinner-logo.png 2014-05-06 20:06:39 +0000 differ
991=== modified file 'src/CMakeLists.txt'
992--- src/CMakeLists.txt 2013-10-31 19:16:23 +0000
993+++ src/CMakeLists.txt 2014-05-06 20:06:39 +0000
994@@ -39,9 +39,16 @@
995 include_directories(
996 ${CMAKE_CURRENT_SOURCE_DIR}
997 ${CMAKE_CURRENT_BINARY_DIR}
998+ ${Boost_INCLUDE_DIRS}
999+ ${GLESv2_INCLUDE_DIRS}
1000+ ${MIRSERVER_INCLUDE_DIRS}
1001+)
1002+add_definitions(
1003+ -DDEFAULT_SPINNER="${CMAKE_INSTALL_FULL_BINDIR}/unity-system-compositor-spinner"
1004 )
1005
1006 # Link against libmirserver
1007+link_directories(${MIRSERVER_LIBRARY_DIRS})
1008 target_link_libraries(unity-system-compositor
1009 mirserver
1010 pthread
1011
1012=== modified file 'src/system_compositor.cpp'
1013--- src/system_compositor.cpp 2014-04-15 20:47:54 +0000
1014+++ src/system_compositor.cpp 2014-05-06 20:06:39 +0000
1015@@ -48,9 +48,10 @@
1016 class SystemCompositorShell : public mf::Shell
1017 {
1018 public:
1019- SystemCompositorShell(std::shared_ptr<mf::Shell> const& self,
1020+ SystemCompositorShell(SystemCompositor *compositor,
1021+ std::shared_ptr<mf::Shell> const& self,
1022 std::shared_ptr<msh::FocusController> const& focus_controller)
1023- : self(self), focus_controller{focus_controller} {}
1024+ : compositor{compositor}, self(self), focus_controller{focus_controller} {}
1025
1026 std::shared_ptr<mf::Session> session_named(std::string const& name)
1027 {
1028@@ -60,42 +61,72 @@
1029 void set_active_session(std::string const& name)
1030 {
1031 active_session = name;
1032-
1033- if (auto session = std::static_pointer_cast<msc::Session>(session_named(name)))
1034- focus_controller->set_focus_to(session);
1035- else
1036- std::cerr << "Unable to set active session, unknown client name " << name << std::endl;
1037+ update_session_focus();
1038 }
1039
1040 void set_next_session(std::string const& name)
1041 {
1042- if (auto const session = std::static_pointer_cast<msc::Session>(session_named(name)))
1043- {
1044- focus_controller->set_focus_to(session); // raise session inside its depth id set
1045- set_active_session(active_session); // to restore input focus to where it should be
1046- }
1047- else
1048- std::cerr << "Unable to set next session, unknown client name " << name << std::endl;
1049+ next_session = name;
1050+ update_session_focus();
1051 }
1052
1053 private:
1054+ void update_session_focus()
1055+ {
1056+ auto spinner = std::static_pointer_cast<msc::Session>(session_named(spinner_session));
1057+ auto next = std::static_pointer_cast<msc::Session>(session_named(next_session));
1058+ auto active = std::static_pointer_cast<msc::Session>(session_named(active_session));
1059+
1060+ if (spinner)
1061+ spinner->hide();
1062+
1063+ if (next)
1064+ {
1065+ std::cerr << "Setting next focus to session " << next_session;
1066+ focus_controller->set_focus_to(next);
1067+ }
1068+ else if (spinner)
1069+ {
1070+ std::cerr << "Setting next focus to spinner";
1071+ spinner->show();
1072+ focus_controller->set_focus_to(spinner);
1073+ }
1074+
1075+ if (active)
1076+ {
1077+ std::cerr << "; active focus to session " << active_session << std::endl;
1078+ focus_controller->set_focus_to(active);
1079+ }
1080+ else if (spinner)
1081+ {
1082+ std::cerr << "; active focus to spinner" << std::endl;
1083+ spinner->show();
1084+ focus_controller->set_focus_to(spinner);
1085+ }
1086+ }
1087+
1088 std::shared_ptr<mf::Session> open_session(
1089 pid_t client_pid,
1090 std::string const& name,
1091 std::shared_ptr<mf::EventSink> const& sink)
1092 {
1093+ std::cerr << "Opening session " << name << std::endl;
1094 auto result = self->open_session(client_pid, name, sink);
1095 sessions[name] = result;
1096
1097- // Opening a new session will steal focus from our active session, so
1098- // restore the focus if needed.
1099- set_active_session(active_session);
1100+ if (client_pid == compositor->get_spinner_pid())
1101+ spinner_session = name;
1102
1103 return result;
1104 }
1105
1106 void close_session(std::shared_ptr<mf::Session> const& session)
1107 {
1108+ std::cerr << "Closing session " << session->name() << std::endl;
1109+
1110+ if (session->name() == spinner_session)
1111+ spinner_session = "";
1112+
1113 sessions.erase(session->name());
1114 self->close_session(session);
1115 }
1116@@ -110,12 +141,19 @@
1117 void handle_surface_created(std::shared_ptr<mf::Session> const& session)
1118 {
1119 self->handle_surface_created(session);
1120+
1121+ // Opening a new surface will steal focus from our active surface, so
1122+ // restore the focus if needed.
1123+ update_session_focus();
1124 }
1125
1126+ SystemCompositor *compositor;
1127 std::shared_ptr<mf::Shell> const self;
1128 std::shared_ptr<msh::FocusController> const focus_controller;
1129 std::map<std::string, std::shared_ptr<mf::Session>> sessions;
1130 std::string active_session;
1131+ std::string next_session;
1132+ std::string spinner_session;
1133 };
1134
1135 class SystemCompositorServerConfiguration : public mir::DefaultServerConfiguration
1136@@ -158,6 +196,15 @@
1137 return x;
1138 }
1139
1140+ std::string spinner()
1141+ {
1142+ // TODO: once our default spinner is ready for use everywhere, replace
1143+ // default value with DEFAULT_SPINNER instead of the empty string.
1144+ auto x = the_options()->get("spinner", "");
1145+ boost::trim(x);
1146+ return x;
1147+ }
1148+
1149 bool public_socket()
1150 {
1151 return !the_options()->is_set("no-file") && the_options()->get("public-socket", true);
1152@@ -217,6 +264,7 @@
1153 return sc_shell([this]
1154 {
1155 return std::make_shared<SystemCompositorShell>(
1156+ compositor,
1157 mir::DefaultServerConfiguration::the_frontend_shell(),
1158 the_focus_controller());
1159 });
1160@@ -244,6 +292,7 @@
1161 ("to-dm-fd", po::value<int>(), "File descriptor of write end of pipe to display manager [int]")
1162 ("blacklist", po::value<std::string>(), "Video blacklist regex to use")
1163 ("version", "Show version of Unity System Compositor")
1164+ ("spinner", po::value<std::string>(), "Path to spinner executable")
1165 ("public-socket", po::value<bool>(), "Make the socket file publicly writable")
1166 ("power-off-delay", po::value<int>(), "Delay in milliseconds before powering off screen [int]")
1167 ("enable-hardware-cursor", po::value<bool>(), "Enable the hardware cursor (disabled by default)");
1168@@ -354,6 +403,11 @@
1169 active_session->set_lifecycle_state(mir_lifecycle_state_resumed);
1170 }
1171
1172+pid_t SystemCompositor::get_spinner_pid() const
1173+{
1174+ return spinner_process.pid();
1175+}
1176+
1177 void SystemCompositor::set_active_session(std::string client_name)
1178 {
1179 std::cerr << "set_active_session" << std::endl;
1180@@ -381,9 +435,22 @@
1181 io_service.run();
1182 }
1183
1184+void SystemCompositor::launch_spinner()
1185+{
1186+ if (config->spinner().empty())
1187+ return;
1188+
1189+ // Launch spinner process to provide default background when a session isn't ready
1190+ QStringList env = QProcess::systemEnvironment();
1191+ env << "MIR_SOCKET=" + QString(config->get_socket_file().c_str());
1192+ spinner_process.setEnvironment(env);
1193+ spinner_process.start(config->spinner().c_str());
1194+}
1195+
1196 void SystemCompositor::qt_main(int argc, char **argv)
1197 {
1198 QCoreApplication app(argc, argv);
1199 DBusScreen dbus_screen(config, config->power_off_delay());
1200+ launch_spinner();
1201 app.exec();
1202 }
1203
1204=== modified file 'src/system_compositor.h'
1205--- src/system_compositor.h 2014-04-15 20:47:54 +0000
1206+++ src/system_compositor.h 2014-05-06 20:06:39 +0000
1207@@ -20,6 +20,7 @@
1208 #define SYSTEM_COMPOSITOR_H_
1209
1210 #include "dm_connection.h"
1211+#include <QProcess>
1212
1213 namespace mir { namespace scene { class Session; } }
1214
1215@@ -32,17 +33,20 @@
1216 void run(int argc, char **argv);
1217 void pause();
1218 void resume();
1219+ pid_t get_spinner_pid() const;
1220
1221 private:
1222 std::shared_ptr<SystemCompositorServerConfiguration> config;
1223 std::shared_ptr<SystemCompositorShell> shell;
1224 boost::asio::io_service io_service;
1225 std::shared_ptr<DMConnection> dm_connection;
1226+ QProcess spinner_process;
1227
1228 void set_active_session(std::string client_name);
1229 void set_next_session(std::string client_name);
1230 void main();
1231 void qt_main(int argc, char **argv);
1232+ void launch_spinner();
1233 };
1234
1235 #endif /* SYSTEM_COMPOSITOR_H_ */

Subscribers

People subscribed via source and target branches