mirror of
https://github.com/trezor/trezor-firmware.git
synced 2024-11-22 15:38:11 +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:
parent
af554458b5
commit
c51f51d233
@ -165,6 +165,7 @@
|
|||||||
#define MICROPY_PY_TREZORPROTO (1)
|
#define MICROPY_PY_TREZORPROTO (1)
|
||||||
#define MICROPY_PY_TREZORTRANSLATE (1)
|
#define MICROPY_PY_TREZORTRANSLATE (1)
|
||||||
#define MICROPY_PY_TREZORUI2 (1)
|
#define MICROPY_PY_TREZORUI2 (1)
|
||||||
|
#define MICROPY_PY_TREZORUI_API (1)
|
||||||
|
|
||||||
#ifdef SYSTEM_VIEW
|
#ifdef SYSTEM_VIEW
|
||||||
#define MP_PLAT_PRINT_STRN(str, len) segger_print(str, len)
|
#define MP_PLAT_PRINT_STRN(str, len) segger_print(str, len)
|
||||||
|
@ -214,6 +214,7 @@ extern const struct _mp_print_t mp_stderr_print;
|
|||||||
#define MICROPY_PY_TREZORPROTO (1)
|
#define MICROPY_PY_TREZORPROTO (1)
|
||||||
#define MICROPY_PY_TREZORTRANSLATE (1)
|
#define MICROPY_PY_TREZORTRANSLATE (1)
|
||||||
#define MICROPY_PY_TREZORUI2 (1)
|
#define MICROPY_PY_TREZORUI2 (1)
|
||||||
|
#define MICROPY_PY_TREZORUI_API (1)
|
||||||
|
|
||||||
#define MP_STATE_PORT MP_STATE_VM
|
#define MP_STATE_PORT MP_STATE_VM
|
||||||
|
|
||||||
|
@ -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_trezorproto;
|
||||||
extern mp_obj_module_t mp_module_trezorui2;
|
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;
|
extern mp_obj_module_t mp_module_trezortranslate;
|
||||||
|
|
||||||
#ifdef TREZOR_EMULATOR
|
#ifdef TREZOR_EMULATOR
|
||||||
|
@ -701,6 +701,7 @@ static void _librust_qstrs(void) {
|
|||||||
MP_QSTR_trace;
|
MP_QSTR_trace;
|
||||||
MP_QSTR_trezorproto;
|
MP_QSTR_trezorproto;
|
||||||
MP_QSTR_trezorui2;
|
MP_QSTR_trezorui2;
|
||||||
|
MP_QSTR_trezorui_api;
|
||||||
MP_QSTR_tutorial;
|
MP_QSTR_tutorial;
|
||||||
MP_QSTR_tutorial__continue;
|
MP_QSTR_tutorial__continue;
|
||||||
MP_QSTR_tutorial__did_you_know;
|
MP_QSTR_tutorial__did_you_know;
|
||||||
|
133
core/embed/rust/src/ui/api/firmware_upy.rs
Normal file
133
core/embed/rust/src/ui/api/firmware_upy.rs
Normal 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(),
|
||||||
|
};
|
@ -2,3 +2,6 @@ pub mod common_c;
|
|||||||
|
|
||||||
#[cfg(feature = "bootloader")]
|
#[cfg(feature = "bootloader")]
|
||||||
pub mod bootloader_c;
|
pub mod bootloader_c;
|
||||||
|
|
||||||
|
#[cfg(feature = "micropython")]
|
||||||
|
pub mod firmware_upy;
|
||||||
|
@ -24,5 +24,7 @@ pub mod model_tr;
|
|||||||
#[cfg(feature = "model_tt")]
|
#[cfg(feature = "model_tt")]
|
||||||
pub mod model_tt;
|
pub mod model_tt;
|
||||||
pub mod ui_features;
|
pub mod ui_features;
|
||||||
|
#[cfg(feature = "micropython")]
|
||||||
|
pub mod ui_features_fw;
|
||||||
|
|
||||||
pub use ui_features::UIFeaturesCommon;
|
pub use ui_features::UIFeaturesCommon;
|
||||||
|
@ -1508,16 +1508,10 @@ extern "C" fn new_warning_hi_prio(n_args: usize, args: *const Obj, kwargs: *mut
|
|||||||
#[no_mangle]
|
#[no_mangle]
|
||||||
pub static mp_module_trezorui2: Module = obj_module! {
|
pub static mp_module_trezorui2: Module = obj_module! {
|
||||||
/// from trezor import utils
|
/// from trezor import utils
|
||||||
|
/// from trezorui_api import *
|
||||||
|
///
|
||||||
Qstr::MP_QSTR___name__ => Qstr::MP_QSTR_trezorui2.to_obj(),
|
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:
|
/// def disable_animation(disable: bool) -> None:
|
||||||
/// """Disable animations, debug builds only."""
|
/// """Disable animations, debug builds only."""
|
||||||
@ -2021,33 +2015,5 @@ pub static mp_module_trezorui2: Module = obj_module! {
|
|||||||
/// cancel_text: str | None = None,
|
/// cancel_text: str | None = None,
|
||||||
/// ) -> LayoutObj[UiResult]:
|
/// ) -> LayoutObj[UiResult]:
|
||||||
/// """Total summary and hold to confirm."""
|
/// """Total summary and hold to confirm."""
|
||||||
Qstr::MP_QSTR_flow_confirm_summary => obj_fn_kw!(0, new_confirm_summary).as_obj(),
|
Qstr::MP_QSTR_flow_confirm_summary => obj_fn_kw!(0, flow::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(),
|
|
||||||
};
|
};
|
||||||
|
@ -13,6 +13,8 @@ pub mod flow;
|
|||||||
#[cfg(feature = "micropython")]
|
#[cfg(feature = "micropython")]
|
||||||
pub mod layout;
|
pub mod layout;
|
||||||
pub mod screens;
|
pub mod screens;
|
||||||
|
#[cfg(feature = "micropython")]
|
||||||
|
pub mod ui_features_fw;
|
||||||
|
|
||||||
pub struct ModelMercuryFeatures;
|
pub struct ModelMercuryFeatures;
|
||||||
|
|
||||||
|
@ -30,7 +30,6 @@ use crate::{
|
|||||||
translations::TR,
|
translations::TR,
|
||||||
trezorhal::model,
|
trezorhal::model,
|
||||||
ui::{
|
ui::{
|
||||||
backlight::BACKLIGHT_LEVELS_OBJ,
|
|
||||||
component::{
|
component::{
|
||||||
base::Component,
|
base::Component,
|
||||||
connect::Connect,
|
connect::Connect,
|
||||||
@ -47,8 +46,7 @@ use crate::{
|
|||||||
},
|
},
|
||||||
geometry,
|
geometry,
|
||||||
layout::{
|
layout::{
|
||||||
base::LAYOUT_STATE,
|
obj::{ComponentMsgObj, LayoutObj},
|
||||||
obj::{ComponentMsgObj, LayoutObj, ATTACH_TYPE_OBJ},
|
|
||||||
result::{CANCELLED, CONFIRMED, INFO},
|
result::{CANCELLED, CONFIRMED, INFO},
|
||||||
util::{upy_disable_animation, ConfirmBlob, RecoveryType},
|
util::{upy_disable_animation, ConfirmBlob, RecoveryType},
|
||||||
},
|
},
|
||||||
@ -1651,17 +1649,11 @@ extern "C" fn new_show_wait_text(message: Obj) -> Obj {
|
|||||||
|
|
||||||
#[no_mangle]
|
#[no_mangle]
|
||||||
pub static mp_module_trezorui2: Module = obj_module! {
|
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(),
|
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:
|
/// def disable_animation(disable: bool) -> None:
|
||||||
/// """Disable animations, debug builds only."""
|
/// """Disable animations, debug builds only."""
|
||||||
Qstr::MP_QSTR_disable_animation => obj_fn_1!(upy_disable_animation).as_obj(),
|
Qstr::MP_QSTR_disable_animation => obj_fn_1!(upy_disable_animation).as_obj(),
|
||||||
@ -2082,32 +2074,4 @@ pub static mp_module_trezorui2: Module = obj_module! {
|
|||||||
/// def show_wait_text(message: str, /) -> None:
|
/// def show_wait_text(message: str, /) -> None:
|
||||||
/// """Show single-line text in the middle of the screen."""
|
/// """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(),
|
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(),
|
|
||||||
};
|
};
|
||||||
|
@ -30,7 +30,6 @@ use crate::{
|
|||||||
translations::TR,
|
translations::TR,
|
||||||
trezorhal::model,
|
trezorhal::model,
|
||||||
ui::{
|
ui::{
|
||||||
backlight::BACKLIGHT_LEVELS_OBJ,
|
|
||||||
component::{
|
component::{
|
||||||
base::ComponentExt,
|
base::ComponentExt,
|
||||||
connect::Connect,
|
connect::Connect,
|
||||||
@ -50,8 +49,7 @@ use crate::{
|
|||||||
},
|
},
|
||||||
geometry,
|
geometry,
|
||||||
layout::{
|
layout::{
|
||||||
base::LAYOUT_STATE,
|
obj::{ComponentMsgObj, LayoutObj},
|
||||||
obj::{ComponentMsgObj, LayoutObj, ATTACH_TYPE_OBJ},
|
|
||||||
result::{CANCELLED, CONFIRMED, INFO},
|
result::{CANCELLED, CONFIRMED, INFO},
|
||||||
util::{upy_disable_animation, ConfirmBlob, PropsList, RecoveryType},
|
util::{upy_disable_animation, ConfirmBlob, PropsList, RecoveryType},
|
||||||
},
|
},
|
||||||
@ -1653,98 +1651,8 @@ extern "C" fn new_show_wait_text(message: Obj) -> Obj {
|
|||||||
#[no_mangle]
|
#[no_mangle]
|
||||||
pub static mp_module_trezorui2: Module = obj_module! {
|
pub static mp_module_trezorui2: Module = obj_module! {
|
||||||
/// from trezor import utils
|
/// 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:
|
/// def disable_animation(disable: bool) -> None:
|
||||||
/// """Disable animations, debug builds only."""
|
/// """Disable animations, debug builds only."""
|
||||||
@ -2182,34 +2090,6 @@ pub static mp_module_trezorui2: Module = obj_module! {
|
|||||||
/// def show_wait_text(message: str, /) -> LayoutObj[None]:
|
/// def show_wait_text(message: str, /) -> LayoutObj[None]:
|
||||||
/// """Show single-line text in the middle of the screen."""
|
/// """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(),
|
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)]
|
#[cfg(test)]
|
||||||
|
@ -25,6 +25,10 @@
|
|||||||
MP_REGISTER_MODULE(MP_QSTR_trezorui2, mp_module_trezorui2);
|
MP_REGISTER_MODULE(MP_QSTR_trezorui2, mp_module_trezorui2);
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
#if MICROPY_PY_TREZORUI_API
|
||||||
|
MP_REGISTER_MODULE(MP_QSTR_trezorui_api, mp_module_trezorui_api);
|
||||||
|
#endif
|
||||||
|
|
||||||
#if MICROPY_PY_TREZORPROTO
|
#if MICROPY_PY_TREZORPROTO
|
||||||
MP_REGISTER_MODULE(MP_QSTR_trezorproto, mp_module_trezorproto);
|
MP_REGISTER_MODULE(MP_QSTR_trezorproto, mp_module_trezorproto);
|
||||||
#endif
|
#endif
|
||||||
|
@ -1,8 +1,6 @@
|
|||||||
from typing import *
|
from typing import *
|
||||||
from trezor import utils
|
from trezor import utils
|
||||||
CONFIRMED: UiResult
|
from trezorui_api import *
|
||||||
CANCELLED: UiResult
|
|
||||||
INFO: UiResult
|
|
||||||
|
|
||||||
|
|
||||||
# rust/src/ui/model_mercury/layout.rs
|
# rust/src/ui/model_mercury/layout.rs
|
||||||
@ -554,38 +552,8 @@ def flow_confirm_summary(
|
|||||||
cancel_text: str | None = None,
|
cancel_text: str | None = None,
|
||||||
) -> LayoutObj[UiResult]:
|
) -> LayoutObj[UiResult]:
|
||||||
"""Total summary and hold to confirm."""
|
"""Total summary and hold to confirm."""
|
||||||
|
from trezor import utils
|
||||||
|
from trezorui_api import *
|
||||||
# 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
|
|
||||||
|
|
||||||
|
|
||||||
# rust/src/ui/model_tr/layout.rs
|
# rust/src/ui/model_tr/layout.rs
|
||||||
@ -1050,102 +1018,8 @@ def confirm_firmware_update(
|
|||||||
# rust/src/ui/model_tr/layout.rs
|
# rust/src/ui/model_tr/layout.rs
|
||||||
def show_wait_text(message: str, /) -> None:
|
def show_wait_text(message: str, /) -> None:
|
||||||
"""Show single-line text in the middle of the screen."""
|
"""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
|
from trezor import utils
|
||||||
T = TypeVar("T")
|
from trezorui_api import *
|
||||||
|
|
||||||
|
|
||||||
# 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
|
|
||||||
|
|
||||||
|
|
||||||
# rust/src/ui/model_tt/layout.rs
|
# rust/src/ui/model_tt/layout.rs
|
||||||
@ -1626,32 +1500,3 @@ def confirm_firmware_update(
|
|||||||
# rust/src/ui/model_tt/layout.rs
|
# rust/src/ui/model_tt/layout.rs
|
||||||
def show_wait_text(message: str, /) -> LayoutObj[None]:
|
def show_wait_text(message: str, /) -> LayoutObj[None]:
|
||||||
"""Show single-line text in the middle of the screen."""
|
"""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]"
|
|
||||||
|
97
core/mocks/generated/trezorui_api.pyi
Normal file
97
core/mocks/generated/trezorui_api.pyi
Normal 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]"
|
@ -8,7 +8,7 @@ if __debug__:
|
|||||||
from micropython import const
|
from micropython import const
|
||||||
from typing import TYPE_CHECKING
|
from typing import TYPE_CHECKING
|
||||||
|
|
||||||
import trezorui2
|
import trezorui_api
|
||||||
from storage import debug as storage
|
from storage import debug as storage
|
||||||
from trezor import io, log, loop, ui, utils, wire, workflow
|
from trezor import io, log, loop, ui, utils, wire, workflow
|
||||||
from trezor.enums import DebugWaitType, MessageType
|
from trezor.enums import DebugWaitType, MessageType
|
||||||
@ -195,11 +195,11 @@ if __debug__:
|
|||||||
|
|
||||||
assert isinstance(ui.CURRENT_LAYOUT, ui.Layout)
|
assert isinstance(ui.CURRENT_LAYOUT, ui.Layout)
|
||||||
if button == DebugButton.NO:
|
if button == DebugButton.NO:
|
||||||
ui.CURRENT_LAYOUT._emit_message(trezorui2.CANCELLED)
|
ui.CURRENT_LAYOUT._emit_message(trezorui_api.CANCELLED)
|
||||||
elif button == DebugButton.YES:
|
elif button == DebugButton.YES:
|
||||||
ui.CURRENT_LAYOUT._emit_message(trezorui2.CONFIRMED)
|
ui.CURRENT_LAYOUT._emit_message(trezorui_api.CONFIRMED)
|
||||||
elif button == DebugButton.INFO:
|
elif button == DebugButton.INFO:
|
||||||
ui.CURRENT_LAYOUT._emit_message(trezorui2.INFO)
|
ui.CURRENT_LAYOUT._emit_message(trezorui_api.INFO)
|
||||||
else:
|
else:
|
||||||
raise RuntimeError("Invalid DebugButton")
|
raise RuntimeError("Invalid DebugButton")
|
||||||
|
|
||||||
|
@ -8,12 +8,12 @@ import trezorui2
|
|||||||
from trezor import io, log, loop, utils, wire, workflow
|
from trezor import io, log, loop, utils, wire, workflow
|
||||||
from trezor.messages import ButtonAck, ButtonRequest
|
from trezor.messages import ButtonAck, ButtonRequest
|
||||||
from trezor.wire import context
|
from trezor.wire import context
|
||||||
from trezorui2 import BacklightLevels, LayoutState
|
from trezorui_api import BacklightLevels, LayoutState
|
||||||
|
|
||||||
if TYPE_CHECKING:
|
if TYPE_CHECKING:
|
||||||
from typing import Any, Callable, Generator, Generic, Iterator, TypeVar
|
from typing import Any, Callable, Generator, Generic, Iterator, TypeVar
|
||||||
|
|
||||||
from trezorui2 import AttachType, LayoutObj, UiResult # noqa: F401
|
from trezorui_api import AttachType, LayoutObj, UiResult # noqa: F401
|
||||||
|
|
||||||
T = TypeVar("T", covariant=True)
|
T = TypeVar("T", covariant=True)
|
||||||
|
|
||||||
|
@ -1,6 +1,7 @@
|
|||||||
from typing import TYPE_CHECKING
|
from typing import TYPE_CHECKING
|
||||||
|
|
||||||
import trezorui2
|
import trezorui2
|
||||||
|
import trezorui_api
|
||||||
from trezor import ui, utils, workflow
|
from trezor import ui, utils, workflow
|
||||||
from trezor.enums import ButtonRequestType
|
from trezor.enums import ButtonRequestType
|
||||||
from trezor.messages import ButtonAck, ButtonRequest
|
from trezor.messages import ButtonAck, ButtonRequest
|
||||||
@ -52,7 +53,7 @@ async def interact(
|
|||||||
# wait for the layout result
|
# wait for the layout result
|
||||||
result = await layout.get_result()
|
result = await layout.get_result()
|
||||||
# raise an exception if the user cancelled the action
|
# 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
|
raise raise_on_cancel
|
||||||
return result
|
return result
|
||||||
|
|
||||||
@ -93,13 +94,13 @@ async def with_info(
|
|||||||
|
|
||||||
first_br = next_br
|
first_br = next_br
|
||||||
|
|
||||||
if result is trezorui2.CONFIRMED:
|
if result is trezorui_api.CONFIRMED:
|
||||||
return
|
return
|
||||||
elif result is trezorui2.INFO:
|
elif result is trezorui_api.INFO:
|
||||||
info_result = await interact(
|
info_result = await interact(
|
||||||
info_layout, next_br, br_code, raise_on_cancel=None
|
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
|
return
|
||||||
else:
|
else:
|
||||||
# no matter what the info layout returns, we always go back to the main layout
|
# no matter what the info layout returns, we always go back to the main layout
|
||||||
@ -108,7 +109,7 @@ async def with_info(
|
|||||||
raise RuntimeError # unexpected result
|
raise RuntimeError # unexpected result
|
||||||
|
|
||||||
|
|
||||||
def draw_simple(layout: trezorui2.LayoutObj[Any]) -> None:
|
def draw_simple(layout: trezorui_api.LayoutObj[Any]) -> None:
|
||||||
# Simple drawing not supported for layouts that set timers.
|
# Simple drawing not supported for layouts that set timers.
|
||||||
def dummy_set_timer(token: int, duration: int) -> None:
|
def dummy_set_timer(token: int, duration: int) -> None:
|
||||||
raise RuntimeError
|
raise RuntimeError
|
||||||
|
@ -2,6 +2,7 @@ from typing import TYPE_CHECKING
|
|||||||
|
|
||||||
import storage.cache as storage_cache
|
import storage.cache as storage_cache
|
||||||
import trezorui2
|
import trezorui2
|
||||||
|
import trezorui_api
|
||||||
from trezor import TR, ui
|
from trezor import TR, ui
|
||||||
|
|
||||||
if TYPE_CHECKING:
|
if TYPE_CHECKING:
|
||||||
@ -126,7 +127,7 @@ class Busyscreen(HomescreenBase):
|
|||||||
|
|
||||||
# Handle timeout.
|
# Handle timeout.
|
||||||
result = await super().get_result()
|
result = await super().get_result()
|
||||||
assert result == trezorui2.CANCELLED
|
assert result == trezorui_api.CANCELLED
|
||||||
storage_cache.delete(storage_cache.APP_COMMON_BUSY_DEADLINE_MS)
|
storage_cache.delete(storage_cache.APP_COMMON_BUSY_DEADLINE_MS)
|
||||||
set_homescreen()
|
set_homescreen()
|
||||||
return result
|
return result
|
||||||
|
@ -1,6 +1,7 @@
|
|||||||
from typing import TYPE_CHECKING
|
from typing import TYPE_CHECKING
|
||||||
|
|
||||||
import trezorui2
|
import trezorui2
|
||||||
|
import trezorui_api
|
||||||
from trezor import TR, ui, utils
|
from trezor import TR, ui, utils
|
||||||
from trezor.enums import ButtonRequestType
|
from trezor.enums import ButtonRequestType
|
||||||
from trezor.wire import ActionCancelled
|
from trezor.wire import ActionCancelled
|
||||||
@ -17,9 +18,9 @@ if TYPE_CHECKING:
|
|||||||
|
|
||||||
BR_CODE_OTHER = ButtonRequestType.Other # global_import_cache
|
BR_CODE_OTHER = ButtonRequestType.Other # global_import_cache
|
||||||
|
|
||||||
CONFIRMED = trezorui2.CONFIRMED
|
CONFIRMED = trezorui_api.CONFIRMED
|
||||||
CANCELLED = trezorui2.CANCELLED
|
CANCELLED = trezorui_api.CANCELLED
|
||||||
INFO = trezorui2.INFO
|
INFO = trezorui_api.INFO
|
||||||
|
|
||||||
|
|
||||||
def confirm_action(
|
def confirm_action(
|
||||||
|
@ -1,4 +1,5 @@
|
|||||||
import trezorui2
|
import trezorui2
|
||||||
|
import trezorui_api
|
||||||
from trezor import ui
|
from trezor import ui
|
||||||
from trezor.enums import ButtonRequestType
|
from trezor.enums import ButtonRequestType
|
||||||
|
|
||||||
@ -20,7 +21,7 @@ async def confirm_fido(
|
|||||||
)
|
)
|
||||||
result = await interact(confirm, "confirm_fido", ButtonRequestType.Other)
|
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
|
# 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
|
# by playing back a click to the Rust layout and getting out the selected number
|
||||||
# that way
|
# that way
|
||||||
@ -51,4 +52,4 @@ async def confirm_fido_reset() -> bool:
|
|||||||
prompt_screen=True,
|
prompt_screen=True,
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
return (await confirm.get_result()) is trezorui2.CONFIRMED
|
return (await confirm.get_result()) is trezorui_api.CONFIRMED
|
||||||
|
@ -1,15 +1,16 @@
|
|||||||
from typing import TYPE_CHECKING
|
from typing import TYPE_CHECKING
|
||||||
|
|
||||||
import trezorui2
|
import trezorui2
|
||||||
|
import trezorui_api
|
||||||
from trezor import TR
|
from trezor import TR
|
||||||
from trezor.enums import ButtonRequestType, RecoveryType
|
from trezor.enums import ButtonRequestType, RecoveryType
|
||||||
|
|
||||||
from ..common import interact
|
from ..common import interact
|
||||||
from . import raise_if_not_confirmed
|
from . import raise_if_not_confirmed
|
||||||
|
|
||||||
CONFIRMED = trezorui2.CONFIRMED # global_import_cache
|
CONFIRMED = trezorui_api.CONFIRMED # global_import_cache
|
||||||
CANCELLED = trezorui2.CANCELLED # global_import_cache
|
CANCELLED = trezorui_api.CANCELLED # global_import_cache
|
||||||
INFO = trezorui2.INFO # global_import_cache
|
INFO = trezorui_api.INFO # global_import_cache
|
||||||
|
|
||||||
if TYPE_CHECKING:
|
if TYPE_CHECKING:
|
||||||
from apps.management.recovery_device.layout import RemainingSharesInfo
|
from apps.management.recovery_device.layout import RemainingSharesInfo
|
||||||
|
@ -1,6 +1,7 @@
|
|||||||
from typing import Awaitable, Callable, Sequence
|
from typing import Awaitable, Callable, Sequence
|
||||||
|
|
||||||
import trezorui2
|
import trezorui2
|
||||||
|
import trezorui_api
|
||||||
from trezor import TR, ui
|
from trezor import TR, ui
|
||||||
from trezor.enums import ButtonRequestType
|
from trezor.enums import ButtonRequestType
|
||||||
from trezor.wire import ActionCancelled
|
from trezor.wire import ActionCancelled
|
||||||
@ -8,7 +9,7 @@ from trezor.wire import ActionCancelled
|
|||||||
from ..common import interact
|
from ..common import interact
|
||||||
from . import raise_if_not_confirmed, show_success
|
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(
|
def show_share_words(
|
||||||
@ -172,7 +173,7 @@ async def _prompt_number(
|
|||||||
# so use the initial one
|
# so use the initial one
|
||||||
return count
|
return count
|
||||||
|
|
||||||
if result is not trezorui2.CANCELLED:
|
if result is not trezorui_api.CANCELLED:
|
||||||
assert isinstance(result, int)
|
assert isinstance(result, int)
|
||||||
return result
|
return result
|
||||||
else:
|
else:
|
||||||
|
@ -1,6 +1,7 @@
|
|||||||
from typing import TYPE_CHECKING
|
from typing import TYPE_CHECKING
|
||||||
|
|
||||||
import trezorui2
|
import trezorui2
|
||||||
|
import trezorui_api
|
||||||
from trezor import TR, ui, utils
|
from trezor import TR, ui, utils
|
||||||
from trezor.enums import ButtonRequestType
|
from trezor.enums import ButtonRequestType
|
||||||
from trezor.wire import ActionCancelled
|
from trezor.wire import ActionCancelled
|
||||||
@ -13,9 +14,9 @@ if TYPE_CHECKING:
|
|||||||
from ..common import ExceptionType, PropertyType
|
from ..common import ExceptionType, PropertyType
|
||||||
|
|
||||||
|
|
||||||
CONFIRMED = trezorui2.CONFIRMED
|
CONFIRMED = trezorui_api.CONFIRMED
|
||||||
CANCELLED = trezorui2.CANCELLED
|
CANCELLED = trezorui_api.CANCELLED
|
||||||
INFO = trezorui2.INFO
|
INFO = trezorui_api.INFO
|
||||||
|
|
||||||
BR_CODE_OTHER = ButtonRequestType.Other # global_import_cache
|
BR_CODE_OTHER = ButtonRequestType.Other # global_import_cache
|
||||||
|
|
||||||
@ -1128,7 +1129,7 @@ def error_popup(
|
|||||||
*,
|
*,
|
||||||
button: str = "",
|
button: str = "",
|
||||||
timeout_ms: int = 0,
|
timeout_ms: int = 0,
|
||||||
) -> trezorui2.LayoutObj[trezorui2.UiResult]:
|
) -> trezorui_api.LayoutObj[trezorui_api.UiResult]:
|
||||||
if button:
|
if button:
|
||||||
raise NotImplementedError("Button not implemented")
|
raise NotImplementedError("Button not implemented")
|
||||||
|
|
||||||
|
@ -1,4 +1,5 @@
|
|||||||
import trezorui2
|
import trezorui2
|
||||||
|
import trezorui_api
|
||||||
from trezor import ui
|
from trezor import ui
|
||||||
from trezor.enums import ButtonRequestType
|
from trezor.enums import ButtonRequestType
|
||||||
|
|
||||||
@ -24,7 +25,7 @@ async def confirm_fido(
|
|||||||
|
|
||||||
# For the usage in device tests, assuming CONFIRMED (sent by debuglink)
|
# For the usage in device tests, assuming CONFIRMED (sent by debuglink)
|
||||||
# is choosing the first credential.
|
# is choosing the first credential.
|
||||||
if __debug__ and result is trezorui2.CONFIRMED:
|
if __debug__ and result is trezorui_api.CONFIRMED:
|
||||||
return 0
|
return 0
|
||||||
|
|
||||||
raise RuntimeError # should not get here, cancellation is handled by `interact`
|
raise RuntimeError # should not get here, cancellation is handled by `interact`
|
||||||
@ -40,4 +41,4 @@ async def confirm_fido_reset() -> bool:
|
|||||||
verb_cancel="",
|
verb_cancel="",
|
||||||
verb=TR.buttons__confirm,
|
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
|
||||||
|
@ -1,6 +1,7 @@
|
|||||||
from typing import TYPE_CHECKING
|
from typing import TYPE_CHECKING
|
||||||
|
|
||||||
import trezorui2
|
import trezorui2
|
||||||
|
import trezorui_api
|
||||||
from trezor import TR, ui
|
from trezor import TR, ui
|
||||||
from trezor.enums import ButtonRequestType, RecoveryType
|
from trezor.enums import ButtonRequestType, RecoveryType
|
||||||
|
|
||||||
@ -137,7 +138,7 @@ async def continue_recovery(
|
|||||||
ButtonRequestType.RecoveryHomepage,
|
ButtonRequestType.RecoveryHomepage,
|
||||||
raise_on_cancel=None,
|
raise_on_cancel=None,
|
||||||
)
|
)
|
||||||
if result is trezorui2.CONFIRMED:
|
if result is trezorui_api.CONFIRMED:
|
||||||
return True
|
return True
|
||||||
|
|
||||||
try:
|
try:
|
||||||
|
@ -3,11 +3,12 @@ from typing import TYPE_CHECKING
|
|||||||
import trezorui2
|
import trezorui2
|
||||||
from trezor import TR
|
from trezor import TR
|
||||||
from trezor.enums import ButtonRequestType
|
from trezor.enums import ButtonRequestType
|
||||||
|
import trezorui_api
|
||||||
|
|
||||||
from ..common import interact, raise_if_not_confirmed
|
from ..common import interact, raise_if_not_confirmed
|
||||||
from . import confirm_action, show_success, show_warning
|
from . import confirm_action, show_success, show_warning
|
||||||
|
|
||||||
CONFIRMED = trezorui2.CONFIRMED # global_import_cache
|
CONFIRMED = trezorui_api.CONFIRMED # global_import_cache
|
||||||
|
|
||||||
if TYPE_CHECKING:
|
if TYPE_CHECKING:
|
||||||
from typing import Awaitable, Sequence
|
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(
|
return show_warning(
|
||||||
"backup_warning",
|
"backup_warning",
|
||||||
TR.words__title_remember,
|
TR.words__title_remember,
|
||||||
@ -295,7 +296,7 @@ def show_reset_warning(
|
|||||||
subheader: str | None = None,
|
subheader: str | None = None,
|
||||||
button: str | None = None,
|
button: str | None = None,
|
||||||
br_code: ButtonRequestType = ButtonRequestType.Warning,
|
br_code: ButtonRequestType = ButtonRequestType.Warning,
|
||||||
) -> Awaitable[trezorui2.UiResult]:
|
) -> Awaitable[trezorui_api.UiResult]:
|
||||||
button = button or TR.buttons__try_again # def_arg
|
button = button or TR.buttons__try_again # def_arg
|
||||||
|
|
||||||
return show_warning(
|
return show_warning(
|
||||||
|
@ -1,6 +1,7 @@
|
|||||||
from typing import TYPE_CHECKING
|
from typing import TYPE_CHECKING
|
||||||
|
|
||||||
import trezorui2
|
import trezorui2
|
||||||
|
import trezorui_api
|
||||||
from trezor import TR, ui, utils
|
from trezor import TR, ui, utils
|
||||||
from trezor.enums import ButtonRequestType
|
from trezor.enums import ButtonRequestType
|
||||||
from trezor.wire import ActionCancelled
|
from trezor.wire import ActionCancelled
|
||||||
@ -15,9 +16,9 @@ if TYPE_CHECKING:
|
|||||||
|
|
||||||
BR_CODE_OTHER = ButtonRequestType.Other # global_import_cache
|
BR_CODE_OTHER = ButtonRequestType.Other # global_import_cache
|
||||||
|
|
||||||
CONFIRMED = trezorui2.CONFIRMED
|
CONFIRMED = trezorui_api.CONFIRMED
|
||||||
CANCELLED = trezorui2.CANCELLED
|
CANCELLED = trezorui_api.CANCELLED
|
||||||
INFO = trezorui2.INFO
|
INFO = trezorui_api.INFO
|
||||||
|
|
||||||
|
|
||||||
def confirm_action(
|
def confirm_action(
|
||||||
|
@ -1,4 +1,5 @@
|
|||||||
import trezorui2
|
import trezorui2
|
||||||
|
import trezorui_api
|
||||||
from trezor import ui
|
from trezor import ui
|
||||||
from trezor.enums import ButtonRequestType
|
from trezor.enums import ButtonRequestType
|
||||||
|
|
||||||
@ -20,7 +21,7 @@ async def confirm_fido(
|
|||||||
)
|
)
|
||||||
result = await interact(confirm, "confirm_fido", ButtonRequestType.Other)
|
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
|
# 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
|
# by playing back a click to the Rust layout and getting out the selected number
|
||||||
# that way
|
# that way
|
||||||
@ -32,7 +33,7 @@ async def confirm_fido(
|
|||||||
msg = confirm.touch_event(io.TOUCH_END, 220, 220)
|
msg = confirm.touch_event(io.TOUCH_END, 220, 220)
|
||||||
if confirm.paint():
|
if confirm.paint():
|
||||||
ui.refresh()
|
ui.refresh()
|
||||||
assert msg is trezorui2.LayoutState.DONE
|
assert msg is trezorui_api.LayoutState.DONE
|
||||||
retval = confirm.return_value()
|
retval = confirm.return_value()
|
||||||
assert isinstance(retval, int)
|
assert isinstance(retval, int)
|
||||||
return retval
|
return retval
|
||||||
@ -59,4 +60,4 @@ async def confirm_fido_reset() -> bool:
|
|||||||
reverse=True,
|
reverse=True,
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
return (await confirm.get_result()) is trezorui2.CONFIRMED
|
return (await confirm.get_result()) is trezorui_api.CONFIRMED
|
||||||
|
@ -1,6 +1,7 @@
|
|||||||
from typing import TYPE_CHECKING
|
from typing import TYPE_CHECKING
|
||||||
|
|
||||||
import trezorui2
|
import trezorui2
|
||||||
|
import trezorui_api
|
||||||
from trezor import TR, ui
|
from trezor import TR, ui
|
||||||
from trezor.enums import ButtonRequestType
|
from trezor.enums import ButtonRequestType
|
||||||
|
|
||||||
@ -54,7 +55,7 @@ def show_remaining_shares(
|
|||||||
groups: set[tuple[str, ...]],
|
groups: set[tuple[str, ...]],
|
||||||
shares_remaining: list[int],
|
shares_remaining: list[int],
|
||||||
group_threshold: int,
|
group_threshold: int,
|
||||||
) -> Awaitable[trezorui2.UiResult]:
|
) -> Awaitable[trezorui_api.UiResult]:
|
||||||
from trezor import strings
|
from trezor import strings
|
||||||
from trezor.crypto.slip39 import MAX_SHARE_COUNT
|
from trezor.crypto.slip39 import MAX_SHARE_COUNT
|
||||||
|
|
||||||
@ -161,9 +162,9 @@ async def continue_recovery(
|
|||||||
raise_on_cancel=None,
|
raise_on_cancel=None,
|
||||||
)
|
)
|
||||||
|
|
||||||
if result is trezorui2.CONFIRMED:
|
if result is trezorui_api.CONFIRMED:
|
||||||
return True
|
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)
|
await show_remaining_shares(*remaining_shares_info)
|
||||||
else:
|
else:
|
||||||
try:
|
try:
|
||||||
|
@ -1,12 +1,13 @@
|
|||||||
from typing import Awaitable, Callable, Sequence
|
from typing import Awaitable, Callable, Sequence
|
||||||
|
|
||||||
import trezorui2
|
import trezorui2
|
||||||
|
import trezorui_api
|
||||||
from trezor import TR
|
from trezor import TR
|
||||||
from trezor.enums import ButtonRequestType
|
from trezor.enums import ButtonRequestType
|
||||||
|
|
||||||
from ..common import interact, raise_if_not_confirmed
|
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]:
|
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(
|
return interact(
|
||||||
trezorui2.show_info(
|
trezorui2.show_info(
|
||||||
title=TR.reset__never_make_digital_copy,
|
title=TR.reset__never_make_digital_copy,
|
||||||
@ -351,7 +352,7 @@ def show_reset_warning(
|
|||||||
subheader: str | None = None,
|
subheader: str | None = None,
|
||||||
button: str | None = None,
|
button: str | None = None,
|
||||||
br_code: ButtonRequestType = ButtonRequestType.Warning,
|
br_code: ButtonRequestType = ButtonRequestType.Warning,
|
||||||
) -> Awaitable[trezorui2.UiResult]:
|
) -> Awaitable[trezorui_api.UiResult]:
|
||||||
button = button or TR.buttons__try_again # def_arg
|
button = button or TR.buttons__try_again # def_arg
|
||||||
return interact(
|
return interact(
|
||||||
trezorui2.show_warning(
|
trezorui2.show_warning(
|
||||||
|
Loading…
Reference in New Issue
Block a user