Merge develop into main (#339)

- Update ILI9341 driver to v2.0.1
- Lots of code cleanup for apps
- Refactor app "type" into "category" and added flags to the manifest (for show/hide statusbar and for hidden apps)
- Rename some ElfApp-related functionality and improved the way the static data was managed
- Rename "filebrowser" to "files"
- Added cstring functions to tt_init.cpp
- Minor fix in Boot app
- Updated external apps for SDK changes
This commit is contained in:
Ken Van Hoeylandt 2025-09-17 23:42:49 +02:00 committed by GitHub
parent a2af95b92d
commit faab6d825f
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
82 changed files with 349 additions and 360 deletions

View File

@ -1,5 +1,5 @@
dependencies: dependencies:
espressif/esp_lcd_ili9341: "2.0.0" espressif/esp_lcd_ili9341: "2.0.1"
atanisoft/esp_lcd_ili9488: "1.0.10" atanisoft/esp_lcd_ili9488: "1.0.10"
espressif/esp_lcd_touch: "1.1.2" espressif/esp_lcd_touch: "1.1.2"
atanisoft/esp_lcd_touch_xpt2046: "1.0.5" atanisoft/esp_lcd_touch_xpt2046: "1.0.5"

View File

@ -48,13 +48,6 @@ if (DEFINED ENV{ESP_IDF_VERSION})
set(EXCLUDE_COMPONENTS "Simulator") set(EXCLUDE_COMPONENTS "Simulator")
# LVGL
# set(LV_CONF_PATH Libraries/lvgl/src/lv_conf_kconfig.h)
# get_filename_component(
# LV_CONF_PATH Libraries/lvgl/src/lv_conf_kconfig.h ABSOLUTE
# )
# add_compile_definitions(LV_CONF_PATH="${LVGL_CONFIG_FULL_PATH}")
idf_build_set_property(LINK_OPTIONS "-Wl,--wrap=esp_panic_handler" APPEND) idf_build_set_property(LINK_OPTIONS "-Wl,--wrap=esp_panic_handler" APPEND)
idf_build_set_property(LINK_OPTIONS "-Wl,--wrap=esp_log_write" APPEND) idf_build_set_property(LINK_OPTIONS "-Wl,--wrap=esp_log_write" APPEND)
idf_build_set_property(LINK_OPTIONS "-Wl,--wrap=lv_button_create" APPEND) idf_build_set_property(LINK_OPTIONS "-Wl,--wrap=lv_button_create" APPEND)

View File

@ -3,20 +3,18 @@
## Higher Priority ## Higher Priority
- Show a warning in the web installer when flashing CYD 28R board regarding v1/v2/v3 - Show a warning in the web installer when flashing CYD 28R board regarding v1/v2/v3
- Fix Development service: when no SD card is present, the app fails to install. Consider installing to `/data`
Note: Change app install to "transfer file" functionality. We can have a proper install when we have app packaging.
Note: Consider installation path option in interface
- External app loading: Check the version of Tactility and check ESP target hardware to check for compatibility. - External app loading: Check the version of Tactility and check ESP target hardware to check for compatibility.
- Make a URL handler. Use it for handling local files. Match file types with apps. - Make a URL handler. Use it for handling local files. Match file types with apps.
Create some kind of "intent" handler like on Android. Create some kind of "intent" handler like on Android.
The intent can have an action (e.g. view), a URL and an optional bundle. The intent can have an action (e.g. view), a URL and an optional bundle.
The manifest can provide the intent handler The manifest can provide the intent handler
- Update ILI934x to v2.0.1
- Apps with update timer in onCreate() should check `lvgl::isStarted()`
- CrowPanel Basic 3.5": check why GraphicsDemo fails - CrowPanel Basic 3.5": check why GraphicsDemo fails
- CrowPanel Basic 3.5": check why System Info doesn't show storage info - CrowPanel Basic 3.5": check why System Info doesn't show storage info
- Update to LVGL v9.3 stable - When an SD card is detected, check if it has been initialized and assigned as data partition.
- Create `app::getSettingsPath()` to get paths to properties files by first trying sd card and then trying `/data` If the user choses to select it, then copy files from /data over to it.
Write the user choice to a file on the card.
File contains 3 statuses: ignore, data, .. initdata?
The latter is used for auto-selecting it as data partition.
## Medium Priority ## Medium Priority
@ -33,7 +31,6 @@
## Lower Priority ## Lower Priority
- Rename `filebrowser` to `files` and `FileBrowser.cpp` to `Files.cpp`
- Implement system suspend that turns off the screen - Implement system suspend that turns off the screen
- The boot button on some devices can be used as GPIO_NUM_0 at runtime - The boot button on some devices can be used as GPIO_NUM_0 at runtime
- Localize all apps - Localize all apps
@ -95,3 +92,24 @@
- Display touch calibration - Display touch calibration
- RSS reader - RSS reader
- Static file web server (with option to specify path and port) - Static file web server (with option to specify path and port)
- Diceware
# App Store
- Register user
- Name
- Company
- Email
- Password
- Create/destroy session
- List apps
- Install app
- App ID
- App version
- Add/remove API key
- Upload app
- Category
- File
- Name
- Description
- List apps

View File

@ -14,7 +14,6 @@ static void destroyApp(void* app) {
} }
ExternalAppManifest manifest = { ExternalAppManifest manifest = {
.name = "Hello World",
.createData = createApp, .createData = createApp,
.destroyData = destroyApp, .destroyData = destroyApp,
.onShow = onShow, .onShow = onShow,

View File

@ -1,7 +1,7 @@
[manifest] [manifest]
version=0.1 version=0.1
[target] [target]
sdk=0.5.0 sdk=0.6.0-SNAPSHOT1
platforms=esp32,esp32s3 platforms=esp32,esp32s3
[app] [app]
id=com.bytewelder.calculator id=com.bytewelder.calculator

View File

@ -14,7 +14,7 @@ import shutil
import configparser import configparser
ttbuild_path = ".tactility" ttbuild_path = ".tactility"
ttbuild_version = "2.1.0" ttbuild_version = "2.1.1"
ttbuild_cdn = "https://cdn.tactility.one" ttbuild_cdn = "https://cdn.tactility.one"
ttbuild_sdk_json_validity = 3600 # seconds ttbuild_sdk_json_validity = 3600 # seconds
ttport = 6666 ttport = 6666
@ -618,7 +618,7 @@ if __name__ == "__main__":
print_help() print_help()
exit_with_error("Commandline parameter missing") exit_with_error("Commandline parameter missing")
uninstall_action(manifest, sys.argv[2]) uninstall_action(manifest, sys.argv[2])
elif action_arg == "bir": elif action_arg == "bir" or action_arg == "brrr":
if len(sys.argv) < 3: if len(sys.argv) < 3:
print_help() print_help()
exit_with_error("Commandline parameter missing") exit_with_error("Commandline parameter missing")

View File

@ -89,7 +89,6 @@ static void onDestroy(AppHandle appHandle, void* data) {
} }
ExternalAppManifest manifest = { ExternalAppManifest manifest = {
.name = "Hello World",
.onCreate = onCreate, .onCreate = onCreate,
.onDestroy = onDestroy .onDestroy = onDestroy
}; };

View File

@ -1,7 +1,7 @@
[manifest] [manifest]
version=0.1 version=0.1
[target] [target]
sdk=0.5.0 sdk=0.6.0-SNAPSHOT1
platforms=esp32,esp32s3 platforms=esp32,esp32s3
[app] [app]
id=com.bytewelder.graphicsdemo id=com.bytewelder.graphicsdemo

View File

@ -14,7 +14,7 @@ import shutil
import configparser import configparser
ttbuild_path = ".tactility" ttbuild_path = ".tactility"
ttbuild_version = "2.1.0" ttbuild_version = "2.1.1"
ttbuild_cdn = "https://cdn.tactility.one" ttbuild_cdn = "https://cdn.tactility.one"
ttbuild_sdk_json_validity = 3600 # seconds ttbuild_sdk_json_validity = 3600 # seconds
ttport = 6666 ttport = 6666
@ -618,7 +618,7 @@ if __name__ == "__main__":
print_help() print_help()
exit_with_error("Commandline parameter missing") exit_with_error("Commandline parameter missing")
uninstall_action(manifest, sys.argv[2]) uninstall_action(manifest, sys.argv[2])
elif action_arg == "bir": elif action_arg == "bir" or action_arg == "brrr":
if len(sys.argv) < 3: if len(sys.argv) < 3:
print_help() print_help()
exit_with_error("Commandline parameter missing") exit_with_error("Commandline parameter missing")

View File

@ -15,7 +15,6 @@ static void onShow(AppHandle app, void* data, lv_obj_t* parent) {
} }
ExternalAppManifest manifest = { ExternalAppManifest manifest = {
.name = "Hello World",
.onShow = onShow .onShow = onShow
}; };

View File

@ -1,7 +1,7 @@
[manifest] [manifest]
version=0.1 version=0.1
[target] [target]
sdk=0.5.0 sdk=0.6.0-SNAPSHOT1
platforms=esp32,esp32s3 platforms=esp32,esp32s3
[app] [app]
id=com.bytewelder.helloworld id=com.bytewelder.helloworld

View File

@ -14,7 +14,7 @@ import shutil
import configparser import configparser
ttbuild_path = ".tactility" ttbuild_path = ".tactility"
ttbuild_version = "2.1.0" ttbuild_version = "2.1.1"
ttbuild_cdn = "https://cdn.tactility.one" ttbuild_cdn = "https://cdn.tactility.one"
ttbuild_sdk_json_validity = 3600 # seconds ttbuild_sdk_json_validity = 3600 # seconds
ttport = 6666 ttport = 6666
@ -618,7 +618,7 @@ if __name__ == "__main__":
print_help() print_help()
exit_with_error("Commandline parameter missing") exit_with_error("Commandline parameter missing")
uninstall_action(manifest, sys.argv[2]) uninstall_action(manifest, sys.argv[2])
elif action_arg == "bir": elif action_arg == "bir" or action_arg == "brrr":
if len(sys.argv) < 3: if len(sys.argv) < 3:
print_help() print_help()
exit_with_error("Commandline parameter missing") exit_with_error("Commandline parameter missing")

View File

@ -64,14 +64,14 @@ public:
auto lock = getMutex().asScopedLock(); auto lock = getMutex().asScopedLock();
lock.lock(); lock.lock();
if (resultHolder != nullptr) { if (resultHolder == nullptr) {
return false;
}
outResult = resultHolder->result; outResult = resultHolder->result;
outBundle = std::move(resultHolder->resultData); outBundle = std::move(resultHolder->resultData);
resultHolder = nullptr; resultHolder = nullptr;
return true; return true;
} else {
return false;
}
} }
}; };

View File

@ -13,7 +13,7 @@ enum class Result;
typedef union { typedef union {
struct { struct {
bool showStatusbar : 1; bool hideStatusbar : 1;
}; };
unsigned char flags; unsigned char flags;
} Flags; } Flags;

View File

@ -1,6 +1,6 @@
#pragma once #pragma once
#include "Tactility/app/AppRegistration.h" #include <Tactility/app/AppRegistration.h>
#include <string> #include <string>
@ -10,13 +10,7 @@ class App;
class AppContext; class AppContext;
/** Application types */ /** Application types */
enum class Type { enum class Category {
/** Boot screen, shown before desktop is launched. */
Boot,
/** A launcher app sits at the root of the app stack after the boot splash is finished */
Launcher,
/** Apps that generally aren't started from the desktop (e.g. image viewer) */
Hidden,
/** Standard apps, provided by the system. */ /** Standard apps, provided by the system. */
System, System,
/** The apps that are launched/shown by the Settings app. The Settings app itself is of type AppTypeSystem. */ /** The apps that are launched/shown by the Settings app. The Settings app itself is of type AppTypeSystem. */
@ -60,6 +54,15 @@ public:
typedef std::shared_ptr<App>(*CreateApp)(); typedef std::shared_ptr<App>(*CreateApp)();
struct AppManifest { struct AppManifest {
struct Flags {
constexpr static uint32_t None = 0;
/** Don't show the statusbar */
constexpr static uint32_t HideStatusBar = 1 << 0;
/** Hint to other systems to not show this app (e.g. in launcher or settings) */
constexpr static uint32_t Hidden = 1 << 1;
};
/** The identifier by which the app is launched by the system and other apps. */ /** The identifier by which the app is launched by the system and other apps. */
std::string id = {}; std::string id = {};
@ -69,12 +72,15 @@ struct AppManifest {
/** Optional icon. */ /** Optional icon. */
std::string icon = {}; std::string icon = {};
/** App type affects launch behaviour. */ /** App category helps with listing apps in Launcher, app list or settings apps. */
Type type = Type::User; Category category = Category::User;
/** Where the app is located */ /** Where the app is located */
Location location = Location::internal(); Location location = Location::internal();
/** Controls various settings */
uint32_t flags = Flags::None;
/** Create the instance of the app */ /** Create the instance of the app */
CreateApp createApp = nullptr; CreateApp createApp = nullptr;
}; };

View File

@ -14,9 +14,7 @@ typedef void (*OnShow)(void* appContext, void* _Nullable data, lv_obj_t* parent)
typedef void (*OnHide)(void* appContext, void* _Nullable data); typedef void (*OnHide)(void* appContext, void* _Nullable data);
typedef void (*OnResult)(void* appContext, void* _Nullable data, LaunchId launchId, Result result, Bundle* resultData); typedef void (*OnResult)(void* appContext, void* _Nullable data, LaunchId launchId, Result result, Bundle* resultData);
void setElfAppManifest( void setElfAppParameters(
const char* name,
const char* _Nullable icon,
CreateData _Nullable createData, CreateData _Nullable createData,
DestroyData _Nullable destroyData, DestroyData _Nullable destroyData,
OnCreate _Nullable onCreate, OnCreate _Nullable onCreate,

View File

@ -1 +0,0 @@
#pragma once

View File

@ -37,4 +37,5 @@ namespace tt::app::alertdialog {
* @return a value greater than 0 when a selection was done, or -1 when the app was closed clicking one of the selection buttons. * @return a value greater than 0 when a selection was done, or -1 when the app was closed clicking one of the selection buttons.
*/ */
int32_t getResultIndex(const Bundle& bundle); int32_t getResultIndex(const Bundle& bundle);
} }

View File

@ -1,6 +1,6 @@
#pragma once #pragma once
namespace tt::app::filebrowser { namespace tt::app::files {
void start(); void start();

View File

@ -3,7 +3,6 @@
#include <Tactility/Bundle.h> #include <Tactility/Bundle.h>
#include <string> #include <string>
#include <vector>
/** /**
* Start the app by its ID and provide: * Start the app by its ID and provide:

View File

@ -23,4 +23,5 @@ namespace tt::app::selectiondialog {
* @return a value greater than 0 when a selection was done, or -1 when the app was closed without selecting an item. * @return a value greater than 0 when a selection was done, or -1 when the app was closed without selecting an item.
*/ */
int32_t getResultIndex(const Bundle& bundle); int32_t getResultIndex(const Bundle& bundle);
} }

View File

@ -1,8 +1,8 @@
#pragma once #pragma once
#include "Tactility/app/AppContext.h" #include <Tactility/app/AppContext.h>
#include "Tactility/app/AppManifest.h" #include <Tactility/app/AppManifest.h>
#include "Tactility/app/ElfApp.h" #include <Tactility/app/ElfApp.h>
#include <Tactility/Bundle.h> #include <Tactility/Bundle.h>
#include <Tactility/Mutex.h> #include <Tactility/Mutex.h>
@ -29,7 +29,7 @@ class AppInstance : public AppContext {
const std::shared_ptr<AppManifest> manifest; const std::shared_ptr<AppManifest> manifest;
State state = State::Initial; State state = State::Initial;
LaunchId launchId; LaunchId launchId;
Flags flags = { .showStatusbar = true }; Flags flags = { .hideStatusbar = true };
/** @brief Optional parameters to start the app with /** @brief Optional parameters to start the app with
* When these are stored in the app struct, the struct takes ownership. * When these are stored in the app struct, the struct takes ownership.
* Do not mutate after app creation. * Do not mutate after app creation.

View File

@ -6,9 +6,9 @@
#include <vector> #include <vector>
#include <dirent.h> #include <dirent.h>
namespace tt::app::filebrowser { namespace tt::app::files {
class State { class State final {
public: public:
@ -44,7 +44,7 @@ public:
template <std::invocable<const std::vector<dirent> &> Func> template <std::invocable<const std::vector<dirent> &> Func>
void withEntries(Func&& onEntries) const { void withEntries(Func&& onEntries) const {
mutex.withLock([&]() { mutex.withLock([&] {
std::invoke(std::forward<Func>(onEntries), dir_entries); std::invoke(std::forward<Func>(onEntries), dir_entries);
}); });
} }
@ -61,13 +61,9 @@ public:
std::string getSelectedChildPath() const; std::string getSelectedChildPath() const;
PendingAction getPendingAction() const { PendingAction getPendingAction() const { return action; }
return action;
}
void setPendingAction(PendingAction newAction) { void setPendingAction(PendingAction newAction) { action = newAction; }
action = newAction;
}
}; };
} }

View File

@ -2,7 +2,7 @@
#include <string> #include <string>
namespace tt::app::filebrowser { namespace tt::app::files {
bool isSupportedAppFile(const std::string& filename); bool isSupportedAppFile(const std::string& filename);
bool isSupportedImageFile(const std::string& filename); bool isSupportedImageFile(const std::string& filename);

View File

@ -2,14 +2,14 @@
#include "./State.h" #include "./State.h"
#include "Tactility/app/AppManifest.h" #include <Tactility/app/AppManifest.h>
#include <lvgl.h> #include <lvgl.h>
#include <memory> #include <memory>
namespace tt::app::filebrowser { namespace tt::app::files {
class View { class View final {
std::shared_ptr<State> state; std::shared_ptr<State> state;
lv_obj_t* dir_entry_list = nullptr; lv_obj_t* dir_entry_list = nullptr;

View File

@ -8,7 +8,7 @@
namespace tt::app::fileselection { namespace tt::app::fileselection {
class State { class State final {
Mutex mutex = Mutex(Mutex::Type::Recursive); Mutex mutex = Mutex(Mutex::Type::Recursive);
std::vector<dirent> dir_entries; std::vector<dirent> dir_entries;

View File

@ -3,14 +3,14 @@
#include "./State.h" #include "./State.h"
#include "./FileSelectionPrivate.h" #include "./FileSelectionPrivate.h"
#include "Tactility/app/AppManifest.h" #include <Tactility/app/AppManifest.h>
#include <lvgl.h> #include <lvgl.h>
#include <memory> #include <memory>
namespace tt::app::fileselection { namespace tt::app::fileselection {
class View { class View final {
std::shared_ptr<State> state; std::shared_ptr<State> state;
lv_obj_t* dir_entry_list = nullptr; lv_obj_t* dir_entry_list = nullptr;

View File

@ -1,12 +1,5 @@
#pragma once #pragma once
#include <Tactility/hal/i2c/I2c.h>
#include <Tactility/Mutex.h>
#include <Tactility/TactilityCore.h>
#include <Tactility/Thread.h>
#include <Tactility/Timer.h>
#include <lvgl.h>
#include <memory> #include <memory>
namespace tt::app::i2cscanner { namespace tt::app::i2cscanner {

View File

@ -1,6 +1,6 @@
#pragma once #pragma once
#include "Tactility/i18n/TextResources.h" #include <Tactility/i18n/TextResources.h>
// WARNING: This file is auto-generated. Do not edit manually. // WARNING: This file is auto-generated. Do not edit manually.

View File

@ -1,6 +1,6 @@
#pragma once #pragma once
#include "Tactility/i18n/TextResources.h" #include <Tactility/i18n/TextResources.h>
// WARNING: This file is auto-generated. Do not edit manually. // WARNING: This file is auto-generated. Do not edit manually.

View File

@ -1,13 +1,13 @@
#pragma once #pragma once
#include "./View.h" #include "View.h"
#include "Tactility/Preferences.h" #include <Tactility/Preferences.h>
#include "Tactility/Tactility.h" #include <Tactility/Tactility.h>
#include "Tactility/app/alertdialog/AlertDialog.h" #include <Tactility/app/alertdialog/AlertDialog.h>
#include "Tactility/hal/uart/Uart.h" #include <Tactility/hal/uart/Uart.h>
#include "Tactility/lvgl/LvglSync.h" #include <Tactility/lvgl/LvglSync.h>
#include "Tactility/lvgl/Style.h" #include <Tactility/lvgl/Style.h>
#include <Tactility/StringUtils.h> #include <Tactility/StringUtils.h>
#include <string> #include <string>
@ -127,7 +127,7 @@ public:
lv_obj_add_event_cb(connect_button, onConnectCallback, LV_EVENT_SHORT_CLICKED, this); lv_obj_add_event_cb(connect_button, onConnectCallback, LV_EVENT_SHORT_CLICKED, this);
} }
void onStop() { void onStop() override {
int speed = getSpeedInput(); int speed = getSpeedInput();
if (speed > 0) { if (speed > 0) {
preferences.putInt32("speed", speed); preferences.putInt32("speed", speed);

View File

@ -1,21 +1,18 @@
#pragma once #pragma once
#include "./View.h" #include <Tactility/app/serialconsole/View.h>
#include "Tactility/Timer.h" #include <Tactility/Timer.h>
#include <cstring> #include <cstring>
#include <sstream> #include <sstream>
#define TAG "SerialConsole"
namespace tt::app::serialconsole { namespace tt::app::serialconsole {
constexpr auto* TAG = "SerialConsole";
constexpr size_t receiveBufferSize = 512; constexpr size_t receiveBufferSize = 512;
constexpr size_t renderBufferSize = receiveBufferSize + 2; // Leave space for newline at split and null terminator at the end constexpr size_t renderBufferSize = receiveBufferSize + 2; // Leave space for newline at split and null terminator at the end
class ConsoleView final : public View { class ConsoleView final : public View {
private:
lv_obj_t* _Nullable parent = nullptr; lv_obj_t* _Nullable parent = nullptr;
lv_obj_t* _Nullable logTextarea = nullptr; lv_obj_t* _Nullable logTextarea = nullptr;
lv_obj_t* _Nullable inputTextarea = nullptr; lv_obj_t* _Nullable inputTextarea = nullptr;
@ -215,7 +212,7 @@ public:
viewThread = std::make_unique<Thread>( viewThread = std::make_unique<Thread>(
"SerConsView", "SerConsView",
4096, 4096,
[this]() { [this] {
return this->viewThreadMain(); return this->viewThreadMain();
} }
); );
@ -276,7 +273,7 @@ public:
startViews(parent); startViews(parent);
} }
void onStop() final { void onStop() override {
stopViews(); stopViews();
stopLogic(); stopLogic();
stopUart(); stopUart();

View File

@ -1,7 +1,5 @@
#pragma once #pragma once
#include <lvgl.h>
namespace tt::app::serialconsole { namespace tt::app::serialconsole {
class View { class View {

View File

@ -1,8 +1,6 @@
#pragma once #pragma once
#include <Tactility/Bundle.h> #include <string>
#include <Tactility/Mutex.h>
#include <Tactility/service/wifi/Wifi.h>
namespace tt::app::wifiapsettings { namespace tt::app::wifiapsettings {

View File

@ -5,7 +5,7 @@
namespace tt::app::wificonnect { namespace tt::app::wificonnect {
class State { class State final {
Mutex lock; Mutex lock;
service::wifi::settings::WifiApSettings apSettings; service::wifi::settings::WifiApSettings apSettings;
bool connectionError = false; bool connectionError = false;

View File

@ -3,7 +3,7 @@
#include "./Bindings.h" #include "./Bindings.h"
#include "./State.h" #include "./State.h"
#include "Tactility/app/AppContext.h" #include <Tactility/app/AppContext.h>
#include <lvgl.h> #include <lvgl.h>
@ -11,7 +11,7 @@ namespace tt::app::wificonnect {
class WifiConnect; class WifiConnect;
class View { class View final {
Bindings* bindings; Bindings* bindings;
State* state; State* state;

View File

@ -1,16 +1,16 @@
#pragma once #pragma once
#include "Tactility/app/App.h" #include <Tactility/app/App.h>
#include "Tactility/app/wificonnect/Bindings.h" #include <Tactility/app/wificonnect/Bindings.h>
#include "Tactility/app/wificonnect/State.h" #include <Tactility/app/wificonnect/State.h>
#include "Tactility/app/wificonnect/View.h" #include <Tactility/app/wificonnect/View.h>
#include <Tactility/Mutex.h> #include <Tactility/Mutex.h>
#include <Tactility/service/wifi/Wifi.h> #include <Tactility/service/wifi/Wifi.h>
namespace tt::app::wificonnect { namespace tt::app::wificonnect {
class WifiConnect : public App { class WifiConnect final : public App {
Mutex mutex; Mutex mutex;
State state; State state;
@ -27,7 +27,7 @@ class WifiConnect : public App {
public: public:
WifiConnect(); WifiConnect();
~WifiConnect(); ~WifiConnect() override;
void lock(); void lock();
void unlock(); void unlock();
@ -39,7 +39,6 @@ public:
Bindings& getBindings() { return bindings; } Bindings& getBindings() { return bindings; }
View& getView() { return view; } View& getView() { return view; }
void requestViewUpdate(); void requestViewUpdate();
}; };

View File

@ -8,7 +8,7 @@ namespace tt::app::wifimanage {
/** /**
* View's state * View's state
*/ */
class State { class State final {
Mutex mutex = Mutex(Mutex::Type::Recursive); Mutex mutex = Mutex(Mutex::Type::Recursive);
bool scanning = false; bool scanning = false;
@ -32,7 +32,7 @@ public:
template <std::invocable<const std::vector<service::wifi::ApRecord>&> Func> template <std::invocable<const std::vector<service::wifi::ApRecord>&> Func>
void withApRecords(Func&& onApRecords) const { void withApRecords(Func&& onApRecords) const {
mutex.withLock([&]() { mutex.withLock([&] {
std::invoke(std::forward<Func>(onApRecords), apRecords); std::invoke(std::forward<Func>(onApRecords), apRecords);
}); });
} }

View File

@ -3,19 +3,17 @@
#include "./Bindings.h" #include "./Bindings.h"
#include "./State.h" #include "./State.h"
#include "Tactility/app/AppContext.h" #include <Tactility/app/AppContext.h>
#include <lvgl.h> #include <lvgl.h>
namespace tt::app::wifimanage { namespace tt::app::wifimanage {
class View { class View final {
private:
Bindings* bindings; Bindings* bindings;
State* state; State* state;
std::unique_ptr<app::Paths> paths; std::unique_ptr<Paths> paths;
lv_obj_t* root = nullptr; lv_obj_t* root = nullptr;
lv_obj_t* enable_switch = nullptr; lv_obj_t* enable_switch = nullptr;
lv_obj_t* enable_on_boot_switch = nullptr; lv_obj_t* enable_on_boot_switch = nullptr;

View File

@ -3,7 +3,7 @@
#include "./View.h" #include "./View.h"
#include "./State.h" #include "./State.h"
#include "Tactility/app/App.h" #include <Tactility/app/App.h>
#include <Tactility/PubSub.h> #include <Tactility/PubSub.h>
#include <Tactility/Mutex.h> #include <Tactility/Mutex.h>
@ -11,7 +11,7 @@
namespace tt::app::wifimanage { namespace tt::app::wifimanage {
class WifiManage : public App { class WifiManage final : public App {
PubSub<service::wifi::WifiEvent>::SubscriptionHandle wifiSubscription = nullptr; PubSub<service::wifi::WifiEvent>::SubscriptionHandle wifiSubscription = nullptr;
Mutex mutex; Mutex mutex;

View File

@ -60,7 +60,7 @@ namespace app {
namespace chat { extern const AppManifest manifest; } namespace chat { extern const AppManifest manifest; }
namespace development { extern const AppManifest manifest; } namespace development { extern const AppManifest manifest; }
namespace display { extern const AppManifest manifest; } namespace display { extern const AppManifest manifest; }
namespace filebrowser { extern const AppManifest manifest; } namespace files { extern const AppManifest manifest; }
namespace fileselection { extern const AppManifest manifest; } namespace fileselection { extern const AppManifest manifest; }
namespace gpio { extern const AppManifest manifest; } namespace gpio { extern const AppManifest manifest; }
namespace gpssettings { extern const AppManifest manifest; } namespace gpssettings { extern const AppManifest manifest; }
@ -102,7 +102,7 @@ static void registerSystemApps() {
addApp(app::applist::manifest); addApp(app::applist::manifest);
addApp(app::calculator::manifest); addApp(app::calculator::manifest);
addApp(app::display::manifest); addApp(app::display::manifest);
addApp(app::filebrowser::manifest); addApp(app::files::manifest);
addApp(app::fileselection::manifest); addApp(app::fileselection::manifest);
addApp(app::gpio::manifest); addApp(app::gpio::manifest);
addApp(app::imageviewer::manifest); addApp(app::imageviewer::manifest);
@ -178,7 +178,7 @@ static void registerInstalledApp(std::string path) {
app::addApp({ app::addApp({
.id = app_id_entry->second, .id = app_id_entry->second,
.name = app_name_entry->second, .name = app_name_entry->second,
.type = app::Type::User, .category = app::Category::User,
.location = app::Location::external(path) .location = app::Location::external(path)
}); });
} }

View File

@ -216,7 +216,7 @@ bool install(const std::string& path) {
addApp({ addApp({
.id = app_id_iterator->second, .id = app_id_iterator->second,
.name = app_name_entry->second, .name = app_name_entry->second,
.type = Type::User, .category = Category::User,
.location = Location::external(renamed_target_path) .location = Location::external(renamed_target_path)
}); });

View File

@ -1,41 +1,22 @@
#ifdef ESP_PLATFORM #ifdef ESP_PLATFORM
#include "Tactility/app/ElfApp.h" #include <Tactility/app/alertdialog/AlertDialog.h>
#include "Tactility/file/File.h" #include <Tactility/app/ElfApp.h>
#include "Tactility/file/FileLock.h" #include <Tactility/file/File.h>
#include "Tactility/service/loader/Loader.h" #include <Tactility/file/FileLock.h>
#include <Tactility/Log.h> #include <Tactility/Log.h>
#include <Tactility/service/loader/Loader.h>
#include <Tactility/StringUtils.h> #include <Tactility/StringUtils.h>
#include "esp_elf.h" #include <esp_elf.h>
#include <string> #include <string>
#include <utility> #include <utility>
#include <Tactility/app/alertdialog/AlertDialog.h>
namespace tt::app { namespace tt::app {
constexpr auto* TAG = "ElfApp"; constexpr auto* TAG = "ElfApp";
struct ElfManifest {
/** The user-readable name of the app. Used in UI. */
std::string name;
/** Optional icon. */
std::string icon;
CreateData _Nullable createData = nullptr;
DestroyData _Nullable destroyData = nullptr;
OnCreate _Nullable onCreate = nullptr;
OnDestroy _Nullable onDestroy = nullptr;
OnShow _Nullable onShow = nullptr;
OnHide _Nullable onHide = nullptr;
OnResult _Nullable onResult = nullptr;
};
static size_t elfManifestSetCount = 0;
static ElfManifest elfManifest;
static std::shared_ptr<Lock> elfManifestLock = std::make_shared<Mutex>();
static std::string getErrorCodeString(int error_code) { static std::string getErrorCodeString(int error_code) {
switch (error_code) { switch (error_code) {
case ENOMEM: case ENOMEM:
@ -47,7 +28,30 @@ static std::string getErrorCodeString(int error_code) {
} }
} }
class ElfApp : public App { class ElfApp final : public App {
public:
struct Parameters {
CreateData _Nullable createData = nullptr;
DestroyData _Nullable destroyData = nullptr;
OnCreate _Nullable onCreate = nullptr;
OnDestroy _Nullable onDestroy = nullptr;
OnShow _Nullable onShow = nullptr;
OnHide _Nullable onHide = nullptr;
OnResult _Nullable onResult = nullptr;
};
static void setParameters(const Parameters& parameters) {
staticParameters = parameters;
staticParametersSetCount++;
}
private:
static Parameters staticParameters;
static size_t staticParametersSetCount;
static std::shared_ptr<Lock> staticParametersLock;
const std::string appPath; const std::string appPath;
std::unique_ptr<uint8_t[]> elfFileData; std::unique_ptr<uint8_t[]> elfFileData;
@ -60,7 +64,7 @@ class ElfApp : public App {
.entry = nullptr .entry = nullptr
}; };
bool shouldCleanupElf = false; // Whether we have to clean up the above "elf" object bool shouldCleanupElf = false; // Whether we have to clean up the above "elf" object
std::unique_ptr<ElfManifest> manifest; std::unique_ptr<Parameters> manifest;
void* data = nullptr; void* data = nullptr;
std::string lastError = ""; std::string lastError = "";
@ -128,10 +132,10 @@ public:
void onCreate(AppContext& appContext) override { void onCreate(AppContext& appContext) override {
// Because we use global variables, we have to ensure that we are not starting 2 apps in parallel // Because we use global variables, we have to ensure that we are not starting 2 apps in parallel
// We use a ScopedLock so we don't have to safeguard all branches // We use a ScopedLock so we don't have to safeguard all branches
auto lock = elfManifestLock->asScopedLock(); auto lock = staticParametersLock->asScopedLock();
lock.lock(); lock.lock();
elfManifestSetCount = 0; staticParametersSetCount = 0;
if (!startElf()) { if (!startElf()) {
service::loader::stopApp(); service::loader::stopApp();
auto message = lastError.empty() ? "Application failed to start." : std::format("Application failed to start: {}", lastError); auto message = lastError.empty() ? "Application failed to start." : std::format("Application failed to start: {}", lastError);
@ -139,13 +143,13 @@ public:
return; return;
} }
if (elfManifestSetCount == 0) { if (staticParametersSetCount == 0) {
service::loader::stopApp(); service::loader::stopApp();
alertdialog::start("Error", "Application failed to start: application failed to register itself"); alertdialog::start("Error", "Application failed to start: application failed to register itself");
return; return;
} }
manifest = std::make_unique<ElfManifest>(elfManifest); manifest = std::make_unique<Parameters>(staticParameters);
lock.unlock(); lock.unlock();
if (manifest->createData != nullptr) { if (manifest->createData != nullptr) {
@ -192,9 +196,11 @@ public:
} }
}; };
void setElfAppManifest( ElfApp::Parameters ElfApp::staticParameters;
const char* name, size_t ElfApp::staticParametersSetCount = 0;
const char* _Nullable icon, std::shared_ptr<Lock> ElfApp::staticParametersLock = std::make_shared<Mutex>();
void setElfAppParameters(
CreateData _Nullable createData, CreateData _Nullable createData,
DestroyData _Nullable destroyData, DestroyData _Nullable destroyData,
OnCreate _Nullable onCreate, OnCreate _Nullable onCreate,
@ -203,9 +209,7 @@ void setElfAppManifest(
OnHide _Nullable onHide, OnHide _Nullable onHide,
OnResult _Nullable onResult OnResult _Nullable onResult
) { ) {
elfManifest = ElfManifest { ElfApp::setParameters({
.name = name ? name : "",
.icon = icon ? icon : "",
.createData = createData, .createData = createData,
.destroyData = destroyData, .destroyData = destroyData,
.onCreate = onCreate, .onCreate = onCreate,
@ -213,8 +217,7 @@ void setElfAppManifest(
.onShow = onShow, .onShow = onShow,
.onHide = onHide, .onHide = onHide,
.onResult = onResult .onResult = onResult
}; });
elfManifestSetCount++;
} }
std::shared_ptr<App> createElfApp(const std::shared_ptr<AppManifest>& manifest) { std::shared_ptr<App> createElfApp(const std::shared_ptr<AppManifest>& manifest) {

View File

@ -175,7 +175,8 @@ extern const AppManifest manifest = {
.id = "AddGps", .id = "AddGps",
.name = "Add GPS", .name = "Add GPS",
.icon = LV_SYMBOL_GPS, .icon = LV_SYMBOL_GPS,
.type = Type::Hidden, .category = Category::System,
.flags = AppManifest::Flags::Hidden,
.createApp = create<AddGpsApp> .createApp = create<AddGpsApp>
}; };

View File

@ -128,7 +128,8 @@ public:
extern const AppManifest manifest = { extern const AppManifest manifest = {
.id = "AlertDialog", .id = "AlertDialog",
.name = "Alert Dialog", .name = "Alert Dialog",
.type = Type::Hidden, .category = Category::System,
.flags = AppManifest::Flags::Hidden,
.createApp = create<AlertDialogApp> .createApp = create<AlertDialogApp>
}; };

View File

@ -40,7 +40,9 @@ public:
std::ranges::sort(manifests, SortAppManifestByName); std::ranges::sort(manifests, SortAppManifestByName);
for (const auto& manifest: manifests) { for (const auto& manifest: manifests) {
if (manifest->type == Type::User || manifest->type == Type::System) { bool is_valid_category = (manifest->category == Category::User) || (manifest->category == Category::System);
bool is_visible = (manifest->flags & AppManifest::Flags::Hidden) == 0u;
if (is_valid_category && is_visible) {
createAppWidget(manifest, list); createAppWidget(manifest, list);
} }
} }
@ -50,7 +52,8 @@ public:
extern const AppManifest manifest = { extern const AppManifest manifest = {
.id = "AppList", .id = "AppList",
.name = "Apps", .name = "Apps",
.type = Type::Hidden, .category = Category::System,
.flags = AppManifest::Flags::Hidden,
.createApp = create<AppListApp>, .createApp = create<AppListApp>,
}; };

View File

@ -124,7 +124,6 @@ class BootApp : public App {
settings::BootSettings boot_properties; settings::BootSettings boot_properties;
if (!settings::loadBootSettings(boot_properties) || boot_properties.launcherAppId.empty()) { if (!settings::loadBootSettings(boot_properties) || boot_properties.launcherAppId.empty()) {
TT_LOG_E(TAG, "Launcher not configured"); TT_LOG_E(TAG, "Launcher not configured");
stop();
return; return;
} }
@ -164,7 +163,8 @@ public:
extern const AppManifest manifest = { extern const AppManifest manifest = {
.id = "Boot", .id = "Boot",
.name = "Boot", .name = "Boot",
.type = Type::Boot, .category = Category::System,
.flags = AppManifest::Flags::HideStatusBar | AppManifest::Flags::Hidden,
.createApp = create<BootApp> .createApp = create<BootApp>
}; };

View File

@ -118,7 +118,8 @@ public:
extern const AppManifest manifest = { extern const AppManifest manifest = {
.id = "CrashDiagnostics", .id = "CrashDiagnostics",
.name = "Crash Diagnostics", .name = "Crash Diagnostics",
.type = Type::Hidden, .category = Category::System,
.flags = AppManifest::Flags::Hidden,
.createApp = create<CrashDiagnosticsApp> .createApp = create<CrashDiagnosticsApp>
}; };

View File

@ -164,7 +164,7 @@ public:
extern const AppManifest manifest = { extern const AppManifest manifest = {
.id = "Development", .id = "Development",
.name = "Development", .name = "Development",
.type = Type::Settings, .category = Category::Settings,
.createApp = create<DevelopmentApp> .createApp = create<DevelopmentApp>
}; };

View File

@ -167,7 +167,7 @@ extern const AppManifest manifest = {
.id = "Display", .id = "Display",
.name = "Display", .name = "Display",
.icon = TT_ASSETS_APP_ICON_DISPLAY_SETTINGS, .icon = TT_ASSETS_APP_ICON_DISPLAY_SETTINGS,
.type = Type::Settings, .category = Category::Settings,
.createApp = create<DisplayApp> .createApp = create<DisplayApp>
}; };

View File

@ -1,24 +1,24 @@
#include "Tactility/app/filebrowser/View.h" #include <Tactility/app/files/View.h>
#include "Tactility/app/filebrowser/State.h" #include <Tactility/app/files/State.h>
#include "Tactility/app/AppContext.h" #include <Tactility/app/AppContext.h>
#include <Tactility/Assets.h> #include <Tactility/Assets.h>
#include <Tactility/service/loader/Loader.h> #include <Tactility/service/loader/Loader.h>
#include <memory> #include <memory>
namespace tt::app::filebrowser { namespace tt::app::files {
constexpr auto* TAG = "FileBrowser";
extern const AppManifest manifest; extern const AppManifest manifest;
class FileBrowser : public App { class FilesApp final : public App {
std::unique_ptr<View> view; std::unique_ptr<View> view;
std::shared_ptr<State> state; std::shared_ptr<State> state;
public: public:
FileBrowser() {
FilesApp() {
state = std::make_shared<State>(); state = std::make_shared<State>();
view = std::make_unique<View>(state); view = std::make_unique<View>(state);
} }
@ -36,8 +36,9 @@ extern const AppManifest manifest = {
.id = "Files", .id = "Files",
.name = "Files", .name = "Files",
.icon = TT_ASSETS_APP_ICON_FILES, .icon = TT_ASSETS_APP_ICON_FILES,
.type = Type::Hidden, .category = Category::System,
.createApp = create<FileBrowser> .flags = AppManifest::Flags::Hidden,
.createApp = create<FilesApp>
}; };
void start() { void start() {

View File

@ -1,7 +1,8 @@
#include "Tactility/app/filebrowser/State.h" #include <Tactility/app/files/State.h>
#include <Tactility/file/File.h> #include <Tactility/file/File.h>
#include "Tactility/hal/sdcard/SdCardDevice.h" #include <Tactility/file/FileLock.h>
#include <Tactility/hal/sdcard/SdCardDevice.h>
#include <Tactility/Log.h> #include <Tactility/Log.h>
#include <Tactility/MountPoints.h> #include <Tactility/MountPoints.h>
#include <Tactility/kernel/Kernel.h> #include <Tactility/kernel/Kernel.h>
@ -10,11 +11,10 @@
#include <unistd.h> #include <unistd.h>
#include <vector> #include <vector>
#include <dirent.h> #include <dirent.h>
#include <Tactility/file/FileLock.h>
namespace tt::app::filebrowser { namespace tt::app::files {
constexpr auto* TAG = "FileBrowser"; constexpr auto* TAG = "Files";
State::State() { State::State() {
if (kernel::getPlatform() == kernel::PlatformSimulator) { if (kernel::getPlatform() == kernel::PlatformSimulator) {

View File

@ -1,9 +1,9 @@
#include <Tactility/StringUtils.h> #include <Tactility/StringUtils.h>
#include <Tactility/TactilityCore.h> #include <Tactility/TactilityCore.h>
namespace tt::app::filebrowser { namespace tt::app::files {
constexpr auto* TAG = "FileBrowser"; constexpr auto* TAG = "Files";
bool isSupportedAppFile(const std::string& filename) { bool isSupportedAppFile(const std::string& filename) {
return filename.ends_with(".app"); return filename.ends_with(".app");

View File

@ -1,13 +1,13 @@
#include "Tactility/app/filebrowser/View.h" #include <Tactility/app/files/View.h>
#include "Tactility/app/filebrowser/SupportedFiles.h" #include <Tactility/app/files/SupportedFiles.h>
#include "Tactility/app/alertdialog/AlertDialog.h" #include <Tactility/app/alertdialog/AlertDialog.h>
#include "Tactility/app/imageviewer/ImageViewer.h" #include <Tactility/app/imageviewer/ImageViewer.h>
#include "Tactility/app/inputdialog/InputDialog.h" #include <Tactility/app/inputdialog/InputDialog.h>
#include "Tactility/app/notes/Notes.h" #include <Tactility/app/notes/Notes.h>
#include "Tactility/app/ElfApp.h" #include <Tactility/app/ElfApp.h>
#include "Tactility/lvgl/Toolbar.h" #include <Tactility/lvgl/Toolbar.h>
#include "Tactility/lvgl/LvglSync.h" #include <Tactility/lvgl/LvglSync.h>
#include <Tactility/Tactility.h> #include <Tactility/Tactility.h>
#include <Tactility/file/File.h> #include <Tactility/file/File.h>
@ -19,17 +19,17 @@
#include <Tactility/file/FileLock.h> #include <Tactility/file/FileLock.h>
#ifdef ESP_PLATFORM #ifdef ESP_PLATFORM
#include "Tactility/service/loader/Loader.h" #include <Tactility/service/loader/Loader.h>
#endif #endif
namespace tt::app::filebrowser { namespace tt::app::files {
constexpr auto* TAG = "FileBrowser"; constexpr auto* TAG = "Files";
// region Callbacks // region Callbacks
static void dirEntryListScrollBeginCallback(lv_event_t* event) { static void dirEntryListScrollBeginCallback(lv_event_t* event) {
auto* view = static_cast<View*>(lv_event_get_user_data(event)); auto* view = static_cast<files::View*>(lv_event_get_user_data(event));
view->onDirEntryListScrollBegin(); view->onDirEntryListScrollBegin();
} }

View File

@ -59,7 +59,8 @@ extern const AppManifest manifest = {
.id = "FileSelection", .id = "FileSelection",
.name = "File Selection", .name = "File Selection",
.icon = TT_ASSETS_APP_ICON_FILES, .icon = TT_ASSETS_APP_ICON_FILES,
.type = Type::Hidden, .category = Category::System,
.flags = AppManifest::Flags::Hidden,
.createApp = create<FileSelection> .createApp = create<FileSelection>
}; };

View File

@ -191,7 +191,7 @@ extern const AppManifest manifest = {
.id = "Gpio", .id = "Gpio",
.name = "GPIO", .name = "GPIO",
.icon = TT_ASSETS_APP_ICON_GPIO, .icon = TT_ASSETS_APP_ICON_GPIO,
.type = Type::System, .category = Category::System,
.createApp = create<GpioApp> .createApp = create<GpioApp>
}; };

View File

@ -19,12 +19,12 @@ extern AppManifest manifest;
namespace tt::app::gpssettings { namespace tt::app::gpssettings {
constexpr const char* TAG = "GpsSettings";
extern const AppManifest manifest; extern const AppManifest manifest;
class GpsSettingsApp final : public App { class GpsSettingsApp final : public App {
static constexpr auto* TAG = "GpsSettings";
std::unique_ptr<Timer> timer; std::unique_ptr<Timer> timer;
std::shared_ptr<GpsSettingsApp*> appReference = std::make_shared<GpsSettingsApp*>(this); std::shared_ptr<GpsSettingsApp*> appReference = std::make_shared<GpsSettingsApp*>(this);
lv_obj_t* statusWrapper = nullptr; lv_obj_t* statusWrapper = nullptr;
@ -268,13 +268,13 @@ class GpsSettingsApp final : public App {
public: public:
GpsSettingsApp() { GpsSettingsApp() {
timer = std::make_unique<Timer>(Timer::Type::Periodic, [this]() { timer = std::make_unique<Timer>(Timer::Type::Periodic, [this] {
updateViews(); updateViews();
}); });
service = service::gps::findGpsService(); service = service::gps::findGpsService();
} }
void onShow(AppContext& app, lv_obj_t* parent) final { void onShow(AppContext& app, lv_obj_t* parent) override {
lv_obj_set_flex_flow(parent, LV_FLEX_FLOW_COLUMN); lv_obj_set_flex_flow(parent, LV_FLEX_FLOW_COLUMN);
lv_obj_set_style_pad_row(parent, 0, LV_STATE_DEFAULT); lv_obj_set_style_pad_row(parent, 0, LV_STATE_DEFAULT);
@ -337,7 +337,7 @@ public:
updateViews(); updateViews();
} }
void onHide(AppContext& app) final { void onHide(AppContext& app) override {
service->getStatePubsub()->unsubscribe(serviceStateSubscription); service->getStatePubsub()->unsubscribe(serviceStateSubscription);
serviceStateSubscription = nullptr; serviceStateSubscription = nullptr;
} }
@ -347,7 +347,7 @@ extern const AppManifest manifest = {
.id = "GpsSettings", .id = "GpsSettings",
.name = "GPS", .name = "GPS",
.icon = LV_SYMBOL_GPS, .icon = LV_SYMBOL_GPS,
.type = Type::Settings, .category = Category::Settings,
.createApp = create<GpsSettingsApp> .createApp = create<GpsSettingsApp>
}; };

View File

@ -1,13 +1,12 @@
#include "Tactility/app/i2cscanner/I2cScannerPrivate.h" #include <Tactility/app/i2cscanner/I2cScannerPrivate.h>
#include "Tactility/app/i2cscanner/I2cScannerThread.h" #include <Tactility/app/i2cscanner/I2cHelpers.h>
#include "Tactility/app/i2cscanner/I2cHelpers.h"
#include "Tactility/Preferences.h" #include <Tactility/Preferences.h>
#include "Tactility/app/AppContext.h" #include <Tactility/app/AppContext.h>
#include "Tactility/hal/i2c/I2cDevice.h" #include <Tactility/hal/i2c/I2cDevice.h>
#include "Tactility/lvgl/LvglSync.h" #include <Tactility/lvgl/LvglSync.h>
#include "Tactility/lvgl/Toolbar.h" #include <Tactility/lvgl/Toolbar.h>
#include "Tactility/service/loader/Loader.h" #include <Tactility/service/loader/Loader.h>
#include <Tactility/Assets.h> #include <Tactility/Assets.h>
#include <Tactility/Tactility.h> #include <Tactility/Tactility.h>
@ -15,14 +14,14 @@
#include <format> #include <format>
#define START_SCAN_TEXT "Scan"
#define STOP_SCAN_TEXT "Stop scan"
namespace tt::app::i2cscanner { namespace tt::app::i2cscanner {
extern const AppManifest manifest; extern const AppManifest manifest;
class I2cScannerApp : public App { class I2cScannerApp final : public App {
static constexpr auto* START_SCAN_TEXT = "Scan";
static constexpr auto* STOP_SCAN_TEXT = "Stop scan";
// Core // Core
Mutex mutex = Mutex(Mutex::Type::Recursive); Mutex mutex = Mutex(Mutex::Type::Recursive);
@ -181,13 +180,6 @@ void I2cScannerApp::onPressScanCallback(lv_event_t* event) {
} }
} }
void I2cScannerApp::onScanTimerCallback() {
auto app = optApp();
if (app != nullptr) {
app->onScanTimer();
}
}
// endregion Callbacks // endregion Callbacks
bool I2cScannerApp::getPort(i2c_port_t* outPort) { bool I2cScannerApp::getPort(i2c_port_t* outPort) {
@ -285,8 +277,8 @@ void I2cScannerApp::startScanning() {
lv_obj_clean(scanListWidget); lv_obj_clean(scanListWidget);
scanState = ScanStateScanning; scanState = ScanStateScanning;
scanTimer = std::make_unique<Timer>(Timer::Type::Once, []{ scanTimer = std::make_unique<Timer>(Timer::Type::Once, [this]{
onScanTimerCallback(); onScanTimer();
}); });
scanTimer->start(10); scanTimer->start(10);
mutex.unlock(); mutex.unlock();
@ -414,7 +406,7 @@ extern const AppManifest manifest = {
.id = "I2cScanner", .id = "I2cScanner",
.name = "I2C Scanner", .name = "I2C Scanner",
.icon = TT_ASSETS_APP_ICON_I2C_SETTINGS, .icon = TT_ASSETS_APP_ICON_I2C_SETTINGS,
.type = Type::System, .category = Category::System,
.createApp = create<I2cScannerApp> .createApp = create<I2cScannerApp>
}; };

View File

@ -96,7 +96,7 @@ extern const AppManifest manifest = {
.id = "I2cSettings", .id = "I2cSettings",
.name = "I2C", .name = "I2C",
.icon = TT_ASSETS_APP_ICON_I2C_SETTINGS, .icon = TT_ASSETS_APP_ICON_I2C_SETTINGS,
.type = Type::Settings, .category = Category::Settings,
.createApp = create<I2cSettingsApp> .createApp = create<I2cSettingsApp>
}; };

View File

@ -62,7 +62,8 @@ class ImageViewerApp : public App {
extern const AppManifest manifest = { extern const AppManifest manifest = {
.id = "ImageViewer", .id = "ImageViewer",
.name = "Image Viewer", .name = "Image Viewer",
.type = Type::Hidden, .category = Category::System,
.flags = AppManifest::Flags::Hidden,
.createApp = create<ImageViewerApp> .createApp = create<ImageViewerApp>
}; };

View File

@ -120,7 +120,8 @@ public:
extern const AppManifest manifest = { extern const AppManifest manifest = {
.id = "InputDialog", .id = "InputDialog",
.name = "Input Dialog", .name = "Input Dialog",
.type = Type::Hidden, .category = Category::System,
.flags = AppManifest::Flags::Hidden,
.createApp = create<InputDialogApp> .createApp = create<InputDialogApp>
}; };

View File

@ -138,7 +138,7 @@ public:
extern const AppManifest manifest = { extern const AppManifest manifest = {
.id = "Launcher", .id = "Launcher",
.name = "Launcher", .name = "Launcher",
.type = Type::Launcher, .category = Category::System,
.createApp = create<LauncherApp> .createApp = create<LauncherApp>
}; };

View File

@ -165,7 +165,7 @@ extern const AppManifest manifest = {
.id = "LocaleSettings", .id = "LocaleSettings",
.name = "Region & Language", .name = "Region & Language",
.icon = TT_ASSETS_APP_ICON_TIME_DATE_SETTINGS, .icon = TT_ASSETS_APP_ICON_TIME_DATE_SETTINGS,
.type = Type::Settings, .category = Category::Settings,
.createApp = create<LocaleSettingsApp> .createApp = create<LocaleSettingsApp>
}; };

View File

@ -123,7 +123,7 @@ extern const AppManifest manifest = {
.id = "Log", .id = "Log",
.name = "Log", .name = "Log",
.icon = LV_SYMBOL_LIST, .icon = LV_SYMBOL_LIST,
.type = Type::System, .category = Category::System,
.createApp = create<LogApp> .createApp = create<LogApp>
}; };

View File

@ -192,7 +192,7 @@ extern const AppManifest manifest = {
.id = "Power", .id = "Power",
.name = "Power", .name = "Power",
.icon = TT_ASSETS_APP_ICON_POWER_SETTINGS, .icon = TT_ASSETS_APP_ICON_POWER_SETTINGS,
.type = Type::Settings, .category = Category::Settings,
.createApp = create<PowerApp> .createApp = create<PowerApp>
}; };

View File

@ -281,7 +281,7 @@ extern const AppManifest manifest = {
.id = "Screenshot", .id = "Screenshot",
.name = "Screenshot", .name = "Screenshot",
.icon = LV_SYMBOL_IMAGE, .icon = LV_SYMBOL_IMAGE,
.type = Type::System, .category = Category::System,
.createApp = create<ScreenshotApp> .createApp = create<ScreenshotApp>
}; };

View File

@ -114,7 +114,8 @@ public:
extern const AppManifest manifest = { extern const AppManifest manifest = {
.id = "SelectionDialog", .id = "SelectionDialog",
.name = "Selection Dialog", .name = "Selection Dialog",
.type = Type::Hidden, .category = Category::System,
.flags = AppManifest::Flags::Hidden,
.createApp = create<SelectionDialogApp> .createApp = create<SelectionDialogApp>
}; };

View File

@ -1,4 +1,4 @@
#include "Tactility/app/serialconsole/ConnectView.h" #include "../../../Private/Tactility/app/serialconsole/ConnectView.h"
#include "Tactility/app/serialconsole/ConsoleView.h" #include "Tactility/app/serialconsole/ConsoleView.h"
#include "Tactility/lvgl/Style.h" #include "Tactility/lvgl/Style.h"
@ -88,7 +88,7 @@ extern const AppManifest manifest = {
.id = "SerialConsole", .id = "SerialConsole",
.name = "Serial Console", .name = "Serial Console",
.icon = LV_SYMBOL_LIST, .icon = LV_SYMBOL_LIST,
.type = Type::System, .category = Category::System,
.createApp = create<SerialConsoleApp> .createApp = create<SerialConsoleApp>
}; };

View File

@ -38,7 +38,7 @@ class SettingsApp : public App {
auto manifests = getApps(); auto manifests = getApps();
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 == Type::Settings) { if (manifest->category == Category::Settings) {
createWidget(manifest, list); createWidget(manifest, list);
} }
} }
@ -49,7 +49,8 @@ extern const AppManifest manifest = {
.id = "Settings", .id = "Settings",
.name = "Settings", .name = "Settings",
.icon = TT_ASSETS_APP_ICON_SETTINGS, .icon = TT_ASSETS_APP_ICON_SETTINGS,
.type = Type::Hidden, .category = Category::System,
.flags = AppManifest::Flags::Hidden,
.createApp = create<SettingsApp> .createApp = create<SettingsApp>
}; };

View File

@ -295,7 +295,7 @@ extern const AppManifest manifest = {
.id = "SystemInfo", .id = "SystemInfo",
.name = "System Info", .name = "System Info",
.icon = TT_ASSETS_APP_ICON_SYSTEM_INFO, .icon = TT_ASSETS_APP_ICON_SYSTEM_INFO,
.type = Type::System, .category = Category::System,
.createApp = create<SystemInfoApp> .createApp = create<SystemInfoApp>
}; };

View File

@ -60,7 +60,7 @@ extern const AppManifest manifest = {
.id = "TimeDateSettings", .id = "TimeDateSettings",
.name = "Time & Date", .name = "Time & Date",
.icon = TT_ASSETS_APP_ICON_TIME_DATE_SETTINGS, .icon = TT_ASSETS_APP_ICON_TIME_DATE_SETTINGS,
.type = Type::Settings, .category = Category::Settings,
.createApp = create<TimeDateSettingsApp> .createApp = create<TimeDateSettingsApp>
}; };

View File

@ -1,9 +1,9 @@
#include "Tactility/app/AppContext.h" #include <Tactility/app/AppContext.h>
#include "Tactility/app/AppManifest.h" #include <Tactility/app/AppManifest.h>
#include "Tactility/app/timezone/TimeZone.h" #include <Tactility/app/timezone/TimeZone.h>
#include "Tactility/lvgl/Toolbar.h" #include <Tactility/lvgl/Toolbar.h>
#include "Tactility/lvgl/LvglSync.h" #include <Tactility/lvgl/LvglSync.h>
#include "Tactility/service/loader/Loader.h" #include <Tactility/service/loader/Loader.h>
#include <Tactility/MountPoints.h> #include <Tactility/MountPoints.h>
#include <Tactility/StringUtils.h> #include <Tactility/StringUtils.h>
@ -15,9 +15,8 @@
namespace tt::app::timezone { namespace tt::app::timezone {
constexpr auto* TAG = "TimeZone"; constexpr auto* TAG = "TimeZone";
constexpr auto* RESULT_BUNDLE_CODE_INDEX = "code";
#define RESULT_BUNDLE_CODE_INDEX "code" constexpr auto* RESULT_BUNDLE_NAME_INDEX = "name";
#define RESULT_BUNDLE_NAME_INDEX "name"
extern const AppManifest manifest; extern const AppManifest manifest;
@ -113,14 +112,6 @@ class TimeZoneApp final : public App {
lv_obj_add_event_cb(btn, &onListItemSelectedCallback, LV_EVENT_SHORT_CLICKED, (void*)index); lv_obj_add_event_cb(btn, &onListItemSelectedCallback, LV_EVENT_SHORT_CLICKED, (void*)index);
} }
static void updateTimerCallback() {
auto appContext = getCurrentAppContext();
if (appContext != nullptr && appContext->getManifest().id == manifest.id) {
auto app = std::static_pointer_cast<TimeZoneApp>(appContext->getApp());
app->updateList();
}
}
void readTimeZones(std::string filter) { void readTimeZones(std::string filter) {
auto path = std::string(file::MOUNT_POINT_SYSTEM) + "/timezones.csv"; auto path = std::string(file::MOUNT_POINT_SYSTEM) + "/timezones.csv";
auto* file = fopen(path.c_str(), "rb"); auto* file = fopen(path.c_str(), "rb");
@ -228,14 +219,17 @@ public:
} }
void onCreate(AppContext& app) override { void onCreate(AppContext& app) override {
updateTimer = std::make_unique<Timer>(Timer::Type::Once, [] { updateTimerCallback(); }); updateTimer = std::make_unique<Timer>(Timer::Type::Once, [this] {
updateList();
});
} }
}; };
extern const AppManifest manifest = { extern const AppManifest manifest = {
.id = "TimeZone", .id = "TimeZone",
.name = "Select timezone", .name = "Select timezone",
.type = Type::Hidden, .category = Category::System,
.flags = AppManifest::Flags::Hidden,
.createApp = create<TimeZoneApp> .createApp = create<TimeZoneApp>
}; };

View File

@ -45,7 +45,7 @@ extern const AppManifest manifest = {
.id = "UsbSettings", .id = "UsbSettings",
.name = "USB", .name = "USB",
.icon = LV_SYMBOL_USB, .icon = LV_SYMBOL_USB,
.type = Type::Settings, .category = Category::Settings,
.createApp = create<UsbSettingsApp> .createApp = create<UsbSettingsApp>
}; };

View File

@ -1,20 +1,18 @@
#include "Tactility/app/wifiapsettings/WifiApSettings.h" #include <Tactility/service/wifi/WifiApSettings.h>
#include <Tactility/service/wifi/Wifi.h>
#include "Tactility/app/App.h" #include <Tactility/app/App.h>
#include "Tactility/app/AppContext.h" #include <Tactility/app/AppContext.h>
#include "Tactility/app/AppManifest.h" #include <Tactility/app/AppManifest.h>
#include "Tactility/app/alertdialog/AlertDialog.h" #include <Tactility/app/alertdialog/AlertDialog.h>
#include "Tactility/lvgl/Style.h" #include <Tactility/lvgl/Style.h>
#include "Tactility/lvgl/Toolbar.h" #include <Tactility/lvgl/Toolbar.h>
#include <Tactility/TactilityCore.h> #include <Tactility/TactilityCore.h>
#include <Tactility/service/wifi/WifiSettings.h>
#include <lvgl.h> #include <lvgl.h>
namespace tt::app::wifiapsettings { namespace tt::app::wifiapsettings {
#define TAG "wifi_ap_settings" constexpr auto* TAG = "WifiApSettings";
extern const AppManifest manifest; extern const AppManifest manifest;
@ -146,7 +144,8 @@ extern const AppManifest manifest = {
.id = "WifiApSettings", .id = "WifiApSettings",
.name = "Wi-Fi AP Settings", .name = "Wi-Fi AP Settings",
.icon = LV_SYMBOL_WIFI, .icon = LV_SYMBOL_WIFI,
.type = Type::Hidden, .category = Category::System,
.flags = AppManifest::Flags::Hidden,
.createApp = create<WifiApSettings> .createApp = create<WifiApSettings>
}; };

View File

@ -97,7 +97,8 @@ extern const AppManifest manifest = {
.id = "WifiConnect", .id = "WifiConnect",
.name = "Wi-Fi Connect", .name = "Wi-Fi Connect",
.icon = LV_SYMBOL_WIFI, .icon = LV_SYMBOL_WIFI,
.type = Type::Hidden, .category = Category::System,
.flags = AppManifest::Flags::Hidden,
.createApp = create<WifiConnect> .createApp = create<WifiConnect>
}; };

View File

@ -136,7 +136,7 @@ extern const AppManifest manifest = {
.id = "WifiManage", .id = "WifiManage",
.name = "Wi-Fi", .name = "Wi-Fi",
.icon = LV_SYMBOL_WIFI, .icon = LV_SYMBOL_WIFI,
.type = Type::Settings, .category = Category::Settings,
.createApp = create<WifiManage> .createApp = create<WifiManage>
}; };

View File

@ -1,7 +1,6 @@
#include "Tactility/service/gui/GuiService.h" #include "Tactility/service/gui/GuiService.h"
#include "Tactility/lvgl/LvglSync.h" #include "Tactility/lvgl/LvglSync.h"
#include "Tactility/lvgl/Statusbar.h" #include "Tactility/lvgl/Statusbar.h"
#include "Tactility/lvgl/Style.h"
#include "Tactility/service/loader/Loader.h" #include "Tactility/service/loader/Loader.h"
#include <Tactility/Tactility.h> #include <Tactility/Tactility.h>
@ -92,10 +91,10 @@ void GuiService::redraw() {
lv_group_set_default(group); lv_group_set_default(group);
app::Flags flags = std::static_pointer_cast<app::AppInstance>(appToRender)->getFlags(); app::Flags flags = std::static_pointer_cast<app::AppInstance>(appToRender)->getFlags();
if (flags.showStatusbar) { if (flags.hideStatusbar) {
lv_obj_remove_flag(statusbarWidget, LV_OBJ_FLAG_HIDDEN);
} else {
lv_obj_add_flag(statusbarWidget, LV_OBJ_FLAG_HIDDEN); lv_obj_add_flag(statusbarWidget, LV_OBJ_FLAG_HIDDEN);
} else {
lv_obj_remove_flag(statusbarWidget, LV_OBJ_FLAG_HIDDEN);
} }
lv_obj_t* container = createAppViews(appRootWidget); lv_obj_t* container = createAppViews(appRootWidget);

View File

@ -103,7 +103,7 @@ void LoaderService::onStartAppMessage(const std::string& id, app::LaunchId launc
auto previous_app = !appStack.empty() ? appStack.top() : nullptr; auto previous_app = !appStack.empty() ? appStack.top() : nullptr;
auto new_app = std::make_shared<app::AppInstance>(app_manifest, launchId, parameters); auto new_app = std::make_shared<app::AppInstance>(app_manifest, launchId, parameters);
new_app->mutableFlags().showStatusbar = (app_manifest->type != app::Type::Boot); new_app->mutableFlags().hideStatusbar = (app_manifest->flags & app::AppManifest::Flags::HideStatusBar);
appStack.push(new_app); appStack.push(new_app);
transitionAppToState(new_app, app::State::Initial); transitionAppToState(new_app, app::State::Initial);
@ -141,7 +141,7 @@ void LoaderService::onStopAppMessage(const std::string& id) {
return; return;
} }
if (original_stack_size == 1 && app_to_stop->getManifest().type != app::Type::Boot) { if (original_stack_size == 1 && app_to_stop->getManifest().name != "Boot") {
TT_LOG_E(TAG, "Stop app: can't stop root app"); TT_LOG_E(TAG, "Stop app: can't stop root app");
return; return;
} }

View File

@ -1,9 +1,10 @@
#include "Tactility/lvgl/Statusbar.h" #include <Tactility/lvgl/Statusbar.h>
#include "Tactility/lvgl/LvglSync.h" #include <Tactility/lvgl/LvglSync.h>
#include "Tactility/hal/power/PowerDevice.h" #include <Tactility/hal/power/PowerDevice.h>
#include "Tactility/hal/sdcard/SdCardDevice.h" #include <Tactility/hal/sdcard/SdCardDevice.h>
#include "Tactility/service/gps/GpsService.h" #include <Tactility/lvgl/Lvgl.h>
#include <Tactility/service/gps/GpsService.h>
#include <Tactility/Mutex.h> #include <Tactility/Mutex.h>
#include <Tactility/Tactility.h> #include <Tactility/Tactility.h>
#include <Tactility/Timer.h> #include <Tactility/Timer.h>
@ -16,31 +17,31 @@ namespace tt::service::statusbar {
constexpr auto* TAG = "StatusbarService"; constexpr auto* TAG = "StatusbarService";
// SD card status // SD card status
#define STATUSBAR_ICON_SDCARD "sdcard.png" constexpr auto* STATUSBAR_ICON_SDCARD = "sdcard.png";
#define STATUSBAR_ICON_SDCARD_ALERT "sdcard_alert.png" constexpr auto* STATUSBAR_ICON_SDCARD_ALERT = "sdcard_alert.png";
// Wifi status // Wifi status
#define STATUSBAR_ICON_WIFI_OFF_WHITE "wifi_off_white.png" constexpr auto* STATUSBAR_ICON_WIFI_OFF_WHITE = "wifi_off_white.png";
#define STATUSBAR_ICON_WIFI_SCAN_WHITE "wifi_scan_white.png" constexpr auto* STATUSBAR_ICON_WIFI_SCAN_WHITE = "wifi_scan_white.png";
#define STATUSBAR_ICON_WIFI_SIGNAL_WEAK_WHITE "wifi_signal_weak_white.png" constexpr auto* STATUSBAR_ICON_WIFI_SIGNAL_WEAK_WHITE = "wifi_signal_weak_white.png";
#define STATUSBAR_ICON_WIFI_SIGNAL_MEDIUM_WHITE "wifi_signal_medium_white.png" constexpr auto* STATUSBAR_ICON_WIFI_SIGNAL_MEDIUM_WHITE = "wifi_signal_medium_white.png";
#define STATUSBAR_ICON_WIFI_SIGNAL_STRONG_WHITE "wifi_signal_strong_white.png" constexpr auto* STATUSBAR_ICON_WIFI_SIGNAL_STRONG_WHITE = "wifi_signal_strong_white.png";
// Power status // Power status
#define STATUSBAR_ICON_POWER_0 "power_0.png" constexpr auto* STATUSBAR_ICON_POWER_0 = "power_0.png";
#define STATUSBAR_ICON_POWER_10 "power_10.png" constexpr auto* STATUSBAR_ICON_POWER_10 = "power_10.png";
#define STATUSBAR_ICON_POWER_20 "power_20.png" constexpr auto* STATUSBAR_ICON_POWER_20 = "power_20.png";
#define STATUSBAR_ICON_POWER_30 "power_30.png" constexpr auto* STATUSBAR_ICON_POWER_30 = "power_30.png";
#define STATUSBAR_ICON_POWER_40 "power_40.png" constexpr auto* STATUSBAR_ICON_POWER_40 = "power_40.png";
#define STATUSBAR_ICON_POWER_50 "power_50.png" constexpr auto* STATUSBAR_ICON_POWER_50 = "power_50.png";
#define STATUSBAR_ICON_POWER_60 "power_60.png" constexpr auto* STATUSBAR_ICON_POWER_60 = "power_60.png";
#define STATUSBAR_ICON_POWER_70 "power_70.png" constexpr auto* STATUSBAR_ICON_POWER_70 = "power_70.png";
#define STATUSBAR_ICON_POWER_80 "power_80.png" constexpr auto* STATUSBAR_ICON_POWER_80 = "power_80.png";
#define STATUSBAR_ICON_POWER_90 "power_90.png" constexpr auto* STATUSBAR_ICON_POWER_90 = "power_90.png";
#define STATUSBAR_ICON_POWER_100 "power_100.png" constexpr auto* STATUSBAR_ICON_POWER_100 = "power_100.png";
// GPS // GPS
#define STATUSBAR_ICON_GPS "location.png" constexpr auto* STATUSBAR_ICON_GPS = "location.png";
extern const ServiceManifest manifest; extern const ServiceManifest manifest;
@ -222,15 +223,15 @@ class StatusbarService final : public Service {
} }
void update() { void update() {
// TODO: Make thread-safe for LVGL if (lvgl::isStarted()) {
if (lvgl::lock(100)) {
updateGpsIcon(); updateGpsIcon();
updateWifiIcon(); updateWifiIcon();
updateSdCardIcon(); updateSdCardIcon();
updatePowerStatusIcon(); updatePowerStatusIcon();
lvgl::unlock();
}
} }
static void onUpdate(const std::shared_ptr<StatusbarService>& service) {
service->update();
} }
public: public:
@ -262,10 +263,10 @@ public:
auto service = findServiceById<StatusbarService>(manifest.id); auto service = findServiceById<StatusbarService>(manifest.id);
assert(service); assert(service);
onUpdate(service); service->update();
updateTimer = std::make_unique<Timer>(Timer::Type::Periodic, [service] { updateTimer = std::make_unique<Timer>(Timer::Type::Periodic, [service] {
onUpdate(service); service->update();
}); });
// We want to try and scan more often in case of startup or scan lock failure // We want to try and scan more often in case of startup or scan lock failure

View File

@ -28,10 +28,6 @@ typedef void (*AppOnHide)(AppHandle app, void* _Nullable data);
typedef void (*AppOnResult)(AppHandle app, void* _Nullable data, AppLaunchId launchId, AppResult result, BundleHandle resultData); typedef void (*AppOnResult)(AppHandle app, void* _Nullable data, AppLaunchId launchId, AppResult result, BundleHandle resultData);
typedef struct { typedef struct {
/** The application's human-readable name */
const char* name;
/** The application icon (you can use LV_SYMBOL_* too) */
const char* _Nullable icon;
/** The application can allocate data to re-use later (e.g. struct with state) */ /** The application can allocate data to re-use later (e.g. struct with state) */
AppCreateData _Nullable createData; AppCreateData _Nullable createData;
/** If createData is specified, this one must be specified too */ /** If createData is specified, this one must be specified too */

View File

@ -3,18 +3,16 @@
#include <Tactility/Check.h> #include <Tactility/Check.h>
#include <Tactility/app/ElfApp.h> #include <Tactility/app/ElfApp.h>
#define TAG "tt_app"
extern "C" { extern "C" {
constexpr auto TAG = "tt_app";
void tt_app_register( void tt_app_register(
const ExternalAppManifest* manifest const ExternalAppManifest* manifest
) { ) {
#ifdef ESP_PLATFORM #ifdef ESP_PLATFORM
assert((manifest->createData == nullptr) == (manifest->destroyData == nullptr)); assert((manifest->createData == nullptr) == (manifest->destroyData == nullptr));
setElfAppManifest( tt::app::setElfAppParameters(
manifest->name,
manifest->icon,
manifest->createData, manifest->createData,
manifest->destroyData, manifest->destroyData,
manifest->onCreate, manifest->onCreate,

View File

@ -90,6 +90,9 @@ const esp_elfsym elf_symbols[] {
ESP_ELFSYM_EXPORT(strstr), ESP_ELFSYM_EXPORT(strstr),
ESP_ELFSYM_EXPORT(memset), ESP_ELFSYM_EXPORT(memset),
ESP_ELFSYM_EXPORT(memcpy), ESP_ELFSYM_EXPORT(memcpy),
ESP_ELFSYM_EXPORT(memcmp),
ESP_ELFSYM_EXPORT(memchr),
ESP_ELFSYM_EXPORT(memmove),
// ctype // ctype
ESP_ELFSYM_EXPORT(isalnum), ESP_ELFSYM_EXPORT(isalnum),
ESP_ELFSYM_EXPORT(isalpha), ESP_ELFSYM_EXPORT(isalpha),