]> glassweightruler.freedombox.rocks Git - Ventoy.git/blob - GRUB2/MOD_SRC/grub-2.04/grub-core/normal/menu.c
9f44fbd23fdd55d46aa30ba32e376150b2ca0062
[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 char g_ventoy_theme_path[256] = {0};
406 static const char *g_ventoy_cur_img_path = NULL;
407 static void menu_set_chosen_tip(grub_menu_t menu, int entry)
408 {
409 int i;
410 img_info *img;
411 menu_tip *tip;
412 grub_menu_entry_t e = grub_menu_get_entry (menu, entry);
413
414 if (g_ventoy_theme_path[0])
415 {
416 grub_env_set("theme", g_ventoy_theme_path);
417 }
418
419 g_ventoy_tip_msg1 = g_ventoy_tip_msg2 = NULL;
420 if (e && e->id && grub_strncmp(e->id, "VID_", 4) == 0)
421 {
422 g_ventoy_theme_path[0] = 0;
423 img = (img_info *)(void *)grub_strtoul(e->id + 4, NULL, 16);
424 if (img)
425 {
426 g_ventoy_tip_msg1 = img->tip1;
427 g_ventoy_tip_msg2 = img->tip2;
428 g_ventoy_cur_img_path = img->path;
429 }
430 }
431 else if (e && e->id && grub_strncmp(e->id, "DIR_", 4) == 0)
432 {
433 g_ventoy_theme_path[0] = 0;
434 for (i = 0; i < e->argc; i++)
435 {
436 if (e->args[i] && grub_strncmp(e->args[i], "_VTIP_", 6) == 0)
437 {
438 break;
439 }
440 }
441
442 if (i < e->argc)
443 {
444 tip = (menu_tip *)(void *)grub_strtoul(e->args[i] + 6, NULL, 16);
445 if (tip)
446 {
447 g_ventoy_tip_msg1 = tip->tip1;
448 g_ventoy_tip_msg2 = tip->tip2;
449 }
450 }
451 }
452 }
453
454 static void
455 menu_set_chosen_entry (grub_menu_t menu, int entry)
456 {
457 struct grub_menu_viewer *cur;
458
459 menu_set_chosen_tip(menu, entry);
460 for (cur = viewers; cur; cur = cur->next)
461 cur->set_chosen_entry (entry, cur->data);
462 }
463
464 static void
465 menu_print_timeout (int timeout)
466 {
467 struct grub_menu_viewer *cur;
468 for (cur = viewers; cur; cur = cur->next)
469 cur->print_timeout (timeout, cur->data);
470 }
471
472 static void
473 menu_fini (void)
474 {
475 struct grub_menu_viewer *cur, *next;
476 for (cur = viewers; cur; cur = next)
477 {
478 next = cur->next;
479 cur->fini (cur->data);
480 grub_free (cur);
481 }
482 viewers = NULL;
483 }
484
485 static void
486 menu_init (int entry, grub_menu_t menu, int nested)
487 {
488 struct grub_term_output *term;
489 int gfxmenu = 0;
490
491 FOR_ACTIVE_TERM_OUTPUTS(term)
492 if (term->fullscreen)
493 {
494 if (grub_env_get ("theme"))
495 {
496 if (!grub_gfxmenu_try_hook)
497 {
498 grub_dl_load ("gfxmenu");
499 grub_print_error ();
500 }
501 if (grub_gfxmenu_try_hook)
502 {
503 grub_err_t err;
504 err = grub_gfxmenu_try_hook (entry, menu, nested);
505 if(!err)
506 {
507 gfxmenu = 1;
508 break;
509 }
510 }
511 else
512 grub_error (GRUB_ERR_BAD_MODULE,
513 N_("module `%s' isn't loaded"),
514 "gfxmenu");
515 grub_print_error ();
516 grub_wait_after_message ();
517 }
518 grub_errno = GRUB_ERR_NONE;
519 term->fullscreen ();
520 break;
521 }
522
523 FOR_ACTIVE_TERM_OUTPUTS(term)
524 {
525 grub_err_t err;
526
527 if (grub_strcmp (term->name, "gfxterm") == 0 && gfxmenu)
528 continue;
529
530 err = grub_menu_try_text (term, entry, menu, nested);
531 if(!err)
532 continue;
533 grub_print_error ();
534 grub_errno = GRUB_ERR_NONE;
535 }
536 }
537
538 static void
539 clear_timeout (void)
540 {
541 struct grub_menu_viewer *cur;
542 for (cur = viewers; cur; cur = cur->next)
543 cur->clear_timeout (cur->data);
544 }
545
546 void
547 grub_menu_register_viewer (struct grub_menu_viewer *viewer)
548 {
549 viewer->next = viewers;
550 viewers = viewer;
551 }
552
553 static int
554 menuentry_eq (const char *id, const char *spec)
555 {
556 const char *ptr1, *ptr2;
557 ptr1 = id;
558 ptr2 = spec;
559 while (1)
560 {
561 if (*ptr2 == '>' && ptr2[1] != '>' && *ptr1 == 0)
562 return 1;
563 if (*ptr2 == '>' && ptr2[1] != '>')
564 return 0;
565 if (*ptr2 == '>')
566 ptr2++;
567 if (*ptr1 != *ptr2)
568 return 0;
569 if (*ptr1 == 0)
570 return 1;
571 ptr1++;
572 ptr2++;
573 }
574 }
575
576
577 /* Get the entry number from the variable NAME. */
578 static int
579 get_entry_number (grub_menu_t menu, const char *name)
580 {
581 const char *val;
582 int entry;
583
584 val = grub_env_get (name);
585 if (! val)
586 return -1;
587
588 grub_error_push ();
589
590 entry = (int) grub_strtoul (val, 0, 0);
591
592 if (grub_errno == GRUB_ERR_BAD_NUMBER)
593 {
594 /* See if the variable matches the title of a menu entry. */
595 grub_menu_entry_t e = menu->entry_list;
596 int i;
597
598 grub_errno = GRUB_ERR_NONE;
599
600 for (i = 0; e; i++)
601 {
602 if (menuentry_eq (e->title, val)
603 || menuentry_eq (e->id, val))
604 {
605 entry = i;
606 break;
607 }
608 e = e->next;
609 }
610
611 if (! e)
612 entry = -1;
613 }
614
615 if (grub_errno != GRUB_ERR_NONE)
616 {
617 grub_errno = GRUB_ERR_NONE;
618 entry = -1;
619 }
620
621 grub_error_pop ();
622
623 return entry;
624 }
625
626 /* Check whether a second has elapsed since the last tick. If so, adjust
627 the timer and return 1; otherwise, return 0. */
628 static int
629 has_second_elapsed (grub_uint64_t *saved_time)
630 {
631 grub_uint64_t current_time;
632
633 current_time = grub_get_time_ms ();
634 if (current_time - *saved_time >= 1000)
635 {
636 *saved_time = current_time;
637 return 1;
638 }
639 else
640 return 0;
641 }
642
643 static void
644 print_countdown (struct grub_term_coordinate *pos, int n)
645 {
646 grub_term_restore_pos (pos);
647 /* NOTE: Do not remove the trailing space characters.
648 They are required to clear the line. */
649 grub_printf ("%d ", n);
650 grub_refresh ();
651 }
652
653 #define GRUB_MENU_PAGE_SIZE 10
654
655 /* Show the menu and handle menu entry selection. Returns the menu entry
656 index that should be executed or -1 if no entry should be executed (e.g.,
657 Esc pressed to exit a sub-menu or switching menu viewers).
658 If the return value is not -1, then *AUTO_BOOT is nonzero iff the menu
659 entry to be executed is a result of an automatic default selection because
660 of the timeout. */
661 static int
662 run_menu (grub_menu_t menu, int nested, int *auto_boot)
663 {
664 const char *cmdstr;
665 grub_uint64_t saved_time;
666 int default_entry,current_entry;
667 int timeout;
668 enum timeout_style timeout_style;
669
670 default_entry = get_entry_number (menu, "default");
671
672 if (g_ventoy_suppress_esc)
673 default_entry = g_ventoy_suppress_esc_default;
674
675 /* If DEFAULT_ENTRY is not within the menu entries, fall back to
676 the first entry. */
677 else if (default_entry < 0 || default_entry >= menu->size)
678 default_entry = 0;
679
680 timeout = grub_menu_get_timeout ();
681 if (timeout < 0)
682 /* If there is no timeout, the "countdown" and "hidden" styles result in
683 the system doing nothing and providing no or very little indication
684 why. Technically this is what the user asked for, but it's not very
685 useful and likely to be a source of confusion, so we disallow this. */
686 grub_env_unset ("timeout_style");
687
688 timeout_style = get_timeout_style ();
689
690 if (timeout_style == TIMEOUT_STYLE_COUNTDOWN
691 || timeout_style == TIMEOUT_STYLE_HIDDEN)
692 {
693 static struct grub_term_coordinate *pos;
694 int entry = -1;
695
696 if (timeout_style == TIMEOUT_STYLE_COUNTDOWN && timeout)
697 {
698 pos = grub_term_save_pos ();
699 print_countdown (pos, timeout);
700 }
701
702 /* Enter interruptible sleep until Escape or a menu hotkey is pressed,
703 or the timeout expires. */
704 saved_time = grub_get_time_ms ();
705 while (1)
706 {
707 int key;
708
709 key = grub_getkey_noblock ();
710 if (key != GRUB_TERM_NO_KEY)
711 {
712 entry = get_entry_index_by_hotkey (menu, key);
713 if (entry >= 0)
714 break;
715 }
716 if (key == GRUB_TERM_ESC)
717 {
718 timeout = -1;
719 break;
720 }
721
722 if (timeout > 0 && has_second_elapsed (&saved_time))
723 {
724 timeout--;
725 if (timeout_style == TIMEOUT_STYLE_COUNTDOWN)
726 print_countdown (pos, timeout);
727 }
728
729 if (timeout == 0)
730 /* We will fall through to auto-booting the default entry. */
731 break;
732 }
733
734 grub_env_unset ("timeout");
735 grub_env_unset ("timeout_style");
736 if (entry >= 0)
737 {
738 *auto_boot = 0;
739 return entry;
740 }
741 }
742
743 /* If timeout is 0, drawing is pointless (and ugly). */
744 if (timeout == 0)
745 {
746 *auto_boot = 1;
747 return default_entry;
748 }
749
750 current_entry = default_entry;
751
752 refresh:
753 menu_set_chosen_tip(menu, current_entry);
754 menu_init (current_entry, menu, nested);
755
756 /* Initialize the time. */
757 saved_time = grub_get_time_ms ();
758
759 timeout = grub_menu_get_timeout ();
760
761 if (timeout > 0)
762 menu_print_timeout (timeout);
763 else
764 clear_timeout ();
765
766 while (1)
767 {
768 int c;
769 timeout = grub_menu_get_timeout ();
770
771 if (grub_normal_exit_level)
772 return -1;
773
774 if (timeout > 0 && has_second_elapsed (&saved_time))
775 {
776 timeout--;
777 grub_menu_set_timeout (timeout);
778 menu_print_timeout (timeout);
779 }
780
781 if (timeout == 0)
782 {
783 grub_env_unset ("timeout");
784 *auto_boot = 1;
785 menu_fini ();
786 return default_entry;
787 }
788
789 c = grub_getkey_noblock ();
790
791 /* Negative values are returned on error. */
792 if ((c != GRUB_TERM_NO_KEY) && (c > 0))
793 {
794 if (timeout >= 0)
795 {
796 grub_env_unset ("timeout");
797 grub_env_unset ("fallback");
798 clear_timeout ();
799 }
800
801 switch (c)
802 {
803 case GRUB_TERM_KEY_HOME:
804 case GRUB_TERM_CTRL | 'a':
805 current_entry = 0;
806 menu_set_chosen_entry (menu, current_entry);
807 break;
808
809 case GRUB_TERM_KEY_END:
810 case GRUB_TERM_CTRL | 'e':
811 current_entry = menu->size - 1;
812 menu_set_chosen_entry (menu, current_entry);
813 break;
814
815 case GRUB_TERM_KEY_UP:
816 case GRUB_TERM_CTRL | 'p':
817 case '^':
818 if (current_entry > 0)
819 current_entry--;
820 menu_set_chosen_entry (menu, current_entry);
821 break;
822
823 case GRUB_TERM_CTRL | 'n':
824 case GRUB_TERM_KEY_DOWN:
825 case 'v':
826 if (current_entry < menu->size - 1)
827 current_entry++;
828 menu_set_chosen_entry (menu, current_entry);
829 break;
830
831 case GRUB_TERM_CTRL | 'g':
832 case GRUB_TERM_KEY_PPAGE:
833 if (current_entry < GRUB_MENU_PAGE_SIZE)
834 current_entry = 0;
835 else
836 current_entry -= GRUB_MENU_PAGE_SIZE;
837 menu_set_chosen_entry (menu, current_entry);
838 break;
839
840 case GRUB_TERM_CTRL | 'c':
841 case GRUB_TERM_KEY_NPAGE:
842 if (current_entry + GRUB_MENU_PAGE_SIZE < menu->size)
843 current_entry += GRUB_MENU_PAGE_SIZE;
844 else
845 current_entry = menu->size - 1;
846 menu_set_chosen_entry (menu, current_entry);
847 break;
848
849 case '\n':
850 case '\r':
851 // case GRUB_TERM_KEY_RIGHT:
852 case GRUB_TERM_CTRL | 'f':
853 menu_fini ();
854 *auto_boot = 0;
855 return current_entry;
856
857 case GRUB_TERM_ESC:
858 if (nested && 0 == g_ventoy_suppress_esc)
859 {
860 menu_fini ();
861 return -1;
862 }
863 break;
864
865 case 'c':
866 menu_fini ();
867 grub_cmdline_run (1, 0);
868 goto refresh;
869
870 case 'e':
871 menu_fini ();
872 {
873 grub_menu_entry_t e = grub_menu_get_entry (menu, current_entry);
874 if (e)
875 grub_menu_entry_run (e);
876 }
877 goto refresh;
878
879 case GRUB_TERM_KEY_F2:
880 case '2':
881 VTOY_COMM_HOTKEY("VTOY_F2_CMD");
882 break;
883 case GRUB_TERM_KEY_F3:
884 case '3':
885 VTOY_COMM_HOTKEY("VTOY_F3_CMD");
886 break;
887 case GRUB_TERM_KEY_F4:
888 case '4':
889 VTOY_COMM_HOTKEY("VTOY_F4_CMD");
890 break;
891 case GRUB_TERM_KEY_F5:
892 case '5':
893 VTOY_COMM_HOTKEY("VTOY_F5_CMD");
894 break;
895 case GRUB_TERM_KEY_F6:
896 case '6':
897 VTOY_COMM_HOTKEY("VTOY_F6_CMD");
898 break;
899 case GRUB_TERM_KEY_F7:
900 menu_fini ();
901 if (g_ventoy_terminal_output == 0)
902 {
903 grub_script_execute_sourcecode("terminal_output console");
904 g_ventoy_terminal_output = 1;
905 }
906 else
907 {
908 grub_script_execute_sourcecode("terminal_output gfxterm");
909 g_ventoy_terminal_output = 0;
910 }
911 goto refresh;
912 case GRUB_TERM_KEY_F1:
913 case '1':
914 menu_fini ();
915 g_ventoy_memdisk_mode = 1 - g_ventoy_memdisk_mode;
916 g_ventoy_menu_refresh = 1;
917 goto refresh;
918
919 case (GRUB_TERM_CTRL | 'i'):
920 menu_fini ();
921 g_ventoy_iso_raw = 1 - g_ventoy_iso_raw;
922 g_ventoy_menu_refresh = 1;
923 goto refresh;
924
925 case (GRUB_TERM_CTRL | 'r'):
926 menu_fini ();
927 g_ventoy_grub2_mode = 1 - g_ventoy_grub2_mode;
928 g_ventoy_menu_refresh = 1;
929 goto refresh;
930
931 case (GRUB_TERM_CTRL | 'w'):
932 menu_fini ();
933 g_ventoy_wimboot_mode = 1 - g_ventoy_wimboot_mode;
934 g_ventoy_menu_refresh = 1;
935 goto refresh;
936
937 case (GRUB_TERM_CTRL | 'u'):
938 menu_fini ();
939 g_ventoy_iso_uefi_drv = 1 - g_ventoy_iso_uefi_drv;
940 g_ventoy_menu_refresh = 1;
941 goto refresh;
942
943 case (GRUB_TERM_CTRL | 'h'):
944 case 'h':
945 {
946 cmdstr = grub_env_get("VTOY_HELP_CMD");
947 if (cmdstr)
948 {
949 grub_script_execute_sourcecode(cmdstr);
950 while (grub_getkey() != GRUB_TERM_ESC)
951 ;
952 menu_fini ();
953 goto refresh;
954 }
955 break;
956 }
957 case (GRUB_TERM_CTRL | 'm'):
958 case 'm':
959 {
960 if (g_ventoy_cur_img_path)
961 {
962 grub_env_set("VTOY_CHKSUM_FILE_PATH", g_ventoy_cur_img_path);
963 cmdstr = grub_env_get("VTOY_CHKSUM_CMD");
964 if (cmdstr)
965 {
966 menu_fini();
967 grub_script_execute_sourcecode(cmdstr);
968 goto refresh;
969 }
970 }
971 else
972 {
973 grub_env_set("VTOY_CHKSUM_FILE_PATH", "X");
974 }
975 break;
976 }
977 default:
978 {
979 int entry;
980
981 entry = get_entry_index_by_hotkey (menu, c);
982 if (entry >= 0)
983 {
984 menu_fini ();
985 *auto_boot = 0;
986 return entry;
987 }
988 }
989 break;
990 }
991 }
992 }
993
994 /* Never reach here. */
995 }
996
997 /* Callback invoked immediately before a menu entry is executed. */
998 static void
999 notify_booting (grub_menu_entry_t entry,
1000 void *userdata __attribute__((unused)))
1001 {
1002 grub_printf (" ");
1003 grub_printf_ (N_("Booting `%s'"), entry->title);
1004 grub_printf ("\n\n");
1005 }
1006
1007 /* Callback invoked when a default menu entry executed because of a timeout
1008 has failed and an attempt will be made to execute the next fallback
1009 entry, ENTRY. */
1010 static void
1011 notify_fallback (grub_menu_entry_t entry,
1012 void *userdata __attribute__((unused)))
1013 {
1014 grub_printf ("\n ");
1015 grub_printf_ (N_("Falling back to `%s'"), entry->title);
1016 grub_printf ("\n\n");
1017 grub_millisleep (DEFAULT_ENTRY_ERROR_DELAY_MS);
1018 }
1019
1020 /* Callback invoked when a menu entry has failed and there is no remaining
1021 fallback entry to attempt. */
1022 static void
1023 notify_execution_failure (void *userdata __attribute__((unused)))
1024 {
1025 if (grub_errno != GRUB_ERR_NONE)
1026 {
1027 grub_print_error ();
1028 grub_errno = GRUB_ERR_NONE;
1029 }
1030 grub_printf ("\n ");
1031 grub_printf_ (N_("Failed to boot both default and fallback entries.\n"));
1032 grub_wait_after_message ();
1033 }
1034
1035 /* Callbacks used by the text menu to provide user feedback when menu entries
1036 are executed. */
1037 static struct grub_menu_execute_callback execution_callback =
1038 {
1039 .notify_booting = notify_booting,
1040 .notify_fallback = notify_fallback,
1041 .notify_failure = notify_execution_failure
1042 };
1043
1044 static grub_err_t
1045 show_menu (grub_menu_t menu, int nested, int autobooted)
1046 {
1047 const char *def;
1048 def = grub_env_get("VTOY_DEFAULT_IMAGE");
1049
1050 while (1)
1051 {
1052 int boot_entry;
1053 grub_menu_entry_t e;
1054 int auto_boot;
1055
1056 boot_entry = run_menu (menu, nested, &auto_boot);
1057 if (boot_entry < 0)
1058 break;
1059
1060 if (auto_boot && def && grub_strcmp(def, "VTOY_EXIT") == 0) {
1061 grub_exit();
1062 }
1063
1064 if (autobooted == 0 && auto_boot == 0) {
1065 g_ventoy_last_entry = boot_entry;
1066 if (g_ventoy_menu_esc)
1067 break;
1068 }
1069
1070 if (autobooted == 0 && g_ventoy_menu_esc && auto_boot) {
1071 g_ventoy_last_entry = boot_entry;
1072 break;
1073 }
1074
1075 e = grub_menu_get_entry (menu, boot_entry);
1076 if (! e)
1077 continue; /* Menu is empty. */
1078
1079 if (2 == e->argc && e->args && e->args[1] && grub_strncmp(e->args[1], "VTOY_RET", 8) == 0)
1080 break;
1081
1082 grub_cls ();
1083
1084 if (auto_boot)
1085 grub_menu_execute_with_fallback (menu, e, autobooted,
1086 &execution_callback, 0);
1087 else
1088 grub_menu_execute_entry (e, 0);
1089 if (autobooted)
1090 break;
1091
1092 if (2 == e->argc && e->args && e->args[1] && grub_strncmp(e->args[1], "VTOY_RUN_RET", 12) == 0)
1093 break;
1094 }
1095
1096 return GRUB_ERR_NONE;
1097 }
1098
1099 grub_err_t
1100 grub_show_menu (grub_menu_t menu, int nested, int autoboot)
1101 {
1102 grub_err_t err1, err2;
1103
1104 while (1)
1105 {
1106 err1 = show_menu (menu, nested, autoboot);
1107 autoboot = 0;
1108 grub_print_error ();
1109
1110 if (grub_normal_exit_level)
1111 break;
1112
1113 err2 = grub_auth_check_authentication (NULL);
1114 if (err2)
1115 {
1116 grub_print_error ();
1117 grub_errno = GRUB_ERR_NONE;
1118 continue;
1119 }
1120
1121 break;
1122 }
1123
1124 return err1;
1125 }