29 lv_font_glyph_dsc_t g;
30 if (!lv_font_get_glyph_dsc(f, &g,
'0', 0)) {
return 0; }
31 const lv_coord_t ink_mid = f->line_height - f->base_line - g.ofs_y - (g.box_h / 2);
32 return (f->line_height / 2) - ink_mid;
47 lv_coord_t stop = r.x2;
52 lv_coord_t x = (r.x2 > stop) ? (stop - r.x2) : 0;
82 if (on) { lv_obj_clear_state(
s_charge_btn, LV_STATE_DISABLED); }
83 else { lv_obj_add_state(
s_charge_btn, LV_STATE_DISABLED); }
89 lv_obj_set_style_text_color(lbl, on ?
COL_BG :
COL_DIM, LV_PART_MAIN);
115 snprintf(buf,
sizeof(buf),
"%" PRIu64
".%02" PRIu64,
121 snprintf(buf,
sizeof(buf),
".%02" PRIu64,
s_amount_cents % 100ULL);
170 lv_obj_set_style_pad_ver(kb, 0, LV_PART_MAIN);
171 lv_obj_set_style_pad_row(kb, 0, LV_PART_MAIN);
185 lv_obj_draw_part_dsc_t *dsc = lv_event_get_draw_part_dsc(e);
186 if ((dsc == NULL) || (dsc->class_p != &lv_btnmatrix_class) ||
187 (dsc->type != LV_BTNMATRIX_DRAW_PART_BTN)) {
190 const char *key = lv_btnmatrix_get_btn_text(lv_event_get_target(e), dsc->id);
191 if ((key == NULL) || (strcmp(key, LV_SYMBOL_BACKSPACE) != 0)) {
return; }
195 if (lv_event_get_code(e) == LV_EVENT_DRAW_PART_BEGIN) {
196 if (dsc->label_dsc != NULL) { dsc->label_dsc->opa = LV_OPA_TRANSP; }
200 const lv_coord_t cx = (dsc->draw_area->x1 + dsc->draw_area->x2) / 2;
201 const lv_coord_t cy = (dsc->draw_area->y1 + dsc->draw_area->y2) / 2;
203 lv_draw_line_dsc_t ld;
204 lv_draw_line_dsc_init(&ld);
212 const lv_coord_t xr = (lv_coord_t)(cx +
BSP_W);
213 const lv_coord_t xt = (lv_coord_t)(cx -
BSP_W);
215 const lv_coord_t xn = (lv_coord_t)(nx -
BSP_NOSE_DX);
216 const lv_coord_t l = (lv_coord_t)(nx +
BSP_R);
217 const lv_coord_t r = (lv_coord_t)(xr -
BSP_R);
218 const lv_coord_t t = (lv_coord_t)(cy -
BSP_H);
219 const lv_coord_t b = (lv_coord_t)(cy +
BSP_H);
220 const lv_coord_t tr = (lv_coord_t)(t +
BSP_R);
221 const lv_coord_t br = (lv_coord_t)(b -
BSP_R);
222 const lv_coord_t tn = (lv_coord_t)(t +
BSP_NOSE_DY);
223 const lv_coord_t bn = (lv_coord_t)(b -
BSP_NOSE_DY);
227 const lv_coord_t xd = (lv_coord_t)(r +
BSP_R45);
228 const lv_coord_t td = (lv_coord_t)(t +
BSP_R -
BSP_R45);
229 const lv_coord_t bd = (lv_coord_t)(b -
BSP_R +
BSP_R45);
233 const lv_point_t seg[11][2] = {
234 { { l, t }, { r, t } },
235 { { r, t }, { xd, td } },
236 { { xd, td }, { xr, tr } },
237 { { xr, tr }, { xr, br } },
238 { { xr, br }, { xd, bd } },
239 { { xd, bd }, { r, b } },
240 { { r, b }, { l, b } },
241 { { l, b }, { xn, bn } },
242 { { xn, bn }, { xt, cy } },
243 { { xt, cy }, { xn, tn } },
244 { { xn, tn }, { l, t } },
246 for (
int i = 0; i < 11; i++) {
247 lv_draw_line(dsc->draw_ctx, &ld, &seg[i][0], &seg[i][1]);
251 const lv_coord_t kx = cx + (
BSP_NOSE / 2);
256 lv_draw_line(dsc->draw_ctx, &ld, &a1, &a2);
257 lv_draw_line(dsc->draw_ctx, &ld, &b1, &b2);
263 lv_obj_t *bm = lv_event_get_target(e);
264 const char *txt = lv_btnmatrix_get_btn_text(bm, lv_btnmatrix_get_selected_btn(bm));
265 if (txt == NULL) {
return; }
268 if (strcmp(txt, LV_SYMBOL_BACKSPACE) == 0) {
270 }
else if (strcmp(txt,
"00") == 0) {
272 }
else if ((txt[0] >=
'0') && (txt[0] <=
'9') && (txt[1] ==
'\0')) {
274 static_cast<unsigned>(txt[0] -
'0'), cap);
288 if (ta == NULL) {
return; }
289 const bool masked = lv_textarea_get_password_mode(ta);
290 lv_textarea_set_password_mode(ta, !masked);
291 if (eye_lbl != NULL) {
292 lv_label_set_text(eye_lbl, masked ? LV_SYMBOL_EYE_CLOSE
293 : LV_SYMBOL_EYE_OPEN);
299 lv_obj_set_style_bg_color(lv_scr_act(), lv_color_white(), LV_PART_MAIN);
301 lv_obj_t *logo = lv_img_create(lv_scr_act());
302 lv_img_set_src(logo, &logo_img);
308 make_label(lv_scr_act(),
"cryptnox-pos", lv_color_black(),
313 lv_obj_t *sp = lv_spinner_create(lv_scr_act(), 1000, 60);
314 lv_obj_set_size(sp, 28, 28);
315 lv_obj_align(sp, LV_ALIGN_BOTTOM_MID, 0, -48);
316 lv_obj_set_style_arc_width(sp, 3, LV_PART_MAIN);
317 lv_obj_set_style_arc_width(sp, 3, LV_PART_INDICATOR);
318 lv_obj_set_style_arc_color(sp,
COL_SURFACE, LV_PART_MAIN);
319 lv_obj_set_style_arc_color(sp,
COL_ACCENT, LV_PART_INDICATOR);
325 LV_ALIGN_BOTTOM_MID, 0, -20);
328 lv_obj_set_style_text_align(
s_boot_step_lbl, LV_TEXT_ALIGN_CENTER, LV_PART_MAIN);
345 lv_obj_t *row = lv_obj_create(card);
346 lv_obj_remove_style_all(row);
347 lv_obj_set_size(row, LV_SIZE_CONTENT, LV_SIZE_CONTENT);
348 lv_obj_clear_flag(row, LV_OBJ_FLAG_SCROLLABLE);
349 lv_obj_set_flex_flow(row, LV_FLEX_FLOW_ROW);
350 lv_obj_set_flex_align(row, LV_FLEX_ALIGN_CENTER, LV_FLEX_ALIGN_END,
351 LV_FLEX_ALIGN_CENTER);
360 static const char *amap[] = {
364 "00",
"0", LV_SYMBOL_BACKSPACE,
""
366 lv_obj_t *kb = lv_btnmatrix_create(card);
367 lv_btnmatrix_set_map(kb, amap);
369 lv_obj_align(kb, LV_ALIGN_TOP_MID, 0, 58);
370 lv_obj_set_style_bg_opa(kb, LV_OPA_TRANSP, LV_PART_MAIN);
371 lv_obj_set_style_border_width(kb, 0, LV_PART_MAIN);
374 lv_obj_set_style_pad_hor(kb, 0, LV_PART_MAIN);
377 lv_obj_set_style_bg_opa(kb, LV_OPA_TRANSP, LV_PART_ITEMS);
378 lv_obj_set_style_bg_color(kb,
COL_SURFACE, LV_PART_ITEMS | LV_STATE_PRESSED);
379 lv_obj_set_style_bg_opa(kb, LV_OPA_COVER, LV_PART_ITEMS | LV_STATE_PRESSED);
380 lv_obj_set_style_border_width(kb, 0, LV_PART_ITEMS);
381 lv_obj_set_style_shadow_width(kb, 0, LV_PART_ITEMS);
382 lv_obj_set_style_text_color(kb,
COL_TEXT, LV_PART_ITEMS);
384 lv_obj_set_style_radius(kb, 8, LV_PART_ITEMS);
385 lv_obj_add_event_cb(kb,
amount_kbd_cb, LV_EVENT_VALUE_CHANGED, NULL);
430 lv_obj_align_to(cusdc, amt, LV_ALIGN_OUT_RIGHT_MID, 6, 0);
436 lv_obj_align_to(tick, cusdc, LV_ALIGN_OUT_RIGHT_MID, 4, 0);
452 lv_label_set_long_mode(addr, LV_LABEL_LONG_WRAP);
453 lv_obj_set_width(addr, VW);
461 lv_label_set_long_mode(ctr, LV_LABEL_LONG_WRAP);
462 lv_obj_set_width(ctr, VW);
468 lv_obj_t *tn =
make_label(card,
"Test network - no real funds",
470 LV_ALIGN_DEFAULT, 0, 0);
471 lv_obj_update_layout(ctr);
472 lv_obj_align_to(tn, ctr, LV_ALIGN_OUT_BOTTOM_LEFT, 0, 6);
488 const char *p = lv_textarea_get_text(
s_pin_ta);
489 size_t len = (p != NULL) ? strlen(p) : 0U;
490 if (len < 4U) {
return; }
501 strncpy(
s_card_note,
"Reading your payout addresses",
517 lv_obj_t *bm = lv_event_get_target(e);
518 uint32_t
id = lv_btnmatrix_get_selected_btn(bm);
519 const char *txt = lv_btnmatrix_get_btn_text(bm,
id);
520 if ((txt == NULL) || (
s_pin_ta == NULL)) {
return; }
522 if (strcmp(txt, LV_SYMBOL_OK) == 0) {
524 }
else if (strcmp(txt, LV_SYMBOL_BACKSPACE) == 0) {
526 }
else if ((txt[0] >=
'0') && (txt[0] <=
'9') && (txt[1] ==
'\0')) {
527 lv_textarea_add_char(
s_pin_ta,
static_cast<uint32_t
>(txt[0]));
534 lv_obj_t *ta = lv_event_get_target(e);
535 lv_obj_t *hint =
static_cast<lv_obj_t *
>(lv_event_get_user_data(e));
536 const char *txt = lv_textarea_get_text(ta);
537 if (hint == NULL) {
return; }
538 if ((txt != NULL) && (txt[0] !=
'\0')) {
539 lv_obj_add_flag(hint, LV_OBJ_FLAG_HIDDEN);
541 lv_obj_clear_flag(hint, LV_OBJ_FLAG_HIDDEN);
545static lv_obj_t *
make_code_field(uint32_t max_len, lv_coord_t x1, lv_coord_t y,
546 const char *hint, lv_coord_t w,
547 lv_obj_t *parent = NULL) {
548 lv_obj_t *host = (parent != NULL) ? parent : lv_scr_act();
549 lv_obj_t *ta = lv_textarea_create(host);
550 lv_textarea_set_password_mode(ta,
true);
553 lv_textarea_set_password_show_time(ta, 500);
554 lv_textarea_set_one_line(ta,
true);
555 lv_textarea_set_max_length(ta, max_len);
556 lv_textarea_set_text(ta,
"");
557 lv_obj_clear_flag(ta, LV_OBJ_FLAG_CLICKABLE);
558 lv_obj_set_width(ta, w);
561 lv_obj_set_style_pad_ver(ta,
FIELD_PAD_V, LV_PART_MAIN);
562 lv_obj_align(ta, LV_ALIGN_TOP_LEFT, x1, y);
563 lv_obj_set_style_text_align(ta, LV_TEXT_ALIGN_CENTER, LV_PART_MAIN);
571 lv_obj_update_layout(ta);
572 lv_obj_align_to(l, ta, LV_ALIGN_CENTER, 0, 0);
573 lv_obj_add_event_cb(ta,
code_hint_cb, LV_EVENT_VALUE_CHANGED, l);
580 static const char *kbd_map[] = {
584 LV_SYMBOL_BACKSPACE,
"0", LV_SYMBOL_OK,
""
586 lv_obj_t *kb = lv_btnmatrix_create((parent != NULL) ? parent : lv_scr_act());
587 lv_btnmatrix_set_map(kb, kbd_map);
588 lv_obj_set_size(kb, w, h);
589 lv_obj_align(kb, LV_ALIGN_BOTTOM_MID, 0, -6);
590 lv_obj_set_style_bg_opa(kb, LV_OPA_TRANSP, LV_PART_MAIN);
591 lv_obj_set_style_border_width(kb, 0, LV_PART_MAIN);
594 lv_obj_set_style_pad_hor(kb, 0, LV_PART_MAIN);
596 lv_obj_set_style_bg_opa(kb, LV_OPA_TRANSP, LV_PART_ITEMS);
597 lv_obj_set_style_bg_color(kb,
COL_SURFACE, LV_PART_ITEMS | LV_STATE_PRESSED);
598 lv_obj_set_style_bg_opa(kb, LV_OPA_COVER, LV_PART_ITEMS | LV_STATE_PRESSED);
599 lv_obj_set_style_border_width(kb, 0, LV_PART_ITEMS);
600 lv_obj_set_style_shadow_width(kb, 0, LV_PART_ITEMS);
601 lv_obj_set_style_text_color(kb,
COL_TEXT, LV_PART_ITEMS);
603 lv_obj_set_style_radius(kb, 8, LV_PART_ITEMS);
604 lv_obj_add_event_cb(kb, cb, LV_EVENT_VALUE_CHANGED, NULL);
616lv_obj_t *
make_code_row(lv_obj_t *host, lv_obj_t *kb, uint32_t max_len,
618 lv_obj_t **eye_lbl) {
619 lv_obj_update_layout(kb);
620 const lv_area_t *k =
reinterpret_cast<lv_btnmatrix_t *
>(kb)->button_areas;
621 const lv_coord_t kx = lv_obj_get_x(kb);
625 const lv_coord_t inset = 8;
626 const lv_coord_t grow = 12;
627 const lv_coord_t w = ((k[1].x2 - k[0].x1) + 1) - inset + grow;
630 const lv_coord_t x1 = (eye_lbl == NULL)
631 ? kx + ((lv_obj_get_width(kb) - w) / 2)
632 : kx + k[0].x1 + inset;
634 if (eye_lbl == NULL) {
return ta; }
638 lv_obj_set_width(eye, (k[2].x2 - k[2].x1) + 1);
639 lv_obj_align_to(eye, ta, LV_ALIGN_OUT_RIGHT_MID,
640 (k[2].x1 - k[1].x2) - 1 - grow, 0);
641 *eye_lbl = lv_obj_get_child(eye, 0);
651 CW_Utils::secure_wipe(
reinterpret_cast<uint8_t *
>(
s_pin),
sizeof(
s_pin));
670 lv_obj_set_style_transform_zoom(
static_cast<lv_obj_t *
>(obj), v, LV_PART_MAIN);
674void pop_in(lv_obj_t *obj) {
675 lv_obj_update_layout(obj);
676 lv_obj_set_style_transform_pivot_x(obj, lv_obj_get_width(obj) / 2, LV_PART_MAIN);
677 lv_obj_set_style_transform_pivot_y(obj, lv_obj_get_height(obj) / 2, LV_PART_MAIN);
683 lv_anim_set_var(&a, obj);
685 lv_anim_set_values(&a, 10, 256);
686 lv_anim_set_time(&a, 420);
687 lv_anim_set_path_cb(&a, lv_anim_path_overshoot);
695static void hash_short(
const char *h,
char *out,
size_t n) {
696 size_t len = strlen(h);
697 if ((len <= 14U) || (n < 16U)) {
698 snprintf(out, n,
"%s", h);
701 snprintf(out, n,
"%.6s...%s", h, h + len - 4U);
714 const lv_font_t *font, lv_coord_t y,
bool ticker) {
715 lv_obj_t *row = lv_obj_create(parent);
716 lv_obj_remove_style_all(row);
717 lv_obj_set_size(row, LV_SIZE_CONTENT, LV_SIZE_CONTENT);
718 lv_obj_clear_flag(row, LV_OBJ_FLAG_SCROLLABLE);
719 lv_obj_set_flex_flow(row, LV_FLEX_FLOW_ROW);
720 lv_obj_set_flex_align(row, LV_FLEX_ALIGN_CENTER, LV_FLEX_ALIGN_CENTER,
721 LV_FLEX_ALIGN_CENTER);
722 lv_obj_set_style_pad_column(row, 6, LV_PART_MAIN);
728 LV_ALIGN_DEFAULT, 0, 0);
731 lv_obj_update_layout(row);
732 lv_obj_align(row, LV_ALIGN_TOP_MID, 0,
733 y - ((lv_obj_get_height(row) - lv_obj_get_height(al)) / 2));
748 LV_ALIGN_TOP_MID, 0, 8);
752 LV_ALIGN_TOP_MID, 0, 82);
768 LV_ALIGN_TOP_MID, 0, 92);
777 LV_ALIGN_TOP_MID, 0, 168);
800 LV_ALIGN_TOP_MID, 0, 114);
802 "It may still arrive. Check the explorer "
803 "before charging again.",
805 lv_label_set_long_mode(note, LV_LABEL_LONG_WRAP);
806 lv_obj_set_width(note, VW);
807 lv_obj_set_style_text_align(note, LV_TEXT_ALIGN_CENTER, LV_PART_MAIN);
808 lv_obj_align(note, LV_ALIGN_TOP_MID, 0, 138);
820 lv_obj_t *cross =
make_label(card, LV_SYMBOL_CLOSE, lv_color_hex(0xEC5B5B),
827 lv_label_set_long_mode(info, LV_LABEL_LONG_WRAP);
828 lv_obj_set_width(info, VW);
829 lv_obj_set_style_text_align(info, LV_TEXT_ALIGN_CENTER, LV_PART_MAIN);
830 lv_obj_align(info, LV_ALIGN_TOP_MID, 0, 124);
841 const char *state_str =
846 lv_obj_t *sp = lv_spinner_create(card, 1000, 60);
847 lv_obj_set_size(sp, 60, 60);
848 lv_obj_align(sp, LV_ALIGN_TOP_MID, 0, 34);
849 lv_obj_set_style_arc_width(sp, 6, LV_PART_MAIN);
850 lv_obj_set_style_arc_width(sp, 6, LV_PART_INDICATOR);
851 lv_obj_set_style_arc_color(sp,
COL_SURFACE, LV_PART_MAIN);
852 lv_obj_set_style_arc_color(sp,
COL_ACCENT, LV_PART_INDICATOR);
855 LV_ALIGN_TOP_MID, 0, 112);
864 lv_obj_set_style_text_align(
s_tx_info_lbl, LV_TEXT_ALIGN_CENTER, LV_PART_MAIN);
884 title =
"Wallet not ready";
885 body =
"The card reader answered but the Cryptnox wallet could not "
886 "be initialised.\n\n"
887 "The terminal restarts by itself in 30 s. If this keeps "
888 "happening, the reader or its firmware is at fault.";
891 title =
"Invalid configuration";
892 body =
"An address built into this firmware (config.h) does not "
894 "The terminal restarts in 30 s. Install a build with a "
895 "corrected config.h.";
899 title =
"NFC reader not found";
900 body =
"The PN532 module did not answer on the I2C bus.\n\n"
901 "Check the SDA/SCL wiring, the RST pin and the 3V3 supply. "
902 "The terminal restarts by itself in 30 s.";
907 LV_ALIGN_TOP_MID, 0, 124);
911 lv_label_set_long_mode(b, LV_LABEL_LONG_WRAP);
912 lv_obj_set_width(b, 216);
913 lv_obj_set_style_text_align(b, LV_TEXT_ALIGN_CENTER, LV_PART_MAIN);
921 LV_ALIGN_BOTTOM_MID, 0, -8);
922 lv_label_set_long_mode(d, LV_LABEL_LONG_WRAP);
923 lv_obj_set_width(d, 216);
924 lv_obj_set_style_text_align(d, LV_TEXT_ALIGN_CENTER, LV_PART_MAIN);
926 lv_obj_update_layout(b);
927 lv_obj_update_layout(d);
928 const lv_coord_t body_end = lv_obj_get_y(b) + lv_obj_get_height(b) + 8;
929 if (lv_obj_get_y(d) < body_end) {
930 lv_obj_align(d, LV_ALIGN_TOP_MID, 0, body_end);
static const pos_net_info_t * pos_net_info(pos_net_t net)
Names for net, or Ethereum's for a value out of range.
lv_font_t font_inter_14_medium
lv_font_t font_pjs_20_medium
lv_font_t font_pjs_20_semibold
lv_font_t font_pjs_28_light
lv_font_t font_pjs_28_semibold
static uint64_t amount_key_00(uint64_t cents, uint64_t cap)
The "00" key: two zeroes shifted in, clamped to cap.
static uint64_t amount_key_back(uint64_t cents)
The backspace key: the last digit dropped.
static void amount_format(uint64_t units, char *out, size_t n)
"12.50": 6-decimal base units to two places, truncated.
static uint64_t amount_key_digit(uint64_t cents, unsigned digit, uint64_t cap)
A digit shifted in from the right; refused whole if it passes cap.
static uint64_t amount_cents_to_units(uint64_t cents)
Keypad cents to 6-decimal base units.
static ui_event_cb_t s_cb
pos_chain_t settings_get_chain(void)
Selected chain, or POS_CHAIN_ETH_USDC if never set.
bool settings_get_mainnet(void)
true when the terminal is on the production networks.
const char * asset_caption(pos_chain_t c)
void request_screen(ui_screen_t s)
uint64_t amount_cents_max(void)
lv_obj_t * s_amount_cents_label
lv_obj_t * s_amount_label
lv_obj_t * s_boot_step_lbl
uint64_t s_confirm_amount
const char * asset_name(pos_chain_t c)
const pos_asset_t * asset(pos_chain_t c)
@ UI_TX_STATE_UNCONFIRMED
Private to the ui*.cpp files: the state and helpers they share.
lv_obj_t * build_page(int step, bool band=true)
lv_obj_t * make_asset_badge(lv_obj_t *parent, pos_chain_t chain)
lv_obj_t * make_button(lv_obj_t *parent, const char *label, lv_color_t bg, lv_color_t fg, lv_coord_t w, lv_coord_t h, lv_align_t align, lv_coord_t x, lv_coord_t y, BtnAction act, const lv_font_t *font)
void asset_btn_set_compact(bool compact)
lv_obj_t * make_icon_button(const char *sym, BtnAction act, lv_obj_t *parent=NULL)
lv_obj_t * make_ghost_button(lv_obj_t *parent, const char *label, lv_coord_t w, lv_align_t align, lv_coord_t x, lv_coord_t y, BtnAction act)
lv_obj_t * make_label(lv_obj_t *parent, const char *txt, lv_color_t color, const lv_font_t *font, lv_align_t align, lv_coord_t x, lv_coord_t y)
void make_tap_mark(lv_obj_t *parent, lv_coord_t y)
void build_header(const char *title)
lv_obj_t * make_title(const char *txt, bool has_icon, lv_obj_t *parent=NULL)
Screen title, centred but never underneath the top-left icon button.
lv_obj_t * make_asset_button(lv_obj_t *parent)
#define AMOUNT_SMALL_CENTS_FROM
static void amount_update_display(void)
void pop_in(lv_obj_t *obj)
static void kbd_backspace_draw_cb(lv_event_t *e)
lv_obj_t * make_code_row(lv_obj_t *host, lv_obj_t *kb, uint32_t max_len, const char *hint, BtnAction eye_act, lv_obj_t **eye_lbl)
static void code_hint_cb(lv_event_t *e)
static void amount_row_place(void)
static void pin_submit(void)
void code_field_reveal(lv_obj_t *ta, lv_obj_t *eye_lbl)
static void tx_amount_row(lv_obj_t *parent, const char *amt, const lv_font_t *font, lv_coord_t y, bool ticker)
void build_tx_status(void)
void build_boot_error(void)
static void amount_kbd_cb(lv_event_t *e)
static void kbd_fill_rows(lv_obj_t *kb)
static lv_coord_t amount_ink_shift(const lv_font_t *f)
lv_obj_t * make_numeric_keypad(lv_event_cb_t cb, lv_obj_t *parent, lv_coord_t w, lv_coord_t h)
void charge_set_busy(void)
static lv_obj_t * make_code_field(uint32_t max_len, lv_coord_t x1, lv_coord_t y, const char *hint, lv_coord_t w, lv_obj_t *parent=NULL)
static void charge_set_enabled(bool on)
static void zoom_anim_cb(void *obj, int32_t v)
static void pin_kbd_cb(lv_event_t *e)
static void hash_short(const char *h, char *out, size_t n)