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)
This commit is contained in:
Ken Van Hoeylandt 2024-12-17 18:11:28 +01:00 committed by GitHub
parent 80b69b7f45
commit b4592dd7d1
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
13 changed files with 394 additions and 35 deletions

View File

@ -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()

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.3 KiB

After

Width:  |  Height:  |  Size: 1.9 KiB

BIN
Data/assets/spinner.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 384 B

View File

@ -0,0 +1,275 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!-- Created with Inkscape (http://www.inkscape.org/) -->
<svg
width="32"
height="32"
viewBox="0 0 8.4666668 8.4666668"
version="1.1"
id="svg1"
inkscape:version="1.4 (e7c3feb100, 2024-10-09)"
sodipodi:docname="TactilitySpinner.svg"
inkscape:export-filename="../assets/spinner.png"
inkscape:export-xdpi="72"
inkscape:export-ydpi="72"
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
xmlns="http://www.w3.org/2000/svg"
xmlns:svg="http://www.w3.org/2000/svg">
<sodipodi:namedview
id="namedview1"
pagecolor="#505050"
bordercolor="#eeeeee"
borderopacity="1"
inkscape:showpageshadow="0"
inkscape:pageopacity="0"
inkscape:pagecheckerboard="0"
inkscape:deskcolor="#d1d1d1"
inkscape:document-units="mm"
inkscape:zoom="3.4114265"
inkscape:cx="36.78813"
inkscape:cy="35.762166"
inkscape:window-width="1503"
inkscape:window-height="930"
inkscape:window-x="0"
inkscape:window-y="0"
inkscape:window-maximized="1"
inkscape:current-layer="g4"
showgrid="false" />
<defs
id="defs1">
<inkscape:path-effect
effect="fillet_chamfer"
id="path-effect4"
is_visible="true"
lpeversion="1"
nodesatellites_param="F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,9.9852864,0,1"
radius="0"
unit="px"
method="auto"
mode="F"
chamfer_steps="1"
flexible="false"
use_knot_distance="true"
apply_no_radius="true"
apply_with_radius="true"
only_selected="false"
hide_knots="false" />
<inkscape:path-effect
effect="fillet_chamfer"
id="path-effect3"
is_visible="true"
lpeversion="1"
nodesatellites_param="F,0,0,1,0,9.96875,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1"
radius="0"
unit="px"
method="auto"
mode="F"
chamfer_steps="1"
flexible="false"
use_knot_distance="true"
apply_no_radius="true"
apply_with_radius="true"
only_selected="false"
hide_knots="false" />
<inkscape:path-effect
effect="fillet_chamfer"
id="path-effect2"
is_visible="true"
lpeversion="1"
nodesatellites_param="F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,10.022406,0,1"
radius="0"
unit="px"
method="auto"
mode="F"
chamfer_steps="1"
flexible="false"
use_knot_distance="true"
apply_no_radius="true"
apply_with_radius="true"
only_selected="false"
hide_knots="false" />
<inkscape:path-effect
effect="fillet_chamfer"
id="path-effect1"
is_visible="true"
lpeversion="1"
nodesatellites_param="F,0,0,1,0,10.022406,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1"
radius="0"
unit="px"
method="auto"
mode="F"
chamfer_steps="1"
flexible="false"
use_knot_distance="true"
apply_no_radius="true"
apply_with_radius="true"
only_selected="false"
hide_knots="false" />
<inkscape:path-effect
effect="fillet_chamfer"
id="path-effect34"
is_visible="true"
lpeversion="1"
nodesatellites_param="F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,9.5761962,0,1"
radius="0"
unit="px"
method="auto"
mode="F"
chamfer_steps="1"
flexible="false"
use_knot_distance="true"
apply_no_radius="true"
apply_with_radius="true"
only_selected="false"
hide_knots="false" />
<inkscape:path-effect
effect="fillet_chamfer"
id="path-effect33"
is_visible="true"
lpeversion="1"
nodesatellites_param="F,0,0,1,0,0,0,1 @ F,0,0,1,0,9.7667784,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1"
radius="0"
unit="px"
method="auto"
mode="F"
chamfer_steps="1"
flexible="false"
use_knot_distance="true"
apply_no_radius="true"
apply_with_radius="true"
only_selected="false"
hide_knots="false" />
<inkscape:path-effect
effect="fillet_chamfer"
id="path-effect32"
is_visible="true"
lpeversion="1"
nodesatellites_param="F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,10.151862,0,1"
radius="0"
unit="px"
method="auto"
mode="F"
chamfer_steps="1"
flexible="false"
use_knot_distance="true"
apply_no_radius="true"
apply_with_radius="true"
only_selected="false"
hide_knots="false" />
<inkscape:path-effect
effect="fillet_chamfer"
id="path-effect31"
is_visible="true"
lpeversion="1"
nodesatellites_param="F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,9.8324877,0,1"
radius="0"
unit="px"
method="auto"
mode="F"
chamfer_steps="1"
flexible="false"
use_knot_distance="true"
apply_no_radius="true"
apply_with_radius="true"
only_selected="false"
hide_knots="false" />
<inkscape:path-effect
effect="fillet_chamfer"
id="path-effect30"
is_visible="true"
lpeversion="1"
nodesatellites_param="F,0,0,1,0,10.269671,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1"
radius="0"
unit="px"
method="auto"
mode="F"
chamfer_steps="1"
flexible="false"
use_knot_distance="true"
apply_no_radius="true"
apply_with_radius="true"
only_selected="false"
hide_knots="false" />
<inkscape:path-effect
effect="fillet_chamfer"
id="path-effect29"
is_visible="true"
lpeversion="1"
nodesatellites_param="F,0,0,1,0,9.7906096,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1"
radius="0"
unit="px"
method="auto"
mode="F"
chamfer_steps="1"
flexible="false"
use_knot_distance="true"
apply_no_radius="true"
apply_with_radius="true"
only_selected="false"
hide_knots="false" />
</defs>
<g
id="g4"
inkscape:label="Large with textt"
inkscape:export-filename="g4.png"
inkscape:export-xdpi="21.879999"
inkscape:export-ydpi="21.879999">
<g
id="g29"
transform="matrix(0.09407407,0,0,0.09407407,-0.94074074,-11.288888)"
inkscape:label="Glyph">
<g
id="g28"
inkscape:label="Upright T"
style="fill:#7376ff;fill-opacity:1" />
<g
id="g28-0"
transform="rotate(180,55,165)"
inkscape:label="Upside-down T"
style="fill:#f876e9;fill-opacity:1" />
<path
style="fill:#00bbff;fill-opacity:1;stroke-width:0.289836;paint-order:stroke fill markers"
id="rect26"
width="60"
height="10"
x="10"
y="150"
sodipodi:type="rect"
inkscape:path-effect="#path-effect1"
d="M 20.022406,150 H 70 v 10 H 10 a 10.011209,10.011209 135.06412 0 1 10.022406,-10 z" />
<path
style="fill:#ee81c3;fill-opacity:1;stroke-width:0.289836;paint-order:stroke fill markers"
id="rect26-4"
width="60"
height="10"
x="10"
y="150"
sodipodi:type="rect"
inkscape:path-effect="#path-effect3"
d="M 19.96875,150 H 70 v 10 H 10 v -0.0312 A 9.96875,9.96875 135 0 1 19.96875,150 Z"
transform="rotate(180,55,165)" />
<path
style="fill:#00bbff;fill-opacity:1;stroke-width:0.264583;paint-order:stroke fill markers"
id="rect28"
width="10"
height="60"
x="40"
y="150"
sodipodi:type="rect"
inkscape:path-effect="#path-effect4"
d="m 40,150 h 10 v 60 h -0.01471 A 9.9852864,9.9852864 45 0 1 40,200.01471 Z" />
<path
style="fill:#ee81c3;fill-opacity:1;stroke-width:0.264583;paint-order:stroke fill markers"
id="rect28-8"
width="10"
height="60"
x="40"
y="150"
sodipodi:type="rect"
inkscape:path-effect="#path-effect2"
d="m 40,150 h 10 v 60 A 10.011209,10.011209 45.064117 0 1 40,199.97759 Z"
transform="rotate(180,55,165)" />
</g>
</g>
</svg>

After

Width:  |  Height:  |  Size: 8.3 KiB

View File

@ -2,16 +2,16 @@
<!-- Created with Inkscape (http://www.inkscape.org/) -->
<svg
width="800"
height="800"
viewBox="0 0 211.66667 211.66667"
width="80"
height="103"
viewBox="0 0 21.166667 27.252084"
version="1.1"
id="svg1"
inkscape:version="1.4 (e7c3feb100, 2024-10-09)"
sodipodi:docname="Tactility.svg"
sodipodi:docname="boot_logo.svg"
inkscape:export-filename="../assets/boot_logo.png"
inkscape:export-xdpi="22.013334"
inkscape:export-ydpi="22.013334"
inkscape:export-xdpi="96"
inkscape:export-ydpi="96"
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
xmlns="http://www.w3.org/2000/svg"
@ -26,15 +26,15 @@
inkscape:pagecheckerboard="0"
inkscape:deskcolor="#d1d1d1"
inkscape:document-units="mm"
inkscape:zoom="1"
inkscape:cx="411"
inkscape:cy="336"
inkscape:zoom="2.7930129"
inkscape:cx="69.996097"
inkscape:cy="34.013449"
inkscape:window-width="1503"
inkscape:window-height="930"
inkscape:window-x="0"
inkscape:window-y="0"
inkscape:window-maximized="1"
inkscape:current-layer="g29"
inkscape:current-layer="svg1"
showgrid="false" />
<defs
id="defs1">
@ -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)">
<g
id="g29"
transform="translate(50,-70)"
@ -237,7 +238,8 @@
y="150"
sodipodi:type="rect"
inkscape:path-effect="#path-effect1"
d="M 20.022406,150 H 70 v 10 H 10 a 10.011209,10.011209 135.06412 0 1 10.022406,-10 z" />
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)" />
<path
style="fill:#ee81c3;fill-opacity:1;stroke-width:0.289836;paint-order:stroke fill markers"
id="rect26-4"
@ -248,7 +250,7 @@
sodipodi:type="rect"
inkscape:path-effect="#path-effect3"
d="M 19.96875,150 H 70 v 10 H 10 v -0.0312 A 9.96875,9.96875 135 0 1 19.96875,150 Z"
transform="rotate(180,55,165)" />
transform="matrix(-0.98657364,0,0,-1,109.86574,330)" />
<path
style="fill:#00bbff;fill-opacity:1;stroke-width:0.264583;paint-order:stroke fill markers"
id="rect28"

Before

Width:  |  Height:  |  Size: 9.0 KiB

After

Width:  |  Height:  |  Size: 9.2 KiB

View File

@ -8,8 +8,10 @@
- loading splash (even in Files app)
- WiFi is on and navigating back to Desktop
Suggested mitigation: When no PSRAM is availabl, use simplified desktop buttons
- WiFi fails quietly when there isn't enough memory. Add statusbar icon for memory pressure. Show error in WiFi screen (e.g. AlertDialog when SPI is not enabled and available memory is below a certain amount)
# TODOs
- When WiFi is on, but there is no connection, it sort of seems like WiFi is off. Find better icon? Gnome uses a grayed-out 100% connectivity one.
- Create different partitions files for different ESP flash size targets (N4, N8, N16, N32)
- Rewrite `sdcard` HAL to class
- Attach ELF data to wrapper app (as app data) (check that app state is "running"!) so you can run more than 1 external apps at a time.

View File

@ -15,7 +15,7 @@ void setBacklightDuty(uint8_t value) {
uint8_t getBacklightDuty() {
int32_t result;
if (preferences.optInt32(BACKLIGHT_DUTY_KEY, result)) {
return (uint8_t)(result % 255);
return (uint8_t)(result % 256);
} else {
return 200;
}

View File

@ -40,34 +40,58 @@ int scandir(
}
*output = static_cast<dirent**>(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<dirent*>(malloc(sizeof(struct dirent)));
memcpy(dirent_array[dirent_buffer_index], current_entry, sizeof(struct dirent));
dirent_array[next_dirent_index] = static_cast<dirent*>(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;
};
}

View File

@ -204,7 +204,7 @@ static void createFileWidget(lv_obj_t* parent, struct dirent* dir_entry) {
static void updateViews(std::shared_ptr<Data> 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]);
}
}

View File

@ -6,6 +6,7 @@
#include "service/wifi/Wifi.h"
#include "lvgl/Style.h"
#include "lvgl/Toolbar.h"
#include "lvgl/Spinner.h"
#include <string>
#include <set>
@ -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);

View File

@ -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);
}

View File

@ -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);

View File

@ -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