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