fix(menu): let a key press show a hidden menu

A menu with a zero timeout or menu-hidden now waits 100 ms for a key press
before booting the default entry, which is how the boot loader interface
lets a user interrupt a hidden menu. menu-disabled still skips the wait.
This commit is contained in:
alex authored and Alex Zenla committed 2026-10-03 19:41:39 -07:00
1 parent 590eeead5e
commit 8bb5f1132d
2 files changed
+31 -3

No files matched your search

+11 -2
View File
@@ -410,6 +410,9 @@ fn run() -> Result<()> {
// Whether the boot menu should wait for the user, instead of counting down. // Whether the boot menu should wait for the user, instead of counting down.
let mut wait_for_user = false; let mut wait_for_user = false;
// Whether the menu is disabled, so that a key press can't bring it up.
let mut menu_disabled = false;
// Apply the chosen timeout. // Apply the chosen timeout.
match timeout { match timeout {
BootloaderInterfaceTimeout::MenuForce => { BootloaderInterfaceTimeout::MenuForce => {
@@ -424,11 +427,17 @@ fn run() -> Result<()> {
menu_timeout = timeout; menu_timeout = timeout;
} }
BootloaderInterfaceTimeout::MenuHidden | BootloaderInterfaceTimeout::MenuDisabled => { BootloaderInterfaceTimeout::MenuHidden => {
// Hide the boot menu by setting the timeout to zero. // Hide the boot menu by setting the timeout to zero.
menu_timeout = 0; menu_timeout = 0;
} }
BootloaderInterfaceTimeout::MenuDisabled => {
// Hide the boot menu, and don't let a key press show it.
menu_timeout = 0;
menu_disabled = true;
}
BootloaderInterfaceTimeout::Timeout(timeout) => { BootloaderInterfaceTimeout::Timeout(timeout) => {
// Configure the timeout to the specified value. // Configure the timeout to the specified value.
menu_timeout = timeout; menu_timeout = timeout;
@@ -593,7 +602,7 @@ fn run() -> Result<()> {
let entry = match forced_entry { let entry = match forced_entry {
Some(entry) => entry, Some(entry) => entry,
// Delegate to the menu to select an entry to boot. // Delegate to the menu to select an entry to boot.
None => menu::select(&timer, menu_timeout, menu_style, &entries) None => menu::select(&timer, menu_timeout, menu_disabled, menu_style, &entries)
.context("unable to select entry via boot menu")?, .context("unable to select entry via boot menu")?,
}; };
+20 -1
View File
@@ -31,6 +31,9 @@ mod logo;
/// simple: A full-screen menu that selects entries with the arrow keys. /// simple: A full-screen menu that selects entries with the arrow keys.
pub mod simple; pub mod simple;
/// How long a hidden menu waits for a key press before the default entry is booted.
const HIDDEN_MENU_KEY_WAIT: Duration = Duration::from_millis(100);
/// The longest duration a single timer is set for. This is well within the range of /// The longest duration a single timer is set for. This is well within the range of
/// the 100ns units that timers are set in, and still over a century long. /// the 100ns units that timers are set in, and still over a century long.
const MAX_TIMER_DURATION: Duration = Duration::from_secs(u32::MAX as u64); const MAX_TIMER_DURATION: Duration = Duration::from_secs(u32::MAX as u64);
@@ -167,13 +170,29 @@ pub fn wait_for_events(events: &[Event], timeout: Option<Duration>) -> Result<Op
} }
/// Shows a boot menu of the specified `style` to select a bootable entry to boot. /// Shows a boot menu of the specified `style` to select a bootable entry to boot.
/// See [BootMenu::select] for how `timeout` is handled. /// See [BootMenu::select] for how `timeout` is handled. A zero `timeout` hides the menu, but
/// the menu is still shown if a key is pressed right away, unless `menu_disabled` is set.
pub fn select<'live>( pub fn select<'live>(
timer: &'live PlatformTimer, timer: &'live PlatformTimer,
timeout: Option<Duration>, timeout: Option<Duration>,
menu_disabled: bool,
style: MenuStyle, style: MenuStyle,
entries: &'live [BootableEntry], entries: &'live [BootableEntry],
) -> Result<&'live BootableEntry> { ) -> Result<&'live BootableEntry> {
// A hidden menu gives the user a moment to ask for the menu with a key press.
let timeout = if timeout.is_some_and(|timeout| timeout.is_zero()) && !menu_disabled {
match uefi::system::with_stdin(|input| read_key(input, Some(HIDDEN_MENU_KEY_WAIT))) {
Ok(Some(_)) => None,
Ok(None) => timeout,
Err(error) => {
warn!("unable to check for a key press: {:#}", error);
timeout
}
}
} else {
timeout
};
// Notify the bootloader interface that we are about to display the menu. // Notify the bootloader interface that we are about to display the menu.
// This is only informational, so it should not prevent booting. // This is only informational, so it should not prevent booting.
if let Err(error) = BootloaderInterface::mark_menu(timer) { if let Err(error) = BootloaderInterface::mark_menu(timer) {