fix(menu): let the basic menu select entries numbered 10 and above

The basic menu listed entries as [10], [11] and so on, but only read a
single digit, so those entries could never be selected.

Keep reading digits while another digit could still select a different
entry, and boot once it can't or when enter is pressed. With ten
entries or fewer, a single digit boots the entry right away as before.
This commit is contained in:
alex authored and Alex Zenla committed 2026-09-26 20:52:15 -07:00
1 parent 9e595bf8d5
commit df0bda04c0
1 file changed
+58 -17
+58 -17
View File
@@ -14,11 +14,14 @@ pub struct BasicMenu;
/// Represents the operation that can be performed by the boot menu.
#[derive(PartialEq, Eq)]
enum MenuOperation {
/// The user selected a numbered entry.
Number(usize),
/// The user typed a digit of an entry number.
Digit(usize),
/// The user selected the escape key to exit the boot menu.
Exit,
/// The user selected the enter key to display the entries again.
/// The user selected the enter key to boot the typed entry number,
/// or to display the entries again if no number was typed.
Enter,
/// The user selected some other key to display the entries again.
Continue,
/// Timeout occurred.
Timeout,
@@ -42,11 +45,15 @@ fn read(input: &mut Input, timeout: Option<Duration>) -> Result<MenuOperation> {
}
// Convert the key to a char.
let c: char = c.into();
// Serial consoles may send a line feed instead of a carriage return.
if matches!(c, '\r' | '\n') {
return Ok(MenuOperation::Enter);
}
// Find the key pressed in the entry number table or continue.
Ok(ENTRY_NUMBER_TABLE
.iter()
.position(|&x| x == c)
.map(MenuOperation::Number)
.map(MenuOperation::Digit)
.unwrap_or(MenuOperation::Continue))
}
@@ -80,7 +87,13 @@ fn select_with_input<'a>(
// The time to wait for a key, or None once a key stops the countdown.
let mut countdown = Some(timeout);
// The entry number typed so far, while more digits could still select another entry.
let mut typed: Option<usize> = None;
// Whether to print the entries before reading the next key.
let mut display = true;
loop {
if display {
// Print all the entries with the number used to select them.
info!("Boot Menu:");
for (index, entry) in entries.iter().enumerate() {
@@ -88,10 +101,13 @@ fn select_with_input<'a>(
info!(" [{}] {}", index, title);
}
// Read from input until a valid operation is selected.
let operation = loop {
info!("Select a boot entry using the number keys.");
if entries.len() > ENTRY_NUMBER_TABLE.len() {
info!("Press enter after the number if the entry does not boot right away.");
}
info!("Press Escape to exit and enter to display the entries again.");
display = false;
}
let operation = read(input, countdown)?;
@@ -100,15 +116,36 @@ fn select_with_input<'a>(
countdown = None;
}
if operation != MenuOperation::Nop {
break operation;
}
};
match operation {
// Entry was selected by number. If the number is invalid, we continue.
MenuOperation::Number(index) => {
let Some(entry) = entries.get(index) else {
// A digit of the entry number was typed.
MenuOperation::Digit(digit) => {
let number = typed
.take()
.unwrap_or(0)
.saturating_mul(10)
.saturating_add(digit);
// If another digit could select a different entry, wait for it or for enter.
if number != 0 && number.saturating_mul(10) < entries.len() {
typed = Some(number);
continue;
}
// Otherwise, boot the entry. If the number is invalid, we continue.
let Some(entry) = entries.get(number) else {
info!("invalid entry number");
continue;
};
return Ok(entry);
}
// Enter boots the typed entry number, or displays the entries again.
MenuOperation::Enter => {
let Some(number) = typed.take() else {
display = true;
continue;
};
let Some(entry) = entries.get(number) else {
info!("invalid entry number");
continue;
};
@@ -121,10 +158,14 @@ fn select_with_input<'a>(
return default_entry(entries);
}
// If the operation is to continue or nop, we can just run the loop again.
MenuOperation::Continue | MenuOperation::Nop => {
continue;
// Any other key clears the typed number and displays the entries again.
MenuOperation::Continue => {
typed = None;
display = true;
}
// If the operation is nop, there is nothing to do.
MenuOperation::Nop => {}
}
}
}