You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
trezor-firmware/core/src/trezor/ui/layouts/tr/__init__.py

296 lines
8.0 KiB

from typing import TYPE_CHECKING, Sequence
from trezor import io, log, loop, ui, wire, workflow
from trezor.enums import ButtonRequestType
import trezorui2
from ..common import button_request, interact
if TYPE_CHECKING:
from typing import Any, NoReturn, Type
ExceptionType = BaseException | Type[BaseException]
class _RustLayout(ui.Layout):
# pylint: disable=super-init-not-called
def __init__(self, layout: Any):
self.layout = layout
self.timer = loop.Timer()
self.layout.set_timer_fn(self.set_timer)
def set_timer(self, token: int, deadline: int) -> None:
self.timer.schedule(deadline, token)
def create_tasks(self) -> tuple[loop.Task, ...]:
return self.handle_input_and_rendering(), self.handle_timers()
def handle_input_and_rendering(self) -> loop.Task: # type: ignore [awaitable-is-generator]
button = loop.wait(io.BUTTON)
ui.display.clear()
self.layout.paint()
while True:
# Using `yield` instead of `await` to avoid allocations.
event, button_num = yield button
workflow.idle_timer.touch()
msg = None
if event in (io.BUTTON_PRESSED, io.BUTTON_RELEASED):
msg = self.layout.button_event(event, button_num)
self.layout.paint()
if msg is not None:
raise ui.Result(msg)
def handle_timers(self) -> loop.Task: # type: ignore [awaitable-is-generator]
while True:
# Using `yield` instead of `await` to avoid allocations.
token = yield self.timer
msg = self.layout.timer(token)
self.layout.paint()
if msg is not None:
raise ui.Result(msg)
async def confirm_action(
ctx: wire.GenericContext,
br_type: str,
title: str,
action: str | None = None,
description: str | None = None,
description_param: str | None = None,
description_param_font: int = ui.BOLD,
verb: str | bytes | None = "OK",
verb_cancel: str | bytes | None = "cancel",
hold: bool = False,
hold_danger: bool = False,
icon: str | None = None,
icon_color: int | None = None,
reverse: bool = False,
larger_vspace: bool = False,
exc: ExceptionType = wire.ActionCancelled,
br_code: ButtonRequestType = ButtonRequestType.Other,
) -> None:
if isinstance(verb, bytes) or isinstance(verb_cancel, bytes):
raise NotImplementedError
if description is not None and description_param is not None:
if description_param_font != ui.BOLD:
log.error(__name__, "confirm_action description_param_font not implemented")
description = description.format(description_param)
if hold:
log.error(__name__, "confirm_action hold not implemented")
result = await interact(
ctx,
_RustLayout(
trezorui2.confirm_action(
title=title.upper(),
action=action,
description=description,
verb=verb,
verb_cancel=verb_cancel,
hold=hold,
reverse=reverse,
)
),
br_type,
br_code,
)
if result is not trezorui2.CONFIRMED:
raise exc
async def confirm_text(
ctx: wire.GenericContext,
br_type: str,
title: str,
data: str,
description: str | None = None,
br_code: ButtonRequestType = ButtonRequestType.Other,
icon: str = ui.ICON_SEND, # TODO cleanup @ redesign
icon_color: int = ui.GREEN, # TODO cleanup @ redesign
) -> None:
result = await interact(
ctx,
_RustLayout(
trezorui2.confirm_text(
title=title.upper(),
data=data,
description=description,
)
),
br_type,
br_code,
)
if result is not trezorui2.CONFIRMED:
raise wire.ActionCancelled
async def show_success(
ctx: wire.GenericContext,
br_type: str,
content: str,
) -> None:
result = await interact(
ctx,
_RustLayout(
trezorui2.confirm_text(
title="Success",
data=content,
description="",
)
),
br_type,
br_code=ButtonRequestType.Other,
)
if result is not trezorui2.CONFIRMED:
raise wire.ActionCancelled
async def show_address(
ctx: wire.GenericContext,
address: str,
*,
case_sensitive: bool = True,
address_qr: str | None = None,
title: str = "Confirm address",
network: str | None = None,
multisig_index: int | None = None,
xpubs: Sequence[str] = (),
address_extra: str | None = None,
title_qr: str | None = None,
) -> None:
result = await interact(
ctx,
_RustLayout(
trezorui2.confirm_text(
title="ADDRESS",
data=address,
description="Confirm address",
)
),
"show_address",
ButtonRequestType.Address,
)
if result is not trezorui2.CONFIRMED:
raise wire.ActionCancelled
async def confirm_output(
ctx: wire.GenericContext,
address: str,
amount: str,
font_amount: int = ui.NORMAL, # TODO cleanup @ redesign
title: str = "Confirm sending",
icon: str = ui.ICON_SEND,
) -> None:
result = await interact(
ctx,
_RustLayout(
trezorui2.confirm_text(
title=title,
data=f"Send {amount} to {address}?",
description="Confirm Output",
)
),
"confirm_output",
ButtonRequestType.Other,
)
if result is not trezorui2.CONFIRMED:
raise wire.ActionCancelled
async def confirm_total(
ctx: wire.GenericContext,
total_amount: str,
fee_amount: str,
title: str = "Confirm transaction",
total_label: str = "Total amount:\n",
fee_label: str = "\nincluding fee:\n",
icon_color: int = ui.GREEN,
br_type: str = "confirm_total",
br_code: ButtonRequestType = ButtonRequestType.SignTx,
) -> None:
result = await interact(
ctx,
_RustLayout(
trezorui2.confirm_text(
title=title,
data=f"{total_label}{total_amount}\n{fee_label}{fee_amount}",
description="Confirm Output",
)
),
br_type,
br_code,
)
if result is not trezorui2.CONFIRMED:
raise wire.ActionCancelled
async def confirm_blob(
ctx: wire.GenericContext,
br_type: str,
title: str,
data: bytes | str,
description: str | None = None,
hold: bool = False,
br_code: ButtonRequestType = ButtonRequestType.Other,
icon: str = ui.ICON_SEND, # TODO cleanup @ redesign
icon_color: int = ui.GREEN, # TODO cleanup @ redesign
ask_pagination: bool = False,
) -> None:
result = await interact(
ctx,
_RustLayout(
trezorui2.confirm_text(
title=title,
data=str(data),
description=description,
)
),
br_type,
br_code,
)
if result is not trezorui2.CONFIRMED:
raise wire.ActionCancelled
def draw_simple_text(title: str, description: str = "") -> None:
log.error(__name__, "draw_simple_text not implemented")
async def request_pin_on_device(
ctx: wire.GenericContext,
prompt: str,
attempts_remaining: int | None,
allow_cancel: bool,
) -> str:
await button_request(ctx, "pin_device", code=ButtonRequestType.PinEntry)
# TODO: this should not be callable on TR
return "1234"
async def show_error_and_raise(
ctx: wire.GenericContext,
br_type: str,
content: str,
header: str = "Error",
subheader: str | None = None,
button: str = "Close",
red: bool = False,
exc: ExceptionType = wire.ActionCancelled,
) -> NoReturn:
raise NotImplementedError
async def show_popup(
title: str,
description: str,
subtitle: str | None = None,
description_param: str = "",
timeout_ms: int = 3000,
) -> None:
raise NotImplementedError