2 * GRUB -- GRand Unified Bootloader
3 * Copyright (C) 2003,2004,2005,2006,2007,2008 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 available completions. Press C-x (\'x\' with Ctrl) to boot,\n\
86 C-c (\'c\' with Ctrl) for a command-line 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
)
120 grub_uint32_t
*unicode_title
;
122 grub_uint8_t old_color_normal
, old_color_highlight
;
124 title
= entry
? entry
->title
: "";
125 unicode_title
= grub_malloc (grub_strlen (title
) * sizeof (*unicode_title
));
127 /* XXX How to show this error? */
130 len
= grub_utf8_to_ucs4 (unicode_title
, (grub_uint8_t
*) title
,
131 grub_strlen (title
));
134 /* It is an invalid sequence. */
135 grub_free (unicode_title
);
139 grub_getcolor (&old_color_normal
, &old_color_highlight
);
140 grub_setcolor (grub_color_menu_normal
, grub_color_menu_highlight
);
141 grub_setcolorstate (highlight
142 ? GRUB_TERM_COLOR_HIGHLIGHT
143 : GRUB_TERM_COLOR_NORMAL
);
145 grub_gotoxy (GRUB_TERM_LEFT_BORDER_X
+ GRUB_TERM_MARGIN
, y
);
147 for (x
= GRUB_TERM_LEFT_BORDER_X
+ GRUB_TERM_MARGIN
+ 1, i
= 0;
148 x
< GRUB_TERM_LEFT_BORDER_X
+ GRUB_TERM_BORDER_WIDTH
- GRUB_TERM_MARGIN
;
152 && x
<= (GRUB_TERM_LEFT_BORDER_X
+ GRUB_TERM_BORDER_WIDTH
153 - GRUB_TERM_MARGIN
- 1))
157 width
= grub_getcharwidth (unicode_title
[i
]);
159 if (x
+ width
> (GRUB_TERM_LEFT_BORDER_X
+ GRUB_TERM_BORDER_WIDTH
160 - GRUB_TERM_MARGIN
- 1))
161 grub_putcode (GRUB_TERM_DISP_RIGHT
);
163 grub_putcode (unicode_title
[i
]);
173 grub_setcolorstate (GRUB_TERM_COLOR_NORMAL
);
176 grub_gotoxy (GRUB_TERM_CURSOR_X
, y
);
178 grub_setcolor (old_color_normal
, old_color_highlight
);
179 grub_setcolorstate (GRUB_TERM_COLOR_NORMAL
);
180 grub_free (unicode_title
);
184 print_entries (grub_menu_t menu
, int first
, int offset
)
189 grub_gotoxy (GRUB_TERM_LEFT_BORDER_X
+ GRUB_TERM_BORDER_WIDTH
,
190 GRUB_TERM_FIRST_ENTRY_Y
);
193 grub_putcode (GRUB_TERM_DISP_UP
);
197 e
= get_entry (menu
, first
);
199 for (i
= 0; i
< GRUB_TERM_NUM_ENTRIES
; i
++)
201 print_entry (GRUB_TERM_FIRST_ENTRY_Y
+ i
, offset
== i
, e
);
206 grub_gotoxy (GRUB_TERM_LEFT_BORDER_X
+ GRUB_TERM_BORDER_WIDTH
,
207 GRUB_TERM_TOP_BORDER_Y
+ GRUB_TERM_NUM_ENTRIES
);
210 grub_putcode (GRUB_TERM_DISP_DOWN
);
214 grub_gotoxy (GRUB_TERM_CURSOR_X
, GRUB_TERM_FIRST_ENTRY_Y
+ offset
);
217 /* Initialize the screen. If NESTED is non-zero, assume that this menu
218 is run from another menu or a command-line. If EDIT is non-zero, show
219 a message for the menu entry editor. */
221 grub_menu_init_page (int nested
, int edit
)
223 grub_uint8_t old_color_normal
, old_color_highlight
;
225 grub_getcolor (&old_color_normal
, &old_color_highlight
);
227 /* By default, use the same colors for the menu. */
228 grub_color_menu_normal
= old_color_normal
;
229 grub_color_menu_highlight
= old_color_highlight
;
231 /* Then give user a chance to replace them. */
232 grub_parse_color_name_pair (&grub_color_menu_normal
, grub_env_get ("menu_color_normal"));
233 grub_parse_color_name_pair (&grub_color_menu_highlight
, grub_env_get ("menu_color_highlight"));
235 grub_normal_init_page ();
236 grub_setcolor (grub_color_menu_normal
, grub_color_menu_highlight
);
238 grub_setcolor (old_color_normal
, old_color_highlight
);
239 print_message (nested
, edit
);
242 /* Return the current timeout. If the variable "timeout" is not set or
243 invalid, return -1. */
250 val
= grub_env_get ("timeout");
256 timeout
= (int) grub_strtoul (val
, 0, 0);
258 /* If the value is invalid, unset the variable. */
259 if (grub_errno
!= GRUB_ERR_NONE
)
261 grub_env_unset ("timeout");
262 grub_errno
= GRUB_ERR_NONE
;
271 /* Set current timeout in the variable "timeout". */
273 set_timeout (int timeout
)
275 /* Ignore TIMEOUT if it is zero, because it will be unset really soon. */
280 grub_sprintf (buf
, "%d", timeout
);
281 grub_env_set ("timeout", buf
);
285 /* Get the entry number from the variable NAME. */
287 get_entry_number (const char *name
)
292 val
= grub_env_get (name
);
298 entry
= (int) grub_strtoul (val
, 0, 0);
300 if (grub_errno
!= GRUB_ERR_NONE
)
302 grub_errno
= GRUB_ERR_NONE
;
312 print_timeout (int timeout
, int offset
, int second_stage
)
314 /* NOTE: Do not remove the trailing space characters.
315 They are required to clear the line. */
316 char *msg
= " The highlighted entry will be booted automatically in %ds. ";
317 char *msg_end
= grub_strchr (msg
, '%');
319 grub_gotoxy (second_stage
? (msg_end
- msg
) : 0, GRUB_TERM_HEIGHT
- 3);
320 grub_printf (second_stage
? msg_end
: msg
, timeout
);
321 grub_gotoxy (GRUB_TERM_CURSOR_X
, GRUB_TERM_FIRST_ENTRY_Y
+ offset
);
326 run_menu (grub_menu_t menu
, int nested
)
329 grub_uint64_t saved_time
;
335 default_entry
= get_entry_number ("default");
337 /* If DEFAULT_ENTRY is not within the menu entries, fall back to
339 if (default_entry
< 0 || default_entry
>= menu
->size
)
342 /* If timeout is 0, drawing is pointless (and ugly). */
343 if (get_timeout () == 0)
344 return default_entry
;
346 offset
= default_entry
;
347 if (offset
> GRUB_TERM_NUM_ENTRIES
- 1)
349 first
= offset
- (GRUB_TERM_NUM_ENTRIES
- 1);
350 offset
= GRUB_TERM_NUM_ENTRIES
- 1;
353 /* Initialize the time. */
354 saved_time
= grub_get_time_ms ();
358 grub_menu_init_page (nested
, 0);
359 print_entries (menu
, first
, offset
);
362 timeout
= get_timeout ();
365 print_timeout (timeout
, offset
, 0);
370 timeout
= get_timeout ();
374 grub_uint64_t current_time
;
376 current_time
= grub_get_time_ms ();
377 if (current_time
- saved_time
>= 1000)
380 set_timeout (timeout
);
381 saved_time
= current_time
;
382 print_timeout (timeout
, offset
, 1);
388 grub_env_unset ("timeout");
389 return default_entry
;
392 if (grub_checkkey () >= 0 || timeout
< 0)
394 c
= GRUB_TERM_ASCII_CHAR (grub_getkey ());
398 grub_gotoxy (0, GRUB_TERM_HEIGHT
- 3);
401 grub_env_unset ("timeout");
402 grub_env_unset ("fallback");
403 grub_gotoxy (GRUB_TERM_CURSOR_X
, GRUB_TERM_FIRST_ENTRY_Y
+ offset
);
411 print_entries (menu
, first
, offset
);
415 offset
= menu
->size
- 1;
416 if (offset
> GRUB_TERM_NUM_ENTRIES
- 1)
418 first
= offset
- (GRUB_TERM_NUM_ENTRIES
- 1);
419 offset
= GRUB_TERM_NUM_ENTRIES
- 1;
421 print_entries (menu
, first
, offset
);
428 print_entry (GRUB_TERM_FIRST_ENTRY_Y
+ offset
, 0,
429 get_entry (menu
, first
+ offset
));
431 print_entry (GRUB_TERM_FIRST_ENTRY_Y
+ offset
, 1,
432 get_entry (menu
, first
+ offset
));
437 print_entries (menu
, first
, offset
);
443 if (menu
->size
> first
+ offset
+ 1)
445 if (offset
< GRUB_TERM_NUM_ENTRIES
- 1)
447 print_entry (GRUB_TERM_FIRST_ENTRY_Y
+ offset
, 0,
448 get_entry (menu
, first
+ offset
));
450 print_entry (GRUB_TERM_FIRST_ENTRY_Y
+ offset
, 1,
451 get_entry (menu
, first
+ offset
));
456 print_entries (menu
, first
, offset
);
461 case GRUB_TERM_PPAGE
:
468 first
-= GRUB_TERM_NUM_ENTRIES
;
476 print_entries (menu
, first
, offset
);
479 case GRUB_TERM_NPAGE
:
482 offset
+= GRUB_TERM_NUM_ENTRIES
- 1;
483 if (first
+ offset
>= menu
->size
)
485 offset
= menu
->size
- first
- 1;
490 first
+= GRUB_TERM_NUM_ENTRIES
;
492 if (first
+ offset
>= menu
->size
)
494 first
-= GRUB_TERM_NUM_ENTRIES
;
495 offset
+= GRUB_TERM_NUM_ENTRIES
;
497 if (offset
> menu
->size
- 1 ||
498 offset
> GRUB_TERM_NUM_ENTRIES
- 1)
500 offset
= menu
->size
- first
- 1;
502 if (offset
> GRUB_TERM_NUM_ENTRIES
)
504 first
+= offset
- GRUB_TERM_NUM_ENTRIES
+ 1;
505 offset
= GRUB_TERM_NUM_ENTRIES
- 1;
509 print_entries (menu
, first
, offset
);
516 return first
+ offset
;
527 grub_cmdline_run (1);
532 grub_menu_entry_t e
= get_entry (menu
, first
+ offset
);
534 grub_menu_entry_run (e
);
546 /* Never reach here. */
550 /* Run a menu entry. */
552 run_menu_entry (grub_menu_entry_t entry
)
554 grub_script_execute (entry
->commands
);
556 if (grub_errno
== GRUB_ERR_NONE
&& grub_loader_is_loaded ())
557 /* Implicit execution of boot, only if something is loaded. */
558 grub_command_execute ("boot", 0);
562 grub_menu_run (grub_menu_t menu
, int nested
)
570 boot_entry
= run_menu (menu
, nested
);
574 e
= get_entry (menu
, boot_entry
);
576 continue; /* Menu is empty. */
581 grub_printf (" Booting \'%s\'\n\n", e
->title
);
585 /* Deal with a fallback entry. */
586 /* FIXME: Multiple fallback entries like GRUB Legacy. */
587 fallback_entry
= get_entry_number ("fallback");
588 if (fallback_entry
>= 0)
591 grub_errno
= GRUB_ERR_NONE
;
593 e
= get_entry (menu
, fallback_entry
);
594 grub_env_unset ("fallback");
595 grub_printf ("\n Falling back to \'%s\'\n\n", e
->title
);
599 if (grub_errno
!= GRUB_ERR_NONE
)
602 grub_errno
= GRUB_ERR_NONE
;
604 grub_wait_after_message ();