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