diff --git a/core/embed/bootloader/bootui.c b/core/embed/bootloader/bootui.c index e5e84b6a6c..8609d93fae 100644 --- a/core/embed/bootloader/bootui.c +++ b/core/embed/bootloader/bootui.c @@ -237,7 +237,7 @@ void ui_screen_install_confirm_newvendor(const vendor_header *const vhdr, void ui_screen_install(void) { display_bar(0, 0, DISPLAY_RESX, DISPLAY_RESY, COLOR_WHITE); display_loader(0, -20, COLOR_BL_PROCESS, COLOR_WHITE, toi_icon_install, - sizeof(toi_icon_install), COLOR_BLACK); + sizeof(toi_icon_install), COLOR_BLACK, 0); display_text_center(DISPLAY_RESX / 2, DISPLAY_RESY - 24, "Installing firmware", -1, FONT_NORMAL, COLOR_BLACK, COLOR_WHITE); @@ -245,12 +245,12 @@ void ui_screen_install(void) { void ui_screen_install_progress_erase(int pos, int len) { display_loader(250 * pos / len, -20, COLOR_BL_PROCESS, COLOR_WHITE, - toi_icon_install, sizeof(toi_icon_install), COLOR_BLACK); + toi_icon_install, sizeof(toi_icon_install), COLOR_BLACK, 0); } void ui_screen_install_progress_upload(int pos) { display_loader(pos, -20, COLOR_BL_PROCESS, COLOR_WHITE, toi_icon_install, - sizeof(toi_icon_install), COLOR_BLACK); + sizeof(toi_icon_install), COLOR_BLACK, 0); } // wipe UI @@ -275,14 +275,14 @@ void ui_screen_wipe_confirm(void) { void ui_screen_wipe(void) { display_bar(0, 0, DISPLAY_RESX, DISPLAY_RESY, COLOR_WHITE); display_loader(0, -20, COLOR_BL_PROCESS, COLOR_WHITE, toi_icon_wipe, - sizeof(toi_icon_wipe), COLOR_BLACK); + sizeof(toi_icon_wipe), COLOR_BLACK, 0); display_text_center(DISPLAY_RESX / 2, DISPLAY_RESY - 24, "Wiping device", -1, FONT_NORMAL, COLOR_BLACK, COLOR_WHITE); } void ui_screen_wipe_progress(int pos, int len) { display_loader(1000 * pos / len, -20, COLOR_BL_PROCESS, COLOR_WHITE, - toi_icon_wipe, sizeof(toi_icon_wipe), COLOR_BLACK); + toi_icon_wipe, sizeof(toi_icon_wipe), COLOR_BLACK, 0); } // done UI @@ -301,7 +301,7 @@ void ui_screen_done(int restart_seconds, secbool full_redraw) { display_bar(0, 0, DISPLAY_RESX, DISPLAY_RESY, COLOR_WHITE); } display_loader(1000, -20, COLOR_BL_DONE, COLOR_WHITE, toi_icon_done, - sizeof(toi_icon_done), COLOR_BLACK); + sizeof(toi_icon_done), COLOR_BLACK, 0); if (secfalse == full_redraw) { display_bar(0, DISPLAY_RESY - 24 - 18, 240, 23, COLOR_WHITE); } @@ -314,7 +314,7 @@ void ui_screen_done(int restart_seconds, secbool full_redraw) { void ui_screen_fail(void) { display_bar(0, 0, DISPLAY_RESX, DISPLAY_RESY, COLOR_WHITE); display_loader(1000, -20, COLOR_BL_FAIL, COLOR_WHITE, toi_icon_fail, - sizeof(toi_icon_fail), COLOR_BLACK); + sizeof(toi_icon_fail), COLOR_BLACK, 0); display_text_center(DISPLAY_RESX / 2, DISPLAY_RESY - 24, "Failed! Please, reconnect.", -1, FONT_NORMAL, COLOR_BLACK, COLOR_WHITE); diff --git a/core/embed/extmod/modtrezorui/display.c b/core/embed/extmod/modtrezorui/display.c index eba7ecfd28..f7601dd8c6 100644 --- a/core/embed/extmod/modtrezorui/display.c +++ b/core/embed/extmod/modtrezorui/display.c @@ -318,7 +318,7 @@ static void inflate_callback_loader(uint8_t byte, uint32_t pos, void display_loader(uint16_t progress, int yoffset, uint16_t fgcolor, uint16_t bgcolor, const uint8_t *icon, uint32_t iconlen, - uint16_t iconfgcolor) { + uint16_t iconfgcolor, uint16_t slice_span) { #if TREZOR_MODEL == T uint16_t colortable[16], iconcolortable[16]; set_color_table(colortable, fgcolor, bgcolor); @@ -378,7 +378,7 @@ void display_loader(uint16_t progress, int yoffset, uint16_t fgcolor, PIXELDATA(iconcolortable[c]); } else { uint8_t c; - if (progress > a) { + if (progress > a && (slice_span == 0 || progress < (a + slice_span))) { c = (img_loader[my][mx] & 0x00F0) >> 4; } else { c = img_loader[my][mx] & 0x000F; diff --git a/core/embed/extmod/modtrezorui/display.h b/core/embed/extmod/modtrezorui/display.h index 578025d49f..141aba1070 100644 --- a/core/embed/extmod/modtrezorui/display.h +++ b/core/embed/extmod/modtrezorui/display.h @@ -84,7 +84,7 @@ void display_icon(int x, int y, int w, int h, const void *data, int datalen, uint16_t fgcolor, uint16_t bgcolor); void display_loader(uint16_t progress, int yoffset, uint16_t fgcolor, uint16_t bgcolor, const uint8_t *icon, uint32_t iconlen, - uint16_t iconfgcolor); + uint16_t iconfgcolor, uint16_t slice_span); #ifndef TREZOR_PRINT_DISABLE void display_print_color(uint16_t fgcolor, uint16_t bgcolor); diff --git a/core/embed/extmod/modtrezorui/modtrezorui-display.h b/core/embed/extmod/modtrezorui/modtrezorui-display.h index 862cd11991..90c78e6c12 100644 --- a/core/embed/extmod/modtrezorui/modtrezorui-display.h +++ b/core/embed/extmod/modtrezorui/modtrezorui-display.h @@ -198,7 +198,7 @@ STATIC mp_obj_t mod_trezorui_Display_icon(size_t n_args, const mp_obj_t *args) { STATIC MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(mod_trezorui_Display_icon_obj, 6, 6, mod_trezorui_Display_icon); /// def loader(self, progress: int, yoffset: int, fgcolor: int, bgcolor: int, -/// icon: bytes = None, iconfgcolor: int = None) -> None: +/// icon: bytes = None, iconfgcolor: int = None, slice_span: int = None) -> None: /// ''' /// Renders a rotating loader graphic. /// Progress determines its position (0-1000), fgcolor is used as foreground @@ -206,6 +206,8 @@ STATIC MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(mod_trezorui_Display_icon_obj, 6, 6, /// icon is drawn in the middle using the color specified in iconfgcolor. /// Icon needs to be of exactly LOADER_ICON_SIZE x LOADER_ICON_SIZE pixels /// size. +/// If slice_span is defined the progress is sliced to emulate indeterminate +/// loader. /// ''' STATIC mp_obj_t mod_trezorui_Display_loader(size_t n_args, const mp_obj_t *args) { @@ -213,8 +215,11 @@ STATIC mp_obj_t mod_trezorui_Display_loader(size_t n_args, mp_int_t yoffset = mp_obj_get_int(args[2]); mp_int_t fgcolor = mp_obj_get_int(args[3]); mp_int_t bgcolor = mp_obj_get_int(args[4]); - if (n_args > 5) { // icon provided - mp_buffer_info_t icon; + mp_buffer_info_t icon = {.buf = NULL, .len=0}; + uint16_t iconfgcolor = 0; + uint16_t slice_span = 0; + + if (n_args > 5 && args[5] != mp_const_none) { // icon provided mp_get_buffer_raise(args[5], &icon, MP_BUFFER_READ); const uint8_t *data = icon.buf; if (icon.len < 8 || memcmp(data, "TOIg", 4) != 0) { @@ -229,21 +234,25 @@ STATIC mp_obj_t mod_trezorui_Display_loader(size_t n_args, if (datalen != icon.len - 12) { mp_raise_ValueError("Invalid size of data"); } - uint16_t iconfgcolor; + if (n_args > 6) { // icon color provided iconfgcolor = mp_obj_get_int(args[6]); } else { iconfgcolor = ~bgcolor; // invert } - display_loader(progress, yoffset, fgcolor, bgcolor, icon.buf, icon.len, - iconfgcolor); - } else { - display_loader(progress, yoffset, fgcolor, bgcolor, NULL, 0, 0); } + + if (n_args > 7 && args[7] != mp_const_none) { // slice span provided + slice_span = mp_obj_get_int(args[7]); + } + + display_loader(progress, yoffset, fgcolor, bgcolor, icon.buf, icon.len, + iconfgcolor, slice_span); + return mp_const_none; } STATIC MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(mod_trezorui_Display_loader_obj, 5, - 7, mod_trezorui_Display_loader); + 8, mod_trezorui_Display_loader); /// def print(self, text: str) -> None: /// ''' diff --git a/core/mocks/generated/trezorui.py b/core/mocks/generated/trezorui.py index d42a3083c8..178902fede 100644 --- a/core/mocks/generated/trezorui.py +++ b/core/mocks/generated/trezorui.py @@ -51,12 +51,14 @@ class Display: The icon needs to be in TREZOR Optimized Image Format (TOIF) - gray-scale mode. ''' - def loader(self, progress: int, yoffset: int, fgcolor: int, bgcolor: int, icon: bytes = None, iconfgcolor: int = None) -> None: + def loader(self, progress: int, yoffset: int, fgcolor: int, bgcolor: int, icon: bytes = None, iconfgcolor: int = None, slice_span: int = None) -> None: ''' Renders a rotating loader graphic. Progress determines its position (0-1000), fgcolor is used as foreground color, bgcolor as background. When icon and iconfgcolor are provided, an icon is drawn in the middle using the color specified in iconfgcolor. Icon needs to be of exactly LOADER_ICON_SIZE x LOADER_ICON_SIZE pixels size. + If slice_span is defined the progress is sliced to emulate indeterminate + loader. ''' def print(self, text: str) -> None: diff --git a/core/src/apps/monero/layout/confirms.py b/core/src/apps/monero/layout/confirms.py index b2abb37b9c..b02819e1df 100644 --- a/core/src/apps/monero/layout/confirms.py +++ b/core/src/apps/monero/layout/confirms.py @@ -42,7 +42,7 @@ async def require_confirm_tx_key(ctx, export_key=False): return await require_confirm(ctx, content, ButtonRequestType.SignTx) -async def require_confirm_transaction(ctx, tsx_data, network_type): +async def require_confirm_transaction(ctx, state, tsx_data, network_type): """ Ask for confirmation from user. """ @@ -70,10 +70,7 @@ async def require_confirm_transaction(ctx, tsx_data, network_type): await _require_confirm_payment_id(ctx, tsx_data.payment_id) await _require_confirm_fee(ctx, tsx_data.fee) - - text = Text("Signing transaction", ui.ICON_SEND, icon_color=ui.BLUE) - text.normal("Signing...") - text.render() + await transaction_step(state, 0) async def _require_confirm_output(ctx, dst, network_type, payment_id): @@ -119,46 +116,68 @@ async def _require_confirm_fee(ctx, fee): await require_hold_to_confirm(ctx, content, ButtonRequestType.ConfirmOutput) -@ui.layout -async def transaction_step(ctx, step, sub_step=None, sub_step_total=None): +@ui.layout_no_slide +async def transaction_step(state, step, sub_step=None): info = [] - if step == 100: - info = ["Processing inputs", "%d/%d" % (sub_step + 1, sub_step_total)] + if step == 0: + info = ["Signing..."] + elif step == 100: + info = ["Processing inputs", "%d/%d" % (sub_step + 1, state.input_count)] elif step == 200: - info = ["Sorting"] + info = ["Sorting..."] elif step == 300: - info = [ - "Processing inputs", - "phase 2", - "%d/%d" % (sub_step + 1, sub_step_total), - ] + info = ["Hashing inputs", "%d/%d" % (sub_step + 1, state.input_count)] + elif step == 350: + info = ["Processing..."] elif step == 400: - info = ["Processing outputs", "%d/%d" % (sub_step + 1, sub_step_total)] + info = ["Processing outputs", "%d/%d" % (sub_step + 1, state.output_count)] elif step == 500: info = ["Postprocessing..."] elif step == 600: - info = ["Signing inputs", "%d/%d" % (sub_step + 1, sub_step_total)] + info = ["Signing inputs", "%d/%d" % (sub_step + 1, state.input_count)] else: info = ["Processing..."] + state.progress_cur += 1 + + ui.display.clear() text = Text("Signing transaction", ui.ICON_SEND, icon_color=ui.BLUE) - text.normal(*info) text.render() + p = int(1000.0 * state.progress_cur / state.progress_total) + ui.display.loader(p, -4, ui.WHITE, ui.BG) + ui.display.text_center(ui.WIDTH // 2, 210, info[0], ui.NORMAL, ui.FG, ui.BG) + if len(info) > 1: + ui.display.text_center(ui.WIDTH // 2, 235, info[1], ui.NORMAL, ui.FG, ui.BG) + ui.display.refresh() -@ui.layout + +@ui.layout_no_slide async def keyimage_sync_step(ctx, current, total_num): if current is None: return + ui.display.clear() text = Text("Syncing", ui.ICON_SEND, icon_color=ui.BLUE) - text.normal("%d/%d" % (current + 1, total_num)) text.render() + p = (int(1000.0 * (current + 1) / total_num)) if total_num > 0 else 0 + ui.display.loader(p, 18, ui.WHITE, ui.BG) + ui.display.refresh() -@ui.layout + +@ui.layout_no_slide async def live_refresh_step(ctx, current): if current is None: return + ui.display.clear() text = Text("Refreshing", ui.ICON_SEND, icon_color=ui.BLUE) - text.normal("%d" % current) text.render() + + step = 6 + p = int(1000.0 * (current / step)) % 1000 + if p == 0 and current > 0: + p = 1000 + + ui.display.loader(p, 18, ui.WHITE, ui.BG, None, 0, 1000 // step) + ui.display.text_center(ui.WIDTH // 2, 145, "%d" % current, ui.NORMAL, ui.FG, ui.BG) + ui.display.refresh() diff --git a/core/src/apps/monero/signing/state.py b/core/src/apps/monero/signing/state.py index d608d226a6..22c240d871 100644 --- a/core/src/apps/monero/signing/state.py +++ b/core/src/apps/monero/signing/state.py @@ -68,6 +68,9 @@ class State: self.input_count = 0 self.output_count = 0 + self.progress_total = 0 + self.progress_cur = 0 + self.output_change = None self.fee = 0 diff --git a/core/src/apps/monero/signing/step_01_init_transaction.py b/core/src/apps/monero/signing/step_01_init_transaction.py index 9778e53c27..989d16f188 100644 --- a/core/src/apps/monero/signing/step_01_init_transaction.py +++ b/core/src/apps/monero/signing/step_01_init_transaction.py @@ -36,16 +36,19 @@ async def init_transaction( state.mem_trace(1) + state.input_count = tsx_data.num_inputs + state.output_count = len(tsx_data.outputs) + state.progress_total = 4 + 3 * state.input_count + state.output_count + state.progress_cur = 0 + # Ask for confirmation await confirms.require_confirm_transaction( - state.ctx, tsx_data, state.creds.network_type + state.ctx, state, tsx_data, state.creds.network_type ) gc.collect() state.mem_trace(3) # Basic transaction parameters - state.input_count = tsx_data.num_inputs - state.output_count = len(tsx_data.outputs) state.output_change = tsx_data.change_dts state.mixin = tsx_data.mixin state.fee = tsx_data.fee diff --git a/core/src/apps/monero/signing/step_02_set_input.py b/core/src/apps/monero/signing/step_02_set_input.py index af62823264..ce408e83c1 100644 --- a/core/src/apps/monero/signing/step_02_set_input.py +++ b/core/src/apps/monero/signing/step_02_set_input.py @@ -32,9 +32,7 @@ async def set_input(state: State, src_entr: MoneroTransactionSourceEntry): state.current_input_index += 1 - await confirms.transaction_step( - state.ctx, state.STEP_INP, state.current_input_index, state.input_count - ) + await confirms.transaction_step(state, state.STEP_INP, state.current_input_index) if state.current_input_index >= state.input_count: raise ValueError("Too many inputs") diff --git a/core/src/apps/monero/signing/step_03_inputs_permutation.py b/core/src/apps/monero/signing/step_03_inputs_permutation.py index cbd19c36b0..14f538fb7e 100644 --- a/core/src/apps/monero/signing/step_03_inputs_permutation.py +++ b/core/src/apps/monero/signing/step_03_inputs_permutation.py @@ -21,7 +21,7 @@ async def tsx_inputs_permutation(state: State, permutation: list): MoneroTransactionInputsPermutationAck, ) - await transaction_step(state.ctx, state.STEP_PERM) + await transaction_step(state, state.STEP_PERM) """ Set permutation on the inputs - sorted by key image on host. diff --git a/core/src/apps/monero/signing/step_04_input_vini.py b/core/src/apps/monero/signing/step_04_input_vini.py index 874bc09e73..2c88f7965c 100644 --- a/core/src/apps/monero/signing/step_04_input_vini.py +++ b/core/src/apps/monero/signing/step_04_input_vini.py @@ -33,7 +33,7 @@ async def input_vini( ) await confirms.transaction_step( - state.ctx, state.STEP_VINI, state.current_input_index + 1, state.input_count + state, state.STEP_VINI, state.current_input_index + 1 ) if state.current_input_index >= state.input_count: raise ValueError("Too many inputs") diff --git a/core/src/apps/monero/signing/step_05_all_inputs_set.py b/core/src/apps/monero/signing/step_05_all_inputs_set.py index 86b55271c0..dac0d28ad7 100644 --- a/core/src/apps/monero/signing/step_05_all_inputs_set.py +++ b/core/src/apps/monero/signing/step_05_all_inputs_set.py @@ -12,7 +12,7 @@ from apps.monero.xmr import crypto async def all_inputs_set(state: State): state.mem_trace(0) - await confirms.transaction_step(state.ctx, state.STEP_ALL_IN) + await confirms.transaction_step(state, state.STEP_ALL_IN) from trezor.messages.MoneroTransactionAllInputsSetAck import ( MoneroTransactionAllInputsSetAck, diff --git a/core/src/apps/monero/signing/step_06_set_output.py b/core/src/apps/monero/signing/step_06_set_output.py index d0ecc55cf1..6f0e30f167 100644 --- a/core/src/apps/monero/signing/step_06_set_output.py +++ b/core/src/apps/monero/signing/step_06_set_output.py @@ -23,10 +23,7 @@ async def set_output( # Progress update only for master message (skip for offloaded BP msg) if not is_offloaded_bp: await confirms.transaction_step( - state.ctx, - state.STEP_OUT, - state.current_output_index + 1, - state.output_count, + state, state.STEP_OUT, state.current_output_index + 1 ) state.mem_trace(1, True) diff --git a/core/src/apps/monero/signing/step_07_all_outputs_set.py b/core/src/apps/monero/signing/step_07_all_outputs_set.py index e5708a7d9c..e836aaae01 100644 --- a/core/src/apps/monero/signing/step_07_all_outputs_set.py +++ b/core/src/apps/monero/signing/step_07_all_outputs_set.py @@ -18,7 +18,7 @@ from apps.monero.xmr import crypto async def all_outputs_set(state: State): state.mem_trace(0) - await confirms.transaction_step(state.ctx, state.STEP_ALL_OUT) + await confirms.transaction_step(state, state.STEP_ALL_OUT) state.mem_trace(1) _validate(state) diff --git a/core/src/apps/monero/signing/step_09_sign_input.py b/core/src/apps/monero/signing/step_09_sign_input.py index 0a7639e4e0..8b71e146cc 100644 --- a/core/src/apps/monero/signing/step_09_sign_input.py +++ b/core/src/apps/monero/signing/step_09_sign_input.py @@ -48,7 +48,7 @@ async def sign_input( :return: Generated signature MGs[i] """ await confirms.transaction_step( - state.ctx, state.STEP_SIGN, state.current_input_index + 1, state.input_count + state, state.STEP_SIGN, state.current_input_index + 1 ) state.current_input_index += 1