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