diff --git a/core/embed/rust/librust_qstr.h b/core/embed/rust/librust_qstr.h index 80d56f5fc..d0d88024a 100644 --- a/core/embed/rust/librust_qstr.h +++ b/core/embed/rust/librust_qstr.h @@ -20,6 +20,7 @@ static void _librust_qstrs(void) { MP_QSTR_jpeg_info; MP_QSTR_jpeg_test; MP_QSTR_confirm_action; + MP_QSTR_confirm_homescreen; MP_QSTR_confirm_blob; MP_QSTR_confirm_properties; MP_QSTR_confirm_coinjoin; @@ -99,6 +100,7 @@ static void _librust_qstrs(void) { MP_QSTR_min_count; MP_QSTR_max_count; MP_QSTR_items; + MP_QSTR_image; MP_QSTR_active; MP_QSTR_info_button; MP_QSTR_time_ms; diff --git a/core/embed/rust/src/ui/component/painter.rs b/core/embed/rust/src/ui/component/painter.rs index 7a572b10d..9e337cc9d 100644 --- a/core/embed/rust/src/ui/component/painter.rs +++ b/core/embed/rust/src/ui/component/painter.rs @@ -1,7 +1,7 @@ use crate::ui::{ component::{Component, Event, EventCtx, Never}, display, - geometry::Rect, + geometry::{Offset, Rect}, }; pub struct Painter { @@ -64,3 +64,9 @@ pub fn image_painter(image: &'static [u8]) -> Painter { let f = move |area: Rect| display::image(area.center(), image); Painter::new(f) } + +pub fn jpeg_painter(image: &'static [u8], size: Offset, scale: u8) -> Painter { + let off = Offset::new(size.x / (2 << scale), size.y / (2 << scale)); + let f = move |area: Rect| display::tjpgd::jpeg(image, area.center() - off, scale); + Painter::new(f) +} diff --git a/core/embed/rust/src/ui/model_tt/layout.rs b/core/embed/rust/src/ui/model_tt/layout.rs index 5cd7eb92e..0d60ad0c5 100644 --- a/core/embed/rust/src/ui/model_tt/layout.rs +++ b/core/embed/rust/src/ui/model_tt/layout.rs @@ -1,9 +1,10 @@ use core::{cmp::Ordering, convert::TryInto}; +use cstr_core::CStr; use crate::{ error::Error, micropython::{ - buffer::StrBuffer, + buffer::{get_buffer, StrBuffer}, gc::Gc, iter::{Iter, IterBuf}, list::List, @@ -28,6 +29,7 @@ use crate::{ }, Border, Component, Empty, Timeout, TimeoutMsg, }, + display::tjpgd::jpeg_info, geometry, layout::{ obj::{ComponentMsgObj, LayoutObj}, @@ -466,6 +468,38 @@ extern "C" fn new_confirm_properties(n_args: usize, args: *const Obj, kwargs: *m unsafe { util::try_with_args_and_kwargs(n_args, args, kwargs, block) } } +extern "C" fn new_confirm_homescreen(n_args: usize, args: *const Obj, kwargs: *mut Map) -> Obj { + let block = move |_args: &[Obj], kwargs: &Map| { + let title: StrBuffer = kwargs.get(Qstr::MP_QSTR_title)?.try_into()?; + let data: Obj = kwargs.get(Qstr::MP_QSTR_image)?; + let buffer = unsafe { get_buffer(data) }; + + if let Ok(buffer) = buffer { + let info = jpeg_info(buffer); + if let Some(info) = info { + let buttons = Button::cancel_confirm_text(None, "CONFIRM"); + let obj = LayoutObj::new( + Frame::centered( + theme::label_title(), + title, + Dialog::new(painter::jpeg_painter(buffer, info.0, 1), buttons), + ) + .with_border(theme::borders()), + )?; + Ok(obj.into()) + } else { + let msg = unsafe { CStr::from_bytes_with_nul_unchecked(b"Invalid image.\0") }; + Err(Error::ValueError(msg)) + } + } else { + let msg = unsafe { CStr::from_bytes_with_nul_unchecked(b"Buffer error.\0") }; + Err(Error::ValueError(msg)) + } + }; + + unsafe { util::try_with_args_and_kwargs(n_args, args, kwargs, block) } +} + extern "C" fn new_confirm_reset_device(n_args: usize, args: *const Obj, kwargs: *mut Map) -> Obj { let block = move |_args: &[Obj], kwargs: &Map| { let title: StrBuffer = kwargs.get(Qstr::MP_QSTR_title)?.try_into()?; @@ -1316,6 +1350,15 @@ pub static mp_module_trezorui2: Module = obj_module! { /// """Confirm action.""" Qstr::MP_QSTR_confirm_action => obj_fn_kw!(0, new_confirm_action).as_obj(), + + /// def confirm_homescreen( + /// *, + /// title: str, + /// image: bytes, + /// ) -> object: + /// """Confirm homescreen.""" + Qstr::MP_QSTR_confirm_homescreen => obj_fn_kw!(0, new_confirm_homescreen).as_obj(), + /// def confirm_blob( /// *, /// title: str, diff --git a/core/mocks/generated/trezorui2.pyi b/core/mocks/generated/trezorui2.pyi index 33bfa2729..bb1f4b289 100644 --- a/core/mocks/generated/trezorui2.pyi +++ b/core/mocks/generated/trezorui2.pyi @@ -116,6 +116,15 @@ def confirm_action( """Confirm action.""" +# rust/src/ui/model_tt/layout.rs +def confirm_homescreen( + *, + title: str, + image: bytes, +) -> object: + """Confirm homescreen.""" + + # rust/src/ui/model_tt/layout.rs def confirm_blob( *, diff --git a/core/src/apps/management/apply_settings.py b/core/src/apps/management/apply_settings.py index 36a93a851..38b45cb2b 100644 --- a/core/src/apps/management/apply_settings.py +++ b/core/src/apps/management/apply_settings.py @@ -1,7 +1,7 @@ from typing import TYPE_CHECKING from trezor.enums import ButtonRequestType -from trezor.ui.layouts import confirm_action +from trezor.ui.layouts import confirm_action, confirm_homescreen from trezor.wire import DataError if TYPE_CHECKING: @@ -73,7 +73,7 @@ async def apply_settings(ctx: Context, msg: ApplySettings) -> Success: if homescreen is not None: _validate_homescreen(homescreen) - await _require_confirm_change_homescreen(ctx) + await _require_confirm_change_homescreen(ctx, homescreen) try: storage_device.set_homescreen(homescreen) except ValueError: @@ -122,14 +122,22 @@ async def apply_settings(ctx: Context, msg: ApplySettings) -> Success: return Success(message="Settings applied") -async def _require_confirm_change_homescreen(ctx: GenericContext) -> None: - await confirm_action( - ctx, - "set_homescreen", - "Set homescreen", - description="Do you really want to change the homescreen image?", - br_code=BRT_PROTECT_CALL, - ) +async def _require_confirm_change_homescreen( + ctx: GenericContext, homescreen: bytes +) -> None: + if homescreen == b"": + await confirm_action( + ctx, + "set_homescreen", + "Set homescreen", + description="Do you really want to set default homescreen image?", + br_code=BRT_PROTECT_CALL, + ) + else: + await confirm_homescreen( + ctx, + homescreen, + ) async def _require_confirm_change_label(ctx: GenericContext, label: str) -> None: diff --git a/core/src/trezor/ui/layouts/tt_v2/__init__.py b/core/src/trezor/ui/layouts/tt_v2/__init__.py index 4fd2f2cae..3d8608cbc 100644 --- a/core/src/trezor/ui/layouts/tt_v2/__init__.py +++ b/core/src/trezor/ui/layouts/tt_v2/__init__.py @@ -338,6 +338,25 @@ async def confirm_path_warning( ) +async def confirm_homescreen( + ctx: GenericContext, + image: bytes, +) -> None: + await raise_if_not_confirmed( + interact( + ctx, + RustLayout( + trezorui2.confirm_homescreen( + title="SET HOMESCREEN", + image=image, + ) + ), + "set_homesreen", + ButtonRequestType.ProtectCall, + ) + ) + + def _show_xpub(xpub: str, title: str, cancel: str | None) -> ui.Layout: content = RustLayout( trezorui2.confirm_blob(