1
0
mirror of https://github.com/trezor/trezor-firmware.git synced 2025-02-13 08:02:01 +00:00

refactor(core): render prodtest UI in rust

[no changelog]
This commit is contained in:
tychovrahe 2025-02-07 16:02:49 +01:00 committed by TychoVrahe
parent 1700841856
commit c3981cdebe
16 changed files with 428 additions and 173 deletions

View File

@ -58,7 +58,7 @@ SOURCE_MOD_CRYPTO += [
# modtrezorui
CPPPATH_MOD += [
'vendor/micropython/lib/uzlib',
'vendor/micropython/lib/uzlib'
]
SOURCE_MOD += [
@ -138,6 +138,23 @@ env.Replace(
env.Replace(
TREZOR_MODEL=TREZOR_MODEL, )
ALLPATHS = [
'embed/rust',
'embed/projects/prodtest',
'embed/rtl/inc',
'embed/models',
'embed/gfx/inc',
'embed/sys/bsp/inc',
'embed/util/image/inc',
'embed/util/rsod/inc',
'embed/util/scm_revision/inc',
'embed/util/translations/inc',
'embed/upymod/modtrezorui',
]
ALLPATHS += CPPPATH_MOD + PATH_HAL
env.Replace(
COPT=env.get('ENV').get('OPTIMIZE', '-Os'),
CCFLAGS='$COPT '
@ -149,18 +166,8 @@ env.Replace(
'-fstack-protector-all '
+ env.get('ENV')["CPU_CCFLAGS"] + CCFLAGS_MOD,
CCFLAGS_QSTR='-DNO_QSTR -DN_X64 -DN_X86 -DN_THUMB',
LINKFLAGS=f'-T build/prodtest/memory.ld -Wl,--gc-sections -Wl,--print-memory-usage -Wl,-Map=build/prodtest/prodtest.map -Wl,--warn-common',
CPPPATH=[
'embed/projects/prodtest',
'embed/rtl/inc',
'embed/models',
'embed/gfx/inc',
'embed/sys/bsp/inc',
'embed/util/image/inc',
'embed/util/rsod/inc',
'embed/util/scm_revision/inc',
'embed/upymod/modtrezorui',
] + CPPPATH_MOD + PATH_HAL,
LINKFLAGS=['-Tbuild/prodtest/memory.ld', '-Wl,--gc-sections', '-Wl,--print-memory-usage', '-Wl,-Map=build/prodtest/prodtest.map', '-Wl,--warn-common'],
CPPPATH=ALLPATHS,
CPPDEFINES=[
'TREZOR_PRODTEST',
'TREZOR_MODEL_'+TREZOR_MODEL,
@ -197,6 +204,20 @@ obj_program.extend(env.Object(source=SOURCE_MOD_CRYPTO, CCFLAGS='$CCFLAGS -ftriv
obj_program.extend(env.Object(source=SOURCE_PRODTEST))
obj_program.extend(env.Object(source=SOURCE_HAL))
#
# Rust library
#
features = ['prodtest',] + FEATURES_AVAILABLE + RUST_UI_FEATURES
rust = tools.add_rust_lib(
env,
'prodtest',
'release',
features,
ALLPATHS,
str(Dir('.').abspath))
if (vh := ARGUMENTS.get("VENDOR_HEADER", None)):
VENDORHEADER = vh
@ -232,10 +253,11 @@ program_elf = env.Command(
target='prodtest.elf',
source=obj_program,
action=
'$LINK -o $TARGET $CCFLAGS $CFLAGS $LINKFLAGS $SOURCES -lc_nano -lgcc -lm',
'$LINK -o $TARGET $CCFLAGS $CFLAGS $SOURCES $LINKFLAGS -lc_nano -lgcc -lm',
)
env.Depends(program_elf, linkerscript_gen)
env.Depends(program_elf, rust)
BINARY_NAME = f"build/prodtest/prodtest-{TREZOR_MODEL}"
BINARY_NAME += "-" + tools.get_version('embed/projects/prodtest/version.h')

View File

@ -17,9 +17,9 @@
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include <rust_ui_prodtest.h>
#include <trezor_rtl.h>
#include <gfx/gfx_draw.h>
#include <io/display.h>
#include <rtl/cli.h>
@ -29,24 +29,14 @@ static void prodtest_display_border(cli_t* cli) {
return;
}
gfx_clear();
cli_trace(cli, "Drawing display border...");
gfx_rect_t r_out = gfx_rect_wh(0, 0, DISPLAY_RESX, DISPLAY_RESY);
gfx_rect_t r_in = gfx_rect_wh(1, 1, DISPLAY_RESX - 2, DISPLAY_RESY - 2);
gfx_draw_bar(r_out, COLOR_WHITE);
gfx_draw_bar(r_in, COLOR_BLACK);
display_refresh();
screen_prodtest_border();
cli_ok(cli, "");
}
static void prodtest_display_bars(cli_t* cli) {
gfx_clear();
const char* colors = cli_arg(cli, "colors");
size_t color_count = strlen(colors);
@ -59,34 +49,13 @@ static void prodtest_display_bars(cli_t* cli) {
cli_trace(cli, "Drawing %d vertical bars...", color_count);
screen_prodtest_bars(colors, color_count);
for (size_t i = 0; i < color_count; i++) {
gfx_color_t c = COLOR_BLACK; // black
switch (colors[i]) {
case 'R':
case 'r':
c = gfx_color_rgb(255, 0, 0);
break;
case 'G':
case 'g':
c = gfx_color_rgb(0, 255, 0);
break;
case 'B':
case 'b':
c = gfx_color_rgb(0, 0, 255);
break;
case 'W':
case 'w':
c = COLOR_WHITE;
break;
default:
invalid_color = true;
break;
if (strchr("RGBWrgbw", colors[i]) == NULL) {
invalid_color = true;
break;
}
int x1 = (DISPLAY_RESX * i) / color_count;
int x2 = (DISPLAY_RESX * (i + 1)) / color_count;
gfx_draw_bar(gfx_rect(x1, 0, x2, DISPLAY_RESY), c);
}
if (strlen(colors) == 0 || invalid_color) {
@ -102,7 +71,7 @@ static void prodtest_display_set_backlight(cli_t* cli) {
uint32_t level = 0;
if (!cli_arg_uint32(cli, "level", &level) || level > 255) {
cli_error_arg(cli, "Expecting backlig level in range 0-255 (100%%).");
cli_error_arg(cli, "Expecting backlight level in range 0-255 (100%%).");
return;
}

View File

@ -17,26 +17,15 @@
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include <rust_ui_prodtest.h>
#include <trezor_model.h>
#include <trezor_rtl.h>
#include <gfx/fonts.h>
#include <gfx/gfx_draw.h>
#include <io/display.h>
#include <rtl/cli.h>
#include <sys/bootutils.h>
#include <sys/mpu.h>
#include <sys/systick.h>
#include <util/fwutils.h>
#include <version.h>
static gfx_text_attr_t bold = {
.font = FONT_BOLD,
.fg_color = COLOR_WHITE,
.bg_color = COLOR_BLACK,
};
static void prodtest_prodtest_intro(cli_t* cli) {
cli_trace(cli, "Welcome to Trezor %s Production Test Firmware v%d.%d.%d.",
MODEL_NAME, VERSION_MAJOR, VERSION_MINOR, VERSION_PATCH);
@ -63,10 +52,8 @@ static void prodtest_prodtest_wipe(cli_t* cli) {
cli_trace(cli, "Invalidating the production test firmware header...");
firmware_invalidate_header();
gfx_clear();
gfx_offset_t pos = gfx_offset(DISPLAY_RESX / 2, DISPLAY_RESY / 2 + 10);
gfx_draw_text(pos, "WIPED", -1, &bold, GFX_ALIGN_CENTER);
display_refresh();
const char msg[] = "WIPED";
screen_prodtest_show_text(msg, strlen(msg));
cli_ok(cli, "");
}

View File

@ -17,23 +17,16 @@
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include <rust_ui_prodtest.h>
#ifdef USE_TOUCH
#include <trezor_rtl.h>
#include <gfx/fonts.h>
#include <gfx/gfx_draw.h>
#include <io/display.h>
#include <io/touch.h>
#include <rtl/cli.h>
#include <sys/systick.h>
const static gfx_text_attr_t bold = {
.font = FONT_BOLD,
.fg_color = COLOR_WHITE,
.bg_color = COLOR_BLACK,
};
static bool ensure_touch_init(cli_t* cli) {
cli_trace(cli, "Initializing the touch controller...");
if (sectrue != touch_init()) {
@ -108,27 +101,24 @@ static void prodtest_touch_test(cli_t* cli) {
return;
}
const int width = DISPLAY_RESX / 2;
const int height = DISPLAY_RESY / 2;
const int16_t width = DISPLAY_RESX / 2;
const int16_t height = DISPLAY_RESY / 2;
gfx_clear();
switch (position) {
case 1:
gfx_draw_bar(gfx_rect_wh(0, 0, width, height), COLOR_WHITE);
screen_prodtest_touch(0, 0, width, height);
break;
case 2:
gfx_draw_bar(gfx_rect_wh(width, 0, width, height), COLOR_WHITE);
screen_prodtest_touch(width, 0, width, height);
break;
case 3:
gfx_draw_bar(gfx_rect_wh(width, height, width, height), COLOR_WHITE);
screen_prodtest_touch(width, height, width, height);
break;
default:
gfx_draw_bar(gfx_rect_wh(0, height, width, height), COLOR_WHITE);
screen_prodtest_touch(0, height, width, height);
break;
}
display_refresh();
uint32_t event = 0;
if (touch_click_timeout(cli, &event, timeout)) {
uint16_t x = touch_unpack_x(event);
@ -140,8 +130,7 @@ static void prodtest_touch_test(cli_t* cli) {
}
}
gfx_clear();
display_refresh();
screen_prodtest_welcome();
}
static void prodtest_touch_test_custom(cli_t* cli) {
@ -188,9 +177,7 @@ static void prodtest_touch_test_custom(cli_t* cli) {
cli_trace(cli, "Drawing a rectangle at [%d, %d] with size [%d x %d]...", x, y,
width, height);
gfx_clear();
gfx_draw_bar(gfx_rect_wh(x, y, width, height), COLOR_WHITE);
display_refresh();
screen_prodtest_touch(x, y, width, height);
uint32_t expire_time = ticks_timeout(timeout);
@ -226,8 +213,7 @@ static void prodtest_touch_test_custom(cli_t* cli) {
}
}
gfx_clear();
display_refresh();
screen_prodtest_welcome();
}
static void prodtest_touch_test_idle(cli_t* cli) {
@ -243,10 +229,8 @@ static void prodtest_touch_test_idle(cli_t* cli) {
return;
}
gfx_clear();
gfx_offset_t pos = gfx_offset(DISPLAY_RESX / 2, DISPLAY_RESY / 2);
gfx_draw_text(pos, "DON'T TOUCH", -1, &bold, GFX_ALIGN_CENTER);
display_refresh();
const char msg[] = "DON'T TOUCH";
screen_prodtest_show_text(msg, strlen(msg));
if (!ensure_touch_init(cli)) {
return;
@ -273,8 +257,7 @@ static void prodtest_touch_test_idle(cli_t* cli) {
cli_ok(cli, "");
cleanup:
gfx_clear();
display_refresh();
screen_prodtest_welcome();
}
static void prodtest_touch_test_power(cli_t* cli) {
@ -290,10 +273,8 @@ static void prodtest_touch_test_power(cli_t* cli) {
return;
}
gfx_clear();
gfx_offset_t pos = gfx_offset(DISPLAY_RESX / 2, DISPLAY_RESY / 2);
gfx_draw_text(pos, "MEASURING", -1, &bold, GFX_ALIGN_CENTER);
display_refresh();
const char text[] = "MEASURING";
screen_prodtest_show_text(text, strlen(text));
cli_trace(cli, "Setting touch controller power for %d ms...", timeout);
@ -311,8 +292,7 @@ static void prodtest_touch_test_power(cli_t* cli) {
cleanup:
touch_power_set(false);
gfx_clear();
display_refresh();
screen_prodtest_welcome();
}
static void prodtest_touch_test_sensitivity(cli_t* cli) {
@ -338,28 +318,22 @@ static void prodtest_touch_test_sensitivity(cli_t* cli) {
cli_trace(cli, "Running touch controller test...");
cli_trace(cli, "Press CTRL+C for exit.");
gfx_clear();
display_refresh();
for (;;) {
uint32_t evt = touch_get_event();
if (evt & TOUCH_START || evt & TOUCH_MOVE) {
int x = touch_unpack_x(evt);
int y = touch_unpack_y(evt);
gfx_clear();
gfx_draw_bar(gfx_rect_wh(x - 48, y - 48, 96, 96), COLOR_WHITE);
display_refresh();
screen_prodtest_touch(x - 48, y - 48, 96, 96);
} else if (evt & TOUCH_END) {
gfx_clear();
display_refresh();
screen_prodtest_touch(0, 0, 0, 0);
}
if (cli_aborted(cli)) {
break;
}
}
gfx_clear();
display_refresh();
screen_prodtest_welcome();
}
// clang-format off

View File

@ -20,18 +20,15 @@
#include <trezor_model.h>
#include <trezor_rtl.h>
#include <gfx/fonts.h>
#include <gfx/gfx_draw.h>
#include <io/display.h>
#include <io/display_utils.h>
#include <io/usb.h>
#include <rtl/cli.h>
#include <sys/system.h>
#include <sys/systick.h>
#include <util/flash.h>
#include <util/flash_otp.h>
#include <util/rsod.h>
#include "rust_ui_prodtest.h"
#ifdef USE_BUTTON
#include <io/button.h>
#endif
@ -144,53 +141,17 @@ static void usb_init_all(void) {
ensure(usb_start(), NULL);
}
static inline gfx_rect_t gfx_rect_shrink(gfx_rect_t r, int padding) {
gfx_rect_t result = {
.x0 = r.x0 + padding,
.y0 = r.y0 + padding,
.x1 = r.x1 - padding,
.y1 = r.y1 - padding,
};
return result;
}
static void draw_welcome_screen(void) {
gfx_clear();
gfx_rect_t r = gfx_rect_wh(0, 0, DISPLAY_RESX, DISPLAY_RESY);
uint8_t qr_scale = 4;
int16_t text_offset = 30;
gfx_text_attr_t bold = {
.font = FONT_BOLD,
.fg_color = COLOR_WHITE,
.bg_color = COLOR_BLACK,
};
#if defined TREZOR_MODEL_T2B1 || defined TREZOR_MODEL_T3B1
gfx_draw_bar(r, COLOR_WHITE);
qr_scale = 2;
text_offset = 9;
bold.fg_color = COLOR_BLACK;
bold.bg_color = COLOR_WHITE;
#else
gfx_draw_bar(gfx_rect_shrink(r, 3), COLOR_WHITE);
gfx_draw_bar(gfx_rect_shrink(r, 4), COLOR_BLACK);
#endif
char dom[32];
static void show_welcome_screen(void) {
char dom[32] = {0};
// format: {MODEL_IDENTIFIER}YYMMDD
if (sectrue == flash_otp_read(FLASH_OTP_BLOCK_BATCH, 0, (uint8_t *)dom, 32) &&
dom[31] == 0 && cstr_starts_with(dom, MODEL_IDENTIFIER)) {
gfx_offset_t pos;
pos = gfx_offset(DISPLAY_RESX / 2, DISPLAY_RESY / 2);
gfx_draw_qrcode(pos, qr_scale, dom);
pos = gfx_offset(DISPLAY_RESX / 2, DISPLAY_RESY - text_offset);
gfx_draw_text(pos, dom + sizeof(MODEL_IDENTIFIER) - 1, -1, &bold,
GFX_ALIGN_CENTER);
if ((sectrue ==
flash_otp_read(FLASH_OTP_BLOCK_BATCH, 0, (uint8_t *)dom, 32) &&
dom[31] == 0 && cstr_starts_with(dom, MODEL_IDENTIFIER))) {
screen_prodtest_info(dom, strlen(dom), dom + sizeof(MODEL_IDENTIFIER) - 1,
strlen(dom) - sizeof(MODEL_IDENTIFIER) + 1);
} else {
screen_prodtest_welcome();
}
display_refresh();
}
static void drivers_init(void) {
@ -230,9 +191,7 @@ int main(void) {
drivers_init();
usb_init_all();
// Draw welcome screen
draw_welcome_screen();
display_fade(0, BACKLIGHT_NORMAL, 1000);
show_welcome_screen();
// Initialize command line interface
cli_init(&g_cli, console_read, console_write, NULL);

View File

@ -29,6 +29,7 @@ ui_empty_lock = []
ui_jpeg = []
hw_jpeg_decoder = []
bootloader = []
prodtest = []
button = []
touch = []
clippy = []

View File

@ -0,0 +1,14 @@
#include <trezor_types.h>
void screen_prodtest_info(char* id, uint8_t id_len, char* date,
uint8_t date_len);
void screen_prodtest_welcome(void);
void screen_prodtest_bars(const char* colors, size_t color_count);
void screen_prodtest_show_text(const char* text, uint8_t text_len);
void screen_prodtest_touch(int16_t x0, int16_t y0, int16_t w, int16_t h);
void screen_prodtest_border(void);

View File

@ -5,3 +5,6 @@ pub mod bootloader_c;
#[cfg(feature = "micropython")]
pub mod firmware_micropython;
#[cfg(feature = "prodtest")]
pub mod prodtest_c;

View File

@ -0,0 +1,49 @@
use crate::ui::{ui_prodtest::ProdtestUI, util::from_c_array, ModelUI};
#[cfg(feature = "touch")]
use crate::ui::geometry::{Offset, Point, Rect};
#[cfg(feature = "touch")]
use cty::int16_t;
#[no_mangle]
extern "C" fn screen_prodtest_welcome() {
ModelUI::screen_prodtest_welcome();
}
#[no_mangle]
extern "C" fn screen_prodtest_info(
id: *const cty::c_char,
id_len: u8,
date: *const cty::c_char,
date_len: u8,
) {
let id = unwrap!(unsafe { from_c_array(id, id_len as usize) });
let date = unwrap!(unsafe { from_c_array(date, date_len as usize) });
ModelUI::screen_prodtest_info(id, date);
}
#[no_mangle]
extern "C" fn screen_prodtest_show_text(text: *const cty::c_char, text_len: u8) {
let text = unwrap!(unsafe { from_c_array(text, text_len as usize) });
ModelUI::screen_prodtest_show_text(text);
}
#[no_mangle]
extern "C" fn screen_prodtest_border() {
ModelUI::screen_prodtest_border();
}
#[no_mangle]
extern "C" fn screen_prodtest_bars(colors: *const cty::c_char, colors_len: u8) {
let colors: &str = unwrap!(unsafe { from_c_array(colors, colors_len as usize) });
ModelUI::screen_prodtest_bars(colors);
}
#[no_mangle]
#[cfg(feature = "touch")]
extern "C" fn screen_prodtest_touch(x0: int16_t, y0: int16_t, w: int16_t, h: int16_t) {
let area = Rect::from_top_left_and_size(Point::new(x0, y0), Offset::new(w, h));
ModelUI::screen_prodtest_touch(area);
}

View File

@ -23,6 +23,9 @@ pub struct UIBolt;
#[cfg(feature = "micropython")]
pub mod ui_firmware;
#[cfg(feature = "prodtest")]
pub mod prodtest;
impl CommonUI for UIBolt {
#[cfg(feature = "backlight")]
fn fadein() {

View File

@ -0,0 +1,136 @@
use crate::ui::{
component::{base::Component, Qr},
constant::screen,
display,
display::Color,
geometry::{Alignment, Offset, Rect},
layout_bolt::{fonts, theme, UIBolt},
shape,
shape::render_on_display,
ui_prodtest::ProdtestUI,
};
impl ProdtestUI for UIBolt {
fn screen_prodtest_welcome() {
display::sync();
render_on_display(None, Some(Color::black()), |target| {
let area = screen();
shape::Bar::new(area)
.with_fg(Color::white())
.with_thickness(1)
.render(target);
});
display::refresh();
display::fade_backlight_duration(theme::backlight::get_backlight_normal(), 150);
}
fn screen_prodtest_info(id: &str, date: &str) {
display::sync();
let qr = Qr::new(id, true);
let mut qr = unwrap!(qr).with_border(4);
// place the qr in the middle of the screen and size it to half the screen
let qr_width = screen().width() / 2;
let qr_area = Rect::from_center_and_size(screen().center(), Offset::uniform(qr_width));
qr.place(qr_area);
render_on_display(None, Some(Color::black()), |target| {
let area = screen();
shape::Bar::new(area).with_fg(Color::white()).render(target);
qr.render(target);
shape::Text::new(
screen().bottom_center() - Offset::y(10),
date,
fonts::FONT_BOLD_UPPER,
)
.with_fg(Color::white())
.with_align(Alignment::Center)
.render(target);
});
display::refresh();
display::fade_backlight_duration(theme::backlight::get_backlight_normal(), 150);
}
fn screen_prodtest_show_text(text: &str) {
display::sync();
render_on_display(None, Some(Color::black()), |target| {
shape::Text::new(screen().center(), text, fonts::FONT_BOLD_UPPER)
.with_fg(Color::white())
.with_align(Alignment::Center)
.render(target);
});
display::refresh();
display::fade_backlight_duration(theme::backlight::get_backlight_normal(), 150);
}
fn screen_prodtest_border() {
display::sync();
render_on_display(None, Some(Color::black()), |target| {
let area = screen();
shape::Bar::new(area)
.with_fg(Color::white())
.with_thickness(1)
.render(target);
});
display::refresh();
display::fade_backlight_duration(theme::backlight::get_backlight_normal(), 150);
}
fn screen_prodtest_bars(colors: &str) {
display::sync();
let num_colors = colors.chars().count();
let width = if num_colors > 0 {
screen().width() / num_colors as i16
} else {
0
};
render_on_display(None, Some(Color::black()), |target| {
for (i, c) in colors.chars().enumerate() {
let color = match c {
'r' | 'R' => Color::rgb(255, 0, 0),
'g' | 'G' => Color::rgb(0, 255, 0),
'b' | 'B' => Color::rgb(0, 0, 255),
'w' | 'W' => Color::white(),
_ => Color::black(),
};
let area = Rect::from_top_left_and_size(
screen().top_left() + Offset::x(i as i16 * width),
Offset::new(width, screen().height()),
);
shape::Bar::new(area)
.with_fg(color)
.with_bg(color)
.render(target);
}
});
display::refresh();
display::fade_backlight_duration(theme::backlight::get_backlight_normal(), 150);
}
fn screen_prodtest_touch(area: Rect) {
display::sync();
render_on_display(None, Some(Color::black()), |target| {
shape::Bar::new(area)
.with_fg(Color::white())
.with_bg(Color::white())
.render(target);
});
display::refresh();
display::set_backlight(theme::backlight::get_backlight_normal());
}
}

View File

@ -1,4 +1,4 @@
#[cfg(not(feature = "bootloader"))]
#[cfg(not(any(feature = "bootloader", feature = "prodtest")))]
use crate::storage;
// Typical backlight values.
@ -9,24 +9,24 @@ const BACKLIGHT_NONE: u8 = 0;
const BACKLIGHT_MIN: u8 = 10;
const BACKLIGHT_MAX: u8 = 255;
#[cfg(feature = "bootloader")]
#[cfg(any(feature = "bootloader", feature = "prodtest"))]
pub fn get_backlight_normal() -> u8 {
BACKLIGHT_NORMAL
}
#[cfg(not(feature = "bootloader"))]
#[cfg(not(any(feature = "bootloader", feature = "prodtest")))]
pub fn get_backlight_normal() -> u8 {
storage::get_brightness()
.unwrap_or(BACKLIGHT_NORMAL)
.clamp(BACKLIGHT_MIN, BACKLIGHT_MAX)
}
#[cfg(feature = "bootloader")]
#[cfg(any(feature = "bootloader", feature = "prodtest"))]
pub fn get_backlight_low() -> u8 {
BACKLIGHT_LOW
}
#[cfg(not(feature = "bootloader"))]
#[cfg(not(any(feature = "bootloader", feature = "prodtest")))]
pub fn get_backlight_low() -> u8 {
storage::get_brightness()
.unwrap_or(BACKLIGHT_LOW)

View File

@ -2,6 +2,10 @@ use super::{geometry::Rect, CommonUI};
#[cfg(feature = "bootloader")]
pub mod bootloader;
#[cfg(feature = "prodtest")]
pub mod prodtest;
pub mod common_messages;
pub mod component;
#[cfg(feature = "micropython")]

View File

@ -0,0 +1,115 @@
use crate::ui::{
component::{base::Component, Qr},
constant::screen,
display,
display::Color,
geometry::{Alignment, Offset, Rect},
layout_caesar::{fonts, UICaesar},
shape,
shape::render_on_display,
ui_prodtest::ProdtestUI,
};
impl ProdtestUI for UICaesar {
fn screen_prodtest_welcome() {
display::sync();
render_on_display(None, Some(Color::black()), |target| {
let area = screen();
shape::Bar::new(area)
.with_fg(Color::white())
.with_bg(Color::white())
.render(target);
});
display::refresh();
}
fn screen_prodtest_info(id: &str, date: &str) {
display::sync();
let qr = Qr::new(id, true);
let mut qr = unwrap!(qr).with_border(1);
let qr_width = 50;
let qr_area = Rect::from_center_and_size(screen().center(), Offset::uniform(qr_width))
.translate(Offset::y(-5));
qr.place(qr_area);
render_on_display(None, Some(Color::black()), |target| {
qr.render(target);
shape::Text::new(screen().bottom_center(), date, fonts::FONT_BOLD_UPPER)
.with_fg(Color::white())
.with_align(Alignment::Center)
.render(target);
});
display::refresh();
}
fn screen_prodtest_show_text(text: &str) {
display::sync();
render_on_display(None, Some(Color::black()), |target| {
shape::Text::new(screen().center(), text, fonts::FONT_BOLD_UPPER)
.with_fg(Color::white())
.with_align(Alignment::Center)
.render(target);
});
display::refresh();
}
fn screen_prodtest_border() {
display::sync();
render_on_display(None, Some(Color::black()), |target| {
let area = screen();
shape::Bar::new(area)
.with_fg(Color::white())
.with_thickness(1)
.render(target);
});
display::refresh();
}
fn screen_prodtest_bars(colors: &str) {
display::sync();
let num_colors = colors.chars().count();
let width = if num_colors > 0 {
screen().width() / num_colors as i16
} else {
0
};
render_on_display(None, Some(Color::black()), |target| {
for (i, c) in colors.chars().enumerate() {
let color = match c {
'r' | 'R' => Color::rgb(255, 0, 0),
'g' | 'G' => Color::rgb(0, 255, 0),
'b' | 'B' => Color::rgb(0, 0, 255),
'w' | 'W' => Color::white(),
_ => Color::black(),
};
let area = Rect::from_top_left_and_size(
screen().top_left() + Offset::x(i as i16 * width),
Offset::new(width, screen().height()),
);
shape::Bar::new(area)
.with_fg(color)
.with_bg(color)
.render(target);
}
});
display::refresh();
}
fn screen_prodtest_touch(_area: Rect) {
unimplemented!();
}
}

View File

@ -26,6 +26,10 @@ pub mod layout_delizia;
#[cfg(feature = "bootloader")]
pub mod ui_bootloader;
#[cfg(feature = "prodtest")]
pub mod ui_prodtest;
pub mod ui_common;
#[cfg(feature = "micropython")]
pub mod ui_firmware;

View File

@ -0,0 +1,15 @@
use crate::ui::geometry::Rect;
pub trait ProdtestUI {
fn screen_prodtest_welcome();
fn screen_prodtest_info(id: &str, date: &str);
fn screen_prodtest_show_text(text: &str);
fn screen_prodtest_border();
fn screen_prodtest_bars(colors: &str);
fn screen_prodtest_touch(area: Rect);
}