2 * GRUB -- GRand Unified Bootloader
3 * Copyright (C) 2003,2004,2005,2006,2007,2008,2009 Free Software Foundation, Inc.
5 * GRUB is free software: you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License as published by
7 * the Free Software Foundation, either version 3 of the License, or
8 * (at your option) any later version.
10 * GRUB is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
15 * You should have received a copy of the GNU General Public License
16 * along with GRUB. If not, see <http://www.gnu.org/licenses/>.
19 #include <grub/normal.h>
20 #include <grub/term.h>
21 #include <grub/misc.h>
22 #include <grub/loader.h>
24 #include <grub/time.h>
26 #include <grub/script.h>
28 static grub_uint8_t grub_color_menu_normal
;
29 static grub_uint8_t grub_color_menu_highlight
;
31 /* Wait until the user pushes any key so that the user
32 can see what happened. */
34 grub_wait_after_message (void)
36 grub_printf ("\nPress any key to continue...");
37 (void) grub_getkey ();
45 grub_setcolorstate (GRUB_TERM_COLOR_NORMAL
);
47 grub_gotoxy (GRUB_TERM_MARGIN
, GRUB_TERM_TOP_BORDER_Y
);
48 grub_putcode (GRUB_TERM_DISP_UL
);
49 for (i
= 0; i
< (unsigned) GRUB_TERM_BORDER_WIDTH
- 2; i
++)
50 grub_putcode (GRUB_TERM_DISP_HLINE
);
51 grub_putcode (GRUB_TERM_DISP_UR
);
53 for (i
= 0; i
< (unsigned) GRUB_TERM_NUM_ENTRIES
; i
++)
55 grub_gotoxy (GRUB_TERM_MARGIN
, GRUB_TERM_TOP_BORDER_Y
+ i
+ 1);
56 grub_putcode (GRUB_TERM_DISP_VLINE
);
57 grub_gotoxy (GRUB_TERM_MARGIN
+ GRUB_TERM_BORDER_WIDTH
- 1,
58 GRUB_TERM_TOP_BORDER_Y
+ i
+ 1);
59 grub_putcode (GRUB_TERM_DISP_VLINE
);
62 grub_gotoxy (GRUB_TERM_MARGIN
,
63 GRUB_TERM_TOP_BORDER_Y
+ GRUB_TERM_NUM_ENTRIES
+ 1);
64 grub_putcode (GRUB_TERM_DISP_LL
);
65 for (i
= 0; i
< (unsigned) GRUB_TERM_BORDER_WIDTH
- 2; i
++)
66 grub_putcode (GRUB_TERM_DISP_HLINE
);
67 grub_putcode (GRUB_TERM_DISP_LR
);
69 grub_setcolorstate (GRUB_TERM_COLOR_NORMAL
);
71 grub_gotoxy (GRUB_TERM_MARGIN
,
72 (GRUB_TERM_TOP_BORDER_Y
+ GRUB_TERM_NUM_ENTRIES
73 + GRUB_TERM_MARGIN
+ 1));
77 print_message (int nested
, int edit
)
79 grub_setcolorstate (GRUB_TERM_COLOR_NORMAL
);
84 Minimum Emacs-like screen editing is supported. TAB lists\n\
85 completions. Press Ctrl-x to boot, Ctrl-c for a command-line\n\
86 or ESC to return menu.");
91 Use the %C and %C keys to select which entry is highlighted.\n",
92 (grub_uint32_t
) GRUB_TERM_DISP_UP
, (grub_uint32_t
) GRUB_TERM_DISP_DOWN
);
94 Press enter to boot the selected OS, \'e\' to edit the\n\
95 commands before booting or \'c\' for a command-line.");
98 ESC to return previous menu.");
103 static grub_menu_entry_t
104 get_entry (grub_menu_t menu
, int no
)
108 for (e
= menu
->entry_list
; e
&& no
> 0; e
= e
->next
, no
--)
115 print_entry (int y
, int highlight
, grub_menu_entry_t entry
)
119 grub_size_t title_len
;
121 grub_uint32_t
*unicode_title
;
123 grub_uint8_t old_color_normal
, old_color_highlight
;
125 title
= entry
? entry
->title
: "";
126 title_len
= grub_strlen (title
);
127 unicode_title
= grub_malloc (title_len
* sizeof (*unicode_title
));
129 /* XXX How to show this error? */
132 len
= grub_utf8_to_ucs4 (unicode_title
, title_len
,
133 (grub_uint8_t
*) title
, -1, 0);
136 /* It is an invalid sequence. */
137 grub_free (unicode_title
);
141 grub_getcolor (&old_color_normal
, &old_color_highlight
);
142 grub_setcolor (grub_color_menu_normal
, grub_color_menu_highlight
);
143 grub_setcolorstate (highlight
144 ? GRUB_TERM_COLOR_HIGHLIGHT
145 : GRUB_TERM_COLOR_NORMAL
);
147 grub_gotoxy (GRUB_TERM_LEFT_BORDER_X
+ GRUB_TERM_MARGIN
, y
);
149 for (x
= GRUB_TERM_LEFT_BORDER_X
+ GRUB_TERM_MARGIN
+ 1, i
= 0;
150 x
< GRUB_TERM_LEFT_BORDER_X
+ GRUB_TERM_BORDER_WIDTH
- GRUB_TERM_MARGIN
;
154 && x
<= (GRUB_TERM_LEFT_BORDER_X
+ GRUB_TERM_BORDER_WIDTH
155 - GRUB_TERM_MARGIN
- 1))
159 width
= grub_getcharwidth (unicode_title
[i
]);
161 if (x
+ width
> (GRUB_TERM_LEFT_BORDER_X
+ GRUB_TERM_BORDER_WIDTH
162 - GRUB_TERM_MARGIN
- 1))
163 grub_putcode (GRUB_TERM_DISP_RIGHT
);
165 grub_putcode (unicode_title
[i
]);
175 grub_setcolorstate (GRUB_TERM_COLOR_NORMAL
);
178 grub_gotoxy (GRUB_TERM_CURSOR_X
, y
);
180 grub_setcolor (old_color_normal
, old_color_highlight
);
181 grub_setcolorstate (GRUB_TERM_COLOR_NORMAL
);
182 grub_free (unicode_title
);
186 print_entries (grub_menu_t menu
, int first
, int offset
)
191 grub_gotoxy (GRUB_TERM_LEFT_BORDER_X
+ GRUB_TERM_BORDER_WIDTH
,
192 GRUB_TERM_FIRST_ENTRY_Y
);
195 grub_putcode (GRUB_TERM_DISP_UP
);
199 e
= get_entry (menu
, first
);
201 for (i
= 0; i
< GRUB_TERM_NUM_ENTRIES
; i
++)
203 print_entry (GRUB_TERM_FIRST_ENTRY_Y
+ i
, offset
== i
, e
);
208 grub_gotoxy (GRUB_TERM_LEFT_BORDER_X
+ GRUB_TERM_BORDER_WIDTH
,
209 GRUB_TERM_TOP_BORDER_Y
+ GRUB_TERM_NUM_ENTRIES
);
212 grub_putcode (GRUB_TERM_DISP_DOWN
);
216 grub_gotoxy (GRUB_TERM_CURSOR_X
, GRUB_TERM_FIRST_ENTRY_Y
+ offset
);
219 /* Initialize the screen. If NESTED is non-zero, assume that this menu
220 is run from another menu or a command-line. If EDIT is non-zero, show
221 a message for the menu entry editor. */
223 grub_menu_init_page (int nested
, int edit
)
225 grub_uint8_t old_color_normal
, old_color_highlight
;
227 grub_getcolor (&old_color_normal
, &old_color_highlight
);
229 /* By default, use the same colors for the menu. */
230 grub_color_menu_normal
= old_color_normal
;
231 grub_color_menu_highlight
= old_color_highlight
;
233 /* Then give user a chance to replace them. */
234 grub_parse_color_name_pair (&grub_color_menu_normal
, grub_env_get ("menu_color_normal"));
235 grub_parse_color_name_pair (&grub_color_menu_highlight
, grub_env_get ("menu_color_highlight"));
237 grub_normal_init_page ();
238 grub_setcolor (grub_color_menu_normal
, grub_color_menu_highlight
);
240 grub_setcolor (old_color_normal
, old_color_highlight
);
241 print_message (nested
, edit
);
244 /* Return the current timeout. If the variable "timeout" is not set or
245 invalid, return -1. */
252 val
= grub_env_get ("timeout");
258 timeout
= (int) grub_strtoul (val
, 0, 0);
260 /* If the value is invalid, unset the variable. */
261 if (grub_errno
!= GRUB_ERR_NONE
)
263 grub_env_unset ("timeout");
264 grub_errno
= GRUB_ERR_NONE
;
273 /* Set current timeout in the variable "timeout". */
275 set_timeout (int timeout
)
277 /* Ignore TIMEOUT if it is zero, because it will be unset really soon. */
282 grub_sprintf (buf
, "%d", timeout
);
283 grub_env_set ("timeout", buf
);
287 /* Get the entry number from the variable NAME. */
289 get_entry_number (const char *name
)
294 val
= grub_env_get (name
);
300 entry
= (int) grub_strtoul (val
, 0, 0);
302 if (grub_errno
!= GRUB_ERR_NONE
)
304 grub_errno
= GRUB_ERR_NONE
;
314 print_timeout (int timeout
, int offset
, int second_stage
)
316 /* NOTE: Do not remove the trailing space characters.
317 They are required to clear the line. */
318 char *msg
= " The highlighted entry will be booted automatically in %ds. ";
319 char *msg_end
= grub_strchr (msg
, '%');
321 grub_gotoxy (second_stage
? (msg_end
- msg
) : 0, GRUB_TERM_HEIGHT
- 3);
322 grub_printf (second_stage
? msg_end
: msg
, timeout
);
323 grub_gotoxy (GRUB_TERM_CURSOR_X
, GRUB_TERM_FIRST_ENTRY_Y
+ offset
);
328 run_menu (grub_menu_t menu
, int nested
)
331 grub_uint64_t saved_time
;
337 default_entry
= get_entry_number ("default");
339 /* If DEFAULT_ENTRY is not within the menu entries, fall back to
341 if (default_entry
< 0 || default_entry
>= menu
->size
)
344 /* If timeout is 0, drawing is pointless (and ugly). */
345 if (get_timeout () == 0)
346 return default_entry
;
348 offset
= default_entry
;
349 if (offset
> GRUB_TERM_NUM_ENTRIES
- 1)
351 first
= offset
- (GRUB_TERM_NUM_ENTRIES
- 1);
352 offset
= GRUB_TERM_NUM_ENTRIES
- 1;
355 /* Initialize the time. */
356 saved_time
= grub_get_time_ms ();
360 grub_menu_init_page (nested
, 0);
361 print_entries (menu
, first
, offset
);
364 timeout
= get_timeout ();
367 print_timeout (timeout
, offset
, 0);
372 timeout
= get_timeout ();
376 grub_uint64_t current_time
;
378 current_time
= grub_get_time_ms ();
379 if (current_time
- saved_time
>= 1000)
382 set_timeout (timeout
);
383 saved_time
= current_time
;
384 print_timeout (timeout
, offset
, 1);
390 grub_env_unset ("timeout");
391 return default_entry
;
394 if (grub_checkkey () >= 0 || timeout
< 0)
396 c
= GRUB_TERM_ASCII_CHAR (grub_getkey ());
400 grub_gotoxy (0, GRUB_TERM_HEIGHT
- 3);
403 grub_env_unset ("timeout");
404 grub_env_unset ("fallback");
405 grub_gotoxy (GRUB_TERM_CURSOR_X
, GRUB_TERM_FIRST_ENTRY_Y
+ offset
);
413 print_entries (menu
, first
, offset
);
417 offset
= menu
->size
- 1;
418 if (offset
> GRUB_TERM_NUM_ENTRIES
- 1)
420 first
= offset
- (GRUB_TERM_NUM_ENTRIES
- 1);
421 offset
= GRUB_TERM_NUM_ENTRIES
- 1;
423 print_entries (menu
, first
, offset
);
430 print_entry (GRUB_TERM_FIRST_ENTRY_Y
+ offset
, 0,
431 get_entry (menu
, first
+ offset
));
433 print_entry (GRUB_TERM_FIRST_ENTRY_Y
+ offset
, 1,
434 get_entry (menu
, first
+ offset
));
439 print_entries (menu
, first
, offset
);
445 if (menu
->size
> first
+ offset
+ 1)
447 if (offset
< GRUB_TERM_NUM_ENTRIES
- 1)
449 print_entry (GRUB_TERM_FIRST_ENTRY_Y
+ offset
, 0,
450 get_entry (menu
, first
+ offset
));
452 print_entry (GRUB_TERM_FIRST_ENTRY_Y
+ offset
, 1,
453 get_entry (menu
, first
+ offset
));
458 print_entries (menu
, first
, offset
);
463 case GRUB_TERM_PPAGE
:
470 first
-= GRUB_TERM_NUM_ENTRIES
;
478 print_entries (menu
, first
, offset
);
481 case GRUB_TERM_NPAGE
:
484 offset
+= GRUB_TERM_NUM_ENTRIES
- 1;
485 if (first
+ offset
>= menu
->size
)
487 offset
= menu
->size
- first
- 1;
492 first
+= GRUB_TERM_NUM_ENTRIES
;
494 if (first
+ offset
>= menu
->size
)
496 first
-= GRUB_TERM_NUM_ENTRIES
;
497 offset
+= GRUB_TERM_NUM_ENTRIES
;
499 if (offset
> menu
->size
- 1 ||
500 offset
> GRUB_TERM_NUM_ENTRIES
- 1)
502 offset
= menu
->size
- first
- 1;
504 if (offset
> GRUB_TERM_NUM_ENTRIES
)
506 first
+= offset
- GRUB_TERM_NUM_ENTRIES
+ 1;
507 offset
= GRUB_TERM_NUM_ENTRIES
- 1;
511 print_entries (menu
, first
, offset
);
518 return first
+ offset
;
529 grub_cmdline_run (1);
534 grub_menu_entry_t e
= get_entry (menu
, first
+ offset
);
536 grub_menu_entry_run (e
);
548 /* Never reach here. */
552 /* Run a menu entry. */
554 run_menu_entry (grub_menu_entry_t entry
)
556 grub_script_execute (entry
->commands
);
558 if (grub_errno
== GRUB_ERR_NONE
&& grub_loader_is_loaded ())
559 /* Implicit execution of boot, only if something is loaded. */
560 grub_command_execute ("boot", 0);
564 grub_menu_run (grub_menu_t menu
, int nested
)
572 boot_entry
= run_menu (menu
, nested
);
576 e
= get_entry (menu
, boot_entry
);
578 continue; /* Menu is empty. */
583 grub_printf (" Booting \'%s\'\n\n", e
->title
);
587 /* Deal with a fallback entry. */
588 /* FIXME: Multiple fallback entries like GRUB Legacy. */
589 fallback_entry
= get_entry_number ("fallback");
590 if (fallback_entry
>= 0)
593 grub_errno
= GRUB_ERR_NONE
;
595 e
= get_entry (menu
, fallback_entry
);
596 grub_env_unset ("fallback");
597 grub_printf ("\n Falling back to \'%s\'\n\n", e
->title
);
601 if (grub_errno
!= GRUB_ERR_NONE
)
604 grub_errno
= GRUB_ERR_NONE
;
606 grub_wait_after_message ();