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