]> glassweightruler.freedombox.rocks Git - Ventoy.git/blob - GRUB2/MOD_SRC/grub-2.04/grub-core/normal/menu.c
e416c6f322cca2dbefa1de9a046458b4225b2e48
[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 = 0;
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 if (0 == g_ventoy_fn_mutex) {
807 cmdstr = grub_env_get("VTOY_F2_CMD");
808 if (cmdstr)
809 {
810 menu_fini ();
811 g_ventoy_fn_mutex = 1;
812 grub_script_execute_sourcecode(cmdstr);
813 g_ventoy_fn_mutex = 0;
814 goto refresh;
815 }
816 }
817 break;
818 case GRUB_TERM_KEY_F3:
819 if (0 == g_ventoy_fn_mutex) {
820 cmdstr = grub_env_get("VTOY_F3_CMD");
821 if (cmdstr)
822 {
823 menu_fini ();
824 grub_script_execute_sourcecode(cmdstr);
825 goto refresh;
826 }
827 }
828 break;
829 case GRUB_TERM_KEY_F4:
830 if (0 == g_ventoy_fn_mutex) {
831 cmdstr = grub_env_get("VTOY_F4_CMD");
832 if (cmdstr)
833 {
834 menu_fini ();
835 g_ventoy_fn_mutex = 1;
836 grub_script_execute_sourcecode(cmdstr);
837 g_ventoy_fn_mutex = 0;
838 goto refresh;
839 }
840 }
841 break;
842 case GRUB_TERM_KEY_F5:
843 if (0 == g_ventoy_fn_mutex) {
844 cmdstr = grub_env_get("VTOY_F5_CMD");
845 if (cmdstr)
846 {
847 menu_fini ();
848 g_ventoy_fn_mutex = 1;
849 grub_script_execute_sourcecode(cmdstr);
850 g_ventoy_fn_mutex = 0;
851 goto refresh;
852 }
853 }
854 break;
855 case GRUB_TERM_KEY_F6:
856 cmdstr = grub_env_get("VTOY_F6_CMD");
857 if (cmdstr)
858 {
859 menu_fini ();
860 grub_script_execute_sourcecode(cmdstr);
861 goto refresh;
862 }
863 break;
864 case GRUB_TERM_KEY_F7:
865 cmdstr = grub_env_get("VTOY_F7_CMD");
866 if (cmdstr)
867 {
868 menu_fini ();
869 grub_script_execute_sourcecode(cmdstr);
870 goto refresh;
871 }
872 break;
873 case GRUB_TERM_KEY_F1:
874 menu_fini ();
875 g_ventoy_memdisk_mode = 1 - g_ventoy_memdisk_mode;
876 g_ventoy_menu_refresh = 1;
877 goto refresh;
878
879 case (GRUB_TERM_CTRL | 'i'):
880 menu_fini ();
881 g_ventoy_iso_raw = 1 - g_ventoy_iso_raw;
882 g_ventoy_menu_refresh = 1;
883 goto refresh;
884
885 case (GRUB_TERM_CTRL | 'u'):
886 menu_fini ();
887 g_ventoy_iso_uefi_drv = 1 - g_ventoy_iso_uefi_drv;
888 g_ventoy_menu_refresh = 1;
889 goto refresh;
890
891 default:
892 {
893 int entry;
894
895 entry = get_entry_index_by_hotkey (menu, c);
896 if (entry >= 0)
897 {
898 menu_fini ();
899 *auto_boot = 0;
900 return entry;
901 }
902 }
903 break;
904 }
905 }
906 }
907
908 /* Never reach here. */
909 }
910
911 /* Callback invoked immediately before a menu entry is executed. */
912 static void
913 notify_booting (grub_menu_entry_t entry,
914 void *userdata __attribute__((unused)))
915 {
916 grub_printf (" ");
917 grub_printf_ (N_("Booting `%s'"), entry->title);
918 grub_printf ("\n\n");
919 }
920
921 /* Callback invoked when a default menu entry executed because of a timeout
922 has failed and an attempt will be made to execute the next fallback
923 entry, ENTRY. */
924 static void
925 notify_fallback (grub_menu_entry_t entry,
926 void *userdata __attribute__((unused)))
927 {
928 grub_printf ("\n ");
929 grub_printf_ (N_("Falling back to `%s'"), entry->title);
930 grub_printf ("\n\n");
931 grub_millisleep (DEFAULT_ENTRY_ERROR_DELAY_MS);
932 }
933
934 /* Callback invoked when a menu entry has failed and there is no remaining
935 fallback entry to attempt. */
936 static void
937 notify_execution_failure (void *userdata __attribute__((unused)))
938 {
939 if (grub_errno != GRUB_ERR_NONE)
940 {
941 grub_print_error ();
942 grub_errno = GRUB_ERR_NONE;
943 }
944 grub_printf ("\n ");
945 grub_printf_ (N_("Failed to boot both default and fallback entries.\n"));
946 grub_wait_after_message ();
947 }
948
949 /* Callbacks used by the text menu to provide user feedback when menu entries
950 are executed. */
951 static struct grub_menu_execute_callback execution_callback =
952 {
953 .notify_booting = notify_booting,
954 .notify_fallback = notify_fallback,
955 .notify_failure = notify_execution_failure
956 };
957
958 static grub_err_t
959 show_menu (grub_menu_t menu, int nested, int autobooted)
960 {
961 while (1)
962 {
963 int boot_entry;
964 grub_menu_entry_t e;
965 int auto_boot;
966
967 boot_entry = run_menu (menu, nested, &auto_boot);
968 if (boot_entry < 0)
969 break;
970
971 g_ventoy_last_entry = boot_entry;
972 if (g_ventoy_menu_esc)
973 break;
974
975 e = grub_menu_get_entry (menu, boot_entry);
976 if (! e)
977 continue; /* Menu is empty. */
978
979 if (2 == e->argc && e->args && e->args[1] && grub_strncmp(e->args[1], "VTOY_RET", 8) == 0)
980 break;
981
982 grub_cls ();
983
984 if (auto_boot)
985 grub_menu_execute_with_fallback (menu, e, autobooted,
986 &execution_callback, 0);
987 else
988 grub_menu_execute_entry (e, 0);
989 if (autobooted)
990 break;
991 }
992
993 return GRUB_ERR_NONE;
994 }
995
996 grub_err_t
997 grub_show_menu (grub_menu_t menu, int nested, int autoboot)
998 {
999 grub_err_t err1, err2;
1000
1001 while (1)
1002 {
1003 err1 = show_menu (menu, nested, autoboot);
1004 autoboot = 0;
1005 grub_print_error ();
1006
1007 if (grub_normal_exit_level)
1008 break;
1009
1010 err2 = grub_auth_check_authentication (NULL);
1011 if (err2)
1012 {
1013 grub_print_error ();
1014 grub_errno = GRUB_ERR_NONE;
1015 continue;
1016 }
1017
1018 break;
1019 }
1020
1021 return err1;
1022 }