Files
tactility_apps/Apps/Diceware/main/Source/Diceware.cpp
T
Ken Van Hoeylandt b6c27b64d4 Tactility.py 3.0.0 and SDK updates for 0.7.0-dev (#19)
- Remove `Libraries/Str`
- Replaced usage of `TactilityC` FreeRtos features with `TactilityFreeRtos` library
- Update `tactility.py`
- Removed redundant code from `TactilityCpp`
2026-01-04 02:20:06 +01:00

187 lines
6.5 KiB
C++

#include "Diceware.h"
#include <tt_app_alertdialog.h>
#include <tt_lock.h>
#include <tt_lvgl.h>
#include <tt_lvgl_toolbar.h>
#include <esp_random.h>
#include <esp_log.h>
#include <Tactility/kernel/Kernel.h>
constexpr char* TAG = "Diceware";
static void skipNewlines(FILE* file, const int count) {
char c;
int count_in_file = 0;
while (count_in_file < count && fread(&c, 1, 1, file)) {
if (c == '\n') {
count_in_file++;
}
}
}
static std::string readWord(FILE* file) {
char c;
std::string result;
// Read word until newline
while (fread(&c, 1, 1, file) && c != '\n') { result += c; }
return result;
}
static std::string readWordAtLine(const AppHandle handle, const int lineIndex) {
char path[256];
size_t size = 256;
tt_app_get_assets_child_path(handle, "eff_large_wordlist.txt", path, &size);
if (size == 0) {
ESP_LOGE(TAG, "Failed to get assets path");
return "";
}
auto lock = tt_lock_alloc_for_path(path);
std::string word;
if (tt_lock_acquire(lock, tt::kernel::MAX_TICKS)) {
FILE* file = fopen(path, "r");
if (file != nullptr) {
skipNewlines(file, lineIndex);
word = readWord(file);
fclose(file);
} else { ESP_LOGE(TAG, "Failed to open %s", path); }
tt_lock_release(lock);
} else { ESP_LOGE(TAG, "Failed to acquire lock for %s", path); }
tt_lock_free(lock);
return word;
}
int32_t Diceware::jobMain() {
std::string result;
for (int i = 0; i < wordCount; i++) {
constexpr int line_count = 7776;
const auto line_index = esp_random() % line_count;
auto word = readWordAtLine(handle, line_index);
result += word;
result += " ";
}
onFinishJob(result);
return 0;
}
void Diceware::cleanupJob() {
if (jobThread != nullptr) {
jobThread->join();
jobThread = nullptr;
}
}
void Diceware::startJob(uint32_t jobWordCount) {
cleanupJob();
wordCount = jobWordCount;
jobThread = std::make_unique<tt::Thread>("Diceware", 4096, [this] {
return jobMain();
});
jobThread->start();
}
void Diceware::onFinishJob(std::string result) {
tt_lvgl_lock(tt::kernel::MAX_TICKS);
lv_label_set_text(resultLabel, result.c_str());
tt_lvgl_unlock();
}
void Diceware::onClickGenerate(lv_event_t* e) {
auto* application = static_cast<Diceware*>(lv_event_get_user_data(e));
auto* spinbox = application->spinbox;
lv_label_set_text(application->resultLabel, "Generating...");
const auto word_count = lv_spinbox_get_value(spinbox);
application->startJob(word_count);
}
void Diceware::onSpinboxDecrement(lv_event_t* e) {
auto* spinbox = static_cast<lv_obj_t*>(lv_event_get_user_data(e));
lv_spinbox_decrement(spinbox);
}
void Diceware::onSpinboxIncrement(lv_event_t* e) {
auto* spinbox = static_cast<lv_obj_t*>(lv_event_get_user_data(e));
lv_spinbox_increment(spinbox);
}
void Diceware::onHelpClicked(lv_event_t* e) {
const char* buttons[] = { "OK" };
tt_app_alertdialog_start("Diceware Info", "The hardware random number generator can use the Wi-Fi radio to improve randomness. There's no need to connect to a Wi-Fi network for this to work.", buttons, 1);
}
void Diceware::onShow(AppHandle appHandle, lv_obj_t* parent) {
handle = appHandle;
lv_obj_set_flex_flow(parent, LV_FLEX_FLOW_COLUMN);
lv_obj_set_style_pad_row(parent, 0, LV_STATE_DEFAULT);
auto* toolbar = tt_lvgl_toolbar_create_for_app(parent, appHandle);
tt_lvgl_toolbar_add_text_button_action(toolbar, "?", onHelpClicked, nullptr);
auto* wrapper = lv_obj_create(parent);
lv_obj_set_style_border_width(wrapper, 0, LV_STATE_DEFAULT);
lv_obj_set_flex_flow(wrapper, LV_FLEX_FLOW_COLUMN);
lv_obj_set_flex_align(wrapper, LV_FLEX_ALIGN_START, LV_FLEX_ALIGN_CENTER, LV_FLEX_ALIGN_START);
lv_obj_set_width(wrapper, LV_PCT(100));
lv_obj_set_flex_grow(wrapper, 1);
auto* top_row_wrapper = lv_obj_create(wrapper);
lv_obj_set_style_border_width(top_row_wrapper, 0, LV_STATE_DEFAULT);
lv_obj_set_size(top_row_wrapper, LV_PCT(100), LV_SIZE_CONTENT);
lv_obj_set_style_pad_all(top_row_wrapper, 0, LV_STATE_DEFAULT);
auto* generate_button = lv_button_create(top_row_wrapper);
lv_obj_align(generate_button, LV_ALIGN_LEFT_MID, 0, 0);
auto* generate_label = lv_label_create(generate_button);
lv_label_set_text(generate_label, "Generate");
lv_obj_add_event_cb(generate_button, onClickGenerate, LV_EVENT_SHORT_CLICKED, this);
spinbox = lv_spinbox_create(top_row_wrapper);
lv_spinbox_set_range(spinbox, 4, 12);
lv_spinbox_set_value(spinbox, 5);
lv_spinbox_set_step(spinbox, 1);
lv_spinbox_set_digit_format(spinbox, 1, 0);
lv_obj_set_width(spinbox, 30);
auto* spinbox_dec_button = lv_button_create(top_row_wrapper);
lv_obj_set_style_bg_image_src(spinbox_dec_button, LV_SYMBOL_MINUS, LV_STATE_DEFAULT);
lv_obj_add_event_cb(spinbox_dec_button, onSpinboxDecrement, LV_EVENT_SHORT_CLICKED, spinbox);
lv_obj_set_style_pad_all(spinbox_dec_button, 16, LV_STATE_DEFAULT);
auto* spinbox_inc_button = lv_button_create(top_row_wrapper);
// lv_obj_align_to(spinbox_inc_button, spinbox, LV_ALIGN_OUT_RIGHT_MID, 5, 0);
lv_obj_set_style_bg_image_src(spinbox_inc_button, LV_SYMBOL_PLUS, LV_STATE_DEFAULT);
lv_obj_add_event_cb(spinbox_inc_button, onSpinboxIncrement, LV_EVENT_SHORT_CLICKED, spinbox);
lv_obj_set_style_pad_all(spinbox_inc_button, 16, LV_STATE_DEFAULT);
// Align spinbox widgets
lv_obj_align(spinbox_inc_button, LV_ALIGN_RIGHT_MID, 0, 0);
lv_obj_align_to(spinbox, spinbox_inc_button, LV_ALIGN_OUT_LEFT_MID, -5, 0);
lv_obj_align_to(spinbox_dec_button, spinbox, LV_ALIGN_OUT_LEFT_MID, -5, 0);
auto* result_wrapper = lv_obj_create(wrapper);
lv_obj_set_flex_grow(result_wrapper, 1);
lv_obj_set_width(result_wrapper, LV_PCT(100));
lv_obj_set_style_pad_all(result_wrapper, 0, LV_STATE_DEFAULT);
resultLabel = lv_label_create(result_wrapper);
// See https://docs.espressif.com/projects/esp-idf/en/latest/esp32/api-reference/system/random.html
lv_label_set_text(resultLabel, "Press Generate button\nWi-Fi improves randomness.\nSee info button.");
lv_label_set_long_mode(resultLabel, LV_LABEL_LONG_MODE_WRAP);
lv_obj_set_size(resultLabel, LV_PCT(100), LV_SIZE_CONTENT);
lv_obj_set_align(resultLabel, LV_ALIGN_CENTER);
lv_obj_set_style_text_align(resultLabel, LV_TEXT_ALIGN_CENTER, LV_STATE_DEFAULT);
}
void Diceware::onHide(AppHandle context) {
cleanupJob();
}