mirror of
https://github.com/trezor/trezor-firmware.git
synced 2025-04-14 14:27:25 +00:00
feat(core): show welcome screen after device is connected
[no changelog]
This commit is contained in:
parent
5d987b2bc9
commit
acec852aa4
BIN
core/assets/logo.jpg
Normal file
BIN
core/assets/logo.jpg
Normal file
Binary file not shown.
After Width: | Height: | Size: 12 KiB |
@ -178,6 +178,7 @@ fn generate_micropython_bindings() {
|
||||
.allowlist_var("mp_type_dict")
|
||||
// fun
|
||||
.allowlist_type("mp_obj_fun_builtin_fixed_t")
|
||||
.allowlist_var("mp_type_fun_builtin_0")
|
||||
.allowlist_var("mp_type_fun_builtin_1")
|
||||
.allowlist_var("mp_type_fun_builtin_2")
|
||||
.allowlist_var("mp_type_fun_builtin_3")
|
||||
|
@ -52,6 +52,7 @@ static void _librust_qstrs(void) {
|
||||
MP_QSTR_show_group_share_success;
|
||||
MP_QSTR_show_homescreen;
|
||||
MP_QSTR_show_lockscreen;
|
||||
MP_QSTR_draw_welcome_screen;
|
||||
MP_QSTR_show_remaining_shares;
|
||||
MP_QSTR_show_share_words;
|
||||
MP_QSTR_show_progress;
|
||||
|
@ -1,3 +1,20 @@
|
||||
/// Create an object for an exported function taking no arguments.
|
||||
macro_rules! obj_fn_0 {
|
||||
($f:expr) => {{
|
||||
#[allow(unused_unsafe)]
|
||||
unsafe {
|
||||
use $crate::micropython::ffi;
|
||||
|
||||
ffi::mp_obj_fun_builtin_fixed_t {
|
||||
base: ffi::mp_obj_base_t {
|
||||
type_: &ffi::mp_type_fun_builtin_0,
|
||||
},
|
||||
fun: ffi::_mp_obj_fun_builtin_fixed_t__bindgen_ty_1 { _0: Some($f) },
|
||||
}
|
||||
}
|
||||
}};
|
||||
}
|
||||
|
||||
/// Create an object for an exported function taking 1 arg.
|
||||
macro_rules! obj_fn_1 {
|
||||
($f:expr) => {{
|
||||
|
@ -460,7 +460,8 @@ pub enum Alignment {
|
||||
pub type Alignment2D = (Alignment, Alignment);
|
||||
|
||||
pub const TOP_LEFT: Alignment2D = (Alignment::Start, Alignment::Start);
|
||||
pub const TOP_RIGHT: Alignment2D = (Alignment::Start, Alignment::End);
|
||||
pub const TOP_RIGHT: Alignment2D = (Alignment::End, Alignment::Start);
|
||||
pub const TOP_CENTER: Alignment2D = (Alignment::Center, Alignment::Start);
|
||||
pub const CENTER: Alignment2D = (Alignment::Center, Alignment::Center);
|
||||
pub const BOTTOM_LEFT: Alignment2D = (Alignment::Start, Alignment::End);
|
||||
pub const BOTTOM_RIGHT: Alignment2D = (Alignment::End, Alignment::End);
|
||||
|
@ -13,6 +13,7 @@ mod page;
|
||||
mod progress;
|
||||
mod scroll;
|
||||
mod swipe;
|
||||
mod welcome_screen;
|
||||
|
||||
pub use button::{
|
||||
Button, ButtonContent, ButtonMsg, ButtonStyle, ButtonStyleSheet, CancelConfirmMsg,
|
||||
@ -37,5 +38,6 @@ pub use page::{SwipeHoldPage, SwipePage};
|
||||
pub use progress::Progress;
|
||||
pub use scroll::ScrollBar;
|
||||
pub use swipe::{Swipe, SwipeDirection};
|
||||
pub use welcome_screen::WelcomeScreen;
|
||||
|
||||
use super::theme;
|
||||
|
59
core/embed/rust/src/ui/model_tt/component/welcome_screen.rs
Normal file
59
core/embed/rust/src/ui/model_tt/component/welcome_screen.rs
Normal file
@ -0,0 +1,59 @@
|
||||
use crate::ui::{
|
||||
component::{Component, Event, EventCtx, Never},
|
||||
display::{self, Icon},
|
||||
geometry::{self, Offset, Rect},
|
||||
model_tt::theme,
|
||||
};
|
||||
|
||||
const TEXT_BOTTOM_MARGIN: i16 = 24; // matching the homescreen label margin
|
||||
const ICON_TOP_MARGIN: i16 = 62;
|
||||
const MODEL_NAME: &str = "Trezor Model T";
|
||||
const MODEL_NAME_FONT: display::Font = display::Font::DEMIBOLD;
|
||||
|
||||
pub struct WelcomeScreen {
|
||||
area: Rect,
|
||||
}
|
||||
|
||||
impl WelcomeScreen {
|
||||
pub fn new() -> Self {
|
||||
Self { area: Rect::zero() }
|
||||
}
|
||||
}
|
||||
|
||||
impl Component for WelcomeScreen {
|
||||
type Msg = Never;
|
||||
|
||||
fn place(&mut self, bounds: Rect) -> Rect {
|
||||
self.area = bounds;
|
||||
self.area
|
||||
}
|
||||
|
||||
fn event(&mut self, _ctx: &mut EventCtx, _event: Event) -> Option<Self::Msg> {
|
||||
None
|
||||
}
|
||||
|
||||
fn paint(&mut self) {
|
||||
display::text_center(
|
||||
self.area.bottom_center() - Offset::y(TEXT_BOTTOM_MARGIN),
|
||||
MODEL_NAME,
|
||||
MODEL_NAME_FONT,
|
||||
theme::FG,
|
||||
theme::BG,
|
||||
);
|
||||
Icon::new(theme::ICON_LOGO).draw(
|
||||
self.area.top_center() + Offset::y(ICON_TOP_MARGIN),
|
||||
geometry::TOP_CENTER,
|
||||
theme::FG,
|
||||
theme::BG,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "ui_debug")]
|
||||
impl crate::trace::Trace for WelcomeScreen {
|
||||
fn trace(&self, t: &mut dyn crate::trace::Tracer) {
|
||||
t.open("WelcomeScreen");
|
||||
t.string(MODEL_NAME);
|
||||
t.close();
|
||||
}
|
||||
}
|
@ -30,7 +30,7 @@ use crate::{
|
||||
},
|
||||
Border, Component, Empty, FormattedText, Timeout, TimeoutMsg,
|
||||
},
|
||||
display::{tjpgd::jpeg_info, toif::Icon},
|
||||
display::{self, tjpgd::jpeg_info, toif::Icon},
|
||||
geometry,
|
||||
layout::{
|
||||
obj::{ComponentMsgObj, LayoutObj},
|
||||
@ -51,8 +51,9 @@ use super::{
|
||||
MnemonicKeyboardMsg, NotificationFrame, NumberInputDialog, NumberInputDialogMsg,
|
||||
PassphraseKeyboard, PassphraseKeyboardMsg, PinKeyboard, PinKeyboardMsg, Progress,
|
||||
SelectWordCount, SelectWordCountMsg, SelectWordMsg, Slip39Input, SwipeHoldPage, SwipePage,
|
||||
WelcomeScreen,
|
||||
},
|
||||
theme,
|
||||
constant, theme,
|
||||
};
|
||||
|
||||
impl TryFrom<CancelConfirmMsg> for Obj {
|
||||
@ -1343,6 +1344,16 @@ extern "C" fn new_show_busyscreen(n_args: usize, args: *const Obj, kwargs: *mut
|
||||
unsafe { util::try_with_args_and_kwargs(n_args, args, kwargs, block) }
|
||||
}
|
||||
|
||||
extern "C" fn draw_welcome_screen() -> Obj {
|
||||
// No need of util::try_or_raise, this does not allocate
|
||||
let mut screen = WelcomeScreen::new();
|
||||
screen.place(constant::screen());
|
||||
display::sync();
|
||||
screen.paint();
|
||||
display::set_backlight(150); // BACKLIGHT_NORMAL
|
||||
Obj::const_none()
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
pub static mp_module_trezorui2: Module = obj_module! {
|
||||
Qstr::MP_QSTR___name__ => Qstr::MP_QSTR_trezorui2.to_obj(),
|
||||
@ -1708,6 +1719,10 @@ pub static mp_module_trezorui2: Module = obj_module! {
|
||||
/// ) -> CANCELLED:
|
||||
/// """Homescreen used for indicating coinjoin in progress."""
|
||||
Qstr::MP_QSTR_show_busyscreen => obj_fn_kw!(0, new_show_busyscreen).as_obj(),
|
||||
|
||||
/// def draw_welcome_screen() -> None:
|
||||
/// """Show logo icon with the model name at the bottom and return."""
|
||||
Qstr::MP_QSTR_draw_welcome_screen => obj_fn_0!(draw_welcome_screen).as_obj(),
|
||||
};
|
||||
|
||||
#[cfg(test)]
|
||||
|
BIN
core/embed/rust/src/ui/model_tt/res/logo.toif
Normal file
BIN
core/embed/rust/src/ui/model_tt/res/logo.toif
Normal file
Binary file not shown.
@ -63,6 +63,7 @@ pub const ICON_MAGIC: &[u8] = include_res!("model_tt/res/magic.toif");
|
||||
pub const ICON_LIST_CURRENT: &[u8] = include_res!("model_tt/res/current.toif");
|
||||
pub const ICON_LIST_CHECK: &[u8] = include_res!("model_tt/res/check.toif");
|
||||
pub const ICON_LOCK: &[u8] = include_res!("model_tt/res/lock.toif");
|
||||
pub const ICON_LOGO: &[u8] = include_res!("model_tt/res/logo.toif");
|
||||
|
||||
// Large, three-color icons.
|
||||
pub const WARN_COLOR: Color = YELLOW;
|
||||
|
@ -423,3 +423,8 @@ def show_busyscreen(
|
||||
skip_first_paint: bool,
|
||||
) -> CANCELLED:
|
||||
"""Homescreen used for indicating coinjoin in progress."""
|
||||
|
||||
|
||||
# rust/src/ui/model_tt/layout.rs
|
||||
def draw_welcome_screen() -> None:
|
||||
"""Show logo icon with the model name at the bottom and return."""
|
||||
|
@ -1,21 +1,58 @@
|
||||
# isort:skip_file
|
||||
|
||||
import trezorui2
|
||||
import utime
|
||||
|
||||
# Showing welcome screen as soon as possible
|
||||
# (display is also prepared on that occasion).
|
||||
# Remembering time to control how long we show it.
|
||||
trezorui2.draw_welcome_screen()
|
||||
welcome_screen_start_ms = utime.ticks_ms()
|
||||
|
||||
import storage
|
||||
import storage.device
|
||||
from trezor import config, log, loop, ui, utils, wire
|
||||
from trezor.pin import show_pin_timeout
|
||||
from trezor.pin import (
|
||||
allow_all_loader_messages,
|
||||
ignore_nonpin_loader_messages,
|
||||
show_pin_timeout,
|
||||
)
|
||||
from trezor.ui.layouts.homescreen import Lockscreen
|
||||
|
||||
from apps.common.request_pin import can_lock_device, verify_user_pin
|
||||
|
||||
_WELCOME_SCREEN_MS = 1500 # how long do we want to show welcome screen (minimum)
|
||||
|
||||
|
||||
def enforce_welcome_screen_duration() -> None:
|
||||
"""Make sure we will show the welcome screen for appropriate amount of time."""
|
||||
# Not wasting the time in debug builds (saves time during emulator debugging)
|
||||
if __debug__:
|
||||
return
|
||||
while utime.ticks_ms() - welcome_screen_start_ms < _WELCOME_SCREEN_MS:
|
||||
utime.sleep_ms(100)
|
||||
|
||||
|
||||
async def bootscreen() -> None:
|
||||
"""Sequence of actions to be done on boot (after device is connected).
|
||||
|
||||
We are starting with welcome_screen on the screen and want to show it
|
||||
for at least _WELCOME_SCREEN_MS before any other screen.
|
||||
|
||||
Any non-PIN loaders are ignored during this function.
|
||||
Allowing all of them before returning.
|
||||
"""
|
||||
lockscreen = Lockscreen(label=storage.device.get_label(), bootscreen=True)
|
||||
ui.display.orientation(storage.device.get_rotation())
|
||||
while True:
|
||||
try:
|
||||
if can_lock_device():
|
||||
enforce_welcome_screen_duration()
|
||||
await lockscreen
|
||||
await verify_user_pin()
|
||||
storage.init_unlocked()
|
||||
enforce_welcome_screen_duration()
|
||||
allow_all_loader_messages()
|
||||
return
|
||||
except wire.PinCancelled:
|
||||
# verify_user_pin will convert a SdCardUnavailable (in case of sd salt)
|
||||
@ -29,8 +66,9 @@ async def bootscreen() -> None:
|
||||
utils.halt(e.__class__.__name__)
|
||||
|
||||
|
||||
ui.display.backlight(ui.BACKLIGHT_NONE)
|
||||
ui.backlight_fade(ui.BACKLIGHT_NORMAL)
|
||||
# Ignoring all non-PIN messages in the boot-phase (turned off in `bootscreen()`).
|
||||
ignore_nonpin_loader_messages()
|
||||
|
||||
config.init(show_pin_timeout)
|
||||
|
||||
if __debug__ and not utils.EMULATOR:
|
||||
|
@ -10,6 +10,18 @@ _progress_layout: ProgressLayout | None = None
|
||||
_started_with_empty_loader = False
|
||||
keepalive_callback: Any = None
|
||||
|
||||
_ignore_loader_messages: tuple[str, ...] = ()
|
||||
|
||||
|
||||
def ignore_nonpin_loader_messages() -> None:
|
||||
global _ignore_loader_messages
|
||||
_ignore_loader_messages = ("Processing", "Starting up")
|
||||
|
||||
|
||||
def allow_all_loader_messages() -> None:
|
||||
global _ignore_loader_messages
|
||||
_ignore_loader_messages = ()
|
||||
|
||||
|
||||
def render_empty_loader(message: str, description: str) -> None:
|
||||
"""Render empty loader to prevent the screen appear to be frozen."""
|
||||
@ -27,9 +39,8 @@ def render_empty_loader(message: str, description: str) -> None:
|
||||
def show_pin_timeout(seconds: int, progress: int, message: str) -> bool:
|
||||
from trezor.ui.layouts import pin_progress
|
||||
|
||||
# We do not want to show the progress loader when starting the device.
|
||||
# Progress should be shown only for PIN purposes.
|
||||
if message == "Starting up":
|
||||
# Possibility to ignore certain messages - not showing loader for them
|
||||
if message in _ignore_loader_messages:
|
||||
return False
|
||||
|
||||
global _previous_seconds
|
||||
|
Loading…
Reference in New Issue
Block a user