blob: dd4c131101762b18771de5d198866d478163cca1 [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
David Sodmanbf3f2842014-11-12 08:26:58 -080053static void splash_rgb(png_struct *png, png_row_info *row_info, png_byte *data)
54{
55 unsigned int i;
David Sodmanbbcb0522014-09-19 10:34:07 -070056
David Sodmanbf3f2842014-11-12 08:26:58 -080057 for (i = 0; i < row_info->rowbytes; i+= 4) {
58 uint8_t r, g, b, a;
59 uint32_t pixel;
60
61 r = data[i + 0];
62 g = data[i + 1];
63 b = data[i + 2];
64 a = data[i + 3];
65 pixel = (a << 24) | (r << 16) | (g << 8) | b;
66 memcpy(data + i, &pixel, sizeof(pixel));
67 }
68}
69
70static int splash_load_image_from_file(splash_t* splash, splash_image_t* image)
71{
72 png_struct *png;
73 png_info *info;
74 png_uint_32 width, height, pitch, row;
75 int bpp, color_type, interlace_mthd;
76 png_byte **rows;
77
78 if (image->fp != NULL)
79 return 1;
80
81 image->fp = fopen(image->filename, "rb");
82 if (image->fp == NULL)
83 return 1;
84
85 png = png_create_read_struct(PNG_LIBPNG_VER_STRING, NULL, NULL, NULL);
86 info = png_create_info_struct(png);
87
88 if (info == NULL)
89 return 1;
90
91 png_init_io(png, image->fp);
92
93 if (setjmp(png_jmpbuf(png)) != 0) {
94 fclose(image->fp);
95 return 1;
96 }
97
98 png_read_info(png, info);
99 png_get_IHDR(png, info, &width, &height, &bpp, &color_type,
100 &interlace_mthd, NULL, NULL);
101
102 pitch = 4 * width;
103
104 switch (color_type)
105 {
106 case PNG_COLOR_TYPE_PALETTE:
107 png_set_palette_to_rgb(png);
108 break;
109
110 case PNG_COLOR_TYPE_GRAY:
111 case PNG_COLOR_TYPE_GRAY_ALPHA:
112 png_set_gray_to_rgb(png);
113 }
114
115 if (png_get_valid(png, info, PNG_INFO_tRNS))
116 png_set_tRNS_to_alpha(png);
117
118 switch (bpp)
119 {
120 default:
121 if (bpp < 8)
122 png_set_packing(png);
123 break;
124 case 16:
125 png_set_strip_16(png);
126 break;
127 }
128
129 if (interlace_mthd != PNG_INTERLACE_NONE)
130 png_set_interlace_handling(png);
131
132 png_set_filler(png, 0xff, PNG_FILLER_AFTER);
133
134 png_set_read_user_transform_fn(png, splash_rgb);
135 png_read_update_info(png, info);
136
137 rows = malloc(height * sizeof(*rows));
138 image->layout.address = malloc(height * pitch);
139
140 for (row = 0; row < height; row++) {
141 rows[row] = &image->layout.as_png_bytes[row * pitch];
142 }
143
144 png_read_image(png, rows);
145 free(rows);
146
147 png_read_end(png, info);
148 fclose(image->fp);
149 png_destroy_read_struct(&png, &info, NULL);
150
151 image->width = width;
152 image->height = height;
153 image->pitch = pitch;
154
155 return 0;
156}
157
158static int splash_image_show(splash_t *splash,
159 splash_image_t* image,
160 uint32_t *video_buffer)
161{
162 uint32_t j;
163 uint32_t startx, starty;
164 buffer_properties_t *bp;
165 uint32_t *buffer;
166
167
168 bp = video_get_buffer_properties(splash->video);
169 startx = (bp->width - image->width) / 2;
170 starty = (bp->height - image->height) / 2;
171
172 buffer = video_lock(splash->video);
173
174 if (buffer != NULL) {
175 for (j = starty; j < starty + image->height; j++) {
176 memcpy(buffer + j * bp->pitch/4 + startx,
177 image->layout.address + (j - starty)*image->pitch, image->pitch);
178 }
179 }
180
181 video_unlock(splash->video);
182 return 0;
183}
184
185splash_t* splash_init()
David Sodmanbbcb0522014-09-19 10:34:07 -0700186{
187 splash_t* splash;
David Sodman73e82272014-11-17 10:11:47 -0800188 FILE *cookie_fp;
David Sodmanbbcb0522014-09-19 10:34:07 -0700189
190 splash = (splash_t*)calloc(1, sizeof(splash_t));
191 if (splash == NULL)
192 return NULL;
193
194 splash->num_images = 0;
David Sodmanbf3f2842014-11-12 08:26:58 -0800195 splash->video = video_init();
David Sodmanbbcb0522014-09-19 10:34:07 -0700196
David Sodman73e82272014-11-17 10:11:47 -0800197 cookie_fp = fopen("/tmp/display_info.bin", "wb");
198 if (cookie_fp) {
199 fwrite(&splash->video->internal_panel, sizeof(char), 1, cookie_fp);
200 fwrite(splash->video->edid, EDID_SIZE, 1, cookie_fp);
201 fclose(cookie_fp);
202 }
203
David Sodmanbbcb0522014-09-19 10:34:07 -0700204 return splash;
205}
206
207int splash_destroy(splash_t* splash)
208{
209 return 0;
210}
211
212int splash_set_frame_rate(splash_t *splash, int32_t rate)
213{
214 if (rate <= 0 || rate > 120)
215 return 1;
216
217 splash->frame_interval = rate;
218 return 0;
219}
220
221int splash_set_clear(splash_t *splash, int32_t clear_color)
222{
223 splash->clear = clear_color;
224 return 0;
225}
226
227int splash_add_image(splash_t* splash, const char* filename)
228{
229 if (splash->num_images >= MAX_SPLASH_IMAGES)
230 return 1;
231
232 strcpy(splash->images[splash->num_images].filename, filename);
233 splash->num_images++;
234 return 0;
235}
236
237static void
238frecon_dbus_path_message_func(dbus_t* dbus, void* user_data)
239{
240 splash_t* splash = (splash_t*)user_data;
241
242 if (!splash->devmode)
243 exit(EXIT_SUCCESS);
244
245 dbus_stop_wait(dbus);
David Sodmanbf3f2842014-11-12 08:26:58 -0800246 video_close(splash->video);
David Sodmanbbcb0522014-09-19 10:34:07 -0700247}
248
249static void splash_clear_screen(splash_t *splash, uint32_t *video_buffer)
250{
251 int i,j;
252 buffer_properties_t *bp;
253
254 video_setmode(splash->video);
255
David Sodman4371e912014-12-08 14:14:35 -0800256 /* After the mode is set, there is nothing splash
257 * needs master for
258 */
259 video_release(splash->video);
260
David Sodmanbbcb0522014-09-19 10:34:07 -0700261 bp = video_get_buffer_properties(splash->video);
262
263 for (j = 0; j < bp->height; j++) {
264 for (i = 0; i < bp->width; i++) {
265 (video_buffer + bp->pitch/4 * j)[i] = splash->clear;
266 }
267 }
268}
269
270int splash_run(splash_t* splash, dbus_t** dbus)
271{
272 int i;
273 uint32_t* video_buffer;
274 int status;
275 bool db_status;
276 int64_t last_show_ms;
277 int64_t now_ms;
278 int64_t sleep_ms;
279 struct timespec sleep_spec;
280 int fd;
281 int num_written;
David Sodman3430aae2014-11-10 08:15:01 -0800282 int wfm_status;
David Sodmanbbcb0522014-09-19 10:34:07 -0700283
284 status = 0;
285
286 /*
287 * First draw the actual splash screen
288 */
289 video_buffer = video_lock(splash->video);
290 if (video_buffer != NULL) {
291 splash_clear_screen(splash, video_buffer);
292 last_show_ms = -1;
293 for (i = 0; i < splash->num_images; i++) {
294 status = splash_load_image_from_file(splash, &splash->images[i]);
295 if (status != 0) {
296 LOG(WARNING, "splash_load_image_from_file failed: %d\n", status);
297 break;
298 }
299
300 now_ms = get_monotonic_time_ms();
301 if (last_show_ms > 0) {
302 sleep_ms = splash->frame_interval - (now_ms - last_show_ms);
303 if (sleep_ms > 0) {
304 sleep_spec.tv_sec = sleep_ms / MS_PER_SEC;
305 sleep_spec.tv_nsec = (sleep_ms % MS_PER_SEC) * NS_PER_MS;
306 nanosleep(&sleep_spec, NULL);
307 }
308 }
309
310 now_ms = get_monotonic_time_ms();
311
312 status = splash_image_show(splash, &splash->images[i], video_buffer);
313 if (status != 0) {
314 LOG(WARNING, "splash_image_show failed: %d", status);
315 break;
316 }
317 last_show_ms = now_ms;
318 }
319 video_unlock(splash->video);
320
321 /*
322 * Next wait until chrome has drawn on top of the splash. In dev mode,
323 * dbus_wait_for_messages will return when chrome is visible. In
324 * verified mode, the frecon app will exit before dbus_wait_for_messages
325 * returns
326 */
327 do {
328 *dbus = dbus_init();
329 usleep(50000);
330 } while (*dbus == NULL);
331
332 splash_set_dbus(splash, *dbus);
333
334 db_status = dbus_signal_match_handler(*dbus,
335 kLoginPromptVisibleSignal,
336 kSessionManagerServicePath,
337 kSessionManagerInterface,
338 kLoginPromptVisiibleRule,
339 frecon_dbus_path_message_func, splash);
340
David Sodman3430aae2014-11-10 08:15:01 -0800341 if (db_status) {
342 wfm_status = dbus_wait_for_messages(*dbus, MAX_SPLASH_WAITTIME);
343 switch (wfm_status) {
344 case DBUS_STATUS_TIMEOUT:
345 LOG(WARNING, "timed out waiting for messages\n");
346 break;
347 }
348 }
David Sodmanbbcb0522014-09-19 10:34:07 -0700349
350
David Sodmanbf3f2842014-11-12 08:26:58 -0800351 if (splash->devmode) {
David Sodmanbbcb0522014-09-19 10:34:07 -0700352 /*
David Sodmanbf3f2842014-11-12 08:26:58 -0800353 * Now set drm_master_relax so that we can transfer drm_master between
354 * chrome and frecon
David Sodmanbbcb0522014-09-19 10:34:07 -0700355 */
David Sodmanbf3f2842014-11-12 08:26:58 -0800356 fd = open("/sys/kernel/debug/dri/drm_master_relax", O_WRONLY);
357 if (fd != -1) {
358 num_written = write(fd, "Y", 1);
359 close(fd);
360
361 /*
362 * If we can't set drm_master relax, then transitions between chrome
363 * and frecon won't work. No point in having frecon hold any resources
364 */
365 if (num_written != 1) {
366 LOG(ERROR, "Unable to set drm_master_relax");
367 splash->devmode = false;
368 }
369 } else {
370 LOG(ERROR, "unable to open drm_master_relax");
David Sodmanbbcb0522014-09-19 10:34:07 -0700371 }
David Sodmanbbcb0522014-09-19 10:34:07 -0700372 }
373 }
David Sodman003faed2014-11-03 09:02:10 -0800374
David Sodman35d6bd82014-11-24 08:23:00 -0800375
David Sodman003faed2014-11-03 09:02:10 -0800376 (void)dbus_method_call0(splash->dbus,
377 kLibCrosServiceName,
378 kLibCrosServicePath,
379 kLibCrosServiceInterface,
380 kTakeDisplayOwnership);
David Sodmanbbcb0522014-09-19 10:34:07 -0700381 return status;
382}
383
David Sodmanbbcb0522014-09-19 10:34:07 -0700384void splash_set_dbus(splash_t* splash, dbus_t* dbus)
385{
386 splash->dbus = dbus;
387}
388
389void splash_set_devmode(splash_t* splash)
390{
391 splash->devmode = true;
392}