mirror of
https://github.com/trezor/trezor-firmware.git
synced 2025-02-16 01:22:02 +00:00
feat(core/rust): add bootloader design for T2B1
[no changelog]
This commit is contained in:
parent
e928568339
commit
531511407b
@ -221,7 +221,7 @@ build_cross: ## build mpy-cross port
|
||||
|
||||
## clean commands:
|
||||
|
||||
clean: clean_boardloader clean_bootloader clean_bootloader_ci clean_prodtest clean_reflash clean_firmware clean_unix clean_cross ## clean all
|
||||
clean: clean_boardloader clean_bootloader clean_bootloader_emu clean_bootloader_ci clean_prodtest clean_reflash clean_firmware clean_unix clean_cross ## clean all
|
||||
rm -f ".sconsign.dblite"
|
||||
|
||||
clean_boardloader: ## clean boardloader build
|
||||
@ -233,6 +233,9 @@ clean_bootloader: ## clean bootloader build
|
||||
clean_bootloader_ci: ## clean bootloader_ci build
|
||||
rm -rf $(BOOTLOADER_CI_BUILD_DIR)
|
||||
|
||||
clean_bootloader_emu: ## clean bootloader_emu build
|
||||
rm -rf $(BOOTLOADER_EMU_BUILD_DIR)
|
||||
|
||||
clean_prodtest: ## clean prodtest build
|
||||
rm -rf $(PRODTEST_BUILD_DIR)
|
||||
|
||||
|
@ -28,11 +28,11 @@ CPPPATH_MOD = []
|
||||
CPPDEFINES_MOD = []
|
||||
SOURCE_MOD = []
|
||||
|
||||
if TREZOR_MODEL in ('1', 'R'):
|
||||
if TREZOR_MODEL in ('R', ):
|
||||
FONT_NORMAL='Font_PixelOperator_Regular_8'
|
||||
FONT_DEMIBOLD=None
|
||||
FONT_BOLD=None
|
||||
FONT_MONO='Font_PixelOperatorMono_Regular_8'
|
||||
FONT_DEMIBOLD='Font_PixelOperator_Regular_8'
|
||||
FONT_BOLD='Font_PixelOperator_Bold_8'
|
||||
FONT_MONO='Font_PixelOperator_Regular_8'
|
||||
if TREZOR_MODEL in ('T', ):
|
||||
FONT_NORMAL='Font_TTHoves_Regular_21'
|
||||
FONT_DEMIBOLD=None
|
||||
|
@ -29,9 +29,9 @@ SOURCE_MOD = []
|
||||
|
||||
if TREZOR_MODEL in ('1', 'R'):
|
||||
FONT_NORMAL='Font_PixelOperator_Regular_8'
|
||||
FONT_DEMIBOLD=None
|
||||
FONT_BOLD=None
|
||||
FONT_MONO='Font_PixelOperatorMono_Regular_8'
|
||||
FONT_DEMIBOLD='Font_PixelOperator_Regular_8'
|
||||
FONT_BOLD='Font_PixelOperator_Bold_8'
|
||||
FONT_MONO='Font_PixelOperator_Regular_8'
|
||||
if TREZOR_MODEL in ('T', ):
|
||||
FONT_NORMAL='Font_TTHoves_Regular_21'
|
||||
FONT_DEMIBOLD=None
|
||||
@ -165,7 +165,7 @@ env.Replace(
|
||||
'BOOTLOADER',
|
||||
'TREZOR_EMULATOR',
|
||||
'HW_MODEL=' + MODEL_AS_NUMBER,
|
||||
'HW_REVISION=0',
|
||||
'HW_REVISION=' + ('6' if TREZOR_MODEL in ('R',) else '0'),
|
||||
'TREZOR_MODEL_'+TREZOR_MODEL,
|
||||
'TREZOR_BOARD=\\"board-unix.h\\"',
|
||||
'PB_FIELD_16BIT',
|
||||
@ -225,7 +225,7 @@ def cargo_build():
|
||||
|
||||
cargo_opts = [
|
||||
f'--target={RUST_TARGET}',
|
||||
f'--target-dir=../../build/bootloader_emu/rust',
|
||||
'--target-dir=../../build/bootloader_emu/rust',
|
||||
'--no-default-features',
|
||||
'--features ' + ','.join(features),
|
||||
'-Z build-std=core',
|
||||
@ -239,9 +239,9 @@ rust = env.Command(
|
||||
source='',
|
||||
action=cargo_build(), )
|
||||
|
||||
env.Append(LINKFLAGS=f' -L{RUST_LIBDIR}')
|
||||
env.Append(LINKFLAGS=f' -l{RUST_LIB}')
|
||||
env.Append(LINKFLAGS=f' -lm')
|
||||
env.Append(LINKFLAGS=f'-L{RUST_LIBDIR}')
|
||||
env.Append(LINKFLAGS=f'-l{RUST_LIB}')
|
||||
env.Append(LINKFLAGS='-lm')
|
||||
|
||||
#
|
||||
# Program objects
|
||||
|
BIN
core/assets/model_r/cancel_no_outline_inverted.png
Normal file
BIN
core/assets/model_r/cancel_no_outline_inverted.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 8.0 KiB |
BIN
core/assets/model_r/info.png
Normal file
BIN
core/assets/model_r/info.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 7.7 KiB |
BIN
core/assets/model_r/info_invert.png
Normal file
BIN
core/assets/model_r/info_invert.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 6.7 KiB |
BIN
core/assets/model_r/logo_22_33_empty.png
Normal file
BIN
core/assets/model_r/logo_22_33_empty.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 10 KiB |
@ -529,7 +529,7 @@ int bootloader_main(void) {
|
||||
ui_screen_boot_click();
|
||||
}
|
||||
|
||||
ui_fadeout();
|
||||
ui_screen_boot_empty(false);
|
||||
}
|
||||
|
||||
ensure_compatible_settings();
|
||||
|
@ -25,5 +25,4 @@ uint32_t screen_install_fail(void);
|
||||
void screen_welcome_model(void);
|
||||
void screen_welcome(void);
|
||||
void screen_boot_empty(bool fading);
|
||||
|
||||
void display_image(int16_t x, int16_t y, const uint8_t* data, uint32_t datalen);
|
||||
|
@ -1,22 +1,20 @@
|
||||
use crate::ui::{
|
||||
component::{
|
||||
text::paragraphs::{ParagraphVecShort, Paragraphs},
|
||||
Child, Component, Event, EventCtx, Pad,
|
||||
},
|
||||
constant::screen,
|
||||
display::{Color, Icon},
|
||||
geometry::{Point, Rect, CENTER},
|
||||
component::{Child, Component, ComponentExt, Event, EventCtx, Label, Pad},
|
||||
display::{self, Color, Font},
|
||||
geometry::{Point, Rect},
|
||||
};
|
||||
|
||||
use super::{
|
||||
super::{
|
||||
component::Button,
|
||||
constant::{HEIGHT, WIDTH},
|
||||
theme::WHITE,
|
||||
component::{ButtonController, ButtonControllerMsg, ButtonLayout, ButtonPos},
|
||||
theme::{BUTTON_HEIGHT, TITLE_AREA_HEIGHT},
|
||||
},
|
||||
theme::WHITE,
|
||||
ReturnToC,
|
||||
};
|
||||
|
||||
const ALERT_AREA_START: i16 = 39;
|
||||
|
||||
#[derive(Copy, Clone)]
|
||||
pub enum ConfirmMsg {
|
||||
Cancel = 1,
|
||||
@ -29,94 +27,176 @@ impl ReturnToC for ConfirmMsg {
|
||||
}
|
||||
}
|
||||
|
||||
pub struct Confirm {
|
||||
pub struct Confirm<'a> {
|
||||
bg: Pad,
|
||||
bg_color: Color,
|
||||
icon: Option<Icon>,
|
||||
message: Child<Paragraphs<ParagraphVecShort<&'static str>>>,
|
||||
left: Child<Button<&'static str>>,
|
||||
right: Child<Button<&'static str>>,
|
||||
confirm_left: bool,
|
||||
title: &'static str,
|
||||
message: Child<Label<&'a str>>,
|
||||
alert: Option<Label<&'a str>>,
|
||||
info_title: Option<&'static str>,
|
||||
info_text: Option<Label<&'a str>>,
|
||||
button_text: &'static str,
|
||||
buttons: ButtonController<&'static str>,
|
||||
/// Whether we are on the info screen (optional extra screen)
|
||||
showing_info_screen: bool,
|
||||
}
|
||||
|
||||
impl Confirm {
|
||||
impl<'a> Confirm<'a> {
|
||||
pub fn new(
|
||||
bg_color: Color,
|
||||
icon: Option<Icon>,
|
||||
message: Paragraphs<ParagraphVecShort<&'static str>>,
|
||||
left: Button<&'static str>,
|
||||
right: Button<&'static str>,
|
||||
confirm_left: bool,
|
||||
title: &'static str,
|
||||
message: Label<&'a str>,
|
||||
alert: Option<Label<&'a str>>,
|
||||
button_text: &'static str,
|
||||
) -> Self {
|
||||
let mut instance = Self {
|
||||
bg: Pad::with_background(bg_color),
|
||||
let btn_layout = Self::get_button_layout_general(false, button_text, false);
|
||||
Self {
|
||||
bg: Pad::with_background(bg_color).with_clear(),
|
||||
bg_color,
|
||||
icon,
|
||||
title,
|
||||
message: Child::new(message),
|
||||
left: Child::new(left),
|
||||
right: Child::new(right),
|
||||
confirm_left,
|
||||
};
|
||||
instance.bg.clear();
|
||||
instance
|
||||
alert,
|
||||
info_title: None,
|
||||
info_text: None,
|
||||
button_text,
|
||||
buttons: ButtonController::new(btn_layout),
|
||||
showing_info_screen: false,
|
||||
}
|
||||
}
|
||||
|
||||
/// Adding optional info screen
|
||||
pub fn with_info_screen(mut self, info_title: &'static str, info_text: Label<&'a str>) -> Self {
|
||||
self.info_title = Some(info_title);
|
||||
self.info_text = Some(info_text);
|
||||
self.buttons = ButtonController::new(self.get_button_layout());
|
||||
self
|
||||
}
|
||||
|
||||
fn has_info_screen(&self) -> bool {
|
||||
self.info_title.is_some()
|
||||
}
|
||||
|
||||
fn get_button_layout(&self) -> ButtonLayout<&'static str> {
|
||||
Self::get_button_layout_general(
|
||||
self.showing_info_screen,
|
||||
self.button_text,
|
||||
self.has_info_screen(),
|
||||
)
|
||||
}
|
||||
|
||||
/// Not relying on self here, to call it in constructor.
|
||||
fn get_button_layout_general(
|
||||
showing_info_screen: bool,
|
||||
button_text: &'static str,
|
||||
has_info_screen: bool,
|
||||
) -> ButtonLayout<&'static str> {
|
||||
if showing_info_screen {
|
||||
ButtonLayout::arrow_none_none()
|
||||
} else if has_info_screen {
|
||||
ButtonLayout::cancel_armed_text(button_text, "i")
|
||||
} else {
|
||||
ButtonLayout::cancel_none_text(button_text)
|
||||
}
|
||||
}
|
||||
|
||||
/// Reflecting the current page in the buttons.
|
||||
fn update_buttons(&mut self) {
|
||||
let btn_layout = self.get_button_layout();
|
||||
self.buttons.set(btn_layout);
|
||||
}
|
||||
|
||||
fn update_everything(&mut self, ctx: &mut EventCtx) {
|
||||
self.bg.clear();
|
||||
self.update_buttons();
|
||||
self.info_text.request_complete_repaint(ctx);
|
||||
self.message.request_complete_repaint(ctx);
|
||||
self.alert.request_complete_repaint(ctx);
|
||||
self.buttons.request_complete_repaint(ctx);
|
||||
self.request_complete_repaint(ctx);
|
||||
}
|
||||
}
|
||||
|
||||
impl Component for Confirm {
|
||||
impl<'a> Component for Confirm<'a> {
|
||||
type Msg = ConfirmMsg;
|
||||
|
||||
fn place(&mut self, bounds: Rect) -> Rect {
|
||||
self.bg
|
||||
.place(Rect::new(Point::new(0, 0), Point::new(WIDTH, HEIGHT)));
|
||||
self.message
|
||||
.place(Rect::new(Point::new(10, 0), Point::new(118, 50)));
|
||||
self.bg.place(bounds);
|
||||
|
||||
let button_area = bounds.split_bottom(12).1;
|
||||
self.left.place(button_area);
|
||||
self.right.place(button_area);
|
||||
// Divide the screen into areas
|
||||
let (_title_area, minus_title) = bounds.split_top(TITLE_AREA_HEIGHT);
|
||||
let (between_title_and_buttons, button_area) = minus_title.split_bottom(BUTTON_HEIGHT);
|
||||
|
||||
// Texts for the main screen
|
||||
let (message_area, alert_area) = if self.alert.is_some() {
|
||||
between_title_and_buttons.split_top(ALERT_AREA_START - TITLE_AREA_HEIGHT)
|
||||
} else {
|
||||
(between_title_and_buttons, Rect::zero())
|
||||
};
|
||||
self.message.place(message_area);
|
||||
self.alert.place(alert_area);
|
||||
|
||||
// Text for the info screen
|
||||
self.info_text.place(between_title_and_buttons);
|
||||
|
||||
self.buttons.place(button_area);
|
||||
|
||||
bounds
|
||||
}
|
||||
|
||||
fn event(&mut self, _ctx: &mut EventCtx, _event: Event) -> Option<Self::Msg> {
|
||||
// TODO: to be fixed in bootloader branch
|
||||
// if let Some(Clicked) = self.left.event(ctx, event) {
|
||||
// return if self.confirm_left {
|
||||
// Some(Self::Msg::Confirm)
|
||||
// } else {
|
||||
// Some(Self::Msg::Cancel)
|
||||
// };
|
||||
// };
|
||||
// if let Some(Clicked) = self.right.event(ctx, event) {
|
||||
// return if self.confirm_left {
|
||||
// Some(Self::Msg::Cancel)
|
||||
// } else {
|
||||
// Some(Self::Msg::Confirm)
|
||||
// };
|
||||
// };
|
||||
None
|
||||
fn event(&mut self, ctx: &mut EventCtx, event: Event) -> Option<Self::Msg> {
|
||||
let msg = self.buttons.event(ctx, event);
|
||||
if self.showing_info_screen {
|
||||
// Showing the info screen currently - going back with the left button
|
||||
if let Some(ButtonControllerMsg::Triggered(ButtonPos::Left)) = msg {
|
||||
self.showing_info_screen = false;
|
||||
self.update_everything(ctx);
|
||||
};
|
||||
None
|
||||
} else if self.has_info_screen() {
|
||||
// Being on the "main" screen but with an info screen available on the right
|
||||
match msg {
|
||||
Some(ButtonControllerMsg::Triggered(ButtonPos::Left)) => Some(ConfirmMsg::Cancel),
|
||||
Some(ButtonControllerMsg::Triggered(ButtonPos::Middle)) => {
|
||||
Some(ConfirmMsg::Confirm)
|
||||
}
|
||||
Some(ButtonControllerMsg::Triggered(ButtonPos::Right)) => {
|
||||
self.showing_info_screen = true;
|
||||
self.update_everything(ctx);
|
||||
None
|
||||
}
|
||||
_ => None,
|
||||
}
|
||||
} else {
|
||||
// There is just one main screen without info screen
|
||||
match msg {
|
||||
Some(ButtonControllerMsg::Triggered(ButtonPos::Left)) => Some(ConfirmMsg::Cancel),
|
||||
Some(ButtonControllerMsg::Triggered(ButtonPos::Right)) => Some(ConfirmMsg::Confirm),
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn paint(&mut self) {
|
||||
self.bg.paint();
|
||||
|
||||
if let Some(icon) = self.icon {
|
||||
icon.draw(
|
||||
Point::new(screen().center().x, 45),
|
||||
CENTER,
|
||||
WHITE,
|
||||
self.bg_color,
|
||||
);
|
||||
}
|
||||
let display_top_left = |text: &str| {
|
||||
display::text_top_left(Point::zero(), text, Font::BOLD, WHITE, self.bg_color);
|
||||
};
|
||||
|
||||
self.message.paint();
|
||||
self.left.paint();
|
||||
self.right.paint();
|
||||
// We are either on the info screen or on the "main" screen
|
||||
if self.showing_info_screen {
|
||||
display_top_left(unwrap!(self.info_title));
|
||||
self.info_text.paint();
|
||||
} else {
|
||||
display_top_left(self.title);
|
||||
self.message.paint();
|
||||
self.alert.paint();
|
||||
}
|
||||
self.buttons.paint();
|
||||
}
|
||||
|
||||
#[cfg(feature = "ui_bounds")]
|
||||
fn bounds(&self, sink: &mut dyn FnMut(Rect)) {
|
||||
self.left.bounds(sink);
|
||||
self.right.bounds(sink);
|
||||
self.buttons.bounds(sink);
|
||||
}
|
||||
}
|
||||
|
47
core/embed/rust/src/ui/model_tr/bootloader/connect.rs
Normal file
47
core/embed/rust/src/ui/model_tr/bootloader/connect.rs
Normal file
@ -0,0 +1,47 @@
|
||||
use crate::ui::{
|
||||
component::{Component, Event, EventCtx, Never, Pad},
|
||||
display::{self, Font},
|
||||
geometry::{Offset, Rect},
|
||||
};
|
||||
|
||||
use super::theme::{BLD_BG, BLD_FG};
|
||||
|
||||
pub struct Connect {
|
||||
bg: Pad,
|
||||
message: &'static str,
|
||||
}
|
||||
|
||||
impl Connect {
|
||||
pub fn new(message: &'static str) -> Self {
|
||||
Self {
|
||||
bg: Pad::with_background(BLD_BG).with_clear(),
|
||||
message,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Component for Connect {
|
||||
type Msg = Never;
|
||||
|
||||
fn place(&mut self, bounds: Rect) -> Rect {
|
||||
self.bg.place(bounds);
|
||||
bounds
|
||||
}
|
||||
|
||||
fn event(&mut self, _ctx: &mut EventCtx, _event: Event) -> Option<Self::Msg> {
|
||||
None
|
||||
}
|
||||
|
||||
fn paint(&mut self) {
|
||||
let font = Font::NORMAL;
|
||||
|
||||
self.bg.paint();
|
||||
display::text_center(
|
||||
self.bg.area.center() + Offset::y(font.text_height() / 2),
|
||||
self.message,
|
||||
font,
|
||||
BLD_FG,
|
||||
BLD_BG,
|
||||
);
|
||||
}
|
||||
}
|
@ -1,26 +1,25 @@
|
||||
use crate::ui::{
|
||||
component::{
|
||||
text::paragraphs::{Paragraph, ParagraphVecShort, Paragraphs, VecExt},
|
||||
Child, Component, Event, EventCtx, Pad,
|
||||
},
|
||||
geometry::{LinearPlacement, Point, Rect},
|
||||
component::{Child, Component, Event, EventCtx, Label, Pad},
|
||||
geometry::{Alignment, Rect, TOP_LEFT, TOP_RIGHT},
|
||||
};
|
||||
|
||||
use super::super::{
|
||||
bootloader::{
|
||||
theme::{bld_button_default, BLD_BG, TEXT_NORMAL},
|
||||
title::Title,
|
||||
ReturnToC,
|
||||
use super::{
|
||||
super::{
|
||||
component::{ButtonController, ButtonControllerMsg::Triggered, ButtonLayout, ButtonPos},
|
||||
theme::{BUTTON_HEIGHT, ICON_WARN_TITLE, TITLE_AREA_HEIGHT},
|
||||
},
|
||||
component::{Button, ButtonPos},
|
||||
constant::{HEIGHT, WIDTH},
|
||||
theme::{BLD_BG, BLD_FG, TEXT_NORMAL},
|
||||
ReturnToC,
|
||||
};
|
||||
|
||||
const LEFT_BUTTON_TEXT: &str = "INSTALL FW";
|
||||
const RIGHT_BUTTON_TEXT: &str = "MENU";
|
||||
|
||||
#[repr(u32)]
|
||||
#[derive(Copy, Clone)]
|
||||
pub enum IntroMsg {
|
||||
Menu = 1,
|
||||
Host = 2,
|
||||
GoToMenu = 1,
|
||||
InstallFirmware = 2,
|
||||
}
|
||||
impl ReturnToC for IntroMsg {
|
||||
fn return_to_c(self) -> u32 {
|
||||
@ -28,87 +27,75 @@ impl ReturnToC for IntroMsg {
|
||||
}
|
||||
}
|
||||
|
||||
pub struct Intro {
|
||||
pub struct Intro<'a> {
|
||||
bg: Pad,
|
||||
title: Child<Title>,
|
||||
host: Child<Button<&'static str>>,
|
||||
menu: Child<Button<&'static str>>,
|
||||
text: Child<Paragraphs<ParagraphVecShort<&'static str>>>,
|
||||
title: Child<Label<&'a str>>,
|
||||
buttons: Child<ButtonController<&'static str>>,
|
||||
text: Child<Label<&'a str>>,
|
||||
}
|
||||
|
||||
impl Intro {
|
||||
pub fn new(bld_version: &'static str, vendor: &'static str, version: &'static str) -> Self {
|
||||
let mut messages = ParagraphVecShort::new();
|
||||
|
||||
messages.add(Paragraph::new(&TEXT_NORMAL, version));
|
||||
messages.add(Paragraph::new(&TEXT_NORMAL, vendor));
|
||||
|
||||
let p1 =
|
||||
Paragraphs::new(messages).with_placement(LinearPlacement::vertical().align_at_start());
|
||||
|
||||
let mut instance = Self {
|
||||
bg: Pad::with_background(BLD_BG),
|
||||
title: Child::new(Title::new(bld_version)),
|
||||
host: Child::new(Button::with_text(
|
||||
ButtonPos::Left,
|
||||
"INSTALL FIRMWARE",
|
||||
bld_button_default(),
|
||||
)),
|
||||
menu: Child::new(Button::with_text(
|
||||
ButtonPos::Right,
|
||||
"MENU",
|
||||
bld_button_default(),
|
||||
)),
|
||||
text: Child::new(p1),
|
||||
};
|
||||
|
||||
instance.bg.clear();
|
||||
instance
|
||||
impl<'a> Intro<'a> {
|
||||
pub fn new(title: &'a str, content: &'a str) -> Self {
|
||||
Self {
|
||||
bg: Pad::with_background(BLD_BG).with_clear(),
|
||||
title: Child::new(
|
||||
Label::new(title, Alignment::Center, TEXT_NORMAL)
|
||||
.vertically_aligned(Alignment::Center),
|
||||
),
|
||||
buttons: Child::new(ButtonController::new(ButtonLayout::text_none_text(
|
||||
LEFT_BUTTON_TEXT,
|
||||
RIGHT_BUTTON_TEXT,
|
||||
))),
|
||||
text: Child::new(
|
||||
Label::new(content, Alignment::Start, TEXT_NORMAL)
|
||||
.vertically_aligned(Alignment::Center),
|
||||
),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Component for Intro {
|
||||
impl<'a> Component for Intro<'a> {
|
||||
type Msg = IntroMsg;
|
||||
|
||||
fn place(&mut self, bounds: Rect) -> Rect {
|
||||
self.bg
|
||||
.place(Rect::new(Point::new(0, 0), Point::new(WIDTH, HEIGHT)));
|
||||
self.title
|
||||
.place(Rect::new(Point::new(10, 0), Point::new(128, 8)));
|
||||
self.bg.place(bounds);
|
||||
|
||||
let button_area = bounds.split_bottom(12).1;
|
||||
self.host.place(button_area);
|
||||
self.menu.place(button_area);
|
||||
// Title on top, buttons on bottom, text in between
|
||||
let (title_area, text_and_buttons_area) = bounds.split_top(TITLE_AREA_HEIGHT);
|
||||
let (text_area, buttons_area) = text_and_buttons_area.split_bottom(BUTTON_HEIGHT);
|
||||
|
||||
self.text
|
||||
.place(Rect::new(Point::new(10, 20), Point::new(118, 50)));
|
||||
self.title.place(title_area);
|
||||
self.buttons.place(buttons_area);
|
||||
self.text.place(text_area);
|
||||
bounds
|
||||
}
|
||||
|
||||
fn event(&mut self, _ctx: &mut EventCtx, _event: Event) -> Option<Self::Msg> {
|
||||
// TODO: to be fixed in bootloader branch
|
||||
// if let Some(Clicked) = self.menu.event(ctx, event) {
|
||||
// return Some(Self::Msg::Menu);
|
||||
// };
|
||||
// if let Some(Clicked) = self.host.event(ctx, event) {
|
||||
// return Some(Self::Msg::Host);
|
||||
// };
|
||||
fn event(&mut self, ctx: &mut EventCtx, event: Event) -> Option<Self::Msg> {
|
||||
let msg = self.buttons.event(ctx, event);
|
||||
|
||||
if let Some(Triggered(ButtonPos::Left)) = msg {
|
||||
return Some(Self::Msg::InstallFirmware);
|
||||
};
|
||||
if let Some(Triggered(ButtonPos::Right)) = msg {
|
||||
return Some(Self::Msg::GoToMenu);
|
||||
};
|
||||
None
|
||||
}
|
||||
|
||||
fn paint(&mut self) {
|
||||
self.bg.paint();
|
||||
self.title.paint();
|
||||
let area = self.bg.area;
|
||||
ICON_WARN_TITLE.draw(area.top_left(), TOP_LEFT, BLD_FG, BLD_BG);
|
||||
ICON_WARN_TITLE.draw(area.top_right(), TOP_RIGHT, BLD_FG, BLD_BG);
|
||||
self.text.paint();
|
||||
self.host.paint();
|
||||
self.menu.paint();
|
||||
self.buttons.paint();
|
||||
}
|
||||
|
||||
#[cfg(feature = "ui_bounds")]
|
||||
fn bounds(&self, sink: &mut dyn FnMut(Rect)) {
|
||||
self.title.bounds(sink);
|
||||
self.text.bounds(sink);
|
||||
self.host.bounds(sink);
|
||||
self.menu.bounds(sink);
|
||||
self.buttons.bounds(sink);
|
||||
}
|
||||
}
|
||||
|
@ -1,46 +1,152 @@
|
||||
#[cfg(feature = "ui_debug")]
|
||||
use crate::trace::{Trace, Tracer};
|
||||
use crate::ui::{
|
||||
component::{Child, Component, Event, EventCtx, Never, Pad},
|
||||
geometry::{Point, Rect},
|
||||
component::{Child, Component, Event, EventCtx, Pad},
|
||||
constant::screen,
|
||||
display,
|
||||
display::{Font, Icon},
|
||||
geometry::{Offset, Point, Rect, CENTER},
|
||||
};
|
||||
|
||||
use super::super::{
|
||||
bootloader::{theme::BLD_BG, title::Title},
|
||||
constant::{HEIGHT, WIDTH},
|
||||
use super::{
|
||||
super::component::{Choice, ChoiceFactory, ChoicePage},
|
||||
theme::{BLD_BG, BLD_FG, ICON_EXIT, ICON_REDO, ICON_TRASH},
|
||||
ReturnToC,
|
||||
};
|
||||
|
||||
#[repr(u32)]
|
||||
#[derive(Copy, Clone)]
|
||||
pub enum MenuMsg {
|
||||
Close = 1,
|
||||
Reboot = 2,
|
||||
FactoryReset = 3,
|
||||
}
|
||||
impl ReturnToC for MenuMsg {
|
||||
fn return_to_c(self) -> u32 {
|
||||
self as u32
|
||||
}
|
||||
}
|
||||
|
||||
const CHOICE_LENGTH: usize = 3;
|
||||
const SCREEN_CENTER: Point = screen().center();
|
||||
|
||||
pub struct MenuChoice {
|
||||
first_line: &'static str,
|
||||
second_line: &'static str,
|
||||
icon: Icon,
|
||||
}
|
||||
|
||||
impl MenuChoice {
|
||||
pub fn new(first_line: &'static str, second_line: &'static str, icon: Icon) -> Self {
|
||||
Self {
|
||||
first_line,
|
||||
second_line,
|
||||
icon,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Choice<&'static str> for MenuChoice {
|
||||
fn paint_center(&self, _area: Rect, _inverse: bool) {
|
||||
// Icon on top and two lines of text below
|
||||
self.icon
|
||||
.draw(SCREEN_CENTER + Offset::y(-20), CENTER, BLD_FG, BLD_BG);
|
||||
|
||||
display::text_center(SCREEN_CENTER, self.first_line, Font::NORMAL, BLD_FG, BLD_BG);
|
||||
display::text_center(
|
||||
SCREEN_CENTER + Offset::y(10),
|
||||
self.second_line,
|
||||
Font::NORMAL,
|
||||
BLD_FG,
|
||||
BLD_BG,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "ui_debug")]
|
||||
impl Trace for MenuChoice {
|
||||
fn trace(&self, t: &mut dyn Tracer) {
|
||||
t.component("MenuChoice");
|
||||
}
|
||||
}
|
||||
|
||||
pub struct MenuChoiceFactory;
|
||||
|
||||
impl MenuChoiceFactory {
|
||||
const CHOICES: [(&'static str, &'static str, Icon); CHOICE_LENGTH] = [
|
||||
("Factory", "reset", ICON_TRASH),
|
||||
("Reboot", "Trezor", ICON_REDO),
|
||||
("Exit", "menu", ICON_EXIT),
|
||||
];
|
||||
|
||||
pub fn new() -> Self {
|
||||
Self {}
|
||||
}
|
||||
}
|
||||
|
||||
impl ChoiceFactory<&'static str> for MenuChoiceFactory {
|
||||
type Action = MenuMsg;
|
||||
type Item = MenuChoice;
|
||||
|
||||
fn count(&self) -> usize {
|
||||
CHOICE_LENGTH
|
||||
}
|
||||
|
||||
fn get(&self, choice_index: usize) -> (Self::Item, Self::Action) {
|
||||
let choice_item = MenuChoice::new(
|
||||
Self::CHOICES[choice_index].0,
|
||||
Self::CHOICES[choice_index].1,
|
||||
Self::CHOICES[choice_index].2,
|
||||
);
|
||||
let action = match choice_index {
|
||||
0 => MenuMsg::FactoryReset,
|
||||
1 => MenuMsg::Reboot,
|
||||
2 => MenuMsg::Close,
|
||||
_ => unreachable!(),
|
||||
};
|
||||
(choice_item, action)
|
||||
}
|
||||
}
|
||||
|
||||
pub struct Menu {
|
||||
bg: Pad,
|
||||
title: Child<Title>,
|
||||
pad: Pad,
|
||||
choice_page: Child<ChoicePage<MenuChoiceFactory, &'static str, MenuMsg>>,
|
||||
}
|
||||
|
||||
impl Menu {
|
||||
pub fn new(bld_version: &'static str) -> Self {
|
||||
let mut instance = Self {
|
||||
bg: Pad::with_background(BLD_BG),
|
||||
title: Child::new(Title::new(bld_version)),
|
||||
};
|
||||
instance.bg.clear();
|
||||
instance
|
||||
pub fn new() -> Self {
|
||||
let choices = MenuChoiceFactory::new();
|
||||
Self {
|
||||
pad: Pad::with_background(BLD_BG).with_clear(),
|
||||
choice_page: Child::new(
|
||||
ChoicePage::new(choices)
|
||||
.with_carousel(true)
|
||||
.with_only_one_item(true),
|
||||
),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Component for Menu {
|
||||
type Msg = Never;
|
||||
type Msg = MenuMsg;
|
||||
|
||||
fn place(&mut self, bounds: Rect) -> Rect {
|
||||
self.bg
|
||||
.place(Rect::new(Point::new(0, 0), Point::new(WIDTH, HEIGHT)));
|
||||
self.title
|
||||
.place(Rect::new(Point::new(10, 0), Point::new(128, 8)));
|
||||
self.pad.place(bounds);
|
||||
self.choice_page.place(bounds);
|
||||
bounds
|
||||
}
|
||||
|
||||
fn event(&mut self, _ctx: &mut EventCtx, _event: Event) -> Option<Self::Msg> {
|
||||
None
|
||||
fn event(&mut self, ctx: &mut EventCtx, event: Event) -> Option<Self::Msg> {
|
||||
self.choice_page.event(ctx, event)
|
||||
}
|
||||
|
||||
fn paint(&mut self) {
|
||||
self.bg.paint();
|
||||
self.title.paint();
|
||||
self.pad.paint();
|
||||
self.choice_page.paint();
|
||||
}
|
||||
|
||||
#[cfg(feature = "ui_bounds")]
|
||||
fn bounds(&self, sink: &mut dyn FnMut(Rect)) {
|
||||
self.choice_page.bounds(sink)
|
||||
}
|
||||
}
|
||||
|
@ -1,42 +1,38 @@
|
||||
use crate::{
|
||||
trezorhal::io::io_button_read,
|
||||
strutil::hexlify,
|
||||
time::Duration,
|
||||
trezorhal::{io::io_button_read, time},
|
||||
ui::{
|
||||
component::{Component, Never},
|
||||
display::{self, Font},
|
||||
geometry::Point,
|
||||
component::{Component, Event, EventCtx, Label, LineBreaking::BreakWordsNoHyphen, Never},
|
||||
constant::SCREEN,
|
||||
display::{self, Color, Font, Icon},
|
||||
event::ButtonEvent,
|
||||
geometry::{Alignment, Alignment::Center, Offset, Rect, TOP_CENTER},
|
||||
util::{from_c_array, from_c_str},
|
||||
},
|
||||
};
|
||||
use heapless::String;
|
||||
|
||||
use super::{
|
||||
component::{ResultScreen, WelcomeScreen},
|
||||
theme::ICON_SUCCESS,
|
||||
};
|
||||
|
||||
mod confirm;
|
||||
mod connect;
|
||||
mod intro;
|
||||
mod menu;
|
||||
mod theme;
|
||||
mod title;
|
||||
|
||||
use crate::ui::{
|
||||
component::{
|
||||
text::paragraphs::{Paragraph, ParagraphVecShort, Paragraphs, VecExt},
|
||||
Event, EventCtx,
|
||||
},
|
||||
constant::{screen, WIDTH},
|
||||
display::{Color, Icon, TextOverlay},
|
||||
event::ButtonEvent,
|
||||
geometry::{LinearPlacement, Offset, Rect, CENTER},
|
||||
util::{from_c_array, from_c_str},
|
||||
};
|
||||
|
||||
use super::{
|
||||
component::{Button, ButtonPos, ResultScreen},
|
||||
constant,
|
||||
theme::{ICON_FAIL, ICON_SUCCESS, LOGO_EMPTY},
|
||||
};
|
||||
mod welcome;
|
||||
|
||||
use confirm::Confirm;
|
||||
use connect::Connect;
|
||||
use intro::Intro;
|
||||
use menu::Menu;
|
||||
use theme::{bld_button_cancel, bld_button_default, BLD_BG, BLD_FG};
|
||||
use theme::{BLD_BG, BLD_FG, ICON_ALERT, ICON_SPINNER, LOGO_EMPTY};
|
||||
use welcome::Welcome;
|
||||
|
||||
const SCREEN_ADJ: Rect = screen().split_top(64).0;
|
||||
pub type BootloaderString = String<128>;
|
||||
|
||||
pub trait ReturnToC {
|
||||
fn return_to_c(self) -> u32;
|
||||
@ -76,8 +72,9 @@ where
|
||||
F: Component,
|
||||
F::Msg: ReturnToC,
|
||||
{
|
||||
frame.place(SCREEN_ADJ);
|
||||
frame.place(SCREEN);
|
||||
frame.paint();
|
||||
display::refresh();
|
||||
|
||||
while button_eval().is_some() {}
|
||||
|
||||
@ -92,6 +89,7 @@ where
|
||||
}
|
||||
|
||||
frame.paint();
|
||||
display::refresh();
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -100,7 +98,7 @@ fn show<F>(frame: &mut F)
|
||||
where
|
||||
F: Component,
|
||||
{
|
||||
frame.place(SCREEN_ADJ);
|
||||
frame.place(SCREEN);
|
||||
display::sync();
|
||||
frame.paint();
|
||||
display::refresh();
|
||||
@ -111,81 +109,74 @@ extern "C" fn screen_install_confirm(
|
||||
vendor_str: *const cty::c_char,
|
||||
vendor_str_len: u8,
|
||||
version: *const cty::c_char,
|
||||
_fingerprint: *const cty::uint8_t,
|
||||
downgrade: bool,
|
||||
vendor: bool,
|
||||
fingerprint: *const cty::uint8_t,
|
||||
should_keep_seed: bool,
|
||||
is_newvendor: bool,
|
||||
version_cmp: cty::c_int,
|
||||
) -> u32 {
|
||||
let text = unwrap!(unsafe { from_c_array(vendor_str, vendor_str_len as usize) });
|
||||
let version = unwrap!(unsafe { from_c_str(version) });
|
||||
|
||||
let icon: Option<Icon> = None;
|
||||
|
||||
let msg = if downgrade {
|
||||
"Downgrade firmware by"
|
||||
} else if vendor {
|
||||
"Change vendor to"
|
||||
} else {
|
||||
"Update firmware by"
|
||||
let mut fingerprint_buffer: [u8; 64] = [0; 64];
|
||||
let fingerprint_str = unsafe {
|
||||
let fingerprint_slice = core::slice::from_raw_parts(fingerprint as *const u8, 32);
|
||||
hexlify(fingerprint_slice, &mut fingerprint_buffer);
|
||||
core::str::from_utf8_unchecked(fingerprint_buffer.as_ref())
|
||||
};
|
||||
|
||||
let mut message = ParagraphVecShort::new();
|
||||
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(text));
|
||||
|
||||
message.add(Paragraph::new(&theme::TEXT_NORMAL, msg).centered());
|
||||
message.add(Paragraph::new(&theme::TEXT_NORMAL, text).centered());
|
||||
message.add(Paragraph::new(&theme::TEXT_NORMAL, version).centered());
|
||||
let title_str = if is_newvendor {
|
||||
"CHANGE FW VENDOR"
|
||||
} else if version_cmp > 0 {
|
||||
"UPDATE FIRMWARE"
|
||||
} else if version_cmp == 0 {
|
||||
"REINSTALL FW"
|
||||
} else {
|
||||
"DOWNGRADE FW"
|
||||
};
|
||||
|
||||
if vendor || downgrade {
|
||||
message.add(Paragraph::new(&theme::TEXT_BOLD, "Seed will be erased!").centered());
|
||||
}
|
||||
let message = Label::new(version_str.as_str(), Alignment::Start, theme::TEXT_NORMAL)
|
||||
.vertically_aligned(Center);
|
||||
let fingerprint = Label::new(
|
||||
fingerprint_str,
|
||||
Alignment::Start,
|
||||
theme::TEXT_NORMAL.with_line_breaking(BreakWordsNoHyphen),
|
||||
)
|
||||
.vertically_aligned(Center);
|
||||
|
||||
// TODO: this relies on StrBuffer support for bootloader, decide what to do
|
||||
let left = Button::with_text(ButtonPos::Left, "CANCEL", bld_button_cancel());
|
||||
let right = Button::with_text(ButtonPos::Right, "INSTALL", bld_button_default());
|
||||
|
||||
let mut frame = Confirm::new(
|
||||
BLD_BG,
|
||||
icon,
|
||||
Paragraphs::new(message).with_placement(LinearPlacement::vertical().align_at_center()),
|
||||
left,
|
||||
right,
|
||||
false,
|
||||
);
|
||||
let alert = (!should_keep_seed).then_some(Label::new(
|
||||
"Seed will be erased!",
|
||||
Alignment::Start,
|
||||
theme::TEXT_NORMAL,
|
||||
));
|
||||
|
||||
let mut frame = Confirm::new(BLD_BG, title_str, message, alert, "INSTALL")
|
||||
.with_info_screen("FW FINGERPRINT", fingerprint);
|
||||
run(&mut frame)
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
extern "C" fn screen_wipe_confirm() -> u32 {
|
||||
let icon: Option<Icon> = None; //Some(ERASE_BIG);
|
||||
let message = Label::new(
|
||||
"Seed and firmware will be erased!",
|
||||
Alignment::Start,
|
||||
theme::TEXT_NORMAL,
|
||||
)
|
||||
.vertically_aligned(Center);
|
||||
|
||||
let mut messages = ParagraphVecShort::new();
|
||||
|
||||
messages.add(
|
||||
Paragraph::new(
|
||||
&theme::TEXT_NORMAL,
|
||||
"Do you really want to wipe the device?",
|
||||
)
|
||||
.centered(),
|
||||
);
|
||||
messages.add(Paragraph::new(&theme::TEXT_BOLD, "Seed will be erased!").centered());
|
||||
|
||||
let message =
|
||||
Paragraphs::new(messages).with_placement(LinearPlacement::vertical().align_at_center());
|
||||
|
||||
// TODO: this relies on StrBuffer support for bootloader, decide what to do
|
||||
let left = Button::with_text(ButtonPos::Left, "WIPE", bld_button_default());
|
||||
let right = Button::with_text(ButtonPos::Right, "CANCEL", bld_button_cancel());
|
||||
|
||||
let mut frame = Confirm::new(BLD_BG, icon, message, left, right, true);
|
||||
let mut frame = Confirm::new(BLD_BG, "FACTORY RESET", message, None, "RESET");
|
||||
|
||||
run(&mut frame)
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
extern "C" fn screen_menu(bld_version: *const cty::c_char) -> u32 {
|
||||
let bld_version = unwrap!(unsafe { from_c_str(bld_version) });
|
||||
|
||||
run(&mut Menu::new(bld_version))
|
||||
extern "C" fn screen_menu(_bld_version: *const cty::c_char) -> u32 {
|
||||
run(&mut Menu::new())
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
@ -199,196 +190,184 @@ extern "C" fn screen_intro(
|
||||
let version = unwrap!(unsafe { from_c_str(version) });
|
||||
let bld_version = unwrap!(unsafe { from_c_str(bld_version) });
|
||||
|
||||
run(&mut Intro::new(bld_version, vendor, version))
|
||||
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(), version_str.as_str());
|
||||
run(&mut frame)
|
||||
}
|
||||
|
||||
fn screen_progress(
|
||||
text: &str,
|
||||
text2: &str,
|
||||
progress: u16,
|
||||
initialize: bool,
|
||||
fg_color: Color,
|
||||
bg_color: Color,
|
||||
_icon: Option<(&[u8], Color)>,
|
||||
icon: Option<(Icon, Color)>,
|
||||
) {
|
||||
if initialize {
|
||||
display::rect_fill(constant::screen(), bg_color);
|
||||
display::rect_fill(SCREEN, bg_color);
|
||||
}
|
||||
|
||||
let loader_area = Rect::new(Point::new(5, 24), Point::new(WIDTH - 5, 24 + 16));
|
||||
let progress = if progress < 20 { 20 } else { progress };
|
||||
|
||||
let mut text = TextOverlay::new(text, Font::NORMAL);
|
||||
text.place(loader_area.center() + Offset::y(Font::NORMAL.text_height() / 2));
|
||||
|
||||
let fill_to = (loader_area.width() as u32 * progress as u32) / 1000;
|
||||
|
||||
display::bar_with_text_and_fill(
|
||||
loader_area,
|
||||
Some(&text),
|
||||
display::rect_rounded2_partial(
|
||||
Rect::new(
|
||||
SCREEN.top_center() + Offset::new(-9, 3),
|
||||
SCREEN.top_center() + Offset::new(9, 18 + 3),
|
||||
),
|
||||
fg_color,
|
||||
bg_color,
|
||||
0,
|
||||
fill_to as _,
|
||||
((100_u32 * progress as u32) / 1000) as _,
|
||||
icon,
|
||||
);
|
||||
display::text_center(
|
||||
SCREEN.center() + Offset::y(8),
|
||||
text,
|
||||
Font::BOLD,
|
||||
fg_color,
|
||||
bg_color,
|
||||
);
|
||||
display::text_center(
|
||||
SCREEN.center() + Offset::y(20),
|
||||
text2,
|
||||
Font::BOLD,
|
||||
fg_color,
|
||||
bg_color,
|
||||
);
|
||||
|
||||
display::refresh();
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
extern "C" fn screen_install_progress(progress: u16, initialize: bool, _initial_setup: bool) {
|
||||
screen_progress(
|
||||
"INSTALLING FIRMWARE",
|
||||
"Installing",
|
||||
"firmware",
|
||||
progress,
|
||||
initialize,
|
||||
BLD_FG,
|
||||
BLD_BG,
|
||||
None,
|
||||
)
|
||||
Some((ICON_SUCCESS, BLD_FG)),
|
||||
);
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
extern "C" fn screen_wipe_progress(progress: u16, initialize: bool) {
|
||||
screen_progress(
|
||||
"WIPING DEVICE",
|
||||
"Resetting",
|
||||
"Trezor",
|
||||
progress,
|
||||
initialize,
|
||||
theme::BLD_FG,
|
||||
BLD_FG,
|
||||
BLD_BG,
|
||||
None,
|
||||
)
|
||||
Some((ICON_SUCCESS, BLD_FG)),
|
||||
);
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
extern "C" fn screen_connect() {
|
||||
let mut messages = ParagraphVecShort::new();
|
||||
|
||||
messages.add(Paragraph::new(&theme::TEXT_NORMAL, "Waiting for host...").centered());
|
||||
|
||||
let mut frame =
|
||||
Paragraphs::new(messages).with_placement(LinearPlacement::vertical().align_at_center());
|
||||
|
||||
let mut frame = Connect::new("Waiting for host...");
|
||||
show(&mut frame);
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
extern "C" fn screen_wipe_success() {
|
||||
let mut messages = ParagraphVecShort::new();
|
||||
messages.add(Paragraph::new(&theme::TEXT_NORMAL, "Device wiped").centered());
|
||||
messages.add(Paragraph::new(&theme::TEXT_NORMAL, "successfully.").centered());
|
||||
let title = Label::new("Trezor Reset", Alignment::Center, theme::TEXT_BOLD)
|
||||
.vertically_aligned(Alignment::Center);
|
||||
|
||||
let m_top =
|
||||
Paragraphs::new(messages).with_placement(LinearPlacement::vertical().align_at_center());
|
||||
let content = Label::new(
|
||||
"Reconnect\nthe device",
|
||||
Alignment::Center,
|
||||
theme::TEXT_NORMAL,
|
||||
)
|
||||
.vertically_aligned(Alignment::Center);
|
||||
|
||||
let mut messages = ParagraphVecShort::new();
|
||||
messages.add(Paragraph::new(&theme::TEXT_NORMAL, "PLEASE RECONNECT").centered());
|
||||
messages.add(Paragraph::new(&theme::TEXT_NORMAL, "THE DEVICE").centered());
|
||||
|
||||
let m_bottom =
|
||||
Paragraphs::new(messages).with_placement(LinearPlacement::vertical().align_at_center());
|
||||
|
||||
let mut frame = ResultScreen::new(BLD_FG, BLD_BG, ICON_SUCCESS, m_top, m_bottom, true);
|
||||
let mut frame = ResultScreen::new(BLD_FG, BLD_BG, ICON_SPINNER, title, content, true);
|
||||
show(&mut frame);
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
extern "C" fn screen_wipe_fail() {
|
||||
let mut messages = ParagraphVecShort::new();
|
||||
messages.add(Paragraph::new(&theme::TEXT_NORMAL, "Device wipe was").centered());
|
||||
messages.add(Paragraph::new(&theme::TEXT_NORMAL, "not successful.").centered());
|
||||
let title = Label::new("Reset failed", Alignment::Center, theme::TEXT_BOLD)
|
||||
.vertically_aligned(Alignment::Center);
|
||||
|
||||
let m_top =
|
||||
Paragraphs::new(messages).with_placement(LinearPlacement::vertical().align_at_center());
|
||||
let content = Label::new(
|
||||
"Please reconnect\nthe device",
|
||||
Alignment::Center,
|
||||
theme::TEXT_NORMAL,
|
||||
)
|
||||
.vertically_aligned(Alignment::Center);
|
||||
|
||||
let mut messages = ParagraphVecShort::new();
|
||||
messages.add(Paragraph::new(&theme::TEXT_NORMAL, "PLEASE RECONNECT").centered());
|
||||
messages.add(Paragraph::new(&theme::TEXT_NORMAL, "THE DEVICE").centered());
|
||||
|
||||
let m_bottom =
|
||||
Paragraphs::new(messages).with_placement(LinearPlacement::vertical().align_at_center());
|
||||
|
||||
let mut frame = ResultScreen::new(BLD_FG, BLD_BG, ICON_FAIL, m_top, m_bottom, true);
|
||||
let mut frame = ResultScreen::new(BLD_FG, BLD_BG, ICON_ALERT, title, content, true);
|
||||
show(&mut frame);
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
extern "C" fn screen_boot_empty(_firmware_present: bool, _fading: bool) {
|
||||
Icon::new(LOGO_EMPTY).draw(SCREEN_ADJ.center(), CENTER, BLD_FG, BLD_BG);
|
||||
extern "C" fn screen_boot_empty(_firmware_present: bool) {
|
||||
display::rect_fill(SCREEN, BLD_BG);
|
||||
LOGO_EMPTY.draw(
|
||||
SCREEN.top_center() + Offset::y(11),
|
||||
TOP_CENTER,
|
||||
BLD_FG,
|
||||
BLD_BG,
|
||||
);
|
||||
display::refresh();
|
||||
if !_firmware_present {
|
||||
time::sleep(Duration::from_millis(1000));
|
||||
}
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
extern "C" fn screen_install_fail() {
|
||||
let mut messages = ParagraphVecShort::new();
|
||||
messages.add(Paragraph::new(&theme::TEXT_NORMAL, "Firmware installation was").centered());
|
||||
messages.add(Paragraph::new(&theme::TEXT_NORMAL, "not successful.").centered());
|
||||
let m_top =
|
||||
Paragraphs::new(messages).with_placement(LinearPlacement::vertical().align_at_center());
|
||||
let title = Label::new("Install failed", Alignment::Center, theme::TEXT_BOLD)
|
||||
.vertically_aligned(Alignment::Center);
|
||||
|
||||
let mut messages = ParagraphVecShort::new();
|
||||
messages.add(Paragraph::new(&theme::TEXT_NORMAL, "PLEASE RECONNECT").centered());
|
||||
messages.add(Paragraph::new(&theme::TEXT_NORMAL, "THE DEVICE").centered());
|
||||
let content = Label::new(
|
||||
"Please reconnect\nthe device",
|
||||
Alignment::Center,
|
||||
theme::TEXT_NORMAL,
|
||||
)
|
||||
.vertically_aligned(Alignment::Center);
|
||||
|
||||
let m_bottom =
|
||||
Paragraphs::new(messages).with_placement(LinearPlacement::vertical().align_at_center());
|
||||
|
||||
let mut frame = ResultScreen::new(BLD_FG, BLD_BG, ICON_FAIL, m_top, m_bottom, true);
|
||||
show(&mut frame);
|
||||
}
|
||||
|
||||
fn screen_install_success_bld(msg: &'static str, complete_draw: bool) {
|
||||
let mut messages = ParagraphVecShort::new();
|
||||
messages.add(Paragraph::new(&theme::TEXT_NORMAL, "Firmware installed").centered());
|
||||
messages.add(Paragraph::new(&theme::TEXT_NORMAL, "successfully.").centered());
|
||||
|
||||
let m_top =
|
||||
Paragraphs::new(messages).with_placement(LinearPlacement::vertical().align_at_center());
|
||||
|
||||
let mut messages = ParagraphVecShort::new();
|
||||
messages.add(Paragraph::new(&theme::TEXT_NORMAL, msg).centered());
|
||||
|
||||
let m_bottom =
|
||||
Paragraphs::new(messages).with_placement(LinearPlacement::vertical().align_at_center());
|
||||
|
||||
let mut frame = ResultScreen::new(BLD_FG, BLD_BG, ICON_SUCCESS, m_top, m_bottom, complete_draw);
|
||||
show(&mut frame);
|
||||
}
|
||||
|
||||
fn screen_install_success_initial(msg: &'static str, complete_draw: bool) {
|
||||
let mut messages = ParagraphVecShort::new();
|
||||
messages.add(Paragraph::new(&theme::TEXT_NORMAL, "Firmware installed").centered());
|
||||
messages.add(Paragraph::new(&theme::TEXT_NORMAL, "successfully.").centered());
|
||||
|
||||
let m_top =
|
||||
Paragraphs::new(messages).with_placement(LinearPlacement::vertical().align_at_center());
|
||||
|
||||
let mut messages = ParagraphVecShort::new();
|
||||
messages.add(Paragraph::new(&theme::TEXT_NORMAL, msg).centered());
|
||||
|
||||
let m_bottom =
|
||||
Paragraphs::new(messages).with_placement(LinearPlacement::vertical().align_at_center());
|
||||
|
||||
let mut frame = ResultScreen::new(BLD_FG, BLD_BG, ICON_SUCCESS, m_top, m_bottom, complete_draw);
|
||||
let mut frame = ResultScreen::new(BLD_FG, BLD_BG, ICON_ALERT, title, content, true);
|
||||
show(&mut frame);
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
extern "C" fn screen_install_success(
|
||||
reboot_msg: *const cty::c_char,
|
||||
initial_setup: bool,
|
||||
_initial_setup: bool,
|
||||
complete_draw: bool,
|
||||
) {
|
||||
let msg = unwrap!(unsafe { from_c_str(reboot_msg) });
|
||||
if initial_setup {
|
||||
screen_install_success_initial(msg, complete_draw)
|
||||
} else {
|
||||
screen_install_success_bld(msg, complete_draw)
|
||||
}
|
||||
|
||||
let title = Label::new("Firmware installed", Alignment::Center, theme::TEXT_BOLD)
|
||||
.vertically_aligned(Alignment::Center);
|
||||
|
||||
let content = Label::new(msg, Alignment::Center, theme::TEXT_NORMAL)
|
||||
.vertically_aligned(Alignment::Center);
|
||||
|
||||
let mut frame = ResultScreen::new(BLD_FG, BLD_BG, ICON_SPINNER, title, content, complete_draw);
|
||||
show(&mut frame);
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
extern "C" fn screen_welcome() {
|
||||
let mut messages = ParagraphVecShort::new();
|
||||
messages.add(Paragraph::new(&theme::TEXT_BOLD, "Get started with").centered());
|
||||
messages.add(Paragraph::new(&theme::TEXT_BOLD, "your trezor at").centered());
|
||||
messages.add(Paragraph::new(&theme::TEXT_BOLD, "trezor.io/start").centered());
|
||||
let mut frame =
|
||||
Paragraphs::new(messages).with_placement(LinearPlacement::vertical().align_at_center());
|
||||
let mut frame = Welcome::new();
|
||||
show(&mut frame);
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
extern "C" fn screen_welcome_model() {
|
||||
let mut frame = WelcomeScreen::new();
|
||||
show(&mut frame);
|
||||
}
|
||||
|
@ -1,24 +1,21 @@
|
||||
use crate::ui::{
|
||||
component::text::TextStyle,
|
||||
display::{Color, Font},
|
||||
geometry::Offset,
|
||||
display::{toif::Icon, Color, Font},
|
||||
};
|
||||
|
||||
use super::super::{
|
||||
component::ButtonStyleSheet,
|
||||
theme::{BG, BLACK, FG, WHITE},
|
||||
};
|
||||
pub use super::super::theme::{BLACK, WHITE};
|
||||
|
||||
pub const BLD_BG: Color = BLACK;
|
||||
pub const BLD_FG: Color = WHITE;
|
||||
|
||||
pub fn bld_button_default() -> ButtonStyleSheet {
|
||||
ButtonStyleSheet::new(BG, FG, false, false, None, Offset::zero())
|
||||
}
|
||||
|
||||
pub fn bld_button_cancel() -> ButtonStyleSheet {
|
||||
ButtonStyleSheet::new(FG, BG, false, false, None, Offset::zero())
|
||||
}
|
||||
include_icon!(LOGO_EMPTY, "model_tr/res/logo_22_33_empty.toif");
|
||||
include_icon!(ICON_TRASH, "model_tr/res/trash.toif");
|
||||
include_icon!(ICON_ALERT, "model_tr/res/alert.toif");
|
||||
include_icon!(ICON_SPINNER, "model_tr/res/spinner.toif");
|
||||
include_icon!(ICON_REDO, "model_tr/res/redo.toif");
|
||||
include_icon!(ICON_EXIT, "model_tr/res/exit.toif");
|
||||
include_icon!(ICON_INFO, "model_tr/res/info.toif");
|
||||
include_icon!(ICON_INFO_INVERTED, "model_tr/res/info_inverted.toif");
|
||||
|
||||
pub const TEXT_NORMAL: TextStyle = TextStyle::new(Font::NORMAL, BLD_FG, BLD_BG, BLD_FG, BLD_FG);
|
||||
pub const TEXT_BOLD: TextStyle = TextStyle::new(Font::NORMAL, BLD_FG, BLD_BG, BLD_FG, BLD_FG);
|
||||
pub const TEXT_BOLD: TextStyle = TextStyle::new(Font::BOLD, BLD_FG, BLD_BG, BLD_FG, BLD_FG);
|
||||
|
@ -1,54 +0,0 @@
|
||||
use crate::ui::{
|
||||
component::{Component, Event, EventCtx, Never},
|
||||
display::{self, Font},
|
||||
geometry::{Point, Rect},
|
||||
};
|
||||
|
||||
use super::theme::{BLD_BG, BLD_FG};
|
||||
|
||||
pub struct Title {
|
||||
version: &'static str,
|
||||
area: Rect,
|
||||
}
|
||||
|
||||
impl Title {
|
||||
pub fn new(version: &'static str) -> Self {
|
||||
Self {
|
||||
version,
|
||||
area: Rect::zero(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Component for Title {
|
||||
type Msg = Never;
|
||||
|
||||
fn place(&mut self, bounds: Rect) -> Rect {
|
||||
self.area = bounds;
|
||||
bounds
|
||||
}
|
||||
|
||||
fn event(&mut self, _ctx: &mut EventCtx, _event: Event) -> Option<Self::Msg> {
|
||||
None
|
||||
}
|
||||
|
||||
fn paint(&mut self) {
|
||||
display::text_top_left(
|
||||
self.area.top_left(),
|
||||
"BOOTLOADER",
|
||||
Font::NORMAL,
|
||||
BLD_FG,
|
||||
BLD_BG,
|
||||
);
|
||||
display::text_top_left(
|
||||
Point::new(self.area.top_left().x + 65, self.area.top_left().y),
|
||||
self.version,
|
||||
Font::NORMAL,
|
||||
BLD_FG,
|
||||
BLD_BG,
|
||||
);
|
||||
}
|
||||
|
||||
#[cfg(feature = "ui_bounds")]
|
||||
fn bounds(&self, _sink: &mut dyn FnMut(Rect)) {}
|
||||
}
|
60
core/embed/rust/src/ui/model_tr/bootloader/welcome.rs
Normal file
60
core/embed/rust/src/ui/model_tr/bootloader/welcome.rs
Normal file
@ -0,0 +1,60 @@
|
||||
use crate::ui::{
|
||||
component::{Component, Event, EventCtx, Never, Pad},
|
||||
display::{self, Font},
|
||||
geometry::{Offset, Rect},
|
||||
};
|
||||
|
||||
use super::theme::{BLD_BG, BLD_FG};
|
||||
|
||||
pub struct Welcome {
|
||||
bg: Pad,
|
||||
}
|
||||
|
||||
impl Welcome {
|
||||
pub fn new() -> Self {
|
||||
Self {
|
||||
bg: Pad::with_background(BLD_BG).with_clear(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Component for Welcome {
|
||||
type Msg = Never;
|
||||
|
||||
fn place(&mut self, bounds: Rect) -> Rect {
|
||||
self.bg.place(bounds);
|
||||
bounds
|
||||
}
|
||||
|
||||
fn event(&mut self, _ctx: &mut EventCtx, _event: Event) -> Option<Self::Msg> {
|
||||
None
|
||||
}
|
||||
|
||||
fn paint(&mut self) {
|
||||
self.bg.paint();
|
||||
|
||||
let top_center = self.bg.area.top_center();
|
||||
|
||||
display::text_center(
|
||||
top_center + Offset::y(24),
|
||||
"Get started with",
|
||||
Font::NORMAL,
|
||||
BLD_FG,
|
||||
BLD_BG,
|
||||
);
|
||||
display::text_center(
|
||||
top_center + Offset::y(32),
|
||||
"your Trezor at",
|
||||
Font::NORMAL,
|
||||
BLD_FG,
|
||||
BLD_BG,
|
||||
);
|
||||
display::text_center(
|
||||
top_center + Offset::y(48),
|
||||
"trezor.io/start",
|
||||
Font::BOLD,
|
||||
BLD_FG,
|
||||
BLD_BG,
|
||||
);
|
||||
}
|
||||
}
|
4
core/embed/rust/src/ui/model_tr/common_messages.rs
Normal file
4
core/embed/rust/src/ui/model_tr/common_messages.rs
Normal file
@ -0,0 +1,4 @@
|
||||
pub enum CancelConfirmMsg {
|
||||
Cancelled,
|
||||
Confirmed,
|
||||
}
|
@ -1,5 +1,5 @@
|
||||
use crate::{
|
||||
strutil::{ShortString, StringType},
|
||||
strutil::StringType,
|
||||
time::Duration,
|
||||
ui::{
|
||||
component::{Component, Event, EventCtx, Never},
|
||||
@ -124,7 +124,18 @@ where
|
||||
};
|
||||
let content_width = match &self.content {
|
||||
ButtonContent::Text(text) => style.font.visible_text_width(text.as_ref()),
|
||||
ButtonContent::Icon(icon) => icon.toif.width() - 1,
|
||||
ButtonContent::Icon(icon, icon_pressed) => {
|
||||
let width = if self.state == State::Pressed {
|
||||
if let Some(icon_pressed) = icon_pressed {
|
||||
icon_pressed.toif.width()
|
||||
} else {
|
||||
icon.toif.width()
|
||||
}
|
||||
} else {
|
||||
icon.toif.width()
|
||||
};
|
||||
width - 1
|
||||
}
|
||||
};
|
||||
content_width + 2 * outline
|
||||
};
|
||||
@ -241,7 +252,8 @@ where
|
||||
match &self.content {
|
||||
ButtonContent::Text(text) => {
|
||||
display::text_left(
|
||||
self.get_text_baseline(style),
|
||||
self.get_text_baseline(style)
|
||||
- Offset::x(style.font.start_x_bearing(text.as_ref())),
|
||||
text.as_ref(),
|
||||
style.font,
|
||||
text_color,
|
||||
@ -424,12 +436,12 @@ impl<T> ButtonDetails<T> {
|
||||
|
||||
/// Down arrow to signal paginating forward. Takes half the screen's width
|
||||
pub fn down_arrow_icon_wide() -> Self {
|
||||
Self::icon(theme::ICON_ARROW_DOWN).fixed_width(HALF_SCREEN_BUTTON_WIDTH)
|
||||
Self::icon(theme::ICON_ARROW_DOWN).with_fixed_width(HALF_SCREEN_BUTTON_WIDTH)
|
||||
}
|
||||
|
||||
/// Up arrow to signal paginating back. Takes half the screen's width
|
||||
pub fn up_arrow_icon_wide() -> Self {
|
||||
Self::icon(theme::ICON_ARROW_UP).fixed_width(HALF_SCREEN_BUTTON_WIDTH)
|
||||
Self::icon(theme::ICON_ARROW_UP).with_fixed_width(HALF_SCREEN_BUTTON_WIDTH)
|
||||
}
|
||||
|
||||
/// Icon of a bin to signal deleting.
|
||||
@ -471,8 +483,8 @@ impl<T> ButtonDetails<T> {
|
||||
self
|
||||
}
|
||||
|
||||
/// Width of the button.
|
||||
pub fn fixed_width(mut self, width: i16) -> Self {
|
||||
/// Specifying the width of the button.
|
||||
pub fn with_fixed_width(mut self, width: i16) -> Self {
|
||||
self.fixed_width = Some(width);
|
||||
self
|
||||
}
|
||||
@ -880,6 +892,9 @@ impl ButtonActions {
|
||||
|
||||
// DEBUG-ONLY SECTION BELOW
|
||||
|
||||
#[cfg(feature = "ui_debug")]
|
||||
use crate::strutil::ShortString;
|
||||
|
||||
#[cfg(feature = "ui_debug")]
|
||||
impl<T: StringType> crate::trace::Trace for Button<T> {
|
||||
fn trace(&self, t: &mut dyn crate::trace::Tracer) {
|
||||
|
@ -77,7 +77,7 @@ where
|
||||
|
||||
fn paint_left(&self) {
|
||||
let baseline = Point::new(self.pad.area.x0, self.y_baseline());
|
||||
common::display(baseline, &self.text, self.font);
|
||||
common::display_left(baseline, &self.text, self.font);
|
||||
}
|
||||
|
||||
fn paint_center(&self) {
|
||||
@ -108,7 +108,7 @@ where
|
||||
};
|
||||
|
||||
let baseline = Point::new(self.pad.area.x0 + x_offset, self.y_baseline());
|
||||
common::display(baseline, &text_to_display, self.font);
|
||||
common::display_left(baseline, &text_to_display, self.font);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -6,7 +6,7 @@ use crate::ui::{
|
||||
use super::theme;
|
||||
|
||||
/// Display white text on black background
|
||||
pub fn display<T: AsRef<str>>(baseline: Point, text: &T, font: Font) {
|
||||
pub fn display_left<T: AsRef<str>>(baseline: Point, text: &T, font: Font) {
|
||||
display::text_left(baseline, text.as_ref(), font, theme::FG, theme::BG);
|
||||
}
|
||||
|
||||
|
96
core/embed/rust/src/ui/model_tr/component/error.rs
Normal file
96
core/embed/rust/src/ui/model_tr/component/error.rs
Normal file
@ -0,0 +1,96 @@
|
||||
use crate::ui::{
|
||||
component::{Child, Component, Event, EventCtx, Label, Never, Pad},
|
||||
constant::screen,
|
||||
display,
|
||||
geometry::{Alignment::Center, Offset, Point, Rect, TOP_LEFT, TOP_RIGHT},
|
||||
};
|
||||
|
||||
use super::super::{
|
||||
theme,
|
||||
theme::{BG, FG, TITLE_AREA_HEIGHT},
|
||||
};
|
||||
|
||||
const FOOTER_AREA_HEIGHT: i16 = 20;
|
||||
const MESSAGE_AREA_HEIGHT: i16 = 32;
|
||||
const DIVIDER_POSITION: i16 = 43;
|
||||
|
||||
pub struct ErrorScreen<T> {
|
||||
bg: Pad,
|
||||
show_icons: bool,
|
||||
title: Child<Label<T>>,
|
||||
message: Child<Label<T>>,
|
||||
footer: Child<Label<T>>,
|
||||
area: Rect,
|
||||
}
|
||||
|
||||
impl<T: AsRef<str>> ErrorScreen<T> {
|
||||
pub fn new(title: T, message: T, footer: T) -> Self {
|
||||
let title = Label::new(title, Center, theme::TEXT_BOLD);
|
||||
let message = Label::new(message, Center, theme::TEXT_NORMAL).vertically_aligned(Center);
|
||||
let footer = Label::new(footer, Center, theme::TEXT_NORMAL).vertically_aligned(Center);
|
||||
|
||||
Self {
|
||||
bg: Pad::with_background(BG).with_clear(),
|
||||
show_icons: true,
|
||||
title: Child::new(title),
|
||||
message: Child::new(message),
|
||||
footer: Child::new(footer),
|
||||
area: Rect::zero(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: AsRef<str>> Component for ErrorScreen<T> {
|
||||
type Msg = Never;
|
||||
|
||||
fn place(&mut self, bounds: Rect) -> Rect {
|
||||
self.bg.place(screen());
|
||||
|
||||
let title_area = Rect::new(screen().top_left(), screen().top_right() + Offset::y(11));
|
||||
self.title.place(title_area);
|
||||
|
||||
let text_width = self.title.inner().max_size().x;
|
||||
|
||||
if text_width > title_area.width() - 2 * TITLE_AREA_HEIGHT {
|
||||
self.show_icons = false;
|
||||
}
|
||||
|
||||
let message_area = Rect::new(
|
||||
title_area.bottom_left(),
|
||||
title_area.bottom_right() + Offset::y(MESSAGE_AREA_HEIGHT),
|
||||
);
|
||||
self.message.place(message_area);
|
||||
|
||||
let footer_area = Rect::new(
|
||||
screen().bottom_left() + Offset::y(-FOOTER_AREA_HEIGHT),
|
||||
screen().bottom_right(),
|
||||
);
|
||||
self.footer.place(footer_area);
|
||||
|
||||
self.area = bounds;
|
||||
screen()
|
||||
}
|
||||
|
||||
fn event(&mut self, _ctx: &mut EventCtx, _event: Event) -> Option<Self::Msg> {
|
||||
None
|
||||
}
|
||||
|
||||
fn paint(&mut self) {
|
||||
self.bg.paint();
|
||||
|
||||
if self.show_icons {
|
||||
theme::ICON_WARN_TITLE.draw(screen().top_left(), TOP_LEFT, FG, BG);
|
||||
theme::ICON_WARN_TITLE.draw(screen().top_right(), TOP_RIGHT, FG, BG);
|
||||
}
|
||||
self.title.paint();
|
||||
self.message.paint();
|
||||
// divider line
|
||||
let bar = Rect::from_center_and_size(
|
||||
Point::new(self.area.center().x, DIVIDER_POSITION),
|
||||
Offset::new(self.area.width(), 1),
|
||||
);
|
||||
display::rect_fill(bar, FG);
|
||||
|
||||
self.footer.paint();
|
||||
}
|
||||
}
|
@ -1,7 +1,7 @@
|
||||
use crate::{
|
||||
strutil::StringType,
|
||||
ui::{
|
||||
component::{base::Component, text::layout::LayoutFit, FormattedText, Paginate},
|
||||
component::{base::Component, FormattedText, Paginate},
|
||||
geometry::Rect,
|
||||
},
|
||||
};
|
||||
@ -200,6 +200,9 @@ where
|
||||
|
||||
// DEBUG-ONLY SECTION BELOW
|
||||
|
||||
#[cfg(feature = "ui_debug")]
|
||||
use crate::ui::component::text::layout::LayoutFit;
|
||||
|
||||
#[cfg(feature = "ui_debug")]
|
||||
impl<T> crate::trace::Trace for Page<T>
|
||||
where
|
||||
|
@ -6,20 +6,23 @@ use crate::{
|
||||
},
|
||||
};
|
||||
|
||||
use super::{
|
||||
super::{theme, ButtonController, ButtonControllerMsg, ButtonLayout, ButtonPos},
|
||||
choice_item::ChoiceItem,
|
||||
};
|
||||
use super::super::{theme, ButtonController, ButtonControllerMsg, ButtonLayout, ButtonPos};
|
||||
|
||||
const DEFAULT_ITEMS_DISTANCE: i16 = 10;
|
||||
const DEFAULT_Y_BASELINE: i16 = 20;
|
||||
|
||||
pub trait Choice<T: StringType> {
|
||||
// Only `paint_center` is required, the rest is optional
|
||||
// and therefore has a default implementation.
|
||||
fn paint_center(&self, area: Rect, inverse: bool);
|
||||
fn width_center(&self) -> i16;
|
||||
fn width_center(&self) -> i16 {
|
||||
0
|
||||
}
|
||||
|
||||
fn paint_side(&self, area: Rect);
|
||||
fn width_side(&self) -> i16;
|
||||
fn paint_side(&self, _area: Rect) {}
|
||||
fn width_side(&self) -> i16 {
|
||||
0
|
||||
}
|
||||
|
||||
fn btn_layout(&self) -> ButtonLayout<T> {
|
||||
ButtonLayout::default_three_icons()
|
||||
@ -37,9 +40,10 @@ pub trait Choice<T: StringType> {
|
||||
/// This way, no more than one item is stored in memory at any time.
|
||||
pub trait ChoiceFactory<T: StringType> {
|
||||
type Action;
|
||||
type Item: Choice<T>;
|
||||
|
||||
fn count(&self) -> usize;
|
||||
fn get(&self, index: usize) -> (ChoiceItem<T>, Self::Action);
|
||||
fn get(&self, index: usize) -> (Self::Item, Self::Action);
|
||||
}
|
||||
|
||||
/// General component displaying a set of items on the screen
|
||||
@ -223,7 +227,7 @@ where
|
||||
}
|
||||
|
||||
/// Getting the choice on the current index
|
||||
pub fn get_current_choice(&self) -> (ChoiceItem<T>, A) {
|
||||
pub fn get_current_choice(&self) -> (<F as ChoiceFactory<T>>::Item, A) {
|
||||
self.choices.get(self.page_counter)
|
||||
}
|
||||
|
||||
@ -436,6 +440,7 @@ impl<F, T, A> crate::trace::Trace for ChoicePage<F, T, A>
|
||||
where
|
||||
F: ChoiceFactory<T, Action = A>,
|
||||
T: StringType + Clone,
|
||||
<F as ChoiceFactory<T>>::Item: crate::trace::Trace,
|
||||
{
|
||||
fn trace(&self, t: &mut dyn crate::trace::Tracer) {
|
||||
t.component("ChoicePage");
|
||||
|
@ -20,14 +20,15 @@ impl ChoiceFactoryNumberInput {
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: StringType> ChoiceFactory<T> for ChoiceFactoryNumberInput {
|
||||
impl<T: StringType + Clone> ChoiceFactory<T> for ChoiceFactoryNumberInput {
|
||||
type Action = u32;
|
||||
type Item = ChoiceItem<T>;
|
||||
|
||||
fn count(&self) -> usize {
|
||||
(self.max - self.min + 1) as usize
|
||||
}
|
||||
|
||||
fn get(&self, choice_index: usize) -> (ChoiceItem<T>, Self::Action) {
|
||||
fn get(&self, choice_index: usize) -> (Self::Item, Self::Action) {
|
||||
let num = self.min + choice_index as u32;
|
||||
let text: String<10> = String::from(num);
|
||||
let mut choice_item = ChoiceItem::new(text, ButtonLayout::default_three_icons());
|
||||
@ -47,7 +48,7 @@ impl<T: StringType> ChoiceFactory<T> for ChoiceFactoryNumberInput {
|
||||
|
||||
/// Simple wrapper around `ChoicePage` that allows for
|
||||
/// inputting a list of values and receiving the chosen one.
|
||||
pub struct NumberInput<T: StringType> {
|
||||
pub struct NumberInput<T: StringType + Clone> {
|
||||
choice_page: ChoicePage<ChoiceFactoryNumberInput, T, u32>,
|
||||
min: u32,
|
||||
}
|
||||
|
@ -4,7 +4,6 @@ use crate::{
|
||||
component::{text::common::TextBox, Child, Component, ComponentExt, Event, EventCtx},
|
||||
display::Icon,
|
||||
geometry::Rect,
|
||||
model_tr::layout::CancelConfirmMsg,
|
||||
util::char_to_string,
|
||||
},
|
||||
};
|
||||
@ -12,7 +11,8 @@ use crate::{
|
||||
use heapless::String;
|
||||
|
||||
use super::super::{
|
||||
theme, ButtonDetails, ButtonLayout, ChangingTextLine, ChoiceFactory, ChoiceItem, ChoicePage,
|
||||
theme, ButtonDetails, ButtonLayout, CancelConfirmMsg, ChangingTextLine, ChoiceFactory,
|
||||
ChoiceItem, ChoicePage,
|
||||
};
|
||||
|
||||
/// Defines the choices currently available on the screen
|
||||
@ -210,8 +210,9 @@ impl ChoiceFactoryPassphrase {
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: StringType> ChoiceFactory<T> for ChoiceFactoryPassphrase {
|
||||
impl<T: StringType + Clone> ChoiceFactory<T> for ChoiceFactoryPassphrase {
|
||||
type Action = PassphraseAction;
|
||||
type Item = ChoiceItem<T>;
|
||||
|
||||
fn count(&self) -> usize {
|
||||
let length = get_category_length(&self.current_category);
|
||||
@ -221,7 +222,7 @@ impl<T: StringType> ChoiceFactory<T> for ChoiceFactoryPassphrase {
|
||||
_ => length + 1,
|
||||
}
|
||||
}
|
||||
fn get(&self, choice_index: usize) -> (ChoiceItem<T>, Self::Action) {
|
||||
fn get(&self, choice_index: usize) -> (Self::Item, Self::Action) {
|
||||
match self.current_category {
|
||||
ChoiceCategory::Menu => self.get_menu_item(choice_index),
|
||||
_ => self.get_character_item(choice_index),
|
||||
@ -230,7 +231,7 @@ impl<T: StringType> ChoiceFactory<T> for ChoiceFactoryPassphrase {
|
||||
}
|
||||
|
||||
/// Component for entering a passphrase.
|
||||
pub struct PassphraseEntry<T: StringType> {
|
||||
pub struct PassphraseEntry<T: StringType + Clone> {
|
||||
choice_page: ChoicePage<ChoiceFactoryPassphrase, T, PassphraseAction>,
|
||||
passphrase_dots: Child<ChangingTextLine<String<MAX_PASSPHRASE_LENGTH>>>,
|
||||
show_plain_passphrase: bool,
|
||||
|
@ -5,12 +5,12 @@ use crate::{
|
||||
component::{text::common::TextBox, Child, Component, ComponentExt, Event, EventCtx},
|
||||
display::Icon,
|
||||
geometry::Rect,
|
||||
model_tr::layout::CancelConfirmMsg,
|
||||
},
|
||||
};
|
||||
|
||||
use super::super::{
|
||||
theme, ButtonDetails, ButtonLayout, ChangingTextLine, ChoiceFactory, ChoiceItem, ChoicePage,
|
||||
theme, ButtonDetails, ButtonLayout, CancelConfirmMsg, ChangingTextLine, ChoiceFactory,
|
||||
ChoiceItem, ChoicePage,
|
||||
};
|
||||
use heapless::String;
|
||||
|
||||
@ -44,10 +44,11 @@ const CHOICES: [(&str, PinAction, Option<Icon>); CHOICE_LENGTH] = [
|
||||
|
||||
struct ChoiceFactoryPIN;
|
||||
|
||||
impl<T: StringType> ChoiceFactory<T> for ChoiceFactoryPIN {
|
||||
impl<T: StringType + Clone> ChoiceFactory<T> for ChoiceFactoryPIN {
|
||||
type Action = PinAction;
|
||||
type Item = ChoiceItem<T>;
|
||||
|
||||
fn get(&self, choice_index: usize) -> (ChoiceItem<T>, Self::Action) {
|
||||
fn get(&self, choice_index: usize) -> (Self::Item, Self::Action) {
|
||||
let (choice_str, action, icon) = CHOICES[choice_index];
|
||||
|
||||
let mut choice_item = ChoiceItem::new(choice_str, ButtonLayout::default_three_icons());
|
||||
@ -72,7 +73,7 @@ impl<T: StringType> ChoiceFactory<T> for ChoiceFactoryPIN {
|
||||
}
|
||||
|
||||
/// Component for entering a PIN.
|
||||
pub struct PinEntry<T: StringType> {
|
||||
pub struct PinEntry<T: StringType + Clone> {
|
||||
choice_page: ChoicePage<ChoiceFactoryPIN, T, PinAction>,
|
||||
pin_line: Child<ChangingTextLine<String<MAX_PIN_LENGTH>>>,
|
||||
subprompt_line: Child<ChangingTextLine<T>>,
|
||||
|
@ -28,14 +28,15 @@ impl<T: StringType> ChoiceFactorySimple<T> {
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: StringType> ChoiceFactory<T> for ChoiceFactorySimple<T> {
|
||||
impl<T: StringType + Clone> ChoiceFactory<T> for ChoiceFactorySimple<T> {
|
||||
type Action = usize;
|
||||
type Item = ChoiceItem<T>;
|
||||
|
||||
fn count(&self) -> usize {
|
||||
self.choices.len()
|
||||
}
|
||||
|
||||
fn get(&self, choice_index: usize) -> (ChoiceItem<T>, Self::Action) {
|
||||
fn get(&self, choice_index: usize) -> (Self::Item, Self::Action) {
|
||||
let text = &self.choices[choice_index];
|
||||
let mut choice_item = ChoiceItem::new(text, ButtonLayout::default_three_icons());
|
||||
|
||||
@ -58,7 +59,7 @@ impl<T: StringType> ChoiceFactory<T> for ChoiceFactorySimple<T> {
|
||||
/// inputting a list of values and receiving the chosen one.
|
||||
pub struct SimpleChoice<T>
|
||||
where
|
||||
T: StringType,
|
||||
T: StringType + Clone,
|
||||
{
|
||||
choice_page: ChoicePage<ChoiceFactorySimple<T>, T, usize>,
|
||||
pub return_index: bool,
|
||||
|
@ -58,8 +58,9 @@ impl ChoiceFactoryWordlist {
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: StringType> ChoiceFactory<T> for ChoiceFactoryWordlist {
|
||||
impl<T: StringType + Clone> ChoiceFactory<T> for ChoiceFactoryWordlist {
|
||||
type Action = WordlistAction;
|
||||
type Item = ChoiceItem<T>;
|
||||
|
||||
fn count(&self) -> usize {
|
||||
// Accounting for the DELETE option (+1)
|
||||
@ -70,7 +71,7 @@ impl<T: StringType> ChoiceFactory<T> for ChoiceFactoryWordlist {
|
||||
}
|
||||
}
|
||||
|
||||
fn get(&self, choice_index: usize) -> (ChoiceItem<T>, Self::Action) {
|
||||
fn get(&self, choice_index: usize) -> (Self::Item, Self::Action) {
|
||||
// Putting DELETE as the first option in both cases
|
||||
// (is a requirement for WORDS, doing it for LETTERS as well to unite it)
|
||||
if choice_index == DELETE_INDEX {
|
||||
@ -101,7 +102,7 @@ impl<T: StringType> ChoiceFactory<T> for ChoiceFactoryWordlist {
|
||||
}
|
||||
|
||||
/// Component for entering a mnemonic from a wordlist - BIP39 or SLIP39.
|
||||
pub struct WordlistEntry<T: StringType> {
|
||||
pub struct WordlistEntry<T: StringType + Clone> {
|
||||
choice_page: ChoicePage<ChoiceFactoryWordlist, T, WordlistAction>,
|
||||
chosen_letters: Child<ChangingTextLine<String<{ MAX_WORD_LENGTH + 1 }>>>,
|
||||
textbox: TextBox<MAX_WORD_LENGTH>,
|
||||
|
@ -1,18 +1,21 @@
|
||||
mod button;
|
||||
mod button_controller;
|
||||
mod common;
|
||||
mod error;
|
||||
mod hold_to_confirm;
|
||||
mod input_methods;
|
||||
mod loader;
|
||||
mod result;
|
||||
mod welcome_screen;
|
||||
|
||||
use super::{constant, theme};
|
||||
use super::{common_messages, constant, theme};
|
||||
pub use button::{
|
||||
Button, ButtonAction, ButtonActions, ButtonContent, ButtonDetails, ButtonLayout, ButtonPos,
|
||||
ButtonStyle, ButtonStyleSheet,
|
||||
};
|
||||
pub use button_controller::{ButtonController, ButtonControllerMsg};
|
||||
pub use common_messages::CancelConfirmMsg;
|
||||
pub use error::ErrorScreen;
|
||||
pub use hold_to_confirm::{HoldToConfirm, HoldToConfirmMsg};
|
||||
pub use input_methods::{
|
||||
choice::{Choice, ChoiceFactory, ChoicePage},
|
||||
@ -28,6 +31,7 @@ mod coinjoin_progress;
|
||||
mod flow;
|
||||
mod flow_pages;
|
||||
mod frame;
|
||||
#[cfg(feature = "micropython")]
|
||||
mod homescreen;
|
||||
mod page;
|
||||
mod progress;
|
||||
@ -45,6 +49,7 @@ pub use coinjoin_progress::CoinJoinProgress;
|
||||
pub use flow::Flow;
|
||||
pub use flow_pages::{FlowPages, Page};
|
||||
pub use frame::{Frame, ScrollableContent, ScrollableFrame};
|
||||
#[cfg(feature = "micropython")]
|
||||
pub use homescreen::{Homescreen, Lockscreen};
|
||||
pub use input_methods::{
|
||||
number_input::NumberInput,
|
||||
|
@ -1,33 +1,31 @@
|
||||
use crate::{
|
||||
strutil::StringType,
|
||||
ui::{
|
||||
component::{
|
||||
text::paragraphs::{ParagraphVecShort, Paragraphs},
|
||||
Child, Component, Event, EventCtx, Never, Pad,
|
||||
},
|
||||
constant::{screen, HEIGHT, WIDTH},
|
||||
display::{Color, Icon},
|
||||
geometry::{Offset, Point, Rect, CENTER},
|
||||
},
|
||||
use crate::ui::{
|
||||
component::{Child, Component, Event, EventCtx, Label, Never, Pad},
|
||||
constant::{screen, HEIGHT, WIDTH},
|
||||
display::{Color, Icon},
|
||||
geometry::{Offset, Point, Rect, CENTER},
|
||||
};
|
||||
|
||||
pub struct ResultScreen<T> {
|
||||
const MESSAGE_AREA_START: i16 = 26;
|
||||
const FOOTER_AREA_START: i16 = 40;
|
||||
const ICON_TOP: i16 = 12;
|
||||
|
||||
pub struct ResultScreen<'a> {
|
||||
bg: Pad,
|
||||
small_pad: Pad,
|
||||
fg_color: Color,
|
||||
bg_color: Color,
|
||||
icon: Icon,
|
||||
message_top: Child<Paragraphs<ParagraphVecShort<T>>>,
|
||||
message_bottom: Child<Paragraphs<ParagraphVecShort<T>>>,
|
||||
message_top: Child<Label<&'static str>>,
|
||||
message_bottom: Child<Label<&'a str>>,
|
||||
}
|
||||
|
||||
impl<T: StringType> ResultScreen<T> {
|
||||
impl<'a> ResultScreen<'a> {
|
||||
pub fn new(
|
||||
fg_color: Color,
|
||||
bg_color: Color,
|
||||
icon: Icon,
|
||||
message_top: Paragraphs<ParagraphVecShort<T>>,
|
||||
message_bottom: Paragraphs<ParagraphVecShort<T>>,
|
||||
title: Label<&'static str>,
|
||||
content: Label<&'a str>,
|
||||
complete_draw: bool,
|
||||
) -> Self {
|
||||
let mut instance = Self {
|
||||
@ -36,8 +34,8 @@ impl<T: StringType> ResultScreen<T> {
|
||||
fg_color,
|
||||
bg_color,
|
||||
icon,
|
||||
message_top: Child::new(message_top),
|
||||
message_bottom: Child::new(message_bottom),
|
||||
message_top: Child::new(title),
|
||||
message_bottom: Child::new(content),
|
||||
};
|
||||
|
||||
if complete_draw {
|
||||
@ -49,17 +47,18 @@ impl<T: StringType> ResultScreen<T> {
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: StringType> Component for ResultScreen<T> {
|
||||
impl<'a> Component for ResultScreen<'a> {
|
||||
type Msg = Never;
|
||||
|
||||
fn place(&mut self, bounds: Rect) -> Rect {
|
||||
self.bg
|
||||
.place(Rect::new(Point::new(0, 0), Point::new(WIDTH, HEIGHT)));
|
||||
self.bg.place(bounds);
|
||||
|
||||
self.message_top
|
||||
.place(Rect::new(Point::new(0, 26), Point::new(WIDTH, 40)));
|
||||
self.message_top.place(Rect::new(
|
||||
Point::new(0, MESSAGE_AREA_START),
|
||||
Point::new(WIDTH, FOOTER_AREA_START),
|
||||
));
|
||||
|
||||
let bottom_area = Rect::new(Point::new(0, 40), Point::new(WIDTH, HEIGHT));
|
||||
let bottom_area = Rect::new(Point::new(0, FOOTER_AREA_START), Point::new(WIDTH, HEIGHT));
|
||||
|
||||
self.small_pad.place(bottom_area);
|
||||
self.message_bottom.place(bottom_area);
|
||||
@ -76,7 +75,7 @@ impl<T: StringType> Component for ResultScreen<T> {
|
||||
self.small_pad.paint();
|
||||
|
||||
self.icon.draw(
|
||||
screen().top_center() + Offset::y(12),
|
||||
screen().top_center() + Offset::y(ICON_TOP),
|
||||
CENTER,
|
||||
self.fg_color,
|
||||
self.bg_color,
|
||||
|
@ -12,7 +12,7 @@ use crate::{
|
||||
|
||||
use heapless::{String, Vec};
|
||||
|
||||
use super::{common::display, scrollbar::SCROLLBAR_SPACE, theme, title::Title, ScrollBar};
|
||||
use super::{common::display_left, scrollbar::SCROLLBAR_SPACE, theme, title::Title, ScrollBar};
|
||||
|
||||
const WORDS_PER_PAGE: usize = 3;
|
||||
const EXTRA_LINE_HEIGHT: i16 = 2;
|
||||
@ -140,8 +140,8 @@ where
|
||||
}
|
||||
let word = &self.share_words[index];
|
||||
let baseline = self.area.top_left() + Offset::new(NUMBER_X_OFFSET, y_offset);
|
||||
display(baseline, &inttostr!(index as u8 + 1), NUMBER_FONT);
|
||||
display(baseline + Offset::x(NUMBER_WORD_OFFSET), &word, WORD_FONT);
|
||||
display_left(baseline, &inttostr!(index as u8 + 1), NUMBER_FONT);
|
||||
display_left(baseline + Offset::x(NUMBER_WORD_OFFSET), &word, WORD_FONT);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -14,7 +14,9 @@ pub const MODEL_NAME: &str = "Trezor Model R";
|
||||
pub const fn size() -> Offset {
|
||||
Offset::new(WIDTH, HEIGHT)
|
||||
}
|
||||
pub const SIZE: Offset = size();
|
||||
|
||||
pub const fn screen() -> Rect {
|
||||
Rect::from_top_left_and_size(Point::zero(), size())
|
||||
Rect::from_top_left_and_size(Point::zero(), SIZE)
|
||||
}
|
||||
pub const SCREEN: Rect = screen();
|
||||
|
@ -45,7 +45,7 @@ use crate::{
|
||||
|
||||
use super::{
|
||||
component::{
|
||||
AddressDetails, ButtonActions, ButtonDetails, ButtonLayout, ButtonPage,
|
||||
AddressDetails, ButtonActions, ButtonDetails, ButtonLayout, ButtonPage, CancelConfirmMsg,
|
||||
CancelInfoConfirmMsg, CoinJoinProgress, Flow, FlowPages, Frame, Homescreen, Lockscreen,
|
||||
NumberInput, Page, PassphraseEntry, PinEntry, Progress, ScrollableContent, ScrollableFrame,
|
||||
ShareWords, ShowMore, SimpleChoice, WelcomeScreen, WordlistEntry, WordlistType,
|
||||
@ -53,11 +53,6 @@ use super::{
|
||||
constant, theme,
|
||||
};
|
||||
|
||||
pub enum CancelConfirmMsg {
|
||||
Cancelled,
|
||||
Confirmed,
|
||||
}
|
||||
|
||||
impl From<CancelConfirmMsg> for Obj {
|
||||
fn from(value: CancelConfirmMsg) -> Self {
|
||||
match value {
|
||||
|
@ -1,5 +1,6 @@
|
||||
#[cfg(feature = "bootloader")]
|
||||
pub mod bootloader;
|
||||
pub mod common_messages;
|
||||
pub mod component;
|
||||
pub mod constant;
|
||||
#[cfg(feature = "micropython")]
|
||||
|
BIN
core/embed/rust/src/ui/model_tr/res/alert.toif
Normal file
BIN
core/embed/rust/src/ui/model_tr/res/alert.toif
Normal file
Binary file not shown.
BIN
core/embed/rust/src/ui/model_tr/res/bld_header_warn.toif
Normal file
BIN
core/embed/rust/src/ui/model_tr/res/bld_header_warn.toif
Normal file
Binary file not shown.
BIN
core/embed/rust/src/ui/model_tr/res/download.toif
Normal file
BIN
core/embed/rust/src/ui/model_tr/res/download.toif
Normal file
Binary file not shown.
BIN
core/embed/rust/src/ui/model_tr/res/exit.toif
Normal file
BIN
core/embed/rust/src/ui/model_tr/res/exit.toif
Normal file
Binary file not shown.
BIN
core/embed/rust/src/ui/model_tr/res/logo_22_33_empty.toif
Normal file
BIN
core/embed/rust/src/ui/model_tr/res/logo_22_33_empty.toif
Normal file
Binary file not shown.
BIN
core/embed/rust/src/ui/model_tr/res/redo.toif
Normal file
BIN
core/embed/rust/src/ui/model_tr/res/redo.toif
Normal file
Binary file not shown.
BIN
core/embed/rust/src/ui/model_tr/res/spinner.toif
Normal file
BIN
core/embed/rust/src/ui/model_tr/res/spinner.toif
Normal file
Binary file not shown.
BIN
core/embed/rust/src/ui/model_tr/res/trash.toif
Normal file
BIN
core/embed/rust/src/ui/model_tr/res/trash.toif
Normal file
Binary file not shown.
@ -1,18 +1,8 @@
|
||||
#[cfg(feature = "micropython")]
|
||||
use crate::micropython::buffer::StrBuffer;
|
||||
use crate::ui::{
|
||||
component::{
|
||||
text::paragraphs::{Paragraph, ParagraphVecShort, Paragraphs, VecExt},
|
||||
Component,
|
||||
},
|
||||
geometry::LinearPlacement,
|
||||
};
|
||||
use crate::ui::component::base::Component;
|
||||
|
||||
use super::{
|
||||
component::ResultScreen,
|
||||
constant,
|
||||
theme::{BLACK, ICON_FAIL, TEXT_BOLD, TEXT_NORMAL, WHITE},
|
||||
};
|
||||
use super::{component::ErrorScreen, constant};
|
||||
|
||||
#[cfg(not(feature = "micropython"))]
|
||||
// SAFETY: Actually safe but see below
|
||||
@ -33,21 +23,7 @@ pub fn screen_fatal_error(title: &str, msg: &str, footer: &str) {
|
||||
let msg = unsafe { get_str(msg) };
|
||||
let footer = unsafe { get_str(footer) };
|
||||
|
||||
let mut messages = ParagraphVecShort::new();
|
||||
|
||||
messages.add(Paragraph::new(&TEXT_BOLD, title).centered());
|
||||
messages.add(Paragraph::new(&TEXT_NORMAL, msg).centered());
|
||||
let m_top =
|
||||
Paragraphs::new(messages).with_placement(LinearPlacement::vertical().align_at_center());
|
||||
|
||||
let mut messages = ParagraphVecShort::new();
|
||||
|
||||
messages.add(Paragraph::new(&TEXT_BOLD, footer).centered());
|
||||
|
||||
let m_bottom =
|
||||
Paragraphs::new(messages).with_placement(LinearPlacement::vertical().align_at_center());
|
||||
|
||||
let mut frame = ResultScreen::new(WHITE, BLACK, ICON_FAIL, m_top, m_bottom, true);
|
||||
let mut frame = ErrorScreen::new(title, msg, footer);
|
||||
frame.place(constant::screen());
|
||||
frame.paint();
|
||||
}
|
||||
|
@ -44,7 +44,7 @@ pub fn textstyle_number_bold_or_mono(num: i32) -> &'static TextStyle {
|
||||
}
|
||||
|
||||
// BLD icons
|
||||
pub const LOGO_EMPTY: &[u8] = include_res!("model_tr/res/trezor_empty.toif");
|
||||
include_icon!(LOGO_EMPTY, "model_tr/res/trezor_empty.toif");
|
||||
include_icon!(ICON_FAIL, "model_tr/res/fail.toif");
|
||||
|
||||
// Firmware icons
|
||||
@ -58,6 +58,10 @@ include_icon!(ICON_ARROW_DOWN, "model_tr/res/arrow_down.toif"); // 10*6
|
||||
include_icon!(ICON_ARROW_BACK_UP, "model_tr/res/arrow_back_up.toif"); // 8*8
|
||||
include_icon!(ICON_BIN, "model_tr/res/bin.toif"); // 10*10
|
||||
include_icon!(ICON_CANCEL, "model_tr/res/cancel_no_outline.toif"); // 8*8
|
||||
include_icon!(
|
||||
ICON_CANCEL_INVERTED,
|
||||
"model_tr/res/cancel_no_outline_inverted.toif"
|
||||
); // 8*8
|
||||
include_icon!(ICON_DELETE, "model_tr/res/delete.toif"); // 10*7
|
||||
include_icon!(ICON_EYE, "model_tr/res/eye_round.toif"); // 12*7
|
||||
include_icon!(ICON_LOCK, "model_tr/res/lock.toif"); // 10*10
|
||||
@ -69,6 +73,7 @@ include_icon!(ICON_SUCCESS, "model_tr/res/success.toif");
|
||||
include_icon!(ICON_TICK, "model_tr/res/tick.toif"); // 8*6
|
||||
include_icon!(ICON_TICK_FAT, "model_tr/res/tick_fat.toif"); // 8*6
|
||||
include_icon!(ICON_WARNING, "model_tr/res/warning.toif"); // 12*12
|
||||
include_icon!(ICON_WARN_TITLE, "model_tr/res/bld_header_warn.toif");
|
||||
|
||||
// checklist settings
|
||||
pub const CHECKLIST_SPACING: i16 = 5;
|
||||
@ -83,6 +88,7 @@ pub const BUTTON_CONTENT_HEIGHT: i16 = 7;
|
||||
pub const BUTTON_OUTLINE: i16 = 3;
|
||||
pub const BUTTON_ARMS: i16 = 2;
|
||||
pub const BUTTON_HEIGHT: i16 = BUTTON_CONTENT_HEIGHT + 2 * BUTTON_OUTLINE;
|
||||
pub const TITLE_AREA_HEIGHT: i16 = 12;
|
||||
|
||||
// How many pixels should be between text and icons.
|
||||
pub const ELLIPSIS_ICON_MARGIN: i16 = 4;
|
||||
|
@ -1,5 +1,3 @@
|
||||
#[cfg(not(feature = "bootloader"))]
|
||||
use crate::ui::display;
|
||||
#[cfg(feature = "bootloader")]
|
||||
use crate::ui::model_tt::bootloader::theme::DEVICE_NAME;
|
||||
use crate::ui::{
|
||||
@ -14,6 +12,8 @@ const TEXT_BOTTOM_MARGIN: i16 = 24; // matching the homescreen label margin
|
||||
const ICON_TOP_MARGIN: i16 = 48;
|
||||
#[cfg(not(feature = "bootloader"))]
|
||||
const MODEL_NAME_FONT: display::Font = display::Font::DEMIBOLD;
|
||||
#[cfg(not(feature = "bootloader"))]
|
||||
use crate::ui::display;
|
||||
|
||||
pub struct WelcomeScreen {
|
||||
area: Rect,
|
||||
|
@ -14,7 +14,9 @@ pub const MODEL_NAME: &str = "Trezor Model T";
|
||||
pub const fn size() -> Offset {
|
||||
Offset::new(WIDTH, HEIGHT)
|
||||
}
|
||||
pub const SIZE: Offset = size();
|
||||
|
||||
pub const fn screen() -> Rect {
|
||||
Rect::from_top_left_and_size(Point::zero(), size())
|
||||
Rect::from_top_left_and_size(Point::zero(), SIZE)
|
||||
}
|
||||
pub const SCREEN: Rect = screen();
|
||||
|
@ -132,6 +132,21 @@ static inline void spi_send(const uint8_t *data, int len) {
|
||||
}
|
||||
}
|
||||
|
||||
void display_handle_init(void) {
|
||||
spi_handle.Instance = OLED_SPI;
|
||||
spi_handle.Init.BaudRatePrescaler = SPI_BAUDRATEPRESCALER_8;
|
||||
spi_handle.Init.Direction = SPI_DIRECTION_2LINES;
|
||||
spi_handle.Init.CLKPhase = SPI_PHASE_1EDGE;
|
||||
spi_handle.Init.CLKPolarity = SPI_POLARITY_LOW;
|
||||
spi_handle.Init.CRCCalculation = SPI_CRCCALCULATION_DISABLE;
|
||||
spi_handle.Init.CRCPolynomial = 7;
|
||||
spi_handle.Init.DataSize = SPI_DATASIZE_8BIT;
|
||||
spi_handle.Init.FirstBit = SPI_FIRSTBIT_MSB;
|
||||
spi_handle.Init.NSS = SPI_NSS_HARD_OUTPUT;
|
||||
spi_handle.Init.TIMode = SPI_TIMODE_DISABLE;
|
||||
spi_handle.Init.Mode = SPI_MODE_MASTER;
|
||||
}
|
||||
|
||||
void display_init(void) {
|
||||
OLED_DC_CLK_ENA();
|
||||
OLED_CS_CLK_ENA();
|
||||
@ -167,18 +182,7 @@ void display_init(void) {
|
||||
GPIO_InitStructure.Pin = OLED_SPI_MOSI_PIN;
|
||||
HAL_GPIO_Init(OLED_SPI_MOSI_PORT, &GPIO_InitStructure);
|
||||
|
||||
spi_handle.Instance = OLED_SPI;
|
||||
spi_handle.Init.BaudRatePrescaler = SPI_BAUDRATEPRESCALER_8;
|
||||
spi_handle.Init.Direction = SPI_DIRECTION_2LINES;
|
||||
spi_handle.Init.CLKPhase = SPI_PHASE_1EDGE;
|
||||
spi_handle.Init.CLKPolarity = SPI_POLARITY_LOW;
|
||||
spi_handle.Init.CRCCalculation = SPI_CRCCALCULATION_DISABLE;
|
||||
spi_handle.Init.CRCPolynomial = 7;
|
||||
spi_handle.Init.DataSize = SPI_DATASIZE_8BIT;
|
||||
spi_handle.Init.FirstBit = SPI_FIRSTBIT_MSB;
|
||||
spi_handle.Init.NSS = SPI_NSS_HARD_OUTPUT;
|
||||
spi_handle.Init.TIMode = SPI_TIMODE_DISABLE;
|
||||
spi_handle.Init.Mode = SPI_MODE_MASTER;
|
||||
display_handle_init();
|
||||
if (HAL_OK != HAL_SPI_Init(&spi_handle)) {
|
||||
// TODO: error
|
||||
return;
|
||||
@ -230,7 +234,7 @@ void display_init(void) {
|
||||
display_refresh();
|
||||
}
|
||||
|
||||
void display_reinit(void) { display_init(); }
|
||||
void display_reinit(void) { display_handle_init(); }
|
||||
|
||||
static inline uint8_t reverse_byte(uint8_t b) {
|
||||
b = (b & 0xF0) >> 4 | (b & 0x0F) << 4;
|
||||
|
@ -18,6 +18,10 @@
|
||||
|
||||
#include "display-unix.h"
|
||||
|
||||
#ifdef TREZOR_MODEL_R
|
||||
#define USE_BUTTON 1
|
||||
#elif TREZOR_MODEL_T
|
||||
#define USE_TOUCH 1
|
||||
#endif
|
||||
|
||||
#endif //_BOARD_UNIX_H
|
||||
|
@ -92,6 +92,12 @@ uint32_t touch_is_detected(void) { return _touch_detected; }
|
||||
|
||||
#include "button.h"
|
||||
|
||||
static char last_left = 0, last_right = 0;
|
||||
|
||||
char button_state_left(void) { return last_left; }
|
||||
|
||||
char button_state_right(void) { return last_right; }
|
||||
|
||||
uint32_t button_read(void) {
|
||||
SDL_Event event;
|
||||
SDL_PumpEvents();
|
||||
@ -103,8 +109,10 @@ uint32_t button_read(void) {
|
||||
}
|
||||
switch (event.key.keysym.sym) {
|
||||
case SDLK_LEFT:
|
||||
last_left = 1;
|
||||
return BTN_EVT_DOWN | BTN_LEFT;
|
||||
case SDLK_RIGHT:
|
||||
last_right = 1;
|
||||
return BTN_EVT_DOWN | BTN_RIGHT;
|
||||
}
|
||||
break;
|
||||
@ -114,8 +122,10 @@ uint32_t button_read(void) {
|
||||
}
|
||||
switch (event.key.keysym.sym) {
|
||||
case SDLK_LEFT:
|
||||
last_left = 0;
|
||||
return BTN_EVT_UP | BTN_LEFT;
|
||||
case SDLK_RIGHT:
|
||||
last_right = 0;
|
||||
return BTN_EVT_UP | BTN_RIGHT;
|
||||
}
|
||||
break;
|
||||
@ -124,4 +134,6 @@ uint32_t button_read(void) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
void button_init(void) {}
|
||||
|
||||
#endif
|
||||
|
Loading…
Reference in New Issue
Block a user