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