mirror of
https://github.com/edera-dev/sprout.git
synced 2026-11-04 18:42:21 +00:00
fix(menu): stop the basic menu countdown on a key press
Every key that didn't select an entry, like enter or an arrow key, made the basic menu wait for the full timeout again. Stop the countdown on any key instead, like the simple menu, so the menu waits for the user once they start using it.
This commit is contained in:
1 parent
78bcaa79ff
commit
9e595bf8d5
3 files changed
+41
-26
No files matched your search
@@ -35,8 +35,9 @@ pub trait BootMenu {
|
||||
}
|
||||
|
||||
/// Read a key from `input`, giving up once `timeout` passes.
|
||||
/// If `timeout` is [None], this waits for a key indefinitely.
|
||||
/// Returns the key that was pressed, or [None] if the timeout passed.
|
||||
pub fn read_key(input: &mut Input, timeout: Duration) -> Result<Option<Key>> {
|
||||
pub fn read_key(input: &mut Input, timeout: Option<Duration>) -> Result<Option<Key>> {
|
||||
// The event to wait for a key press.
|
||||
let key_event = input
|
||||
.wait_for_key_event()
|
||||
@@ -58,8 +59,11 @@ pub fn read_key(input: &mut Input, timeout: Duration) -> Result<Option<Key>> {
|
||||
// Set a timer to trigger after the specified duration.
|
||||
// The timer is limited to what the firmware can represent, as a longer timeout
|
||||
// can't be converted into a timer trigger.
|
||||
let trigger = TimerTrigger::Relative(timeout.min(MAX_TIMER_DURATION));
|
||||
uefi::boot::set_timer(&events[0], trigger).context("unable to set timeout timer")?;
|
||||
// Without a timeout, the timer is never set, so it never triggers.
|
||||
if let Some(timeout) = timeout {
|
||||
let trigger = TimerTrigger::Relative(timeout.min(MAX_TIMER_DURATION));
|
||||
uefi::boot::set_timer(&events[0], trigger).context("unable to set timeout timer")?;
|
||||
}
|
||||
|
||||
loop {
|
||||
// Wait for either the timer event or the key event to trigger.
|
||||
|
||||
@@ -26,11 +26,11 @@ enum MenuOperation {
|
||||
Nop,
|
||||
}
|
||||
|
||||
/// Read a key from the input device with a duration, returning the [MenuOperation] that was
|
||||
/// performed.
|
||||
fn read(input: &mut Input, timeout: &Duration) -> Result<MenuOperation> {
|
||||
/// Read a key from the input device with an optional timeout, returning the [MenuOperation]
|
||||
/// that was performed. Without a timeout, this waits for a key indefinitely.
|
||||
fn read(input: &mut Input, timeout: Option<Duration>) -> Result<MenuOperation> {
|
||||
// If the timer triggered, the user did not select a numbered entry.
|
||||
let Some(key) = read_key(input, *timeout)? else {
|
||||
let Some(key) = read_key(input, timeout)? else {
|
||||
return Ok(MenuOperation::Timeout);
|
||||
};
|
||||
|
||||
@@ -58,34 +58,48 @@ fn read(input: &mut Input, timeout: &Duration) -> Result<MenuOperation> {
|
||||
}
|
||||
}
|
||||
|
||||
/// Find the default entry in `entries`.
|
||||
fn default_entry(entries: &[BootableEntry]) -> Result<&BootableEntry> {
|
||||
entries
|
||||
.iter()
|
||||
.find(|item| item.is_default())
|
||||
.context("no default entry available")
|
||||
}
|
||||
|
||||
/// Selects an entry from the list of entries using the boot menu.
|
||||
fn select_with_input<'a>(
|
||||
input: &mut Input,
|
||||
timeout: Duration,
|
||||
entries: &'a [BootableEntry],
|
||||
) -> Result<&'a BootableEntry> {
|
||||
// If the timeout is zero, boot the default entry without showing the menu.
|
||||
if 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);
|
||||
|
||||
loop {
|
||||
// If the timeout is not zero, let's display the boot menu.
|
||||
if !timeout.is_zero() {
|
||||
// Print all the entries with the number used to select them.
|
||||
info!("Boot Menu:");
|
||||
for (index, entry) in entries.iter().enumerate() {
|
||||
let title = entry.context().stamp(&entry.declaration().title);
|
||||
info!(" [{}] {}", index, title);
|
||||
}
|
||||
// Print all the entries with the number used to select them.
|
||||
info!("Boot Menu:");
|
||||
for (index, entry) in entries.iter().enumerate() {
|
||||
let title = entry.context().stamp(&entry.declaration().title);
|
||||
info!(" [{}] {}", index, title);
|
||||
}
|
||||
|
||||
// Read from input until a valid operation is selected.
|
||||
let operation = loop {
|
||||
// If the timeout is zero, we can exit immediately because there is nothing to do.
|
||||
if timeout.is_zero() {
|
||||
break MenuOperation::Exit;
|
||||
}
|
||||
|
||||
info!("Select a boot entry using the number keys.");
|
||||
info!("Press Escape to exit and enter to display the entries again.");
|
||||
|
||||
let operation = read(input, &timeout)?;
|
||||
let operation = read(input, countdown)?;
|
||||
|
||||
// Any key stops the countdown, so the menu waits for the user from now on.
|
||||
if operation != MenuOperation::Timeout {
|
||||
countdown = None;
|
||||
}
|
||||
|
||||
if operation != MenuOperation::Nop {
|
||||
break operation;
|
||||
}
|
||||
@@ -104,10 +118,7 @@ fn select_with_input<'a>(
|
||||
// When the user exits the boot menu or a timeout occurs, we should
|
||||
// boot the default entry, if any.
|
||||
MenuOperation::Exit | MenuOperation::Timeout => {
|
||||
return entries
|
||||
.iter()
|
||||
.find(|item| item.is_default())
|
||||
.context("no default entry available");
|
||||
return default_entry(entries);
|
||||
}
|
||||
|
||||
// If the operation is to continue or nop, we can just run the loop again.
|
||||
|
||||
@@ -193,7 +193,7 @@ fn run(
|
||||
|
||||
// Wait for a key, waking up every tick to update the countdown.
|
||||
let tick = remaining.map_or(TICK, |remaining| remaining.min(TICK));
|
||||
let Some(key) = read_key(input, tick)? else {
|
||||
let Some(key) = read_key(input, Some(tick))? else {
|
||||
// Without a countdown there's nothing to do until a key is pressed.
|
||||
let Some(left) = remaining else {
|
||||
continue;
|
||||
|
||||
Reference in new issue
Block a user