App improvements (#103)

This commit is contained in:
Ken Van Hoeylandt 2024-12-03 23:13:59 +01:00 committed by GitHub
parent c7314546fe
commit f31c7f00ae
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
23 changed files with 246 additions and 229 deletions

View File

@ -1,7 +1,7 @@
#include "lvgl.h" #include "lvgl.h"
#include "lvgl/Toolbar.h" #include "lvgl/Toolbar.h"
static void app_show(tt::app::App& app, lv_obj_t* parent) { static void onShow(tt::app::App& app, lv_obj_t* parent) {
lv_obj_t* toolbar = tt::lvgl::toolbar_create(parent, app); lv_obj_t* toolbar = tt::lvgl::toolbar_create(parent, app);
lv_obj_align(toolbar, LV_ALIGN_TOP_MID, 0, 0); lv_obj_align(toolbar, LV_ALIGN_TOP_MID, 0, 0);
@ -13,5 +13,5 @@ static void app_show(tt::app::App& app, lv_obj_t* parent) {
extern const tt::app::Manifest hello_world_app = { extern const tt::app::Manifest hello_world_app = {
.id = "HelloWorld", .id = "HelloWorld",
.name = "Hello World", .name = "Hello World",
.onShow = &app_show, .onShow = onShow,
}; };

View File

@ -6,7 +6,7 @@ The app goes through these states:
Let's look at a scenario where an app launches another app: Let's look at a scenario where an app launches another app:
1. `first` app starts: `first.on_create()` -> `first.on_show()` 1. `first` app starts: `first.onStart()` -> `first.onShow()`
2. `second` app starts: `first.on_hide()` -> `second.on_create()` -> `second.on_show()` 2. `second` app starts: `first.onHide()` -> `second.onStart()` -> `second.onShow()`
3. `second` app stops: `second.on_hide()` -> `second.on_destroy()` -> `first.on_show()` 3. `second` app stops: `second.onHide()` -> `second.onStop()` -> `first.onShow()`
4. `first` app stops: `first.on_hide()` -> `first.on_destroy()` 4. `first` app stops: `first.onHide()` -> `first.onStop()`

View File

@ -1,4 +1,5 @@
# TODOs # TODOs
- Bug: sdcard file reading fails (due to `A:/` prefix?)
- Publish firmwares with upload tool - Publish firmwares with upload tool
- Bug: When closing a top level app, there's often an error "can't stop root app" - Bug: When closing a top level app, there's often an error "can't stop root app"
- Bug: I2C Scanner is on M5Stack devices is broken - Bug: I2C Scanner is on M5Stack devices is broken

View File

@ -42,7 +42,7 @@ UI is created with [lvgl](https://github.com/lvgl/lvgl) which has lots of [widge
Creating a touch-capable UI is [easy](https://docs.lvgl.io/9.0/get-started/quick-overview.html) and doesn't require your own render loop! Creating a touch-capable UI is [easy](https://docs.lvgl.io/9.0/get-started/quick-overview.html) and doesn't require your own render loop!
```C++ ```C++
static void app_show(tt::app::App app, lv_obj_t* parent) { static void onShow(tt::app::App app, lv_obj_t* parent) {
// Default toolbar with app name and close button // Default toolbar with app name and close button
lv_obj_t* toolbar = tt::lvgl::toolbar_create(parent, app); lv_obj_t* toolbar = tt::lvgl::toolbar_create(parent, app);
lv_obj_align(toolbar, LV_ALIGN_TOP_MID, 0, 0); lv_obj_align(toolbar, LV_ALIGN_TOP_MID, 0, 0);
@ -57,7 +57,7 @@ static void app_show(tt::app::App app, lv_obj_t* parent) {
extern const tt::app::Manifest manifest = { extern const tt::app::Manifest manifest = {
.id = "HelloWorld", // Used to identify and start an app .id = "HelloWorld", // Used to identify and start an app
.name = "Hello World", // Shown on the desktop and app's toolbar .name = "Hello World", // Shown on the desktop and app's toolbar
.onShow = &app_show // A minimal setup sets the on_show() function .onShow = onShow // A minimal setup sets the onShow() function
}; };
``` ```

View File

@ -4,7 +4,7 @@
namespace tt::service::gui { namespace tt::service::gui {
/** Process draw call. Calls on_show callback. /** Process draw call. Calls onShow callback.
* To be used by GUI, called on redraw. * To be used by GUI, called on redraw.
* *
* @param view_port ViewPort instance * @param view_port ViewPort instance

View File

@ -7,7 +7,7 @@
#include "hal/Display.h" #include "hal/Display.h"
#include "service/loader/Loader.h" #include "service/loader/Loader.h"
#include "lvgl/Style.h" #include "lvgl/Style.h"
#include "app/display/DisplayPreferences.h" #include "app/display/DisplaySettings.h"
#ifdef ESP_PLATFORM #ifdef ESP_PLATFORM
#include "sdkconfig.h" #include "sdkconfig.h"
@ -33,7 +33,7 @@ static int32_t threadCallback(TT_UNUSED void* context) {
auto* hal_display = (tt::hal::Display*)lv_display_get_user_data(lvgl_display); auto* hal_display = (tt::hal::Display*)lv_display_get_user_data(lvgl_display);
tt_assert(hal_display != nullptr); tt_assert(hal_display != nullptr);
if (hal_display->supportsBacklightDuty()) { if (hal_display->supportsBacklightDuty()) {
int32_t backlight_duty = app::display::preferences_get_backlight_duty(); int32_t backlight_duty = app::display::getBacklightDuty();
hal_display->setBacklightDuty(backlight_duty); hal_display->setBacklightDuty(backlight_duty);
} }

View File

@ -7,7 +7,7 @@
namespace tt::app::desktop { namespace tt::app::desktop {
static void on_app_pressed(lv_event_t* e) { static void onAppPressed(lv_event_t* e) {
lv_event_code_t code = lv_event_get_code(e); lv_event_code_t code = lv_event_get_code(e);
if (code == LV_EVENT_CLICKED) { if (code == LV_EVENT_CLICKED) {
const auto* manifest = static_cast<const Manifest*>(lv_event_get_user_data(e)); const auto* manifest = static_cast<const Manifest*>(lv_event_get_user_data(e));
@ -15,15 +15,15 @@ static void on_app_pressed(lv_event_t* e) {
} }
} }
static void create_app_widget(const Manifest* manifest, void* parent) { static void createAppWidget(const Manifest* manifest, void* parent) {
tt_check(parent); tt_check(parent);
auto* list = static_cast<lv_obj_t*>(parent); auto* list = static_cast<lv_obj_t*>(parent);
const void* icon = !manifest->icon.empty() ? manifest->icon.c_str() : TT_ASSETS_APP_ICON_FALLBACK; const void* icon = !manifest->icon.empty() ? manifest->icon.c_str() : TT_ASSETS_APP_ICON_FALLBACK;
lv_obj_t* btn = lv_list_add_button(list, icon, manifest->name.c_str()); lv_obj_t* btn = lv_list_add_button(list, icon, manifest->name.c_str());
lv_obj_add_event_cb(btn, &on_app_pressed, LV_EVENT_CLICKED, (void*)manifest); lv_obj_add_event_cb(btn, &onAppPressed, LV_EVENT_CLICKED, (void*)manifest);
} }
static void desktop_show(TT_UNUSED App& app, lv_obj_t* parent) { static void onShow(TT_UNUSED App& app, lv_obj_t* parent) {
lv_obj_t* list = lv_list_create(parent); lv_obj_t* list = lv_list_create(parent);
lv_obj_set_size(list, LV_PCT(100), LV_PCT(100)); lv_obj_set_size(list, LV_PCT(100), LV_PCT(100));
lv_obj_center(list); lv_obj_center(list);
@ -34,14 +34,14 @@ static void desktop_show(TT_UNUSED App& app, lv_obj_t* parent) {
lv_list_add_text(list, "User"); lv_list_add_text(list, "User");
for (const auto& manifest: manifests) { for (const auto& manifest: manifests) {
if (manifest->type == TypeUser) { if (manifest->type == TypeUser) {
create_app_widget(manifest, list); createAppWidget(manifest, list);
} }
} }
lv_list_add_text(list, "System"); lv_list_add_text(list, "System");
for (const auto& manifest: manifests) { for (const auto& manifest: manifests) {
if (manifest->type == TypeSystem) { if (manifest->type == TypeSystem) {
create_app_widget(manifest, list); createAppWidget(manifest, list);
} }
} }
} }
@ -50,7 +50,7 @@ extern const Manifest manifest = {
.id = "Desktop", .id = "Desktop",
.name = "Desktop", .name = "Desktop",
.type = TypeDesktop, .type = TypeDesktop,
.onShow = &desktop_show, .onShow = onShow,
}; };
} // namespace } // namespace

View File

@ -1,6 +1,6 @@
#include "app/App.h" #include "app/App.h"
#include "Assets.h" #include "Assets.h"
#include "DisplayPreferences.h" #include "DisplaySettings.h"
#include "Tactility.h" #include "Tactility.h"
#include "lvgl/Toolbar.h" #include "lvgl/Toolbar.h"
#include "lvgl.h" #include "lvgl.h"
@ -13,7 +13,7 @@ namespace tt::app::display {
static bool backlight_duty_set = false; static bool backlight_duty_set = false;
static uint8_t backlight_duty = 255; static uint8_t backlight_duty = 255;
static void slider_event_cb(lv_event_t* event) { static void onSliderEvent(lv_event_t* event) {
auto* slider = static_cast<lv_obj_t*>(lv_event_get_target(event)); auto* slider = static_cast<lv_obj_t*>(lv_event_get_target(event));
auto* lvgl_display = lv_display_get_default(); auto* lvgl_display = lv_display_get_default();
tt_assert(lvgl_display != nullptr); tt_assert(lvgl_display != nullptr);
@ -35,7 +35,7 @@ static void slider_event_cb(lv_event_t* event) {
#define ORIENTATION_PORTRAIT_LEFT 2 #define ORIENTATION_PORTRAIT_LEFT 2
#define ORIENTATION_PORTRAIT_RIGHT 3 #define ORIENTATION_PORTRAIT_RIGHT 3
static lv_display_rotation_t orientation_setting_to_display_rotation(uint32_t setting) { static lv_display_rotation_t orientationSettingToDisplayOrientation(uint32_t setting) {
if (setting == ORIENTATION_LANDSCAPE_FLIPPED) { if (setting == ORIENTATION_LANDSCAPE_FLIPPED) {
return LV_DISPLAY_ROTATION_180; return LV_DISPLAY_ROTATION_180;
} else if (setting == ORIENTATION_PORTRAIT_LEFT) { } else if (setting == ORIENTATION_PORTRAIT_LEFT) {
@ -47,7 +47,7 @@ static lv_display_rotation_t orientation_setting_to_display_rotation(uint32_t se
} }
} }
static uint32_t display_rotation_to_orientation_setting(lv_display_rotation_t orientation) { static uint32_t dipslayOrientationToOrientationSetting(lv_display_rotation_t orientation) {
if (orientation == LV_DISPLAY_ROTATION_90) { if (orientation == LV_DISPLAY_ROTATION_90) {
return ORIENTATION_PORTRAIT_RIGHT; return ORIENTATION_PORTRAIT_RIGHT;
} else if (orientation == LV_DISPLAY_ROTATION_180) { } else if (orientation == LV_DISPLAY_ROTATION_180) {
@ -59,18 +59,18 @@ static uint32_t display_rotation_to_orientation_setting(lv_display_rotation_t or
} }
} }
static void on_orientation_set(lv_event_t* event) { static void onOrientationSet(lv_event_t* event) {
auto* dropdown = static_cast<lv_obj_t*>(lv_event_get_target(event)); auto* dropdown = static_cast<lv_obj_t*>(lv_event_get_target(event));
uint32_t selected = lv_dropdown_get_selected(dropdown); uint32_t selected = lv_dropdown_get_selected(dropdown);
TT_LOG_I(TAG, "Selected %ld", selected); TT_LOG_I(TAG, "Selected %ld", selected);
lv_display_rotation_t rotation = orientation_setting_to_display_rotation(selected); lv_display_rotation_t rotation = orientationSettingToDisplayOrientation(selected);
if (lv_display_get_rotation(lv_display_get_default()) != rotation) { if (lv_display_get_rotation(lv_display_get_default()) != rotation) {
lv_display_set_rotation(lv_display_get_default(), rotation); lv_display_set_rotation(lv_display_get_default(), rotation);
preferences_set_rotation(rotation); setRotation(rotation);
} }
} }
static void app_show(App& app, lv_obj_t* parent) { static void onShow(App& app, lv_obj_t* parent) {
lv_obj_set_flex_flow(parent, LV_FLEX_FLOW_COLUMN); lv_obj_set_flex_flow(parent, LV_FLEX_FLOW_COLUMN);
lvgl::toolbar_create(parent, app); lvgl::toolbar_create(parent, app);
@ -92,7 +92,7 @@ static void app_show(App& app, lv_obj_t* parent) {
lv_obj_set_width(brightness_slider, LV_PCT(50)); lv_obj_set_width(brightness_slider, LV_PCT(50));
lv_obj_align(brightness_slider, LV_ALIGN_TOP_RIGHT, -8, 0); lv_obj_align(brightness_slider, LV_ALIGN_TOP_RIGHT, -8, 0);
lv_slider_set_range(brightness_slider, 0, 255); lv_slider_set_range(brightness_slider, 0, 255);
lv_obj_add_event_cb(brightness_slider, slider_event_cb, LV_EVENT_VALUE_CHANGED, nullptr); lv_obj_add_event_cb(brightness_slider, onSliderEvent, LV_EVENT_VALUE_CHANGED, nullptr);
auto* lvgl_display = lv_display_get_default(); auto* lvgl_display = lv_display_get_default();
tt_assert(lvgl_display != nullptr); tt_assert(lvgl_display != nullptr);
@ -103,7 +103,7 @@ static void app_show(App& app, lv_obj_t* parent) {
lv_slider_set_value(brightness_slider, 255, LV_ANIM_OFF); lv_slider_set_value(brightness_slider, 255, LV_ANIM_OFF);
lv_obj_add_state(brightness_slider, LV_STATE_DISABLED); lv_obj_add_state(brightness_slider, LV_STATE_DISABLED);
} else { } else {
uint8_t value = preferences_get_backlight_duty(); uint8_t value = getBacklightDuty();
lv_slider_set_value(brightness_slider, value, LV_ANIM_OFF); lv_slider_set_value(brightness_slider, value, LV_ANIM_OFF);
} }
@ -114,16 +114,16 @@ static void app_show(App& app, lv_obj_t* parent) {
lv_obj_t* orientation_dropdown = lv_dropdown_create(wrapper); lv_obj_t* orientation_dropdown = lv_dropdown_create(wrapper);
lv_dropdown_set_options(orientation_dropdown, "Landscape\nLandscape (flipped)\nPortrait Left\nPortrait Right"); lv_dropdown_set_options(orientation_dropdown, "Landscape\nLandscape (flipped)\nPortrait Left\nPortrait Right");
lv_obj_align(orientation_dropdown, LV_ALIGN_TOP_RIGHT, 0, 32); lv_obj_align(orientation_dropdown, LV_ALIGN_TOP_RIGHT, 0, 32);
lv_obj_add_event_cb(orientation_dropdown, on_orientation_set, LV_EVENT_VALUE_CHANGED, nullptr); lv_obj_add_event_cb(orientation_dropdown, onOrientationSet, LV_EVENT_VALUE_CHANGED, nullptr);
uint32_t orientation_selected = display_rotation_to_orientation_setting( uint32_t orientation_selected = dipslayOrientationToOrientationSetting(
lv_display_get_rotation(lv_display_get_default()) lv_display_get_rotation(lv_display_get_default())
); );
lv_dropdown_set_selected(orientation_dropdown, orientation_selected); lv_dropdown_set_selected(orientation_dropdown, orientation_selected);
} }
static void app_hide(TT_UNUSED App& app) { static void onHide(TT_UNUSED App& app) {
if (backlight_duty_set) { if (backlight_duty_set) {
preferences_set_backlight_duty(backlight_duty); setBacklightDuty(backlight_duty);
} }
} }
@ -134,8 +134,8 @@ extern const Manifest manifest = {
.type = TypeSettings, .type = TypeSettings,
.onStart = nullptr, .onStart = nullptr,
.onStop = nullptr, .onStop = nullptr,
.onShow = &app_show, .onShow = onShow,
.onHide = &app_hide .onHide = onHide
}; };
} // namespace } // namespace

View File

@ -1,13 +0,0 @@
#pragma once
#include <cstdint>
#include <src/display/lv_display.h>
namespace tt::app::display {
void preferences_set_backlight_duty(uint8_t value);
uint8_t preferences_get_backlight_duty();
void preferences_set_rotation(lv_display_rotation_t rotation);
lv_display_rotation_t preferences_get_rotation();
} // namespace

View File

@ -1,4 +1,4 @@
#include "DisplayPreferences.h" #include "DisplaySettings.h"
#include "Preferences.h" #include "Preferences.h"
namespace tt::app::display { namespace tt::app::display {
@ -8,11 +8,11 @@ tt::Preferences preferences("display");
#define BACKLIGHT_DUTY_KEY "backlight_duty" #define BACKLIGHT_DUTY_KEY "backlight_duty"
#define ROTATION_KEY "rotation" #define ROTATION_KEY "rotation"
void preferences_set_backlight_duty(uint8_t value) { void setBacklightDuty(uint8_t value) {
preferences.putInt32(BACKLIGHT_DUTY_KEY, (int32_t)value); preferences.putInt32(BACKLIGHT_DUTY_KEY, (int32_t)value);
} }
uint8_t preferences_get_backlight_duty() { uint8_t getBacklightDuty() {
int32_t result; int32_t result;
if (preferences.optInt32(BACKLIGHT_DUTY_KEY, result)) { if (preferences.optInt32(BACKLIGHT_DUTY_KEY, result)) {
return (uint8_t)(result % 255); return (uint8_t)(result % 255);
@ -21,11 +21,11 @@ uint8_t preferences_get_backlight_duty() {
} }
} }
void preferences_set_rotation(lv_display_rotation_t rotation) { void setRotation(lv_display_rotation_t rotation) {
preferences.putInt32(ROTATION_KEY, (int32_t)rotation); preferences.putInt32(ROTATION_KEY, (int32_t)rotation);
} }
lv_display_rotation_t preferences_get_rotation() { lv_display_rotation_t getRotation() {
int32_t rotation; int32_t rotation;
if (preferences.optInt32(ROTATION_KEY, rotation)) { if (preferences.optInt32(ROTATION_KEY, rotation)) {
return (lv_display_rotation_t)rotation; return (lv_display_rotation_t)rotation;

View File

@ -0,0 +1,12 @@
#pragma once
#include <src/display/lv_display.h>
namespace tt::app::display {
void setBacklightDuty(uint8_t value);
uint8_t getBacklightDuty();
void setRotation(lv_display_rotation_t rotation);
lv_display_rotation_t getRotation();
} // namespace

View File

@ -7,23 +7,23 @@ namespace tt::app::files {
/** File types for `dirent`'s `d_type`. */ /** File types for `dirent`'s `d_type`. */
enum { enum {
TT_DT_UNKNOWN = 0, TT_DT_UNKNOWN = 0,
#define TT_DT_UNKNOWN TT_DT_UNKNOWN #define TT_DT_UNKNOWN TT_DT_UNKNOWN // Unknown type
TT_DT_FIFO = 1, TT_DT_FIFO = 1,
#define TT_DT_FIFO TT_DT_FIFO #define TT_DT_FIFO TT_DT_FIFO // Named pipe or FIFO
TT_DT_CHR = 2, TT_DT_CHR = 2,
#define TT_DT_CHR TT_DT_CHR #define TT_DT_CHR TT_DT_CHR // Character device
TT_DT_DIR = 4, TT_DT_DIR = 4,
#define TT_DT_DIR TT_DT_DIR #define TT_DT_DIR TT_DT_DIR // Directory
TT_DT_BLK = 6, TT_DT_BLK = 6,
#define TT_DT_BLK TT_DT_BLK #define TT_DT_BLK TT_DT_BLK // Block device
TT_DT_REG = 8, TT_DT_REG = 8,
#define TT_DT_REG TT_DT_REG #define TT_DT_REG TT_DT_REG // Regular file
TT_DT_LNK = 10, TT_DT_LNK = 10,
#define TT_DT_LNK TT_DT_LNK #define TT_DT_LNK TT_DT_LNK // Symbolic link
TT_DT_SOCK = 12, TT_DT_SOCK = 12,
#define TT_DT_SOCK TT_DT_SOCK #define TT_DT_SOCK TT_DT_SOCK // Local-domain socket
TT_DT_WHT = 14 TT_DT_WHT = 14
#define TT_DT_WHT TT_DT_WHT #define TT_DT_WHT TT_DT_WHT // Whiteout inodes
}; };
typedef int (*ScandirFilter)(const struct dirent*); typedef int (*ScandirFilter)(const struct dirent*);

View File

@ -23,7 +23,7 @@ namespace tt::app::files {
* @param extension the extension to look for, including the period symbol, in lower case * @param extension the extension to look for, including the period symbol, in lower case
* @return true on match * @return true on match
*/ */
static bool has_file_extension(const char* path, const char* extension) { static bool hasFileExtension(const char* path, const char* extension) {
size_t postfix_len = strlen(extension); size_t postfix_len = strlen(extension);
size_t base_len = strlen(path); size_t base_len = strlen(path);
if (base_len < postfix_len) { if (base_len < postfix_len) {
@ -39,27 +39,27 @@ static bool has_file_extension(const char* path, const char* extension) {
return true; return true;
} }
static bool is_supported_image_file(const char* filename) { static bool isSupportedImageFile(const char* filename) {
// Currently only the PNG library is built into Tactility // Currently only the PNG library is built into Tactility
return has_file_extension(filename, ".png"); return hasFileExtension(filename, ".png");
} }
static bool is_supported_text_file(const char* filename) { static bool isSupportedTextFile(const char* filename) {
return has_file_extension(filename, ".txt") || return hasFileExtension(filename, ".txt") ||
has_file_extension(filename, ".ini") || hasFileExtension(filename, ".ini") ||
has_file_extension(filename, ".json") || hasFileExtension(filename, ".json") ||
has_file_extension(filename, ".yaml") || hasFileExtension(filename, ".yaml") ||
has_file_extension(filename, ".yml") || hasFileExtension(filename, ".yml") ||
has_file_extension(filename, ".lua") || hasFileExtension(filename, ".lua") ||
has_file_extension(filename, ".js") || hasFileExtension(filename, ".js") ||
has_file_extension(filename, ".properties"); hasFileExtension(filename, ".properties");
} }
// region Views // region Views
static void update_views(Data* data); static void updateViews(Data* data);
static void on_navigate_up_pressed(lv_event_t* event) { static void onNavigateUpPressed(lv_event_t* event) {
auto* files_data = (Data*)lv_event_get_user_data(event); auto* files_data = (Data*)lv_event_get_user_data(event);
if (strcmp(files_data->current_path, "/") != 0) { if (strcmp(files_data->current_path, "/") != 0) {
TT_LOG_I(TAG, "Navigating upwards"); TT_LOG_I(TAG, "Navigating upwards");
@ -68,14 +68,14 @@ static void on_navigate_up_pressed(lv_event_t* event) {
data_set_entries_for_path(files_data, new_absolute_path); data_set_entries_for_path(files_data, new_absolute_path);
} }
} }
update_views(files_data); updateViews(files_data);
} }
static void on_exit_app_pressed(TT_UNUSED lv_event_t* event) { static void onExitAppPressed(TT_UNUSED lv_event_t* event) {
service::loader::stopApp(); service::loader::stopApp();
} }
static void view_file(const char* path, const char* filename) { static void viewFile(const char* path, const char* filename) {
size_t path_len = strlen(path); size_t path_len = strlen(path);
size_t filename_len = strlen(filename); size_t filename_len = strlen(filename);
char* filepath = static_cast<char*>(malloc(path_len + filename_len + 2)); char* filepath = static_cast<char*>(malloc(path_len + filename_len + 2));
@ -102,11 +102,11 @@ static void view_file(const char* path, const char* filename) {
TT_LOG_I(TAG, "Clicked %s", filepath); TT_LOG_I(TAG, "Clicked %s", filepath);
if (is_supported_image_file(filename)) { if (isSupportedImageFile(filename)) {
Bundle bundle; Bundle bundle;
bundle.putString(IMAGE_VIEWER_FILE_ARGUMENT, processed_filepath); bundle.putString(IMAGE_VIEWER_FILE_ARGUMENT, processed_filepath);
service::loader::startApp("ImageViewer", false, bundle); service::loader::startApp("ImageViewer", false, bundle);
} else if (is_supported_text_file(filename)) { } else if (isSupportedTextFile(filename)) {
Bundle bundle; Bundle bundle;
if (get_platform() == PlatformEsp) { if (get_platform() == PlatformEsp) {
bundle.putString(TEXT_VIEWER_FILE_ARGUMENT, processed_filepath); bundle.putString(TEXT_VIEWER_FILE_ARGUMENT, processed_filepath);
@ -122,7 +122,7 @@ static void view_file(const char* path, const char* filename) {
free(filepath); free(filepath);
} }
static void on_file_pressed(lv_event_t* event) { static void onFilePressed(lv_event_t* event) {
lv_event_code_t code = lv_event_get_code(event); lv_event_code_t code = lv_event_get_code(event);
if (code == LV_EVENT_CLICKED) { if (code == LV_EVENT_CLICKED) {
lv_obj_t* button = lv_event_get_current_target_obj(event); lv_obj_t* button = lv_event_get_current_target_obj(event);
@ -134,30 +134,30 @@ static void on_file_pressed(lv_event_t* event) {
switch (dir_entry->d_type) { switch (dir_entry->d_type) {
case TT_DT_DIR: case TT_DT_DIR:
data_set_entries_for_child_path(files_data, dir_entry->d_name); data_set_entries_for_child_path(files_data, dir_entry->d_name);
update_views(files_data); updateViews(files_data);
break; break;
case TT_DT_LNK: case TT_DT_LNK:
TT_LOG_W(TAG, "opening links is not supported"); TT_LOG_W(TAG, "opening links is not supported");
break; break;
case TT_DT_REG: case TT_DT_REG:
view_file(files_data->current_path, dir_entry->d_name); viewFile(files_data->current_path, dir_entry->d_name);
break; break;
default: default:
// Assume it's a file // Assume it's a file
// TODO: Find a better way to identify a file // TODO: Find a better way to identify a file
view_file(files_data->current_path, dir_entry->d_name); viewFile(files_data->current_path, dir_entry->d_name);
break; break;
} }
} }
} }
static void create_file_widget(Data* files_data, lv_obj_t* parent, struct dirent* dir_entry) { static void createFileWidget(Data* files_data, lv_obj_t* parent, struct dirent* dir_entry) {
tt_check(parent); tt_check(parent);
auto* list = (lv_obj_t*)parent; auto* list = (lv_obj_t*)parent;
const char* symbol; const char* symbol;
if (dir_entry->d_type == TT_DT_DIR) { if (dir_entry->d_type == TT_DT_DIR) {
symbol = LV_SYMBOL_DIRECTORY; symbol = LV_SYMBOL_DIRECTORY;
} else if (is_supported_image_file(dir_entry->d_name)) { } else if (isSupportedImageFile(dir_entry->d_name)) {
symbol = LV_SYMBOL_IMAGE; symbol = LV_SYMBOL_IMAGE;
} else if (dir_entry->d_type == TT_DT_LNK) { } else if (dir_entry->d_type == TT_DT_LNK) {
symbol = LV_SYMBOL_LOOP; symbol = LV_SYMBOL_LOOP;
@ -166,13 +166,14 @@ static void create_file_widget(Data* files_data, lv_obj_t* parent, struct dirent
} }
lv_obj_t* button = lv_list_add_button(list, symbol, dir_entry->d_name); lv_obj_t* button = lv_list_add_button(list, symbol, dir_entry->d_name);
lv_obj_set_user_data(button, files_data); lv_obj_set_user_data(button, files_data);
lv_obj_add_event_cb(button, &on_file_pressed, LV_EVENT_CLICKED, (void*)dir_entry); lv_obj_add_event_cb(button, &onFilePressed, LV_EVENT_CLICKED, (void*)dir_entry);
} }
static void update_views(Data* data) { static void updateViews(Data* data) {
lv_obj_clean(data->list); lv_obj_clean(data->list);
for (int i = 0; i < data->dir_entries_count; ++i) { for (int i = 0; i < data->dir_entries_count; ++i) {
create_file_widget(data, data->list, data->dir_entries[i]); TT_LOG_I(TAG, "Entry: %s %d", data->dir_entries[i]->d_name, data->dir_entries[i]->d_type);
createFileWidget(data, data->list, data->dir_entries[i]);
} }
} }
@ -180,23 +181,23 @@ static void update_views(Data* data) {
// region Lifecycle // region Lifecycle
static void on_show(App& app, lv_obj_t* parent) { static void onShow(App& app, lv_obj_t* parent) {
auto* data = static_cast<Data*>(app.getData()); auto* data = static_cast<Data*>(app.getData());
lv_obj_set_flex_flow(parent, LV_FLEX_FLOW_COLUMN); lv_obj_set_flex_flow(parent, LV_FLEX_FLOW_COLUMN);
lv_obj_t* toolbar = lvgl::toolbar_create(parent, "Files"); lv_obj_t* toolbar = lvgl::toolbar_create(parent, "Files");
lvgl::toolbar_set_nav_action(toolbar, LV_SYMBOL_CLOSE, &on_exit_app_pressed, nullptr); lvgl::toolbar_set_nav_action(toolbar, LV_SYMBOL_CLOSE, &onExitAppPressed, nullptr);
lvgl::toolbar_add_action(toolbar, LV_SYMBOL_UP, &on_navigate_up_pressed, data); lvgl::toolbar_add_action(toolbar, LV_SYMBOL_UP, &onNavigateUpPressed, data);
data->list = lv_list_create(parent); data->list = lv_list_create(parent);
lv_obj_set_width(data->list, LV_PCT(100)); lv_obj_set_width(data->list, LV_PCT(100));
lv_obj_set_flex_grow(data->list, 1); lv_obj_set_flex_grow(data->list, 1);
update_views(data); updateViews(data);
} }
static void on_start(App& app) { static void onStart(App& app) {
auto* data = data_alloc(); auto* data = data_alloc();
// PC platform is bound to current work directory because of the LVGL file system mapping // PC platform is bound to current work directory because of the LVGL file system mapping
if (get_platform() == PlatformSimulator) { if (get_platform() == PlatformSimulator) {
@ -214,7 +215,7 @@ static void on_start(App& app) {
app.setData(data); app.setData(data);
} }
static void on_stop(App& app) { static void onStop(App& app) {
auto* data = static_cast<Data*>(app.getData()); auto* data = static_cast<Data*>(app.getData());
data_free(data); data_free(data);
} }
@ -226,9 +227,9 @@ extern const Manifest manifest = {
.name = "Files", .name = "Files",
.icon = TT_ASSETS_APP_ICON_FILES, .icon = TT_ASSETS_APP_ICON_FILES,
.type = TypeSystem, .type = TypeSystem,
.onStart = &on_start, .onStart = onStart,
.onStop = &on_stop, .onStop = onStop,
.onShow = &on_show, .onShow = onShow,
}; };
} // namespace } // namespace

View File

@ -9,41 +9,57 @@
namespace tt::app::gpio { namespace tt::app::gpio {
typedef struct { class Gpio {
lv_obj_t* lv_pins[GPIO_NUM_MAX];
uint8_t pin_states[GPIO_NUM_MAX];
Thread* thread;
Mutex* mutex;
bool thread_interrupted;
} Gpio;
static void lock(Gpio* gpio) { private:
tt_check(tt_mutex_acquire(gpio->mutex, 1000) == TtStatusOk);
lv_obj_t* lvPins[GPIO_NUM_MAX] = {0 };
uint8_t pinStates[GPIO_NUM_MAX] = {0 };
Thread* thread = nullptr;
Mutex mutex;
bool interruptTask = true;
public:
void lock() {
tt_check(mutex.acquire(1000) == TtStatusOk);
} }
static void unlock(Gpio* gpio) { void unlock() {
tt_check(tt_mutex_release(gpio->mutex) == TtStatusOk); tt_check(mutex.release() == TtStatusOk);
} }
static void update_pin_states(Gpio* gpio) { void onShow(App& app, lv_obj_t* parent);
lock(gpio); void onHide(App& app);
void startTask();
void stopTask();
bool shouldInterruptTask() { return interruptTask; };
void updatePinStates();
void updatePinWidgets();
};
void Gpio::updatePinStates() {
lock();
// Update pin states // Update pin states
for (int i = 0; i < GPIO_NUM_MAX; ++i) { for (int i = 0; i < GPIO_NUM_MAX; ++i) {
#ifdef ESP_PLATFORM #ifdef ESP_PLATFORM
gpio->pin_states[i] = gpio_get_level((gpio_num_t)i); pinStates[i] = gpio_get_level((gpio_num_t)i);
#else #else
gpio->pin_states[i] = gpio_get_level(i); pinStates[i] = gpio_get_level(i);
#endif #endif
} }
unlock(gpio); unlock();
} }
static void update_pin_widgets(Gpio* gpio) { void Gpio::updatePinWidgets() {
if (lvgl::lock(100)) { if (lvgl::lock(100)) {
lock(gpio); lock();
for (int j = 0; j < GPIO_NUM_MAX; ++j) { for (int j = 0; j < GPIO_NUM_MAX; ++j) {
int level = gpio->pin_states[j]; int level = pinStates[j];
lv_obj_t* label = gpio->lv_pins[j]; lv_obj_t* label = lvPins[j];
void* label_user_data = lv_obj_get_user_data(label); void* label_user_data = lv_obj_get_user_data(label);
// The user data stores the state, so we can avoid unnecessary updates // The user data stores the state, so we can avoid unnecessary updates
if ((void*)level != label_user_data) { if ((void*)level != label_user_data) {
@ -56,11 +72,11 @@ static void update_pin_widgets(Gpio* gpio) {
} }
} }
lvgl::unlock(); lvgl::unlock();
unlock(gpio); unlock();
} }
} }
static lv_obj_t* create_gpio_row_wrapper(lv_obj_t* parent) { static lv_obj_t* createGpioRowWrapper(lv_obj_t* parent) {
lv_obj_t* wrapper = lv_obj_create(parent); lv_obj_t* wrapper = lv_obj_create(parent);
lv_obj_set_style_pad_all(wrapper, 0, 0); lv_obj_set_style_pad_all(wrapper, 0, 0);
lv_obj_set_style_border_width(wrapper, 0, 0); lv_obj_set_style_border_width(wrapper, 0, 0);
@ -70,57 +86,56 @@ static lv_obj_t* create_gpio_row_wrapper(lv_obj_t* parent) {
// region Task // region Task
static int32_t gpio_task(void* context) { static int32_t taskMain(void* context) {
Gpio* gpio = (Gpio*)context; Gpio* gpio = (Gpio*)context;
bool interrupted = false; bool interrupted = false;
while (!interrupted) { while (!interrupted) {
delay_ms(100); delay_ms(100);
update_pin_states(gpio); gpio->updatePinStates();
update_pin_widgets(gpio); gpio->updatePinWidgets();
lock(gpio); gpio->lock();
interrupted = gpio->thread_interrupted; interrupted = gpio->shouldInterruptTask();
unlock(gpio); gpio->unlock();
} }
return 0; return 0;
} }
static void task_start(Gpio* gpio) { void Gpio::startTask() {
tt_assert(gpio->thread == nullptr); lock();
lock(gpio); tt_assert(thread == nullptr);
gpio->thread = new Thread( thread = new Thread(
"gpio", "gpio",
4096, 4096,
&gpio_task, &taskMain,
gpio this
); );
gpio->thread_interrupted = false; interruptTask = false;
gpio->thread->start(); thread->start();
unlock(gpio); unlock();
} }
static void task_stop(Gpio* gpio) { void Gpio::stopTask() {
tt_assert(gpio->thread); tt_assert(thread);
lock(gpio); lock();
gpio->thread_interrupted = true; interruptTask = true;
unlock(gpio); unlock();
gpio->thread->join(); thread->join();
lock(gpio); lock();
delete gpio->thread; delete thread;
gpio->thread = nullptr; thread = nullptr;
unlock(gpio); unlock();
} }
// endregion Task // endregion Task
// region App lifecycle
static void app_show(App& app, lv_obj_t* parent) { void Gpio::onShow(App& app, lv_obj_t* parent) {
auto* gpio = (Gpio*)app.getData(); auto* gpio = (Gpio*)app.getData();
lv_obj_set_flex_flow(parent, LV_FLEX_FLOW_COLUMN); lv_obj_set_flex_flow(parent, LV_FLEX_FLOW_COLUMN);
@ -137,10 +152,10 @@ static void app_show(App& app, lv_obj_t* parent) {
uint8_t column_limit = 10; uint8_t column_limit = 10;
int32_t x_spacing = 20; int32_t x_spacing = 20;
lv_obj_t* row_wrapper = create_gpio_row_wrapper(wrapper); lv_obj_t* row_wrapper = createGpioRowWrapper(wrapper);
lv_obj_align(row_wrapper, LV_ALIGN_TOP_MID, 0, 0); lv_obj_align(row_wrapper, LV_ALIGN_TOP_MID, 0, 0);
lock(gpio); gpio->lock();
for (int i = GPIO_NUM_MIN; i < GPIO_NUM_MAX; ++i) { for (int i = GPIO_NUM_MIN; i < GPIO_NUM_MAX; ++i) {
// Add the GPIO number before the first item on a row // Add the GPIO number before the first item on a row
@ -153,7 +168,7 @@ static void app_show(App& app, lv_obj_t* parent) {
lv_obj_t* status_label = lv_label_create(row_wrapper); lv_obj_t* status_label = lv_label_create(row_wrapper);
lv_obj_set_pos(status_label, (int32_t)((column+1) * x_spacing), 0); lv_obj_set_pos(status_label, (int32_t)((column+1) * x_spacing), 0);
lv_label_set_text_fmt(status_label, "%s", LV_SYMBOL_STOP); lv_label_set_text_fmt(status_label, "%s", LV_SYMBOL_STOP);
gpio->lv_pins[i] = status_label; gpio->lvPins[i] = status_label;
column++; column++;
@ -164,37 +179,43 @@ static void app_show(App& app, lv_obj_t* parent) {
lv_obj_set_pos(postfix, (int32_t)((column+1) * x_spacing), 0); lv_obj_set_pos(postfix, (int32_t)((column+1) * x_spacing), 0);
// Add a new row wrapper underneath the last one // Add a new row wrapper underneath the last one
lv_obj_t* new_row_wrapper = create_gpio_row_wrapper(wrapper); lv_obj_t* new_row_wrapper = createGpioRowWrapper(wrapper);
lv_obj_align_to(new_row_wrapper, row_wrapper, LV_ALIGN_BOTTOM_LEFT, 0, 4); lv_obj_align_to(new_row_wrapper, row_wrapper, LV_ALIGN_BOTTOM_LEFT, 0, 4);
row_wrapper = new_row_wrapper; row_wrapper = new_row_wrapper;
column = 0; column = 0;
} }
} }
unlock(gpio); gpio->unlock();
task_start(gpio); gpio->startTask();
} }
static void on_hide(App& app) { void Gpio::onHide(App& app) {
auto* gpio = (Gpio*)app.getData(); auto* gpio = (Gpio*)app.getData();
task_stop(gpio); gpio->stopTask();
} }
static void on_start(App& app) {
auto* gpio = new Gpio { // region App lifecycle
.lv_pins = { nullptr },
.pin_states = { 0 }, static void onShow(App& app, lv_obj_t* parent) {
.thread = nullptr, auto* gpio = (Gpio*)app.getData();
.mutex = tt_mutex_alloc(MutexTypeNormal), gpio->onShow(app, parent);
.thread_interrupted = true, }
};
static void onHide(App& app) {
auto* gpio = (Gpio*)app.getData();
gpio->onHide(app);
}
static void onStart(App& app) {
auto* gpio = new Gpio();
app.setData(gpio); app.setData(gpio);
} }
static void on_stop(App& app) { static void onStop(App& app) {
auto* gpio = (Gpio*)app.getData(); auto* gpio = (Gpio*)app.getData();
tt_mutex_free(gpio->mutex);
delete gpio; delete gpio;
} }
@ -204,10 +225,10 @@ extern const Manifest manifest = {
.id = "Gpio", .id = "Gpio",
.name = "GPIO", .name = "GPIO",
.type = TypeSystem, .type = TypeSystem,
.onStart = &on_start, .onStart = onStart,
.onStop = &on_stop, .onStop = onStop,
.onShow = &app_show, .onShow = onShow,
.onHide = &on_hide .onHide = onHide
}; };
} // namespace } // namespace

View File

@ -7,7 +7,7 @@
namespace tt::app::i2csettings { namespace tt::app::i2csettings {
static void on_switch_toggle(lv_event_t* event) { static void onSwitchToggled(lv_event_t* event) {
lv_event_code_t code = lv_event_get_code(event); lv_event_code_t code = lv_event_get_code(event);
auto* state_switch = static_cast<lv_obj_t*>(lv_event_get_target(event)); auto* state_switch = static_cast<lv_obj_t*>(lv_event_get_target(event));
const hal::i2c::Configuration* configuration = static_cast<hal::i2c::Configuration*>(lv_event_get_user_data(event)); const hal::i2c::Configuration* configuration = static_cast<hal::i2c::Configuration*>(lv_event_get_user_data(event));
@ -46,7 +46,7 @@ static void show(lv_obj_t* parent, const hal::i2c::Configuration& configuration)
lv_obj_add_state(state_switch, LV_STATE_CHECKED); lv_obj_add_state(state_switch, LV_STATE_CHECKED);
} }
lv_obj_add_event_cb(state_switch, on_switch_toggle, LV_EVENT_VALUE_CHANGED, (void*)&configuration); lv_obj_add_event_cb(state_switch, onSwitchToggled, LV_EVENT_VALUE_CHANGED, (void*) &configuration);
} }
// SDA label // SDA label
@ -69,7 +69,7 @@ static void show(lv_obj_t* parent, const hal::i2c::Configuration& configuration)
} }
} }
static void on_show(App& app, lv_obj_t* parent) { static void onShow(App& app, lv_obj_t* parent) {
lv_obj_set_flex_flow(parent, LV_FLEX_FLOW_COLUMN); lv_obj_set_flex_flow(parent, LV_FLEX_FLOW_COLUMN);
lvgl::toolbar_create(parent, app); lvgl::toolbar_create(parent, app);
@ -90,7 +90,7 @@ extern const Manifest manifest = {
.name = "I2C", .name = "I2C",
.icon = TT_ASSETS_APP_ICON_I2C_SETTINGS, .icon = TT_ASSETS_APP_ICON_I2C_SETTINGS,
.type = TypeSettings, .type = TypeSettings,
.onShow = &on_show .onShow = onShow
}; };
} // namespace } // namespace

View File

@ -8,7 +8,7 @@ namespace tt::app::imageviewer {
#define TAG "image_viewer" #define TAG "image_viewer"
static void on_show(App& app, lv_obj_t* parent) { static void onShow(App& app, lv_obj_t* parent) {
lv_obj_set_flex_flow(parent, LV_FLEX_FLOW_COLUMN); lv_obj_set_flex_flow(parent, LV_FLEX_FLOW_COLUMN);
lvgl::toolbar_create(parent, app); lvgl::toolbar_create(parent, app);
@ -35,7 +35,7 @@ extern const Manifest manifest = {
.id = "ImageViewer", .id = "ImageViewer",
.name = "Image Viewer", .name = "Image Viewer",
.type = TypeHidden, .type = TypeHidden,
.onShow = &on_show .onShow = onShow
}; };
} // namespace } // namespace

View File

@ -18,10 +18,10 @@ typedef struct {
lv_obj_t* charge_state; lv_obj_t* charge_state;
lv_obj_t* charge_level; lv_obj_t* charge_level;
lv_obj_t* current; lv_obj_t* current;
} AppData; } Data;
static void app_update_ui(void* callbackContext) { static void updateUi(void* callbackContext) {
auto* data = (AppData*)callbackContext; auto* data = (Data*)callbackContext;
bool charging_enabled = data->power->isChargingEnabled(); bool charging_enabled = data->power->isChargingEnabled();
const char* charge_state = data->power->isCharging() ? "yes" : "no"; const char* charge_state = data->power->isCharging() ? "yes" : "no";
uint8_t charge_level = data->power->getChargeLevel(); uint8_t charge_level = data->power->getChargeLevel();
@ -40,20 +40,20 @@ static void app_update_ui(void* callbackContext) {
lvgl::unlock(); lvgl::unlock();
} }
static void on_power_enabled_change(lv_event_t* event) { static void onPowerEnabledChanged(lv_event_t* event) {
lv_event_code_t code = lv_event_get_code(event); lv_event_code_t code = lv_event_get_code(event);
auto* enable_switch = static_cast<lv_obj_t*>(lv_event_get_target(event)); auto* enable_switch = static_cast<lv_obj_t*>(lv_event_get_target(event));
if (code == LV_EVENT_VALUE_CHANGED) { if (code == LV_EVENT_VALUE_CHANGED) {
bool is_on = lv_obj_has_state(enable_switch, LV_STATE_CHECKED); bool is_on = lv_obj_has_state(enable_switch, LV_STATE_CHECKED);
auto* data = static_cast<AppData*>(lv_event_get_user_data(event)); auto* data = static_cast<Data*>(lv_event_get_user_data(event));
if (data->power->isChargingEnabled() != is_on) { if (data->power->isChargingEnabled() != is_on) {
data->power->setChargingEnabled(is_on); data->power->setChargingEnabled(is_on);
app_update_ui(data); updateUi(data);
} }
} }
} }
static void app_show(App& app, lv_obj_t* parent) { static void onShow(App& app, lv_obj_t* parent) {
lv_obj_set_flex_flow(parent, LV_FLEX_FLOW_COLUMN); lv_obj_set_flex_flow(parent, LV_FLEX_FLOW_COLUMN);
lvgl::toolbar_create(parent, app); lvgl::toolbar_create(parent, app);
@ -64,7 +64,7 @@ static void app_show(App& app, lv_obj_t* parent) {
lv_obj_set_flex_grow(wrapper, 1); lv_obj_set_flex_grow(wrapper, 1);
lv_obj_set_flex_flow(wrapper, LV_FLEX_FLOW_COLUMN); lv_obj_set_flex_flow(wrapper, LV_FLEX_FLOW_COLUMN);
auto* data = static_cast<AppData*>(app.getData()); auto* data = static_cast<Data*>(app.getData());
// Top row: enable/disable // Top row: enable/disable
lv_obj_t* switch_container = lv_obj_create(wrapper); lv_obj_t* switch_container = lv_obj_create(wrapper);
@ -78,7 +78,7 @@ static void app_show(App& app, lv_obj_t* parent) {
lv_obj_set_align(enable_label, LV_ALIGN_LEFT_MID); lv_obj_set_align(enable_label, LV_ALIGN_LEFT_MID);
lv_obj_t* enable_switch = lv_switch_create(switch_container); lv_obj_t* enable_switch = lv_switch_create(switch_container);
lv_obj_add_event_cb(enable_switch, on_power_enabled_change, LV_EVENT_ALL, data); lv_obj_add_event_cb(enable_switch, onPowerEnabledChanged, LV_EVENT_ALL, data);
lv_obj_set_align(enable_switch, LV_ALIGN_RIGHT_MID); lv_obj_set_align(enable_switch, LV_ALIGN_RIGHT_MID);
data->enable_switch = enable_switch; data->enable_switch = enable_switch;
@ -86,25 +86,25 @@ static void app_show(App& app, lv_obj_t* parent) {
data->charge_level = lv_label_create(wrapper); data->charge_level = lv_label_create(wrapper);
data->current = lv_label_create(wrapper); data->current = lv_label_create(wrapper);
app_update_ui(data); updateUi(data);
data->update_timer->start(ms_to_ticks(1000)); data->update_timer->start(ms_to_ticks(1000));
} }
static void app_hide(TT_UNUSED App& app) { static void onHide(TT_UNUSED App& app) {
auto* data = static_cast<AppData*>(app.getData()); auto* data = static_cast<Data*>(app.getData());
data->update_timer->stop(); data->update_timer->stop();
} }
static void app_start(App& app) { static void onStart(App& app) {
auto* data = new AppData(); auto* data = new Data();
data->update_timer = new Timer(Timer::TypePeriodic, &app_update_ui, data); data->update_timer = new Timer(Timer::TypePeriodic, &updateUi, data);
data->power = getConfiguration()->hardware->power; data->power = getConfiguration()->hardware->power;
assert(data->power != nullptr); // The Power app only shows up on supported devices assert(data->power != nullptr); // The Power app only shows up on supported devices
app.setData(data); app.setData(data);
} }
static void app_stop(App& app) { static void onStop(App& app) {
auto* data = static_cast<AppData*>(app.getData()); auto* data = static_cast<Data*>(app.getData());
delete data->update_timer; delete data->update_timer;
delete data; delete data;
} }
@ -114,10 +114,10 @@ extern const Manifest manifest = {
.name = "Power", .name = "Power",
.icon = TT_ASSETS_APP_ICON_POWER_SETTINGS, .icon = TT_ASSETS_APP_ICON_POWER_SETTINGS,
.type = TypeSettings, .type = TypeSettings,
.onStart = &app_start, .onStart = onStart,
.onStop = &app_stop, .onStop = onStop,
.onShow = &app_show, .onShow = onShow,
.onHide = &app_hide .onHide = onHide
}; };
} // namespace } // namespace

View File

@ -2,17 +2,17 @@
namespace tt::app::screenshot { namespace tt::app::screenshot {
static void on_show(App& app, lv_obj_t* parent) { static void onShow(App& app, lv_obj_t* parent) {
auto* ui = static_cast<ScreenshotUi*>(app.getData()); auto* ui = static_cast<ScreenshotUi*>(app.getData());
create_ui(app, ui, parent); create_ui(app, ui, parent);
} }
static void on_start(App& app) { static void onStart(App& app) {
auto* ui = static_cast<ScreenshotUi*>(malloc(sizeof(ScreenshotUi))); auto* ui = static_cast<ScreenshotUi*>(malloc(sizeof(ScreenshotUi)));
app.setData(ui); app.setData(ui);
} }
static void on_stop(App& app) { static void onStop(App& app) {
auto* ui = static_cast<ScreenshotUi*>(app.getData()); auto* ui = static_cast<ScreenshotUi*>(app.getData());
free(ui); free(ui);
} }
@ -22,9 +22,9 @@ extern const Manifest manifest = {
.name = "_Screenshot", // So it gets put at the bottom of the desktop and becomes less visible on small screen devices .name = "_Screenshot", // So it gets put at the bottom of the desktop and becomes less visible on small screen devices
.icon = LV_SYMBOL_IMAGE, .icon = LV_SYMBOL_IMAGE,
.type = TypeSystem, .type = TypeSystem,
.onStart = &on_start, .onStart = onStart,
.onStop = &on_stop, .onStop = onStop,
.onShow = &on_show, .onShow = onShow,
}; };
} // namespace } // namespace

View File

@ -103,7 +103,7 @@ extern const Manifest manifest = {
.id = "SelectionDialog", .id = "SelectionDialog",
.name = "Selection Dialog", .name = "Selection Dialog",
.type = TypeHidden, .type = TypeHidden,
.onShow = &onShow .onShow = onShow
}; };
} }

View File

@ -8,7 +8,7 @@
namespace tt::app::settings { namespace tt::app::settings {
static void on_app_pressed(lv_event_t* e) { static void onAppPressed(lv_event_t* e) {
lv_event_code_t code = lv_event_get_code(e); lv_event_code_t code = lv_event_get_code(e);
if (code == LV_EVENT_CLICKED) { if (code == LV_EVENT_CLICKED) {
const auto* manifest = static_cast<const Manifest*>(lv_event_get_user_data(e)); const auto* manifest = static_cast<const Manifest*>(lv_event_get_user_data(e));
@ -16,15 +16,15 @@ static void on_app_pressed(lv_event_t* e) {
} }
} }
static void create_app_widget(const Manifest* manifest, void* parent) { static void createWidget(const Manifest* manifest, void* parent) {
tt_check(parent); tt_check(parent);
auto* list = (lv_obj_t*)parent; auto* list = (lv_obj_t*)parent;
const void* icon = !manifest->icon.empty() ? manifest->icon.c_str() : TT_ASSETS_APP_ICON_FALLBACK; const void* icon = !manifest->icon.empty() ? manifest->icon.c_str() : TT_ASSETS_APP_ICON_FALLBACK;
lv_obj_t* btn = lv_list_add_button(list, icon, manifest->name.c_str()); lv_obj_t* btn = lv_list_add_button(list, icon, manifest->name.c_str());
lv_obj_add_event_cb(btn, &on_app_pressed, LV_EVENT_CLICKED, (void*)manifest); lv_obj_add_event_cb(btn, &onAppPressed, LV_EVENT_CLICKED, (void*)manifest);
} }
static void on_show(App& app, lv_obj_t* parent) { static void onShow(App& app, lv_obj_t* parent) {
lv_obj_set_flex_flow(parent, LV_FLEX_FLOW_COLUMN); lv_obj_set_flex_flow(parent, LV_FLEX_FLOW_COLUMN);
lvgl::toolbar_create(parent, app); lvgl::toolbar_create(parent, app);
@ -37,7 +37,7 @@ static void on_show(App& app, lv_obj_t* parent) {
std::sort(manifests.begin(), manifests.end(), SortAppManifestByName); std::sort(manifests.begin(), manifests.end(), SortAppManifestByName);
for (const auto& manifest: manifests) { for (const auto& manifest: manifests) {
if (manifest->type == TypeSettings) { if (manifest->type == TypeSettings) {
create_app_widget(manifest, list); createWidget(manifest, list);
} }
} }
} }
@ -47,10 +47,7 @@ extern const Manifest manifest = {
.name = "Settings", .name = "Settings",
.icon = TT_ASSETS_APP_ICON_SETTINGS, .icon = TT_ASSETS_APP_ICON_SETTINGS,
.type = TypeSystem, .type = TypeSystem,
.onStart = nullptr, .onShow = onShow,
.onStop = nullptr,
.onShow = &on_show,
.onHide = nullptr
}; };
} // namespace } // namespace

View File

@ -5,7 +5,7 @@
namespace tt::app::systeminfo { namespace tt::app::systeminfo {
static size_t get_heap_free() { static size_t getHeapFree() {
#ifdef ESP_PLATFORM #ifdef ESP_PLATFORM
return heap_caps_get_free_size(MALLOC_CAP_INTERNAL); return heap_caps_get_free_size(MALLOC_CAP_INTERNAL);
#else #else
@ -13,7 +13,7 @@ static size_t get_heap_free() {
#endif #endif
} }
static size_t get_heap_total() { static size_t getHeapTotal() {
#ifdef ESP_PLATFORM #ifdef ESP_PLATFORM
return heap_caps_get_total_size(MALLOC_CAP_INTERNAL); return heap_caps_get_total_size(MALLOC_CAP_INTERNAL);
#else #else
@ -21,7 +21,7 @@ static size_t get_heap_total() {
#endif #endif
} }
static size_t get_spi_free() { static size_t getSpiFree() {
#ifdef ESP_PLATFORM #ifdef ESP_PLATFORM
return heap_caps_get_free_size(MALLOC_CAP_SPIRAM); return heap_caps_get_free_size(MALLOC_CAP_SPIRAM);
#else #else
@ -29,7 +29,7 @@ static size_t get_spi_free() {
#endif #endif
} }
static size_t get_spi_total() { static size_t getSpiTotal() {
#ifdef ESP_PLATFORM #ifdef ESP_PLATFORM
return heap_caps_get_total_size(MALLOC_CAP_SPIRAM); return heap_caps_get_total_size(MALLOC_CAP_SPIRAM);
#else #else
@ -37,7 +37,7 @@ static size_t get_spi_total() {
#endif #endif
} }
static void add_memory_bar(lv_obj_t* parent, const char* label, size_t used, size_t total) { static void addMemoryBar(lv_obj_t* parent, const char* label, size_t used, size_t total) {
lv_obj_t* container = lv_obj_create(parent); lv_obj_t* container = lv_obj_create(parent);
lv_obj_set_size(container, LV_PCT(100), LV_SIZE_CONTENT); lv_obj_set_size(container, LV_PCT(100), LV_SIZE_CONTENT);
lv_obj_set_style_pad_all(container, 0, 0); lv_obj_set_style_pad_all(container, 0, 0);
@ -65,7 +65,7 @@ static void add_memory_bar(lv_obj_t* parent, const char* label, size_t used, siz
lv_obj_set_style_text_align(bottom_label, LV_TEXT_ALIGN_RIGHT, 0); lv_obj_set_style_text_align(bottom_label, LV_TEXT_ALIGN_RIGHT, 0);
} }
static void on_show(App& app, lv_obj_t* parent) { static void onShow(App& app, lv_obj_t* parent) {
lv_obj_set_flex_flow(parent, LV_FLEX_FLOW_COLUMN); lv_obj_set_flex_flow(parent, LV_FLEX_FLOW_COLUMN);
lvgl::toolbar_create(parent, app); lvgl::toolbar_create(parent, app);
@ -83,8 +83,8 @@ static void on_show(App& app, lv_obj_t* parent) {
lv_obj_set_flex_flow(memory_wrapper, LV_FLEX_FLOW_COLUMN); lv_obj_set_flex_flow(memory_wrapper, LV_FLEX_FLOW_COLUMN);
lv_obj_set_size(memory_wrapper, LV_PCT(100), LV_SIZE_CONTENT); lv_obj_set_size(memory_wrapper, LV_PCT(100), LV_SIZE_CONTENT);
add_memory_bar(memory_wrapper, "Heap", get_heap_total() - get_heap_free(), get_heap_total()); addMemoryBar(memory_wrapper, "Heap", getHeapTotal() - getHeapFree(), getHeapTotal());
add_memory_bar(memory_wrapper, "SPI", get_spi_total() - get_spi_free(), get_spi_total()); addMemoryBar(memory_wrapper, "SPI", getSpiTotal() - getSpiFree(), getSpiTotal());
#ifdef ESP_PLATFORM #ifdef ESP_PLATFORM
// Build info // Build info
@ -97,8 +97,6 @@ static void on_show(App& app, lv_obj_t* parent) {
lv_obj_t* esp_idf_version = lv_label_create(build_info_wrapper); lv_obj_t* esp_idf_version = lv_label_create(build_info_wrapper);
lv_label_set_text_fmt(esp_idf_version, "IDF version: %d.%d.%d", ESP_IDF_VERSION_MAJOR, ESP_IDF_VERSION_MINOR, ESP_IDF_VERSION_PATCH); lv_label_set_text_fmt(esp_idf_version, "IDF version: %d.%d.%d", ESP_IDF_VERSION_MAJOR, ESP_IDF_VERSION_MINOR, ESP_IDF_VERSION_PATCH);
#endif #endif
} }
extern const Manifest manifest = { extern const Manifest manifest = {
@ -108,7 +106,7 @@ extern const Manifest manifest = {
.type = TypeSystem, .type = TypeSystem,
.onStart = nullptr, .onStart = nullptr,
.onStop = nullptr, .onStop = nullptr,
.onShow = &on_show .onShow = onShow
}; };
} // namespace } // namespace

View File

@ -9,7 +9,7 @@
namespace tt::app::textviewer { namespace tt::app::textviewer {
static void on_show(App& app, lv_obj_t* parent) { static void onShow(App& app, lv_obj_t* parent) {
lv_obj_set_flex_flow(parent, LV_FLEX_FLOW_COLUMN); lv_obj_set_flex_flow(parent, LV_FLEX_FLOW_COLUMN);
lvgl::toolbar_create(parent, app); lvgl::toolbar_create(parent, app);
@ -35,7 +35,7 @@ extern const Manifest manifest = {
.id = "TextViewer", .id = "TextViewer",
.name = "Text Viewer", .name = "Text Viewer",
.type = TypeHidden, .type = TypeHidden,
.onShow = &on_show .onShow = onShow
}; };
} // namespace } // namespace

View File

@ -1,4 +1,4 @@
#include "app/display/DisplayPreferences.h" #include "app/display/DisplaySettings.h"
#include "lvgl.h" #include "lvgl.h"
#include "hal/Configuration.h" #include "hal/Configuration.h"
#include "hal/Display.h" #include "hal/Display.h"
@ -32,7 +32,7 @@ bool initDisplay(const hal::Configuration& config) {
tt_assert(existing_display_user_data == nullptr); tt_assert(existing_display_user_data == nullptr);
lv_display_set_user_data(lvgl_display, display); lv_display_set_user_data(lvgl_display, display);
lv_display_rotation_t rotation = app::display::preferences_get_rotation(); lv_display_rotation_t rotation = app::display::getRotation();
if (rotation != lv_disp_get_rotation(lv_disp_get_default())) { if (rotation != lv_disp_get_rotation(lv_disp_get_default())) {
lv_disp_set_rotation(lv_disp_get_default(), static_cast<lv_display_rotation_t>(rotation)); lv_disp_set_rotation(lv_disp_get_default(), static_cast<lv_display_rotation_t>(rotation));
} }