]> git.proxmox.com Git - grub2.git/blame - grub-core/normal/menu_text.c
pull-in block-arg branch
[grub2.git] / grub-core / normal / menu_text.c
CommitLineData
772e23da 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.
5 *
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.
10 *
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.
15 *
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/>.
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/time.h>
26#include <grub/env.h>
772e23da 27#include <grub/menu_viewer.h>
0648f857 28#include <grub/i18n.h>
2e713831 29#include <grub/charset.h>
772e23da 30
31static grub_uint8_t grub_color_menu_normal;
32static grub_uint8_t grub_color_menu_highlight;
33
2e713831 34struct menu_viewer_data
772e23da 35{
2e713831
VS
36 int first, offset;
37 grub_menu_t menu;
38 struct grub_term_output *term;
39};
772e23da 40
b99518d1 41grub_ssize_t
2e713831
VS
42grub_getstringwidth (grub_uint32_t * str, const grub_uint32_t * last_position,
43 struct grub_term_output *term)
69055f8a
CPE
44{
45 grub_ssize_t width = 0;
46
47 while (str < last_position)
48 {
0a239a82
VS
49 struct grub_unicode_glyph glyph;
50 str += grub_unicode_aglomerate_comb (str, last_position - str, &glyph);
51 width += grub_term_getcharwidth (term, &glyph);
69055f8a
CPE
52 }
53 return width;
54}
55
7f39d92f 56void
2e713831
VS
57grub_print_message_indented (const char *msg, int margin_left, int margin_right,
58 struct grub_term_output *term)
69055f8a 59{
69055f8a 60 grub_uint32_t *unicode_msg;
b99518d1 61 grub_uint32_t *last_position;
69055f8a 62
b99518d1 63 int msg_len;
69055f8a 64
b99518d1 65 msg_len = grub_utf8_to_ucs4_alloc (msg, &unicode_msg, &last_position);
69055f8a
CPE
66
67 if (msg_len < 0)
68 {
69055f8a
CPE
69 return;
70 }
71
f588f1c8
VS
72 grub_print_ucs4 (unicode_msg, last_position, margin_left, margin_right, term);
73
69055f8a
CPE
74 grub_free (unicode_msg);
75}
76
77
772e23da 78static void
2e713831 79draw_border (struct grub_term_output *term)
772e23da 80{
81 unsigned i;
82
2e713831 83 grub_term_setcolorstate (term, GRUB_TERM_COLOR_NORMAL);
772e23da 84
2e713831 85 grub_term_gotoxy (term, GRUB_TERM_MARGIN, GRUB_TERM_TOP_BORDER_Y);
b764bff2 86 grub_putcode (GRUB_UNICODE_CORNER_UL, term);
2e713831 87 for (i = 0; i < (unsigned) grub_term_border_width (term) - 2; i++)
b764bff2
VS
88 grub_putcode (GRUB_UNICODE_HLINE, term);
89 grub_putcode (GRUB_UNICODE_CORNER_UR, term);
772e23da 90
2e713831 91 for (i = 0; i < (unsigned) grub_term_num_entries (term); i++)
772e23da 92 {
2e713831 93 grub_term_gotoxy (term, GRUB_TERM_MARGIN, GRUB_TERM_TOP_BORDER_Y + i + 1);
b764bff2 94 grub_putcode (GRUB_UNICODE_VLINE, term);
2e713831
VS
95 grub_term_gotoxy (term, GRUB_TERM_MARGIN + grub_term_border_width (term)
96 - 1,
97 GRUB_TERM_TOP_BORDER_Y + i + 1);
b764bff2 98 grub_putcode (GRUB_UNICODE_VLINE, term);
772e23da 99 }
100
2e713831
VS
101 grub_term_gotoxy (term, GRUB_TERM_MARGIN,
102 GRUB_TERM_TOP_BORDER_Y + grub_term_num_entries (term) + 1);
b764bff2 103 grub_putcode (GRUB_UNICODE_CORNER_LL, term);
2e713831 104 for (i = 0; i < (unsigned) grub_term_border_width (term) - 2; i++)
b764bff2
VS
105 grub_putcode (GRUB_UNICODE_HLINE, term);
106 grub_putcode (GRUB_UNICODE_CORNER_LR, term);
772e23da 107
2e713831 108 grub_term_setcolorstate (term, GRUB_TERM_COLOR_NORMAL);
772e23da 109
2e713831
VS
110 grub_term_gotoxy (term, GRUB_TERM_MARGIN,
111 (GRUB_TERM_TOP_BORDER_Y + grub_term_num_entries (term)
112 + GRUB_TERM_MARGIN + 1));
772e23da 113}
114
115static void
2e713831 116print_message (int nested, int edit, struct grub_term_output *term)
772e23da 117{
2e713831 118 grub_term_setcolorstate (term, GRUB_TERM_COLOR_NORMAL);
772e23da 119
120 if (edit)
121 {
2e713831 122 grub_putcode ('\n', term);
25f4e252
EC
123#ifdef GRUB_MACHINE_EFI
124 grub_print_message_indented (_("Minimum Emacs-like screen editing is \
125supported. TAB lists completions. Press F1 to boot, F2=Ctrl-a, F3=Ctrl-e, \
126F4 for a command-line or ESC to discard edits and return to the GRUB menu."),
127 STANDARD_MARGIN, STANDARD_MARGIN, term);
128#else
7f39d92f 129 grub_print_message_indented (_("Minimum Emacs-like screen editing is \
69055f8a 130supported. TAB lists completions. Press Ctrl-x to boot, Ctrl-c for a \
25f4e252
EC
131command-line or ESC to discard edits and return to the GRUB menu."),
132 STANDARD_MARGIN, STANDARD_MARGIN, term);
133#endif
772e23da 134 }
135 else
136 {
8b442f3f
VS
137 const char *msg = _("Use the %C and %C keys to select which "
138 "entry is highlighted.\n");
139 char *msg_translated;
140
b764bff2
VS
141 msg_translated = grub_xasprintf (msg, GRUB_UNICODE_UPARROW,
142 GRUB_UNICODE_DOWNARROW);
8b442f3f
VS
143 if (!msg_translated)
144 return;
c2bd1d18 145 grub_putcode ('\n', term);
2e713831
VS
146 grub_print_message_indented (msg_translated, STANDARD_MARGIN,
147 STANDARD_MARGIN, term);
69055f8a
CPE
148
149 grub_free (msg_translated);
150
772e23da 151 if (nested)
d75d75d9
VS
152 {
153 grub_print_message_indented
154 (_("Press enter to boot the selected OS, "
155 "\'e\' to edit the commands before booting "
156 "or \'c\' for a command-line. ESC to return previous menu.\n"),
157 STANDARD_MARGIN, STANDARD_MARGIN, term);
158 }
159 else
160 {
161 grub_print_message_indented
162 (_("Press enter to boot the selected OS, "
163 "\'e\' to edit the commands before booting "
164 "or \'c\' for a command-line.\n"),
165 STANDARD_MARGIN, STANDARD_MARGIN, term);
166 }
772e23da 167 }
168}
169
170static void
2e713831
VS
171print_entry (int y, int highlight, grub_menu_entry_t entry,
172 struct grub_term_output *term)
772e23da 173{
174 int x;
175 const char *title;
176 grub_size_t title_len;
177 grub_ssize_t len;
178 grub_uint32_t *unicode_title;
179 grub_ssize_t i;
180 grub_uint8_t old_color_normal, old_color_highlight;
181
182 title = entry ? entry->title : "";
183 title_len = grub_strlen (title);
184 unicode_title = grub_malloc (title_len * sizeof (*unicode_title));
185 if (! unicode_title)
186 /* XXX How to show this error? */
187 return;
188
189 len = grub_utf8_to_ucs4 (unicode_title, title_len,
190 (grub_uint8_t *) title, -1, 0);
191 if (len < 0)
192 {
193 /* It is an invalid sequence. */
194 grub_free (unicode_title);
195 return;
196 }
197
2e713831
VS
198 grub_term_getcolor (term, &old_color_normal, &old_color_highlight);
199 grub_term_setcolor (term, grub_color_menu_normal, grub_color_menu_highlight);
200 grub_term_setcolorstate (term, highlight
201 ? GRUB_TERM_COLOR_HIGHLIGHT
202 : GRUB_TERM_COLOR_NORMAL);
772e23da 203
2e713831 204 grub_term_gotoxy (term, GRUB_TERM_LEFT_BORDER_X + GRUB_TERM_MARGIN, y);
772e23da 205
0a239a82 206 int last_printed = 0;
772e23da 207 for (x = GRUB_TERM_LEFT_BORDER_X + GRUB_TERM_MARGIN + 1, i = 0;
2e713831 208 x < (int) (GRUB_TERM_LEFT_BORDER_X + grub_term_border_width (term)
0a239a82 209 - GRUB_TERM_MARGIN);)
772e23da 210 {
211 if (i < len
2e713831
VS
212 && x <= (int) (GRUB_TERM_LEFT_BORDER_X + grub_term_border_width (term)
213 - GRUB_TERM_MARGIN - 1))
772e23da 214 {
215 grub_ssize_t width;
0a239a82 216 struct grub_unicode_glyph glyph;
772e23da 217
0a239a82
VS
218 i += grub_unicode_aglomerate_comb (unicode_title + i,
219 len - i, &glyph);
772e23da 220
0a239a82 221 width = grub_term_getcharwidth (term, &glyph);
53c648d2 222 grub_free (glyph.combining);
0a239a82
VS
223
224 if (x + width <= (int) (GRUB_TERM_LEFT_BORDER_X
2e713831
VS
225 + grub_term_border_width (term)
226 - GRUB_TERM_MARGIN - 1))
0a239a82 227 last_printed = i;
772e23da 228 x += width;
229 }
230 else
0a239a82 231 break;
772e23da 232 }
0a239a82
VS
233
234 grub_print_ucs4 (unicode_title,
f588f1c8 235 unicode_title + last_printed, 0, 0, term);
0a239a82
VS
236
237 if (last_printed != len)
238 {
b764bff2 239 grub_putcode (GRUB_UNICODE_RIGHTARROW, term);
0a239a82 240 struct grub_unicode_glyph pseudo_glyph = {
b764bff2 241 .base = GRUB_UNICODE_RIGHTARROW,
0a239a82
VS
242 .variant = 0,
243 .attributes = 0,
244 .ncomb = 0,
50186d82
VS
245 .combining = 0,
246 .estimated_width = 1
0a239a82
VS
247 };
248 x += grub_term_getcharwidth (term, &pseudo_glyph);
249 }
250
251 for (; x < (int) (GRUB_TERM_LEFT_BORDER_X + grub_term_border_width (term)
252 - GRUB_TERM_MARGIN); x++)
253 grub_putcode (' ', term);
254
2e713831
VS
255 grub_term_setcolorstate (term, GRUB_TERM_COLOR_NORMAL);
256 grub_putcode (' ', term);
772e23da 257
2e713831 258 grub_term_gotoxy (term, grub_term_cursor_x (term), y);
772e23da 259
2e713831
VS
260 grub_term_setcolor (term, old_color_normal, old_color_highlight);
261 grub_term_setcolorstate (term, GRUB_TERM_COLOR_NORMAL);
772e23da 262 grub_free (unicode_title);
263}
264
265static void
2e713831
VS
266print_entries (grub_menu_t menu, int first, int offset,
267 struct grub_term_output *term)
772e23da 268{
269 grub_menu_entry_t e;
270 int i;
271
2e713831
VS
272 grub_term_gotoxy (term,
273 GRUB_TERM_LEFT_BORDER_X + grub_term_border_width (term),
274 GRUB_TERM_FIRST_ENTRY_Y);
772e23da 275
276 if (first)
b764bff2 277 grub_putcode (GRUB_UNICODE_UPARROW, term);
772e23da 278 else
2e713831 279 grub_putcode (' ', term);
772e23da 280
281 e = grub_menu_get_entry (menu, first);
282
2e713831 283 for (i = 0; i < grub_term_num_entries (term); i++)
772e23da 284 {
2e713831 285 print_entry (GRUB_TERM_FIRST_ENTRY_Y + i, offset == i, e, term);
772e23da 286 if (e)
287 e = e->next;
288 }
289
2e713831
VS
290 grub_term_gotoxy (term, GRUB_TERM_LEFT_BORDER_X
291 + grub_term_border_width (term),
292 GRUB_TERM_TOP_BORDER_Y + grub_term_num_entries (term));
772e23da 293
294 if (e)
b764bff2 295 grub_putcode (GRUB_UNICODE_DOWNARROW, term);
772e23da 296 else
2e713831 297 grub_putcode (' ', term);
772e23da 298
2e713831
VS
299 grub_term_gotoxy (term, grub_term_cursor_x (term),
300 GRUB_TERM_FIRST_ENTRY_Y + offset);
772e23da 301}
302
303/* Initialize the screen. If NESTED is non-zero, assume that this menu
304 is run from another menu or a command-line. If EDIT is non-zero, show
305 a message for the menu entry editor. */
306void
2e713831
VS
307grub_menu_init_page (int nested, int edit,
308 struct grub_term_output *term)
772e23da 309{
310 grub_uint8_t old_color_normal, old_color_highlight;
311
2e713831 312 grub_term_getcolor (term, &old_color_normal, &old_color_highlight);
772e23da 313
314 /* By default, use the same colors for the menu. */
315 grub_color_menu_normal = old_color_normal;
316 grub_color_menu_highlight = old_color_highlight;
317
318 /* Then give user a chance to replace them. */
2e713831
VS
319 grub_parse_color_name_pair (&grub_color_menu_normal,
320 grub_env_get ("menu_color_normal"));
321 grub_parse_color_name_pair (&grub_color_menu_highlight,
322 grub_env_get ("menu_color_highlight"));
323
324 grub_normal_init_page (term);
325 grub_term_setcolor (term, grub_color_menu_normal, grub_color_menu_highlight);
326 draw_border (term);
327 grub_term_setcolor (term, old_color_normal, old_color_highlight);
328 print_message (nested, edit, term);
772e23da 329}
330
331static void
2e713831 332menu_text_print_timeout (int timeout, void *dataptr)
772e23da 333{
69055f8a 334 const char *msg =
a173283f 335 _("The highlighted entry will be executed automatically in %ds.");
2e713831 336 struct menu_viewer_data *data = dataptr;
8b442f3f 337 char *msg_translated;
2e713831 338 int posx;
69055f8a 339
2e713831 340 grub_term_gotoxy (data->term, 0, grub_term_height (data->term) - 3);
772e23da 341
61eb45ee 342 msg_translated = grub_xasprintf (msg, timeout);
8b442f3f 343 if (!msg_translated)
2e713831
VS
344 {
345 grub_print_error ();
346 grub_errno = GRUB_ERR_NONE;
347 return;
348 }
bfd5e52b 349
2e713831 350 grub_print_message_indented (msg_translated, 3, 0, data->term);
a2c1332b 351
2e713831 352 posx = grub_term_getxy (data->term) >> 8;
f588f1c8 353 grub_print_spaces (data->term, grub_term_width (data->term) - posx - 1);
69055f8a 354
2e713831
VS
355 grub_term_gotoxy (data->term,
356 grub_term_cursor_x (data->term),
357 GRUB_TERM_FIRST_ENTRY_Y + data->offset);
358 grub_term_refresh (data->term);
bfd5e52b 359}
772e23da 360
2e713831
VS
361static void
362menu_text_set_chosen_entry (int entry, void *dataptr)
772e23da 363{
2e713831
VS
364 struct menu_viewer_data *data = dataptr;
365 int oldoffset = data->offset;
366 int complete_redraw = 0;
772e23da 367
2e713831
VS
368 data->offset = entry - data->first;
369 if (data->offset > grub_term_num_entries (data->term) - 1)
772e23da 370 {
fa533ebb 371 data->first = entry - (grub_term_num_entries (data->term) - 1);
2e713831
VS
372 data->offset = grub_term_num_entries (data->term) - 1;
373 complete_redraw = 1;
772e23da 374 }
2e713831 375 if (data->offset < 0)
772e23da 376 {
2e713831
VS
377 data->offset = 0;
378 data->first = entry;
379 complete_redraw = 1;
772e23da 380 }
2e713831
VS
381 if (complete_redraw)
382 print_entries (data->menu, data->first, data->offset, data->term);
383 else
772e23da 384 {
2e713831
VS
385 print_entry (GRUB_TERM_FIRST_ENTRY_Y + oldoffset, 0,
386 grub_menu_get_entry (data->menu, data->first + oldoffset),
387 data->term);
388 print_entry (GRUB_TERM_FIRST_ENTRY_Y + data->offset, 1,
389 grub_menu_get_entry (data->menu, data->first + data->offset),
390 data->term);
772e23da 391 }
2e713831 392 grub_term_refresh (data->term);
772e23da 393}
394
772e23da 395static void
2e713831 396menu_text_fini (void *dataptr)
772e23da 397{
2e713831 398 struct menu_viewer_data *data = dataptr;
772e23da 399
2e713831 400 grub_term_setcursor (data->term, 1);
fa533ebb 401 grub_term_cls (data->term);
772e23da 402
772e23da 403}
404
772e23da 405static void
2e713831 406menu_text_clear_timeout (void *dataptr)
772e23da 407{
2e713831
VS
408 struct menu_viewer_data *data = dataptr;
409
410 grub_term_gotoxy (data->term, 0, grub_term_height (data->term) - 3);
f588f1c8 411 grub_print_spaces (data->term, grub_term_width (data->term) - 1);
2e713831
VS
412 grub_term_gotoxy (data->term, grub_term_cursor_x (data->term),
413 GRUB_TERM_FIRST_ENTRY_Y + data->offset);
414 grub_term_refresh (data->term);
772e23da 415}
416
27a8ee52
VS
417grub_err_t
418grub_menu_try_text (struct grub_term_output *term,
419 int entry, grub_menu_t menu, int nested)
772e23da 420{
2e713831
VS
421 struct menu_viewer_data *data;
422 struct grub_menu_viewer *instance;
772e23da 423
27a8ee52
VS
424 instance = grub_zalloc (sizeof (*instance));
425 if (!instance)
426 return grub_errno;
772e23da 427
27a8ee52
VS
428 data = grub_zalloc (sizeof (*data));
429 if (!data)
430 {
431 grub_free (instance);
432 return grub_errno;
433 }
772e23da 434
27a8ee52
VS
435 data->term = term;
436 instance->data = data;
437 instance->set_chosen_entry = menu_text_set_chosen_entry;
438 instance->print_timeout = menu_text_print_timeout;
439 instance->clear_timeout = menu_text_clear_timeout;
440 instance->fini = menu_text_fini;
772e23da 441
27a8ee52 442 data->menu = menu;
772e23da 443
27a8ee52
VS
444 data->offset = entry;
445 data->first = 0;
446 if (data->offset > grub_term_num_entries (data->term) - 1)
447 {
448 data->first = data->offset - (grub_term_num_entries (data->term) - 1);
449 data->offset = grub_term_num_entries (data->term) - 1;
772e23da 450 }
451
27a8ee52
VS
452 grub_term_setcursor (data->term, 0);
453 grub_menu_init_page (nested, 0, data->term);
454 print_entries (menu, data->first, data->offset, data->term);
455 grub_term_refresh (data->term);
456 grub_menu_register_viewer (instance);
457
772e23da 458 return GRUB_ERR_NONE;
459}