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