cryptnox-pos 1.0.0
Standalone USDC payment terminal firmware (ESP32 + Cryptnox smart card)
Loading...
Searching...
No Matches
ui_setup.cpp
Go to the documentation of this file.
1/*
2 * SPDX-License-Identifier: LGPL-3.0-or-later
3 * Copyright (c) 2026 Cryptnox SA
4 */
5
11
12#include "ui_internal.h"
13
14/* First-run greeting, in the sale flow's card on the ramp so setup and selling
15 * read as one product. It waits for a tap: it is the only moment the terminal
16 * has the operator's attention before the setup steps start asking for things. */
17void build_welcome(void) {
18 lv_obj_t *card = build_page(PAY_STEP_NONE, false);
19
20 /* The logo is its own 80px render, not the 120px one zoomed: that RGB565
21 * image has no alpha and draws grey edges when scaled. The column is
22 * hand-balanced around the Start button (208..252) — moving one offset eats
23 * into a neighbour. */
24 lv_obj_t *logo = lv_img_create(card);
25 lv_img_set_src(logo, &logo_mid);
26 lv_obj_align(logo, LV_ALIGN_TOP_MID, 0, 12);
27 pop_in(logo); /* settled screen, so the flourish is welcome here */
28
29 /* "Thank you for choosing Cryptnox POS." split over two lines: the product
30 * name stays black to carry the sentence, the lead-in is grey. */
31 make_label(card, "Thank you for choosing", COL_DIM,
32 &font_inter_14, LV_ALIGN_TOP_MID, 0, 104);
33 lv_obj_t *brand = make_label(card, "Cryptnox POS", COL_TEXT,
34 &font_pjs_20_medium, LV_ALIGN_TOP_MID, 0, 124);
35
36 /* Anchored under the brand, not at a fixed y, because it may wrap. Width
37 * and long mode first, so the measurement sees them. */
38 lv_obj_t *sub = make_label(card, s_welcome_sub,
40 LV_ALIGN_CENTER, 0, 0);
41 lv_obj_set_width(sub, CARD_W - (2 * CARD_PAD));
42 lv_label_set_long_mode(sub, LV_LABEL_LONG_WRAP);
43 lv_obj_set_style_text_align(sub, LV_TEXT_ALIGN_CENTER, LV_PART_MAIN);
44 lv_obj_update_layout(brand);
45 lv_obj_align_to(sub, brand, LV_ALIGN_OUT_BOTTOM_MID, 0, 6);
46
47 (void)make_button(card, "Start", COL_ACTION, COL_BG,
48 CARD_BTN_W, CARD_BTN_H, LV_ALIGN_BOTTOM_MID, 0, CARD_BTN_Y,
50}
51
52/* Phone setup: the same screen at every step, captioned with the current one.
53 *
54 * One QR code carrying "WIFI:T:WPA;S:...;P:...;;", which iOS and Android cameras
55 * join directly; the captive portal then opens the form, so there is no URL. The
56 * SSID and passphrase are printed underneath as a fallback. All three go away
57 * once a browser has been let in.
58 *
59 * Deliberately no on-panel alternative: past the admin code the wizard is a
60 * browser flow. The admin code stays on the panel because its value is that it
61 * never crosses a network.
62 *
63 * The last step thanks the operator and offers Finish, which applies the
64 * settings (see main). */
65void build_prov(void) {
66 const prov_step_t step = static_cast<prov_step_t>(s_prov_step);
67
68 /* The end of the wizard gets the whole card: nothing left to scan, and the
69 * one thing to say is that it worked. */
70 if (step == PROV_STEP_DONE) {
71 lv_obj_t *card = build_page(PAY_STEP_NONE, false); /* setup: no band */
72 lv_obj_t *chk = make_label(card, LV_SYMBOL_OK, COL_SUCCESS,
73 &font_icons_48, LV_ALIGN_TOP_MID, 0, 16);
74 pop_in(chk);
75 make_label(card, "All set", COL_TEXT, &font_pjs_20_medium,
76 LV_ALIGN_TOP_MID, 0, 76);
77 lv_obj_t *b = make_label(card,
78 "Thank you - your terminal is configured.\n\n"
79 "It restarts once to apply everything.",
81 LV_ALIGN_TOP_MID, 0, 106);
82 lv_obj_set_width(b, CARD_W - (2 * CARD_PAD));
83 lv_label_set_long_mode(b, LV_LABEL_LONG_WRAP);
84 lv_obj_set_style_text_align(b, LV_TEXT_ALIGN_CENTER, LV_PART_MAIN);
85 lv_obj_align(b, LV_ALIGN_TOP_MID, 0, 106);
86
87 (void)make_button(card, "Finish", COL_ACTION, COL_BG,
88 CARD_BTN_W, CARD_BTN_H, LV_ALIGN_BOTTOM_MID, 0, CARD_BTN_Y,
90 return;
91 }
92
93 /* Once the browser is in, the screen is only a status line, so it sits in
94 * the sale card on the ramp like the other setup screens. The QR step keeps
95 * the whole white screen: code, credentials and spinner need every row. */
96 const bool authed = prov_authed();
97 lv_obj_t *host = lv_scr_act();
98 if (authed) {
99 host = build_page(PAY_STEP_NONE, false); /* setup: no band */
100 } else {
101 clear_screen();
102 lv_obj_set_style_bg_color(lv_scr_act(), lv_color_white(), LV_PART_MAIN);
103 }
104 const lv_coord_t dy = authed ? 6 : 0; /* the card's top padding */
105
106 /* The title names the step and is the biggest thing on the screen, so a
107 * step advancing is visible while the QR code below stays the same. Step
108 * numbers count the whole flow: 1 is the admin code created on the panel.
109 * The Wi-Fi-only re-join has no steps to count, so it says what it is. */
110 const bool numbered = !prov_wifi_only();
111 const char *eyebrow = "Setup";
112 const char *title = "Nothing to set";
113 /* Each hint describes THIS screen, not the one after it. */
114 const char *hint = "";
115 switch (step) {
116 case PROV_STEP_AUTH:
117 eyebrow = "Step 2";
118 title = "Connect your phone";
119 hint = "Scan to join the terminal's Wi-Fi";
120 break;
121 case PROV_STEP_ADDR:
122 eyebrow = "Step 4";
123 title = "Payout addresses";
124 hint = "Continue on your phone";
125 break;
126 case PROV_STEP_WIFI:
127 eyebrow = numbered ? "Step 5" : "Wi-Fi";
128 title = "Wi-Fi network";
129 hint = numbered ? "Continue on your phone"
130 : "Scan with your phone";
131 break;
132 default:
133 break;
134 }
135
136 make_label(host, eyebrow, COL_DIM, &font_inter_14,
137 LV_ALIGN_TOP_MID, 0, 6 + dy);
139 LV_ALIGN_TOP_MID, 0, 24 + dy); /* 18px step line ends at 24 */
140 make_label(host, hint, COL_DIM,
141 &font_inter_14, LV_ALIGN_TOP_MID, 0, 50 + dy);
142
143 /* The QR code and AP credentials are for joining, so they go once the phone
144 * is let in; left up they would read as "scan this again". */
145 if (!authed) {
146 lv_obj_t *qr = lv_qrcode_create(lv_scr_act(), 112, COL_TEXT, COL_BG);
147 if (qr != NULL) {
148 const char *payload = prov_qr_payload();
149 (void)lv_qrcode_update(qr, payload, strlen(payload));
150 lv_obj_align(qr, LV_ALIGN_TOP_MID, 0, 70);
151 /* White quiet zone: a code drawn hard against a coloured edge is a code
152 * half the scanners in the world will not see. */
153 lv_obj_set_style_border_color(qr, COL_BG, LV_PART_MAIN);
154 lv_obj_set_style_border_width(qr, 4, LV_PART_MAIN);
155 }
156
157 /* Both labelled: someone typing them into a phone's Wi-Fi sheet is
158 * filling in two named boxes. */
159 make_label(lv_scr_act(), "Or join manually:", COL_DIM,
160 &font_inter_14, LV_ALIGN_TOP_MID, 0, 194);
161 make_label(lv_scr_act(), "Network", COL_DIM, &font_inter_14,
162 LV_ALIGN_TOP_LEFT, 24, 214);
163 make_label(lv_scr_act(), prov_ap_ssid(), COL_TEXT,
164 &font_inter_14_medium, LV_ALIGN_TOP_LEFT, 100, 214);
165 make_label(lv_scr_act(), "Password", COL_DIM, &font_inter_14,
166 LV_ALIGN_TOP_LEFT, 24, 232);
167 make_label(lv_scr_act(), prov_ap_pass(), COL_TEXT,
168 &font_inter_14_medium, LV_ALIGN_TOP_LEFT, 100, 232);
169 } else {
170 lv_obj_t *on = make_label(host,
171 "Connected - the setup page is open in the "
172 "browser.",
174 LV_ALIGN_TOP_MID, 0, 86);
175 lv_obj_set_width(on, CARD_W - (2 * CARD_PAD));
176 lv_label_set_long_mode(on, LV_LABEL_LONG_WRAP);
177 lv_obj_set_style_text_align(on, LV_TEXT_ALIGN_CENTER, LV_PART_MAIN);
178 lv_obj_align(on, LV_ALIGN_TOP_MID, 0, 86);
179
180 /* Only in this branch: a card is read from the browser's setup page, so
181 * there is no route to a failed read that is not already past the QR
182 * code. */
183 if (s_prov_msg[0] != '\0') {
184 lv_obj_t *m = make_label(host, s_prov_msg, COL_DANGER,
186 LV_ALIGN_TOP_MID, 0, 130);
187 lv_obj_set_width(m, CARD_W - (2 * CARD_PAD));
188 lv_label_set_long_mode(m, LV_LABEL_LONG_WRAP);
189 lv_obj_set_style_text_align(m, LV_TEXT_ALIGN_CENTER, LV_PART_MAIN);
190 /* Under the Connected line, however many lines it wrapped to. Both
191 * start high (86) and the spinner sits low so three lines of each
192 * still end above it — the join error is two lines at 204px. */
193 lv_obj_update_layout(on);
194 lv_obj_align_to(m, on, LV_ALIGN_OUT_BOTTOM_MID, 0, 12);
195 }
196 }
197
198 /* A spinner: the operator is looking at the browser, and this says the
199 * terminal is still with them. "a connection", not "your phone": a laptop
200 * can join the AP just as well. */
201 lv_obj_t *sp = lv_spinner_create(host, 1000, 60);
202 lv_obj_set_size(sp, 24, 24);
203 lv_obj_align(sp, LV_ALIGN_BOTTOM_MID, 0, authed ? -12 : -34);
204 lv_obj_set_style_arc_width(sp, 3, LV_PART_MAIN);
205 lv_obj_set_style_arc_width(sp, 3, LV_PART_INDICATOR);
206 lv_obj_set_style_arc_color(sp, COL_SURFACE, LV_PART_MAIN);
207 lv_obj_set_style_arc_color(sp, COL_ACCENT, LV_PART_INDICATOR);
208 /* Only before the join: after it the line above already says "Connected",
209 * and the spinner alone says the terminal is waiting on the browser. */
210 if (!authed) {
211 make_label(lv_scr_act(), "Waiting for a connection", COL_DIM,
212 &font_inter_14, LV_ALIGN_BOTTOM_MID, 0, -10);
213 }
214}
215
216/* A payout address or a token contract proposed from a browser, shown for
217 * acceptance here. The modal is the security boundary, not decoration: a browser
218 * can propose a value, only the panel in the operator's hands can store one — and
219 * that holds for the card-derived route too, which comes through the same
220 * handshake rather than writing straight to NVS.
221 *
222 * The value is drawn wrapped at 14 px rather than elided — the operator is being
223 * asked to compare it against their own records character by character, so every
224 * character has to be on the screen. */
227 char label[40] = "";
228 char addr[SETTINGS_PAYOUT_MAX] = "";
229 if (!prov_pending(&kind, label, sizeof(label), addr, sizeof(addr))) { return; }
230
231 const bool contract = (kind == PROV_ASK_CONTRACT_ETH) ||
232 (kind == PROV_ASK_CONTRACT_TRON);
233
234 /* Stacked and measured with ota_text/ota_fit_card, because the label, the
235 * address and the warning all vary in height, and a fixed layout lets a
236 * taller block run under the Accept button on the screen that decides where
237 * the takings go. The label wraps rather than eliding, for the same reason
238 * the address does. */
239 lv_obj_t *card = open_modal(OTA_CARD_W, 276);
240
241 lv_obj_t *t = ota_text(card, NULL,
242 contract ? "Set token contract?"
243 : "Set payout address?",
245 lv_obj_t *l = ota_text(card, t, label, COL_DIM, &font_inter_14);
246 lv_obj_t *a = ota_text(card, l, addr, COL_TEXT, &font_inter_14_medium);
247 /* Kept to the sentence that changes a decision: the card grows only up to
248 * ota_fit_card's ceiling of one screen, past which the buttons overlap the
249 * text. */
250 lv_obj_t *warn = ota_text(card, a,
251 contract
252 ? "A wrong contract charges a different asset."
253 : "Takings will be sent here. Check it against "
254 "your own records.",
256 ota_fit_card(card, warn, 86); /* two 40px buttons at -46 and -2 */
257
258 /* Reject is the wide, plainly-labelled one and Accept is the deliberate tap:
259 * the safe answer to "a stranger's address appeared on my terminal" is no. */
260 (void)make_button(card, "Accept", COL_ACCENT, COL_BG, OTA_TEXT_W, 40,
261 LV_ALIGN_BOTTOM_MID, 0, -46, ACT_PROV_OK,
263 (void)make_button(card, "Reject", COL_SURFACE, COL_TEXT, OTA_TEXT_W, 40,
264 LV_ALIGN_BOTTOM_MID, 0, -2, ACT_PROV_NO,
266}
267
268/* "Hold your card to the reader" while an address is derived from it. Its own
269 * screen rather than the transaction one: nothing is being paid, and that screen's
270 * wording and its Cancel semantics both belong to a sale. */
271void build_card_wait(void) {
272 /* Same chrome as the sale, deliberately without a lit dash: reading an
273 * address off a card during setup is not a step of anybody's payment. */
274 lv_obj_t *card = build_page(PAY_STEP_NONE, false); /* setup: no band */
275 const lv_coord_t VW = CARD_W - (2 * CARD_PAD);
276
277 make_label(card, "Cryptnox card", COL_DIM, &font_inter_14,
278 LV_ALIGN_TOP_MID, 0, 10);
279
280 /* Centred in the band between the caption and the line below, computed
281 * from TAP_MARK_H because the generator chooses the mark's height. */
282 make_tap_mark(card, 32 + ((88 - TAP_MARK_H) / 2));
283
284 make_label(card, "Hold card to reader", COL_TEXT,
285 &font_pjs_20_medium, LV_ALIGN_TOP_MID, 0, 124);
286
287 /* 154: "Reading your payout addresses" wraps to two lines at 204px, and
288 * they must end above the Cancel button (208). */
289 lv_obj_t *info = make_label(card, s_card_note, COL_DIM,
290 &font_inter_14, LV_ALIGN_TOP_MID, 0, 154);
291 lv_obj_set_width(info, VW);
292 lv_label_set_long_mode(info, LV_LABEL_LONG_WRAP);
293 lv_obj_set_style_text_align(info, LV_TEXT_ALIGN_CENTER, LV_PART_MAIN);
294 lv_obj_align(info, LV_ALIGN_TOP_MID, 0, 154);
295
296 (void)make_ghost_button(card, "Cancel", CARD_BTN_W,
297 LV_ALIGN_BOTTOM_MID, 0, CARD_BTN_Y, ACT_CANCEL);
298}
299
300/* Header with a back arrow (to amount entry) instead of the burger. */
301static void build_header_back(const char *title) {
302 (void)make_title(title, true);
303 make_divider(lv_scr_act(), HDR_DIVIDER_Y);
304 (void)make_icon_button(LV_SYMBOL_LEFT, ACT_WIFI_CANCEL);
305}
306
307static void wifi_item_cb(lv_event_t *e) {
308 intptr_t idx = reinterpret_cast<intptr_t>(lv_event_get_user_data(e));
309 if ((idx < 0) || (idx >= s_ap_count)) { return; }
310 strncpy(s_wifi_ssid, s_aps[idx].ssid, sizeof(s_wifi_ssid) - 1);
311 s_wifi_ssid[sizeof(s_wifi_ssid) - 1] = '\0';
313}
314void build_wifi_list(void) {
315 clear_screen();
316 build_header_back("Wi-Fi");
317
318 /* Why the picker opened, otherwise the operator lands in Wi-Fi setup with no
319 * idea what failed. */
320 lv_coord_t list_y = 48;
321 if (s_wifi_note[0] != '\0') {
322 lv_obj_t *note = make_label(lv_scr_act(), s_wifi_note, COL_DANGER,
324 LV_ALIGN_TOP_MID, 0, 50);
325 lv_label_set_long_mode(note, LV_LABEL_LONG_WRAP);
326 lv_obj_set_width(note, 216);
327 lv_obj_set_style_text_align(note, LV_TEXT_ALIGN_CENTER, LV_PART_MAIN);
328 /* Measure rather than assume two lines: the list then clears the note at
329 * any wrap depth, so a longer message can never overprint it. */
330 lv_obj_update_layout(note);
331 list_y = 50 + lv_obj_get_height(note) + 6;
332 }
333
334 if (s_ap_count == 0U) {
335 make_label(lv_scr_act(), "No networks found", COL_DIM,
336 &font_inter_14, LV_ALIGN_CENTER, 0, 0);
337 make_button(lv_scr_act(), "Rescan", COL_ACTION, COL_BG, 140, ACT_BTN_H,
338 LV_ALIGN_BOTTOM_MID, 0, ACT_BTN_Y, ACT_WIFI,
340 return;
341 }
342
343 lv_obj_t *list = lv_list_create(lv_scr_act());
344 lv_obj_set_size(list, SCR_W - 12, SCR_H - 4 - list_y);
345 lv_obj_align(list, LV_ALIGN_TOP_MID, 0, list_y);
346 lv_obj_set_style_bg_color(list, COL_BG, LV_PART_MAIN);
347 lv_obj_set_style_border_width(list, 0, LV_PART_MAIN);
348 lv_obj_set_style_pad_row(list, 4, LV_PART_MAIN);
349
350 for (uint16_t i = 0U; i < s_ap_count; i++) {
351 lv_obj_t *btn = lv_list_add_btn(list, LV_SYMBOL_WIFI, s_aps[i].ssid);
352 lv_obj_set_style_bg_color(btn, COL_SURFACE, LV_PART_MAIN);
353 lv_obj_set_style_text_color(btn, COL_TEXT, LV_PART_MAIN);
354 lv_obj_set_style_radius(btn, 8, LV_PART_MAIN);
355 lv_obj_add_event_cb(btn, wifi_item_cb, LV_EVENT_CLICKED,
356 reinterpret_cast<void *>(static_cast<intptr_t>(i)));
357 }
358}
359
360static void wifi_pass_kb_cb(lv_event_t *e) {
361 lv_event_code_t code = lv_event_get_code(e);
362 if (code == LV_EVENT_READY) { /* keyboard check-mark */
363 if (s_wifi_pass_ta != NULL) {
364 const char *p = lv_textarea_get_text(s_wifi_pass_ta);
365 strncpy(s_wifi_pass, (p != NULL) ? p : "", sizeof(s_wifi_pass) - 1);
366 s_wifi_pass[sizeof(s_wifi_pass) - 1] = '\0';
367 }
368 set_wifi_progress("Connecting to", s_wifi_ssid);
370 if (s_cb != NULL) { s_cb(UI_EVENT_WIFI_TRY, 0); }
371 } else if (code == LV_EVENT_CANCEL) { /* keyboard close */
373 }
374}
375void build_wifi_pass(void) {
376 clear_screen();
378 LV_ALIGN_TOP_MID, 0, 6);
379
380 s_wifi_pass_ta = lv_textarea_create(lv_scr_act());
381 lv_textarea_set_one_line(s_wifi_pass_ta, true);
382 lv_textarea_set_text(s_wifi_pass_ta, "");
383 lv_textarea_set_placeholder_text(s_wifi_pass_ta, "Password");
384 /* Masked by default: this screen faces the customer. The eye beside it
385 * reveals on demand, for checking a long passphrase. */
386 lv_textarea_set_password_mode(s_wifi_pass_ta, true);
387 /* Echo each character briefly, then mask — long enough to catch a typo,
388 * a third of LVGL's 1500 ms default on a customer-facing screen. The eye
389 * stays the way to re-read the whole passphrase, on the merchant's terms. */
390 lv_textarea_set_password_show_time(s_wifi_pass_ta, 500);
391 /* Same sizing rule as the card PIN and admin code fields: field and eye are
392 * one group, sized to the full-width keyboard less a margin either side, and
393 * centred as a pair. */
394 lv_obj_set_width(s_wifi_pass_ta, CODE_FIELD_W(SCR_W - (2 * FIELD_PAD_V)));
395 lv_obj_align(s_wifi_pass_ta, LV_ALIGN_TOP_MID, CODE_FIELD_X, 28);
396 lv_obj_set_style_pad_ver(s_wifi_pass_ta, FIELD_PAD_V, LV_PART_MAIN);
397
398 /* make_icon_button() places itself top-left for the burger; move it beside
399 * the field. Keep the label handle so the glyph can be swapped in place. Same
400 * pair on the card-PIN screen — see build_pin(). */
401 lv_obj_t *eye = make_icon_button(LV_SYMBOL_EYE_OPEN, ACT_WIFI_PASS_REVEAL);
402 lv_obj_align_to(eye, s_wifi_pass_ta, LV_ALIGN_OUT_RIGHT_MID, CODE_EYE_GAP, 0);
403 s_wifi_eye_lbl = lv_obj_get_child(eye, 0);
404
405 lv_obj_t *kb = lv_keyboard_create(lv_scr_act());
406 lv_keyboard_set_textarea(kb, s_wifi_pass_ta);
407 lv_obj_add_event_cb(kb, wifi_pass_kb_cb, LV_EVENT_ALL, NULL);
408}
410 clear_screen();
411 build_header("Wi-Fi");
412
413 if (s_wifi_name[0] == '\0') {
414 /* Nothing to name ("Scanning..."): the caption is the whole message. */
415 make_label(lv_scr_act(), s_wifi_caption, COL_TEXT,
416 &font_pjs_20_medium, LV_ALIGN_CENTER, 0, 0);
417 } else {
418 make_label(lv_scr_act(), s_wifi_caption, COL_DIM,
419 &font_inter_14, LV_ALIGN_CENTER, 0, -30);
420
421 /* LONG_DOT elides on real glyph metrics, so a 32-char SSID never
422 * overflows. Width must be set before the long mode. */
423 lv_obj_t *name = make_label(lv_scr_act(), s_wifi_name, COL_TEXT,
424 &font_pjs_20_medium, LV_ALIGN_CENTER, 0, 0);
425 lv_obj_set_width(name, SCR_W - 24);
426 lv_label_set_long_mode(name, LV_LABEL_LONG_DOT);
427 lv_obj_set_style_text_align(name, LV_TEXT_ALIGN_CENTER, LV_PART_MAIN);
428 lv_obj_align(name, LV_ALIGN_CENTER, 0, 0); /* re-centre after the resize */
429 }
430}
lv_font_t font_icons_48
lv_font_t font_inter_14
lv_font_t font_inter_14_medium
lv_font_t font_pjs_20_medium
lv_font_t font_pjs_20_semibold
const lv_img_dsc_t logo_mid
Definition logo_mid.c:812
static net_wifi_ap_t s_aps[PROV_MAX_APS]
static uint16_t s_ap_count
static bool authed(httpd_req_t *req)
Whether the request is the browser that was let in.
const char * prov_ap_pass(void)
AP passphrase, or "" while no portal is up.
bool prov_wifi_only(void)
Whether the portal is the cut-down Wi-Fi-only flow.
const char * prov_qr_payload(void)
What the panel's QR code should carry.
const char * prov_ap_ssid(void)
AP SSID, or "" while no portal is up.
static ui_event_cb_t s_cb
bool prov_authed(void)
Whether the browser session is authorised to change anything.
bool prov_pending(prov_ask_t *kind, char *label, size_t label_n, char *value, size_t value_n)
Fetch the value a browser proposed but nobody has accepted.
prov_ask_t
What the panel is being asked to accept, for prov_pending.
Definition provision.h:128
@ PROV_ASK_CONTRACT_ETH
Definition provision.h:132
@ PROV_ASK_CONTRACT_TRON
Definition provision.h:133
@ PROV_ASK_NONE
Definition provision.h:129
prov_step_t
Where the wizard has got to.
Definition provision.h:118
@ PROV_STEP_WIFI
Definition provision.h:122
@ PROV_STEP_AUTH
Definition provision.h:120
@ PROV_STEP_ADDR
Definition provision.h:121
@ PROV_STEP_DONE
Definition provision.h:123
#define SETTINGS_PAYOUT_MAX
Longest payout address plus NUL — "0x" + 40 hex, or 34 base58 Tron.
Definition settings.h:214
char s_wifi_name[33]
Definition ui.cpp:115
char s_wifi_note[64]
Definition ui.cpp:105
void set_wifi_progress(const char *caption, const char *name)
Definition ui.cpp:247
char s_wifi_pass[65]
Definition ui.cpp:104
char s_prov_msg[64]
Definition ui.cpp:136
void request_screen(ui_screen_t s)
Definition ui.cpp:241
char s_wifi_ssid[33]
Definition ui.cpp:103
lv_obj_t * s_wifi_eye_lbl
Definition ui.cpp:107
volatile int s_prov_step
Definition ui.cpp:132
char s_welcome_sub[96]
Definition ui.cpp:151
char s_wifi_caption[24]
Definition ui.cpp:114
char s_card_note[64]
Definition ui.cpp:67
lv_obj_t * s_wifi_pass_ta
Definition ui.cpp:106
@ UI_SCREEN_WIFI_PASS
Definition ui.h:41
@ UI_SCREEN_WIFI_LIST
Definition ui.h:40
@ UI_SCREEN_WIFI_CONNECTING
Definition ui.h:42
@ UI_EVENT_WIFI_TRY
Definition ui.h:61
lv_obj_t * open_modal(lv_coord_t w, lv_coord_t h)
Definition ui_admin.cpp:434
lv_obj_t * ota_text(lv_obj_t *card, lv_obj_t *above, const char *txt, lv_color_t col, const lv_font_t *font)
Definition ui_admin.cpp:477
void ota_fit_card(lv_obj_t *card, lv_obj_t *last, lv_coord_t buttons_h)
Definition ui_admin.cpp:502
Private to the ui*.cpp files: the state and helpers they share.
@ PAY_STEP_NONE
lv_obj_t * build_page(int step, bool band=true)
#define ACT_BTN_Y
#define CODE_EYE_GAP
#define COL_DANGER
void pop_in(lv_obj_t *obj)
Definition ui_sale.cpp:673
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)
#define HDR_DIVIDER_Y
lv_obj_t * make_icon_button(const char *sym, BtnAction act, lv_obj_t *parent=NULL)
#define OTA_CARD_W
void make_divider(lv_obj_t *parent, lv_coord_t y)
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)
#define SCR_H
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)
#define COL_SURFACE
#define COL_DIM
#define CARD_BTN_Y
void make_tap_mark(lv_obj_t *parent, lv_coord_t y)
#define SCR_W
#define CARD_PAD
#define CARD_BTN_W
#define OTA_TEXT_W
#define COL_TEXT
#define CODE_FIELD_X
#define COL_ACCENT
#define ACT_BTN_H
#define FIELD_PAD_V
#define TAP_MARK_H
void build_header(const char *title)
void clear_screen(void)
#define COL_SUCCESS
@ ACT_WIFI_PASS_REVEAL
@ ACT_WIFI_CANCEL
@ ACT_PROV_OK
@ ACT_WELCOME_OK
@ ACT_CANCEL
@ ACT_WIFI
@ ACT_PROV_NO
@ ACT_PROV_FINISH
#define CARD_BTN_H
#define CODE_FIELD_W(kbd_w)
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.
#define COL_BG
#define COL_ACTION
#define CARD_W
void build_wifi_list(void)
Definition ui_setup.cpp:314
void build_prov(void)
Definition ui_setup.cpp:65
void build_wifi_pass(void)
Definition ui_setup.cpp:375
static void wifi_pass_kb_cb(lv_event_t *e)
Definition ui_setup.cpp:360
void build_card_wait(void)
Definition ui_setup.cpp:271
void build_prov_confirm(void)
Definition ui_setup.cpp:225
static void build_header_back(const char *title)
Definition ui_setup.cpp:301
void build_wifi_connecting(void)
Definition ui_setup.cpp:409
void build_welcome(void)
Definition ui_setup.cpp:17
static void wifi_item_cb(lv_event_t *e)
Definition ui_setup.cpp:307