]>
Commit | Line | Data |
---|---|---|
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 | |
31 | static grub_uint8_t grub_color_menu_normal; | |
32 | static grub_uint8_t grub_color_menu_highlight; | |
33 | ||
2e713831 | 34 | struct 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 | 41 | grub_ssize_t |
2e713831 VS |
42 | grub_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 | 56 | void |
2e713831 VS |
57 | grub_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 | 78 | static void |
2e713831 | 79 | draw_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 | ||
115 | static void | |
2e713831 | 116 | print_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 \ | |
125 | supported. TAB lists completions. Press F1 to boot, F2=Ctrl-a, F3=Ctrl-e, \ | |
126 | F4 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 | 130 | supported. TAB lists completions. Press Ctrl-x to boot, Ctrl-c for a \ |
25f4e252 EC |
131 | command-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 | ||
170 | static void | |
2e713831 VS |
171 | print_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 | ||
265 | static void | |
2e713831 VS |
266 | print_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. */ | |
306 | void | |
2e713831 VS |
307 | grub_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 | ||
331 | static void | |
2e713831 | 332 | menu_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 |
361 | static void |
362 | menu_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 | 395 | static void |
2e713831 | 396 | menu_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 | 405 | static void |
2e713831 | 406 | menu_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 |
417 | grub_err_t |
418 | grub_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 | } |