]> git.proxmox.com Git - grub2.git/blob - normal/menu.c
2005-08-21 Yoshinori K. Okuji <okuji@enbug.org>
[grub2.git] / normal / menu.c
1 /*
2 * GRUB -- GRand Unified Bootloader
3 * Copyright (C) 2003,2004,2005 Free Software Foundation, Inc.
4 *
5 * This program 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 2 of the License, or
8 * (at your option) any later version.
9 *
10 * This program 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.
14 *
15 * You should have received a copy of the GNU General Public License
16 * along with this program; if not, write to the Free Software
17 * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
18 */
19
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/machine/time.h>
26
27 static void
28 draw_border (void)
29 {
30 unsigned i;
31
32 grub_setcolorstate (GRUB_TERM_COLOR_NORMAL);
33
34 grub_gotoxy (GRUB_TERM_MARGIN, GRUB_TERM_TOP_BORDER_Y);
35 grub_putcode (GRUB_TERM_DISP_UL);
36 for (i = 0; i < (unsigned) GRUB_TERM_BORDER_WIDTH - 2; i++)
37 grub_putcode (GRUB_TERM_DISP_HLINE);
38 grub_putcode (GRUB_TERM_DISP_UR);
39
40 for (i = 0; i < (unsigned) GRUB_TERM_NUM_ENTRIES; i++)
41 {
42 grub_gotoxy (GRUB_TERM_MARGIN, GRUB_TERM_TOP_BORDER_Y + i + 1);
43 grub_putcode (GRUB_TERM_DISP_VLINE);
44 grub_gotoxy (GRUB_TERM_MARGIN + GRUB_TERM_BORDER_WIDTH - 1,
45 GRUB_TERM_TOP_BORDER_Y + i + 1);
46 grub_putcode (GRUB_TERM_DISP_VLINE);
47 }
48
49 grub_gotoxy (GRUB_TERM_MARGIN,
50 GRUB_TERM_TOP_BORDER_Y + GRUB_TERM_NUM_ENTRIES + 1);
51 grub_putcode (GRUB_TERM_DISP_LL);
52 for (i = 0; i < (unsigned) GRUB_TERM_BORDER_WIDTH - 2; i++)
53 grub_putcode (GRUB_TERM_DISP_HLINE);
54 grub_putcode (GRUB_TERM_DISP_LR);
55
56 grub_setcolorstate (GRUB_TERM_COLOR_STANDARD);
57
58 grub_gotoxy (GRUB_TERM_MARGIN,
59 (GRUB_TERM_TOP_BORDER_Y + GRUB_TERM_NUM_ENTRIES
60 + GRUB_TERM_MARGIN + 1));
61 }
62
63 static void
64 print_message (int nested, int edit)
65 {
66 if (edit)
67 {
68 grub_printf ("\n\
69 Minimum Emacs-like screen editing is supported. TAB lists\n\
70 available completions. Press C-x (\'x\' with Ctrl) to boot,\n\
71 C-c (\'c\' with Ctrl) for a command-line or ESC to return menu.");
72 }
73 else
74 {
75 grub_printf ("\n\
76 Use the %C and %C keys to select which entry is highlighted.\n",
77 (grub_uint32_t) GRUB_TERM_DISP_UP, (grub_uint32_t) GRUB_TERM_DISP_DOWN);
78 grub_printf ("\
79 Press enter to boot the selected OS, \'e\' to edit the\n\
80 commands before booting or \'c\' for a command-line.");
81 if (nested)
82 grub_printf ("\n\
83 ESC to return previous menu.");
84 }
85
86 }
87
88 static grub_menu_entry_t
89 get_entry (grub_menu_t menu, int no)
90 {
91 grub_menu_entry_t e;
92
93 for (e = menu->entry_list; e && no > 0; e = e->next, no--)
94 ;
95
96 return e;
97 }
98
99 static void
100 print_entry (int y, int highlight, grub_menu_entry_t entry)
101 {
102 int x;
103 const char *title;
104 grub_ssize_t len;
105 grub_uint32_t *unicode_title;
106 grub_ssize_t i;
107
108 title = entry ? entry->title : "";
109 unicode_title = grub_malloc (grub_strlen (title) * sizeof (*unicode_title));
110 if (! unicode_title)
111 /* XXX How to show this error? */
112 return;
113
114 len = grub_utf8_to_ucs4 (unicode_title, title, grub_strlen (title));
115 if (len < 0)
116 {
117 /* It is an invalid sequence. */
118 grub_free (unicode_title);
119 return;
120 }
121
122 grub_setcolorstate (highlight
123 ? GRUB_TERM_COLOR_HIGHLIGHT
124 : GRUB_TERM_COLOR_NORMAL);
125
126 grub_gotoxy (GRUB_TERM_LEFT_BORDER_X + GRUB_TERM_MARGIN, y);
127
128 for (x = GRUB_TERM_LEFT_BORDER_X + GRUB_TERM_MARGIN + 1, i = 0;
129 x < GRUB_TERM_LEFT_BORDER_X + GRUB_TERM_BORDER_WIDTH - GRUB_TERM_MARGIN;
130 i++)
131 {
132 if (i < len
133 && x <= (GRUB_TERM_LEFT_BORDER_X + GRUB_TERM_BORDER_WIDTH
134 - GRUB_TERM_MARGIN - 1))
135 {
136 grub_ssize_t width;
137
138 width = grub_getcharwidth (unicode_title[i]);
139
140 if (x + width > (GRUB_TERM_LEFT_BORDER_X + GRUB_TERM_BORDER_WIDTH
141 - GRUB_TERM_MARGIN - 1))
142 grub_putcode (GRUB_TERM_DISP_RIGHT);
143 else
144 grub_putcode (unicode_title[i]);
145
146 x += width;
147 }
148 else
149 {
150 grub_putchar (' ');
151 x++;
152 }
153 }
154 grub_gotoxy (GRUB_TERM_CURSOR_X, y);
155
156 grub_setcolorstate (GRUB_TERM_COLOR_STANDARD);
157 grub_free (unicode_title);
158 }
159
160 static void
161 print_entries (grub_menu_t menu, int first, int offset)
162 {
163 grub_menu_entry_t e;
164 int i;
165
166 grub_gotoxy (GRUB_TERM_LEFT_BORDER_X + GRUB_TERM_BORDER_WIDTH,
167 GRUB_TERM_FIRST_ENTRY_Y);
168
169 if (first)
170 grub_putcode (GRUB_TERM_DISP_UP);
171 else
172 grub_putchar (' ');
173
174 e = get_entry (menu, first);
175
176 for (i = 0; i < GRUB_TERM_NUM_ENTRIES; i++)
177 {
178 print_entry (GRUB_TERM_FIRST_ENTRY_Y + i, offset == i, e);
179 if (e)
180 e = e->next;
181 }
182
183 grub_gotoxy (GRUB_TERM_LEFT_BORDER_X + GRUB_TERM_BORDER_WIDTH,
184 GRUB_TERM_TOP_BORDER_Y + GRUB_TERM_NUM_ENTRIES);
185
186 if (e)
187 grub_putcode (GRUB_TERM_DISP_DOWN);
188 else
189 grub_putchar (' ');
190
191 grub_gotoxy (GRUB_TERM_CURSOR_X, GRUB_TERM_FIRST_ENTRY_Y + offset);
192 }
193
194 /* Initialize the screen. If NESTED is non-zero, assume that this menu
195 is run from another menu or a command-line. If EDIT is non-zero, show
196 a message for the menu entry editor. */
197 void
198 grub_menu_init_page (int nested, int edit)
199 {
200 grub_normal_init_page ();
201 draw_border ();
202 print_message (nested, edit);
203 }
204
205 static int
206 run_menu (grub_menu_t menu, int nested)
207 {
208 int first, offset;
209 unsigned long saved_time;
210
211 first = 0;
212 offset = menu->default_entry;
213 if (offset > GRUB_TERM_NUM_ENTRIES - 1)
214 {
215 first = offset - (GRUB_TERM_NUM_ENTRIES - 1);
216 offset = GRUB_TERM_NUM_ENTRIES - 1;
217 }
218
219 /* Initialize the time. */
220 saved_time = grub_get_rtc ();
221
222 refresh:
223 grub_setcursor (0);
224 grub_menu_init_page (nested, 0);
225 print_entries (menu, first, offset);
226 grub_refresh ();
227
228 while (1)
229 {
230 int c;
231
232 if (menu->timeout > 0)
233 {
234 unsigned long current_time;
235
236 current_time = grub_get_rtc ();
237 if (current_time - saved_time >= GRUB_TICKS_PER_SECOND)
238 {
239 menu->timeout--;
240 saved_time = current_time;
241 }
242
243 grub_gotoxy (0, GRUB_TERM_HEIGHT - 3);
244 /* NOTE: Do not remove the trailing space characters.
245 They are required to clear the line. */
246 grub_printf ("\
247 The highlighted entry will be booted automatically in %d seconds. ",
248 menu->timeout);
249 grub_gotoxy (GRUB_TERM_CURSOR_X, GRUB_TERM_FIRST_ENTRY_Y + offset);
250 grub_refresh ();
251 }
252
253 if (menu->timeout == 0)
254 {
255 menu->timeout = -1;
256 return menu->default_entry;
257 }
258
259 if (grub_checkkey () >= 0 || menu->timeout < 0)
260 {
261 c = GRUB_TERM_ASCII_CHAR (grub_getkey ());
262
263 if (menu->timeout >= 0)
264 {
265 grub_gotoxy (0, GRUB_TERM_HEIGHT - 3);
266 grub_printf ("\
267 ");
268 menu->timeout = -1;
269 menu->fallback_entry = -1;
270 grub_gotoxy (GRUB_TERM_CURSOR_X, GRUB_TERM_FIRST_ENTRY_Y + offset);
271 }
272
273 switch (c)
274 {
275 case 16:
276 case '^':
277 if (offset > 0)
278 {
279 print_entry (GRUB_TERM_FIRST_ENTRY_Y + offset, 0,
280 get_entry (menu, first + offset));
281 offset--;
282 print_entry (GRUB_TERM_FIRST_ENTRY_Y + offset, 1,
283 get_entry (menu, first + offset));
284 }
285 else if (first > 0)
286 {
287 first--;
288 print_entries (menu, first, offset);
289 }
290 break;
291
292 case 14:
293 case 'v':
294 if (menu->size > first + offset + 1)
295 {
296 if (offset < GRUB_TERM_NUM_ENTRIES - 1)
297 {
298 print_entry (GRUB_TERM_FIRST_ENTRY_Y + offset, 0,
299 get_entry (menu, first + offset));
300 offset++;
301 print_entry (GRUB_TERM_FIRST_ENTRY_Y + offset, 1,
302 get_entry (menu, first + offset));
303 }
304 else
305 {
306 first++;
307 print_entries (menu, first, offset);
308 }
309 }
310 break;
311
312 case '\n':
313 case '\r':
314 case 6:
315 grub_setcursor (1);
316 return first + offset;
317
318 case '\e':
319 if (nested)
320 {
321 grub_setcursor (1);
322 return -1;
323 }
324 break;
325
326 case 'c':
327 grub_cmdline_run (1);
328 goto refresh;
329
330 case 'e':
331 grub_menu_entry_run (get_entry (menu, first + offset));
332 goto refresh;
333
334 default:
335 break;
336 }
337
338 grub_refresh ();
339 }
340 }
341
342 /* Never reach here. */
343 return -1;
344 }
345
346 /* Run a menu entry. */
347 static void
348 run_menu_entry (grub_menu_entry_t entry)
349 {
350 grub_command_list_t cl;
351
352 for (cl = entry->command_list; cl != 0; cl = cl->next)
353 {
354 if (cl->command[0] == '\0')
355 /* Ignore an empty command line. */
356 continue;
357
358 if (grub_command_execute (cl->command, 0) != 0)
359 break;
360 }
361
362 if (grub_errno == GRUB_ERR_NONE && grub_loader_is_loaded ())
363 /* Implicit execution of boot, only if something is loaded. */
364 grub_command_execute ("boot", 0);
365 }
366
367 void
368 grub_menu_run (grub_menu_t menu, int nested)
369 {
370 while (1)
371 {
372 int boot_entry;
373 grub_menu_entry_t e;
374
375 boot_entry = run_menu (menu, nested);
376 if (boot_entry < 0)
377 break;
378
379 grub_cls ();
380 grub_setcursor (1);
381
382 e = get_entry (menu, boot_entry);
383 grub_printf (" Booting \'%s\'\n\n", e->title);
384
385 run_menu_entry (e);
386
387 /* Deal with a fallback entry. */
388 /* FIXME: Multiple fallback entries like GRUB Legacy. */
389 if (menu->fallback_entry >= 0)
390 {
391 grub_print_error ();
392 grub_errno = GRUB_ERR_NONE;
393
394 e = get_entry (menu, menu->fallback_entry);
395 menu->fallback_entry = -1;
396 grub_printf ("\n Falling back to \'%s\'\n\n", e->title);
397 run_menu_entry (e);
398 }
399
400 if (grub_errno != GRUB_ERR_NONE)
401 {
402 grub_print_error ();
403 grub_errno = GRUB_ERR_NONE;
404
405 /* Wait until the user pushes any key so that the user
406 can see what happened. */
407 grub_printf ("\nPress any key to continue...");
408 (void) grub_getkey ();
409 }
410 }
411 }