core: add support for the autoverbose feature
[fbsplash.git] / core / src / libfbsplashrender.c
blob4aee0b7b5ee43f5d5619c10583b76ba48eff7458
1 /*
2 * libsplashrender.c - A library of useful splash rendering routines to be used
3 * in programs implementing support for the splash theme files.
5 * Copyright (c) 2007, Michal Januszewski <spock@gentoo.org>
7 * This file is subject to the terms and conditions of the GNU General Public
8 * License v2. See the file COPYING in the main directory of this archive for
9 * more details.
11 #include <stdlib.h>
12 #include <stdio.h>
13 #include <string.h>
14 #include <unistd.h>
15 #include <dirent.h>
16 #include <errno.h>
17 #include <sys/types.h>
18 #include <sys/select.h>
19 #include <sys/stat.h>
20 #include <sys/ioctl.h>
21 #include <sys/mman.h>
22 #include <fcntl.h>
23 #include <termios.h>
24 #include <linux/limits.h>
25 #include <linux/input.h>
27 #include "common.h"
28 #include "render.h"
30 static int fd_fb0 = -1;
31 static int fb = -1;
32 int fd_fb = -1;
33 u8 *fb_mem = NULL;
34 int fd_tty[MAX_NR_CONSOLES];
35 struct fb_data fbd;
36 sendian_t endianess;
38 static void obj_free(obj *o)
40 if (!o)
41 return;
43 if (o->bgcache)
44 free(o->bgcache);
46 if (o->type == o_box) {
47 box *b = o->p;
48 if (b->inter)
49 free(container_of(b->inter));
50 if (b->curr)
51 free(b->curr);
54 #if WANT_MNG
55 else if (o->type == o_anim) {
56 anim *a = o->p;
57 if (a->filename) {
58 free(a->filename);
61 if (a->mng) {
62 mng_done(a->mng);
65 #endif
66 #if WANT_TTF
67 else if (o->type == o_text) {
68 text *t = o->p;
69 if (t->cache) {
70 free(t->cache);
73 if (t->val) {
74 free(t->val);
77 #endif
79 free(o);
80 return;
83 static int fb_init(int tty, bool create)
85 struct fb_con2fbmap con2fb;
87 con2fb.console = config.tty_s;
88 con2fb.framebuffer = 0;
90 ioctl(fd_fb0, FBIOGET_CON2FBMAP, &con2fb);
92 if (con2fb.framebuffer == fb)
93 return 0;
95 fb = con2fb.framebuffer;
96 if (fd_fb != -1) {
97 close(fd_fb);
98 fd_fb = -1;
101 fd_fb = fb_open(fb, create);
102 if (fd_fb < 0)
103 return -1;
105 if (fb_get_settings(fd_fb))
106 return -1;
108 if (fb_mem) {
109 fb_unmap(fb_mem);
110 fb_mem = NULL;
113 fb_mem = fb_mmap(fd_fb);
114 if (!fb_mem)
115 return -1;
117 return 0;
120 static void fb_cmap_directcolor_set(int fd)
122 int len, i;
123 struct fb_cmap cmap;
125 len = min(min(fbd.var.red.length, fbd.var.green.length), fbd.var.blue.length);
127 cmap.start = 0;
128 cmap.len = (1 << len);
129 cmap.transp = NULL;
130 cmap.red = malloc(2 * 256 * 3);
131 if (!cmap.red)
132 return;
134 cmap.green = cmap.red + 256;
135 cmap.blue = cmap.green + 256;
137 for (i = 0; i < cmap.len; i++) {
138 cmap.red[i] = cmap.green[i] = cmap.blue[i] = (0xffff * i)/(cmap.len-1);
141 ioctl(fd, FBIOPUTCMAP, &cmap);
142 free(cmap.red);
145 static void detect_endianess(sendian_t *end)
147 u16 t = 0x1122;
149 if (*(u8*)&t == 0x22) {
150 *end = little;
151 } else {
152 *end = big;
157 * Init the splashrender library.
159 * @param create Create device nodes if they're missing.
161 int fbsplashr_init(bool create)
163 detect_endianess(&endianess);
164 memset(fd_tty, -1, sizeof(fd_tty));
166 fd_fb0 = fb_open(0, create);
167 if (fd_fb0 < 0)
168 return -1;
170 if (fb_init(config.tty_s, create))
171 return -2;
173 #if WANT_TTF
174 if (TTF_Init() < 0) {
175 iprint(MSG_ERROR, "Couldn't initialize TTF.\n");
176 return -3;
178 #endif
179 return 0;
182 #ifndef TARGET_KERNEL
184 #define INPUT_BASE "/sys/class/input"
185 #define INPUT_MAX 16
187 static int ev_fd[INPUT_MAX];
188 static int ev_cnt;
189 static int ev_highest;
192 * Initialize the fbsplash input system.
194 * The fbsplash input uses the Linux event device framework. It will not
195 * work if evdev isn't built into the kernel or compiled as a module and
196 * loaded prior to calling this function.
198 * @return 0 on success, a negative value otherwise.
200 int fbsplashr_input_init()
202 struct dirent *entry;
203 char buf[PATH_MAX];
204 FILE *fp;
205 DIR *input_cls;
206 int ev, num, err = -1;
208 input_cls = opendir(INPUT_BASE);
209 if (input_cls) {
210 while ((entry = readdir(input_cls)) && ev_cnt < INPUT_MAX) {
212 /* Look for all input* files */
213 if (!strncmp(entry->d_name, "input", 5)) {
214 snprintf(buf, PATH_MAX, INPUT_BASE "/%s/capabilities/ev", entry->d_name);
215 fp = fopen(buf, "r");
216 fscanf(fp, "%x", &ev);
218 /* Check if the device supports KEY and REP. It appears that all keyboard
219 * drivers have at least these two capabilities. */
220 if ((ev & 0x100002) == 0x100002) {
221 sscanf(entry->d_name + 5, "%d", &num);
222 snprintf(buf, PATH_MAX, "/dev/input/event%d", num);
223 ev_fd[ev_cnt] = open(buf, O_RDWR | O_NONBLOCK);
224 if (ev_fd[ev_cnt] >= 0) {
225 ev_highest = ev_fd[ev_cnt];
226 ev_cnt++;
227 err = 0;
230 fclose(fp);
234 closedir(input_cls);
236 return err;
240 * Clean up after fbsplashr_input_init().
242 void fbsplashr_input_cleanup()
244 int i;
246 for (i = 0; i < ev_cnt; i++) {
247 close(ev_fd[i]);
250 ev_highest = 0;
251 ev_cnt = 0;
253 return;
257 * Wait for a keypress or check whether a key has been pressed.
259 * @param block If true, this function will block.
260 * @return Key code as defined in linux/input.h, 0 if no key has been
261 * pressed or an error occured.
263 unsigned short fbsplashr_input_getkey(bool block)
265 struct timeval timeout = {0, 0};
266 struct input_event ev;
267 unsigned char a;
268 int err, i;
269 fd_set fds;
271 if (!ev_cnt)
272 return 0;
274 FD_ZERO(&fds);
276 for (i = 0; i < ev_cnt; i++) {
277 FD_SET(ev_fd[i], &fds);
280 poll:
281 if (block) {
282 err = select(ev_highest + 1, &fds, NULL, NULL, NULL);
283 } else {
284 err = select(ev_highest + 1, &fds, NULL, NULL, &timeout);
286 if (err < 0) {
287 if (errno == EINTR)
288 goto poll;
289 else
290 return 0;
291 } else if (err == 0) {
292 return 0;
295 /* We only process the first event device for which there is
296 * data available. */
297 for (i = 0; i < ev_cnt; i++) {
298 if (FD_ISSET(ev_fd[i], &fds)) {
299 break;
303 while ((err = read(ev_fd[i], &ev, sizeof(ev))) == sizeof(ev) || errno == EINTR) {
304 if (ev.type == EV_KEY && ev.value == 0)
305 return ev.code;
306 errno = 0;
309 if (block)
310 goto poll;
312 return 0;
315 #endif /* TARGET_KERNEL */
318 * Cleanup after fbsplashr_init() and subsequent calls to any
319 * libsplashrender routines.
321 void fbsplashr_cleanup()
323 int i;
325 #if WANT_TTF
326 TTF_Quit();
327 #endif
329 fb_unmap(fb_mem);
331 for (i = 0; i < MAX_NR_CONSOLES; i++) {
332 if (fd_tty[i] != -1) {
333 close(fd_tty[i]);
334 fd_tty[i] = -1;
340 * Render the splash screen to a buffer.
342 * @param theme Theme for which the screen is to be rendered.
343 * @param buffer Buffer when the rendered image is to be stored. The image
344 * will be in the same format as that used by the fb device
345 * which handles the silent splash tty. The buffer should be
346 * long enough to contain the whole image (xres * yres * bytes per pixel)
347 * where xres and yres are the horizontal and vertical resolution
348 * of the current theme or of the video mode active on the framebuffer
349 * device.
350 * @param repaint The whole screen is rendered if true, only updated parts otherwise.
352 * @return 0 on success, a negative value otherwise.
354 int fbsplashr_render_buf(struct fbspl_theme *theme, void *buffer, bool repaint)
356 /* FIXME: 8bpp modes aren't supported yet */
357 if (fbd.var.bits_per_pixel == 8)
358 return -2;
360 if (!(theme->modes & FBSPL_MODE_SILENT))
361 return -1;
363 if (repaint) {
364 memcpy(buffer, theme->silent_img.data, theme->xres * theme->yres * fbd.bytespp);
365 invalidate_all(theme);
368 render_objs(theme, buffer, FBSPL_MODE_SILENT, repaint);
369 return 0;
373 * Render the splash directly to the screen.
375 * @param theme Theme for which the screen is to be rendered.
376 * @param repaint The whole screeen is rendered if true, only updated parts otherwise.
377 * @param bgnd Return immediately if true, wait for all effects to be rendered otherwise.
378 * Effects such as fadein/fadeout take some time to be fully displayed. If this
379 * parameter is set to true, they will be rendered from a separate, forked process.
380 * @param effects Indicates which effects are to be used to display the image. Valid values
381 * are constants prefixed with SPL_EFF_.
383 * @return 0 on success, a negative value otherwise.
385 int fbsplashr_render_screen(struct fbspl_theme *theme, bool repaint, bool bgnd, char effects)
387 if (!fbsplashr_render_buf(theme, theme->bgbuf, repaint)) {
388 if (repaint) {
389 if (effects & FBSPL_EFF_FADEIN) {
390 fade(theme, fb_mem, theme->bgbuf, theme->silent_img.cmap, bgnd ? 1 : 0, fd_fb, 0);
391 } else if (effects & FBSPL_EFF_FADEOUT) {
392 fade(theme, fb_mem, theme->bgbuf, theme->silent_img.cmap, bgnd ? 1 : 0, fd_fb, 1);
393 } else {
394 if (theme->silent_img.cmap.red)
395 ioctl(fd_fb, FBIOPUTCMAP, &theme->silent_img.cmap);
397 /* Update CMAP if we're in a DIRECTCOLOR mode. */
398 if (fbd.fix.visual == FB_VISUAL_DIRECTCOLOR)
399 fb_cmap_directcolor_set(fd_fb);
401 put_img(theme, fb_mem, theme->bgbuf);
403 } else {
404 paint_img(theme, fb_mem, theme->bgbuf);
406 return 0;
407 } else {
408 return -1;
413 * Load a splash theme specified by config.theme.
415 * @return A pointer to a theme descriptor, which is then passed to any
416 * libfbsplashrender functions.
418 struct fbspl_theme *fbsplashr_theme_load()
420 char buf[512];
421 stheme_t *st;
422 item *i;
423 int j;
425 if (!config.theme)
426 return NULL;
428 st = calloc(1, sizeof(stheme_t));
429 if (!st)
430 return NULL;
432 st->modes = FBSPL_MODE_SILENT | FBSPL_MODE_VERBOSE;
433 st->xres = fbd.var.xres;
434 st->yres = fbd.var.yres;
435 st->log_lines = 5;
436 st->log_cols = 80;
437 st->log_cnt = 0;
439 fbsplash_get_res(config.theme, &st->xres, &st->yres);
440 if (st->xres == 0 || st->yres == 0)
441 return NULL;
443 snprintf(buf, 512, FBSPL_THEME_DIR "/%s/%dx%d.cfg", config.theme, st->xres, st->yres);
445 st->xmarg = (fbd.var.xres - st->xres) / 2;
446 st->ymarg = (fbd.var.yres - st->yres) / 2;
448 list_init(st->blit);
449 list_init(st->objs);
450 list_init(st->fxobjs);
451 list_init(st->textbox);
452 list_init(st->anims);
453 list_init(st->icons);
454 list_init(st->fonts);
455 list_init(st->rects);
457 /* Parse the config file. */
458 parse_cfg(buf, st);
460 /* Check for config file sanity for the given splash mode and
461 * load background images and icons. */
462 if ((config.reqmode & FBSPL_MODE_VERBOSE) &&
463 cfg_check_sanity(st, 'v'))
464 st->modes &= ~FBSPL_MODE_VERBOSE;
465 else
466 load_images(st, 'v');
468 if ((config.reqmode & FBSPL_MODE_SILENT) &&
469 cfg_check_sanity(st, 's'))
470 st->modes &= ~FBSPL_MODE_SILENT;
471 else
472 load_images(st, 's');
474 #if WANT_MNG
475 load_anims(st);
477 /* Initialize the first frame of all animations. */
478 for (i = st->anims.head; i != NULL; i = i->next) {
479 mng_anim *mng;
480 anim *ca = i->p;
481 obj *co = container_of(ca);
483 if (!co->visible ||
484 (ca->flags & F_ANIM_METHOD_MASK) == F_ANIM_PROPORTIONAL)
485 continue;
487 mng = mng_get_userdata(ca->mng);
488 if (!mng->displayed_first)
489 anim_render_canvas(ca);
491 #endif
493 #if WANT_TTF
494 load_fonts(st);
495 #endif
497 invalidate_all(st);
498 st->bgbuf = malloc(st->xres * st->yres * fbd.bytespp);
500 /* Initialize the bouding rectangles. */
501 bnd_init(st);
503 /* Initialize the message log. */
504 list_init(st->msglog);
506 for (i = st->objs.head; i != NULL; i = i->next) {
507 obj *co = i->p;
508 if (co->visible && co->blendin > 0) {
509 co->visible = false;
510 obj_visibility_set(st, co, true);
514 return st;
518 * Free a theme descriptor struct returned by fbsplashr_theme_load().
520 * @param theme Theme descriptor to be freed.
522 void fbsplashr_theme_free(struct fbspl_theme *theme)
524 item *i, *j;
525 int k;
527 if (!theme)
528 return;
530 free(theme->bgbuf);
532 if (theme->pic)
533 free(theme->pic);
535 if (theme->pic256)
536 free(theme->pic256);
538 if (theme->silentpic)
539 free(theme->silentpic);
541 if (theme->silentpic256)
542 free(theme->silentpic256);
544 for (i = theme->objs.head ; i != NULL; ) {
545 obj *o = (obj*)i->p;
546 j = i->next;
547 obj_free(o);
548 free(i);
549 i = j;
552 for (i = theme->icons.head; i != NULL; ) {
553 icon_img *ii = (icon_img*) i->p;
554 j = i->next;
555 if (ii->filename)
556 free(ii->filename);
557 if (ii->picbuf)
558 free(ii->picbuf);
559 free(ii);
560 free(i);
561 i = j;
564 list_free(theme->fxobjs, false);
565 list_free(theme->textbox, false);
566 list_free(theme->anims, false);
567 list_free(theme->rects, true);
568 list_free(theme->blit, true);
570 /* Free background pictures */
571 if (theme->verbose_img.data)
572 free((u8*)theme->verbose_img.data);
573 if (theme->verbose_img.cmap.red)
574 free(theme->verbose_img.cmap.red);
576 if (theme->silent_img.data)
577 free((u8*)theme->silent_img.data);
578 if (theme->silent_img.cmap.red)
579 free(theme->silent_img.cmap.red);
581 #if WANT_TTF
582 free_fonts(theme);
583 #endif
585 /* Free the message log. */
586 list_free(theme->msglog, true);
588 free(theme);
591 static void vt_cursor_disable(int fd)
593 write(fd, "\e[?25l\e[?1c", 11);
596 static void vt_cursor_enable(int fd)
598 write(fd, "\e[?25h\e[?0c", 11);
602 * Prepare the silent tty (config.tty_s) for displaying the silent splash screen.
604 * @param clear If true, the silent tty will be cleared from any text it might contain.
606 int fbsplashr_tty_silent_init(bool clear)
608 struct termios w;
609 int fd;
611 if (fd_tty[config.tty_s] == -1)
612 fd_tty[config.tty_s] = tty_open(config.tty_s);
614 fd = fd_tty[config.tty_s];
615 if (!fd)
616 return -1;
618 tcgetattr(fd, &w);
619 w.c_lflag &= ~(ICANON|ECHO);
620 w.c_cc[VTIME] = 0;
621 w.c_cc[VMIN] = 1;
622 tcsetattr(fd, TCSANOW, &w);
623 vt_cursor_disable(fd);
625 if (clear) {
626 /* Clear display */
627 write(fd, "\e[H\e[2J", 7);
630 return 0;
634 * Restore the silent tty to its previous settings after a call to fbsplashr_tty_silent_init().
636 int fbsplashr_tty_silent_cleanup()
638 struct termios w;
639 int fd;
641 if (fd_tty[config.tty_s] == -1)
642 fd_tty[config.tty_s] = tty_open(config.tty_s);
644 fd = fd_tty[config.tty_s];
645 if (!fd)
646 return -1;
648 tcgetattr(fd, &w);
649 w.c_lflag &= (ICANON|ECHO);
650 w.c_cc[VTIME] = 0;
651 w.c_cc[VMIN] = 1;
652 tcsetattr(fd, TCSANOW, &w);
653 vt_cursor_enable(fd);
655 return 0;
659 * Set a new silent tty.
661 * @param tty The new silent tty.
663 int fbsplashr_tty_silent_set(int tty)
665 if (tty < 0 || tty > MAX_NR_CONSOLES)
666 return -1;
668 if (fd_tty[tty] == -1)
669 fd_tty[tty] = tty_open(tty);
671 if (fb_init(tty, false))
672 return -1;
674 config.tty_s = tty;
676 return 0;
680 * Update all internal settings related to the silent tty.
682 * To be called when switching to the silent tty.
684 * @return 0 if all settings have been updated, 1 if the theme has to be reloaded.
686 int fbsplashr_tty_silent_update()
688 struct fb_var_screeninfo old_var;
689 struct fb_fix_screeninfo old_fix;
690 int ret = 0;
692 memcpy(&old_fix, &fbd.fix, sizeof(old_fix));
693 memcpy(&old_var, &fbd.var, sizeof(old_var));
695 fb_get_settings(fd_fb);
697 old_var.yoffset = fbd.var.yoffset;
698 old_var.xoffset = fbd.var.xoffset;
701 * Has the video mode changed? If it has, we'll have to reload
702 * the theme.
704 if (memcmp(&fbd.fix, &old_fix, sizeof(struct fb_fix_screeninfo)) ||
705 memcmp(&fbd.var, &old_var, sizeof(struct fb_var_screeninfo))) {
707 munmap(fb_mem, old_fix.line_length * old_var.yres);
708 fb_mem = fb_mmap(fd_fb);
709 ret = 1;
712 /* Update CMAP if we're in a DIRECTCOLOR mode. */
713 if (fbd.fix.visual == FB_VISUAL_DIRECTCOLOR)
714 fb_cmap_directcolor_set(fd_fb);
716 return ret;
720 * Set a new main message.
722 * @param theme Theme descriptor.
723 * @param msg The new main message.
725 void fbsplashr_message_set(struct fbspl_theme *theme, const char *msg)
727 fbsplash_acc_message_set(msg);
729 #if WANT_TTF
730 obj *o;
731 text *t;
733 o = theme->objs.tail->p;
734 t = o->p;
736 if (t->val)
737 free(t->val);
739 o->invalid = true;
740 t->val = strdup(config.message);
741 #endif
745 * Set a new progress value.
747 * @param theme Theme descriptor.
748 * @param progress The new progress value.
750 void fbsplashr_progress_set(struct fbspl_theme *theme, int progress)
752 if (progress < 0)
753 progress = 0;
755 if (progress > FBSPL_PROGRESS_MAX)
756 progress = FBSPL_PROGRESS_MAX;
758 config.progress = progress;
759 invalidate_progress(theme);