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(),
316 lv_obj_t *sp = lv_spinner_create(lv_scr_act(), 1000, 60);
317 lv_obj_set_size(sp, 28, 28);
318 lv_obj_align(sp, LV_ALIGN_BOTTOM_MID, 0, -48);
319 lv_obj_set_style_arc_width(sp, 3, LV_PART_MAIN);
320 lv_obj_set_style_arc_width(sp, 3, LV_PART_INDICATOR);
321 lv_obj_set_style_arc_color(sp,
COL_SURFACE, LV_PART_MAIN);
322 lv_obj_set_style_arc_color(sp,
COL_ACCENT, LV_PART_INDICATOR);
328 LV_ALIGN_BOTTOM_MID, 0, -20);
331 lv_obj_set_style_text_align(
s_boot_step_lbl, LV_TEXT_ALIGN_CENTER, LV_PART_MAIN);
348 lv_obj_t *row = lv_obj_create(card);
349 lv_obj_remove_style_all(row);
350 lv_obj_set_size(row, LV_SIZE_CONTENT, LV_SIZE_CONTENT);
351 lv_obj_clear_flag(row, LV_OBJ_FLAG_SCROLLABLE);
352 lv_obj_set_flex_flow(row, LV_FLEX_FLOW_ROW);
353 lv_obj_set_flex_align(row, LV_FLEX_ALIGN_CENTER, LV_FLEX_ALIGN_END,
354 LV_FLEX_ALIGN_CENTER);
363 static const char *amap[] = {
367 "00",
"0", LV_SYMBOL_BACKSPACE,
""
369 lv_obj_t *kb = lv_btnmatrix_create(card);
370 lv_btnmatrix_set_map(kb, amap);
372 lv_obj_align(kb, LV_ALIGN_TOP_MID, 0, 58);
373 lv_obj_set_style_bg_opa(kb, LV_OPA_TRANSP, LV_PART_MAIN);
374 lv_obj_set_style_border_width(kb, 0, LV_PART_MAIN);
377 lv_obj_set_style_pad_hor(kb, 0, LV_PART_MAIN);
380 lv_obj_set_style_bg_opa(kb, LV_OPA_TRANSP, LV_PART_ITEMS);
381 lv_obj_set_style_bg_color(kb,
COL_SURFACE, LV_PART_ITEMS | LV_STATE_PRESSED);
382 lv_obj_set_style_bg_opa(kb, LV_OPA_COVER, LV_PART_ITEMS | LV_STATE_PRESSED);
383 lv_obj_set_style_border_width(kb, 0, LV_PART_ITEMS);
384 lv_obj_set_style_shadow_width(kb, 0, LV_PART_ITEMS);
385 lv_obj_set_style_text_color(kb,
COL_TEXT, LV_PART_ITEMS);
387 lv_obj_set_style_radius(kb, 8, LV_PART_ITEMS);
388 lv_obj_add_event_cb(kb,
amount_kbd_cb, LV_EVENT_VALUE_CHANGED, NULL);
433 lv_obj_align_to(cusdc, amt, LV_ALIGN_OUT_RIGHT_MID, 6, 0);
439 lv_obj_align_to(tick, cusdc, LV_ALIGN_OUT_RIGHT_MID, 4, 0);
455 lv_label_set_long_mode(addr, LV_LABEL_LONG_WRAP);
456 lv_obj_set_width(addr, VW);
464 lv_label_set_long_mode(ctr, LV_LABEL_LONG_WRAP);
465 lv_obj_set_width(ctr, VW);
471 lv_obj_t *tn =
make_label(card,
"Test network - no real funds",
473 LV_ALIGN_DEFAULT, 0, 0);
474 lv_obj_update_layout(ctr);
475 lv_obj_align_to(tn, ctr, LV_ALIGN_OUT_BOTTOM_LEFT, 0, 6);
491 const char *p = lv_textarea_get_text(
s_pin_ta);
492 size_t len = (p != NULL) ? strlen(p) : 0U;
493 if (len < 4U) {
return; }
504 strncpy(
s_card_note,
"Reading your payout addresses",
520 lv_obj_t *bm = lv_event_get_target(e);
521 uint32_t
id = lv_btnmatrix_get_selected_btn(bm);
522 const char *txt = lv_btnmatrix_get_btn_text(bm,
id);
523 if ((txt == NULL) || (
s_pin_ta == NULL)) {
return; }
525 if (strcmp(txt, LV_SYMBOL_OK) == 0) {
527 }
else if (strcmp(txt, LV_SYMBOL_BACKSPACE) == 0) {
529 }
else if ((txt[0] >=
'0') && (txt[0] <=
'9') && (txt[1] ==
'\0')) {
530 lv_textarea_add_char(
s_pin_ta,
static_cast<uint32_t
>(txt[0]));
537 lv_obj_t *ta = lv_event_get_target(e);
538 lv_obj_t *hint =
static_cast<lv_obj_t *
>(lv_event_get_user_data(e));
539 const char *txt = lv_textarea_get_text(ta);
540 if (hint == NULL) {
return; }
541 if ((txt != NULL) && (txt[0] !=
'\0')) {
542 lv_obj_add_flag(hint, LV_OBJ_FLAG_HIDDEN);
544 lv_obj_clear_flag(hint, LV_OBJ_FLAG_HIDDEN);
548static lv_obj_t *
make_code_field(uint32_t max_len, lv_coord_t x1, lv_coord_t y,
549 const char *hint, lv_coord_t w,
550 lv_obj_t *parent = NULL) {
551 lv_obj_t *host = (parent != NULL) ? parent : lv_scr_act();
552 lv_obj_t *ta = lv_textarea_create(host);
553 lv_textarea_set_password_mode(ta,
true);
556 lv_textarea_set_password_show_time(ta, 500);
557 lv_textarea_set_one_line(ta,
true);
558 lv_textarea_set_max_length(ta, max_len);
559 lv_textarea_set_text(ta,
"");
560 lv_obj_clear_flag(ta, LV_OBJ_FLAG_CLICKABLE);
561 lv_obj_set_width(ta, w);
564 lv_obj_set_style_pad_ver(ta,
FIELD_PAD_V, LV_PART_MAIN);
565 lv_obj_align(ta, LV_ALIGN_TOP_LEFT, x1, y);
566 lv_obj_set_style_text_align(ta, LV_TEXT_ALIGN_CENTER, LV_PART_MAIN);
574 lv_obj_update_layout(ta);
575 lv_obj_align_to(l, ta, LV_ALIGN_CENTER, 0, 0);
576 lv_obj_add_event_cb(ta,
code_hint_cb, LV_EVENT_VALUE_CHANGED, l);
583 static const char *kbd_map[] = {
587 LV_SYMBOL_BACKSPACE,
"0", LV_SYMBOL_OK,
""
589 lv_obj_t *kb = lv_btnmatrix_create((parent != NULL) ? parent : lv_scr_act());
590 lv_btnmatrix_set_map(kb, kbd_map);
591 lv_obj_set_size(kb, w, h);
592 lv_obj_align(kb, LV_ALIGN_BOTTOM_MID, 0, -6);
593 lv_obj_set_style_bg_opa(kb, LV_OPA_TRANSP, LV_PART_MAIN);
594 lv_obj_set_style_border_width(kb, 0, LV_PART_MAIN);
597 lv_obj_set_style_pad_hor(kb, 0, LV_PART_MAIN);
599 lv_obj_set_style_bg_opa(kb, LV_OPA_TRANSP, LV_PART_ITEMS);
600 lv_obj_set_style_bg_color(kb,
COL_SURFACE, LV_PART_ITEMS | LV_STATE_PRESSED);
601 lv_obj_set_style_bg_opa(kb, LV_OPA_COVER, LV_PART_ITEMS | LV_STATE_PRESSED);
602 lv_obj_set_style_border_width(kb, 0, LV_PART_ITEMS);
603 lv_obj_set_style_shadow_width(kb, 0, LV_PART_ITEMS);
604 lv_obj_set_style_text_color(kb,
COL_TEXT, LV_PART_ITEMS);
606 lv_obj_set_style_radius(kb, 8, LV_PART_ITEMS);
607 lv_obj_add_event_cb(kb, cb, LV_EVENT_VALUE_CHANGED, NULL);
619lv_obj_t *
make_code_row(lv_obj_t *host, lv_obj_t *kb, uint32_t max_len,
621 lv_obj_t **eye_lbl) {
622 lv_obj_update_layout(kb);
623 const lv_area_t *k =
reinterpret_cast<lv_btnmatrix_t *
>(kb)->button_areas;
624 const lv_coord_t kx = lv_obj_get_x(kb);
628 const lv_coord_t inset = 8;
629 const lv_coord_t grow = 12;
630 const lv_coord_t w = ((k[1].x2 - k[0].x1) + 1) - inset + grow;
633 const lv_coord_t x1 = (eye_lbl == NULL)
634 ? kx + ((lv_obj_get_width(kb) - w) / 2)
635 : kx + k[0].x1 + inset;
637 if (eye_lbl == NULL) {
return ta; }
641 lv_obj_set_width(eye, (k[2].x2 - k[2].x1) + 1);
642 lv_obj_align_to(eye, ta, LV_ALIGN_OUT_RIGHT_MID,
643 (k[2].x1 - k[1].x2) - 1 - grow, 0);
644 *eye_lbl = lv_obj_get_child(eye, 0);
654 CW_Utils::secure_wipe(
reinterpret_cast<uint8_t *
>(
s_pin),
sizeof(
s_pin));
673 lv_obj_set_style_transform_zoom(
static_cast<lv_obj_t *
>(obj), v, LV_PART_MAIN);
677void pop_in(lv_obj_t *obj) {
678 lv_obj_update_layout(obj);
679 lv_obj_set_style_transform_pivot_x(obj, lv_obj_get_width(obj) / 2, LV_PART_MAIN);
680 lv_obj_set_style_transform_pivot_y(obj, lv_obj_get_height(obj) / 2, LV_PART_MAIN);
686 lv_anim_set_var(&a, obj);
688 lv_anim_set_values(&a, 10, 256);
689 lv_anim_set_time(&a, 420);
690 lv_anim_set_path_cb(&a, lv_anim_path_overshoot);
698static void hash_short(
const char *h,
char *out,
size_t n) {
699 size_t len = strlen(h);
700 if ((len <= 14U) || (n < 16U)) {
701 snprintf(out, n,
"%s", h);
704 snprintf(out, n,
"%.6s...%s", h, h + len - 4U);
717 const lv_font_t *font, lv_coord_t y,
bool ticker) {
718 lv_obj_t *row = lv_obj_create(parent);
719 lv_obj_remove_style_all(row);
720 lv_obj_set_size(row, LV_SIZE_CONTENT, LV_SIZE_CONTENT);
721 lv_obj_clear_flag(row, LV_OBJ_FLAG_SCROLLABLE);
722 lv_obj_set_flex_flow(row, LV_FLEX_FLOW_ROW);
723 lv_obj_set_flex_align(row, LV_FLEX_ALIGN_CENTER, LV_FLEX_ALIGN_CENTER,
724 LV_FLEX_ALIGN_CENTER);
725 lv_obj_set_style_pad_column(row, 6, LV_PART_MAIN);
731 LV_ALIGN_DEFAULT, 0, 0);
734 lv_obj_update_layout(row);
735 lv_obj_align(row, LV_ALIGN_TOP_MID, 0,
736 y - ((lv_obj_get_height(row) - lv_obj_get_height(al)) / 2));
751 LV_ALIGN_TOP_MID, 0, 8);
755 LV_ALIGN_TOP_MID, 0, 82);
771 LV_ALIGN_TOP_MID, 0, 92);
780 LV_ALIGN_TOP_MID, 0, 168);
803 LV_ALIGN_TOP_MID, 0, 114);
805 "It may still arrive. Check the explorer "
806 "before charging again.",
808 lv_label_set_long_mode(note, LV_LABEL_LONG_WRAP);
809 lv_obj_set_width(note, VW);
810 lv_obj_set_style_text_align(note, LV_TEXT_ALIGN_CENTER, LV_PART_MAIN);
811 lv_obj_align(note, LV_ALIGN_TOP_MID, 0, 138);
823 lv_obj_t *cross =
make_label(card, LV_SYMBOL_CLOSE, lv_color_hex(0xEC5B5B),
830 lv_label_set_long_mode(info, LV_LABEL_LONG_WRAP);
831 lv_obj_set_width(info, VW);
832 lv_obj_set_style_text_align(info, LV_TEXT_ALIGN_CENTER, LV_PART_MAIN);
833 lv_obj_align(info, LV_ALIGN_TOP_MID, 0, 124);
844 const char *state_str =
849 lv_obj_t *sp = lv_spinner_create(card, 1000, 60);
850 lv_obj_set_size(sp, 60, 60);
851 lv_obj_align(sp, LV_ALIGN_TOP_MID, 0, 34);
852 lv_obj_set_style_arc_width(sp, 6, LV_PART_MAIN);
853 lv_obj_set_style_arc_width(sp, 6, LV_PART_INDICATOR);
854 lv_obj_set_style_arc_color(sp,
COL_SURFACE, LV_PART_MAIN);
855 lv_obj_set_style_arc_color(sp,
COL_ACCENT, LV_PART_INDICATOR);
858 LV_ALIGN_TOP_MID, 0, 112);
867 lv_obj_set_style_text_align(
s_tx_info_lbl, LV_TEXT_ALIGN_CENTER, LV_PART_MAIN);
887 title =
"Wallet not ready";
888 body =
"The card reader answered but the Cryptnox wallet could not "
889 "be initialised.\n\n"
890 "The terminal restarts by itself in 30 s. If this keeps "
891 "happening, the reader or its firmware is at fault.";
894 title =
"Invalid configuration";
895 body =
"An address built into this firmware (config.h) does not "
897 "The terminal restarts in 30 s. Install a build with a "
898 "corrected config.h.";
902 title =
"NFC reader not found";
903 body =
"The PN532 module did not answer on the I2C bus.\n\n"
904 "Check the SDA/SCL wiring, the RST pin and the 3V3 supply. "
905 "The terminal restarts by itself in 30 s.";
910 LV_ALIGN_TOP_MID, 0, 124);
914 lv_label_set_long_mode(b, LV_LABEL_LONG_WRAP);
915 lv_obj_set_width(b, 216);
916 lv_obj_set_style_text_align(b, LV_TEXT_ALIGN_CENTER, LV_PART_MAIN);
924 LV_ALIGN_BOTTOM_MID, 0, -8);
925 lv_label_set_long_mode(d, LV_LABEL_LONG_WRAP);
926 lv_obj_set_width(d, 216);
927 lv_obj_set_style_text_align(d, LV_TEXT_ALIGN_CENTER, LV_PART_MAIN);
929 lv_obj_update_layout(b);
930 lv_obj_update_layout(d);
931 const lv_coord_t body_end = lv_obj_get_y(b) + lv_obj_get_height(b) + 8;
932 if (lv_obj_get_y(d) < body_end) {
933 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)
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)