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