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