]> glassweightruler.freedombox.rocks Git - Ventoy.git/blob - GRUB2/MOD_SRC/grub-2.04/grub-core/normal/menu.c
c83c911b5f74c7719a8cd47c45631a4a219d56de
[Ventoy.git] / GRUB2 / MOD_SRC / grub-2.04 / grub-core / normal / menu.c
1 /* menu.c - General supporting functionality for menus. */
2 /*
3 * GRUB -- GRand Unified Bootloader
4 * Copyright (C) 2003,2004,2005,2006,2007,2008,2009,2010 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/misc.h>
22 #include <grub/loader.h>
23 #include <grub/mm.h>
24 #include <grub/time.h>
25 #include <grub/env.h>
26 #include <grub/menu_viewer.h>
27 #include <grub/command.h>
28 #include <grub/parser.h>
29 #include <grub/auth.h>
30 #include <grub/i18n.h>
31 #include <grub/term.h>
32 #include <grub/script_sh.h>
33 #include <grub/gfxterm.h>
34 #include <grub/dl.h>
35 #include <grub/env.h>
36 #include <grub/extcmd.h>
37 #include <grub/ventoy.h>
38 #include "ventoy/ventoy_def.h"
39
40 int g_ventoy_menu_refresh = 0;
41 int g_ventoy_memdisk_mode = 0;
42 int g_ventoy_iso_raw = 0;
43 int g_ventoy_grub2_mode = 0;
44 int g_ventoy_wimboot_mode = 0;
45 int g_ventoy_iso_uefi_drv = 0;
46 int g_ventoy_last_entry = -1;
47 int g_ventoy_suppress_esc = 0;
48 int g_ventoy_suppress_esc_default = 1;
49 int g_ventoy_menu_esc = 0;
50 int g_ventoy_fn_mutex = 0;
51 int g_ventoy_terminal_output = 0;
52
53 #define VTOY_COMM_HOTKEY(cmdkey) \
54 if (0 == g_ventoy_fn_mutex) { \
55 cmdstr = grub_env_get(cmdkey); \
56 if (cmdstr) \
57 { \
58 menu_fini (); \
59 g_ventoy_fn_mutex = 1; \
60 grub_script_execute_sourcecode(cmdstr); \
61 g_ventoy_fn_mutex = 0; \
62 goto refresh; \
63 } \
64 }
65
66 /* Time to delay after displaying an error message about a default/fallback
67 entry failing to boot. */
68 #define DEFAULT_ENTRY_ERROR_DELAY_MS 2500
69
70 grub_err_t (*grub_gfxmenu_try_hook) (int entry, grub_menu_t menu,
71 int nested) = NULL;
72
73 enum timeout_style {
74 TIMEOUT_STYLE_MENU,
75 TIMEOUT_STYLE_COUNTDOWN,
76 TIMEOUT_STYLE_HIDDEN
77 };
78
79 struct timeout_style_name {
80 const char *name;
81 enum timeout_style style;
82 } timeout_style_names[] = {
83 {"menu", TIMEOUT_STYLE_MENU},
84 {"countdown", TIMEOUT_STYLE_COUNTDOWN},
85 {"hidden", TIMEOUT_STYLE_HIDDEN},
86 {NULL, 0}
87 };
88
89 /* Wait until the user pushes any key so that the user
90 can see what happened. */
91 void
92 grub_wait_after_message (void)
93 {
94 grub_uint64_t endtime;
95 grub_xputs ("\n");
96 grub_printf_ (N_("Press any key to continue..."));
97 grub_refresh ();
98
99 endtime = grub_get_time_ms () + 10000;
100
101 while (grub_get_time_ms () < endtime
102 && grub_getkey_noblock () == GRUB_TERM_NO_KEY);
103
104 grub_xputs ("\n");
105 }
106
107 /* Get a menu entry by its index in the entry list. */
108 grub_menu_entry_t
109 grub_menu_get_entry (grub_menu_t menu, int no)
110 {
111 grub_menu_entry_t e;
112
113 for (e = menu->entry_list; e && no > 0; e = e->next, no--)
114 ;
115
116 return e;
117 }
118
119 /* Get the index of a menu entry associated with a given hotkey, or -1. */
120 static int
121 get_entry_index_by_hotkey (grub_menu_t menu, int hotkey)
122 {
123 grub_menu_entry_t entry;
124 int i;
125
126 for (i = 0, entry = menu->entry_list; i < menu->size;
127 i++, entry = entry->next)
128 if (entry->hotkey == hotkey)
129 return i;
130
131 return -1;
132 }
133
134 /* Return the timeout style. If the variable "timeout_style" is not set or
135 invalid, default to TIMEOUT_STYLE_MENU. */
136 static enum timeout_style
137 get_timeout_style (void)
138 {
139 const char *val;
140 struct timeout_style_name *style_name;
141
142 val = grub_env_get ("timeout_style");
143 if (!val)
144 return TIMEOUT_STYLE_MENU;
145
146 for (style_name = timeout_style_names; style_name->name; style_name++)
147 if (grub_strcmp (style_name->name, val) == 0)
148 return style_name->style;
149
150 return TIMEOUT_STYLE_MENU;
151 }
152
153 /* Return the current timeout. If the variable "timeout" is not set or
154 invalid, return -1. */
155 int
156 grub_menu_get_timeout (void)
157 {
158 const char *val;
159 int timeout;
160
161 val = grub_env_get ("timeout");
162 if (! val)
163 return -1;
164
165 grub_error_push ();
166
167 timeout = (int) grub_strtoul (val, 0, 0);
168
169 /* If the value is invalid, unset the variable. */
170 if (grub_errno != GRUB_ERR_NONE)
171 {
172 grub_env_unset ("timeout");
173 grub_errno = GRUB_ERR_NONE;
174 timeout = -1;
175 }
176
177 grub_error_pop ();
178
179 return timeout;
180 }
181
182 /* Set current timeout in the variable "timeout". */
183 void
184 grub_menu_set_timeout (int timeout)
185 {
186 /* Ignore TIMEOUT if it is zero, because it will be unset really soon. */
187 if (timeout > 0)
188 {
189 char buf[16];
190
191 grub_snprintf (buf, sizeof (buf), "%d", timeout);
192 grub_env_set ("timeout", buf);
193 }
194 }
195
196 /* Get the first entry number from the value of the environment variable NAME,
197 which is a space-separated list of non-negative integers. The entry number
198 which is returned is stripped from the value of NAME. If no entry number
199 can be found, -1 is returned. */
200 static int
201 get_and_remove_first_entry_number (const char *name)
202 {
203 const char *val;
204 char *tail;
205 int entry;
206
207 val = grub_env_get (name);
208 if (! val)
209 return -1;
210
211 grub_error_push ();
212
213 entry = (int) grub_strtoul (val, &tail, 0);
214
215 if (grub_errno == GRUB_ERR_NONE)
216 {
217 /* Skip whitespace to find the next digit. */
218 while (*tail && grub_isspace (*tail))
219 tail++;
220 grub_env_set (name, tail);
221 }
222 else
223 {
224 grub_env_unset (name);
225 grub_errno = GRUB_ERR_NONE;
226 entry = -1;
227 }
228
229 grub_error_pop ();
230
231 return entry;
232 }
233
234 /* Run a menu entry. */
235 static void
236 grub_menu_execute_entry(grub_menu_entry_t entry, int auto_boot)
237 {
238 grub_err_t err = GRUB_ERR_NONE;
239 int errs_before;
240 grub_menu_t menu = NULL;
241 char *optr, *buf, *oldchosen = NULL, *olddefault = NULL;
242 const char *ptr, *chosen, *def;
243 grub_size_t sz = 0;
244
245 if (entry->restricted)
246 err = grub_auth_check_authentication (entry->users);
247
248 if (err)
249 {
250 grub_print_error ();
251 grub_errno = GRUB_ERR_NONE;
252 return;
253 }
254
255 errs_before = grub_err_printed_errors;
256
257 chosen = grub_env_get ("chosen");
258 def = grub_env_get ("default");
259
260 if (entry->submenu)
261 {
262 grub_env_context_open ();
263 menu = grub_zalloc (sizeof (*menu));
264 if (! menu)
265 return;
266 grub_env_set_menu (menu);
267 if (auto_boot)
268 grub_env_set ("timeout", "0");
269 }
270
271 for (ptr = entry->id; *ptr; ptr++)
272 sz += (*ptr == '>') ? 2 : 1;
273 if (chosen)
274 {
275 oldchosen = grub_strdup (chosen);
276 if (!oldchosen)
277 grub_print_error ();
278 }
279 if (def)
280 {
281 olddefault = grub_strdup (def);
282 if (!olddefault)
283 grub_print_error ();
284 }
285 sz++;
286 if (chosen)
287 sz += grub_strlen (chosen);
288 sz++;
289 buf = grub_malloc (sz);
290 if (!buf)
291 grub_print_error ();
292 else
293 {
294 optr = buf;
295 if (chosen)
296 {
297 optr = grub_stpcpy (optr, chosen);
298 *optr++ = '>';
299 }
300 for (ptr = entry->id; *ptr; ptr++)
301 {
302 if (*ptr == '>')
303 *optr++ = '>';
304 *optr++ = *ptr;
305 }
306 *optr = 0;
307 grub_env_set ("chosen", buf);
308 grub_env_export ("chosen");
309 grub_free (buf);
310 }
311
312 for (ptr = def; ptr && *ptr; ptr++)
313 {
314 if (ptr[0] == '>' && ptr[1] == '>')
315 {
316 ptr++;
317 continue;
318 }
319 if (ptr[0] == '>')
320 break;
321 }
322
323 if (ptr && ptr[0] && ptr[1])
324 grub_env_set ("default", ptr + 1);
325 else
326 grub_env_unset ("default");
327
328 grub_script_execute_new_scope (entry->sourcecode, entry->argc, entry->args);
329
330 if (errs_before != grub_err_printed_errors)
331 grub_wait_after_message ();
332
333 errs_before = grub_err_printed_errors;
334
335 if (grub_errno == GRUB_ERR_NONE && grub_loader_is_loaded ())
336 /* Implicit execution of boot, only if something is loaded. */
337 grub_command_execute ("boot", 0, 0);
338
339 if (errs_before != grub_err_printed_errors)
340 grub_wait_after_message ();
341
342 if (entry->submenu)
343 {
344 if (menu && menu->size)
345 {
346 grub_show_menu (menu, 1, auto_boot);
347 grub_normal_free_menu (menu);
348 }
349 grub_env_context_close ();
350 }
351 if (oldchosen)
352 grub_env_set ("chosen", oldchosen);
353 else
354 grub_env_unset ("chosen");
355 if (olddefault)
356 grub_env_set ("default", olddefault);
357 else
358 grub_env_unset ("default");
359 grub_env_unset ("timeout");
360 }
361
362 /* Execute ENTRY from the menu MENU, falling back to entries specified
363 in the environment variable "fallback" if it fails. CALLBACK is a
364 pointer to a struct of function pointers which are used to allow the
365 caller provide feedback to the user. */
366 static void
367 grub_menu_execute_with_fallback (grub_menu_t menu,
368 grub_menu_entry_t entry,
369 int autobooted,
370 grub_menu_execute_callback_t callback,
371 void *callback_data)
372 {
373 int fallback_entry;
374
375 callback->notify_booting (entry, callback_data);
376
377 grub_menu_execute_entry (entry, 1);
378
379 /* Deal with fallback entries. */
380 while ((fallback_entry = get_and_remove_first_entry_number ("fallback"))
381 >= 0)
382 {
383 grub_print_error ();
384 grub_errno = GRUB_ERR_NONE;
385
386 entry = grub_menu_get_entry (menu, fallback_entry);
387 callback->notify_fallback (entry, callback_data);
388 grub_menu_execute_entry (entry, 1);
389 /* If the function call to execute the entry returns at all, then this is
390 taken to indicate a boot failure. For menu entries that do something
391 other than actually boot an operating system, this could assume
392 incorrectly that something failed. */
393 }
394
395 if (!autobooted)
396 callback->notify_failure (callback_data);
397 }
398
399 static struct grub_menu_viewer *viewers;
400
401 int g_menu_update_mode = 0;
402 int g_ventoy_tip_label_enable = 0;
403 const char * g_ventoy_tip_msg1 = NULL;
404 const char * g_ventoy_tip_msg2 = NULL;
405 static const char *g_ventoy_cur_img_path = NULL;
406 static void menu_set_chosen_tip(grub_menu_t menu, int entry)
407 {
408 img_info *img;
409 grub_menu_entry_t e = grub_menu_get_entry (menu, entry);
410
411 g_ventoy_tip_msg1 = g_ventoy_tip_msg2 = NULL;
412 if (e && e->id && grub_strncmp(e->id, "VID_", 4) == 0)
413 {
414 img = (img_info *)(void *)grub_strtoul(e->id + 4, NULL, 16);
415 if (img)
416 {
417 g_ventoy_tip_msg1 = img->tip1;
418 g_ventoy_tip_msg2 = img->tip2;
419 g_ventoy_cur_img_path = img->path;
420 }
421 }
422 }
423
424 static void
425 menu_set_chosen_entry (grub_menu_t menu, int entry)
426 {
427 struct grub_menu_viewer *cur;
428
429 menu_set_chosen_tip(menu, entry);
430 for (cur = viewers; cur; cur = cur->next)
431 cur->set_chosen_entry (entry, cur->data);
432 }
433
434 static void
435 menu_print_timeout (int timeout)
436 {
437 struct grub_menu_viewer *cur;
438 for (cur = viewers; cur; cur = cur->next)
439 cur->print_timeout (timeout, cur->data);
440 }
441
442 static void
443 menu_fini (void)
444 {
445 struct grub_menu_viewer *cur, *next;
446 for (cur = viewers; cur; cur = next)
447 {
448 next = cur->next;
449 cur->fini (cur->data);
450 grub_free (cur);
451 }
452 viewers = NULL;
453 }
454
455 static void
456 menu_init (int entry, grub_menu_t menu, int nested)
457 {
458 struct grub_term_output *term;
459 int gfxmenu = 0;
460
461 FOR_ACTIVE_TERM_OUTPUTS(term)
462 if (term->fullscreen)
463 {
464 if (grub_env_get ("theme"))
465 {
466 if (!grub_gfxmenu_try_hook)
467 {
468 grub_dl_load ("gfxmenu");
469 grub_print_error ();
470 }
471 if (grub_gfxmenu_try_hook)
472 {
473 grub_err_t err;
474 err = grub_gfxmenu_try_hook (entry, menu, nested);
475 if(!err)
476 {
477 gfxmenu = 1;
478 break;
479 }
480 }
481 else
482 grub_error (GRUB_ERR_BAD_MODULE,
483 N_("module `%s' isn't loaded"),
484 "gfxmenu");
485 grub_print_error ();
486 grub_wait_after_message ();
487 }
488 grub_errno = GRUB_ERR_NONE;
489 term->fullscreen ();
490 break;
491 }
492
493 FOR_ACTIVE_TERM_OUTPUTS(term)
494 {
495 grub_err_t err;
496
497 if (grub_strcmp (term->name, "gfxterm") == 0 && gfxmenu)
498 continue;
499
500 err = grub_menu_try_text (term, entry, menu, nested);
501 if(!err)
502 continue;
503 grub_print_error ();
504 grub_errno = GRUB_ERR_NONE;
505 }
506 }
507
508 static void
509 clear_timeout (void)
510 {
511 struct grub_menu_viewer *cur;
512 for (cur = viewers; cur; cur = cur->next)
513 cur->clear_timeout (cur->data);
514 }
515
516 void
517 grub_menu_register_viewer (struct grub_menu_viewer *viewer)
518 {
519 viewer->next = viewers;
520 viewers = viewer;
521 }
522
523 static int
524 menuentry_eq (const char *id, const char *spec)
525 {
526 const char *ptr1, *ptr2;
527 ptr1 = id;
528 ptr2 = spec;
529 while (1)
530 {
531 if (*ptr2 == '>' && ptr2[1] != '>' && *ptr1 == 0)
532 return 1;
533 if (*ptr2 == '>' && ptr2[1] != '>')
534 return 0;
535 if (*ptr2 == '>')
536 ptr2++;
537 if (*ptr1 != *ptr2)
538 return 0;
539 if (*ptr1 == 0)
540 return 1;
541 ptr1++;
542 ptr2++;
543 }
544 }
545
546
547 /* Get the entry number from the variable NAME. */
548 static int
549 get_entry_number (grub_menu_t menu, const char *name)
550 {
551 const char *val;
552 int entry;
553
554 val = grub_env_get (name);
555 if (! val)
556 return -1;
557
558 grub_error_push ();
559
560 entry = (int) grub_strtoul (val, 0, 0);
561
562 if (grub_errno == GRUB_ERR_BAD_NUMBER)
563 {
564 /* See if the variable matches the title of a menu entry. */
565 grub_menu_entry_t e = menu->entry_list;
566 int i;
567
568 grub_errno = GRUB_ERR_NONE;
569
570 for (i = 0; e; i++)
571 {
572 if (menuentry_eq (e->title, val)
573 || menuentry_eq (e->id, val))
574 {
575 entry = i;
576 break;
577 }
578 e = e->next;
579 }
580
581 if (! e)
582 entry = -1;
583 }
584
585 if (grub_errno != GRUB_ERR_NONE)
586 {
587 grub_errno = GRUB_ERR_NONE;
588 entry = -1;
589 }
590
591 grub_error_pop ();
592
593 return entry;
594 }
595
596 /* Check whether a second has elapsed since the last tick. If so, adjust
597 the timer and return 1; otherwise, return 0. */
598 static int
599 has_second_elapsed (grub_uint64_t *saved_time)
600 {
601 grub_uint64_t current_time;
602
603 current_time = grub_get_time_ms ();
604 if (current_time - *saved_time >= 1000)
605 {
606 *saved_time = current_time;
607 return 1;
608 }
609 else
610 return 0;
611 }
612
613 static void
614 print_countdown (struct grub_term_coordinate *pos, int n)
615 {
616 grub_term_restore_pos (pos);
617 /* NOTE: Do not remove the trailing space characters.
618 They are required to clear the line. */
619 grub_printf ("%d ", n);
620 grub_refresh ();
621 }
622
623 #define GRUB_MENU_PAGE_SIZE 10
624
625 /* Show the menu and handle menu entry selection. Returns the menu entry
626 index that should be executed or -1 if no entry should be executed (e.g.,
627 Esc pressed to exit a sub-menu or switching menu viewers).
628 If the return value is not -1, then *AUTO_BOOT is nonzero iff the menu
629 entry to be executed is a result of an automatic default selection because
630 of the timeout. */
631 static int
632 run_menu (grub_menu_t menu, int nested, int *auto_boot)
633 {
634 const char *cmdstr;
635 grub_uint64_t saved_time;
636 int default_entry,current_entry;
637 int timeout;
638 enum timeout_style timeout_style;
639
640 default_entry = get_entry_number (menu, "default");
641
642 if (g_ventoy_suppress_esc)
643 default_entry = g_ventoy_suppress_esc_default;
644 else if (g_ventoy_last_entry >= 0 && g_ventoy_last_entry < menu->size) {
645 default_entry = g_ventoy_last_entry;
646 }
647 /* If DEFAULT_ENTRY is not within the menu entries, fall back to
648 the first entry. */
649 else if (default_entry < 0 || default_entry >= menu->size)
650 default_entry = 0;
651
652 timeout = grub_menu_get_timeout ();
653 if (timeout < 0)
654 /* If there is no timeout, the "countdown" and "hidden" styles result in
655 the system doing nothing and providing no or very little indication
656 why. Technically this is what the user asked for, but it's not very
657 useful and likely to be a source of confusion, so we disallow this. */
658 grub_env_unset ("timeout_style");
659
660 timeout_style = get_timeout_style ();
661
662 if (timeout_style == TIMEOUT_STYLE_COUNTDOWN
663 || timeout_style == TIMEOUT_STYLE_HIDDEN)
664 {
665 static struct grub_term_coordinate *pos;
666 int entry = -1;
667
668 if (timeout_style == TIMEOUT_STYLE_COUNTDOWN && timeout)
669 {
670 pos = grub_term_save_pos ();
671 print_countdown (pos, timeout);
672 }
673
674 /* Enter interruptible sleep until Escape or a menu hotkey is pressed,
675 or the timeout expires. */
676 saved_time = grub_get_time_ms ();
677 while (1)
678 {
679 int key;
680
681 key = grub_getkey_noblock ();
682 if (key != GRUB_TERM_NO_KEY)
683 {
684 entry = get_entry_index_by_hotkey (menu, key);
685 if (entry >= 0)
686 break;
687 }
688 if (key == GRUB_TERM_ESC)
689 {
690 timeout = -1;
691 break;
692 }
693
694 if (timeout > 0 && has_second_elapsed (&saved_time))
695 {
696 timeout--;
697 if (timeout_style == TIMEOUT_STYLE_COUNTDOWN)
698 print_countdown (pos, timeout);
699 }
700
701 if (timeout == 0)
702 /* We will fall through to auto-booting the default entry. */
703 break;
704 }
705
706 grub_env_unset ("timeout");
707 grub_env_unset ("timeout_style");
708 if (entry >= 0)
709 {
710 *auto_boot = 0;
711 return entry;
712 }
713 }
714
715 /* If timeout is 0, drawing is pointless (and ugly). */
716 if (timeout == 0)
717 {
718 *auto_boot = 1;
719 return default_entry;
720 }
721
722 current_entry = default_entry;
723
724 refresh:
725 menu_set_chosen_tip(menu, current_entry);
726 menu_init (current_entry, menu, nested);
727
728 /* Initialize the time. */
729 saved_time = grub_get_time_ms ();
730
731 timeout = grub_menu_get_timeout ();
732
733 if (timeout > 0)
734 menu_print_timeout (timeout);
735 else
736 clear_timeout ();
737
738 while (1)
739 {
740 int c;
741 timeout = grub_menu_get_timeout ();
742
743 if (grub_normal_exit_level)
744 return -1;
745
746 if (timeout > 0 && has_second_elapsed (&saved_time))
747 {
748 timeout--;
749 grub_menu_set_timeout (timeout);
750 menu_print_timeout (timeout);
751 }
752
753 if (timeout == 0)
754 {
755 grub_env_unset ("timeout");
756 *auto_boot = 1;
757 menu_fini ();
758 return default_entry;
759 }
760
761 c = grub_getkey_noblock ();
762
763 /* Negative values are returned on error. */
764 if ((c != GRUB_TERM_NO_KEY) && (c > 0))
765 {
766 if (timeout >= 0)
767 {
768 grub_env_unset ("timeout");
769 grub_env_unset ("fallback");
770 clear_timeout ();
771 }
772
773 switch (c)
774 {
775 case GRUB_TERM_KEY_HOME:
776 case GRUB_TERM_CTRL | 'a':
777 current_entry = 0;
778 menu_set_chosen_entry (menu, current_entry);
779 break;
780
781 case GRUB_TERM_KEY_END:
782 case GRUB_TERM_CTRL | 'e':
783 current_entry = menu->size - 1;
784 menu_set_chosen_entry (menu, current_entry);
785 break;
786
787 case GRUB_TERM_KEY_UP:
788 case GRUB_TERM_CTRL | 'p':
789 case '^':
790 if (current_entry > 0)
791 current_entry--;
792 menu_set_chosen_entry (menu, current_entry);
793 break;
794
795 case GRUB_TERM_CTRL | 'n':
796 case GRUB_TERM_KEY_DOWN:
797 case 'v':
798 if (current_entry < menu->size - 1)
799 current_entry++;
800 menu_set_chosen_entry (menu, current_entry);
801 break;
802
803 case GRUB_TERM_CTRL | 'g':
804 case GRUB_TERM_KEY_PPAGE:
805 if (current_entry < GRUB_MENU_PAGE_SIZE)
806 current_entry = 0;
807 else
808 current_entry -= GRUB_MENU_PAGE_SIZE;
809 menu_set_chosen_entry (menu, current_entry);
810 break;
811
812 case GRUB_TERM_CTRL | 'c':
813 case GRUB_TERM_KEY_NPAGE:
814 if (current_entry + GRUB_MENU_PAGE_SIZE < menu->size)
815 current_entry += GRUB_MENU_PAGE_SIZE;
816 else
817 current_entry = menu->size - 1;
818 menu_set_chosen_entry (menu, current_entry);
819 break;
820
821 case '\n':
822 case '\r':
823 // case GRUB_TERM_KEY_RIGHT:
824 case GRUB_TERM_CTRL | 'f':
825 menu_fini ();
826 *auto_boot = 0;
827 return current_entry;
828
829 case GRUB_TERM_ESC:
830 if (nested && 0 == g_ventoy_suppress_esc)
831 {
832 menu_fini ();
833 return -1;
834 }
835 break;
836
837 case 'c':
838 menu_fini ();
839 grub_cmdline_run (1, 0);
840 goto refresh;
841
842 case 'e':
843 menu_fini ();
844 {
845 grub_menu_entry_t e = grub_menu_get_entry (menu, current_entry);
846 if (e)
847 grub_menu_entry_run (e);
848 }
849 goto refresh;
850
851 case GRUB_TERM_KEY_F2:
852 case '2':
853 VTOY_COMM_HOTKEY("VTOY_F2_CMD");
854 break;
855 case GRUB_TERM_KEY_F3:
856 case '3':
857 VTOY_COMM_HOTKEY("VTOY_F3_CMD");
858 break;
859 case GRUB_TERM_KEY_F4:
860 case '4':
861 VTOY_COMM_HOTKEY("VTOY_F4_CMD");
862 break;
863 case GRUB_TERM_KEY_F5:
864 case '5':
865 VTOY_COMM_HOTKEY("VTOY_F5_CMD");
866 break;
867 case GRUB_TERM_KEY_F6:
868 case '6':
869 VTOY_COMM_HOTKEY("VTOY_F6_CMD");
870 break;
871 case GRUB_TERM_KEY_F7:
872 menu_fini ();
873 if (g_ventoy_terminal_output == 0)
874 {
875 grub_script_execute_sourcecode("terminal_output console");
876 g_ventoy_terminal_output = 1;
877 }
878 else
879 {
880 grub_script_execute_sourcecode("terminal_output gfxterm");
881 g_ventoy_terminal_output = 0;
882 }
883 goto refresh;
884 case GRUB_TERM_KEY_F1:
885 case '1':
886 menu_fini ();
887 g_ventoy_memdisk_mode = 1 - g_ventoy_memdisk_mode;
888 g_ventoy_menu_refresh = 1;
889 goto refresh;
890
891 case (GRUB_TERM_CTRL | 'i'):
892 menu_fini ();
893 g_ventoy_iso_raw = 1 - g_ventoy_iso_raw;
894 g_ventoy_menu_refresh = 1;
895 goto refresh;
896
897 case (GRUB_TERM_CTRL | 'r'):
898 menu_fini ();
899 g_ventoy_grub2_mode = 1 - g_ventoy_grub2_mode;
900 g_ventoy_menu_refresh = 1;
901 goto refresh;
902
903 case (GRUB_TERM_CTRL | 'w'):
904 menu_fini ();
905 g_ventoy_wimboot_mode = 1 - g_ventoy_wimboot_mode;
906 g_ventoy_menu_refresh = 1;
907 goto refresh;
908
909 case (GRUB_TERM_CTRL | 'u'):
910 menu_fini ();
911 g_ventoy_iso_uefi_drv = 1 - g_ventoy_iso_uefi_drv;
912 g_ventoy_menu_refresh = 1;
913 goto refresh;
914
915 case (GRUB_TERM_CTRL | 'h'):
916 {
917 cmdstr = grub_env_get("VTOY_HELP_CMD");
918 if (cmdstr)
919 {
920 grub_script_execute_sourcecode(cmdstr);
921 while (grub_getkey() != GRUB_TERM_ESC)
922 ;
923 menu_fini ();
924 goto refresh;
925 }
926 break;
927 }
928 case (GRUB_TERM_CTRL | 'm'):
929 {
930 if (g_ventoy_cur_img_path)
931 {
932 grub_env_set("VTOY_CHKSUM_FILE_PATH", g_ventoy_cur_img_path);
933 cmdstr = grub_env_get("VTOY_CHKSUM_CMD");
934 if (cmdstr)
935 {
936 menu_fini();
937 grub_script_execute_sourcecode(cmdstr);
938 goto refresh;
939 }
940 }
941 else
942 {
943 grub_env_set("VTOY_CHKSUM_FILE_PATH", "X");
944 }
945 break;
946 }
947 default:
948 {
949 int entry;
950
951 entry = get_entry_index_by_hotkey (menu, c);
952 if (entry >= 0)
953 {
954 menu_fini ();
955 *auto_boot = 0;
956 return entry;
957 }
958 }
959 break;
960 }
961 }
962 }
963
964 /* Never reach here. */
965 }
966
967 /* Callback invoked immediately before a menu entry is executed. */
968 static void
969 notify_booting (grub_menu_entry_t entry,
970 void *userdata __attribute__((unused)))
971 {
972 grub_printf (" ");
973 grub_printf_ (N_("Booting `%s'"), entry->title);
974 grub_printf ("\n\n");
975 }
976
977 /* Callback invoked when a default menu entry executed because of a timeout
978 has failed and an attempt will be made to execute the next fallback
979 entry, ENTRY. */
980 static void
981 notify_fallback (grub_menu_entry_t entry,
982 void *userdata __attribute__((unused)))
983 {
984 grub_printf ("\n ");
985 grub_printf_ (N_("Falling back to `%s'"), entry->title);
986 grub_printf ("\n\n");
987 grub_millisleep (DEFAULT_ENTRY_ERROR_DELAY_MS);
988 }
989
990 /* Callback invoked when a menu entry has failed and there is no remaining
991 fallback entry to attempt. */
992 static void
993 notify_execution_failure (void *userdata __attribute__((unused)))
994 {
995 if (grub_errno != GRUB_ERR_NONE)
996 {
997 grub_print_error ();
998 grub_errno = GRUB_ERR_NONE;
999 }
1000 grub_printf ("\n ");
1001 grub_printf_ (N_("Failed to boot both default and fallback entries.\n"));
1002 grub_wait_after_message ();
1003 }
1004
1005 /* Callbacks used by the text menu to provide user feedback when menu entries
1006 are executed. */
1007 static struct grub_menu_execute_callback execution_callback =
1008 {
1009 .notify_booting = notify_booting,
1010 .notify_fallback = notify_fallback,
1011 .notify_failure = notify_execution_failure
1012 };
1013
1014 static grub_err_t
1015 show_menu (grub_menu_t menu, int nested, int autobooted)
1016 {
1017 const char *def;
1018 def = grub_env_get("VTOY_DEFAULT_IMAGE");
1019
1020 while (1)
1021 {
1022 int boot_entry;
1023 grub_menu_entry_t e;
1024 int auto_boot;
1025
1026 boot_entry = run_menu (menu, nested, &auto_boot);
1027 if (boot_entry < 0)
1028 break;
1029
1030 if (auto_boot && def && grub_strcmp(def, "VTOY_EXIT") == 0) {
1031 grub_exit();
1032 }
1033
1034 if (autobooted == 0 && auto_boot == 0) {
1035 g_ventoy_last_entry = boot_entry;
1036 if (g_ventoy_menu_esc)
1037 break;
1038 }
1039
1040 if (autobooted == 0 && g_ventoy_menu_esc && auto_boot) {
1041 g_ventoy_last_entry = boot_entry;
1042 break;
1043 }
1044
1045 e = grub_menu_get_entry (menu, boot_entry);
1046 if (! e)
1047 continue; /* Menu is empty. */
1048
1049 if (2 == e->argc && e->args && e->args[1] && grub_strncmp(e->args[1], "VTOY_RET", 8) == 0)
1050 break;
1051
1052 grub_cls ();
1053
1054 if (auto_boot)
1055 grub_menu_execute_with_fallback (menu, e, autobooted,
1056 &execution_callback, 0);
1057 else
1058 grub_menu_execute_entry (e, 0);
1059 if (autobooted)
1060 break;
1061
1062 if (2 == e->argc && e->args && e->args[1] && grub_strncmp(e->args[1], "VTOY_RUN_RET", 12) == 0)
1063 break;
1064 }
1065
1066 return GRUB_ERR_NONE;
1067 }
1068
1069 grub_err_t
1070 grub_show_menu (grub_menu_t menu, int nested, int autoboot)
1071 {
1072 grub_err_t err1, err2;
1073
1074 while (1)
1075 {
1076 err1 = show_menu (menu, nested, autoboot);
1077 autoboot = 0;
1078 grub_print_error ();
1079
1080 if (grub_normal_exit_level)
1081 break;
1082
1083 err2 = grub_auth_check_authentication (NULL);
1084 if (err2)
1085 {
1086 grub_print_error ();
1087 grub_errno = GRUB_ERR_NONE;
1088 continue;
1089 }
1090
1091 break;
1092 }
1093
1094 return err1;
1095 }