1 /* menu_text.c - Basic text menu implementation. */
3 * GRUB -- GRand Unified Bootloader
4 * Copyright (C) 2003,2004,2005,2006,2007,2008,2009 Free Software Foundation, Inc.
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.
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.
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/>.
20 #include <grub/normal.h>
21 #include <grub/term.h>
22 #include <grub/misc.h>
23 #include <grub/loader.h>
25 #include <grub/time.h>
27 #include <grub/menu_viewer.h>
28 #include <grub/i18n.h>
29 #include <grub/charset.h>
31 static grub_uint8_t grub_color_menu_normal
;
32 static grub_uint8_t grub_color_menu_highlight
;
34 struct menu_viewer_data
37 /* The number of entries shown at a time. */
40 struct grub_term_output
*term
;
44 grub_term_cursor_x (struct grub_term_output
*term
)
46 return (GRUB_TERM_LEFT_BORDER_X
+ grub_term_border_width (term
)
47 - GRUB_TERM_MARGIN
- 1);
51 grub_getstringwidth (grub_uint32_t
* str
, const grub_uint32_t
* last_position
,
52 struct grub_term_output
*term
)
54 grub_ssize_t width
= 0;
56 while (str
< last_position
)
58 struct grub_unicode_glyph glyph
;
59 str
+= grub_unicode_aglomerate_comb (str
, last_position
- str
, &glyph
);
60 width
+= grub_term_getcharwidth (term
, &glyph
);
66 grub_print_message_indented_real (const char *msg
, int margin_left
,
68 struct grub_term_output
*term
, int dry_run
)
70 grub_uint32_t
*unicode_msg
;
71 grub_uint32_t
*last_position
;
72 grub_size_t msg_len
= grub_strlen (msg
) + 2;
75 unicode_msg
= grub_malloc (msg_len
* sizeof (grub_uint32_t
));
80 msg_len
= grub_utf8_to_ucs4 (unicode_msg
, msg_len
,
81 (grub_uint8_t
*) msg
, -1, 0);
83 last_position
= unicode_msg
+ msg_len
;
84 *last_position
++ = '\n';
88 ret
= grub_ucs4_count_lines (unicode_msg
, last_position
, margin_left
,
91 grub_print_ucs4 (unicode_msg
, last_position
, margin_left
,
94 grub_free (unicode_msg
);
100 grub_print_message_indented (const char *msg
, int margin_left
, int margin_right
,
101 struct grub_term_output
*term
)
103 grub_print_message_indented_real (msg
, margin_left
, margin_right
, term
, 0);
107 draw_border (struct grub_term_output
*term
, int num_entries
)
111 grub_term_setcolorstate (term
, GRUB_TERM_COLOR_NORMAL
);
113 grub_term_gotoxy (term
, GRUB_TERM_MARGIN
, GRUB_TERM_TOP_BORDER_Y
);
114 grub_putcode (GRUB_UNICODE_CORNER_UL
, term
);
115 for (i
= 0; i
< (unsigned) grub_term_border_width (term
) - 2; i
++)
116 grub_putcode (GRUB_UNICODE_HLINE
, term
);
117 grub_putcode (GRUB_UNICODE_CORNER_UR
, term
);
119 for (i
= 0; i
< (unsigned) num_entries
; i
++)
121 grub_term_gotoxy (term
, GRUB_TERM_MARGIN
, GRUB_TERM_TOP_BORDER_Y
+ i
+ 1);
122 grub_putcode (GRUB_UNICODE_VLINE
, term
);
123 grub_term_gotoxy (term
, GRUB_TERM_MARGIN
+ grub_term_border_width (term
)
125 GRUB_TERM_TOP_BORDER_Y
+ i
+ 1);
126 grub_putcode (GRUB_UNICODE_VLINE
, term
);
129 grub_term_gotoxy (term
, GRUB_TERM_MARGIN
,
130 GRUB_TERM_TOP_BORDER_Y
+ num_entries
+ 1);
131 grub_putcode (GRUB_UNICODE_CORNER_LL
, term
);
132 for (i
= 0; i
< (unsigned) grub_term_border_width (term
) - 2; i
++)
133 grub_putcode (GRUB_UNICODE_HLINE
, term
);
134 grub_putcode (GRUB_UNICODE_CORNER_LR
, term
);
136 grub_term_setcolorstate (term
, GRUB_TERM_COLOR_NORMAL
);
138 grub_term_gotoxy (term
, GRUB_TERM_MARGIN
,
139 (GRUB_TERM_TOP_BORDER_Y
+ num_entries
140 + GRUB_TERM_MARGIN
+ 1));
144 print_message (int nested
, int edit
, struct grub_term_output
*term
, int dry_run
)
147 grub_term_setcolorstate (term
, GRUB_TERM_COLOR_NORMAL
);
154 grub_putcode ('\n', term
);
155 ret
+= grub_print_message_indented_real (_("Minimum Emacs-like screen editing is \
156 supported. TAB lists completions. Press Ctrl-x or F10 to boot, Ctrl-c or F2 for a \
157 command-line or ESC to discard edits and return to the GRUB menu."),
158 STANDARD_MARGIN
, STANDARD_MARGIN
,
163 const char *msg
= _("Use the %C and %C keys to select which "
164 "entry is highlighted.");
165 char *msg_translated
;
167 msg_translated
= grub_xasprintf (msg
, GRUB_UNICODE_UPARROW
,
168 GRUB_UNICODE_DOWNARROW
);
174 grub_putcode ('\n', term
);
175 ret
+= grub_print_message_indented_real (msg_translated
, STANDARD_MARGIN
,
176 STANDARD_MARGIN
, term
, dry_run
);
178 grub_free (msg_translated
);
182 ret
+= grub_print_message_indented_real
183 (_("Press enter to boot the selected OS, "
184 "`e' to edit the commands before booting "
185 "or `c' for a command-line. ESC to return previous menu."),
186 STANDARD_MARGIN
, STANDARD_MARGIN
, term
, dry_run
);
190 ret
+= grub_print_message_indented_real
191 (_("Press enter to boot the selected OS, "
192 "`e' to edit the commands before booting "
193 "or `c' for a command-line."),
194 STANDARD_MARGIN
, STANDARD_MARGIN
, term
, dry_run
);
201 print_entry (int y
, int highlight
, grub_menu_entry_t entry
,
202 struct grub_term_output
*term
)
206 grub_size_t title_len
;
208 grub_uint32_t
*unicode_title
;
210 grub_uint8_t old_color_normal
, old_color_highlight
;
212 title
= entry
? entry
->title
: "";
213 title_len
= grub_strlen (title
);
214 unicode_title
= grub_malloc (title_len
* sizeof (*unicode_title
));
216 /* XXX How to show this error? */
219 len
= grub_utf8_to_ucs4 (unicode_title
, title_len
,
220 (grub_uint8_t
*) title
, -1, 0);
223 /* It is an invalid sequence. */
224 grub_free (unicode_title
);
228 old_color_normal
= grub_term_normal_color
;
229 old_color_highlight
= grub_term_highlight_color
;
230 grub_term_normal_color
= grub_color_menu_normal
;
231 grub_term_highlight_color
= grub_color_menu_highlight
;
232 grub_term_setcolorstate (term
, highlight
233 ? GRUB_TERM_COLOR_HIGHLIGHT
234 : GRUB_TERM_COLOR_NORMAL
);
236 grub_term_gotoxy (term
, GRUB_TERM_LEFT_BORDER_X
+ GRUB_TERM_MARGIN
, y
);
238 int last_printed
= 0;
240 for (i
= 0; i
< len
; i
++)
241 if (unicode_title
[i
] == '\n' || unicode_title
[i
] == '\b'
242 || unicode_title
[i
] == '\r' || unicode_title
[i
] == '\e')
243 unicode_title
[i
] = ' ';
245 for (x
= GRUB_TERM_LEFT_BORDER_X
+ GRUB_TERM_MARGIN
+ 1, i
= 0;
246 x
< (int) (GRUB_TERM_LEFT_BORDER_X
+ grub_term_border_width (term
)
247 - GRUB_TERM_MARGIN
);)
250 && x
<= (int) (GRUB_TERM_LEFT_BORDER_X
+ grub_term_border_width (term
)
251 - GRUB_TERM_MARGIN
- 1))
254 struct grub_unicode_glyph glyph
;
256 i
+= grub_unicode_aglomerate_comb (unicode_title
+ i
,
259 width
= grub_term_getcharwidth (term
, &glyph
);
260 grub_free (glyph
.combining
);
262 if (x
+ width
<= (int) (GRUB_TERM_LEFT_BORDER_X
263 + grub_term_border_width (term
)
264 - GRUB_TERM_MARGIN
- 1))
272 grub_print_ucs4 (unicode_title
,
273 unicode_title
+ last_printed
, 0, 0, term
);
275 if (last_printed
!= len
)
277 grub_putcode (GRUB_UNICODE_RIGHTARROW
, term
);
278 struct grub_unicode_glyph pseudo_glyph
= {
279 .base
= GRUB_UNICODE_RIGHTARROW
,
286 x
+= grub_term_getcharwidth (term
, &pseudo_glyph
);
289 for (; x
< (int) (GRUB_TERM_LEFT_BORDER_X
+ grub_term_border_width (term
)
290 - GRUB_TERM_MARGIN
); x
++)
291 grub_putcode (' ', term
);
293 grub_term_setcolorstate (term
, GRUB_TERM_COLOR_NORMAL
);
294 grub_putcode (' ', term
);
296 grub_term_gotoxy (term
, grub_term_cursor_x (term
), y
);
298 grub_term_normal_color
= old_color_normal
;
299 grub_term_highlight_color
= old_color_highlight
;
301 grub_term_setcolorstate (term
, GRUB_TERM_COLOR_NORMAL
);
302 grub_free (unicode_title
);
306 print_entries (grub_menu_t menu
, const struct menu_viewer_data
*data
)
311 grub_term_gotoxy (data
->term
,
312 GRUB_TERM_LEFT_BORDER_X
+ grub_term_border_width (data
->term
),
313 GRUB_TERM_FIRST_ENTRY_Y
);
316 grub_putcode (GRUB_UNICODE_UPARROW
, data
->term
);
318 grub_putcode (' ', data
->term
);
320 e
= grub_menu_get_entry (menu
, data
->first
);
322 for (i
= 0; i
< data
->num_entries
; i
++)
324 print_entry (GRUB_TERM_FIRST_ENTRY_Y
+ i
, data
->offset
== i
,
330 grub_term_gotoxy (data
->term
, GRUB_TERM_LEFT_BORDER_X
331 + grub_term_border_width (data
->term
),
332 GRUB_TERM_TOP_BORDER_Y
+ data
->num_entries
);
335 grub_putcode (GRUB_UNICODE_DOWNARROW
, data
->term
);
337 grub_putcode (' ', data
->term
);
339 grub_term_gotoxy (data
->term
, grub_term_cursor_x (data
->term
),
340 GRUB_TERM_FIRST_ENTRY_Y
+ data
->offset
);
343 /* Initialize the screen. If NESTED is non-zero, assume that this menu
344 is run from another menu or a command-line. If EDIT is non-zero, show
345 a message for the menu entry editor. */
347 grub_menu_init_page (int nested
, int edit
, int *num_entries
,
348 struct grub_term_output
*term
)
350 grub_uint8_t old_color_normal
, old_color_highlight
;
352 /* 3 lines for timeout message and bottom margin. 2 lines for the border. */
353 *num_entries
= grub_term_height (term
) - GRUB_TERM_TOP_BORDER_Y
354 - (print_message (nested
, edit
, term
, 1) + 3) - 2;
356 /* By default, use the same colors for the menu. */
357 old_color_normal
= grub_term_normal_color
;
358 old_color_highlight
= grub_term_highlight_color
;
359 grub_color_menu_normal
= grub_term_normal_color
;
360 grub_color_menu_highlight
= grub_term_highlight_color
;
362 /* Then give user a chance to replace them. */
363 grub_parse_color_name_pair (&grub_color_menu_normal
,
364 grub_env_get ("menu_color_normal"));
365 grub_parse_color_name_pair (&grub_color_menu_highlight
,
366 grub_env_get ("menu_color_highlight"));
368 grub_normal_init_page (term
);
369 grub_term_normal_color
= grub_color_menu_normal
;
370 grub_term_highlight_color
= grub_color_menu_highlight
;
371 draw_border (term
, *num_entries
);
372 grub_term_normal_color
= old_color_normal
;
373 grub_term_highlight_color
= old_color_highlight
;
374 print_message (nested
, edit
, term
, 0);
378 menu_text_print_timeout (int timeout
, void *dataptr
)
381 _("The highlighted entry will be executed automatically in %ds.");
382 struct menu_viewer_data
*data
= dataptr
;
383 char *msg_translated
;
386 grub_term_gotoxy (data
->term
, 0, grub_term_height (data
->term
) - 3);
388 msg_translated
= grub_xasprintf (msg
, timeout
);
392 grub_errno
= GRUB_ERR_NONE
;
396 grub_print_message_indented (msg_translated
, 3, 0, data
->term
);
398 posx
= grub_term_getxy (data
->term
) >> 8;
399 grub_print_spaces (data
->term
, grub_term_width (data
->term
) - posx
- 1);
401 grub_term_gotoxy (data
->term
,
402 grub_term_cursor_x (data
->term
),
403 GRUB_TERM_FIRST_ENTRY_Y
+ data
->offset
);
404 grub_term_refresh (data
->term
);
408 menu_text_set_chosen_entry (int entry
, void *dataptr
)
410 struct menu_viewer_data
*data
= dataptr
;
411 int oldoffset
= data
->offset
;
412 int complete_redraw
= 0;
414 data
->offset
= entry
- data
->first
;
415 if (data
->offset
> data
->num_entries
- 1)
417 data
->first
= entry
- (data
->num_entries
- 1);
418 data
->offset
= data
->num_entries
- 1;
421 if (data
->offset
< 0)
428 print_entries (data
->menu
, data
);
431 print_entry (GRUB_TERM_FIRST_ENTRY_Y
+ oldoffset
, 0,
432 grub_menu_get_entry (data
->menu
, data
->first
+ oldoffset
),
434 print_entry (GRUB_TERM_FIRST_ENTRY_Y
+ data
->offset
, 1,
435 grub_menu_get_entry (data
->menu
, data
->first
+ data
->offset
),
438 grub_term_refresh (data
->term
);
442 menu_text_fini (void *dataptr
)
444 struct menu_viewer_data
*data
= dataptr
;
446 grub_term_setcursor (data
->term
, 1);
447 grub_term_cls (data
->term
);
452 menu_text_clear_timeout (void *dataptr
)
454 struct menu_viewer_data
*data
= dataptr
;
456 grub_term_gotoxy (data
->term
, 0, grub_term_height (data
->term
) - 3);
457 grub_print_spaces (data
->term
, grub_term_width (data
->term
) - 1);
458 grub_term_gotoxy (data
->term
, grub_term_cursor_x (data
->term
),
459 GRUB_TERM_FIRST_ENTRY_Y
+ data
->offset
);
460 grub_term_refresh (data
->term
);
464 grub_menu_try_text (struct grub_term_output
*term
,
465 int entry
, grub_menu_t menu
, int nested
)
467 struct menu_viewer_data
*data
;
468 struct grub_menu_viewer
*instance
;
470 instance
= grub_zalloc (sizeof (*instance
));
474 data
= grub_zalloc (sizeof (*data
));
477 grub_free (instance
);
482 instance
->data
= data
;
483 instance
->set_chosen_entry
= menu_text_set_chosen_entry
;
484 instance
->print_timeout
= menu_text_print_timeout
;
485 instance
->clear_timeout
= menu_text_clear_timeout
;
486 instance
->fini
= menu_text_fini
;
490 data
->offset
= entry
;
493 grub_term_setcursor (data
->term
, 0);
494 grub_menu_init_page (nested
, 0, &data
->num_entries
, data
->term
);
496 if (data
->offset
> data
->num_entries
- 1)
498 data
->first
= data
->offset
- (data
->num_entries
- 1);
499 data
->offset
= data
->num_entries
- 1;
502 print_entries (menu
, data
);
503 grub_term_refresh (data
->term
);
504 grub_menu_register_viewer (instance
);
506 return GRUB_ERR_NONE
;