From b4592dd7d1d6d6d120808299c8fd80533cdb2f61 Mon Sep 17 00:00:00 2001 From: Ken Van Hoeylandt Date: Tue, 17 Dec 2024 18:11:28 +0100 Subject: [PATCH] Various improvements and fixes (#131) - Added a custom spinner using the Tactility logo - Fix for crash in version logging - Make file browsing less verbose in log - Fix memory leak in FileUtils - Fix bug when display brightness was set to 255: after reboot it would be set to 0 - Smaller boot logo (removed empty space) --- CMakeLists.txt | 2 +- Data/assets/boot_logo.png | Bin 2367 -> 1896 bytes Data/assets/spinner.png | Bin 0 -> 384 bytes Data/assets_sources/TactilitySpinner.svg | 275 ++++++++++++++++++ .../{Tactility.svg => boot_logo.svg} | 28 +- Documentation/ideas.md | 2 + .../Source/app/display/DisplaySettings.cpp | 2 +- Tactility/Source/app/files/FileUtils.cpp | 42 ++- Tactility/Source/app/files/Files.cpp | 2 +- Tactility/Source/app/wifimanage/View.cpp | 6 +- Tactility/Source/lvgl/Spinner.cpp | 53 ++++ Tactility/Source/lvgl/Spinner.h | 8 + Tactility/Source/lvgl/Toolbar.cpp | 9 +- 13 files changed, 394 insertions(+), 35 deletions(-) create mode 100644 Data/assets/spinner.png create mode 100644 Data/assets_sources/TactilitySpinner.svg rename Data/assets_sources/{Tactility.svg => boot_logo.svg} (94%) create mode 100644 Tactility/Source/lvgl/Spinner.cpp create mode 100644 Tactility/Source/lvgl/Spinner.h diff --git a/CMakeLists.txt b/CMakeLists.txt index 08dd22c9..f609c8fd 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -61,7 +61,7 @@ if (DEFINED ENV{ESP_IDF_VERSION}) idf_build_set_property(LINK_OPTIONS "-Wl,--wrap=esp_panic_handler" APPEND) file(READ version.txt TACTILITY_VERSION) - add_compile_definitions(TT_VERSION="$TACTILITY_VERSION") + add_compile_definitions(TT_VERSION="${TACTILITY_VERSION}") else() message("Building for sim target") endif() diff --git a/Data/assets/boot_logo.png b/Data/assets/boot_logo.png index 0a3c5fc8b0d255f5abe14f758f8c98a3f4fdac29..2ef1005740eb493d4424e79eaaee53e69b63ec24 100644 GIT binary patch literal 1896 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 diff --git a/Data/assets/spinner.png b/Data/assets/spinner.png new file mode 100644 index 0000000000000000000000000000000000000000..714847dffd035573e9e7bf84f7012cc5e145422b GIT binary patch literal 384 zcmV-`0e}99P)6{;K%9ayJuWHS&_=#Mj*t>ifKb`w zQ4o`g-L)eZ2wu6Goo{Dm=b0qx>iK3ijUx3s>vFPpefa2#jcaEIB2!P|?89OK7tYuw zfJ%a6?_+iW0R6TBUi|~SiY;TGF1NrD&vC~FpXUHVBd`R( z9PM4hD%k7>MVV~dV&CVkRz;9Da?=2<46=^OtKb%4q}7n?==WR2-vGR~3lP~Z0RJ9z enNR@MtddV3Sx%)~Y1!le0000 + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/Data/assets_sources/Tactility.svg b/Data/assets_sources/boot_logo.svg similarity index 94% rename from Data/assets_sources/Tactility.svg rename to Data/assets_sources/boot_logo.svg index 699ed9cf..1b02be37 100644 --- a/Data/assets_sources/Tactility.svg +++ b/Data/assets_sources/boot_logo.svg @@ -2,16 +2,16 @@ @@ -214,7 +214,8 @@ inkscape:label="Large with textt" inkscape:export-filename="g4.png" inkscape:export-xdpi="21.879999" - inkscape:export-ydpi="21.879999"> + inkscape:export-ydpi="21.879999" + transform="matrix(0.23518519,0,0,0.23518519,-14.084653,-11.732801)"> + d="M 20.022406,150 H 70 v 10 H 10 a 10.011209,10.011209 135.06412 0 1 10.022406,-10 z" + transform="matrix(0.98657364,0,0,1,0.13426361,0)" /> + transform="matrix(-0.98657364,0,0,-1,109.86574,330)" /> (malloc(sizeof(void*) * SCANDIR_LIMIT)); + if (*output == nullptr) { + TT_LOG_E(TAG, "Out of memory"); + closedir(dir); + return -1; + } + struct dirent** dirent_array = *output; - int dirent_buffer_index = 0; + int next_dirent_index = 0; struct dirent* current_entry; + bool out_of_memory = false; while ((current_entry = readdir(dir)) != nullptr) { if (filter(current_entry) == 0) { - dirent_array[dirent_buffer_index] = static_cast(malloc(sizeof(struct dirent))); - memcpy(dirent_array[dirent_buffer_index], current_entry, sizeof(struct dirent)); + dirent_array[next_dirent_index] = static_cast(malloc(sizeof(struct dirent))); + if (dirent_array[next_dirent_index] != nullptr) { + memcpy(dirent_array[next_dirent_index], current_entry, sizeof(struct dirent)); - dirent_buffer_index++; - if (dirent_buffer_index >= SCANDIR_LIMIT) { - TT_LOG_E(TAG, "Directory has more than %d files", SCANDIR_LIMIT); + next_dirent_index++; + if (next_dirent_index >= SCANDIR_LIMIT) { + TT_LOG_E(TAG, "Directory has more than %d files", SCANDIR_LIMIT); + break; + } + } else { + TT_LOG_E(TAG, "Alloc failed. Aborting and cleaning up."); + out_of_memory = true; break; } } } - if (dirent_buffer_index == 0) { + // Out-of-memory clean-up + if (out_of_memory && next_dirent_index > 0) { + for (int i = 0; i < next_dirent_index; ++i) { + TT_LOG_I(TAG, "Cleanup item %d", i); + free(dirent_array[i]); + } + TT_LOG_I(TAG, "Free"); + free(*output); + closedir(dir); + return -1; + // Empty directory + } else if (next_dirent_index == 0) { free(*output); *output = nullptr; } else { if (sort) { - qsort(dirent_array, dirent_buffer_index, sizeof(struct dirent*), (__compar_fn_t)sort); + qsort(dirent_array, next_dirent_index, sizeof(struct dirent*), (__compar_fn_t)sort); } } closedir(dir); - return dirent_buffer_index; + return next_dirent_index; }; } diff --git a/Tactility/Source/app/files/Files.cpp b/Tactility/Source/app/files/Files.cpp index b9a93272..2a16fb06 100644 --- a/Tactility/Source/app/files/Files.cpp +++ b/Tactility/Source/app/files/Files.cpp @@ -204,7 +204,7 @@ static void createFileWidget(lv_obj_t* parent, struct dirent* dir_entry) { static void updateViews(std::shared_ptr data) { lv_obj_clean(data->list); for (int i = 0; i < data->dir_entries_count; ++i) { - TT_LOG_I(TAG, "Entry: %s %d", data->dir_entries[i]->d_name, data->dir_entries[i]->d_type); + TT_LOG_D(TAG, "Entry: %s %d", data->dir_entries[i]->d_name, data->dir_entries[i]->d_type); createFileWidget(data->list, data->dir_entries[i]); } } diff --git a/Tactility/Source/app/wifimanage/View.cpp b/Tactility/Source/app/wifimanage/View.cpp index ad8eb983..1fd1f0e5 100644 --- a/Tactility/Source/app/wifimanage/View.cpp +++ b/Tactility/Source/app/wifimanage/View.cpp @@ -6,6 +6,7 @@ #include "service/wifi/Wifi.h" #include "lvgl/Style.h" #include "lvgl/Toolbar.h" +#include "lvgl/Spinner.h" #include #include @@ -96,10 +97,7 @@ void View::createSsidListItem(const service::wifi::WifiApRecord& record, bool is lv_obj_align(info_label, LV_ALIGN_CENTER, 0, 0); if (isConnecting) { - lv_obj_t* connecting_spinner = lv_spinner_create(wrapper); - lv_obj_set_size(connecting_spinner, 40, 40); - lv_spinner_set_anim_params(connecting_spinner, 1000, 60); - lv_obj_set_style_pad_all(connecting_spinner, 4, 0); + lv_obj_t* connecting_spinner = tt_spinner_create(wrapper); lv_obj_align_to(connecting_spinner, info_wrapper, LV_ALIGN_OUT_LEFT_MID, -8, 0); } else { const char* icon = service::statusbar::getWifiStatusIconForRssi(record.rssi, record.auth_mode != WIFI_AUTH_OPEN); diff --git a/Tactility/Source/lvgl/Spinner.cpp b/Tactility/Source/lvgl/Spinner.cpp new file mode 100644 index 00000000..23722094 --- /dev/null +++ b/Tactility/Source/lvgl/Spinner.cpp @@ -0,0 +1,53 @@ +#define LV_USE_PRIVATE_API 1 // For actual lv_obj_t declaration + +#include "lvgl.h" +#include "CoreDefines.h" +#include "Log.h" + +static void tt_spinner_constructor(const lv_obj_class_t* object_class, lv_obj_t* object); + +const lv_obj_class_t tt_spinner_class = { + .base_class = &lv_image_class, + .constructor_cb = tt_spinner_constructor, + .destructor_cb = nullptr, + .event_cb = nullptr, + .user_data = nullptr, + .name = "tt_spinner", + .width_def = 0, + .height_def = 0, + .editable = 0, + .group_def = 0, + .instance_size = 0, + .theme_inheritable = 0 +}; + +lv_obj_t* tt_spinner_create(lv_obj_t* parent) { + lv_obj_t* obj = lv_obj_class_create_obj(&tt_spinner_class, parent); + lv_obj_class_init_obj(obj); + + lv_image_set_src(obj, "A:/assets/spinner.png"); + + return obj; +} + +static void anim_rotation_callback(void* var, int32_t v) { + auto* object = (lv_obj_t*) var; + auto width = lv_obj_get_width(object); + auto height = lv_obj_get_width(object); + lv_obj_set_style_transform_pivot_x(object, width / 2, 0); + lv_obj_set_style_transform_pivot_y(object, height / 2, 0); + lv_obj_set_style_transform_rotation(object, v, 0); +} + +static void tt_spinner_constructor(TT_UNUSED const lv_obj_class_t* object_class, lv_obj_t* object) { + lv_obj_remove_flag(object, LV_OBJ_FLAG_CLICKABLE); + + lv_anim_t a; + lv_anim_init(&a); + lv_anim_set_var(&a, object); + lv_anim_set_values(&a, 0, 3600); + lv_anim_set_duration(&a, 800); + lv_anim_set_repeat_count(&a, LV_ANIM_REPEAT_INFINITE); + lv_anim_set_exec_cb(&a, anim_rotation_callback); + lv_anim_start(&a); +} diff --git a/Tactility/Source/lvgl/Spinner.h b/Tactility/Source/lvgl/Spinner.h new file mode 100644 index 00000000..718005ca --- /dev/null +++ b/Tactility/Source/lvgl/Spinner.h @@ -0,0 +1,8 @@ +#include "lvgl.h" + +/** + * Create the Tactility spinner widget + * @param parent pointer to an object, it will be the parent of the new spinner. + * @return the created spinner + */ +lv_obj_t* tt_spinner_create(lv_obj_t* parent); diff --git a/Tactility/Source/lvgl/Toolbar.cpp b/Tactility/Source/lvgl/Toolbar.cpp index 32eff6b9..77d9c293 100644 --- a/Tactility/Source/lvgl/Toolbar.cpp +++ b/Tactility/Source/lvgl/Toolbar.cpp @@ -1,10 +1,10 @@ #define LV_USE_PRIVATE_API 1 // For actual lv_obj_t declaration #include "Toolbar.h" -#include "Tactility.h" #include "service/loader/Loader.h" #include "lvgl/Spacer.h" #include "lvgl/Style.h" +#include "Spinner.h" #define SPINNER_HEIGHT TOOLBAR_HEIGHT @@ -90,6 +90,7 @@ lv_obj_t* toolbar_create(lv_obj_t* parent, const std::string& title) { lv_obj_set_flex_flow(toolbar->action_container, LV_FLEX_FLOW_ROW); lv_obj_set_style_pad_all(toolbar->action_container, 0, 0); lv_obj_set_style_border_width(toolbar->action_container, 0, 0); + lv_obj_set_flex_align(toolbar->action_container, LV_FLEX_ALIGN_START, LV_FLEX_ALIGN_CENTER, LV_FLEX_ALIGN_START); toolbar_set_nav_action(obj, LV_SYMBOL_CLOSE, &stop_app, nullptr); @@ -137,11 +138,7 @@ lv_obj_t* toolbar_add_switch_action(lv_obj_t* obj) { lv_obj_t* toolbar_add_spinner_action(lv_obj_t* obj) { auto* toolbar = (Toolbar*)obj; - lv_obj_t* widget = lv_spinner_create(toolbar->action_container); - lv_obj_set_size(widget, SPINNER_HEIGHT, SPINNER_HEIGHT); - lv_spinner_set_anim_params(widget, 1000, 60); - lv_obj_set_style_pad_all(widget, 4, 0); - return widget; + return tt_spinner_create(toolbar->action_container); } } // namespace