1
0
mirror of https://github.com/trezor/trezor-firmware.git synced 2024-12-01 20:08:26 +00:00

feat(core): introduce trezorui_api

The new module is the place for type definitions of the interface
between rust and micropython world.

The goal is to replace `trezorui2` with `trezorui_api` without
duplicated definitions.
This commit is contained in:
obrusvit 2024-10-18 15:11:58 +02:00
parent c44f901a97
commit 99bb1965eb
30 changed files with 316 additions and 403 deletions

View File

@ -165,6 +165,7 @@
#define MICROPY_PY_TREZORPROTO (1)
#define MICROPY_PY_TREZORTRANSLATE (1)
#define MICROPY_PY_TREZORUI2 (1)
#define MICROPY_PY_TREZORUI_API (1)
#ifdef SYSTEM_VIEW
#define MP_PLAT_PRINT_STRN(str, len) segger_print(str, len)

View File

@ -214,6 +214,7 @@ extern const struct _mp_print_t mp_stderr_print;
#define MICROPY_PY_TREZORPROTO (1)
#define MICROPY_PY_TREZORTRANSLATE (1)
#define MICROPY_PY_TREZORUI2 (1)
#define MICROPY_PY_TREZORUI_API (1)
#define MP_STATE_PORT MP_STATE_VM

View File

@ -9,6 +9,7 @@ mp_obj_t protobuf_debug_msg_def_type();
extern mp_obj_module_t mp_module_trezorproto;
extern mp_obj_module_t mp_module_trezorui2;
extern mp_obj_module_t mp_module_trezorui_api;
extern mp_obj_module_t mp_module_trezortranslate;
#ifdef TREZOR_EMULATOR

View File

@ -703,6 +703,7 @@ static void _librust_qstrs(void) {
MP_QSTR_trace;
MP_QSTR_trezorproto;
MP_QSTR_trezorui2;
MP_QSTR_trezorui_api;
MP_QSTR_tutorial;
MP_QSTR_tutorial__continue;
MP_QSTR_tutorial__did_you_know;

View File

@ -0,0 +1,133 @@
use crate::micropython::map::Map;
use crate::ui::layout::obj::ATTACH_TYPE_OBJ;
use crate::ui::layout::base::LAYOUT_STATE;
use crate::ui::backlight::BACKLIGHT_LEVELS_OBJ;
use crate::{
micropython::{macros::obj_module, module::Module, qstr::Qstr},
ui::layout::result::{CANCELLED, CONFIRMED, INFO},
};
#[no_mangle]
pub static mp_module_trezorui_api: Module = obj_module! {
/// from trezor import utils
///
/// T = TypeVar("T")
///
/// class LayoutObj(Generic[T]):
/// """Representation of a Rust-based layout object.
/// see `trezor::ui::layout::obj::LayoutObj`.
/// """
///
/// def attach_timer_fn(self, fn: Callable[[int, int], None], attach_type: AttachType | None) -> LayoutState | None:
/// """Attach a timer setter function.
///
/// The layout object can call the timer setter with two arguments,
/// `token` and `duration_ms`. When `duration_ms` elapses, the layout object
/// expects a callback to `self.timer(token)`.
/// """
///
/// if utils.USE_TOUCH:
/// def touch_event(self, event: int, x: int, y: int) -> LayoutState | None:
/// """Receive a touch event `event` at coordinates `x`, `y`."""
///
/// if utils.USE_BUTTON:
/// def button_event(self, event: int, button: int) -> LayoutState | None:
/// """Receive a button event `event` for button `button`."""
///
/// def progress_event(self, value: int, description: str) -> LayoutState | None:
/// """Receive a progress event."""
///
/// def usb_event(self, connected: bool) -> LayoutState | None:
/// """Receive a USB connect/disconnect event."""
///
/// def timer(self, token: int) -> LayoutState | None:
/// """Callback for the timer set by `attach_timer_fn`.
///
/// This function should be called by the executor after the corresponding
/// duration elapses.
/// """
///
/// def paint(self) -> bool:
/// """Paint the layout object on screen.
///
/// Will only paint updated parts of the layout as required.
/// Returns True if any painting actually happened.
/// """
///
/// def request_complete_repaint(self) -> None:
/// """Request a complete repaint of the screen.
///
/// Does not repaint the screen, a subsequent call to `paint()` is required.
/// """
///
/// if __debug__:
/// def trace(self, tracer: Callable[[str], None]) -> None:
/// """Generate a JSON trace of the layout object.
///
/// The JSON can be emitted as a sequence of calls to `tracer`, each of
/// which is not necessarily a valid JSON chunk. The caller must
/// reassemble the chunks to get a sensible result.
/// """
///
/// def bounds(self) -> None:
/// """Paint bounds of individual components on screen."""
///
/// def page_count(self) -> int:
/// """Return the number of pages in the layout object."""
///
/// def button_request(self) -> tuple[int, str] | None:
/// """Return (code, type) of button request made during the last event or timer pass."""
///
/// def get_transition_out(self) -> AttachType:
/// """Return the transition type."""
///
/// def return_value(self) -> T:
/// """Retrieve the return value of the layout object."""
///
/// def __del__(self) -> None:
/// """Calls drop on contents of the root component."""
///
/// class UiResult:
/// """Result of a UI operation."""
/// pass
///
/// mock:global
Qstr::MP_QSTR___name__ => Qstr::MP_QSTR_trezorui_api.to_obj(),
/// CONFIRMED: UiResult
Qstr::MP_QSTR_CONFIRMED => CONFIRMED.as_obj(),
/// CANCELLED: UiResult
Qstr::MP_QSTR_CANCELLED => CANCELLED.as_obj(),
/// INFO: UiResult
Qstr::MP_QSTR_INFO => INFO.as_obj(),
/// class BacklightLevels:
/// """Backlight levels. Values dynamically update based on user settings."""
/// MAX: ClassVar[int]
/// NORMAL: ClassVar[int]
/// LOW: ClassVar[int]
/// DIM: ClassVar[int]
/// NONE: ClassVar[int]
///
/// mock:global
Qstr::MP_QSTR_BacklightLevels => BACKLIGHT_LEVELS_OBJ.as_obj(),
/// class AttachType:
/// INITIAL: ClassVar[int]
/// RESUME: ClassVar[int]
/// SWIPE_UP: ClassVar[int]
/// SWIPE_DOWN: ClassVar[int]
/// SWIPE_LEFT: ClassVar[int]
/// SWIPE_RIGHT: ClassVar[int]
Qstr::MP_QSTR_AttachType => ATTACH_TYPE_OBJ.as_obj(),
/// class LayoutState:
/// """Layout state."""
/// INITIAL: "ClassVar[LayoutState]"
/// ATTACHED: "ClassVar[LayoutState]"
/// TRANSITIONING: "ClassVar[LayoutState]"
/// DONE: "ClassVar[LayoutState]"
Qstr::MP_QSTR_LayoutState => LAYOUT_STATE.as_obj(),
};

View File

@ -2,3 +2,6 @@ pub mod common_c;
#[cfg(feature = "bootloader")]
pub mod bootloader_c;
#[cfg(feature = "micropython")]
pub mod firmware_upy;

View File

@ -24,5 +24,7 @@ pub mod model_tr;
#[cfg(feature = "model_tt")]
pub mod model_tt;
pub mod ui_features;
#[cfg(feature = "micropython")]
pub mod ui_features_fw;
pub use ui_features::UIFeaturesCommon;

View File

@ -1523,16 +1523,10 @@ extern "C" fn new_warning_hi_prio(n_args: usize, args: *const Obj, kwargs: *mut
#[no_mangle]
pub static mp_module_trezorui2: Module = obj_module! {
/// from trezor import utils
/// from trezorui_api import *
///
Qstr::MP_QSTR___name__ => Qstr::MP_QSTR_trezorui2.to_obj(),
/// CONFIRMED: UiResult
Qstr::MP_QSTR_CONFIRMED => CONFIRMED.as_obj(),
/// CANCELLED: UiResult
Qstr::MP_QSTR_CANCELLED => CANCELLED.as_obj(),
/// INFO: UiResult
Qstr::MP_QSTR_INFO => INFO.as_obj(),
/// def disable_animation(disable: bool) -> None:
/// """Disable animations, debug builds only."""
@ -2041,33 +2035,5 @@ pub static mp_module_trezorui2: Module = obj_module! {
/// cancel_text: str | None = None,
/// ) -> LayoutObj[UiResult]:
/// """Total summary and hold to confirm."""
Qstr::MP_QSTR_flow_confirm_summary => obj_fn_kw!(0, new_confirm_summary).as_obj(),
/// class BacklightLevels:
/// """Backlight levels. Values dynamically update based on user settings."""
/// MAX: ClassVar[int]
/// NORMAL: ClassVar[int]
/// LOW: ClassVar[int]
/// DIM: ClassVar[int]
/// NONE: ClassVar[int]
///
/// mock:global
Qstr::MP_QSTR_BacklightLevels => BACKLIGHT_LEVELS_OBJ.as_obj(),
/// class AttachType:
/// INITIAL: ClassVar[int]
/// RESUME: ClassVar[int]
/// SWIPE_UP: ClassVar[int]
/// SWIPE_DOWN: ClassVar[int]
/// SWIPE_LEFT: ClassVar[int]
/// SWIPE_RIGHT: ClassVar[int]
Qstr::MP_QSTR_AttachType => ATTACH_TYPE_OBJ.as_obj(),
/// class LayoutState:
/// """Layout state."""
/// INITIAL: "ClassVar[LayoutState]"
/// ATTACHED: "ClassVar[LayoutState]"
/// TRANSITIONING: "ClassVar[LayoutState]"
/// DONE: "ClassVar[LayoutState]"
Qstr::MP_QSTR_LayoutState => LAYOUT_STATE.as_obj(),
Qstr::MP_QSTR_flow_confirm_summary => obj_fn_kw!(0, flow::new_confirm_summary).as_obj(),
};

View File

@ -13,6 +13,8 @@ pub mod flow;
#[cfg(feature = "micropython")]
pub mod layout;
pub mod screens;
#[cfg(feature = "micropython")]
pub mod ui_features_fw;
pub struct ModelMercuryFeatures;

View File

@ -30,7 +30,6 @@ use crate::{
translations::TR,
trezorhal::model,
ui::{
backlight::BACKLIGHT_LEVELS_OBJ,
component::{
base::Component,
connect::Connect,
@ -47,8 +46,7 @@ use crate::{
},
geometry,
layout::{
base::LAYOUT_STATE,
obj::{ComponentMsgObj, LayoutObj, ATTACH_TYPE_OBJ},
obj::{ComponentMsgObj, LayoutObj},
result::{CANCELLED, CONFIRMED, INFO},
util::{upy_disable_animation, ConfirmBlob, RecoveryType},
},
@ -1651,17 +1649,11 @@ extern "C" fn new_show_wait_text(message: Obj) -> Obj {
#[no_mangle]
pub static mp_module_trezorui2: Module = obj_module! {
/// from trezor import utils
/// from trezorui_api import *
///
Qstr::MP_QSTR___name__ => Qstr::MP_QSTR_trezorui2.to_obj(),
/// CONFIRMED: UiResult
Qstr::MP_QSTR_CONFIRMED => CONFIRMED.as_obj(),
/// CANCELLED: UiResult
Qstr::MP_QSTR_CANCELLED => CANCELLED.as_obj(),
/// INFO: UiResult
Qstr::MP_QSTR_INFO => INFO.as_obj(),
/// def disable_animation(disable: bool) -> None:
/// """Disable animations, debug builds only."""
Qstr::MP_QSTR_disable_animation => obj_fn_1!(upy_disable_animation).as_obj(),
@ -2083,32 +2075,4 @@ pub static mp_module_trezorui2: Module = obj_module! {
/// def show_wait_text(message: str, /) -> None:
/// """Show single-line text in the middle of the screen."""
Qstr::MP_QSTR_show_wait_text => obj_fn_1!(new_show_wait_text).as_obj(),
/// class BacklightLevels:
/// """Backlight levels. Values dynamically update based on user settings."""
/// MAX: ClassVar[int]
/// NORMAL: ClassVar[int]
/// LOW: ClassVar[int]
/// DIM: ClassVar[int]
/// NONE: ClassVar[int]
///
/// mock:global
Qstr::MP_QSTR_BacklightLevels => BACKLIGHT_LEVELS_OBJ.as_obj(),
/// class AttachType:
/// INITIAL: ClassVar[int]
/// RESUME: ClassVar[int]
/// SWIPE_UP: ClassVar[int]
/// SWIPE_DOWN: ClassVar[int]
/// SWIPE_LEFT: ClassVar[int]
/// SWIPE_RIGHT: ClassVar[int]
Qstr::MP_QSTR_AttachType => ATTACH_TYPE_OBJ.as_obj(),
/// class LayoutState:
/// """Layout state."""
/// INITIAL: "ClassVar[LayoutState]"
/// ATTACHED: "ClassVar[LayoutState]"
/// TRANSITIONING: "ClassVar[LayoutState]"
/// DONE: "ClassVar[LayoutState]"
Qstr::MP_QSTR_LayoutState => LAYOUT_STATE.as_obj(),
};

View File

@ -30,7 +30,6 @@ use crate::{
translations::TR,
trezorhal::model,
ui::{
backlight::BACKLIGHT_LEVELS_OBJ,
component::{
base::ComponentExt,
connect::Connect,
@ -50,8 +49,7 @@ use crate::{
},
geometry,
layout::{
base::LAYOUT_STATE,
obj::{ComponentMsgObj, LayoutObj, ATTACH_TYPE_OBJ},
obj::{ComponentMsgObj, LayoutObj},
result::{CANCELLED, CONFIRMED, INFO},
util::{upy_disable_animation, ConfirmBlob, PropsList, RecoveryType},
},
@ -1640,98 +1638,8 @@ extern "C" fn new_show_wait_text(message: Obj) -> Obj {
#[no_mangle]
pub static mp_module_trezorui2: Module = obj_module! {
/// from trezor import utils
/// from trezorui_api import *
///
/// T = TypeVar("T")
///
/// class LayoutObj(Generic[T]):
/// """Representation of a Rust-based layout object.
/// see `trezor::ui::layout::obj::LayoutObj`.
/// """
///
/// def attach_timer_fn(self, fn: Callable[[int, int], None], attach_type: AttachType | None) -> LayoutState | None:
/// """Attach a timer setter function.
///
/// The layout object can call the timer setter with two arguments,
/// `token` and `duration_ms`. When `duration_ms` elapses, the layout object
/// expects a callback to `self.timer(token)`.
/// """
///
/// if utils.USE_TOUCH:
/// def touch_event(self, event: int, x: int, y: int) -> LayoutState | None:
/// """Receive a touch event `event` at coordinates `x`, `y`."""
///
/// if utils.USE_BUTTON:
/// def button_event(self, event: int, button: int) -> LayoutState | None:
/// """Receive a button event `event` for button `button`."""
///
/// def progress_event(self, value: int, description: str) -> LayoutState | None:
/// """Receive a progress event."""
///
/// def usb_event(self, connected: bool) -> LayoutState | None:
/// """Receive a USB connect/disconnect event."""
///
/// def timer(self, token: int) -> LayoutState | None:
/// """Callback for the timer set by `attach_timer_fn`.
///
/// This function should be called by the executor after the corresponding
/// duration elapses.
/// """
///
/// def paint(self) -> bool:
/// """Paint the layout object on screen.
///
/// Will only paint updated parts of the layout as required.
/// Returns True if any painting actually happened.
/// """
///
/// def request_complete_repaint(self) -> None:
/// """Request a complete repaint of the screen.
///
/// Does not repaint the screen, a subsequent call to `paint()` is required.
/// """
///
/// if __debug__:
/// def trace(self, tracer: Callable[[str], None]) -> None:
/// """Generate a JSON trace of the layout object.
///
/// The JSON can be emitted as a sequence of calls to `tracer`, each of
/// which is not necessarily a valid JSON chunk. The caller must
/// reassemble the chunks to get a sensible result.
/// """
///
/// def bounds(self) -> None:
/// """Paint bounds of individual components on screen."""
///
/// def page_count(self) -> int:
/// """Return the number of pages in the layout object."""
///
/// def button_request(self) -> tuple[int, str] | None:
/// """Return (code, type) of button request made during the last event or timer pass."""
///
/// def get_transition_out(self) -> AttachType:
/// """Return the transition type."""
///
/// def return_value(self) -> T:
/// """Retrieve the return value of the layout object."""
///
/// def __del__(self) -> None:
/// """Calls drop on contents of the root component."""
///
/// class UiResult:
/// """Result of an UI operation."""
/// pass
///
/// mock:global
Qstr::MP_QSTR___name__ => Qstr::MP_QSTR_trezorui2.to_obj(),
/// CONFIRMED: UiResult
Qstr::MP_QSTR_CONFIRMED => CONFIRMED.as_obj(),
/// CANCELLED: UiResult
Qstr::MP_QSTR_CANCELLED => CANCELLED.as_obj(),
/// INFO: UiResult
Qstr::MP_QSTR_INFO => INFO.as_obj(),
/// def disable_animation(disable: bool) -> None:
/// """Disable animations, debug builds only."""
@ -2170,34 +2078,6 @@ pub static mp_module_trezorui2: Module = obj_module! {
/// def show_wait_text(message: str, /) -> LayoutObj[None]:
/// """Show single-line text in the middle of the screen."""
Qstr::MP_QSTR_show_wait_text => obj_fn_1!(new_show_wait_text).as_obj(),
/// class BacklightLevels:
/// """Backlight levels. Values dynamically update based on user settings."""
/// MAX: ClassVar[int]
/// NORMAL: ClassVar[int]
/// LOW: ClassVar[int]
/// DIM: ClassVar[int]
/// NONE: ClassVar[int]
///
/// mock:global
Qstr::MP_QSTR_BacklightLevels => BACKLIGHT_LEVELS_OBJ.as_obj(),
/// class AttachType:
/// INITIAL: ClassVar[int]
/// RESUME: ClassVar[int]
/// SWIPE_UP: ClassVar[int]
/// SWIPE_DOWN: ClassVar[int]
/// SWIPE_LEFT: ClassVar[int]
/// SWIPE_RIGHT: ClassVar[int]
Qstr::MP_QSTR_AttachType => ATTACH_TYPE_OBJ.as_obj(),
/// class LayoutState:
/// """Layout state."""
/// INITIAL: "ClassVar[LayoutState]"
/// ATTACHED: "ClassVar[LayoutState]"
/// TRANSITIONING: "ClassVar[LayoutState]"
/// DONE: "ClassVar[LayoutState]"
Qstr::MP_QSTR_LayoutState => LAYOUT_STATE.as_obj(),
};
#[cfg(test)]

View File

@ -25,6 +25,10 @@
MP_REGISTER_MODULE(MP_QSTR_trezorui2, mp_module_trezorui2);
#endif
#if MICROPY_PY_TREZORUI_API
MP_REGISTER_MODULE(MP_QSTR_trezorui_api, mp_module_trezorui_api);
#endif
#if MICROPY_PY_TREZORPROTO
MP_REGISTER_MODULE(MP_QSTR_trezorproto, mp_module_trezorproto);
#endif

View File

@ -1,8 +1,6 @@
from typing import *
from trezor import utils
CONFIRMED: UiResult
CANCELLED: UiResult
INFO: UiResult
from trezorui_api import *
# rust/src/ui/model_mercury/layout.rs
@ -559,38 +557,8 @@ def flow_confirm_summary(
cancel_text: str | None = None,
) -> LayoutObj[UiResult]:
"""Total summary and hold to confirm."""
# rust/src/ui/model_mercury/layout.rs
class BacklightLevels:
"""Backlight levels. Values dynamically update based on user settings."""
MAX: ClassVar[int]
NORMAL: ClassVar[int]
LOW: ClassVar[int]
DIM: ClassVar[int]
NONE: ClassVar[int]
# rust/src/ui/model_mercury/layout.rs
class AttachType:
INITIAL: ClassVar[int]
RESUME: ClassVar[int]
SWIPE_UP: ClassVar[int]
SWIPE_DOWN: ClassVar[int]
SWIPE_LEFT: ClassVar[int]
SWIPE_RIGHT: ClassVar[int]
# rust/src/ui/model_mercury/layout.rs
class LayoutState:
"""Layout state."""
INITIAL: "ClassVar[LayoutState]"
ATTACHED: "ClassVar[LayoutState]"
TRANSITIONING: "ClassVar[LayoutState]"
DONE: "ClassVar[LayoutState]"
CONFIRMED: UiResult
CANCELLED: UiResult
INFO: UiResult
from trezor import utils
from trezorui_api import *
# rust/src/ui/model_tr/layout.rs
@ -1056,102 +1024,8 @@ def confirm_firmware_update(
# rust/src/ui/model_tr/layout.rs
def show_wait_text(message: str, /) -> None:
"""Show single-line text in the middle of the screen."""
# rust/src/ui/model_tr/layout.rs
class BacklightLevels:
"""Backlight levels. Values dynamically update based on user settings."""
MAX: ClassVar[int]
NORMAL: ClassVar[int]
LOW: ClassVar[int]
DIM: ClassVar[int]
NONE: ClassVar[int]
# rust/src/ui/model_tr/layout.rs
class AttachType:
INITIAL: ClassVar[int]
RESUME: ClassVar[int]
SWIPE_UP: ClassVar[int]
SWIPE_DOWN: ClassVar[int]
SWIPE_LEFT: ClassVar[int]
SWIPE_RIGHT: ClassVar[int]
# rust/src/ui/model_tr/layout.rs
class LayoutState:
"""Layout state."""
INITIAL: "ClassVar[LayoutState]"
ATTACHED: "ClassVar[LayoutState]"
TRANSITIONING: "ClassVar[LayoutState]"
DONE: "ClassVar[LayoutState]"
from trezor import utils
T = TypeVar("T")
# rust/src/ui/model_tt/layout.rs
class LayoutObj(Generic[T]):
"""Representation of a Rust-based layout object.
see `trezor::ui::layout::obj::LayoutObj`.
"""
def attach_timer_fn(self, fn: Callable[[int, int], None], attach_type: AttachType | None) -> LayoutState | None:
"""Attach a timer setter function.
The layout object can call the timer setter with two arguments,
`token` and `duration_ms`. When `duration_ms` elapses, the layout object
expects a callback to `self.timer(token)`.
"""
if utils.USE_TOUCH:
def touch_event(self, event: int, x: int, y: int) -> LayoutState | None:
"""Receive a touch event `event` at coordinates `x`, `y`."""
if utils.USE_BUTTON:
def button_event(self, event: int, button: int) -> LayoutState | None:
"""Receive a button event `event` for button `button`."""
def progress_event(self, value: int, description: str) -> LayoutState | None:
"""Receive a progress event."""
def usb_event(self, connected: bool) -> LayoutState | None:
"""Receive a USB connect/disconnect event."""
def timer(self, token: int) -> LayoutState | None:
"""Callback for the timer set by `attach_timer_fn`.
This function should be called by the executor after the corresponding
duration elapses.
"""
def paint(self) -> bool:
"""Paint the layout object on screen.
Will only paint updated parts of the layout as required.
Returns True if any painting actually happened.
"""
def request_complete_repaint(self) -> None:
"""Request a complete repaint of the screen.
Does not repaint the screen, a subsequent call to `paint()` is required.
"""
if __debug__:
def trace(self, tracer: Callable[[str], None]) -> None:
"""Generate a JSON trace of the layout object.
The JSON can be emitted as a sequence of calls to `tracer`, each of
which is not necessarily a valid JSON chunk. The caller must
reassemble the chunks to get a sensible result.
"""
def bounds(self) -> None:
"""Paint bounds of individual components on screen."""
def page_count(self) -> int:
"""Return the number of pages in the layout object."""
def button_request(self) -> tuple[int, str] | None:
"""Return (code, type) of button request made during the last event or timer pass."""
def get_transition_out(self) -> AttachType:
"""Return the transition type."""
def return_value(self) -> T:
"""Retrieve the return value of the layout object."""
def __del__(self) -> None:
"""Calls drop on contents of the root component."""
# rust/src/ui/model_tt/layout.rs
class UiResult:
"""Result of an UI operation."""
pass
CONFIRMED: UiResult
CANCELLED: UiResult
INFO: UiResult
from trezorui_api import *
# rust/src/ui/model_tt/layout.rs
@ -1633,32 +1507,3 @@ def confirm_firmware_update(
# rust/src/ui/model_tt/layout.rs
def show_wait_text(message: str, /) -> LayoutObj[None]:
"""Show single-line text in the middle of the screen."""
# rust/src/ui/model_tt/layout.rs
class BacklightLevels:
"""Backlight levels. Values dynamically update based on user settings."""
MAX: ClassVar[int]
NORMAL: ClassVar[int]
LOW: ClassVar[int]
DIM: ClassVar[int]
NONE: ClassVar[int]
# rust/src/ui/model_tt/layout.rs
class AttachType:
INITIAL: ClassVar[int]
RESUME: ClassVar[int]
SWIPE_UP: ClassVar[int]
SWIPE_DOWN: ClassVar[int]
SWIPE_LEFT: ClassVar[int]
SWIPE_RIGHT: ClassVar[int]
# rust/src/ui/model_tt/layout.rs
class LayoutState:
"""Layout state."""
INITIAL: "ClassVar[LayoutState]"
ATTACHED: "ClassVar[LayoutState]"
TRANSITIONING: "ClassVar[LayoutState]"
DONE: "ClassVar[LayoutState]"

View File

@ -0,0 +1,97 @@
from typing import *
from trezor import utils
T = TypeVar("T")
# rust/src/ui/api/firmware_upy.rs
class LayoutObj(Generic[T]):
"""Representation of a Rust-based layout object.
see `trezor::ui::layout::obj::LayoutObj`.
"""
def attach_timer_fn(self, fn: Callable[[int, int], None], attach_type: AttachType | None) -> LayoutState | None:
"""Attach a timer setter function.
The layout object can call the timer setter with two arguments,
`token` and `duration_ms`. When `duration_ms` elapses, the layout object
expects a callback to `self.timer(token)`.
"""
if utils.USE_TOUCH:
def touch_event(self, event: int, x: int, y: int) -> LayoutState | None:
"""Receive a touch event `event` at coordinates `x`, `y`."""
if utils.USE_BUTTON:
def button_event(self, event: int, button: int) -> LayoutState | None:
"""Receive a button event `event` for button `button`."""
def progress_event(self, value: int, description: str) -> LayoutState | None:
"""Receive a progress event."""
def usb_event(self, connected: bool) -> LayoutState | None:
"""Receive a USB connect/disconnect event."""
def timer(self, token: int) -> LayoutState | None:
"""Callback for the timer set by `attach_timer_fn`.
This function should be called by the executor after the corresponding
duration elapses.
"""
def paint(self) -> bool:
"""Paint the layout object on screen.
Will only paint updated parts of the layout as required.
Returns True if any painting actually happened.
"""
def request_complete_repaint(self) -> None:
"""Request a complete repaint of the screen.
Does not repaint the screen, a subsequent call to `paint()` is required.
"""
if __debug__:
def trace(self, tracer: Callable[[str], None]) -> None:
"""Generate a JSON trace of the layout object.
The JSON can be emitted as a sequence of calls to `tracer`, each of
which is not necessarily a valid JSON chunk. The caller must
reassemble the chunks to get a sensible result.
"""
def bounds(self) -> None:
"""Paint bounds of individual components on screen."""
def page_count(self) -> int:
"""Return the number of pages in the layout object."""
def button_request(self) -> tuple[int, str] | None:
"""Return (code, type) of button request made during the last event or timer pass."""
def get_transition_out(self) -> AttachType:
"""Return the transition type."""
def return_value(self) -> T:
"""Retrieve the return value of the layout object."""
def __del__(self) -> None:
"""Calls drop on contents of the root component."""
# rust/src/ui/api/firmware_upy.rs
class UiResult:
"""Result of a UI operation."""
pass
CONFIRMED: UiResult
CANCELLED: UiResult
INFO: UiResult
# rust/src/ui/api/firmware_upy.rs
class BacklightLevels:
"""Backlight levels. Values dynamically update based on user settings."""
MAX: ClassVar[int]
NORMAL: ClassVar[int]
LOW: ClassVar[int]
DIM: ClassVar[int]
NONE: ClassVar[int]
# rust/src/ui/api/firmware_upy.rs
class AttachType:
INITIAL: ClassVar[int]
RESUME: ClassVar[int]
SWIPE_UP: ClassVar[int]
SWIPE_DOWN: ClassVar[int]
SWIPE_LEFT: ClassVar[int]
SWIPE_RIGHT: ClassVar[int]
# rust/src/ui/api/firmware_upy.rs
class LayoutState:
"""Layout state."""
INITIAL: "ClassVar[LayoutState]"
ATTACHED: "ClassVar[LayoutState]"
TRANSITIONING: "ClassVar[LayoutState]"
DONE: "ClassVar[LayoutState]"

View File

@ -8,7 +8,7 @@ if __debug__:
from micropython import const
from typing import TYPE_CHECKING
import trezorui2
import trezorui_api
from storage import debug as storage
from trezor import io, log, loop, ui, utils, wire, workflow
from trezor.enums import DebugWaitType, MessageType
@ -195,11 +195,11 @@ if __debug__:
assert isinstance(ui.CURRENT_LAYOUT, ui.Layout)
if button == DebugButton.NO:
ui.CURRENT_LAYOUT._emit_message(trezorui2.CANCELLED)
ui.CURRENT_LAYOUT._emit_message(trezorui_api.CANCELLED)
elif button == DebugButton.YES:
ui.CURRENT_LAYOUT._emit_message(trezorui2.CONFIRMED)
ui.CURRENT_LAYOUT._emit_message(trezorui_api.CONFIRMED)
elif button == DebugButton.INFO:
ui.CURRENT_LAYOUT._emit_message(trezorui2.INFO)
ui.CURRENT_LAYOUT._emit_message(trezorui_api.INFO)
else:
raise RuntimeError("Invalid DebugButton")

View File

@ -8,12 +8,12 @@ import trezorui2
from trezor import io, log, loop, utils, wire, workflow
from trezor.messages import ButtonAck, ButtonRequest
from trezor.wire import context
from trezorui2 import AttachType, BacklightLevels, LayoutState
from trezorui_api import AttachType, BacklightLevels, LayoutState
if TYPE_CHECKING:
from typing import Any, Callable, Generator, Generic, Iterator, TypeVar
from trezorui2 import LayoutObj, UiResult # noqa: F401
from trezorui_api import LayoutObj, UiResult # noqa: F401
T = TypeVar("T", covariant=True)

View File

@ -1,6 +1,6 @@
from typing import TYPE_CHECKING
import trezorui2
import trezorui_api
from trezor import ui, workflow
from trezor.enums import ButtonRequestType
from trezor.messages import ButtonAck, ButtonRequest
@ -52,7 +52,7 @@ async def interact(
# wait for the layout result
result = await layout.get_result()
# raise an exception if the user cancelled the action
if raise_on_cancel is not None and result is trezorui2.CANCELLED:
if raise_on_cancel is not None and result is trezorui_api.CANCELLED:
raise raise_on_cancel
return result
@ -93,13 +93,13 @@ async def with_info(
first_br = next_br
if result is trezorui2.CONFIRMED:
if result is trezorui_api.CONFIRMED:
return
elif result is trezorui2.INFO:
elif result is trezorui_api.INFO:
info_result = await interact(
info_layout, next_br, br_code, raise_on_cancel=None
)
if info_layout_can_confirm and info_result is trezorui2.CONFIRMED:
if info_layout_can_confirm and info_result is trezorui_api.CONFIRMED:
return
else:
# no matter what the info layout returns, we always go back to the main layout
@ -108,5 +108,5 @@ async def with_info(
raise RuntimeError # unexpected result
def draw_simple(layout: trezorui2.LayoutObj[Any]) -> None:
def draw_simple(layout: trezorui_api.LayoutObj[Any]) -> None:
ui.Layout(layout).start()

View File

@ -2,6 +2,7 @@ from typing import TYPE_CHECKING
import storage.cache as storage_cache
import trezorui2
import trezorui_api
from trezor import TR, ui
if TYPE_CHECKING:
@ -129,7 +130,7 @@ class Busyscreen(HomescreenBase):
# Handle timeout.
result = await super().get_result()
assert result == trezorui2.CANCELLED
assert result == trezorui_api.CANCELLED
storage_cache.delete(storage_cache.APP_COMMON_BUSY_DEADLINE_MS)
set_homescreen()
return result

View File

@ -1,6 +1,7 @@
from typing import TYPE_CHECKING
import trezorui2
import trezorui_api
from trezor import TR, ui, utils
from trezor.enums import ButtonRequestType
from trezor.wire import ActionCancelled
@ -17,9 +18,9 @@ if TYPE_CHECKING:
BR_CODE_OTHER = ButtonRequestType.Other # global_import_cache
CONFIRMED = trezorui2.CONFIRMED
CANCELLED = trezorui2.CANCELLED
INFO = trezorui2.INFO
CONFIRMED = trezorui_api.CONFIRMED
CANCELLED = trezorui_api.CANCELLED
INFO = trezorui_api.INFO
def confirm_action(

View File

@ -1,4 +1,5 @@
import trezorui2
import trezorui_api
from trezor import ui
from trezor.enums import ButtonRequestType
@ -20,7 +21,7 @@ async def confirm_fido(
)
result = await interact(confirm, "confirm_fido", ButtonRequestType.Other)
if __debug__ and result is trezorui2.CONFIRMED:
if __debug__ and result is trezorui_api.CONFIRMED:
# debuglink will directly inject a CONFIRMED message which we need to handle
# by playing back a click to the Rust layout and getting out the selected number
# that way
@ -51,4 +52,4 @@ async def confirm_fido_reset() -> bool:
prompt_screen=True,
)
)
return (await confirm.get_result()) is trezorui2.CONFIRMED
return (await confirm.get_result()) is trezorui_api.CONFIRMED

View File

@ -1,15 +1,16 @@
from typing import TYPE_CHECKING
import trezorui2
import trezorui_api
from trezor import TR
from trezor.enums import ButtonRequestType, RecoveryType
from ..common import interact
from . import raise_if_not_confirmed
CONFIRMED = trezorui2.CONFIRMED # global_import_cache
CANCELLED = trezorui2.CANCELLED # global_import_cache
INFO = trezorui2.INFO # global_import_cache
CONFIRMED = trezorui_api.CONFIRMED # global_import_cache
CANCELLED = trezorui_api.CANCELLED # global_import_cache
INFO = trezorui_api.INFO # global_import_cache
if TYPE_CHECKING:
from apps.management.recovery_device.layout import RemainingSharesInfo

View File

@ -1,6 +1,7 @@
from typing import Awaitable, Callable, Sequence
import trezorui2
import trezorui_api
from trezor import TR, ui
from trezor.enums import ButtonRequestType
from trezor.wire import ActionCancelled
@ -8,7 +9,7 @@ from trezor.wire import ActionCancelled
from ..common import interact
from . import raise_if_not_confirmed, show_success
CONFIRMED = trezorui2.CONFIRMED # global_import_cache
CONFIRMED = trezorui_api.CONFIRMED # global_import_cache
def show_share_words(
@ -172,7 +173,7 @@ async def _prompt_number(
# so use the initial one
return count
if result is not trezorui2.CANCELLED:
if result is not trezorui_api.CANCELLED:
assert isinstance(result, int)
return result
else:

View File

@ -1,6 +1,7 @@
from typing import TYPE_CHECKING
import trezorui2
import trezorui_api
from trezor import TR, ui, utils
from trezor.enums import ButtonRequestType
from trezor.wire import ActionCancelled
@ -13,9 +14,9 @@ if TYPE_CHECKING:
from ..common import ExceptionType, PropertyType
CONFIRMED = trezorui2.CONFIRMED
CANCELLED = trezorui2.CANCELLED
INFO = trezorui2.INFO
CONFIRMED = trezorui_api.CONFIRMED
CANCELLED = trezorui_api.CANCELLED
INFO = trezorui_api.INFO
BR_CODE_OTHER = ButtonRequestType.Other # global_import_cache
@ -1133,7 +1134,7 @@ def error_popup(
*,
button: str = "",
timeout_ms: int = 0,
) -> trezorui2.LayoutObj[trezorui2.UiResult]:
) -> trezorui_api.LayoutObj[trezorui_api.UiResult]:
if button:
raise NotImplementedError("Button not implemented")

View File

@ -1,4 +1,5 @@
import trezorui2
import trezorui_api
from trezor import ui
from trezor.enums import ButtonRequestType
@ -24,7 +25,7 @@ async def confirm_fido(
# For the usage in device tests, assuming CONFIRMED (sent by debuglink)
# is choosing the first credential.
if __debug__ and result is trezorui2.CONFIRMED:
if __debug__ and result is trezorui_api.CONFIRMED:
return 0
raise RuntimeError # should not get here, cancellation is handled by `interact`
@ -40,4 +41,4 @@ async def confirm_fido_reset() -> bool:
verb_cancel="",
verb=TR.buttons__confirm,
)
return (await ui.Layout(confirm).get_result()) is trezorui2.CONFIRMED
return (await ui.Layout(confirm).get_result()) is trezorui_api.CONFIRMED

View File

@ -1,6 +1,7 @@
from typing import TYPE_CHECKING
import trezorui2
import trezorui_api
from trezor import TR, ui
from trezor.enums import ButtonRequestType, RecoveryType
@ -137,7 +138,7 @@ async def continue_recovery(
ButtonRequestType.RecoveryHomepage,
raise_on_cancel=None,
)
if result is trezorui2.CONFIRMED:
if result is trezorui_api.CONFIRMED:
return True
try:

View File

@ -3,11 +3,12 @@ from typing import TYPE_CHECKING
import trezorui2
from trezor import TR
from trezor.enums import ButtonRequestType
import trezorui_api
from ..common import interact, raise_if_not_confirmed
from . import confirm_action, show_success, show_warning
CONFIRMED = trezorui2.CONFIRMED # global_import_cache
CONFIRMED = trezorui_api.CONFIRMED # global_import_cache
if TYPE_CHECKING:
from typing import Awaitable, Sequence
@ -268,7 +269,7 @@ def show_intro_backup(single_share: bool, num_of_words: int | None) -> Awaitable
)
def show_warning_backup() -> Awaitable[trezorui2.UiResult]:
def show_warning_backup() -> Awaitable[trezorui_api.UiResult]:
return show_warning(
"backup_warning",
TR.words__title_remember,
@ -295,7 +296,7 @@ def show_reset_warning(
subheader: str | None = None,
button: str | None = None,
br_code: ButtonRequestType = ButtonRequestType.Warning,
) -> Awaitable[trezorui2.UiResult]:
) -> Awaitable[trezorui_api.UiResult]:
button = button or TR.buttons__try_again # def_arg
return show_warning(

View File

@ -1,6 +1,7 @@
from typing import TYPE_CHECKING
import trezorui2
import trezorui_api
from trezor import TR, ui, utils
from trezor.enums import ButtonRequestType
from trezor.wire import ActionCancelled
@ -15,9 +16,9 @@ if TYPE_CHECKING:
BR_CODE_OTHER = ButtonRequestType.Other # global_import_cache
CONFIRMED = trezorui2.CONFIRMED
CANCELLED = trezorui2.CANCELLED
INFO = trezorui2.INFO
CONFIRMED = trezorui_api.CONFIRMED
CANCELLED = trezorui_api.CANCELLED
INFO = trezorui_api.INFO
def confirm_action(

View File

@ -1,4 +1,5 @@
import trezorui2
import trezorui_api
from trezor import ui
from trezor.enums import ButtonRequestType
@ -20,7 +21,7 @@ async def confirm_fido(
)
result = await interact(confirm, "confirm_fido", ButtonRequestType.Other)
if __debug__ and result is trezorui2.CONFIRMED:
if __debug__ and result is trezorui_api.CONFIRMED:
# debuglink will directly inject a CONFIRMED message which we need to handle
# by playing back a click to the Rust layout and getting out the selected number
# that way
@ -32,7 +33,7 @@ async def confirm_fido(
msg = confirm.touch_event(io.TOUCH_END, 220, 220)
if confirm.paint():
ui.refresh()
assert msg is trezorui2.LayoutState.DONE
assert msg is trezorui_api.LayoutState.DONE
retval = confirm.return_value()
assert isinstance(retval, int)
return retval
@ -59,4 +60,4 @@ async def confirm_fido_reset() -> bool:
reverse=True,
)
)
return (await confirm.get_result()) is trezorui2.CONFIRMED
return (await confirm.get_result()) is trezorui_api.CONFIRMED

View File

@ -1,6 +1,7 @@
from typing import TYPE_CHECKING
import trezorui2
import trezorui_api
from trezor import TR, ui
from trezor.enums import ButtonRequestType
@ -54,7 +55,7 @@ def show_remaining_shares(
groups: set[tuple[str, ...]],
shares_remaining: list[int],
group_threshold: int,
) -> Awaitable[trezorui2.UiResult]:
) -> Awaitable[trezorui_api.UiResult]:
from trezor import strings
from trezor.crypto.slip39 import MAX_SHARE_COUNT
@ -161,9 +162,9 @@ async def continue_recovery(
raise_on_cancel=None,
)
if result is trezorui2.CONFIRMED:
if result is trezorui_api.CONFIRMED:
return True
elif result is trezorui2.INFO and remaining_shares_info is not None:
elif result is trezorui_api.INFO and remaining_shares_info is not None:
await show_remaining_shares(*remaining_shares_info)
else:
try:

View File

@ -1,12 +1,13 @@
from typing import Awaitable, Callable, Sequence
import trezorui2
import trezorui_api
from trezor import TR
from trezor.enums import ButtonRequestType
from ..common import interact, raise_if_not_confirmed
CONFIRMED = trezorui2.CONFIRMED # global_import_cache
CONFIRMED = trezorui_api.CONFIRMED # global_import_cache
def _split_share_into_pages(share_words: Sequence[str], per_page: int = 4) -> list[str]:
@ -323,7 +324,7 @@ def show_intro_backup(single_share: bool, num_of_words: int | None) -> Awaitable
)
def show_warning_backup() -> Awaitable[trezorui2.UiResult]:
def show_warning_backup() -> Awaitable[trezorui_api.UiResult]:
return interact(
trezorui2.show_info(
title=TR.reset__never_make_digital_copy,
@ -351,7 +352,7 @@ def show_reset_warning(
subheader: str | None = None,
button: str | None = None,
br_code: ButtonRequestType = ButtonRequestType.Warning,
) -> Awaitable[trezorui2.UiResult]:
) -> Awaitable[trezorui_api.UiResult]:
button = button or TR.buttons__try_again # def_arg
return interact(
trezorui2.show_warning(