mirror of
https://github.com/trezor/trezor-firmware.git
synced 2025-03-12 14:16:06 +00:00
feat(eckhart): implement BootloaderUI
- implementation using the new bootloader components - the implementation sits in the separate file `ui_bootloader.rs`
This commit is contained in:
parent
942e3953e2
commit
b90dc276da
@ -1,476 +1,13 @@
|
||||
use crate::{
|
||||
trezorhal::secbool::secbool,
|
||||
ui::{
|
||||
component::{connect::Connect, Label},
|
||||
display::{self, Color, Icon},
|
||||
geometry::{Alignment, Offset, Point, Rect},
|
||||
layout::simplified::{run, show},
|
||||
},
|
||||
};
|
||||
use heapless::String;
|
||||
mod bld_actionbar;
|
||||
mod bld_header;
|
||||
mod bld_menu;
|
||||
mod bld_menu_screen;
|
||||
mod bld_text_screen;
|
||||
mod welcome_screen;
|
||||
|
||||
use super::{
|
||||
bootloader::welcome::Welcome,
|
||||
component::{Button, ResultScreen, WelcomeScreen},
|
||||
cshape::{render_loader, LoaderRange},
|
||||
fonts,
|
||||
theme::{
|
||||
backlight,
|
||||
bootloader::{
|
||||
button_bld, button_bld_menu, button_confirm, button_wipe_cancel, button_wipe_confirm,
|
||||
BLD_BG, BLD_FG, BLD_TITLE_COLOR, BLD_WIPE_COLOR, CHECK24, CHECK40, DOWNLOAD24, FIRE32,
|
||||
FIRE40, RESULT_FW_INSTALL, RESULT_WIPE, TEXT_BOLD, TEXT_NORMAL, TEXT_WIPE_BOLD,
|
||||
TEXT_WIPE_NORMAL, WARNING40, WELCOME_COLOR, X24,
|
||||
},
|
||||
GREEN_LIGHT, GREY,
|
||||
},
|
||||
UIEckhart,
|
||||
};
|
||||
|
||||
use crate::ui::{ui_bootloader::BootloaderUI, CommonUI};
|
||||
|
||||
use crate::ui::{
|
||||
display::{toif::Toif, LOADER_MAX},
|
||||
geometry::Alignment2D,
|
||||
shape,
|
||||
shape::render_on_display,
|
||||
};
|
||||
|
||||
use ufmt::uwrite;
|
||||
|
||||
use super::theme::bootloader::BLD_WARN_COLOR;
|
||||
use intro::Intro;
|
||||
use menu::Menu;
|
||||
|
||||
mod confirm;
|
||||
pub mod intro;
|
||||
pub mod menu;
|
||||
pub mod welcome;
|
||||
use confirm::{Confirm, ConfirmTitle};
|
||||
|
||||
pub type BootloaderString = String<128>;
|
||||
|
||||
const RECONNECT_MESSAGE: &str = "PLEASE RECONNECT\nTHE DEVICE";
|
||||
|
||||
const SCREEN: Rect = UIEckhart::SCREEN;
|
||||
const PROGRESS_TEXT_ORIGIN: Point = Point::new(2, 28);
|
||||
|
||||
impl UIEckhart {
|
||||
fn screen_progress(
|
||||
text: &str,
|
||||
progress: u16,
|
||||
initialize: bool,
|
||||
fg_color: Color,
|
||||
bg_color: Color,
|
||||
icon: Option<(Icon, Color)>,
|
||||
center_text: Option<&str>,
|
||||
) {
|
||||
if initialize {
|
||||
Self::fadeout();
|
||||
}
|
||||
display::sync();
|
||||
|
||||
render_on_display(None, Some(bg_color), |target| {
|
||||
shape::Text::new(PROGRESS_TEXT_ORIGIN, text, fonts::FONT_SATOSHI_REGULAR_38)
|
||||
.with_fg(BLD_FG)
|
||||
.render(target);
|
||||
|
||||
let loader_offset: i16 = 19;
|
||||
let center_text_offset: i16 = 10;
|
||||
let center = SCREEN.center() + Offset::y(loader_offset);
|
||||
let inactive_color = bg_color.blend(fg_color, 85);
|
||||
let end = 360.0 * progress as f32 / 1000.0;
|
||||
|
||||
render_loader(
|
||||
center,
|
||||
inactive_color,
|
||||
fg_color,
|
||||
bg_color,
|
||||
if progress >= LOADER_MAX {
|
||||
LoaderRange::Full
|
||||
} else {
|
||||
LoaderRange::FromTo(0.0, end)
|
||||
},
|
||||
target,
|
||||
);
|
||||
|
||||
if let Some((icon, color)) = icon {
|
||||
shape::ToifImage::new(center, icon.toif)
|
||||
.with_align(Alignment2D::CENTER)
|
||||
.with_fg(color)
|
||||
.render(target);
|
||||
}
|
||||
|
||||
if let Some(center_text) = center_text {
|
||||
shape::Text::new(
|
||||
SCREEN.center() + Offset::y(loader_offset + center_text_offset),
|
||||
center_text,
|
||||
fonts::FONT_SATOSHI_REGULAR_38,
|
||||
)
|
||||
.with_align(Alignment::Center)
|
||||
.with_fg(GREY)
|
||||
.render(target);
|
||||
}
|
||||
});
|
||||
|
||||
display::refresh();
|
||||
if initialize {
|
||||
Self::fadein();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl BootloaderUI for UIEckhart {
|
||||
fn screen_welcome() {
|
||||
let mut frame = Welcome::new();
|
||||
show(&mut frame, true);
|
||||
}
|
||||
|
||||
fn screen_install_success(restart_seconds: u8, initial_setup: bool, complete_draw: bool) {
|
||||
let mut reboot_msg = BootloaderString::new();
|
||||
|
||||
let bg_color = if initial_setup { WELCOME_COLOR } else { BLD_BG };
|
||||
let fg_color = if initial_setup { GREEN_LIGHT } else { BLD_FG };
|
||||
|
||||
if restart_seconds >= 1 {
|
||||
// in practice, restart_seconds is 5 or less so this is fine
|
||||
let seconds_char = b'0' + restart_seconds % 10;
|
||||
unwrap!(reboot_msg.push(seconds_char as char));
|
||||
let progress = (5 - (restart_seconds as u16)).clamp(0, 5) * 200;
|
||||
|
||||
Self::screen_progress(
|
||||
"Restarting device",
|
||||
progress,
|
||||
complete_draw,
|
||||
fg_color,
|
||||
bg_color,
|
||||
None,
|
||||
Some(reboot_msg.as_str()),
|
||||
);
|
||||
} else {
|
||||
Self::screen_progress(
|
||||
"Firmware installed",
|
||||
1000,
|
||||
complete_draw,
|
||||
fg_color,
|
||||
bg_color,
|
||||
Some((Icon::new(CHECK24), BLD_FG)),
|
||||
None,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
fn screen_install_fail() {
|
||||
let mut frame = ResultScreen::new(
|
||||
&RESULT_FW_INSTALL,
|
||||
Icon::new(WARNING40),
|
||||
"Firmware installation was not successful".into(),
|
||||
Label::centered(RECONNECT_MESSAGE.into(), RESULT_FW_INSTALL.title_style())
|
||||
.vertically_centered(),
|
||||
true,
|
||||
);
|
||||
show(&mut frame, true);
|
||||
}
|
||||
|
||||
fn screen_install_confirm(
|
||||
vendor: &str,
|
||||
version: &str,
|
||||
fingerprint: &str,
|
||||
should_keep_seed: bool,
|
||||
is_newvendor: bool,
|
||||
is_newinstall: bool,
|
||||
version_cmp: i32,
|
||||
) -> u32 {
|
||||
let mut version_str: BootloaderString = String::new();
|
||||
unwrap!(version_str.push_str("Firmware version "));
|
||||
unwrap!(version_str.push_str(version));
|
||||
unwrap!(version_str.push_str("\nby "));
|
||||
unwrap!(version_str.push_str(vendor));
|
||||
|
||||
let title_str = if is_newinstall {
|
||||
"INSTALL FIRMWARE"
|
||||
} else if is_newvendor {
|
||||
"CHANGE FW\nVENDOR"
|
||||
} else if version_cmp > 0 {
|
||||
"UPDATE FIRMWARE"
|
||||
} else if version_cmp == 0 {
|
||||
"REINSTALL FW"
|
||||
} else {
|
||||
"DOWNGRADE FW"
|
||||
};
|
||||
let title = Label::left_aligned(title_str.into(), TEXT_BOLD).vertically_centered();
|
||||
let msg = Label::left_aligned(version_str.as_str().into(), TEXT_NORMAL);
|
||||
let alert = (!should_keep_seed).then_some(Label::left_aligned(
|
||||
"SEED WILL BE ERASED!".into(),
|
||||
TEXT_BOLD,
|
||||
));
|
||||
|
||||
let (left, right) = if should_keep_seed {
|
||||
let l = Button::with_text("CANCEL".into())
|
||||
.styled(button_bld())
|
||||
.with_text_align(Alignment::Center);
|
||||
let r = Button::with_text("INSTALL".into())
|
||||
.styled(button_confirm())
|
||||
.with_text_align(Alignment::Center);
|
||||
(l, r)
|
||||
} else {
|
||||
let l = Button::with_icon(Icon::new(X24))
|
||||
.styled(button_bld())
|
||||
.with_text_align(Alignment::Center);
|
||||
let r = Button::with_icon(Icon::new(CHECK24))
|
||||
.styled(button_confirm())
|
||||
.with_text_align(Alignment::Center);
|
||||
(l, r)
|
||||
};
|
||||
|
||||
let mut frame = Confirm::new(BLD_BG, left, right, ConfirmTitle::Text(title), msg)
|
||||
.with_info(
|
||||
"FW FINGERPRINT".into(),
|
||||
fingerprint.into(),
|
||||
button_bld_menu(),
|
||||
);
|
||||
|
||||
if let Some(alert) = alert {
|
||||
frame = frame.with_alert(alert);
|
||||
}
|
||||
|
||||
run(&mut frame)
|
||||
}
|
||||
|
||||
fn screen_wipe_confirm() -> u32 {
|
||||
let icon = Icon::new(FIRE40);
|
||||
|
||||
let msg = Label::centered(
|
||||
"Are you sure you want to factory reset the device?".into(),
|
||||
TEXT_WIPE_NORMAL,
|
||||
);
|
||||
let alert = Label::centered("SEED AND FIRMWARE\nWILL BE ERASED!".into(), TEXT_WIPE_BOLD);
|
||||
|
||||
let right = Button::with_text("RESET".into())
|
||||
.styled(button_wipe_confirm())
|
||||
.with_text_align(Alignment::Center);
|
||||
let left = Button::with_text("CANCEL".into())
|
||||
.styled(button_wipe_cancel())
|
||||
.with_text_align(Alignment::Center);
|
||||
|
||||
let mut frame = Confirm::new(BLD_WIPE_COLOR, left, right, ConfirmTitle::Icon(icon), msg)
|
||||
.with_alert(alert);
|
||||
|
||||
run(&mut frame)
|
||||
}
|
||||
|
||||
fn screen_unlock_bootloader_confirm() -> u32 {
|
||||
let title =
|
||||
Label::left_aligned("UNLOCK BOOTLOADER".into(), TEXT_BOLD).vertically_centered();
|
||||
let msg = Label::centered("This action cannot be undone!".into(), TEXT_NORMAL);
|
||||
|
||||
let right = Button::with_text("UNLOCK".into())
|
||||
.styled(button_confirm())
|
||||
.with_text_align(Alignment::Center);
|
||||
let left = Button::with_text("CANCEL".into())
|
||||
.styled(button_bld())
|
||||
.with_text_align(Alignment::Center);
|
||||
|
||||
let mut frame = Confirm::new(BLD_BG, left, right, ConfirmTitle::Text(title), msg);
|
||||
|
||||
run(&mut frame)
|
||||
}
|
||||
|
||||
fn screen_unlock_bootloader_success() {
|
||||
let mut frame = ResultScreen::new(
|
||||
&RESULT_FW_INSTALL,
|
||||
Icon::new(CHECK40),
|
||||
"Bootloader unlocked".into(),
|
||||
Label::centered(RECONNECT_MESSAGE.into(), RESULT_FW_INSTALL.title_style())
|
||||
.vertically_centered(),
|
||||
true,
|
||||
);
|
||||
show(&mut frame, true);
|
||||
}
|
||||
|
||||
fn screen_menu(firmware_present: secbool) -> u32 {
|
||||
run(&mut Menu::new(firmware_present))
|
||||
}
|
||||
|
||||
fn screen_intro(bld_version: &str, vendor: &str, version: &str, fw_ok: bool) -> u32 {
|
||||
let mut title_str: BootloaderString = String::new();
|
||||
unwrap!(title_str.push_str("BOOTLOADER "));
|
||||
unwrap!(title_str.push_str(bld_version));
|
||||
|
||||
let mut version_str: BootloaderString = String::new();
|
||||
unwrap!(version_str.push_str("Firmware version "));
|
||||
unwrap!(version_str.push_str(version));
|
||||
unwrap!(version_str.push_str("\nby "));
|
||||
unwrap!(version_str.push_str(vendor));
|
||||
|
||||
let mut frame = Intro::new(
|
||||
title_str.as_str().into(),
|
||||
version_str.as_str().into(),
|
||||
fw_ok,
|
||||
);
|
||||
|
||||
run(&mut frame)
|
||||
}
|
||||
|
||||
fn screen_boot_stage_1(fading: bool) {
|
||||
if fading {
|
||||
Self::fadeout();
|
||||
}
|
||||
|
||||
let mut frame = WelcomeScreen::new();
|
||||
show(&mut frame, false);
|
||||
|
||||
if fading {
|
||||
Self::fadein();
|
||||
} else {
|
||||
display::set_backlight(backlight::get_backlight_normal());
|
||||
}
|
||||
}
|
||||
|
||||
fn screen_wipe_progress(progress: u16, initialize: bool) {
|
||||
Self::screen_progress(
|
||||
"Resetting Trezor",
|
||||
progress,
|
||||
initialize,
|
||||
BLD_FG,
|
||||
BLD_WIPE_COLOR,
|
||||
Some((Icon::new(FIRE32), BLD_FG)),
|
||||
None,
|
||||
)
|
||||
}
|
||||
|
||||
fn screen_install_progress(progress: u16, initialize: bool, initial_setup: bool) {
|
||||
let bg_color = if initial_setup { WELCOME_COLOR } else { BLD_BG };
|
||||
let fg_color = if initial_setup { GREEN_LIGHT } else { BLD_FG };
|
||||
let icon_color = BLD_FG;
|
||||
|
||||
Self::screen_progress(
|
||||
"Installing firmware",
|
||||
progress,
|
||||
initialize,
|
||||
fg_color,
|
||||
bg_color,
|
||||
Some((Icon::new(DOWNLOAD24), icon_color)),
|
||||
None,
|
||||
)
|
||||
}
|
||||
|
||||
fn screen_connect(initial_setup: bool) {
|
||||
let bg = if initial_setup { WELCOME_COLOR } else { BLD_BG };
|
||||
let mut frame = Connect::new(
|
||||
"Waiting for host...",
|
||||
fonts::FONT_SATOSHI_REGULAR_38,
|
||||
BLD_TITLE_COLOR,
|
||||
bg,
|
||||
);
|
||||
show(&mut frame, true);
|
||||
}
|
||||
|
||||
fn screen_wipe_success() {
|
||||
let mut frame = ResultScreen::new(
|
||||
&RESULT_WIPE,
|
||||
Icon::new(CHECK40),
|
||||
"Trezor reset\nsuccessfully".into(),
|
||||
Label::centered(RECONNECT_MESSAGE.into(), RESULT_WIPE.title_style())
|
||||
.vertically_centered(),
|
||||
true,
|
||||
);
|
||||
show(&mut frame, true);
|
||||
}
|
||||
|
||||
fn screen_wipe_fail() {
|
||||
let mut frame = ResultScreen::new(
|
||||
&RESULT_WIPE,
|
||||
Icon::new(WARNING40),
|
||||
"Trezor reset was\nnot successful".into(),
|
||||
Label::centered(RECONNECT_MESSAGE.into(), RESULT_WIPE.title_style())
|
||||
.vertically_centered(),
|
||||
true,
|
||||
);
|
||||
show(&mut frame, true);
|
||||
}
|
||||
|
||||
fn screen_boot(
|
||||
warning: bool,
|
||||
vendor_str: Option<&str>,
|
||||
version: [u8; 4],
|
||||
vendor_img: &'static [u8],
|
||||
wait: i32,
|
||||
) {
|
||||
let bg_color = if warning {
|
||||
BLD_WARN_COLOR
|
||||
} else {
|
||||
WELCOME_COLOR
|
||||
};
|
||||
|
||||
display::sync();
|
||||
|
||||
render_on_display(None, Some(bg_color), |target| {
|
||||
// Draw vendor image if it's valid and has size of 120x120
|
||||
if let Ok(toif) = Toif::new(vendor_img) {
|
||||
if (toif.width() == 120) && (toif.height() == 120) {
|
||||
// Image position depends on the vendor string presence
|
||||
let pos = if vendor_str.is_some() {
|
||||
Point::new(SCREEN.width() / 2, 30)
|
||||
} else {
|
||||
Point::new(SCREEN.width() / 2, 60)
|
||||
};
|
||||
|
||||
shape::ToifImage::new(pos, toif)
|
||||
.with_align(Alignment2D::TOP_CENTER)
|
||||
.with_fg(BLD_FG)
|
||||
.render(target);
|
||||
}
|
||||
}
|
||||
|
||||
// Draw vendor string if present
|
||||
if let Some(text) = vendor_str {
|
||||
let pos = Point::new(SCREEN.width() / 2, SCREEN.height() - 5 - 50);
|
||||
shape::Text::new(pos, text, fonts::FONT_SATOSHI_REGULAR_38)
|
||||
.with_align(Alignment::Center)
|
||||
.with_fg(BLD_FG) //COLOR_BL_BG
|
||||
.render(target);
|
||||
|
||||
let pos = Point::new(SCREEN.width() / 2, SCREEN.height() - 5 - 25);
|
||||
|
||||
let mut version_text: BootloaderString = String::new();
|
||||
unwrap!(uwrite!(
|
||||
version_text,
|
||||
"{}.{}.{}",
|
||||
version[0],
|
||||
version[1],
|
||||
version[2]
|
||||
));
|
||||
|
||||
shape::Text::new(pos, version_text.as_str(), fonts::FONT_SATOSHI_REGULAR_38)
|
||||
.with_align(Alignment::Center)
|
||||
.with_fg(BLD_FG)
|
||||
.render(target);
|
||||
}
|
||||
|
||||
// Draw a message
|
||||
match wait.cmp(&0) {
|
||||
core::cmp::Ordering::Equal => {}
|
||||
core::cmp::Ordering::Greater => {
|
||||
let mut text: BootloaderString = String::new();
|
||||
unwrap!(uwrite!(text, "starting in {} s", wait));
|
||||
|
||||
let pos = Point::new(SCREEN.width() / 2, SCREEN.height() - 5);
|
||||
shape::Text::new(pos, text.as_str(), fonts::FONT_SATOSHI_REGULAR_38)
|
||||
.with_align(Alignment::Center)
|
||||
.with_fg(BLD_FG)
|
||||
.render(target);
|
||||
}
|
||||
core::cmp::Ordering::Less => {
|
||||
let pos = Point::new(SCREEN.width() / 2, SCREEN.height() - 5);
|
||||
shape::Text::new(pos, "click to continue ...", fonts::FONT_SATOSHI_REGULAR_38)
|
||||
.with_align(Alignment::Center)
|
||||
.with_fg(BLD_FG)
|
||||
.render(target);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
display::refresh();
|
||||
}
|
||||
}
|
||||
pub use bld_actionbar::{BldActionBar, BldActionBarMsg};
|
||||
pub use bld_header::{BldHeader, BldHeaderMsg};
|
||||
pub use bld_menu::BldMenu;
|
||||
pub use bld_menu_screen::BldMenuScreen;
|
||||
pub use bld_text_screen::BldTextScreen;
|
||||
pub use welcome_screen::BldWelcomeScreen;
|
||||
|
432
core/embed/rust/src/ui/layout_eckhart/ui_bootloader.rs
Normal file
432
core/embed/rust/src/ui/layout_eckhart/ui_bootloader.rs
Normal file
@ -0,0 +1,432 @@
|
||||
use crate::{
|
||||
trezorhal::secbool::{secbool, sectrue},
|
||||
ui::{
|
||||
component::Label,
|
||||
display::{self, toif::Toif, Color},
|
||||
geometry::{Alignment, Alignment2D, Offset, Point, Rect},
|
||||
layout::simplified::{run, show},
|
||||
shape::{self, render_on_display},
|
||||
ui_bootloader::BootloaderUI,
|
||||
CommonUI,
|
||||
},
|
||||
};
|
||||
|
||||
use heapless::String;
|
||||
use ufmt::uwrite;
|
||||
|
||||
use super::{
|
||||
bootloader::{
|
||||
BldActionBar, BldHeader, BldHeaderMsg, BldMenuScreen, BldTextScreen, BldWelcomeScreen,
|
||||
},
|
||||
component::{Button, WelcomeScreen},
|
||||
cshape::{render_loader, ScreenBorder},
|
||||
fonts,
|
||||
theme::{
|
||||
self, backlight,
|
||||
bootloader::{
|
||||
button_cancel, button_confirm, button_wipe_confirm, BLD_BG, BLD_FG, BLD_WARN_COLOR,
|
||||
TEXT_FW_FINGERPRINT, TEXT_WARNING, WELCOME_COLOR,
|
||||
},
|
||||
button_default, BLUE, GREY, ICON_CHECKMARK, ICON_CLOSE, ICON_CROSS, RED, TEXT_NORMAL,
|
||||
TEXT_SMALL_GREY,
|
||||
},
|
||||
UIEckhart,
|
||||
};
|
||||
|
||||
pub type BootloaderString = String<128>;
|
||||
|
||||
const RECONNECT_MESSAGE: &str = "Please reconnect\nthe device";
|
||||
|
||||
const SCREEN: Rect = UIEckhart::SCREEN;
|
||||
// TODO: adjust offset
|
||||
const PROGRESS_TEXT_ORIGIN: Point = SCREEN.top_left().ofs(Offset::new(24, 48));
|
||||
const SCREEN_BORDER_BLUE: ScreenBorder = ScreenBorder::new(BLUE);
|
||||
const SCREEN_BORDER_RED: ScreenBorder = ScreenBorder::new(RED);
|
||||
|
||||
impl UIEckhart {
|
||||
fn screen_progress(
|
||||
text: &str,
|
||||
progress: u16,
|
||||
initialize: bool,
|
||||
bg_color: Color,
|
||||
center_text: Option<&str>,
|
||||
) {
|
||||
if initialize {
|
||||
Self::fadeout();
|
||||
}
|
||||
display::sync();
|
||||
|
||||
render_on_display(None, Some(bg_color), |target| {
|
||||
shape::Text::new(PROGRESS_TEXT_ORIGIN, text, fonts::FONT_SATOSHI_REGULAR_38)
|
||||
.with_align(Alignment::Start)
|
||||
.with_fg(BLD_FG)
|
||||
.render(target);
|
||||
|
||||
let border: &ScreenBorder = match bg_color {
|
||||
RED => &SCREEN_BORDER_RED,
|
||||
_ => &SCREEN_BORDER_BLUE,
|
||||
};
|
||||
render_loader(progress, border, target);
|
||||
|
||||
if let Some(center_text) = center_text {
|
||||
shape::Text::new(SCREEN.center(), center_text, fonts::FONT_SATOSHI_REGULAR_38)
|
||||
.with_align(Alignment::Center)
|
||||
.with_fg(GREY)
|
||||
.render(target);
|
||||
}
|
||||
});
|
||||
|
||||
display::refresh();
|
||||
if initialize {
|
||||
Self::fadein();
|
||||
}
|
||||
}
|
||||
}
|
||||
impl BootloaderUI for UIEckhart {
|
||||
fn screen_welcome() {
|
||||
let mut frame = BldWelcomeScreen::new();
|
||||
show(&mut frame, true);
|
||||
}
|
||||
|
||||
fn screen_install_success(restart_seconds: u8, initial_setup: bool, complete_draw: bool) {
|
||||
let mut reboot_msg = BootloaderString::new();
|
||||
|
||||
let bg_color = if initial_setup { WELCOME_COLOR } else { BLD_BG };
|
||||
|
||||
if restart_seconds >= 1 {
|
||||
// in practice, restart_seconds is 5 or less so this is fine
|
||||
let seconds_char = b'0' + restart_seconds % 10;
|
||||
unwrap!(reboot_msg.push(seconds_char as char));
|
||||
let progress = (5 - (restart_seconds as u16)).clamp(0, 5) * 200;
|
||||
|
||||
Self::screen_progress(
|
||||
"Restarting device",
|
||||
progress,
|
||||
complete_draw,
|
||||
bg_color,
|
||||
Some(reboot_msg.as_str()),
|
||||
);
|
||||
} else {
|
||||
Self::screen_progress("Firmware installed", 1000, complete_draw, bg_color, None);
|
||||
}
|
||||
}
|
||||
|
||||
fn screen_install_fail() {
|
||||
let mut screen = BldTextScreen::new(Label::new(
|
||||
"Firmware installation was not successful".into(),
|
||||
Alignment::Start,
|
||||
TEXT_NORMAL,
|
||||
))
|
||||
.with_header(BldHeader::new_pay_attention())
|
||||
.with_footer(
|
||||
Label::centered(RECONNECT_MESSAGE.into(), TEXT_SMALL_GREY).vertically_centered(),
|
||||
)
|
||||
.with_screen_border(SCREEN_BORDER_BLUE);
|
||||
|
||||
show(&mut screen, true);
|
||||
}
|
||||
|
||||
fn screen_install_confirm(
|
||||
vendor: &str,
|
||||
version: &str,
|
||||
fingerprint: &str,
|
||||
should_keep_seed: bool,
|
||||
is_newvendor: bool,
|
||||
is_newinstall: bool,
|
||||
version_cmp: i32,
|
||||
) -> u32 {
|
||||
let mut version_str: BootloaderString = String::new();
|
||||
unwrap!(version_str.push_str("Firmware version "));
|
||||
unwrap!(version_str.push_str(version));
|
||||
unwrap!(version_str.push_str("\nby "));
|
||||
unwrap!(version_str.push_str(vendor));
|
||||
|
||||
let title_str = if is_newinstall {
|
||||
"Install firmware"
|
||||
} else if is_newvendor {
|
||||
"Change fw vendor"
|
||||
} else if version_cmp > 0 {
|
||||
"Update firmware"
|
||||
} else if version_cmp == 0 {
|
||||
"Reinstall firmware"
|
||||
} else {
|
||||
"Downgrade firmware"
|
||||
};
|
||||
let msg = Label::left_aligned(version_str.as_str().into(), TEXT_NORMAL);
|
||||
let alert = (!should_keep_seed).then_some(Label::left_aligned(
|
||||
"SEED WILL BE ERASED!".into(),
|
||||
TEXT_NORMAL,
|
||||
));
|
||||
|
||||
let header = BldHeader::new(title_str.into()).with_right_button(
|
||||
Button::with_icon(theme::ICON_INFO).styled(theme::button_default()),
|
||||
BldHeaderMsg::Info,
|
||||
);
|
||||
let (left, right) = if should_keep_seed {
|
||||
let l = Button::with_text("Cancel".into())
|
||||
.styled(button_cancel())
|
||||
.with_text_align(Alignment::Center);
|
||||
let r = Button::with_text("Install".into())
|
||||
.styled(button_confirm())
|
||||
.with_text_align(Alignment::Center);
|
||||
(l, r)
|
||||
} else {
|
||||
let l = Button::with_icon(ICON_CROSS)
|
||||
.styled(button_cancel())
|
||||
.with_text_align(Alignment::Center);
|
||||
let r = Button::with_icon(ICON_CHECKMARK)
|
||||
.styled(button_confirm())
|
||||
.with_text_align(Alignment::Center);
|
||||
(l, r)
|
||||
};
|
||||
|
||||
let mut screen = BldTextScreen::new(msg)
|
||||
.with_header(header)
|
||||
.with_action_bar(BldActionBar::new_double(left, right))
|
||||
.with_screen_border(SCREEN_BORDER_BLUE)
|
||||
.with_more_info(
|
||||
BldHeader::new("FW Fingerprint".into())
|
||||
.with_right_button(Button::with_icon(ICON_CLOSE), BldHeaderMsg::Cancelled),
|
||||
Label::left_aligned(fingerprint.into(), TEXT_FW_FINGERPRINT),
|
||||
);
|
||||
|
||||
if let Some(alert) = alert {
|
||||
screen = screen.with_label2(alert);
|
||||
}
|
||||
|
||||
run(&mut screen)
|
||||
}
|
||||
|
||||
fn screen_wipe_confirm() -> u32 {
|
||||
let msg = Label::left_aligned(
|
||||
"Are you sure you want to factory reset the device?".into(),
|
||||
TEXT_NORMAL,
|
||||
);
|
||||
let alert = Label::left_aligned("SEED and FIRMWARE will be erased!".into(), TEXT_NORMAL);
|
||||
|
||||
let right = Button::with_text("Reset".into()).styled(button_wipe_confirm());
|
||||
let left = Button::with_icon(theme::ICON_CHEVRON_LEFT).styled(button_default());
|
||||
|
||||
let mut screen = BldTextScreen::new(msg)
|
||||
.with_label2(alert)
|
||||
.with_header(BldHeader::new_pay_attention())
|
||||
.with_action_bar(BldActionBar::new_double(left, right))
|
||||
.with_screen_border(SCREEN_BORDER_RED);
|
||||
|
||||
run(&mut screen)
|
||||
}
|
||||
|
||||
fn screen_unlock_bootloader_confirm() -> u32 {
|
||||
let msg1 =
|
||||
Label::left_aligned("Unlock bootloader".into(), TEXT_NORMAL).vertically_centered();
|
||||
let msg2 = Label::centered("This action cannot be undone!".into(), TEXT_NORMAL);
|
||||
|
||||
let right = Button::with_text("Unlock".into()).styled(button_confirm());
|
||||
let left = Button::with_icon(theme::ICON_CHEVRON_LEFT).styled(button_cancel());
|
||||
|
||||
let mut screen = BldTextScreen::new(msg1)
|
||||
.with_label2(msg2)
|
||||
.with_header(BldHeader::new_pay_attention())
|
||||
.with_action_bar(BldActionBar::new_double(left, right))
|
||||
.with_screen_border(SCREEN_BORDER_RED);
|
||||
|
||||
run(&mut screen)
|
||||
}
|
||||
|
||||
fn screen_unlock_bootloader_success() {
|
||||
let mut screen = BldTextScreen::new(Label::new(
|
||||
"Bootloader unlocked".into(),
|
||||
Alignment::Start,
|
||||
TEXT_NORMAL,
|
||||
))
|
||||
.with_header(BldHeader::new_pay_attention())
|
||||
.with_footer(
|
||||
Label::centered(RECONNECT_MESSAGE.into(), TEXT_SMALL_GREY).vertically_centered(),
|
||||
)
|
||||
.with_screen_border(SCREEN_BORDER_BLUE);
|
||||
|
||||
show(&mut screen, true);
|
||||
}
|
||||
|
||||
fn screen_menu(firmware_present: secbool) -> u32 {
|
||||
run(&mut BldMenuScreen::new(firmware_present == sectrue))
|
||||
}
|
||||
|
||||
fn screen_intro(bld_version: &str, vendor: &str, version: &str, fw_ok: bool) -> u32 {
|
||||
let mut title_str: BootloaderString = String::new();
|
||||
unwrap!(title_str.push_str("Bootloader "));
|
||||
unwrap!(title_str.push_str(bld_version));
|
||||
|
||||
let mut version_str: BootloaderString = String::new();
|
||||
unwrap!(version_str.push_str("Firmware version\n"));
|
||||
unwrap!(version_str.push_str(version));
|
||||
unwrap!(version_str.push_str(" by "));
|
||||
unwrap!(version_str.push_str(vendor));
|
||||
|
||||
let mut screen = BldTextScreen::new(Label::left_aligned(
|
||||
version_str.as_str().into(),
|
||||
TEXT_NORMAL,
|
||||
))
|
||||
.with_header(BldHeader::new(title_str.as_str().into()).with_menu_button())
|
||||
.with_action_bar(BldActionBar::new_single(
|
||||
Button::with_text("Connect to host device".into()).styled(button_confirm()),
|
||||
))
|
||||
.with_screen_border(SCREEN_BORDER_BLUE);
|
||||
|
||||
if !fw_ok {
|
||||
screen = screen.with_label2(
|
||||
Label::new("FIRMWARE CORRUPTED".into(), Alignment::Start, TEXT_WARNING)
|
||||
.vertically_centered(),
|
||||
);
|
||||
}
|
||||
|
||||
run(&mut screen)
|
||||
}
|
||||
|
||||
fn screen_boot_stage_1(fading: bool) {
|
||||
if fading {
|
||||
Self::fadeout();
|
||||
}
|
||||
|
||||
let mut frame = WelcomeScreen::new();
|
||||
show(&mut frame, false);
|
||||
|
||||
if fading {
|
||||
Self::fadein();
|
||||
} else {
|
||||
display::set_backlight(backlight::get_backlight_normal());
|
||||
}
|
||||
}
|
||||
|
||||
fn screen_wipe_progress(progress: u16, initialize: bool) {
|
||||
Self::screen_progress("Resetting Trezor", progress, initialize, RED, None)
|
||||
}
|
||||
|
||||
fn screen_install_progress(progress: u16, initialize: bool, _initial_setup: bool) {
|
||||
Self::screen_progress("Installing firmware", progress, initialize, BLD_BG, None)
|
||||
}
|
||||
|
||||
fn screen_connect(_initial_setup: bool) {
|
||||
// NOTE: other layouts use a Connect component. Eckhart uses a BldTextScreen so
|
||||
// that the ScreenBorder can be shown and alignment can be adjusted.
|
||||
let mut screen = BldTextScreen::new(Label::left_aligned(
|
||||
"Waiting for host...".into(),
|
||||
TEXT_NORMAL,
|
||||
))
|
||||
.with_screen_border(SCREEN_BORDER_BLUE);
|
||||
show(&mut screen, true);
|
||||
}
|
||||
|
||||
fn screen_wipe_success() {
|
||||
let mut screen = BldTextScreen::new(Label::new(
|
||||
"Trezor reset successfully".into(),
|
||||
Alignment::Start,
|
||||
TEXT_NORMAL,
|
||||
))
|
||||
.with_header(BldHeader::new("Done".into()).with_icon(theme::ICON_DONE, theme::GREY))
|
||||
.with_footer(
|
||||
Label::centered(RECONNECT_MESSAGE.into(), TEXT_SMALL_GREY).vertically_centered(),
|
||||
)
|
||||
.with_screen_border(SCREEN_BORDER_RED);
|
||||
|
||||
show(&mut screen, true);
|
||||
}
|
||||
|
||||
fn screen_wipe_fail() {
|
||||
let mut screen = BldTextScreen::new(Label::new(
|
||||
"Trezor reset was\nnot successful".into(),
|
||||
Alignment::Start,
|
||||
TEXT_NORMAL,
|
||||
))
|
||||
.with_header(BldHeader::new_pay_attention())
|
||||
.with_footer(
|
||||
Label::centered(RECONNECT_MESSAGE.into(), TEXT_SMALL_GREY).vertically_centered(),
|
||||
)
|
||||
.with_screen_border(SCREEN_BORDER_RED);
|
||||
show(&mut screen, true);
|
||||
}
|
||||
|
||||
fn screen_boot(
|
||||
warning: bool,
|
||||
vendor_str: Option<&str>,
|
||||
version: [u8; 4],
|
||||
vendor_img: &'static [u8],
|
||||
wait: i32,
|
||||
) {
|
||||
let bg_color = if warning {
|
||||
BLD_WARN_COLOR
|
||||
} else {
|
||||
WELCOME_COLOR
|
||||
};
|
||||
|
||||
display::sync();
|
||||
|
||||
render_on_display(None, Some(bg_color), |target| {
|
||||
// Draw vendor image if it's valid and has size of 120x120
|
||||
if let Ok(toif) = Toif::new(vendor_img) {
|
||||
if (toif.width() == 120) && (toif.height() == 120) {
|
||||
// Image position depends on the vendor string presence
|
||||
let pos = if vendor_str.is_some() {
|
||||
Point::new(SCREEN.width() / 2, 30)
|
||||
} else {
|
||||
Point::new(SCREEN.width() / 2, 60)
|
||||
};
|
||||
|
||||
shape::ToifImage::new(pos, toif)
|
||||
.with_align(Alignment2D::TOP_CENTER)
|
||||
.with_fg(BLD_FG)
|
||||
.render(target);
|
||||
}
|
||||
}
|
||||
|
||||
// Draw vendor string if present
|
||||
if let Some(text) = vendor_str {
|
||||
let pos = Point::new(SCREEN.width() / 2, SCREEN.height() - 5 - 50);
|
||||
shape::Text::new(pos, text, fonts::FONT_SATOSHI_REGULAR_38)
|
||||
.with_align(Alignment::Center)
|
||||
.with_fg(BLD_FG) //COLOR_BL_BG
|
||||
.render(target);
|
||||
|
||||
let pos = Point::new(SCREEN.width() / 2, SCREEN.height() - 5 - 25);
|
||||
|
||||
let mut version_text: BootloaderString = String::new();
|
||||
unwrap!(uwrite!(
|
||||
version_text,
|
||||
"{}.{}.{}",
|
||||
version[0],
|
||||
version[1],
|
||||
version[2]
|
||||
));
|
||||
|
||||
shape::Text::new(pos, version_text.as_str(), fonts::FONT_SATOSHI_REGULAR_38)
|
||||
.with_align(Alignment::Center)
|
||||
.with_fg(BLD_FG)
|
||||
.render(target);
|
||||
}
|
||||
|
||||
// Draw a message
|
||||
match wait.cmp(&0) {
|
||||
core::cmp::Ordering::Equal => {}
|
||||
core::cmp::Ordering::Greater => {
|
||||
let mut text: BootloaderString = String::new();
|
||||
unwrap!(uwrite!(text, "starting in {} s", wait));
|
||||
|
||||
let pos = Point::new(SCREEN.width() / 2, SCREEN.height() - 5);
|
||||
shape::Text::new(pos, text.as_str(), fonts::FONT_SATOSHI_REGULAR_38)
|
||||
.with_align(Alignment::Center)
|
||||
.with_fg(BLD_FG)
|
||||
.render(target);
|
||||
}
|
||||
core::cmp::Ordering::Less => {
|
||||
let pos = Point::new(SCREEN.width() / 2, SCREEN.height() - 5);
|
||||
shape::Text::new(pos, "click to continue ...", fonts::FONT_SATOSHI_REGULAR_38)
|
||||
.with_align(Alignment::Center)
|
||||
.with_fg(BLD_FG)
|
||||
.render(target);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
display::refresh();
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue
Block a user