Merge lp:~unity-team/unity-system-compositor/new-gl-screen into lp:unity-system-compositor
- new-gl-screen
- Merge into trunk
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 |
Related bugs: |
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-
PS Jenkins bot (ps-jenkins) wrote : | # |
Oliver Grawert (ogra) wrote : | # |
Is the design work coordinated with the spinner we have in the recovery img now (which is shown during upgrades) ?
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-
so it seems there is something missing in usc to actually execute the spinner when the option is set
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.
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.
Oliver Grawert (ogra) wrote : | # |
Ok with the proper option it works on all devices for me (tested on flo, mako and manta)
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.
Michał Sawicz (saviq) wrote : | # |
Hey folks.
Looks like we need to make the libandroid-
PS Jenkins bot (ps-jenkins) wrote : | # |
PASSED: Continuous integration, rev:149
http://
Executed test runs:
SUCCESS: http://
SUCCESS: http://
Click here to trigger a rebuild:
http://
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
Michael Terry (mterry) wrote : | # |
Saviq, I've updated this branch to not require libandroid-
PS Jenkins bot (ps-jenkins) wrote : | # |
PASSED: Continuous integration, rev:150
http://
Executed test runs:
SUCCESS: http://
SUCCESS: http://
Click here to trigger a rebuild:
http://
Michał Sawicz (saviq) : | # |
Preview Diff
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' |
988 | Binary 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' |
990 | Binary 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_ */ |
PASSED: Continuous integration, rev:131 jenkins. qa.ubuntu. com/job/ unity-system- compositor- ci/90/ jenkins. qa.ubuntu. com/job/ unity-system- compositor- trusty- amd64-ci/ 42 jenkins. qa.ubuntu. com/job/ unity-system- compositor- trusty- armhf-ci/ 42
http://
Executed test runs:
SUCCESS: http://
SUCCESS: http://
Click here to trigger a rebuild: s-jenkins. ubuntu- ci:8080/ job/unity- system- compositor- ci/90/rebuild
http://