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