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 struct grub_term_screen_geometry geo
;
42 TIMEOUT_TERSE_NO_MARGIN
45 struct grub_term_output
*term
;
49 grub_term_cursor_x (const struct grub_term_screen_geometry
*geo
)
51 return (geo
->first_entry_x
+ geo
->entry_width
);
55 grub_getstringwidth (grub_uint32_t
* str
, const grub_uint32_t
* last_position
,
56 struct grub_term_output
*term
)
58 grub_ssize_t width
= 0;
60 while (str
< last_position
)
62 struct grub_unicode_glyph glyph
;
64 str
+= grub_unicode_aglomerate_comb (str
, last_position
- str
, &glyph
);
65 width
+= grub_term_getcharwidth (term
, &glyph
);
66 grub_unicode_destroy_glyph (&glyph
);
72 grub_print_message_indented_real (const char *msg
, int margin_left
,
74 struct grub_term_output
*term
, int dry_run
)
76 grub_uint32_t
*unicode_msg
;
77 grub_uint32_t
*last_position
;
78 grub_size_t msg_len
= grub_strlen (msg
) + 2;
81 unicode_msg
= grub_malloc (msg_len
* sizeof (grub_uint32_t
));
86 msg_len
= grub_utf8_to_ucs4 (unicode_msg
, msg_len
,
87 (grub_uint8_t
*) msg
, -1, 0);
89 last_position
= unicode_msg
+ msg_len
;
93 ret
= grub_ucs4_count_lines (unicode_msg
, last_position
, margin_left
,
96 grub_print_ucs4_menu (unicode_msg
, last_position
, margin_left
,
97 margin_right
, term
, 0, -1, 0, 0);
99 grub_free (unicode_msg
);
105 grub_print_message_indented (const char *msg
, int margin_left
, int margin_right
,
106 struct grub_term_output
*term
)
108 grub_print_message_indented_real (msg
, margin_left
, margin_right
, term
, 0);
112 draw_border (struct grub_term_output
*term
, const struct grub_term_screen_geometry
*geo
)
116 grub_term_setcolorstate (term
, GRUB_TERM_COLOR_NORMAL
);
118 grub_term_gotoxy (term
, geo
->first_entry_x
- 1, geo
->first_entry_y
- 1);
119 grub_putcode (GRUB_UNICODE_CORNER_UL
, term
);
120 for (i
= 0; i
< geo
->entry_width
+ 1; i
++)
121 grub_putcode (GRUB_UNICODE_HLINE
, term
);
122 grub_putcode (GRUB_UNICODE_CORNER_UR
, term
);
124 for (i
= 0; i
< geo
->num_entries
; i
++)
126 grub_term_gotoxy (term
, geo
->first_entry_x
- 1, geo
->first_entry_y
+ i
);
127 grub_putcode (GRUB_UNICODE_VLINE
, term
);
128 grub_term_gotoxy (term
, geo
->first_entry_x
+ geo
->entry_width
+ 1,
129 geo
->first_entry_y
+ i
);
130 grub_putcode (GRUB_UNICODE_VLINE
, term
);
133 grub_term_gotoxy (term
, geo
->first_entry_x
- 1,
134 geo
->first_entry_y
- 1 + geo
->num_entries
+ 1);
135 grub_putcode (GRUB_UNICODE_CORNER_LL
, term
);
136 for (i
= 0; i
< geo
->entry_width
+ 1; i
++)
137 grub_putcode (GRUB_UNICODE_HLINE
, term
);
138 grub_putcode (GRUB_UNICODE_CORNER_LR
, term
);
140 grub_term_setcolorstate (term
, GRUB_TERM_COLOR_NORMAL
);
142 grub_term_gotoxy (term
, geo
->first_entry_x
- 1,
143 (geo
->first_entry_y
- 1 + geo
->num_entries
144 + GRUB_TERM_MARGIN
+ 1));
148 print_message (int nested
, int edit
, struct grub_term_output
*term
, int dry_run
)
151 grub_term_setcolorstate (term
, GRUB_TERM_COLOR_NORMAL
);
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
);
171 ret
+= grub_print_message_indented_real (msg_translated
, STANDARD_MARGIN
,
172 STANDARD_MARGIN
, term
, dry_run
);
174 grub_free (msg_translated
);
178 ret
+= grub_print_message_indented_real
179 (_("Press enter to boot the selected OS, "
180 "`e' to edit the commands before booting "
181 "or `c' for a command-line. ESC to return previous menu."),
182 STANDARD_MARGIN
, STANDARD_MARGIN
, term
, dry_run
);
186 ret
+= grub_print_message_indented_real
187 (_("Press enter to boot the selected OS, "
188 "`e' to edit the commands before booting "
189 "or `c' for a command-line."),
190 STANDARD_MARGIN
, STANDARD_MARGIN
, term
, dry_run
);
197 print_entry (int y
, int highlight
, grub_menu_entry_t entry
,
198 const struct menu_viewer_data
*data
)
201 grub_size_t title_len
;
203 grub_uint32_t
*unicode_title
;
205 grub_uint8_t old_color_normal
, old_color_highlight
;
207 title
= entry
? entry
->title
: "";
208 title_len
= grub_strlen (title
);
209 unicode_title
= grub_malloc (title_len
* sizeof (*unicode_title
));
211 /* XXX How to show this error? */
214 len
= grub_utf8_to_ucs4 (unicode_title
, title_len
,
215 (grub_uint8_t
*) title
, -1, 0);
218 /* It is an invalid sequence. */
219 grub_free (unicode_title
);
223 old_color_normal
= grub_term_normal_color
;
224 old_color_highlight
= grub_term_highlight_color
;
225 grub_term_normal_color
= grub_color_menu_normal
;
226 grub_term_highlight_color
= grub_color_menu_highlight
;
227 grub_term_setcolorstate (data
->term
, highlight
228 ? GRUB_TERM_COLOR_HIGHLIGHT
229 : GRUB_TERM_COLOR_NORMAL
);
231 grub_term_gotoxy (data
->term
, data
->geo
.first_entry_x
, y
);
233 for (i
= 0; i
< len
; i
++)
234 if (unicode_title
[i
] == '\n' || unicode_title
[i
] == '\b'
235 || unicode_title
[i
] == '\r' || unicode_title
[i
] == '\e')
236 unicode_title
[i
] = ' ';
238 if (data
->geo
.num_entries
> 1)
239 grub_putcode (highlight
? '*' : ' ', data
->term
);
241 grub_print_ucs4_menu (unicode_title
,
244 data
->geo
.right_margin
,
246 GRUB_UNICODE_RIGHTARROW
, 0);
248 grub_term_setcolorstate (data
->term
, GRUB_TERM_COLOR_NORMAL
);
249 grub_term_gotoxy (data
->term
, grub_term_cursor_x (&data
->geo
), y
);
251 grub_term_normal_color
= old_color_normal
;
252 grub_term_highlight_color
= old_color_highlight
;
254 grub_term_setcolorstate (data
->term
, GRUB_TERM_COLOR_NORMAL
);
255 grub_free (unicode_title
);
259 print_entries (grub_menu_t menu
, const struct menu_viewer_data
*data
)
264 grub_term_gotoxy (data
->term
,
265 data
->geo
.first_entry_x
+ data
->geo
.entry_width
266 + data
->geo
.border
+ 1,
267 data
->geo
.first_entry_y
);
269 if (data
->geo
.num_entries
!= 1)
272 grub_putcode (GRUB_UNICODE_UPARROW
, data
->term
);
274 grub_putcode (' ', data
->term
);
276 e
= grub_menu_get_entry (menu
, data
->first
);
278 for (i
= 0; i
< data
->geo
.num_entries
; i
++)
280 print_entry (data
->geo
.first_entry_y
+ i
, data
->offset
== i
,
286 grub_term_gotoxy (data
->term
, data
->geo
.first_entry_x
+ data
->geo
.entry_width
287 + data
->geo
.border
+ 1,
288 data
->geo
.first_entry_y
+ data
->geo
.num_entries
- 1);
289 if (data
->geo
.num_entries
== 1)
291 if (data
->first
&& e
)
292 grub_putcode (GRUB_UNICODE_UPDOWNARROW
, data
->term
);
293 else if (data
->first
)
294 grub_putcode (GRUB_UNICODE_UPARROW
, data
->term
);
296 grub_putcode (GRUB_UNICODE_DOWNARROW
, data
->term
);
298 grub_putcode (' ', data
->term
);
303 grub_putcode (GRUB_UNICODE_DOWNARROW
, data
->term
);
305 grub_putcode (' ', data
->term
);
308 grub_term_gotoxy (data
->term
, grub_term_cursor_x (&data
->geo
),
309 data
->geo
.first_entry_y
+ data
->offset
);
312 /* Initialize the screen. If NESTED is non-zero, assume that this menu
313 is run from another menu or a command-line. If EDIT is non-zero, show
314 a message for the menu entry editor. */
316 grub_menu_init_page (int nested
, int edit
,
317 struct grub_term_screen_geometry
*geo
,
318 struct grub_term_output
*term
)
320 grub_uint8_t old_color_normal
, old_color_highlight
;
322 int bottom_message
= 1;
327 geo
->first_entry_x
= 1 /* margin */ + 1 /* border */;
328 geo
->entry_width
= grub_term_width (term
) - 5;
330 geo
->first_entry_y
= 2 /* two empty lines*/
331 + 1 /* GNU GRUB version text */ + 1 /* top border */;
333 geo
->timeout_lines
= 2;
335 /* 3 lines for timeout message and bottom margin. 2 lines for the border. */
336 geo
->num_entries
= grub_term_height (term
) - geo
->first_entry_y
337 - 1 /* bottom border */
338 - 1 /* empty line before info message*/
339 - geo
->timeout_lines
/* timeout */
340 - 1 /* empty final line */;
341 msg_num_lines
= print_message (nested
, edit
, term
, 1);
342 if (geo
->num_entries
- msg_num_lines
< 3
343 || geo
->entry_width
< 10)
345 geo
->num_entries
+= 4;
346 geo
->first_entry_y
-= 2;
348 geo
->first_entry_x
-= 1;
349 geo
->entry_width
+= 1;
351 if (geo
->num_entries
- msg_num_lines
< 3
352 || geo
->entry_width
< 10)
354 geo
->num_entries
+= 2;
355 geo
->first_entry_y
-= 1;
356 geo
->first_entry_x
-= 1;
357 geo
->entry_width
+= 2;
361 if (geo
->num_entries
- msg_num_lines
< 3
362 && geo
->timeout_lines
== 2)
364 geo
->timeout_lines
= 1;
368 if (geo
->num_entries
- msg_num_lines
< 3)
370 geo
->num_entries
+= 1;
371 geo
->first_entry_y
-= 1;
375 if (geo
->num_entries
- msg_num_lines
>= 2)
376 geo
->num_entries
-= msg_num_lines
;
380 /* By default, use the same colors for the menu. */
381 old_color_normal
= grub_term_normal_color
;
382 old_color_highlight
= grub_term_highlight_color
;
383 grub_color_menu_normal
= grub_term_normal_color
;
384 grub_color_menu_highlight
= grub_term_highlight_color
;
386 /* Then give user a chance to replace them. */
387 grub_parse_color_name_pair (&grub_color_menu_normal
,
388 grub_env_get ("menu_color_normal"));
389 grub_parse_color_name_pair (&grub_color_menu_highlight
,
390 grub_env_get ("menu_color_highlight"));
393 grub_normal_init_page (term
, empty_lines
);
395 grub_term_cls (term
);
397 grub_term_normal_color
= grub_color_menu_normal
;
398 grub_term_highlight_color
= grub_color_menu_highlight
;
400 draw_border (term
, geo
);
401 grub_term_normal_color
= old_color_normal
;
402 grub_term_highlight_color
= old_color_highlight
;
403 geo
->timeout_y
= geo
->first_entry_y
+ geo
->num_entries
404 + geo
->border
+ empty_lines
;
407 grub_term_gotoxy (term
, GRUB_TERM_MARGIN
,
410 print_message (nested
, edit
, term
, 0);
411 geo
->timeout_y
+= msg_num_lines
;
413 geo
->right_margin
= grub_term_width (term
)
415 - geo
->entry_width
- 1;
419 menu_text_print_timeout (int timeout
, void *dataptr
)
422 _("The highlighted entry will be executed automatically in %ds.");
423 const char *msg_terse
= _("%ds");
424 struct menu_viewer_data
*data
= dataptr
;
425 char *msg_translated
= 0;
427 grub_term_gotoxy (data
->term
, 0, data
->geo
.timeout_y
);
429 if (data
->timeout_msg
== TIMEOUT_TERSE
430 || data
->timeout_msg
== TIMEOUT_TERSE_NO_MARGIN
)
431 msg_translated
= grub_xasprintf (msg_terse
, timeout
);
433 msg_translated
= grub_xasprintf (msg
, timeout
);
437 grub_errno
= GRUB_ERR_NONE
;
441 if (data
->timeout_msg
== TIMEOUT_UNKNOWN
)
443 data
->timeout_msg
= grub_print_message_indented_real (msg_translated
,
445 <= data
->geo
.timeout_lines
? TIMEOUT_NORMAL
: TIMEOUT_TERSE
;
446 if (data
->timeout_msg
== TIMEOUT_TERSE
)
448 grub_free (msg_translated
);
449 msg_translated
= grub_xasprintf (msg_terse
, timeout
);
450 if (grub_term_width (data
->term
) < 10)
451 data
->timeout_msg
= TIMEOUT_TERSE_NO_MARGIN
;
455 grub_print_message_indented (msg_translated
,
456 data
->timeout_msg
== TIMEOUT_TERSE_NO_MARGIN
? 0 : 3,
457 data
->timeout_msg
== TIMEOUT_TERSE_NO_MARGIN
? 0 : 1,
459 grub_free (msg_translated
);
461 grub_term_gotoxy (data
->term
,
462 grub_term_cursor_x (&data
->geo
),
463 data
->geo
.first_entry_y
+ data
->offset
);
464 grub_term_refresh (data
->term
);
468 menu_text_set_chosen_entry (int entry
, void *dataptr
)
470 struct menu_viewer_data
*data
= dataptr
;
471 int oldoffset
= data
->offset
;
472 int complete_redraw
= 0;
474 data
->offset
= entry
- data
->first
;
475 if (data
->offset
> data
->geo
.num_entries
- 1)
477 data
->first
= entry
- (data
->geo
.num_entries
- 1);
478 data
->offset
= data
->geo
.num_entries
- 1;
481 if (data
->offset
< 0)
488 print_entries (data
->menu
, data
);
491 print_entry (data
->geo
.first_entry_y
+ oldoffset
, 0,
492 grub_menu_get_entry (data
->menu
, data
->first
+ oldoffset
),
494 print_entry (data
->geo
.first_entry_y
+ data
->offset
, 1,
495 grub_menu_get_entry (data
->menu
, data
->first
+ data
->offset
),
498 grub_term_refresh (data
->term
);
502 menu_text_fini (void *dataptr
)
504 struct menu_viewer_data
*data
= dataptr
;
506 grub_term_setcursor (data
->term
, 1);
507 grub_term_cls (data
->term
);
512 menu_text_clear_timeout (void *dataptr
)
514 struct menu_viewer_data
*data
= dataptr
;
517 for (i
= 0; i
< data
->geo
.timeout_lines
;i
++)
519 grub_term_gotoxy (data
->term
, 0, data
->geo
.timeout_y
+ i
);
520 grub_print_spaces (data
->term
, grub_term_width (data
->term
) - 1);
522 if (data
->geo
.num_entries
<= 5 && !data
->geo
.border
)
524 grub_term_gotoxy (data
->term
, data
->geo
.first_entry_x
+ data
->geo
.entry_width
525 + data
->geo
.border
+ 1,
526 data
->geo
.first_entry_y
+ data
->geo
.num_entries
- 1);
527 grub_putcode (' ', data
->term
);
529 data
->geo
.timeout_lines
= 0;
530 data
->geo
.num_entries
++;
531 print_entries (data
->menu
, data
);
533 grub_term_gotoxy (data
->term
, grub_term_cursor_x (&data
->geo
),
534 data
->geo
.first_entry_y
+ data
->offset
);
535 grub_term_refresh (data
->term
);
539 grub_menu_try_text (struct grub_term_output
*term
,
540 int entry
, grub_menu_t menu
, int nested
)
542 struct menu_viewer_data
*data
;
543 struct grub_menu_viewer
*instance
;
545 instance
= grub_zalloc (sizeof (*instance
));
549 data
= grub_zalloc (sizeof (*data
));
552 grub_free (instance
);
557 instance
->data
= data
;
558 instance
->set_chosen_entry
= menu_text_set_chosen_entry
;
559 instance
->print_timeout
= menu_text_print_timeout
;
560 instance
->clear_timeout
= menu_text_clear_timeout
;
561 instance
->fini
= menu_text_fini
;
565 data
->offset
= entry
;
568 grub_term_setcursor (data
->term
, 0);
569 grub_menu_init_page (nested
, 0, &data
->geo
, data
->term
);
571 if (data
->offset
> data
->geo
.num_entries
- 1)
573 data
->first
= data
->offset
- (data
->geo
.num_entries
- 1);
574 data
->offset
= data
->geo
.num_entries
- 1;
577 print_entries (menu
, data
);
578 grub_term_refresh (data
->term
);
579 grub_menu_register_viewer (instance
);
581 return GRUB_ERR_NONE
;