File indexing completed on 2024-05-19 05:47:17
0001 /* 0002 * Copyright (C) 2016 Harald Sitter <sitter@kde.org> 0003 * Copyright (C) 2010 Canonical Ltd. 0004 * Copyright (C) 2008-2012 Red Hat, Inc. 0005 * 0006 * This program is free software; you can redistribute it and/or modify 0007 * it under the terms of the GNU General Public License as published by 0008 * the Free Software Foundation; either version 2, or (at your option) 0009 * any later version. 0010 * 0011 * This program is distributed in the hope that it will be useful, 0012 * but WITHOUT ANY WARRANTY; without even the implied warranty of 0013 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 0014 * GNU General Public License for more details. 0015 * 0016 * You should have received a copy of the GNU General Public License 0017 * along with this program; if not, write to the Free Software 0018 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 0019 * 02111-1307, USA. 0020 * 0021 */ 0022 0023 #include "ply-text-progress-bar.h" 0024 #include <assert.h> 0025 #include <errno.h> 0026 #include <fcntl.h> 0027 #include <math.h> 0028 #include <signal.h> 0029 #include <stdbool.h> 0030 #include <stdio.h> 0031 #include <stdint.h> 0032 #include <stdlib.h> 0033 #include <string.h> 0034 #include <sys/ioctl.h> 0035 #include <sys/stat.h> 0036 #include <sys/time.h> 0037 #include <sys/types.h> 0038 #include <termios.h> 0039 #include <unistd.h> 0040 #include <values.h> 0041 #include <wchar.h> 0042 0043 #include <ply-trigger.h> 0044 #include <ply-boot-splash-plugin.h> 0045 #include <ply-buffer.h> 0046 #include <ply-event-loop.h> 0047 #include <ply-key-file.h> 0048 #include <ply-list.h> 0049 #include <ply-logger.h> 0050 #include <ply-text-display.h> 0051 #include <ply-utils.h> 0052 0053 #include <linux/kd.h> 0054 0055 #define CLEAR_LINE_SEQUENCE "\033[2K\r\n" 0056 #define BACKSPACE "\b\033[0K" 0057 #define PLUGIN_NAME "breeze-text" 0058 0059 typedef enum { 0060 PLY_BOOT_SPLASH_DISPLAY_NORMAL, 0061 PLY_BOOT_SPLASH_DISPLAY_QUESTION_ENTRY, 0062 PLY_BOOT_SPLASH_DISPLAY_PASSWORD_ENTRY 0063 } ply_boot_splash_display_type_t; 0064 0065 struct _ply_boot_splash_plugin 0066 { 0067 ply_event_loop_t *loop; 0068 ply_boot_splash_mode_t mode; 0069 0070 ply_list_t *views; 0071 0072 ply_boot_splash_display_type_t state; 0073 0074 char *message; 0075 0076 uint32_t is_animating : 1; 0077 uint32_t black; 0078 uint32_t white; 0079 uint32_t brown; 0080 uint32_t blue; 0081 char *title; 0082 }; 0083 0084 typedef struct 0085 { 0086 ply_boot_splash_plugin_t *plugin; 0087 ply_text_display_t *display; 0088 breeze_text_progress_bar_t *progress_bar; 0089 } view_t; 0090 0091 static void hide_splash_screen (ply_boot_splash_plugin_t *plugin, 0092 ply_event_loop_t *loop); 0093 0094 static view_t * 0095 view_new (ply_boot_splash_plugin_t *plugin, 0096 ply_text_display_t *display) 0097 { 0098 view_t *view; 0099 0100 view = calloc (1, sizeof (view_t)); 0101 view->plugin = plugin; 0102 view->display = display; 0103 0104 view->progress_bar = breeze_text_progress_bar_new (); 0105 0106 return view; 0107 } 0108 0109 static void 0110 view_free (view_t *view) 0111 { 0112 breeze_text_progress_bar_free (view->progress_bar); 0113 0114 free (view); 0115 } 0116 0117 static void 0118 view_show_message (view_t *view) 0119 { 0120 ply_boot_splash_plugin_t *plugin; 0121 int display_width, display_height, y; 0122 ply_terminal_color_t color; 0123 char *message; 0124 0125 plugin = view->plugin; 0126 0127 display_width = ply_text_display_get_number_of_columns (view->display); 0128 display_height = ply_text_display_get_number_of_rows (view->display); 0129 0130 if (!strncmp (plugin->message, "keys:", 5)) 0131 { 0132 message = plugin->message + 5; 0133 color = PLY_TERMINAL_COLOR_WHITE; 0134 y = display_height - 4; 0135 } 0136 else 0137 { 0138 message = plugin->message; 0139 color = PLY_TERMINAL_COLOR_BLUE; 0140 y = display_height / 2 + 7; 0141 } 0142 0143 ply_text_display_set_cursor_position (view->display, 0, y); 0144 ply_text_display_clear_line (view->display); 0145 ply_text_display_set_cursor_position (view->display, 0146 (display_width - 0147 strlen (message)) / 2, 0148 y); 0149 0150 ply_text_display_set_foreground_color (view->display, color); 0151 ply_text_display_write (view->display, "%s", message); 0152 } 0153 0154 static void 0155 view_show_prompt (view_t *view, 0156 const char *prompt, 0157 const char *entered_text) 0158 { 0159 int display_width, display_height; 0160 0161 display_width = ply_text_display_get_number_of_columns (view->display); 0162 display_height = ply_text_display_get_number_of_rows (view->display); 0163 0164 ply_text_display_set_cursor_position (view->display, 0, 0165 display_height / 2 + 8); 0166 ply_text_display_clear_line (view->display); 0167 ply_text_display_set_cursor_position (view->display, 0168 display_width / 2 - (strlen (prompt)), 0169 display_height / 2 + 8); 0170 0171 ply_text_display_write (view->display, "%s:%s", prompt, entered_text); 0172 0173 ply_text_display_show_cursor (view->display); 0174 } 0175 0176 static void 0177 view_start_animation (view_t *view) 0178 { 0179 ply_boot_splash_plugin_t *plugin; 0180 ply_terminal_t *terminal; 0181 0182 assert (view != NULL); 0183 0184 plugin = view->plugin; 0185 0186 terminal = ply_text_display_get_terminal (view->display); 0187 0188 ply_terminal_set_color_hex_value (terminal, 0189 PLY_TERMINAL_COLOR_BLACK, 0190 plugin->black); 0191 ply_terminal_set_color_hex_value (terminal, 0192 PLY_TERMINAL_COLOR_WHITE, 0193 plugin->white); 0194 ply_terminal_set_color_hex_value (terminal, 0195 PLY_TERMINAL_COLOR_BLUE, 0196 plugin->blue); 0197 ply_terminal_set_color_hex_value (terminal, 0198 PLY_TERMINAL_COLOR_BROWN, 0199 plugin->brown); 0200 0201 ply_text_display_set_background_color (view->display, 0202 PLY_TERMINAL_COLOR_BLACK); 0203 ply_text_display_clear_screen (view->display); 0204 ply_text_display_hide_cursor (view->display); 0205 0206 if (plugin->mode == PLY_BOOT_SPLASH_MODE_SHUTDOWN) 0207 { 0208 breeze_text_progress_bar_hide (view->progress_bar); 0209 return; 0210 } 0211 0212 breeze_text_progress_bar_show (view->progress_bar, 0213 view->display); 0214 } 0215 0216 static void 0217 view_redraw (view_t *view) 0218 { 0219 unsigned long screen_width, screen_height; 0220 0221 screen_width = ply_text_display_get_number_of_columns (view->display); 0222 screen_height = ply_text_display_get_number_of_rows (view->display); 0223 0224 ply_text_display_draw_area (view->display, 0, 0, 0225 screen_width, screen_height); 0226 } 0227 0228 static void 0229 redraw_views (ply_boot_splash_plugin_t *plugin) 0230 { 0231 ply_list_node_t *node; 0232 0233 node = ply_list_get_first_node (plugin->views); 0234 while (node != NULL) 0235 { 0236 ply_list_node_t *next_node; 0237 view_t *view; 0238 0239 view = ply_list_node_get_data (node); 0240 next_node = ply_list_get_next_node (plugin->views, node); 0241 0242 view_redraw (view); 0243 0244 node = next_node; 0245 } 0246 } 0247 0248 static void 0249 view_hide (view_t *view) 0250 { 0251 if (view->display != NULL) 0252 { 0253 ply_terminal_t *terminal; 0254 0255 terminal = ply_text_display_get_terminal (view->display); 0256 0257 ply_text_display_set_background_color (view->display, PLY_TERMINAL_COLOR_DEFAULT); 0258 ply_text_display_clear_screen (view->display); 0259 ply_text_display_show_cursor (view->display); 0260 0261 ply_terminal_reset_colors (terminal); 0262 } 0263 } 0264 0265 static void 0266 hide_views (ply_boot_splash_plugin_t *plugin) 0267 { 0268 ply_list_node_t *node; 0269 0270 node = ply_list_get_first_node (plugin->views); 0271 while (node != NULL) 0272 { 0273 ply_list_node_t *next_node; 0274 view_t *view; 0275 0276 view = ply_list_node_get_data (node); 0277 next_node = ply_list_get_next_node (plugin->views, node); 0278 0279 view_hide (view); 0280 0281 node = next_node; 0282 } 0283 } 0284 0285 static void 0286 pause_views (ply_boot_splash_plugin_t *plugin) 0287 { 0288 ply_list_node_t *node; 0289 0290 node = ply_list_get_first_node (plugin->views); 0291 while (node != NULL) 0292 { 0293 ply_list_node_t *next_node; 0294 view_t *view; 0295 0296 view = ply_list_node_get_data (node); 0297 next_node = ply_list_get_next_node (plugin->views, node); 0298 0299 ply_text_display_pause_updates (view->display); 0300 0301 node = next_node; 0302 } 0303 } 0304 0305 static void 0306 unpause_views (ply_boot_splash_plugin_t *plugin) 0307 { 0308 ply_list_node_t *node; 0309 0310 node = ply_list_get_first_node (plugin->views); 0311 while (node != NULL) 0312 { 0313 ply_list_node_t *next_node; 0314 view_t *view; 0315 0316 view = ply_list_node_get_data (node); 0317 next_node = ply_list_get_next_node (plugin->views, node); 0318 0319 ply_text_display_unpause_updates (view->display); 0320 0321 node = next_node; 0322 } 0323 } 0324 0325 static ply_boot_splash_plugin_t * 0326 create_plugin (ply_key_file_t *key_file) 0327 { 0328 char *option; 0329 0330 ply_boot_splash_plugin_t *plugin; 0331 0332 ply_trace ("creating plugin"); 0333 0334 plugin = calloc (1, sizeof (ply_boot_splash_plugin_t)); 0335 plugin->message = NULL; 0336 0337 plugin->views = ply_list_new (); 0338 0339 /* Not a pretty API for setting defaults for your config file... */ 0340 plugin->black = 0x000000; 0341 plugin->white = 0xeff0f1; // progress bar block 1 0342 plugin->blue = 0xeff0f1; // progress bar block 2 0343 plugin->brown = 0xeff0f1; // progress bar block 3 0344 0345 option = ply_key_file_get_value (key_file, PLUGIN_NAME, "black"); 0346 if (option) 0347 sscanf(option, "0x%x", &plugin->black); 0348 option = ply_key_file_get_value (key_file, PLUGIN_NAME, "white"); 0349 if (option) 0350 sscanf(option, "0x%x", &plugin->white); 0351 option = ply_key_file_get_value (key_file, PLUGIN_NAME, "brown"); 0352 if (option) 0353 sscanf(option, "0x%x", &plugin->brown); 0354 option = ply_key_file_get_value (key_file, PLUGIN_NAME, "blue"); 0355 if (option) 0356 sscanf(option, "0x%x", &plugin->blue); 0357 0358 plugin->title = ply_key_file_get_value (key_file, PLUGIN_NAME, "title"); 0359 0360 return plugin; 0361 } 0362 0363 static void 0364 detach_from_event_loop (ply_boot_splash_plugin_t *plugin) 0365 { 0366 plugin->loop = NULL; 0367 0368 ply_trace ("detaching from event loop"); 0369 } 0370 0371 static void 0372 free_views (ply_boot_splash_plugin_t *plugin) 0373 { 0374 ply_list_node_t *node; 0375 0376 node = ply_list_get_first_node (plugin->views); 0377 0378 while (node != NULL) 0379 { 0380 ply_list_node_t *next_node; 0381 view_t *view; 0382 0383 view = ply_list_node_get_data (node); 0384 next_node = ply_list_get_next_node (plugin->views, node); 0385 0386 view_free (view); 0387 ply_list_remove_node (plugin->views, node); 0388 0389 node = next_node; 0390 } 0391 0392 ply_list_free (plugin->views); 0393 plugin->views = NULL; 0394 } 0395 0396 static void 0397 destroy_plugin (ply_boot_splash_plugin_t *plugin) 0398 { 0399 ply_trace ("destroying plugin"); 0400 0401 if (plugin == NULL) 0402 return; 0403 0404 /* It doesn't ever make sense to keep this plugin on screen 0405 * after exit 0406 */ 0407 hide_splash_screen (plugin, plugin->loop); 0408 0409 free_views (plugin); 0410 if (plugin->message != NULL) 0411 free (plugin->message); 0412 0413 free (plugin); 0414 } 0415 0416 static void 0417 show_message (ply_boot_splash_plugin_t *plugin) 0418 { 0419 ply_list_node_t *node; 0420 0421 node = ply_list_get_first_node (plugin->views); 0422 while (node != NULL) 0423 { 0424 ply_list_node_t *next_node; 0425 view_t *view; 0426 0427 view = ply_list_node_get_data (node); 0428 next_node = ply_list_get_next_node (plugin->views, node); 0429 0430 view_show_message (view); 0431 0432 node = next_node; 0433 } 0434 } 0435 0436 static void 0437 start_animation (ply_boot_splash_plugin_t *plugin) 0438 { 0439 ply_list_node_t *node; 0440 0441 assert (plugin != NULL); 0442 assert (plugin->loop != NULL); 0443 0444 redraw_views (plugin); 0445 0446 if (plugin->message != NULL) 0447 show_message (plugin); 0448 0449 if (plugin->is_animating) 0450 return; 0451 0452 node = ply_list_get_first_node (plugin->views); 0453 while (node != NULL) 0454 { 0455 ply_list_node_t *next_node; 0456 view_t *view; 0457 0458 view = ply_list_node_get_data (node); 0459 next_node = ply_list_get_next_node (plugin->views, node); 0460 0461 view_start_animation (view); 0462 0463 node = next_node; 0464 } 0465 0466 plugin->is_animating = true; 0467 } 0468 0469 static void 0470 stop_animation (ply_boot_splash_plugin_t *plugin) 0471 { 0472 ply_list_node_t *node; 0473 0474 assert (plugin != NULL); 0475 assert (plugin->loop != NULL); 0476 0477 if (!plugin->is_animating) 0478 return; 0479 0480 plugin->is_animating = false; 0481 0482 node = ply_list_get_first_node (plugin->views); 0483 while (node != NULL) 0484 { 0485 ply_list_node_t *next_node; 0486 view_t *view; 0487 0488 view = ply_list_node_get_data (node); 0489 next_node = ply_list_get_next_node (plugin->views, node); 0490 0491 breeze_text_progress_bar_hide (view->progress_bar); 0492 0493 node = next_node; 0494 } 0495 redraw_views (plugin); 0496 } 0497 0498 static void 0499 on_draw (view_t *view, 0500 ply_terminal_t *terminal, 0501 int x, 0502 int y, 0503 int width, 0504 int height) 0505 { 0506 ply_text_display_clear_screen (view->display); 0507 } 0508 0509 static void 0510 add_text_display (ply_boot_splash_plugin_t *plugin, 0511 ply_text_display_t *display) 0512 { 0513 view_t *view; 0514 ply_terminal_t *terminal; 0515 0516 view = view_new (plugin, display); 0517 0518 terminal = ply_text_display_get_terminal (view->display); 0519 if (ply_terminal_open (terminal)) 0520 { 0521 ply_terminal_set_mode (terminal, PLY_TERMINAL_MODE_TEXT); 0522 ply_terminal_activate_vt (terminal); 0523 } 0524 0525 ply_text_display_set_draw_handler (view->display, 0526 (ply_text_display_draw_handler_t) 0527 on_draw, view); 0528 0529 ply_list_append_data (plugin->views, view); 0530 } 0531 0532 static void 0533 remove_text_display (ply_boot_splash_plugin_t *plugin, 0534 ply_text_display_t *display) 0535 { 0536 ply_list_node_t *node; 0537 0538 node = ply_list_get_first_node (plugin->views); 0539 while (node != NULL) 0540 { 0541 view_t *view; 0542 ply_list_node_t *next_node; 0543 0544 view = ply_list_node_get_data (node); 0545 next_node = ply_list_get_next_node (plugin->views, node); 0546 0547 if (view->display == display) 0548 { 0549 ply_text_display_set_draw_handler (view->display, 0550 NULL, NULL); 0551 view_free (view); 0552 ply_list_remove_node (plugin->views, node); 0553 return; 0554 } 0555 0556 node = next_node; 0557 } 0558 } 0559 0560 static bool 0561 show_splash_screen (ply_boot_splash_plugin_t *plugin, 0562 ply_event_loop_t *loop, 0563 ply_buffer_t *boot_buffer, 0564 ply_boot_splash_mode_t mode) 0565 { 0566 assert (plugin != NULL); 0567 0568 plugin->loop = loop; 0569 plugin->mode = mode; 0570 ply_event_loop_watch_for_exit (loop, (ply_event_loop_exit_handler_t) 0571 detach_from_event_loop, 0572 plugin); 0573 0574 ply_show_new_kernel_messages (false); 0575 start_animation (plugin); 0576 0577 return true; 0578 } 0579 0580 static void 0581 update_status (ply_boot_splash_plugin_t *plugin, 0582 const char *status) 0583 { 0584 assert (plugin != NULL); 0585 0586 ply_trace ("status update"); 0587 } 0588 0589 static void 0590 on_boot_progress (ply_boot_splash_plugin_t *plugin, 0591 double duration, 0592 double percent_done) 0593 { 0594 ply_list_node_t *node; 0595 double total_duration; 0596 0597 total_duration = duration / percent_done; 0598 0599 /* Fun made-up smoothing function to make the growth asymptotic: 0600 * fraction(time,estimate)=1-2^(-(time^1.45)/estimate) */ 0601 percent_done = 1.0 - pow (2.0, -pow (duration, 1.45) / total_duration) * (1.0 - percent_done); 0602 0603 node = ply_list_get_first_node (plugin->views); 0604 0605 while (node != NULL) 0606 { 0607 ply_list_node_t *next_node; 0608 view_t *view; 0609 0610 view = ply_list_node_get_data (node); 0611 next_node = ply_list_get_next_node (plugin->views, node); 0612 0613 { 0614 int display_width = ply_text_display_get_number_of_columns (view->display); 0615 int display_height = ply_text_display_get_number_of_rows (view->display); 0616 0617 ply_text_display_set_cursor_position (view->display, 0618 (display_width - 12) / 2, 0619 display_height / 2); 0620 0621 ply_text_display_set_background_color (view->display, PLY_TERMINAL_COLOR_BLACK); 0622 ply_text_display_set_foreground_color (view->display, PLY_TERMINAL_COLOR_WHITE); 0623 ply_text_display_write (view->display, "%s", plugin->title); 0624 } 0625 0626 breeze_text_progress_bar_set_percent_done (view->progress_bar, percent_done); 0627 breeze_text_progress_bar_draw (view->progress_bar); 0628 0629 node = next_node; 0630 } 0631 } 0632 0633 static void 0634 hide_splash_screen (ply_boot_splash_plugin_t *plugin, 0635 ply_event_loop_t *loop) 0636 { 0637 assert (plugin != NULL); 0638 0639 ply_trace ("hiding splash screen"); 0640 0641 if (plugin->loop != NULL) 0642 { 0643 stop_animation (plugin); 0644 0645 ply_event_loop_stop_watching_for_exit (plugin->loop, 0646 (ply_event_loop_exit_handler_t) 0647 detach_from_event_loop, 0648 plugin); 0649 detach_from_event_loop (plugin); 0650 } 0651 0652 hide_views (plugin); 0653 ply_show_new_kernel_messages (true); 0654 } 0655 0656 static void 0657 display_normal (ply_boot_splash_plugin_t *plugin) 0658 { 0659 pause_views (plugin); 0660 if (plugin->state != PLY_BOOT_SPLASH_DISPLAY_NORMAL) 0661 { 0662 plugin->state = PLY_BOOT_SPLASH_DISPLAY_NORMAL; 0663 start_animation (plugin); 0664 redraw_views (plugin); 0665 } 0666 unpause_views (plugin); 0667 } 0668 0669 static void 0670 display_message (ply_boot_splash_plugin_t *plugin, 0671 const char *message) 0672 { 0673 if (plugin->message != NULL) 0674 free (plugin->message); 0675 0676 plugin->message = strdup (message); 0677 start_animation (plugin); 0678 } 0679 0680 static void 0681 show_password_prompt (ply_boot_splash_plugin_t *plugin, 0682 const char *prompt, 0683 int bullets) 0684 { 0685 ply_list_node_t *node; 0686 int i; 0687 char *entered_text; 0688 0689 entered_text = calloc (bullets + 1, sizeof (char)); 0690 0691 for (i = 0; i < bullets; i++) 0692 entered_text[i] = '*'; 0693 0694 node = ply_list_get_first_node (plugin->views); 0695 while (node != NULL) 0696 { 0697 ply_list_node_t *next_node; 0698 view_t *view; 0699 0700 view = ply_list_node_get_data (node); 0701 next_node = ply_list_get_next_node (plugin->views, node); 0702 0703 view_show_prompt (view, prompt, entered_text); 0704 0705 node = next_node; 0706 } 0707 free (entered_text); 0708 } 0709 0710 static void 0711 show_prompt (ply_boot_splash_plugin_t *plugin, 0712 const char *prompt, 0713 const char *text) 0714 { 0715 ply_list_node_t *node; 0716 0717 node = ply_list_get_first_node (plugin->views); 0718 while (node != NULL) 0719 { 0720 ply_list_node_t *next_node; 0721 view_t *view; 0722 0723 view = ply_list_node_get_data (node); 0724 next_node = ply_list_get_next_node (plugin->views, node); 0725 0726 view_show_prompt (view, prompt, text); 0727 0728 node = next_node; 0729 } 0730 } 0731 0732 static void 0733 display_password (ply_boot_splash_plugin_t *plugin, 0734 const char *prompt, 0735 int bullets) 0736 { 0737 pause_views (plugin); 0738 if (plugin->state == PLY_BOOT_SPLASH_DISPLAY_NORMAL) 0739 stop_animation (plugin); 0740 0741 plugin->state = PLY_BOOT_SPLASH_DISPLAY_PASSWORD_ENTRY; 0742 0743 if (!prompt) 0744 prompt = "Password"; 0745 0746 show_password_prompt (plugin, prompt, bullets); 0747 0748 unpause_views (plugin); 0749 } 0750 0751 static void 0752 display_question (ply_boot_splash_plugin_t *plugin, 0753 const char *prompt, 0754 const char *entry_text) 0755 { 0756 pause_views (plugin); 0757 if (plugin->state == PLY_BOOT_SPLASH_DISPLAY_NORMAL) 0758 stop_animation (plugin); 0759 0760 plugin->state = PLY_BOOT_SPLASH_DISPLAY_PASSWORD_ENTRY; 0761 0762 if (!prompt) 0763 prompt = "Password"; 0764 0765 show_prompt (plugin, prompt, entry_text); 0766 0767 unpause_views (plugin); 0768 } 0769 0770 ply_boot_splash_plugin_interface_t * 0771 ply_boot_splash_plugin_get_interface (void) 0772 { 0773 static ply_boot_splash_plugin_interface_t plugin_interface = 0774 { 0775 .create_plugin = create_plugin, 0776 .destroy_plugin = destroy_plugin, 0777 .add_text_display = add_text_display, 0778 .remove_text_display = remove_text_display, 0779 .show_splash_screen = show_splash_screen, 0780 .update_status = update_status, 0781 .on_boot_progress = on_boot_progress, 0782 .hide_splash_screen = hide_splash_screen, 0783 .display_normal = display_normal, 0784 .display_message = display_message, 0785 .display_password = display_password, 0786 .display_question = display_question, 0787 }; 0788 0789 return &plugin_interface; 0790 } 0791 0792 /* vim: set ts=4 sw=4 expandtab autoindent cindent cino={.5s,(0: */