mirror of
https://github.com/edera-dev/sprout.git
synced 2026-11-04 16:42:20 +00:00
feat(boot): add enable-logo and enable-animation to the graphical menu (#123)
This commit is contained in:
5 files changed
+78
-25
No files matched your search
@@ -630,8 +630,8 @@ fn run(reboot_on_error: &mut bool) -> Result<()> {
|
||||
.menu_style
|
||||
.unwrap_or(config.options.menu_style);
|
||||
|
||||
// The graphical menu only uses the mouse when it is enabled in the configuration.
|
||||
let graphical_mouse = config.menu_styles.graphical.enable_mouse;
|
||||
// The settings of the graphical menu, which only uses the mouse when it is enabled.
|
||||
let graphical = &config.menu_styles.graphical;
|
||||
|
||||
// Find the forced boot entry, unless the boot menu is forced.
|
||||
// The oneshot entry from the bootloader interface is forced by id, and it is used instead of
|
||||
@@ -672,7 +672,7 @@ fn run(reboot_on_error: &mut bool) -> Result<()> {
|
||||
menu_timeout,
|
||||
menu_disabled,
|
||||
menu_style,
|
||||
graphical_mouse,
|
||||
graphical,
|
||||
&entries,
|
||||
)
|
||||
.context("unable to select entry via boot menu")?,
|
||||
|
||||
@@ -6,7 +6,7 @@ use alloc::vec;
|
||||
use alloc::vec::Vec;
|
||||
use anyhow::{Context, Result};
|
||||
use core::time::Duration;
|
||||
use edera_sprout_config::MenuStyle;
|
||||
use edera_sprout_config::{GraphicalMenuConfiguration, MenuStyle};
|
||||
use eficore::bootloader_interface::BootloaderInterface;
|
||||
use eficore::platform::timer::PlatformTimer;
|
||||
use log::warn;
|
||||
@@ -177,7 +177,7 @@ pub fn select<'live>(
|
||||
timeout: Option<Duration>,
|
||||
menu_disabled: bool,
|
||||
style: MenuStyle,
|
||||
graphical_mouse: bool,
|
||||
graphical: &GraphicalMenuConfiguration,
|
||||
entries: &'live [BootableEntry],
|
||||
) -> Result<&'live BootableEntry> {
|
||||
// A hidden menu gives the user a moment to ask for the menu with a key press.
|
||||
@@ -210,7 +210,9 @@ pub fn select<'live>(
|
||||
// The graphical menu needs a graphics output, which not every machine has.
|
||||
// If it fails, the simple menu is used instead, which falls back to the basic menu.
|
||||
MenuStyle::Graphical => GraphicalMenu {
|
||||
enable_mouse: graphical_mouse,
|
||||
enable_mouse: graphical.enable_mouse,
|
||||
enable_logo: graphical.enable_logo,
|
||||
enable_animation: graphical.enable_animation,
|
||||
}
|
||||
.select(timeout, entries)
|
||||
.or_else(|error| {
|
||||
|
||||
@@ -94,6 +94,10 @@ const CURSOR: [&str; 16] = [
|
||||
pub struct GraphicalMenu {
|
||||
/// Whether the mouse is used when the firmware has a pointing device.
|
||||
pub enable_mouse: bool,
|
||||
/// Whether the logo is drawn next to the name.
|
||||
pub enable_logo: bool,
|
||||
/// Whether the logo bounces.
|
||||
pub enable_animation: bool,
|
||||
}
|
||||
|
||||
/// The pointing devices of the firmware.
|
||||
@@ -231,8 +235,8 @@ struct Layout {
|
||||
columns: usize,
|
||||
/// The row of the line between the entries and the status line.
|
||||
footer: usize,
|
||||
/// The column, row, width and height of the logo when it is on the ground.
|
||||
logo: (usize, usize, usize, usize),
|
||||
/// The column, row, width and height of the logo when it is on the ground, if it is shown.
|
||||
logo: Option<(usize, usize, usize, usize)>,
|
||||
/// How many pixels the logo rises while it bounces.
|
||||
bounce: usize,
|
||||
/// The column, row and scale of the name, if there is room for it.
|
||||
@@ -243,7 +247,7 @@ struct Layout {
|
||||
|
||||
impl Layout {
|
||||
/// Calculate the layout of `entries` on a framebuffer of `screen` width and height.
|
||||
fn new(screen: (usize, usize), entries: &[BootableEntry]) -> Self {
|
||||
fn new(screen: (usize, usize), entries: &[BootableEntry], logo: bool, animate: bool) -> Self {
|
||||
let (width, height) = screen;
|
||||
|
||||
// Large screens get larger text so that it stays readable, but not so large that the
|
||||
@@ -286,16 +290,18 @@ impl Layout {
|
||||
(header, visible) = fit(logo_height);
|
||||
}
|
||||
let logo_width = logo_height * LOGO_WIDTH / LOGO_HEIGHT;
|
||||
let bounce = logo_height / 16;
|
||||
let bounce = if animate { logo_height / 16 } else { 0 };
|
||||
|
||||
// The name is next to the logo, if the two fit together.
|
||||
let title_scale = (logo_height / 32).clamp(scale, scale * 3);
|
||||
let title_width = TITLE.len() * GLYPH_SIZE * title_scale;
|
||||
let fits = logo_width + cell + title_width <= width.saturating_sub(margin * 2);
|
||||
let group = if fits {
|
||||
logo_width + cell + title_width
|
||||
} else {
|
||||
logo_width
|
||||
// Without the logo, the name is alone and takes the place of the group.
|
||||
let shown = if logo { logo_width + cell } else { 0 };
|
||||
let fits = shown + title_width <= width.saturating_sub(margin * 2);
|
||||
let group = match (logo, fits) {
|
||||
(true, true) => shown + title_width,
|
||||
(true, false) => logo_width,
|
||||
(false, _) => title_width,
|
||||
};
|
||||
let group_x = width.saturating_sub(group) / 2;
|
||||
|
||||
@@ -325,10 +331,10 @@ impl Layout {
|
||||
text_x: panel_x + padding + cell,
|
||||
columns: list_width.saturating_sub(cell * 2) / cell,
|
||||
footer: list_y + list + cell,
|
||||
logo: (group_x, top + bounce, logo_width, logo_height),
|
||||
logo: logo.then_some((group_x, top + bounce, logo_width, logo_height)),
|
||||
bounce,
|
||||
title: fits.then_some((
|
||||
group_x + logo_width + cell,
|
||||
group_x + shown,
|
||||
top + bounce + logo_height.saturating_sub(GLYPH_SIZE * title_scale) / 2,
|
||||
title_scale,
|
||||
)),
|
||||
@@ -375,7 +381,11 @@ impl Screen {
|
||||
/// Open every display that the firmware has, or fail if it has none.
|
||||
/// The console splitter has a graphics output that draws to every display. It has no device
|
||||
/// path, so it is only used if there are no outputs for the displays themselves.
|
||||
fn open_all(entries: &[BootableEntry]) -> Result<Vec<Self>> {
|
||||
fn open_all(
|
||||
entries: &[BootableEntry],
|
||||
enable_logo: bool,
|
||||
enable_animation: bool,
|
||||
) -> Result<Vec<Self>> {
|
||||
let handles = uefi::boot::find_handles::<GraphicsOutput>()
|
||||
.context("unable to find a graphics output")?;
|
||||
let has_device_path = |handle: &Handle| {
|
||||
@@ -412,7 +422,7 @@ impl Screen {
|
||||
) else {
|
||||
continue;
|
||||
};
|
||||
let layout = Layout::new((width, height), entries);
|
||||
let layout = Layout::new((width, height), entries, enable_logo, enable_animation);
|
||||
render_base(&mut base, &layout);
|
||||
screens.push(Self {
|
||||
gop,
|
||||
@@ -604,7 +614,9 @@ fn draw_centered(
|
||||
|
||||
/// Draw the logo above the entries, bouncing a little with each of `ticks`.
|
||||
fn draw_logo(fb: &mut Framebuffer, layout: &Layout, ticks: usize) {
|
||||
let (x, floor, width, height) = layout.logo;
|
||||
let Some((x, floor, width, height)) = layout.logo else {
|
||||
return;
|
||||
};
|
||||
|
||||
// The logo rises and falls by up to `bounce` pixels.
|
||||
let phase = ticks % BOUNCE_TICKS;
|
||||
@@ -930,7 +942,7 @@ impl BootMenu for GraphicalMenu {
|
||||
return Ok(&entries[default]);
|
||||
}
|
||||
|
||||
let mut screens = Screen::open_all(entries)?;
|
||||
let mut screens = Screen::open_all(entries, self.enable_logo, self.enable_animation)?;
|
||||
let mut mouse = self.enable_mouse.then(Mouse::open).flatten();
|
||||
|
||||
let result = uefi::system::with_stdin(|input| {
|
||||
|
||||
@@ -111,12 +111,37 @@ pub struct MenuStylesConfiguration {
|
||||
}
|
||||
|
||||
/// Settings for the graphical boot menu.
|
||||
#[derive(Serialize, Deserialize, Debug, Default, Clone)]
|
||||
#[derive(Serialize, Deserialize, Debug, Clone)]
|
||||
pub struct GraphicalMenuConfiguration {
|
||||
/// Enables selecting entries with the mouse, and shows the cursor. When disabled, or when
|
||||
/// the firmware has no pointing device, entries are selected with the keyboard only.
|
||||
#[serde(rename = "enable-mouse", default)]
|
||||
pub enable_mouse: bool,
|
||||
|
||||
/// Shows the Sprout logo next to the name above the entries. When disabled, only the name is
|
||||
/// shown.
|
||||
#[serde(rename = "enable-logo", default = "default_true")]
|
||||
pub enable_logo: bool,
|
||||
|
||||
/// Animates the logo, which bounces. When disabled, the logo stays still.
|
||||
#[serde(rename = "enable-animation", default = "default_true")]
|
||||
pub enable_animation: bool,
|
||||
}
|
||||
|
||||
/// The default graphical menu settings, which match an empty graphical section.
|
||||
impl Default for GraphicalMenuConfiguration {
|
||||
fn default() -> Self {
|
||||
Self {
|
||||
enable_mouse: false,
|
||||
enable_logo: true,
|
||||
enable_animation: true,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// The default for settings that are on unless they are turned off.
|
||||
fn default_true() -> bool {
|
||||
true
|
||||
}
|
||||
|
||||
/// The default configuration, which matches an empty configuration file.
|
||||
@@ -203,6 +228,16 @@ mod tests {
|
||||
assert!(!empty().menu_styles.graphical.enable_mouse);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn graphical_logo_and_animation_are_on_by_default() {
|
||||
let graphical = RootConfiguration::default().menu_styles.graphical;
|
||||
assert!(graphical.enable_logo);
|
||||
assert!(graphical.enable_animation);
|
||||
let graphical = empty().menu_styles.graphical;
|
||||
assert!(graphical.enable_logo);
|
||||
assert!(graphical.enable_animation);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn bls_strict_mode_is_off_by_default() {
|
||||
assert!(!RootConfiguration::default().options.bls_strict_mode);
|
||||
|
||||
+7
-3
@@ -142,11 +142,15 @@ Settings for a particular menu style. Only the graphical menu has any.
|
||||
```toml
|
||||
[menu-styles.graphical]
|
||||
enable-mouse = true
|
||||
enable-logo = false
|
||||
enable-animation = false
|
||||
```
|
||||
|
||||
| Key | Type | Default | Meaning |
|
||||
|----------------|---------|---------|----------------------------------------------------------------------|
|
||||
| `enable-mouse` | boolean | `false` | Select entries with the mouse and show the cursor. Without it, or when the firmware has no pointing device, the menu takes the keyboard only. |
|
||||
| Key | Type | Default | Meaning |
|
||||
|--------------------|---------|---------|------------------------------------------------------------------|
|
||||
| `enable-mouse` | boolean | `false` | Select entries with the mouse and show the cursor. Without it, or when the firmware has no pointing device, the menu takes the keyboard only. |
|
||||
| `enable-logo` | boolean | `true` | Show the Sprout logo next to the name. Without it, only the name is shown. |
|
||||
| `enable-animation` | boolean | `true` | Bounce the logo. Without it, the logo stays still. |
|
||||
|
||||
## `drivers`
|
||||
|
||||
|
||||
Reference in new issue
Block a user