Upgraded GRUB2 to 2.00 release.
[AROS.git] / arch / all-pc / boot / grub2-aros / grub-core / normal / menu_text.c
blob1687c2841e81389221d90f31b709385e26f230cb
1 /* menu_text.c - Basic text menu implementation. */
2 /*
3 * GRUB -- GRand Unified Bootloader
4 * Copyright (C) 2003,2004,2005,2006,2007,2008,2009 Free Software Foundation, Inc.
6 * GRUB is free software: you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License as published by
8 * the Free Software Foundation, either version 3 of the License, or
9 * (at your option) any later version.
11 * GRUB is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
16 * You should have received a copy of the GNU General Public License
17 * along with GRUB. If not, see <http://www.gnu.org/licenses/>.
20 #include <grub/normal.h>
21 #include <grub/term.h>
22 #include <grub/misc.h>
23 #include <grub/loader.h>
24 #include <grub/mm.h>
25 #include <grub/time.h>
26 #include <grub/env.h>
27 #include <grub/menu_viewer.h>
28 #include <grub/i18n.h>
29 #include <grub/charset.h>
31 static grub_uint8_t grub_color_menu_normal;
32 static grub_uint8_t grub_color_menu_highlight;
34 struct menu_viewer_data
36 int first, offset;
37 /* The number of entries shown at a time. */
38 int num_entries;
39 grub_menu_t menu;
40 struct grub_term_output *term;
43 static inline int
44 grub_term_cursor_x (struct grub_term_output *term)
46 return (GRUB_TERM_LEFT_BORDER_X + grub_term_border_width (term)
47 - GRUB_TERM_MARGIN - 1);
50 grub_ssize_t
51 grub_getstringwidth (grub_uint32_t * str, const grub_uint32_t * last_position,
52 struct grub_term_output *term)
54 grub_ssize_t width = 0;
56 while (str < last_position)
58 struct grub_unicode_glyph glyph;
59 str += grub_unicode_aglomerate_comb (str, last_position - str, &glyph);
60 width += grub_term_getcharwidth (term, &glyph);
62 return width;
65 static int
66 grub_print_message_indented_real (const char *msg, int margin_left,
67 int margin_right,
68 struct grub_term_output *term, int dry_run)
70 grub_uint32_t *unicode_msg;
71 grub_uint32_t *last_position;
72 grub_size_t msg_len = grub_strlen (msg) + 2;
73 int ret = 0;
75 unicode_msg = grub_malloc (msg_len * sizeof (grub_uint32_t));
77 if (!unicode_msg)
78 return 0;
80 msg_len = grub_utf8_to_ucs4 (unicode_msg, msg_len,
81 (grub_uint8_t *) msg, -1, 0);
83 last_position = unicode_msg + msg_len;
84 *last_position++ = '\n';
85 *last_position = 0;
87 if (dry_run)
88 ret = grub_ucs4_count_lines (unicode_msg, last_position, margin_left,
89 margin_right, term);
90 else
91 grub_print_ucs4 (unicode_msg, last_position, margin_left,
92 margin_right, term);
94 grub_free (unicode_msg);
96 return ret;
99 void
100 grub_print_message_indented (const char *msg, int margin_left, int margin_right,
101 struct grub_term_output *term)
103 grub_print_message_indented_real (msg, margin_left, margin_right, term, 0);
106 static void
107 draw_border (struct grub_term_output *term, int num_entries)
109 unsigned i;
111 grub_term_setcolorstate (term, GRUB_TERM_COLOR_NORMAL);
113 grub_term_gotoxy (term, GRUB_TERM_MARGIN, GRUB_TERM_TOP_BORDER_Y);
114 grub_putcode (GRUB_UNICODE_CORNER_UL, term);
115 for (i = 0; i < (unsigned) grub_term_border_width (term) - 2; i++)
116 grub_putcode (GRUB_UNICODE_HLINE, term);
117 grub_putcode (GRUB_UNICODE_CORNER_UR, term);
119 for (i = 0; i < (unsigned) num_entries; i++)
121 grub_term_gotoxy (term, GRUB_TERM_MARGIN, GRUB_TERM_TOP_BORDER_Y + i + 1);
122 grub_putcode (GRUB_UNICODE_VLINE, term);
123 grub_term_gotoxy (term, GRUB_TERM_MARGIN + grub_term_border_width (term)
124 - 1,
125 GRUB_TERM_TOP_BORDER_Y + i + 1);
126 grub_putcode (GRUB_UNICODE_VLINE, term);
129 grub_term_gotoxy (term, GRUB_TERM_MARGIN,
130 GRUB_TERM_TOP_BORDER_Y + num_entries + 1);
131 grub_putcode (GRUB_UNICODE_CORNER_LL, term);
132 for (i = 0; i < (unsigned) grub_term_border_width (term) - 2; i++)
133 grub_putcode (GRUB_UNICODE_HLINE, term);
134 grub_putcode (GRUB_UNICODE_CORNER_LR, term);
136 grub_term_setcolorstate (term, GRUB_TERM_COLOR_NORMAL);
138 grub_term_gotoxy (term, GRUB_TERM_MARGIN,
139 (GRUB_TERM_TOP_BORDER_Y + num_entries
140 + GRUB_TERM_MARGIN + 1));
143 static int
144 print_message (int nested, int edit, struct grub_term_output *term, int dry_run)
146 int ret = 0;
147 grub_term_setcolorstate (term, GRUB_TERM_COLOR_NORMAL);
149 if (edit)
151 if(dry_run)
152 ret++;
153 else
154 grub_putcode ('\n', term);
155 ret += grub_print_message_indented_real (_("Minimum Emacs-like screen editing is \
156 supported. TAB lists completions. Press Ctrl-x or F10 to boot, Ctrl-c or F2 for a \
157 command-line or ESC to discard edits and return to the GRUB menu."),
158 STANDARD_MARGIN, STANDARD_MARGIN,
159 term, dry_run);
161 else
163 const char *msg = _("Use the %C and %C keys to select which "
164 "entry is highlighted.");
165 char *msg_translated;
167 msg_translated = grub_xasprintf (msg, GRUB_UNICODE_UPARROW,
168 GRUB_UNICODE_DOWNARROW);
169 if (!msg_translated)
170 return 0;
171 if(dry_run)
172 ret++;
173 else
174 grub_putcode ('\n', term);
175 ret += grub_print_message_indented_real (msg_translated, STANDARD_MARGIN,
176 STANDARD_MARGIN, term, dry_run);
178 grub_free (msg_translated);
180 if (nested)
182 ret += grub_print_message_indented_real
183 (_("Press enter to boot the selected OS, "
184 "`e' to edit the commands before booting "
185 "or `c' for a command-line. ESC to return previous menu."),
186 STANDARD_MARGIN, STANDARD_MARGIN, term, dry_run);
188 else
190 ret += grub_print_message_indented_real
191 (_("Press enter to boot the selected OS, "
192 "`e' to edit the commands before booting "
193 "or `c' for a command-line."),
194 STANDARD_MARGIN, STANDARD_MARGIN, term, dry_run);
197 return ret;
200 static void
201 print_entry (int y, int highlight, grub_menu_entry_t entry,
202 struct grub_term_output *term)
204 int x;
205 const char *title;
206 grub_size_t title_len;
207 grub_ssize_t len;
208 grub_uint32_t *unicode_title;
209 grub_ssize_t i;
210 grub_uint8_t old_color_normal, old_color_highlight;
212 title = entry ? entry->title : "";
213 title_len = grub_strlen (title);
214 unicode_title = grub_malloc (title_len * sizeof (*unicode_title));
215 if (! unicode_title)
216 /* XXX How to show this error? */
217 return;
219 len = grub_utf8_to_ucs4 (unicode_title, title_len,
220 (grub_uint8_t *) title, -1, 0);
221 if (len < 0)
223 /* It is an invalid sequence. */
224 grub_free (unicode_title);
225 return;
228 grub_term_getcolor (term, &old_color_normal, &old_color_highlight);
229 grub_term_setcolor (term, grub_color_menu_normal, grub_color_menu_highlight);
230 grub_term_setcolorstate (term, highlight
231 ? GRUB_TERM_COLOR_HIGHLIGHT
232 : GRUB_TERM_COLOR_NORMAL);
234 grub_term_gotoxy (term, GRUB_TERM_LEFT_BORDER_X + GRUB_TERM_MARGIN, y);
236 int last_printed = 0;
238 for (i = 0; i < len; i++)
239 if (unicode_title[i] == '\n' || unicode_title[i] == '\b'
240 || unicode_title[i] == '\r' || unicode_title[i] == '\e')
241 unicode_title[i] = ' ';
243 for (x = GRUB_TERM_LEFT_BORDER_X + GRUB_TERM_MARGIN + 1, i = 0;
244 x < (int) (GRUB_TERM_LEFT_BORDER_X + grub_term_border_width (term)
245 - GRUB_TERM_MARGIN);)
247 if (i < len
248 && x <= (int) (GRUB_TERM_LEFT_BORDER_X + grub_term_border_width (term)
249 - GRUB_TERM_MARGIN - 1))
251 grub_ssize_t width;
252 struct grub_unicode_glyph glyph;
254 i += grub_unicode_aglomerate_comb (unicode_title + i,
255 len - i, &glyph);
257 width = grub_term_getcharwidth (term, &glyph);
258 grub_free (glyph.combining);
260 if (x + width <= (int) (GRUB_TERM_LEFT_BORDER_X
261 + grub_term_border_width (term)
262 - GRUB_TERM_MARGIN - 1))
263 last_printed = i;
264 x += width;
266 else
267 break;
270 grub_print_ucs4 (unicode_title,
271 unicode_title + last_printed, 0, 0, term);
273 if (last_printed != len)
275 grub_putcode (GRUB_UNICODE_RIGHTARROW, term);
276 struct grub_unicode_glyph pseudo_glyph = {
277 .base = GRUB_UNICODE_RIGHTARROW,
278 .variant = 0,
279 .attributes = 0,
280 .ncomb = 0,
281 .combining = 0,
282 .estimated_width = 1
284 x += grub_term_getcharwidth (term, &pseudo_glyph);
287 for (; x < (int) (GRUB_TERM_LEFT_BORDER_X + grub_term_border_width (term)
288 - GRUB_TERM_MARGIN); x++)
289 grub_putcode (' ', term);
291 grub_term_setcolorstate (term, GRUB_TERM_COLOR_NORMAL);
292 grub_putcode (' ', term);
294 grub_term_gotoxy (term, grub_term_cursor_x (term), y);
296 grub_term_setcolor (term, old_color_normal, old_color_highlight);
297 grub_term_setcolorstate (term, GRUB_TERM_COLOR_NORMAL);
298 grub_free (unicode_title);
301 static void
302 print_entries (grub_menu_t menu, const struct menu_viewer_data *data)
304 grub_menu_entry_t e;
305 int i;
307 grub_term_gotoxy (data->term,
308 GRUB_TERM_LEFT_BORDER_X + grub_term_border_width (data->term),
309 GRUB_TERM_FIRST_ENTRY_Y);
311 if (data->first)
312 grub_putcode (GRUB_UNICODE_UPARROW, data->term);
313 else
314 grub_putcode (' ', data->term);
316 e = grub_menu_get_entry (menu, data->first);
318 for (i = 0; i < data->num_entries; i++)
320 print_entry (GRUB_TERM_FIRST_ENTRY_Y + i, data->offset == i,
321 e, data->term);
322 if (e)
323 e = e->next;
326 grub_term_gotoxy (data->term, GRUB_TERM_LEFT_BORDER_X
327 + grub_term_border_width (data->term),
328 GRUB_TERM_TOP_BORDER_Y + data->num_entries);
330 if (e)
331 grub_putcode (GRUB_UNICODE_DOWNARROW, data->term);
332 else
333 grub_putcode (' ', data->term);
335 grub_term_gotoxy (data->term, grub_term_cursor_x (data->term),
336 GRUB_TERM_FIRST_ENTRY_Y + data->offset);
339 /* Initialize the screen. If NESTED is non-zero, assume that this menu
340 is run from another menu or a command-line. If EDIT is non-zero, show
341 a message for the menu entry editor. */
342 void
343 grub_menu_init_page (int nested, int edit, int *num_entries,
344 struct grub_term_output *term)
346 grub_uint8_t old_color_normal, old_color_highlight;
348 /* 3 lines for timeout message and bottom margin. 2 lines for the border. */
349 *num_entries = grub_term_height (term) - GRUB_TERM_TOP_BORDER_Y
350 - (print_message (nested, edit, term, 1) + 3) - 2;
352 grub_term_getcolor (term, &old_color_normal, &old_color_highlight);
354 /* By default, use the same colors for the menu. */
355 grub_color_menu_normal = old_color_normal;
356 grub_color_menu_highlight = old_color_highlight;
358 /* Then give user a chance to replace them. */
359 grub_parse_color_name_pair (&grub_color_menu_normal,
360 grub_env_get ("menu_color_normal"));
361 grub_parse_color_name_pair (&grub_color_menu_highlight,
362 grub_env_get ("menu_color_highlight"));
364 grub_normal_init_page (term);
365 grub_term_setcolor (term, grub_color_menu_normal, grub_color_menu_highlight);
366 draw_border (term, *num_entries);
367 grub_term_setcolor (term, old_color_normal, old_color_highlight);
368 print_message (nested, edit, term, 0);
371 static void
372 menu_text_print_timeout (int timeout, void *dataptr)
374 const char *msg =
375 _("The highlighted entry will be executed automatically in %ds.");
376 struct menu_viewer_data *data = dataptr;
377 char *msg_translated;
378 int posx;
380 grub_term_gotoxy (data->term, 0, grub_term_height (data->term) - 3);
382 msg_translated = grub_xasprintf (msg, timeout);
383 if (!msg_translated)
385 grub_print_error ();
386 grub_errno = GRUB_ERR_NONE;
387 return;
390 grub_print_message_indented (msg_translated, 3, 0, data->term);
392 posx = grub_term_getxy (data->term) >> 8;
393 grub_print_spaces (data->term, grub_term_width (data->term) - posx - 1);
395 grub_term_gotoxy (data->term,
396 grub_term_cursor_x (data->term),
397 GRUB_TERM_FIRST_ENTRY_Y + data->offset);
398 grub_term_refresh (data->term);
401 static void
402 menu_text_set_chosen_entry (int entry, void *dataptr)
404 struct menu_viewer_data *data = dataptr;
405 int oldoffset = data->offset;
406 int complete_redraw = 0;
408 data->offset = entry - data->first;
409 if (data->offset > data->num_entries - 1)
411 data->first = entry - (data->num_entries - 1);
412 data->offset = data->num_entries - 1;
413 complete_redraw = 1;
415 if (data->offset < 0)
417 data->offset = 0;
418 data->first = entry;
419 complete_redraw = 1;
421 if (complete_redraw)
422 print_entries (data->menu, data);
423 else
425 print_entry (GRUB_TERM_FIRST_ENTRY_Y + oldoffset, 0,
426 grub_menu_get_entry (data->menu, data->first + oldoffset),
427 data->term);
428 print_entry (GRUB_TERM_FIRST_ENTRY_Y + data->offset, 1,
429 grub_menu_get_entry (data->menu, data->first + data->offset),
430 data->term);
432 grub_term_refresh (data->term);
435 static void
436 menu_text_fini (void *dataptr)
438 struct menu_viewer_data *data = dataptr;
440 grub_term_setcursor (data->term, 1);
441 grub_term_cls (data->term);
445 static void
446 menu_text_clear_timeout (void *dataptr)
448 struct menu_viewer_data *data = dataptr;
450 grub_term_gotoxy (data->term, 0, grub_term_height (data->term) - 3);
451 grub_print_spaces (data->term, grub_term_width (data->term) - 1);
452 grub_term_gotoxy (data->term, grub_term_cursor_x (data->term),
453 GRUB_TERM_FIRST_ENTRY_Y + data->offset);
454 grub_term_refresh (data->term);
457 grub_err_t
458 grub_menu_try_text (struct grub_term_output *term,
459 int entry, grub_menu_t menu, int nested)
461 struct menu_viewer_data *data;
462 struct grub_menu_viewer *instance;
464 instance = grub_zalloc (sizeof (*instance));
465 if (!instance)
466 return grub_errno;
468 data = grub_zalloc (sizeof (*data));
469 if (!data)
471 grub_free (instance);
472 return grub_errno;
475 data->term = term;
476 instance->data = data;
477 instance->set_chosen_entry = menu_text_set_chosen_entry;
478 instance->print_timeout = menu_text_print_timeout;
479 instance->clear_timeout = menu_text_clear_timeout;
480 instance->fini = menu_text_fini;
482 data->menu = menu;
484 data->offset = entry;
485 data->first = 0;
487 grub_term_setcursor (data->term, 0);
488 grub_menu_init_page (nested, 0, &data->num_entries, data->term);
490 if (data->offset > data->num_entries - 1)
492 data->first = data->offset - (data->num_entries - 1);
493 data->offset = data->num_entries - 1;
496 print_entries (menu, data);
497 grub_term_refresh (data->term);
498 grub_menu_register_viewer (instance);
500 return GRUB_ERR_NONE;