fix(menu): show the boot menu when it is forced

--force-menu and LoaderConfigTimeout=menu-force only stopped --boot and
LoaderEntryOneShot from skipping the menu. The timeout was left as is,
so with a zero timeout the menu booted the default entry without being
drawn, and menu-force counted down and booted the default entry, where
the Boot Loader Interface says it should wait for the user.

Menus now take an optional timeout. menu-force waits for the user, and
--force-menu keeps the configured timeout unless it is zero, in which
case it waits for the user too.
This commit is contained in:
alex authored and Alex Zenla committed 2026-09-26 20:52:15 -07:00
1 parent df0bda04c0
commit e8e3361764
4 files changed
+27 -14

No files matched your search

+4 -4
View File
@@ -76,16 +76,16 @@ fn default_entry(entries: &[BootableEntry]) -> Result<&BootableEntry> {
/// Selects an entry from the list of entries using the boot menu.
fn select_with_input<'a>(
input: &mut Input,
timeout: Duration,
timeout: Option<Duration>,
entries: &'a [BootableEntry],
) -> Result<&'a BootableEntry> {
// If the timeout is zero, boot the default entry without showing the menu.
if timeout.is_zero() {
if timeout.is_some_and(|timeout| timeout.is_zero()) {
return default_entry(entries);
}
// The time to wait for a key, or None once a key stops the countdown.
let mut countdown = Some(timeout);
let mut countdown = timeout;
// The entry number typed so far, while more digits could still select another entry.
let mut typed: Option<usize> = None;
@@ -176,7 +176,7 @@ impl BootMenu for BasicMenu {
/// within the context of the standard input device.
fn select<'a>(
&self,
timeout: Duration,
timeout: Option<Duration>,
entries: &'a [BootableEntry],
) -> Result<&'a BootableEntry> {
uefi::system::with_stdin(move |input| select_with_input(input, timeout, entries))
+6 -5
View File
@@ -163,11 +163,12 @@ fn render(
/// Run the menu until an entry is chosen, returning the index of that entry.
/// The `selected` entry is booted if no key is pressed before `timeout` passes.
/// Without a timeout, the menu waits for the user.
fn run(
input: &mut Input,
output: &mut Output,
layout: &Layout,
timeout: Duration,
timeout: Option<Duration>,
entries: &[BootableEntry],
mut selected: usize,
) -> Result<usize> {
@@ -175,7 +176,7 @@ fn run(
// The first visible entry, which changes when scrolling through a long list.
let mut offset = 0;
// The time left before booting, or None once a key stops the countdown.
let mut remaining = Some(timeout);
let mut remaining = timeout;
loop {
// Scroll so that the selected entry is visible.
@@ -230,7 +231,7 @@ fn run(
fn select_with_console<'a>(
input: &mut Input,
output: &mut Output,
timeout: Duration,
timeout: Option<Duration>,
entries: &'a [BootableEntry],
) -> Result<&'a BootableEntry> {
let default = entries
@@ -239,7 +240,7 @@ fn select_with_console<'a>(
.context("no default entry available")?;
// If the timeout is zero, boot the default entry without showing the menu.
if timeout.is_zero() {
if timeout.is_some_and(|timeout| timeout.is_zero()) {
return Ok(&entries[default]);
}
@@ -269,7 +270,7 @@ impl BootMenu for SimpleMenu {
/// within the context of the standard input and output devices.
fn select<'a>(
&self,
timeout: Duration,
timeout: Option<Duration>,
entries: &'a [BootableEntry],
) -> Result<&'a BootableEntry> {
uefi::system::with_stdin(|input| {