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