v0.0.31 - Harden LVGL screen transitions with widget pointer resets and null guards

This commit is contained in:
Laan Tungir
2026-05-28 09:22:42 -04:00
parent 430e391347
commit 6fd09f521a
4 changed files with 529 additions and 73 deletions

View File

@@ -277,6 +277,72 @@ size_t mnemonic_words_for_letter(char letter, size_t *out_start)
return s_letter_count[idx]; return s_letter_count[idx];
} }
size_t mnemonic_words_with_prefix(const char *prefix, uint16_t *out_indices, size_t max_out)
{
size_t start = 0;
size_t count = 0;
size_t written = 0;
if (prefix == NULL || prefix[0] == '\0') {
return 0;
}
count = mnemonic_words_for_letter(prefix[0], &start);
if (count == 0) {
return 0;
}
for (size_t i = 0; i < count; ++i) {
size_t idx = start + i;
const char *word = BIP39_WORDLIST[idx];
if (strncmp(word, prefix, strlen(prefix)) == 0) {
if (out_indices != NULL && written < max_out) {
out_indices[written] = (uint16_t)idx;
}
written++;
}
}
return written;
}
bool mnemonic_letter_extends_prefix(const char *prefix, size_t prefix_len, char next_letter)
{
char probe[16];
size_t start = 0;
size_t count = 0;
if (next_letter >= 'A' && next_letter <= 'Z') {
next_letter = (char)(next_letter - 'A' + 'a');
}
if (next_letter < 'a' || next_letter > 'z') {
return false;
}
if (prefix == NULL || prefix_len >= sizeof(probe) - 1) {
return false;
}
memcpy(probe, prefix, prefix_len);
probe[prefix_len] = next_letter;
probe[prefix_len + 1] = '\0';
count = mnemonic_words_for_letter(probe[0], &start);
if (count == 0) {
return false;
}
for (size_t i = 0; i < count; ++i) {
const char *word = BIP39_WORDLIST[start + i];
if (strncmp(word, probe, prefix_len + 1) == 0) {
return true;
}
}
return false;
}
const char *mnemonic_word_at(size_t index) const char *mnemonic_word_at(size_t index)
{ {
if (index >= 2048) { if (index >= 2048) {

View File

@@ -1,5 +1,6 @@
#pragma once #pragma once
#include <stdbool.h>
#include <stddef.h> #include <stddef.h>
#include <stdint.h> #include <stdint.h>
@@ -7,4 +8,6 @@ int generate_mnemonic_12(char *out, size_t out_len);
int mnemonic_to_seed(const char *mnemonic, uint8_t seed[64]); int mnemonic_to_seed(const char *mnemonic, uint8_t seed[64]);
int mnemonic_validate(const char *mnemonic); int mnemonic_validate(const char *mnemonic);
size_t mnemonic_words_for_letter(char letter, size_t *out_start); size_t mnemonic_words_for_letter(char letter, size_t *out_start);
size_t mnemonic_words_with_prefix(const char *prefix, uint16_t *out_indices, size_t max_out);
bool mnemonic_letter_extends_prefix(const char *prefix, size_t prefix_len, char next_letter);
const char *mnemonic_word_at(size_t index); const char *mnemonic_word_at(size_t index);

View File

@@ -13,10 +13,15 @@
#include "ili9341.h" #include "ili9341.h"
#include "lvgl.h" #include "lvgl.h"
#include "mnemonic.h" #include "mnemonic.h"
#include "secure_mem.h"
#include "touch.h" #include "touch.h"
#define UI_TICK_MS 2 #define UI_TICK_MS 2
#define UI_DRAW_LINES 20 #define UI_DRAW_LINES 20
#define UI_MNEMONIC_WORDS 12
#define UI_BIP39_WORD_MAX 9
#define UI_SUGGESTION_STORE_MAX 64
#define UI_SUGGESTION_VISIBLE_MAX 32
static const char *TAG = "ui_lvgl"; static const char *TAG = "ui_lvgl";
@@ -30,7 +35,21 @@ static bool s_done = false;
static bool s_have_mnemonic = false; static bool s_have_mnemonic = false;
static char s_selected_mnemonic[256]; static char s_selected_mnemonic[256];
static lv_obj_t *s_status_label = NULL; static lv_obj_t *s_status_label = NULL;
static lv_obj_t *s_entry_ta = NULL;
static char s_committed_words[UI_MNEMONIC_WORDS][UI_BIP39_WORD_MAX];
static size_t s_word_count = 0;
static char s_prefix[UI_BIP39_WORD_MAX];
static size_t s_prefix_len = 0;
static bool s_checksum_override = false;
static lv_obj_t *s_entry_words_label = NULL;
static lv_obj_t *s_entry_prefix_label = NULL;
static lv_obj_t *s_suggestion_row = NULL;
static lv_obj_t *s_key_matrix = NULL;
static lv_obj_t *s_checksum_panel = NULL;
static uint16_t s_match_indices[UI_SUGGESTION_STORE_MAX];
static size_t s_match_total = 0;
static ui_approval_decision_t s_approval_decision = UI_APPROVAL_TIMEOUT; static ui_approval_decision_t s_approval_decision = UI_APPROVAL_TIMEOUT;
static lv_obj_t *s_approval_countdown = NULL; static lv_obj_t *s_approval_countdown = NULL;
@@ -41,6 +60,18 @@ static int64_t s_approval_deadline_us = 0;
static char s_event_lines[UI_EVENT_LOG_MAX][UI_EVENT_LINE_MAX]; static char s_event_lines[UI_EVENT_LOG_MAX][UI_EVENT_LINE_MAX];
static int s_event_count = 0; static int s_event_count = 0;
static void reset_screen_widget_refs(void)
{
s_status_label = NULL;
s_entry_words_label = NULL;
s_entry_prefix_label = NULL;
s_suggestion_row = NULL;
s_key_matrix = NULL;
s_checksum_panel = NULL;
s_approval_countdown = NULL;
s_match_total = 0;
}
static void style_screen(lv_obj_t *scr) static void style_screen(lv_obj_t *scr)
{ {
lv_obj_set_style_bg_opa(scr, LV_OPA_COVER, 0); lv_obj_set_style_bg_opa(scr, LV_OPA_COVER, 0);
@@ -62,31 +93,24 @@ static void style_button(lv_obj_t *btn)
lv_obj_set_style_text_color(btn, lv_color_hex(0xFF0000), LV_STATE_FOCUSED); lv_obj_set_style_text_color(btn, lv_color_hex(0xFF0000), LV_STATE_FOCUSED);
} }
static void style_text_area(lv_obj_t *ta) static void style_key_matrix(lv_obj_t *km)
{ {
lv_obj_set_style_bg_opa(ta, LV_OPA_COVER, 0); lv_obj_set_style_bg_opa(km, LV_OPA_COVER, 0);
lv_obj_set_style_bg_color(ta, lv_color_hex(0x000000), 0); lv_obj_set_style_bg_color(km, lv_color_hex(0x000000), 0);
lv_obj_set_style_border_width(ta, 2, 0); lv_obj_set_style_border_width(km, 1, 0);
lv_obj_set_style_border_color(ta, lv_color_hex(0xFFFFFF), 0); lv_obj_set_style_border_color(km, lv_color_hex(0xFFFFFF), 0);
lv_obj_set_style_text_color(ta, lv_color_hex(0xFFFFFF), 0);
lv_obj_set_style_text_color(ta, lv_color_hex(0xFF0000), LV_PART_CURSOR);
}
static void style_keyboard(lv_obj_t *kb) lv_obj_set_style_bg_opa(km, LV_OPA_COVER, LV_PART_ITEMS);
{ lv_obj_set_style_bg_color(km, lv_color_hex(0x000000), LV_PART_ITEMS);
lv_obj_set_style_bg_opa(kb, LV_OPA_COVER, 0); lv_obj_set_style_border_color(km, lv_color_hex(0xFFFFFF), LV_PART_ITEMS);
lv_obj_set_style_bg_color(kb, lv_color_hex(0x000000), 0); lv_obj_set_style_border_width(km, 1, LV_PART_ITEMS);
lv_obj_set_style_border_width(kb, 1, 0); lv_obj_set_style_text_color(km, lv_color_hex(0xFFFFFF), LV_PART_ITEMS);
lv_obj_set_style_border_color(kb, lv_color_hex(0xFFFFFF), 0);
lv_obj_set_style_bg_opa(kb, LV_OPA_COVER, LV_PART_ITEMS); lv_obj_set_style_border_color(km, lv_color_hex(0xFF0000), LV_PART_ITEMS | LV_STATE_PRESSED);
lv_obj_set_style_bg_color(kb, lv_color_hex(0x000000), LV_PART_ITEMS); lv_obj_set_style_text_color(km, lv_color_hex(0xFF0000), LV_PART_ITEMS | LV_STATE_PRESSED);
lv_obj_set_style_border_color(kb, lv_color_hex(0xFFFFFF), LV_PART_ITEMS);
lv_obj_set_style_border_width(kb, 1, LV_PART_ITEMS);
lv_obj_set_style_text_color(kb, lv_color_hex(0xFFFFFF), LV_PART_ITEMS);
lv_obj_set_style_border_color(kb, lv_color_hex(0xFF0000), LV_PART_ITEMS | LV_STATE_PRESSED); lv_obj_set_style_text_color(km, lv_color_hex(0x505050), LV_PART_ITEMS | LV_STATE_DISABLED);
lv_obj_set_style_text_color(kb, lv_color_hex(0xFF0000), LV_PART_ITEMS | LV_STATE_PRESSED); lv_obj_set_style_border_color(km, lv_color_hex(0x303030), LV_PART_ITEMS | LV_STATE_DISABLED);
} }
static void lvgl_tick_cb(void *arg) static void lvgl_tick_cb(void *arg)
@@ -198,85 +222,443 @@ static void on_generate(lv_event_t *e)
} }
static void build_main_menu(void); static void build_main_menu(void);
static void refresh_entry_ui(void);
static bool verify_or_prompt_checksum(void);
static void on_back_to_menu(lv_event_t *e) static const char *s_key_map[] = {
"q", "w", "e", "r", "t", "y", "u", "i", "o", "p", "\n",
"a", "s", "d", "f", "g", "h", "j", "k", "l", "\n",
"z", "x", "c", "v", "b", "n", "m", "<", "",
};
static void clear_entry_state(void)
{ {
(void)e; secure_memzero(s_committed_words, sizeof(s_committed_words));
build_main_menu(); secure_memzero(s_prefix, sizeof(s_prefix));
s_word_count = 0;
s_prefix_len = 0;
s_checksum_override = false;
s_match_total = 0;
} }
static void on_use_entry(lv_event_t *e) static bool join_committed_words(char *out, size_t out_len)
{
size_t cursor = 0;
if (out == NULL || out_len == 0) {
return false;
}
out[0] = '\0';
for (size_t i = 0; i < s_word_count; ++i) {
size_t wl = strlen(s_committed_words[i]);
if (cursor != 0) {
if (cursor + 1 >= out_len) {
out[0] = '\0';
return false;
}
out[cursor++] = ' ';
}
if (cursor + wl >= out_len) {
out[0] = '\0';
return false;
}
memcpy(out + cursor, s_committed_words[i], wl);
cursor += wl;
out[cursor] = '\0';
}
return true;
}
static void commit_word(const char *word)
{
if (word == NULL || s_word_count >= UI_MNEMONIC_WORDS) {
return;
}
memset(s_committed_words[s_word_count], 0, UI_BIP39_WORD_MAX);
strncpy(s_committed_words[s_word_count], word, UI_BIP39_WORD_MAX - 1);
s_word_count++;
secure_memzero(s_prefix, sizeof(s_prefix));
s_prefix_len = 0;
s_checksum_override = false;
}
static void on_checksum_use_anyway(lv_event_t *e)
{ {
(void)e; (void)e;
if (s_entry_ta == NULL) { char joined[256];
return; memset(joined, 0, sizeof(joined));
} if (!join_committed_words(joined, sizeof(joined))) {
set_status("Mnemonic buffer error");
const char *txt = lv_textarea_get_text(s_entry_ta); secure_memzero(joined, sizeof(joined));
if (txt == NULL || txt[0] == '\0') {
set_status("Mnemonic is empty");
return;
}
if (mnemonic_validate(txt) != 0) {
set_status("Invalid mnemonic");
return; return;
} }
memset(s_selected_mnemonic, 0, sizeof(s_selected_mnemonic)); memset(s_selected_mnemonic, 0, sizeof(s_selected_mnemonic));
strncpy(s_selected_mnemonic, txt, sizeof(s_selected_mnemonic) - 1); strncpy(s_selected_mnemonic, joined, sizeof(s_selected_mnemonic) - 1);
s_have_mnemonic = true; s_have_mnemonic = true;
s_done = true; s_done = true;
s_checksum_override = true;
secure_memzero(joined, sizeof(joined));
}
static void on_checksum_edit_words(lv_event_t *e)
{
(void)e;
if (s_checksum_panel != NULL) {
lv_obj_del(s_checksum_panel);
s_checksum_panel = NULL;
}
set_status("Checksum mismatch: edit or use anyway");
refresh_entry_ui();
}
static bool verify_or_prompt_checksum(void)
{
char joined[256];
if (s_key_matrix == NULL || s_entry_words_label == NULL || s_entry_prefix_label == NULL) {
return false;
}
if (s_word_count != UI_MNEMONIC_WORDS || s_prefix_len != 0) {
set_status("Need exactly 12 complete words");
return false;
}
memset(joined, 0, sizeof(joined));
if (!join_committed_words(joined, sizeof(joined))) {
set_status("Mnemonic buffer error");
secure_memzero(joined, sizeof(joined));
return false;
}
if (mnemonic_validate(joined) == 0) {
if (s_checksum_panel != NULL) {
lv_obj_del(s_checksum_panel);
s_checksum_panel = NULL;
}
set_status("Checksum valid");
memset(s_selected_mnemonic, 0, sizeof(s_selected_mnemonic));
strncpy(s_selected_mnemonic, joined, sizeof(s_selected_mnemonic) - 1);
s_have_mnemonic = true;
s_done = true;
secure_memzero(joined, sizeof(joined));
return true;
}
if (s_checksum_panel != NULL) {
lv_obj_del(s_checksum_panel);
s_checksum_panel = NULL;
}
s_checksum_panel = lv_obj_create(lv_scr_act());
lv_obj_set_size(s_checksum_panel, DISPLAY_WIDTH - 20, 82);
lv_obj_align(s_checksum_panel, LV_ALIGN_TOP_MID, 0, 78);
lv_obj_set_style_bg_color(s_checksum_panel, lv_color_hex(0x000000), 0);
lv_obj_set_style_border_color(s_checksum_panel, lv_color_hex(0xFFFFFF), 0);
lv_obj_set_style_border_width(s_checksum_panel, 2, 0);
lv_obj_set_style_radius(s_checksum_panel, 6, 0);
lv_obj_set_style_pad_all(s_checksum_panel, 6, 0);
lv_obj_t *txt = lv_label_create(s_checksum_panel);
lv_label_set_text(txt, "Checksum invalid. Use anyway?");
lv_obj_set_style_text_color(txt, lv_color_hex(0xFF0000), 0);
lv_obj_align(txt, LV_ALIGN_TOP_MID, 0, 0);
lv_obj_t *use_btn = lv_btn_create(s_checksum_panel);
lv_obj_set_size(use_btn, 128, 30);
lv_obj_align(use_btn, LV_ALIGN_BOTTOM_LEFT, 4, -2);
style_button(use_btn);
lv_obj_set_style_border_color(use_btn, lv_color_hex(0xFF0000), 0);
lv_obj_add_event_cb(use_btn, on_checksum_use_anyway, LV_EVENT_RELEASED, NULL);
lv_obj_t *use_lbl = lv_label_create(use_btn);
lv_label_set_text(use_lbl, "Use Anyway");
lv_obj_center(use_lbl);
lv_obj_t *edit_btn = lv_btn_create(s_checksum_panel);
lv_obj_set_size(edit_btn, 128, 30);
lv_obj_align(edit_btn, LV_ALIGN_BOTTOM_RIGHT, -4, -2);
style_button(edit_btn);
lv_obj_add_event_cb(edit_btn, on_checksum_edit_words, LV_EVENT_RELEASED, NULL);
lv_obj_t *edit_lbl = lv_label_create(edit_btn);
lv_label_set_text(edit_lbl, "Edit Words");
lv_obj_center(edit_lbl);
set_status("Checksum invalid");
secure_memzero(joined, sizeof(joined));
return false;
}
static void on_back_to_menu(lv_event_t *e)
{
(void)e;
if (s_checksum_panel != NULL) {
lv_obj_del(s_checksum_panel);
s_checksum_panel = NULL;
}
clear_entry_state();
build_main_menu();
}
static void on_suggestion_tap(lv_event_t *e)
{
const char *word = (const char *)lv_event_get_user_data(e);
if (s_suggestion_row == NULL || word == NULL || s_word_count >= UI_MNEMONIC_WORDS) {
return;
}
commit_word(word);
refresh_entry_ui();
if (s_word_count == UI_MNEMONIC_WORDS) {
(void)verify_or_prompt_checksum();
}
}
static void entry_backspace_step(void)
{
if (s_prefix_len > 0) {
s_prefix_len--;
s_prefix[s_prefix_len] = '\0';
} else if (s_word_count > 0) {
s_word_count--;
memset(s_prefix, 0, sizeof(s_prefix));
strncpy(s_prefix, s_committed_words[s_word_count], sizeof(s_prefix) - 1);
s_prefix_len = strlen(s_prefix);
secure_memzero(s_committed_words[s_word_count], sizeof(s_committed_words[s_word_count]));
}
s_checksum_override = false;
if (s_checksum_panel != NULL) {
lv_obj_del(s_checksum_panel);
s_checksum_panel = NULL;
}
refresh_entry_ui();
}
static void on_key_matrix(lv_event_t *e)
{
lv_obj_t *obj = lv_event_get_target(e);
if (s_key_matrix == NULL || obj != s_key_matrix) {
return;
}
uint16_t idx = lv_btnmatrix_get_selected_btn(obj);
const char *txt = lv_btnmatrix_get_btn_text(obj, idx);
if (txt == NULL || txt[0] == '\0' || txt[1] != '\0') {
return;
}
if (txt[0] == '<') {
entry_backspace_step();
return;
}
if (s_word_count >= UI_MNEMONIC_WORDS) {
set_status("Already have 12 words");
return;
}
if (s_prefix_len + 1 >= sizeof(s_prefix)) {
set_status("Word too long");
return;
}
s_prefix[s_prefix_len++] = txt[0];
s_prefix[s_prefix_len] = '\0';
s_checksum_override = false;
refresh_entry_ui();
if (s_match_total == 1) {
const char *word = mnemonic_word_at(s_match_indices[0]);
if (word != NULL) {
commit_word(word);
refresh_entry_ui();
if (s_word_count == UI_MNEMONIC_WORDS) {
(void)verify_or_prompt_checksum();
}
}
}
}
static void refresh_entry_ui(void)
{
size_t shown = 0;
char words_line[256];
char prefix_line[64];
if (s_checksum_panel != NULL) {
lv_obj_del(s_checksum_panel);
s_checksum_panel = NULL;
}
memset(words_line, 0, sizeof(words_line));
(void)join_committed_words(words_line, sizeof(words_line));
if (s_entry_words_label != NULL) {
if (s_word_count == 0) {
lv_label_set_text(s_entry_words_label, "(no words selected)");
} else {
lv_label_set_text(s_entry_words_label, words_line);
}
}
snprintf(prefix_line, sizeof(prefix_line), "Word %u/12 prefix: %s",
(unsigned)(s_word_count + 1),
(s_prefix_len > 0) ? s_prefix : "-");
if (s_entry_prefix_label != NULL) {
lv_label_set_text(s_entry_prefix_label, prefix_line);
}
if (s_suggestion_row != NULL) {
lv_obj_clean(s_suggestion_row);
if (s_prefix_len > 0 && s_word_count < UI_MNEMONIC_WORDS) {
s_match_total = mnemonic_words_with_prefix(s_prefix, s_match_indices, UI_SUGGESTION_STORE_MAX);
shown = (s_match_total > UI_SUGGESTION_VISIBLE_MAX) ? UI_SUGGESTION_VISIBLE_MAX : s_match_total;
for (size_t i = 0; i < shown; ++i) {
const char *word = mnemonic_word_at(s_match_indices[i]);
lv_obj_t *btn;
lv_obj_t *lbl;
if (word == NULL) {
continue;
}
btn = lv_btn_create(s_suggestion_row);
lv_obj_set_size(btn, 84, 26);
style_button(btn);
lv_obj_add_event_cb(btn, on_suggestion_tap, LV_EVENT_RELEASED, (void *)word);
lbl = lv_label_create(btn);
lv_label_set_text(lbl, word);
lv_obj_center(lbl);
}
if (s_match_total > shown) {
char more[24];
lv_obj_t *m = lv_label_create(s_suggestion_row);
snprintf(more, sizeof(more), "+%u more", (unsigned)(s_match_total - shown));
lv_label_set_text(m, more);
lv_obj_set_style_text_color(m, lv_color_hex(0xFFFFFF), 0);
}
if (s_match_total == 0) {
set_status("No matches");
} else {
set_status("");
}
} else {
s_match_total = 0;
}
}
if (s_key_matrix != NULL) {
const uint16_t matrix_btn_count = 27;
for (uint16_t i = 0; i < matrix_btn_count; ++i) {
const char *txt = lv_btnmatrix_get_btn_text(s_key_matrix, i);
bool enabled = false;
if (txt == NULL || txt[0] == '\0' || txt[1] != '\0') {
continue;
}
if (txt[0] == '<') {
enabled = (s_prefix_len > 0 || s_word_count > 0);
} else if (s_word_count >= UI_MNEMONIC_WORDS) {
enabled = false;
} else if (s_prefix_len == 0) {
size_t start = 0;
enabled = (mnemonic_words_for_letter(txt[0], &start) > 0);
} else {
enabled = mnemonic_letter_extends_prefix(s_prefix, s_prefix_len, txt[0]);
}
if (enabled) {
lv_btnmatrix_clear_btn_ctrl(s_key_matrix, i, LV_BTNMATRIX_CTRL_DISABLED);
} else {
lv_btnmatrix_set_btn_ctrl(s_key_matrix, i, LV_BTNMATRIX_CTRL_DISABLED);
}
}
}
} }
static void build_entry_screen(void) static void build_entry_screen(void)
{ {
lv_obj_t *scr = lv_scr_act(); lv_obj_t *scr = lv_scr_act();
lv_obj_t *title;
lv_obj_t *close_btn;
lv_obj_t *close_lbl;
reset_screen_widget_refs();
lv_obj_clean(scr); lv_obj_clean(scr);
style_screen(scr); style_screen(scr);
clear_entry_state();
lv_obj_t *title = lv_label_create(scr); title = lv_label_create(scr);
lv_label_set_text(title, "Enter Mnemonic"); lv_label_set_text(title, "Enter Mnemonic");
lv_obj_set_style_text_font(title, &lv_font_montserrat_14, 0); lv_obj_set_style_text_font(title, &lv_font_montserrat_14, 0);
lv_obj_set_style_text_color(title, lv_color_hex(0xFFFFFF), 0); lv_obj_set_style_text_color(title, lv_color_hex(0xFFFFFF), 0);
lv_obj_align(title, LV_ALIGN_TOP_MID, 0, 6); lv_obj_align(title, LV_ALIGN_TOP_MID, 0, 4);
s_entry_ta = lv_textarea_create(scr); s_entry_words_label = lv_label_create(scr);
lv_obj_set_size(s_entry_ta, DISPLAY_WIDTH - 16, 70); lv_obj_set_width(s_entry_words_label, DISPLAY_WIDTH - 16);
lv_obj_align(s_entry_ta, LV_ALIGN_TOP_MID, 0, 32); lv_label_set_long_mode(s_entry_words_label, LV_LABEL_LONG_WRAP);
lv_textarea_set_placeholder_text(s_entry_ta, "12 words separated by spaces"); lv_label_set_text(s_entry_words_label, "(no words selected)");
lv_textarea_set_one_line(s_entry_ta, false); lv_obj_set_style_text_color(s_entry_words_label, lv_color_hex(0xFFFFFF), 0);
style_text_area(s_entry_ta); lv_obj_align(s_entry_words_label, LV_ALIGN_TOP_LEFT, 8, 26);
lv_obj_t *kb = lv_keyboard_create(scr); s_entry_prefix_label = lv_label_create(scr);
lv_obj_set_size(kb, DISPLAY_WIDTH, 110); lv_label_set_text(s_entry_prefix_label, "Word 1/12 prefix: -");
lv_obj_align(kb, LV_ALIGN_BOTTOM_MID, 0, 0); lv_obj_set_style_text_color(s_entry_prefix_label, lv_color_hex(0xFFFFFF), 0);
lv_keyboard_set_textarea(kb, s_entry_ta); lv_obj_align(s_entry_prefix_label, LV_ALIGN_TOP_LEFT, 8, 66);
style_keyboard(kb);
lv_obj_t *use_btn = lv_btn_create(scr);
lv_obj_set_size(use_btn, 150, 30);
lv_obj_align(use_btn, LV_ALIGN_TOP_LEFT, 8, 110);
lv_obj_add_event_cb(use_btn, on_use_entry, LV_EVENT_RELEASED, NULL);
style_button(use_btn);
lv_obj_t *use_lbl = lv_label_create(use_btn);
lv_label_set_text(use_lbl, "Use Mnemonic");
lv_obj_center(use_lbl);
lv_obj_t *back_btn = lv_btn_create(scr);
lv_obj_set_size(back_btn, 150, 30);
lv_obj_align(back_btn, LV_ALIGN_TOP_RIGHT, -8, 110);
lv_obj_add_event_cb(back_btn, on_back_to_menu, LV_EVENT_RELEASED, NULL);
style_button(back_btn);
lv_obj_t *back_lbl = lv_label_create(back_btn);
lv_label_set_text(back_lbl, "Back");
lv_obj_center(back_lbl);
s_status_label = lv_label_create(scr); s_status_label = lv_label_create(scr);
lv_label_set_text(s_status_label, ""); lv_label_set_text(s_status_label, "");
lv_obj_set_style_text_color(s_status_label, lv_color_hex(0xFF0000), 0); lv_obj_set_style_text_color(s_status_label, lv_color_hex(0xFF0000), 0);
lv_obj_align(s_status_label, LV_ALIGN_TOP_LEFT, 8, 144); lv_obj_align(s_status_label, LV_ALIGN_TOP_LEFT, 8, 82);
s_suggestion_row = lv_obj_create(scr);
lv_obj_set_size(s_suggestion_row, DISPLAY_WIDTH - 12, 30);
lv_obj_align(s_suggestion_row, LV_ALIGN_TOP_MID, 0, 96);
lv_obj_set_style_bg_color(s_suggestion_row, lv_color_hex(0x000000), 0);
lv_obj_set_style_border_color(s_suggestion_row, lv_color_hex(0xFFFFFF), 0);
lv_obj_set_style_border_width(s_suggestion_row, 1, 0);
lv_obj_set_style_pad_all(s_suggestion_row, 2, 0);
lv_obj_set_scroll_dir(s_suggestion_row, LV_DIR_HOR);
lv_obj_set_flex_flow(s_suggestion_row, LV_FLEX_FLOW_ROW);
lv_obj_set_flex_align(s_suggestion_row, LV_FLEX_ALIGN_START, LV_FLEX_ALIGN_CENTER, LV_FLEX_ALIGN_CENTER);
close_btn = lv_btn_create(scr);
lv_obj_set_size(close_btn, 42, 30);
lv_obj_align(close_btn, LV_ALIGN_TOP_RIGHT, -4, 2);
style_button(close_btn);
lv_obj_add_event_cb(close_btn, on_back_to_menu, LV_EVENT_CLICKED, NULL);
close_lbl = lv_label_create(close_btn);
lv_label_set_text(close_lbl, "X");
lv_obj_center(close_lbl);
s_key_matrix = lv_btnmatrix_create(scr);
lv_obj_set_size(s_key_matrix, DISPLAY_WIDTH - 16, 100);
lv_obj_align(s_key_matrix, LV_ALIGN_TOP_MID, 0, 132);
lv_btnmatrix_set_map(s_key_matrix, s_key_map);
lv_obj_add_event_cb(s_key_matrix, on_key_matrix, LV_EVENT_VALUE_CHANGED, NULL);
style_key_matrix(s_key_matrix);
lv_obj_set_style_pad_all(s_key_matrix, 0, 0);
lv_obj_set_style_pad_gap(s_key_matrix, 0, LV_PART_ITEMS);
lv_obj_set_style_border_width(s_key_matrix, 1, LV_PART_ITEMS);
refresh_entry_ui();
} }
static void on_enter(lv_event_t *e) static void on_enter(lv_event_t *e)
@@ -316,6 +698,7 @@ static void on_use_viewed(lv_event_t *e)
static void build_view_screen(void) static void build_view_screen(void)
{ {
lv_obj_t *scr = lv_scr_act(); lv_obj_t *scr = lv_scr_act();
reset_screen_widget_refs();
lv_obj_clean(scr); lv_obj_clean(scr);
style_screen(scr); style_screen(scr);
@@ -370,6 +753,7 @@ static void on_view(lv_event_t *e)
static void build_main_menu(void) static void build_main_menu(void)
{ {
lv_obj_t *scr = lv_scr_act(); lv_obj_t *scr = lv_scr_act();
reset_screen_widget_refs();
lv_obj_clean(scr); lv_obj_clean(scr);
style_screen(scr); style_screen(scr);
@@ -424,6 +808,7 @@ void ui_show_approval_prompt(const char *caller_pubkey_hex, int kind, const char
} }
lv_obj_t *scr = lv_scr_act(); lv_obj_t *scr = lv_scr_act();
reset_screen_widget_refs();
lv_obj_clean(scr); lv_obj_clean(scr);
style_screen(scr); style_screen(scr);
@@ -535,6 +920,7 @@ ui_approval_decision_t ui_wait_for_approval(uint32_t timeout_ms)
static void show_event_log_screen(void) static void show_event_log_screen(void)
{ {
lv_obj_t *scr = lv_scr_act(); lv_obj_t *scr = lv_scr_act();
reset_screen_widget_refs();
lv_obj_clean(scr); lv_obj_clean(scr);
style_screen(scr); style_screen(scr);
@@ -579,6 +965,7 @@ void ui_show_idle(const char *npub_full, const char *mnemonic_full, const char *
} }
lv_obj_t *scr = lv_scr_act(); lv_obj_t *scr = lv_scr_act();
reset_screen_widget_refs();
lv_obj_clean(scr); lv_obj_clean(scr);
style_screen(scr); style_screen(scr);

View File

@@ -491,8 +491,8 @@ int socket_name_random(char *out, size_t out_len);
/* Version information (auto-updated by build/version tooling) */ /* Version information (auto-updated by build/version tooling) */
#define NSIGNER_VERSION_MAJOR 0 #define NSIGNER_VERSION_MAJOR 0
#define NSIGNER_VERSION_MINOR 0 #define NSIGNER_VERSION_MINOR 0
#define NSIGNER_VERSION_PATCH 30 #define NSIGNER_VERSION_PATCH 31
#define NSIGNER_VERSION "v0.0.30" #define NSIGNER_VERSION "v0.0.31"
/* NSIGNER_HEADERLESS_DECLS_END */ /* NSIGNER_HEADERLESS_DECLS_END */