From 041d1ea37802bf7178a31a53f96c26efa6b8fb7b Mon Sep 17 00:00:00 2001 From: James Date: Fri, 16 Nov 2012 10:41:01 +0000 Subject: fish --- grub-core/normal/menu_text.c | 491 +++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 491 insertions(+) create mode 100644 grub-core/normal/menu_text.c (limited to 'grub-core/normal/menu_text.c') diff --git a/grub-core/normal/menu_text.c b/grub-core/normal/menu_text.c new file mode 100644 index 0000000..93f0492 --- /dev/null +++ b/grub-core/normal/menu_text.c @@ -0,0 +1,491 @@ +/* menu_text.c - Basic text menu implementation. */ +/* + * GRUB -- GRand Unified Bootloader + * Copyright (C) 2003,2004,2005,2006,2007,2008,2009 Free Software Foundation, Inc. + * + * GRUB 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 3 of the License, or + * (at your option) any later version. + * + * GRUB 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 GRUB. If not, see . + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +static grub_uint8_t grub_color_menu_normal; +static grub_uint8_t grub_color_menu_highlight; + +struct menu_viewer_data +{ + int first, offset; + /* The number of entries shown at a time. */ + int num_entries; + grub_menu_t menu; + struct grub_term_output *term; +}; + +static inline int +grub_term_cursor_x (struct grub_term_output *term) +{ + return (GRUB_TERM_LEFT_BORDER_X + grub_term_border_width (term) + - GRUB_TERM_MARGIN - 1); +} + +grub_ssize_t +grub_getstringwidth (grub_uint32_t * str, const grub_uint32_t * last_position, + struct grub_term_output *term) +{ + grub_ssize_t width = 0; + + while (str < last_position) + { + struct grub_unicode_glyph glyph; + str += grub_unicode_aglomerate_comb (str, last_position - str, &glyph); + width += grub_term_getcharwidth (term, &glyph); + } + return width; +} + +static int +grub_print_message_indented_real (const char *msg, int margin_left, + int margin_right, + struct grub_term_output *term, int dry_run) +{ + grub_uint32_t *unicode_msg; + grub_uint32_t *last_position; + + int msg_len; + int ret = 0; + + msg_len = grub_utf8_to_ucs4_alloc (msg, &unicode_msg, &last_position); + + if (msg_len < 0) + { + return 0; + } + + if (dry_run) + ret = grub_ucs4_count_lines (unicode_msg, last_position, margin_left, + margin_right, term); + else + grub_print_ucs4 (unicode_msg, last_position, margin_left, + margin_right, term); + + grub_free (unicode_msg); + + return ret; +} + +void +grub_print_message_indented (const char *msg, int margin_left, int margin_right, + struct grub_term_output *term) +{ + grub_print_message_indented_real (msg, margin_left, margin_right, term, 0); +} + +static void +draw_border (struct grub_term_output *term, int num_entries) +{ + unsigned i; + + grub_term_setcolorstate (term, GRUB_TERM_COLOR_NORMAL); + + grub_term_gotoxy (term, GRUB_TERM_MARGIN, GRUB_TERM_TOP_BORDER_Y); + grub_putcode (GRUB_UNICODE_CORNER_UL, term); + for (i = 0; i < (unsigned) grub_term_border_width (term) - 2; i++) + grub_putcode (GRUB_UNICODE_HLINE, term); + grub_putcode (GRUB_UNICODE_CORNER_UR, term); + + for (i = 0; i < (unsigned) num_entries; i++) + { + grub_term_gotoxy (term, GRUB_TERM_MARGIN, GRUB_TERM_TOP_BORDER_Y + i + 1); + grub_putcode (GRUB_UNICODE_VLINE, term); + grub_term_gotoxy (term, GRUB_TERM_MARGIN + grub_term_border_width (term) + - 1, + GRUB_TERM_TOP_BORDER_Y + i + 1); + grub_putcode (GRUB_UNICODE_VLINE, term); + } + + grub_term_gotoxy (term, GRUB_TERM_MARGIN, + GRUB_TERM_TOP_BORDER_Y + num_entries + 1); + grub_putcode (GRUB_UNICODE_CORNER_LL, term); + for (i = 0; i < (unsigned) grub_term_border_width (term) - 2; i++) + grub_putcode (GRUB_UNICODE_HLINE, term); + grub_putcode (GRUB_UNICODE_CORNER_LR, term); + + grub_term_setcolorstate (term, GRUB_TERM_COLOR_NORMAL); + + grub_term_gotoxy (term, GRUB_TERM_MARGIN, + (GRUB_TERM_TOP_BORDER_Y + num_entries + + GRUB_TERM_MARGIN + 1)); +} + +static int +print_message (int nested, int edit, struct grub_term_output *term, int dry_run) +{ + int ret = 0; + grub_term_setcolorstate (term, GRUB_TERM_COLOR_NORMAL); + + if (edit) + { + if(dry_run) + ret++; + else + grub_putcode ('\n', term); + ret += grub_print_message_indented_real (_("Minimum Emacs-like screen editing is \ +supported. TAB lists completions. Press Ctrl-x or F10 to boot, Ctrl-c or F2 for a \ +command-line or ESC to discard edits and return to the GRUB menu."), + STANDARD_MARGIN, STANDARD_MARGIN, + term, dry_run); + } + else + { + const char *msg = _("Use the %C and %C keys to select which " + "entry is highlighted.\n"); + char *msg_translated; + + msg_translated = grub_xasprintf (msg, GRUB_UNICODE_UPARROW, + GRUB_UNICODE_DOWNARROW); + if (!msg_translated) + return 0; + if(dry_run) + ret++; + else + grub_putcode ('\n', term); + ret += grub_print_message_indented_real (msg_translated, STANDARD_MARGIN, + STANDARD_MARGIN, term, dry_run); + + grub_free (msg_translated); + + if (nested) + { + ret += grub_print_message_indented_real + (_("Press enter to boot the selected OS, " + "\'e\' to edit the commands before booting " + "or \'c\' for a command-line. ESC to return previous menu.\n"), + STANDARD_MARGIN, STANDARD_MARGIN, term, dry_run); + } + else + { + ret += grub_print_message_indented_real + (_("Press enter to boot the selected OS, " + "\'e\' to edit the commands before booting " + "or \'c\' for a command-line.\n"), + STANDARD_MARGIN, STANDARD_MARGIN, term, dry_run); + } + } + return ret; +} + +static void +print_entry (int y, int highlight, grub_menu_entry_t entry, + struct grub_term_output *term) +{ + int x; + const char *title; + grub_size_t title_len; + grub_ssize_t len; + grub_uint32_t *unicode_title; + grub_ssize_t i; + grub_uint8_t old_color_normal, old_color_highlight; + + title = entry ? entry->title : ""; + title_len = grub_strlen (title); + unicode_title = grub_malloc (title_len * sizeof (*unicode_title)); + if (! unicode_title) + /* XXX How to show this error? */ + return; + + len = grub_utf8_to_ucs4 (unicode_title, title_len, + (grub_uint8_t *) title, -1, 0); + if (len < 0) + { + /* It is an invalid sequence. */ + grub_free (unicode_title); + return; + } + + grub_term_getcolor (term, &old_color_normal, &old_color_highlight); + grub_term_setcolor (term, grub_color_menu_normal, grub_color_menu_highlight); + grub_term_setcolorstate (term, highlight + ? GRUB_TERM_COLOR_HIGHLIGHT + : GRUB_TERM_COLOR_NORMAL); + + grub_term_gotoxy (term, GRUB_TERM_LEFT_BORDER_X + GRUB_TERM_MARGIN, y); + + int last_printed = 0; + for (x = GRUB_TERM_LEFT_BORDER_X + GRUB_TERM_MARGIN + 1, i = 0; + x < (int) (GRUB_TERM_LEFT_BORDER_X + grub_term_border_width (term) + - GRUB_TERM_MARGIN);) + { + if (i < len + && x <= (int) (GRUB_TERM_LEFT_BORDER_X + grub_term_border_width (term) + - GRUB_TERM_MARGIN - 1)) + { + grub_ssize_t width; + struct grub_unicode_glyph glyph; + + i += grub_unicode_aglomerate_comb (unicode_title + i, + len - i, &glyph); + + width = grub_term_getcharwidth (term, &glyph); + grub_free (glyph.combining); + + if (x + width <= (int) (GRUB_TERM_LEFT_BORDER_X + + grub_term_border_width (term) + - GRUB_TERM_MARGIN - 1)) + last_printed = i; + x += width; + } + else + break; + } + + grub_print_ucs4 (unicode_title, + unicode_title + last_printed, 0, 0, term); + + if (last_printed != len) + { + grub_putcode (GRUB_UNICODE_RIGHTARROW, term); + struct grub_unicode_glyph pseudo_glyph = { + .base = GRUB_UNICODE_RIGHTARROW, + .variant = 0, + .attributes = 0, + .ncomb = 0, + .combining = 0, + .estimated_width = 1 + }; + x += grub_term_getcharwidth (term, &pseudo_glyph); + } + + for (; x < (int) (GRUB_TERM_LEFT_BORDER_X + grub_term_border_width (term) + - GRUB_TERM_MARGIN); x++) + grub_putcode (' ', term); + + grub_term_setcolorstate (term, GRUB_TERM_COLOR_NORMAL); + grub_putcode (' ', term); + + grub_term_gotoxy (term, grub_term_cursor_x (term), y); + + grub_term_setcolor (term, old_color_normal, old_color_highlight); + grub_term_setcolorstate (term, GRUB_TERM_COLOR_NORMAL); + grub_free (unicode_title); +} + +static void +print_entries (grub_menu_t menu, const struct menu_viewer_data *data) +{ + grub_menu_entry_t e; + int i; + + grub_term_gotoxy (data->term, + GRUB_TERM_LEFT_BORDER_X + grub_term_border_width (data->term), + GRUB_TERM_FIRST_ENTRY_Y); + + if (data->first) + grub_putcode (GRUB_UNICODE_UPARROW, data->term); + else + grub_putcode (' ', data->term); + + e = grub_menu_get_entry (menu, data->first); + + for (i = 0; i < data->num_entries; i++) + { + print_entry (GRUB_TERM_FIRST_ENTRY_Y + i, data->offset == i, + e, data->term); + if (e) + e = e->next; + } + + grub_term_gotoxy (data->term, GRUB_TERM_LEFT_BORDER_X + + grub_term_border_width (data->term), + GRUB_TERM_TOP_BORDER_Y + data->num_entries); + + if (e) + grub_putcode (GRUB_UNICODE_DOWNARROW, data->term); + else + grub_putcode (' ', data->term); + + grub_term_gotoxy (data->term, grub_term_cursor_x (data->term), + GRUB_TERM_FIRST_ENTRY_Y + data->offset); +} + +/* Initialize the screen. If NESTED is non-zero, assume that this menu + is run from another menu or a command-line. If EDIT is non-zero, show + a message for the menu entry editor. */ +void +grub_menu_init_page (int nested, int edit, int *num_entries, + struct grub_term_output *term) +{ + grub_uint8_t old_color_normal, old_color_highlight; + + /* 3 lines for timeout message and bottom margin. 2 lines for the border. */ + *num_entries = grub_term_height (term) - GRUB_TERM_TOP_BORDER_Y + - (print_message (nested, edit, term, 1) + 3) - 2; + + grub_term_getcolor (term, &old_color_normal, &old_color_highlight); + + /* By default, use the same colors for the menu. */ + grub_color_menu_normal = old_color_normal; + grub_color_menu_highlight = old_color_highlight; + + /* Then give user a chance to replace them. */ + grub_parse_color_name_pair (&grub_color_menu_normal, + grub_env_get ("menu_color_normal")); + grub_parse_color_name_pair (&grub_color_menu_highlight, + grub_env_get ("menu_color_highlight")); + + grub_normal_init_page (term); + grub_term_setcolor (term, grub_color_menu_normal, grub_color_menu_highlight); + draw_border (term, *num_entries); + grub_term_setcolor (term, old_color_normal, old_color_highlight); + print_message (nested, edit, term, 0); +} + +static void +menu_text_print_timeout (int timeout, void *dataptr) +{ + const char *msg = + _("The highlighted entry will be executed automatically in %ds."); + struct menu_viewer_data *data = dataptr; + char *msg_translated; + int posx; + + grub_term_gotoxy (data->term, 0, grub_term_height (data->term) - 3); + + msg_translated = grub_xasprintf (msg, timeout); + if (!msg_translated) + { + grub_print_error (); + grub_errno = GRUB_ERR_NONE; + return; + } + + grub_print_message_indented (msg_translated, 3, 0, data->term); + + posx = grub_term_getxy (data->term) >> 8; + grub_print_spaces (data->term, grub_term_width (data->term) - posx - 1); + + grub_term_gotoxy (data->term, + grub_term_cursor_x (data->term), + GRUB_TERM_FIRST_ENTRY_Y + data->offset); + grub_term_refresh (data->term); +} + +static void +menu_text_set_chosen_entry (int entry, void *dataptr) +{ + struct menu_viewer_data *data = dataptr; + int oldoffset = data->offset; + int complete_redraw = 0; + + data->offset = entry - data->first; + if (data->offset > data->num_entries - 1) + { + data->first = entry - (data->num_entries - 1); + data->offset = data->num_entries - 1; + complete_redraw = 1; + } + if (data->offset < 0) + { + data->offset = 0; + data->first = entry; + complete_redraw = 1; + } + if (complete_redraw) + print_entries (data->menu, data); + else + { + print_entry (GRUB_TERM_FIRST_ENTRY_Y + oldoffset, 0, + grub_menu_get_entry (data->menu, data->first + oldoffset), + data->term); + print_entry (GRUB_TERM_FIRST_ENTRY_Y + data->offset, 1, + grub_menu_get_entry (data->menu, data->first + data->offset), + data->term); + } + grub_term_refresh (data->term); +} + +static void +menu_text_fini (void *dataptr) +{ + struct menu_viewer_data *data = dataptr; + + grub_term_setcursor (data->term, 1); + grub_term_cls (data->term); + +} + +static void +menu_text_clear_timeout (void *dataptr) +{ + struct menu_viewer_data *data = dataptr; + + grub_term_gotoxy (data->term, 0, grub_term_height (data->term) - 3); + grub_print_spaces (data->term, grub_term_width (data->term) - 1); + grub_term_gotoxy (data->term, grub_term_cursor_x (data->term), + GRUB_TERM_FIRST_ENTRY_Y + data->offset); + grub_term_refresh (data->term); +} + +grub_err_t +grub_menu_try_text (struct grub_term_output *term, + int entry, grub_menu_t menu, int nested) +{ + struct menu_viewer_data *data; + struct grub_menu_viewer *instance; + + instance = grub_zalloc (sizeof (*instance)); + if (!instance) + return grub_errno; + + data = grub_zalloc (sizeof (*data)); + if (!data) + { + grub_free (instance); + return grub_errno; + } + + data->term = term; + instance->data = data; + instance->set_chosen_entry = menu_text_set_chosen_entry; + instance->print_timeout = menu_text_print_timeout; + instance->clear_timeout = menu_text_clear_timeout; + instance->fini = menu_text_fini; + + data->menu = menu; + + data->offset = entry; + data->first = 0; + + grub_term_setcursor (data->term, 0); + grub_menu_init_page (nested, 0, &data->num_entries, data->term); + + if (data->offset > data->num_entries - 1) + { + data->first = data->offset - (data->num_entries - 1); + data->offset = data->num_entries - 1; + } + + print_entries (menu, data); + grub_term_refresh (data->term); + grub_menu_register_viewer (instance); + + return GRUB_ERR_NONE; +} -- cgit v1.2.3