From 8bb5f1132de298c1a0271341dc540e21d8d314b2 Mon Sep 17 00:00:00 2001 From: Alex Zenla Date: Sat, 3 Oct 2026 19:29:26 -0700 Subject: [PATCH] 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. --- crates/boot/src/main.rs | 13 +++++++++++-- crates/boot/src/menu.rs | 21 ++++++++++++++++++++- 2 files changed, 31 insertions(+), 3 deletions(-) diff --git a/crates/boot/src/main.rs b/crates/boot/src/main.rs index 35ee57b..de90d14 100644 --- a/crates/boot/src/main.rs +++ b/crates/boot/src/main.rs @@ -410,6 +410,9 @@ fn run() -> Result<()> { // Whether the boot menu should wait for the user, instead of counting down. 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. match timeout { BootloaderInterfaceTimeout::MenuForce => { @@ -424,11 +427,17 @@ fn run() -> Result<()> { menu_timeout = timeout; } - BootloaderInterfaceTimeout::MenuHidden | BootloaderInterfaceTimeout::MenuDisabled => { + BootloaderInterfaceTimeout::MenuHidden => { // Hide the boot menu by setting the timeout to zero. 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) => { // Configure the timeout to the specified value. menu_timeout = timeout; @@ -593,7 +602,7 @@ fn run() -> Result<()> { let entry = match forced_entry { Some(entry) => entry, // 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")?, }; diff --git a/crates/boot/src/menu.rs b/crates/boot/src/menu.rs index 519f273..ab17128 100644 --- a/crates/boot/src/menu.rs +++ b/crates/boot/src/menu.rs @@ -31,6 +31,9 @@ mod logo; /// simple: A full-screen menu that selects entries with the arrow keys. 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 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); @@ -167,13 +170,29 @@ pub fn wait_for_events(events: &[Event], timeout: Option) -> Result( timer: &'live PlatformTimer, timeout: Option, + menu_disabled: bool, style: MenuStyle, entries: &'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. // This is only informational, so it should not prevent booting. if let Err(error) = BootloaderInterface::mark_menu(timer) {