/* ubuntu-text.c - boot splash plugin * * Copyright (C) 2010 Canonical Ltd. * Copyright (C) 2008 Red Hat, Inc. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2, or (at your option) * any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA * 02111-1307, USA. * * Written by: Scott James Remnant * Adam Jackson * Ray Strode */ #include "config.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "ply-trigger.h" #include "ply-boot-splash-plugin.h" #include "ply-buffer.h" #include "ply-event-loop.h" #include "ply-key-file.h" #include "ply-list.h" #include "ply-logger.h" #include "ply-text-display.h" #include "ply-text-progress-bar.h" #include "ply-utils.h" #include #define CLEAR_LINE_SEQUENCE "\033[2K\r\n" #define BACKSPACE "\b\033[0K" typedef enum { PLY_BOOT_SPLASH_DISPLAY_NORMAL, PLY_BOOT_SPLASH_DISPLAY_QUESTION_ENTRY, PLY_BOOT_SPLASH_DISPLAY_PASSWORD_ENTRY } ply_boot_splash_display_type_t; struct _ply_boot_splash_plugin { ply_event_loop_t *loop; ply_boot_splash_mode_t mode; ply_list_t *views; ply_boot_splash_display_type_t state; char *message; uint32_t is_animating : 1; uint32_t black; uint32_t white; uint32_t brown; uint32_t blue; char *title; }; typedef struct { ply_boot_splash_plugin_t *plugin; ply_text_display_t *display; } view_t; static void hide_splash_screen (ply_boot_splash_plugin_t *plugin, ply_event_loop_t *loop); static view_t * view_new (ply_boot_splash_plugin_t *plugin, ply_text_display_t *display) { view_t *view; view = calloc (1, sizeof (view_t)); view->plugin = plugin; view->display = display; return view; } static void view_free (view_t *view) { free (view); } static void view_show_message (view_t *view) { ply_boot_splash_plugin_t *plugin; int display_width, display_height, y; ply_terminal_color_t color; char *message; plugin = view->plugin; display_width = ply_text_display_get_number_of_columns (view->display); display_height = ply_text_display_get_number_of_rows (view->display); if (!strncmp (plugin->message, "keys:", 5)) { message = plugin->message + 5; color = PLY_TERMINAL_COLOR_WHITE; y = display_height - 4; } else { message = plugin->message; color = PLY_TERMINAL_COLOR_BLUE; y = display_height / 2 + 7; } ply_text_display_set_cursor_position (view->display, 0, y); ply_text_display_clear_line (view->display); ply_text_display_set_cursor_position (view->display, (display_width - strlen (message)) / 2, y); ply_text_display_set_foreground_color (view->display, color); ply_text_display_write (view->display, "%s", message); } static void view_show_prompt (view_t *view, const char *prompt, const char *entered_text) { ply_boot_splash_plugin_t *plugin; int display_width, display_height; int i; plugin = view->plugin; display_width = ply_text_display_get_number_of_columns (view->display); display_height = ply_text_display_get_number_of_rows (view->display); ply_text_display_set_cursor_position (view->display, 0, display_height / 2 + 8); ply_text_display_clear_line (view->display); ply_text_display_set_cursor_position (view->display, display_width / 2 - (strlen (prompt)), display_height / 2 + 8); ply_text_display_write (view->display, "%s:%s", prompt, entered_text); ply_text_display_show_cursor (view->display); } static void view_start_animation (view_t *view) { ply_boot_splash_plugin_t *plugin; ply_terminal_t *terminal; assert (view != NULL); plugin = view->plugin; terminal = ply_text_display_get_terminal (view->display); ply_terminal_set_color_hex_value (terminal, PLY_TERMINAL_COLOR_BLACK, plugin->black); ply_terminal_set_color_hex_value (terminal, PLY_TERMINAL_COLOR_WHITE, plugin->white); ply_terminal_set_color_hex_value (terminal, PLY_TERMINAL_COLOR_BROWN, plugin->brown); ply_terminal_set_color_hex_value (terminal, PLY_TERMINAL_COLOR_BLUE, plugin->blue); ply_text_display_set_background_color (view->display, PLY_TERMINAL_COLOR_BLACK); ply_text_display_clear_screen (view->display); ply_text_display_hide_cursor (view->display); } static void view_redraw (view_t *view) { unsigned long screen_width, screen_height; screen_width = ply_text_display_get_number_of_columns (view->display); screen_height = ply_text_display_get_number_of_rows (view->display); ply_text_display_draw_area (view->display, 0, 0, screen_width, screen_height); } static void redraw_views (ply_boot_splash_plugin_t *plugin) { ply_list_node_t *node; node = ply_list_get_first_node (plugin->views); while (node != NULL) { ply_list_node_t *next_node; view_t *view; view = ply_list_node_get_data (node); next_node = ply_list_get_next_node (plugin->views, node); view_redraw (view); node = next_node; } } static void view_hide (view_t *view) { if (view->display != NULL) { ply_terminal_t *terminal; terminal = ply_text_display_get_terminal (view->display); ply_text_display_set_background_color (view->display, PLY_TERMINAL_COLOR_DEFAULT); ply_text_display_clear_screen (view->display); ply_text_display_show_cursor (view->display); ply_terminal_reset_colors (terminal); } } static void hide_views (ply_boot_splash_plugin_t *plugin) { ply_list_node_t *node; node = ply_list_get_first_node (plugin->views); while (node != NULL) { ply_list_node_t *next_node; view_t *view; view = ply_list_node_get_data (node); next_node = ply_list_get_next_node (plugin->views, node); view_hide (view); node = next_node; } } static void pause_views (ply_boot_splash_plugin_t *plugin) { ply_list_node_t *node; node = ply_list_get_first_node (plugin->views); while (node != NULL) { ply_list_node_t *next_node; view_t *view; view = ply_list_node_get_data (node); next_node = ply_list_get_next_node (plugin->views, node); ply_text_display_pause_updates (view->display); node = next_node; } } static void unpause_views (ply_boot_splash_plugin_t *plugin) { ply_list_node_t *node; node = ply_list_get_first_node (plugin->views); while (node != NULL) { ply_list_node_t *next_node; view_t *view; view = ply_list_node_get_data (node); next_node = ply_list_get_next_node (plugin->views, node); ply_text_display_unpause_updates (view->display); node = next_node; } } static ply_boot_splash_plugin_t * create_plugin (ply_key_file_t *key_file) { char *option; ply_boot_splash_plugin_t *plugin; ply_trace ("creating plugin"); plugin = calloc (1, sizeof (ply_boot_splash_plugin_t)); plugin->message = NULL; plugin->views = ply_list_new (); /* Not a pretty API for setting defaults for your config file... */ plugin->black = 0x2c001e; plugin->white = 0xffffff; plugin->brown = 0xff4012; plugin->blue = 0x988592; option = ply_key_file_get_value (key_file, "ubuntu-text", "black"); if (option) sscanf(option, "0x%x", &plugin->black); option = ply_key_file_get_value (key_file, "ubuntu-text", "white"); if (option) sscanf(option, "0x%x", &plugin->white); option = ply_key_file_get_value (key_file, "ubuntu-text", "brown"); if (option) sscanf(option, "0x%x", &plugin->brown); option = ply_key_file_get_value (key_file, "ubuntu-text", "blue"); if (option) sscanf(option, "0x%x", &plugin->blue); plugin->title = ply_key_file_get_value (key_file, "ubuntu-text", "title"); return plugin; } static void detach_from_event_loop (ply_boot_splash_plugin_t *plugin) { plugin->loop = NULL; ply_trace ("detaching from event loop"); } static void free_views (ply_boot_splash_plugin_t *plugin) { ply_list_node_t *node; node = ply_list_get_first_node (plugin->views); while (node != NULL) { ply_list_node_t *next_node; view_t *view; view = ply_list_node_get_data (node); next_node = ply_list_get_next_node (plugin->views, node); view_free (view); ply_list_remove_node (plugin->views, node); node = next_node; } ply_list_free (plugin->views); plugin->views = NULL; } static void destroy_plugin (ply_boot_splash_plugin_t *plugin) { ply_trace ("destroying plugin"); if (plugin == NULL) return; /* It doesn't ever make sense to keep this plugin on screen * after exit */ hide_splash_screen (plugin, plugin->loop); free_views (plugin); if (plugin->message != NULL) free (plugin->message); free (plugin); } static void show_message (ply_boot_splash_plugin_t *plugin) { ply_list_node_t *node; node = ply_list_get_first_node (plugin->views); while (node != NULL) { ply_list_node_t *next_node; view_t *view; view = ply_list_node_get_data (node); next_node = ply_list_get_next_node (plugin->views, node); view_show_message (view); node = next_node; } } static void animate_frame (ply_boot_splash_plugin_t *plugin, int frame) { ply_list_node_t *node; node = ply_list_get_first_node (plugin->views); while (node != NULL) { ply_list_node_t *next_node; view_t *view; int display_width, display_height; view = ply_list_node_get_data (node); next_node = ply_list_get_next_node (plugin->views, node); display_width = ply_text_display_get_number_of_columns (view->display); display_height = ply_text_display_get_number_of_rows (view->display); ply_text_display_set_cursor_position (view->display, (display_width - 12) / 2, display_height / 2); ply_text_display_set_background_color (view->display, PLY_TERMINAL_COLOR_BLACK); ply_text_display_set_foreground_color (view->display, PLY_TERMINAL_COLOR_WHITE); ply_text_display_write (view->display, plugin->title); ply_text_display_set_cursor_position (view->display, (display_width - 10) / 2, (display_height / 2) + 2); if ((frame < 1) || (frame > 4)) ply_text_display_set_foreground_color (view->display, PLY_TERMINAL_COLOR_WHITE); else ply_text_display_set_foreground_color (view->display, PLY_TERMINAL_COLOR_BROWN); ply_text_display_write (view->display, ". "); if ((frame < 2) || (frame > 5)) ply_text_display_set_foreground_color (view->display, PLY_TERMINAL_COLOR_WHITE); else ply_text_display_set_foreground_color (view->display, PLY_TERMINAL_COLOR_BROWN); ply_text_display_write (view->display, ". "); if ((frame < 3) || (frame > 6)) ply_text_display_set_foreground_color (view->display, PLY_TERMINAL_COLOR_WHITE); else ply_text_display_set_foreground_color (view->display, PLY_TERMINAL_COLOR_BROWN); ply_text_display_write (view->display, ". "); if (frame < 4) ply_text_display_set_foreground_color (view->display, PLY_TERMINAL_COLOR_WHITE); else ply_text_display_set_foreground_color (view->display, PLY_TERMINAL_COLOR_BROWN); ply_text_display_write (view->display, "."); node = next_node; } } static void on_timeout (ply_boot_splash_plugin_t *plugin) { static int frame = 0; frame += 1; frame %= 8; animate_frame (plugin, frame); ply_event_loop_watch_for_timeout (plugin->loop, 1.0, (ply_event_loop_timeout_handler_t) on_timeout, plugin); } static void start_animation (ply_boot_splash_plugin_t *plugin) { ply_list_node_t *node; assert (plugin != NULL); assert (plugin->loop != NULL); redraw_views (plugin); if (plugin->message != NULL) show_message (plugin); if (plugin->is_animating) return; node = ply_list_get_first_node (plugin->views); while (node != NULL) { ply_list_node_t *next_node; view_t *view; view = ply_list_node_get_data (node); next_node = ply_list_get_next_node (plugin->views, node); view_start_animation (view); node = next_node; } plugin->is_animating = true; animate_frame (plugin, 0); ply_event_loop_watch_for_timeout (plugin->loop, 1.0, (ply_event_loop_timeout_handler_t) on_timeout, plugin); } static void stop_animation (ply_boot_splash_plugin_t *plugin) { ply_list_node_t *node; assert (plugin != NULL); assert (plugin->loop != NULL); if (!plugin->is_animating) return; plugin->is_animating = false; node = ply_list_get_first_node (plugin->views); while (node != NULL) { ply_list_node_t *next_node; view_t *view; view = ply_list_node_get_data (node); next_node = ply_list_get_next_node (plugin->views, node); node = next_node; } ply_event_loop_stop_watching_for_timeout (plugin->loop, (ply_event_loop_timeout_handler_t) on_timeout, plugin); redraw_views (plugin); } static void on_draw (view_t *view, ply_terminal_t *terminal, int x, int y, int width, int height) { } static void add_text_display (ply_boot_splash_plugin_t *plugin, ply_text_display_t *display) { view_t *view; ply_terminal_t *terminal; view = view_new (plugin, display); terminal = ply_text_display_get_terminal (view->display); if (ply_terminal_open (terminal)) { ply_terminal_set_mode (terminal, PLY_TERMINAL_MODE_TEXT); ply_terminal_activate_vt (terminal); } ply_text_display_set_draw_handler (view->display, (ply_text_display_draw_handler_t) on_draw, view); ply_list_append_data (plugin->views, view); } static void remove_text_display (ply_boot_splash_plugin_t *plugin, ply_text_display_t *display) { ply_list_node_t *node; node = ply_list_get_first_node (plugin->views); while (node != NULL) { view_t *view; ply_list_node_t *next_node; view = ply_list_node_get_data (node); next_node = ply_list_get_next_node (plugin->views, node); if (view->display == display) { ply_text_display_set_draw_handler (view->display, NULL, NULL); view_free (view); ply_list_remove_node (plugin->views, node); return; } node = next_node; } } static bool show_splash_screen (ply_boot_splash_plugin_t *plugin, ply_event_loop_t *loop, ply_buffer_t *boot_buffer, ply_boot_splash_mode_t mode) { assert (plugin != NULL); plugin->loop = loop; plugin->mode = mode; ply_event_loop_watch_for_exit (loop, (ply_event_loop_exit_handler_t) detach_from_event_loop, plugin); ply_show_new_kernel_messages (false); start_animation (plugin); return true; } static void update_status (ply_boot_splash_plugin_t *plugin, const char *status) { assert (plugin != NULL); ply_trace ("status update"); } static void hide_splash_screen (ply_boot_splash_plugin_t *plugin, ply_event_loop_t *loop) { assert (plugin != NULL); ply_trace ("hiding splash screen"); if (plugin->loop != NULL) { stop_animation (plugin); ply_event_loop_stop_watching_for_exit (plugin->loop, (ply_event_loop_exit_handler_t) detach_from_event_loop, plugin); detach_from_event_loop (plugin); } hide_views (plugin); ply_show_new_kernel_messages (true); } static void display_normal (ply_boot_splash_plugin_t *plugin) { pause_views (plugin); if (plugin->state != PLY_BOOT_SPLASH_DISPLAY_NORMAL) { plugin->state = PLY_BOOT_SPLASH_DISPLAY_NORMAL; start_animation (plugin); redraw_views (plugin); } unpause_views (plugin); } static void display_message (ply_boot_splash_plugin_t *plugin, const char *message) { if (plugin->message != NULL) free (plugin->message); plugin->message = strdup (message); start_animation (plugin); } static void show_password_prompt (ply_boot_splash_plugin_t *plugin, const char *prompt, int bullets) { ply_list_node_t *node; int i; char *entered_text; entered_text = calloc (bullets + 1, sizeof (char)); for (i = 0; i < bullets; i++) entered_text[i] = '*'; node = ply_list_get_first_node (plugin->views); while (node != NULL) { ply_list_node_t *next_node; view_t *view; view = ply_list_node_get_data (node); next_node = ply_list_get_next_node (plugin->views, node); view_show_prompt (view, prompt, entered_text); node = next_node; } free (entered_text); } static void show_prompt (ply_boot_splash_plugin_t *plugin, const char *prompt, const char *text) { ply_list_node_t *node; node = ply_list_get_first_node (plugin->views); while (node != NULL) { ply_list_node_t *next_node; view_t *view; view = ply_list_node_get_data (node); next_node = ply_list_get_next_node (plugin->views, node); view_show_prompt (view, prompt, text); node = next_node; } } static void display_password (ply_boot_splash_plugin_t *plugin, const char *prompt, int bullets) { pause_views (plugin); if (plugin->state == PLY_BOOT_SPLASH_DISPLAY_NORMAL) stop_animation (plugin); plugin->state = PLY_BOOT_SPLASH_DISPLAY_PASSWORD_ENTRY; if (!prompt) prompt = "Password"; show_password_prompt (plugin, prompt, bullets); unpause_views (plugin); } static void display_question (ply_boot_splash_plugin_t *plugin, const char *prompt, const char *entry_text) { pause_views (plugin); if (plugin->state == PLY_BOOT_SPLASH_DISPLAY_NORMAL) stop_animation (plugin); plugin->state = PLY_BOOT_SPLASH_DISPLAY_PASSWORD_ENTRY; if (!prompt) prompt = "Password"; show_prompt (plugin, prompt, entry_text); unpause_views (plugin); } ply_boot_splash_plugin_interface_t * ply_boot_splash_plugin_get_interface (void) { static ply_boot_splash_plugin_interface_t plugin_interface = { .create_plugin = create_plugin, .destroy_plugin = destroy_plugin, .add_text_display = add_text_display, .remove_text_display = remove_text_display, .show_splash_screen = show_splash_screen, .update_status = update_status, .hide_splash_screen = hide_splash_screen, .display_normal = display_normal, .display_message = display_message, .display_password = display_password, .display_question = display_question, }; return &plugin_interface; } /* vim: set ts=4 sw=4 expandtab autoindent cindent cino={.5s,(0: */