working mnemonic input

This commit is contained in:
Michele Balistreri 2023-07-17 15:54:52 +02:00
parent c5a254627a
commit b1cacb869c
No known key found for this signature in database
GPG Key ID: E9567DA33A4F791A
7 changed files with 193 additions and 18 deletions

View File

@ -7,6 +7,16 @@
#define KEYPAD_GPIO_COL_OFF GPIO_KEYPAD_COL_0
#define KEYPAD_DEBOUNCE_THRESHOLD 10
#define KEYPAD_LONG_PRESS_THRESHOLD 100
static inline void keypad_report_key(keypad_key_t key, bool is_long) {
g_ui_ctx.keypad.last_key = key;
g_ui_ctx.keypad.last_key_long = is_long;
BaseType_t xHigherPriorityTaskWoken = pdFALSE;
xTaskNotifyIndexedFromISR(APP_TASK(ui), UI_NOTIFICATION_IDX, UI_KEY_EVT, eSetBits, &xHigherPriorityTaskWoken);
portYIELD_FROM_ISR(xHigherPriorityTaskWoken);
}
void keypad_scan_tick() {
for (int i = 0; i < KEYPAD_COLS; i++) {
@ -16,17 +26,17 @@ void keypad_scan_tick() {
uint32_t duration = g_ui_ctx.keypad.matrix_state[key];
g_ui_ctx.keypad.matrix_state[key] = 0;
if (duration > KEYPAD_DEBOUNCE_THRESHOLD) {
g_ui_ctx.keypad.last_key = key;
g_ui_ctx.keypad.last_key_duration = duration;
BaseType_t xHigherPriorityTaskWoken = pdFALSE;
xTaskNotifyIndexedFromISR(APP_TASK(ui), UI_NOTIFICATION_IDX, UI_KEY_EVT, eSetBits, &xHigherPriorityTaskWoken);
portYIELD_FROM_ISR(xHigherPriorityTaskWoken);
if (duration > KEYPAD_DEBOUNCE_THRESHOLD && duration < KEYPAD_LONG_PRESS_THRESHOLD) {
keypad_report_key(key, false);
break;
}
} else {
g_ui_ctx.keypad.matrix_state[key]++;
if (g_ui_ctx.keypad.matrix_state[key] >= KEYPAD_LONG_PRESS_THRESHOLD && g_ui_ctx.keypad.matrix_state[key] < (KEYPAD_LONG_PRESS_THRESHOLD * 2)) {
g_ui_ctx.keypad.matrix_state[key] = KEYPAD_LONG_PRESS_THRESHOLD * 2;
keypad_report_key(key, true);
break;
}
}
}

View File

@ -2,6 +2,7 @@
#define _KEYPAD_H_
#include <stdint.h>
#include <stdbool.h>
#define KEYPAD_ROWS 4
#define KEYPAD_COLS 3
@ -29,7 +30,7 @@ typedef enum {
typedef struct {
uint32_t matrix_state[KEYPAD_ROWS * KEYPAD_COLS];
uint32_t last_key_duration;
bool last_key_long;
keypad_key_t last_key;
uint8_t current_row;
} keypad_t;

View File

@ -243,6 +243,20 @@ hal_err_t screen_draw_char(const screen_text_ctx_t* ctx, char c) {
return screen_draw_glyph(ctx, screen_lookup_glyph(ctx->font, c));
}
hal_err_t screen_draw_chars(screen_text_ctx_t* ctx, const char* str, int len) {
while(len--) {
const glyph_t* glyph = screen_lookup_glyph(ctx->font, *(str++));
if (screen_draw_glyph(ctx, glyph) != HAL_SUCCESS) {
return HAL_FAIL;
}
ctx->x += glyph->xAdvance;
}
return HAL_SUCCESS;
}
hal_err_t screen_draw_string(screen_text_ctx_t* ctx, const char* str) {
char c;

View File

@ -69,6 +69,7 @@ hal_err_t screen_draw_glyph(const screen_text_ctx_t* ctx, const glyph_t* glyph);
// High level API
hal_err_t screen_draw_char(const screen_text_ctx_t* ctx, char c);
hal_err_t screen_draw_chars(screen_text_ctx_t* ctx, const char* str, int len);
hal_err_t screen_draw_string(screen_text_ctx_t* ctx, const char* str);
hal_err_t screen_draw_text(screen_text_ctx_t* ctx, uint16_t max_x, uint16_t max_y, const uint8_t* text, size_t len);
hal_err_t screen_fill_area(const screen_area_t* area, uint16_t color);

View File

@ -6,6 +6,7 @@
#include "theme.h"
#include "crypto/bip39.h"
#include "crypto/util.h"
#include "keypad/keypad.h"
#include "ui/ui.h"
#include "ui/ui_internal.h"
@ -14,8 +15,22 @@
#define KEY_BACKSPACE 0x08
#define KEY_RETURN 0x0d
#define KEY_ESCAPE 0x1b
#define WORD_MAX_LEN 8
#define KEYBOARD_TOP_Y (SCREEN_HEIGHT - (TH_KEYBOARD_KEY_SIZE * 3))
#define KEYBOARD_ROW1_LEN 10
#define KEYBOARD_ROW2_LEN 9
#define KEYBOARD_ROW3_LEN 7
#define KEYBOARD_ROW1_LIMIT KEYBOARD_ROW1_LEN
#define KEYBOARD_ROW2_LIMIT (KEYBOARD_ROW1_LIMIT + KEYBOARD_ROW2_LEN)
#define KEYBOARD_ROW3_LIMIT (KEYBOARD_ROW2_LIMIT + KEYBOARD_ROW3_LEN)
const char KEYPAD_TO_DIGIT[] = {'1', '2', '3', '4', '5', '6', '7', '8', '9', DIG_INV, '0', DIG_INV, DIG_INV, DIG_INV};
const char KEYBOARD_MAP[] = {
'q', 'w', 'e', 'r', 't', 'y', 'u', 'i', 'o', 'p',
@ -144,8 +159,116 @@ app_err_t input_pin() {
}
}
static inline void input_keyboard_render_key(char c, uint16_t x, uint16_t y, bool selected) {
screen_area_t key_area = { .x = x, .y = y, .width = TH_KEYBOARD_KEY_SIZE, .height = TH_KEYBOARD_KEY_SIZE };
screen_text_ctx_t ctx = { .font = TH_FONT_TEXT, .fg = TH_COLOR_TEXT_FG, .y = y };
const glyph_t* glyph = screen_lookup_glyph(ctx.font, c);
ctx.bg = selected ? TH_KEYBOARD_KEY_SELECTED_BG : TH_KEYBOARD_KEY_BG;
ctx.x = x + ((TH_KEYBOARD_KEY_SIZE - glyph->width) / 2);
screen_fill_area(&key_area, ctx.bg);
screen_draw_glyph(&ctx, glyph);
}
static inline void input_keyboard_render(int idx) {
int i = 0;
while (i < KEYBOARD_ROW1_LIMIT) {
input_keyboard_render_key(KEYBOARD_MAP[i], (i * TH_KEYBOARD_KEY_SIZE), KEYBOARD_TOP_Y, idx == i);
i++;
}
while (i < KEYBOARD_ROW2_LIMIT) {
input_keyboard_render_key(KEYBOARD_MAP[i], ((i - 10) * TH_KEYBOARD_KEY_SIZE), (KEYBOARD_TOP_Y + TH_KEYBOARD_KEY_SIZE), idx == i);
i++;
}
screen_area_t padding = {
.x = KEYBOARD_ROW2_LEN * TH_KEYBOARD_KEY_SIZE,
.y = KEYBOARD_TOP_Y + TH_KEYBOARD_KEY_SIZE,
.width = TH_KEYBOARD_KEY_SIZE,
.height = TH_KEYBOARD_KEY_SIZE
};
screen_fill_area(&padding, TH_KEYBOARD_KEY_BG);
while (i < KEYBOARD_ROW3_LIMIT) {
input_keyboard_render_key(KEYBOARD_MAP[i], ((i - 19) * TH_KEYBOARD_KEY_SIZE), (KEYBOARD_TOP_Y + (TH_KEYBOARD_KEY_SIZE * 2)), idx == i);
i++;
}
padding.x = KEYBOARD_ROW3_LEN * TH_KEYBOARD_KEY_SIZE;
padding.y = KEYBOARD_TOP_Y + (TH_KEYBOARD_KEY_SIZE * 2);
padding.width = (TH_KEYBOARD_KEY_SIZE * 3);
screen_fill_area(&padding, TH_KEYBOARD_KEY_BG);
}
static char input_keyboard(int *idx) {
return KEY_RETURN;
while(1) {
input_keyboard_render(*idx);
switch(ui_wait_keypress(portMAX_DELAY)) {
case KEYPAD_KEY_UP:
if (*idx >= KEYBOARD_ROW2_LIMIT) {
*idx -= KEYBOARD_ROW2_LEN;
} else if (*idx >= KEYBOARD_ROW1_LIMIT) {
*idx -= KEYBOARD_ROW1_LEN;
}
break;
case KEYPAD_KEY_LEFT:
if ((*idx > KEYBOARD_ROW2_LIMIT) ||
((*idx > KEYBOARD_ROW1_LIMIT) && (*idx < KEYBOARD_ROW2_LIMIT)) ||
((*idx > 0) && (*idx < KEYBOARD_ROW1_LIMIT))) {
(*idx)--;
}
break;
case KEYPAD_KEY_RIGHT:
if ((*idx < (KEYBOARD_ROW1_LIMIT - 1)) ||
((*idx < (KEYBOARD_ROW2_LIMIT - 1)) && (*idx >= KEYBOARD_ROW1_LIMIT)) ||
((*idx < (KEYBOARD_ROW3_LIMIT - 1)) && (*idx >= KEYBOARD_ROW2_LIMIT))) {
(*idx)++;
}
break;
case KEYPAD_KEY_DOWN:
if (*idx < KEYBOARD_ROW1_LIMIT) {
*idx = APP_MIN(*idx + KEYBOARD_ROW1_LEN, (KEYBOARD_ROW2_LIMIT - 1));
} else if (*idx < KEYBOARD_ROW2_LIMIT) {
*idx = APP_MIN(*idx + KEYBOARD_ROW2_LEN, (KEYBOARD_ROW3_LIMIT - 1));
}
break;
case KEYPAD_KEY_BACK:
return g_ui_ctx.keypad.last_key_long ? KEY_ESCAPE : KEY_BACKSPACE;
case KEYPAD_KEY_CONFIRM:
return g_ui_ctx.keypad.last_key_long ? KEY_RETURN : KEYBOARD_MAP[*idx];
default:
break;
}
}
}
static void input_render_text_field(char* str, uint16_t y, int len, int suggestion_len) {
screen_text_ctx_t ctx = {
.font = TH_FONT_TEXT,
.fg = TH_TEXT_FIELD_FG,
.bg = TH_TEXT_FIELD_BG,
.x = TH_TEXT_FIELD_MARGIN,
.y = y
};
screen_area_t field_area = {
.x = TH_TEXT_FIELD_MARGIN,
.y = y,
.width = SCREEN_WIDTH - (TH_TEXT_FIELD_MARGIN * 2),
.height = TH_TEXT_FIELD_HEIGHT
};
screen_fill_area(&field_area, ctx.bg);
screen_draw_chars(&ctx, str, len);
ctx.fg = TH_TEXT_FIELD_SUGGESTION_FG;
screen_draw_chars(&ctx, &str[len], suggestion_len);
}
static void input_mnemonic_title(uint8_t i) {
@ -153,31 +276,43 @@ static void input_mnemonic_title(uint8_t i) {
int base_len = strlen(base_title);
int buf_len = base_len + 4;
uint8_t title_buf[buf_len];
char* title = (char *) u32toa(i, title_buf, buf_len);
char* title = (char *) u32toa(i + 1, title_buf, buf_len);
title -= base_len;
memcpy(title, base_title, base_len);
dialog_title(title);
}
static void input_mnemonic_render(char* word, int len, uint16_t idx) {
static void input_mnemonic_render(const char* word, int len, uint16_t idx) {
int suggestion_len;
if (idx != UINT16_MAX) {
word = BIP39_WORDLIST_ENGLISH[idx];
suggestion_len = strlen(word) - len;
} else {
suggestion_len = 0;
}
input_render_text_field(word, TH_TITLE_HEIGHT + TH_TEXT_FIELD_MARGIN, len, suggestion_len);
}
static uint16_t input_mnemonic_lookup(char* word, int len, uint16_t idx) {
if (len == 0) {
return UINT16_MAX;
}
while (idx < BIP39_WORD_COUNT) {
int cmp = strncmp(word, BIP39_WORDLIST_ENGLISH[idx], len);
if (!cmp) {
break;
if (cmp == 0) {
return idx;
} else if (cmp < 0) {
idx++;
return UINT16_MAX;
} else {
idx = UINT16_MAX;
break;
idx++;
}
}
return idx;
return UINT16_MAX;
}
static uint16_t input_mnemonic_get_word(int i) {
@ -201,6 +336,8 @@ static uint16_t input_mnemonic_get_word(int i) {
len--;
idx = input_mnemonic_lookup(word, len, 0);
}
} else if (c == KEY_ESCAPE) {
//TODO: implement backtracking
} else if (len < WORD_MAX_LEN) {
word[len++] = c;
idx = input_mnemonic_lookup(word, len, (idx == UINT16_MAX ? 0 : idx));
@ -209,6 +346,8 @@ static uint16_t input_mnemonic_get_word(int i) {
}
app_err_t input_mnemonic() {
dialog_footer(TH_TITLE_HEIGHT);
for (int i = 0; i < g_ui_cmd.params.input_mnemo.len; i++) {
g_ui_cmd.params.input_mnemo.indexes[i] = input_mnemonic_get_word(i);
}

View File

@ -64,4 +64,14 @@
#define TH_PIN_FIELD_VERTICAL_MARGIN 16
#define TH_PIN_FIELD_DIGIT_MARGIN TH_DEF_LEFT_MARGIN
#define TH_KEYBOARD_KEY_SIZE 32
#define TH_KEYBOARD_KEY_BG SCREEN_RGB(37, 41, 56)
#define TH_KEYBOARD_KEY_SELECTED_BG SCREEN_RGB(8, 183, 156)
#define TH_TEXT_FIELD_BG SCREEN_RGB(37, 41, 56)
#define TH_TEXT_FIELD_FG TH_COLOR_FG
#define TH_TEXT_FIELD_SUGGESTION_FG SCREEN_RGB(127, 127, 127)
#define TH_TEXT_FIELD_MARGIN 8
#define TH_TEXT_FIELD_HEIGHT 30
#endif

View File

@ -172,7 +172,7 @@ core_evt_t ui_read_mnemonic(uint16_t* indexes, uint32_t* len) {
return CORE_EVT_UI_CANCELLED;
}
g_ui_cmd.type = UI_CMD_INPUT_PIN;
g_ui_cmd.type = UI_CMD_INPUT_MNEMO;
g_ui_cmd.params.input_mnemo.indexes = indexes;
g_ui_cmd.params.input_mnemo.len = *len;