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
17 #include <sys/types.h>
18 #include <sys/select.h>
20 #include <sys/ioctl.h>
24 #include <linux/limits.h>
25 #include <linux/input.h>
30 static int fd_fb0
= -1;
34 int fd_tty
[MAX_NR_CONSOLES
];
38 static void obj_free(obj
*o
)
46 if (o
->type
== o_box
) {
49 free(container_of(b
->inter
));
55 else if (o
->type
== o_anim
) {
67 else if (o
->type
== o_text
) {
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
)
95 fb
= con2fb
.framebuffer
;
101 fd_fb
= fb_open(fb
, create
);
105 if (fb_get_settings(fd_fb
))
113 fb_mem
= fb_mmap(fd_fb
);
120 static void fb_cmap_directcolor_set(int fd
)
125 len
= min(min(fbd
.var
.red
.length
, fbd
.var
.green
.length
), fbd
.var
.blue
.length
);
128 cmap
.len
= (1 << len
);
130 cmap
.red
= malloc(2 * 256 * 3);
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
);
145 static void detect_endianess(sendian_t
*end
)
149 if (*(u8
*)&t
== 0x22) {
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
);
170 if (fb_init(config
.tty_s
, create
))
174 if (TTF_Init() < 0) {
175 iprint(MSG_ERROR
, "Couldn't initialize TTF.\n");
182 #ifndef TARGET_KERNEL
184 #define INPUT_BASE "/sys/class/input"
187 static int ev_fd
[INPUT_MAX
];
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
;
206 int ev
, num
, err
= -1;
208 input_cls
= opendir(INPUT_BASE
);
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
];
240 * Clean up after fbsplashr_input_init().
242 void fbsplashr_input_cleanup()
246 for (i
= 0; i
< ev_cnt
; i
++) {
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
;
276 for (i
= 0; i
< ev_cnt
; i
++) {
277 FD_SET(ev_fd
[i
], &fds
);
282 err
= select(ev_highest
+ 1, &fds
, NULL
, NULL
, NULL
);
284 err
= select(ev_highest
+ 1, &fds
, NULL
, NULL
, &timeout
);
291 } else if (err
== 0) {
295 /* We only process the first event device for which there is
297 for (i
= 0; i
< ev_cnt
; i
++) {
298 if (FD_ISSET(ev_fd
[i
], &fds
)) {
303 while ((err
= read(ev_fd
[i
], &ev
, sizeof(ev
))) == sizeof(ev
) || errno
== EINTR
) {
304 if (ev
.type
== EV_KEY
&& ev
.value
== 0)
315 #endif /* TARGET_KERNEL */
318 * Cleanup after fbsplashr_init() and subsequent calls to any
319 * libsplashrender routines.
321 void fbsplashr_cleanup()
331 for (i
= 0; i
< MAX_NR_CONSOLES
; i
++) {
332 if (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
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)
360 if (!(theme
->modes
& FBSPL_MODE_SILENT
))
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
);
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
)) {
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);
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
);
404 paint_img(theme
, fb_mem
, theme
->bgbuf
);
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()
428 st
= calloc(1, sizeof(stheme_t
));
432 st
->modes
= FBSPL_MODE_SILENT
| FBSPL_MODE_VERBOSE
;
433 st
->xres
= fbd
.var
.xres
;
434 st
->yres
= fbd
.var
.yres
;
439 fbsplash_get_res(config
.theme
, &st
->xres
, &st
->yres
);
440 if (st
->xres
== 0 || st
->yres
== 0)
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;
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. */
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
;
466 load_images(st
, 'v');
468 if ((config
.reqmode
& FBSPL_MODE_SILENT
) &&
469 cfg_check_sanity(st
, 's'))
470 st
->modes
&= ~FBSPL_MODE_SILENT
;
472 load_images(st
, 's');
477 /* Initialize the first frame of all animations. */
478 for (i
= st
->anims
.head
; i
!= NULL
; i
= i
->next
) {
481 obj
*co
= container_of(ca
);
484 (ca
->flags
& F_ANIM_METHOD_MASK
) == F_ANIM_PROPORTIONAL
)
487 mng
= mng_get_userdata(ca
->mng
);
488 if (!mng
->displayed_first
)
489 anim_render_canvas(ca
);
498 st
->bgbuf
= malloc(st
->xres
* st
->yres
* fbd
.bytespp
);
500 /* Initialize the bouding rectangles. */
503 /* Initialize the message log. */
504 list_init(st
->msglog
);
506 for (i
= st
->objs
.head
; i
!= NULL
; i
= i
->next
) {
508 if (co
->visible
&& co
->blendin
> 0) {
510 obj_visibility_set(st
, co
, true);
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
)
538 if (theme
->silentpic
)
539 free(theme
->silentpic
);
541 if (theme
->silentpic256
)
542 free(theme
->silentpic256
);
544 for (i
= theme
->objs
.head
; i
!= NULL
; ) {
552 for (i
= theme
->icons
.head
; i
!= NULL
; ) {
553 icon_img
*ii
= (icon_img
*) i
->p
;
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
);
585 /* Free the message log. */
586 list_free(theme
->msglog
, true);
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
)
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
];
619 w
.c_lflag
&= ~(ICANON
|ECHO
);
622 tcsetattr(fd
, TCSANOW
, &w
);
623 vt_cursor_disable(fd
);
627 write(fd
, "\e[H\e[2J", 7);
634 * Restore the silent tty to its previous settings after a call to fbsplashr_tty_silent_init().
636 int fbsplashr_tty_silent_cleanup()
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
];
649 w
.c_lflag
&= (ICANON
|ECHO
);
652 tcsetattr(fd
, TCSANOW
, &w
);
653 vt_cursor_enable(fd
);
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
)
668 if (fd_tty
[tty
] == -1)
669 fd_tty
[tty
] = tty_open(tty
);
671 if (fb_init(tty
, false))
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
;
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
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
);
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
);
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
);
733 o
= theme
->objs
.tail
->p
;
740 t
->val
= strdup(config
.message
);
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
)
755 if (progress
> FBSPL_PROGRESS_MAX
)
756 progress
= FBSPL_PROGRESS_MAX
;
758 config
.progress
= progress
;
759 invalidate_progress(theme
);