blob: 71244e55189e09a3fbc79ec9dc8348859807c71a [file] [log] [blame]
David Sodmanbbcb0522014-09-19 10:34:07 -07001/*
2 * Copyright (c) 2014 The Chromium OS Authors. All rights reserved.
3 * Use of this source code is governed by a BSD-style license that can be
4 * found in the LICENSE file.
5 */
6
7#include <stdbool.h>
8#include <stddef.h>
9#include <stdio.h>
10#include <stdlib.h>
11#include <string.h>
12#include <sys/mman.h>
13#include <unistd.h>
14#include <fcntl.h>
15
16#include <math.h>
17#include <png.h>
18
19#include "util.h"
20#include "splash.h"
21#include "dbus_interface.h"
22
23#define MAX_SPLASH_IMAGES (30)
24#define FILENAME_LENGTH (100)
David Sodman3430aae2014-11-10 08:15:01 -080025#define MAX_SPLASH_WAITTIME (5000)
David Sodmanbbcb0522014-09-19 10:34:07 -070026
27typedef union {
28 uint32_t *as_pixels;
29 png_byte *as_png_bytes;
30 char *address;
31} splash_layout_t;
32
33typedef struct {
34 char filename[FILENAME_LENGTH];
35 FILE *fp;
36 splash_layout_t layout;
37 png_uint_32 width;
38 png_uint_32 height;
39 png_uint_32 pitch;
40} splash_image_t;
41
42struct _splash_t {
43 video_t *video;
44 int num_images;
45 splash_image_t images[MAX_SPLASH_IMAGES];
46 int frame_interval;
47 uint32_t clear;
48 bool terminated;
49 bool devmode;
50 dbus_t *dbus;
51};
52
53static int splash_load_image_from_file(splash_t* splash, splash_image_t* image);
54static int splash_image_show(splash_t *splash, splash_image_t* image,
55 uint32_t *video_buffer);
56static void splash_rgb(png_struct *png, png_row_info *row_info, png_byte *data);
57
58splash_t* splash_init(video_t *video)
59{
60 splash_t* splash;
61
62 splash = (splash_t*)calloc(1, sizeof(splash_t));
63 if (splash == NULL)
64 return NULL;
65
66 splash->num_images = 0;
67 splash->video = video;
68
69 return splash;
70}
71
72int splash_destroy(splash_t* splash)
73{
74 return 0;
75}
76
77int splash_set_frame_rate(splash_t *splash, int32_t rate)
78{
79 if (rate <= 0 || rate > 120)
80 return 1;
81
82 splash->frame_interval = rate;
83 return 0;
84}
85
86int splash_set_clear(splash_t *splash, int32_t clear_color)
87{
88 splash->clear = clear_color;
89 return 0;
90}
91
92int splash_add_image(splash_t* splash, const char* filename)
93{
94 if (splash->num_images >= MAX_SPLASH_IMAGES)
95 return 1;
96
97 strcpy(splash->images[splash->num_images].filename, filename);
98 splash->num_images++;
99 return 0;
100}
101
102static void
103frecon_dbus_path_message_func(dbus_t* dbus, void* user_data)
104{
105 splash_t* splash = (splash_t*)user_data;
106
107 if (!splash->devmode)
108 exit(EXIT_SUCCESS);
109
110 dbus_stop_wait(dbus);
111}
112
113static void splash_clear_screen(splash_t *splash, uint32_t *video_buffer)
114{
115 int i,j;
116 buffer_properties_t *bp;
117
118 video_setmode(splash->video);
119
120 bp = video_get_buffer_properties(splash->video);
121
122 for (j = 0; j < bp->height; j++) {
123 for (i = 0; i < bp->width; i++) {
124 (video_buffer + bp->pitch/4 * j)[i] = splash->clear;
125 }
126 }
127}
128
129int splash_run(splash_t* splash, dbus_t** dbus)
130{
131 int i;
132 uint32_t* video_buffer;
133 int status;
134 bool db_status;
135 int64_t last_show_ms;
136 int64_t now_ms;
137 int64_t sleep_ms;
138 struct timespec sleep_spec;
139 int fd;
140 int num_written;
David Sodman3430aae2014-11-10 08:15:01 -0800141 int wfm_status;
David Sodmanbbcb0522014-09-19 10:34:07 -0700142
143 status = 0;
144
145 /*
146 * First draw the actual splash screen
147 */
148 video_buffer = video_lock(splash->video);
149 if (video_buffer != NULL) {
150 splash_clear_screen(splash, video_buffer);
151 last_show_ms = -1;
152 for (i = 0; i < splash->num_images; i++) {
153 status = splash_load_image_from_file(splash, &splash->images[i]);
154 if (status != 0) {
155 LOG(WARNING, "splash_load_image_from_file failed: %d\n", status);
156 break;
157 }
158
159 now_ms = get_monotonic_time_ms();
160 if (last_show_ms > 0) {
161 sleep_ms = splash->frame_interval - (now_ms - last_show_ms);
162 if (sleep_ms > 0) {
163 sleep_spec.tv_sec = sleep_ms / MS_PER_SEC;
164 sleep_spec.tv_nsec = (sleep_ms % MS_PER_SEC) * NS_PER_MS;
165 nanosleep(&sleep_spec, NULL);
166 }
167 }
168
169 now_ms = get_monotonic_time_ms();
170
171 status = splash_image_show(splash, &splash->images[i], video_buffer);
172 if (status != 0) {
173 LOG(WARNING, "splash_image_show failed: %d", status);
174 break;
175 }
176 last_show_ms = now_ms;
177 }
178 video_unlock(splash->video);
179
180 /*
181 * Next wait until chrome has drawn on top of the splash. In dev mode,
182 * dbus_wait_for_messages will return when chrome is visible. In
183 * verified mode, the frecon app will exit before dbus_wait_for_messages
184 * returns
185 */
186 do {
187 *dbus = dbus_init();
188 usleep(50000);
189 } while (*dbus == NULL);
190
191 splash_set_dbus(splash, *dbus);
192
193 db_status = dbus_signal_match_handler(*dbus,
194 kLoginPromptVisibleSignal,
195 kSessionManagerServicePath,
196 kSessionManagerInterface,
197 kLoginPromptVisiibleRule,
198 frecon_dbus_path_message_func, splash);
199
David Sodman3430aae2014-11-10 08:15:01 -0800200 if (db_status) {
201 wfm_status = dbus_wait_for_messages(*dbus, MAX_SPLASH_WAITTIME);
202 switch (wfm_status) {
203 case DBUS_STATUS_TIMEOUT:
204 LOG(WARNING, "timed out waiting for messages\n");
205 break;
206 }
207 }
David Sodmanbbcb0522014-09-19 10:34:07 -0700208
209
210 /*
211 * Now set drm_master_relax so that we can transfer drm_master between
212 * chrome and frecon
213 */
214 fd = open("/sys/kernel/debug/dri/drm_master_relax", O_WRONLY);
215 if (fd != -1) {
216 num_written = write(fd, "Y", 1);
217 close(fd);
218
219 /*
220 * If we can't set drm_master relax, then transitions between chrome
221 * and frecon won't work. No point in having frecon hold any resources
222 */
223 if (num_written != 1) {
224 LOG(ERROR, "Unable to set drm_master_relax");
225 splash->devmode = false;
226 }
227 } else {
228 LOG(ERROR, "unable to open drm_master_relax");
229 }
230 }
David Sodman003faed2014-11-03 09:02:10 -0800231
Stéphane Marchesin66399132014-11-06 12:03:26 -0800232 /* Let chrome know it's ok to take drmMaster */
David Sodman003faed2014-11-03 09:02:10 -0800233 (void)dbus_method_call0(splash->dbus,
234 kLibCrosServiceName,
235 kLibCrosServicePath,
236 kLibCrosServiceInterface,
237 kTakeDisplayOwnership);
David Sodmanbbcb0522014-09-19 10:34:07 -0700238 return status;
239}
240
241
242static int splash_load_image_from_file(splash_t* splash, splash_image_t* image)
243{
244 png_struct *png;
245 png_info *info;
246 png_uint_32 width, height, pitch, row;
247 int bpp, color_type, interlace_mthd;
248 png_byte **rows;
249
250 if (image->fp != NULL)
251 return 1;
252
253 image->fp = fopen(image->filename, "rb");
254 if (image->fp == NULL)
255 return 1;
256
257
258 png = png_create_read_struct(PNG_LIBPNG_VER_STRING, NULL, NULL, NULL);
259 info = png_create_info_struct(png);
260
261 if (info == NULL)
262 return 1;
263
264 png_init_io(png, image->fp);
265
266 if (setjmp(png_jmpbuf(png)) != 0) {
267 fclose(image->fp);
268 return 1;
269 }
270
271 png_read_info(png, info);
272 png_get_IHDR(png, info, &width, &height, &bpp, &color_type,
273 &interlace_mthd, NULL, NULL);
274
275 pitch = 4 * width;
276
277 switch (color_type)
278 {
279 case PNG_COLOR_TYPE_PALETTE:
280 png_set_palette_to_rgb(png);
281 break;
282
283 case PNG_COLOR_TYPE_GRAY:
284 case PNG_COLOR_TYPE_GRAY_ALPHA:
285 png_set_gray_to_rgb(png);
286 }
287
288 if (png_get_valid(png, info, PNG_INFO_tRNS))
289 png_set_tRNS_to_alpha(png);
290
291 switch (bpp)
292 {
293 default:
294 if (bpp < 8)
295 png_set_packing(png);
296 break;
297 case 16:
298 png_set_strip_16(png);
299 break;
300 }
301
302 if (interlace_mthd != PNG_INTERLACE_NONE)
303 png_set_interlace_handling(png);
304
305 png_set_filler(png, 0xff, PNG_FILLER_AFTER);
306
307 png_set_read_user_transform_fn(png, splash_rgb);
308 png_read_update_info(png, info);
309
310 rows = malloc(height * sizeof(*rows));
311 image->layout.address = malloc(height * pitch);
312
313 for (row = 0; row < height; row++) {
314 rows[row] = &image->layout.as_png_bytes[row * pitch];
315 }
316
317 png_read_image(png, rows);
318 free(rows);
319
320 png_read_end(png, info);
321 fclose(image->fp);
322 png_destroy_read_struct(&png, &info, NULL);
323
324 image->width = width;
325 image->height = height;
326 image->pitch = pitch;
327
328 return 0;
329}
330
331static
332int splash_image_show(splash_t *splash,
333 splash_image_t* image,
334 uint32_t *video_buffer)
335{
336 uint32_t j;
337 uint32_t startx, starty;
338 buffer_properties_t *bp;
339 uint32_t *buffer;
340
341
342 bp = video_get_buffer_properties(splash->video);
343 startx = (bp->width - image->width) / 2;
344 starty = (bp->height - image->height) / 2;
345
346 buffer = video_lock(splash->video);
347
348 if (buffer != NULL) {
349 for (j = starty; j < starty + image->height; j++) {
350 memcpy(buffer + j * bp->pitch/4 + startx,
351 image->layout.address + (j - starty)*image->pitch, image->pitch);
352 }
353 }
354
355 video_unlock(splash->video);
356 return 0;
357}
358
359void splash_set_dbus(splash_t* splash, dbus_t* dbus)
360{
361 splash->dbus = dbus;
362}
363
364void splash_set_devmode(splash_t* splash)
365{
366 splash->devmode = true;
367}
368
369static
370void splash_rgb(png_struct *png, png_row_info *row_info, png_byte *data)
371{
372 unsigned int i;
373
374 for (i = 0; i < row_info->rowbytes; i+= 4) {
375 uint8_t r, g, b, a;
376 uint32_t pixel;
377
378 r = data[i + 0];
379 g = data[i + 1];
380 b = data[i + 2];
381 a = data[i + 3];
382 pixel = (a << 24) | (r << 16) | (g << 8) | b;
383 memcpy(data + i, &pixel, sizeof(pixel));
384 }
385}