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