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
38 struct grub_term_output
*term
;
42 grub_getstringwidth (grub_uint32_t
* str
, const grub_uint32_t
* last_position
,
43 struct grub_term_output
*term
)
45 grub_ssize_t width
= 0;
47 while (str
< last_position
)
49 struct grub_unicode_glyph glyph
;
50 str
+= grub_unicode_aglomerate_comb (str
, last_position
- str
, &glyph
);
51 width
+= grub_term_getcharwidth (term
, &glyph
);
57 grub_print_message_indented (const char *msg
, int margin_left
, int margin_right
,
58 struct grub_term_output
*term
)
60 grub_uint32_t
*unicode_msg
;
61 grub_uint32_t
*last_position
;
65 msg_len
= grub_utf8_to_ucs4_alloc (msg
, &unicode_msg
, &last_position
);
72 grub_print_ucs4 (unicode_msg
, last_position
, margin_left
, margin_right
, term
);
77 grub_uint32_t
*current_position
= unicode_msg
;
79 grub_uint32_t
*next_new_line
= unicode_msg
;
83 while (current_position
< last_position
)
86 grub_putcode ('\n', term
);
88 next_new_line
= (grub_uint32_t
*) last_position
;
90 while (grub_getstringwidth (current_position
, next_new_line
,term
)
92 || (next_new_line
!= last_position
&& *next_new_line
!= ' '
93 && next_new_line
> current_position
))
98 if (next_new_line
== current_position
)
100 next_new_line
= (next_new_line
+ line_len
> last_position
) ?
101 (grub_uint32_t
*) last_position
: next_new_line
+ line_len
;
104 print_spaces (margin_left
, term
);
105 grub_print_ucs4 (current_position
, next_new_line
, term
);
108 current_position
= next_new_line
;
112 grub_free (unicode_msg
);
117 draw_border (struct grub_term_output
*term
)
121 grub_term_setcolorstate (term
, GRUB_TERM_COLOR_NORMAL
);
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
);
129 for (i
= 0; i
< (unsigned) grub_term_num_entries (term
); i
++)
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
)
135 GRUB_TERM_TOP_BORDER_Y
+ i
+ 1);
136 grub_putcode (GRUB_TERM_DISP_VLINE
, term
);
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
);
146 grub_term_setcolorstate (term
, GRUB_TERM_COLOR_NORMAL
);
148 grub_term_gotoxy (term
, GRUB_TERM_MARGIN
,
149 (GRUB_TERM_TOP_BORDER_Y
+ grub_term_num_entries (term
)
150 + GRUB_TERM_MARGIN
+ 1));
154 print_message (int nested
, int edit
, struct grub_term_output
*term
)
156 grub_term_setcolorstate (term
, GRUB_TERM_COLOR_NORMAL
);
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
,
168 const char *msg
= _("Use the %C and %C keys to select which "
169 "entry is highlighted.\n");
170 char *msg_translated
;
172 msg_translated
= grub_xasprintf (msg
, (grub_uint32_t
) GRUB_TERM_DISP_UP
,
173 (grub_uint32_t
) GRUB_TERM_DISP_DOWN
);
177 grub_print_message_indented (msg_translated
, STANDARD_MARGIN
,
178 STANDARD_MARGIN
, term
);
180 grub_free (msg_translated
);
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
);
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
);
202 print_entry (int y
, int highlight
, grub_menu_entry_t entry
,
203 struct grub_term_output
*term
)
207 grub_size_t title_len
;
209 grub_uint32_t
*unicode_title
;
211 grub_uint8_t old_color_normal
, old_color_highlight
;
213 title
= entry
? entry
->title
: "";
214 title_len
= grub_strlen (title
);
215 unicode_title
= grub_malloc (title_len
* sizeof (*unicode_title
));
217 /* XXX How to show this error? */
220 len
= grub_utf8_to_ucs4 (unicode_title
, title_len
,
221 (grub_uint8_t
*) title
, -1, 0);
224 /* It is an invalid sequence. */
225 grub_free (unicode_title
);
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
);
235 grub_term_gotoxy (term
, GRUB_TERM_LEFT_BORDER_X
+ GRUB_TERM_MARGIN
, y
);
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
);)
243 && x
<= (int) (GRUB_TERM_LEFT_BORDER_X
+ grub_term_border_width (term
)
244 - GRUB_TERM_MARGIN
- 1))
247 struct grub_unicode_glyph glyph
;
249 i
+= grub_unicode_aglomerate_comb (unicode_title
+ i
,
252 width
= grub_term_getcharwidth (term
, &glyph
);
253 grub_free (glyph
.combining
);
255 if (x
+ width
<= (int) (GRUB_TERM_LEFT_BORDER_X
256 + grub_term_border_width (term
)
257 - GRUB_TERM_MARGIN
- 1))
265 grub_print_ucs4 (unicode_title
,
266 unicode_title
+ last_printed
, 0, 0, term
);
268 if (last_printed
!= len
)
270 grub_putcode (GRUB_TERM_DISP_RIGHT
, term
);
271 struct grub_unicode_glyph pseudo_glyph
= {
272 .base
= GRUB_TERM_DISP_RIGHT
,
278 x
+= grub_term_getcharwidth (term
, &pseudo_glyph
);
281 for (; x
< (int) (GRUB_TERM_LEFT_BORDER_X
+ grub_term_border_width (term
)
282 - GRUB_TERM_MARGIN
); x
++)
283 grub_putcode (' ', term
);
285 grub_term_setcolorstate (term
, GRUB_TERM_COLOR_NORMAL
);
286 grub_putcode (' ', term
);
288 grub_term_gotoxy (term
, grub_term_cursor_x (term
), y
);
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
);
296 print_entries (grub_menu_t menu
, int first
, int offset
,
297 struct grub_term_output
*term
)
302 grub_term_gotoxy (term
,
303 GRUB_TERM_LEFT_BORDER_X
+ grub_term_border_width (term
),
304 GRUB_TERM_FIRST_ENTRY_Y
);
307 grub_putcode (GRUB_TERM_DISP_UP
, term
);
309 grub_putcode (' ', term
);
311 e
= grub_menu_get_entry (menu
, first
);
313 for (i
= 0; i
< grub_term_num_entries (term
); i
++)
315 print_entry (GRUB_TERM_FIRST_ENTRY_Y
+ i
, offset
== i
, e
, term
);
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
));
325 grub_putcode (GRUB_TERM_DISP_DOWN
, term
);
327 grub_putcode (' ', term
);
329 grub_term_gotoxy (term
, grub_term_cursor_x (term
),
330 GRUB_TERM_FIRST_ENTRY_Y
+ offset
);
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. */
337 grub_menu_init_page (int nested
, int edit
,
338 struct grub_term_output
*term
)
340 grub_uint8_t old_color_normal
, old_color_highlight
;
342 grub_term_getcolor (term
, &old_color_normal
, &old_color_highlight
);
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
;
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"));
354 grub_normal_init_page (term
);
355 grub_term_setcolor (term
, grub_color_menu_normal
, grub_color_menu_highlight
);
357 grub_term_setcolor (term
, old_color_normal
, old_color_highlight
);
358 print_message (nested
, edit
, term
);
362 menu_text_print_timeout (int timeout
, void *dataptr
)
365 _("The highlighted entry will be executed automatically in %ds.");
366 struct menu_viewer_data
*data
= dataptr
;
367 char *msg_translated
;
370 grub_term_gotoxy (data
->term
, 0, grub_term_height (data
->term
) - 3);
372 msg_translated
= grub_xasprintf (msg
, timeout
);
376 grub_errno
= GRUB_ERR_NONE
;
380 grub_print_message_indented (msg_translated
, 3, 0, data
->term
);
382 posx
= grub_term_getxy (data
->term
) >> 8;
383 grub_print_spaces (data
->term
, grub_term_width (data
->term
) - posx
- 1);
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
);
392 menu_text_set_chosen_entry (int entry
, void *dataptr
)
394 struct menu_viewer_data
*data
= dataptr
;
395 int oldoffset
= data
->offset
;
396 int complete_redraw
= 0;
398 data
->offset
= entry
- data
->first
;
399 if (data
->offset
> grub_term_num_entries (data
->term
) - 1)
401 data
->first
= entry
- (grub_term_num_entries (data
->term
) - 1);
402 data
->offset
= grub_term_num_entries (data
->term
) - 1;
405 if (data
->offset
< 0)
412 print_entries (data
->menu
, data
->first
, data
->offset
, data
->term
);
415 print_entry (GRUB_TERM_FIRST_ENTRY_Y
+ oldoffset
, 0,
416 grub_menu_get_entry (data
->menu
, data
->first
+ oldoffset
),
418 print_entry (GRUB_TERM_FIRST_ENTRY_Y
+ data
->offset
, 1,
419 grub_menu_get_entry (data
->menu
, data
->first
+ data
->offset
),
422 grub_term_refresh (data
->term
);
426 menu_text_fini (void *dataptr
)
428 struct menu_viewer_data
*data
= dataptr
;
430 grub_term_setcursor (data
->term
, 1);
431 grub_term_cls (data
->term
);
436 menu_text_clear_timeout (void *dataptr
)
438 struct menu_viewer_data
*data
= dataptr
;
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
);
448 grub_menu_try_text (struct grub_term_output
*term
,
449 int entry
, grub_menu_t menu
, int nested
)
451 struct menu_viewer_data
*data
;
452 struct grub_menu_viewer
*instance
;
454 instance
= grub_zalloc (sizeof (*instance
));
458 data
= grub_zalloc (sizeof (*data
));
461 grub_free (instance
);
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
;
474 data
->offset
= entry
;
476 if (data
->offset
> grub_term_num_entries (data
->term
) - 1)
478 data
->first
= data
->offset
- (grub_term_num_entries (data
->term
) - 1);
479 data
->offset
= grub_term_num_entries (data
->term
) - 1;
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
);
488 return GRUB_ERR_NONE
;